@farthershore/product 0.6.0 → 0.6.1
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 +1 -1
- package/dist/bin.js +540 -558
- package/dist/codegen.js +203 -165
- package/dist/index.js +540 -558
- package/dist/types/codegen/index.d.ts +9 -1
- package/dist/types/declarations.d.ts +3 -1
- package/dist/types/ir-types.d.ts +13 -1
- package/dist/types/product.d.ts +12 -4
- package/dist/types/route-metering.d.ts +3 -10
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -154,15 +154,9 @@ var planLimitsSchema = z.array(planLimitRuleSchema).max(20);
|
|
|
154
154
|
|
|
155
155
|
// ../contracts/dist/plans/spec/subscriber-change-policy.js
|
|
156
156
|
import { z as z2 } from "zod";
|
|
157
|
-
var subscriberChangeActionSchema = z2.enum([
|
|
158
|
-
"preserve_current_period",
|
|
159
|
-
"switch_immediately",
|
|
160
|
-
"switch_immediately_prorate",
|
|
161
|
-
"new_subscribers_only"
|
|
162
|
-
]);
|
|
157
|
+
var subscriberChangeActionSchema = z2.enum(["immediate", "period_end"]);
|
|
163
158
|
var subscriberChangePolicySchema = z2.object({
|
|
164
|
-
default: subscriberChangeActionSchema.default("
|
|
165
|
-
proration: z2.enum(["none", "prorate", "credit"]).default("none"),
|
|
159
|
+
default: subscriberChangeActionSchema.default("period_end"),
|
|
166
160
|
when: z2.object({
|
|
167
161
|
price_increase: subscriberChangeActionSchema.optional(),
|
|
168
162
|
price_decrease: subscriberChangeActionSchema.optional(),
|
|
@@ -174,24 +168,20 @@ var subscriberChangePolicySchema = z2.object({
|
|
|
174
168
|
credit_reduced: subscriberChangeActionSchema.optional(),
|
|
175
169
|
rating_changed: subscriberChangeActionSchema.optional()
|
|
176
170
|
}).strict().default({
|
|
177
|
-
price_increase: "
|
|
178
|
-
price_decrease: "
|
|
179
|
-
feature_added: "
|
|
180
|
-
feature_removed: "
|
|
181
|
-
limit_increased: "
|
|
182
|
-
limit_reduced: "
|
|
183
|
-
credit_increased: "
|
|
184
|
-
credit_reduced: "
|
|
185
|
-
rating_changed: "
|
|
171
|
+
price_increase: "period_end",
|
|
172
|
+
price_decrease: "immediate",
|
|
173
|
+
feature_added: "immediate",
|
|
174
|
+
feature_removed: "period_end",
|
|
175
|
+
limit_increased: "immediate",
|
|
176
|
+
limit_reduced: "period_end",
|
|
177
|
+
credit_increased: "immediate",
|
|
178
|
+
credit_reduced: "period_end",
|
|
179
|
+
rating_changed: "period_end"
|
|
186
180
|
}),
|
|
187
181
|
allowImmediatePriceIncrease: z2.boolean().default(false),
|
|
188
182
|
allowImmediateEntitlementReduction: z2.boolean().default(false)
|
|
189
183
|
}).strict().superRefine((policy, ctx) => {
|
|
190
|
-
|
|
191
|
-
"switch_immediately",
|
|
192
|
-
"switch_immediately_prorate"
|
|
193
|
-
]);
|
|
194
|
-
if (immediate.has(policy.when.price_increase ?? policy.default) && !policy.allowImmediatePriceIncrease) {
|
|
184
|
+
if ((policy.when.price_increase ?? policy.default) === "immediate" && !policy.allowImmediatePriceIncrease) {
|
|
195
185
|
ctx.addIssue({
|
|
196
186
|
code: "custom",
|
|
197
187
|
path: ["when", "price_increase"],
|
|
@@ -203,7 +193,7 @@ var subscriberChangePolicySchema = z2.object({
|
|
|
203
193
|
"limit_reduced",
|
|
204
194
|
"credit_reduced"
|
|
205
195
|
]) {
|
|
206
|
-
if (
|
|
196
|
+
if ((policy.when[key] ?? policy.default) === "immediate" && !policy.allowImmediateEntitlementReduction) {
|
|
207
197
|
ctx.addIssue({
|
|
208
198
|
code: "custom",
|
|
209
199
|
path: ["when", key],
|
|
@@ -214,30 +204,22 @@ var subscriberChangePolicySchema = z2.object({
|
|
|
214
204
|
});
|
|
215
205
|
|
|
216
206
|
// ../contracts/dist/plans/spec/plan-pricing.js
|
|
217
|
-
import { z as
|
|
207
|
+
import { z as z5 } from "zod";
|
|
218
208
|
|
|
219
209
|
// ../contracts/dist/plans/grants.js
|
|
220
210
|
import { z as z3 } from "zod";
|
|
221
|
-
var
|
|
222
|
-
kind: z3.literal("
|
|
223
|
-
amount_cents: z3.number().int().nonnegative()
|
|
224
|
-
});
|
|
225
|
-
var oneTimeGrantSchema = z3.object({
|
|
226
|
-
kind: z3.literal("one_time"),
|
|
227
|
-
amount_cents: z3.number().int().nonnegative()
|
|
228
|
-
});
|
|
229
|
-
var promotionalGrantSchema = z3.object({
|
|
230
|
-
kind: z3.literal("promotional"),
|
|
211
|
+
var creditGrantSchema = z3.object({
|
|
212
|
+
kind: z3.literal("credit"),
|
|
231
213
|
amount_cents: z3.number().int().nonnegative(),
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
214
|
+
/** When true, the credit is re-granted at every period rollover. Default
|
|
215
|
+
* false = a one-shot grant at subscription start. */
|
|
216
|
+
recurs: z3.boolean().optional(),
|
|
217
|
+
/** Optional expiry — Stripe expires the grant this many days after issue.
|
|
218
|
+
* Mainly used by promotional credits to prevent indefinite carry-forward. */
|
|
219
|
+
expires_after_days: z3.number().int().positive().optional(),
|
|
220
|
+
/** Optional campaign label. A credit carrying a label is a promotional /
|
|
221
|
+
* ops-issued grant rather than the canonical recurring/one-time credit. */
|
|
222
|
+
label: z3.string().min(1).max(120).optional()
|
|
241
223
|
});
|
|
242
224
|
var topUpGrantSchema = z3.object({
|
|
243
225
|
kind: z3.literal("top_up"),
|
|
@@ -250,67 +232,75 @@ var topUpGrantSchema = z3.object({
|
|
|
250
232
|
* Typically >= price_cents (the difference is the bulk discount). */
|
|
251
233
|
credit_cents: z3.number().int().positive()
|
|
252
234
|
});
|
|
253
|
-
var autoRechargeGrantSchema = z3.object({
|
|
254
|
-
kind: z3.literal("auto_recharge"),
|
|
255
|
-
/** When current balance < this many cents, trigger a refill. */
|
|
256
|
-
threshold_cents: z3.number().int().nonnegative(),
|
|
257
|
-
/** Charge + grant this much on each refill, in cents. */
|
|
258
|
-
refill_cents: z3.number().int().positive()
|
|
259
|
-
});
|
|
260
235
|
var grantSchema = z3.discriminatedUnion("kind", [
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
promotionalGrantSchema,
|
|
264
|
-
trialGrantSchema,
|
|
265
|
-
rolloverGrantSchema,
|
|
266
|
-
topUpGrantSchema,
|
|
267
|
-
autoRechargeGrantSchema
|
|
236
|
+
creditGrantSchema,
|
|
237
|
+
topUpGrantSchema
|
|
268
238
|
]);
|
|
239
|
+
function isRecurringCredit(g) {
|
|
240
|
+
return g.kind === "credit" && g.recurs === true && g.label === void 0;
|
|
241
|
+
}
|
|
242
|
+
function isOneTimeCredit(g) {
|
|
243
|
+
return g.kind === "credit" && g.recurs !== true && g.label === void 0;
|
|
244
|
+
}
|
|
269
245
|
function refineSingleCanonicalGrant(grants, ctx, path = ["grants"]) {
|
|
270
246
|
if (!grants)
|
|
271
247
|
return;
|
|
272
248
|
let recurringCount = 0;
|
|
273
249
|
let oneTimeCount = 0;
|
|
274
250
|
for (const g of grants) {
|
|
275
|
-
if (g
|
|
251
|
+
if (isRecurringCredit(g))
|
|
276
252
|
recurringCount += 1;
|
|
277
|
-
else if (g
|
|
253
|
+
else if (isOneTimeCredit(g))
|
|
278
254
|
oneTimeCount += 1;
|
|
279
255
|
}
|
|
280
256
|
if (recurringCount > 1) {
|
|
281
257
|
ctx.addIssue({
|
|
282
258
|
code: "custom",
|
|
283
|
-
message: "At most one
|
|
259
|
+
message: "At most one recurring `credit` grant (recurs:true, no label) is allowed \u2014 recurring credit is a single canonical entry.",
|
|
284
260
|
path
|
|
285
261
|
});
|
|
286
262
|
}
|
|
287
263
|
if (oneTimeCount > 1) {
|
|
288
264
|
ctx.addIssue({
|
|
289
265
|
code: "custom",
|
|
290
|
-
message: "At most one `
|
|
266
|
+
message: "At most one one-time `credit` grant (recurs:false, no label) is allowed \u2014 one-time credit is a single canonical entry.",
|
|
291
267
|
path
|
|
292
268
|
});
|
|
293
269
|
}
|
|
294
270
|
}
|
|
295
271
|
|
|
272
|
+
// ../contracts/dist/plans/credit-policy.js
|
|
273
|
+
import { z as z4 } from "zod";
|
|
274
|
+
var rolloverPolicySchema = z4.object({
|
|
275
|
+
percent: z4.number().int().min(0).max(100)
|
|
276
|
+
});
|
|
277
|
+
var autoRechargePolicySchema = z4.object({
|
|
278
|
+
threshold_cents: z4.number().int().nonnegative(),
|
|
279
|
+
refill_cents: z4.number().int().positive()
|
|
280
|
+
});
|
|
281
|
+
var creditPolicySchema = z4.object({
|
|
282
|
+
rollover: rolloverPolicySchema.optional(),
|
|
283
|
+
auto_recharge: autoRechargePolicySchema.optional()
|
|
284
|
+
});
|
|
285
|
+
|
|
296
286
|
// ../contracts/dist/plans/spec/plan-pricing.js
|
|
297
|
-
var meterKindSchema =
|
|
298
|
-
var meterTierSchema =
|
|
299
|
-
up_to:
|
|
300
|
-
price_per_unit_micros:
|
|
287
|
+
var meterKindSchema = z5.enum(["linear", "active_count"]);
|
|
288
|
+
var meterTierSchema = z5.object({
|
|
289
|
+
up_to: z5.number().int().positive().nullable(),
|
|
290
|
+
price_per_unit_micros: z5.number().int().nonnegative()
|
|
301
291
|
});
|
|
302
|
-
var tieredPricingSchema =
|
|
303
|
-
strategy:
|
|
304
|
-
tiers:
|
|
292
|
+
var tieredPricingSchema = z5.object({
|
|
293
|
+
strategy: z5.enum(["graduated", "volume"]),
|
|
294
|
+
tiers: z5.array(meterTierSchema).min(1).max(20)
|
|
305
295
|
});
|
|
306
|
-
var meterSchema =
|
|
307
|
-
dimension:
|
|
296
|
+
var meterSchema = z5.object({
|
|
297
|
+
dimension: z5.string().min(1).max(64).regex(/^[a-z0-9_]+$/, "Meter dimension must be lowercase alphanumeric with underscores"),
|
|
308
298
|
/** Aggregation kind. `linear` (default) sums event quantities across
|
|
309
299
|
* the period. `active_count` samples the latest quantity in the
|
|
310
300
|
* period (use for seats / active-user / occupancy snapshots). */
|
|
311
301
|
kind: meterKindSchema.default("linear"),
|
|
312
302
|
/** Flat per-unit rate. Mutually exclusive with `tiered`. */
|
|
313
|
-
price_per_unit_micros:
|
|
303
|
+
price_per_unit_micros: z5.number().int().nonnegative().default(0),
|
|
314
304
|
/** Tiered schedule. Mutually exclusive with a non-zero
|
|
315
305
|
* `price_per_unit_micros`. */
|
|
316
306
|
tiered: tieredPricingSchema.optional(),
|
|
@@ -324,7 +314,7 @@ var meterSchema = z4.object({
|
|
|
324
314
|
* Mutually exclusive with `tiered` — a tiered schedule already
|
|
325
315
|
* expresses the full breakpoint ladder (model the free pool as a
|
|
326
316
|
* zero-priced first tier instead). Only meaningful on a flat meter. */
|
|
327
|
-
included_units:
|
|
317
|
+
included_units: z5.number().int().positive().optional()
|
|
328
318
|
}).superRefine((m, ctx) => {
|
|
329
319
|
if (m.tiered && m.price_per_unit_micros > 0) {
|
|
330
320
|
ctx.addIssue({
|
|
@@ -375,99 +365,106 @@ var meterSchema = z4.object({
|
|
|
375
365
|
}
|
|
376
366
|
}
|
|
377
367
|
});
|
|
378
|
-
var billingIntervalSchema =
|
|
379
|
-
var planPricingSchema =
|
|
368
|
+
var billingIntervalSchema = z5.enum(["month", "year"]);
|
|
369
|
+
var planPricingSchema = z5.object({
|
|
380
370
|
/** Per-dimension price list. Empty array = no metered billing. */
|
|
381
|
-
meters:
|
|
371
|
+
meters: z5.array(meterSchema).default([]),
|
|
382
372
|
/** Flat subscription fee per period, in cents. 0 = no recurring fee. */
|
|
383
|
-
recurring_fee_cents:
|
|
373
|
+
recurring_fee_cents: z5.number().int().nonnegative().default(0),
|
|
384
374
|
/** Billing cadence for the recurring fee + metered usage. Defaults to
|
|
385
375
|
* `month`; set `year` for annual billing (drives both the Stripe price
|
|
386
376
|
* `recurring.interval` and the spend-cap / quota billing-period window
|
|
387
377
|
* length). */
|
|
388
378
|
billing_interval: billingIntervalSchema.default("month"),
|
|
389
379
|
/** Unified credit-grant array — the SINGLE credit surface. Recurring +
|
|
390
|
-
* one-time credit are canonical entries here (the legacy scalar
|
|
391
|
-
* were removed). */
|
|
392
|
-
grants:
|
|
380
|
+
* one-time credit are canonical `credit` entries here (the legacy scalar
|
|
381
|
+
* knobs were removed). */
|
|
382
|
+
grants: z5.array(grantSchema).max(40).default([]),
|
|
383
|
+
/** Non-grant credit policies (rollover + auto-recharge). Optional; absent =
|
|
384
|
+
* no rollover / no auto-recharge. */
|
|
385
|
+
creditPolicy: creditPolicySchema.optional(),
|
|
393
386
|
/** Free-trial length in days. Trial enforcement runs through Stripe
|
|
394
387
|
* webhooks → `lifecycle_block` constraint at runtime. */
|
|
395
|
-
trial_days:
|
|
388
|
+
trial_days: z5.number().int().nonnegative().default(0),
|
|
396
389
|
/** Optional hard cap on monthly spend, in cents. Orthogonal to the
|
|
397
390
|
* billing math — the gateway blocks once this cap is reached even if
|
|
398
391
|
* credit balance remains. Stripe doesn't enforce this; the runtime
|
|
399
392
|
* does, via a `quota`-shaped constraint. */
|
|
400
|
-
max_monthly_spend_cents:
|
|
393
|
+
max_monthly_spend_cents: z5.number().int().nonnegative().optional()
|
|
401
394
|
});
|
|
402
395
|
|
|
403
396
|
// ../contracts/dist/plans/spec/plan-variant.js
|
|
404
|
-
import { z as
|
|
405
|
-
var proRationOnRollbackSchema =
|
|
397
|
+
import { z as z6 } from "zod";
|
|
398
|
+
var proRationOnRollbackSchema = z6.enum(["NONE", "PRORATE", "CREDIT"]).default("NONE");
|
|
406
399
|
var billingKnobsShape = {
|
|
407
|
-
meters:
|
|
408
|
-
recurring_fee_cents:
|
|
400
|
+
meters: z6.array(meterSchema).default([]),
|
|
401
|
+
recurring_fee_cents: z6.number().int().nonnegative().default(0),
|
|
409
402
|
/** Billing cadence (`month` default / `year`). Drives the Stripe price
|
|
410
403
|
* `recurring.interval` and the entitlement billing-period window. */
|
|
411
404
|
billing_interval: billingIntervalSchema.default("month"),
|
|
412
|
-
trial_days:
|
|
405
|
+
trial_days: z6.number().int().nonnegative().default(0),
|
|
413
406
|
/** Hard maximum on metered spend per period. Gateway blocks once
|
|
414
407
|
* cumulative usage cost reaches this cap. Orthogonal to the
|
|
415
408
|
* recurring fee — the fee is always charged. */
|
|
416
|
-
max_monthly_spend_cents:
|
|
409
|
+
max_monthly_spend_cents: z6.number().int().nonnegative().optional(),
|
|
417
410
|
/** Minimum metered spend per period. When set, the invoice floors
|
|
418
411
|
* the metered total at this value (Railway-style minimum-spend).
|
|
419
412
|
* Orthogonal to `recurring_fee_cents` — the fee is added on top.
|
|
420
413
|
* Set to 0 / omit to disable. */
|
|
421
|
-
min_monthly_spend_cents:
|
|
414
|
+
min_monthly_spend_cents: z6.number().int().nonnegative().optional(),
|
|
422
415
|
/**
|
|
423
|
-
* Unified grants array — the SINGLE credit surface
|
|
424
|
-
*
|
|
425
|
-
*
|
|
416
|
+
* Unified grants array — the SINGLE credit surface. Each entry expresses
|
|
417
|
+
* one credit grant primitive: a parametrized `credit` (recurring /
|
|
418
|
+
* one-time / promotional, distinguished by `recurs` / `label`) or a
|
|
419
|
+
* purchasable `top_up` pack.
|
|
426
420
|
*
|
|
427
|
-
* Recurring + one-time credit are declared here as canonical
|
|
428
|
-
*
|
|
429
|
-
*
|
|
430
|
-
*
|
|
431
|
-
*
|
|
432
|
-
* consolidated list.
|
|
421
|
+
* Recurring + one-time credit are declared here as canonical `credit`
|
|
422
|
+
* entries (at most one recurring + one one-time, enforced by
|
|
423
|
+
* `refineSingleCanonicalGrant`); call `getEffectiveGrants(plan)` to read
|
|
424
|
+
* the consolidated list. Trial is driven by `trial_days`; rollover +
|
|
425
|
+
* auto-recharge live in `creditPolicy`.
|
|
433
426
|
*/
|
|
434
|
-
grants:
|
|
427
|
+
grants: z6.array(grantSchema).max(40).default([]),
|
|
428
|
+
/** Non-grant credit policies (rollover + auto-recharge). */
|
|
429
|
+
creditPolicy: creditPolicySchema.optional()
|
|
435
430
|
};
|
|
436
431
|
var billingKnobsOptionalShape = {
|
|
437
|
-
meters:
|
|
438
|
-
recurring_fee_cents:
|
|
432
|
+
meters: z6.array(meterSchema).optional(),
|
|
433
|
+
recurring_fee_cents: z6.number().int().nonnegative().optional(),
|
|
439
434
|
/** Variant override for the billing cadence. Absent → inherit the
|
|
440
435
|
* parent plan's `billing_interval`. */
|
|
441
436
|
billing_interval: billingIntervalSchema.optional(),
|
|
442
|
-
trial_days:
|
|
443
|
-
max_monthly_spend_cents:
|
|
444
|
-
min_monthly_spend_cents:
|
|
437
|
+
trial_days: z6.number().int().nonnegative().optional(),
|
|
438
|
+
max_monthly_spend_cents: z6.number().int().nonnegative().optional(),
|
|
439
|
+
min_monthly_spend_cents: z6.number().int().nonnegative().optional(),
|
|
445
440
|
/** Variant override for the grants array — the single credit surface.
|
|
446
441
|
* When present, fully replaces the parent plan's grants (no shallow
|
|
447
442
|
* merge — grants are collectively meaningful). */
|
|
448
|
-
grants:
|
|
443
|
+
grants: z6.array(grantSchema).max(40).optional(),
|
|
444
|
+
/** Variant override for the credit-policy block. */
|
|
445
|
+
creditPolicy: creditPolicySchema.optional()
|
|
449
446
|
};
|
|
450
|
-
var planVariantObjectSchema =
|
|
447
|
+
var planVariantObjectSchema = z6.object({
|
|
451
448
|
/**
|
|
452
449
|
* Stable variant id — used as the second half of the CompiledPlan
|
|
453
450
|
* lineage key, so changing it counts as create-new + archive-old.
|
|
454
451
|
* Lower-case kebab-case to match URL-share-link readability.
|
|
455
452
|
*/
|
|
456
|
-
id:
|
|
453
|
+
id: z6.string().min(1).max(64).regex(/^[a-z0-9_-]+$/, "Variant id must be lowercase alphanumeric with hyphens/underscores"),
|
|
457
454
|
/** Human-readable label for dashboards / observability. */
|
|
458
|
-
label:
|
|
455
|
+
label: z6.string().max(200).optional(),
|
|
459
456
|
/**
|
|
460
457
|
* Rollout percentage (0-100). 0 = paused (variant exists but no new
|
|
461
458
|
* assignments); 100 = full takeover (effectively a forced graduation
|
|
462
459
|
* for new subscribers, but legacy subs stay on parent until period end).
|
|
463
460
|
*/
|
|
464
|
-
rolloutPercent:
|
|
461
|
+
rolloutPercent: z6.number().int().min(0).max(100),
|
|
465
462
|
/**
|
|
466
463
|
* Seed for the deterministic hash function. Rotating the seed
|
|
467
464
|
* invalidates existing variant assignments — useful for re-running an
|
|
468
465
|
* experiment with a fresh cohort.
|
|
469
466
|
*/
|
|
470
|
-
assignmentSeed:
|
|
467
|
+
assignmentSeed: z6.string().min(1).max(100).default("default"),
|
|
471
468
|
/**
|
|
472
469
|
* What happens to billing when this variant gets rolled back AND the
|
|
473
470
|
* subscriber has already been billed for the experimental price:
|
|
@@ -481,22 +478,22 @@ var planVariantObjectSchema = z5.object({
|
|
|
481
478
|
// plan. The pre-0.53 `pricing: planPricingSchema.optional()` field is
|
|
482
479
|
// gone; variants now override the knobs directly.
|
|
483
480
|
...billingKnobsOptionalShape,
|
|
484
|
-
limits:
|
|
485
|
-
featureGates:
|
|
481
|
+
limits: z6.array(planLimitRuleSchema).max(20).optional(),
|
|
482
|
+
featureGates: z6.record(z6.string(), z6.boolean()).optional(),
|
|
486
483
|
/** See `planSpecSchema.capability_limits`. Variant overrides are
|
|
487
484
|
* merged shallowly onto the parent plan's map — missing keys
|
|
488
485
|
* inherit from the parent. */
|
|
489
|
-
capability_limits:
|
|
490
|
-
overageBehavior:
|
|
486
|
+
capability_limits: z6.record(z6.string().min(1).max(120), z6.union([z6.number().int().nonnegative(), z6.boolean()])).optional(),
|
|
487
|
+
overageBehavior: z6.enum(["block", "allow_and_bill"]).optional()
|
|
491
488
|
});
|
|
492
489
|
var planVariantSchema = planVariantObjectSchema.superRefine((variant, ctx) => {
|
|
493
490
|
refineSingleCanonicalGrant(variant.grants, ctx);
|
|
494
491
|
});
|
|
495
|
-
var planSpecObjectSchema =
|
|
496
|
-
key:
|
|
497
|
-
name:
|
|
498
|
-
description:
|
|
499
|
-
details:
|
|
492
|
+
var planSpecObjectSchema = z6.object({
|
|
493
|
+
key: z6.string().min(1).max(64).regex(/^[a-z0-9_-]+$/, "Plan key must be lowercase alphanumeric with hyphens/underscores"),
|
|
494
|
+
name: z6.string().min(1).max(100),
|
|
495
|
+
description: z6.string().max(500).optional(),
|
|
496
|
+
details: z6.array(z6.string().max(200)).max(10).optional(),
|
|
500
497
|
// ---------------------------------------------------------------------
|
|
501
498
|
// 5-knob billing shape (v0.53.0)
|
|
502
499
|
// ---------------------------------------------------------------------
|
|
@@ -510,7 +507,7 @@ var planSpecObjectSchema = z5.object({
|
|
|
510
507
|
// runs through Stripe webhooks → `lifecycle_block` constraint at
|
|
511
508
|
// runtime; there's no `trial_expiry` constraint kind anymore.
|
|
512
509
|
...billingKnobsShape,
|
|
513
|
-
free:
|
|
510
|
+
free: z6.boolean().default(false),
|
|
514
511
|
// `plan.features` removed in feat/feature-plans-link — features
|
|
515
512
|
// declare their plans via `feature.plans[]` (canonical, feature-first
|
|
516
513
|
// direction). Builders writing the manifest now go to the feature
|
|
@@ -518,8 +515,8 @@ var planSpecObjectSchema = z5.object({
|
|
|
518
515
|
// compiler walks `spec.features` and includes a feature's routes for
|
|
519
516
|
// a plan when the plan's key appears in `feature.plans[]`. See
|
|
520
517
|
// shared-types/src/plans/spec/product.ts (featureCatalogEntrySchema).
|
|
521
|
-
limits:
|
|
522
|
-
featureGates:
|
|
518
|
+
limits: z6.array(planLimitRuleSchema).max(20).default([]),
|
|
519
|
+
featureGates: z6.record(z6.string(), z6.boolean()).optional(),
|
|
523
520
|
/**
|
|
524
521
|
* Control-plane capability limits (Phase 1a, v0.56+).
|
|
525
522
|
*
|
|
@@ -549,9 +546,9 @@ var planSpecObjectSchema = z5.object({
|
|
|
549
546
|
* environments: 1
|
|
550
547
|
* enterprise_sso: true
|
|
551
548
|
*/
|
|
552
|
-
capability_limits:
|
|
553
|
-
overageBehavior:
|
|
554
|
-
selfServeEnabled:
|
|
549
|
+
capability_limits: z6.record(z6.string().min(1).max(120), z6.union([z6.number().int().nonnegative(), z6.boolean()])).default({}),
|
|
550
|
+
overageBehavior: z6.enum(["block", "allow_and_bill"]).default("block"),
|
|
551
|
+
selfServeEnabled: z6.boolean().default(true),
|
|
555
552
|
/**
|
|
556
553
|
* Phase A0 — multi-stable plan support.
|
|
557
554
|
*
|
|
@@ -563,7 +560,7 @@ var planSpecObjectSchema = z5.object({
|
|
|
563
560
|
* from YAML while subs are pinned to it is a compile error (would
|
|
564
561
|
* orphan the cohort).
|
|
565
562
|
*/
|
|
566
|
-
legacy:
|
|
563
|
+
legacy: z6.boolean().optional().default(false),
|
|
567
564
|
/**
|
|
568
565
|
* Phase A0 — A/B testing variants of this plan. Each variant compiles
|
|
569
566
|
* into a sibling `CompiledPlan` with status `EXPERIMENTAL` and
|
|
@@ -573,11 +570,11 @@ var planSpecObjectSchema = z5.object({
|
|
|
573
570
|
* - Cannot coexist with `legacy: true` on the same plan
|
|
574
571
|
* - Variant `id` must be unique within the variants array
|
|
575
572
|
*/
|
|
576
|
-
variants:
|
|
577
|
-
archive:
|
|
578
|
-
at:
|
|
579
|
-
transitionTo:
|
|
580
|
-
strategy:
|
|
573
|
+
variants: z6.array(planVariantSchema).max(4).optional(),
|
|
574
|
+
archive: z6.object({
|
|
575
|
+
at: z6.string().datetime().optional(),
|
|
576
|
+
transitionTo: z6.string().optional(),
|
|
577
|
+
strategy: z6.enum(["auto", "explicit", "block"]).default("auto")
|
|
581
578
|
}).optional()
|
|
582
579
|
});
|
|
583
580
|
var planSpecSchema = planSpecObjectSchema.superRefine((plan, ctx) => {
|
|
@@ -585,10 +582,10 @@ var planSpecSchema = planSpecObjectSchema.superRefine((plan, ctx) => {
|
|
|
585
582
|
});
|
|
586
583
|
|
|
587
584
|
// ../contracts/dist/plans/spec/webhooks.js
|
|
588
|
-
import { z as
|
|
585
|
+
import { z as z8 } from "zod";
|
|
589
586
|
|
|
590
587
|
// ../contracts/dist/webhooks/events.js
|
|
591
|
-
import { z as
|
|
588
|
+
import { z as z7 } from "zod";
|
|
592
589
|
var WEBHOOK_EVENT_NAMES = [
|
|
593
590
|
"subscription.created",
|
|
594
591
|
"subscription.updated",
|
|
@@ -599,26 +596,26 @@ var WEBHOOK_EVENT_NAMES = [
|
|
|
599
596
|
"entitlement.changed",
|
|
600
597
|
"usage.threshold_reached"
|
|
601
598
|
];
|
|
602
|
-
var webhookEventNameSchema =
|
|
599
|
+
var webhookEventNameSchema = z7.enum(WEBHOOK_EVENT_NAMES);
|
|
603
600
|
|
|
604
601
|
// ../contracts/dist/plans/spec/webhooks.js
|
|
605
602
|
var WEBHOOK_SECRET_PLACEHOLDER_PATTERN = /^\$\{[A-Z][A-Z0-9_]{0,127}\}$/;
|
|
606
|
-
var webhookSecretSchema =
|
|
603
|
+
var webhookSecretSchema = z8.string().min(3).max(200).refine((value) => WEBHOOK_SECRET_PLACEHOLDER_PATTERN.test(value), {
|
|
607
604
|
message: "secret must use ${VAR} interpolation syntax (e.g. ${WEBHOOK_SECRET}); raw secrets in YAML are rejected. Define the env var in the per-product secret store and reference it here."
|
|
608
605
|
});
|
|
609
|
-
var webhookRetryPolicySchema =
|
|
610
|
-
maxAttempts:
|
|
611
|
-
backoff:
|
|
606
|
+
var webhookRetryPolicySchema = z8.object({
|
|
607
|
+
maxAttempts: z8.number().int().min(1).max(20).default(5),
|
|
608
|
+
backoff: z8.enum(["exponential", "fixed"]).default("exponential")
|
|
612
609
|
});
|
|
613
|
-
var webhookEndpointSchema =
|
|
610
|
+
var webhookEndpointSchema = z8.object({
|
|
614
611
|
/**
|
|
615
612
|
* Stable endpoint id — used as the third key of the
|
|
616
613
|
* `(productId, environmentId, id)` uniqueness tuple. Idempotent upsert
|
|
617
614
|
* keys on this id; renaming an id is delete + recreate.
|
|
618
615
|
*/
|
|
619
|
-
id:
|
|
616
|
+
id: z8.string().min(1).max(64).regex(/^[a-z0-9_-]+$/, "Webhook endpoint id must be lowercase alphanumeric with hyphens/underscores"),
|
|
620
617
|
/** Public HTTPS URL the dispatcher POSTs to. */
|
|
621
|
-
url:
|
|
618
|
+
url: z8.string().url("webhooks.endpoints[].url must be a valid URL"),
|
|
622
619
|
/**
|
|
623
620
|
* Signing secret. MUST be a `${VAR}` placeholder; raw secrets in YAML
|
|
624
621
|
* are rejected by invariant 8. The seal pass resolves this against the
|
|
@@ -631,22 +628,22 @@ var webhookEndpointSchema = z7.object({
|
|
|
631
628
|
* Each value is validated against `webhookEventNameSchema` —
|
|
632
629
|
* unknown events fail invariant 9.
|
|
633
630
|
*/
|
|
634
|
-
events:
|
|
635
|
-
enabled:
|
|
631
|
+
events: z8.array(webhookEventNameSchema).min(1, "webhooks.endpoints[].events must subscribe to \u2265 1 event"),
|
|
632
|
+
enabled: z8.boolean().default(true),
|
|
636
633
|
retryPolicy: webhookRetryPolicySchema.default({
|
|
637
634
|
maxAttempts: 5,
|
|
638
635
|
backoff: "exponential"
|
|
639
636
|
})
|
|
640
637
|
});
|
|
641
|
-
var webhooksBlockSchema =
|
|
642
|
-
endpoints:
|
|
638
|
+
var webhooksBlockSchema = z8.object({
|
|
639
|
+
endpoints: z8.array(webhookEndpointSchema).max(50).default([])
|
|
643
640
|
});
|
|
644
641
|
|
|
645
642
|
// ../contracts/dist/plans/spec/environments.js
|
|
646
|
-
import { z as
|
|
647
|
-
var planOverrideSchema =
|
|
648
|
-
meters:
|
|
649
|
-
recurring_fee_cents:
|
|
643
|
+
import { z as z9 } from "zod";
|
|
644
|
+
var planOverrideSchema = z9.object({
|
|
645
|
+
meters: z9.array(meterSchema).optional(),
|
|
646
|
+
recurring_fee_cents: z9.number().int().nonnegative().optional(),
|
|
650
647
|
/** Per-env override for the billing cadence. Absent → inherit the
|
|
651
648
|
* base plan's `billing_interval`. */
|
|
652
649
|
billing_interval: billingIntervalSchema.optional(),
|
|
@@ -654,35 +651,38 @@ var planOverrideSchema = z8.object({
|
|
|
654
651
|
* surface. When present it fully replaces the parent plan's grants
|
|
655
652
|
* for this environment (the legacy recurring/one-time scalar knobs
|
|
656
653
|
* were removed). */
|
|
657
|
-
grants:
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
654
|
+
grants: z9.array(grantSchema).max(40).optional(),
|
|
655
|
+
/** Per-env override for the credit-policy block (rollover +
|
|
656
|
+
* auto-recharge). */
|
|
657
|
+
creditPolicy: creditPolicySchema.optional(),
|
|
658
|
+
trial_days: z9.number().int().nonnegative().optional(),
|
|
659
|
+
max_monthly_spend_cents: z9.number().int().nonnegative().optional(),
|
|
660
|
+
limits: z9.array(planLimitRuleSchema).max(20).optional(),
|
|
661
|
+
featureGates: z9.record(z9.string(), z9.boolean()).optional(),
|
|
662
|
+
overageBehavior: z9.enum(["block", "allow_and_bill"]).optional(),
|
|
663
|
+
selfServeEnabled: z9.boolean().optional(),
|
|
664
|
+
legacy: z9.boolean().optional()
|
|
665
665
|
}).strict();
|
|
666
|
-
var webhookEndpointOverrideSchema =
|
|
667
|
-
url:
|
|
666
|
+
var webhookEndpointOverrideSchema = z9.object({
|
|
667
|
+
url: z9.string().url().optional(),
|
|
668
668
|
secret: webhookSecretSchema.optional(),
|
|
669
|
-
events:
|
|
670
|
-
enabled:
|
|
669
|
+
events: z9.array(webhookEventNameSchema).optional(),
|
|
670
|
+
enabled: z9.boolean().optional(),
|
|
671
671
|
retryPolicy: webhookRetryPolicySchema.partial().optional()
|
|
672
672
|
}).strict();
|
|
673
|
-
var environmentOverrideBlockSchema =
|
|
674
|
-
plans:
|
|
675
|
-
webhooks:
|
|
676
|
-
endpoints:
|
|
673
|
+
var environmentOverrideBlockSchema = z9.object({
|
|
674
|
+
plans: z9.record(z9.string(), planOverrideSchema).optional(),
|
|
675
|
+
webhooks: z9.object({
|
|
676
|
+
endpoints: z9.record(z9.string(), webhookEndpointOverrideSchema).optional()
|
|
677
677
|
}).strict().optional()
|
|
678
678
|
}).strict();
|
|
679
|
-
var environmentsBlockSchema =
|
|
679
|
+
var environmentsBlockSchema = z9.record(z9.string().min(1).max(64), environmentOverrideBlockSchema);
|
|
680
680
|
|
|
681
681
|
// ../contracts/dist/plans/spec/product.js
|
|
682
|
-
import { z as
|
|
682
|
+
import { z as z19 } from "zod";
|
|
683
683
|
|
|
684
684
|
// ../contracts/dist/plans/spec/frontend-layer.js
|
|
685
|
-
import { z as
|
|
685
|
+
import { z as z10 } from "zod";
|
|
686
686
|
var FRONTEND_MANIFEST_SCHEMA_VERSION = 1;
|
|
687
687
|
var KNOWN_FRONTEND_COMPONENT_IDS = [
|
|
688
688
|
"plans_table",
|
|
@@ -712,31 +712,31 @@ var RESERVED_TEMPLATE_PATHS = [
|
|
|
712
712
|
"/persona",
|
|
713
713
|
"/persona-sign-in"
|
|
714
714
|
];
|
|
715
|
-
var frontendPathSchema =
|
|
716
|
-
var frontendCapabilityRefSchema =
|
|
717
|
-
var frontendNavItemSchema =
|
|
718
|
-
label:
|
|
715
|
+
var frontendPathSchema = z10.string().min(1).max(200).regex(/^\/[a-zA-Z0-9_./-]*$/, "path must start with / and contain only [a-zA-Z0-9_./-]");
|
|
716
|
+
var frontendCapabilityRefSchema = z10.string().min(1).max(120).regex(/^[a-z0-9_-]+$/);
|
|
717
|
+
var frontendNavItemSchema = z10.object({
|
|
718
|
+
label: z10.string().min(1).max(80),
|
|
719
719
|
path: frontendPathSchema,
|
|
720
720
|
capability: frontendCapabilityRefSchema.optional()
|
|
721
721
|
});
|
|
722
|
-
var frontendComponentIdSchema =
|
|
723
|
-
var frontendComponentSchema =
|
|
722
|
+
var frontendComponentIdSchema = z10.enum(KNOWN_FRONTEND_COMPONENT_IDS);
|
|
723
|
+
var frontendComponentSchema = z10.object({
|
|
724
724
|
component: frontendComponentIdSchema,
|
|
725
|
-
props:
|
|
725
|
+
props: z10.record(z10.string().min(1).max(80), z10.unknown()).optional(),
|
|
726
726
|
capability: frontendCapabilityRefSchema.optional(),
|
|
727
|
-
gateMode:
|
|
727
|
+
gateMode: z10.enum(["hide", "disable", "upsell"]).default("hide")
|
|
728
728
|
});
|
|
729
|
-
var frontendPageSchema =
|
|
729
|
+
var frontendPageSchema = z10.object({
|
|
730
730
|
path: frontendPathSchema,
|
|
731
|
-
title:
|
|
732
|
-
requiresAuth:
|
|
731
|
+
title: z10.string().min(1).max(120),
|
|
732
|
+
requiresAuth: z10.boolean(),
|
|
733
733
|
capability: frontendCapabilityRefSchema.optional(),
|
|
734
|
-
components:
|
|
734
|
+
components: z10.array(frontendComponentSchema).max(12).default([])
|
|
735
735
|
});
|
|
736
|
-
var frontendManifestSchema =
|
|
737
|
-
version:
|
|
738
|
-
nav:
|
|
739
|
-
pages:
|
|
736
|
+
var frontendManifestSchema = z10.object({
|
|
737
|
+
version: z10.literal(FRONTEND_MANIFEST_SCHEMA_VERSION),
|
|
738
|
+
nav: z10.array(frontendNavItemSchema).max(12).default([]),
|
|
739
|
+
pages: z10.array(frontendPageSchema).max(24).default([])
|
|
740
740
|
}).superRefine((manifest, ctx) => {
|
|
741
741
|
const seenPages = /* @__PURE__ */ new Set();
|
|
742
742
|
manifest.pages.forEach((page, index) => {
|
|
@@ -768,32 +768,35 @@ function isReservedTemplatePath(path) {
|
|
|
768
768
|
}
|
|
769
769
|
|
|
770
770
|
// ../contracts/dist/plans/spec/migrations-layer.js
|
|
771
|
-
import { z as
|
|
772
|
-
var planKeySchema =
|
|
773
|
-
var versionRefSchema =
|
|
774
|
-
|
|
771
|
+
import { z as z11 } from "zod";
|
|
772
|
+
var planKeySchema = z11.string().min(1).max(64).regex(/^[a-z0-9_-]+$/, "Plan key must be lowercase alphanumeric with hyphens/underscores");
|
|
773
|
+
var versionRefSchema = z11.union([
|
|
774
|
+
z11.literal("head"),
|
|
775
|
+
z11.string().regex(/^[1-9][0-9]*$/, 'version must be "head" or a positive integer')
|
|
776
|
+
]);
|
|
777
|
+
var planVersionRefSchema = z11.object({
|
|
775
778
|
plan: planKeySchema,
|
|
776
779
|
version: versionRefSchema.optional()
|
|
777
780
|
});
|
|
778
|
-
var migrationTargetSchema =
|
|
781
|
+
var migrationTargetSchema = z11.object({
|
|
779
782
|
plan: planKeySchema,
|
|
780
|
-
version:
|
|
783
|
+
version: z11.literal("head").default("head")
|
|
781
784
|
});
|
|
782
|
-
var pinnedPlanVersionSchema =
|
|
785
|
+
var pinnedPlanVersionSchema = z11.object({
|
|
783
786
|
plan: planKeySchema,
|
|
784
787
|
version: versionRefSchema
|
|
785
788
|
});
|
|
786
|
-
var migrationEffectiveSchema =
|
|
789
|
+
var migrationEffectiveSchema = z11.enum([
|
|
787
790
|
"grandfather",
|
|
788
791
|
"next_renewal",
|
|
789
792
|
"by_date",
|
|
790
793
|
"immediate",
|
|
791
794
|
"opt_in"
|
|
792
795
|
]);
|
|
793
|
-
var migrationProrationSchema =
|
|
794
|
-
var migrationExistingCustomersSchema =
|
|
796
|
+
var migrationProrationSchema = z11.enum(["none", "prorate", "credit"]);
|
|
797
|
+
var migrationExistingCustomersSchema = z11.object({
|
|
795
798
|
effective: migrationEffectiveSchema,
|
|
796
|
-
date:
|
|
799
|
+
date: z11.string().datetime().optional(),
|
|
797
800
|
proration: migrationProrationSchema.optional()
|
|
798
801
|
}).superRefine((policy, ctx) => {
|
|
799
802
|
if (policy.effective === "by_date" && policy.date === void 0) {
|
|
@@ -804,21 +807,21 @@ var migrationExistingCustomersSchema = z10.object({
|
|
|
804
807
|
});
|
|
805
808
|
}
|
|
806
809
|
});
|
|
807
|
-
var migrationPinSchema =
|
|
808
|
-
subscriber:
|
|
810
|
+
var migrationPinSchema = z11.object({
|
|
811
|
+
subscriber: z11.string().min(1).max(200),
|
|
809
812
|
pinTo: pinnedPlanVersionSchema,
|
|
810
|
-
until:
|
|
811
|
-
notes:
|
|
813
|
+
until: z11.string().datetime().optional(),
|
|
814
|
+
notes: z11.string().max(1e3).optional()
|
|
812
815
|
});
|
|
813
|
-
var migrationDeclSchema =
|
|
814
|
-
id:
|
|
816
|
+
var migrationDeclSchema = z11.object({
|
|
817
|
+
id: z11.string().min(1).max(120).regex(/^[a-z0-9][a-z0-9_.-]*$/, "Migration id must be lowercase alphanumeric with dots, hyphens, or underscores"),
|
|
815
818
|
from: planVersionRefSchema,
|
|
816
819
|
to: migrationTargetSchema,
|
|
817
|
-
newCustomers:
|
|
820
|
+
newCustomers: z11.literal("immediate").default("immediate"),
|
|
818
821
|
existingCustomers: migrationExistingCustomersSchema,
|
|
819
|
-
pins:
|
|
822
|
+
pins: z11.array(migrationPinSchema).max(50).default([])
|
|
820
823
|
});
|
|
821
|
-
var migrationDeclsSchema =
|
|
824
|
+
var migrationDeclsSchema = z11.array(migrationDeclSchema).superRefine((migrations, ctx) => {
|
|
822
825
|
const seen = /* @__PURE__ */ new Set();
|
|
823
826
|
migrations.forEach((migration, index) => {
|
|
824
827
|
if (seen.has(migration.id)) {
|
|
@@ -833,18 +836,18 @@ var migrationDeclsSchema = z10.array(migrationDeclSchema).superRefine((migration
|
|
|
833
836
|
});
|
|
834
837
|
|
|
835
838
|
// ../contracts/dist/plans/spec/counted-resources.js
|
|
836
|
-
import { z as
|
|
837
|
-
var countedResourceScopeSchema =
|
|
838
|
-
var countedResourceCountSourceSchema =
|
|
839
|
+
import { z as z12 } from "zod";
|
|
840
|
+
var countedResourceScopeSchema = z12.enum(["subscription", "subject"]);
|
|
841
|
+
var countedResourceCountSourceSchema = z12.enum([
|
|
839
842
|
"reported",
|
|
840
843
|
"action_inferred"
|
|
841
844
|
]);
|
|
842
|
-
var countedResourceNameSchema =
|
|
843
|
-
var countedResourceSchema =
|
|
845
|
+
var countedResourceNameSchema = z12.string().min(1).max(100).regex(/^[a-z0-9_.:-]+$/);
|
|
846
|
+
var countedResourceSchema = z12.object({
|
|
844
847
|
name: countedResourceNameSchema,
|
|
845
|
-
display:
|
|
848
|
+
display: z12.string().min(1).max(120).optional(),
|
|
846
849
|
scope: countedResourceScopeSchema.default("subscription"),
|
|
847
|
-
subjectType:
|
|
850
|
+
subjectType: z12.string().min(1).max(64).regex(/^[a-zA-Z0-9_.:-]+$/).optional(),
|
|
848
851
|
countSource: countedResourceCountSourceSchema.default("reported")
|
|
849
852
|
}).superRefine((resource, ctx) => {
|
|
850
853
|
if (resource.scope === "subject" && !resource.subjectType) {
|
|
@@ -862,23 +865,23 @@ var countedResourceSchema = z11.object({
|
|
|
862
865
|
});
|
|
863
866
|
}
|
|
864
867
|
});
|
|
865
|
-
var countedResourcesSchema =
|
|
868
|
+
var countedResourcesSchema = z12.array(countedResourceSchema).max(100).default([]);
|
|
866
869
|
|
|
867
870
|
// ../contracts/dist/plans/addons.js
|
|
868
|
-
import { z as
|
|
869
|
-
var addOnSchema =
|
|
871
|
+
import { z as z13 } from "zod";
|
|
872
|
+
var addOnSchema = z13.object({
|
|
870
873
|
/** Stable identifier — appears in `SubscriptionAddOn.addOnKey` and in
|
|
871
874
|
* Stripe metadata. Lowercase alphanumeric with hyphens/underscores
|
|
872
875
|
* to match the plan-key grammar. */
|
|
873
|
-
key:
|
|
876
|
+
key: z13.string().min(1).max(64).regex(/^[a-z0-9_-]+$/, "AddOn key must be lowercase alphanumeric with hyphens/underscores"),
|
|
874
877
|
/** Display name (Pricing page chip, settings list, etc.). */
|
|
875
|
-
name:
|
|
878
|
+
name: z13.string().min(1).max(100),
|
|
876
879
|
/** Short description for tooltips / chooser. */
|
|
877
|
-
description:
|
|
880
|
+
description: z13.string().max(500).optional(),
|
|
878
881
|
/** Recurring fee added to the subscription invoice while the AddOn is
|
|
879
882
|
* active. 0 = no recurring fee (e.g. for boolean feature toggles or
|
|
880
883
|
* one-time-grant-only packs). */
|
|
881
|
-
recurring_fee_cents:
|
|
884
|
+
recurring_fee_cents: z13.number().int().nonnegative().default(0),
|
|
882
885
|
/** Billing cadence for this add-on's recurring fee (`month` default /
|
|
883
886
|
* `year`). Drives the add-on's Stripe price `recurring.interval`. */
|
|
884
887
|
billing_interval: billingIntervalSchema.default("month"),
|
|
@@ -886,83 +889,83 @@ var addOnSchema = z12.object({
|
|
|
886
889
|
* into the effective entitlement's meter list (by dimension).
|
|
887
890
|
* Conflict resolution at compile time: AddOn meter wins over base
|
|
888
891
|
* plan meter for the same dimension (PR 2a-2 implements this). */
|
|
889
|
-
meters:
|
|
892
|
+
meters: z13.array(meterSchema).default([]),
|
|
890
893
|
/** Grants issued when the AddOn is activated and/or on each period
|
|
891
894
|
* rollover (kind-dependent). Same vocabulary as plan grants. */
|
|
892
|
-
grants:
|
|
895
|
+
grants: z13.array(grantSchema).max(40).default([]),
|
|
893
896
|
/** Capability bumps. Numeric limits combine with the base via MAX
|
|
894
897
|
* (so an "extra seats" addon raises the ceiling without lowering
|
|
895
898
|
* it). Boolean flags combine with OR (any true wins). */
|
|
896
|
-
capability_limits:
|
|
899
|
+
capability_limits: z13.record(z13.string().min(1).max(120), z13.union([z13.number().int().nonnegative(), z13.boolean()])).default({}),
|
|
897
900
|
/** Additive feature gates. Keys true here are appended to the
|
|
898
901
|
* effective entitlement's gate set. */
|
|
899
|
-
featureGates:
|
|
902
|
+
featureGates: z13.record(z13.string(), z13.boolean()).optional(),
|
|
900
903
|
/** Whether the AddOn is visible in the subscriber-facing catalog.
|
|
901
904
|
* When false, only admin-issued (not self-serve). Defaults true. */
|
|
902
|
-
selfServeEnabled:
|
|
905
|
+
selfServeEnabled: z13.boolean().default(true)
|
|
903
906
|
});
|
|
904
|
-
var addOnsBlockSchema =
|
|
905
|
-
var subscriptionAddOnStatusSchema =
|
|
907
|
+
var addOnsBlockSchema = z13.record(z13.string().min(1).max(64), addOnSchema).default({});
|
|
908
|
+
var subscriptionAddOnStatusSchema = z13.enum([
|
|
906
909
|
"active",
|
|
907
910
|
"canceled",
|
|
908
911
|
"paused",
|
|
909
912
|
"pending_activation"
|
|
910
913
|
]);
|
|
911
|
-
var subscriptionAddOnSchema =
|
|
914
|
+
var subscriptionAddOnSchema = z13.object({
|
|
912
915
|
/** References `product.add_ons.<addOnKey>`. */
|
|
913
|
-
addOnKey:
|
|
916
|
+
addOnKey: z13.string().min(1).max(64),
|
|
914
917
|
/** Status drives entitlement composition: only `active` AddOns
|
|
915
918
|
* contribute to the effective entitlement. */
|
|
916
919
|
status: subscriptionAddOnStatusSchema.default("active"),
|
|
917
920
|
/** When the AddOn became active (ISO-8601). Drives one-time grant
|
|
918
921
|
* issuance and recurring-fee start. */
|
|
919
|
-
activatedAt:
|
|
922
|
+
activatedAt: z13.string().datetime().optional(),
|
|
920
923
|
/** When the AddOn was canceled (ISO-8601). Null while active. */
|
|
921
|
-
canceledAt:
|
|
924
|
+
canceledAt: z13.string().datetime().nullable().optional(),
|
|
922
925
|
/** Stripe subscription-item id for the recurring-fee line, when
|
|
923
926
|
* the AddOn declares a non-zero `recurring_fee_cents`. */
|
|
924
|
-
stripeSubscriptionItemId:
|
|
927
|
+
stripeSubscriptionItemId: z13.string().optional()
|
|
925
928
|
});
|
|
926
929
|
|
|
927
930
|
// ../contracts/dist/plans/spec/backend-layer.js
|
|
928
|
-
import { z as
|
|
931
|
+
import { z as z14 } from "zod";
|
|
929
932
|
var BACKEND_ID_PATTERN = /^[a-z0-9][a-z0-9_-]*$/;
|
|
930
|
-
var backendIdSchema =
|
|
931
|
-
var backendTransportModeSchema =
|
|
933
|
+
var backendIdSchema = z14.string().min(1).max(64).regex(BACKEND_ID_PATTERN, "backend id must be a lowercase slug ([a-z0-9][a-z0-9_-]*)");
|
|
934
|
+
var backendTransportModeSchema = z14.enum([
|
|
932
935
|
"public_origin",
|
|
933
936
|
"mtls",
|
|
934
937
|
"cloudflare_tunnel"
|
|
935
938
|
]);
|
|
936
|
-
var backendTransportRunnerSchema =
|
|
939
|
+
var backendTransportRunnerSchema = z14.enum([
|
|
937
940
|
"managed_cloudflared",
|
|
938
941
|
"sidecar"
|
|
939
942
|
]);
|
|
940
|
-
var backendTransportSchema =
|
|
943
|
+
var backendTransportSchema = z14.object({
|
|
941
944
|
mode: backendTransportModeSchema.default("public_origin"),
|
|
942
945
|
runner: backendTransportRunnerSchema.optional()
|
|
943
946
|
}).strict();
|
|
944
|
-
var backendVerificationSchema =
|
|
945
|
-
required:
|
|
947
|
+
var backendVerificationSchema = z14.object({
|
|
948
|
+
required: z14.boolean().default(false)
|
|
946
949
|
}).strict();
|
|
947
|
-
var backendDefinitionSchema =
|
|
950
|
+
var backendDefinitionSchema = z14.object({
|
|
948
951
|
/** Human-friendly label. Defaults to the id when omitted. */
|
|
949
|
-
name:
|
|
952
|
+
name: z14.string().min(1).max(120).optional(),
|
|
950
953
|
/** Stable slug for the backend (origin-hostname / token scoping). Defaults
|
|
951
954
|
* to the id when omitted. */
|
|
952
955
|
slug: backendIdSchema.optional(),
|
|
953
956
|
transport: backendTransportSchema.default({ mode: "public_origin" }),
|
|
954
957
|
verification: backendVerificationSchema.default({ required: false }),
|
|
955
958
|
/** Meter allow-list. Omitted = all product meters allowed. */
|
|
956
|
-
meters:
|
|
959
|
+
meters: z14.array(z14.string().min(1).max(64)).max(100).optional(),
|
|
957
960
|
/** Marks the default backend when a product declares more than one. At
|
|
958
961
|
* most one backend may set this (compiler enforces / AMBIGUOUS_DEFAULT). */
|
|
959
|
-
default:
|
|
962
|
+
default: z14.boolean().optional(),
|
|
960
963
|
/** Reachable origin for `public_origin` / `mtls`. */
|
|
961
|
-
originUrl:
|
|
964
|
+
originUrl: z14.string().url().optional(),
|
|
962
965
|
/** Access-protected `*.fs-origin` host for `cloudflare_tunnel`. */
|
|
963
|
-
originHostname:
|
|
966
|
+
originHostname: z14.string().min(1).max(255).optional()
|
|
964
967
|
}).strict();
|
|
965
|
-
var productBackendBlockSchema =
|
|
968
|
+
var productBackendBlockSchema = z14.record(backendIdSchema, backendDefinitionSchema);
|
|
966
969
|
var routeBackendBindingSchema = backendIdSchema;
|
|
967
970
|
var BACKEND_DIAGNOSTIC_CODES = {
|
|
968
971
|
unknownBackendInRoute: "UNKNOWN_BACKEND_IN_ROUTE",
|
|
@@ -984,16 +987,16 @@ function resolveDefaultBackendId(backends) {
|
|
|
984
987
|
}
|
|
985
988
|
|
|
986
989
|
// ../contracts/dist/plans/spec/routes-layer.js
|
|
987
|
-
import { z as
|
|
990
|
+
import { z as z18 } from "zod";
|
|
988
991
|
|
|
989
992
|
// ../contracts/dist/plans/spec/policies-layer.js
|
|
990
|
-
import { z as
|
|
993
|
+
import { z as z16 } from "zod";
|
|
991
994
|
|
|
992
995
|
// ../contracts/dist/plans/spec/policy-types.js
|
|
993
|
-
import { z as
|
|
994
|
-
var rateLimitWindowSchema =
|
|
995
|
-
var rateLimitConfigSchema =
|
|
996
|
-
strategy:
|
|
996
|
+
import { z as z15 } from "zod";
|
|
997
|
+
var rateLimitWindowSchema = z15.string().min(2).max(20).regex(/^\d+(ms|s|m|h)$/, "rate_limit window must look like `60s`, `5m`, `1h`");
|
|
998
|
+
var rateLimitConfigSchema = z15.object({
|
|
999
|
+
strategy: z15.enum(["token_bucket", "sliding_window", "fixed_window"]).default("token_bucket"),
|
|
997
1000
|
/**
|
|
998
1001
|
* Which request dimensions identify the bucket. v0.3.0 supports a
|
|
999
1002
|
* fixed set; extending requires a coordinated gateway/policy-engine
|
|
@@ -1001,10 +1004,10 @@ var rateLimitConfigSchema = z14.object({
|
|
|
1001
1004
|
* `ip` is for unauthenticated probes; `credential` is finer-grained
|
|
1002
1005
|
* than subscription (per-key throttling).
|
|
1003
1006
|
*/
|
|
1004
|
-
dimensions:
|
|
1005
|
-
limits:
|
|
1007
|
+
dimensions: z15.array(z15.enum(["subscription", "credential", "ip", "route"])).min(1).max(4).default(["subscription"]),
|
|
1008
|
+
limits: z15.array(z15.object({
|
|
1006
1009
|
window: rateLimitWindowSchema,
|
|
1007
|
-
max:
|
|
1010
|
+
max: z15.number().int().positive().max(1e7)
|
|
1008
1011
|
})).min(1).max(10),
|
|
1009
1012
|
/**
|
|
1010
1013
|
* Bounded fail-open behaviour for DO outages. See architecture RFC
|
|
@@ -1014,11 +1017,11 @@ var rateLimitConfigSchema = z14.object({
|
|
|
1014
1017
|
* `recovery_threshold` consecutive successes restore normal
|
|
1015
1018
|
* evaluation.
|
|
1016
1019
|
*/
|
|
1017
|
-
fail_open:
|
|
1018
|
-
max_consecutive_failures:
|
|
1019
|
-
max_window_seconds:
|
|
1020
|
-
recovery_threshold:
|
|
1021
|
-
degraded_mode:
|
|
1020
|
+
fail_open: z15.object({
|
|
1021
|
+
max_consecutive_failures: z15.number().int().positive().default(100),
|
|
1022
|
+
max_window_seconds: z15.number().int().positive().default(60),
|
|
1023
|
+
recovery_threshold: z15.number().int().positive().default(50),
|
|
1024
|
+
degraded_mode: z15.enum([
|
|
1022
1025
|
"safe_mode_block",
|
|
1023
1026
|
"safe_mode_throttle",
|
|
1024
1027
|
"runtime_killswitch_trigger"
|
|
@@ -1030,19 +1033,19 @@ var rateLimitConfigSchema = z14.object({
|
|
|
1030
1033
|
degraded_mode: "safe_mode_throttle"
|
|
1031
1034
|
})
|
|
1032
1035
|
});
|
|
1033
|
-
var authConfigSchema =
|
|
1034
|
-
header_name:
|
|
1036
|
+
var authConfigSchema = z15.object({
|
|
1037
|
+
header_name: z15.string().min(1).max(100).default("x-api-key"),
|
|
1035
1038
|
/**
|
|
1036
1039
|
* How the gateway constructs the upstream Authorization header:
|
|
1037
1040
|
* - `none` → no upstream auth header added
|
|
1038
1041
|
* - `static_bearer` → forward a configured static token
|
|
1039
1042
|
* - `subscriber_jwt` → mint a per-subscriber JWT (out of scope v0.3.0)
|
|
1040
1043
|
*/
|
|
1041
|
-
upstream_token_source:
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
type:
|
|
1045
|
-
token_secret_ref:
|
|
1044
|
+
upstream_token_source: z15.discriminatedUnion("type", [
|
|
1045
|
+
z15.object({ type: z15.literal("none") }),
|
|
1046
|
+
z15.object({
|
|
1047
|
+
type: z15.literal("static_bearer"),
|
|
1048
|
+
token_secret_ref: z15.string().min(1).max(200).describe("Reference into the secret store (e.g. CF Secret name); not the raw token")
|
|
1046
1049
|
})
|
|
1047
1050
|
]).default({ type: "none" }),
|
|
1048
1051
|
/**
|
|
@@ -1050,32 +1053,32 @@ var authConfigSchema = z14.object({
|
|
|
1050
1053
|
* additional gating beyond the entitlement check. v0.3.0 ships with
|
|
1051
1054
|
* `strict` as the default.
|
|
1052
1055
|
*/
|
|
1053
|
-
scope_mode:
|
|
1056
|
+
scope_mode: z15.enum(["strict", "advisory", "off"]).default("strict")
|
|
1054
1057
|
});
|
|
1055
|
-
var concurrencyConfigSchema =
|
|
1056
|
-
max_in_flight:
|
|
1058
|
+
var concurrencyConfigSchema = z15.object({
|
|
1059
|
+
max_in_flight: z15.number().int().positive().max(1e4),
|
|
1057
1060
|
/**
|
|
1058
1061
|
* Which dimensions key the lease bucket. Matches the existing
|
|
1059
1062
|
* ConcurrencyLease DO `idFromName` pattern (subscription | capability
|
|
1060
1063
|
* tuple).
|
|
1061
1064
|
*/
|
|
1062
|
-
dimensions:
|
|
1065
|
+
dimensions: z15.array(z15.enum(["subscription", "credential", "capability"])).min(1).max(3).default(["subscription"]),
|
|
1063
1066
|
/**
|
|
1064
1067
|
* Optional capability scope. When set, the lease bucket is keyed
|
|
1065
1068
|
* partly by this capability name — separate buckets per capability.
|
|
1066
1069
|
*/
|
|
1067
|
-
capability:
|
|
1070
|
+
capability: z15.string().min(1).max(120).optional(),
|
|
1068
1071
|
/**
|
|
1069
1072
|
* Lease TTL — releases automatically after this many seconds even if
|
|
1070
1073
|
* the request never returns (defensive default 30s, mirrors existing
|
|
1071
1074
|
* ConcurrencyLease behaviour).
|
|
1072
1075
|
*/
|
|
1073
|
-
lease_ttl_seconds:
|
|
1074
|
-
fail_open:
|
|
1075
|
-
max_consecutive_failures:
|
|
1076
|
-
max_window_seconds:
|
|
1077
|
-
recovery_threshold:
|
|
1078
|
-
degraded_mode:
|
|
1076
|
+
lease_ttl_seconds: z15.number().int().positive().max(600).default(30),
|
|
1077
|
+
fail_open: z15.object({
|
|
1078
|
+
max_consecutive_failures: z15.number().int().positive().default(50),
|
|
1079
|
+
max_window_seconds: z15.number().int().positive().default(60),
|
|
1080
|
+
recovery_threshold: z15.number().int().positive().default(20),
|
|
1081
|
+
degraded_mode: z15.enum([
|
|
1079
1082
|
"safe_mode_block",
|
|
1080
1083
|
"safe_mode_throttle",
|
|
1081
1084
|
"runtime_killswitch_trigger"
|
|
@@ -1087,22 +1090,22 @@ var concurrencyConfigSchema = z14.object({
|
|
|
1087
1090
|
degraded_mode: "safe_mode_throttle"
|
|
1088
1091
|
})
|
|
1089
1092
|
});
|
|
1090
|
-
var retryConfigSchema =
|
|
1091
|
-
max_attempts:
|
|
1093
|
+
var retryConfigSchema = z15.object({
|
|
1094
|
+
max_attempts: z15.number().int().min(1).max(5).default(2),
|
|
1092
1095
|
/**
|
|
1093
1096
|
* HTTP status codes that trigger a retry. 5xx is the default; opt
|
|
1094
1097
|
* into 429 retries only when the upstream understands `Retry-After`.
|
|
1095
1098
|
*/
|
|
1096
|
-
retry_on_status:
|
|
1099
|
+
retry_on_status: z15.array(z15.number().int().min(400).max(599)).min(1).max(20).default([502, 503, 504]),
|
|
1097
1100
|
/**
|
|
1098
1101
|
* Backoff curve. Total wall-clock attempt time is bounded so the
|
|
1099
1102
|
* gateway worker cannot block past `total_budget_ms`.
|
|
1100
1103
|
*/
|
|
1101
|
-
backoff:
|
|
1102
|
-
initial_ms:
|
|
1103
|
-
multiplier:
|
|
1104
|
-
jitter:
|
|
1105
|
-
total_budget_ms:
|
|
1104
|
+
backoff: z15.object({
|
|
1105
|
+
initial_ms: z15.number().int().positive().max(5e3).default(100),
|
|
1106
|
+
multiplier: z15.number().positive().max(10).default(2),
|
|
1107
|
+
jitter: z15.enum(["none", "full", "equal"]).default("equal"),
|
|
1108
|
+
total_budget_ms: z15.number().int().positive().max(3e4).default(5e3)
|
|
1106
1109
|
}).default({
|
|
1107
1110
|
initial_ms: 100,
|
|
1108
1111
|
multiplier: 2,
|
|
@@ -1110,46 +1113,46 @@ var retryConfigSchema = z14.object({
|
|
|
1110
1113
|
total_budget_ms: 5e3
|
|
1111
1114
|
})
|
|
1112
1115
|
});
|
|
1113
|
-
var transformConfigSchema =
|
|
1116
|
+
var transformConfigSchema = z15.object({
|
|
1114
1117
|
/**
|
|
1115
1118
|
* When the transform applies. `request` runs before upstream forward;
|
|
1116
1119
|
* `response` runs after. Most transforms are one or the other; both
|
|
1117
1120
|
* is rare.
|
|
1118
1121
|
*/
|
|
1119
|
-
applies_to:
|
|
1122
|
+
applies_to: z15.enum(["request", "response", "both"]).default("request"),
|
|
1120
1123
|
/**
|
|
1121
1124
|
* List of key rewrites. Source path uses dot notation (`a.b.c`);
|
|
1122
1125
|
* `target` may include the same syntax to move keys around. Drops
|
|
1123
1126
|
* are expressed as `target: null`.
|
|
1124
1127
|
*/
|
|
1125
|
-
rewrites:
|
|
1126
|
-
source:
|
|
1127
|
-
target:
|
|
1128
|
+
rewrites: z15.array(z15.object({
|
|
1129
|
+
source: z15.string().min(1).max(200),
|
|
1130
|
+
target: z15.string().min(1).max(200).nullable()
|
|
1128
1131
|
})).min(1).max(20)
|
|
1129
1132
|
});
|
|
1130
|
-
var policyBodySchema =
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1133
|
+
var policyBodySchema = z15.discriminatedUnion("type", [
|
|
1134
|
+
z15.object({ type: z15.literal("rate_limit"), config: rateLimitConfigSchema }),
|
|
1135
|
+
z15.object({ type: z15.literal("auth"), config: authConfigSchema }),
|
|
1136
|
+
z15.object({ type: z15.literal("concurrency"), config: concurrencyConfigSchema }),
|
|
1137
|
+
z15.object({ type: z15.literal("retry"), config: retryConfigSchema }),
|
|
1138
|
+
z15.object({ type: z15.literal("transform"), config: transformConfigSchema })
|
|
1136
1139
|
]);
|
|
1137
1140
|
|
|
1138
1141
|
// ../contracts/dist/plans/spec/policies-layer.js
|
|
1139
|
-
var cacheProfileSchema =
|
|
1140
|
-
var policyCompatibilitySchema =
|
|
1141
|
-
route_types:
|
|
1142
|
-
meters:
|
|
1143
|
-
auth_modes:
|
|
1142
|
+
var cacheProfileSchema = z16.enum(["long", "short", "blocking"]).default("long");
|
|
1143
|
+
var policyCompatibilitySchema = z16.object({
|
|
1144
|
+
route_types: z16.array(z16.enum(["http"])).max(5).optional(),
|
|
1145
|
+
meters: z16.array(z16.string().min(1).max(64)).max(20).optional(),
|
|
1146
|
+
auth_modes: z16.array(z16.enum(["api_key", "oauth2", "anonymous"])).max(5).optional()
|
|
1144
1147
|
});
|
|
1145
|
-
var policyLayerSchema =
|
|
1148
|
+
var policyLayerSchema = z16.intersection(z16.object({
|
|
1146
1149
|
/**
|
|
1147
1150
|
* Policy name. Referenced by routes via `policies: [<name>]`. Must
|
|
1148
1151
|
* be unique across the product; the compiler enforces this in the
|
|
1149
1152
|
* cross-layer validation pass.
|
|
1150
1153
|
*/
|
|
1151
|
-
name:
|
|
1152
|
-
description:
|
|
1154
|
+
name: z16.string().min(1).max(64).regex(/^[a-z0-9_-]+$/, "Policy name must be lowercase alphanumeric with hyphens/underscores"),
|
|
1155
|
+
description: z16.string().max(500).optional(),
|
|
1153
1156
|
compatible_with: policyCompatibilitySchema.default({}),
|
|
1154
1157
|
/**
|
|
1155
1158
|
* Mutation class — runtime vs contractual. Policies are operational
|
|
@@ -1157,38 +1160,38 @@ var policyLayerSchema = z15.intersection(z15.object({
|
|
|
1157
1160
|
* `contractual` signals that changes to it require human approval
|
|
1158
1161
|
* (invariant #16).
|
|
1159
1162
|
*/
|
|
1160
|
-
mutation_class:
|
|
1163
|
+
mutation_class: z16.enum(["runtime", "contractual"]).default("runtime"),
|
|
1161
1164
|
cacheProfile: cacheProfileSchema
|
|
1162
1165
|
}), policyBodySchema);
|
|
1163
1166
|
|
|
1164
1167
|
// ../contracts/dist/framework/actions/index.js
|
|
1165
|
-
import { z as
|
|
1166
|
-
var actionKindSchema =
|
|
1167
|
-
var actionAuditPolicySchema =
|
|
1168
|
-
var actionSubjectBindingSchema =
|
|
1169
|
-
type:
|
|
1170
|
-
from:
|
|
1171
|
-
name:
|
|
1172
|
-
});
|
|
1173
|
-
var actionResourceEffectSchema =
|
|
1174
|
-
resource:
|
|
1175
|
-
effect:
|
|
1176
|
-
});
|
|
1177
|
-
var actionSpecSchema =
|
|
1178
|
-
id:
|
|
1179
|
-
title:
|
|
1168
|
+
import { z as z17 } from "zod";
|
|
1169
|
+
var actionKindSchema = z17.enum(["query", "mutation"]);
|
|
1170
|
+
var actionAuditPolicySchema = z17.enum(["none", "metadata", "full"]);
|
|
1171
|
+
var actionSubjectBindingSchema = z17.object({
|
|
1172
|
+
type: z17.string().min(1).max(64).regex(/^[a-zA-Z0-9_.:-]+$/),
|
|
1173
|
+
from: z17.enum(["header", "path_param"]),
|
|
1174
|
+
name: z17.string().min(1).max(120)
|
|
1175
|
+
});
|
|
1176
|
+
var actionResourceEffectSchema = z17.object({
|
|
1177
|
+
resource: z17.string().min(1).max(100).regex(/^[a-z0-9_.:-]+$/),
|
|
1178
|
+
effect: z17.enum(["create", "delete"])
|
|
1179
|
+
});
|
|
1180
|
+
var actionSpecSchema = z17.object({
|
|
1181
|
+
id: z17.string().min(1).max(160).regex(/^[a-z0-9_.:-]+$/),
|
|
1182
|
+
title: z17.string().min(1).max(160).optional(),
|
|
1180
1183
|
kind: actionKindSchema,
|
|
1181
|
-
actorType:
|
|
1184
|
+
actorType: z17.string().min(1).max(64).regex(/^[a-zA-Z0-9_.:-]+$/).optional(),
|
|
1182
1185
|
subject: actionSubjectBindingSchema.optional(),
|
|
1183
|
-
inputSchemaRef:
|
|
1186
|
+
inputSchemaRef: z17.string().min(1).max(240).optional(),
|
|
1184
1187
|
audit: actionAuditPolicySchema.default("metadata"),
|
|
1185
1188
|
resource: actionResourceEffectSchema.optional()
|
|
1186
1189
|
});
|
|
1187
1190
|
|
|
1188
1191
|
// ../contracts/dist/plans/spec/routes-layer.js
|
|
1189
|
-
var routeMatchSchema =
|
|
1190
|
-
method:
|
|
1191
|
-
path:
|
|
1192
|
+
var routeMatchSchema = z18.object({
|
|
1193
|
+
method: z18.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "*"]).default("*"),
|
|
1194
|
+
path: z18.string().min(1).max(500).regex(/^\/[a-zA-Z0-9_/:.{}*-]*$/, "path must start with / and contain only [a-zA-Z0-9_/:.{}*-]")
|
|
1192
1195
|
});
|
|
1193
1196
|
function statusPolicyPartIsValid(part) {
|
|
1194
1197
|
const trimmed = part.trim();
|
|
@@ -1206,60 +1209,60 @@ function statusPolicyPartIsValid(part) {
|
|
|
1206
1209
|
function isRouteStatusCodePolicyString(value) {
|
|
1207
1210
|
return value.split(",").every(statusPolicyPartIsValid);
|
|
1208
1211
|
}
|
|
1209
|
-
var routeStatusCodePolicySchema =
|
|
1210
|
-
|
|
1212
|
+
var routeStatusCodePolicySchema = z18.union([
|
|
1213
|
+
z18.string().min(1).max(100).refine(isRouteStatusCodePolicyString, {
|
|
1211
1214
|
message: "onStatusCodes must be comma-separated HTTP status codes or numeric ranges, e.g. 200-299,304"
|
|
1212
1215
|
}),
|
|
1213
|
-
|
|
1216
|
+
z18.array(z18.number().int().min(100).max(599)).min(1).max(100)
|
|
1214
1217
|
]);
|
|
1215
|
-
var routeDefinitionSchema =
|
|
1218
|
+
var routeDefinitionSchema = z18.object({
|
|
1216
1219
|
match: routeMatchSchema,
|
|
1217
|
-
metering:
|
|
1218
|
-
defaults:
|
|
1219
|
-
reports:
|
|
1220
|
-
estimates:
|
|
1220
|
+
metering: z18.object({
|
|
1221
|
+
defaults: z18.record(z18.string().min(1).max(64), z18.number().finite().nonnegative()).optional(),
|
|
1222
|
+
reports: z18.array(z18.string().min(1).max(64)).max(20).optional(),
|
|
1223
|
+
estimates: z18.record(z18.string().min(1).max(64), z18.number().finite().nonnegative()).optional(),
|
|
1221
1224
|
onStatusCodes: routeStatusCodePolicySchema.optional()
|
|
1222
1225
|
}).optional(),
|
|
1223
|
-
unmetered:
|
|
1224
|
-
inheritDefaultMeters:
|
|
1226
|
+
unmetered: z18.boolean().optional(),
|
|
1227
|
+
inheritDefaultMeters: z18.boolean().optional(),
|
|
1225
1228
|
/** Optional explicit action id. When absent, the compiler derives an
|
|
1226
1229
|
* implicit action from feature + method + path. */
|
|
1227
|
-
action:
|
|
1230
|
+
action: z18.string().min(1).max(160).regex(/^[a-z0-9_.:-]+$/).optional(),
|
|
1228
1231
|
/** BYO-Backend V1 — optional route→backend binding. Omitted = the sole /
|
|
1229
1232
|
* default backend (single-backend products stay zero-config). The schema
|
|
1230
1233
|
* is `.strict()`, so this key MUST be declared here or `parse` throws on
|
|
1231
1234
|
* it (anti-`.strict()`). */
|
|
1232
1235
|
backend: routeBackendBindingSchema.optional()
|
|
1233
1236
|
}).strict();
|
|
1234
|
-
var routeUpstreamSchema =
|
|
1235
|
-
override_origin:
|
|
1237
|
+
var routeUpstreamSchema = z18.object({
|
|
1238
|
+
override_origin: z18.string().url("override_origin must be a valid URL").nullable().default(null)
|
|
1236
1239
|
});
|
|
1237
|
-
var routeRuntimeSchema =
|
|
1238
|
-
rollout_key:
|
|
1240
|
+
var routeRuntimeSchema = z18.object({
|
|
1241
|
+
rollout_key: z18.string().min(1).max(120).regex(/^[a-z0-9_-]+$/, "rollout_key must be lowercase alphanumeric with hyphens/underscores").optional(),
|
|
1239
1242
|
/**
|
|
1240
1243
|
* Optional runtime flags this feature depends on. The runtime
|
|
1241
1244
|
* evaluator AND's the feature's enablement across all referenced
|
|
1242
1245
|
* flags. If any flag is disabled, the route returns the configured
|
|
1243
1246
|
* fallback (404 by default — see /runtime failure matrix).
|
|
1244
1247
|
*/
|
|
1245
|
-
required_flags:
|
|
1248
|
+
required_flags: z18.array(z18.string().min(1).max(120).regex(/^[a-z0-9_-]+$/)).max(10).optional()
|
|
1246
1249
|
});
|
|
1247
|
-
var routeLayerSchema =
|
|
1250
|
+
var routeLayerSchema = z18.object({
|
|
1248
1251
|
/**
|
|
1249
1252
|
* Feature key — the entitlement unit. Surfaced in dashboards,
|
|
1250
1253
|
* subscriptions, and the gateway's matched-route trace.
|
|
1251
1254
|
*/
|
|
1252
|
-
feature:
|
|
1253
|
-
description:
|
|
1255
|
+
feature: z18.string().min(1).max(100).regex(/^[a-z0-9_.:-]+$/, "feature key must be lowercase alphanumeric with [_.:-]"),
|
|
1256
|
+
description: z18.string().max(500).optional(),
|
|
1254
1257
|
/**
|
|
1255
1258
|
* Route additions are contractual by default — they expose new API
|
|
1256
1259
|
* surface to subscribers. Internal/non-customer-visible routes can
|
|
1257
1260
|
* mark themselves `runtime` to allow autonomous agent flips
|
|
1258
1261
|
* (invariant #16; see RFC approval matrix).
|
|
1259
1262
|
*/
|
|
1260
|
-
mutation_class:
|
|
1263
|
+
mutation_class: z18.enum(["runtime", "contractual"]).default("contractual"),
|
|
1261
1264
|
cacheProfile: cacheProfileSchema,
|
|
1262
|
-
routes:
|
|
1265
|
+
routes: z18.array(routeDefinitionSchema).min(1).max(50),
|
|
1263
1266
|
upstream: routeUpstreamSchema.default({ override_origin: null }),
|
|
1264
1267
|
/**
|
|
1265
1268
|
* Ordered list of policy names to apply. Executed sequentially by
|
|
@@ -1267,17 +1270,17 @@ var routeLayerSchema = z17.object({
|
|
|
1267
1270
|
* MUST declare compatible `compatible_with` envelopes for this
|
|
1268
1271
|
* feature's route/meter shape — the compiler enforces.
|
|
1269
1272
|
*/
|
|
1270
|
-
policies:
|
|
1273
|
+
policies: z18.array(z18.string().min(1).max(64).regex(/^[a-z0-9_-]+$/)).max(20).default([]),
|
|
1271
1274
|
runtime: routeRuntimeSchema.default({}),
|
|
1272
1275
|
/**
|
|
1273
1276
|
* Plans that grant this feature directly. Shared feature bundles are
|
|
1274
1277
|
* expressed in capability layers via `includes_features`; route layers do
|
|
1275
1278
|
* not declare capability membership.
|
|
1276
1279
|
*/
|
|
1277
|
-
plans:
|
|
1280
|
+
plans: z18.array(z18.string().min(1).max(64)).max(20).default([]),
|
|
1278
1281
|
/** Explicit actions declared by this feature. Routes reference them by
|
|
1279
1282
|
* `route.action`; routes without a binding receive implicit actions. */
|
|
1280
|
-
actions:
|
|
1283
|
+
actions: z18.array(actionSpecSchema).max(100).optional(),
|
|
1281
1284
|
/** BYO-Backend V1 — feature-level default backend binding. Routes in this
|
|
1282
1285
|
* layer with no explicit `route.backend` inherit this; routes may still
|
|
1283
1286
|
* override per-route. Omitted = the product's sole / default backend. */
|
|
@@ -1597,18 +1600,18 @@ function planPriceKey(plan, monthly) {
|
|
|
1597
1600
|
}
|
|
1598
1601
|
|
|
1599
1602
|
// ../contracts/dist/plans/spec/product.js
|
|
1600
|
-
var productIdentitySchema =
|
|
1601
|
-
subdomain:
|
|
1603
|
+
var productIdentitySchema = z19.object({
|
|
1604
|
+
subdomain: z19.string().min(1).max(63).regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/, "Subdomain must be lowercase alphanumeric with optional hyphens")
|
|
1602
1605
|
});
|
|
1603
|
-
var meterEnforcementTypeSchema =
|
|
1606
|
+
var meterEnforcementTypeSchema = z19.enum([
|
|
1604
1607
|
"exact_pre_request",
|
|
1605
1608
|
"estimated_then_settled",
|
|
1606
1609
|
"postpaid",
|
|
1607
1610
|
"strict_concurrency"
|
|
1608
1611
|
]);
|
|
1609
|
-
var meterDefinitionSchema =
|
|
1610
|
-
key:
|
|
1611
|
-
display:
|
|
1612
|
+
var meterDefinitionSchema = z19.object({
|
|
1613
|
+
key: z19.string().min(1).max(64).regex(/^[a-z0-9_]+$/, "Meter key must be lowercase alphanumeric with underscores"),
|
|
1614
|
+
display: z19.string().min(1).max(100),
|
|
1612
1615
|
// v0.42.0 — `type: "built-in" | "custom"` removed. The runtime never
|
|
1613
1616
|
// read it (gateway estimators key on meter NAME, Stripe meter
|
|
1614
1617
|
// creation keys on meter KEY); it was schema documentation. Wizard
|
|
@@ -1616,11 +1619,11 @@ var meterDefinitionSchema = z18.object({
|
|
|
1616
1619
|
// ai_usage → `dollars`); Custom template lets builders define keys
|
|
1617
1620
|
// freely. Old specs with `type: ...` parse cleanly because Zod
|
|
1618
1621
|
// strips unknown fields by default.
|
|
1619
|
-
unit:
|
|
1622
|
+
unit: z19.string().max(20).optional(),
|
|
1620
1623
|
/** Reusable pre-request estimate for routes that dynamically report this meter. */
|
|
1621
|
-
estimate:
|
|
1624
|
+
estimate: z19.number().finite().nonnegative().optional(),
|
|
1622
1625
|
/** Fixed per-request default applied by Product SDK helpers. */
|
|
1623
|
-
routeDefault:
|
|
1626
|
+
routeDefault: z19.number().finite().nonnegative().optional(),
|
|
1624
1627
|
/**
|
|
1625
1628
|
* Runtime enforcement semantics for this meter. This is compiled into
|
|
1626
1629
|
* signed gateway artifacts so the edge chooses reservation, settlement,
|
|
@@ -1645,7 +1648,7 @@ var meterDefinitionSchema = z18.object({
|
|
|
1645
1648
|
* current `window`. Defaults to `COUNT` (one event = one unit) so
|
|
1646
1649
|
* existing meters that didn't declare aggregation continue to work.
|
|
1647
1650
|
*/
|
|
1648
|
-
aggregation:
|
|
1651
|
+
aggregation: z19.enum(["SUM", "COUNT", "MAX", "UNIQUE_COUNT", "LATEST"]).default("COUNT"),
|
|
1649
1652
|
/**
|
|
1650
1653
|
* Aggregation window. `billing_period` (the default) makes the
|
|
1651
1654
|
* meter accumulate across the subscription's billing period.
|
|
@@ -1653,88 +1656,65 @@ var meterDefinitionSchema = z18.object({
|
|
|
1653
1656
|
* rate-limit-shaped meters where the period boundary is a fixed
|
|
1654
1657
|
* wall-clock interval, not the subscription anniversary.
|
|
1655
1658
|
*/
|
|
1656
|
-
window:
|
|
1659
|
+
window: z19.enum(["minute", "hour", "day", "month", "billing_period"]).default("billing_period"),
|
|
1657
1660
|
/**
|
|
1658
1661
|
* Property on the event payload to read for `SUM` and `MAX`
|
|
1659
1662
|
* aggregations. Optional at the schema level; core's `validate.ts`
|
|
1660
1663
|
* (Phase 1b) enforces "required when aggregation is SUM or MAX".
|
|
1661
1664
|
*/
|
|
1662
|
-
valueProperty:
|
|
1665
|
+
valueProperty: z19.string().optional(),
|
|
1663
1666
|
/**
|
|
1664
1667
|
* Property on the event payload to dedupe on for `UNIQUE_COUNT`
|
|
1665
1668
|
* aggregation. Optional at the schema level; core's validate-pass
|
|
1666
1669
|
* enforces "required when aggregation is UNIQUE_COUNT".
|
|
1667
1670
|
*/
|
|
1668
|
-
uniqueProperty:
|
|
1671
|
+
uniqueProperty: z19.string().optional(),
|
|
1669
1672
|
/**
|
|
1670
1673
|
* Optional grouping dimensions. When set, the aggregation is per
|
|
1671
1674
|
* unique combination of these properties' values, not a single
|
|
1672
1675
|
* scalar. Used for per-region or per-model breakdowns.
|
|
1673
1676
|
*/
|
|
1674
|
-
groupBy:
|
|
1677
|
+
groupBy: z19.array(z19.string()).optional(),
|
|
1675
1678
|
/**
|
|
1676
1679
|
* Lago-side event code for ingress correlation. Matches Lago's
|
|
1677
1680
|
* BillableMetric `code` so events sent to Lago land on the right
|
|
1678
1681
|
* meter without a per-meter translation table.
|
|
1679
1682
|
*/
|
|
1680
|
-
eventCode:
|
|
1681
|
-
});
|
|
1682
|
-
var usageMeasureSchema = z18.string().min(1).max(64).regex(/^[a-z0-9_]+$/, "Usage measure must be lowercase alphanumeric with underscores");
|
|
1683
|
-
var usageRatingPricePolicySchema = z18.enum([
|
|
1684
|
-
"pass_through",
|
|
1685
|
-
"markup",
|
|
1686
|
-
"fixed_margin",
|
|
1687
|
-
"customer_rate"
|
|
1688
|
-
]);
|
|
1689
|
-
var fixedRatingSchema = z18.object({
|
|
1690
|
-
source: z18.literal("fixed"),
|
|
1691
|
-
rates: z18.record(z18.string().min(1), z18.record(usageMeasureSchema, z18.number().int().nonnegative()))
|
|
1683
|
+
eventCode: z19.string().optional()
|
|
1692
1684
|
});
|
|
1693
|
-
var
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
marginMicros: z18.number().int().nonnegative().optional()
|
|
1699
|
-
});
|
|
1700
|
-
var upstreamReportedRatingSchema = z18.object({
|
|
1701
|
-
source: z18.literal("upstream_reported"),
|
|
1702
|
-
amountField: z18.string().min(1).max(500),
|
|
1703
|
-
currencyField: z18.string().min(1).max(500).optional()
|
|
1704
|
-
});
|
|
1705
|
-
var externalRateApiRatingSchema = z18.object({
|
|
1706
|
-
source: z18.literal("external_rate_api"),
|
|
1707
|
-
resolver: z18.string().min(1).max(100),
|
|
1708
|
-
configRef: z18.string().min(1).max(200).optional()
|
|
1685
|
+
var usageMeasureSchema = z19.string().min(1).max(64).regex(/^[a-z0-9_]+$/, "Usage measure must be lowercase alphanumeric with underscores");
|
|
1686
|
+
var usageRatingPricePolicySchema = z19.enum(["pass_through", "markup"]);
|
|
1687
|
+
var fixedRatingSchema = z19.object({
|
|
1688
|
+
source: z19.literal("fixed"),
|
|
1689
|
+
rates: z19.record(z19.string().min(1), z19.record(usageMeasureSchema, z19.number().int().nonnegative()))
|
|
1709
1690
|
});
|
|
1710
|
-
var
|
|
1711
|
-
source:
|
|
1712
|
-
|
|
1713
|
-
|
|
1691
|
+
var providerCatalogRatingSchema = z19.object({
|
|
1692
|
+
source: z19.literal("provider_catalog"),
|
|
1693
|
+
catalog: z19.string().min(1).max(100),
|
|
1694
|
+
pricePolicy: usageRatingPricePolicySchema.default("pass_through"),
|
|
1695
|
+
markupPercent: z19.number().nonnegative().max(1e4).optional(),
|
|
1696
|
+
marginMicros: z19.number().int().nonnegative().optional()
|
|
1714
1697
|
});
|
|
1715
|
-
var usageRatingSchema =
|
|
1698
|
+
var usageRatingSchema = z19.discriminatedUnion("source", [
|
|
1716
1699
|
fixedRatingSchema,
|
|
1717
|
-
providerCatalogRatingSchema
|
|
1718
|
-
upstreamReportedRatingSchema,
|
|
1719
|
-
externalRateApiRatingSchema,
|
|
1720
|
-
customRatingSchema
|
|
1700
|
+
providerCatalogRatingSchema
|
|
1721
1701
|
]);
|
|
1722
|
-
var usageMeterSchema =
|
|
1723
|
-
selector:
|
|
1724
|
-
measures:
|
|
1702
|
+
var usageMeterSchema = z19.object({
|
|
1703
|
+
selector: z19.string().min(1).max(100).optional(),
|
|
1704
|
+
measures: z19.array(usageMeasureSchema).min(1).max(20),
|
|
1725
1705
|
rating: usageRatingSchema.optional()
|
|
1726
1706
|
});
|
|
1727
|
-
var usageBlockSchema =
|
|
1728
|
-
meters:
|
|
1707
|
+
var usageBlockSchema = z19.object({
|
|
1708
|
+
meters: z19.record(z19.string().min(1).max(64).regex(/^[a-z0-9_]+$/), usageMeterSchema)
|
|
1729
1709
|
});
|
|
1730
|
-
var routeMeteringSchema =
|
|
1731
|
-
defaults:
|
|
1732
|
-
reports:
|
|
1733
|
-
estimates:
|
|
1710
|
+
var routeMeteringSchema = z19.object({
|
|
1711
|
+
defaults: z19.record(z19.string().min(1).max(64), z19.number().finite().nonnegative()).optional(),
|
|
1712
|
+
reports: z19.array(z19.string().min(1).max(64)).max(20).optional(),
|
|
1713
|
+
estimates: z19.record(z19.string().min(1).max(64), z19.number().finite().nonnegative()).optional(),
|
|
1734
1714
|
onStatusCodes: routeStatusCodePolicySchema.optional()
|
|
1735
1715
|
});
|
|
1736
|
-
var featureRouteSchema =
|
|
1737
|
-
method:
|
|
1716
|
+
var featureRouteSchema = z19.object({
|
|
1717
|
+
method: z19.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "*"]).default("*"),
|
|
1738
1718
|
// Path is the route under the product's baseUrl. OpenAPI parameter
|
|
1739
1719
|
// syntax is supported and translated by the compiler:
|
|
1740
1720
|
// /users/:id → /users/*
|
|
@@ -1742,48 +1722,48 @@ var featureRouteSchema = z18.object({
|
|
|
1742
1722
|
// Path-globs `*` (one segment) and `**` (any subpath) are passed
|
|
1743
1723
|
// through. The compiler rejects ambiguous compound segments like
|
|
1744
1724
|
// `/foo/:a-:b` — parameter names must occupy whole segments.
|
|
1745
|
-
path:
|
|
1725
|
+
path: z19.string().min(1).max(500).regex(/^\/[a-zA-Z0-9_/:.{}*-]*$/, "path must start with / and contain only [a-zA-Z0-9_/:.{}*-]"),
|
|
1746
1726
|
// Explicit no-usage route. Dynamic/static route metering is declared
|
|
1747
1727
|
// exclusively under `metering`.
|
|
1748
|
-
unmetered:
|
|
1728
|
+
unmetered: z19.boolean().optional(),
|
|
1749
1729
|
metering: routeMeteringSchema.optional(),
|
|
1750
|
-
inheritDefaultMeters:
|
|
1730
|
+
inheritDefaultMeters: z19.boolean().optional(),
|
|
1751
1731
|
// BYO-Backend V1 — route→backend binding. The compiler materializes
|
|
1752
1732
|
// per-feature route layers into this strict route shape, so the key MUST be
|
|
1753
1733
|
// declared here or `parse` throws on it (anti-`.strict()`).
|
|
1754
1734
|
backend: routeBackendBindingSchema.optional()
|
|
1755
1735
|
}).strict();
|
|
1756
|
-
var featureCatalogEntrySchema =
|
|
1736
|
+
var featureCatalogEntrySchema = z19.object({
|
|
1757
1737
|
// Optional human-friendly summary; surfaced in dashboards / settings UI.
|
|
1758
|
-
description:
|
|
1759
|
-
routes:
|
|
1738
|
+
description: z19.string().max(500).optional(),
|
|
1739
|
+
routes: z19.array(featureRouteSchema).min(1).max(50),
|
|
1760
1740
|
// Plans that grant this feature. Feature-first canonical mapping —
|
|
1761
1741
|
// builders declare "which plans get this feature" on the feature
|
|
1762
1742
|
// itself rather than enumerating features per plan. Required and
|
|
1763
1743
|
// non-empty: a feature with no plans grants nothing and is a likely
|
|
1764
1744
|
// typo. Cross-reference validation (every key resolves to an
|
|
1765
1745
|
// existing plan) lives in `validateFeatureReferences` below.
|
|
1766
|
-
plans:
|
|
1746
|
+
plans: z19.array(z19.string().min(1)).min(1).max(20)
|
|
1767
1747
|
});
|
|
1768
|
-
var featureCatalogSchema =
|
|
1769
|
-
var productCleanupPolicyModeSchema =
|
|
1748
|
+
var featureCatalogSchema = z19.record(z19.string().min(1).max(100).regex(/^[a-z0-9_.:-]+$/), featureCatalogEntrySchema);
|
|
1749
|
+
var productCleanupPolicyModeSchema = z19.enum([
|
|
1770
1750
|
"report",
|
|
1771
1751
|
"pull_request"
|
|
1772
1752
|
]);
|
|
1773
|
-
var productChangeApprovalRiskSchema =
|
|
1753
|
+
var productChangeApprovalRiskSchema = z19.enum([
|
|
1774
1754
|
"safe",
|
|
1775
1755
|
"non_blocking",
|
|
1776
1756
|
"economic_risk",
|
|
1777
1757
|
"blocking"
|
|
1778
1758
|
]);
|
|
1779
|
-
var productOperatorPoliciesSchema =
|
|
1759
|
+
var productOperatorPoliciesSchema = z19.object({
|
|
1780
1760
|
/**
|
|
1781
1761
|
* Route cleanup operator. Disabled by default; report-mode is the safe
|
|
1782
1762
|
* default so a product can surface zero-traffic runtime-route candidates
|
|
1783
1763
|
* before it opts into draft PR mutation.
|
|
1784
1764
|
*/
|
|
1785
|
-
cleanup:
|
|
1786
|
-
enabled:
|
|
1765
|
+
cleanup: z19.object({
|
|
1766
|
+
enabled: z19.boolean().default(false),
|
|
1787
1767
|
mode: productCleanupPolicyModeSchema.default("report")
|
|
1788
1768
|
}).default({ enabled: false, mode: "report" }),
|
|
1789
1769
|
/**
|
|
@@ -1791,9 +1771,9 @@ var productOperatorPoliciesSchema = z18.object({
|
|
|
1791
1771
|
* the policy a first-class product-as-code field even while enforcement is
|
|
1792
1772
|
* still report/label-only.
|
|
1793
1773
|
*/
|
|
1794
|
-
change_approval:
|
|
1795
|
-
auto_merge_max_risk:
|
|
1796
|
-
require_human_for:
|
|
1774
|
+
change_approval: z19.object({
|
|
1775
|
+
auto_merge_max_risk: z19.enum(["none", "safe", "non_blocking"]).default("none"),
|
|
1776
|
+
require_human_for: z19.array(productChangeApprovalRiskSchema).default(["economic_risk", "blocking"])
|
|
1797
1777
|
}).default({
|
|
1798
1778
|
auto_merge_max_risk: "none",
|
|
1799
1779
|
require_human_for: ["economic_risk", "blocking"]
|
|
@@ -1805,15 +1785,15 @@ var productOperatorPoliciesSchema = z18.object({
|
|
|
1805
1785
|
require_human_for: ["economic_risk", "blocking"]
|
|
1806
1786
|
}
|
|
1807
1787
|
});
|
|
1808
|
-
var customerIdentityRequirementSchema =
|
|
1788
|
+
var customerIdentityRequirementSchema = z19.enum([
|
|
1809
1789
|
"org_only",
|
|
1810
1790
|
"org_and_user"
|
|
1811
1791
|
]);
|
|
1812
|
-
var customerPortalAuthStrategySchema =
|
|
1792
|
+
var customerPortalAuthStrategySchema = z19.enum([
|
|
1813
1793
|
"clerk",
|
|
1814
1794
|
"test-personas"
|
|
1815
1795
|
]);
|
|
1816
|
-
var productCustomerContextSchema =
|
|
1796
|
+
var productCustomerContextSchema = z19.object({
|
|
1817
1797
|
/**
|
|
1818
1798
|
* Edge credential identity policy. This is intentionally Product-scoped:
|
|
1819
1799
|
* B7 keeps Product as the product boundary and avoids customer-side
|
|
@@ -1825,19 +1805,19 @@ var productCustomerContextSchema = z18.object({
|
|
|
1825
1805
|
* runtime signing secret in product/product.config.ts. Core generates/preserves the
|
|
1826
1806
|
* secret when this is true and clears it when explicitly false.
|
|
1827
1807
|
*/
|
|
1828
|
-
context_tokens:
|
|
1829
|
-
enabled:
|
|
1808
|
+
context_tokens: z19.object({
|
|
1809
|
+
enabled: z19.boolean().default(true)
|
|
1830
1810
|
}).optional(),
|
|
1831
1811
|
/**
|
|
1832
1812
|
* Portal auth strategy for environment-scoped product applies. Production
|
|
1833
1813
|
* portal auth is provisioner-owned; preview/test environments can opt into
|
|
1834
1814
|
* test personas through Product-as-Code.
|
|
1835
1815
|
*/
|
|
1836
|
-
portal_auth:
|
|
1816
|
+
portal_auth: z19.object({
|
|
1837
1817
|
strategy: customerPortalAuthStrategySchema
|
|
1838
1818
|
}).optional()
|
|
1839
1819
|
});
|
|
1840
|
-
var productSurfaceTypeSchema =
|
|
1820
|
+
var productSurfaceTypeSchema = z19.enum([
|
|
1841
1821
|
"frontend",
|
|
1842
1822
|
"api",
|
|
1843
1823
|
"docs",
|
|
@@ -1847,84 +1827,84 @@ var productSurfaceTypeSchema = z18.enum([
|
|
|
1847
1827
|
"worker",
|
|
1848
1828
|
"agent"
|
|
1849
1829
|
]);
|
|
1850
|
-
var productSurfaceSchema =
|
|
1851
|
-
key:
|
|
1830
|
+
var productSurfaceSchema = z19.object({
|
|
1831
|
+
key: z19.string().min(1).max(64).regex(/^[a-z0-9_-]+$/, "Surface key must be lowercase alphanumeric with hyphens/underscores"),
|
|
1852
1832
|
type: productSurfaceTypeSchema,
|
|
1853
|
-
display:
|
|
1854
|
-
description:
|
|
1833
|
+
display: z19.string().min(1).max(100).optional(),
|
|
1834
|
+
description: z19.string().max(500).optional()
|
|
1855
1835
|
});
|
|
1856
|
-
var productEntitlementSchema =
|
|
1857
|
-
key:
|
|
1858
|
-
description:
|
|
1859
|
-
capabilities:
|
|
1860
|
-
featureGates:
|
|
1861
|
-
limits:
|
|
1862
|
-
meters:
|
|
1863
|
-
});
|
|
1864
|
-
var productSurfacesSchema =
|
|
1865
|
-
var productEntitlementsSchema =
|
|
1866
|
-
var productWorkflowKindSchema =
|
|
1836
|
+
var productEntitlementSchema = z19.object({
|
|
1837
|
+
key: z19.string().min(1).max(64).regex(/^[a-z0-9_-]+$/, "Entitlement key must be lowercase alphanumeric with hyphens/underscores"),
|
|
1838
|
+
description: z19.string().max(500).optional(),
|
|
1839
|
+
capabilities: z19.array(z19.string().min(1).max(100)).max(100).optional(),
|
|
1840
|
+
featureGates: z19.record(z19.string().min(1), z19.boolean()).optional(),
|
|
1841
|
+
limits: z19.array(planLimitRuleSchema).max(100).optional(),
|
|
1842
|
+
meters: z19.array(z19.string().min(1).max(64)).max(100).optional()
|
|
1843
|
+
});
|
|
1844
|
+
var productSurfacesSchema = z19.array(productSurfaceSchema).max(20).default([]);
|
|
1845
|
+
var productEntitlementsSchema = z19.array(productEntitlementSchema).max(100).default([]);
|
|
1846
|
+
var productWorkflowKindSchema = z19.enum([
|
|
1867
1847
|
"async_job",
|
|
1868
1848
|
"agent_task",
|
|
1869
1849
|
"scheduled",
|
|
1870
1850
|
"lifecycle",
|
|
1871
1851
|
"background"
|
|
1872
1852
|
]);
|
|
1873
|
-
var productWorkflowTriggerSchema =
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
type:
|
|
1877
|
-
cron:
|
|
1853
|
+
var productWorkflowTriggerSchema = z19.discriminatedUnion("type", [
|
|
1854
|
+
z19.object({ type: z19.literal("manual") }),
|
|
1855
|
+
z19.object({
|
|
1856
|
+
type: z19.literal("schedule"),
|
|
1857
|
+
cron: z19.string().min(1).max(120)
|
|
1878
1858
|
}),
|
|
1879
|
-
|
|
1880
|
-
type:
|
|
1881
|
-
event:
|
|
1859
|
+
z19.object({
|
|
1860
|
+
type: z19.literal("event"),
|
|
1861
|
+
event: z19.string().min(1).max(120)
|
|
1882
1862
|
}),
|
|
1883
|
-
|
|
1884
|
-
type:
|
|
1885
|
-
path:
|
|
1863
|
+
z19.object({
|
|
1864
|
+
type: z19.literal("api"),
|
|
1865
|
+
path: z19.string().min(1).max(240).regex(/^\//, "path must start with /")
|
|
1886
1866
|
}),
|
|
1887
|
-
|
|
1888
|
-
type:
|
|
1889
|
-
event:
|
|
1867
|
+
z19.object({
|
|
1868
|
+
type: z19.literal("lifecycle"),
|
|
1869
|
+
event: z19.string().min(1).max(120)
|
|
1890
1870
|
})
|
|
1891
1871
|
]);
|
|
1892
|
-
var productWorkflowSchema =
|
|
1893
|
-
key:
|
|
1894
|
-
title:
|
|
1895
|
-
description:
|
|
1872
|
+
var productWorkflowSchema = z19.object({
|
|
1873
|
+
key: z19.string().min(1).max(64).regex(/^[a-z0-9_-]+$/, "Workflow key must be lowercase alphanumeric with hyphens/underscores"),
|
|
1874
|
+
title: z19.string().min(1).max(120).optional(),
|
|
1875
|
+
description: z19.string().max(1e3).optional(),
|
|
1896
1876
|
kind: productWorkflowKindSchema,
|
|
1897
1877
|
trigger: productWorkflowTriggerSchema,
|
|
1898
|
-
capabilities:
|
|
1899
|
-
meters:
|
|
1900
|
-
estimates:
|
|
1901
|
-
metadata:
|
|
1902
|
-
});
|
|
1903
|
-
var productWorkflowsSchema =
|
|
1904
|
-
var productSpecSchema =
|
|
1905
|
-
product:
|
|
1906
|
-
name:
|
|
1907
|
-
displayName:
|
|
1908
|
-
description:
|
|
1909
|
-
baseUrl:
|
|
1910
|
-
sandboxBaseUrl:
|
|
1911
|
-
visibility:
|
|
1878
|
+
capabilities: z19.array(z19.string().min(1).max(100)).max(100).optional(),
|
|
1879
|
+
meters: z19.array(z19.string().min(1).max(64)).max(100).optional(),
|
|
1880
|
+
estimates: z19.record(z19.string().min(1).max(64), z19.number().finite()).optional(),
|
|
1881
|
+
metadata: z19.record(z19.string().min(1), z19.unknown()).optional()
|
|
1882
|
+
});
|
|
1883
|
+
var productWorkflowsSchema = z19.array(productWorkflowSchema).max(100).default([]);
|
|
1884
|
+
var productSpecSchema = z19.object({
|
|
1885
|
+
product: z19.object({
|
|
1886
|
+
name: z19.string().min(1).max(100),
|
|
1887
|
+
displayName: z19.string().max(200).optional(),
|
|
1888
|
+
description: z19.string().max(2e3).optional(),
|
|
1889
|
+
baseUrl: z19.string().url("baseUrl must be a valid URL"),
|
|
1890
|
+
sandboxBaseUrl: z19.string().url("sandboxBaseUrl must be a valid URL").optional(),
|
|
1891
|
+
visibility: z19.enum(["public", "private"]).default("public"),
|
|
1912
1892
|
// Branding
|
|
1913
|
-
logoUrl:
|
|
1914
|
-
primaryColor:
|
|
1893
|
+
logoUrl: z19.string().url().optional(),
|
|
1894
|
+
primaryColor: z19.string().regex(/^#[0-9a-fA-F]{6}$/).optional(),
|
|
1915
1895
|
// Environment
|
|
1916
|
-
envBranchPrefix:
|
|
1896
|
+
envBranchPrefix: z19.string().max(50).nullable().optional()
|
|
1917
1897
|
}),
|
|
1918
|
-
gateway:
|
|
1919
|
-
authHeader:
|
|
1920
|
-
upstreamAuth:
|
|
1921
|
-
type:
|
|
1922
|
-
token:
|
|
1898
|
+
gateway: z19.object({
|
|
1899
|
+
authHeader: z19.string().min(1).max(100).default("x-api-key"),
|
|
1900
|
+
upstreamAuth: z19.object({
|
|
1901
|
+
type: z19.enum(["none", "static_bearer"]),
|
|
1902
|
+
token: z19.string().optional()
|
|
1923
1903
|
}).default({ type: "none" })
|
|
1924
1904
|
}),
|
|
1925
|
-
metering:
|
|
1926
|
-
meters:
|
|
1927
|
-
billOn4xx:
|
|
1905
|
+
metering: z19.object({
|
|
1906
|
+
meters: z19.array(meterDefinitionSchema).max(10).default([]),
|
|
1907
|
+
billOn4xx: z19.boolean().default(false)
|
|
1928
1908
|
}).default({ meters: [], billOn4xx: false }),
|
|
1929
1909
|
/**
|
|
1930
1910
|
* BYO-Backend V1 — first-class backend declarations, keyed by backend id.
|
|
@@ -1939,7 +1919,7 @@ var productSpecSchema = z18.object({
|
|
|
1939
1919
|
*/
|
|
1940
1920
|
backend: productBackendBlockSchema.optional(),
|
|
1941
1921
|
usage: usageBlockSchema.optional(),
|
|
1942
|
-
usagePricing:
|
|
1922
|
+
usagePricing: z19.never({
|
|
1943
1923
|
error: "usagePricing is not supported. Define usage.meters.<key>.rating instead."
|
|
1944
1924
|
}).optional(),
|
|
1945
1925
|
features: featureCatalogSchema.optional(),
|
|
@@ -1960,53 +1940,51 @@ var productSpecSchema = z18.object({
|
|
|
1960
1940
|
// billing shape via the unified 5-knob spec (`meters`,
|
|
1961
1941
|
// `recurring_fee_cents`, `recurring_credit_grant_cents`,
|
|
1962
1942
|
// `one_time_credit_grant_cents`, `trial_days`,
|
|
1963
|
-
// `max_monthly_spend_cents`). The product-level `billing` block
|
|
1964
|
-
//
|
|
1965
|
-
// `
|
|
1966
|
-
|
|
1967
|
-
|
|
1943
|
+
// `max_monthly_spend_cents`). The product-level `billing` block retains
|
|
1944
|
+
// only the live transition-policy fields. v(refactor) — the inert
|
|
1945
|
+
// `gracePeriodDays` knob was deleted (the compiler hardcoded it and
|
|
1946
|
+
// Stripe Smart Retries owns dunning); the block now carries the
|
|
1947
|
+
// subscriber-change timing policy + the limit-upgrade projection flag.
|
|
1948
|
+
billing: z19.object({
|
|
1968
1949
|
// When true (default), a plan limit INCREASE re-projects onto active
|
|
1969
1950
|
// subscribers immediately; a DECREASE always defers to period end.
|
|
1970
1951
|
// Read by advanceActiveSubscribersToLatestCompiledPlans.
|
|
1971
|
-
applyLimitUpgradesInstantly:
|
|
1952
|
+
applyLimitUpgradesInstantly: z19.boolean().optional(),
|
|
1972
1953
|
subscriberChangePolicy: subscriberChangePolicySchema.default({
|
|
1973
|
-
default: "
|
|
1974
|
-
proration: "none",
|
|
1954
|
+
default: "period_end",
|
|
1975
1955
|
when: {
|
|
1976
|
-
price_increase: "
|
|
1977
|
-
price_decrease: "
|
|
1978
|
-
feature_added: "
|
|
1979
|
-
feature_removed: "
|
|
1980
|
-
limit_increased: "
|
|
1981
|
-
limit_reduced: "
|
|
1982
|
-
credit_increased: "
|
|
1983
|
-
credit_reduced: "
|
|
1984
|
-
rating_changed: "
|
|
1956
|
+
price_increase: "period_end",
|
|
1957
|
+
price_decrease: "immediate",
|
|
1958
|
+
feature_added: "immediate",
|
|
1959
|
+
feature_removed: "period_end",
|
|
1960
|
+
limit_increased: "immediate",
|
|
1961
|
+
limit_reduced: "period_end",
|
|
1962
|
+
credit_increased: "immediate",
|
|
1963
|
+
credit_reduced: "period_end",
|
|
1964
|
+
rating_changed: "period_end"
|
|
1985
1965
|
},
|
|
1986
1966
|
allowImmediatePriceIncrease: false,
|
|
1987
1967
|
allowImmediateEntitlementReduction: false
|
|
1988
1968
|
})
|
|
1989
1969
|
}).default({
|
|
1990
|
-
gracePeriodDays: 3,
|
|
1991
1970
|
subscriberChangePolicy: {
|
|
1992
|
-
default: "
|
|
1993
|
-
proration: "none",
|
|
1971
|
+
default: "period_end",
|
|
1994
1972
|
when: {
|
|
1995
|
-
price_increase: "
|
|
1996
|
-
price_decrease: "
|
|
1997
|
-
feature_added: "
|
|
1998
|
-
feature_removed: "
|
|
1999
|
-
limit_increased: "
|
|
2000
|
-
limit_reduced: "
|
|
2001
|
-
credit_increased: "
|
|
2002
|
-
credit_reduced: "
|
|
2003
|
-
rating_changed: "
|
|
1973
|
+
price_increase: "period_end",
|
|
1974
|
+
price_decrease: "immediate",
|
|
1975
|
+
feature_added: "immediate",
|
|
1976
|
+
feature_removed: "period_end",
|
|
1977
|
+
limit_increased: "immediate",
|
|
1978
|
+
limit_reduced: "period_end",
|
|
1979
|
+
credit_increased: "immediate",
|
|
1980
|
+
credit_reduced: "period_end",
|
|
1981
|
+
rating_changed: "period_end"
|
|
2004
1982
|
},
|
|
2005
1983
|
allowImmediatePriceIncrease: false,
|
|
2006
1984
|
allowImmediateEntitlementReduction: false
|
|
2007
1985
|
}
|
|
2008
1986
|
}),
|
|
2009
|
-
plans:
|
|
1987
|
+
plans: z19.array(planSpecSchema).max(4).default([]),
|
|
2010
1988
|
/**
|
|
2011
1989
|
* Add-on catalog (v0.56+). Composable economic + entitlement
|
|
2012
1990
|
* overlays that subscribers can pile on top of their base plan.
|
|
@@ -2049,16 +2027,16 @@ var productSpecSchema = z18.object({
|
|
|
2049
2027
|
* require_deprecation_window_days: 90
|
|
2050
2028
|
* require_successor_route: true
|
|
2051
2029
|
*/
|
|
2052
|
-
lifecycle:
|
|
2053
|
-
breaking_changes:
|
|
2030
|
+
lifecycle: z19.object({
|
|
2031
|
+
breaking_changes: z19.object({
|
|
2054
2032
|
/** Minimum days a route must have been marked for removal
|
|
2055
2033
|
* (in main-branch YAML) before the publish gate will let
|
|
2056
2034
|
* it actually be removed. Set to 0 to disable. */
|
|
2057
|
-
require_deprecation_window_days:
|
|
2035
|
+
require_deprecation_window_days: z19.number().int().nonnegative().default(0),
|
|
2058
2036
|
/** When true, a route removal must declare a successor
|
|
2059
2037
|
* route via the lifecycle metadata (mechanics in core
|
|
2060
2038
|
* 3b-2) before the publish gate accepts it. */
|
|
2061
|
-
require_successor_route:
|
|
2039
|
+
require_successor_route: z19.boolean().default(false)
|
|
2062
2040
|
}).default({
|
|
2063
2041
|
require_deprecation_window_days: 0,
|
|
2064
2042
|
require_successor_route: false
|
|
@@ -2104,8 +2082,8 @@ var productSpecSchema = z18.object({
|
|
|
2104
2082
|
* (preserves today's behaviour). Compiler validation pins the
|
|
2105
2083
|
* value to a real `plans[].key`.
|
|
2106
2084
|
*/
|
|
2107
|
-
ephemeral:
|
|
2108
|
-
defaultPlan:
|
|
2085
|
+
ephemeral: z19.object({
|
|
2086
|
+
defaultPlan: z19.string().min(1).optional()
|
|
2109
2087
|
}).optional()
|
|
2110
2088
|
}).superRefine((spec, ctx) => {
|
|
2111
2089
|
rejectUsagePricing(spec, ctx);
|
|
@@ -2117,24 +2095,24 @@ var productSpecSchema = z18.object({
|
|
|
2117
2095
|
validateLimitMeterReachability(spec, ctx);
|
|
2118
2096
|
validateBackendReferences(spec, ctx);
|
|
2119
2097
|
});
|
|
2120
|
-
var productPhaseSchema =
|
|
2098
|
+
var productPhaseSchema = z19.object({
|
|
2121
2099
|
product: productSpecSchema.shape.product
|
|
2122
2100
|
});
|
|
2123
|
-
var gatewayPhaseSchema =
|
|
2101
|
+
var gatewayPhaseSchema = z19.object({
|
|
2124
2102
|
gateway: productSpecSchema.shape.gateway
|
|
2125
2103
|
});
|
|
2126
|
-
var meteringPhaseSchema =
|
|
2104
|
+
var meteringPhaseSchema = z19.object({
|
|
2127
2105
|
metering: productSpecSchema.shape.metering
|
|
2128
2106
|
});
|
|
2129
|
-
var plansPhaseSchema =
|
|
2107
|
+
var plansPhaseSchema = z19.object({
|
|
2130
2108
|
plans: productSpecSchema.shape.plans
|
|
2131
2109
|
});
|
|
2132
2110
|
|
|
2133
2111
|
// ../contracts/dist/plans/spec/capabilities-layer.js
|
|
2134
|
-
import { z as
|
|
2135
|
-
var capabilityLayerSchema =
|
|
2136
|
-
capability:
|
|
2137
|
-
description:
|
|
2112
|
+
import { z as z20 } from "zod";
|
|
2113
|
+
var capabilityLayerSchema = z20.object({
|
|
2114
|
+
capability: z20.string().min(1).max(120).regex(/^[a-z0-9_-]+$/, "capability name must be lowercase alphanumeric with hyphens/underscores"),
|
|
2115
|
+
description: z20.string().max(500).optional(),
|
|
2138
2116
|
/**
|
|
2139
2117
|
* Capability composition is contractual by default — including a new
|
|
2140
2118
|
* feature changes the customer's effective entitlement. Mark
|
|
@@ -2142,28 +2120,28 @@ var capabilityLayerSchema = z19.object({
|
|
|
2142
2120
|
* (e.g. an internal "monitoring" capability that gates dashboard
|
|
2143
2121
|
* pages without affecting billable behaviour).
|
|
2144
2122
|
*/
|
|
2145
|
-
mutation_class:
|
|
2146
|
-
includes_features:
|
|
2147
|
-
includes_policies:
|
|
2148
|
-
includes_capabilities:
|
|
2123
|
+
mutation_class: z20.enum(["runtime", "contractual"]).default("contractual"),
|
|
2124
|
+
includes_features: z20.array(z20.string().min(1).max(100).regex(/^[a-z0-9_.:-]+$/)).max(20).default([]),
|
|
2125
|
+
includes_policies: z20.array(z20.string().min(1).max(64).regex(/^[a-z0-9_-]+$/)).max(20).default([]),
|
|
2126
|
+
includes_capabilities: z20.array(z20.string().min(1).max(120).regex(/^[a-z0-9_-]+$/)).max(20).default([])
|
|
2149
2127
|
});
|
|
2150
2128
|
|
|
2151
2129
|
// ../contracts/dist/plans/spec/manifest-ir.js
|
|
2152
2130
|
import { createHash } from "node:crypto";
|
|
2153
|
-
import { z as
|
|
2131
|
+
import { z as z21 } from "zod";
|
|
2154
2132
|
var MANIFEST_IR_VERSION = 1;
|
|
2155
|
-
var manifestIrSchema =
|
|
2156
|
-
irVersion:
|
|
2133
|
+
var manifestIrSchema = z21.object({
|
|
2134
|
+
irVersion: z21.literal(MANIFEST_IR_VERSION),
|
|
2157
2135
|
/** Version of @farthershore/product that emitted this envelope. */
|
|
2158
|
-
sdkVersion:
|
|
2136
|
+
sdkVersion: z21.string().min(1).max(64),
|
|
2159
2137
|
/** Legacy unified ProductSpec — the live `CompileProductOptions.sourceSpec`. */
|
|
2160
2138
|
product: productSpecSchema,
|
|
2161
2139
|
/** One entry per feature, sorted by `feature`. */
|
|
2162
|
-
routes:
|
|
2140
|
+
routes: z21.array(routeLayerSchema).max(200).default([]),
|
|
2163
2141
|
/** Sorted by `name`. */
|
|
2164
|
-
policies:
|
|
2142
|
+
policies: z21.array(policyLayerSchema).max(200).default([]),
|
|
2165
2143
|
/** Sorted by `capability`. */
|
|
2166
|
-
capabilities:
|
|
2144
|
+
capabilities: z21.array(capabilityLayerSchema).max(200).default([])
|
|
2167
2145
|
}).strict();
|
|
2168
2146
|
function canonicalManifestJson(value) {
|
|
2169
2147
|
return stableJson(JSON.parse(JSON.stringify(value)));
|
|
@@ -2203,7 +2181,7 @@ var STARTER = {
|
|
|
2203
2181
|
],
|
|
2204
2182
|
recurring_fee_cents: 2e3,
|
|
2205
2183
|
billing_interval: "month",
|
|
2206
|
-
grants: [{ kind: "
|
|
2184
|
+
grants: [{ kind: "credit", amount_cents: 2e3, recurs: true }],
|
|
2207
2185
|
trial_days: 0
|
|
2208
2186
|
}
|
|
2209
2187
|
};
|
|
@@ -2217,7 +2195,7 @@ var PRO = {
|
|
|
2217
2195
|
],
|
|
2218
2196
|
recurring_fee_cents: 1e4,
|
|
2219
2197
|
billing_interval: "month",
|
|
2220
|
-
grants: [{ kind: "
|
|
2198
|
+
grants: [{ kind: "credit", amount_cents: 2e4, recurs: true }],
|
|
2221
2199
|
trial_days: 14
|
|
2222
2200
|
}
|
|
2223
2201
|
};
|
|
@@ -2231,7 +2209,7 @@ var PREPAID = {
|
|
|
2231
2209
|
],
|
|
2232
2210
|
recurring_fee_cents: 0,
|
|
2233
2211
|
billing_interval: "month",
|
|
2234
|
-
grants: [{ kind: "
|
|
2212
|
+
grants: [{ kind: "credit", amount_cents: 5e3, recurs: false }],
|
|
2235
2213
|
trial_days: 0
|
|
2236
2214
|
}
|
|
2237
2215
|
};
|
|
@@ -2258,28 +2236,31 @@ var PLAN_PRESETS = Object.freeze({
|
|
|
2258
2236
|
});
|
|
2259
2237
|
|
|
2260
2238
|
// ../contracts/dist/plans/subscription-pricing-override.js
|
|
2261
|
-
import { z as
|
|
2262
|
-
var subscriptionPricingOverrideSchema =
|
|
2239
|
+
import { z as z22 } from "zod";
|
|
2240
|
+
var subscriptionPricingOverrideSchema = z22.object({
|
|
2263
2241
|
/** Override the plan's recurring fee for this subscriber. */
|
|
2264
|
-
recurring_fee_cents:
|
|
2242
|
+
recurring_fee_cents: z22.number().int().nonnegative().optional(),
|
|
2265
2243
|
/**
|
|
2266
2244
|
* Override the plan's credit grants. `grants[]` is the single credit
|
|
2267
2245
|
* surface — when present, it fully replaces the plan's grants for this
|
|
2268
2246
|
* subscriber (recurring + one-time credit are canonical entries here;
|
|
2269
2247
|
* the legacy recurring/one-time scalar knobs were removed).
|
|
2270
2248
|
*/
|
|
2271
|
-
grants:
|
|
2249
|
+
grants: z22.array(grantSchema).max(40).optional(),
|
|
2250
|
+
/** Override the plan's credit-policy block (rollover + auto-recharge)
|
|
2251
|
+
* for this subscriber. */
|
|
2252
|
+
creditPolicy: creditPolicySchema.optional(),
|
|
2272
2253
|
/** Override the minimum-spend floor. */
|
|
2273
|
-
min_monthly_spend_cents:
|
|
2254
|
+
min_monthly_spend_cents: z22.number().int().nonnegative().optional(),
|
|
2274
2255
|
/** Override the maximum-spend ceiling. */
|
|
2275
|
-
max_monthly_spend_cents:
|
|
2256
|
+
max_monthly_spend_cents: z22.number().int().nonnegative().optional(),
|
|
2276
2257
|
/** Replace the entire meter list for this subscriber. When set, the
|
|
2277
2258
|
* full plan meter array is replaced (not merged) — call it explicit
|
|
2278
2259
|
* rather than implicit so a deal can also REMOVE billable meters,
|
|
2279
2260
|
* not just adjust rates. */
|
|
2280
|
-
meters:
|
|
2261
|
+
meters: z22.array(meterSchema).optional(),
|
|
2281
2262
|
/** Free-text notes about the deal. Surfaced in admin UI for audit. */
|
|
2282
|
-
notes:
|
|
2263
|
+
notes: z22.string().max(2e3).optional()
|
|
2283
2264
|
});
|
|
2284
2265
|
|
|
2285
2266
|
// src/validate.ts
|
|
@@ -2305,7 +2286,7 @@ function hashIr(ir) {
|
|
|
2305
2286
|
}
|
|
2306
2287
|
|
|
2307
2288
|
// src/version.ts
|
|
2308
|
-
var SDK_VERSION = true ? "0.6.
|
|
2289
|
+
var SDK_VERSION = true ? "0.6.1" : "0.0.0-dev";
|
|
2309
2290
|
|
|
2310
2291
|
// src/refs.ts
|
|
2311
2292
|
function isCapabilityGrant(value) {
|
|
@@ -3061,6 +3042,7 @@ function buildPlanSpec(key, options) {
|
|
|
3061
3042
|
} : {},
|
|
3062
3043
|
...options.meters ? { meters: options.meters } : {},
|
|
3063
3044
|
...creditGrants.length ? { grants: creditGrants } : {},
|
|
3045
|
+
...options.creditPolicy ? { creditPolicy: options.creditPolicy } : {},
|
|
3064
3046
|
...options.trialDays !== void 0 ? { trial_days: options.trialDays } : {},
|
|
3065
3047
|
...options.maxMonthlySpendCents !== void 0 ? { max_monthly_spend_cents: options.maxMonthlySpendCents } : {},
|
|
3066
3048
|
...options.minMonthlySpendCents !== void 0 ? { min_monthly_spend_cents: options.minMonthlySpendCents } : {},
|