@varde-flyt/vfac 0.7.1 → 0.9.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.
Files changed (2) hide show
  1. package/dist/vfac.mjs +1329 -423
  2. package/package.json +1 -1
package/dist/vfac.mjs CHANGED
@@ -11988,12 +11988,26 @@ var coerce = {
11988
11988
  };
11989
11989
  var NEVER = INVALID;
11990
11990
 
11991
+ // ../product-configuration/dist/configuration-bounds.js
11992
+ var MAX_CONFIGURATION_DEPTH = 6;
11993
+ var MAX_CONFIGURATION_LEAVES = 256;
11994
+ var MAX_CONFIGURATION_NODES = 512;
11995
+ var MAX_CONFIGURATION_CHILDREN = 64;
11996
+ var MAX_CONFIGURATION_VALUE_LEAVES = 1e3;
11997
+ var MAX_CONFIGURATION_ISSUES = 50;
11998
+ var MAX_CONFIGURATION_VALUE_BYTES = 16384;
11999
+
11991
12000
  // ../product-configuration/dist/json-schema-annex.js
11992
12001
  var ANNEXES = /* @__PURE__ */ new WeakMap();
11993
12002
  function withJsonSchemaAnnex(schema, annex) {
11994
12003
  ANNEXES.set(schema, annex);
11995
12004
  return schema;
11996
12005
  }
12006
+ var DEFINITION_NAMES = /* @__PURE__ */ new WeakMap();
12007
+ function withJsonSchemaDefinition(schema, name) {
12008
+ DEFINITION_NAMES.set(schema, name);
12009
+ return schema;
12010
+ }
11997
12011
 
11998
12012
  // ../product-configuration/dist/infrastructure-vocabulary.js
