@revturbine/cli 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/dist/cli.js +3068 -335
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -52,9 +52,9 @@ var DataClassification = {
|
|
|
52
52
|
Unrestricted: { "x-revturbine-data-classification": "unrestricted" }
|
|
53
53
|
};
|
|
54
54
|
var READ_ONLY_META_KEY = "readOnly";
|
|
55
|
-
function toWritableSchema(
|
|
55
|
+
function toWritableSchema(schema) {
|
|
56
56
|
const writableShape = {};
|
|
57
|
-
for (const [fieldName, fieldSchema] of Object.entries(
|
|
57
|
+
for (const [fieldName, fieldSchema] of Object.entries(schema.shape)) {
|
|
58
58
|
if (typeof fieldSchema !== "object" || fieldSchema === null) {
|
|
59
59
|
continue;
|
|
60
60
|
}
|
|
@@ -65,8 +65,8 @@ function toWritableSchema(schema2) {
|
|
|
65
65
|
}
|
|
66
66
|
return z.object(writableShape);
|
|
67
67
|
}
|
|
68
|
-
function toCreateSchema(
|
|
69
|
-
const writable = toWritableSchema(
|
|
68
|
+
function toCreateSchema(schema) {
|
|
69
|
+
const writable = toWritableSchema(schema);
|
|
70
70
|
const handleField = writable.shape["handle"];
|
|
71
71
|
if (handleField && typeof handleField.regex === "function") {
|
|
72
72
|
return writable.extend({ handle: handleField.regex(HANDLE_PATTERN) });
|
|
@@ -375,6 +375,11 @@ var PlanSchema = IdField.merge(TimestampFields).merge(TenantIdField).merge(Ancho
|
|
|
375
375
|
handle: HandleField.meta(Unrestricted2),
|
|
376
376
|
tier_position: z5.number().int().min(0).default(0).meta(Unrestricted2),
|
|
377
377
|
sort_order: z5.number().int().default(0).meta(Unrestricted2),
|
|
378
|
+
// Plan-level visibility default (plan 91 Part B). Distinct from the
|
|
379
|
+
// per-variation `PlanVariationSchema.visibility`: a free/custom tier with no
|
|
380
|
+
// priced variation can still be unlisted/legacy. Persisted so it round-trips
|
|
381
|
+
// (plan 146 found it was declared on the portable config but had no column).
|
|
382
|
+
visibility: PlanVisibilitySchema.default("public").meta(Unrestricted2),
|
|
378
383
|
metadata: MetadataField.meta(Unrestricted2)
|
|
379
384
|
}).meta(
|
|
380
385
|
{
|
|
@@ -415,6 +420,9 @@ var AddOnSchema = IdField.merge(TimestampFields).merge(TenantIdField).merge(Anch
|
|
|
415
420
|
name: NameField.meta(Unrestricted2),
|
|
416
421
|
handle: HandleField.meta(Unrestricted2),
|
|
417
422
|
sort_order: z5.number().int().default(0).meta(Unrestricted2),
|
|
423
|
+
// Add-on visibility default — same rationale as PlanSchema (plan 91 Part B);
|
|
424
|
+
// metadata, not price, so it lives on the add-on independent of variations.
|
|
425
|
+
visibility: PlanVisibilitySchema.default("public").meta(Unrestricted2),
|
|
418
426
|
metadata: MetadataField.meta(Unrestricted2)
|
|
419
427
|
}).meta(
|
|
420
428
|
{
|
|
@@ -862,97 +870,9 @@ var EntitlementRuleTargetSchema = z6.object({
|
|
|
862
870
|
}).meta(
|
|
863
871
|
{ id: "EntitlementRuleTarget", "x-revturbine-schema-persistence": Transient3, "x-revturbine-schema-exposure": External3 }
|
|
864
872
|
);
|
|
865
|
-
var EntitlementRulePeriodUnitSchema = z6.enum(["month", "day", "week", "quarter", "year", "billing_period", "
|
|
873
|
+
var EntitlementRulePeriodUnitSchema = z6.enum(["month", "day", "week", "quarter", "year", "billing_period", "hour", "six_hours"]).meta(
|
|
866
874
|
{ id: "EntitlementRulePeriodUnit", "x-revturbine-schema-persistence": Transient3, "x-revturbine-schema-exposure": External3 }
|
|
867
875
|
);
|
|
868
|
-
var LimitValueField = z6.union([z6.number(), z6.literal("unlimited")]).nullable().optional();
|
|
869
|
-
var EntitlementTypeFieldsFeatureSchema = z6.looseObject({
|
|
870
|
-
kind: z6.literal("feature").meta(Unrestricted3),
|
|
871
|
-
enabled: z6.boolean().optional().meta(Unrestricted3)
|
|
872
|
-
}).meta(
|
|
873
|
-
{ id: "EntitlementTypeFieldsFeature", "x-revturbine-schema-persistence": Transient3, "x-revturbine-schema-exposure": External3 }
|
|
874
|
-
);
|
|
875
|
-
var EntitlementTypeFieldsCapabilityTierSchema = z6.looseObject({
|
|
876
|
-
kind: z6.literal("capability_tier").meta(Unrestricted3),
|
|
877
|
-
tier_name: z6.string().optional().meta(Unrestricted3),
|
|
878
|
-
tier_description: z6.string().optional().meta(Unrestricted3)
|
|
879
|
-
}).meta(
|
|
880
|
-
{ id: "EntitlementTypeFieldsCapabilityTier", "x-revturbine-schema-persistence": Transient3, "x-revturbine-schema-exposure": External3 }
|
|
881
|
-
);
|
|
882
|
-
var EntitlementTypeFieldsUsageLimitSchema = z6.looseObject({
|
|
883
|
-
kind: z6.literal("usage_limit").meta(Unrestricted3),
|
|
884
|
-
limit_value: LimitValueField.meta(Unrestricted3),
|
|
885
|
-
unit: z6.string().optional().meta(Unrestricted3),
|
|
886
|
-
period: UsagePeriodScopeSchema.optional().meta(Unrestricted3),
|
|
887
|
-
enforcement: EnforcementModeSchema.optional().meta(Unrestricted3)
|
|
888
|
-
}).meta(
|
|
889
|
-
{ id: "EntitlementTypeFieldsUsageLimit", "x-revturbine-schema-persistence": Transient3, "x-revturbine-schema-exposure": External3 }
|
|
890
|
-
);
|
|
891
|
-
var EntitlementTypeFieldsPricePerUnitSchema = z6.looseObject({
|
|
892
|
-
kind: z6.literal("price_per_unit").meta(Unrestricted3),
|
|
893
|
-
amount_cents: z6.number().optional().meta(Unrestricted3),
|
|
894
|
-
currency: z6.string().optional().meta(Unrestricted3),
|
|
895
|
-
unit: z6.string().optional().meta(Unrestricted3),
|
|
896
|
-
period: UsagePeriodScopeSchema.optional().meta(Unrestricted3)
|
|
897
|
-
}).meta(
|
|
898
|
-
{ id: "EntitlementTypeFieldsPricePerUnit", "x-revturbine-schema-persistence": Transient3, "x-revturbine-schema-exposure": External3 }
|
|
899
|
-
);
|
|
900
|
-
var EntitlementTypeFieldsRateLimitSchema = z6.looseObject({
|
|
901
|
-
kind: z6.literal("rate_limit").meta(Unrestricted3),
|
|
902
|
-
rate_value: z6.number().optional().meta(Unrestricted3),
|
|
903
|
-
period_scope: UsagePeriodScopeSchema.optional().meta(Unrestricted3),
|
|
904
|
-
enforcement: EnforcementModeSchema.optional().meta(Unrestricted3)
|
|
905
|
-
}).meta(
|
|
906
|
-
{ id: "EntitlementTypeFieldsRateLimit", "x-revturbine-schema-persistence": Transient3, "x-revturbine-schema-exposure": External3 }
|
|
907
|
-
);
|
|
908
|
-
var EntitlementTypeFieldsCreditsSchema = z6.looseObject({
|
|
909
|
-
kind: z6.literal("credits").meta(Unrestricted3),
|
|
910
|
-
allowance: LimitValueField.meta(Unrestricted3),
|
|
911
|
-
initial_grant: z6.number().optional().meta(Unrestricted3),
|
|
912
|
-
unit: z6.string().optional().meta(Unrestricted3),
|
|
913
|
-
// Wire cadence uses the `per_*` vocabulary; the persisted `reset_period`
|
|
914
|
-
// column uses the bare vocabulary (`month`). Both are declared so the
|
|
915
|
-
// translation gap is visible to the contract instead of silent (plan 109).
|
|
916
|
-
period: UsagePeriodScopeSchema.optional().meta(Unrestricted3),
|
|
917
|
-
reset_period: EntitlementRulePeriodUnitSchema.optional().meta(Unrestricted3),
|
|
918
|
-
rollover: z6.boolean().optional().meta(Unrestricted3),
|
|
919
|
-
// `max_balance` — the maximum balance allowed at any time — is the ONLY
|
|
920
|
-
// credits cap in the model (Kent, 2026-07-22). The older `max_rollover`
|
|
921
|
-
// (how much unused balance carried into the next period) was folded away
|
|
922
|
-
// when plan 28 REQ-13 renamed the column, and authored configs have since
|
|
923
|
-
// been migrated off it. Do not re-add it: it has no column, IR field, or
|
|
924
|
-
// bundle slot — plan 37 retired its FlatBuffer slot to a -1 sentinel — so
|
|
925
|
-
// declaring it would promise a round-trip the model cannot honour.
|
|
926
|
-
max_balance: LimitValueField.meta(Unrestricted3),
|
|
927
|
-
top_up_available: z6.boolean().optional().meta(Unrestricted3)
|
|
928
|
-
}).meta(
|
|
929
|
-
{ id: "EntitlementTypeFieldsCredits", "x-revturbine-schema-persistence": Transient3, "x-revturbine-schema-exposure": External3 }
|
|
930
|
-
);
|
|
931
|
-
var EntitlementTypeFieldsSeatSchema = z6.looseObject({
|
|
932
|
-
kind: z6.literal("seat").meta(Unrestricted3),
|
|
933
|
-
included_seats: z6.union([z6.number(), z6.literal("unlimited")]).optional().meta(Unrestricted3),
|
|
934
|
-
// Authored configs already carry `null` here; plan 72 treats null as the
|
|
935
|
-
// canonical "unlimited". The 999999 sentinel is an export-side artifact.
|
|
936
|
-
max_seats: z6.union([z6.number(), z6.literal("unlimited")]).nullable().optional().meta(Unrestricted3),
|
|
937
|
-
seat_type: z6.string().optional().meta(Unrestricted3)
|
|
938
|
-
}).meta(
|
|
939
|
-
{ id: "EntitlementTypeFieldsSeat", "x-revturbine-schema-persistence": Transient3, "x-revturbine-schema-exposure": External3 }
|
|
940
|
-
);
|
|
941
|
-
var EntitlementTypeFieldsUntypedSchema = z6.looseObject({}).meta(
|
|
942
|
-
{ id: "EntitlementTypeFieldsUntyped", "x-revturbine-schema-persistence": Transient3, "x-revturbine-schema-exposure": External3 }
|
|
943
|
-
);
|
|
944
|
-
var ENTITLEMENT_TYPE_FIELDS_VARIANTS = [
|
|
945
|
-
EntitlementTypeFieldsFeatureSchema,
|
|
946
|
-
EntitlementTypeFieldsCapabilityTierSchema,
|
|
947
|
-
EntitlementTypeFieldsUsageLimitSchema,
|
|
948
|
-
EntitlementTypeFieldsPricePerUnitSchema,
|
|
949
|
-
EntitlementTypeFieldsRateLimitSchema,
|
|
950
|
-
EntitlementTypeFieldsCreditsSchema,
|
|
951
|
-
EntitlementTypeFieldsSeatSchema
|
|
952
|
-
];
|
|
953
|
-
var EntitlementTypeFieldsSchema = z6.union([...ENTITLEMENT_TYPE_FIELDS_VARIANTS, EntitlementTypeFieldsUntypedSchema]).meta(
|
|
954
|
-
{ id: "EntitlementTypeFields", "x-revturbine-schema-persistence": Transient3, "x-revturbine-schema-exposure": External3 }
|
|
955
|
-
);
|
|
956
876
|
var EntitlementSchema = IdField.merge(TimestampFields).merge(TenantIdField).merge(AnchorFields).merge(VersionFields).extend({
|
|
957
877
|
anchor_id: z6.string().min(1).meta({ ...Unrestricted3, readOnly: true }),
|
|
958
878
|
name: NameField.meta(Unrestricted3),
|
|
@@ -995,8 +915,11 @@ var EntitlementRuleSchema = IdField.merge(TimestampFields).merge(TenantIdField).
|
|
|
995
915
|
period_scope: UsagePeriodScopeSchema.optional().meta(Unrestricted3),
|
|
996
916
|
// Optional instance label, surfaced when `period_scope = 'per_instance'` (F-1).
|
|
997
917
|
instance: z6.string().max(100).optional().meta(Unrestricted3),
|
|
998
|
-
// Credits reset cadence ("refills every")
|
|
999
|
-
//
|
|
918
|
+
// Credits reset cadence ("refills every"): governs only the per-period
|
|
919
|
+
// `allowance_value` refill. `billing_period` resolves at runtime to the
|
|
920
|
+
// customer's Variation billing period; structural guard below. Absent means
|
|
921
|
+
// one-time only — an `initial_grant` with no recurring refill (plan 147
|
|
922
|
+
// REQ-6; `on_purchase` retired from the enum).
|
|
1000
923
|
reset_period: EntitlementRulePeriodUnitSchema.optional().meta(Unrestricted3),
|
|
1001
924
|
// Type-specific fields (populated based on entitlement type)
|
|
1002
925
|
limit_value: z6.union([z6.number(), z6.literal("unlimited")]).optional().meta(Unrestricted3),
|
|
@@ -1020,7 +943,30 @@ var EntitlementRuleSchema = IdField.merge(TimestampFields).merge(TenantIdField).
|
|
|
1020
943
|
seat_type_id: z6.string().optional().meta(Unrestricted3),
|
|
1021
944
|
included_count: z6.union([z6.number().int(), z6.literal("unlimited")]).optional().meta(Unrestricted3),
|
|
1022
945
|
at_limit_behavior: z6.enum(["hard_cap", "auto_upgrade_at_renewal"]).optional().meta(Unrestricted3),
|
|
1023
|
-
stripe_metered_price_id: z6.string().optional().meta(Unrestricted3)
|
|
946
|
+
stripe_metered_price_id: z6.string().optional().meta(Unrestricted3),
|
|
947
|
+
// ── Plan 147: columns promoted from the deleted portable `type_fields` union
|
|
948
|
+
// so the flat wire projection round-trips (REQ-3 persist verdicts). The wire
|
|
949
|
+
// (RevTurbineConfigEntitlementRulesItemSchema) mirrors these one-to-one under
|
|
950
|
+
// their canonical names; web derives `kind`/`unit`/`tier_*` from the parent
|
|
951
|
+
// entitlement instead of persisting them.
|
|
952
|
+
//
|
|
953
|
+
// feature enable/disable: a present-but-disabled feature rule is a real DENY
|
|
954
|
+
// (`entitlement-check.ts` reads `enabled !== false`), distinct from "no rule".
|
|
955
|
+
// Optional (absent = enabled) matches that evaluator semantic — replaces the
|
|
956
|
+
// hard-coded `true` the export used to emit.
|
|
957
|
+
enabled: z6.boolean().optional().meta(Unrestricted3),
|
|
958
|
+
// How this rule partitions usage across the identity hierarchy. Rule-level in
|
|
959
|
+
// the IR (`encode.ts` maps `r.allocation`); may default from the parent
|
|
960
|
+
// entitlement's `allocation` when unset.
|
|
961
|
+
allocation: UsageAllocationSchema.optional().meta(Unrestricted3),
|
|
962
|
+
// Seat ceiling. null = unlimited (plan 72); the 999999 export sentinel maps
|
|
963
|
+
// back to null at the compile boundary.
|
|
964
|
+
max_seats: z6.union([z6.number(), z6.literal("unlimited")]).nullable().optional().meta(Unrestricted3),
|
|
965
|
+
// price_per_unit content-rendering fields (OQ-7: persisted-and-rendered, NOT
|
|
966
|
+
// evaluated — no IR/evaluator branch). `unit` derives from the entitlement;
|
|
967
|
+
// `period` maps onto the existing `period_scope` column.
|
|
968
|
+
amount_cents: z6.number().int().optional().meta(Unrestricted3),
|
|
969
|
+
currency: z6.string().optional().meta(Unrestricted3)
|
|
1024
970
|
}).meta(
|
|
1025
971
|
{
|
|
1026
972
|
id: "EntitlementRule",
|
|
@@ -1050,6 +996,16 @@ var EntitlementRuleValidatedSchema = EntitlementRuleSchema.superRefine(
|
|
|
1050
996
|
});
|
|
1051
997
|
}
|
|
1052
998
|
}
|
|
999
|
+
const allowance = rule.allowance_value;
|
|
1000
|
+
const hasRecurringAllowance = allowance === "unlimited" || typeof allowance === "number" && allowance > 0;
|
|
1001
|
+
if (hasRecurringAllowance && rule.reset_period == null) {
|
|
1002
|
+
ctx.addIssue({
|
|
1003
|
+
code: "custom",
|
|
1004
|
+
path: ["reset_period"],
|
|
1005
|
+
params: { code: "allowance_requires_reset_period" },
|
|
1006
|
+
message: "a non-zero credits allowance_value requires a reset_period (the per-period refill cadence)"
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1053
1009
|
}
|
|
1054
1010
|
);
|
|
1055
1011
|
var EntitlementRuleWarningCodeSchema = z6.enum(["one_time_period_mismatch"]).meta(
|
|
@@ -4111,10 +4067,39 @@ var RevTurbineConfigEntitlementRulesItemSchema = z19.object({
|
|
|
4111
4067
|
// means "match all users" (replaces the singular `segment_id` field
|
|
4112
4068
|
// and its 'all'/null sentinels).
|
|
4113
4069
|
segment_ids: z19.array(z19.string()).default([]).meta(Unrestricted16),
|
|
4114
|
-
//
|
|
4115
|
-
//
|
|
4116
|
-
//
|
|
4117
|
-
|
|
4070
|
+
// ── Derived denormalizations from the parent entitlement (plan 147, OQ-6).
|
|
4071
|
+
// Resolved via `entitlement_id` on export; ignored on import (the entitlement
|
|
4072
|
+
// is authoritative). `readOnly` → excluded from round-trip obligations: they
|
|
4073
|
+
// are computed, not authored, so requiring a sentinel to preserve them would
|
|
4074
|
+
// test derivation rather than authoring fidelity.
|
|
4075
|
+
kind: EntitlementTypeSchema.optional().meta({ ...Unrestricted16, readOnly: true }),
|
|
4076
|
+
unit: z19.string().optional().meta({ ...Unrestricted16, readOnly: true }),
|
|
4077
|
+
tier_name: z19.string().optional().meta({ ...Unrestricted16, readOnly: true }),
|
|
4078
|
+
tier_description: z19.string().optional().meta({ ...Unrestricted16, readOnly: true }),
|
|
4079
|
+
// ── Flat per-rule fields (plan 147, OQ-6) — single-sourced from the persisted
|
|
4080
|
+
// `EntitlementRuleSchema` under its canonical names (REQ-1/REQ-2), replacing
|
|
4081
|
+
// the deleted nested `type_fields` union. Null-stripped on export. The
|
|
4082
|
+
// evaluated subset lowers into `TypeFieldsIR`; the persisted-not-evaluated
|
|
4083
|
+
// fields (`amount_cents`/`currency`/`rate_value`/`period_scope`/…) round-trip
|
|
4084
|
+
// via web import/export for content rendering (OQ-7), not the bundle.
|
|
4085
|
+
...EntitlementRuleSchema.pick({
|
|
4086
|
+
enabled: true,
|
|
4087
|
+
limit_value: true,
|
|
4088
|
+
enforcement: true,
|
|
4089
|
+
tier_value: true,
|
|
4090
|
+
period_scope: true,
|
|
4091
|
+
included_count: true,
|
|
4092
|
+
seat_type_id: true,
|
|
4093
|
+
initial_grant: true,
|
|
4094
|
+
allowance_value: true,
|
|
4095
|
+
rollover_enabled: true,
|
|
4096
|
+
max_balance: true,
|
|
4097
|
+
reset_period: true,
|
|
4098
|
+
max_seats: true,
|
|
4099
|
+
rate_value: true,
|
|
4100
|
+
amount_cents: true,
|
|
4101
|
+
currency: true
|
|
4102
|
+
}).shape,
|
|
4118
4103
|
current_usage: z19.number().default(0).meta(Unrestricted16),
|
|
4119
4104
|
/** How usage is partitioned across the identity hierarchy. */
|
|
4120
4105
|
allocation: UsageAllocationSchema.optional().meta(Unrestricted16)
|
|
@@ -4347,23 +4332,7 @@ var RevTurbineConfigFreeTrialRuleItemSchema = IdField.merge(FreeTrialRuleCoreFie
|
|
|
4347
4332
|
var RevTurbineConfigReverseTrialRuleItemSchema = IdField.merge(ReverseTrialRuleCoreFieldsSchema).meta(
|
|
4348
4333
|
{ id: "RevTurbineConfigReverseTrialRuleItem", "x-revturbine-schema-persistence": Transient16, "x-revturbine-schema-exposure": External11, ...PENDING_PLAYBOOK_FACETS4 }
|
|
4349
4334
|
);
|
|
4350
|
-
var
|
|
4351
|
-
version: z19.string().meta({ ...Unrestricted16, ...PLAYBOOK_VERSION_HEADER_FACETS }),
|
|
4352
|
-
exported_at: z19.string().datetime().optional().meta({ ...Unrestricted16, ...PLAYBOOK_PROVENANCE_HEADER_FACETS, readOnly: true }),
|
|
4353
|
-
// The @revt-eng/schema package version this config was generated with.
|
|
4354
|
-
schema_version: z19.string().optional().meta({ ...Unrestricted16, ...PLAYBOOK_VERSION_HEADER_FACETS, readOnly: true }),
|
|
4355
|
-
// The compiled-bundle wire-format SCHEMA_VERSION (core/bundle/ir.ts).
|
|
4356
|
-
bundle_schema_version: z19.number().int().optional().meta({ ...Unrestricted16, ...PLAYBOOK_VERSION_HEADER_FACETS, readOnly: true }),
|
|
4357
|
-
// The change set this export represents: the active change set by default,
|
|
4358
|
-
// or a specific change set when one is requested. Null for an unscoped export.
|
|
4359
|
-
change_set_id: z19.string().nullable().default(null).meta({ ...Unrestricted16, ...PLAYBOOK_PROVENANCE_HEADER_FACETS, readOnly: true }),
|
|
4360
|
-
// Origin target identity (plan 131 TASK-10, cli.md "Target-aware, portable"):
|
|
4361
|
-
// stamped by the server on export so a downloaded Config File records where
|
|
4362
|
-
// it came from; upload tooling targets these by default and flags a tenant
|
|
4363
|
-
// mismatch against the session before sending. Optional — hand-authored and
|
|
4364
|
-
// pre-existing configs carry no target.
|
|
4365
|
-
tenant_id: z19.string().optional().meta({ ...Unrestricted16, ...PLAYBOOK_TARGET_FACETS, readOnly: true }),
|
|
4366
|
-
environment_id: z19.string().optional().meta({ ...Unrestricted16, ...PLAYBOOK_TARGET_FACETS, readOnly: true }),
|
|
4335
|
+
var PlaybookBodySchema = z19.object({
|
|
4367
4336
|
plans: z19.array(RevTurbineConfigPlansItemSchema).meta({ ...Unrestricted16, ...PLAYBOOK_SDK_FACETS8 }),
|
|
4368
4337
|
// Optional for back-compat: pre-plan-88 configs (and the live export until web
|
|
4369
4338
|
// adopts the new @revt-eng/schema) omit it. Add-on definitions only; pricing
|
|
@@ -4441,19 +4410,33 @@ var LegacyRevTurbineConfigSchema = z19.object({
|
|
|
4441
4410
|
enforcement_defaults: z19.array(RevTurbineConfigEnforcementDefaultsItemSchema).optional().meta({ ...Unrestricted16, ...PLAYBOOK_SDK_FACETS8 }),
|
|
4442
4411
|
placement_settings: z19.array(RevTurbineConfigPlacementSettingsItemSchema).optional().meta({ ...Unrestricted16, ...PLAYBOOK_SDK_FACETS8 }),
|
|
4443
4412
|
segment_dimensions: z19.array(RevTurbineConfigSegmentDimensionsItemSchema).optional().meta({ ...Unrestricted16, ...PLAYBOOK_SDK_FACETS8 }),
|
|
4444
|
-
meter_bindings: z19.array(RevTurbineConfigMeterBindingsItemSchema).optional().meta({ ...Unrestricted16, ...PLAYBOOK_SDK_FACETS8 })
|
|
4413
|
+
meter_bindings: z19.array(RevTurbineConfigMeterBindingsItemSchema).optional().meta({ ...Unrestricted16, ...PLAYBOOK_SDK_FACETS8 }),
|
|
4414
|
+
experiments: z19.array(z19.unknown()).max(0).optional().meta({
|
|
4415
|
+
...Unrestricted16,
|
|
4416
|
+
...PENDING_PLAYBOOK_FACETS4
|
|
4417
|
+
}),
|
|
4418
|
+
// Reserved like `experiments` above: claim the key now, ship the
|
|
4419
|
+
// semantics later. A Playbook will eventually pin the Signal Catalog
|
|
4420
|
+
// version its targeting was interpreted under, so a decision stays
|
|
4421
|
+
// reproducible after the event taxonomy moves on. Reserving costs one
|
|
4422
|
+
// schema change; adding a top-level Playbook key after the fact costs a
|
|
4423
|
+
// second breaking cascade through the IR, the CLI, demo data, and every
|
|
4424
|
+
// SDK port.
|
|
4425
|
+
//
|
|
4426
|
+
// Empty-object-only until a consumer exists — `.strict()` rejects a
|
|
4427
|
+
// populated `{ id, version }` rather than letting it round-trip as
|
|
4428
|
+
// config nothing reads. Catalog *definitions* stay server-side and must
|
|
4429
|
+
// never reach the browser Playbook; only the reference will live here.
|
|
4430
|
+
signal_catalog: z19.object({}).strict().optional().meta({
|
|
4431
|
+
...Unrestricted16,
|
|
4432
|
+
...PENDING_PLAYBOOK_FACETS4
|
|
4433
|
+
})
|
|
4445
4434
|
}).meta(
|
|
4446
4435
|
{
|
|
4447
|
-
id: "
|
|
4436
|
+
id: "PlaybookBody",
|
|
4448
4437
|
"x-revturbine-schema-persistence": Transient16,
|
|
4449
4438
|
"x-revturbine-schema-exposure": External11,
|
|
4450
|
-
...PLAYBOOK_SDK_FACETS8
|
|
4451
|
-
...schemaDeprecation({
|
|
4452
|
-
since: "0.1.111",
|
|
4453
|
-
replacement: "PlaybookSchema",
|
|
4454
|
-
removeAfter: "one compatibility window",
|
|
4455
|
-
reason: "The legacy wire conflates format and provenance under version and change_set_id."
|
|
4456
|
-
})
|
|
4439
|
+
...PLAYBOOK_SDK_FACETS8
|
|
4457
4440
|
}
|
|
4458
4441
|
);
|
|
4459
4442
|
var PlaybookHeaderSchema = z19.object({
|
|
@@ -4476,12 +4459,17 @@ var PlaybookHeaderSchema = z19.object({
|
|
|
4476
4459
|
...PLAYBOOK_PROVENANCE_HEADER_FACETS,
|
|
4477
4460
|
readOnly: true
|
|
4478
4461
|
}),
|
|
4479
|
-
|
|
4462
|
+
// Origin target identity (plan 131 TASK-10). Optional so a hand-authored /
|
|
4463
|
+
// legacy `export-config.json` (which carries no target) parses unchanged —
|
|
4464
|
+
// the plan 147 TASK-1 header reconciliation: the one config schema must
|
|
4465
|
+
// absorb legacy files that predate target stamping. Stamped by the server
|
|
4466
|
+
// on export when present.
|
|
4467
|
+
tenant_id: z19.string().min(1).optional().meta({
|
|
4480
4468
|
...Unrestricted16,
|
|
4481
4469
|
...PLAYBOOK_TARGET_FACETS,
|
|
4482
4470
|
readOnly: true
|
|
4483
4471
|
}),
|
|
4484
|
-
environment_id: z19.string().min(1).meta({
|
|
4472
|
+
environment_id: z19.string().min(1).optional().meta({
|
|
4485
4473
|
...Unrestricted16,
|
|
4486
4474
|
...PLAYBOOK_TARGET_FACETS,
|
|
4487
4475
|
readOnly: true
|
|
@@ -4514,61 +4502,39 @@ var PlaybookHeaderSchema = z19.object({
|
|
|
4514
4502
|
...PLAYBOOK_PROVENANCE_HEADER_FACETS
|
|
4515
4503
|
}
|
|
4516
4504
|
);
|
|
4517
|
-
var
|
|
4518
|
-
version: true,
|
|
4519
|
-
exported_at: true,
|
|
4520
|
-
schema_version: true,
|
|
4521
|
-
bundle_schema_version: true,
|
|
4522
|
-
change_set_id: true,
|
|
4523
|
-
tenant_id: true,
|
|
4524
|
-
environment_id: true
|
|
4525
|
-
};
|
|
4526
|
-
var PlaybookBodySchema = LegacyRevTurbineConfigSchema.omit(LEGACY_HEADER_KEYS).extend({
|
|
4527
|
-
experiments: z19.array(z19.unknown()).max(0).optional().meta({
|
|
4528
|
-
...Unrestricted16,
|
|
4529
|
-
...PENDING_PLAYBOOK_FACETS4
|
|
4530
|
-
}),
|
|
4531
|
-
// Reserved like `experiments` above: claim the key now, ship the
|
|
4532
|
-
// semantics later. A Playbook will eventually pin the Signal Catalog
|
|
4533
|
-
// version its targeting was interpreted under, so a decision stays
|
|
4534
|
-
// reproducible after the event taxonomy moves on. Reserving costs one
|
|
4535
|
-
// schema change; adding a top-level Playbook key after the fact costs a
|
|
4536
|
-
// second breaking cascade through the IR, the CLI, demo data, and every
|
|
4537
|
-
// SDK port.
|
|
4538
|
-
//
|
|
4539
|
-
// Empty-object-only until a consumer exists — `.strict()` rejects a
|
|
4540
|
-
// populated `{ id, version }` rather than letting it round-trip as
|
|
4541
|
-
// config nothing reads. Catalog *definitions* stay server-side and must
|
|
4542
|
-
// never reach the browser Playbook; only the reference will live here.
|
|
4543
|
-
signal_catalog: z19.object({}).strict().optional().meta({
|
|
4544
|
-
...Unrestricted16,
|
|
4545
|
-
...PENDING_PLAYBOOK_FACETS4
|
|
4546
|
-
})
|
|
4547
|
-
// NOTE: plan_variations / addon_variations are NOT re-declared here — they
|
|
4548
|
-
// now live on LegacyRevTurbineConfigSchema (plan 118) and flow through the
|
|
4549
|
-
// `.omit(LEGACY_HEADER_KEYS)` above, so both the legacy and canonical shapes
|
|
4550
|
-
// carry them from a single definition.
|
|
4551
|
-
// NOTE: the authored-config projections (seat_types, enforcement_defaults,
|
|
4552
|
-
// placement_settings, segment_dimensions, meter_bindings) are active SDK
|
|
4553
|
-
// inputs and flow through from LegacyRevTurbineConfigSchema (they are not
|
|
4554
|
-
// LEGACY_HEADER_KEYS, so `.omit` retains them). See plan 118 TASK-18.
|
|
4555
|
-
}).meta(
|
|
4505
|
+
var PlaybookObjectSchema = PlaybookHeaderSchema.extend(PlaybookBodySchema.shape).meta(
|
|
4556
4506
|
{
|
|
4557
|
-
id: "PlaybookBody",
|
|
4558
4507
|
"x-revturbine-schema-persistence": Transient16,
|
|
4559
4508
|
"x-revturbine-schema-exposure": External11,
|
|
4560
4509
|
...PLAYBOOK_SDK_FACETS8
|
|
4561
4510
|
}
|
|
4562
4511
|
);
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4512
|
+
function isRecord(value) {
|
|
4513
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4514
|
+
}
|
|
4515
|
+
function normalizeConfigHeaderInput(input) {
|
|
4516
|
+
if (!isRecord(input)) return input;
|
|
4517
|
+
const next = { ...input };
|
|
4518
|
+
if (!("artifact_type" in next)) {
|
|
4519
|
+
next.artifact_type = "playbook";
|
|
4569
4520
|
}
|
|
4570
|
-
)
|
|
4571
|
-
|
|
4521
|
+
if ("version" in next) {
|
|
4522
|
+
if (!("format_version" in next)) next.format_version = next.version;
|
|
4523
|
+
delete next.version;
|
|
4524
|
+
}
|
|
4525
|
+
if ("change_set_id" in next) {
|
|
4526
|
+
if (!("playbook_version_id" in next)) next.playbook_version_id = next.change_set_id;
|
|
4527
|
+
delete next.change_set_id;
|
|
4528
|
+
}
|
|
4529
|
+
return next;
|
|
4530
|
+
}
|
|
4531
|
+
var PlaybookSchema = z19.preprocess(normalizeConfigHeaderInput, PlaybookObjectSchema).meta({
|
|
4532
|
+
id: "Playbook",
|
|
4533
|
+
"x-revturbine-schema-persistence": Transient16,
|
|
4534
|
+
"x-revturbine-schema-exposure": External11,
|
|
4535
|
+
...PLAYBOOK_SDK_FACETS8
|
|
4536
|
+
});
|
|
4537
|
+
var RevTurbineConfigSchema = PlaybookSchema;
|
|
4572
4538
|
var configPaths = {
|
|
4573
4539
|
"/api/seat-type-anchors": {
|
|
4574
4540
|
get: operation({
|
|
@@ -5793,7 +5759,15 @@ var AuthSsoProviderSchema = IdField.extend({
|
|
|
5793
5759
|
|
|
5794
5760
|
// src/schema/validators.snapshot.mjs
|
|
5795
5761
|
import { z as z27 } from "zod";
|
|
5762
|
+
import { z as z92 } from "zod";
|
|
5763
|
+
import { z as z32 } from "zod";
|
|
5796
5764
|
import { z as z28 } from "zod";
|
|
5765
|
+
import { z as z42 } from "zod";
|
|
5766
|
+
import { z as z62 } from "zod";
|
|
5767
|
+
import { z as z52 } from "zod";
|
|
5768
|
+
import { z as z72 } from "zod";
|
|
5769
|
+
import { z as z82 } from "zod";
|
|
5770
|
+
import { z as z102 } from "zod";
|
|
5797
5771
|
var SeveritySchema2 = z27.enum([
|
|
5798
5772
|
"error_draft",
|
|
5799
5773
|
"error_launch",
|
|
@@ -6128,153 +6102,2760 @@ function evaluate(graph, opts = {}) {
|
|
|
6128
6102
|
if (!opts.focus) return findings;
|
|
6129
6103
|
return findings.map((f) => spotlights(f, opts.focus) ? { ...f, spotlight: true } : f);
|
|
6130
6104
|
}
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
function
|
|
6147
|
-
|
|
6148
|
-
for (
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
}
|
|
6156
|
-
function configDir() {
|
|
6157
|
-
return resolveConfigDir().dir;
|
|
6158
|
-
}
|
|
6159
|
-
function configFile() {
|
|
6160
|
-
return path.join(configDir(), "credentials.json");
|
|
6161
|
-
}
|
|
6162
|
-
function normalizeBaseUrl(input) {
|
|
6163
|
-
const s = input.trim();
|
|
6164
|
-
if (/^https?:\/\//i.test(s)) return s.replace(/\/+$/, "");
|
|
6165
|
-
const isLocal = /^(localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\])(:\d+)?(\/|$)/i.test(s);
|
|
6166
|
-
return `${isLocal ? "http" : "https"}://${s}`.replace(/\/+$/, "");
|
|
6167
|
-
}
|
|
6168
|
-
function urlKey(baseUrl) {
|
|
6169
|
-
try {
|
|
6170
|
-
return new URL(normalizeBaseUrl(baseUrl)).origin;
|
|
6171
|
-
} catch {
|
|
6172
|
-
return baseUrl.replace(/\/+$/, "");
|
|
6173
|
-
}
|
|
6174
|
-
}
|
|
6175
|
-
function read() {
|
|
6176
|
-
const file = configFile();
|
|
6177
|
-
if (!existsSync(file)) return { version: 1, credentials: {} };
|
|
6178
|
-
try {
|
|
6179
|
-
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
6180
|
-
if (parsed && typeof parsed === "object" && parsed.credentials) {
|
|
6181
|
-
return { version: 1, credentials: parsed.credentials };
|
|
6105
|
+
var HANDLE_PATTERN2 = /^[a-z0-9._]{1,100}$/;
|
|
6106
|
+
var SchemaPersistence2 = {
|
|
6107
|
+
Persisted: "persisted",
|
|
6108
|
+
Transient: "transient"
|
|
6109
|
+
};
|
|
6110
|
+
var SchemaExposure2 = {
|
|
6111
|
+
Internal: "internal",
|
|
6112
|
+
External: "external"
|
|
6113
|
+
};
|
|
6114
|
+
var DataClassification2 = {
|
|
6115
|
+
Pii: { "x-revturbine-data-classification": "pii" },
|
|
6116
|
+
Financial: { "x-revturbine-data-classification": "financial" },
|
|
6117
|
+
Unrestricted: { "x-revturbine-data-classification": "unrestricted" }
|
|
6118
|
+
};
|
|
6119
|
+
var READ_ONLY_META_KEY2 = "readOnly";
|
|
6120
|
+
function toWritableSchema2(schema) {
|
|
6121
|
+
const writableShape = {};
|
|
6122
|
+
for (const [fieldName, fieldSchema] of Object.entries(schema.shape)) {
|
|
6123
|
+
if (typeof fieldSchema !== "object" || fieldSchema === null) {
|
|
6124
|
+
continue;
|
|
6125
|
+
}
|
|
6126
|
+
const isReadOnly = fieldSchema.meta()?.[READ_ONLY_META_KEY2] === true;
|
|
6127
|
+
if (!isReadOnly) {
|
|
6128
|
+
writableShape[fieldName] = fieldSchema;
|
|
6182
6129
|
}
|
|
6183
|
-
} catch {
|
|
6184
6130
|
}
|
|
6185
|
-
return
|
|
6131
|
+
return z28.object(writableShape);
|
|
6186
6132
|
}
|
|
6187
|
-
function
|
|
6188
|
-
const
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
try {
|
|
6193
|
-
chmodSync(configFile(), 384);
|
|
6194
|
-
} catch {
|
|
6133
|
+
function toCreateSchema2(schema) {
|
|
6134
|
+
const writable = toWritableSchema2(schema);
|
|
6135
|
+
const handleField = writable.shape["handle"];
|
|
6136
|
+
if (handleField && typeof handleField.regex === "function") {
|
|
6137
|
+
return writable.extend({ handle: handleField.regex(HANDLE_PATTERN2) });
|
|
6195
6138
|
}
|
|
6139
|
+
return writable;
|
|
6196
6140
|
}
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
if (!(key in data.credentials)) return false;
|
|
6209
|
-
delete data.credentials[key];
|
|
6210
|
-
write(data);
|
|
6211
|
-
return true;
|
|
6212
|
-
}
|
|
6213
|
-
function redactToken(token) {
|
|
6214
|
-
if (token.length <= 8) return "rtk_\u2026";
|
|
6215
|
-
return `${token.slice(0, 4)}\u2026${token.slice(-4)}`;
|
|
6216
|
-
}
|
|
6217
|
-
|
|
6218
|
-
// src/lib/device-auth.ts
|
|
6219
|
-
import { spawn } from "child_process";
|
|
6220
|
-
import os2 from "os";
|
|
6221
|
-
function trimUrl(baseUrl) {
|
|
6222
|
-
return baseUrl.replace(/\/+$/, "");
|
|
6223
|
-
}
|
|
6224
|
-
async function startDeviceAuth(baseUrl, label, fetchImpl) {
|
|
6225
|
-
const res = await fetchImpl(`${trimUrl(baseUrl)}/api/cli/device/code`, {
|
|
6226
|
-
method: "POST",
|
|
6227
|
-
headers: { "Content-Type": "application/json" },
|
|
6228
|
-
body: JSON.stringify(label ? { label } : {})
|
|
6229
|
-
});
|
|
6230
|
-
if (!res.ok) {
|
|
6231
|
-
throw new Error(`Could not start device login (${res.status}). Is ${baseUrl} reachable?`);
|
|
6141
|
+
var { Unrestricted: Unrestricted24 } = DataClassification2;
|
|
6142
|
+
var { Transient: Transient22, Persisted: Persisted24 } = SchemaPersistence2;
|
|
6143
|
+
var { Internal: Internal20, External: External13 } = SchemaExposure2;
|
|
6144
|
+
var PaginationParamsSchema2 = z32.object({
|
|
6145
|
+
page: z32.coerce.number().int().min(1).default(1).meta(Unrestricted24),
|
|
6146
|
+
per_page: z32.coerce.number().int().min(1).max(100).default(25).meta(Unrestricted24)
|
|
6147
|
+
}).meta(
|
|
6148
|
+
{
|
|
6149
|
+
id: "PaginationParams",
|
|
6150
|
+
"x-revturbine-schema-persistence": Transient22,
|
|
6151
|
+
"x-revturbine-schema-exposure": External13
|
|
6232
6152
|
}
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
}
|
|
6243
|
-
await opts.sleep(interval * 1e3);
|
|
6244
|
-
const res = await opts.fetchImpl(`${trimUrl(opts.baseUrl)}/api/cli/device/token`, {
|
|
6245
|
-
method: "POST",
|
|
6246
|
-
headers: { "Content-Type": "application/json" },
|
|
6247
|
-
body: JSON.stringify({ device_code: opts.deviceCode })
|
|
6248
|
-
});
|
|
6249
|
-
if (res.ok) {
|
|
6250
|
-
return await res.json();
|
|
6251
|
-
}
|
|
6252
|
-
const body = await res.json().catch(() => ({}));
|
|
6253
|
-
switch (body.error) {
|
|
6254
|
-
case "authorization_pending":
|
|
6255
|
-
opts.onPending?.();
|
|
6256
|
-
continue;
|
|
6257
|
-
case "slow_down":
|
|
6258
|
-
interval += 5;
|
|
6259
|
-
continue;
|
|
6260
|
-
case "access_denied":
|
|
6261
|
-
throw new Error("Authorization was denied in the browser.");
|
|
6262
|
-
case "expired_token":
|
|
6263
|
-
throw new Error("Device code expired. Run --login again.");
|
|
6264
|
-
default:
|
|
6265
|
-
throw new Error(`Authorization failed: ${body.error ?? `HTTP ${res.status}`}`);
|
|
6266
|
-
}
|
|
6153
|
+
);
|
|
6154
|
+
var TimestampFields2 = z32.object({
|
|
6155
|
+
created_at: z32.string().datetime().meta({ ...Unrestricted24, readOnly: true }),
|
|
6156
|
+
updated_at: z32.string().datetime().meta({ ...Unrestricted24, readOnly: true })
|
|
6157
|
+
}).meta(
|
|
6158
|
+
{
|
|
6159
|
+
id: "null",
|
|
6160
|
+
"x-revturbine-schema-persistence": Transient22,
|
|
6161
|
+
"x-revturbine-schema-exposure": External13
|
|
6267
6162
|
}
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6163
|
+
);
|
|
6164
|
+
var IdField2 = z32.object({
|
|
6165
|
+
id: z32.string().min(1).meta({ ...Unrestricted24, readOnly: true })
|
|
6166
|
+
}).meta(
|
|
6167
|
+
{
|
|
6168
|
+
id: "null",
|
|
6169
|
+
"x-revturbine-schema-persistence": Transient22,
|
|
6170
|
+
"x-revturbine-schema-exposure": External13
|
|
6171
|
+
}
|
|
6172
|
+
);
|
|
6173
|
+
var TenantIdField2 = z32.object({
|
|
6174
|
+
tenant_id: z32.string().min(1, "tenant_id is required").meta({ ...Unrestricted24, readOnly: true })
|
|
6175
|
+
}).meta(
|
|
6176
|
+
{
|
|
6177
|
+
id: "null",
|
|
6178
|
+
"x-revturbine-schema-persistence": Transient22,
|
|
6179
|
+
"x-revturbine-schema-exposure": Internal20
|
|
6180
|
+
}
|
|
6181
|
+
);
|
|
6182
|
+
var BillingCadenceSchema2 = z32.enum([
|
|
6183
|
+
"monthly",
|
|
6184
|
+
"annual",
|
|
6185
|
+
"quarterly",
|
|
6186
|
+
"one_time",
|
|
6187
|
+
"usage_based"
|
|
6188
|
+
]).meta(
|
|
6189
|
+
{
|
|
6190
|
+
id: "BillingCadence",
|
|
6191
|
+
"x-revturbine-schema-persistence": Transient22,
|
|
6192
|
+
"x-revturbine-schema-exposure": External13
|
|
6193
|
+
}
|
|
6194
|
+
);
|
|
6195
|
+
var SurfaceTypeSchema2 = z32.enum([
|
|
6196
|
+
"banner",
|
|
6197
|
+
"modal",
|
|
6198
|
+
"tooltip",
|
|
6199
|
+
"sidebar",
|
|
6200
|
+
"inline",
|
|
6201
|
+
"toast",
|
|
6202
|
+
"fullscreen",
|
|
6203
|
+
"email",
|
|
6204
|
+
"sms",
|
|
6205
|
+
"push",
|
|
6206
|
+
"in_page",
|
|
6207
|
+
"button",
|
|
6208
|
+
"full_page",
|
|
6209
|
+
"agent",
|
|
6210
|
+
"cli",
|
|
6211
|
+
"custom"
|
|
6212
|
+
]).meta(
|
|
6213
|
+
{ id: "SurfaceType", "x-revturbine-schema-persistence": Transient22, "x-revturbine-schema-exposure": External13 }
|
|
6214
|
+
);
|
|
6215
|
+
var EntitlementTypeSchema2 = z32.enum([
|
|
6216
|
+
"feature",
|
|
6217
|
+
"capability_tier",
|
|
6218
|
+
"usage_limit",
|
|
6219
|
+
"price_per_unit",
|
|
6220
|
+
"rate_limit",
|
|
6221
|
+
"credits",
|
|
6222
|
+
"seat"
|
|
6223
|
+
]).meta(
|
|
6224
|
+
{
|
|
6225
|
+
id: "EntitlementType",
|
|
6226
|
+
"x-revturbine-schema-persistence": Transient22,
|
|
6227
|
+
"x-revturbine-schema-exposure": External13
|
|
6228
|
+
}
|
|
6229
|
+
);
|
|
6230
|
+
var CurrencySchema2 = z32.enum(["usd", "eur", "gbp"]).default("usd").meta(
|
|
6231
|
+
{
|
|
6232
|
+
id: "Currency",
|
|
6233
|
+
"x-revturbine-schema-persistence": Transient22,
|
|
6234
|
+
"x-revturbine-schema-exposure": External13
|
|
6235
|
+
}
|
|
6236
|
+
);
|
|
6237
|
+
var FeatureFlagValueSchema2 = z32.union([
|
|
6238
|
+
z32.boolean(),
|
|
6239
|
+
z32.number(),
|
|
6240
|
+
z32.string()
|
|
6241
|
+
]).meta(
|
|
6242
|
+
{
|
|
6243
|
+
id: "FeatureFlagValue",
|
|
6244
|
+
"x-revturbine-schema-persistence": Transient22,
|
|
6245
|
+
"x-revturbine-schema-exposure": Internal20
|
|
6246
|
+
}
|
|
6247
|
+
);
|
|
6248
|
+
var NameField2 = z32.string().min(1).max(200);
|
|
6249
|
+
var HandleField2 = z32.string().min(1).max(100);
|
|
6250
|
+
var DescriptionField2 = z32.string().max(500).optional();
|
|
6251
|
+
var MetadataField2 = z32.record(z32.string(), z32.unknown()).default({});
|
|
6252
|
+
var ThresholdPercentField2 = z32.number().int().min(10).max(100).multipleOf(10);
|
|
6253
|
+
var NullableDatetimeField2 = z32.string().datetime().nullable().default(null);
|
|
6254
|
+
var AnchorFields2 = z32.object({
|
|
6255
|
+
environment_id: z32.string().min(1).default("production").meta({ ...Unrestricted24, readOnly: true })
|
|
6256
|
+
}).meta({ id: "null", "x-revturbine-schema-persistence": Transient22, "x-revturbine-schema-exposure": Internal20 });
|
|
6257
|
+
var VersionFields2 = z32.object({
|
|
6258
|
+
// Which playbook version (the review/deploy unit — was `change_set_id`) staged
|
|
6259
|
+
// this ledger row; null once it is the deployed live row.
|
|
6260
|
+
playbook_version_id: z32.string().nullable().default(null).meta(Unrestricted24),
|
|
6261
|
+
is_current: z32.boolean().default(true).meta({ ...Unrestricted24, readOnly: true }),
|
|
6262
|
+
is_deleted: z32.boolean().default(false).meta({ ...Unrestricted24, readOnly: true }),
|
|
6263
|
+
delete_date: z32.string().datetime().nullable().default(null).meta({ ...Unrestricted24, readOnly: true }),
|
|
6264
|
+
// Monotonic version number within a lineage (v1, v2, v3 …); `base_sequence` is
|
|
6265
|
+
// the live sequence this version was forked from (null for the first version),
|
|
6266
|
+
// so a stale draft is detected when live has moved past it.
|
|
6267
|
+
sequence: z32.number().int().min(1).default(1).meta({ ...Unrestricted24, readOnly: true }),
|
|
6268
|
+
base_sequence: z32.number().int().nullable().default(null).meta({ ...Unrestricted24, readOnly: true })
|
|
6269
|
+
}).meta({ id: "null", "x-revturbine-schema-persistence": Transient22, "x-revturbine-schema-exposure": Internal20 });
|
|
6270
|
+
var AnchorBaseSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).merge(AnchorFields2).extend({
|
|
6271
|
+
handle: HandleField2.meta({ ...Unrestricted24, readOnly: true }),
|
|
6272
|
+
active: z32.boolean().default(true).meta({ ...Unrestricted24, readOnly: true })
|
|
6273
|
+
});
|
|
6274
|
+
function makeAnchor2(id) {
|
|
6275
|
+
return AnchorBaseSchema2.meta({
|
|
6276
|
+
id,
|
|
6277
|
+
"x-revturbine-schema-persistence": Persisted24,
|
|
6278
|
+
"x-revturbine-schema-exposure": Internal20
|
|
6279
|
+
});
|
|
6280
|
+
}
|
|
6281
|
+
var SeveritySchema22 = z32.enum(["info", "warning", "critical"]).meta({ id: "Severity", "x-revturbine-schema-persistence": Transient22, "x-revturbine-schema-exposure": Internal20 });
|
|
6282
|
+
var StudioSurfaceTypeSchema2 = z32.enum([
|
|
6283
|
+
"button",
|
|
6284
|
+
"plans_page_ctas",
|
|
6285
|
+
"plans_page_full",
|
|
6286
|
+
"inline_gate_message",
|
|
6287
|
+
"tooltip",
|
|
6288
|
+
"in_page_card",
|
|
6289
|
+
"usage_counter",
|
|
6290
|
+
"credit_counter",
|
|
6291
|
+
"trial_counter",
|
|
6292
|
+
"banner",
|
|
6293
|
+
"modal_optional",
|
|
6294
|
+
"modal_blocking",
|
|
6295
|
+
"toast",
|
|
6296
|
+
"email",
|
|
6297
|
+
"sms",
|
|
6298
|
+
"push",
|
|
6299
|
+
"cli",
|
|
6300
|
+
"agent_connector",
|
|
6301
|
+
"custom_in_app"
|
|
6302
|
+
]).meta({ id: "StudioSurfaceType", "x-revturbine-schema-persistence": Transient22, "x-revturbine-schema-exposure": Internal20 });
|
|
6303
|
+
var CtaActionTypeSchema2 = z32.enum([
|
|
6304
|
+
"open_checkout",
|
|
6305
|
+
"view_plans",
|
|
6306
|
+
"book_demo",
|
|
6307
|
+
"contact_sales",
|
|
6308
|
+
"complete_onboarding",
|
|
6309
|
+
"invite_teammate",
|
|
6310
|
+
"refer_friend",
|
|
6311
|
+
"verify_work_email",
|
|
6312
|
+
"update_payment_method",
|
|
6313
|
+
"enable_auto_renewal",
|
|
6314
|
+
"manage_subscription",
|
|
6315
|
+
"switch_billing_period",
|
|
6316
|
+
"extend_trial",
|
|
6317
|
+
"open_rt_placement",
|
|
6318
|
+
"dismiss",
|
|
6319
|
+
"snooze",
|
|
6320
|
+
"custom"
|
|
6321
|
+
]).meta({ id: "CtaActionType", "x-revturbine-schema-persistence": Transient22, "x-revturbine-schema-exposure": External13 });
|
|
6322
|
+
var IdentityKind2 = {
|
|
6323
|
+
/** Author-given, human-meaningful handle (plans, entitlements, segments, …). */
|
|
6324
|
+
Named: "named",
|
|
6325
|
+
/** Opaque machine-minted slug (variations, entitlement_rules, payloads, …). */
|
|
6326
|
+
Minted: "minted"
|
|
6327
|
+
};
|
|
6328
|
+
var SCHEMA_IDENTITY_META_KEY2 = "x-revturbine-schema-identity";
|
|
6329
|
+
function namedIdentity2(handleField = "handle") {
|
|
6330
|
+
return { [SCHEMA_IDENTITY_META_KEY2]: { kind: IdentityKind2.Named, handleField } };
|
|
6331
|
+
}
|
|
6332
|
+
function mintedIdentity2(handleField = "handle") {
|
|
6333
|
+
return { [SCHEMA_IDENTITY_META_KEY2]: { kind: IdentityKind2.Minted, handleField } };
|
|
6334
|
+
}
|
|
6335
|
+
var SchemaContext2 = {
|
|
6336
|
+
Playbook: "playbook",
|
|
6337
|
+
Branding: "branding",
|
|
6338
|
+
Billing: "billing",
|
|
6339
|
+
Metering: "metering",
|
|
6340
|
+
CustomerOperations: "customer_operations",
|
|
6341
|
+
EventIngestion: "event_ingestion"
|
|
6342
|
+
};
|
|
6343
|
+
var SchemaSource2 = {
|
|
6344
|
+
Customer: "customer",
|
|
6345
|
+
Stripe: "stripe",
|
|
6346
|
+
CodeConstant: "code-constant",
|
|
6347
|
+
Runtime: "runtime"
|
|
6348
|
+
};
|
|
6349
|
+
var SCHEMA_CONTEXT_META_KEY2 = "x-revturbine-context";
|
|
6350
|
+
var SCHEMA_IN_CONFIG_META_KEY2 = "x-revturbine-in-config";
|
|
6351
|
+
var SCHEMA_SDK_INPUT_META_KEY2 = "x-revturbine-sdk-input";
|
|
6352
|
+
var SCHEMA_SOURCE_META_KEY2 = "x-revturbine-source";
|
|
6353
|
+
var SCHEMA_DEPRECATION_META_KEY2 = "x-revturbine-deprecation";
|
|
6354
|
+
var DEFAULT_SOURCE_BY_CONTEXT2 = {
|
|
6355
|
+
[SchemaContext2.Playbook]: SchemaSource2.Customer,
|
|
6356
|
+
[SchemaContext2.Branding]: SchemaSource2.Customer,
|
|
6357
|
+
[SchemaContext2.Billing]: SchemaSource2.Stripe,
|
|
6358
|
+
[SchemaContext2.Metering]: SchemaSource2.Customer,
|
|
6359
|
+
[SchemaContext2.CustomerOperations]: SchemaSource2.Runtime,
|
|
6360
|
+
[SchemaContext2.EventIngestion]: SchemaSource2.Runtime
|
|
6361
|
+
};
|
|
6362
|
+
function schemaFacets2(context, options) {
|
|
6363
|
+
return {
|
|
6364
|
+
[SCHEMA_CONTEXT_META_KEY2]: context,
|
|
6365
|
+
[SCHEMA_IN_CONFIG_META_KEY2]: options.inConfig ?? context === SchemaContext2.Playbook,
|
|
6366
|
+
[SCHEMA_SDK_INPUT_META_KEY2]: options.sdkInput,
|
|
6367
|
+
[SCHEMA_SOURCE_META_KEY2]: options.source ?? DEFAULT_SOURCE_BY_CONTEXT2[context]
|
|
6368
|
+
};
|
|
6369
|
+
}
|
|
6370
|
+
function schemaDeprecation2(declaration) {
|
|
6371
|
+
return {
|
|
6372
|
+
deprecated: true,
|
|
6373
|
+
[SCHEMA_DEPRECATION_META_KEY2]: {
|
|
6374
|
+
since: declaration.since,
|
|
6375
|
+
replacement: declaration.replacement,
|
|
6376
|
+
remove_after: declaration.removeAfter,
|
|
6377
|
+
reason: declaration.reason
|
|
6378
|
+
}
|
|
6379
|
+
};
|
|
6380
|
+
}
|
|
6381
|
+
function getSchemaDeprecation(schema) {
|
|
6382
|
+
const meta = schema.meta();
|
|
6383
|
+
if (meta?.deprecated !== true) return void 0;
|
|
6384
|
+
const value = meta[SCHEMA_DEPRECATION_META_KEY2];
|
|
6385
|
+
if (!value || typeof value !== "object") return void 0;
|
|
6386
|
+
const declaration = value;
|
|
6387
|
+
if (typeof declaration.since !== "string" || typeof declaration.replacement !== "string" || typeof declaration.remove_after !== "string" || typeof declaration.reason !== "string") {
|
|
6388
|
+
return void 0;
|
|
6389
|
+
}
|
|
6390
|
+
return {
|
|
6391
|
+
since: declaration.since,
|
|
6392
|
+
replacement: declaration.replacement,
|
|
6393
|
+
removeAfter: declaration.remove_after,
|
|
6394
|
+
reason: declaration.reason
|
|
6395
|
+
};
|
|
6396
|
+
}
|
|
6397
|
+
var ListEnvelope2 = (itemSchema) => z52.object({
|
|
6398
|
+
items: z52.array(itemSchema)
|
|
6399
|
+
});
|
|
6400
|
+
var ErrorEnvelope2 = z52.object({
|
|
6401
|
+
error: z52.string(),
|
|
6402
|
+
code: z52.string(),
|
|
6403
|
+
request_id: z52.string()
|
|
6404
|
+
});
|
|
6405
|
+
var operation2 = (op) => op;
|
|
6406
|
+
var ListQueryParamsSchema2 = z52.object({
|
|
6407
|
+
page: z52.number().int().min(1).default(1).optional(),
|
|
6408
|
+
per_page: z52.number().int().min(1).max(100).default(25).optional(),
|
|
6409
|
+
sort: z52.string().optional(),
|
|
6410
|
+
order: z52.enum(["asc", "desc"]).default("asc").optional(),
|
|
6411
|
+
include_deleted: z52.boolean().default(false).optional()
|
|
6412
|
+
});
|
|
6413
|
+
var { Unrestricted: Unrestricted25 } = DataClassification2;
|
|
6414
|
+
var { Persisted: Persisted25, Transient: Transient23 } = SchemaPersistence2;
|
|
6415
|
+
var { Internal: Internal22, External: External22 } = SchemaExposure2;
|
|
6416
|
+
var PLAYBOOK_SDK_FACETS9 = schemaFacets2(SchemaContext2.Playbook, { sdkInput: true });
|
|
6417
|
+
var PENDING_PLAYBOOK_FACETS5 = schemaFacets2(SchemaContext2.Playbook, {
|
|
6418
|
+
inConfig: false,
|
|
6419
|
+
sdkInput: false
|
|
6420
|
+
});
|
|
6421
|
+
var UsagePeriodScopeSchema2 = z62.enum(["per_month", "per_year", "per_billing_period", "lifetime", "concurrent", "per_instance", "per_second", "per_minute", "per_hour", "per_6_hours", "per_day", "per_week"]).meta(
|
|
6422
|
+
{ id: "UsagePeriodScope", "x-revturbine-schema-persistence": Transient23, "x-revturbine-schema-exposure": External22 }
|
|
6423
|
+
);
|
|
6424
|
+
var UsageAllocationSchema2 = z62.enum(["account_pool", "per_instance", "per_user", "per_user_pooled"]).meta(
|
|
6425
|
+
{ id: "UsageAllocation", "x-revturbine-schema-persistence": Transient23, "x-revturbine-schema-exposure": External22 }
|
|
6426
|
+
);
|
|
6427
|
+
var EntitlementGrantStatusSchema2 = z62.enum(["allowed", "limited", "denied"]).meta(
|
|
6428
|
+
{ id: "EntitlementGrantStatus", "x-revturbine-schema-persistence": Transient23, "x-revturbine-schema-exposure": External22 }
|
|
6429
|
+
);
|
|
6430
|
+
var EntitlementGrantSourceSchema2 = z62.enum(["rule", "user_context", "override"]).meta(
|
|
6431
|
+
{ id: "EntitlementGrantSource", "x-revturbine-schema-persistence": Transient23, "x-revturbine-schema-exposure": External22 }
|
|
6432
|
+
);
|
|
6433
|
+
var EnforcementModeSchema2 = z62.enum(["hard_block", "soft_block", "degrade", "allow_overage"]).meta(
|
|
6434
|
+
{ id: "EnforcementMode", "x-revturbine-schema-persistence": Transient23, "x-revturbine-schema-exposure": External22 }
|
|
6435
|
+
);
|
|
6436
|
+
var EntitlementGrantSchema2 = z62.object({
|
|
6437
|
+
entitlement_id: z62.string().min(1).meta(Unrestricted25),
|
|
6438
|
+
entitlement_handle: z62.string().min(1).meta(Unrestricted25),
|
|
6439
|
+
status: EntitlementGrantStatusSchema2.meta(Unrestricted25),
|
|
6440
|
+
limit: z62.number().optional().meta(Unrestricted25),
|
|
6441
|
+
used: z62.number().optional().meta(Unrestricted25),
|
|
6442
|
+
allocation: UsageAllocationSchema2.optional().meta(Unrestricted25),
|
|
6443
|
+
enforcement: EnforcementModeSchema2.optional().meta(Unrestricted25),
|
|
6444
|
+
/** How this grant was derived. */
|
|
6445
|
+
source: EntitlementGrantSourceSchema2.optional().meta(Unrestricted25),
|
|
6446
|
+
// ── Derivation context (populated when source = 'rule') ──
|
|
6447
|
+
/** The plan that activated this rule-derived grant. */
|
|
6448
|
+
plan_id: z62.string().optional().meta(Unrestricted25),
|
|
6449
|
+
/** The segment that matched for this rule-derived grant. */
|
|
6450
|
+
segment_id: z62.string().optional().meta(Unrestricted25),
|
|
6451
|
+
/** The seat type that qualified this grant (when allocation is per-seat). */
|
|
6452
|
+
seat_type_id: z62.string().optional().meta(Unrestricted25),
|
|
6453
|
+
/** The entitlement rule id that produced this grant. */
|
|
6454
|
+
rule_id: z62.string().optional().meta(Unrestricted25)
|
|
6455
|
+
}).meta(
|
|
6456
|
+
{
|
|
6457
|
+
id: "EntitlementGrant",
|
|
6458
|
+
"x-revturbine-schema-persistence": Transient23,
|
|
6459
|
+
"x-revturbine-schema-exposure": External22,
|
|
6460
|
+
"x-revturbine-data-classification": "operational"
|
|
6461
|
+
}
|
|
6462
|
+
);
|
|
6463
|
+
var EntitlementGrantSetSchema2 = z62.object({
|
|
6464
|
+
account: z62.record(z62.string(), EntitlementGrantSchema2).optional().meta(Unrestricted25),
|
|
6465
|
+
instance: z62.record(z62.string(), EntitlementGrantSchema2).optional().meta(Unrestricted25),
|
|
6466
|
+
user: z62.record(z62.string(), EntitlementGrantSchema2).optional().meta(Unrestricted25)
|
|
6467
|
+
}).meta(
|
|
6468
|
+
{
|
|
6469
|
+
id: "EntitlementGrantSet",
|
|
6470
|
+
"x-revturbine-schema-persistence": Transient23,
|
|
6471
|
+
"x-revturbine-schema-exposure": External22,
|
|
6472
|
+
"x-revturbine-data-classification": "operational"
|
|
6473
|
+
}
|
|
6474
|
+
);
|
|
6475
|
+
var RuleVisibilitySchema2 = z62.enum(["public", "non_public"]).meta(
|
|
6476
|
+
{ id: "RuleVisibility", "x-revturbine-schema-persistence": Transient23, "x-revturbine-schema-exposure": External22 }
|
|
6477
|
+
);
|
|
6478
|
+
var EntitlementRuleTargetKindSchema2 = z62.enum(["plan", "plan_variation", "addon", "addon_variation"]).meta(
|
|
6479
|
+
{ id: "EntitlementRuleTargetKind", "x-revturbine-schema-persistence": Transient23, "x-revturbine-schema-exposure": External22 }
|
|
6480
|
+
);
|
|
6481
|
+
var EntitlementRuleTargetSchema2 = z62.object({
|
|
6482
|
+
kind: EntitlementRuleTargetKindSchema2.meta(Unrestricted25),
|
|
6483
|
+
id: z62.string().min(1).meta(Unrestricted25)
|
|
6484
|
+
}).meta(
|
|
6485
|
+
{ id: "EntitlementRuleTarget", "x-revturbine-schema-persistence": Transient23, "x-revturbine-schema-exposure": External22 }
|
|
6486
|
+
);
|
|
6487
|
+
var EntitlementRulePeriodUnitSchema2 = z62.enum(["month", "day", "week", "quarter", "year", "billing_period", "hour", "six_hours"]).meta(
|
|
6488
|
+
{ id: "EntitlementRulePeriodUnit", "x-revturbine-schema-persistence": Transient23, "x-revturbine-schema-exposure": External22 }
|
|
6489
|
+
);
|
|
6490
|
+
var EntitlementSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).merge(AnchorFields2).merge(VersionFields2).extend({
|
|
6491
|
+
anchor_id: z62.string().min(1).meta({ ...Unrestricted25, readOnly: true }),
|
|
6492
|
+
name: NameField2.meta(Unrestricted25),
|
|
6493
|
+
handle: HandleField2.meta(Unrestricted25),
|
|
6494
|
+
customer_facing_description: z62.string().max(500).optional().meta(Unrestricted25),
|
|
6495
|
+
type: EntitlementTypeSchema2.meta(Unrestricted25),
|
|
6496
|
+
unit: z62.string().max(100).optional().meta(Unrestricted25),
|
|
6497
|
+
period_scope: UsagePeriodScopeSchema2.optional().meta(Unrestricted25),
|
|
6498
|
+
allocation: UsageAllocationSchema2.optional().meta(Unrestricted25),
|
|
6499
|
+
tier_definitions: z62.array(z62.object({
|
|
6500
|
+
name: z62.string(),
|
|
6501
|
+
handle: z62.string(),
|
|
6502
|
+
description: z62.string().optional()
|
|
6503
|
+
})).optional().meta(Unrestricted25),
|
|
6504
|
+
sort_order: z62.number().int().default(0).meta(Unrestricted25),
|
|
6505
|
+
metadata: MetadataField2.meta(Unrestricted25)
|
|
6506
|
+
}).meta(
|
|
6507
|
+
{
|
|
6508
|
+
id: "Entitlement",
|
|
6509
|
+
"x-revturbine-schema-persistence": Persisted25,
|
|
6510
|
+
"x-revturbine-schema-exposure": External22,
|
|
6511
|
+
...PLAYBOOK_SDK_FACETS9,
|
|
6512
|
+
...namedIdentity2()
|
|
6513
|
+
}
|
|
6514
|
+
);
|
|
6515
|
+
var EntitlementAnchorSchema2 = makeAnchor2("EntitlementAnchor");
|
|
6516
|
+
var EntitlementRuleSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).merge(AnchorFields2).merge(VersionFields2).extend({
|
|
6517
|
+
anchor_id: z62.string().min(1).meta({ ...Unrestricted25, readOnly: true }),
|
|
6518
|
+
handle: HandleField2.meta({ ...Unrestricted25, readOnly: true }),
|
|
6519
|
+
entitlement_id: z62.string().min(1).meta(Unrestricted25),
|
|
6520
|
+
targets: z62.array(EntitlementRuleTargetSchema2).min(1).meta(Unrestricted25),
|
|
6521
|
+
// Segment scoping per spec §2.5: array of segment IDs interpreted with
|
|
6522
|
+
// intra-dimension OR + cross-dimension AND at evaluation time. The
|
|
6523
|
+
// dimensions registry resolves each ID → dimension. Empty array means
|
|
6524
|
+
// "match all users" (replaces the legacy 'all' sentinel).
|
|
6525
|
+
segment_ids: z62.array(z62.string()).default([]).meta(Unrestricted25),
|
|
6526
|
+
visibility: RuleVisibilitySchema2.default("public").meta(Unrestricted25),
|
|
6527
|
+
// Usage-Limit "measured over" window, rule-level (plan #55). Rate Limit
|
|
6528
|
+
// keeps its entitlement-level `period_scope`; this is the per-rule one.
|
|
6529
|
+
period_scope: UsagePeriodScopeSchema2.optional().meta(Unrestricted25),
|
|
6530
|
+
// Optional instance label, surfaced when `period_scope = 'per_instance'` (F-1).
|
|
6531
|
+
instance: z62.string().max(100).optional().meta(Unrestricted25),
|
|
6532
|
+
// Credits reset cadence ("refills every"): governs only the per-period
|
|
6533
|
+
// `allowance_value` refill. `billing_period` resolves at runtime to the
|
|
6534
|
+
// customer's Variation billing period; structural guard below. Absent means
|
|
6535
|
+
// one-time only — an `initial_grant` with no recurring refill (plan 147
|
|
6536
|
+
// REQ-6; `on_purchase` retired from the enum).
|
|
6537
|
+
reset_period: EntitlementRulePeriodUnitSchema2.optional().meta(Unrestricted25),
|
|
6538
|
+
// Type-specific fields (populated based on entitlement type)
|
|
6539
|
+
limit_value: z62.union([z62.number(), z62.literal("unlimited")]).optional().meta(Unrestricted25),
|
|
6540
|
+
enforcement: EnforcementModeSchema2.optional().meta(Unrestricted25),
|
|
6541
|
+
// Usage-warning emitter (plan 138 REQ-13): the percent at which this rule
|
|
6542
|
+
// emits a usage-warning crossing event. 10% increments so a placement's
|
|
6543
|
+
// `threshold_percent` (same field type) can align with — "should match one
|
|
6544
|
+
// of those global values" (placement-studio-ui.md §3.4). Optional; a rule
|
|
6545
|
+
// with no warning threshold emits none.
|
|
6546
|
+
warning_threshold_percent: ThresholdPercentField2.optional().meta(Unrestricted25),
|
|
6547
|
+
overage_price_ref: z62.string().optional().meta(Unrestricted25),
|
|
6548
|
+
grace_period_hours: z62.number().int().min(0).optional().meta(Unrestricted25),
|
|
6549
|
+
tier_value: z62.string().optional().meta(Unrestricted25),
|
|
6550
|
+
rate_value: z62.number().optional().meta(Unrestricted25),
|
|
6551
|
+
initial_grant: z62.number().optional().meta(Unrestricted25),
|
|
6552
|
+
allowance_value: z62.union([z62.number(), z62.literal("unlimited")]).optional().meta(Unrestricted25),
|
|
6553
|
+
rollover_enabled: z62.boolean().optional().meta(Unrestricted25),
|
|
6554
|
+
// Ceiling on total balance — refills clipped, add-on top-ups bypass.
|
|
6555
|
+
// NOT NULL JSONB at the DB level; JSON-null value means "no ceiling."
|
|
6556
|
+
max_balance: z62.union([z62.number(), z62.literal("unlimited")]).nullable().default(null).meta(Unrestricted25),
|
|
6557
|
+
seat_type_id: z62.string().optional().meta(Unrestricted25),
|
|
6558
|
+
included_count: z62.union([z62.number().int(), z62.literal("unlimited")]).optional().meta(Unrestricted25),
|
|
6559
|
+
at_limit_behavior: z62.enum(["hard_cap", "auto_upgrade_at_renewal"]).optional().meta(Unrestricted25),
|
|
6560
|
+
stripe_metered_price_id: z62.string().optional().meta(Unrestricted25),
|
|
6561
|
+
// ── Plan 147: columns promoted from the deleted portable `type_fields` union
|
|
6562
|
+
// so the flat wire projection round-trips (REQ-3 persist verdicts). The wire
|
|
6563
|
+
// (RevTurbineConfigEntitlementRulesItemSchema) mirrors these one-to-one under
|
|
6564
|
+
// their canonical names; web derives `kind`/`unit`/`tier_*` from the parent
|
|
6565
|
+
// entitlement instead of persisting them.
|
|
6566
|
+
//
|
|
6567
|
+
// feature enable/disable: a present-but-disabled feature rule is a real DENY
|
|
6568
|
+
// (`entitlement-check.ts` reads `enabled !== false`), distinct from "no rule".
|
|
6569
|
+
// Optional (absent = enabled) matches that evaluator semantic — replaces the
|
|
6570
|
+
// hard-coded `true` the export used to emit.
|
|
6571
|
+
enabled: z62.boolean().optional().meta(Unrestricted25),
|
|
6572
|
+
// How this rule partitions usage across the identity hierarchy. Rule-level in
|
|
6573
|
+
// the IR (`encode.ts` maps `r.allocation`); may default from the parent
|
|
6574
|
+
// entitlement's `allocation` when unset.
|
|
6575
|
+
allocation: UsageAllocationSchema2.optional().meta(Unrestricted25),
|
|
6576
|
+
// Seat ceiling. null = unlimited (plan 72); the 999999 export sentinel maps
|
|
6577
|
+
// back to null at the compile boundary.
|
|
6578
|
+
max_seats: z62.union([z62.number(), z62.literal("unlimited")]).nullable().optional().meta(Unrestricted25),
|
|
6579
|
+
// price_per_unit content-rendering fields (OQ-7: persisted-and-rendered, NOT
|
|
6580
|
+
// evaluated — no IR/evaluator branch). `unit` derives from the entitlement;
|
|
6581
|
+
// `period` maps onto the existing `period_scope` column.
|
|
6582
|
+
amount_cents: z62.number().int().optional().meta(Unrestricted25),
|
|
6583
|
+
currency: z62.string().optional().meta(Unrestricted25)
|
|
6584
|
+
}).meta(
|
|
6585
|
+
{
|
|
6586
|
+
id: "EntitlementRule",
|
|
6587
|
+
"x-revturbine-schema-persistence": Persisted25,
|
|
6588
|
+
"x-revturbine-schema-exposure": Internal22,
|
|
6589
|
+
...PLAYBOOK_SDK_FACETS9,
|
|
6590
|
+
// Bucket 2: a rule IS its scope (entitlement + segments + targets); its
|
|
6591
|
+
// limit/enforcement is the mutable payload. Behaviour-only edits coalesce
|
|
6592
|
+
// as the same rule; a scope edit is a new rule identity (plan 121 REQ-3).
|
|
6593
|
+
// segment_ids/targets are jsonb arrays — the DB unique index normalizes them.
|
|
6594
|
+
...mintedIdentity2()
|
|
6595
|
+
}
|
|
6596
|
+
);
|
|
6597
|
+
var EntitlementRuleAnchorSchema2 = makeAnchor2("EntitlementRuleAnchor");
|
|
6598
|
+
var EntitlementRuleValidatedSchema2 = EntitlementRuleSchema2.superRefine(
|
|
6599
|
+
(rule, ctx) => {
|
|
6600
|
+
if (rule.reset_period === "billing_period") {
|
|
6601
|
+
const nonVariationIdx = rule.targets.findIndex(
|
|
6602
|
+
(t) => t.kind !== "plan_variation" && t.kind !== "addon_variation"
|
|
6603
|
+
);
|
|
6604
|
+
if (nonVariationIdx !== -1) {
|
|
6605
|
+
ctx.addIssue({
|
|
6606
|
+
code: "custom",
|
|
6607
|
+
path: ["reset_period"],
|
|
6608
|
+
params: { code: "billing_period_target_mismatch" },
|
|
6609
|
+
message: "reset_period='billing_period' requires all targets to be plan_variation or addon_variation"
|
|
6610
|
+
});
|
|
6611
|
+
}
|
|
6612
|
+
}
|
|
6613
|
+
const allowance = rule.allowance_value;
|
|
6614
|
+
const hasRecurringAllowance = allowance === "unlimited" || typeof allowance === "number" && allowance > 0;
|
|
6615
|
+
if (hasRecurringAllowance && rule.reset_period == null) {
|
|
6616
|
+
ctx.addIssue({
|
|
6617
|
+
code: "custom",
|
|
6618
|
+
path: ["reset_period"],
|
|
6619
|
+
params: { code: "allowance_requires_reset_period" },
|
|
6620
|
+
message: "a non-zero credits allowance_value requires a reset_period (the per-period refill cadence)"
|
|
6621
|
+
});
|
|
6622
|
+
}
|
|
6623
|
+
}
|
|
6624
|
+
);
|
|
6625
|
+
var EntitlementRuleWarningCodeSchema2 = z62.enum(["one_time_period_mismatch"]).meta(
|
|
6626
|
+
{ id: "EntitlementRuleWarningCode", "x-revturbine-schema-persistence": Transient23, "x-revturbine-schema-exposure": External22 }
|
|
6627
|
+
);
|
|
6628
|
+
var EntitlementRuleWarningSchema2 = z62.object({
|
|
6629
|
+
code: EntitlementRuleWarningCodeSchema2.meta(Unrestricted25),
|
|
6630
|
+
message: z62.string().min(1).meta(Unrestricted25),
|
|
6631
|
+
/** Optional dotted field path the warning relates to (e.g. `['targets']`). */
|
|
6632
|
+
path: z62.array(z62.union([z62.string(), z62.number().int()])).optional().meta(Unrestricted25)
|
|
6633
|
+
}).meta(
|
|
6634
|
+
{ id: "EntitlementRuleWarning", "x-revturbine-schema-persistence": Transient23, "x-revturbine-schema-exposure": External22 }
|
|
6635
|
+
);
|
|
6636
|
+
var EntitlementRuleWriteResponseSchema2 = EntitlementRuleSchema2.extend({
|
|
6637
|
+
warnings: z62.array(EntitlementRuleWarningSchema2).optional().meta(Unrestricted25)
|
|
6638
|
+
}).meta(
|
|
6639
|
+
{
|
|
6640
|
+
id: "EntitlementRuleWriteResponse",
|
|
6641
|
+
"x-revturbine-schema-persistence": Transient23,
|
|
6642
|
+
"x-revturbine-schema-exposure": External22
|
|
6643
|
+
}
|
|
6644
|
+
);
|
|
6645
|
+
var EntitlementRuleVariantSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).merge(AnchorFields2).merge(VersionFields2).extend({
|
|
6646
|
+
handle: HandleField2.meta({ ...Unrestricted25, readOnly: true }),
|
|
6647
|
+
rule_id: z62.string().min(1).meta(Unrestricted25),
|
|
6648
|
+
experiment_id: z62.string().min(1).meta(Unrestricted25),
|
|
6649
|
+
variant_name: z62.string().min(1).max(200).meta(Unrestricted25),
|
|
6650
|
+
is_control: z62.boolean().default(false).meta(Unrestricted25),
|
|
6651
|
+
override_fields: z62.record(z62.string(), z62.unknown()).default({}).meta(Unrestricted25)
|
|
6652
|
+
}).meta(
|
|
6653
|
+
{
|
|
6654
|
+
id: "EntitlementRuleVariant",
|
|
6655
|
+
"x-revturbine-schema-persistence": Persisted25,
|
|
6656
|
+
"x-revturbine-schema-exposure": Internal22,
|
|
6657
|
+
...PENDING_PLAYBOOK_FACETS5,
|
|
6658
|
+
...mintedIdentity2()
|
|
6659
|
+
}
|
|
6660
|
+
);
|
|
6661
|
+
var EntitlementWriteSchema2 = toWritableSchema2(EntitlementSchema2);
|
|
6662
|
+
var EntitlementRuleWriteSchema2 = toWritableSchema2(EntitlementRuleSchema2);
|
|
6663
|
+
var entitlementPaths2 = {
|
|
6664
|
+
"/api/entitlement-anchors": {
|
|
6665
|
+
get: operation2({
|
|
6666
|
+
operationId: "listEntitlementAnchors",
|
|
6667
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
6668
|
+
summary: "List entitlement anchors (identity registry)",
|
|
6669
|
+
tags: ["entitlements"],
|
|
6670
|
+
responses: {
|
|
6671
|
+
"200": { description: "Entitlement anchor list", content: { "application/json": { schema: ListEnvelope2(EntitlementAnchorSchema2) } } },
|
|
6672
|
+
default: { description: "Error response", content: { "application/json": { schema: ErrorEnvelope2 } } }
|
|
6673
|
+
},
|
|
6674
|
+
"x-revturbine-operation": { exposure: "internal", resource: "entitlement-anchors", persistence: { table: "entitlements", mode: "list" } }
|
|
6675
|
+
})
|
|
6676
|
+
},
|
|
6677
|
+
"/api/entitlement-rule-anchors": {
|
|
6678
|
+
get: operation2({
|
|
6679
|
+
operationId: "listEntitlementRuleAnchors",
|
|
6680
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
6681
|
+
summary: "List entitlement rule anchors (identity registry)",
|
|
6682
|
+
tags: ["entitlements"],
|
|
6683
|
+
responses: {
|
|
6684
|
+
"200": { description: "Entitlement rule anchor list", content: { "application/json": { schema: ListEnvelope2(EntitlementRuleAnchorSchema2) } } },
|
|
6685
|
+
default: { description: "Error response", content: { "application/json": { schema: ErrorEnvelope2 } } }
|
|
6686
|
+
},
|
|
6687
|
+
"x-revturbine-operation": { exposure: "internal", resource: "entitlement-rule-anchors", persistence: { table: "entitlementRules", mode: "list" } }
|
|
6688
|
+
})
|
|
6689
|
+
},
|
|
6690
|
+
"/api/entitlements": {
|
|
6691
|
+
get: operation2({
|
|
6692
|
+
operationId: "listEntitlements",
|
|
6693
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
6694
|
+
summary: "List entitlements",
|
|
6695
|
+
tags: ["entitlements"],
|
|
6696
|
+
responses: { "200": { description: "Entitlement list", content: { "application/json": { schema: ListEnvelope2(EntitlementSchema2) } } } },
|
|
6697
|
+
"x-revturbine-operation": { exposure: "external", resource: "entitlements", persistence: { table: "entitlementVersions", mode: "list" } }
|
|
6698
|
+
}),
|
|
6699
|
+
post: operation2({
|
|
6700
|
+
operationId: "createEntitlement",
|
|
6701
|
+
summary: "Create entitlement",
|
|
6702
|
+
tags: ["entitlements"],
|
|
6703
|
+
requestBody: { required: true, content: { "application/json": { schema: toCreateSchema2(EntitlementSchema2) } } },
|
|
6704
|
+
responses: { "201": { description: "Created", content: { "application/json": { schema: EntitlementSchema2 } } } },
|
|
6705
|
+
"x-revturbine-operation": { exposure: "external", resource: "entitlements", persistence: { table: "entitlementVersions", mode: "create" } }
|
|
6706
|
+
})
|
|
6707
|
+
},
|
|
6708
|
+
"/api/entitlements/{entitlementId}": {
|
|
6709
|
+
get: operation2({
|
|
6710
|
+
operationId: "getEntitlement",
|
|
6711
|
+
requestParams: { path: z62.object({ entitlementId: z62.string() }) },
|
|
6712
|
+
summary: "Get entitlement",
|
|
6713
|
+
tags: ["entitlements"],
|
|
6714
|
+
responses: { "200": { description: "Entitlement detail", content: { "application/json": { schema: EntitlementSchema2 } } } },
|
|
6715
|
+
"x-revturbine-operation": { exposure: "external", resource: "entitlements", persistence: { table: "entitlementVersions", mode: "get" } }
|
|
6716
|
+
}),
|
|
6717
|
+
patch: operation2({
|
|
6718
|
+
operationId: "updateEntitlement",
|
|
6719
|
+
requestParams: { path: z62.object({ entitlementId: z62.string() }) },
|
|
6720
|
+
summary: "Update entitlement",
|
|
6721
|
+
tags: ["entitlements"],
|
|
6722
|
+
requestBody: { required: true, content: { "application/json": { schema: EntitlementWriteSchema2 } } },
|
|
6723
|
+
responses: { "200": { description: "Updated", content: { "application/json": { schema: EntitlementSchema2 } } } },
|
|
6724
|
+
"x-revturbine-operation": { exposure: "external", resource: "entitlements", persistence: { table: "entitlementVersions", mode: "update" } }
|
|
6725
|
+
}),
|
|
6726
|
+
delete: operation2({
|
|
6727
|
+
operationId: "deleteEntitlement",
|
|
6728
|
+
requestParams: { path: z62.object({ entitlementId: z62.string() }) },
|
|
6729
|
+
summary: "Delete entitlement",
|
|
6730
|
+
tags: ["entitlements"],
|
|
6731
|
+
responses: { "204": { description: "Deleted" }, default: { description: "Error", content: { "application/json": { schema: ErrorEnvelope2 } } } },
|
|
6732
|
+
"x-revturbine-operation": { exposure: "external", resource: "entitlements", persistence: { table: "entitlementVersions", mode: "delete" } }
|
|
6733
|
+
})
|
|
6734
|
+
},
|
|
6735
|
+
"/api/entitlements/{entitlementId}/rules": {
|
|
6736
|
+
get: operation2({
|
|
6737
|
+
operationId: "listEntitlementRules",
|
|
6738
|
+
requestParams: { path: z62.object({ entitlementId: z62.string() }), query: ListQueryParamsSchema2 },
|
|
6739
|
+
summary: "List rules for entitlement",
|
|
6740
|
+
tags: ["entitlements"],
|
|
6741
|
+
responses: { "200": { description: "Rule list", content: { "application/json": { schema: ListEnvelope2(EntitlementRuleSchema2) } } } },
|
|
6742
|
+
"x-revturbine-operation": { exposure: "external", resource: "entitlement-rules", persistence: { table: "entitlementRuleVersions", mode: "list" } }
|
|
6743
|
+
}),
|
|
6744
|
+
post: operation2({
|
|
6745
|
+
operationId: "createEntitlementRule",
|
|
6746
|
+
requestParams: { path: z62.object({ entitlementId: z62.string() }) },
|
|
6747
|
+
summary: "Create entitlement rule",
|
|
6748
|
+
tags: ["entitlements"],
|
|
6749
|
+
requestBody: { required: true, content: { "application/json": { schema: toCreateSchema2(EntitlementRuleSchema2) } } },
|
|
6750
|
+
responses: { "201": { description: "Created", content: { "application/json": { schema: EntitlementRuleWriteResponseSchema2 } } } },
|
|
6751
|
+
"x-revturbine-operation": { exposure: "external", resource: "entitlement-rules", persistence: { table: "entitlementRuleVersions", mode: "create" } }
|
|
6752
|
+
})
|
|
6753
|
+
},
|
|
6754
|
+
"/api/entitlement-rules/{ruleId}": {
|
|
6755
|
+
patch: operation2({
|
|
6756
|
+
operationId: "updateEntitlementRule",
|
|
6757
|
+
requestParams: { path: z62.object({ ruleId: z62.string() }) },
|
|
6758
|
+
summary: "Update entitlement rule",
|
|
6759
|
+
tags: ["entitlements"],
|
|
6760
|
+
requestBody: { required: true, content: { "application/json": { schema: EntitlementRuleWriteSchema2 } } },
|
|
6761
|
+
responses: { "200": { description: "Updated", content: { "application/json": { schema: EntitlementRuleWriteResponseSchema2 } } } },
|
|
6762
|
+
"x-revturbine-operation": { exposure: "external", resource: "entitlement-rules", persistence: { table: "entitlementRuleVersions", mode: "update" } }
|
|
6763
|
+
}),
|
|
6764
|
+
delete: operation2({
|
|
6765
|
+
operationId: "deleteEntitlementRule",
|
|
6766
|
+
requestParams: { path: z62.object({ ruleId: z62.string() }) },
|
|
6767
|
+
summary: "Delete entitlement rule",
|
|
6768
|
+
tags: ["entitlements"],
|
|
6769
|
+
responses: { "204": { description: "Deleted" } },
|
|
6770
|
+
"x-revturbine-operation": { exposure: "external", resource: "entitlement-rules", persistence: { table: "entitlementRuleVersions", mode: "delete" } }
|
|
6771
|
+
})
|
|
6772
|
+
},
|
|
6773
|
+
"/api/entitlement-rules/{ruleId}/duplicate": {
|
|
6774
|
+
post: operation2({
|
|
6775
|
+
operationId: "duplicateEntitlementRule",
|
|
6776
|
+
requestParams: { path: z62.object({ ruleId: z62.string() }) },
|
|
6777
|
+
summary: "Duplicate entitlement rule",
|
|
6778
|
+
tags: ["entitlements"],
|
|
6779
|
+
responses: { "201": { description: "Duplicated", content: { "application/json": { schema: EntitlementRuleWriteResponseSchema2 } } } },
|
|
6780
|
+
"x-revturbine-operation": { exposure: "external", resource: "entitlement-rules", persistence: { table: "entitlementRuleVersions", mode: "duplicate" } }
|
|
6781
|
+
})
|
|
6782
|
+
},
|
|
6783
|
+
"/api/entitlement-rules/{ruleId}/variants": {
|
|
6784
|
+
get: operation2({
|
|
6785
|
+
operationId: "listEntitlementRuleVariants",
|
|
6786
|
+
requestParams: { path: z62.object({ ruleId: z62.string() }), query: ListQueryParamsSchema2 },
|
|
6787
|
+
summary: "List A/B variants for rule",
|
|
6788
|
+
tags: ["entitlements"],
|
|
6789
|
+
responses: { "200": { description: "Variant list", content: { "application/json": { schema: ListEnvelope2(EntitlementRuleVariantSchema2) } } } },
|
|
6790
|
+
"x-revturbine-operation": { exposure: "external", resource: "entitlement-rule-variants", persistence: { table: "entitlementRuleVariants", mode: "list" } }
|
|
6791
|
+
}),
|
|
6792
|
+
post: operation2({
|
|
6793
|
+
operationId: "createEntitlementRuleVariant",
|
|
6794
|
+
requestParams: { path: z62.object({ ruleId: z62.string() }) },
|
|
6795
|
+
summary: "Create rule A/B variant",
|
|
6796
|
+
tags: ["entitlements"],
|
|
6797
|
+
requestBody: { required: true, content: { "application/json": { schema: toCreateSchema2(EntitlementRuleVariantSchema2) } } },
|
|
6798
|
+
responses: { "201": { description: "Created", content: { "application/json": { schema: EntitlementRuleVariantSchema2 } } } },
|
|
6799
|
+
"x-revturbine-operation": { exposure: "external", resource: "entitlement-rule-variants", persistence: { table: "entitlementRuleVariants", mode: "create" } }
|
|
6800
|
+
})
|
|
6801
|
+
}
|
|
6802
|
+
};
|
|
6803
|
+
var { Unrestricted: Unrestricted32 } = DataClassification2;
|
|
6804
|
+
var { Persisted: Persisted32, Transient: Transient32 } = SchemaPersistence2;
|
|
6805
|
+
var { Internal: Internal32 } = SchemaExposure2;
|
|
6806
|
+
var PLAYBOOK_SDK_FACETS22 = schemaFacets2(SchemaContext2.Playbook, { sdkInput: true });
|
|
6807
|
+
var PENDING_PLAYBOOK_SDK_FACETS5 = schemaFacets2(SchemaContext2.Playbook, {
|
|
6808
|
+
inConfig: false,
|
|
6809
|
+
sdkInput: true
|
|
6810
|
+
});
|
|
6811
|
+
var TrialStatusSchema2 = z72.enum(["not_started", "active", "expired", "converted", "cancelled"]).meta(
|
|
6812
|
+
{ id: "TrialStatus", "x-revturbine-schema-persistence": Transient32, "x-revturbine-schema-exposure": Internal32 }
|
|
6813
|
+
);
|
|
6814
|
+
var TrialLimitTypeSchema2 = z72.enum(["time", "usage"]).meta(
|
|
6815
|
+
{ id: "TrialLimitType", "x-revturbine-schema-persistence": Transient32, "x-revturbine-schema-exposure": Internal32 }
|
|
6816
|
+
);
|
|
6817
|
+
var FreeTrialRuleCoreFieldsSchema2 = z72.object({
|
|
6818
|
+
name: NameField2.meta(Unrestricted32),
|
|
6819
|
+
handle: HandleField2.meta(Unrestricted32),
|
|
6820
|
+
// plan_id null = "All plans" — see plans-entitlements-studio-ui.md §2.4.1.
|
|
6821
|
+
plan_id: z72.string().nullable().optional().meta(Unrestricted32),
|
|
6822
|
+
segment_id: z72.string().nullable().optional().meta(Unrestricted32),
|
|
6823
|
+
// Defaults to 'time' so every pre-existing rule keeps its current
|
|
6824
|
+
// duration-based semantics. Set to 'usage' to scope the trial by
|
|
6825
|
+
// consumption of `usage_entitlement_handle` up to
|
|
6826
|
+
// `usage_limit_value`; the time fields below are then ignored.
|
|
6827
|
+
trial_limit_type: TrialLimitTypeSchema2.default("time").meta(Unrestricted32),
|
|
6828
|
+
// Time-based: rule is skipped at runtime when null/blank. The
|
|
6829
|
+
// Default Trial Length global was removed (no fallback exists).
|
|
6830
|
+
duration_days: z72.number().int().min(1).max(365).nullable().optional().meta(Unrestricted32),
|
|
6831
|
+
grace_period_days: z72.number().int().min(0).default(0).meta(Unrestricted32),
|
|
6832
|
+
// Usage-based: the entitlement whose consumption gates the trial,
|
|
6833
|
+
// and the cap. Both required when `trial_limit_type === 'usage'`;
|
|
6834
|
+
// otherwise ignored. Cross-field validation is done at the API
|
|
6835
|
+
// boundary (web app's POST handler) rather than here so partial
|
|
6836
|
+
// drafts stay round-trippable.
|
|
6837
|
+
usage_entitlement_handle: z72.string().min(1).optional().meta(Unrestricted32),
|
|
6838
|
+
usage_limit_value: z72.number().int().min(1).optional().meta(Unrestricted32),
|
|
6839
|
+
require_payment_method: z72.boolean().default(false).meta(Unrestricted32),
|
|
6840
|
+
auto_convert: z72.boolean().default(true).meta(Unrestricted32),
|
|
6841
|
+
/**
|
|
6842
|
+
* Post-trial destination plans. At end of trial the control plane
|
|
6843
|
+
* places the user on either:
|
|
6844
|
+
* - `convert_to_plan_id` — typically a PAID plan; the user must
|
|
6845
|
+
* already have a payment method on file (require_payment_method
|
|
6846
|
+
* usually implies this). Used when `auto_convert: true` AND
|
|
6847
|
+
* the user has met any conversion preconditions.
|
|
6848
|
+
* - `fallback_plan_id` — typically a FREE plan (the company's
|
|
6849
|
+
* Free tier or equivalent "no plan"). Used when the user did
|
|
6850
|
+
* not convert (auto_convert: false, missing payment method,
|
|
6851
|
+
* declined upsell, etc.). When unset the user reverts to
|
|
6852
|
+
* "no plan" / pre-trial state.
|
|
6853
|
+
*/
|
|
6854
|
+
convert_to_plan_id: z72.string().optional().meta(Unrestricted32),
|
|
6855
|
+
fallback_plan_id: z72.string().optional().meta(Unrestricted32),
|
|
6856
|
+
limit_per_customer: z72.number().int().min(1).default(1).meta(Unrestricted32),
|
|
6857
|
+
is_active: z72.boolean().default(true).meta(Unrestricted32),
|
|
6858
|
+
metadata: MetadataField2.meta(Unrestricted32)
|
|
6859
|
+
});
|
|
6860
|
+
var FreeTrialRuleSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).merge(AnchorFields2).merge(VersionFields2).extend({
|
|
6861
|
+
anchor_id: z72.string().min(1).meta({ ...Unrestricted32, readOnly: true })
|
|
6862
|
+
}).merge(FreeTrialRuleCoreFieldsSchema2).meta(
|
|
6863
|
+
{ id: "FreeTrialRule", "x-revturbine-schema-persistence": Persisted32, "x-revturbine-schema-exposure": Internal32, ...PLAYBOOK_SDK_FACETS22, ...namedIdentity2() }
|
|
6864
|
+
);
|
|
6865
|
+
var FreeTrialRuleAnchorSchema2 = makeAnchor2("FreeTrialRuleAnchor");
|
|
6866
|
+
var ReverseTrialStartPolicySchema2 = z72.enum(["signup", "first_premium_access", "manual"]).meta(
|
|
6867
|
+
{ id: "ReverseTrialStartPolicy", "x-revturbine-schema-persistence": Transient32, "x-revturbine-schema-exposure": Internal32 }
|
|
6868
|
+
);
|
|
6869
|
+
var ReverseTrialRuleCoreFieldsSchema2 = z72.object({
|
|
6870
|
+
name: NameField2.meta(Unrestricted32),
|
|
6871
|
+
handle: HandleField2.meta(Unrestricted32),
|
|
6872
|
+
premium_plan_id: z72.string().min(1).meta(Unrestricted32),
|
|
6873
|
+
fallback_plan_id: z72.string().min(1).meta(Unrestricted32),
|
|
6874
|
+
segment_id: z72.string().nullable().optional().meta(Unrestricted32),
|
|
6875
|
+
trial_limit_type: TrialLimitTypeSchema2.default("time").meta(Unrestricted32),
|
|
6876
|
+
duration_days: z72.number().int().min(1).max(365).nullable().optional().meta(Unrestricted32),
|
|
6877
|
+
usage_entitlement_handle: z72.string().min(1).optional().meta(Unrestricted32),
|
|
6878
|
+
usage_limit_value: z72.number().int().min(1).optional().meta(Unrestricted32),
|
|
6879
|
+
start_policy: ReverseTrialStartPolicySchema2.default("signup").meta(Unrestricted32),
|
|
6880
|
+
show_upgrade_prompt_at_day: z72.number().int().min(0).optional().meta(Unrestricted32),
|
|
6881
|
+
entitlements_during_trial: z72.array(z72.string()).default([]).meta(Unrestricted32),
|
|
6882
|
+
is_active: z72.boolean().default(true).meta(Unrestricted32),
|
|
6883
|
+
metadata: MetadataField2.meta(Unrestricted32)
|
|
6884
|
+
});
|
|
6885
|
+
var ReverseTrialRuleSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).merge(AnchorFields2).merge(VersionFields2).extend({
|
|
6886
|
+
anchor_id: z72.string().min(1).meta({ ...Unrestricted32, readOnly: true })
|
|
6887
|
+
}).merge(ReverseTrialRuleCoreFieldsSchema2).meta(
|
|
6888
|
+
{ id: "ReverseTrialRule", "x-revturbine-schema-persistence": Persisted32, "x-revturbine-schema-exposure": Internal32, ...PLAYBOOK_SDK_FACETS22, ...namedIdentity2() }
|
|
6889
|
+
);
|
|
6890
|
+
var ReverseTrialRuleAnchorSchema2 = makeAnchor2("ReverseTrialRuleAnchor");
|
|
6891
|
+
var TrialLimitPolicySchema2 = z72.enum(["1_per_lifetime", "1_per_plan", "1_per_year", "unlimited"]).meta(
|
|
6892
|
+
{ id: "TrialLimitPolicy", "x-revturbine-schema-persistence": Transient32, "x-revturbine-schema-exposure": Internal32 }
|
|
6893
|
+
);
|
|
6894
|
+
var TrialEligibilityScopeSchema2 = z72.enum(["per_customer", "per_email_domain"]).meta(
|
|
6895
|
+
{ id: "TrialEligibilityScope", "x-revturbine-schema-persistence": Transient32, "x-revturbine-schema-exposure": Internal32 }
|
|
6896
|
+
);
|
|
6897
|
+
var FreeTrialSettingsSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).extend({
|
|
6898
|
+
trial_limit_policy: TrialLimitPolicySchema2.default("1_per_lifetime").meta(Unrestricted32),
|
|
6899
|
+
eligibility_scope: TrialEligibilityScopeSchema2.default("per_customer").meta(Unrestricted32)
|
|
6900
|
+
}).meta(
|
|
6901
|
+
{ id: "FreeTrialSettings", "x-revturbine-schema-persistence": Persisted32, "x-revturbine-schema-exposure": Internal32, ...PENDING_PLAYBOOK_SDK_FACETS5 }
|
|
6902
|
+
);
|
|
6903
|
+
var ReverseTrialSettingsSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).extend({
|
|
6904
|
+
trial_limit_policy: TrialLimitPolicySchema2.default("1_per_lifetime").meta(Unrestricted32),
|
|
6905
|
+
eligibility_scope: TrialEligibilityScopeSchema2.default("per_customer").meta(Unrestricted32)
|
|
6906
|
+
}).meta(
|
|
6907
|
+
{ id: "ReverseTrialSettings", "x-revturbine-schema-persistence": Persisted32, "x-revturbine-schema-exposure": Internal32, ...PENDING_PLAYBOOK_SDK_FACETS5 }
|
|
6908
|
+
);
|
|
6909
|
+
var TrialInstanceSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).extend({
|
|
6910
|
+
customer_id: z72.string().min(1).meta(Unrestricted32),
|
|
6911
|
+
rule_id: z72.string().min(1).meta(Unrestricted32),
|
|
6912
|
+
rule_type: z72.enum(["free_trial", "reverse_trial"]).meta(Unrestricted32),
|
|
6913
|
+
plan_id: z72.string().min(1).meta(Unrestricted32),
|
|
6914
|
+
status: TrialStatusSchema2.default("active").meta(Unrestricted32),
|
|
6915
|
+
started_at: z72.string().datetime().meta({ ...Unrestricted32, readOnly: true }),
|
|
6916
|
+
/**
|
|
6917
|
+
* Time-based expiry. Required for time-based trials; null for
|
|
6918
|
+
* pure usage-based trials (which expire when consumption crosses
|
|
6919
|
+
* `usage_limit_value` regardless of clock time).
|
|
6920
|
+
*/
|
|
6921
|
+
expires_at: z72.string().datetime().nullable().optional().meta(Unrestricted32),
|
|
6922
|
+
/**
|
|
6923
|
+
* Snapshot of the rule's `trial_limit_type` at the moment the
|
|
6924
|
+
* instance was created. Persisted so subsequent changes to the
|
|
6925
|
+
* rule's mode don't retroactively alter the user's trial
|
|
6926
|
+
* semantics. Defaults to 'time' for backward compatibility with
|
|
6927
|
+
* pre-existing instances.
|
|
6928
|
+
*/
|
|
6929
|
+
trial_limit_type: TrialLimitTypeSchema2.default("time").meta(Unrestricted32),
|
|
6930
|
+
/**
|
|
6931
|
+
* Snapshot of the rule's `usage_entitlement_handle` for
|
|
6932
|
+
* usage-based trials. Server queries the user's current
|
|
6933
|
+
* consumption of this entitlement to derive
|
|
6934
|
+
* `UserTrialStatus.usage_consumed` at read time.
|
|
6935
|
+
*/
|
|
6936
|
+
usage_entitlement_handle: z72.string().min(1).optional().meta(Unrestricted32),
|
|
6937
|
+
/**
|
|
6938
|
+
* Snapshot of the rule's `usage_limit_value`. Persisted so
|
|
6939
|
+
* mid-trial limit changes on the rule don't shrink/expand a
|
|
6940
|
+
* user's in-flight trial.
|
|
6941
|
+
*/
|
|
6942
|
+
usage_limit_value: z72.number().int().min(1).optional().meta(Unrestricted32),
|
|
6943
|
+
converted_at: NullableDatetimeField2.meta(Unrestricted32),
|
|
6944
|
+
cancelled_at: NullableDatetimeField2.meta(Unrestricted32),
|
|
6945
|
+
metadata: MetadataField2.meta(Unrestricted32)
|
|
6946
|
+
}).meta(
|
|
6947
|
+
{ id: "TrialInstance", "x-revturbine-schema-persistence": Persisted32, "x-revturbine-schema-exposure": Internal32 }
|
|
6948
|
+
);
|
|
6949
|
+
var trialPaths2 = {
|
|
6950
|
+
"/api/free-trial-rule-anchors": {
|
|
6951
|
+
get: operation2({
|
|
6952
|
+
operationId: "listFreeTrialRuleAnchors",
|
|
6953
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
6954
|
+
summary: "List free trial rule anchors (identity registry)",
|
|
6955
|
+
tags: ["trials"],
|
|
6956
|
+
responses: {
|
|
6957
|
+
"200": { description: "Free trial rule anchor list", content: { "application/json": { schema: ListEnvelope2(FreeTrialRuleAnchorSchema2) } } },
|
|
6958
|
+
default: { description: "Error response", content: { "application/json": { schema: ErrorEnvelope2 } } }
|
|
6959
|
+
},
|
|
6960
|
+
"x-revturbine-operation": { exposure: "internal", resource: "free-trial-rule-anchors", persistence: { table: "freeTrialRules", mode: "list" } }
|
|
6961
|
+
})
|
|
6962
|
+
},
|
|
6963
|
+
"/api/trials/free-rules": {
|
|
6964
|
+
get: operation2({
|
|
6965
|
+
operationId: "listFreeTrialRules",
|
|
6966
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
6967
|
+
summary: "List free trial rules",
|
|
6968
|
+
tags: ["trials"],
|
|
6969
|
+
responses: { "200": { description: "Free trial rule list", content: { "application/json": { schema: ListEnvelope2(FreeTrialRuleSchema2) } } } },
|
|
6970
|
+
"x-revturbine-operation": { exposure: "internal", resource: "trial-rules", persistence: { table: "freeTrialRuleVersions", mode: "list" } }
|
|
6971
|
+
}),
|
|
6972
|
+
post: operation2({
|
|
6973
|
+
operationId: "createFreeTrialRule",
|
|
6974
|
+
summary: "Create free trial rule",
|
|
6975
|
+
tags: ["trials"],
|
|
6976
|
+
requestBody: { required: true, content: { "application/json": { schema: FreeTrialRuleSchema2 } } },
|
|
6977
|
+
responses: { "201": { description: "Created", content: { "application/json": { schema: FreeTrialRuleSchema2 } } } },
|
|
6978
|
+
"x-revturbine-operation": { exposure: "internal", resource: "trial-rules", persistence: { table: "freeTrialRuleVersions", mode: "create" } }
|
|
6979
|
+
})
|
|
6980
|
+
},
|
|
6981
|
+
"/api/trials/free-rules/{ruleId}": {
|
|
6982
|
+
get: operation2({
|
|
6983
|
+
operationId: "getFreeTrialRule",
|
|
6984
|
+
requestParams: { path: z72.object({ ruleId: z72.string() }) },
|
|
6985
|
+
summary: "Get free trial rule",
|
|
6986
|
+
tags: ["trials"],
|
|
6987
|
+
responses: { "200": { description: "Free trial rule", content: { "application/json": { schema: FreeTrialRuleSchema2 } } } },
|
|
6988
|
+
"x-revturbine-operation": { exposure: "internal", resource: "trial-rules", persistence: { table: "freeTrialRuleVersions", mode: "get" } }
|
|
6989
|
+
}),
|
|
6990
|
+
patch: operation2({
|
|
6991
|
+
operationId: "updateFreeTrialRule",
|
|
6992
|
+
requestParams: { path: z72.object({ ruleId: z72.string() }) },
|
|
6993
|
+
summary: "Update free trial rule",
|
|
6994
|
+
tags: ["trials"],
|
|
6995
|
+
requestBody: { required: true, content: { "application/json": { schema: FreeTrialRuleSchema2.partial() } } },
|
|
6996
|
+
responses: { "200": { description: "Updated", content: { "application/json": { schema: FreeTrialRuleSchema2 } } } },
|
|
6997
|
+
"x-revturbine-operation": { exposure: "internal", resource: "trial-rules", persistence: { table: "freeTrialRuleVersions", mode: "update" } }
|
|
6998
|
+
}),
|
|
6999
|
+
delete: operation2({
|
|
7000
|
+
operationId: "deleteFreeTrialRule",
|
|
7001
|
+
requestParams: { path: z72.object({ ruleId: z72.string() }) },
|
|
7002
|
+
summary: "Delete free trial rule",
|
|
7003
|
+
tags: ["trials"],
|
|
7004
|
+
responses: { "204": { description: "Deleted" } },
|
|
7005
|
+
"x-revturbine-operation": { exposure: "internal", resource: "trial-rules", persistence: { table: "freeTrialRuleVersions", mode: "delete" } }
|
|
7006
|
+
})
|
|
7007
|
+
},
|
|
7008
|
+
"/api/reverse-trial-rule-anchors": {
|
|
7009
|
+
get: operation2({
|
|
7010
|
+
operationId: "listReverseTrialRuleAnchors",
|
|
7011
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
7012
|
+
summary: "List reverse trial rule anchors (identity registry)",
|
|
7013
|
+
tags: ["trials"],
|
|
7014
|
+
responses: {
|
|
7015
|
+
"200": { description: "Reverse trial rule anchor list", content: { "application/json": { schema: ListEnvelope2(ReverseTrialRuleAnchorSchema2) } } },
|
|
7016
|
+
default: { description: "Error response", content: { "application/json": { schema: ErrorEnvelope2 } } }
|
|
7017
|
+
},
|
|
7018
|
+
"x-revturbine-operation": { exposure: "internal", resource: "reverse-trial-rule-anchors", persistence: { table: "reverseTrialRules", mode: "list" } }
|
|
7019
|
+
})
|
|
7020
|
+
},
|
|
7021
|
+
"/api/trials/reverse-rules": {
|
|
7022
|
+
get: operation2({
|
|
7023
|
+
operationId: "listReverseTrialRules",
|
|
7024
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
7025
|
+
summary: "List reverse trial rules",
|
|
7026
|
+
tags: ["trials"],
|
|
7027
|
+
responses: { "200": { description: "Reverse trial rule list", content: { "application/json": { schema: ListEnvelope2(ReverseTrialRuleSchema2) } } } },
|
|
7028
|
+
"x-revturbine-operation": { exposure: "internal", resource: "reverse-trial-rules", persistence: { table: "reverseTrialRuleVersions", mode: "list" } }
|
|
7029
|
+
}),
|
|
7030
|
+
post: operation2({
|
|
7031
|
+
operationId: "createReverseTrialRule",
|
|
7032
|
+
summary: "Create reverse trial rule",
|
|
7033
|
+
tags: ["trials"],
|
|
7034
|
+
requestBody: { required: true, content: { "application/json": { schema: ReverseTrialRuleSchema2 } } },
|
|
7035
|
+
responses: { "201": { description: "Created", content: { "application/json": { schema: ReverseTrialRuleSchema2 } } } },
|
|
7036
|
+
"x-revturbine-operation": { exposure: "internal", resource: "reverse-trial-rules", persistence: { table: "reverseTrialRuleVersions", mode: "create" } }
|
|
7037
|
+
})
|
|
7038
|
+
},
|
|
7039
|
+
"/api/trials/reverse-rules/{ruleId}": {
|
|
7040
|
+
get: operation2({
|
|
7041
|
+
operationId: "getReverseTrialRule",
|
|
7042
|
+
requestParams: { path: z72.object({ ruleId: z72.string() }) },
|
|
7043
|
+
summary: "Get reverse trial rule",
|
|
7044
|
+
tags: ["trials"],
|
|
7045
|
+
responses: { "200": { description: "Reverse trial rule", content: { "application/json": { schema: ReverseTrialRuleSchema2 } } } },
|
|
7046
|
+
"x-revturbine-operation": { exposure: "internal", resource: "reverse-trial-rules", persistence: { table: "reverseTrialRuleVersions", mode: "get" } }
|
|
7047
|
+
}),
|
|
7048
|
+
patch: operation2({
|
|
7049
|
+
operationId: "updateReverseTrialRule",
|
|
7050
|
+
requestParams: { path: z72.object({ ruleId: z72.string() }) },
|
|
7051
|
+
summary: "Update reverse trial rule",
|
|
7052
|
+
tags: ["trials"],
|
|
7053
|
+
requestBody: { required: true, content: { "application/json": { schema: ReverseTrialRuleSchema2.partial() } } },
|
|
7054
|
+
responses: { "200": { description: "Updated", content: { "application/json": { schema: ReverseTrialRuleSchema2 } } } },
|
|
7055
|
+
"x-revturbine-operation": { exposure: "internal", resource: "reverse-trial-rules", persistence: { table: "reverseTrialRuleVersions", mode: "update" } }
|
|
7056
|
+
}),
|
|
7057
|
+
delete: operation2({
|
|
7058
|
+
operationId: "deleteReverseTrialRule",
|
|
7059
|
+
requestParams: { path: z72.object({ ruleId: z72.string() }) },
|
|
7060
|
+
summary: "Delete reverse trial rule",
|
|
7061
|
+
tags: ["trials"],
|
|
7062
|
+
responses: { "204": { description: "Deleted" } },
|
|
7063
|
+
"x-revturbine-operation": { exposure: "internal", resource: "reverse-trial-rules", persistence: { table: "reverseTrialRuleVersions", mode: "delete" } }
|
|
7064
|
+
})
|
|
7065
|
+
},
|
|
7066
|
+
"/api/trials/free-settings": {
|
|
7067
|
+
get: operation2({
|
|
7068
|
+
operationId: "listFreeTrialSettings",
|
|
7069
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
7070
|
+
summary: "List free trial settings (one row per tenant)",
|
|
7071
|
+
tags: ["trials"],
|
|
7072
|
+
responses: { "200": { description: "Free trial settings list", content: { "application/json": { schema: ListEnvelope2(FreeTrialSettingsSchema2) } } } },
|
|
7073
|
+
"x-revturbine-operation": { exposure: "internal", resource: "free-trial-settings", persistence: { table: "freeTrialSettings", mode: "list" } }
|
|
7074
|
+
}),
|
|
7075
|
+
post: operation2({
|
|
7076
|
+
operationId: "createFreeTrialSettings",
|
|
7077
|
+
summary: "Create free trial settings",
|
|
7078
|
+
tags: ["trials"],
|
|
7079
|
+
requestBody: { required: true, content: { "application/json": { schema: FreeTrialSettingsSchema2 } } },
|
|
7080
|
+
responses: { "201": { description: "Created", content: { "application/json": { schema: FreeTrialSettingsSchema2 } } } },
|
|
7081
|
+
"x-revturbine-operation": { exposure: "internal", resource: "free-trial-settings", persistence: { table: "freeTrialSettings", mode: "create" } }
|
|
7082
|
+
})
|
|
7083
|
+
},
|
|
7084
|
+
"/api/trials/free-settings/{settingsId}": {
|
|
7085
|
+
get: operation2({
|
|
7086
|
+
operationId: "getFreeTrialSettings",
|
|
7087
|
+
requestParams: { path: z72.object({ settingsId: z72.string() }) },
|
|
7088
|
+
summary: "Get free trial settings",
|
|
7089
|
+
tags: ["trials"],
|
|
7090
|
+
responses: { "200": { description: "Free trial settings", content: { "application/json": { schema: FreeTrialSettingsSchema2 } } } },
|
|
7091
|
+
"x-revturbine-operation": { exposure: "internal", resource: "free-trial-settings", persistence: { table: "freeTrialSettings", mode: "get" } }
|
|
7092
|
+
}),
|
|
7093
|
+
patch: operation2({
|
|
7094
|
+
operationId: "updateFreeTrialSettings",
|
|
7095
|
+
requestParams: { path: z72.object({ settingsId: z72.string() }) },
|
|
7096
|
+
summary: "Update free trial settings",
|
|
7097
|
+
tags: ["trials"],
|
|
7098
|
+
requestBody: { required: true, content: { "application/json": { schema: FreeTrialSettingsSchema2.partial() } } },
|
|
7099
|
+
responses: { "200": { description: "Updated", content: { "application/json": { schema: FreeTrialSettingsSchema2 } } } },
|
|
7100
|
+
"x-revturbine-operation": { exposure: "internal", resource: "free-trial-settings", persistence: { table: "freeTrialSettings", mode: "update" } }
|
|
7101
|
+
})
|
|
7102
|
+
},
|
|
7103
|
+
"/api/trials/reverse-settings": {
|
|
7104
|
+
get: operation2({
|
|
7105
|
+
operationId: "listReverseTrialSettings",
|
|
7106
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
7107
|
+
summary: "List reverse trial settings (one row per tenant)",
|
|
7108
|
+
tags: ["trials"],
|
|
7109
|
+
responses: { "200": { description: "Reverse trial settings list", content: { "application/json": { schema: ListEnvelope2(ReverseTrialSettingsSchema2) } } } },
|
|
7110
|
+
"x-revturbine-operation": { exposure: "internal", resource: "reverse-trial-settings", persistence: { table: "reverseTrialSettings", mode: "list" } }
|
|
7111
|
+
}),
|
|
7112
|
+
post: operation2({
|
|
7113
|
+
operationId: "createReverseTrialSettings",
|
|
7114
|
+
summary: "Create reverse trial settings",
|
|
7115
|
+
tags: ["trials"],
|
|
7116
|
+
requestBody: { required: true, content: { "application/json": { schema: ReverseTrialSettingsSchema2 } } },
|
|
7117
|
+
responses: { "201": { description: "Created", content: { "application/json": { schema: ReverseTrialSettingsSchema2 } } } },
|
|
7118
|
+
"x-revturbine-operation": { exposure: "internal", resource: "reverse-trial-settings", persistence: { table: "reverseTrialSettings", mode: "create" } }
|
|
7119
|
+
})
|
|
7120
|
+
},
|
|
7121
|
+
"/api/trials/reverse-settings/{settingsId}": {
|
|
7122
|
+
get: operation2({
|
|
7123
|
+
operationId: "getReverseTrialSettings",
|
|
7124
|
+
requestParams: { path: z72.object({ settingsId: z72.string() }) },
|
|
7125
|
+
summary: "Get reverse trial settings",
|
|
7126
|
+
tags: ["trials"],
|
|
7127
|
+
responses: { "200": { description: "Reverse trial settings", content: { "application/json": { schema: ReverseTrialSettingsSchema2 } } } },
|
|
7128
|
+
"x-revturbine-operation": { exposure: "internal", resource: "reverse-trial-settings", persistence: { table: "reverseTrialSettings", mode: "get" } }
|
|
7129
|
+
}),
|
|
7130
|
+
patch: operation2({
|
|
7131
|
+
operationId: "updateReverseTrialSettings",
|
|
7132
|
+
requestParams: { path: z72.object({ settingsId: z72.string() }) },
|
|
7133
|
+
summary: "Update reverse trial settings",
|
|
7134
|
+
tags: ["trials"],
|
|
7135
|
+
requestBody: { required: true, content: { "application/json": { schema: ReverseTrialSettingsSchema2.partial() } } },
|
|
7136
|
+
responses: { "200": { description: "Updated", content: { "application/json": { schema: ReverseTrialSettingsSchema2 } } } },
|
|
7137
|
+
"x-revturbine-operation": { exposure: "internal", resource: "reverse-trial-settings", persistence: { table: "reverseTrialSettings", mode: "update" } }
|
|
7138
|
+
})
|
|
7139
|
+
},
|
|
7140
|
+
"/api/trials/instances": {
|
|
7141
|
+
get: operation2({
|
|
7142
|
+
operationId: "listTrialInstances",
|
|
7143
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
7144
|
+
summary: "List trial instances",
|
|
7145
|
+
tags: ["trials"],
|
|
7146
|
+
responses: { "200": { description: "Trial instance list", content: { "application/json": { schema: ListEnvelope2(TrialInstanceSchema2) } } } },
|
|
7147
|
+
"x-revturbine-operation": { exposure: "internal", resource: "trial-instances", persistence: { table: "trialInstances", mode: "list" } }
|
|
7148
|
+
})
|
|
7149
|
+
},
|
|
7150
|
+
"/api/trials/instances/{instanceId}": {
|
|
7151
|
+
get: operation2({
|
|
7152
|
+
operationId: "getTrialInstance",
|
|
7153
|
+
requestParams: { path: z72.object({ instanceId: z72.string() }) },
|
|
7154
|
+
summary: "Get trial instance",
|
|
7155
|
+
tags: ["trials"],
|
|
7156
|
+
responses: { "200": { description: "Trial instance", content: { "application/json": { schema: TrialInstanceSchema2 } } } },
|
|
7157
|
+
"x-revturbine-operation": { exposure: "internal", resource: "trial-instances", persistence: { table: "trialInstances", mode: "get" } }
|
|
7158
|
+
})
|
|
7159
|
+
},
|
|
7160
|
+
"/api/trials/instances/{instanceId}/cancel": {
|
|
7161
|
+
post: operation2({
|
|
7162
|
+
operationId: "cancelTrialInstance",
|
|
7163
|
+
requestParams: { path: z72.object({ instanceId: z72.string() }) },
|
|
7164
|
+
summary: "Cancel an active trial",
|
|
7165
|
+
tags: ["trials"],
|
|
7166
|
+
responses: { "200": { description: "Cancelled", content: { "application/json": { schema: TrialInstanceSchema2 } } } },
|
|
7167
|
+
"x-revturbine-operation": { exposure: "internal", resource: "trial-instances", persistence: { table: "trialInstances", mode: "update" } }
|
|
7168
|
+
})
|
|
7169
|
+
},
|
|
7170
|
+
"/api/trials/instances/{instanceId}/convert": {
|
|
7171
|
+
post: operation2({
|
|
7172
|
+
operationId: "convertTrialInstance",
|
|
7173
|
+
requestParams: { path: z72.object({ instanceId: z72.string() }) },
|
|
7174
|
+
summary: "Convert trial to paid subscription",
|
|
7175
|
+
tags: ["trials"],
|
|
7176
|
+
requestBody: { required: true, content: { "application/json": { schema: z72.object({ plan_id: z72.string().optional() }) } } },
|
|
7177
|
+
responses: { "200": { description: "Converted", content: { "application/json": { schema: TrialInstanceSchema2 } } } },
|
|
7178
|
+
"x-revturbine-operation": { exposure: "internal", resource: "trial-instances", persistence: { table: "trialInstances", mode: "update" } }
|
|
7179
|
+
})
|
|
7180
|
+
}
|
|
7181
|
+
};
|
|
7182
|
+
var { Unrestricted: Unrestricted42, Financial: Financial4 } = DataClassification2;
|
|
7183
|
+
var { Persisted: Persisted42, Transient: Transient42 } = SchemaPersistence2;
|
|
7184
|
+
var { External: External32 } = SchemaExposure2;
|
|
7185
|
+
var PLAYBOOK_SDK_FACETS32 = schemaFacets2(SchemaContext2.Playbook, { sdkInput: true });
|
|
7186
|
+
var PLAYBOOK_AUTHORING_FACETS3 = schemaFacets2(SchemaContext2.Playbook, { sdkInput: false });
|
|
7187
|
+
var BILLING_FACETS3 = schemaFacets2(SchemaContext2.Billing, { sdkInput: false });
|
|
7188
|
+
var PlanVisibilitySchema2 = z82.enum(["public", "unlisted", "legacy"]).meta(
|
|
7189
|
+
{
|
|
7190
|
+
id: "PlanVisibility",
|
|
7191
|
+
"x-revturbine-schema-persistence": Transient42,
|
|
7192
|
+
"x-revturbine-schema-exposure": External32
|
|
7193
|
+
}
|
|
7194
|
+
);
|
|
7195
|
+
var PricingModelSchema2 = z82.enum(["flat", "per_unit", "tiered", "metered"]).meta(
|
|
7196
|
+
{
|
|
7197
|
+
id: "PricingModel",
|
|
7198
|
+
"x-revturbine-schema-persistence": Transient42,
|
|
7199
|
+
"x-revturbine-schema-exposure": External32
|
|
7200
|
+
}
|
|
7201
|
+
);
|
|
7202
|
+
var PriceSourceSchema2 = z82.enum(["stripe", "static"]).meta(
|
|
7203
|
+
{
|
|
7204
|
+
id: "PriceSource",
|
|
7205
|
+
"x-revturbine-schema-persistence": Transient42,
|
|
7206
|
+
"x-revturbine-schema-exposure": External32
|
|
7207
|
+
}
|
|
7208
|
+
);
|
|
7209
|
+
var PlanSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).merge(AnchorFields2).merge(VersionFields2).extend({
|
|
7210
|
+
anchor_id: z82.string().min(1).meta({ ...Unrestricted42, readOnly: true }),
|
|
7211
|
+
name: NameField2.meta(Unrestricted42),
|
|
7212
|
+
handle: HandleField2.meta(Unrestricted42),
|
|
7213
|
+
tier_position: z82.number().int().min(0).default(0).meta(Unrestricted42),
|
|
7214
|
+
sort_order: z82.number().int().default(0).meta(Unrestricted42),
|
|
7215
|
+
// Plan-level visibility default (plan 91 Part B). Distinct from the
|
|
7216
|
+
// per-variation `PlanVariationSchema.visibility`: a free/custom tier with no
|
|
7217
|
+
// priced variation can still be unlisted/legacy. Persisted so it round-trips
|
|
7218
|
+
// (plan 146 found it was declared on the portable config but had no column).
|
|
7219
|
+
visibility: PlanVisibilitySchema2.default("public").meta(Unrestricted42),
|
|
7220
|
+
metadata: MetadataField2.meta(Unrestricted42)
|
|
7221
|
+
}).meta(
|
|
7222
|
+
{
|
|
7223
|
+
id: "Plan",
|
|
7224
|
+
"x-revturbine-schema-persistence": Persisted42,
|
|
7225
|
+
"x-revturbine-schema-exposure": External32,
|
|
7226
|
+
...PLAYBOOK_SDK_FACETS32,
|
|
7227
|
+
...namedIdentity2()
|
|
7228
|
+
}
|
|
7229
|
+
);
|
|
7230
|
+
var PlanAnchorSchema2 = makeAnchor2("PlanAnchor");
|
|
7231
|
+
var PlanVariationSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).merge(AnchorFields2).merge(VersionFields2).extend({
|
|
7232
|
+
anchor_id: z82.string().min(1).meta({ ...Unrestricted42, readOnly: true }),
|
|
7233
|
+
handle: HandleField2.meta({ ...Unrestricted42, readOnly: true }),
|
|
7234
|
+
plan_id: z82.string().min(1).meta(Unrestricted42),
|
|
7235
|
+
billing_period: z82.enum(["monthly", "annual", "one_time", "custom"]).meta(Unrestricted42),
|
|
7236
|
+
segment_id: z82.string().nullable().default(null).meta(Unrestricted42),
|
|
7237
|
+
price_amount: z82.number().min(0).meta(Financial4),
|
|
7238
|
+
pricing_model: PricingModelSchema2.meta(Unrestricted42),
|
|
7239
|
+
visibility: PlanVisibilitySchema2.default("public").meta(Unrestricted42),
|
|
7240
|
+
// Soft reference → StripePrice.stripe_price_id (the backend Stripe-price mirror).
|
|
7241
|
+
// No DB foreign key: stripe_price_id lives on the append-only version tables and
|
|
7242
|
+
// a hard FK would block plan-122 price-deletion sync (plan 118 FK decision, devkit #472).
|
|
7243
|
+
stripe_price_id: z82.string().optional().meta(Unrestricted42),
|
|
7244
|
+
price_source: PriceSourceSchema2.default("static").meta(Unrestricted42)
|
|
7245
|
+
}).meta(
|
|
7246
|
+
{
|
|
7247
|
+
id: "PlanVariation",
|
|
7248
|
+
"x-revturbine-schema-persistence": Persisted42,
|
|
7249
|
+
"x-revturbine-schema-exposure": External32,
|
|
7250
|
+
...BILLING_FACETS3,
|
|
7251
|
+
...mintedIdentity2()
|
|
7252
|
+
}
|
|
7253
|
+
);
|
|
7254
|
+
var PlanVariationAnchorSchema2 = makeAnchor2("PlanVariationAnchor");
|
|
7255
|
+
var AddOnSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).merge(AnchorFields2).merge(VersionFields2).extend({
|
|
7256
|
+
anchor_id: z82.string().min(1).meta({ ...Unrestricted42, readOnly: true }),
|
|
7257
|
+
name: NameField2.meta(Unrestricted42),
|
|
7258
|
+
handle: HandleField2.meta(Unrestricted42),
|
|
7259
|
+
sort_order: z82.number().int().default(0).meta(Unrestricted42),
|
|
7260
|
+
// Add-on visibility default — same rationale as PlanSchema (plan 91 Part B);
|
|
7261
|
+
// metadata, not price, so it lives on the add-on independent of variations.
|
|
7262
|
+
visibility: PlanVisibilitySchema2.default("public").meta(Unrestricted42),
|
|
7263
|
+
metadata: MetadataField2.meta(Unrestricted42)
|
|
7264
|
+
}).meta(
|
|
7265
|
+
{
|
|
7266
|
+
id: "AddOn",
|
|
7267
|
+
"x-revturbine-schema-persistence": Persisted42,
|
|
7268
|
+
"x-revturbine-schema-exposure": External32,
|
|
7269
|
+
...PLAYBOOK_AUTHORING_FACETS3,
|
|
7270
|
+
...namedIdentity2()
|
|
7271
|
+
}
|
|
7272
|
+
);
|
|
7273
|
+
var AddOnAnchorSchema2 = makeAnchor2("AddOnAnchor");
|
|
7274
|
+
var AddOnVariationSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).merge(AnchorFields2).merge(VersionFields2).extend({
|
|
7275
|
+
anchor_id: z82.string().min(1).meta({ ...Unrestricted42, readOnly: true }),
|
|
7276
|
+
handle: HandleField2.meta({ ...Unrestricted42, readOnly: true }),
|
|
7277
|
+
addon_id: z82.string().min(1).meta(Unrestricted42),
|
|
7278
|
+
// 'one_time' is first-class — Stripe one-time Prices (credit packs,
|
|
7279
|
+
// expansion packages) bind here without coercion to 'custom'.
|
|
7280
|
+
billing_period: z82.enum(["monthly", "annual", "one_time", "custom"]).meta(Unrestricted42),
|
|
7281
|
+
segment_id: z82.string().nullable().default(null).meta(Unrestricted42),
|
|
7282
|
+
price_amount: z82.number().min(0).meta(Financial4),
|
|
7283
|
+
pricing_model: PricingModelSchema2.meta(Unrestricted42),
|
|
7284
|
+
visibility: PlanVisibilitySchema2.default("public").meta(Unrestricted42),
|
|
7285
|
+
// Soft reference → StripePrice.stripe_price_id (the backend Stripe-price mirror).
|
|
7286
|
+
// No DB foreign key: stripe_price_id lives on the append-only version tables and
|
|
7287
|
+
// a hard FK would block plan-122 price-deletion sync (plan 118 FK decision, devkit #472).
|
|
7288
|
+
stripe_price_id: z82.string().optional().meta(Unrestricted42),
|
|
7289
|
+
price_source: PriceSourceSchema2.default("static").meta(Unrestricted42)
|
|
7290
|
+
}).meta(
|
|
7291
|
+
{
|
|
7292
|
+
id: "AddOnVariation",
|
|
7293
|
+
"x-revturbine-schema-persistence": Persisted42,
|
|
7294
|
+
"x-revturbine-schema-exposure": External32,
|
|
7295
|
+
...BILLING_FACETS3,
|
|
7296
|
+
...mintedIdentity2()
|
|
7297
|
+
}
|
|
7298
|
+
);
|
|
7299
|
+
var AddOnVariationAnchorSchema2 = makeAnchor2("AddOnVariationAnchor");
|
|
7300
|
+
var StripePriceBillingPeriodSchema2 = z82.enum(["monthly", "annual", "one_time", "custom"]).meta({
|
|
7301
|
+
id: "StripePriceBillingPeriod",
|
|
7302
|
+
"x-revturbine-schema-persistence": Transient42,
|
|
7303
|
+
"x-revturbine-schema-exposure": External32
|
|
7304
|
+
});
|
|
7305
|
+
var StripePriceSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).extend({
|
|
7306
|
+
stripe_price_id: z82.string().min(1).meta(Unrestricted42),
|
|
7307
|
+
stripe_product_id: z82.string().min(1).meta(Unrestricted42),
|
|
7308
|
+
billing_period: StripePriceBillingPeriodSchema2.meta(Unrestricted42),
|
|
7309
|
+
unit_amount_cents: z82.number().int().min(0).nullable().default(null).meta(Financial4),
|
|
7310
|
+
currency: CurrencySchema2.meta(Financial4),
|
|
7311
|
+
pricing_model: PricingModelSchema2.meta(Unrestricted42),
|
|
7312
|
+
nickname: z82.string().nullable().default(null).meta(Unrestricted42),
|
|
7313
|
+
// Placeholder/seed price (demo or pre-integration tenants) vs a real
|
|
7314
|
+
// Stripe-synced mirror row. Migration backfills existing seed rows to true.
|
|
7315
|
+
is_mock: z82.boolean().default(false).meta(Unrestricted42),
|
|
7316
|
+
// Timestamp of the last successful sync from Stripe; null for rows that were
|
|
7317
|
+
// never sourced from a real Stripe Price (seeds/mocks).
|
|
7318
|
+
last_updated_from_stripe: NullableDatetimeField2.meta(Unrestricted42)
|
|
7319
|
+
}).meta({
|
|
7320
|
+
id: "StripePrice",
|
|
7321
|
+
"x-revturbine-schema-persistence": Persisted42,
|
|
7322
|
+
"x-revturbine-schema-exposure": External32,
|
|
7323
|
+
...BILLING_FACETS3
|
|
7324
|
+
});
|
|
7325
|
+
var PlanWriteSchema2 = toWritableSchema2(PlanSchema2);
|
|
7326
|
+
var PlanVariationWriteSchema2 = toWritableSchema2(PlanVariationSchema2);
|
|
7327
|
+
var AddOnWriteSchema2 = toWritableSchema2(AddOnSchema2);
|
|
7328
|
+
var AddOnVariationWriteSchema2 = toWritableSchema2(AddOnVariationSchema2);
|
|
7329
|
+
var StripePriceWriteSchema2 = toWritableSchema2(StripePriceSchema2);
|
|
7330
|
+
var planPaths2 = {
|
|
7331
|
+
"/api/plan-anchors": {
|
|
7332
|
+
get: operation2({
|
|
7333
|
+
operationId: "listPlanAnchors",
|
|
7334
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
7335
|
+
summary: "List plan anchors (identity registry)",
|
|
7336
|
+
tags: ["plans"],
|
|
7337
|
+
responses: {
|
|
7338
|
+
"200": { description: "Plan anchor list", content: { "application/json": { schema: ListEnvelope2(PlanAnchorSchema2) } } },
|
|
7339
|
+
default: { description: "Error response", content: { "application/json": { schema: ErrorEnvelope2 } } }
|
|
7340
|
+
},
|
|
7341
|
+
"x-revturbine-operation": { exposure: "internal", resource: "plan-anchors", persistence: { table: "plans", mode: "list" } }
|
|
7342
|
+
})
|
|
7343
|
+
},
|
|
7344
|
+
"/api/plans": {
|
|
7345
|
+
get: operation2({
|
|
7346
|
+
operationId: "listPlans",
|
|
7347
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
7348
|
+
summary: "List plans",
|
|
7349
|
+
tags: ["plans"],
|
|
7350
|
+
responses: {
|
|
7351
|
+
"200": { description: "Plan list response", content: { "application/json": { schema: ListEnvelope2(PlanSchema2) } } },
|
|
7352
|
+
default: { description: "Error response", content: { "application/json": { schema: ErrorEnvelope2 } } }
|
|
7353
|
+
},
|
|
7354
|
+
"x-revturbine-operation": { exposure: "external", resource: "plans", persistence: { table: "planVersions", mode: "list" } }
|
|
7355
|
+
}),
|
|
7356
|
+
post: operation2({
|
|
7357
|
+
operationId: "createPlan",
|
|
7358
|
+
summary: "Create plan",
|
|
7359
|
+
tags: ["plans"],
|
|
7360
|
+
requestBody: { required: true, content: { "application/json": { schema: toCreateSchema2(PlanSchema2) } } },
|
|
7361
|
+
responses: {
|
|
7362
|
+
"201": { description: "Created plan", content: { "application/json": { schema: PlanSchema2 } } },
|
|
7363
|
+
default: { description: "Error response", content: { "application/json": { schema: ErrorEnvelope2 } } }
|
|
7364
|
+
},
|
|
7365
|
+
"x-revturbine-operation": { exposure: "external", resource: "plans", persistence: { table: "planVersions", mode: "create" } }
|
|
7366
|
+
})
|
|
7367
|
+
},
|
|
7368
|
+
"/api/plans/{planId}": {
|
|
7369
|
+
get: operation2({
|
|
7370
|
+
operationId: "getPlan",
|
|
7371
|
+
requestParams: { path: z82.object({ planId: z82.string() }) },
|
|
7372
|
+
summary: "Get plan by ID",
|
|
7373
|
+
tags: ["plans"],
|
|
7374
|
+
responses: {
|
|
7375
|
+
"200": { description: "Plan detail", content: { "application/json": { schema: PlanSchema2 } } },
|
|
7376
|
+
default: { description: "Error response", content: { "application/json": { schema: ErrorEnvelope2 } } }
|
|
7377
|
+
},
|
|
7378
|
+
"x-revturbine-operation": { exposure: "external", resource: "plans", persistence: { table: "planVersions", mode: "get" } }
|
|
7379
|
+
}),
|
|
7380
|
+
patch: operation2({
|
|
7381
|
+
operationId: "updatePlan",
|
|
7382
|
+
requestParams: { path: z82.object({ planId: z82.string() }) },
|
|
7383
|
+
summary: "Update plan",
|
|
7384
|
+
tags: ["plans"],
|
|
7385
|
+
requestBody: { required: true, content: { "application/json": { schema: PlanWriteSchema2 } } },
|
|
7386
|
+
responses: {
|
|
7387
|
+
"200": { description: "Updated plan", content: { "application/json": { schema: PlanSchema2 } } },
|
|
7388
|
+
default: { description: "Error response", content: { "application/json": { schema: ErrorEnvelope2 } } }
|
|
7389
|
+
},
|
|
7390
|
+
"x-revturbine-operation": { exposure: "external", resource: "plans", persistence: { table: "planVersions", mode: "update" } }
|
|
7391
|
+
}),
|
|
7392
|
+
delete: operation2({
|
|
7393
|
+
operationId: "deletePlan",
|
|
7394
|
+
requestParams: { path: z82.object({ planId: z82.string() }) },
|
|
7395
|
+
summary: "Delete plan",
|
|
7396
|
+
tags: ["plans"],
|
|
7397
|
+
responses: {
|
|
7398
|
+
"204": { description: "Plan deleted" },
|
|
7399
|
+
default: { description: "Error response", content: { "application/json": { schema: ErrorEnvelope2 } } }
|
|
7400
|
+
},
|
|
7401
|
+
"x-revturbine-operation": { exposure: "external", resource: "plans", persistence: { table: "planVersions", mode: "delete" } }
|
|
7402
|
+
})
|
|
7403
|
+
},
|
|
7404
|
+
"/api/plans/reorder": {
|
|
7405
|
+
post: operation2({
|
|
7406
|
+
operationId: "reorderPlans",
|
|
7407
|
+
summary: "Reorder plans",
|
|
7408
|
+
tags: ["plans"],
|
|
7409
|
+
requestBody: { required: true, content: { "application/json": { schema: z82.object({ ids: z82.array(z82.string()) }) } } },
|
|
7410
|
+
responses: { "200": { description: "Reordered" } },
|
|
7411
|
+
"x-revturbine-operation": { exposure: "external", resource: "plans", persistence: { table: "planVersions", mode: "reorder" } }
|
|
7412
|
+
})
|
|
7413
|
+
},
|
|
7414
|
+
"/api/plan-variation-anchors": {
|
|
7415
|
+
get: operation2({
|
|
7416
|
+
operationId: "listPlanVariationAnchors",
|
|
7417
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
7418
|
+
summary: "List plan variation anchors (identity registry)",
|
|
7419
|
+
tags: ["plans"],
|
|
7420
|
+
responses: {
|
|
7421
|
+
"200": { description: "Plan variation anchor list", content: { "application/json": { schema: ListEnvelope2(PlanVariationAnchorSchema2) } } },
|
|
7422
|
+
default: { description: "Error response", content: { "application/json": { schema: ErrorEnvelope2 } } }
|
|
7423
|
+
},
|
|
7424
|
+
"x-revturbine-operation": { exposure: "internal", resource: "plan-variation-anchors", persistence: { table: "planVariations", mode: "list" } }
|
|
7425
|
+
})
|
|
7426
|
+
},
|
|
7427
|
+
"/api/plans/{planId}/variations": {
|
|
7428
|
+
get: operation2({
|
|
7429
|
+
operationId: "listPlanVariations",
|
|
7430
|
+
requestParams: { path: z82.object({ planId: z82.string() }), query: ListQueryParamsSchema2 },
|
|
7431
|
+
summary: "List plan variations",
|
|
7432
|
+
tags: ["plans"],
|
|
7433
|
+
responses: { "200": { description: "Plan variations", content: { "application/json": { schema: ListEnvelope2(PlanVariationSchema2) } } } },
|
|
7434
|
+
"x-revturbine-operation": { exposure: "external", resource: "plan-variations", persistence: { table: "planVariationVersions", mode: "list" } }
|
|
7435
|
+
}),
|
|
7436
|
+
post: operation2({
|
|
7437
|
+
operationId: "createPlanVariation",
|
|
7438
|
+
requestParams: { path: z82.object({ planId: z82.string() }) },
|
|
7439
|
+
summary: "Create plan variation",
|
|
7440
|
+
tags: ["plans"],
|
|
7441
|
+
requestBody: { required: true, content: { "application/json": { schema: toCreateSchema2(PlanVariationSchema2) } } },
|
|
7442
|
+
responses: { "201": { description: "Created plan variation", content: { "application/json": { schema: PlanVariationSchema2 } } } },
|
|
7443
|
+
"x-revturbine-operation": { exposure: "external", resource: "plan-variations", persistence: { table: "planVariationVersions", mode: "create" } }
|
|
7444
|
+
})
|
|
7445
|
+
},
|
|
7446
|
+
"/api/plan-variations/{variationId}": {
|
|
7447
|
+
get: operation2({
|
|
7448
|
+
operationId: "getPlanVariation",
|
|
7449
|
+
requestParams: { path: z82.object({ variationId: z82.string() }) },
|
|
7450
|
+
summary: "Get plan variation by ID",
|
|
7451
|
+
tags: ["plans"],
|
|
7452
|
+
responses: { "200": { description: "Plan variation detail", content: { "application/json": { schema: PlanVariationSchema2 } } } },
|
|
7453
|
+
"x-revturbine-operation": { exposure: "external", resource: "plan-variations", persistence: { table: "planVariationVersions", mode: "get" } }
|
|
7454
|
+
}),
|
|
7455
|
+
patch: operation2({
|
|
7456
|
+
operationId: "updatePlanVariation",
|
|
7457
|
+
requestParams: { path: z82.object({ variationId: z82.string() }) },
|
|
7458
|
+
summary: "Update plan variation",
|
|
7459
|
+
tags: ["plans"],
|
|
7460
|
+
requestBody: { required: true, content: { "application/json": { schema: PlanVariationWriteSchema2 } } },
|
|
7461
|
+
responses: { "200": { description: "Updated plan variation", content: { "application/json": { schema: PlanVariationSchema2 } } } },
|
|
7462
|
+
"x-revturbine-operation": { exposure: "external", resource: "plan-variations", persistence: { table: "planVariationVersions", mode: "update" } }
|
|
7463
|
+
}),
|
|
7464
|
+
delete: operation2({
|
|
7465
|
+
operationId: "deletePlanVariation",
|
|
7466
|
+
requestParams: { path: z82.object({ variationId: z82.string() }) },
|
|
7467
|
+
summary: "Delete plan variation",
|
|
7468
|
+
tags: ["plans"],
|
|
7469
|
+
responses: { "204": { description: "Plan variation deleted" } },
|
|
7470
|
+
"x-revturbine-operation": { exposure: "external", resource: "plan-variations", persistence: { table: "planVariationVersions", mode: "delete" } }
|
|
7471
|
+
})
|
|
7472
|
+
},
|
|
7473
|
+
"/api/addon-anchors": {
|
|
7474
|
+
get: operation2({
|
|
7475
|
+
operationId: "listAddOnAnchors",
|
|
7476
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
7477
|
+
summary: "List add-on anchors (identity registry)",
|
|
7478
|
+
tags: ["plans"],
|
|
7479
|
+
responses: {
|
|
7480
|
+
"200": { description: "Add-on anchor list", content: { "application/json": { schema: ListEnvelope2(AddOnAnchorSchema2) } } },
|
|
7481
|
+
default: { description: "Error response", content: { "application/json": { schema: ErrorEnvelope2 } } }
|
|
7482
|
+
},
|
|
7483
|
+
"x-revturbine-operation": { exposure: "internal", resource: "addon-anchors", persistence: { table: "addons", mode: "list" } }
|
|
7484
|
+
})
|
|
7485
|
+
},
|
|
7486
|
+
"/api/addons": {
|
|
7487
|
+
get: operation2({
|
|
7488
|
+
operationId: "listAddOns",
|
|
7489
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
7490
|
+
summary: "List add-ons",
|
|
7491
|
+
tags: ["plans"],
|
|
7492
|
+
responses: { "200": { description: "Add-on list", content: { "application/json": { schema: ListEnvelope2(AddOnSchema2) } } } },
|
|
7493
|
+
"x-revturbine-operation": { exposure: "external", resource: "addons", persistence: { table: "addonVersions", mode: "list" } }
|
|
7494
|
+
}),
|
|
7495
|
+
post: operation2({
|
|
7496
|
+
operationId: "createAddOn",
|
|
7497
|
+
summary: "Create add-on",
|
|
7498
|
+
tags: ["plans"],
|
|
7499
|
+
requestBody: { required: true, content: { "application/json": { schema: toCreateSchema2(AddOnSchema2) } } },
|
|
7500
|
+
responses: { "201": { description: "Created add-on", content: { "application/json": { schema: AddOnSchema2 } } } },
|
|
7501
|
+
"x-revturbine-operation": { exposure: "external", resource: "addons", persistence: { table: "addonVersions", mode: "create" } }
|
|
7502
|
+
})
|
|
7503
|
+
},
|
|
7504
|
+
"/api/addons/{addonId}": {
|
|
7505
|
+
get: operation2({
|
|
7506
|
+
operationId: "getAddOn",
|
|
7507
|
+
requestParams: { path: z82.object({ addonId: z82.string() }) },
|
|
7508
|
+
summary: "Get add-on by ID",
|
|
7509
|
+
tags: ["plans"],
|
|
7510
|
+
responses: { "200": { description: "Add-on detail", content: { "application/json": { schema: AddOnSchema2 } } } },
|
|
7511
|
+
"x-revturbine-operation": { exposure: "external", resource: "addons", persistence: { table: "addonVersions", mode: "get" } }
|
|
7512
|
+
}),
|
|
7513
|
+
patch: operation2({
|
|
7514
|
+
operationId: "updateAddOn",
|
|
7515
|
+
requestParams: { path: z82.object({ addonId: z82.string() }) },
|
|
7516
|
+
summary: "Update add-on",
|
|
7517
|
+
tags: ["plans"],
|
|
7518
|
+
requestBody: { required: true, content: { "application/json": { schema: AddOnWriteSchema2 } } },
|
|
7519
|
+
responses: { "200": { description: "Updated add-on", content: { "application/json": { schema: AddOnSchema2 } } } },
|
|
7520
|
+
"x-revturbine-operation": { exposure: "external", resource: "addons", persistence: { table: "addonVersions", mode: "update" } }
|
|
7521
|
+
}),
|
|
7522
|
+
delete: operation2({
|
|
7523
|
+
operationId: "deleteAddOn",
|
|
7524
|
+
requestParams: { path: z82.object({ addonId: z82.string() }) },
|
|
7525
|
+
summary: "Delete add-on",
|
|
7526
|
+
tags: ["plans"],
|
|
7527
|
+
responses: { "204": { description: "Add-on deleted" } },
|
|
7528
|
+
"x-revturbine-operation": { exposure: "external", resource: "addons", persistence: { table: "addonVersions", mode: "delete" } }
|
|
7529
|
+
})
|
|
7530
|
+
},
|
|
7531
|
+
"/api/addon-variation-anchors": {
|
|
7532
|
+
get: operation2({
|
|
7533
|
+
operationId: "listAddOnVariationAnchors",
|
|
7534
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
7535
|
+
summary: "List add-on variation anchors (identity registry)",
|
|
7536
|
+
tags: ["plans"],
|
|
7537
|
+
responses: {
|
|
7538
|
+
"200": { description: "Add-on variation anchor list", content: { "application/json": { schema: ListEnvelope2(AddOnVariationAnchorSchema2) } } },
|
|
7539
|
+
default: { description: "Error response", content: { "application/json": { schema: ErrorEnvelope2 } } }
|
|
7540
|
+
},
|
|
7541
|
+
"x-revturbine-operation": { exposure: "internal", resource: "addon-variation-anchors", persistence: { table: "addonVariations", mode: "list" } }
|
|
7542
|
+
})
|
|
7543
|
+
},
|
|
7544
|
+
"/api/addons/{addonId}/variations": {
|
|
7545
|
+
get: operation2({
|
|
7546
|
+
operationId: "listAddOnVariations",
|
|
7547
|
+
requestParams: { path: z82.object({ addonId: z82.string() }), query: ListQueryParamsSchema2 },
|
|
7548
|
+
summary: "List add-on variations",
|
|
7549
|
+
tags: ["plans"],
|
|
7550
|
+
responses: { "200": { description: "Add-on variations", content: { "application/json": { schema: ListEnvelope2(AddOnVariationSchema2) } } } },
|
|
7551
|
+
"x-revturbine-operation": { exposure: "external", resource: "addon-variations", persistence: { table: "addonVariationVersions", mode: "list" } }
|
|
7552
|
+
}),
|
|
7553
|
+
post: operation2({
|
|
7554
|
+
operationId: "createAddOnVariation",
|
|
7555
|
+
requestParams: { path: z82.object({ addonId: z82.string() }) },
|
|
7556
|
+
summary: "Create add-on variation",
|
|
7557
|
+
tags: ["plans"],
|
|
7558
|
+
requestBody: { required: true, content: { "application/json": { schema: toCreateSchema2(AddOnVariationSchema2) } } },
|
|
7559
|
+
responses: { "201": { description: "Created variation", content: { "application/json": { schema: AddOnVariationSchema2 } } } },
|
|
7560
|
+
"x-revturbine-operation": { exposure: "external", resource: "addon-variations", persistence: { table: "addonVariationVersions", mode: "create" } }
|
|
7561
|
+
})
|
|
7562
|
+
},
|
|
7563
|
+
"/api/addon-variations/{variationId}": {
|
|
7564
|
+
get: operation2({
|
|
7565
|
+
operationId: "getAddOnVariation",
|
|
7566
|
+
requestParams: { path: z82.object({ variationId: z82.string() }) },
|
|
7567
|
+
summary: "Get add-on variation by ID",
|
|
7568
|
+
tags: ["plans"],
|
|
7569
|
+
responses: { "200": { description: "Add-on variation detail", content: { "application/json": { schema: AddOnVariationSchema2 } } } },
|
|
7570
|
+
"x-revturbine-operation": { exposure: "external", resource: "addon-variations", persistence: { table: "addonVariationVersions", mode: "get" } }
|
|
7571
|
+
}),
|
|
7572
|
+
patch: operation2({
|
|
7573
|
+
operationId: "updateAddOnVariation",
|
|
7574
|
+
requestParams: { path: z82.object({ variationId: z82.string() }) },
|
|
7575
|
+
summary: "Update add-on variation",
|
|
7576
|
+
tags: ["plans"],
|
|
7577
|
+
requestBody: { required: true, content: { "application/json": { schema: AddOnVariationWriteSchema2 } } },
|
|
7578
|
+
responses: { "200": { description: "Updated variation", content: { "application/json": { schema: AddOnVariationSchema2 } } } },
|
|
7579
|
+
"x-revturbine-operation": { exposure: "external", resource: "addon-variations", persistence: { table: "addonVariationVersions", mode: "update" } }
|
|
7580
|
+
}),
|
|
7581
|
+
delete: operation2({
|
|
7582
|
+
operationId: "deleteAddOnVariation",
|
|
7583
|
+
requestParams: { path: z82.object({ variationId: z82.string() }) },
|
|
7584
|
+
summary: "Delete add-on variation",
|
|
7585
|
+
tags: ["plans"],
|
|
7586
|
+
responses: { "204": { description: "Variation deleted" } },
|
|
7587
|
+
"x-revturbine-operation": { exposure: "external", resource: "addon-variations", persistence: { table: "addonVariationVersions", mode: "delete" } }
|
|
7588
|
+
})
|
|
7589
|
+
},
|
|
7590
|
+
"/api/stripe-prices": {
|
|
7591
|
+
get: operation2({
|
|
7592
|
+
operationId: "listStripePrices",
|
|
7593
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
7594
|
+
summary: "List Stripe prices",
|
|
7595
|
+
tags: ["plans"],
|
|
7596
|
+
responses: { "200": { description: "Stripe price list", content: { "application/json": { schema: ListEnvelope2(StripePriceSchema2) } } } },
|
|
7597
|
+
"x-revturbine-operation": { exposure: "external", resource: "stripe-prices", persistence: { table: "stripePrices", mode: "list" } }
|
|
7598
|
+
}),
|
|
7599
|
+
post: operation2({
|
|
7600
|
+
operationId: "createStripePrice",
|
|
7601
|
+
summary: "Create Stripe price",
|
|
7602
|
+
tags: ["plans"],
|
|
7603
|
+
requestBody: { required: true, content: { "application/json": { schema: toCreateSchema2(StripePriceSchema2) } } },
|
|
7604
|
+
responses: { "201": { description: "Created Stripe price", content: { "application/json": { schema: StripePriceSchema2 } } } },
|
|
7605
|
+
"x-revturbine-operation": { exposure: "external", resource: "stripe-prices", persistence: { table: "stripePrices", mode: "create" } }
|
|
7606
|
+
})
|
|
7607
|
+
},
|
|
7608
|
+
"/api/stripe-prices/{id}": {
|
|
7609
|
+
get: operation2({
|
|
7610
|
+
operationId: "getStripePrice",
|
|
7611
|
+
requestParams: { path: z82.object({ id: z82.string() }) },
|
|
7612
|
+
summary: "Get Stripe price by ID",
|
|
7613
|
+
tags: ["plans"],
|
|
7614
|
+
responses: { "200": { description: "Stripe price detail", content: { "application/json": { schema: StripePriceSchema2 } } } },
|
|
7615
|
+
"x-revturbine-operation": { exposure: "external", resource: "stripe-prices", persistence: { table: "stripePrices", mode: "get" } }
|
|
7616
|
+
}),
|
|
7617
|
+
patch: operation2({
|
|
7618
|
+
operationId: "updateStripePrice",
|
|
7619
|
+
requestParams: { path: z82.object({ id: z82.string() }) },
|
|
7620
|
+
summary: "Update Stripe price",
|
|
7621
|
+
tags: ["plans"],
|
|
7622
|
+
requestBody: { required: true, content: { "application/json": { schema: StripePriceWriteSchema2 } } },
|
|
7623
|
+
responses: { "200": { description: "Updated Stripe price", content: { "application/json": { schema: StripePriceSchema2 } } } },
|
|
7624
|
+
"x-revturbine-operation": { exposure: "external", resource: "stripe-prices", persistence: { table: "stripePrices", mode: "update" } }
|
|
7625
|
+
}),
|
|
7626
|
+
delete: operation2({
|
|
7627
|
+
operationId: "deleteStripePrice",
|
|
7628
|
+
requestParams: { path: z82.object({ id: z82.string() }) },
|
|
7629
|
+
summary: "Delete Stripe price",
|
|
7630
|
+
tags: ["plans"],
|
|
7631
|
+
responses: { "204": { description: "Stripe price deleted" } },
|
|
7632
|
+
"x-revturbine-operation": { exposure: "external", resource: "stripe-prices", persistence: { table: "stripePrices", mode: "delete" } }
|
|
7633
|
+
})
|
|
7634
|
+
}
|
|
7635
|
+
};
|
|
7636
|
+
var { Unrestricted: Unrestricted52 } = DataClassification2;
|
|
7637
|
+
var { Persisted: Persisted52, Transient: Transient52 } = SchemaPersistence2;
|
|
7638
|
+
var { Internal: Internal42, External: External42 } = SchemaExposure2;
|
|
7639
|
+
var PLAYBOOK_SDK_FACETS42 = schemaFacets2(SchemaContext2.Playbook, { sdkInput: true });
|
|
7640
|
+
var PLAYBOOK_AUTHORING_FACETS22 = schemaFacets2(SchemaContext2.Playbook, { sdkInput: false });
|
|
7641
|
+
var PENDING_PLAYBOOK_FACETS22 = schemaFacets2(SchemaContext2.Playbook, {
|
|
7642
|
+
inConfig: false,
|
|
7643
|
+
sdkInput: false
|
|
7644
|
+
});
|
|
7645
|
+
var PENDING_PLAYBOOK_SDK_FACETS22 = schemaFacets2(SchemaContext2.Playbook, {
|
|
7646
|
+
inConfig: false,
|
|
7647
|
+
sdkInput: true
|
|
7648
|
+
});
|
|
7649
|
+
var PLAYBOOK_VERSION_HEADER_FACETS2 = schemaFacets2(SchemaContext2.Playbook, {
|
|
7650
|
+
inConfig: false,
|
|
7651
|
+
sdkInput: true,
|
|
7652
|
+
source: SchemaSource2.CodeConstant
|
|
7653
|
+
});
|
|
7654
|
+
var PLAYBOOK_PROVENANCE_HEADER_FACETS2 = schemaFacets2(SchemaContext2.Playbook, {
|
|
7655
|
+
inConfig: false,
|
|
7656
|
+
sdkInput: true,
|
|
7657
|
+
source: SchemaSource2.Runtime
|
|
7658
|
+
});
|
|
7659
|
+
var PLAYBOOK_TARGET_FACETS2 = schemaFacets2(SchemaContext2.Playbook, {
|
|
7660
|
+
inConfig: false,
|
|
7661
|
+
sdkInput: false,
|
|
7662
|
+
source: SchemaSource2.Runtime
|
|
7663
|
+
});
|
|
7664
|
+
var BILLING_FACETS22 = schemaFacets2(SchemaContext2.Billing, { sdkInput: false });
|
|
7665
|
+
var METERING_FACETS2 = schemaFacets2(SchemaContext2.Metering, { sdkInput: false });
|
|
7666
|
+
var LEGACY_BRANDING_FACETS2 = schemaFacets2(SchemaContext2.Branding, {
|
|
7667
|
+
inConfig: true,
|
|
7668
|
+
sdkInput: true
|
|
7669
|
+
});
|
|
7670
|
+
var BRANDING_FACETS3 = schemaFacets2(SchemaContext2.Branding, { sdkInput: false });
|
|
7671
|
+
var PLAYBOOK_FORMAT_VERSION2 = "1.0.0";
|
|
7672
|
+
var SeatTypeSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).merge(AnchorFields2).merge(VersionFields2).extend({
|
|
7673
|
+
anchor_id: z92.string().min(1).meta({ ...Unrestricted52, readOnly: true }),
|
|
7674
|
+
name: NameField2.meta(Unrestricted52),
|
|
7675
|
+
handle: HandleField2.meta(Unrestricted52),
|
|
7676
|
+
description: DescriptionField2.meta(Unrestricted52),
|
|
7677
|
+
is_default: z92.boolean().default(false).meta(Unrestricted52),
|
|
7678
|
+
entitlement_ids: z92.array(z92.string()).default([]).meta(Unrestricted52),
|
|
7679
|
+
metadata: MetadataField2.meta(Unrestricted52)
|
|
7680
|
+
}).meta(
|
|
7681
|
+
{ id: "SeatType", "x-revturbine-schema-persistence": Persisted52, "x-revturbine-schema-exposure": Internal42, ...PENDING_PLAYBOOK_FACETS22, ...namedIdentity2() }
|
|
7682
|
+
);
|
|
7683
|
+
var SeatTypeAnchorSchema2 = makeAnchor2("SeatTypeAnchor");
|
|
7684
|
+
var PersonalizationTokenSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).merge(AnchorFields2).merge(VersionFields2).extend({
|
|
7685
|
+
anchor_id: z92.string().min(1).meta({ ...Unrestricted52, readOnly: true }),
|
|
7686
|
+
handle: HandleField2.meta(Unrestricted52),
|
|
7687
|
+
label: z92.string().min(1).meta(Unrestricted52),
|
|
7688
|
+
description: z92.string().nullable().default(null).meta(Unrestricted52),
|
|
7689
|
+
category: z92.enum(["user", "plan", "usage", "trial", "billing", "promotion", "custom"]).meta(Unrestricted52),
|
|
7690
|
+
data_source: z92.string().nullable().default(null).meta(Unrestricted52),
|
|
7691
|
+
example_value: z92.string().nullable().default(null).meta(Unrestricted52),
|
|
7692
|
+
value_map: z92.record(z92.string(), z92.string()).default({}).meta(Unrestricted52),
|
|
7693
|
+
format: z92.enum(["string", "number", "currency", "percentage", "date"]).nullable().default(null).meta(Unrestricted52),
|
|
7694
|
+
metadata: MetadataField2.meta(Unrestricted52)
|
|
7695
|
+
}).meta(
|
|
7696
|
+
{ id: "PersonalizationToken", "x-revturbine-schema-persistence": Persisted52, "x-revturbine-schema-exposure": Internal42, ...PENDING_PLAYBOOK_FACETS22, ...namedIdentity2() }
|
|
7697
|
+
);
|
|
7698
|
+
var PersonalizationTokenAnchorSchema2 = makeAnchor2("PersonalizationTokenAnchor");
|
|
7699
|
+
var OnboardingStateSchema2 = z92.enum(["not_started", "started", "details_submitted", "charges_enabled", "activated", "deauthorized"]).meta({ id: "OnboardingState", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": Internal42 });
|
|
7700
|
+
var StripeIntegrationConfigSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).merge(AnchorFields2).merge(VersionFields2).extend({
|
|
7701
|
+
handle: HandleField2.meta({ ...Unrestricted52, readOnly: true }),
|
|
7702
|
+
stripe_account_id: z92.string().min(1).meta(Unrestricted52),
|
|
7703
|
+
live_mode: z92.boolean().default(false).meta(Unrestricted52),
|
|
7704
|
+
/** Funnel state for the Connect onboarding pipeline. */
|
|
7705
|
+
onboarding_state: OnboardingStateSchema2.default("not_started").meta({ ...Unrestricted52, readOnly: true }),
|
|
7706
|
+
/** Connect onboarding status — tracks whether hosted onboarding is complete. */
|
|
7707
|
+
onboarding_complete: z92.boolean().default(false).meta({ ...Unrestricted52, readOnly: true }),
|
|
7708
|
+
/** Whether the connected account can process charges (read from Stripe). */
|
|
7709
|
+
charges_enabled: z92.boolean().default(false).meta({ ...Unrestricted52, readOnly: true }),
|
|
7710
|
+
/** Whether the connected account has details submitted (read from Stripe). */
|
|
7711
|
+
details_submitted: z92.boolean().default(false).meta({ ...Unrestricted52, readOnly: true }),
|
|
7712
|
+
/** Whether the connected account can receive payouts (read from Stripe). */
|
|
7713
|
+
payouts_enabled: z92.boolean().default(false).meta({ ...Unrestricted52, readOnly: true }),
|
|
7714
|
+
webhook_secret_set: z92.boolean().default(false).meta({ ...Unrestricted52, readOnly: true }),
|
|
7715
|
+
sync_products: z92.boolean().default(true).meta(Unrestricted52),
|
|
7716
|
+
sync_prices: z92.boolean().default(true).meta(Unrestricted52),
|
|
7717
|
+
sync_subscriptions: z92.boolean().default(true).meta(Unrestricted52),
|
|
7718
|
+
sync_invoices: z92.boolean().default(false).meta(Unrestricted52),
|
|
7719
|
+
default_currency: z92.string().length(3).default("USD").meta(Unrestricted52),
|
|
7720
|
+
tax_behavior: z92.enum(["inclusive", "exclusive", "unspecified"]).default("unspecified").meta(Unrestricted52),
|
|
7721
|
+
/** ISO timestamp of the last successful full data sync from Stripe. */
|
|
7722
|
+
last_sync_at: z92.string().optional().meta({ ...Unrestricted52, readOnly: true }),
|
|
7723
|
+
metadata: MetadataField2.meta(Unrestricted52)
|
|
7724
|
+
}).meta(
|
|
7725
|
+
{ id: "StripeIntegrationConfig", "x-revturbine-schema-persistence": Persisted52, "x-revturbine-schema-exposure": Internal42, ...BILLING_FACETS22, ...mintedIdentity2() }
|
|
7726
|
+
);
|
|
7727
|
+
var BrandingConfigSchema2 = z92.object({
|
|
7728
|
+
theme: z92.record(z92.string(), z92.unknown()).optional().meta(Unrestricted52),
|
|
7729
|
+
workspace_name: z92.string().optional().meta(Unrestricted52),
|
|
7730
|
+
logo_url: z92.string().optional().meta(Unrestricted52),
|
|
7731
|
+
support_email: z92.string().optional().meta(Unrestricted52)
|
|
7732
|
+
}).meta(
|
|
7733
|
+
{
|
|
7734
|
+
id: "BrandingConfig",
|
|
7735
|
+
"x-revturbine-schema-persistence": Transient52,
|
|
7736
|
+
"x-revturbine-schema-exposure": External42,
|
|
7737
|
+
...BRANDING_FACETS3
|
|
7738
|
+
}
|
|
7739
|
+
);
|
|
7740
|
+
var MeteringConfigSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).merge(AnchorFields2).merge(VersionFields2).extend({
|
|
7741
|
+
handle: HandleField2.meta({ ...Unrestricted52, readOnly: true }),
|
|
7742
|
+
entitlement_id: z92.string().min(1).meta(Unrestricted52),
|
|
7743
|
+
meter_key: z92.string().min(1).max(100).meta(Unrestricted52),
|
|
7744
|
+
aggregation_type: z92.enum(["sum", "count", "max", "last_value"]).default("sum").meta(Unrestricted52),
|
|
7745
|
+
reset_period: z92.enum(["none", "daily", "weekly", "monthly", "yearly"]).default("monthly").meta(Unrestricted52),
|
|
7746
|
+
stripe_meter_id: z92.string().nullable().default(null).meta(Unrestricted52),
|
|
7747
|
+
is_active: z92.boolean().default(true).meta(Unrestricted52),
|
|
7748
|
+
metadata: MetadataField2.meta(Unrestricted52)
|
|
7749
|
+
}).meta(
|
|
7750
|
+
{ id: "MeteringConfig", "x-revturbine-schema-persistence": Persisted52, "x-revturbine-schema-exposure": Internal42, ...METERING_FACETS2, ...mintedIdentity2() }
|
|
7751
|
+
);
|
|
7752
|
+
var EnforcementActionSchema2 = z92.enum(["block", "warn", "downgrade", "throttle", "notify_admin", "custom"]).meta(
|
|
7753
|
+
{ id: "EnforcementAction", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": Internal42 }
|
|
7754
|
+
);
|
|
7755
|
+
var UsageEnforcementSettingsSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).merge(AnchorFields2).merge(VersionFields2).extend({
|
|
7756
|
+
anchor_id: z92.string().min(1).meta({ ...Unrestricted52, readOnly: true }),
|
|
7757
|
+
handle: HandleField2.meta({ ...Unrestricted52, readOnly: true }),
|
|
7758
|
+
entitlement_id: z92.string().min(1).meta(Unrestricted52),
|
|
7759
|
+
soft_limit_percent: z92.number().min(0).max(100).default(80).meta(Unrestricted52),
|
|
7760
|
+
hard_limit_percent: z92.number().min(0).max(100).default(100).meta(Unrestricted52),
|
|
7761
|
+
soft_limit_action: EnforcementActionSchema2.default("warn").meta(Unrestricted52),
|
|
7762
|
+
hard_limit_action: EnforcementActionSchema2.default("block").meta(Unrestricted52),
|
|
7763
|
+
grace_period_hours: z92.number().int().min(0).default(0).meta(Unrestricted52),
|
|
7764
|
+
notification_channels: z92.array(z92.enum(["email", "in_app", "webhook"])).default(["in_app"]).meta(Unrestricted52),
|
|
7765
|
+
is_active: z92.boolean().default(true).meta(Unrestricted52)
|
|
7766
|
+
}).meta(
|
|
7767
|
+
{ id: "UsageEnforcementSettings", "x-revturbine-schema-persistence": Persisted52, "x-revturbine-schema-exposure": Internal42, ...PENDING_PLAYBOOK_SDK_FACETS22, ...mintedIdentity2() }
|
|
7768
|
+
);
|
|
7769
|
+
var UsageEnforcementSettingsAnchorSchema2 = makeAnchor2("UsageEnforcementSettingsAnchor");
|
|
7770
|
+
var PlacementSettingsCapRuleGroupItemSchema2 = z92.object({
|
|
7771
|
+
kind: z92.enum(["template", "slot"]).meta(Unrestricted52),
|
|
7772
|
+
id: z92.string().min(1).meta(Unrestricted52),
|
|
7773
|
+
label: z92.string().min(1).optional().meta(Unrestricted52)
|
|
7774
|
+
}).meta(
|
|
7775
|
+
{ id: "PlacementSettingsCapRuleGroupItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": Internal42 }
|
|
7776
|
+
);
|
|
7777
|
+
var PlacementSettingsCapRuleSchema2 = z92.object({
|
|
7778
|
+
id: z92.string().min(1).meta(Unrestricted52),
|
|
7779
|
+
group: z92.array(PlacementSettingsCapRuleGroupItemSchema2).min(1).meta(Unrestricted52),
|
|
7780
|
+
cap: z92.object({
|
|
7781
|
+
count: z92.number().int().min(1).meta(Unrestricted52),
|
|
7782
|
+
period: z92.enum(["session", "day", "week", "month"]).meta(Unrestricted52)
|
|
7783
|
+
}).meta(Unrestricted52)
|
|
7784
|
+
}).meta(
|
|
7785
|
+
{ id: "PlacementSettingsCapRule", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": Internal42 }
|
|
7786
|
+
);
|
|
7787
|
+
var PlacementTestModeSchema2 = z92.enum(["off", "test_users", "all_traffic"]).meta(
|
|
7788
|
+
{ id: "PlacementTestMode", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": Internal42 }
|
|
7789
|
+
);
|
|
7790
|
+
var PlacementSettingsCapStateSchema2 = z92.object({
|
|
7791
|
+
capRules: z92.array(PlacementSettingsCapRuleSchema2).default([]).meta(Unrestricted52),
|
|
7792
|
+
sessionCooldownMinutes: z92.number().int().min(0).default(30).meta(Unrestricted52),
|
|
7793
|
+
testMode: PlacementTestModeSchema2.default("off").meta(Unrestricted52)
|
|
7794
|
+
}).meta(
|
|
7795
|
+
{ id: "PlacementSettingsCapState", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": Internal42 }
|
|
7796
|
+
);
|
|
7797
|
+
var PlacementSettingsSchema2 = IdField2.merge(TimestampFields2).merge(TenantIdField2).merge(AnchorFields2).merge(VersionFields2).extend({
|
|
7798
|
+
anchor_id: z92.string().min(1).meta({ ...Unrestricted52, readOnly: true }),
|
|
7799
|
+
handle: HandleField2.meta({ ...Unrestricted52, readOnly: true }),
|
|
7800
|
+
global_frequency_cap: PlacementSettingsCapStateSchema2.nullable().default(null).meta(Unrestricted52),
|
|
7801
|
+
// Legacy companion column kept for migration continuity. The new
|
|
7802
|
+
// wrapper-object encoding above carries period information per
|
|
7803
|
+
// cap rule; this column is always null in v0.1.20+ writes.
|
|
7804
|
+
global_frequency_cap_period: z92.enum(["hour", "day", "week", "month", "session"]).nullable().default(null).meta(Unrestricted52),
|
|
7805
|
+
suppress_for_paid: z92.boolean().default(false).meta(Unrestricted52),
|
|
7806
|
+
suppress_for_trial: z92.boolean().default(false).meta(Unrestricted52),
|
|
7807
|
+
default_dismiss_cooldown_hours: z92.number().int().min(0).default(24).meta(Unrestricted52),
|
|
7808
|
+
allow_stacking: z92.boolean().default(false).meta(Unrestricted52),
|
|
7809
|
+
priority_collision_strategy: z92.enum(["highest_priority", "most_recent", "random"]).default("highest_priority").meta(Unrestricted52)
|
|
7810
|
+
}).meta(
|
|
7811
|
+
{ id: "PlacementSettings", "x-revturbine-schema-persistence": Persisted52, "x-revturbine-schema-exposure": Internal42, ...PENDING_PLAYBOOK_SDK_FACETS22, ...mintedIdentity2() }
|
|
7812
|
+
);
|
|
7813
|
+
var PlacementSettingsAnchorSchema2 = makeAnchor2("PlacementSettingsAnchor");
|
|
7814
|
+
var RevTurbineConfigSegmentsItemPredicatesItemSchema2 = z92.object({
|
|
7815
|
+
field: z92.string().min(1).meta(Unrestricted52),
|
|
7816
|
+
operator: z92.enum(["eq", "neq", "gt", "lt", "gte", "lte", "contains", "in"]).meta(Unrestricted52),
|
|
7817
|
+
value: z92.string().meta(Unrestricted52)
|
|
7818
|
+
}).meta(
|
|
7819
|
+
{ id: "RevTurbineConfigSegmentsItemPredicatesItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42 }
|
|
7820
|
+
);
|
|
7821
|
+
var RevTurbineConfigSegmentsItemSchema2 = z92.object({
|
|
7822
|
+
// Plan 120 TASK-4: the config carries the handle as its sole logical
|
|
7823
|
+
// identifier — the redundant config-level `id` is dropped. The physical
|
|
7824
|
+
// UUID primary key stays in the persisted (Drizzle) row, never the config.
|
|
7825
|
+
name: z92.string().min(1).meta(Unrestricted52),
|
|
7826
|
+
handle: z92.string().min(1).meta(Unrestricted52),
|
|
7827
|
+
predicates: z92.array(RevTurbineConfigSegmentsItemPredicatesItemSchema2).optional().meta(Unrestricted52),
|
|
7828
|
+
// Dimension this segment belongs to (plan #39 REQ-28 / Route A). Optional
|
|
7829
|
+
// for back-compat: pre-plan-39 RevTurbineConfigs and segments not yet
|
|
7830
|
+
// categorised lack it. The entitlement-rule evaluator uses this to
|
|
7831
|
+
// apply intra-dimension OR + cross-dimension AND per spec §2.5; when
|
|
7832
|
+
// missing across all of a rule's segment_ids, the evaluator falls
|
|
7833
|
+
// back to flat-OR (legacy single-segment behaviour).
|
|
7834
|
+
dimension_id: z92.string().optional().meta(Unrestricted52)
|
|
7835
|
+
}).meta(
|
|
7836
|
+
{ id: "RevTurbineConfigSegmentsItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PLAYBOOK_SDK_FACETS42 }
|
|
7837
|
+
);
|
|
7838
|
+
var RevTurbineConfigPlansItemSchema2 = z92.object({
|
|
7839
|
+
// Plan 120 TASK-4: `unique_handle` is the sole logical identifier; the
|
|
7840
|
+
// redundant config-level `id` is dropped (physical UUID PK stays in the row).
|
|
7841
|
+
unique_handle: z92.string().min(1).meta(Unrestricted52),
|
|
7842
|
+
name: z92.string().min(1).meta(Unrestricted52),
|
|
7843
|
+
tier_position: z92.number().int().min(0).default(0).meta(Unrestricted52),
|
|
7844
|
+
sort_order: z92.number().int().min(0).default(0).meta(Unrestricted52),
|
|
7845
|
+
// Plan-level visibility (to_do/91 Part B). Lives on the plan, not a
|
|
7846
|
+
// priced variation, so a free/custom tier with no variation can still be
|
|
7847
|
+
// marked unlisted/legacy and round-trip. Variations may still carry their
|
|
7848
|
+
// own visibility for per-price overrides; this is the plan's default.
|
|
7849
|
+
visibility: PlanVisibilitySchema2.default("public").meta(Unrestricted52)
|
|
7850
|
+
}).meta(
|
|
7851
|
+
{ id: "RevTurbineConfigPlansItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PLAYBOOK_SDK_FACETS42 }
|
|
7852
|
+
);
|
|
7853
|
+
var RevTurbineConfigAddonsItemSchema2 = z92.object({
|
|
7854
|
+
// Plan 120 TASK-4: `unique_handle` is the sole logical identifier; the
|
|
7855
|
+
// redundant config-level `id` is dropped (physical UUID PK stays in the row).
|
|
7856
|
+
unique_handle: z92.string().min(1).meta(Unrestricted52),
|
|
7857
|
+
name: z92.string().min(1).meta(Unrestricted52),
|
|
7858
|
+
sort_order: z92.number().int().min(0).default(0).meta(Unrestricted52),
|
|
7859
|
+
// Add-on visibility (to_do/91 Part B) — same rationale as plans: metadata,
|
|
7860
|
+
// not price, so it lives in the config independent of addon_variations.
|
|
7861
|
+
visibility: PlanVisibilitySchema2.default("public").meta(Unrestricted52)
|
|
7862
|
+
}).meta(
|
|
7863
|
+
{ id: "RevTurbineConfigAddonsItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PLAYBOOK_AUTHORING_FACETS22 }
|
|
7864
|
+
);
|
|
7865
|
+
var RevTurbineConfigPlanVariationsItemSchema2 = z92.object({
|
|
7866
|
+
handle: z92.string().min(1).meta(Unrestricted52),
|
|
7867
|
+
plan_handle: z92.string().min(1).meta(Unrestricted52),
|
|
7868
|
+
billing_period: z92.enum(["monthly", "annual", "one_time", "custom"]).meta(Unrestricted52),
|
|
7869
|
+
segment_handle: z92.string().nullable().default(null).meta(Unrestricted52),
|
|
7870
|
+
price_amount: z92.number().min(0).meta(Unrestricted52),
|
|
7871
|
+
pricing_model: PricingModelSchema2.meta(Unrestricted52),
|
|
7872
|
+
visibility: PlanVisibilitySchema2.default("public").meta(Unrestricted52),
|
|
7873
|
+
stripe_price_id: z92.string().nullable().default(null).meta(Unrestricted52),
|
|
7874
|
+
price_source: PriceSourceSchema2.meta(Unrestricted52)
|
|
7875
|
+
}).meta(
|
|
7876
|
+
{ id: "RevTurbineConfigPlanVariationsItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PENDING_PLAYBOOK_FACETS22 }
|
|
7877
|
+
);
|
|
7878
|
+
var RevTurbineConfigAddonVariationsItemSchema2 = z92.object({
|
|
7879
|
+
handle: z92.string().min(1).meta(Unrestricted52),
|
|
7880
|
+
addon_handle: z92.string().min(1).meta(Unrestricted52),
|
|
7881
|
+
billing_period: z92.enum(["monthly", "annual", "one_time", "custom"]).meta(Unrestricted52),
|
|
7882
|
+
segment_handle: z92.string().nullable().default(null).meta(Unrestricted52),
|
|
7883
|
+
price_amount: z92.number().min(0).meta(Unrestricted52),
|
|
7884
|
+
pricing_model: PricingModelSchema2.meta(Unrestricted52),
|
|
7885
|
+
visibility: PlanVisibilitySchema2.default("public").meta(Unrestricted52),
|
|
7886
|
+
stripe_price_id: z92.string().nullable().default(null).meta(Unrestricted52),
|
|
7887
|
+
price_source: PriceSourceSchema2.meta(Unrestricted52)
|
|
7888
|
+
}).meta(
|
|
7889
|
+
{ id: "RevTurbineConfigAddonVariationsItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PENDING_PLAYBOOK_FACETS22 }
|
|
7890
|
+
);
|
|
7891
|
+
var RevTurbineConfigSeatTypesItemSchema2 = z92.object({
|
|
7892
|
+
handle: z92.string().min(1).meta(Unrestricted52),
|
|
7893
|
+
name: z92.string().min(1).meta(Unrestricted52),
|
|
7894
|
+
description: z92.string().nullable().default(null).meta(Unrestricted52),
|
|
7895
|
+
is_default: z92.boolean().default(false).meta(Unrestricted52),
|
|
7896
|
+
entitlement_handles: z92.array(z92.string()).default([]).meta(Unrestricted52)
|
|
7897
|
+
}).meta(
|
|
7898
|
+
{ id: "RevTurbineConfigSeatTypesItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PLAYBOOK_SDK_FACETS42 }
|
|
7899
|
+
);
|
|
7900
|
+
var RevTurbineConfigEnforcementDefaultsItemSchema2 = z92.object({
|
|
7901
|
+
handle: z92.string().min(1).meta(Unrestricted52),
|
|
7902
|
+
entitlement_handle: z92.string().nullable().default(null).meta(Unrestricted52),
|
|
7903
|
+
soft_limit_percent: z92.number().int().min(0).nullable().default(null).meta(Unrestricted52),
|
|
7904
|
+
hard_limit_percent: z92.number().int().min(0).nullable().default(null).meta(Unrestricted52),
|
|
7905
|
+
soft_limit_action: z92.string().meta(Unrestricted52),
|
|
7906
|
+
hard_limit_action: z92.string().meta(Unrestricted52),
|
|
7907
|
+
grace_period_hours: z92.number().int().min(0).nullable().default(null).meta(Unrestricted52),
|
|
7908
|
+
notification_channels: z92.array(z92.string()).default([]).meta(Unrestricted52),
|
|
7909
|
+
is_active: z92.boolean().default(true).meta(Unrestricted52)
|
|
7910
|
+
}).meta(
|
|
7911
|
+
{ id: "RevTurbineConfigEnforcementDefaultsItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PLAYBOOK_SDK_FACETS42 }
|
|
7912
|
+
);
|
|
7913
|
+
var RevTurbineConfigPlacementSettingsItemSchema2 = z92.object({
|
|
7914
|
+
handle: z92.string().min(1).meta(Unrestricted52),
|
|
7915
|
+
global_frequency_cap: PlacementSettingsCapStateSchema2.nullable().default(null).meta(Unrestricted52),
|
|
7916
|
+
global_frequency_cap_period: z92.enum(["hour", "day", "week", "month", "session"]).nullable().default(null).meta(Unrestricted52),
|
|
7917
|
+
suppress_for_paid: z92.boolean().default(false).meta(Unrestricted52),
|
|
7918
|
+
suppress_for_trial: z92.boolean().default(false).meta(Unrestricted52),
|
|
7919
|
+
default_dismiss_cooldown_hours: z92.number().int().min(0).nullable().default(null).meta(Unrestricted52),
|
|
7920
|
+
allow_stacking: z92.boolean().default(false).meta(Unrestricted52),
|
|
7921
|
+
priority_collision_strategy: z92.string().nullable().default(null).meta(Unrestricted52)
|
|
7922
|
+
}).meta(
|
|
7923
|
+
{ id: "RevTurbineConfigPlacementSettingsItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PLAYBOOK_SDK_FACETS42 }
|
|
7924
|
+
);
|
|
7925
|
+
var RevTurbineConfigSegmentDimensionsItemSchema2 = z92.object({
|
|
7926
|
+
handle: z92.string().min(1).meta(Unrestricted52),
|
|
7927
|
+
name: z92.string().min(1).meta(Unrestricted52),
|
|
7928
|
+
category: z92.string().nullable().default(null).meta(Unrestricted52),
|
|
7929
|
+
visibility_toggle: z92.boolean().default(true).meta(Unrestricted52),
|
|
7930
|
+
source_type: z92.string().nullable().default(null).meta(Unrestricted52)
|
|
7931
|
+
}).meta(
|
|
7932
|
+
{ id: "RevTurbineConfigSegmentDimensionsItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PLAYBOOK_SDK_FACETS42 }
|
|
7933
|
+
);
|
|
7934
|
+
var RevTurbineConfigMeterBindingsItemSchema2 = z92.object({
|
|
7935
|
+
handle: z92.string().min(1).meta(Unrestricted52),
|
|
7936
|
+
entitlement_handle: z92.string().min(1).meta(Unrestricted52),
|
|
7937
|
+
meter_handle: z92.string().min(1).meta(Unrestricted52),
|
|
7938
|
+
limit: z92.number().int().min(0).nullable().default(null).meta(Unrestricted52),
|
|
7939
|
+
reset_period: z92.string().nullable().default(null).meta(Unrestricted52)
|
|
7940
|
+
}).meta(
|
|
7941
|
+
{ id: "RevTurbineConfigMeterBindingsItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PLAYBOOK_SDK_FACETS42 }
|
|
7942
|
+
);
|
|
7943
|
+
var RevTurbineConfigEntitlementsItemSchema2 = z92.object({
|
|
7944
|
+
// Plan 120 TASK-4: `unique_handle` is the sole logical identifier; the
|
|
7945
|
+
// redundant config-level `id` is dropped (physical UUID PK stays in the row).
|
|
7946
|
+
unique_handle: z92.string().min(1).meta(Unrestricted52),
|
|
7947
|
+
name: z92.string().min(1).meta(Unrestricted52),
|
|
7948
|
+
type: EntitlementTypeSchema2.meta(Unrestricted52),
|
|
7949
|
+
unit: z92.string().optional().meta(Unrestricted52),
|
|
7950
|
+
// Ordered tier ladder for a `capability_tier` entitlement — projection of
|
|
7951
|
+
// the authored `EntitlementSchema.tier_definitions` (plan 138 TASK-4).
|
|
7952
|
+
// ARRAY ORDER IS THE RANK: the `entitlement_gate.tier_threshold` placement
|
|
7953
|
+
// trigger fires when the user's current tier ranks below the threshold tier
|
|
7954
|
+
// on this ladder. `name`/`description` are UI-helper denormalizations (plan
|
|
7955
|
+
// 118); the runtime gate reads only the ordered `handle`s.
|
|
7956
|
+
tier_definitions: z92.array(z92.object({
|
|
7957
|
+
name: z92.string(),
|
|
7958
|
+
handle: z92.string(),
|
|
7959
|
+
description: z92.string().optional()
|
|
7960
|
+
})).optional().meta(Unrestricted52)
|
|
7961
|
+
}).meta(
|
|
7962
|
+
{ id: "RevTurbineConfigEntitlementsItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PLAYBOOK_SDK_FACETS42 }
|
|
7963
|
+
);
|
|
7964
|
+
var RevTurbineConfigEntitlementRulesItemSchema2 = z92.object({
|
|
7965
|
+
id: z92.string().min(1).meta(Unrestricted52),
|
|
7966
|
+
entitlement_id: z92.string().min(1).meta(Unrestricted52),
|
|
7967
|
+
targets: z92.array(EntitlementRuleTargetSchema2).min(1).meta(Unrestricted52),
|
|
7968
|
+
// Plan #39 REQ-1: multi-segment scoping per spec §2.5. Empty array
|
|
7969
|
+
// means "match all users" (replaces the singular `segment_id` field
|
|
7970
|
+
// and its 'all'/null sentinels).
|
|
7971
|
+
segment_ids: z92.array(z92.string()).default([]).meta(Unrestricted52),
|
|
7972
|
+
// ── Derived denormalizations from the parent entitlement (plan 147, OQ-6).
|
|
7973
|
+
// Resolved via `entitlement_id` on export; ignored on import (the entitlement
|
|
7974
|
+
// is authoritative). `readOnly` → excluded from round-trip obligations: they
|
|
7975
|
+
// are computed, not authored, so requiring a sentinel to preserve them would
|
|
7976
|
+
// test derivation rather than authoring fidelity.
|
|
7977
|
+
kind: EntitlementTypeSchema2.optional().meta({ ...Unrestricted52, readOnly: true }),
|
|
7978
|
+
unit: z92.string().optional().meta({ ...Unrestricted52, readOnly: true }),
|
|
7979
|
+
tier_name: z92.string().optional().meta({ ...Unrestricted52, readOnly: true }),
|
|
7980
|
+
tier_description: z92.string().optional().meta({ ...Unrestricted52, readOnly: true }),
|
|
7981
|
+
// ── Flat per-rule fields (plan 147, OQ-6) — single-sourced from the persisted
|
|
7982
|
+
// `EntitlementRuleSchema` under its canonical names (REQ-1/REQ-2), replacing
|
|
7983
|
+
// the deleted nested `type_fields` union. Null-stripped on export. The
|
|
7984
|
+
// evaluated subset lowers into `TypeFieldsIR`; the persisted-not-evaluated
|
|
7985
|
+
// fields (`amount_cents`/`currency`/`rate_value`/`period_scope`/…) round-trip
|
|
7986
|
+
// via web import/export for content rendering (OQ-7), not the bundle.
|
|
7987
|
+
...EntitlementRuleSchema2.pick({
|
|
7988
|
+
enabled: true,
|
|
7989
|
+
limit_value: true,
|
|
7990
|
+
enforcement: true,
|
|
7991
|
+
tier_value: true,
|
|
7992
|
+
period_scope: true,
|
|
7993
|
+
included_count: true,
|
|
7994
|
+
seat_type_id: true,
|
|
7995
|
+
initial_grant: true,
|
|
7996
|
+
allowance_value: true,
|
|
7997
|
+
rollover_enabled: true,
|
|
7998
|
+
max_balance: true,
|
|
7999
|
+
reset_period: true,
|
|
8000
|
+
max_seats: true,
|
|
8001
|
+
rate_value: true,
|
|
8002
|
+
amount_cents: true,
|
|
8003
|
+
currency: true
|
|
8004
|
+
}).shape,
|
|
8005
|
+
current_usage: z92.number().default(0).meta(Unrestricted52),
|
|
8006
|
+
/** How usage is partitioned across the identity hierarchy. */
|
|
8007
|
+
allocation: UsageAllocationSchema2.optional().meta(Unrestricted52)
|
|
8008
|
+
}).meta(
|
|
8009
|
+
{ id: "RevTurbineConfigEntitlementRulesItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PLAYBOOK_SDK_FACETS42 }
|
|
8010
|
+
);
|
|
8011
|
+
var RevTurbineConfigSlotConfigsItemSchema2 = z92.object({
|
|
8012
|
+
slot_id: z92.string().min(1).meta(Unrestricted52),
|
|
8013
|
+
active: z92.boolean().meta(Unrestricted52),
|
|
8014
|
+
triggers: z92.array(z92.string()).meta(Unrestricted52)
|
|
8015
|
+
}).meta(
|
|
8016
|
+
{ id: "RevTurbineConfigSlotConfigsItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PLAYBOOK_SDK_FACETS42 }
|
|
8017
|
+
);
|
|
8018
|
+
var RevTurbineConfigPlacementSlotsItemSchema2 = z92.object({
|
|
8019
|
+
id: z92.string().min(1).meta(Unrestricted52),
|
|
8020
|
+
label: z92.string().min(1).meta(Unrestricted52),
|
|
8021
|
+
description: z92.string().meta(Unrestricted52),
|
|
8022
|
+
surface_type: z92.string().meta(Unrestricted52),
|
|
8023
|
+
placement_handle: z92.string().min(1).meta(Unrestricted52),
|
|
8024
|
+
template: z92.string().optional().meta(Unrestricted52)
|
|
8025
|
+
}).meta(
|
|
8026
|
+
{ id: "RevTurbineConfigPlacementSlotsItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PLAYBOOK_SDK_FACETS42 }
|
|
8027
|
+
);
|
|
8028
|
+
var RevTurbineConfigSurfaceTemplatesItemFieldsItemSchema2 = z92.object({
|
|
8029
|
+
name: z92.string().min(1).meta(Unrestricted52),
|
|
8030
|
+
type: z92.string().optional().meta(Unrestricted52),
|
|
8031
|
+
required: z92.boolean().optional().meta(Unrestricted52)
|
|
8032
|
+
}).meta(
|
|
8033
|
+
{ id: "RevTurbineConfigSurfaceTemplatesItemFieldsItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42 }
|
|
8034
|
+
);
|
|
8035
|
+
var RevTurbineConfigSurfaceTemplatesItemSchema2 = z92.object({
|
|
8036
|
+
id: z92.string().min(1).meta(Unrestricted52),
|
|
8037
|
+
surface_type: z92.string().meta(Unrestricted52),
|
|
8038
|
+
fields: z92.array(RevTurbineConfigSurfaceTemplatesItemFieldsItemSchema2).optional().meta(Unrestricted52)
|
|
8039
|
+
}).meta(
|
|
8040
|
+
{ id: "RevTurbineConfigSurfaceTemplatesItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PLAYBOOK_SDK_FACETS42 }
|
|
8041
|
+
);
|
|
8042
|
+
var RevTurbineConfigUiPathActionTypeSchema2 = z92.enum([
|
|
8043
|
+
"open_checkout_modal",
|
|
8044
|
+
"navigate_to_plans",
|
|
8045
|
+
"open_upgrade_modal",
|
|
8046
|
+
"open_placement",
|
|
8047
|
+
"book_demo",
|
|
8048
|
+
"open_feature_tour",
|
|
8049
|
+
"extend_trial",
|
|
8050
|
+
"switch_billing_period",
|
|
8051
|
+
"custom_url",
|
|
8052
|
+
"dismiss",
|
|
8053
|
+
// Additional client-side action types (aligned with CtaPathTypeSchema)
|
|
8054
|
+
"contact_sales",
|
|
8055
|
+
"complete_onboarding",
|
|
8056
|
+
"invite_teammate",
|
|
8057
|
+
"refer_friend",
|
|
8058
|
+
"verify_work_email",
|
|
8059
|
+
"update_payment_method",
|
|
8060
|
+
"enable_auto_renewal",
|
|
8061
|
+
"manage_subscription"
|
|
8062
|
+
]).meta(
|
|
8063
|
+
{ id: "RevTurbineConfigUiPathActionType", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42 }
|
|
8064
|
+
);
|
|
8065
|
+
var ContentUiPathSchema2 = z92.object({
|
|
8066
|
+
name: z92.string().min(1).meta(Unrestricted52),
|
|
8067
|
+
action_type: RevTurbineConfigUiPathActionTypeSchema2.meta(Unrestricted52),
|
|
8068
|
+
plan_handle: z92.string().optional().meta(Unrestricted52),
|
|
8069
|
+
promotion_id: z92.string().optional().meta(Unrestricted52),
|
|
8070
|
+
placement_handle: z92.string().optional().meta(Unrestricted52),
|
|
8071
|
+
url: z92.string().optional().meta(Unrestricted52),
|
|
8072
|
+
tour_id: z92.string().optional().meta(Unrestricted52),
|
|
8073
|
+
target_billing_period: z92.enum(["monthly", "annual"]).optional().meta(Unrestricted52),
|
|
8074
|
+
description: z92.string().optional().meta(Unrestricted52)
|
|
8075
|
+
}).meta(
|
|
8076
|
+
{ id: "ContentUiPath", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PLAYBOOK_SDK_FACETS42 }
|
|
8077
|
+
);
|
|
8078
|
+
var ContentPromotionSchema2 = z92.object({
|
|
8079
|
+
id: z92.string().meta(Unrestricted52),
|
|
8080
|
+
name: z92.string().meta(Unrestricted52),
|
|
8081
|
+
discount: z92.string().meta(Unrestricted52),
|
|
8082
|
+
type: z92.string().meta(Unrestricted52),
|
|
8083
|
+
status: z92.string().meta(Unrestricted52)
|
|
8084
|
+
}).meta(
|
|
8085
|
+
{ id: "ContentPromotion", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PLAYBOOK_SDK_FACETS42 }
|
|
8086
|
+
);
|
|
8087
|
+
var RevTurbineConfigPersonalizationTokensItemSchema2 = z92.object({
|
|
8088
|
+
token: z92.string().regex(/^[a-z][a-z0-9_]*$/).meta(Unrestricted52),
|
|
8089
|
+
label: z92.string().min(1).meta(Unrestricted52),
|
|
8090
|
+
description: z92.string().optional().meta(Unrestricted52),
|
|
8091
|
+
category: z92.enum(["user", "plan", "usage", "trial", "billing", "promotion", "custom"]).meta(Unrestricted52),
|
|
8092
|
+
data_source: z92.string().optional().meta(Unrestricted52),
|
|
8093
|
+
example_value: z92.string().optional().meta(Unrestricted52),
|
|
8094
|
+
value_map: z92.record(z92.string(), z92.string()).optional().meta(Unrestricted52),
|
|
8095
|
+
format: z92.enum(["string", "number", "currency", "percentage", "date"]).optional().meta(Unrestricted52)
|
|
8096
|
+
}).meta(
|
|
8097
|
+
{ id: "RevTurbineConfigPersonalizationTokensItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PLAYBOOK_SDK_FACETS42 }
|
|
8098
|
+
);
|
|
8099
|
+
var MessageBlockContentSchema2 = z92.object({
|
|
8100
|
+
header: z92.string().optional().meta(Unrestricted52),
|
|
8101
|
+
body: z92.string().optional().meta(Unrestricted52),
|
|
8102
|
+
cta_label: z92.string().optional().meta(Unrestricted52),
|
|
8103
|
+
secondary_cta_label: z92.string().optional().meta(Unrestricted52)
|
|
8104
|
+
}).catchall(z92.unknown()).meta(
|
|
8105
|
+
{ id: "MessageBlockContent", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42 }
|
|
8106
|
+
);
|
|
8107
|
+
var MessageBlockSchema2 = z92.object({
|
|
8108
|
+
block_id: z92.string().min(1).meta(Unrestricted52),
|
|
8109
|
+
tenant_id: z92.string().min(1).meta(Unrestricted52),
|
|
8110
|
+
name: z92.string().min(1).meta(Unrestricted52),
|
|
8111
|
+
surface_template_id: z92.string().optional().meta(Unrestricted52),
|
|
8112
|
+
default_content: MessageBlockContentSchema2.meta(Unrestricted52),
|
|
8113
|
+
segment_overrides: z92.array(z92.object({
|
|
8114
|
+
segment_value_id: z92.string(),
|
|
8115
|
+
content: MessageBlockContentSchema2
|
|
8116
|
+
})).optional().meta(Unrestricted52),
|
|
8117
|
+
child_blocks: z92.array(z92.object({
|
|
8118
|
+
slot: z92.string(),
|
|
8119
|
+
block_id: z92.string()
|
|
8120
|
+
})).optional().meta(Unrestricted52),
|
|
8121
|
+
tokens_used: z92.array(z92.string()).optional().meta(Unrestricted52),
|
|
8122
|
+
status: z92.enum(["draft", "active", "archived"]).meta(Unrestricted52),
|
|
8123
|
+
created_at: z92.string().datetime().meta({ ...Unrestricted52, readOnly: true }),
|
|
8124
|
+
updated_at: z92.string().datetime().meta({ ...Unrestricted52, readOnly: true })
|
|
8125
|
+
}).meta(
|
|
8126
|
+
{ id: "MessageBlock", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PLAYBOOK_SDK_FACETS42 }
|
|
8127
|
+
);
|
|
8128
|
+
var RevTurbineConfigStudioCtaConfigSchema2 = z92.object({
|
|
8129
|
+
label: z92.string().meta(Unrestricted52),
|
|
8130
|
+
path: CtaActionTypeSchema2.meta(Unrestricted52),
|
|
8131
|
+
config: z92.record(z92.string(), z92.string()).optional().meta(Unrestricted52)
|
|
8132
|
+
}).meta(
|
|
8133
|
+
{ id: "RevTurbineConfigStudioCtaConfig", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42 }
|
|
8134
|
+
);
|
|
8135
|
+
var RevTurbineConfigStudioPayloadSurfaceSchema2 = z92.object({
|
|
8136
|
+
template_id: z92.string().min(1).meta(Unrestricted52),
|
|
8137
|
+
fields: z92.record(z92.string(), z92.string()).meta(Unrestricted52),
|
|
8138
|
+
ctas: z92.array(RevTurbineConfigStudioCtaConfigSchema2).meta(Unrestricted52)
|
|
8139
|
+
}).meta(
|
|
8140
|
+
{ id: "RevTurbineConfigStudioPayloadSurface", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42 }
|
|
8141
|
+
);
|
|
8142
|
+
var RevTurbineConfigStudioPayloadTargetSchema2 = z92.object({
|
|
8143
|
+
plan_ids: z92.array(z92.string()).meta(Unrestricted52),
|
|
8144
|
+
// Billing-cadence dimension of the Plan Filter (spec §3.1.1 Target).
|
|
8145
|
+
// Empty/absent = no cadence filter. Optional so pre-plan-76 exports parse.
|
|
8146
|
+
billing_cadences: z92.array(z92.string()).optional().meta(Unrestricted52),
|
|
8147
|
+
segment_chips: z92.array(z92.string()).meta(Unrestricted52)
|
|
8148
|
+
}).meta(
|
|
8149
|
+
{ id: "RevTurbineConfigStudioPayloadTarget", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42 }
|
|
8150
|
+
);
|
|
8151
|
+
var RevTurbineConfigPeriodCapSchema2 = z92.object({
|
|
8152
|
+
count: z92.number().int().min(1).meta(Unrestricted52),
|
|
8153
|
+
period: z92.enum(["session", "day", "week", "month", "lifetime"]).meta(Unrestricted52)
|
|
8154
|
+
}).meta(
|
|
8155
|
+
{ id: "RevTurbineConfigPeriodCap", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42 }
|
|
8156
|
+
);
|
|
8157
|
+
var RevTurbineConfigStudioPayloadCapsSchema2 = z92.object({
|
|
8158
|
+
max_per_period: RevTurbineConfigPeriodCapSchema2.optional().meta(Unrestricted52),
|
|
8159
|
+
cooldown_days: z92.number().int().min(0).optional().meta(Unrestricted52)
|
|
8160
|
+
}).meta(
|
|
8161
|
+
{ id: "RevTurbineConfigStudioPayloadCaps", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42 }
|
|
8162
|
+
);
|
|
8163
|
+
var RevTurbineConfigStudioPayloadSchema2 = z92.object({
|
|
8164
|
+
id: z92.string().min(1).meta(Unrestricted52),
|
|
8165
|
+
target: RevTurbineConfigStudioPayloadTargetSchema2.meta(Unrestricted52),
|
|
8166
|
+
surfaces: z92.array(RevTurbineConfigStudioPayloadSurfaceSchema2).meta(Unrestricted52),
|
|
8167
|
+
caps: RevTurbineConfigStudioPayloadCapsSchema2.optional().meta(Unrestricted52),
|
|
8168
|
+
// Optional slot targeting (spec §3.1.1): empty/absent = any compatible slot.
|
|
8169
|
+
surface_slot_ids: z92.array(z92.string()).optional().meta(Unrestricted52),
|
|
8170
|
+
created_at: z92.string().optional().meta({ ...Unrestricted52, readOnly: true }),
|
|
8171
|
+
recommendation_strategy: z92.enum(["next_tier_up", "best_value", "custom"]).optional().default("next_tier_up").meta(Unrestricted52),
|
|
8172
|
+
recommendation_plan_override: z92.string().optional().meta(Unrestricted52)
|
|
8173
|
+
}).meta(
|
|
8174
|
+
{ id: "RevTurbineConfigStudioPayload", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42 }
|
|
8175
|
+
);
|
|
8176
|
+
var RevTurbineConfigPlacementTriggerSchema2 = z92.discriminatedUnion("type", [
|
|
8177
|
+
z92.object({ type: z92.literal("surface_render"), slot_id: z92.string().min(1) }),
|
|
8178
|
+
z92.object({ type: z92.literal("entitlement_gate"), entitlement_handle: z92.string().min(1), tier_threshold: z92.string().optional() }),
|
|
8179
|
+
z92.object({ type: z92.literal("usage_threshold"), entitlement_handle: z92.string().min(1), threshold_percent: ThresholdPercentField2 }),
|
|
8180
|
+
z92.object({ type: z92.literal("credit_threshold"), entitlement_handle: z92.string().min(1), threshold_percent: ThresholdPercentField2 }),
|
|
8181
|
+
z92.object({ type: z92.literal("seat_threshold"), entitlement_handle: z92.string().min(1), threshold_percent: ThresholdPercentField2 }),
|
|
8182
|
+
z92.object({ type: z92.literal("trial_started"), trial_type: z92.enum(["free", "reverse"]).optional() }),
|
|
8183
|
+
z92.object({ type: z92.literal("trial_progress"), progress_percent: z92.number().min(1).max(100) }),
|
|
8184
|
+
z92.object({ type: z92.literal("trial_ending"), days_before_end: z92.number().int().min(0) }),
|
|
8185
|
+
z92.object({ type: z92.literal("trial_ended") }),
|
|
8186
|
+
z92.object({ type: z92.literal("trial_converted") }),
|
|
8187
|
+
z92.object({ type: z92.literal("qualifier"), qualifier: z92.string().min(1) })
|
|
8188
|
+
]).meta(
|
|
8189
|
+
{ id: "RevTurbineConfigPlacementTrigger", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42 }
|
|
8190
|
+
);
|
|
8191
|
+
var RevTurbineConfigPlacementCategorySchema2 = z92.enum(["fixed", "gated", "usage_credit_seat", "trials", "other_conversion", "retention"]).meta(
|
|
8192
|
+
{ id: "RevTurbineConfigPlacementCategory", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42 }
|
|
8193
|
+
);
|
|
8194
|
+
var RevTurbineConfigPlacementItemSchema2 = z92.object({
|
|
8195
|
+
id: z92.string().min(1).meta(Unrestricted52),
|
|
8196
|
+
name: z92.string().min(1).meta(Unrestricted52),
|
|
8197
|
+
category: RevTurbineConfigPlacementCategorySchema2.meta(Unrestricted52),
|
|
8198
|
+
trigger: RevTurbineConfigPlacementTriggerSchema2.meta(Unrestricted52),
|
|
8199
|
+
payloads: z92.array(RevTurbineConfigStudioPayloadSchema2).meta(Unrestricted52),
|
|
8200
|
+
order: z92.number().int().min(0).meta(Unrestricted52)
|
|
8201
|
+
}).meta(
|
|
8202
|
+
{ id: "RevTurbineConfigPlacementItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PLAYBOOK_SDK_FACETS42 }
|
|
8203
|
+
);
|
|
8204
|
+
var RevTurbineConfigPlacementPayloadItemSchema2 = z92.object({
|
|
8205
|
+
payload_id: z92.string().min(1).meta(Unrestricted52),
|
|
8206
|
+
placement_id: z92.string().min(1).meta(Unrestricted52),
|
|
8207
|
+
target: RevTurbineConfigStudioPayloadTargetSchema2.meta(Unrestricted52),
|
|
8208
|
+
caps: RevTurbineConfigStudioPayloadCapsSchema2.optional().meta(Unrestricted52),
|
|
8209
|
+
created_at: z92.string().meta({ ...Unrestricted52, readOnly: true }),
|
|
8210
|
+
updated_at: z92.string().datetime().optional().meta({ ...Unrestricted52, readOnly: true }),
|
|
8211
|
+
source_mode: z92.enum(["inline", "content_linked"]).meta(Unrestricted52),
|
|
8212
|
+
surfaces: z92.array(RevTurbineConfigStudioPayloadSurfaceSchema2).optional().meta(Unrestricted52),
|
|
8213
|
+
// Optional slot targeting (spec §3.1.1): empty/absent = any compatible slot.
|
|
8214
|
+
surface_slot_ids: z92.array(z92.string()).optional().meta(Unrestricted52),
|
|
8215
|
+
content_link: z92.object({
|
|
8216
|
+
message_block_id: z92.string().optional(),
|
|
8217
|
+
ui_path_id: z92.string().optional(),
|
|
8218
|
+
promotion_id: z92.string().optional(),
|
|
8219
|
+
content_payload_id: z92.string().optional()
|
|
8220
|
+
}).optional().meta(Unrestricted52)
|
|
8221
|
+
}).meta(
|
|
8222
|
+
{ id: "RevTurbineConfigPlacementPayloadItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PLAYBOOK_SDK_FACETS42 }
|
|
8223
|
+
);
|
|
8224
|
+
var RevTurbineConfigExtensionRulesItemSchema2 = z92.object({
|
|
8225
|
+
kind: z92.string().min(1).meta(Unrestricted52),
|
|
8226
|
+
schema_version: z92.number().int().nonnegative().meta(Unrestricted52),
|
|
8227
|
+
config: z92.unknown().meta(Unrestricted52)
|
|
8228
|
+
}).meta(
|
|
8229
|
+
{ id: "RevTurbineConfigExtensionRulesItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PLAYBOOK_SDK_FACETS42 }
|
|
8230
|
+
);
|
|
8231
|
+
var RevTurbineConfigFreeTrialRuleItemSchema2 = IdField2.merge(FreeTrialRuleCoreFieldsSchema2).meta(
|
|
8232
|
+
{ id: "RevTurbineConfigFreeTrialRuleItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PENDING_PLAYBOOK_FACETS22 }
|
|
8233
|
+
);
|
|
8234
|
+
var RevTurbineConfigReverseTrialRuleItemSchema2 = IdField2.merge(ReverseTrialRuleCoreFieldsSchema2).meta(
|
|
8235
|
+
{ id: "RevTurbineConfigReverseTrialRuleItem", "x-revturbine-schema-persistence": Transient52, "x-revturbine-schema-exposure": External42, ...PENDING_PLAYBOOK_FACETS22 }
|
|
8236
|
+
);
|
|
8237
|
+
var PlaybookBodySchema2 = z92.object({
|
|
8238
|
+
plans: z92.array(RevTurbineConfigPlansItemSchema2).meta({ ...Unrestricted52, ...PLAYBOOK_SDK_FACETS42 }),
|
|
8239
|
+
// Optional for back-compat: pre-plan-88 configs (and the live export until web
|
|
8240
|
+
// adopts the new @revt-eng/schema) omit it. Add-on definitions only; pricing
|
|
8241
|
+
// (addon_variations) stays in the Stripe layer, like plan_variations.
|
|
8242
|
+
addons: z92.array(RevTurbineConfigAddonsItemSchema2).optional().meta({ ...Unrestricted52, ...PLAYBOOK_AUTHORING_FACETS22 }),
|
|
8243
|
+
entitlements: z92.array(RevTurbineConfigEntitlementsItemSchema2).meta({ ...Unrestricted52, ...PLAYBOOK_SDK_FACETS42 }),
|
|
8244
|
+
entitlement_rules: z92.array(RevTurbineConfigEntitlementRulesItemSchema2).meta({ ...Unrestricted52, ...PLAYBOOK_SDK_FACETS42 }),
|
|
8245
|
+
segments: z92.array(RevTurbineConfigSegmentsItemSchema2).meta({ ...Unrestricted52, ...PLAYBOOK_SDK_FACETS42 }),
|
|
8246
|
+
content_ui_paths: z92.array(ContentUiPathSchema2).meta({ ...Unrestricted52, ...PLAYBOOK_SDK_FACETS42 }),
|
|
8247
|
+
slot_configs: z92.array(RevTurbineConfigSlotConfigsItemSchema2).optional().meta({
|
|
8248
|
+
...Unrestricted52,
|
|
8249
|
+
...PLAYBOOK_SDK_FACETS42,
|
|
8250
|
+
...schemaDeprecation2({
|
|
8251
|
+
since: "0.1.117",
|
|
8252
|
+
replacement: "SDK-local activation/trigger state",
|
|
8253
|
+
removeAfter: "one compatibility window",
|
|
8254
|
+
reason: "Slot activation moved to SDK-local state (plan 118 TASK-6); no longer a Playbook authoring input."
|
|
8255
|
+
})
|
|
8256
|
+
}),
|
|
8257
|
+
content_overrides: z92.record(z92.string(), z92.record(z92.string(), z92.string())).optional().meta({
|
|
8258
|
+
...Unrestricted52,
|
|
8259
|
+
...PLAYBOOK_SDK_FACETS42,
|
|
8260
|
+
...schemaDeprecation2({
|
|
8261
|
+
since: "0.1.117",
|
|
8262
|
+
replacement: "Message Block / Placement Payload content",
|
|
8263
|
+
removeAfter: "one compatibility window",
|
|
8264
|
+
reason: "Content overrides moved to Message Block / Payload content (plan 118 TASK-6); no longer a Playbook authoring input."
|
|
8265
|
+
})
|
|
8266
|
+
}),
|
|
8267
|
+
theme: z92.record(z92.string(), z92.unknown()).optional().meta({
|
|
8268
|
+
...Unrestricted52,
|
|
8269
|
+
...LEGACY_BRANDING_FACETS2,
|
|
8270
|
+
...schemaDeprecation2({
|
|
8271
|
+
since: "0.1.111",
|
|
8272
|
+
replacement: "SDK branding argument",
|
|
8273
|
+
removeAfter: "one compatibility window",
|
|
8274
|
+
reason: "Branding is independently owned and is not Playbook strategy."
|
|
8275
|
+
})
|
|
8276
|
+
}),
|
|
8277
|
+
placement_slots: z92.array(RevTurbineConfigPlacementSlotsItemSchema2).optional().meta({ ...Unrestricted52, ...PLAYBOOK_SDK_FACETS42 }),
|
|
8278
|
+
message_blocks: z92.array(MessageBlockSchema2).optional().meta({ ...Unrestricted52, ...PLAYBOOK_SDK_FACETS42 }),
|
|
8279
|
+
placement_payloads: z92.array(RevTurbineConfigPlacementPayloadItemSchema2).optional().meta({ ...Unrestricted52, ...PLAYBOOK_SDK_FACETS42 }),
|
|
8280
|
+
placements: z92.array(RevTurbineConfigPlacementItemSchema2).optional().meta({ ...Unrestricted52, ...PLAYBOOK_SDK_FACETS42 }),
|
|
8281
|
+
content_promotions: z92.array(ContentPromotionSchema2).optional().meta({ ...Unrestricted52, ...PLAYBOOK_SDK_FACETS42 }),
|
|
8282
|
+
personalization_tokens: z92.array(RevTurbineConfigPersonalizationTokensItemSchema2).optional().meta({ ...Unrestricted52, ...PLAYBOOK_SDK_FACETS42 }),
|
|
8283
|
+
surface_templates: z92.array(RevTurbineConfigSurfaceTemplatesItemSchema2).optional().meta({ ...Unrestricted52, ...PLAYBOOK_SDK_FACETS42 }),
|
|
8284
|
+
/**
|
|
8285
|
+
* Free + reverse trial rule configurations (plan 43). Optional so
|
|
8286
|
+
* pre-trial-runtime configs continue to parse. /api/config/import
|
|
8287
|
+
* applies these to the tenant's free_trial_rules / reverse_trial_rules
|
|
8288
|
+
* tables; /api/config/export reads them out for round-trip.
|
|
8289
|
+
*/
|
|
8290
|
+
free_trial_rules: z92.array(RevTurbineConfigFreeTrialRuleItemSchema2).optional().meta({ ...Unrestricted52, ...PLAYBOOK_AUTHORING_FACETS22 }),
|
|
8291
|
+
reverse_trial_rules: z92.array(RevTurbineConfigReverseTrialRuleItemSchema2).optional().meta({ ...Unrestricted52, ...PLAYBOOK_AUTHORING_FACETS22 }),
|
|
8292
|
+
// Plan / add-on variation prices carried by handle (plan 118 TASK-16). These
|
|
8293
|
+
// live on the legacy schema (not just the canonical Playbook body) so that a
|
|
8294
|
+
// legacy `version`-shaped config — the shape the demo-data configs and the
|
|
8295
|
+
// pre-sales/CLI upload flow still use — can carry variation prices through
|
|
8296
|
+
// normalization instead of having them stripped. Pending until web
|
|
8297
|
+
// import/export activates them (TASK-21).
|
|
8298
|
+
plan_variations: z92.array(RevTurbineConfigPlanVariationsItemSchema2).optional().meta({ ...Unrestricted52, ...PLAYBOOK_AUTHORING_FACETS22 }),
|
|
8299
|
+
addon_variations: z92.array(RevTurbineConfigAddonVariationsItemSchema2).optional().meta({ ...Unrestricted52, ...PLAYBOOK_AUTHORING_FACETS22 }),
|
|
8300
|
+
/**
|
|
8301
|
+
* Tagged-opaque rule entries (Phase 3 / strategy 2). Each entry is
|
|
8302
|
+
* dispatched to the corresponding `RuleAuthoringModule.kind` at
|
|
8303
|
+
* compile time; unknown kinds are skipped silently so authoring can
|
|
8304
|
+
* stage new kinds before the runtime catches up.
|
|
8305
|
+
*/
|
|
8306
|
+
extension_rules: z92.array(RevTurbineConfigExtensionRulesItemSchema2).optional().meta({ ...Unrestricted52, ...PLAYBOOK_SDK_FACETS42 }),
|
|
8307
|
+
// Authored-config projections carried as active SDK inputs (plan 118
|
|
8308
|
+
// TASK-13/18). Declared here (not only on PlaybookBody) so the Bundle
|
|
8309
|
+
// compiler — which lowers the legacy `RevTurbineConfig` view — reads them
|
|
8310
|
+
// with proper types. Projected into the RuleBundle; see core/bundle.
|
|
8311
|
+
seat_types: z92.array(RevTurbineConfigSeatTypesItemSchema2).optional().meta({ ...Unrestricted52, ...PLAYBOOK_SDK_FACETS42 }),
|
|
8312
|
+
enforcement_defaults: z92.array(RevTurbineConfigEnforcementDefaultsItemSchema2).optional().meta({ ...Unrestricted52, ...PLAYBOOK_SDK_FACETS42 }),
|
|
8313
|
+
placement_settings: z92.array(RevTurbineConfigPlacementSettingsItemSchema2).optional().meta({ ...Unrestricted52, ...PLAYBOOK_SDK_FACETS42 }),
|
|
8314
|
+
segment_dimensions: z92.array(RevTurbineConfigSegmentDimensionsItemSchema2).optional().meta({ ...Unrestricted52, ...PLAYBOOK_SDK_FACETS42 }),
|
|
8315
|
+
meter_bindings: z92.array(RevTurbineConfigMeterBindingsItemSchema2).optional().meta({ ...Unrestricted52, ...PLAYBOOK_SDK_FACETS42 }),
|
|
8316
|
+
experiments: z92.array(z92.unknown()).max(0).optional().meta({
|
|
8317
|
+
...Unrestricted52,
|
|
8318
|
+
...PENDING_PLAYBOOK_FACETS22
|
|
8319
|
+
}),
|
|
8320
|
+
// Reserved like `experiments` above: claim the key now, ship the
|
|
8321
|
+
// semantics later. A Playbook will eventually pin the Signal Catalog
|
|
8322
|
+
// version its targeting was interpreted under, so a decision stays
|
|
8323
|
+
// reproducible after the event taxonomy moves on. Reserving costs one
|
|
8324
|
+
// schema change; adding a top-level Playbook key after the fact costs a
|
|
8325
|
+
// second breaking cascade through the IR, the CLI, demo data, and every
|
|
8326
|
+
// SDK port.
|
|
8327
|
+
//
|
|
8328
|
+
// Empty-object-only until a consumer exists — `.strict()` rejects a
|
|
8329
|
+
// populated `{ id, version }` rather than letting it round-trip as
|
|
8330
|
+
// config nothing reads. Catalog *definitions* stay server-side and must
|
|
8331
|
+
// never reach the browser Playbook; only the reference will live here.
|
|
8332
|
+
signal_catalog: z92.object({}).strict().optional().meta({
|
|
8333
|
+
...Unrestricted52,
|
|
8334
|
+
...PENDING_PLAYBOOK_FACETS22
|
|
8335
|
+
})
|
|
8336
|
+
}).meta(
|
|
8337
|
+
{
|
|
8338
|
+
id: "PlaybookBody",
|
|
8339
|
+
"x-revturbine-schema-persistence": Transient52,
|
|
8340
|
+
"x-revturbine-schema-exposure": External42,
|
|
8341
|
+
...PLAYBOOK_SDK_FACETS42
|
|
8342
|
+
}
|
|
8343
|
+
);
|
|
8344
|
+
var PlaybookHeaderSchema2 = z92.object({
|
|
8345
|
+
artifact_type: z92.literal("playbook").meta({
|
|
8346
|
+
...Unrestricted52,
|
|
8347
|
+
...PLAYBOOK_VERSION_HEADER_FACETS2,
|
|
8348
|
+
readOnly: true
|
|
8349
|
+
}),
|
|
8350
|
+
format_version: z92.literal(PLAYBOOK_FORMAT_VERSION2).meta({
|
|
8351
|
+
...Unrestricted52,
|
|
8352
|
+
...PLAYBOOK_VERSION_HEADER_FACETS2,
|
|
8353
|
+
readOnly: true
|
|
8354
|
+
}),
|
|
8355
|
+
playbook_handle: z92.string().min(1).default("default").meta({
|
|
8356
|
+
...Unrestricted52,
|
|
8357
|
+
...PLAYBOOK_VERSION_HEADER_FACETS2
|
|
8358
|
+
}),
|
|
8359
|
+
playbook_version_id: z92.string().nullable().default(null).meta({
|
|
8360
|
+
...Unrestricted52,
|
|
8361
|
+
...PLAYBOOK_PROVENANCE_HEADER_FACETS2,
|
|
8362
|
+
readOnly: true
|
|
8363
|
+
}),
|
|
8364
|
+
// Origin target identity (plan 131 TASK-10). Optional so a hand-authored /
|
|
8365
|
+
// legacy `export-config.json` (which carries no target) parses unchanged —
|
|
8366
|
+
// the plan 147 TASK-1 header reconciliation: the one config schema must
|
|
8367
|
+
// absorb legacy files that predate target stamping. Stamped by the server
|
|
8368
|
+
// on export when present.
|
|
8369
|
+
tenant_id: z92.string().min(1).optional().meta({
|
|
8370
|
+
...Unrestricted52,
|
|
8371
|
+
...PLAYBOOK_TARGET_FACETS2,
|
|
8372
|
+
readOnly: true
|
|
8373
|
+
}),
|
|
8374
|
+
environment_id: z92.string().min(1).optional().meta({
|
|
8375
|
+
...Unrestricted52,
|
|
8376
|
+
...PLAYBOOK_TARGET_FACETS2,
|
|
8377
|
+
readOnly: true
|
|
8378
|
+
}),
|
|
8379
|
+
project_id: z92.string().min(1).optional().meta({
|
|
8380
|
+
...Unrestricted52,
|
|
8381
|
+
...PLAYBOOK_TARGET_FACETS2,
|
|
8382
|
+
readOnly: true
|
|
8383
|
+
}),
|
|
8384
|
+
exported_at: z92.string().datetime().optional().meta({
|
|
8385
|
+
...Unrestricted52,
|
|
8386
|
+
...PLAYBOOK_PROVENANCE_HEADER_FACETS2,
|
|
8387
|
+
readOnly: true
|
|
8388
|
+
}),
|
|
8389
|
+
schema_version: z92.string().min(1).optional().meta({
|
|
8390
|
+
...Unrestricted52,
|
|
8391
|
+
...PLAYBOOK_VERSION_HEADER_FACETS2,
|
|
8392
|
+
readOnly: true
|
|
8393
|
+
}),
|
|
8394
|
+
bundle_schema_version: z92.number().int().nonnegative().optional().meta({
|
|
8395
|
+
...Unrestricted52,
|
|
8396
|
+
...PLAYBOOK_VERSION_HEADER_FACETS2,
|
|
8397
|
+
readOnly: true
|
|
8398
|
+
})
|
|
8399
|
+
}).meta(
|
|
8400
|
+
{
|
|
8401
|
+
id: "PlaybookHeader",
|
|
8402
|
+
"x-revturbine-schema-persistence": Transient52,
|
|
8403
|
+
"x-revturbine-schema-exposure": External42,
|
|
8404
|
+
...PLAYBOOK_PROVENANCE_HEADER_FACETS2
|
|
8405
|
+
}
|
|
8406
|
+
);
|
|
8407
|
+
var PlaybookObjectSchema2 = PlaybookHeaderSchema2.extend(PlaybookBodySchema2.shape).meta(
|
|
8408
|
+
{
|
|
8409
|
+
"x-revturbine-schema-persistence": Transient52,
|
|
8410
|
+
"x-revturbine-schema-exposure": External42,
|
|
8411
|
+
...PLAYBOOK_SDK_FACETS42
|
|
8412
|
+
}
|
|
8413
|
+
);
|
|
8414
|
+
function isRecord2(value) {
|
|
8415
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8416
|
+
}
|
|
8417
|
+
function normalizeConfigHeaderInput2(input) {
|
|
8418
|
+
if (!isRecord2(input)) return input;
|
|
8419
|
+
const next = { ...input };
|
|
8420
|
+
if (!("artifact_type" in next)) {
|
|
8421
|
+
next.artifact_type = "playbook";
|
|
8422
|
+
}
|
|
8423
|
+
if ("version" in next) {
|
|
8424
|
+
if (!("format_version" in next)) next.format_version = next.version;
|
|
8425
|
+
delete next.version;
|
|
8426
|
+
}
|
|
8427
|
+
if ("change_set_id" in next) {
|
|
8428
|
+
if (!("playbook_version_id" in next)) next.playbook_version_id = next.change_set_id;
|
|
8429
|
+
delete next.change_set_id;
|
|
8430
|
+
}
|
|
8431
|
+
return next;
|
|
8432
|
+
}
|
|
8433
|
+
var PlaybookSchema2 = z92.preprocess(normalizeConfigHeaderInput2, PlaybookObjectSchema2).meta({
|
|
8434
|
+
id: "Playbook",
|
|
8435
|
+
"x-revturbine-schema-persistence": Transient52,
|
|
8436
|
+
"x-revturbine-schema-exposure": External42,
|
|
8437
|
+
...PLAYBOOK_SDK_FACETS42
|
|
8438
|
+
});
|
|
8439
|
+
var configPaths2 = {
|
|
8440
|
+
"/api/seat-type-anchors": {
|
|
8441
|
+
get: operation2({
|
|
8442
|
+
operationId: "listSeatTypeAnchors",
|
|
8443
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
8444
|
+
summary: "List seat type anchors (identity registry)",
|
|
8445
|
+
tags: ["config"],
|
|
8446
|
+
responses: {
|
|
8447
|
+
"200": { description: "Seat type anchor list", content: { "application/json": { schema: ListEnvelope2(SeatTypeAnchorSchema2) } } },
|
|
8448
|
+
default: { description: "Error response", content: { "application/json": { schema: ErrorEnvelope2 } } }
|
|
8449
|
+
},
|
|
8450
|
+
"x-revturbine-operation": { exposure: "internal", resource: "seat-type-anchors", persistence: { table: "seatTypes", mode: "list" } }
|
|
8451
|
+
})
|
|
8452
|
+
},
|
|
8453
|
+
"/api/config/seat-types": {
|
|
8454
|
+
get: operation2({
|
|
8455
|
+
operationId: "listSeatTypes",
|
|
8456
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
8457
|
+
summary: "List seat types",
|
|
8458
|
+
tags: ["config"],
|
|
8459
|
+
responses: { "200": { description: "Seat type list", content: { "application/json": { schema: ListEnvelope2(SeatTypeSchema2) } } } },
|
|
8460
|
+
"x-revturbine-operation": { exposure: "internal", resource: "seat-types", persistence: { table: "seatTypeVersions", mode: "list" } }
|
|
8461
|
+
}),
|
|
8462
|
+
post: operation2({
|
|
8463
|
+
operationId: "createSeatType",
|
|
8464
|
+
summary: "Create seat type",
|
|
8465
|
+
tags: ["config"],
|
|
8466
|
+
requestBody: { required: true, content: { "application/json": { schema: toCreateSchema2(SeatTypeSchema2) } } },
|
|
8467
|
+
responses: { "201": { description: "Created", content: { "application/json": { schema: SeatTypeSchema2 } } } },
|
|
8468
|
+
"x-revturbine-operation": { exposure: "internal", resource: "seat-types", persistence: { table: "seatTypeVersions", mode: "create" } }
|
|
8469
|
+
})
|
|
8470
|
+
},
|
|
8471
|
+
"/api/config/seat-types/{id}": {
|
|
8472
|
+
patch: operation2({
|
|
8473
|
+
operationId: "updateSeatType",
|
|
8474
|
+
requestParams: { path: z92.object({ id: z92.string() }) },
|
|
8475
|
+
summary: "Update seat type",
|
|
8476
|
+
tags: ["config"],
|
|
8477
|
+
requestBody: { required: true, content: { "application/json": { schema: SeatTypeSchema2.partial() } } },
|
|
8478
|
+
responses: { "200": { description: "Updated", content: { "application/json": { schema: SeatTypeSchema2 } } } },
|
|
8479
|
+
"x-revturbine-operation": { exposure: "internal", resource: "seat-types", persistence: { table: "seatTypeVersions", mode: "update" } }
|
|
8480
|
+
}),
|
|
8481
|
+
delete: operation2({
|
|
8482
|
+
operationId: "deleteSeatType",
|
|
8483
|
+
requestParams: { path: z92.object({ id: z92.string() }) },
|
|
8484
|
+
summary: "Delete seat type",
|
|
8485
|
+
tags: ["config"],
|
|
8486
|
+
responses: { "204": { description: "Deleted" } },
|
|
8487
|
+
"x-revturbine-operation": { exposure: "internal", resource: "seat-types", persistence: { table: "seatTypeVersions", mode: "delete" } }
|
|
8488
|
+
})
|
|
8489
|
+
},
|
|
8490
|
+
"/api/personalization-token-anchors": {
|
|
8491
|
+
get: operation2({
|
|
8492
|
+
operationId: "listPersonalizationTokenAnchors",
|
|
8493
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
8494
|
+
summary: "List personalization token anchors (identity registry)",
|
|
8495
|
+
tags: ["config"],
|
|
8496
|
+
responses: {
|
|
8497
|
+
"200": { description: "Personalization token anchor list", content: { "application/json": { schema: ListEnvelope2(PersonalizationTokenAnchorSchema2) } } },
|
|
8498
|
+
default: { description: "Error response", content: { "application/json": { schema: ErrorEnvelope2 } } }
|
|
8499
|
+
},
|
|
8500
|
+
"x-revturbine-operation": { exposure: "internal", resource: "personalization-token-anchors", persistence: { table: "personalizationTokens", mode: "list" } }
|
|
8501
|
+
})
|
|
8502
|
+
},
|
|
8503
|
+
"/api/config/personalization-tokens": {
|
|
8504
|
+
get: operation2({
|
|
8505
|
+
operationId: "listPersonalizationTokens",
|
|
8506
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
8507
|
+
summary: "List personalization tokens",
|
|
8508
|
+
tags: ["config"],
|
|
8509
|
+
responses: { "200": { description: "Personalization token list", content: { "application/json": { schema: ListEnvelope2(PersonalizationTokenSchema2) } } } },
|
|
8510
|
+
"x-revturbine-operation": { exposure: "internal", resource: "personalization-tokens", persistence: { table: "personalizationTokenVersions", mode: "list" } }
|
|
8511
|
+
}),
|
|
8512
|
+
post: operation2({
|
|
8513
|
+
operationId: "createPersonalizationToken",
|
|
8514
|
+
summary: "Create personalization token",
|
|
8515
|
+
tags: ["config"],
|
|
8516
|
+
requestBody: { required: true, content: { "application/json": { schema: toCreateSchema2(PersonalizationTokenSchema2) } } },
|
|
8517
|
+
responses: { "201": { description: "Created", content: { "application/json": { schema: PersonalizationTokenSchema2 } } } },
|
|
8518
|
+
"x-revturbine-operation": { exposure: "internal", resource: "personalization-tokens", persistence: { table: "personalizationTokenVersions", mode: "create" } }
|
|
8519
|
+
})
|
|
8520
|
+
},
|
|
8521
|
+
"/api/config/personalization-tokens/{id}": {
|
|
8522
|
+
patch: operation2({
|
|
8523
|
+
operationId: "updatePersonalizationToken",
|
|
8524
|
+
requestParams: { path: z92.object({ id: z92.string() }) },
|
|
8525
|
+
summary: "Update personalization token",
|
|
8526
|
+
tags: ["config"],
|
|
8527
|
+
requestBody: { required: true, content: { "application/json": { schema: PersonalizationTokenSchema2.partial() } } },
|
|
8528
|
+
responses: { "200": { description: "Updated", content: { "application/json": { schema: PersonalizationTokenSchema2 } } } },
|
|
8529
|
+
"x-revturbine-operation": { exposure: "internal", resource: "personalization-tokens", persistence: { table: "personalizationTokenVersions", mode: "update" } }
|
|
8530
|
+
}),
|
|
8531
|
+
delete: operation2({
|
|
8532
|
+
operationId: "deletePersonalizationToken",
|
|
8533
|
+
requestParams: { path: z92.object({ id: z92.string() }) },
|
|
8534
|
+
summary: "Delete personalization token",
|
|
8535
|
+
tags: ["config"],
|
|
8536
|
+
responses: { "204": { description: "Deleted" } },
|
|
8537
|
+
"x-revturbine-operation": { exposure: "internal", resource: "personalization-tokens", persistence: { table: "personalizationTokenVersions", mode: "delete" } }
|
|
8538
|
+
})
|
|
8539
|
+
},
|
|
8540
|
+
"/api/config/stripe": {
|
|
8541
|
+
get: operation2({
|
|
8542
|
+
operationId: "getStripeIntegrationConfig",
|
|
8543
|
+
summary: "Get Stripe integration configuration",
|
|
8544
|
+
tags: ["config"],
|
|
8545
|
+
responses: { "200": { description: "Stripe config", content: { "application/json": { schema: StripeIntegrationConfigSchema2 } } } },
|
|
8546
|
+
"x-revturbine-operation": { exposure: "internal", resource: "stripe-config", persistence: { table: "stripeIntegrationConfig", mode: "get" } }
|
|
8547
|
+
}),
|
|
8548
|
+
put: operation2({
|
|
8549
|
+
operationId: "upsertStripeIntegrationConfig",
|
|
8550
|
+
summary: "Create or update Stripe integration config",
|
|
8551
|
+
tags: ["config"],
|
|
8552
|
+
requestBody: { required: true, content: { "application/json": { schema: toWritableSchema2(StripeIntegrationConfigSchema2) } } },
|
|
8553
|
+
responses: { "200": { description: "Saved", content: { "application/json": { schema: StripeIntegrationConfigSchema2 } } } },
|
|
8554
|
+
"x-revturbine-operation": { exposure: "internal", resource: "stripe-config", persistence: { table: "stripeIntegrationConfig", mode: "upsert" } }
|
|
8555
|
+
})
|
|
8556
|
+
},
|
|
8557
|
+
"/api/config/metering": {
|
|
8558
|
+
get: operation2({
|
|
8559
|
+
operationId: "listMeteringConfigs",
|
|
8560
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
8561
|
+
summary: "List metering configurations",
|
|
8562
|
+
tags: ["config"],
|
|
8563
|
+
responses: { "200": { description: "Metering config list", content: { "application/json": { schema: ListEnvelope2(MeteringConfigSchema2) } } } },
|
|
8564
|
+
"x-revturbine-operation": { exposure: "internal", resource: "metering-config", persistence: { table: "meteringConfigs", mode: "list" } }
|
|
8565
|
+
}),
|
|
8566
|
+
post: operation2({
|
|
8567
|
+
operationId: "createMeteringConfig",
|
|
8568
|
+
summary: "Create metering configuration",
|
|
8569
|
+
tags: ["config"],
|
|
8570
|
+
requestBody: { required: true, content: { "application/json": { schema: toCreateSchema2(MeteringConfigSchema2) } } },
|
|
8571
|
+
responses: { "201": { description: "Created", content: { "application/json": { schema: MeteringConfigSchema2 } } } },
|
|
8572
|
+
"x-revturbine-operation": { exposure: "internal", resource: "metering-config", persistence: { table: "meteringConfigs", mode: "create" } }
|
|
8573
|
+
})
|
|
8574
|
+
},
|
|
8575
|
+
"/api/config/metering/{meteringId}": {
|
|
8576
|
+
patch: operation2({
|
|
8577
|
+
operationId: "updateMeteringConfig",
|
|
8578
|
+
requestParams: { path: z92.object({ meteringId: z92.string() }) },
|
|
8579
|
+
summary: "Update metering configuration",
|
|
8580
|
+
tags: ["config"],
|
|
8581
|
+
requestBody: { required: true, content: { "application/json": { schema: MeteringConfigSchema2.partial() } } },
|
|
8582
|
+
responses: { "200": { description: "Updated", content: { "application/json": { schema: MeteringConfigSchema2 } } } },
|
|
8583
|
+
"x-revturbine-operation": { exposure: "internal", resource: "metering-config", persistence: { table: "meteringConfigs", mode: "update" } }
|
|
8584
|
+
}),
|
|
8585
|
+
delete: operation2({
|
|
8586
|
+
operationId: "deleteMeteringConfig",
|
|
8587
|
+
requestParams: { path: z92.object({ meteringId: z92.string() }) },
|
|
8588
|
+
summary: "Delete metering configuration",
|
|
8589
|
+
tags: ["config"],
|
|
8590
|
+
responses: { "204": { description: "Deleted" } },
|
|
8591
|
+
"x-revturbine-operation": { exposure: "internal", resource: "metering-config", persistence: { table: "meteringConfigs", mode: "delete" } }
|
|
8592
|
+
})
|
|
8593
|
+
},
|
|
8594
|
+
"/api/usage-enforcement-setting-anchors": {
|
|
8595
|
+
get: operation2({
|
|
8596
|
+
operationId: "listUsageEnforcementSettingsAnchors",
|
|
8597
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
8598
|
+
summary: "List usage enforcement setting anchors (identity registry)",
|
|
8599
|
+
tags: ["config"],
|
|
8600
|
+
responses: {
|
|
8601
|
+
"200": { description: "Usage enforcement setting anchor list", content: { "application/json": { schema: ListEnvelope2(UsageEnforcementSettingsAnchorSchema2) } } },
|
|
8602
|
+
default: { description: "Error response", content: { "application/json": { schema: ErrorEnvelope2 } } }
|
|
8603
|
+
},
|
|
8604
|
+
"x-revturbine-operation": { exposure: "internal", resource: "usage-enforcement-setting-anchors", persistence: { table: "usageEnforcementSettings", mode: "list" } }
|
|
8605
|
+
})
|
|
8606
|
+
},
|
|
8607
|
+
"/api/config/usage-enforcement": {
|
|
8608
|
+
get: operation2({
|
|
8609
|
+
operationId: "listUsageEnforcementSettings",
|
|
8610
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
8611
|
+
summary: "List usage enforcement settings",
|
|
8612
|
+
tags: ["config"],
|
|
8613
|
+
responses: { "200": { description: "Enforcement settings", content: { "application/json": { schema: ListEnvelope2(UsageEnforcementSettingsSchema2) } } } },
|
|
8614
|
+
"x-revturbine-operation": { exposure: "internal", resource: "usage-enforcement", persistence: { table: "usageEnforcementSettingVersions", mode: "list" } }
|
|
8615
|
+
}),
|
|
8616
|
+
post: operation2({
|
|
8617
|
+
operationId: "createUsageEnforcementSettings",
|
|
8618
|
+
summary: "Create usage enforcement settings",
|
|
8619
|
+
tags: ["config"],
|
|
8620
|
+
requestBody: { required: true, content: { "application/json": { schema: toCreateSchema2(UsageEnforcementSettingsSchema2) } } },
|
|
8621
|
+
responses: { "201": { description: "Created", content: { "application/json": { schema: UsageEnforcementSettingsSchema2 } } } },
|
|
8622
|
+
"x-revturbine-operation": { exposure: "internal", resource: "usage-enforcement", persistence: { table: "usageEnforcementSettingVersions", mode: "create" } }
|
|
8623
|
+
})
|
|
8624
|
+
},
|
|
8625
|
+
"/api/config/usage-enforcement/{settingsId}": {
|
|
8626
|
+
patch: operation2({
|
|
8627
|
+
operationId: "updateUsageEnforcementSettings",
|
|
8628
|
+
requestParams: { path: z92.object({ settingsId: z92.string() }) },
|
|
8629
|
+
summary: "Update usage enforcement settings",
|
|
8630
|
+
tags: ["config"],
|
|
8631
|
+
requestBody: { required: true, content: { "application/json": { schema: UsageEnforcementSettingsSchema2.partial() } } },
|
|
8632
|
+
responses: { "200": { description: "Updated", content: { "application/json": { schema: UsageEnforcementSettingsSchema2 } } } },
|
|
8633
|
+
"x-revturbine-operation": { exposure: "internal", resource: "usage-enforcement", persistence: { table: "usageEnforcementSettingVersions", mode: "update" } }
|
|
8634
|
+
}),
|
|
8635
|
+
delete: operation2({
|
|
8636
|
+
operationId: "deleteUsageEnforcementSettings",
|
|
8637
|
+
requestParams: { path: z92.object({ settingsId: z92.string() }) },
|
|
8638
|
+
summary: "Delete usage enforcement settings",
|
|
8639
|
+
tags: ["config"],
|
|
8640
|
+
responses: { "204": { description: "Deleted" } },
|
|
8641
|
+
"x-revturbine-operation": { exposure: "internal", resource: "usage-enforcement", persistence: { table: "usageEnforcementSettingVersions", mode: "delete" } }
|
|
8642
|
+
})
|
|
8643
|
+
},
|
|
8644
|
+
"/api/placement-setting-anchors": {
|
|
8645
|
+
get: operation2({
|
|
8646
|
+
operationId: "listPlacementSettingsAnchors",
|
|
8647
|
+
requestParams: { query: ListQueryParamsSchema2 },
|
|
8648
|
+
summary: "List placement setting anchors (identity registry)",
|
|
8649
|
+
tags: ["config"],
|
|
8650
|
+
responses: {
|
|
8651
|
+
"200": { description: "Placement setting anchor list", content: { "application/json": { schema: ListEnvelope2(PlacementSettingsAnchorSchema2) } } },
|
|
8652
|
+
default: { description: "Error response", content: { "application/json": { schema: ErrorEnvelope2 } } }
|
|
8653
|
+
},
|
|
8654
|
+
"x-revturbine-operation": { exposure: "internal", resource: "placement-setting-anchors", persistence: { table: "placementSettings", mode: "list" } }
|
|
8655
|
+
})
|
|
8656
|
+
},
|
|
8657
|
+
"/api/config/placement-settings": {
|
|
8658
|
+
get: operation2({
|
|
8659
|
+
operationId: "getPlacementSettings",
|
|
8660
|
+
summary: "Get global placement settings",
|
|
8661
|
+
tags: ["config"],
|
|
8662
|
+
responses: { "200": { description: "Placement settings", content: { "application/json": { schema: PlacementSettingsSchema2 } } } },
|
|
8663
|
+
"x-revturbine-operation": { exposure: "internal", resource: "placement-settings", persistence: { table: "placementSettingVersions", mode: "get" } }
|
|
8664
|
+
}),
|
|
8665
|
+
put: operation2({
|
|
8666
|
+
operationId: "upsertPlacementSettings",
|
|
8667
|
+
summary: "Create or update global placement settings",
|
|
8668
|
+
tags: ["config"],
|
|
8669
|
+
requestBody: { required: true, content: { "application/json": { schema: toWritableSchema2(PlacementSettingsSchema2) } } },
|
|
8670
|
+
responses: { "200": { description: "Saved", content: { "application/json": { schema: PlacementSettingsSchema2 } } } },
|
|
8671
|
+
"x-revturbine-operation": { exposure: "internal", resource: "placement-settings", persistence: { table: "placementSettingVersions", mode: "upsert" } }
|
|
8672
|
+
})
|
|
8673
|
+
}
|
|
8674
|
+
};
|
|
8675
|
+
var DEPRECATED_FIELD_REPAIR_CODE = "VAL-DEP-01";
|
|
8676
|
+
function isRecord22(value) {
|
|
8677
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8678
|
+
}
|
|
8679
|
+
function readShape(schema) {
|
|
8680
|
+
const shape = schema.shape;
|
|
8681
|
+
if (shape && typeof shape === "object") {
|
|
8682
|
+
return shape;
|
|
8683
|
+
}
|
|
8684
|
+
return void 0;
|
|
8685
|
+
}
|
|
8686
|
+
function repairFinding(field, deprecation) {
|
|
8687
|
+
return {
|
|
8688
|
+
code: DEPRECATED_FIELD_REPAIR_CODE,
|
|
8689
|
+
severity: "warning",
|
|
8690
|
+
targetRef: { field, path: [field] },
|
|
8691
|
+
message: `Deprecated field '${field}' was removed from the config so it stays valid. ${deprecation.reason} Use ${deprecation.replacement} instead (the field is scheduled for removal after ${deprecation.removeAfter}).`,
|
|
8692
|
+
specRef: "config-validation.md deprecation repair (plan 147 REQ-4, AC-3)",
|
|
8693
|
+
detail: `Field deprecated since ${deprecation.since}. The config was repaired by absorbing (removing) this field rather than being rejected for carrying it; supply '${deprecation.replacement}' through its current owner instead.`
|
|
8694
|
+
};
|
|
8695
|
+
}
|
|
8696
|
+
function repairDeprecatedFields(config, schema = PlaybookObjectSchema2) {
|
|
8697
|
+
if (!isRecord22(config)) return { config, findings: [] };
|
|
8698
|
+
const shape = readShape(schema);
|
|
8699
|
+
if (!shape) return { config, findings: [] };
|
|
8700
|
+
const findings = [];
|
|
8701
|
+
let repaired;
|
|
8702
|
+
for (const [field, fieldSchema] of Object.entries(shape)) {
|
|
8703
|
+
const deprecation = getSchemaDeprecation(fieldSchema);
|
|
8704
|
+
if (!deprecation) continue;
|
|
8705
|
+
if (!Object.prototype.hasOwnProperty.call(config, field)) continue;
|
|
8706
|
+
repaired ??= { ...config };
|
|
8707
|
+
delete repaired[field];
|
|
8708
|
+
findings.push(repairFinding(field, deprecation));
|
|
8709
|
+
}
|
|
8710
|
+
return { config: repaired ?? config, findings };
|
|
8711
|
+
}
|
|
8712
|
+
|
|
8713
|
+
// src/schema/version.ts
|
|
8714
|
+
var SCHEMA_VERSION = "0.1.145";
|
|
8715
|
+
|
|
8716
|
+
// src/lib/credentials.ts
|
|
8717
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs";
|
|
8718
|
+
import os from "os";
|
|
8719
|
+
import path from "path";
|
|
8720
|
+
function resolveConfigDir(cwd = process.cwd()) {
|
|
8721
|
+
const override = process.env.REVTURBINE_CONFIG_DIR;
|
|
8722
|
+
if (override) return { dir: path.resolve(override), source: "env" };
|
|
8723
|
+
const scoped = findWorktreeConfigDir(cwd);
|
|
8724
|
+
if (scoped) return { dir: scoped, source: "worktree" };
|
|
8725
|
+
return { dir: path.join(os.homedir(), ".revturbine"), source: "global" };
|
|
8726
|
+
}
|
|
8727
|
+
function findWorktreeConfigDir(start) {
|
|
8728
|
+
let dir = path.resolve(start);
|
|
8729
|
+
for (; ; ) {
|
|
8730
|
+
const candidate = path.join(dir, ".revturbine");
|
|
8731
|
+
if (existsSync(candidate) && statSync(candidate).isDirectory()) return candidate;
|
|
8732
|
+
const parent = path.dirname(dir);
|
|
8733
|
+
if (parent === dir) return null;
|
|
8734
|
+
dir = parent;
|
|
8735
|
+
}
|
|
8736
|
+
}
|
|
8737
|
+
function configDir() {
|
|
8738
|
+
return resolveConfigDir().dir;
|
|
8739
|
+
}
|
|
8740
|
+
function configFile() {
|
|
8741
|
+
return path.join(configDir(), "credentials.json");
|
|
8742
|
+
}
|
|
8743
|
+
function normalizeBaseUrl(input) {
|
|
8744
|
+
const s = input.trim();
|
|
8745
|
+
if (/^https?:\/\//i.test(s)) return s.replace(/\/+$/, "");
|
|
8746
|
+
const isLocal = /^(localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\])(:\d+)?(\/|$)/i.test(s);
|
|
8747
|
+
return `${isLocal ? "http" : "https"}://${s}`.replace(/\/+$/, "");
|
|
8748
|
+
}
|
|
8749
|
+
function urlKey(baseUrl) {
|
|
8750
|
+
try {
|
|
8751
|
+
return new URL(normalizeBaseUrl(baseUrl)).origin;
|
|
8752
|
+
} catch {
|
|
8753
|
+
return baseUrl.replace(/\/+$/, "");
|
|
8754
|
+
}
|
|
8755
|
+
}
|
|
8756
|
+
function read() {
|
|
8757
|
+
const file = configFile();
|
|
8758
|
+
if (!existsSync(file)) return { version: 1, credentials: {} };
|
|
8759
|
+
try {
|
|
8760
|
+
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
8761
|
+
if (parsed && typeof parsed === "object" && parsed.credentials) {
|
|
8762
|
+
return { version: 1, credentials: parsed.credentials };
|
|
8763
|
+
}
|
|
8764
|
+
} catch {
|
|
8765
|
+
}
|
|
8766
|
+
return { version: 1, credentials: {} };
|
|
8767
|
+
}
|
|
8768
|
+
function write(data) {
|
|
8769
|
+
const dir = configDir();
|
|
8770
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 448 });
|
|
8771
|
+
writeFileSync(configFile(), `${JSON.stringify(data, null, 2)}
|
|
8772
|
+
`, { mode: 384 });
|
|
8773
|
+
try {
|
|
8774
|
+
chmodSync(configFile(), 384);
|
|
8775
|
+
} catch {
|
|
8776
|
+
}
|
|
8777
|
+
}
|
|
8778
|
+
function saveCredential(baseUrl, cred) {
|
|
8779
|
+
const data = read();
|
|
8780
|
+
data.credentials[urlKey(baseUrl)] = cred;
|
|
8781
|
+
write(data);
|
|
8782
|
+
}
|
|
8783
|
+
function getCredential(baseUrl) {
|
|
8784
|
+
return read().credentials[urlKey(baseUrl)] ?? null;
|
|
8785
|
+
}
|
|
8786
|
+
function removeCredential(baseUrl) {
|
|
8787
|
+
const data = read();
|
|
8788
|
+
const key = urlKey(baseUrl);
|
|
8789
|
+
if (!(key in data.credentials)) return false;
|
|
8790
|
+
delete data.credentials[key];
|
|
8791
|
+
write(data);
|
|
8792
|
+
return true;
|
|
8793
|
+
}
|
|
8794
|
+
function redactToken(token) {
|
|
8795
|
+
if (token.length <= 8) return "rtk_\u2026";
|
|
8796
|
+
return `${token.slice(0, 4)}\u2026${token.slice(-4)}`;
|
|
8797
|
+
}
|
|
8798
|
+
|
|
8799
|
+
// src/lib/device-auth.ts
|
|
8800
|
+
import { spawn } from "child_process";
|
|
8801
|
+
import os2 from "os";
|
|
8802
|
+
function trimUrl(baseUrl) {
|
|
8803
|
+
return baseUrl.replace(/\/+$/, "");
|
|
8804
|
+
}
|
|
8805
|
+
async function startDeviceAuth(baseUrl, label, fetchImpl) {
|
|
8806
|
+
const res = await fetchImpl(`${trimUrl(baseUrl)}/api/cli/device/code`, {
|
|
8807
|
+
method: "POST",
|
|
8808
|
+
headers: { "Content-Type": "application/json" },
|
|
8809
|
+
body: JSON.stringify(label ? { label } : {})
|
|
8810
|
+
});
|
|
8811
|
+
if (!res.ok) {
|
|
8812
|
+
throw new Error(`Could not start device login (${res.status}). Is ${baseUrl} reachable?`);
|
|
8813
|
+
}
|
|
8814
|
+
return await res.json();
|
|
8815
|
+
}
|
|
8816
|
+
async function pollForToken(opts) {
|
|
8817
|
+
const now = opts.now ?? (() => Date.now());
|
|
8818
|
+
const startedAt = now();
|
|
8819
|
+
let interval = opts.intervalSeconds;
|
|
8820
|
+
for (; ; ) {
|
|
8821
|
+
if (now() - startedAt > opts.expiresInSeconds * 1e3) {
|
|
8822
|
+
throw new Error("Device code expired before authorization. Run --login again.");
|
|
8823
|
+
}
|
|
8824
|
+
await opts.sleep(interval * 1e3);
|
|
8825
|
+
const res = await opts.fetchImpl(`${trimUrl(opts.baseUrl)}/api/cli/device/token`, {
|
|
8826
|
+
method: "POST",
|
|
8827
|
+
headers: { "Content-Type": "application/json" },
|
|
8828
|
+
body: JSON.stringify({ device_code: opts.deviceCode })
|
|
8829
|
+
});
|
|
8830
|
+
if (res.ok) {
|
|
8831
|
+
return await res.json();
|
|
8832
|
+
}
|
|
8833
|
+
const body = await res.json().catch(() => ({}));
|
|
8834
|
+
switch (body.error) {
|
|
8835
|
+
case "authorization_pending":
|
|
8836
|
+
opts.onPending?.();
|
|
8837
|
+
continue;
|
|
8838
|
+
case "slow_down":
|
|
8839
|
+
interval += 5;
|
|
8840
|
+
continue;
|
|
8841
|
+
case "access_denied":
|
|
8842
|
+
throw new Error("Authorization was denied in the browser.");
|
|
8843
|
+
case "expired_token":
|
|
8844
|
+
throw new Error("Device code expired. Run --login again.");
|
|
8845
|
+
default:
|
|
8846
|
+
throw new Error(`Authorization failed: ${body.error ?? `HTTP ${res.status}`}`);
|
|
8847
|
+
}
|
|
8848
|
+
}
|
|
8849
|
+
}
|
|
8850
|
+
function openBrowser(url) {
|
|
8851
|
+
try {
|
|
8852
|
+
const cmd = process.platform === "win32" ? "cmd" : process.platform === "darwin" ? "open" : "xdg-open";
|
|
8853
|
+
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
8854
|
+
const child = spawn(cmd, args, { stdio: "ignore", detached: true });
|
|
8855
|
+
child.on("error", () => {
|
|
8856
|
+
});
|
|
8857
|
+
child.unref();
|
|
8858
|
+
} catch {
|
|
6278
8859
|
}
|
|
6279
8860
|
}
|
|
6280
8861
|
async function deviceLogin(baseUrl, log = console.log) {
|
|
@@ -6563,6 +9144,69 @@ async function resolveActiveDraft(baseUrl, headers, fetchImpl = fetch) {
|
|
|
6563
9144
|
return { ok: res.ok, status: res.status, draft: res.ok ? json.active ?? null : null };
|
|
6564
9145
|
}
|
|
6565
9146
|
|
|
9147
|
+
// src/lib/ingest-keys.ts
|
|
9148
|
+
async function readJson(res) {
|
|
9149
|
+
return await res.json().catch(() => ({}));
|
|
9150
|
+
}
|
|
9151
|
+
function asStringArray(value) {
|
|
9152
|
+
return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
|
|
9153
|
+
}
|
|
9154
|
+
async function createIngestKey(baseUrl, headers, input, fetchImpl = fetch) {
|
|
9155
|
+
const res = await fetchImpl(`${baseUrl}/api/developer/ingest-keys`, {
|
|
9156
|
+
method: "POST",
|
|
9157
|
+
headers,
|
|
9158
|
+
body: JSON.stringify({
|
|
9159
|
+
origin_allowlist: input.originAllowlist,
|
|
9160
|
+
ip_allowlist: input.ipAllowlist ?? []
|
|
9161
|
+
})
|
|
9162
|
+
});
|
|
9163
|
+
const json = await readJson(res);
|
|
9164
|
+
if (!res.ok) return { ok: false, status: res.status, key: null, error: json };
|
|
9165
|
+
return {
|
|
9166
|
+
ok: true,
|
|
9167
|
+
status: res.status,
|
|
9168
|
+
key: {
|
|
9169
|
+
id: typeof json.id === "string" ? json.id : "",
|
|
9170
|
+
token: typeof json.token === "string" ? json.token : "",
|
|
9171
|
+
tokenPreview: typeof json.tokenPreview === "string" ? json.tokenPreview : "",
|
|
9172
|
+
originAllowlist: asStringArray(json.originAllowlist),
|
|
9173
|
+
ipAllowlist: asStringArray(json.ipAllowlist),
|
|
9174
|
+
createdAt: typeof json.createdAt === "string" ? json.createdAt : void 0
|
|
9175
|
+
},
|
|
9176
|
+
error: {}
|
|
9177
|
+
};
|
|
9178
|
+
}
|
|
9179
|
+
async function listIngestKeys(baseUrl, headers, fetchImpl = fetch) {
|
|
9180
|
+
const res = await fetchImpl(`${baseUrl}/api/developer/ingest-keys`, { headers });
|
|
9181
|
+
const json = await readJson(res);
|
|
9182
|
+
const rawKeys = Array.isArray(json.keys) ? json.keys : [];
|
|
9183
|
+
const keys = rawKeys.map((k) => {
|
|
9184
|
+
const row = k ?? {};
|
|
9185
|
+
return {
|
|
9186
|
+
id: typeof row.id === "string" ? row.id : "",
|
|
9187
|
+
tokenPreview: typeof row.tokenPreview === "string" ? row.tokenPreview : "",
|
|
9188
|
+
originAllowlist: asStringArray(row.originAllowlist),
|
|
9189
|
+
ipAllowlist: asStringArray(row.ipAllowlist),
|
|
9190
|
+
createdAt: typeof row.createdAt === "string" ? row.createdAt : void 0,
|
|
9191
|
+
lastUsedAt: typeof row.lastUsedAt === "string" ? row.lastUsedAt : null
|
|
9192
|
+
};
|
|
9193
|
+
});
|
|
9194
|
+
return { ok: res.ok, status: res.status, keys: res.ok ? keys : [] };
|
|
9195
|
+
}
|
|
9196
|
+
async function revokeIngestKey(baseUrl, headers, id, fetchImpl = fetch) {
|
|
9197
|
+
const res = await fetchImpl(`${baseUrl}/api/developer/ingest-keys/${encodeURIComponent(id)}`, {
|
|
9198
|
+
method: "DELETE",
|
|
9199
|
+
headers
|
|
9200
|
+
});
|
|
9201
|
+
return { ok: res.ok, status: res.status };
|
|
9202
|
+
}
|
|
9203
|
+
function formatIngestKeyLine(key) {
|
|
9204
|
+
const origins = key.originAllowlist.length ? key.originAllowlist.join(", ") : "(none)";
|
|
9205
|
+
const ips = key.ipAllowlist.length ? ` ips: ${key.ipAllowlist.join(", ")}` : "";
|
|
9206
|
+
const last = key.lastUsedAt ? ` last used ${key.lastUsedAt}` : "";
|
|
9207
|
+
return `${key.id} ${key.tokenPreview} origins: ${origins}${ips}${last}`;
|
|
9208
|
+
}
|
|
9209
|
+
|
|
6566
9210
|
// src/lib/delegate.ts
|
|
6567
9211
|
var DELEGATION_ENV = "REVTURBINE_DELEGATED";
|
|
6568
9212
|
var NO_LOCAL_FLAG = "--no-local";
|
|
@@ -6615,6 +9259,54 @@ function schemaForConfig(raw) {
|
|
|
6615
9259
|
return header.shape === "canonical" ? { schema: PlaybookSchema, shape: "canonical" } : { schema: RevTurbineConfigSchema, shape: "legacy" };
|
|
6616
9260
|
}
|
|
6617
9261
|
|
|
9262
|
+
// src/lib/offline-config-check.ts
|
|
9263
|
+
var repairDeprecatedFields2 = repairDeprecatedFields;
|
|
9264
|
+
var LEGACY_HEADER_ALIASES = /* @__PURE__ */ new Set(["version", "change_set_id"]);
|
|
9265
|
+
var UNKNOWN_FIELD_CODE = "cli/unknown-field";
|
|
9266
|
+
function isRecord3(value) {
|
|
9267
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9268
|
+
}
|
|
9269
|
+
function unknownTopLevelKeys(raw) {
|
|
9270
|
+
if (!isRecord3(raw)) return [];
|
|
9271
|
+
const { schema } = schemaForConfig(raw);
|
|
9272
|
+
const parsed = schema.safeParse(raw);
|
|
9273
|
+
if (!parsed.success || !isRecord3(parsed.data)) return [];
|
|
9274
|
+
const recognized = new Set(Object.keys(parsed.data));
|
|
9275
|
+
return Object.keys(raw).filter((key) => !recognized.has(key) && !LEGACY_HEADER_ALIASES.has(key));
|
|
9276
|
+
}
|
|
9277
|
+
function offlineAdvisories(raw) {
|
|
9278
|
+
const advisories = [];
|
|
9279
|
+
for (const finding2 of repairDeprecatedFields2(raw).findings) {
|
|
9280
|
+
advisories.push({
|
|
9281
|
+
code: finding2.code,
|
|
9282
|
+
severity: "warning",
|
|
9283
|
+
message: finding2.message,
|
|
9284
|
+
targetRef: { ...finding2.targetRef ?? {} }
|
|
9285
|
+
});
|
|
9286
|
+
}
|
|
9287
|
+
for (const key of unknownTopLevelKeys(raw)) {
|
|
9288
|
+
advisories.push({
|
|
9289
|
+
code: UNKNOWN_FIELD_CODE,
|
|
9290
|
+
severity: "warning",
|
|
9291
|
+
message: `Unknown top-level field '${key}' is not part of the RevTurbine config schema (bundled offline snapshot). The CLI uploads it unchanged so the server can validate it \u2014 remove it if it was a typo.`,
|
|
9292
|
+
targetRef: { field: key, path: [key] }
|
|
9293
|
+
});
|
|
9294
|
+
}
|
|
9295
|
+
return advisories;
|
|
9296
|
+
}
|
|
9297
|
+
function checkOfflineConfig(raw) {
|
|
9298
|
+
const { schema, shape } = schemaForConfig(raw);
|
|
9299
|
+
const advisories = offlineAdvisories(raw);
|
|
9300
|
+
if (shape === "canonical") {
|
|
9301
|
+
return { ok: true, body: raw, shape, advisories, problems: [] };
|
|
9302
|
+
}
|
|
9303
|
+
const parsed = schema.safeParse(raw);
|
|
9304
|
+
if (parsed.success) {
|
|
9305
|
+
return { ok: true, body: raw, shape, advisories, problems: [] };
|
|
9306
|
+
}
|
|
9307
|
+
return { ok: false, body: raw, shape, advisories, problems: parsed.error.issues };
|
|
9308
|
+
}
|
|
9309
|
+
|
|
6618
9310
|
// src/lib/init.ts
|
|
6619
9311
|
var LOCKFILES = [
|
|
6620
9312
|
["pnpm-lock.yaml", "pnpm"],
|
|
@@ -6707,7 +9399,7 @@ var STARTER_PLAYBOOK = {
|
|
|
6707
9399
|
entitlement_id: "ent_advanced_export",
|
|
6708
9400
|
targets: [{ kind: "plan", id: "plan_free" }],
|
|
6709
9401
|
segment_ids: [],
|
|
6710
|
-
|
|
9402
|
+
enabled: false,
|
|
6711
9403
|
current_usage: 0
|
|
6712
9404
|
},
|
|
6713
9405
|
{
|
|
@@ -6715,7 +9407,7 @@ var STARTER_PLAYBOOK = {
|
|
|
6715
9407
|
entitlement_id: "ent_advanced_export",
|
|
6716
9408
|
targets: [{ kind: "plan", id: "plan_pro" }],
|
|
6717
9409
|
segment_ids: [],
|
|
6718
|
-
|
|
9410
|
+
enabled: true,
|
|
6719
9411
|
current_usage: 0
|
|
6720
9412
|
}
|
|
6721
9413
|
],
|
|
@@ -6771,7 +9463,7 @@ function finalOutputLines(params) {
|
|
|
6771
9463
|
lines.push(` ${params.harness.invocation}`);
|
|
6772
9464
|
if (params.harness.label) lines.push(` (detected ${params.harness.label})`);
|
|
6773
9465
|
lines.push("", "Your setup path:");
|
|
6774
|
-
lines.push(" create playbook \u2192
|
|
9466
|
+
lines.push(" create playbook \u2192 app wiring \u2192 billing \u2192 verify \u2192 launch");
|
|
6775
9467
|
lines.push("", "Docs: https://revturbine.com/docs", "");
|
|
6776
9468
|
return lines;
|
|
6777
9469
|
}
|
|
@@ -6907,7 +9599,6 @@ function serverSchemaIsNewer(config, bundledVersion) {
|
|
|
6907
9599
|
// src/cli.ts
|
|
6908
9600
|
var DOCS_URL = "https://github.com/revt-eng/revturbine-cli#readme";
|
|
6909
9601
|
var DEFAULT_URL = "https://revturbine.com/app";
|
|
6910
|
-
var schema = RevTurbineConfigSchema;
|
|
6911
9602
|
function loadConfig(file) {
|
|
6912
9603
|
const resolved = path2.resolve(file);
|
|
6913
9604
|
if (!existsSync2(resolved)) fail(EXIT.USAGE, `Config not found: ${resolved}`);
|
|
@@ -6929,20 +9620,19 @@ function verifyConfig(file) {
|
|
|
6929
9620
|
}
|
|
6930
9621
|
throw err;
|
|
6931
9622
|
}
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
return result.data;
|
|
6940
|
-
}
|
|
6941
|
-
diag(`\u2717 ${file}: schema validation FAILED:`);
|
|
6942
|
-
for (const issue of result.error.issues) {
|
|
6943
|
-
diagRaw(` ${issue.path.join(".") || "<root>"}: ${issue.message}`);
|
|
9623
|
+
const check = checkOfflineConfig(parsed);
|
|
9624
|
+
if (!check.ok) {
|
|
9625
|
+
diag(`\u2717 ${file}: schema validation FAILED:`);
|
|
9626
|
+
for (const issue of check.problems) {
|
|
9627
|
+
diagRaw(` ${issue.path.join(".") || "<root>"}: ${issue.message}`);
|
|
9628
|
+
}
|
|
9629
|
+
return null;
|
|
6944
9630
|
}
|
|
6945
|
-
|
|
9631
|
+
diag(
|
|
9632
|
+
check.shape === "canonical" ? `\u2713 ${file}: ${describePlaybookHeader(header)} \u2014 body validated server-side on import` : `\u2713 ${file}: schema validation passed \u2014 ${describePlaybookHeader(header)}`
|
|
9633
|
+
);
|
|
9634
|
+
if (check.advisories.length > 0) diagRaw(formatFindings(check.advisories));
|
|
9635
|
+
return check.body;
|
|
6946
9636
|
}
|
|
6947
9637
|
function connect(rawUrl, explicitTenantId) {
|
|
6948
9638
|
const url = normalizeBaseUrl(rawUrl);
|
|
@@ -7526,10 +10216,14 @@ program.command("validate").description("Validate a Config File offline (schema)
|
|
|
7526
10216
|
const findings = evaluate(parsed.success ? parsed.data : raw, {
|
|
7527
10217
|
structuralErrors: parsed.success ? void 0 : parsed.error
|
|
7528
10218
|
});
|
|
10219
|
+
const allFindings = [
|
|
10220
|
+
...findings,
|
|
10221
|
+
...offlineAdvisories(raw)
|
|
10222
|
+
];
|
|
7529
10223
|
diag(`Validation for ${file} (offline, ${shape} shape, schema ${SCHEMA_VERSION}):`);
|
|
7530
|
-
process.stdout.write(`${formatFindings(
|
|
10224
|
+
process.stdout.write(`${formatFindings(allFindings)}
|
|
7531
10225
|
`);
|
|
7532
|
-
if (hasBlockingFindings(
|
|
10226
|
+
if (hasBlockingFindings(allFindings)) blockedFiles += 1;
|
|
7533
10227
|
}
|
|
7534
10228
|
if (files.length > 1) diag(`${files.length - blockedFiles}/${files.length} passed.`);
|
|
7535
10229
|
if (blockedFiles > 0) {
|
|
@@ -7729,6 +10423,45 @@ program.command("evaluate").description("Run placement/entitlement decisions for
|
|
|
7729
10423
|
emit(json, true);
|
|
7730
10424
|
}
|
|
7731
10425
|
);
|
|
10426
|
+
var ingestKeys = program.command("ingest-keys").description("Manage public ingest keys (embeddable SDK telemetry credentials) for the tenant.");
|
|
10427
|
+
ingestKeys.command("create").description("Mint a public ingest key. The full token is printed ONCE and cannot be retrieved again.").requiredOption("--origin <url...>", "Allowed browser origin(s), e.g. https://app.example.com (repeatable; at least one required)").option("--ip <cidr...>", "Optional IP / CIDR allowlist (empty = no IP restriction)").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").option("--json", "Machine-readable output (token on stdout for scripted capture)").action(async (opts) => {
|
|
10428
|
+
const conn = connect(opts.url, opts.tenantId);
|
|
10429
|
+
const result = await createIngestKey(conn.url, conn.headers, { originAllowlist: opts.origin, ipAllowlist: opts.ip });
|
|
10430
|
+
if (!result.ok || !result.key) httpFail(conn, "ingest-keys create", result.status, result.error);
|
|
10431
|
+
const key = result.key;
|
|
10432
|
+
if (opts.json) {
|
|
10433
|
+
emit(key, true);
|
|
10434
|
+
return;
|
|
10435
|
+
}
|
|
10436
|
+
diag("\u2713 Minted a public ingest key. STORE THE TOKEN NOW \u2014 it is shown once and cannot be retrieved again.");
|
|
10437
|
+
emit(
|
|
10438
|
+
key,
|
|
10439
|
+
false,
|
|
10440
|
+
[
|
|
10441
|
+
` id: ${key.id}`,
|
|
10442
|
+
` token: ${key.token}`,
|
|
10443
|
+
` origins: ${key.originAllowlist.join(", ")}`,
|
|
10444
|
+
key.ipAllowlist.length ? ` ips: ${key.ipAllowlist.join(", ")}` : void 0
|
|
10445
|
+
].filter((line) => Boolean(line)).join("\n")
|
|
10446
|
+
);
|
|
10447
|
+
});
|
|
10448
|
+
ingestKeys.command("list").description("List the active public ingest keys for the tenant (previews only \u2014 never the full token).").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").option("--json", "Machine-readable output").action(async (opts) => {
|
|
10449
|
+
const conn = connect(opts.url, opts.tenantId);
|
|
10450
|
+
const result = await listIngestKeys(conn.url, conn.headers);
|
|
10451
|
+
if (!result.ok) httpFail(conn, "ingest-keys list", result.status);
|
|
10452
|
+
emit(
|
|
10453
|
+
result.keys,
|
|
10454
|
+
Boolean(opts.json),
|
|
10455
|
+
result.keys.length ? result.keys.map(formatIngestKeyLine).join("\n") : "(no active ingest keys)"
|
|
10456
|
+
);
|
|
10457
|
+
});
|
|
10458
|
+
ingestKeys.command("revoke").description("Revoke a public ingest key by id. The key stops working immediately.").argument("<id>", "The ingest key id (see `revturbine ingest-keys list`)").option("-u, --url <url>", "RevTurbine instance URL", DEFAULT_URL).option("-t, --tenant-id <id>", "x-tenant-id (defaults to the stored token tenant)").option("--yes", "Skip the confirmation prompt").action(async (id, opts) => {
|
|
10459
|
+
const conn = connect(opts.url, opts.tenantId);
|
|
10460
|
+
await confirmOrExit(`Revoke ingest key ${id} on ${conn.url}? The key stops working immediately.`, !!opts.yes);
|
|
10461
|
+
const result = await revokeIngestKey(conn.url, conn.headers, id);
|
|
10462
|
+
if (!result.ok) httpFail(conn, "ingest-keys revoke", result.status);
|
|
10463
|
+
diag(`\u2713 Revoked ingest key ${id}.`);
|
|
10464
|
+
});
|
|
7732
10465
|
var COMMAND_EXAMPLES = {
|
|
7733
10466
|
download: [
|
|
7734
10467
|
"",
|