UNPKG

services-as-software

Version:

Define services with objectives, key results, and various pricing models

173 lines (171 loc) 6.18 kB
// src/pricing/index.ts function calculateCostBasedPrice(pricing, quantity = 1) { const { costBase, fixedCosts = 0, variableCosts = 0 } = pricing; return costBase + fixedCosts + variableCosts * quantity; } function calculateMarginBasedPrice(pricing, quantity = 1) { const { costBase, marginPercentage } = pricing; const cost = costBase * quantity; const margin = cost * (marginPercentage / 100); return cost + margin; } function calculateActivityBasedPrice(pricing, activities) { return pricing.activities.reduce((total, activity) => { const quantity = activities[activity.name] || 0; return total + activity.rate * quantity; }, 0); } function calculateOutcomeBasedPrice(pricing, outcomes) { return pricing.outcomes.reduce((total, outcome) => { const achievedValue = outcomes[outcome.metric] || 0; if (achievedValue >= outcome.targetValue) { return total + outcome.price; } return total; }, 0); } function calculatePrice(pricing, params = {}) { const { quantity = 1, activities = {}, outcomes = {} } = params; switch (pricing.model) { case "cost-based": return calculateCostBasedPrice(pricing, quantity); case "margin-based": return calculateMarginBasedPrice(pricing, quantity); case "activity-based": return calculateActivityBasedPrice(pricing, activities); case "outcome-based": return calculateOutcomeBasedPrice(pricing, outcomes); default: throw new Error(`Unsupported pricing model: ${pricing.model}`); } } // src/index.ts function Service(definition) { validateServiceDefinition(definition); const service = { ...definition, /** * Calculate the price for this service * @param params Parameters for price calculation * @returns Calculated price */ calculatePrice(params) { return calculatePrice(definition.pricing, params); }, /** * Register the service with the service registry * @param options Optional configuration for service registration * @returns Promise resolving to the registered service */ async register(options) { try { const { Services } = await import("services.do"); const services = new Services(options); const serviceDefinition = { name: definition.name, description: definition.description, endpoint: `https://api.services.do/implementations/${definition.implementation.type}/${definition.implementation.id}`, version: definition.implementation.version, metadata: { ...definition.metadata, objective: definition.objective, keyResults: definition.keyResults, pricing: definition.pricing, implementation: definition.implementation } }; const registeredService = await services.register(serviceDefinition); return { ...definition, id: registeredService.id, status: registeredService.status, endpoint: registeredService.endpoint, createdAt: registeredService.createdAt, updatedAt: registeredService.updatedAt }; } catch (error) { console.warn("Failed to register with services.do, using mock implementation", error); return { ...definition, id: generateId(), status: "active", endpoint: `https://api.services.do/services/${generateId()}`, createdAt: (/* @__PURE__ */ new Date()).toISOString(), updatedAt: (/* @__PURE__ */ new Date()).toISOString() }; } }, /** * Track progress towards key results * @param results Record of key result updates */ trackProgress(results) { definition.keyResults.forEach((kr) => { if (kr.description in results) { kr.currentValue = results[kr.description]; } }); }, /** * Check if all key results have been achieved * @returns Whether all key results have been achieved */ isObjectiveAchieved() { return definition.keyResults.every((kr) => kr.currentValue !== void 0 && kr.target !== void 0 && kr.currentValue >= kr.target); } }; return service; } function validateServiceDefinition(definition) { const { name, objective, keyResults, pricing, implementation } = definition; if (!name) { throw new Error("Service name is required"); } if (!objective || !objective.description) { throw new Error("Service objective with description is required"); } if (!keyResults || !Array.isArray(keyResults) || keyResults.length === 0) { throw new Error("At least one key result is required"); } if (!pricing || !pricing.model) { throw new Error("Service pricing model is required"); } if (!implementation || !implementation.type || !implementation.id) { throw new Error("Service implementation details are required"); } switch (pricing.model) { case "cost-based": if (pricing.costBase === void 0) { throw new Error("Cost base is required for cost-based pricing"); } break; case "margin-based": if (pricing.costBase === void 0 || pricing.marginPercentage === void 0) { throw new Error("Cost base and margin percentage are required for margin-based pricing"); } break; case "activity-based": if (!pricing.activities || !Array.isArray(pricing.activities) || pricing.activities.length === 0) { throw new Error("At least one activity is required for activity-based pricing"); } break; case "outcome-based": if (!pricing.outcomes || !Array.isArray(pricing.outcomes) || pricing.outcomes.length === 0) { throw new Error("At least one outcome is required for outcome-based pricing"); } break; default: throw new Error(`Unsupported pricing model: ${pricing.model}`); } } function generateId() { return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); } export { Service, calculateActivityBasedPrice, calculateCostBasedPrice, calculateMarginBasedPrice, calculateOutcomeBasedPrice, calculatePrice };