11999
12013
  function tokenizeIdentifier(identifier) {
@@ -12211,7 +12225,20 @@ var ConfigurationGroupSchema = external_exports.enum([
12211
12225
  var CONFIGURATION_PROPERTY_NAME_REGEX = /^[a-z][a-zA-Z0-9]{1,47}$/;
12212
12226
  var PropertyNameSchema = external_exports.string().regex(CONFIGURATION_PROPERTY_NAME_REGEX, "Configuration property names must be camelCase, start with a lowercase letter, and be 2\u201348 characters.");
12213
12227
  var TitleSchema = external_exports.string().min(2).max(80).describe("Customer-facing field label.");
12214
- var HelpTextSchema = external_exports.string().min(10).max(400).describe("Customer-facing explanation. Required for security-sensitive choices.");
12228
+ var V1_LEAF_BOUNDS = {
12229
+ help: 400,
12230
+ enumValues: 40,
12231
+ stringLength: 4096,
12232
+ defaultLength: 400
12233
+ };
12234
+ var V2_LEAF_BOUNDS = {
12235
+ help: 2e3,
12236
+ enumValues: 256,
12237
+ stringLength: 16384,
12238
+ defaultLength: 2e3
12239
+ };
12240
+ var MAX_DECLARABLE_STRING_LENGTH = V2_LEAF_BOUNDS.stringLength;
12241
+ var helpTextSchema = (bounds) => external_exports.string().min(10).max(bounds.help).describe("Customer-facing explanation. Required for security-sensitive choices.");
12215
12242
  var SECRET_COUPLING_ANNEX = {
12216
12243
  allOf: [
12217
12244
  {
@@ -12230,69 +12257,156 @@ var SECRET_COUPLING_ANNEX = {
12230
12257
  }
12231
12258
  ]
12232
12259
  };
12233
- var StringPropertySchema = external_exports.object({
12234
- type: external_exports.literal("string"),
12235
- title: TitleSchema,
12236
- description: HelpTextSchema.optional(),
12237
- group: ConfigurationGroupSchema,
12238
- enum: external_exports.array(external_exports.string().min(1).max(80)).min(1).max(40).optional(),
12239
- minLength: external_exports.number().int().min(0).max(4096).optional(),
12240
- /**
12241
- * Required unless the value is constrained by `enum`. An unbounded string
12242
- * reaching the Control Plane as desired state is an unbounded write.
12243
- */
12244
- maxLength: external_exports.number().int().min(1).max(4096).optional(),
12245
- pattern: external_exports.string().min(1).max(200).optional(),
12246
- default: external_exports.string().max(400).optional(),
12247
- /**
12248
- * docs/PRODUCT_DEFINITION_GUIDE.md "Secrets": secret input is marked
12249
- * write-only AND secret. Both flags, and the refinement below makes one
12250
- * without the other a validation error.
12251
- */
12252
- writeOnly: external_exports.boolean().optional(),
12253
- "x-secret": external_exports.boolean().optional()
12254
- }).strict();
12255
- withJsonSchemaAnnex(StringPropertySchema, SECRET_COUPLING_ANNEX);
12256
- var NumberPropertySchema = external_exports.object({
12257
- type: external_exports.enum(["integer", "number"]),
12258
- title: TitleSchema,
12259
- description: HelpTextSchema.optional(),
12260
- group: ConfigurationGroupSchema,
12261
- /** Both bounds required — "numeric limits" in the guide is not optional. */
12262
- minimum: external_exports.number(),
12263
- maximum: external_exports.number(),
12264
- default: external_exports.number().optional()
12265
- }).strict();
12266
- var BooleanPropertySchema = external_exports.object({
12267
- type: external_exports.literal("boolean"),
12268
- title: TitleSchema,
12269
- description: HelpTextSchema.optional(),
12270
- group: ConfigurationGroupSchema,
12271
- default: external_exports.boolean().optional()
12272
- }).strict();
12273
- var ArrayItemsSchema = external_exports.object({
12274
- type: external_exports.literal("string"),
12275
- enum: external_exports.array(external_exports.string().min(1).max(80)).min(1).max(40).optional(),
12276
- maxLength: external_exports.number().int().min(1).max(400).optional()
12277
- }).strict();
12278
- var ArrayPropertySchema = external_exports.object({
12279
- type: external_exports.literal("array"),
12280
- title: TitleSchema,
12281
- description: HelpTextSchema.optional(),
12282
- group: ConfigurationGroupSchema,
12283
- items: ArrayItemsSchema,
12284
- minItems: external_exports.number().int().min(0).max(100).optional(),
12285
- /** Required: an unbounded list is an unbounded write. */
12286
- maxItems: external_exports.number().int().min(1).max(100),
12287
- uniqueItems: external_exports.boolean().optional(),
12288
- default: external_exports.array(external_exports.string().max(200)).max(100).optional()
12289
- }).strict();
12260
+ function leafShapes(bounds) {
12261
+ const help = helpTextSchema(bounds);
12262
+ const enumMembers = external_exports.array(external_exports.string().min(1).max(80)).min(1).max(bounds.enumValues).optional();
12263
+ const string = external_exports.object({
12264
+ type: external_exports.literal("string"),
12265
+ title: TitleSchema,
12266
+ description: help.optional(),
12267
+ group: ConfigurationGroupSchema,
12268
+ enum: enumMembers,
12269
+ minLength: external_exports.number().int().min(0).max(bounds.stringLength).optional(),
12270
+ /**
12271
+ * Required unless the value is constrained by `enum`. An unbounded string
12272
+ * reaching the Control Plane as desired state is an unbounded write.
12273
+ */
12274
+ maxLength: external_exports.number().int().min(1).max(bounds.stringLength).optional(),
12275
+ pattern: external_exports.string().min(1).max(200).optional(),
12276
+ default: external_exports.string().max(bounds.defaultLength).optional(),
12277
+ /**
12278
+ * docs/PRODUCT_DEFINITION_GUIDE.md "Secrets": secret input is marked
12279
+ * write-only AND secret. Both flags, and the refinement below makes one
12280
+ * without the other a validation error.
12281
+ */
12282
+ writeOnly: external_exports.boolean().optional(),
12283
+ "x-secret": external_exports.boolean().optional()
12284
+ }).strict();
12285
+ withJsonSchemaAnnex(string, SECRET_COUPLING_ANNEX);
12286
+ const number = external_exports.object({
12287
+ type: external_exports.enum(["integer", "number"]),
12288
+ title: TitleSchema,
12289
+ description: help.optional(),
12290
+ group: ConfigurationGroupSchema,
12291
+ /** Both bounds required — "numeric limits" in the guide is not optional. */
12292
+ minimum: external_exports.number(),
12293
+ maximum: external_exports.number(),
12294
+ default: external_exports.number().optional()
12295
+ }).strict();
12296
+ const boolean = external_exports.object({
12297
+ type: external_exports.literal("boolean"),
12298
+ title: TitleSchema,
12299
+ description: help.optional(),
12300
+ group: ConfigurationGroupSchema,
12301
+ default: external_exports.boolean().optional()
12302
+ }).strict();
12303
+ const items = external_exports.object({
12304
+ type: external_exports.literal("string"),
12305
+ enum: enumMembers,
12306
+ maxLength: external_exports.number().int().min(1).max(bounds.defaultLength).optional()
12307
+ }).strict();
12308
+ const array = external_exports.object({
12309
+ type: external_exports.literal("array"),
12310
+ title: TitleSchema,
12311
+ description: help.optional(),
12312
+ group: ConfigurationGroupSchema,
12313
+ items,
12314
+ minItems: external_exports.number().int().min(0).max(100).optional(),
12315
+ /** Required: an unbounded list is an unbounded write. */
12316
+ maxItems: external_exports.number().int().min(1).max(100),
12317
+ uniqueItems: external_exports.boolean().optional(),
12318
+ default: external_exports.array(external_exports.string().max(200)).max(100).optional()
12319
+ }).strict();
12320
+ return { string, number, boolean, items, array };
12321
+ }
12322
+ var V1_LEAVES = leafShapes(V1_LEAF_BOUNDS);
12323
+ var V2_LEAVES = leafShapes(V2_LEAF_BOUNDS);
12324
+ var StringPropertySchema = V1_LEAVES.string;
12325
+ var NumberPropertySchema = V1_LEAVES.number;
12326
+ var BooleanPropertySchema = V1_LEAVES.boolean;
12327
+ var ArrayPropertySchema = V1_LEAVES.array;
12328
+ var MAX_CONFIGURATION_PROPERTIES = 40;
12290
12329
  var ConfigurationPropertySchema = external_exports.discriminatedUnion("type", [
12291
12330
  StringPropertySchema,
12292
12331
  NumberPropertySchema,
12293
12332
  BooleanPropertySchema,
12294
12333
  ArrayPropertySchema
12295
12334
  ]);
12335
+ var NestedStringPropertySchema = V2_LEAVES.string.omit({ "x-secret": true, writeOnly: true }).extend({ group: ConfigurationGroupSchema.optional() });
12336
+ var V2_NESTED_LEAVES = {
12337
+ string: NestedStringPropertySchema,
12338
+ number: V2_LEAVES.number.extend({ group: ConfigurationGroupSchema.optional() }),
12339
+ boolean: V2_LEAVES.boolean.extend({ group: ConfigurationGroupSchema.optional() }),
12340
+ array: V2_LEAVES.array.extend({ group: ConfigurationGroupSchema.optional() }),
12341
+ items: V2_LEAVES.items
12342
+ };
12343
+ function childBagShape(child) {
12344
+ return {
12345
+ properties: external_exports.record(PropertyNameSchema, child),
12346
+ required: external_exports.array(PropertyNameSchema).max(MAX_CONFIGURATION_CHILDREN).optional(),
12347
+ /**
12348
+ * All-or-none rules, scoped to THIS container.
12349
+ *
12350
+ * ON THE CONTAINER, NEVER AS ROOT CLAUSES NAMING PATHS. The published key
12351
+ * promises that "a publisher running an off-the-shelf validator over that
12352
+ * file must get the answer the platform gives", and JSON Schema 2020-12
12353
+ * scopes `dependentRequired` to the object that CARRIES it. A root clause
12354
+ * naming `/upstream/token` would be a rule no standard validator applies —
12355
+ * the one thing this key may not become. Written here it keeps exactly the
12356
+ * meaning it has everywhere else, and the generated artifact carries it on
12357
+ * the right node.
12358
+ *
12359
+ * A REPEATABLE GROUP GETS ONE PER ROW, which is the honest reading: "if this
12360
+ * server has a URL it needs a token" is a fact about a server, not about the
12361
+ * list.
12362
+ */
12363
+ dependentRequired: external_exports.record(PropertyNameSchema, external_exports.array(PropertyNameSchema).min(1).max(MAX_CONFIGURATION_CHILDREN)).optional().describe("Settings inside this group that must be supplied together."),
12364
+ additionalProperties: external_exports.literal(false)
12365
+ };
12366
+ }
12367
+ function propertyUnion(levelsBelow, leafSet) {
12368
+ const leaves = [leafSet.string, leafSet.number, leafSet.boolean];
12369
+ if (levelsBelow <= 0) {
12370
+ return [...leaves, leafSet.array];
12371
+ }
12372
+ const level = MAX_CONFIGURATION_DEPTH - levelsBelow + 1;
12373
+ const child = withJsonSchemaDefinition(external_exports.discriminatedUnion("type", propertyUnion(levelsBelow - 1, V2_NESTED_LEAVES)), `configurationPropertyLevel${level}`);
12374
+ const bag = childBagShape(child);
12375
+ const groupItems = external_exports.object({ type: external_exports.literal("object"), ...bag }).strict();
12376
+ return [
12377
+ ...leaves,
12378
+ // ONE `array` BRANCH, NOT TWO, because a discriminated union may not carry
12379
+ // two options with the same discriminator value ("duplicate value array" —
12380
+ // zod refuses it at construction). The item shape is the inner
12381
+ // discriminator, which is also what keeps a v1 document's
12382
+ // `items: {type: 'string'}` reading exactly as it always did.
12383
+ leafSet.array.extend({
12384
+ items: external_exports.discriminatedUnion("type", [V2_LEAVES.items, groupItems])
12385
+ }),
12386
+ /**
12387
+ * A group of related settings.
12388
+ *
12389
+ * NO `default`. A container cannot carry one, structurally — `.strict()`
12390
+ * refuses the key — and that removes a whole class of question rather than
12391
+ * answering it: what a whole-subtree default means when the customer
12392
+ * supplied half of it, how it interacts with `dependentRequired`'s
12393
+ * load-bearing "a defaulted setting is never absent", and what
12394
+ * `seedConfiguration` should write into a served example and a file on
12395
+ * disk. Defaults are a LEAF concept here.
12396
+ */
12397
+ external_exports.object({
12398
+ type: external_exports.literal("object"),
12399
+ title: TitleSchema,
12400
+ description: helpTextSchema(V2_LEAF_BOUNDS).optional(),
12401
+ // INHERITED BELOW THE ROOT, for the reason `V2_NESTED_LEAVES` states: a
12402
+ // group is a bucket for a whole subtree, and repeating it is the only
12403
+ // value that could be written.
12404
+ group: levelsBelow === MAX_CONFIGURATION_DEPTH - 1 ? ConfigurationGroupSchema : ConfigurationGroupSchema.optional(),
12405
+ ...bag
12406
+ }).strict()
12407
+ ];
12408
+ }
12409
+ var RootConfigurationPropertySchema = external_exports.discriminatedUnion("type", propertyUnion(MAX_CONFIGURATION_DEPTH - 1, V2_LEAVES));
12296
12410
  var CustomerConfigurationObjectSchema = external_exports.object({
12297
12411
  type: external_exports.literal("object"),
12298
12412
  properties: external_exports.record(PropertyNameSchema, ConfigurationPropertySchema).describe("Customer-editable settings, keyed by camelCase property name."),
@@ -12326,7 +12440,6 @@ var CustomerConfigurationObjectSchema = external_exports.object({
12326
12440
  */
12327
12441
  additionalProperties: external_exports.literal(false)
12328
12442
  }).strict();
12329
- var MAX_CONFIGURATION_PROPERTIES = 40;
12330
12443
  var CustomerConfigurationSchema = CustomerConfigurationObjectSchema.superRefine((config, ctx) => {
12331
12444
  const names = Object.keys(config.properties);
12332
12445
  if (names.length > MAX_CONFIGURATION_PROPERTIES) {
@@ -12338,195 +12451,205 @@ var CustomerConfigurationSchema = CustomerConfigurationObjectSchema.superRefine(
12338
12451
  }
12339
12452
  for (const name of names) {
12340
12453
  const property = config.properties[name];
12341
- const path = ["properties", name];
12342
- const infrastructure = infrastructureVocabularyViolation(name);
12343
- if (infrastructure) {
12454
+ checkDeclaredProperty(name, property, ["properties", name], ctx, true);
12455
+ }
12456
+ for (const name of config.required ?? []) {
12457
+ if (!Object.hasOwn(config.properties, name)) {
12344
12458
  ctx.addIssue({
12345
12459
  code: external_exports.ZodIssueCode.custom,
12346
- path,
12347
- message: `${infrastructure}. Customer settings describe outcomes, not infrastructure.`
12460
+ path: ["required"],
12461
+ message: `"${name}" is listed as required but is not a declared property.`
12348
12462
  });
12349
12463
  }
12464
+ }
12465
+ const duplicates = (config.required ?? []).filter((name, index, all) => all.indexOf(name) !== index);
12466
+ if (duplicates.length > 0) {
12467
+ ctx.addIssue({
12468
+ code: external_exports.ZodIssueCode.custom,
12469
+ path: ["required"],
12470
+ message: `Duplicate required entries: ${[...new Set(duplicates)].join(", ")}.`
12471
+ });
12472
+ }
12473
+ checkDependentRequired(config, ctx);
12474
+ });
12475
+ function checkDeclaredProperty(name, property, path, ctx, atRoot) {
12476
+ const infrastructure = infrastructureVocabularyViolation(name);
12477
+ if (infrastructure) {
12478
+ ctx.addIssue({
12479
+ code: external_exports.ZodIssueCode.custom,
12480
+ path,
12481
+ message: `${infrastructure}. Customer settings describe outcomes, not infrastructure.`
12482
+ });
12483
+ }
12484
+ if (atRoot) {
12350
12485
  const platformOwned = platformOwnedFieldViolation(name);
12351
12486
  if (platformOwned) {
12352
12487
  ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path, message: `${platformOwned}.` });
12353
12488
  }
12354
- for (const [label, values] of enumerableValues(property)) {
12355
- for (const value of values) {
12356
- const violation = infrastructureValueViolation(value);
12357
- if (violation) {
12358
- ctx.addIssue({
12359
- code: external_exports.ZodIssueCode.custom,
12360
- path: [...path, label],
12361
- message: `${violation}. Customer settings offer outcomes, not infrastructure sizes.`
12362
- });
12363
- }
12364
- }
12365
- }
12366
- if (property.type === "string") {
12367
- const secret = property["x-secret"] === true;
12368
- const credential = credentialNameViolation(name);
12369
- if (credential && !secret) {
12370
- ctx.addIssue({
12371
- code: external_exports.ZodIssueCode.custom,
12372
- path,
12373
- message: `${credential} and must be declared "x-secret": true with "writeOnly": true, or renamed if it is not one.`
12374
- });
12375
- }
12376
- if (secret && property.writeOnly !== true) {
12377
- ctx.addIssue({
12378
- code: external_exports.ZodIssueCode.custom,
12379
- path,
12380
- message: `"${name}" is marked x-secret but not writeOnly \u2014 a read API could echo the value.`
12381
- });
12382
- }
12383
- if (property.writeOnly === true && !secret) {
12384
- ctx.addIssue({
12385
- code: external_exports.ZodIssueCode.custom,
12386
- path,
12387
- message: `"${name}" is marked writeOnly but not x-secret \u2014 the value would not be routed to the secret flow.`
12388
- });
12389
- }
12390
- if (secret && property.default !== void 0) {
12391
- ctx.addIssue({
12392
- code: external_exports.ZodIssueCode.custom,
12393
- path: [...path, "default"],
12394
- message: `"${name}" is a secret and must not declare a default value.`
12395
- });
12396
- }
12397
- if (secret && property.enum !== void 0) {
12398
- ctx.addIssue({
12399
- code: external_exports.ZodIssueCode.custom,
12400
- path: [...path, "enum"],
12401
- message: `"${name}" is a secret; an enum would enumerate candidate secret values.`
12402
- });
12403
- }
12404
- if (property.enum === void 0 && property.maxLength === void 0) {
12405
- ctx.addIssue({
12406
- code: external_exports.ZodIssueCode.custom,
12407
- path,
12408
- message: `"${name}" must declare either an enum or a maxLength.`
12409
- });
12410
- }
12411
- if (property.enum !== void 0 && property.default !== void 0 && !property.enum.includes(property.default)) {
12412
- ctx.addIssue({
12413
- code: external_exports.ZodIssueCode.custom,
12414
- path: [...path, "default"],
12415
- message: `"${name}" declares a default outside its enum.`
12416
- });
12417
- }
12418
- if (property.minLength !== void 0 && property.maxLength !== void 0 && property.minLength > property.maxLength) {
12419
- ctx.addIssue({
12420
- code: external_exports.ZodIssueCode.custom,
12421
- path,
12422
- message: `"${name}" has minLength greater than maxLength.`
12423
- });
12424
- }
12425
- if (property.default !== void 0 && property.maxLength !== void 0 && property.default.length > property.maxLength) {
12426
- ctx.addIssue({
12427
- code: external_exports.ZodIssueCode.custom,
12428
- path: [...path, "default"],
12429
- message: `"${name}" declares a default longer than its own maxLength.`
12430
- });
12431
- }
12432
- if (property.pattern !== void 0 && !isSafeRegex(property.pattern)) {
12489
+ }
12490
+ for (const [label, values] of enumerableValues(property)) {
12491
+ for (const value of values) {
12492
+ const violation = infrastructureValueViolation(value);
12493
+ if (violation) {
12433
12494
  ctx.addIssue({
12434
12495
  code: external_exports.ZodIssueCode.custom,
12435
- path: [...path, "pattern"],
12436
- message: `"${name}" declares a pattern that is not a valid regular expression.`
12496
+ path: [...path, label],
12497
+ message: `${violation}. Customer settings offer outcomes, not infrastructure sizes.`
12437
12498
  });
12438
12499
  }
12439
12500
  }
12440
- if (property.type === "integer" || property.type === "number") {
12441
- if (property.minimum > property.maximum) {
12442
- ctx.addIssue({
12443
- code: external_exports.ZodIssueCode.custom,
12444
- path,
12445
- message: `"${name}" has minimum greater than maximum.`
12446
- });
12447
- }
12448
- if (property.default !== void 0 && (property.default < property.minimum || property.default > property.maximum)) {
12501
+ }
12502
+ if (property.type === "string") {
12503
+ const secret = property["x-secret"] === true;
12504
+ const credential = credentialNameViolation(name);
12505
+ if (credential && !secret) {
12506
+ ctx.addIssue({
12507
+ code: external_exports.ZodIssueCode.custom,
12508
+ path,
12509
+ // THE REMEDIATION HAS TO BE ONE THE CONTRACT ALLOWS. At the root, the
12510
+ // answer is to declare the two markers. Below it, that answer is
12511
+ // IMPOSSIBLE — the nested shape omits both keys and `.strict()` refuses
12512
+ // them — so telling a publisher to add them sends them to write a
12513
+ // second invalid document, and an agent following the validator loops.
12514
+ // A validator's errors are part of the agent-facing contract.
12515
+ message: atRoot ? `${credential} and must be declared "x-secret": true with "writeOnly": true, or renamed if it is not one.` : `${credential}, and a credential must be a TOP-LEVEL setting in this contract format \u2014 move it out of the group and declare it "x-secret": true with "writeOnly": true, or rename it if it is not one.`
12516
+ });
12517
+ }
12518
+ if (secret && property.writeOnly !== true) {
12519
+ ctx.addIssue({
12520
+ code: external_exports.ZodIssueCode.custom,
12521
+ path,
12522
+ message: `"${name}" is marked x-secret but not writeOnly \u2014 a read API could echo the value.`
12523
+ });
12524
+ }
12525
+ if (property.writeOnly === true && !secret) {
12526
+ ctx.addIssue({
12527
+ code: external_exports.ZodIssueCode.custom,
12528
+ path,
12529
+ message: `"${name}" is marked writeOnly but not x-secret \u2014 the value would not be routed to the secret flow.`
12530
+ });
12531
+ }
12532
+ if (secret && property.default !== void 0) {
12533
+ ctx.addIssue({
12534
+ code: external_exports.ZodIssueCode.custom,
12535
+ path: [...path, "default"],
12536
+ message: `"${name}" is a secret and must not declare a default value.`
12537
+ });
12538
+ }
12539
+ if (secret && property.enum !== void 0) {
12540
+ ctx.addIssue({
12541
+ code: external_exports.ZodIssueCode.custom,
12542
+ path: [...path, "enum"],
12543
+ message: `"${name}" is a secret; an enum would enumerate candidate secret values.`
12544
+ });
12545
+ }
12546
+ if (property.enum === void 0 && property.maxLength === void 0) {
12547
+ ctx.addIssue({
12548
+ code: external_exports.ZodIssueCode.custom,
12549
+ path,
12550
+ message: `"${name}" must declare either an enum or a maxLength.`
12551
+ });
12552
+ }
12553
+ if (property.enum !== void 0 && property.default !== void 0 && !property.enum.includes(property.default)) {
12554
+ ctx.addIssue({
12555
+ code: external_exports.ZodIssueCode.custom,
12556
+ path: [...path, "default"],
12557
+ message: `"${name}" declares a default outside its enum.`
12558
+ });
12559
+ }
12560
+ if (property.minLength !== void 0 && property.maxLength !== void 0 && property.minLength > property.maxLength) {
12561
+ ctx.addIssue({
12562
+ code: external_exports.ZodIssueCode.custom,
12563
+ path,
12564
+ message: `"${name}" has minLength greater than maxLength.`
12565
+ });
12566
+ }
12567
+ if (property.default !== void 0 && property.maxLength !== void 0 && property.default.length > property.maxLength) {
12568
+ ctx.addIssue({
12569
+ code: external_exports.ZodIssueCode.custom,
12570
+ path: [...path, "default"],
12571
+ message: `"${name}" declares a default longer than its own maxLength.`
12572
+ });
12573
+ }
12574
+ if (property.pattern !== void 0 && !isSafeRegex(property.pattern)) {
12575
+ ctx.addIssue({
12576
+ code: external_exports.ZodIssueCode.custom,
12577
+ path: [...path, "pattern"],
12578
+ message: `"${name}" declares a pattern that is not a valid regular expression.`
12579
+ });
12580
+ }
12581
+ }
12582
+ if (property.type === "integer" || property.type === "number") {
12583
+ if (property.minimum > property.maximum) {
12584
+ ctx.addIssue({
12585
+ code: external_exports.ZodIssueCode.custom,
12586
+ path,
12587
+ message: `"${name}" has minimum greater than maximum.`
12588
+ });
12589
+ }
12590
+ if (property.default !== void 0 && (property.default < property.minimum || property.default > property.maximum)) {
12591
+ ctx.addIssue({
12592
+ code: external_exports.ZodIssueCode.custom,
12593
+ path: [...path, "default"],
12594
+ message: `"${name}" declares a default outside its minimum/maximum range.`
12595
+ });
12596
+ }
12597
+ if (property.type === "integer" && !Number.isInteger(property.default ?? 0)) {
12598
+ ctx.addIssue({
12599
+ code: external_exports.ZodIssueCode.custom,
12600
+ path: [...path, "default"],
12601
+ message: `"${name}" is an integer but declares a fractional default.`
12602
+ });
12603
+ }
12604
+ }
12605
+ if (property.type === "array") {
12606
+ const allowed = property.items.enum;
12607
+ if (allowed === void 0 && property.items.maxLength === void 0) {
12608
+ ctx.addIssue({
12609
+ code: external_exports.ZodIssueCode.custom,
12610
+ path: [...path, "items"],
12611
+ message: `"${name}" items must declare either an enum or a maxLength.`
12612
+ });
12613
+ }
12614
+ if (property.minItems !== void 0 && property.minItems > property.maxItems) {
12615
+ ctx.addIssue({
12616
+ code: external_exports.ZodIssueCode.custom,
12617
+ path,
12618
+ message: `"${name}" has minItems greater than maxItems.`
12619
+ });
12620
+ }
12621
+ if (property.default !== void 0) {
12622
+ if (property.default.length > property.maxItems) {
12449
12623
  ctx.addIssue({
12450
12624
  code: external_exports.ZodIssueCode.custom,
12451
12625
  path: [...path, "default"],
12452
- message: `"${name}" declares a default outside its minimum/maximum range.`
12626
+ message: `"${name}" declares a default longer than maxItems.`
12453
12627
  });
12454
12628
  }
12455
- if (property.type === "integer" && !Number.isInteger(property.default ?? 0)) {
12629
+ const outside = allowed ? property.default.filter((value) => !allowed.includes(value)) : [];
12630
+ if (outside.length > 0) {
12456
12631
  ctx.addIssue({
12457
12632
  code: external_exports.ZodIssueCode.custom,
12458
12633
  path: [...path, "default"],
12459
- message: `"${name}" is an integer but declares a fractional default.`
12460
- });
12461
- }
12462
- }
12463
- if (property.type === "array") {
12464
- const allowed = property.items.enum;
12465
- if (allowed === void 0 && property.items.maxLength === void 0) {
12466
- ctx.addIssue({
12467
- code: external_exports.ZodIssueCode.custom,
12468
- path: [...path, "items"],
12469
- message: `"${name}" items must declare either an enum or a maxLength.`
12470
- });
12471
- }
12472
- if (property.minItems !== void 0 && property.minItems > property.maxItems) {
12473
- ctx.addIssue({
12474
- code: external_exports.ZodIssueCode.custom,
12475
- path,
12476
- message: `"${name}" has minItems greater than maxItems.`
12634
+ message: `"${name}" declares default values outside its items enum: ${outside.join(", ")}.`
12477
12635
  });
12478
12636
  }
12479
- if (property.default !== void 0) {
12480
- if (property.default.length > property.maxItems) {
12481
- ctx.addIssue({
12482
- code: external_exports.ZodIssueCode.custom,
12483
- path: [...path, "default"],
12484
- message: `"${name}" declares a default longer than maxItems.`
12485
- });
12486
- }
12487
- const outside = allowed ? property.default.filter((value) => !allowed.includes(value)) : [];
12488
- if (outside.length > 0) {
12489
- ctx.addIssue({
12490
- code: external_exports.ZodIssueCode.custom,
12491
- path: [...path, "default"],
12492
- message: `"${name}" declares default values outside its items enum: ${outside.join(", ")}.`
12493
- });
12494
- }
12495
- }
12496
12637
  }
12497
12638
  }
12498
- for (const name of config.required ?? []) {
12499
- if (!Object.hasOwn(config.properties, name)) {
12500
- ctx.addIssue({
12501
- code: external_exports.ZodIssueCode.custom,
12502
- path: ["required"],
12503
- message: `"${name}" is listed as required but is not a declared property.`
12504
- });
12505
- }
12506
- }
12507
- const duplicates = (config.required ?? []).filter((name, index, all) => all.indexOf(name) !== index);
12508
- if (duplicates.length > 0) {
12509
- ctx.addIssue({
12510
- code: external_exports.ZodIssueCode.custom,
12511
- path: ["required"],
12512
- message: `Duplicate required entries: ${[...new Set(duplicates)].join(", ")}.`
12513
- });
12514
- }
12515
- checkDependentRequired(config, ctx);
12516
- });
12517
- function checkDependentRequired(config, ctx) {
12639
+ }
12640
+ function checkDependentRequired(config, ctx, at = []) {
12518
12641
  const clauses = Object.entries(config.dependentRequired ?? {});
12519
12642
  if (clauses.length === 0)
12520
12643
  return;
12521
12644
  if (clauses.length > MAX_CONFIGURATION_PROPERTIES) {
12522
12645
  ctx.addIssue({
12523
12646
  code: external_exports.ZodIssueCode.custom,
12524
- path: ["dependentRequired"],
12647
+ path: [...at, "dependentRequired"],
12525
12648
  message: `A Product may declare at most ${MAX_CONFIGURATION_PROPERTIES} dependent-required groups; found ${clauses.length}.`
12526
12649
  });
12527
12650
  }
12528
12651
  for (const [trigger, dependents] of clauses) {
12529
- const path = ["dependentRequired", trigger];
12652
+ const path = [...at, "dependentRequired", trigger];
12530
12653
  const declaredTrigger = config.properties[trigger];
12531
12654
  if (declaredTrigger === void 0) {
12532
12655
  ctx.addIssue({
@@ -12635,42 +12758,424 @@ function checkOneSecretPerGroup(config, ctx) {
12635
12758
  });
12636
12759
  }
12637
12760
  }
12638
- function enumerableValues(property) {
12639
- const out = [];
12640
- if (property.type === "string") {
12641
- if (property.enum)
12642
- out.push(["enum", property.enum]);
12643
- if (property.default !== void 0)
12644
- out.push(["default", [property.default]]);
12761
+ var CustomerConfigurationV2ObjectSchema = external_exports.object({
12762
+ type: external_exports.literal("object"),
12763
+ properties: external_exports.record(PropertyNameSchema, RootConfigurationPropertySchema).describe("Customer-editable settings, keyed by camelCase property name."),
12764
+ required: external_exports.array(PropertyNameSchema).max(MAX_CONFIGURATION_CHILDREN).optional(),
12765
+ /**
12766
+ * ROOT PROPERTIES ONLY, in v2.0.
12767
+ *
12768
+ * The published key promises that "a publisher running an off-the-shelf
12769
+ * validator over that file must get the answer the platform gives", and
12770
+ * JSON Schema 2020-12 `dependentRequired` is scoped to the object that
12771
+ * carries it. A clause naming a nested leaf would be a rule no standard
12772
+ * validator applies, which is the one thing this key may not become. A
12773
+ * nested object may not carry its own clause either — that is expressible
12774
+ * and honest, and it is deliberately left for when something needs it.
12775
+ */
12776
+ dependentRequired: external_exports.record(PropertyNameSchema, external_exports.array(PropertyNameSchema).min(1).max(MAX_CONFIGURATION_CHILDREN)).optional().describe("Settings that must be supplied together, keyed by the triggering property."),
12777
+ additionalProperties: external_exports.literal(false)
12778
+ }).strict();
12779
+ function countNodes(property) {
12780
+ const children = childrenOf(property);
12781
+ if (children === null)
12782
+ return { leaves: 1, nodes: 1, instantiated: 1 };
12783
+ let leaves = 0;
12784
+ let nodes = 1;
12785
+ let instantiated = 0;
12786
+ for (const child of Object.values(children.properties)) {
12787
+ const inner = countNodes(child);
12788
+ leaves += inner.leaves;
12789
+ nodes += inner.nodes;
12790
+ instantiated += inner.instantiated;
12791
+ }
12792
+ if (property.type === "array")
12793
+ instantiated *= property.maxItems;
12794
+ return { leaves, nodes, instantiated };
12795
+ }
12796
+ function childrenOf(property) {
12797
+ if (property.type === "object")
12798
+ return property;
12799
+ if (property.type === "array" && property.items.type === "object")
12800
+ return property.items;
12801
+ return null;
12802
+ }
12803
+ function minimumConfigurationBytes(config) {
12804
+ return minimumGroupBytes(config.properties, config.required ?? []);
12805
+ }
12806
+ function minimumGroupBytes(properties, required) {
12807
+ const names = new Set(required);
12808
+ let total = 2;
12809
+ for (const [name, property] of Object.entries(properties)) {
12810
+ const isContainer = childrenOf(property) !== null;
12811
+ if (isContainer ? !names.has(name) : !names.has(name) && declaredLeafDefault(property) === void 0) {
12812
+ continue;
12813
+ }
12814
+ total += name.length + 3 + minimumPropertyBytes(property);
12815
+ }
12816
+ return total;
12817
+ }
12818
+ function minimumPropertyBytes(property) {
12819
+ const children = childrenOf(property);
12820
+ if (children !== null) {
12821
+ const inner = minimumGroupBytes(children.properties, children.required ?? []);
12822
+ return property.type === "array" ? 2 + (property.minItems ?? 0) * inner : inner;
12823
+ }
12824
+ switch (property.type) {
12825
+ case "string": {
12826
+ const shortestEnum = property.enum?.reduce((a, b) => a.length <= b.length ? a : b);
12827
+ const shortest = shortestEnum?.length ?? Math.max(property.minLength ?? 0, 1);
12828
+ return shortest + 2;
12829
+ }
12830
+ case "integer":
12831
+ case "number":
12832
+ return 1;
12833
+ case "boolean":
12834
+ return 4;
12835
+ case "array":
12836
+ return 2 + (property.minItems ?? 0) * 3;
12837
+ case "object":
12838
+ return 2;
12839
+ }
12840
+ }
12841
+ function declaredLeafDefault(property) {
12842
+ return property.type === "object" ? void 0 : property.default;
12843
+ }
12844
+ function checkPropertyDeclaration(name, property, path, ctx, atRoot) {
12845
+ const children = childrenOf(property);
12846
+ if (children === null) {
12847
+ checkDeclaredProperty(name, property, path, ctx, atRoot);
12848
+ return;
12849
+ }
12850
+ const infrastructure = infrastructureVocabularyViolation(name);
12851
+ if (infrastructure) {
12852
+ ctx.addIssue({
12853
+ code: external_exports.ZodIssueCode.custom,
12854
+ path,
12855
+ message: `${infrastructure}. Customer settings describe outcomes, not infrastructure.`
12856
+ });
12857
+ }
12858
+ if (atRoot) {
12859
+ const platformOwned = platformOwnedFieldViolation(name);
12860
+ if (platformOwned) {
12861
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path, message: `${platformOwned}.` });
12862
+ }
12863
+ }
12864
+ const childNames = Object.keys(children.properties);
12865
+ if (childNames.length === 0) {
12866
+ ctx.addIssue({
12867
+ code: external_exports.ZodIssueCode.custom,
12868
+ path: [...path, "properties"],
12869
+ message: `"${name}" declares no settings. A group with nothing in it renders as an empty section.`
12870
+ });
12871
+ }
12872
+ if (childNames.length > MAX_CONFIGURATION_CHILDREN) {
12873
+ ctx.addIssue({
12874
+ code: external_exports.ZodIssueCode.custom,
12875
+ path: [...path, "properties"],
12876
+ message: `"${name}" may declare at most ${MAX_CONFIGURATION_CHILDREN} settings; found ${childNames.length}.`
12877
+ });
12878
+ }
12879
+ if (property.type === "array" && property.default !== void 0) {
12880
+ ctx.addIssue({
12881
+ code: external_exports.ZodIssueCode.custom,
12882
+ path: [...path, "default"],
12883
+ message: `"${name}" is a list of groups and cannot declare a default. Declare defaults on the settings inside it.`
12884
+ });
12885
+ }
12886
+ if (property.type === "array" && property.minItems !== void 0 && property.minItems > property.maxItems) {
12887
+ ctx.addIssue({
12888
+ code: external_exports.ZodIssueCode.custom,
12889
+ path,
12890
+ message: `"${name}" has minItems greater than maxItems.`
12891
+ });
12892
+ }
12893
+ for (const childName of childNames) {
12894
+ checkPropertyDeclaration(childName, children.properties[childName], [...path, "properties", childName], ctx, false);
12895
+ }
12896
+ for (const required of children.required ?? []) {
12897
+ if (!Object.hasOwn(children.properties, required)) {
12898
+ ctx.addIssue({
12899
+ code: external_exports.ZodIssueCode.custom,
12900
+ path: [...path, "required"],
12901
+ message: `"${required}" is listed as required in "${name}" but is not one of its settings.`
12902
+ });
12903
+ }
12904
+ }
12905
+ const scoped = children;
12906
+ if (scoped.dependentRequired !== void 0) {
12907
+ checkDependentRequired(
12908
+ { properties: children.properties, dependentRequired: scoped.dependentRequired },
12909
+ ctx,
12910
+ // An ARRAY's children live under `items`, which is where the key it
12911
+ // constrains actually sits in the document.
12912
+ property.type === "array" ? [...path, "items"] : path
12913
+ );
12914
+ }
12915
+ }
12916
+ var CustomerConfigurationV2Schema = CustomerConfigurationV2ObjectSchema.superRefine((config, ctx) => {
12917
+ const names = Object.keys(config.properties);
12918
+ if (names.length > MAX_CONFIGURATION_CHILDREN) {
12919
+ ctx.addIssue({
12920
+ code: external_exports.ZodIssueCode.custom,
12921
+ path: ["properties"],
12922
+ message: `A Product may declare at most ${MAX_CONFIGURATION_CHILDREN} root settings; found ${names.length}. Group them, or declare fewer.`
12923
+ });
12924
+ }
12925
+ let leaves = 0;
12926
+ let nodes = 0;
12927
+ let instantiated = 0;
12928
+ for (const name of names) {
12929
+ const property = config.properties[name];
12930
+ const path = ["properties", name];
12931
+ const counted = countNodes(property);
12932
+ leaves += counted.leaves;
12933
+ nodes += counted.nodes;
12934
+ instantiated += counted.instantiated;
12935
+ checkPropertyDeclaration(name, property, path, ctx, true);
12936
+ }
12937
+ if (leaves > MAX_CONFIGURATION_LEAVES) {
12938
+ ctx.addIssue({
12939
+ code: external_exports.ZodIssueCode.custom,
12940
+ path: ["properties"],
12941
+ message: `A Product may declare at most ${MAX_CONFIGURATION_LEAVES} customer settings in total; found ${leaves}.`
12942
+ });
12943
+ }
12944
+ if (nodes > MAX_CONFIGURATION_NODES) {
12945
+ ctx.addIssue({
12946
+ code: external_exports.ZodIssueCode.custom,
12947
+ path: ["properties"],
12948
+ message: `A Product may declare at most ${MAX_CONFIGURATION_NODES} configuration nodes; found ${nodes}.`
12949
+ });
12950
+ }
12951
+ if (instantiated > MAX_CONFIGURATION_VALUE_LEAVES) {
12952
+ ctx.addIssue({
12953
+ code: external_exports.ZodIssueCode.custom,
12954
+ path: ["properties"],
12955
+ message: `This Product's settings could expand to ${instantiated} values. A list instantiates everything inside it once per item, so lists inside lists MULTIPLY. At most ${MAX_CONFIGURATION_VALUE_LEAVES} are allowed; reduce maxItems on the lists, or the number of settings inside them.`
12956
+ });
12957
+ }
12958
+ for (const name of config.required ?? []) {
12959
+ if (!Object.hasOwn(config.properties, name)) {
12960
+ ctx.addIssue({
12961
+ code: external_exports.ZodIssueCode.custom,
12962
+ path: ["required"],
12963
+ message: `"${name}" is listed as required but is not a declared property.`
12964
+ });
12965
+ }
12966
+ }
12967
+ const floor = minimumConfigurationBytes(config);
12968
+ if (floor > MAX_CONFIGURATION_VALUE_BYTES) {
12969
+ ctx.addIssue({
12970
+ code: external_exports.ZodIssueCode.custom,
12971
+ path: ["properties"],
12972
+ message: `The smallest configuration that could satisfy this Product is at least ${floor} bytes, and the limit is ${MAX_CONFIGURATION_VALUE_BYTES}. No customer could deploy it \u2014 reduce the required settings, their minimum lengths, or a list's minItems.`
12973
+ });
12974
+ }
12975
+ checkDependentRequired(config, ctx);
12976
+ checkOneSecretPerGroup(config, ctx);
12977
+ });
12978
+ function enumerableValues(property) {
12979
+ const out = [];
12980
+ if (property.type === "string") {
12981
+ if (property.enum)
12982
+ out.push(["enum", property.enum]);
12983
+ if (property.default !== void 0)
12984
+ out.push(["default", [property.default]]);
12985
+ }
12986
+ if (property.type === "array") {
12987
+ if (property.items.enum)
12988
+ out.push(["items", property.items.enum]);
12989
+ if (property.default)
12990
+ out.push(["default", property.default]);
12991
+ }
12992
+ return out;
12993
+ }
12994
+ function isSafeRegex(pattern) {
12995
+ try {
12996
+ new RegExp(pattern);
12997
+ return true;
12998
+ } catch {
12999
+ return false;
13000
+ }
13001
+ }
13002
+ function isSecretProperty(property) {
13003
+ return property.type === "string" && property["x-secret"] === true;
13004
+ }
13005
+
13006
+ // ../product-configuration/dist/configuration-path.js
13007
+ var MAX_CONFIGURATION_PATH_SEGMENTS = 2 * MAX_CONFIGURATION_DEPTH - 1;
13008
+ function isIndexSegment(raw) {
13009
+ return /^\d+$/.test(raw) && String(Number(raw)) === raw;
13010
+ }
13011
+ function isPathSegment(segment) {
13012
+ if (typeof segment === "number")
13013
+ return Number.isInteger(segment) && segment >= 0;
13014
+ return CONFIGURATION_PROPERTY_NAME_REGEX.test(segment) || isIndexSegment(segment);
13015
+ }
13016
+ function configurationPath(...segments) {
13017
+ if (segments.length === 0 || segments.length > MAX_CONFIGURATION_PATH_SEGMENTS)
13018
+ return null;
13019
+ if (!segments.every(isPathSegment))
13020
+ return null;
13021
+ if (typeof segments[0] === "number" || isIndexSegment(String(segments[0])))
13022
+ return null;
13023
+ if (segments.length === 1)
13024
+ return String(segments[0]);
13025
+ return `/${segments.map(String).join("/")}`;
13026
+ }
13027
+ function parseConfigurationPath(raw) {
13028
+ if (typeof raw !== "string" || raw.length === 0)
13029
+ return null;
13030
+ if (!raw.startsWith("/"))
13031
+ return configurationPath(raw);
13032
+ const segments = raw.slice(1).split("/");
13033
+ if (segments.length < 2)
13034
+ return null;
13035
+ return configurationPath(...segments);
13036
+ }
13037
+ function pathSegments(path) {
13038
+ const raw = path.startsWith("/") ? path.slice(1).split("/") : [path];
13039
+ return raw.map((segment) => isIndexSegment(segment) ? Number(segment) : segment);
13040
+ }
13041
+ function declarationPathOf(path) {
13042
+ const segments = pathSegments(path).filter((segment) => typeof segment !== "number");
13043
+ return configurationPath(...segments) ?? path;
13044
+ }
13045
+ function printableConfigurationPath(path) {
13046
+ return pathSegments(path).join(".");
13047
+ }
13048
+
13049
+ // ../product-configuration/dist/canonical-json.js
13050
+ function canonicalise(value, path) {
13051
+ if (value === null)
13052
+ return "null";
13053
+ if (typeof value === "string")
13054
+ return JSON.stringify(value);
13055
+ if (typeof value === "boolean")
13056
+ return value ? "true" : "false";
13057
+ if (typeof value === "number") {
13058
+ if (!Number.isFinite(value)) {
13059
+ throw new Error(`canonicalJson: non-finite number at ${path}`);
13060
+ }
13061
+ return JSON.stringify(value === 0 ? 0 : value);
13062
+ }
13063
+ if (typeof value !== "object") {
13064
+ throw new Error(`canonicalJson: ${typeof value} is not JSON at ${path}`);
13065
+ }
13066
+ if (Array.isArray(value)) {
13067
+ return `[${value.map((item, i) => canonicalise(item, `${path}[${i}]`)).join(",")}]`;
13068
+ }
13069
+ const entries = Object.entries(value);
13070
+ for (const [key, item] of entries) {
13071
+ if (item === void 0) {
13072
+ throw new Error(`canonicalJson: undefined property at ${path}.${key}`);
13073
+ }
13074
+ }
13075
+ const sorted = entries.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
13076
+ const body = sorted.map(([key, item]) => `${JSON.stringify(key)}:${canonicalise(item, `${path}.${key}`)}`).join(",");
13077
+ return `{${body}}`;
13078
+ }
13079
+ function canonicalJson(value) {
13080
+ return canonicalise(value, "$");
13081
+ }
13082
+
13083
+ // ../product-configuration/dist/configuration-tree.js
13084
+ function underList(node) {
13085
+ return node.containers.some((container) => container.kind === "array");
13086
+ }
13087
+ function containerChildren(property) {
13088
+ if (property.type === "object" && property.properties !== void 0) {
13089
+ return {
13090
+ kind: "object",
13091
+ properties: property.properties,
13092
+ required: property.required ?? []
13093
+ };
12645
13094
  }
12646
- if (property.type === "array") {
12647
- if (property.items.enum)
12648
- out.push(["items", property.items.enum]);
12649
- if (property.default)
12650
- out.push(["default", property.default]);
13095
+ if (property.type === "array" && property.items !== void 0) {
13096
+ const items = property.items;
13097
+ if (items.type !== "object" || items.properties === void 0)
13098
+ return null;
13099
+ return {
13100
+ kind: "array",
13101
+ properties: items.properties,
13102
+ required: items.required ?? []
13103
+ };
12651
13104
  }
13105
+ return null;
13106
+ }
13107
+ function isConfigurationContainer(property) {
13108
+ return containerChildren(property) !== null;
13109
+ }
13110
+ function configurationLeaves(schema) {
13111
+ const out = [];
13112
+ walkDeclarations(schema.properties, [], [], (leaf) => out.push(leaf));
12652
13113
  return out;
12653
13114
  }
12654
- function isSafeRegex(pattern) {
12655
- try {
12656
- new RegExp(pattern);
12657
- return true;
12658
- } catch {
12659
- return false;
13115
+ function configurationContainers(schema) {
13116
+ const out = [];
13117
+ walkDeclarations(schema.properties, [], [], () => {
13118
+ }, (container) => out.push(container));
13119
+ return out;
13120
+ }
13121
+ function walkDeclarations(properties, ancestors, base, onLeaf, onContainer = () => {
13122
+ }) {
13123
+ for (const [name, property] of Object.entries(properties)) {
13124
+ const segments = [...base, name];
13125
+ const path = configurationPath(...segments);
13126
+ if (path === null)
13127
+ continue;
13128
+ const children = containerChildren(property);
13129
+ if (children === null) {
13130
+ onLeaf({ path, name, property, containers: ancestors });
13131
+ continue;
13132
+ }
13133
+ const container = {
13134
+ path,
13135
+ name,
13136
+ kind: children.kind,
13137
+ property,
13138
+ required: children.required,
13139
+ containers: ancestors
13140
+ };
13141
+ onContainer(container);
13142
+ walkDeclarations(children.properties, [...ancestors, container], segments, onLeaf, onContainer);
12660
13143
  }
12661
13144
  }
13145
+ function leafAt(schema, path) {
13146
+ const declaration = declarationPathOf(path);
13147
+ return configurationLeaves(schema).find((leaf) => leaf.path === declaration) ?? null;
13148
+ }
13149
+ function declaresPath(schema, path) {
13150
+ if (leafAt(schema, path) !== null)
13151
+ return true;
13152
+ return configurationContainers(schema).some((container) => container.path === declarationPathOf(path));
13153
+ }
12662
13154
 
12663
13155
  // ../product-configuration/dist/customer-configuration-input.js
12664
- var ABSOLUTE_MAX_STRING = 8192;
13156
+ var ABSOLUTE_MAX_STRING = MAX_DECLARABLE_STRING_LENGTH;
12665
13157
  var MAX_REPORTED_UNDECLARED_KEYS = 10;
12666
13158
  function isPlainObject(value) {
12667
13159
  return typeof value === "object" && value !== null && !Array.isArray(value);
12668
13160
  }
12669
- function isSecretProperty(property) {
12670
- return property.type === "string" && property["x-secret"] === true;
13161
+ function report(walk, field, message) {
13162
+ if (walk.issues.length >= MAX_CONFIGURATION_ISSUES) {
13163
+ walk.suppressed += 1;
13164
+ return;
13165
+ }
13166
+ walk.issues.push({ field, message });
13167
+ }
13168
+ function drain(walk, collected) {
13169
+ for (const issue of collected)
13170
+ report(walk, issue.field, issue.message);
12671
13171
  }
12672
13172
  function validateSubmittedConfiguration(schema, submitted, presence = {}) {
12673
- const issues = [];
13173
+ const walk = {
13174
+ issues: [],
13175
+ suppressed: 0,
13176
+ leaves: 0,
13177
+ referenced: new Set(presence.referenced ?? [])
13178
+ };
12674
13179
  const supplied = /* @__PURE__ */ new Set();
12675
13180
  if (submitted !== void 0 && submitted !== null && !isPlainObject(submitted)) {
12676
13181
  return {
@@ -12680,64 +13185,211 @@ function validateSubmittedConfiguration(schema, submitted, presence = {}) {
12680
13185
  };
12681
13186
  }
12682
13187
  const input = isPlainObject(submitted) ? submitted : {};
12683
- const undeclared = Object.keys(input).filter((name) => !Object.hasOwn(schema.properties, name));
12684
- for (const name of undeclared.slice(0, MAX_REPORTED_UNDECLARED_KEYS)) {
12685
- issues.push({
12686
- field: CONFIGURATION_PROPERTY_NAME_REGEX.test(name) ? name : "",
12687
- message: "This Product does not declare a setting with that name."
12688
- });
12689
- }
12690
- if (undeclared.length > MAX_REPORTED_UNDECLARED_KEYS) {
12691
- issues.push({
13188
+ const secrets = {};
13189
+ const configuration = validateObject({ properties: schema.properties, required: schema.required }, input, null, walk, { secrets, supplied });
13190
+ drain(walk, crossFieldIssues(schema, supplied, presence));
13191
+ if (walk.suppressed > 0) {
13192
+ walk.issues.push({
12692
13193
  field: "",
12693
- // A COUNT, never the names. The count is the actionable part and it
12694
- // cannot carry anything the caller chose.
12695
- message: `${undeclared.length - MAX_REPORTED_UNDECLARED_KEYS} further settings are not declared by this Product.`
13194
+ message: `${walk.suppressed} further problems were found and are not listed.`
12696
13195
  });
12697
13196
  }
12698
- const required = new Set(schema.required ?? []);
12699
- const configuration = {};
12700
- const secrets = {};
12701
- for (const [name, property] of Object.entries(schema.properties)) {
12702
- const blank = typeof input[name] === "string" && input[name].trim().length === 0;
12703
- const present = Object.hasOwn(input, name) && input[name] !== void 0 && !blank;
12704
- const secret = isSecretProperty(property);
12705
- if (present)
12706
- supplied.add(name);
13197
+ if (declaresContainer(schema)) {
13198
+ const size = utf8ByteLength(JSON.stringify(configuration));
13199
+ if (size > MAX_CONFIGURATION_VALUE_BYTES) {
13200
+ report(walk, "", `This configuration is ${size} bytes once defaults are applied, and the limit is ${MAX_CONFIGURATION_VALUE_BYTES}. Remove some items.`);
13201
+ }
13202
+ }
13203
+ if (walk.issues.length > 0)
13204
+ return { ok: false, issues: walk.issues, partial: configuration };
13205
+ return { ok: true, configuration, secrets };
13206
+ }
13207
+ function utf8ByteLength(value) {
13208
+ return new TextEncoder().encode(value).length;
13209
+ }
13210
+ function declaresContainer(schema) {
13211
+ return Object.values(schema.properties).some((property) => isConfigurationContainer(property));
13212
+ }
13213
+ function containsReferenced(walk, container) {
13214
+ if (walk.referenced.size === 0)
13215
+ return false;
13216
+ const parsedOuter = parseConfigurationPath(container);
13217
+ if (parsedOuter === null)
13218
+ return false;
13219
+ const outer = pathSegments(parsedOuter);
13220
+ for (const candidate of walk.referenced) {
13221
+ const parsed = parseConfigurationPath(candidate);
13222
+ if (parsed === null)
13223
+ continue;
13224
+ const inner = pathSegments(parsed);
13225
+ if (inner.length <= outer.length)
13226
+ continue;
13227
+ if (outer.every((segment, index) => inner[index] === segment))
13228
+ return true;
13229
+ }
13230
+ return false;
13231
+ }
13232
+ function validateObject(declaration, input, container, walk, root) {
13233
+ const required = new Set(declaration.required ?? []);
13234
+ const out = {};
13235
+ const fieldFor = (name) => {
13236
+ const path = container === null ? configurationPath(name) : configurationPath(...pathSegments(container), name);
13237
+ return path ?? "";
13238
+ };
13239
+ const undeclared = Object.keys(input).filter((name) => !Object.hasOwn(declaration.properties, name));
13240
+ for (const name of undeclared.slice(0, MAX_REPORTED_UNDECLARED_KEYS)) {
13241
+ report(walk, fieldFor(name), "This Product does not declare a setting with that name.");
13242
+ }
13243
+ if (undeclared.length > MAX_REPORTED_UNDECLARED_KEYS) {
13244
+ report(walk, container === null ? "" : container, `${undeclared.length - MAX_REPORTED_UNDECLARED_KEYS} further settings are not declared by this Product.`);
13245
+ }
13246
+ for (const [name, property] of Object.entries(declaration.properties)) {
13247
+ const field = fieldFor(name);
13248
+ const children = containerDeclaration(property);
13249
+ if (children !== null) {
13250
+ const value = input[name];
13251
+ const filled = validateContainer(name, property, children, value, field, walk);
13252
+ if (filled !== void 0)
13253
+ out[name] = filled;
13254
+ else if (required.has(name) && !containsReferenced(walk, field))
13255
+ report(walk, field, "This setting is required.");
13256
+ continue;
13257
+ }
13258
+ const leaf = property;
13259
+ if (walk.referenced.has(field)) {
13260
+ const sent = input[name];
13261
+ if (Object.hasOwn(input, name) && sent !== void 0) {
13262
+ report(walk, field, "This setting is answered by another Resource, so it cannot also be set here.");
13263
+ }
13264
+ continue;
13265
+ }
13266
+ const raw = input[name];
13267
+ const blank = typeof raw === "string" && raw.trim().length === 0;
13268
+ const present = Object.hasOwn(input, name) && raw !== void 0 && !blank;
13269
+ const secret = root !== null && isSecretProperty(leaf);
13270
+ if (present && root !== null && container === null)
13271
+ root.supplied.add(name);
12707
13272
  if (!present) {
12708
- const fallback = secret ? void 0 : defaultOf(property);
13273
+ const fallback = secret ? void 0 : defaultOf(leaf);
12709
13274
  if (fallback !== void 0) {
12710
- configuration[name] = fallback;
13275
+ walk.leaves += 1;
13276
+ out[name] = fallback;
12711
13277
  } else if (required.has(name)) {
12712
- issues.push({ field: name, message: "This setting is required." });
13278
+ report(walk, field, "This setting is required.");
12713
13279
  }
12714
13280
  continue;
12715
13281
  }
12716
- const value = input[name];
12717
- const before = issues.length;
12718
- const checked = checkProperty(name, property, value, issues);
12719
- if (issues.length !== before || checked === void 0)
13282
+ walk.leaves += 1;
13283
+ const collected = [];
13284
+ const checked = checkProperty(field, leaf, raw, collected);
13285
+ if (collected.length > 0 || checked === void 0) {
13286
+ drain(walk, collected);
12720
13287
  continue;
13288
+ }
12721
13289
  if (secret) {
12722
13290
  if (typeof checked === "string" && checked.length > 0)
12723
- secrets[name] = checked;
12724
- else if (required.has(name)) {
12725
- issues.push({ field: name, message: "This setting is required." });
13291
+ root.secrets[name] = checked;
13292
+ else if (required.has(name))
13293
+ report(walk, field, "This setting is required.");
13294
+ continue;
13295
+ }
13296
+ out[name] = checked;
13297
+ }
13298
+ if (declaration.dependentRequired !== void 0) {
13299
+ const suppliedHere = new Set(Object.keys(out));
13300
+ for (const [trigger, dependents] of Object.entries(declaration.dependentRequired)) {
13301
+ if (!suppliedHere.has(trigger) && !containsReferenced(walk, fieldFor(trigger)))
13302
+ continue;
13303
+ for (const dependent of dependents) {
13304
+ if (suppliedHere.has(dependent) || containsReferenced(walk, fieldFor(dependent)))
13305
+ continue;
13306
+ report(walk, fieldFor(dependent), `This setting is required when ${labelOf(declaration, trigger)} is configured.`);
12726
13307
  }
13308
+ }
13309
+ }
13310
+ return out;
13311
+ }
13312
+ function containerDeclaration(property) {
13313
+ if (property.type === "object" && property.properties !== void 0) {
13314
+ return {
13315
+ kind: "object",
13316
+ properties: property.properties,
13317
+ required: property.required,
13318
+ // CARRIED THROUGH, or the rule the container declares is one the walk
13319
+ // never sees — it publishes cleanly and enforces nothing.
13320
+ dependentRequired: property.dependentRequired,
13321
+ maxItems: 1
13322
+ };
13323
+ }
13324
+ if (property.type === "array" && property.items !== void 0) {
13325
+ const items = property.items;
13326
+ if (items.type !== "object" || items.properties === void 0)
13327
+ return null;
13328
+ return {
13329
+ kind: "array",
13330
+ properties: items.properties,
13331
+ required: items.required,
13332
+ dependentRequired: items.dependentRequired,
13333
+ maxItems: property.maxItems ?? 0,
13334
+ minItems: property.minItems,
13335
+ uniqueItems: property.uniqueItems === true
13336
+ };
13337
+ }
13338
+ return null;
13339
+ }
13340
+ function validateContainer(name, property, children, value, field, walk) {
13341
+ const path = parseConfigurationPath(field);
13342
+ if (children.kind === "object") {
13343
+ if (value === void 0)
13344
+ return void 0;
13345
+ if (!isPlainObject(value)) {
13346
+ report(walk, field, "Expected a group of settings.");
13347
+ return void 0;
13348
+ }
13349
+ const filled = validateObject(children, value, path, walk, null);
13350
+ return Object.keys(filled).length === 0 ? void 0 : filled;
13351
+ }
13352
+ if (value === void 0)
13353
+ return void 0;
13354
+ if (!Array.isArray(value)) {
13355
+ report(walk, field, "Expected a list.");
13356
+ return void 0;
13357
+ }
13358
+ if (value.length > children.maxItems) {
13359
+ report(walk, field, `At most ${children.maxItems} items are allowed.`);
13360
+ return void 0;
13361
+ }
13362
+ if (children.minItems !== void 0 && value.length < children.minItems) {
13363
+ report(walk, field, `At least ${children.minItems} items are required.`);
13364
+ return void 0;
13365
+ }
13366
+ const out = [];
13367
+ for (const [index, element] of value.entries()) {
13368
+ if (walk.leaves >= MAX_CONFIGURATION_VALUE_LEAVES) {
13369
+ report(walk, field, "This configuration has too many values.");
13370
+ break;
13371
+ }
13372
+ if (!isPlainObject(element)) {
13373
+ report(walk, `${field}`, `Item ${index + 1} is not a group of settings.`);
12727
13374
  continue;
12728
13375
  }
12729
- configuration[name] = checked;
13376
+ const elementPath = path === null ? null : configurationPath(...pathSegments(path), index);
13377
+ out.push(validateObject(children, element, elementPath, walk, null));
12730
13378
  }
12731
- issues.push(...crossFieldIssues(schema, supplied, presence));
12732
- if (issues.length > 0)
12733
- return { ok: false, issues, partial: configuration };
12734
- return { ok: true, configuration, secrets };
13379
+ if (children.uniqueItems === true) {
13380
+ const seen = new Set(out.map((entry) => canonicalJson(entry)));
13381
+ if (seen.size !== out.length) {
13382
+ report(walk, field, "Items must be unique.");
13383
+ return void 0;
13384
+ }
13385
+ }
13386
+ return out;
12735
13387
  }
12736
13388
  function crossFieldIssues(schema, supplied, presence) {
12737
13389
  const clauses = Object.entries(schema.dependentRequired ?? {});
12738
13390
  if (clauses.length === 0)
12739
13391
  return [];
12740
- const alsoPresent = new Set(presence.present ?? []);
13392
+ const alsoPresent = /* @__PURE__ */ new Set([...presence.present ?? [], ...presence.referenced ?? []]);
12741
13393
  const unknown = new Set(presence.unknown ?? []);
12742
13394
  const known = (name) => !unknown.has(name);
12743
13395
  const held = (name) => supplied.has(name) || alsoPresent.has(name);
@@ -12762,7 +13414,7 @@ function crossFieldIssues(schema, supplied, presence) {
12762
13414
  }
12763
13415
  function labelOf(schema, name) {
12764
13416
  const property = schema.properties[name];
12765
- return property === void 0 ? `"${name}"` : `"${property.title}"`;
13417
+ return property?.title === void 0 ? `"${name}"` : `"${property.title}"`;
12766
13418
  }
12767
13419
  function withoutCrossFieldRules(schema) {
12768
13420
  if (schema.dependentRequired === void 0)
@@ -12905,11 +13557,11 @@ var RESOURCE_KEY_RULE = "A resource key is 3\u201363 characters of lowercase let
12905
13557
  var ResourceKeySchema = external_exports.string().regex(RESOURCE_KEY_PATTERN, RESOURCE_KEY_RULE).describe(RESOURCE_KEY_RULE);
12906
13558
 
12907
13559
  // ../product-configuration/dist/product-instance-example.js
12908
- function resolveExampleChoice(options, selected, declaredDefault) {
13560
+ function resolveExampleChoice(options, selected, declaredDefault2) {
12909
13561
  if (selected !== void 0)
12910
13562
  return options.includes(selected) ? { value: selected, issue: null } : { value: null, issue: "invalid" };
12911
- if (declaredDefault !== void 0 && options.includes(declaredDefault))
12912
- return { value: declaredDefault, issue: null };
13563
+ if (declaredDefault2 !== void 0 && options.includes(declaredDefault2))
13564
+ return { value: declaredDefault2, issue: null };
12913
13565
  if (options.length === 1)
12914
13566
  return { value: options[0], issue: null };
12915
13567
  return { value: null, issue: options.length === 0 ? "unavailable" : "required" };
@@ -12920,7 +13572,6 @@ function isSecret(declaration) {
12920
13572
  function seedConfiguration(configuration) {
12921
13573
  const properties = configuration?.properties ?? {};
12922
13574
  const required = new Set(configuration?.required ?? []);
12923
- const values = {};
12924
13575
  const open = [];
12925
13576
  const requiredSecrets = [];
12926
13577
  const groups = Object.entries(configuration?.dependentRequired ?? {}).map(([trigger, dependents]) => {
@@ -12931,22 +13582,81 @@ function seedConfiguration(configuration) {
12931
13582
  secrets: members.filter((name) => isSecret(properties[name] ?? {}))
12932
13583
  };
12933
13584
  }).sort((a, b) => a.trigger.localeCompare(b.trigger));
13585
+ const values = seedProperties(properties, required, [], open, requiredSecrets);
13586
+ return { values, open, requiredSecrets, groups };
13587
+ }
13588
+ function seedProperties(properties, required, ancestors, open, requiredSecrets) {
13589
+ const values = {};
12934
13590
  for (const [name, declaration] of Object.entries(properties)) {
12935
- if (isSecret(declaration)) {
13591
+ if (requiredSecrets !== null && isSecret(declaration)) {
12936
13592
  if (required.has(name))
12937
13593
  requiredSecrets.push(name);
12938
13594
  continue;
12939
13595
  }
13596
+ const children = containerChildren2(declaration);
13597
+ if (children !== null) {
13598
+ if (!required.has(name))
13599
+ continue;
13600
+ const childRequired = new Set(children.required);
13601
+ const inner = [...ancestors, name];
13602
+ if (children.kind === "object") {
13603
+ values[name] = seedProperties(children.properties, childRequired, inner, open, null);
13604
+ continue;
13605
+ }
13606
+ const count = children.minItems ?? 0;
13607
+ values[name] = Array.from({ length: count }, () => seedProperties(children.properties, childRequired, inner, open, null));
13608
+ if (count > 1)
13609
+ dedupeOpen(open);
13610
+ continue;
13611
+ }
12940
13612
  if (declaration.default !== void 0) {
12941
13613
  values[name] = declaration.default;
12942
13614
  continue;
12943
13615
  }
12944
13616
  if (required.has(name)) {
12945
13617
  values[name] = "";
12946
- open.push({ name, type: declaration.type ?? null });
13618
+ const path = configurationPath(...ancestors, name);
13619
+ open.push({
13620
+ name: path ?? name,
13621
+ label: path === null ? name : printableConfigurationPath(path),
13622
+ type: declaration.type ?? null
13623
+ });
12947
13624
  }
12948
13625
  }
12949
- return { values, open, requiredSecrets, groups };
13626
+ return values;
13627
+ }
13628
+ function containerChildren2(declaration) {
13629
+ const node = declaration;
13630
+ if (node.type === "object" && node.properties !== void 0) {
13631
+ return {
13632
+ kind: "object",
13633
+ properties: node.properties,
13634
+ required: node.required ?? []
13635
+ };
13636
+ }
13637
+ if (node.type === "array" && node.items !== void 0) {
13638
+ const items = node.items;
13639
+ if (items.type !== "object" || items.properties === void 0)
13640
+ return null;
13641
+ return {
13642
+ kind: "array",
13643
+ properties: items.properties,
13644
+ required: items.required ?? [],
13645
+ ...typeof node.minItems === "number" ? { minItems: node.minItems } : {}
13646
+ };
13647
+ }
13648
+ return null;
13649
+ }
13650
+ function dedupeOpen(open) {
13651
+ const seen = /* @__PURE__ */ new Set();
13652
+ const unique = open.filter((entry) => {
13653
+ if (seen.has(entry.name))
13654
+ return false;
13655
+ seen.add(entry.name);
13656
+ return true;
13657
+ });
13658
+ open.length = 0;
13659
+ open.push(...unique);
12950
13660
  }
12951
13661
  function productInstanceExample(input) {
12952
13662
  return {
@@ -13059,13 +13769,32 @@ var OperationStatusSchema = external_exports.enum([
13059
13769
 
13060
13770
  // ../product-contracts/dist/api-version.js
13061
13771
  var PRODUCT_API_VERSION = "products.vardeflyt.no/v1";
13772
+ var PRODUCT_API_VERSION_V2 = "products.vardeflyt.no/v2";
13773
+ var PARSEABLE_PRODUCT_API_VERSIONS = /* @__PURE__ */ new Set([
13774
+ PRODUCT_API_VERSION,
13775
+ PRODUCT_API_VERSION_V2
13776
+ ]);
13777
+ function supportedList() {
13778
+ return [...PARSEABLE_PRODUCT_API_VERSIONS].map((value) => `"${value}"`).join(" or ");
13779
+ }
13780
+ function unsupportedProductApiVersionMessage() {
13781
+ return `apiVersion must be ${supportedList()}. An unrecognized contract format is refused rather than ignored.`;
13782
+ }
13062
13783
  var PRODUCT_DEFINITION_KIND = "ProductDefinition";
13063
13784
  var DEPLOYMENT_BLUEPRINT_KIND = "DeploymentBlueprint";
13064
13785
  var ProductApiVersionSchema = external_exports.literal(PRODUCT_API_VERSION, {
13065
13786
  errorMap: () => ({
13066
- message: `apiVersion must be "${PRODUCT_API_VERSION}". An unrecognized contract format is refused rather than ignored.`
13787
+ message: `apiVersion must be ${supportedList()}. An unrecognized contract format is refused rather than ignored.`
13067
13788
  })
13068
13789
  });
13790
+ var ProductApiVersionV2Schema = external_exports.literal(PRODUCT_API_VERSION_V2, {
13791
+ errorMap: () => ({
13792
+ message: `apiVersion must be ${supportedList()}. An unrecognized contract format is refused rather than ignored.`
13793
+ })
13794
+ });
13795
+ var ProductApiVersionAnySchema = external_exports.union([ProductApiVersionSchema, ProductApiVersionV2Schema], {
13796
+ errorMap: () => ({ message: unsupportedProductApiVersionMessage() })
13797
+ }).describe("The Product contract format this document is written against.");
13069
13798
  var ResourceApiVersionSchema = external_exports.literal(RESOURCE_API_VERSION, {
13070
13799
  errorMap: () => ({
13071
13800
  message: `apiVersion must be "${RESOURCE_API_VERSION}". An unrecognized contract format is refused rather than ignored.`
@@ -13442,6 +14171,18 @@ var ProductConditionSchema = external_exports.object({
13442
14171
  */
13443
14172
  description: external_exports.string().min(10).max(300)
13444
14173
  }).strict();
14174
+ var RequiredWhenSchema = external_exports.object({
14175
+ /** A declared, non-secret customer-configuration property. */
14176
+ property: external_exports.string().regex(CONFIGURATION_PROPERTY_NAME_REGEX, "Must name a declared customer-configuration property."),
14177
+ /** The values that make this dependency required. Non-empty. */
14178
+ anyOf: external_exports.array(external_exports.string().min(1).max(80)).min(1).max(40)
14179
+ }).strict();
14180
+ var RequiredWhenV2Schema = external_exports.object({
14181
+ path: external_exports.string().refine((raw) => parseConfigurationPath(raw) !== null, {
14182
+ message: 'A configuration path is a root property name, or an RFC 6901 pointer such as "/assistant/model".'
14183
+ }),
14184
+ anyOf: external_exports.array(external_exports.string().min(1).max(80)).min(1).max(40)
14185
+ }).strict();
13445
14186
  var ProductDependencySchema = external_exports.object({
13446
14187
  /**
13447
14188
  * OPTIONAL ONLY WHEN `interface` IS PRESENT, enforced below.
@@ -13556,13 +14297,11 @@ var ProductDependencySchema = external_exports.object({
13556
14297
  * `.optional()`, never `.default()` — see the Blueprint's banner on
13557
14298
  * `runtime`. Every dependency published to date carries none.
13558
14299
  */
13559
- requiredWhen: external_exports.object({
13560
- /** A declared, non-secret customer-configuration property. */
13561
- property: external_exports.string().regex(CONFIGURATION_PROPERTY_NAME_REGEX, "Must name a declared customer-configuration property."),
13562
- /** The values that make this dependency required. Non-empty. */
13563
- anyOf: external_exports.array(external_exports.string().min(1).max(80)).min(1).max(40)
13564
- }).strict().optional()
14300
+ requiredWhen: RequiredWhenSchema.optional()
13565
14301
  }).strict();
14302
+ var ProductDependencyV2Schema = ProductDependencySchema.extend({
14303
+ requiredWhen: RequiredWhenV2Schema.optional()
14304
+ });
13566
14305
  var ServiceInterfaceProvideSchema = external_exports.object({
13567
14306
  key: ServiceBindingKeySchema,
13568
14307
  interfaceId: ServiceInterfaceIdSchema,
@@ -13699,7 +14438,14 @@ var ProductDefinitionObjectSchema = external_exports.object({
13699
14438
  */
13700
14439
  blueprint: external_exports.object({ version: ContractVersionSchema }).strict()
13701
14440
  }).strict();
13702
- var ProductDefinitionSchema = ProductDefinitionObjectSchema.superRefine((definition, ctx) => {
14441
+ var ProductDefinitionSchema = ProductDefinitionObjectSchema.superRefine(checkProductDefinition);
14442
+ var ProductDefinitionV2ObjectSchema = ProductDefinitionObjectSchema.extend({
14443
+ apiVersion: ProductApiVersionV2Schema,
14444
+ configuration: CustomerConfigurationV2Schema,
14445
+ dependencies: external_exports.array(ProductDependencyV2Schema).max(8)
14446
+ });
14447
+ var ProductDefinitionV2Schema = ProductDefinitionV2ObjectSchema.superRefine(checkProductDefinition);
14448
+ function checkProductDefinition(definition, ctx) {
13703
14449
  const profileIds = definition.profiles.map((profile) => profile.id);
13704
14450
  const duplicateProfiles = profileIds.filter((id, i) => profileIds.indexOf(id) !== i);
13705
14451
  if (duplicateProfiles.length > 0) {
@@ -13821,42 +14567,55 @@ var ProductDefinitionSchema = ProductDefinitionObjectSchema.superRefine((definit
13821
14567
  message: "Repeated values in `anyOf`."
13822
14568
  });
13823
14569
  }
13824
- const property = definition.configuration.properties[condition.property];
13825
- if (property === void 0) {
14570
+ const named = "path" in condition ? condition.path : condition.property;
14571
+ const member = "path" in condition ? "path" : "property";
14572
+ const parsed = parseConfigurationPath(named);
14573
+ const leaf = parsed === null ? null : leafAt(definition.configuration, parsed);
14574
+ if (leaf === null) {
14575
+ const isGroup = parsed !== null && declaresPath(definition.configuration, parsed);
14576
+ ctx.addIssue({
14577
+ code: external_exports.ZodIssueCode.custom,
14578
+ path: [...at, member],
14579
+ message: isGroup ? `"${named}" is a group of settings, which holds no single value to compare. Name one setting inside it.` : `"${named}" is not a declared customer configuration property.`
14580
+ });
14581
+ return;
14582
+ }
14583
+ if (underList(leaf)) {
13826
14584
  ctx.addIssue({
13827
14585
  code: external_exports.ZodIssueCode.custom,
13828
- path: [...at, "property"],
13829
- message: `"${condition.property}" is not a declared customer configuration property.`
14586
+ path: [...at, member],
14587
+ message: `"${named}" is inside a list, so it names one value per item rather than one value. A condition must name a setting the instance holds exactly once.`
13830
14588
  });
13831
14589
  return;
13832
14590
  }
14591
+ const property = leaf.property;
13833
14592
  if (property.type !== "string") {
13834
14593
  ctx.addIssue({
13835
14594
  code: external_exports.ZodIssueCode.custom,
13836
- path: [...at, "property"],
13837
- message: `"${condition.property}" is a ${property.type} setting; a condition compares string values.`
14595
+ path: [...at, member],
14596
+ message: `"${named}" is a ${property.type} setting; a condition compares string values.`
13838
14597
  });
13839
14598
  return;
13840
14599
  }
13841
- if (property["x-secret"] === true) {
14600
+ if (isSecretProperty(property)) {
13842
14601
  ctx.addIssue({
13843
14602
  code: external_exports.ZodIssueCode.custom,
13844
- path: [...at, "property"],
13845
- message: `"${condition.property}" is a secret. The platform never reads a secret's value, so a condition on one could never be evaluated.`
14603
+ path: [...at, member],
14604
+ message: `"${named}" is a secret. The platform never reads a secret's value, so a condition on one could never be evaluated.`
13846
14605
  });
13847
14606
  return;
13848
14607
  }
13849
14608
  const single = withoutCrossFieldRules({
13850
14609
  ...definition.configuration,
13851
- properties: { [condition.property]: property },
13852
- required: [condition.property]
14610
+ properties: { [leaf.name]: property },
14611
+ required: [leaf.name]
13853
14612
  });
13854
- const outside = condition.anyOf.filter((value) => !validateSubmittedConfiguration(single, { [condition.property]: value }).ok);
14613
+ const outside = condition.anyOf.filter((value) => !validateSubmittedConfiguration(single, { [leaf.name]: value }).ok);
13855
14614
  if (outside.length > 0) {
13856
14615
  ctx.addIssue({
13857
14616
  code: external_exports.ZodIssueCode.custom,
13858
14617
  path: [...at, "anyOf"],
13859
- message: `"${condition.property}" never accepts ${outside.map((v) => `"${v}"`).join(", ")}, so this condition could never make the dependency required.`
14618
+ message: `"${named}" never accepts ${outside.map((v) => `"${v}"`).join(", ")}, so this condition could never make the dependency required.`
13860
14619
  });
13861
14620
  }
13862
14621
  });
@@ -13970,7 +14729,7 @@ var ProductDefinitionSchema = ProductDefinitionObjectSchema.superRefine((definit
13970
14729
  message: 'dataResidency "global" contradicts externalProcessing false \u2014 data cannot be global without leaving the tenant boundary.'
13971
14730
  });
13972
14731
  }
13973
- });
14732
+ }
13974
14733
 
13975
14734
  // ../product-contracts/dist/announcements.js
13976
14735
  var ANNOUNCED_PRODUCTS = Object.freeze([
@@ -14160,108 +14919,166 @@ var ComponentIngressSchema = external_exports.discriminatedUnion("exposure", [
14160
14919
  authentication: external_exports.enum(["signed-request", "mtls"])
14161
14920
  }).strict()
14162
14921
  ]);
14922
+ var LiteralEnvBindingSchema = external_exports.object({
14923
+ source: external_exports.literal("literal"),
14924
+ name: EnvVarNameSchema,
14925
+ value: external_exports.string().max(512)
14926
+ }).strict();
14927
+ var InstanceEnvBindingSchema = external_exports.object({
14928
+ source: external_exports.literal("instance"),
14929
+ name: EnvVarNameSchema,
14930
+ /** Closed set: the Control Plane knows these without Product knowledge. */
14931
+ field: external_exports.enum([
14932
+ "tenantKey",
14933
+ "tenantId",
14934
+ "projectId",
14935
+ "productInstanceId",
14936
+ "instanceKey",
14937
+ "productId",
14938
+ "productVersion",
14939
+ "profile",
14940
+ "region",
14941
+ "environment"
14942
+ ])
14943
+ }).strict();
14944
+ var PlatformEnvBindingSchema = external_exports.object({
14945
+ source: external_exports.literal("platform"),
14946
+ name: EnvVarNameSchema,
14947
+ key: external_exports.enum(["controlPlaneUrl", "observabilityEndpoint", "logLevel", "serviceVersion"])
14948
+ }).strict();
14949
+ var ComponentEndpointEnvBindingSchema = external_exports.object({
14950
+ source: external_exports.literal("component-endpoint"),
14951
+ name: EnvVarNameSchema,
14952
+ /** Resolved to the sibling component's private URL at reconcile time. */
14953
+ componentId: ComponentIdSchema
14954
+ }).strict();
14955
+ var ComponentPublicUrlEnvBindingSchema = external_exports.object({
14956
+ /**
14957
+ * The CUSTOMER-FACING address of a PUBLIC component — the URL a browser
14958
+ * opens, not the in-network one a sibling calls.
14959
+ *
14960
+ * WHY THE PLATFORM HAS TO SUPPLY IT. The address is assigned during
14961
+ * deployment, so nobody knows it earlier: not the publisher, who writes
14962
+ * the contract months before, and not the customer, who would otherwise
14963
+ * be asked to type an address that does not exist yet. A console that
14964
+ * needs its own address — to build an OAuth redirect URI, to put an
14965
+ * absolute link in an email — had no way to learn it, and asking the
14966
+ * customer produced exactly the failure you would expect: an empty
14967
+ * setting and a console nobody can sign in to.
14968
+ *
14969
+ * It is deliberately NOT derivable from the request. A server behind a
14970
+ * proxy infers its bind address, which looks right in development and is
14971
+ * wrong in production; a forwarded Host header is attacker-controlled.
14972
+ * The platform knows the answer, so the platform states it.
14973
+ *
14974
+ * `componentId` may name this component or another, but it must be a
14975
+ * PUBLIC one: `validateProductContracts` refuses a private target, the
14976
+ * same rule `outputs` already carries, because a private endpoint is not
14977
+ * an address to hand out.
14978
+ */
14979
+ source: external_exports.literal("component-public-url"),
14980
+ name: EnvVarNameSchema,
14981
+ componentId: ComponentIdSchema
14982
+ }).strict();
14983
+ var ServiceBindingAttributeEnvBindingSchema = external_exports.object({
14984
+ /**
14985
+ * A NON-SECRET value from a service binding to ANOTHER Product.
14986
+ *
14987
+ * THE ONLY SOURCE THAT LEAVES THIS PRODUCT INSTANCE. Every other member of
14988
+ * this union resolves inside one deployment: a literal, a fact about this
14989
+ * instance, a sibling component's address, a setting this customer typed.
14990
+ * This one is answered by a different Product Instance, possibly in a
14991
+ * different logical Project, which is why it is the only source whose
14992
+ * value can be absent for a reason that is nobody's mistake.
14993
+ *
14994
+ * ABSENT WHILE THE BINDING IS PENDING, and absent rather than empty. The
14995
+ * variable is left out of the container entirely until the provider
14996
+ * completes the binding, so a Product can tell "not yet" from "set to
14997
+ * nothing" — the same rule `configuration` already follows. Read
14998
+ * `VFAC_SERVICE_BINDING_<KEY>_STATUS`, which the platform injects for
14999
+ * every declared binding, rather than inferring readiness from absence.
15000
+ *
15001
+ * CREDENTIALS DO NOT COME THIS WAY. `field` must name a field the
15002
+ * interface classifies as an attribute; a secret field bound here is
15003
+ * refused by `validateProductContracts()`, and the credential-shaped-name
15004
+ * rule below refuses the variable name independently.
15005
+ */
15006
+ source: external_exports.literal("service-binding-attribute"),
15007
+ name: EnvVarNameSchema,
15008
+ /** The `serviceBindings[].key` this value comes from. */
15009
+ binding: ServiceBindingKeySchema,
15010
+ /** The interface field, e.g. `issuerUrl`. Checked against the registry. */
15011
+ field: external_exports.string().regex(/^[a-z][a-zA-Z0-9]{1,47}$/)
15012
+ }).strict();
15013
+ var ConfigurationEnvBindingSchema = external_exports.object({
15014
+ source: external_exports.literal("configuration"),
15015
+ name: EnvVarNameSchema,
15016
+ /**
15017
+ * A NON-SECRET customer setting from the Product Definition.
15018
+ * `validateProductContracts()` rejects a reference to an `x-secret`
15019
+ * property — those travel through `secrets[]` and Secret Manager, never
15020
+ * through plain environment wiring.
15021
+ */
15022
+ property: external_exports.string().regex(/^[a-z][a-zA-Z0-9]{1,47}$/)
15023
+ }).strict();
14163
15024
  var EnvBindingSchema = external_exports.discriminatedUnion("source", [
15025
+ LiteralEnvBindingSchema,
15026
+ InstanceEnvBindingSchema,
15027
+ PlatformEnvBindingSchema,
15028
+ ComponentEndpointEnvBindingSchema,
15029
+ ComponentPublicUrlEnvBindingSchema,
15030
+ ServiceBindingAttributeEnvBindingSchema,
15031
+ ConfigurationEnvBindingSchema
15032
+ ]);
15033
+ var ConfigurationBindingPathSchema = external_exports.string().refine((raw) => parseConfigurationPath(raw) !== null, {
15034
+ message: 'A configuration path is a root property name, or an RFC 6901 pointer such as "/assistant/model".'
15035
+ }).refine((raw) => !/\/\d+(\/|$)/.test(raw), {
15036
+ message: 'A Blueprint binds a DECLARED setting, so a path may not carry a list index. Bind the list with "configuration-json".'
15037
+ });
15038
+ var EnvBindingV2Schema = external_exports.discriminatedUnion("source", [
15039
+ LiteralEnvBindingSchema,
15040
+ InstanceEnvBindingSchema,
15041
+ PlatformEnvBindingSchema,
15042
+ ComponentEndpointEnvBindingSchema,
15043
+ ComponentPublicUrlEnvBindingSchema,
15044
+ ServiceBindingAttributeEnvBindingSchema,
14164
15045
  external_exports.object({
14165
- source: external_exports.literal("literal"),
14166
- name: EnvVarNameSchema,
14167
- value: external_exports.string().max(512)
14168
- }).strict(),
14169
- external_exports.object({
14170
- source: external_exports.literal("instance"),
14171
- name: EnvVarNameSchema,
14172
- /** Closed set: the Control Plane knows these without Product knowledge. */
14173
- field: external_exports.enum([
14174
- "tenantKey",
14175
- "tenantId",
14176
- "projectId",
14177
- "productInstanceId",
14178
- "instanceKey",
14179
- "productId",
14180
- "productVersion",
14181
- "profile",
14182
- "region",
14183
- "environment"
14184
- ])
14185
- }).strict(),
14186
- external_exports.object({
14187
- source: external_exports.literal("platform"),
14188
- name: EnvVarNameSchema,
14189
- key: external_exports.enum(["controlPlaneUrl", "observabilityEndpoint", "logLevel", "serviceVersion"])
14190
- }).strict(),
14191
- external_exports.object({
14192
- source: external_exports.literal("component-endpoint"),
15046
+ source: external_exports.literal("configuration"),
14193
15047
  name: EnvVarNameSchema,
14194
- /** Resolved to the sibling component's private URL at reconcile time. */
14195
- componentId: ComponentIdSchema
14196
- }).strict(),
14197
- external_exports.object({
14198
15048
  /**
14199
- * The CUSTOMER-FACING address of a PUBLIC component the URL a browser
14200
- * opens, not the in-network one a sibling calls.
14201
- *
14202
- * WHY THE PLATFORM HAS TO SUPPLY IT. The address is assigned during
14203
- * deployment, so nobody knows it earlier: not the publisher, who writes
14204
- * the contract months before, and not the customer, who would otherwise
14205
- * be asked to type an address that does not exist yet. A console that
14206
- * needs its own address — to build an OAuth redirect URI, to put an
14207
- * absolute link in an email — had no way to learn it, and asking the
14208
- * customer produced exactly the failure you would expect: an empty
14209
- * setting and a console nobody can sign in to.
15049
+ * A NON-SECRET customer setting, named by its canonical path.
14210
15050
  *
14211
- * It is deliberately NOT derivable from the request. A server behind a
14212
- * proxy infers its bind address, which looks right in development and is
14213
- * wrong in production; a forwarded Host header is attacker-controlled.
14214
- * The platform knows the answer, so the platform states it.
14215
- *
14216
- * `componentId` may name this component or another, but it must be a
14217
- * PUBLIC one: `validateProductContracts` refuses a private target, the
14218
- * same rule `outputs` already carries, because a private endpoint is not
14219
- * an address to hand out.
15051
+ * `validateProductContracts` refuses a path that names no leaf, a path
15052
+ * that names a GROUP a group has no scalar rendering, and
15053
+ * `configuration-json` is how one is carried and any `x-secret`
15054
+ * property, which travels through `secrets[]` and never through plain
15055
+ * environment wiring.
14220
15056
  */
14221
- source: external_exports.literal("component-public-url"),
14222
- name: EnvVarNameSchema,
14223
- componentId: ComponentIdSchema
15057
+ path: ConfigurationBindingPathSchema
14224
15058
  }).strict(),
14225
15059
  external_exports.object({
15060
+ source: external_exports.literal("configuration-json"),
15061
+ name: EnvVarNameSchema,
14226
15062
  /**
14227
- * A NON-SECRET value from a service binding to ANOTHER Product.
15063
+ * THE WHOLE VALIDATED CONFIGURATION, OR ONE SUBTREE OF IT, AS JSON.
14228
15064
  *
14229
- * THE ONLY SOURCE THAT LEAVES THIS PRODUCT INSTANCE. Every other member of
14230
- * this union resolves inside one deployment: a literal, a fact about this
14231
- * instance, a sibling component's address, a setting this customer typed.
14232
- * This one is answered by a different Product Instance, possibly in a
14233
- * different logical Project, which is why it is the only source whose
14234
- * value can be absent for a reason that is nobody's mistake.
15065
+ * WHY THIS EXISTS. A component may carry at most forty environment
15066
+ * bindings, and a nested Product spends one per leaf so the format that
15067
+ * lets a Product have structure would immediately run out of room to
15068
+ * deliver it. This binds a whole document instead, and the Product's own
15069
+ * runtime translates it into whatever shape it wants. The Control Plane
15070
+ * learns nothing about that shape, which is the point.
14235
15071
  *
14236
- * ABSENT WHILE THE BINDING IS PENDING, and absent rather than empty. The
14237
- * variable is left out of the container entirely until the provider
14238
- * completes the binding, so a Product can tell "not yet" from "set to
14239
- * nothing" the same rule `configuration` already follows. Read
14240
- * `VFAC_SERVICE_BINDING_<KEY>_STATUS`, which the platform injects for
14241
- * every declared binding, rather than inferring readiness from absence.
15072
+ * WHAT IT CARRIES: validated, non-secret configuration with resolved
15073
+ * ORDINARY resource references materialised in place. Never a secret
15074
+ * value secrets are root-only and travel through `secrets[]`, and a
15075
+ * subtree cannot contain one. Serialised with `canonicalJson`, so the same
15076
+ * logical configuration is the same bytes and the environment fingerprint
15077
+ * does not move on key order alone.
14242
15078
  *
14243
- * CREDENTIALS DO NOT COME THIS WAY. `field` must name a field the
14244
- * interface classifies as an attribute; a secret field bound here is
14245
- * refused by `validateProductContracts()`, and the credential-shaped-name
14246
- * rule below refuses the variable name independently.
14247
- */
14248
- source: external_exports.literal("service-binding-attribute"),
14249
- name: EnvVarNameSchema,
14250
- /** The `serviceBindings[].key` this value comes from. */
14251
- binding: ServiceBindingKeySchema,
14252
- /** The interface field, e.g. `issuerUrl`. Checked against the registry. */
14253
- field: external_exports.string().regex(/^[a-z][a-zA-Z0-9]{1,47}$/)
14254
- }).strict(),
14255
- external_exports.object({
14256
- source: external_exports.literal("configuration"),
14257
- name: EnvVarNameSchema,
14258
- /**
14259
- * A NON-SECRET customer setting from the Product Definition.
14260
- * `validateProductContracts()` rejects a reference to an `x-secret`
14261
- * property — those travel through `secrets[]` and Secret Manager, never
14262
- * through plain environment wiring.
15079
+ * Absent `path` means the whole document.
14263
15080
  */
14264
- property: external_exports.string().regex(/^[a-z][a-zA-Z0-9]{1,47}$/)
15081
+ path: ConfigurationBindingPathSchema.optional()
14265
15082
  }).strict()
14266
15083
  ]);
14267
15084
  var MAX_BOOTSTRAP_SCRIPT_LENGTH = 32768;
@@ -14473,11 +15290,12 @@ var ComponentDependencySchema = external_exports.object({
14473
15290
  minVersion: ContractVersionSchema.optional(),
14474
15291
  maxVersion: ContractVersionSchema.optional()
14475
15292
  }).strict();
15293
+ var MAX_COMPONENT_ENV_BINDINGS = 40;
14476
15294
  var componentCommonShape = {
14477
15295
  id: ComponentIdSchema.describe("Scoped by its Product; does not repeat the Product ID (\xA78)."),
14478
15296
  kind: ComponentKindSchema,
14479
15297
  description: external_exports.string().min(10).max(300),
14480
- env: external_exports.array(EnvBindingSchema).max(40),
15298
+ env: external_exports.array(EnvBindingSchema).max(MAX_COMPONENT_ENV_BINDINGS),
14481
15299
  dependsOn: external_exports.array(ComponentDependencySchema).max(8)
14482
15300
  };
14483
15301
  var ServerlessContainerComponentSchema = external_exports.object({
@@ -14529,6 +15347,16 @@ var RuntimeComponentSchema = external_exports.discriminatedUnion("runtime", [
14529
15347
  ServerlessContainerComponentSchema,
14530
15348
  VirtualMachineComponentSchema
14531
15349
  ]);
15350
+ var ServerlessContainerComponentV2Schema = ServerlessContainerComponentSchema.extend({
15351
+ env: external_exports.array(EnvBindingV2Schema).max(MAX_COMPONENT_ENV_BINDINGS)
15352
+ });
15353
+ var VirtualMachineComponentV2Schema = VirtualMachineComponentSchema.extend({
15354
+ env: external_exports.array(EnvBindingV2Schema).max(MAX_COMPONENT_ENV_BINDINGS)
15355
+ });
15356
+ var RuntimeComponentV2Schema = external_exports.discriminatedUnion("runtime", [
15357
+ ServerlessContainerComponentV2Schema,
15358
+ VirtualMachineComponentV2Schema
15359
+ ]);
14532
15360
  function isVirtualMachineComponent(component) {
14533
15361
  return component.runtime === "virtual-machine";
14534
15362
  }
@@ -14777,7 +15605,47 @@ var BlueprintLifecycleSchema = external_exports.object({
14777
15605
  * the service-interface surface is unchanged. Nothing published to date
14778
15606
  * regresses, and a release cannot strand an instance by forgetting it.
14779
15607
  */
14780
- directFromVersions: external_exports.array(ContractVersionSchema).max(20).optional()
15608
+ directFromVersions: external_exports.array(ContractVersionSchema).max(20).optional(),
15609
+ /**
15610
+ * How a stored configuration is carried across a release that MOVED a
15611
+ * setting.
15612
+ *
15613
+ * THE CASE THIS EXISTS FOR is a flat Product taking on structure:
15614
+ * `assistantName` becomes `/assistant/name`. Nothing else on this
15615
+ * platform can express that. Without it the target release simply does
15616
+ * not declare `assistantName`, so the upgrade drops the customer's value
15617
+ * and then refuses the deploy for a missing required setting — and the
15618
+ * customer's answer is gone either way.
15619
+ *
15620
+ * `move` AND NOTHING ELSE. A migration the platform cannot fully
15621
+ * evaluate is one it cannot apply safely, and every other operation a
15622
+ * publisher might want — rename a value, split one setting into two,
15623
+ * compute a default — needs the Product's own knowledge of what those
15624
+ * values MEAN. Moving a value is the one thing the platform can do
15625
+ * correctly without knowing anything about it.
15626
+ *
15627
+ * APPLIED AT CUTOVER, in the transaction that writes the new
15628
+ * configuration, and to the REFERENCE ROWS as well as the stored
15629
+ * document — a Resource Reference is identified by the path it answers,
15630
+ * so a setting that moved takes its edge with it or the edge points at
15631
+ * nothing.
15632
+ *
15633
+ * AN ABSENT SOURCE IS A NO-OP, deliberately. A customer who never
15634
+ * answered an optional setting has nothing to carry, and inventing an
15635
+ * empty value for them would turn "not configured" into "configured
15636
+ * blank" — two states this platform keeps apart everywhere else.
15637
+ *
15638
+ * `.optional()`, never `.default([])`, for the reason
15639
+ * `directFromVersions` states one field above: a materialised key makes
15640
+ * a byte-identical republish `RELEASED_CONTRACT_MUTATED`.
15641
+ */
15642
+ configurationMigrations: external_exports.array(external_exports.object({
15643
+ op: external_exports.literal("move"),
15644
+ /** Where the value is today, in the release being left. */
15645
+ from: ConfigurationBindingPathSchema,
15646
+ /** Where this release declares it. */
15647
+ to: ConfigurationBindingPathSchema
15648
+ }).strict()).max(40).optional()
14781
15649
  }).strict(),
14782
15650
  rollback: external_exports.object({
14783
15651
  supported: external_exports.boolean(),
@@ -14903,7 +15771,14 @@ function isOidcClientServiceBinding(binding) {
14903
15771
  }
14904
15772
  var DeploymentBlueprintObjectSchema = external_exports.object({
14905
15773
  /** The CONTRACT FORMAT version — not the Blueprint's. See api-version.ts. */
14906
- apiVersion: ProductApiVersionSchema,
15774
+ /**
15775
+ * The SHARED base carries either literal; each format pins its own below.
15776
+ *
15777
+ * Nothing parses this object schema directly — `DeploymentBlueprintV1Schema`
15778
+ * and `…V2Schema` extend it — so the permissive literal here is never what a
15779
+ * document is held to, and never what an artifact publishes.
15780
+ */
15781
+ apiVersion: ProductApiVersionAnySchema,
14907
15782
  kind: kindSchema(DEPLOYMENT_BLUEPRINT_KIND),
14908
15783
  productId: ProductIdSchema,
14909
15784
  /** This Blueprint's own version. The Definition pins it exactly. */
@@ -14971,7 +15846,17 @@ var DeploymentBlueprintObjectSchema = external_exports.object({
14971
15846
  outputs: external_exports.array(BlueprintOutputSchema).min(1).max(12),
14972
15847
  lifecycle: BlueprintLifecycleSchema
14973
15848
  }).strict();
14974
- var DeploymentBlueprintSchema = DeploymentBlueprintObjectSchema.superRefine((blueprint, ctx) => {
15849
+ var DeploymentBlueprintV1ObjectSchema = DeploymentBlueprintObjectSchema.extend({
15850
+ apiVersion: ProductApiVersionSchema
15851
+ });
15852
+ var DeploymentBlueprintV2ObjectSchema = DeploymentBlueprintObjectSchema.extend({
15853
+ apiVersion: ProductApiVersionV2Schema,
15854
+ components: external_exports.array(RuntimeComponentV2Schema).min(1).max(MAX_RUNTIME_COMPONENTS)
15855
+ });
15856
+ var DeploymentBlueprintV1Schema = DeploymentBlueprintV1ObjectSchema.superRefine(checkDeploymentBlueprint);
15857
+ var DeploymentBlueprintV2Schema = DeploymentBlueprintV2ObjectSchema.superRefine(checkDeploymentBlueprint);
15858
+ var DeploymentBlueprintSchema = DeploymentBlueprintV1Schema;
15859
+ function checkDeploymentBlueprint(blueprint, ctx) {
14975
15860
  const componentIds = blueprint.components.map((component) => component.id);
14976
15861
  const knownComponents = new Set(componentIds);
14977
15862
  const publicComponents = new Set(blueprint.components.filter((component) => component.ingress.exposure === "public").map((component) => component.id));
@@ -15072,13 +15957,7 @@ var DeploymentBlueprintSchema = DeploymentBlueprintObjectSchema.superRefine((blu
15072
15957
  });
15073
15958
  const handoff = resource.bootstrap.productHandoff;
15074
15959
  if (handoff !== void 0) {
15075
- const handoffPath = [
15076
- "dynamicResources",
15077
- index,
15078
- "bootstrap",
15079
- "productHandoff",
15080
- "componentId"
15081
- ];
15960
+ const handoffPath = ["dynamicResources", index, "bootstrap", "productHandoff", "componentId"];
15082
15961
  const target = blueprint.components.find((component) => component.id === handoff.componentId);
15083
15962
  if (target === void 0) {
15084
15963
  ctx.addIssue({
@@ -15689,7 +16568,7 @@ var DeploymentBlueprintSchema = DeploymentBlueprintObjectSchema.superRefine((blu
15689
16568
  });
15690
16569
  }
15691
16570
  }
15692
- });
16571
+ }
15693
16572
 
15694
16573
  // ../product-contracts/dist/deployability.js
15695
16574
  var VIRTUAL_MACHINE_PROFILE_INTERFACE = {
@@ -15730,6 +16609,9 @@ function isTerminalAction(action) {
15730
16609
  }
15731
16610
  var ManifestActionSchema = external_exports.enum(MANIFEST_ACTIONS);
15732
16611
 
16612
+ // ../product-contracts/dist/contract-issue.js
16613
+ var MAX_SEGMENTS = MAX_CONFIGURATION_PATH_SEGMENTS + 2;
16614
+
15733
16615
  // ../product-contracts/dist/resource-output-reference.js
15734
16616
  var OUTPUT_KEY_RULE = "Output keys are camelCase.";
15735
16617
  var ResourceOutputReferenceSchema = external_exports.object({
@@ -15896,7 +16778,33 @@ var ProductSubmissionObjectSchema = external_exports.object({
15896
16778
  definition: ProductDefinitionSchema,
15897
16779
  blueprint: DeploymentBlueprintSchema
15898
16780
  }).strict();
15899
- var ProductSubmissionSchema = ProductSubmissionObjectSchema.superRefine((submission, ctx) => {
16781
+ var submissionShape = {
16782
+ kind: kindSchema(PRODUCT_SUBMISSION_KIND),
16783
+ submission: SubmissionEnvelopeSchema
16784
+ };
16785
+ var ProductSubmissionV1ObjectSchema = external_exports.object({
16786
+ apiVersion: ProductApiVersionSchema,
16787
+ ...submissionShape,
16788
+ definition: ProductDefinitionSchema,
16789
+ blueprint: DeploymentBlueprintV1Schema
16790
+ }).strict();
16791
+ var ProductSubmissionV2ObjectSchema = external_exports.object({
16792
+ apiVersion: ProductApiVersionV2Schema,
16793
+ ...submissionShape,
16794
+ definition: ProductDefinitionV2Schema,
16795
+ blueprint: DeploymentBlueprintV2Schema
16796
+ }).strict();
16797
+ var ProductSubmissionV1Schema = ProductSubmissionV1ObjectSchema.superRefine(checkProductSubmission);
16798
+ var ProductSubmissionV2Schema = ProductSubmissionV2ObjectSchema.superRefine(checkProductSubmission);
16799
+ var ProductSubmissionSchema = ProductSubmissionV1Schema;
16800
+ function checkProductSubmission(submission, ctx) {
16801
+ if (submission.definition.apiVersion !== submission.apiVersion) {
16802
+ ctx.addIssue({
16803
+ code: external_exports.ZodIssueCode.custom,
16804
+ path: ["definition", "apiVersion"],
16805
+ message: `This submission is written against "${submission.apiVersion}" but its Product Definition declares "${submission.definition.apiVersion}". One submission is one contract format.`
16806
+ });
16807
+ }
15900
16808
  if (submission.definition.id !== submission.submission.productId) {
15901
16809
  ctx.addIssue({
15902
16810
  code: external_exports.ZodIssueCode.custom,
@@ -15918,7 +16826,7 @@ var ProductSubmissionSchema = ProductSubmissionObjectSchema.superRefine((submiss
15918
16826
  message: `The Product Definition is version "${submission.definition.version}" but the submission claims "${submission.submission.version}". They must be the same version.`
15919
16827
  });
15920
16828
  }
15921
- });
16829
+ }
15922
16830
  var PUBLISH_RULE_ANNEX = {
15923
16831
  allOf: [
15924
16832
  {
@@ -16371,12 +17279,12 @@ async function runDoctor(parsed) {
16371
17279
  ok: false,
16372
17280
  detail: "not set. Run `vfac context set --endpoint https://your-organization.cloud.vardeflyt.no`, or set VFAC_ENDPOINT."
16373
17281
  });
16374
- return report(checks, json);
17282
+ return report2(checks, json);
16375
17283
  }
16376
17284
  const checked = validateEndpoint(resolved.value, resolved.source);
16377
17285
  if (!checked.ok) {
16378
17286
  checks.push({ name: "Endpoint", ok: false, detail: checked.message });
16379
- return report(checks, json);
17287
+ return report2(checks, json);
16380
17288
  }
16381
17289
  const endpoint = checked.endpoint;
16382
17290
  checks.push({
@@ -16396,16 +17304,16 @@ async function runDoctor(parsed) {
16396
17304
  const probe = await probeMachineApi({ endpoint });
16397
17305
  if (!probe.ok) {
16398
17306
  checks.push({ name: "Machine API", ok: false, detail: probe.message });
16399
- return report(checks, json);
17307
+ return report2(checks, json);
16400
17308
  }
16401
17309
  checks.push({ name: "Machine API", ok: true, detail: probe.apiVersion });
16402
17310
  const compatible = compatibility(cliVersion(), probe.minimumCliVersion);
16403
17311
  checks.push(compatible);
16404
- if (!compatible.ok) return report(checks, json);
17312
+ if (!compatible.ok) return report2(checks, json);
16405
17313
  const signedIn = await signIn({ endpoint });
16406
17314
  if (!signedIn.ok) {
16407
17315
  checks.push({ name: "Context Gate", ok: false, detail: signedIn.message });
16408
- return report(checks, json);
17316
+ return report2(checks, json);
16409
17317
  }
16410
17318
  checks.push({
16411
17319
  name: "Context Gate",
@@ -16426,7 +17334,7 @@ async function runDoctor(parsed) {
16426
17334
  ok: false,
16427
17335
  detail: who.error.status === 404 ? "this Tenant Portal does not serve /api/v1/whoami yet \u2014 it is running an older build." : who.error.message
16428
17336
  });
16429
- return report(checks, json);
17337
+ return report2(checks, json);
16430
17338
  }
16431
17339
  const access = who.data.access;
16432
17340
  const projects = access.projects.map((project) => project.projectId);
@@ -16486,7 +17394,7 @@ async function runDoctor(parsed) {
16486
17394
  detail: "run `vfac guide` for the platform's own instructions, written for a machine"
16487
17395
  });
16488
17396
  checks.push(await updateAvailable(cliVersion()));
16489
- return report(checks, json);
17397
+ return report2(checks, json);
16490
17398
  }
16491
17399
  function compatibility(local, declared) {
16492
17400
  const name = "Compatibility";
@@ -16566,7 +17474,7 @@ async function updateAvailable(local) {
16566
17474
  facts
16567
17475
  };
16568
17476
  }
16569
- function report(checks, json) {
17477
+ function report2(checks, json) {
16570
17478
  const failed = checks.filter((check) => !check.ok);
16571
17479
  if (json) {
16572
17480
  process.stdout.write(`${JSON.stringify({ ok: failed.length === 0, checks })}
@@ -16942,16 +17850,14 @@ function renderProduct(product) {
16942
17850
  (profile) => `${profile.id}${profile.default ? " (default)" : ""}`
16943
17851
  );
16944
17852
  lines.push(` profile ${profiles.join(", ") || "(none declared)"}`);
16945
- const properties = product.configuration?.properties ?? {};
16946
- const required = product.configuration?.required ?? [];
16947
- if (required.length > 0) {
17853
+ const seed = seedConfiguration(product.configuration);
17854
+ if (seed.open.length > 0 || seed.requiredSecrets.length > 0) {
16948
17855
  lines.push("", " Required configuration");
16949
- for (const name of required) {
16950
- const property = properties[name];
16951
- const secret = property?.writeOnly === true || property?.["x-secret"] === true;
16952
- lines.push(
16953
- ` ${name}${secret ? " (secret \u2014 pass --secret " + name + "=env:VAR)" : ` ${property?.type ?? ""}`}`
16954
- );
17856
+ for (const name of seed.requiredSecrets) {
17857
+ lines.push(` ${name} (secret \u2014 pass --secret ${name}=env:VAR)`);
17858
+ }
17859
+ for (const setting of seed.open) {
17860
+ lines.push(` ${setting.label} ${setting.type ?? ""}`.trimEnd());
16955
17861
  }
16956
17862
  }
16957
17863
  const needed = (product.dependencies ?? []).filter((dependency) => dependency.required);
@@ -17224,7 +18130,7 @@ async function runManifestInit(parsed) {
17224
18130
  }
17225
18131
  for (const setting of seed.open) {
17226
18132
  notes.push(
17227
- `spec.configuration.${setting.name} \u2014 required${setting.type ? ` (${setting.type})` : ""}`
18133
+ `spec.configuration.${setting.label} \u2014 required${setting.type ? ` (${setting.type})` : ""}`
17228
18134
  );
17229
18135
  }
17230
18136
  for (const group of seed.groups) {