@farthershore/product 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -31
- package/dist/bin.js +252 -35
- package/dist/codegen.js +43 -16
- package/dist/index.js +253 -41
- package/dist/types/errors.d.ts +3 -3
- package/dist/types/index.d.ts +4 -6
- package/dist/types/ir-types.d.ts +46 -1
- package/dist/types/{business.d.ts → product.d.ts} +74 -33
- package/dist/types/resource-graph.d.ts +1 -1
- package/package.json +4 -4
package/dist/codegen.js
CHANGED
|
@@ -28,6 +28,9 @@ var TOP_LEVEL_HANDLED = /* @__PURE__ */ new Set([
|
|
|
28
28
|
"policies",
|
|
29
29
|
"customer_context",
|
|
30
30
|
"resources",
|
|
31
|
+
"surfaces",
|
|
32
|
+
"entitlements",
|
|
33
|
+
"workflows",
|
|
31
34
|
"plans"
|
|
32
35
|
]);
|
|
33
36
|
var PLAN_HANDLED_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -58,8 +61,8 @@ function generateManifestSource(ir) {
|
|
|
58
61
|
lines.push(`import { fs } from "@farthershore/product";`);
|
|
59
62
|
lines.push("");
|
|
60
63
|
lines.push(
|
|
61
|
-
`const
|
|
62
|
-
|
|
64
|
+
`const product = fs.product(${lit(product.product.name)}, ${lit(
|
|
65
|
+
productOptions(product),
|
|
63
66
|
0
|
|
64
67
|
)});`
|
|
65
68
|
);
|
|
@@ -68,7 +71,7 @@ function generateManifestSource(ir) {
|
|
|
68
71
|
lines.push("");
|
|
69
72
|
for (const meter of meters) {
|
|
70
73
|
const { key, ...rest } = meter;
|
|
71
|
-
lines.push(`
|
|
74
|
+
lines.push(`product.meter(${lit(key)}, ${lit(rest, 0)});`);
|
|
72
75
|
}
|
|
73
76
|
}
|
|
74
77
|
const resources = product.resources ?? [];
|
|
@@ -76,7 +79,15 @@ function generateManifestSource(ir) {
|
|
|
76
79
|
lines.push("");
|
|
77
80
|
for (const resource of resources) {
|
|
78
81
|
const { name, ...rest } = resource;
|
|
79
|
-
lines.push(`
|
|
82
|
+
lines.push(`product.resource(${lit(name)}, ${lit(rest, 0)});`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const surfaces = product.surfaces ?? [];
|
|
86
|
+
if (surfaces.length) {
|
|
87
|
+
lines.push("");
|
|
88
|
+
for (const surface of surfaces) {
|
|
89
|
+
const { type, key, ...rest } = surface;
|
|
90
|
+
lines.push(`product.surface(${lit(type)}, ${lit({ key, ...rest }, 0)});`);
|
|
80
91
|
}
|
|
81
92
|
}
|
|
82
93
|
if (ir.capabilities.length) {
|
|
@@ -93,7 +104,7 @@ function generateManifestSource(ir) {
|
|
|
93
104
|
if (cap.includes_capabilities?.length)
|
|
94
105
|
options.includesCapabilities = cap.includes_capabilities;
|
|
95
106
|
lines.push(
|
|
96
|
-
`
|
|
107
|
+
`product.capability(${lit(cap.capability)}, ${lit(options, 0)});`
|
|
97
108
|
);
|
|
98
109
|
}
|
|
99
110
|
}
|
|
@@ -118,7 +129,7 @@ function generateManifestSource(ir) {
|
|
|
118
129
|
...cw.auth_modes ? { authModes: cw.auth_modes } : {}
|
|
119
130
|
};
|
|
120
131
|
}
|
|
121
|
-
lines.push(`
|
|
132
|
+
lines.push(`product.policy(${lit(policy.name)}, ${lit(options, 0)});`);
|
|
122
133
|
}
|
|
123
134
|
}
|
|
124
135
|
if (ir.routes.length) {
|
|
@@ -155,43 +166,59 @@ function generateManifestSource(ir) {
|
|
|
155
166
|
...route.inheritDefaultMeters !== void 0 ? { inheritDefaultMeters: route.inheritDefaultMeters } : {},
|
|
156
167
|
...route.action !== void 0 ? { action: route.action } : {}
|
|
157
168
|
}));
|
|
158
|
-
lines.push(`
|
|
169
|
+
lines.push(`product.feature(${lit(file.feature)}, ${lit(options, 0)});`);
|
|
159
170
|
}
|
|
160
171
|
}
|
|
161
172
|
if (product.frontend !== void 0) {
|
|
162
173
|
lines.push("");
|
|
163
|
-
lines.push(`
|
|
174
|
+
lines.push(`product.frontend.manifest(${lit(product.frontend, 0)});`);
|
|
175
|
+
}
|
|
176
|
+
const entitlements = product.entitlements ?? [];
|
|
177
|
+
if (entitlements.length) {
|
|
178
|
+
lines.push("");
|
|
179
|
+
for (const entitlement of entitlements) {
|
|
180
|
+
const { key, ...rest } = entitlement;
|
|
181
|
+
lines.push(`product.entitlement(${lit(key)}, ${lit(rest, 0)});`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const workflows = product.workflows ?? [];
|
|
185
|
+
if (workflows.length) {
|
|
186
|
+
lines.push("");
|
|
187
|
+
for (const workflow of workflows) {
|
|
188
|
+
const { key, ...rest } = workflow;
|
|
189
|
+
lines.push(`product.workflow(${lit(key)}, ${lit(rest, 0)});`);
|
|
190
|
+
}
|
|
164
191
|
}
|
|
165
192
|
if (product.migrations?.length) {
|
|
166
193
|
lines.push("");
|
|
167
|
-
lines.push(`
|
|
194
|
+
lines.push(`product.lifecycle.migrations(${lit(product.migrations, 0)});`);
|
|
168
195
|
}
|
|
169
196
|
const plans = product.plans ?? [];
|
|
170
197
|
if (plans.length) {
|
|
171
198
|
lines.push("");
|
|
172
199
|
for (const plan of plans) {
|
|
173
200
|
lines.push(
|
|
174
|
-
`
|
|
201
|
+
`product.plan(${lit(plan.key)}, ${lit(planOptions(plan), 0)});`
|
|
175
202
|
);
|
|
176
203
|
}
|
|
177
204
|
}
|
|
178
205
|
const patch = productPatch(product);
|
|
179
206
|
if (Object.keys(patch).length) {
|
|
180
207
|
lines.push("");
|
|
181
|
-
lines.push(`
|
|
208
|
+
lines.push(`product.raw.productPatch(${lit(patch, 0)});`);
|
|
182
209
|
}
|
|
183
210
|
lines.push("");
|
|
184
|
-
lines.push("export default
|
|
211
|
+
lines.push("export default product;");
|
|
185
212
|
lines.push("");
|
|
186
213
|
return lines.join("\n");
|
|
187
214
|
}
|
|
188
|
-
function
|
|
215
|
+
function productOptions(product) {
|
|
189
216
|
const block = product.product;
|
|
190
|
-
const options = {
|
|
217
|
+
const options = { origin: block.baseUrl };
|
|
191
218
|
if (block.displayName !== void 0) options.displayName = block.displayName;
|
|
192
219
|
if (block.description !== void 0) options.description = block.description;
|
|
193
220
|
if (block.sandboxBaseUrl !== void 0)
|
|
194
|
-
options.
|
|
221
|
+
options.sandboxOrigin = block.sandboxBaseUrl;
|
|
195
222
|
if (block.visibility !== void 0) options.visibility = block.visibility;
|
|
196
223
|
if (block.logoUrl !== void 0) options.logoUrl = block.logoUrl;
|
|
197
224
|
if (block.primaryColor !== void 0)
|
|
@@ -224,7 +251,7 @@ function customerContextOptions(context) {
|
|
|
224
251
|
return {
|
|
225
252
|
...context.identity_requirement !== void 0 ? { identityRequirement: context.identity_requirement } : {},
|
|
226
253
|
...context.context_tokens !== void 0 ? { contextTokens: context.context_tokens } : {},
|
|
227
|
-
...context.portal_auth !== void 0 ? {
|
|
254
|
+
...context.portal_auth !== void 0 ? { customerAuth: context.portal_auth } : {}
|
|
228
255
|
};
|
|
229
256
|
}
|
|
230
257
|
function planOptions(plan) {
|
package/dist/index.js
CHANGED
|
@@ -1385,7 +1385,7 @@ var customerPortalAuthStrategySchema = z13.enum([
|
|
|
1385
1385
|
var productCustomerContextSchema = z13.object({
|
|
1386
1386
|
/**
|
|
1387
1387
|
* Edge credential identity policy. This is intentionally Product-scoped:
|
|
1388
|
-
* B7 keeps Product as the
|
|
1388
|
+
* B7 keeps Product as the product boundary and avoids customer-side
|
|
1389
1389
|
* Workspace/subject models until per-subject entitlements need them.
|
|
1390
1390
|
*/
|
|
1391
1391
|
identity_requirement: customerIdentityRequirementSchema.optional(),
|
|
@@ -1406,6 +1406,70 @@ var productCustomerContextSchema = z13.object({
|
|
|
1406
1406
|
strategy: customerPortalAuthStrategySchema
|
|
1407
1407
|
}).optional()
|
|
1408
1408
|
});
|
|
1409
|
+
var productSurfaceTypeSchema = z13.enum([
|
|
1410
|
+
"frontend",
|
|
1411
|
+
"api",
|
|
1412
|
+
"docs",
|
|
1413
|
+
"widget",
|
|
1414
|
+
"dashboard",
|
|
1415
|
+
"webhook",
|
|
1416
|
+
"worker",
|
|
1417
|
+
"agent"
|
|
1418
|
+
]);
|
|
1419
|
+
var productSurfaceSchema = z13.object({
|
|
1420
|
+
key: z13.string().min(1).max(64).regex(/^[a-z0-9_-]+$/, "Surface key must be lowercase alphanumeric with hyphens/underscores"),
|
|
1421
|
+
type: productSurfaceTypeSchema,
|
|
1422
|
+
display: z13.string().min(1).max(100).optional(),
|
|
1423
|
+
description: z13.string().max(500).optional()
|
|
1424
|
+
});
|
|
1425
|
+
var productEntitlementSchema = z13.object({
|
|
1426
|
+
key: z13.string().min(1).max(64).regex(/^[a-z0-9_-]+$/, "Entitlement key must be lowercase alphanumeric with hyphens/underscores"),
|
|
1427
|
+
description: z13.string().max(500).optional(),
|
|
1428
|
+
capabilities: z13.array(z13.string().min(1).max(100)).max(100).optional(),
|
|
1429
|
+
featureGates: z13.record(z13.string().min(1), z13.boolean()).optional(),
|
|
1430
|
+
limits: z13.array(planLimitRuleSchema).max(100).optional(),
|
|
1431
|
+
meters: z13.array(z13.string().min(1).max(64)).max(100).optional()
|
|
1432
|
+
});
|
|
1433
|
+
var productSurfacesSchema = z13.array(productSurfaceSchema).max(20).default([]);
|
|
1434
|
+
var productEntitlementsSchema = z13.array(productEntitlementSchema).max(100).default([]);
|
|
1435
|
+
var productWorkflowKindSchema = z13.enum([
|
|
1436
|
+
"async_job",
|
|
1437
|
+
"agent_task",
|
|
1438
|
+
"scheduled",
|
|
1439
|
+
"lifecycle",
|
|
1440
|
+
"background"
|
|
1441
|
+
]);
|
|
1442
|
+
var productWorkflowTriggerSchema = z13.discriminatedUnion("type", [
|
|
1443
|
+
z13.object({ type: z13.literal("manual") }),
|
|
1444
|
+
z13.object({
|
|
1445
|
+
type: z13.literal("schedule"),
|
|
1446
|
+
cron: z13.string().min(1).max(120)
|
|
1447
|
+
}),
|
|
1448
|
+
z13.object({
|
|
1449
|
+
type: z13.literal("event"),
|
|
1450
|
+
event: z13.string().min(1).max(120)
|
|
1451
|
+
}),
|
|
1452
|
+
z13.object({
|
|
1453
|
+
type: z13.literal("api"),
|
|
1454
|
+
path: z13.string().min(1).max(240).regex(/^\//, "path must start with /")
|
|
1455
|
+
}),
|
|
1456
|
+
z13.object({
|
|
1457
|
+
type: z13.literal("lifecycle"),
|
|
1458
|
+
event: z13.string().min(1).max(120)
|
|
1459
|
+
})
|
|
1460
|
+
]);
|
|
1461
|
+
var productWorkflowSchema = z13.object({
|
|
1462
|
+
key: z13.string().min(1).max(64).regex(/^[a-z0-9_-]+$/, "Workflow key must be lowercase alphanumeric with hyphens/underscores"),
|
|
1463
|
+
title: z13.string().min(1).max(120).optional(),
|
|
1464
|
+
description: z13.string().max(1e3).optional(),
|
|
1465
|
+
kind: productWorkflowKindSchema,
|
|
1466
|
+
trigger: productWorkflowTriggerSchema,
|
|
1467
|
+
capabilities: z13.array(z13.string().min(1).max(100)).max(100).optional(),
|
|
1468
|
+
meters: z13.array(z13.string().min(1).max(64)).max(100).optional(),
|
|
1469
|
+
estimates: z13.record(z13.string().min(1).max(64), z13.number().finite()).optional(),
|
|
1470
|
+
metadata: z13.record(z13.string().min(1), z13.unknown()).optional()
|
|
1471
|
+
});
|
|
1472
|
+
var productWorkflowsSchema = z13.array(productWorkflowSchema).max(100).default([]);
|
|
1409
1473
|
var productSpecSchema = z13.object({
|
|
1410
1474
|
product: z13.object({
|
|
1411
1475
|
name: z13.string().min(1).max(100),
|
|
@@ -1439,6 +1503,9 @@ var productSpecSchema = z13.object({
|
|
|
1439
1503
|
resources: countedResourcesSchema,
|
|
1440
1504
|
policies: productOperatorPoliciesSchema,
|
|
1441
1505
|
customer_context: productCustomerContextSchema.optional(),
|
|
1506
|
+
surfaces: productSurfacesSchema,
|
|
1507
|
+
entitlements: productEntitlementsSchema,
|
|
1508
|
+
workflows: productWorkflowsSchema,
|
|
1442
1509
|
/**
|
|
1443
1510
|
* Legacy/internal declarative frontend surface. New product repos customize
|
|
1444
1511
|
* the generated `frontend/` project directly; this remains for existing IR
|
|
@@ -2148,6 +2215,9 @@ var productSpecV2Schema = z20.object({
|
|
|
2148
2215
|
resources: countedResourcesSchema,
|
|
2149
2216
|
policies: productOperatorPoliciesSchema,
|
|
2150
2217
|
customer_context: productCustomerContextSchema.optional(),
|
|
2218
|
+
surfaces: productSurfacesSchema,
|
|
2219
|
+
entitlements: productEntitlementsSchema,
|
|
2220
|
+
workflows: productWorkflowsSchema,
|
|
2151
2221
|
billing: z20.object({
|
|
2152
2222
|
gracePeriodDays: z20.number().int().nonnegative().default(3),
|
|
2153
2223
|
// When true (default), a plan limit INCREASE re-projects onto active
|
|
@@ -2348,10 +2418,13 @@ function canonicalIrJson(ir) {
|
|
|
2348
2418
|
}
|
|
2349
2419
|
|
|
2350
2420
|
// src/version.ts
|
|
2351
|
-
var SDK_VERSION = true ? "0.
|
|
2421
|
+
var SDK_VERSION = true ? "0.5.0" : "0.0.0-dev";
|
|
2352
2422
|
|
|
2353
|
-
// src/
|
|
2354
|
-
var
|
|
2423
|
+
// src/product.ts
|
|
2424
|
+
var PRODUCT_BRAND = Symbol.for("farthershore.product.product");
|
|
2425
|
+
var PRODUCT_MANIFEST_COMPILER = Symbol.for(
|
|
2426
|
+
"farthershore.product.manifestCompiler"
|
|
2427
|
+
);
|
|
2355
2428
|
function isCapabilityGrant(value) {
|
|
2356
2429
|
return typeof value === "object" && value !== null && value.kind === "capability_grant";
|
|
2357
2430
|
}
|
|
@@ -2396,8 +2469,8 @@ function parseRouteMatch(match) {
|
|
|
2396
2469
|
}
|
|
2397
2470
|
return { method, path };
|
|
2398
2471
|
}
|
|
2399
|
-
var
|
|
2400
|
-
[
|
|
2472
|
+
var Product = class {
|
|
2473
|
+
[PRODUCT_BRAND] = true;
|
|
2401
2474
|
name;
|
|
2402
2475
|
options;
|
|
2403
2476
|
graph = new ManifestResourceGraph();
|
|
@@ -2408,15 +2481,16 @@ var Business = class {
|
|
|
2408
2481
|
api;
|
|
2409
2482
|
frontend;
|
|
2410
2483
|
lifecycle;
|
|
2411
|
-
|
|
2484
|
+
offering;
|
|
2485
|
+
/** Escape hatches — raw platform-schema JSON, validated by the compiler. */
|
|
2412
2486
|
raw;
|
|
2413
2487
|
constructor(name, options) {
|
|
2414
2488
|
if (!name || typeof name !== "string") {
|
|
2415
|
-
throw new ManifestBuilderError("fs.
|
|
2489
|
+
throw new ManifestBuilderError("fs.product(name, \u2026): name is required");
|
|
2416
2490
|
}
|
|
2417
|
-
if (!options?.
|
|
2491
|
+
if (!options?.origin) {
|
|
2418
2492
|
throw new ManifestBuilderError(
|
|
2419
|
-
`fs.
|
|
2493
|
+
`fs.product("${name}", \u2026): options.origin is required (the business logic origin Farther Shore calls for customer-facing actions)`
|
|
2420
2494
|
);
|
|
2421
2495
|
}
|
|
2422
2496
|
this.name = name;
|
|
@@ -2431,7 +2505,7 @@ var Business = class {
|
|
|
2431
2505
|
const file = this.getFeatureFile(featureKey);
|
|
2432
2506
|
if (!file) {
|
|
2433
2507
|
throw new ManifestBuilderError(
|
|
2434
|
-
`api.route("${match}"): feature "${featureKey}" is not declared \u2014 call
|
|
2508
|
+
`api.route("${match}"): feature "${featureKey}" is not declared \u2014 call product.feature("${featureKey}", \u2026) first`
|
|
2435
2509
|
);
|
|
2436
2510
|
}
|
|
2437
2511
|
file.routes.push(this.buildRoute(match, options2));
|
|
@@ -2524,6 +2598,9 @@ var Business = class {
|
|
|
2524
2598
|
return this;
|
|
2525
2599
|
}
|
|
2526
2600
|
};
|
|
2601
|
+
this.offering = {
|
|
2602
|
+
plan: (key, options2) => this.plan(key, options2)
|
|
2603
|
+
};
|
|
2527
2604
|
this.raw = {
|
|
2528
2605
|
productPatch: (patch) => {
|
|
2529
2606
|
this.productPatch = deepMerge(this.productPatch, patch);
|
|
@@ -2694,6 +2771,57 @@ var Business = class {
|
|
|
2694
2771
|
this.graph.register("policy", name, file, this.policyDependsOn(file));
|
|
2695
2772
|
return { kind: "policy", key: name };
|
|
2696
2773
|
}
|
|
2774
|
+
surface(type, options = {}) {
|
|
2775
|
+
const key = options.key ?? type;
|
|
2776
|
+
this.assertNewKey("surface", key, "surface");
|
|
2777
|
+
const surface = {
|
|
2778
|
+
key,
|
|
2779
|
+
type,
|
|
2780
|
+
...options.display !== void 0 ? { display: options.display } : {},
|
|
2781
|
+
...options.description !== void 0 ? { description: options.description } : {}
|
|
2782
|
+
};
|
|
2783
|
+
this.graph.register("surface", key, surface);
|
|
2784
|
+
return { kind: "surface", key };
|
|
2785
|
+
}
|
|
2786
|
+
workflow(key, options = {}) {
|
|
2787
|
+
this.assertNewKey("workflow", key, "workflow");
|
|
2788
|
+
const workflow = {
|
|
2789
|
+
key,
|
|
2790
|
+
kind: options.kind ?? "async_job",
|
|
2791
|
+
trigger: options.trigger ?? { type: "manual" },
|
|
2792
|
+
...options.title !== void 0 ? { title: options.title } : {},
|
|
2793
|
+
...options.description !== void 0 ? { description: options.description } : {},
|
|
2794
|
+
...options.capabilities?.length ? { capabilities: options.capabilities.map(keyOf) } : {},
|
|
2795
|
+
...options.meters?.length ? { meters: options.meters.map(keyOf) } : {},
|
|
2796
|
+
...options.estimates !== void 0 ? { estimates: options.estimates } : {},
|
|
2797
|
+
...options.metadata !== void 0 ? { metadata: options.metadata } : {}
|
|
2798
|
+
};
|
|
2799
|
+
this.graph.register(
|
|
2800
|
+
"workflow",
|
|
2801
|
+
key,
|
|
2802
|
+
workflow,
|
|
2803
|
+
this.workflowDependsOn(workflow)
|
|
2804
|
+
);
|
|
2805
|
+
return { kind: "workflow", key };
|
|
2806
|
+
}
|
|
2807
|
+
entitlement(key, options = {}) {
|
|
2808
|
+
this.assertNewKey("entitlement", key, "entitlement");
|
|
2809
|
+
const entitlement = {
|
|
2810
|
+
key,
|
|
2811
|
+
...options.description !== void 0 ? { description: options.description } : {},
|
|
2812
|
+
...options.capabilities?.length ? { capabilities: options.capabilities.map(keyOf) } : {},
|
|
2813
|
+
...options.featureGates !== void 0 ? { featureGates: options.featureGates } : {},
|
|
2814
|
+
...options.limits?.length ? { limits: options.limits } : {},
|
|
2815
|
+
...options.meters?.length ? { meters: options.meters.map(keyOf) } : {}
|
|
2816
|
+
};
|
|
2817
|
+
this.graph.register(
|
|
2818
|
+
"entitlement",
|
|
2819
|
+
key,
|
|
2820
|
+
entitlement,
|
|
2821
|
+
this.entitlementDependsOn(entitlement)
|
|
2822
|
+
);
|
|
2823
|
+
return { kind: "entitlement", key };
|
|
2824
|
+
}
|
|
2697
2825
|
plan(key, options) {
|
|
2698
2826
|
this.assertNewKey("plan", key, "plan");
|
|
2699
2827
|
const capabilityKeys = (options.capabilities ?? []).map(keyOf);
|
|
@@ -2757,19 +2885,17 @@ var Business = class {
|
|
|
2757
2885
|
resourceGraph() {
|
|
2758
2886
|
return this.graph.snapshot();
|
|
2759
2887
|
}
|
|
2760
|
-
/**
|
|
2761
|
-
*
|
|
2762
|
-
|
|
2888
|
+
/** @internal Internal platform compiler entrypoint. Builder code exports the
|
|
2889
|
+
* Product; the bot/CLI/build-runner decide when to compile and apply it. */
|
|
2890
|
+
[PRODUCT_MANIFEST_COMPILER]() {
|
|
2891
|
+
const routes = this.materializeFeatureFiles();
|
|
2892
|
+
this.assertRouteMeteringValid(routes);
|
|
2763
2893
|
this.assertGraphDependenciesSatisfied();
|
|
2764
|
-
this.assertRouteMeteringValid();
|
|
2765
2894
|
const candidate = {
|
|
2766
2895
|
irVersion: 1,
|
|
2767
2896
|
sdkVersion: SDK_VERSION,
|
|
2768
2897
|
product: this.buildProductSpec(),
|
|
2769
|
-
routes
|
|
2770
|
-
"feature",
|
|
2771
|
-
(file) => file.feature
|
|
2772
|
-
),
|
|
2898
|
+
routes,
|
|
2773
2899
|
policies: this.graph.sortedValues(
|
|
2774
2900
|
"policy",
|
|
2775
2901
|
(file) => file.name
|
|
@@ -2789,10 +2915,10 @@ var Business = class {
|
|
|
2789
2915
|
const base = {
|
|
2790
2916
|
product: {
|
|
2791
2917
|
name: this.name,
|
|
2792
|
-
baseUrl: options.
|
|
2918
|
+
baseUrl: options.origin,
|
|
2793
2919
|
...options.displayName !== void 0 ? { displayName: options.displayName } : {},
|
|
2794
2920
|
...options.description !== void 0 ? { description: options.description } : {},
|
|
2795
|
-
...options.
|
|
2921
|
+
...options.sandboxOrigin !== void 0 ? { sandboxBaseUrl: options.sandboxOrigin } : {},
|
|
2796
2922
|
...options.visibility !== void 0 ? { visibility: options.visibility } : {},
|
|
2797
2923
|
...options.logoUrl !== void 0 ? { logoUrl: options.logoUrl } : {},
|
|
2798
2924
|
...options.primaryColor !== void 0 ? { primaryColor: options.primaryColor } : {},
|
|
@@ -2809,6 +2935,24 @@ var Business = class {
|
|
|
2809
2935
|
...options.billing !== void 0 ? { billing: options.billing } : {},
|
|
2810
2936
|
...options.operatorPolicies !== void 0 ? { policies: options.operatorPolicies } : {},
|
|
2811
2937
|
...options.customerContext !== void 0 ? { customer_context: buildCustomerContext(options.customerContext) } : {},
|
|
2938
|
+
...this.graph.values("surface").length ? {
|
|
2939
|
+
surfaces: this.graph.sortedValues(
|
|
2940
|
+
"surface",
|
|
2941
|
+
(surface) => surface.key
|
|
2942
|
+
)
|
|
2943
|
+
} : {},
|
|
2944
|
+
...this.graph.values("entitlement").length ? {
|
|
2945
|
+
entitlements: this.graph.sortedValues(
|
|
2946
|
+
"entitlement",
|
|
2947
|
+
(entitlement) => entitlement.key
|
|
2948
|
+
)
|
|
2949
|
+
} : {},
|
|
2950
|
+
...this.graph.values("workflow").length ? {
|
|
2951
|
+
workflows: this.graph.sortedValues(
|
|
2952
|
+
"workflow",
|
|
2953
|
+
(workflow) => workflow.key
|
|
2954
|
+
)
|
|
2955
|
+
} : {},
|
|
2812
2956
|
...this.graph.has("frontend", "manifest") ? {
|
|
2813
2957
|
frontend: this.graph.get(
|
|
2814
2958
|
"frontend",
|
|
@@ -2844,6 +2988,9 @@ var Business = class {
|
|
|
2844
2988
|
}
|
|
2845
2989
|
routeValueMeterKeys() {
|
|
2846
2990
|
const keys = /* @__PURE__ */ new Set();
|
|
2991
|
+
for (const cost of this.defaultMeterCosts) {
|
|
2992
|
+
keys.add(cost.meter);
|
|
2993
|
+
}
|
|
2847
2994
|
for (const file of this.graph.values("feature")) {
|
|
2848
2995
|
for (const route of file.routes) {
|
|
2849
2996
|
for (const meter of Object.keys(route.metering?.defaults ?? {})) {
|
|
@@ -2854,6 +3001,11 @@ var Business = class {
|
|
|
2854
3001
|
}
|
|
2855
3002
|
}
|
|
2856
3003
|
}
|
|
3004
|
+
for (const workflow of this.graph.values("workflow")) {
|
|
3005
|
+
for (const meter of workflow.meters ?? []) keys.add(meter);
|
|
3006
|
+
for (const meter of Object.keys(workflow.estimates ?? {}))
|
|
3007
|
+
keys.add(meter);
|
|
3008
|
+
}
|
|
2857
3009
|
return keys;
|
|
2858
3010
|
}
|
|
2859
3011
|
buildRoute(match, options) {
|
|
@@ -2878,11 +3030,6 @@ var Business = class {
|
|
|
2878
3030
|
buildRouteMetering(options) {
|
|
2879
3031
|
if (options.unmetered === true) return void 0;
|
|
2880
3032
|
const defaults = {};
|
|
2881
|
-
if (options.inheritDefaultMeters !== false) {
|
|
2882
|
-
for (const cost of this.defaultMeterCosts) {
|
|
2883
|
-
defaults[cost.meter] = (defaults[cost.meter] ?? 0) + cost.value;
|
|
2884
|
-
}
|
|
2885
|
-
}
|
|
2886
3033
|
for (const cost of this.normalizeMeterCosts(options.costs)) {
|
|
2887
3034
|
defaults[cost.meter] = (defaults[cost.meter] ?? 0) + cost.value;
|
|
2888
3035
|
}
|
|
@@ -2910,6 +3057,32 @@ var Business = class {
|
|
|
2910
3057
|
out.onStatusCodes = options.onStatusCodes;
|
|
2911
3058
|
return Object.keys(out).length ? out : void 0;
|
|
2912
3059
|
}
|
|
3060
|
+
materializeFeatureFiles() {
|
|
3061
|
+
return this.graph.sortedValues("feature", (file) => file.feature).map((file) => ({
|
|
3062
|
+
...file,
|
|
3063
|
+
routes: file.routes.map((route) => this.materializeRoute(route))
|
|
3064
|
+
}));
|
|
3065
|
+
}
|
|
3066
|
+
materializeRoute(route) {
|
|
3067
|
+
if (route.unmetered === true) return route;
|
|
3068
|
+
const defaults = {
|
|
3069
|
+
...route.metering?.defaults ?? {}
|
|
3070
|
+
};
|
|
3071
|
+
if (route.inheritDefaultMeters !== false) {
|
|
3072
|
+
for (const cost of this.defaultMeterCosts) {
|
|
3073
|
+
defaults[cost.meter] = (defaults[cost.meter] ?? 0) + cost.value;
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
const hasMetering = route.metering !== void 0 || Object.keys(defaults).length > 0;
|
|
3077
|
+
const metering = hasMetering ? {
|
|
3078
|
+
...route.metering ?? {},
|
|
3079
|
+
...Object.keys(defaults).length ? { defaults } : {}
|
|
3080
|
+
} : void 0;
|
|
3081
|
+
return {
|
|
3082
|
+
...route,
|
|
3083
|
+
...metering ? { metering } : {}
|
|
3084
|
+
};
|
|
3085
|
+
}
|
|
2913
3086
|
normalizeMeterCost(cost) {
|
|
2914
3087
|
if (!cost || cost.kind !== "meter_cost") {
|
|
2915
3088
|
throw new ManifestBuilderError(
|
|
@@ -3034,6 +3207,27 @@ var Business = class {
|
|
|
3034
3207
|
policyDependsOn(file) {
|
|
3035
3208
|
return this.dependenciesFor("meter", file.compatible_with?.meters);
|
|
3036
3209
|
}
|
|
3210
|
+
entitlementDependsOn(entitlement) {
|
|
3211
|
+
const limitDimensions = (entitlement.limits ?? []).map(
|
|
3212
|
+
(limit) => limit.dimension
|
|
3213
|
+
);
|
|
3214
|
+
return [
|
|
3215
|
+
...this.dependenciesFor("capability", entitlement.capabilities),
|
|
3216
|
+
...this.existingDependenciesFor("meter", [
|
|
3217
|
+
...entitlement.meters ?? [],
|
|
3218
|
+
...limitDimensions
|
|
3219
|
+
])
|
|
3220
|
+
];
|
|
3221
|
+
}
|
|
3222
|
+
workflowDependsOn(workflow) {
|
|
3223
|
+
return [
|
|
3224
|
+
...this.dependenciesFor("capability", workflow.capabilities),
|
|
3225
|
+
...this.dependenciesFor("meter", [
|
|
3226
|
+
...workflow.meters ?? [],
|
|
3227
|
+
...Object.keys(workflow.estimates ?? {})
|
|
3228
|
+
])
|
|
3229
|
+
];
|
|
3230
|
+
}
|
|
3037
3231
|
featureDependsOn(file) {
|
|
3038
3232
|
const meterKeys = file.routes.flatMap(
|
|
3039
3233
|
(route) => this.routeMeterDependencyKeys(route)
|
|
@@ -3056,12 +3250,26 @@ var Business = class {
|
|
|
3056
3250
|
}
|
|
3057
3251
|
return [...keys];
|
|
3058
3252
|
}
|
|
3059
|
-
assertRouteMeteringValid() {
|
|
3060
|
-
|
|
3253
|
+
assertRouteMeteringValid(files) {
|
|
3254
|
+
const declaredMeters = new Set(
|
|
3255
|
+
this.graph.values("meter").map((meter) => meter.key)
|
|
3256
|
+
);
|
|
3257
|
+
for (const file of files) {
|
|
3061
3258
|
file.routes.forEach((route, routeIndex) => {
|
|
3062
3259
|
if (route.unmetered === true) return;
|
|
3063
3260
|
const defaults = new Set(Object.keys(route.metering?.defaults ?? {}));
|
|
3261
|
+
for (const meter of defaults) {
|
|
3262
|
+
if (declaredMeters.has(meter)) continue;
|
|
3263
|
+
throw new ManifestBuilderError(
|
|
3264
|
+
`feature "${file.feature}" route ${routeIndex}: fixed meter "${meter}" is not declared \u2014 declare it with product.meter("${meter}", ...) first`
|
|
3265
|
+
);
|
|
3266
|
+
}
|
|
3064
3267
|
for (const meter of route.metering?.reports ?? []) {
|
|
3268
|
+
if (!declaredMeters.has(meter)) {
|
|
3269
|
+
throw new ManifestBuilderError(
|
|
3270
|
+
`feature "${file.feature}" route ${routeIndex}: reported meter "${meter}" is not declared \u2014 declare it with product.meter("${meter}", ...) first`
|
|
3271
|
+
);
|
|
3272
|
+
}
|
|
3065
3273
|
if (defaults.has(meter)) {
|
|
3066
3274
|
throw new ManifestBuilderError(
|
|
3067
3275
|
`feature "${file.feature}" route ${routeIndex}: meter "${meter}" cannot be both a fixed route cost and a dynamic report`
|
|
@@ -3084,6 +3292,12 @@ var Business = class {
|
|
|
3084
3292
|
);
|
|
3085
3293
|
}
|
|
3086
3294
|
}
|
|
3295
|
+
for (const meter of Object.keys(route.metering?.estimates ?? {})) {
|
|
3296
|
+
if (declaredMeters.has(meter)) continue;
|
|
3297
|
+
throw new ManifestBuilderError(
|
|
3298
|
+
`feature "${file.feature}" route ${routeIndex}: estimate meter "${meter}" is not declared \u2014 declare it with product.meter("${meter}", ...) first`
|
|
3299
|
+
);
|
|
3300
|
+
}
|
|
3087
3301
|
});
|
|
3088
3302
|
}
|
|
3089
3303
|
}
|
|
@@ -3139,13 +3353,13 @@ var Business = class {
|
|
|
3139
3353
|
return [...new Set(keys)].filter((key) => this.graph.has(kind, key)).map((key) => resourceDependency(kind, key));
|
|
3140
3354
|
}
|
|
3141
3355
|
};
|
|
3142
|
-
function
|
|
3143
|
-
return typeof value === "object" && value !== null && value[
|
|
3356
|
+
function isProduct(value) {
|
|
3357
|
+
return typeof value === "object" && value !== null && value[PRODUCT_BRAND] === true;
|
|
3144
3358
|
}
|
|
3145
|
-
function
|
|
3146
|
-
const
|
|
3147
|
-
if (configure)
|
|
3148
|
-
return
|
|
3359
|
+
function product(name, options, configure) {
|
|
3360
|
+
const instance = new Product(name, options);
|
|
3361
|
+
if (configure) instance.use(configure);
|
|
3362
|
+
return instance;
|
|
3149
3363
|
}
|
|
3150
3364
|
function isPlainObject(value) {
|
|
3151
3365
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -3154,7 +3368,7 @@ function buildCustomerContext(options) {
|
|
|
3154
3368
|
return {
|
|
3155
3369
|
...options.identityRequirement !== void 0 ? { identity_requirement: options.identityRequirement } : {},
|
|
3156
3370
|
...options.contextTokens !== void 0 ? { context_tokens: options.contextTokens } : {},
|
|
3157
|
-
...options.
|
|
3371
|
+
...options.customerAuth !== void 0 ? { portal_auth: options.customerAuth } : {}
|
|
3158
3372
|
};
|
|
3159
3373
|
}
|
|
3160
3374
|
function deepMerge(base, patch) {
|
|
@@ -3204,19 +3418,17 @@ var price = {
|
|
|
3204
3418
|
};
|
|
3205
3419
|
|
|
3206
3420
|
// src/index.ts
|
|
3207
|
-
var
|
|
3208
|
-
var fs = { business, product, price };
|
|
3421
|
+
var fs = { product, price };
|
|
3209
3422
|
export {
|
|
3210
|
-
BUSINESS_BRAND,
|
|
3211
|
-
Business,
|
|
3212
3423
|
ManifestBuilderError,
|
|
3213
3424
|
ManifestValidationError,
|
|
3425
|
+
PRODUCT_BRAND,
|
|
3426
|
+
Product,
|
|
3214
3427
|
SDK_VERSION,
|
|
3215
|
-
business,
|
|
3216
3428
|
canonicalIrJson,
|
|
3217
3429
|
fs,
|
|
3218
3430
|
hashIr,
|
|
3219
|
-
|
|
3431
|
+
isProduct,
|
|
3220
3432
|
price,
|
|
3221
3433
|
product,
|
|
3222
3434
|
validateManifestIr
|
package/dist/types/errors.d.ts
CHANGED
|
@@ -5,9 +5,9 @@ export type ManifestIssue = {
|
|
|
5
5
|
message: string;
|
|
6
6
|
};
|
|
7
7
|
/**
|
|
8
|
-
* Thrown by
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* Thrown by the platform manifest compiler when the assembled manifest fails
|
|
9
|
+
* schema validation. Plain-data `issues` so the error survives serialization
|
|
10
|
+
* across the runner callback boundary.
|
|
11
11
|
*/
|
|
12
12
|
export declare class ManifestValidationError extends Error {
|
|
13
13
|
readonly issues: ManifestIssue[];
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare const product: typeof business;
|
|
1
|
+
import { product } from "./product.js";
|
|
3
2
|
export declare const fs: {
|
|
4
|
-
|
|
5
|
-
product: typeof business;
|
|
3
|
+
product: typeof product;
|
|
6
4
|
price: {
|
|
7
5
|
monthly(dollars: number): import("./price.js").PriceSpec;
|
|
8
6
|
yearly(dollars: number): import("./price.js").PriceSpec;
|
|
9
7
|
free(): import("./price.js").PriceSpec;
|
|
10
8
|
};
|
|
11
9
|
};
|
|
12
|
-
export {
|
|
10
|
+
export { product, Product, isProduct, PRODUCT_BRAND } from "./product.js";
|
|
13
11
|
export { price } from "./price.js";
|
|
14
12
|
export { validateManifestIr, hashIr, canonicalIrJson } from "./validate.js";
|
|
15
13
|
export { ManifestValidationError, ManifestBuilderError } from "./errors.js";
|
|
16
14
|
export { SDK_VERSION } from "./version.js";
|
|
17
|
-
export type {
|
|
15
|
+
export type { ProductOptions, MeterOptions, RequestMeterOptions, MeterCost, ResourceOptions, CapabilityOptions, ActionOptions, FrontendNavItemOptions, FrontendPageOptions, FrontendComponentOptions, MigrationOptions, FeatureOptions, RouteOptions, PolicyOptions, PlanOptions, SurfaceOptions, EntitlementOptions, MeterRef, ResourceRef, ActionRef, PolicyRef, PlanRef, SurfaceRef, EntitlementRef, FeatureRef, CapabilityRef, PlanCapabilityGrant, ProductModule, } from "./product.js";
|
|
18
16
|
export type { ManifestResourceGraphSnapshot, ManifestResourceKind, ManifestResourceUrn, } from "./resource-graph.js";
|
|
19
17
|
export type { PriceSpec } from "./price.js";
|
|
20
18
|
export type { ManifestIssue } from "./errors.js";
|