@varde-flyt/vfac 0.7.1 → 0.8.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 +1196 -367
  2. package/package.json +1 -1
package/dist/vfac.mjs CHANGED
@@ -11988,6 +11988,14 @@ var coerce = {
11988
11988
  };
11989
11989
  var NEVER = INVALID;
11990
11990
 
11991
+ // ../product-configuration/dist/configuration-bounds.js
11992
+ var MAX_CONFIGURATION_DEPTH = 2;
11993
+ var MAX_CONFIGURATION_LEAVES = 40;
11994
+ var MAX_CONFIGURATION_NODES = 60;
11995
+ var MAX_CONFIGURATION_VALUE_LEAVES = 1e3;
11996
+ var MAX_CONFIGURATION_ISSUES = 50;
11997
+ var MAX_CONFIGURATION_VALUE_BYTES = 16384;
11998
+
11991
11999
  // ../product-configuration/dist/json-schema-annex.js
11992
12000
  var ANNEXES = /* @__PURE__ */ new WeakMap();
11993
12001
  function withJsonSchemaAnnex(schema, annex) {
@@ -12287,12 +12295,49 @@ var ArrayPropertySchema = external_exports.object({
12287
12295
  uniqueItems: external_exports.boolean().optional(),
12288
12296
  default: external_exports.array(external_exports.string().max(200)).max(100).optional()
12289
12297
  }).strict();
12298
+ var MAX_CONFIGURATION_PROPERTIES = 40;
12290
12299
  var ConfigurationPropertySchema = external_exports.discriminatedUnion("type", [
12291
12300
  StringPropertySchema,
12292
12301
  NumberPropertySchema,
12293
12302
  BooleanPropertySchema,
12294
12303
  ArrayPropertySchema
12295
12304
  ]);
12305
+ var NestedStringPropertySchema = StringPropertySchema.omit({
12306
+ "x-secret": true,
12307
+ writeOnly: true
12308
+ });
12309
+ var NestedConfigurationPropertySchema = external_exports.discriminatedUnion("type", [
12310
+ NestedStringPropertySchema,
12311
+ NumberPropertySchema,
12312
+ BooleanPropertySchema,
12313
+ ArrayPropertySchema
12314
+ ]);
12315
+ var nestedPropertiesShape = {
12316
+ properties: external_exports.record(PropertyNameSchema, NestedConfigurationPropertySchema),
12317
+ required: external_exports.array(PropertyNameSchema).max(MAX_CONFIGURATION_PROPERTIES).optional(),
12318
+ additionalProperties: external_exports.literal(false)
12319
+ };
12320
+ var ObjectPropertySchema = external_exports.object({
12321
+ type: external_exports.literal("object"),
12322
+ title: TitleSchema,
12323
+ description: HelpTextSchema.optional(),
12324
+ group: ConfigurationGroupSchema,
12325
+ ...nestedPropertiesShape
12326
+ }).strict();
12327
+ var ObjectArrayItemsSchema = external_exports.object({
12328
+ type: external_exports.literal("object"),
12329
+ ...nestedPropertiesShape
12330
+ }).strict();
12331
+ var ArrayPropertyV2Schema = ArrayPropertySchema.extend({
12332
+ items: external_exports.discriminatedUnion("type", [ArrayItemsSchema, ObjectArrayItemsSchema])
12333
+ });
12334
+ var RootConfigurationPropertySchema = external_exports.discriminatedUnion("type", [
12335
+ StringPropertySchema,
12336
+ NumberPropertySchema,
12337
+ BooleanPropertySchema,
12338
+ ArrayPropertyV2Schema,
12339
+ ObjectPropertySchema
12340
+ ]);
12296
12341
  var CustomerConfigurationObjectSchema = external_exports.object({
12297
12342
  type: external_exports.literal("object"),
12298
12343
  properties: external_exports.record(PropertyNameSchema, ConfigurationPropertySchema).describe("Customer-editable settings, keyed by camelCase property name."),
@@ -12326,7 +12371,6 @@ var CustomerConfigurationObjectSchema = external_exports.object({
12326
12371
  */
12327
12372
  additionalProperties: external_exports.literal(false)
12328
12373
  }).strict();
12329
- var MAX_CONFIGURATION_PROPERTIES = 40;
12330
12374
  var CustomerConfigurationSchema = CustomerConfigurationObjectSchema.superRefine((config, ctx) => {
12331
12375
  const names = Object.keys(config.properties);
12332
12376
  if (names.length > MAX_CONFIGURATION_PROPERTIES) {
@@ -12338,182 +12382,192 @@ var CustomerConfigurationSchema = CustomerConfigurationObjectSchema.superRefine(
12338
12382
  }
12339
12383
  for (const name of names) {
12340
12384
  const property = config.properties[name];
12341
- const path = ["properties", name];
12342
- const infrastructure = infrastructureVocabularyViolation(name);
12343
- if (infrastructure) {
12385
+ checkDeclaredProperty(name, property, ["properties", name], ctx, true);
12386
+ }
12387
+ for (const name of config.required ?? []) {
12388
+ if (!Object.hasOwn(config.properties, name)) {
12344
12389
  ctx.addIssue({
12345
12390
  code: external_exports.ZodIssueCode.custom,
12346
- path,
12347
- message: `${infrastructure}. Customer settings describe outcomes, not infrastructure.`
12391
+ path: ["required"],
12392
+ message: `"${name}" is listed as required but is not a declared property.`
12348
12393
  });
12349
12394
  }
12395
+ }
12396
+ const duplicates = (config.required ?? []).filter((name, index, all) => all.indexOf(name) !== index);
12397
+ if (duplicates.length > 0) {
12398
+ ctx.addIssue({
12399
+ code: external_exports.ZodIssueCode.custom,
12400
+ path: ["required"],
12401
+ message: `Duplicate required entries: ${[...new Set(duplicates)].join(", ")}.`
12402
+ });
12403
+ }
12404
+ checkDependentRequired(config, ctx);
12405
+ });
12406
+ function checkDeclaredProperty(name, property, path, ctx, atRoot) {
12407
+ const infrastructure = infrastructureVocabularyViolation(name);
12408
+ if (infrastructure) {
12409
+ ctx.addIssue({
12410
+ code: external_exports.ZodIssueCode.custom,
12411
+ path,
12412
+ message: `${infrastructure}. Customer settings describe outcomes, not infrastructure.`
12413
+ });
12414
+ }
12415
+ if (atRoot) {
12350
12416
  const platformOwned = platformOwnedFieldViolation(name);
12351
12417
  if (platformOwned) {
12352
12418
  ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path, message: `${platformOwned}.` });
12353
12419
  }
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)) {
12420
+ }
12421
+ for (const [label, values] of enumerableValues(property)) {
12422
+ for (const value of values) {
12423
+ const violation = infrastructureValueViolation(value);
12424
+ if (violation) {
12433
12425
  ctx.addIssue({
12434
12426
  code: external_exports.ZodIssueCode.custom,
12435
- path: [...path, "pattern"],
12436
- message: `"${name}" declares a pattern that is not a valid regular expression.`
12427
+ path: [...path, label],
12428
+ message: `${violation}. Customer settings offer outcomes, not infrastructure sizes.`
12437
12429
  });
12438
12430
  }
12439
12431
  }
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)) {
12432
+ }
12433
+ if (property.type === "string") {
12434
+ const secret = property["x-secret"] === true;
12435
+ const credential = credentialNameViolation(name);
12436
+ if (credential && !secret) {
12437
+ ctx.addIssue({
12438
+ code: external_exports.ZodIssueCode.custom,
12439
+ path,
12440
+ // THE REMEDIATION HAS TO BE ONE THE CONTRACT ALLOWS. At the root, the
12441
+ // answer is to declare the two markers. Below it, that answer is
12442
+ // IMPOSSIBLE — the nested shape omits both keys and `.strict()` refuses
12443
+ // them — so telling a publisher to add them sends them to write a
12444
+ // second invalid document, and an agent following the validator loops.
12445
+ // A validator's errors are part of the agent-facing contract.
12446
+ 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.`
12447
+ });
12448
+ }
12449
+ if (secret && property.writeOnly !== true) {
12450
+ ctx.addIssue({
12451
+ code: external_exports.ZodIssueCode.custom,
12452
+ path,
12453
+ message: `"${name}" is marked x-secret but not writeOnly \u2014 a read API could echo the value.`
12454
+ });
12455
+ }
12456
+ if (property.writeOnly === true && !secret) {
12457
+ ctx.addIssue({
12458
+ code: external_exports.ZodIssueCode.custom,
12459
+ path,
12460
+ message: `"${name}" is marked writeOnly but not x-secret \u2014 the value would not be routed to the secret flow.`
12461
+ });
12462
+ }
12463
+ if (secret && property.default !== void 0) {
12464
+ ctx.addIssue({
12465
+ code: external_exports.ZodIssueCode.custom,
12466
+ path: [...path, "default"],
12467
+ message: `"${name}" is a secret and must not declare a default value.`
12468
+ });
12469
+ }
12470
+ if (secret && property.enum !== void 0) {
12471
+ ctx.addIssue({
12472
+ code: external_exports.ZodIssueCode.custom,
12473
+ path: [...path, "enum"],
12474
+ message: `"${name}" is a secret; an enum would enumerate candidate secret values.`
12475
+ });
12476
+ }
12477
+ if (property.enum === void 0 && property.maxLength === void 0) {
12478
+ ctx.addIssue({
12479
+ code: external_exports.ZodIssueCode.custom,
12480
+ path,
12481
+ message: `"${name}" must declare either an enum or a maxLength.`
12482
+ });
12483
+ }
12484
+ if (property.enum !== void 0 && property.default !== void 0 && !property.enum.includes(property.default)) {
12485
+ ctx.addIssue({
12486
+ code: external_exports.ZodIssueCode.custom,
12487
+ path: [...path, "default"],
12488
+ message: `"${name}" declares a default outside its enum.`
12489
+ });
12490
+ }
12491
+ if (property.minLength !== void 0 && property.maxLength !== void 0 && property.minLength > property.maxLength) {
12492
+ ctx.addIssue({
12493
+ code: external_exports.ZodIssueCode.custom,
12494
+ path,
12495
+ message: `"${name}" has minLength greater than maxLength.`
12496
+ });
12497
+ }
12498
+ if (property.default !== void 0 && property.maxLength !== void 0 && property.default.length > property.maxLength) {
12499
+ ctx.addIssue({
12500
+ code: external_exports.ZodIssueCode.custom,
12501
+ path: [...path, "default"],
12502
+ message: `"${name}" declares a default longer than its own maxLength.`
12503
+ });
12504
+ }
12505
+ if (property.pattern !== void 0 && !isSafeRegex(property.pattern)) {
12506
+ ctx.addIssue({
12507
+ code: external_exports.ZodIssueCode.custom,
12508
+ path: [...path, "pattern"],
12509
+ message: `"${name}" declares a pattern that is not a valid regular expression.`
12510
+ });
12511
+ }
12512
+ }
12513
+ if (property.type === "integer" || property.type === "number") {
12514
+ if (property.minimum > property.maximum) {
12515
+ ctx.addIssue({
12516
+ code: external_exports.ZodIssueCode.custom,
12517
+ path,
12518
+ message: `"${name}" has minimum greater than maximum.`
12519
+ });
12520
+ }
12521
+ if (property.default !== void 0 && (property.default < property.minimum || property.default > property.maximum)) {
12522
+ ctx.addIssue({
12523
+ code: external_exports.ZodIssueCode.custom,
12524
+ path: [...path, "default"],
12525
+ message: `"${name}" declares a default outside its minimum/maximum range.`
12526
+ });
12527
+ }
12528
+ if (property.type === "integer" && !Number.isInteger(property.default ?? 0)) {
12529
+ ctx.addIssue({
12530
+ code: external_exports.ZodIssueCode.custom,
12531
+ path: [...path, "default"],
12532
+ message: `"${name}" is an integer but declares a fractional default.`
12533
+ });
12534
+ }
12535
+ }
12536
+ if (property.type === "array") {
12537
+ const allowed = property.items.enum;
12538
+ if (allowed === void 0 && property.items.maxLength === void 0) {
12539
+ ctx.addIssue({
12540
+ code: external_exports.ZodIssueCode.custom,
12541
+ path: [...path, "items"],
12542
+ message: `"${name}" items must declare either an enum or a maxLength.`
12543
+ });
12544
+ }
12545
+ if (property.minItems !== void 0 && property.minItems > property.maxItems) {
12546
+ ctx.addIssue({
12547
+ code: external_exports.ZodIssueCode.custom,
12548
+ path,
12549
+ message: `"${name}" has minItems greater than maxItems.`
12550
+ });
12551
+ }
12552
+ if (property.default !== void 0) {
12553
+ if (property.default.length > property.maxItems) {
12449
12554
  ctx.addIssue({
12450
12555
  code: external_exports.ZodIssueCode.custom,
12451
12556
  path: [...path, "default"],
12452
- message: `"${name}" declares a default outside its minimum/maximum range.`
12557
+ message: `"${name}" declares a default longer than maxItems.`
12453
12558
  });
12454
12559
  }
12455
- if (property.type === "integer" && !Number.isInteger(property.default ?? 0)) {
12560
+ const outside = allowed ? property.default.filter((value) => !allowed.includes(value)) : [];
12561
+ if (outside.length > 0) {
12456
12562
  ctx.addIssue({
12457
12563
  code: external_exports.ZodIssueCode.custom,
12458
12564
  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.`
12565
+ message: `"${name}" declares default values outside its items enum: ${outside.join(", ")}.`
12477
12566
  });
12478
12567
  }
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
12568
  }
12497
12569
  }
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
- });
12570
+ }
12517
12571
  function checkDependentRequired(config, ctx) {
12518
12572
  const clauses = Object.entries(config.dependentRequired ?? {});
12519
12573
  if (clauses.length === 0)
@@ -12623,41 +12677,412 @@ function checkOneSecretPerGroup(config, ctx) {
12623
12677
  queue.push(next);
12624
12678
  }
12625
12679
  }
12626
- const secrets = component.filter(isSecret2).sort();
12627
- if (secrets.length <= 1)
12680
+ const secrets = component.filter(isSecret2).sort();
12681
+ if (secrets.length <= 1)
12682
+ continue;
12683
+ ctx.addIssue({
12684
+ code: external_exports.ZodIssueCode.custom,
12685
+ // The component has no single name, so the issue is reported against the
12686
+ // map rather than against whichever member happened to be visited first.
12687
+ path: ["dependentRequired"],
12688
+ message: `These settings are configured together and include more than one secret (${secrets.join(", ")}). Only one secret can be supplied per change, so a group must ask for at most one \u2014 otherwise completing it on a running resource takes several, in an order the customer has to work out.`
12689
+ });
12690
+ }
12691
+ }
12692
+ var CustomerConfigurationV2ObjectSchema = external_exports.object({
12693
+ type: external_exports.literal("object"),
12694
+ properties: external_exports.record(PropertyNameSchema, RootConfigurationPropertySchema).describe("Customer-editable settings, keyed by camelCase property name."),
12695
+ required: external_exports.array(PropertyNameSchema).max(MAX_CONFIGURATION_PROPERTIES).optional(),
12696
+ /**
12697
+ * ROOT PROPERTIES ONLY, in v2.0.
12698
+ *
12699
+ * The published key promises that "a publisher running an off-the-shelf
12700
+ * validator over that file must get the answer the platform gives", and
12701
+ * JSON Schema 2020-12 `dependentRequired` is scoped to the object that
12702
+ * carries it. A clause naming a nested leaf would be a rule no standard
12703
+ * validator applies, which is the one thing this key may not become. A
12704
+ * nested object may not carry its own clause either — that is expressible
12705
+ * and honest, and it is deliberately left for when something needs it.
12706
+ */
12707
+ dependentRequired: external_exports.record(PropertyNameSchema, external_exports.array(PropertyNameSchema).min(1).max(MAX_CONFIGURATION_PROPERTIES)).optional().describe("Settings that must be supplied together, keyed by the triggering property."),
12708
+ additionalProperties: external_exports.literal(false)
12709
+ }).strict();
12710
+ function countNodes(property) {
12711
+ if (property.type === "object") {
12712
+ const children = Object.keys(property.properties).length;
12713
+ return { leaves: children, nodes: children + 1, instantiated: children };
12714
+ }
12715
+ if (property.type === "array" && property.items.type === "object") {
12716
+ const children = Object.keys(property.items.properties).length;
12717
+ return {
12718
+ leaves: children,
12719
+ nodes: children + 1,
12720
+ instantiated: children * property.maxItems
12721
+ };
12722
+ }
12723
+ return { leaves: 1, nodes: 1, instantiated: 1 };
12724
+ }
12725
+ function childrenOf(property) {
12726
+ if (property.type === "object")
12727
+ return property;
12728
+ if (property.type === "array" && property.items.type === "object")
12729
+ return property.items;
12730
+ return null;
12731
+ }
12732
+ function minimumConfigurationBytes(config) {
12733
+ const rootRequired = new Set(config.required ?? []);
12734
+ let total = 2;
12735
+ for (const [name, property] of Object.entries(config.properties)) {
12736
+ const children = childrenOf(property);
12737
+ if (children === null) {
12738
+ if (!rootRequired.has(name) && declaredLeafDefault(property) === void 0)
12739
+ continue;
12740
+ total += name.length + 3 + minimumLeafBytes(property);
12741
+ continue;
12742
+ }
12743
+ if (!rootRequired.has(name))
12744
+ continue;
12745
+ const inner = minimumGroupBytes(children.properties, children.required ?? []);
12746
+ total += name.length + 3 + (property.type === "array" ? 2 + (property.minItems ?? 0) * inner : inner);
12747
+ }
12748
+ return total;
12749
+ }
12750
+ function minimumGroupBytes(properties, required) {
12751
+ const names = new Set(required);
12752
+ let total = 2;
12753
+ for (const [name, property] of Object.entries(properties)) {
12754
+ if (!names.has(name) && declaredLeafDefault(property) === void 0)
12755
+ continue;
12756
+ total += name.length + 3 + minimumLeafBytes(property);
12757
+ }
12758
+ return total;
12759
+ }
12760
+ function minimumLeafBytes(property) {
12761
+ switch (property.type) {
12762
+ case "string": {
12763
+ const shortestEnum = property.enum?.reduce((a, b) => a.length <= b.length ? a : b);
12764
+ const shortest = shortestEnum?.length ?? Math.max(property.minLength ?? 0, 1);
12765
+ return shortest + 2;
12766
+ }
12767
+ case "integer":
12768
+ case "number":
12769
+ return 1;
12770
+ case "boolean":
12771
+ return 4;
12772
+ case "array":
12773
+ return 2 + (property.minItems ?? 0) * 3;
12774
+ case "object":
12775
+ return 2;
12776
+ }
12777
+ }
12778
+ function declaredLeafDefault(property) {
12779
+ return property.type === "object" ? void 0 : property.default;
12780
+ }
12781
+ var CustomerConfigurationV2Schema = CustomerConfigurationV2ObjectSchema.superRefine((config, ctx) => {
12782
+ const names = Object.keys(config.properties);
12783
+ if (names.length > MAX_CONFIGURATION_PROPERTIES) {
12784
+ ctx.addIssue({
12785
+ code: external_exports.ZodIssueCode.custom,
12786
+ path: ["properties"],
12787
+ message: `A Product may declare at most ${MAX_CONFIGURATION_PROPERTIES} customer settings; found ${names.length}.`
12788
+ });
12789
+ }
12790
+ let leaves = 0;
12791
+ let nodes = 0;
12792
+ let instantiated = 0;
12793
+ for (const name of names) {
12794
+ const property = config.properties[name];
12795
+ const path = ["properties", name];
12796
+ const counted = countNodes(property);
12797
+ leaves += counted.leaves;
12798
+ nodes += counted.nodes;
12799
+ instantiated += counted.instantiated;
12800
+ const children = childrenOf(property);
12801
+ if (children === null) {
12802
+ checkDeclaredProperty(name, property, path, ctx, true);
12803
+ continue;
12804
+ }
12805
+ const infrastructure = infrastructureVocabularyViolation(name);
12806
+ if (infrastructure) {
12807
+ ctx.addIssue({
12808
+ code: external_exports.ZodIssueCode.custom,
12809
+ path,
12810
+ message: `${infrastructure}. Customer settings describe outcomes, not infrastructure.`
12811
+ });
12812
+ }
12813
+ const platformOwned = platformOwnedFieldViolation(name);
12814
+ if (platformOwned) {
12815
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path, message: `${platformOwned}.` });
12816
+ }
12817
+ const childNames = Object.keys(children.properties);
12818
+ if (childNames.length === 0) {
12819
+ ctx.addIssue({
12820
+ code: external_exports.ZodIssueCode.custom,
12821
+ path: [...path, "properties"],
12822
+ message: `"${name}" declares no settings. A group with nothing in it renders as an empty section.`
12823
+ });
12824
+ }
12825
+ if (childNames.length > MAX_CONFIGURATION_PROPERTIES) {
12826
+ ctx.addIssue({
12827
+ code: external_exports.ZodIssueCode.custom,
12828
+ path: [...path, "properties"],
12829
+ message: `"${name}" may declare at most ${MAX_CONFIGURATION_PROPERTIES} settings; found ${childNames.length}.`
12830
+ });
12831
+ }
12832
+ if (property.type === "array" && property.default !== void 0) {
12833
+ ctx.addIssue({
12834
+ code: external_exports.ZodIssueCode.custom,
12835
+ path: [...path, "default"],
12836
+ message: `"${name}" is a list of groups and cannot declare a default. Declare defaults on the settings inside it.`
12837
+ });
12838
+ }
12839
+ if (property.type === "array" && property.minItems !== void 0 && property.minItems > property.maxItems) {
12840
+ ctx.addIssue({
12841
+ code: external_exports.ZodIssueCode.custom,
12842
+ path,
12843
+ message: `"${name}" has minItems greater than maxItems.`
12844
+ });
12845
+ }
12846
+ for (const childName of childNames) {
12847
+ checkDeclaredProperty(childName, children.properties[childName], [...path, "properties", childName], ctx, false);
12848
+ }
12849
+ for (const required of children.required ?? []) {
12850
+ if (!Object.hasOwn(children.properties, required)) {
12851
+ ctx.addIssue({
12852
+ code: external_exports.ZodIssueCode.custom,
12853
+ path: [...path, "required"],
12854
+ message: `"${required}" is listed as required in "${name}" but is not one of its settings.`
12855
+ });
12856
+ }
12857
+ }
12858
+ }
12859
+ if (leaves > MAX_CONFIGURATION_LEAVES) {
12860
+ ctx.addIssue({
12861
+ code: external_exports.ZodIssueCode.custom,
12862
+ path: ["properties"],
12863
+ message: `A Product may declare at most ${MAX_CONFIGURATION_LEAVES} customer settings in total; found ${leaves}.`
12864
+ });
12865
+ }
12866
+ if (nodes > MAX_CONFIGURATION_NODES) {
12867
+ ctx.addIssue({
12868
+ code: external_exports.ZodIssueCode.custom,
12869
+ path: ["properties"],
12870
+ message: `A Product may declare at most ${MAX_CONFIGURATION_NODES} configuration nodes; found ${nodes}.`
12871
+ });
12872
+ }
12873
+ if (instantiated > MAX_CONFIGURATION_VALUE_LEAVES) {
12874
+ ctx.addIssue({
12875
+ code: external_exports.ZodIssueCode.custom,
12876
+ path: ["properties"],
12877
+ message: `This Product's settings could expand to ${instantiated} values \u2014 a list of groups instantiates its settings once per item. At most ${MAX_CONFIGURATION_VALUE_LEAVES} are allowed; reduce maxItems, or the number of settings inside the list.`
12878
+ });
12879
+ }
12880
+ for (const name of config.required ?? []) {
12881
+ if (!Object.hasOwn(config.properties, name)) {
12882
+ ctx.addIssue({
12883
+ code: external_exports.ZodIssueCode.custom,
12884
+ path: ["required"],
12885
+ message: `"${name}" is listed as required but is not a declared property.`
12886
+ });
12887
+ }
12888
+ }
12889
+ const floor = minimumConfigurationBytes(config);
12890
+ if (floor > MAX_CONFIGURATION_VALUE_BYTES) {
12891
+ ctx.addIssue({
12892
+ code: external_exports.ZodIssueCode.custom,
12893
+ path: ["properties"],
12894
+ 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.`
12895
+ });
12896
+ }
12897
+ checkDependentRequired(config, ctx);
12898
+ checkOneSecretPerGroup(config, ctx);
12899
+ });
12900
+ function enumerableValues(property) {
12901
+ const out = [];
12902
+ if (property.type === "string") {
12903
+ if (property.enum)
12904
+ out.push(["enum", property.enum]);
12905
+ if (property.default !== void 0)
12906
+ out.push(["default", [property.default]]);
12907
+ }
12908
+ if (property.type === "array") {
12909
+ if (property.items.enum)
12910
+ out.push(["items", property.items.enum]);
12911
+ if (property.default)
12912
+ out.push(["default", property.default]);
12913
+ }
12914
+ return out;
12915
+ }
12916
+ function isSafeRegex(pattern) {
12917
+ try {
12918
+ new RegExp(pattern);
12919
+ return true;
12920
+ } catch {
12921
+ return false;
12922
+ }
12923
+ }
12924
+ function isSecretProperty(property) {
12925
+ return property.type === "string" && property["x-secret"] === true;
12926
+ }
12927
+
12928
+ // ../product-configuration/dist/configuration-path.js
12929
+ var MAX_CONFIGURATION_PATH_SEGMENTS = MAX_CONFIGURATION_DEPTH + 1;
12930
+ function isIndexSegment(raw) {
12931
+ return /^\d+$/.test(raw) && String(Number(raw)) === raw;
12932
+ }
12933
+ function isPathSegment(segment) {
12934
+ if (typeof segment === "number")
12935
+ return Number.isInteger(segment) && segment >= 0;
12936
+ return CONFIGURATION_PROPERTY_NAME_REGEX.test(segment) || isIndexSegment(segment);
12937
+ }
12938
+ function configurationPath(...segments) {
12939
+ if (segments.length === 0 || segments.length > MAX_CONFIGURATION_PATH_SEGMENTS)
12940
+ return null;
12941
+ if (!segments.every(isPathSegment))
12942
+ return null;
12943
+ if (typeof segments[0] === "number" || isIndexSegment(String(segments[0])))
12944
+ return null;
12945
+ if (segments.length === 1)
12946
+ return String(segments[0]);
12947
+ return `/${segments.map(String).join("/")}`;
12948
+ }
12949
+ function parseConfigurationPath(raw) {
12950
+ if (typeof raw !== "string" || raw.length === 0)
12951
+ return null;
12952
+ if (!raw.startsWith("/"))
12953
+ return configurationPath(raw);
12954
+ const segments = raw.slice(1).split("/");
12955
+ if (segments.length < 2)
12956
+ return null;
12957
+ return configurationPath(...segments);
12958
+ }
12959
+ function pathSegments(path) {
12960
+ const raw = path.startsWith("/") ? path.slice(1).split("/") : [path];
12961
+ return raw.map((segment) => isIndexSegment(segment) ? Number(segment) : segment);
12962
+ }
12963
+ function declarationPathOf(path) {
12964
+ const segments = pathSegments(path).filter((segment) => typeof segment !== "number");
12965
+ return configurationPath(...segments) ?? path;
12966
+ }
12967
+ function printableConfigurationPath(path) {
12968
+ return pathSegments(path).join(".");
12969
+ }
12970
+
12971
+ // ../product-configuration/dist/canonical-json.js
12972
+ function canonicalise(value, path) {
12973
+ if (value === null)
12974
+ return "null";
12975
+ if (typeof value === "string")
12976
+ return JSON.stringify(value);
12977
+ if (typeof value === "boolean")
12978
+ return value ? "true" : "false";
12979
+ if (typeof value === "number") {
12980
+ if (!Number.isFinite(value)) {
12981
+ throw new Error(`canonicalJson: non-finite number at ${path}`);
12982
+ }
12983
+ return JSON.stringify(value === 0 ? 0 : value);
12984
+ }
12985
+ if (typeof value !== "object") {
12986
+ throw new Error(`canonicalJson: ${typeof value} is not JSON at ${path}`);
12987
+ }
12988
+ if (Array.isArray(value)) {
12989
+ return `[${value.map((item, i) => canonicalise(item, `${path}[${i}]`)).join(",")}]`;
12990
+ }
12991
+ const entries = Object.entries(value);
12992
+ for (const [key, item] of entries) {
12993
+ if (item === void 0) {
12994
+ throw new Error(`canonicalJson: undefined property at ${path}.${key}`);
12995
+ }
12996
+ }
12997
+ const sorted = entries.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
12998
+ const body = sorted.map(([key, item]) => `${JSON.stringify(key)}:${canonicalise(item, `${path}.${key}`)}`).join(",");
12999
+ return `{${body}}`;
13000
+ }
13001
+ function canonicalJson(value) {
13002
+ return canonicalise(value, "$");
13003
+ }
13004
+
13005
+ // ../product-configuration/dist/configuration-tree.js
13006
+ function containerChildren(property) {
13007
+ if (property.type === "object" && property.properties !== void 0) {
13008
+ return {
13009
+ kind: "object",
13010
+ properties: property.properties,
13011
+ required: property.required ?? []
13012
+ };
13013
+ }
13014
+ if (property.type === "array" && property.items !== void 0) {
13015
+ const items = property.items;
13016
+ if (items.type !== "object" || items.properties === void 0)
13017
+ return null;
13018
+ return {
13019
+ kind: "array",
13020
+ properties: items.properties,
13021
+ required: items.required ?? []
13022
+ };
13023
+ }
13024
+ return null;
13025
+ }
13026
+ function isConfigurationContainer(property) {
13027
+ return containerChildren(property) !== null;
13028
+ }
13029
+ function configurationLeaves(schema) {
13030
+ const out = [];
13031
+ for (const [name, property] of Object.entries(schema.properties)) {
13032
+ const children = containerChildren(property);
13033
+ if (children === null) {
13034
+ const path = configurationPath(name);
13035
+ if (path !== null) {
13036
+ out.push({ path, name, property, container: null });
13037
+ }
13038
+ continue;
13039
+ }
13040
+ const containerPath = configurationPath(name);
13041
+ if (containerPath === null)
12628
13042
  continue;
12629
- ctx.addIssue({
12630
- code: external_exports.ZodIssueCode.custom,
12631
- // The component has no single name, so the issue is reported against the
12632
- // map rather than against whichever member happened to be visited first.
12633
- path: ["dependentRequired"],
12634
- message: `These settings are configured together and include more than one secret (${secrets.join(", ")}). Only one secret can be supplied per change, so a group must ask for at most one \u2014 otherwise completing it on a running resource takes several, in an order the customer has to work out.`
12635
- });
13043
+ const container = {
13044
+ path: containerPath,
13045
+ name,
13046
+ kind: children.kind,
13047
+ property,
13048
+ required: children.required
13049
+ };
13050
+ for (const [childName, child] of Object.entries(children.properties)) {
13051
+ const path = configurationPath(name, childName);
13052
+ if (path === null)
13053
+ continue;
13054
+ out.push({ path, name: childName, property: child, container });
13055
+ }
12636
13056
  }
13057
+ return out;
12637
13058
  }
12638
- function enumerableValues(property) {
13059
+ function configurationContainers(schema) {
12639
13060
  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]]);
12645
- }
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]);
13061
+ for (const [name, property] of Object.entries(schema.properties)) {
13062
+ const children = containerChildren(property);
13063
+ if (children === null)
13064
+ continue;
13065
+ const path = configurationPath(name);
13066
+ if (path === null)
13067
+ continue;
13068
+ out.push({
13069
+ path,
13070
+ name,
13071
+ kind: children.kind,
13072
+ property,
13073
+ required: children.required
13074
+ });
12651
13075
  }
12652
13076
  return out;
12653
13077
  }
12654
- function isSafeRegex(pattern) {
12655
- try {
12656
- new RegExp(pattern);
13078
+ function leafAt(schema, path) {
13079
+ const declaration = declarationPathOf(path);
13080
+ return configurationLeaves(schema).find((leaf) => leaf.path === declaration) ?? null;
13081
+ }
13082
+ function declaresPath(schema, path) {
13083
+ if (leafAt(schema, path) !== null)
12657
13084
  return true;
12658
- } catch {
12659
- return false;
12660
- }
13085
+ return configurationContainers(schema).some((container) => container.path === declarationPathOf(path));
12661
13086
  }
12662
13087
 
12663
13088
  // ../product-configuration/dist/customer-configuration-input.js
@@ -12666,11 +13091,24 @@ var MAX_REPORTED_UNDECLARED_KEYS = 10;
12666
13091
  function isPlainObject(value) {
12667
13092
  return typeof value === "object" && value !== null && !Array.isArray(value);
12668
13093
  }
12669
- function isSecretProperty(property) {
12670
- return property.type === "string" && property["x-secret"] === true;
13094
+ function report(walk, field, message) {
13095
+ if (walk.issues.length >= MAX_CONFIGURATION_ISSUES) {
13096
+ walk.suppressed += 1;
13097
+ return;
13098
+ }
13099
+ walk.issues.push({ field, message });
13100
+ }
13101
+ function drain(walk, collected) {
13102
+ for (const issue of collected)
13103
+ report(walk, issue.field, issue.message);
12671
13104
  }
12672
13105
  function validateSubmittedConfiguration(schema, submitted, presence = {}) {
12673
- const issues = [];
13106
+ const walk = {
13107
+ issues: [],
13108
+ suppressed: 0,
13109
+ leaves: 0,
13110
+ referenced: new Set(presence.referenced ?? [])
13111
+ };
12674
13112
  const supplied = /* @__PURE__ */ new Set();
12675
13113
  if (submitted !== void 0 && submitted !== null && !isPlainObject(submitted)) {
12676
13114
  return {
@@ -12680,64 +13118,195 @@ function validateSubmittedConfiguration(schema, submitted, presence = {}) {
12680
13118
  };
12681
13119
  }
12682
13120
  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({
13121
+ const secrets = {};
13122
+ const configuration = validateObject({ properties: schema.properties, required: schema.required }, input, null, walk, { secrets, supplied });
13123
+ drain(walk, crossFieldIssues(schema, supplied, presence));
13124
+ if (walk.suppressed > 0) {
13125
+ walk.issues.push({
12692
13126
  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.`
13127
+ message: `${walk.suppressed} further problems were found and are not listed.`
12696
13128
  });
12697
13129
  }
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);
13130
+ if (declaresContainer(schema)) {
13131
+ const size = utf8ByteLength(JSON.stringify(configuration));
13132
+ if (size > MAX_CONFIGURATION_VALUE_BYTES) {
13133
+ report(walk, "", `This configuration is ${size} bytes once defaults are applied, and the limit is ${MAX_CONFIGURATION_VALUE_BYTES}. Remove some items.`);
13134
+ }
13135
+ }
13136
+ if (walk.issues.length > 0)
13137
+ return { ok: false, issues: walk.issues, partial: configuration };
13138
+ return { ok: true, configuration, secrets };
13139
+ }
13140
+ function utf8ByteLength(value) {
13141
+ return new TextEncoder().encode(value).length;
13142
+ }
13143
+ function declaresContainer(schema) {
13144
+ return Object.values(schema.properties).some((property) => isConfigurationContainer(property));
13145
+ }
13146
+ function containsReferenced(walk, container) {
13147
+ if (walk.referenced.size === 0)
13148
+ return false;
13149
+ const parsedOuter = parseConfigurationPath(container);
13150
+ if (parsedOuter === null)
13151
+ return false;
13152
+ const outer = pathSegments(parsedOuter);
13153
+ for (const candidate of walk.referenced) {
13154
+ const parsed = parseConfigurationPath(candidate);
13155
+ if (parsed === null)
13156
+ continue;
13157
+ const inner = pathSegments(parsed);
13158
+ if (inner.length <= outer.length)
13159
+ continue;
13160
+ if (outer.every((segment, index) => inner[index] === segment))
13161
+ return true;
13162
+ }
13163
+ return false;
13164
+ }
13165
+ function validateObject(declaration, input, container, walk, root) {
13166
+ const required = new Set(declaration.required ?? []);
13167
+ const out = {};
13168
+ const fieldFor = (name) => {
13169
+ const path = container === null ? configurationPath(name) : configurationPath(...pathSegments(container), name);
13170
+ return path ?? "";
13171
+ };
13172
+ const undeclared = Object.keys(input).filter((name) => !Object.hasOwn(declaration.properties, name));
13173
+ for (const name of undeclared.slice(0, MAX_REPORTED_UNDECLARED_KEYS)) {
13174
+ report(walk, fieldFor(name), "This Product does not declare a setting with that name.");
13175
+ }
13176
+ if (undeclared.length > MAX_REPORTED_UNDECLARED_KEYS) {
13177
+ report(walk, container === null ? "" : container, `${undeclared.length - MAX_REPORTED_UNDECLARED_KEYS} further settings are not declared by this Product.`);
13178
+ }
13179
+ for (const [name, property] of Object.entries(declaration.properties)) {
13180
+ const field = fieldFor(name);
13181
+ const children = containerDeclaration(property);
13182
+ if (children !== null) {
13183
+ const value = input[name];
13184
+ const filled = validateContainer(name, property, children, value, field, walk);
13185
+ if (filled !== void 0)
13186
+ out[name] = filled;
13187
+ else if (required.has(name) && !containsReferenced(walk, field))
13188
+ report(walk, field, "This setting is required.");
13189
+ continue;
13190
+ }
13191
+ const leaf = property;
13192
+ if (walk.referenced.has(field)) {
13193
+ const sent = input[name];
13194
+ if (Object.hasOwn(input, name) && sent !== void 0) {
13195
+ report(walk, field, "This setting is answered by another Resource, so it cannot also be set here.");
13196
+ }
13197
+ continue;
13198
+ }
13199
+ const raw = input[name];
13200
+ const blank = typeof raw === "string" && raw.trim().length === 0;
13201
+ const present = Object.hasOwn(input, name) && raw !== void 0 && !blank;
13202
+ const secret = root !== null && isSecretProperty(leaf);
13203
+ if (present && root !== null && container === null)
13204
+ root.supplied.add(name);
12707
13205
  if (!present) {
12708
- const fallback = secret ? void 0 : defaultOf(property);
13206
+ const fallback = secret ? void 0 : defaultOf(leaf);
12709
13207
  if (fallback !== void 0) {
12710
- configuration[name] = fallback;
13208
+ walk.leaves += 1;
13209
+ out[name] = fallback;
12711
13210
  } else if (required.has(name)) {
12712
- issues.push({ field: name, message: "This setting is required." });
13211
+ report(walk, field, "This setting is required.");
12713
13212
  }
12714
13213
  continue;
12715
13214
  }
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)
13215
+ walk.leaves += 1;
13216
+ const collected = [];
13217
+ const checked = checkProperty(field, leaf, raw, collected);
13218
+ if (collected.length > 0 || checked === void 0) {
13219
+ drain(walk, collected);
12720
13220
  continue;
13221
+ }
12721
13222
  if (secret) {
12722
13223
  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." });
12726
- }
13224
+ root.secrets[name] = checked;
13225
+ else if (required.has(name))
13226
+ report(walk, field, "This setting is required.");
12727
13227
  continue;
12728
13228
  }
12729
- configuration[name] = checked;
13229
+ out[name] = checked;
12730
13230
  }
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 };
13231
+ return out;
13232
+ }
13233
+ function containerDeclaration(property) {
13234
+ if (property.type === "object" && property.properties !== void 0) {
13235
+ return {
13236
+ kind: "object",
13237
+ properties: property.properties,
13238
+ required: property.required,
13239
+ maxItems: 1
13240
+ };
13241
+ }
13242
+ if (property.type === "array" && property.items !== void 0) {
13243
+ const items = property.items;
13244
+ if (items.type !== "object" || items.properties === void 0)
13245
+ return null;
13246
+ return {
13247
+ kind: "array",
13248
+ properties: items.properties,
13249
+ required: items.required,
13250
+ maxItems: property.maxItems ?? 0,
13251
+ minItems: property.minItems,
13252
+ uniqueItems: property.uniqueItems === true
13253
+ };
13254
+ }
13255
+ return null;
13256
+ }
13257
+ function validateContainer(name, property, children, value, field, walk) {
13258
+ const path = parseConfigurationPath(field);
13259
+ if (children.kind === "object") {
13260
+ if (value === void 0)
13261
+ return void 0;
13262
+ if (!isPlainObject(value)) {
13263
+ report(walk, field, "Expected a group of settings.");
13264
+ return void 0;
13265
+ }
13266
+ const filled = validateObject(children, value, path, walk, null);
13267
+ return Object.keys(filled).length === 0 ? void 0 : filled;
13268
+ }
13269
+ if (value === void 0)
13270
+ return void 0;
13271
+ if (!Array.isArray(value)) {
13272
+ report(walk, field, "Expected a list.");
13273
+ return void 0;
13274
+ }
13275
+ if (value.length > children.maxItems) {
13276
+ report(walk, field, `At most ${children.maxItems} items are allowed.`);
13277
+ return void 0;
13278
+ }
13279
+ if (children.minItems !== void 0 && value.length < children.minItems) {
13280
+ report(walk, field, `At least ${children.minItems} items are required.`);
13281
+ return void 0;
13282
+ }
13283
+ const out = [];
13284
+ for (const [index, element] of value.entries()) {
13285
+ if (walk.leaves >= MAX_CONFIGURATION_VALUE_LEAVES) {
13286
+ report(walk, field, "This configuration has too many values.");
13287
+ break;
13288
+ }
13289
+ if (!isPlainObject(element)) {
13290
+ report(walk, `${field}`, `Item ${index + 1} is not a group of settings.`);
13291
+ continue;
13292
+ }
13293
+ const elementPath = path === null ? null : configurationPath(...pathSegments(path), index);
13294
+ out.push(validateObject(children, element, elementPath, walk, null));
13295
+ }
13296
+ if (children.uniqueItems === true) {
13297
+ const seen = new Set(out.map((entry) => canonicalJson(entry)));
13298
+ if (seen.size !== out.length) {
13299
+ report(walk, field, "Items must be unique.");
13300
+ return void 0;
13301
+ }
13302
+ }
13303
+ return out;
12735
13304
  }
12736
13305
  function crossFieldIssues(schema, supplied, presence) {
12737
13306
  const clauses = Object.entries(schema.dependentRequired ?? {});
12738
13307
  if (clauses.length === 0)
12739
13308
  return [];
12740
- const alsoPresent = new Set(presence.present ?? []);
13309
+ const alsoPresent = /* @__PURE__ */ new Set([...presence.present ?? [], ...presence.referenced ?? []]);
12741
13310
  const unknown = new Set(presence.unknown ?? []);
12742
13311
  const known = (name) => !unknown.has(name);
12743
13312
  const held = (name) => supplied.has(name) || alsoPresent.has(name);
@@ -12905,11 +13474,11 @@ var RESOURCE_KEY_RULE = "A resource key is 3\u201363 characters of lowercase let
12905
13474
  var ResourceKeySchema = external_exports.string().regex(RESOURCE_KEY_PATTERN, RESOURCE_KEY_RULE).describe(RESOURCE_KEY_RULE);
12906
13475
 
12907
13476
  // ../product-configuration/dist/product-instance-example.js
12908
- function resolveExampleChoice(options, selected, declaredDefault) {
13477
+ function resolveExampleChoice(options, selected, declaredDefault2) {
12909
13478
  if (selected !== void 0)
12910
13479
  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 };
13480
+ if (declaredDefault2 !== void 0 && options.includes(declaredDefault2))
13481
+ return { value: declaredDefault2, issue: null };
12913
13482
  if (options.length === 1)
12914
13483
  return { value: options[0], issue: null };
12915
13484
  return { value: null, issue: options.length === 0 ? "unavailable" : "required" };
@@ -12937,17 +13506,84 @@ function seedConfiguration(configuration) {
12937
13506
  requiredSecrets.push(name);
12938
13507
  continue;
12939
13508
  }
13509
+ const children = containerChildren2(declaration);
13510
+ if (children !== null) {
13511
+ if (!required.has(name))
13512
+ continue;
13513
+ if (children.kind === "object") {
13514
+ values[name] = seedGroup(children, name, open);
13515
+ continue;
13516
+ }
13517
+ const count = children.minItems ?? 0;
13518
+ values[name] = Array.from({ length: count }, () => seedGroup(children, name, open));
13519
+ if (count > 1)
13520
+ dedupeOpen(open);
13521
+ continue;
13522
+ }
12940
13523
  if (declaration.default !== void 0) {
12941
13524
  values[name] = declaration.default;
12942
13525
  continue;
12943
13526
  }
12944
13527
  if (required.has(name)) {
12945
13528
  values[name] = "";
12946
- open.push({ name, type: declaration.type ?? null });
13529
+ open.push({ name, label: name, type: declaration.type ?? null });
12947
13530
  }
12948
13531
  }
12949
13532
  return { values, open, requiredSecrets, groups };
12950
13533
  }
13534
+ function containerChildren2(declaration) {
13535
+ const node = declaration;
13536
+ if (node.type === "object" && node.properties !== void 0) {
13537
+ return {
13538
+ kind: "object",
13539
+ properties: node.properties,
13540
+ required: node.required ?? []
13541
+ };
13542
+ }
13543
+ if (node.type === "array" && node.items !== void 0) {
13544
+ const items = node.items;
13545
+ if (items.type !== "object" || items.properties === void 0)
13546
+ return null;
13547
+ return {
13548
+ kind: "array",
13549
+ properties: items.properties,
13550
+ required: items.required ?? [],
13551
+ ...typeof node.minItems === "number" ? { minItems: node.minItems } : {}
13552
+ };
13553
+ }
13554
+ return null;
13555
+ }
13556
+ function dedupeOpen(open) {
13557
+ const seen = /* @__PURE__ */ new Set();
13558
+ const unique = open.filter((entry) => {
13559
+ if (seen.has(entry.name))
13560
+ return false;
13561
+ seen.add(entry.name);
13562
+ return true;
13563
+ });
13564
+ open.length = 0;
13565
+ open.push(...unique);
13566
+ }
13567
+ function seedGroup(children, container, open) {
13568
+ const required = new Set(children.required);
13569
+ const values = {};
13570
+ for (const [name, declaration] of Object.entries(children.properties)) {
13571
+ if (declaration.default !== void 0) {
13572
+ values[name] = declaration.default;
13573
+ continue;
13574
+ }
13575
+ if (required.has(name)) {
13576
+ values[name] = "";
13577
+ const path = configurationPath(container, name);
13578
+ open.push({
13579
+ name: path ?? name,
13580
+ label: path === null ? name : printableConfigurationPath(path),
13581
+ type: declaration.type ?? null
13582
+ });
13583
+ }
13584
+ }
13585
+ return values;
13586
+ }
12951
13587
  function productInstanceExample(input) {
12952
13588
  return {
12953
13589
  apiVersion: RESOURCE_API_VERSION,
@@ -13059,13 +13695,32 @@ var OperationStatusSchema = external_exports.enum([
13059
13695
 
13060
13696
  // ../product-contracts/dist/api-version.js
13061
13697
  var PRODUCT_API_VERSION = "products.vardeflyt.no/v1";
13698
+ var PRODUCT_API_VERSION_V2 = "products.vardeflyt.no/v2";
13699
+ var PARSEABLE_PRODUCT_API_VERSIONS = /* @__PURE__ */ new Set([
13700
+ PRODUCT_API_VERSION,
13701
+ PRODUCT_API_VERSION_V2
13702
+ ]);
13703
+ function supportedList() {
13704
+ return [...PARSEABLE_PRODUCT_API_VERSIONS].map((value) => `"${value}"`).join(" or ");
13705
+ }
13706
+ function unsupportedProductApiVersionMessage() {
13707
+ return `apiVersion must be ${supportedList()}. An unrecognized contract format is refused rather than ignored.`;
13708
+ }
13062
13709
  var PRODUCT_DEFINITION_KIND = "ProductDefinition";
13063
13710
  var DEPLOYMENT_BLUEPRINT_KIND = "DeploymentBlueprint";
13064
13711
  var ProductApiVersionSchema = external_exports.literal(PRODUCT_API_VERSION, {
13065
13712
  errorMap: () => ({
13066
- message: `apiVersion must be "${PRODUCT_API_VERSION}". An unrecognized contract format is refused rather than ignored.`
13713
+ message: `apiVersion must be ${supportedList()}. An unrecognized contract format is refused rather than ignored.`
13714
+ })
13715
+ });
13716
+ var ProductApiVersionV2Schema = external_exports.literal(PRODUCT_API_VERSION_V2, {
13717
+ errorMap: () => ({
13718
+ message: `apiVersion must be ${supportedList()}. An unrecognized contract format is refused rather than ignored.`
13067
13719
  })
13068
13720
  });
13721
+ var ProductApiVersionAnySchema = external_exports.union([ProductApiVersionSchema, ProductApiVersionV2Schema], {
13722
+ errorMap: () => ({ message: unsupportedProductApiVersionMessage() })
13723
+ }).describe("The Product contract format this document is written against.");
13069
13724
  var ResourceApiVersionSchema = external_exports.literal(RESOURCE_API_VERSION, {
13070
13725
  errorMap: () => ({
13071
13726
  message: `apiVersion must be "${RESOURCE_API_VERSION}". An unrecognized contract format is refused rather than ignored.`
@@ -13442,6 +14097,18 @@ var ProductConditionSchema = external_exports.object({
13442
14097
  */
13443
14098
  description: external_exports.string().min(10).max(300)
13444
14099
  }).strict();
14100
+ var RequiredWhenSchema = external_exports.object({
14101
+ /** A declared, non-secret customer-configuration property. */
14102
+ property: external_exports.string().regex(CONFIGURATION_PROPERTY_NAME_REGEX, "Must name a declared customer-configuration property."),
14103
+ /** The values that make this dependency required. Non-empty. */
14104
+ anyOf: external_exports.array(external_exports.string().min(1).max(80)).min(1).max(40)
14105
+ }).strict();
14106
+ var RequiredWhenV2Schema = external_exports.object({
14107
+ path: external_exports.string().refine((raw) => parseConfigurationPath(raw) !== null, {
14108
+ message: 'A configuration path is a root property name, or an RFC 6901 pointer such as "/assistant/model".'
14109
+ }),
14110
+ anyOf: external_exports.array(external_exports.string().min(1).max(80)).min(1).max(40)
14111
+ }).strict();
13445
14112
  var ProductDependencySchema = external_exports.object({
13446
14113
  /**
13447
14114
  * OPTIONAL ONLY WHEN `interface` IS PRESENT, enforced below.
@@ -13556,13 +14223,11 @@ var ProductDependencySchema = external_exports.object({
13556
14223
  * `.optional()`, never `.default()` — see the Blueprint's banner on
13557
14224
  * `runtime`. Every dependency published to date carries none.
13558
14225
  */
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()
14226
+ requiredWhen: RequiredWhenSchema.optional()
13565
14227
  }).strict();
14228
+ var ProductDependencyV2Schema = ProductDependencySchema.extend({
14229
+ requiredWhen: RequiredWhenV2Schema.optional()
14230
+ });
13566
14231
  var ServiceInterfaceProvideSchema = external_exports.object({
13567
14232
  key: ServiceBindingKeySchema,
13568
14233
  interfaceId: ServiceInterfaceIdSchema,
@@ -13699,7 +14364,14 @@ var ProductDefinitionObjectSchema = external_exports.object({
13699
14364
  */
13700
14365
  blueprint: external_exports.object({ version: ContractVersionSchema }).strict()
13701
14366
  }).strict();
13702
- var ProductDefinitionSchema = ProductDefinitionObjectSchema.superRefine((definition, ctx) => {
14367
+ var ProductDefinitionSchema = ProductDefinitionObjectSchema.superRefine(checkProductDefinition);
14368
+ var ProductDefinitionV2ObjectSchema = ProductDefinitionObjectSchema.extend({
14369
+ apiVersion: ProductApiVersionV2Schema,
14370
+ configuration: CustomerConfigurationV2Schema,
14371
+ dependencies: external_exports.array(ProductDependencyV2Schema).max(8)
14372
+ });
14373
+ var ProductDefinitionV2Schema = ProductDefinitionV2ObjectSchema.superRefine(checkProductDefinition);
14374
+ function checkProductDefinition(definition, ctx) {
13703
14375
  const profileIds = definition.profiles.map((profile) => profile.id);
13704
14376
  const duplicateProfiles = profileIds.filter((id, i) => profileIds.indexOf(id) !== i);
13705
14377
  if (duplicateProfiles.length > 0) {
@@ -13821,42 +14493,55 @@ var ProductDefinitionSchema = ProductDefinitionObjectSchema.superRefine((definit
13821
14493
  message: "Repeated values in `anyOf`."
13822
14494
  });
13823
14495
  }
13824
- const property = definition.configuration.properties[condition.property];
13825
- if (property === void 0) {
14496
+ const named = "path" in condition ? condition.path : condition.property;
14497
+ const member = "path" in condition ? "path" : "property";
14498
+ const parsed = parseConfigurationPath(named);
14499
+ const leaf = parsed === null ? null : leafAt(definition.configuration, parsed);
14500
+ if (leaf === null) {
14501
+ const isGroup = parsed !== null && declaresPath(definition.configuration, parsed);
14502
+ ctx.addIssue({
14503
+ code: external_exports.ZodIssueCode.custom,
14504
+ path: [...at, member],
14505
+ 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.`
14506
+ });
14507
+ return;
14508
+ }
14509
+ if (leaf.container?.kind === "array") {
13826
14510
  ctx.addIssue({
13827
14511
  code: external_exports.ZodIssueCode.custom,
13828
- path: [...at, "property"],
13829
- message: `"${condition.property}" is not a declared customer configuration property.`
14512
+ path: [...at, member],
14513
+ 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
14514
  });
13831
14515
  return;
13832
14516
  }
14517
+ const property = leaf.property;
13833
14518
  if (property.type !== "string") {
13834
14519
  ctx.addIssue({
13835
14520
  code: external_exports.ZodIssueCode.custom,
13836
- path: [...at, "property"],
13837
- message: `"${condition.property}" is a ${property.type} setting; a condition compares string values.`
14521
+ path: [...at, member],
14522
+ message: `"${named}" is a ${property.type} setting; a condition compares string values.`
13838
14523
  });
13839
14524
  return;
13840
14525
  }
13841
- if (property["x-secret"] === true) {
14526
+ if (isSecretProperty(property)) {
13842
14527
  ctx.addIssue({
13843
14528
  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.`
14529
+ path: [...at, member],
14530
+ message: `"${named}" is a secret. The platform never reads a secret's value, so a condition on one could never be evaluated.`
13846
14531
  });
13847
14532
  return;
13848
14533
  }
13849
14534
  const single = withoutCrossFieldRules({
13850
14535
  ...definition.configuration,
13851
- properties: { [condition.property]: property },
13852
- required: [condition.property]
14536
+ properties: { [leaf.name]: property },
14537
+ required: [leaf.name]
13853
14538
  });
13854
- const outside = condition.anyOf.filter((value) => !validateSubmittedConfiguration(single, { [condition.property]: value }).ok);
14539
+ const outside = condition.anyOf.filter((value) => !validateSubmittedConfiguration(single, { [leaf.name]: value }).ok);
13855
14540
  if (outside.length > 0) {
13856
14541
  ctx.addIssue({
13857
14542
  code: external_exports.ZodIssueCode.custom,
13858
14543
  path: [...at, "anyOf"],
13859
- message: `"${condition.property}" never accepts ${outside.map((v) => `"${v}"`).join(", ")}, so this condition could never make the dependency required.`
14544
+ message: `"${named}" never accepts ${outside.map((v) => `"${v}"`).join(", ")}, so this condition could never make the dependency required.`
13860
14545
  });
13861
14546
  }
13862
14547
  });
@@ -13970,7 +14655,7 @@ var ProductDefinitionSchema = ProductDefinitionObjectSchema.superRefine((definit
13970
14655
  message: 'dataResidency "global" contradicts externalProcessing false \u2014 data cannot be global without leaving the tenant boundary.'
13971
14656
  });
13972
14657
  }
13973
- });
14658
+ }
13974
14659
 
13975
14660
  // ../product-contracts/dist/announcements.js
13976
14661
  var ANNOUNCED_PRODUCTS = Object.freeze([
@@ -14160,108 +14845,166 @@ var ComponentIngressSchema = external_exports.discriminatedUnion("exposure", [
14160
14845
  authentication: external_exports.enum(["signed-request", "mtls"])
14161
14846
  }).strict()
14162
14847
  ]);
14848
+ var LiteralEnvBindingSchema = external_exports.object({
14849
+ source: external_exports.literal("literal"),
14850
+ name: EnvVarNameSchema,
14851
+ value: external_exports.string().max(512)
14852
+ }).strict();
14853
+ var InstanceEnvBindingSchema = external_exports.object({
14854
+ source: external_exports.literal("instance"),
14855
+ name: EnvVarNameSchema,
14856
+ /** Closed set: the Control Plane knows these without Product knowledge. */
14857
+ field: external_exports.enum([
14858
+ "tenantKey",
14859
+ "tenantId",
14860
+ "projectId",
14861
+ "productInstanceId",
14862
+ "instanceKey",
14863
+ "productId",
14864
+ "productVersion",
14865
+ "profile",
14866
+ "region",
14867
+ "environment"
14868
+ ])
14869
+ }).strict();
14870
+ var PlatformEnvBindingSchema = external_exports.object({
14871
+ source: external_exports.literal("platform"),
14872
+ name: EnvVarNameSchema,
14873
+ key: external_exports.enum(["controlPlaneUrl", "observabilityEndpoint", "logLevel", "serviceVersion"])
14874
+ }).strict();
14875
+ var ComponentEndpointEnvBindingSchema = external_exports.object({
14876
+ source: external_exports.literal("component-endpoint"),
14877
+ name: EnvVarNameSchema,
14878
+ /** Resolved to the sibling component's private URL at reconcile time. */
14879
+ componentId: ComponentIdSchema
14880
+ }).strict();
14881
+ var ComponentPublicUrlEnvBindingSchema = external_exports.object({
14882
+ /**
14883
+ * The CUSTOMER-FACING address of a PUBLIC component — the URL a browser
14884
+ * opens, not the in-network one a sibling calls.
14885
+ *
14886
+ * WHY THE PLATFORM HAS TO SUPPLY IT. The address is assigned during
14887
+ * deployment, so nobody knows it earlier: not the publisher, who writes
14888
+ * the contract months before, and not the customer, who would otherwise
14889
+ * be asked to type an address that does not exist yet. A console that
14890
+ * needs its own address — to build an OAuth redirect URI, to put an
14891
+ * absolute link in an email — had no way to learn it, and asking the
14892
+ * customer produced exactly the failure you would expect: an empty
14893
+ * setting and a console nobody can sign in to.
14894
+ *
14895
+ * It is deliberately NOT derivable from the request. A server behind a
14896
+ * proxy infers its bind address, which looks right in development and is
14897
+ * wrong in production; a forwarded Host header is attacker-controlled.
14898
+ * The platform knows the answer, so the platform states it.
14899
+ *
14900
+ * `componentId` may name this component or another, but it must be a
14901
+ * PUBLIC one: `validateProductContracts` refuses a private target, the
14902
+ * same rule `outputs` already carries, because a private endpoint is not
14903
+ * an address to hand out.
14904
+ */
14905
+ source: external_exports.literal("component-public-url"),
14906
+ name: EnvVarNameSchema,
14907
+ componentId: ComponentIdSchema
14908
+ }).strict();
14909
+ var ServiceBindingAttributeEnvBindingSchema = external_exports.object({
14910
+ /**
14911
+ * A NON-SECRET value from a service binding to ANOTHER Product.
14912
+ *
14913
+ * THE ONLY SOURCE THAT LEAVES THIS PRODUCT INSTANCE. Every other member of
14914
+ * this union resolves inside one deployment: a literal, a fact about this
14915
+ * instance, a sibling component's address, a setting this customer typed.
14916
+ * This one is answered by a different Product Instance, possibly in a
14917
+ * different logical Project, which is why it is the only source whose
14918
+ * value can be absent for a reason that is nobody's mistake.
14919
+ *
14920
+ * ABSENT WHILE THE BINDING IS PENDING, and absent rather than empty. The
14921
+ * variable is left out of the container entirely until the provider
14922
+ * completes the binding, so a Product can tell "not yet" from "set to
14923
+ * nothing" — the same rule `configuration` already follows. Read
14924
+ * `VFAC_SERVICE_BINDING_<KEY>_STATUS`, which the platform injects for
14925
+ * every declared binding, rather than inferring readiness from absence.
14926
+ *
14927
+ * CREDENTIALS DO NOT COME THIS WAY. `field` must name a field the
14928
+ * interface classifies as an attribute; a secret field bound here is
14929
+ * refused by `validateProductContracts()`, and the credential-shaped-name
14930
+ * rule below refuses the variable name independently.
14931
+ */
14932
+ source: external_exports.literal("service-binding-attribute"),
14933
+ name: EnvVarNameSchema,
14934
+ /** The `serviceBindings[].key` this value comes from. */
14935
+ binding: ServiceBindingKeySchema,
14936
+ /** The interface field, e.g. `issuerUrl`. Checked against the registry. */
14937
+ field: external_exports.string().regex(/^[a-z][a-zA-Z0-9]{1,47}$/)
14938
+ }).strict();
14939
+ var ConfigurationEnvBindingSchema = external_exports.object({
14940
+ source: external_exports.literal("configuration"),
14941
+ name: EnvVarNameSchema,
14942
+ /**
14943
+ * A NON-SECRET customer setting from the Product Definition.
14944
+ * `validateProductContracts()` rejects a reference to an `x-secret`
14945
+ * property — those travel through `secrets[]` and Secret Manager, never
14946
+ * through plain environment wiring.
14947
+ */
14948
+ property: external_exports.string().regex(/^[a-z][a-zA-Z0-9]{1,47}$/)
14949
+ }).strict();
14163
14950
  var EnvBindingSchema = external_exports.discriminatedUnion("source", [
14951
+ LiteralEnvBindingSchema,
14952
+ InstanceEnvBindingSchema,
14953
+ PlatformEnvBindingSchema,
14954
+ ComponentEndpointEnvBindingSchema,
14955
+ ComponentPublicUrlEnvBindingSchema,
14956
+ ServiceBindingAttributeEnvBindingSchema,
14957
+ ConfigurationEnvBindingSchema
14958
+ ]);
14959
+ var ConfigurationBindingPathSchema = external_exports.string().refine((raw) => parseConfigurationPath(raw) !== null, {
14960
+ message: 'A configuration path is a root property name, or an RFC 6901 pointer such as "/assistant/model".'
14961
+ }).refine((raw) => !/\/\d+(\/|$)/.test(raw), {
14962
+ message: 'A Blueprint binds a DECLARED setting, so a path may not carry a list index. Bind the list with "configuration-json".'
14963
+ });
14964
+ var EnvBindingV2Schema = external_exports.discriminatedUnion("source", [
14965
+ LiteralEnvBindingSchema,
14966
+ InstanceEnvBindingSchema,
14967
+ PlatformEnvBindingSchema,
14968
+ ComponentEndpointEnvBindingSchema,
14969
+ ComponentPublicUrlEnvBindingSchema,
14970
+ ServiceBindingAttributeEnvBindingSchema,
14164
14971
  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"),
14972
+ source: external_exports.literal("configuration"),
14193
14973
  name: EnvVarNameSchema,
14194
- /** Resolved to the sibling component's private URL at reconcile time. */
14195
- componentId: ComponentIdSchema
14196
- }).strict(),
14197
- external_exports.object({
14198
14974
  /**
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.
14210
- *
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.
14975
+ * A NON-SECRET customer setting, named by its canonical path.
14215
14976
  *
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.
14977
+ * `validateProductContracts` refuses a path that names no leaf, a path
14978
+ * that names a GROUP a group has no scalar rendering, and
14979
+ * `configuration-json` is how one is carried and any `x-secret`
14980
+ * property, which travels through `secrets[]` and never through plain
14981
+ * environment wiring.
14220
14982
  */
14221
- source: external_exports.literal("component-public-url"),
14222
- name: EnvVarNameSchema,
14223
- componentId: ComponentIdSchema
14983
+ path: ConfigurationBindingPathSchema
14224
14984
  }).strict(),
14225
14985
  external_exports.object({
14986
+ source: external_exports.literal("configuration-json"),
14987
+ name: EnvVarNameSchema,
14226
14988
  /**
14227
- * A NON-SECRET value from a service binding to ANOTHER Product.
14989
+ * THE WHOLE VALIDATED CONFIGURATION, OR ONE SUBTREE OF IT, AS JSON.
14228
14990
  *
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.
14991
+ * WHY THIS EXISTS. A component may carry at most forty environment
14992
+ * bindings, and a nested Product spends one per leaf so the format that
14993
+ * lets a Product have structure would immediately run out of room to
14994
+ * deliver it. This binds a whole document instead, and the Product's own
14995
+ * runtime translates it into whatever shape it wants. The Control Plane
14996
+ * learns nothing about that shape, which is the point.
14235
14997
  *
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.
14998
+ * WHAT IT CARRIES: validated, non-secret configuration with resolved
14999
+ * ORDINARY resource references materialised in place. Never a secret
15000
+ * value secrets are root-only and travel through `secrets[]`, and a
15001
+ * subtree cannot contain one. Serialised with `canonicalJson`, so the same
15002
+ * logical configuration is the same bytes and the environment fingerprint
15003
+ * does not move on key order alone.
14242
15004
  *
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.
15005
+ * Absent `path` means the whole document.
14263
15006
  */
14264
- property: external_exports.string().regex(/^[a-z][a-zA-Z0-9]{1,47}$/)
15007
+ path: ConfigurationBindingPathSchema.optional()
14265
15008
  }).strict()
14266
15009
  ]);
14267
15010
  var MAX_BOOTSTRAP_SCRIPT_LENGTH = 32768;
@@ -14473,11 +15216,12 @@ var ComponentDependencySchema = external_exports.object({
14473
15216
  minVersion: ContractVersionSchema.optional(),
14474
15217
  maxVersion: ContractVersionSchema.optional()
14475
15218
  }).strict();
15219
+ var MAX_COMPONENT_ENV_BINDINGS = 40;
14476
15220
  var componentCommonShape = {
14477
15221
  id: ComponentIdSchema.describe("Scoped by its Product; does not repeat the Product ID (\xA78)."),
14478
15222
  kind: ComponentKindSchema,
14479
15223
  description: external_exports.string().min(10).max(300),
14480
- env: external_exports.array(EnvBindingSchema).max(40),
15224
+ env: external_exports.array(EnvBindingSchema).max(MAX_COMPONENT_ENV_BINDINGS),
14481
15225
  dependsOn: external_exports.array(ComponentDependencySchema).max(8)
14482
15226
  };
14483
15227
  var ServerlessContainerComponentSchema = external_exports.object({
@@ -14529,6 +15273,16 @@ var RuntimeComponentSchema = external_exports.discriminatedUnion("runtime", [
14529
15273
  ServerlessContainerComponentSchema,
14530
15274
  VirtualMachineComponentSchema
14531
15275
  ]);
15276
+ var ServerlessContainerComponentV2Schema = ServerlessContainerComponentSchema.extend({
15277
+ env: external_exports.array(EnvBindingV2Schema).max(MAX_COMPONENT_ENV_BINDINGS)
15278
+ });
15279
+ var VirtualMachineComponentV2Schema = VirtualMachineComponentSchema.extend({
15280
+ env: external_exports.array(EnvBindingV2Schema).max(MAX_COMPONENT_ENV_BINDINGS)
15281
+ });
15282
+ var RuntimeComponentV2Schema = external_exports.discriminatedUnion("runtime", [
15283
+ ServerlessContainerComponentV2Schema,
15284
+ VirtualMachineComponentV2Schema
15285
+ ]);
14532
15286
  function isVirtualMachineComponent(component) {
14533
15287
  return component.runtime === "virtual-machine";
14534
15288
  }
@@ -14777,7 +15531,47 @@ var BlueprintLifecycleSchema = external_exports.object({
14777
15531
  * the service-interface surface is unchanged. Nothing published to date
14778
15532
  * regresses, and a release cannot strand an instance by forgetting it.
14779
15533
  */
14780
- directFromVersions: external_exports.array(ContractVersionSchema).max(20).optional()
15534
+ directFromVersions: external_exports.array(ContractVersionSchema).max(20).optional(),
15535
+ /**
15536
+ * How a stored configuration is carried across a release that MOVED a
15537
+ * setting.
15538
+ *
15539
+ * THE CASE THIS EXISTS FOR is a flat Product taking on structure:
15540
+ * `assistantName` becomes `/assistant/name`. Nothing else on this
15541
+ * platform can express that. Without it the target release simply does
15542
+ * not declare `assistantName`, so the upgrade drops the customer's value
15543
+ * and then refuses the deploy for a missing required setting — and the
15544
+ * customer's answer is gone either way.
15545
+ *
15546
+ * `move` AND NOTHING ELSE. A migration the platform cannot fully
15547
+ * evaluate is one it cannot apply safely, and every other operation a
15548
+ * publisher might want — rename a value, split one setting into two,
15549
+ * compute a default — needs the Product's own knowledge of what those
15550
+ * values MEAN. Moving a value is the one thing the platform can do
15551
+ * correctly without knowing anything about it.
15552
+ *
15553
+ * APPLIED AT CUTOVER, in the transaction that writes the new
15554
+ * configuration, and to the REFERENCE ROWS as well as the stored
15555
+ * document — a Resource Reference is identified by the path it answers,
15556
+ * so a setting that moved takes its edge with it or the edge points at
15557
+ * nothing.
15558
+ *
15559
+ * AN ABSENT SOURCE IS A NO-OP, deliberately. A customer who never
15560
+ * answered an optional setting has nothing to carry, and inventing an
15561
+ * empty value for them would turn "not configured" into "configured
15562
+ * blank" — two states this platform keeps apart everywhere else.
15563
+ *
15564
+ * `.optional()`, never `.default([])`, for the reason
15565
+ * `directFromVersions` states one field above: a materialised key makes
15566
+ * a byte-identical republish `RELEASED_CONTRACT_MUTATED`.
15567
+ */
15568
+ configurationMigrations: external_exports.array(external_exports.object({
15569
+ op: external_exports.literal("move"),
15570
+ /** Where the value is today, in the release being left. */
15571
+ from: ConfigurationBindingPathSchema,
15572
+ /** Where this release declares it. */
15573
+ to: ConfigurationBindingPathSchema
15574
+ }).strict()).max(40).optional()
14781
15575
  }).strict(),
14782
15576
  rollback: external_exports.object({
14783
15577
  supported: external_exports.boolean(),
@@ -14903,7 +15697,14 @@ function isOidcClientServiceBinding(binding) {
14903
15697
  }
14904
15698
  var DeploymentBlueprintObjectSchema = external_exports.object({
14905
15699
  /** The CONTRACT FORMAT version — not the Blueprint's. See api-version.ts. */
14906
- apiVersion: ProductApiVersionSchema,
15700
+ /**
15701
+ * The SHARED base carries either literal; each format pins its own below.
15702
+ *
15703
+ * Nothing parses this object schema directly — `DeploymentBlueprintV1Schema`
15704
+ * and `…V2Schema` extend it — so the permissive literal here is never what a
15705
+ * document is held to, and never what an artifact publishes.
15706
+ */
15707
+ apiVersion: ProductApiVersionAnySchema,
14907
15708
  kind: kindSchema(DEPLOYMENT_BLUEPRINT_KIND),
14908
15709
  productId: ProductIdSchema,
14909
15710
  /** This Blueprint's own version. The Definition pins it exactly. */
@@ -14971,7 +15772,17 @@ var DeploymentBlueprintObjectSchema = external_exports.object({
14971
15772
  outputs: external_exports.array(BlueprintOutputSchema).min(1).max(12),
14972
15773
  lifecycle: BlueprintLifecycleSchema
14973
15774
  }).strict();
14974
- var DeploymentBlueprintSchema = DeploymentBlueprintObjectSchema.superRefine((blueprint, ctx) => {
15775
+ var DeploymentBlueprintV1ObjectSchema = DeploymentBlueprintObjectSchema.extend({
15776
+ apiVersion: ProductApiVersionSchema
15777
+ });
15778
+ var DeploymentBlueprintV2ObjectSchema = DeploymentBlueprintObjectSchema.extend({
15779
+ apiVersion: ProductApiVersionV2Schema,
15780
+ components: external_exports.array(RuntimeComponentV2Schema).min(1).max(MAX_RUNTIME_COMPONENTS)
15781
+ });
15782
+ var DeploymentBlueprintV1Schema = DeploymentBlueprintV1ObjectSchema.superRefine(checkDeploymentBlueprint);
15783
+ var DeploymentBlueprintV2Schema = DeploymentBlueprintV2ObjectSchema.superRefine(checkDeploymentBlueprint);
15784
+ var DeploymentBlueprintSchema = DeploymentBlueprintV1Schema;
15785
+ function checkDeploymentBlueprint(blueprint, ctx) {
14975
15786
  const componentIds = blueprint.components.map((component) => component.id);
14976
15787
  const knownComponents = new Set(componentIds);
14977
15788
  const publicComponents = new Set(blueprint.components.filter((component) => component.ingress.exposure === "public").map((component) => component.id));
@@ -15072,13 +15883,7 @@ var DeploymentBlueprintSchema = DeploymentBlueprintObjectSchema.superRefine((blu
15072
15883
  });
15073
15884
  const handoff = resource.bootstrap.productHandoff;
15074
15885
  if (handoff !== void 0) {
15075
- const handoffPath = [
15076
- "dynamicResources",
15077
- index,
15078
- "bootstrap",
15079
- "productHandoff",
15080
- "componentId"
15081
- ];
15886
+ const handoffPath = ["dynamicResources", index, "bootstrap", "productHandoff", "componentId"];
15082
15887
  const target = blueprint.components.find((component) => component.id === handoff.componentId);
15083
15888
  if (target === void 0) {
15084
15889
  ctx.addIssue({
@@ -15689,7 +16494,7 @@ var DeploymentBlueprintSchema = DeploymentBlueprintObjectSchema.superRefine((blu
15689
16494
  });
15690
16495
  }
15691
16496
  }
15692
- });
16497
+ }
15693
16498
 
15694
16499
  // ../product-contracts/dist/deployability.js
15695
16500
  var VIRTUAL_MACHINE_PROFILE_INTERFACE = {
@@ -15896,7 +16701,33 @@ var ProductSubmissionObjectSchema = external_exports.object({
15896
16701
  definition: ProductDefinitionSchema,
15897
16702
  blueprint: DeploymentBlueprintSchema
15898
16703
  }).strict();
15899
- var ProductSubmissionSchema = ProductSubmissionObjectSchema.superRefine((submission, ctx) => {
16704
+ var submissionShape = {
16705
+ kind: kindSchema(PRODUCT_SUBMISSION_KIND),
16706
+ submission: SubmissionEnvelopeSchema
16707
+ };
16708
+ var ProductSubmissionV1ObjectSchema = external_exports.object({
16709
+ apiVersion: ProductApiVersionSchema,
16710
+ ...submissionShape,
16711
+ definition: ProductDefinitionSchema,
16712
+ blueprint: DeploymentBlueprintV1Schema
16713
+ }).strict();
16714
+ var ProductSubmissionV2ObjectSchema = external_exports.object({
16715
+ apiVersion: ProductApiVersionV2Schema,
16716
+ ...submissionShape,
16717
+ definition: ProductDefinitionV2Schema,
16718
+ blueprint: DeploymentBlueprintV2Schema
16719
+ }).strict();
16720
+ var ProductSubmissionV1Schema = ProductSubmissionV1ObjectSchema.superRefine(checkProductSubmission);
16721
+ var ProductSubmissionV2Schema = ProductSubmissionV2ObjectSchema.superRefine(checkProductSubmission);
16722
+ var ProductSubmissionSchema = ProductSubmissionV1Schema;
16723
+ function checkProductSubmission(submission, ctx) {
16724
+ if (submission.definition.apiVersion !== submission.apiVersion) {
16725
+ ctx.addIssue({
16726
+ code: external_exports.ZodIssueCode.custom,
16727
+ path: ["definition", "apiVersion"],
16728
+ message: `This submission is written against "${submission.apiVersion}" but its Product Definition declares "${submission.definition.apiVersion}". One submission is one contract format.`
16729
+ });
16730
+ }
15900
16731
  if (submission.definition.id !== submission.submission.productId) {
15901
16732
  ctx.addIssue({
15902
16733
  code: external_exports.ZodIssueCode.custom,
@@ -15918,7 +16749,7 @@ var ProductSubmissionSchema = ProductSubmissionObjectSchema.superRefine((submiss
15918
16749
  message: `The Product Definition is version "${submission.definition.version}" but the submission claims "${submission.submission.version}". They must be the same version.`
15919
16750
  });
15920
16751
  }
15921
- });
16752
+ }
15922
16753
  var PUBLISH_RULE_ANNEX = {
15923
16754
  allOf: [
15924
16755
  {
@@ -16371,12 +17202,12 @@ async function runDoctor(parsed) {
16371
17202
  ok: false,
16372
17203
  detail: "not set. Run `vfac context set --endpoint https://your-organization.cloud.vardeflyt.no`, or set VFAC_ENDPOINT."
16373
17204
  });
16374
- return report(checks, json);
17205
+ return report2(checks, json);
16375
17206
  }
16376
17207
  const checked = validateEndpoint(resolved.value, resolved.source);
16377
17208
  if (!checked.ok) {
16378
17209
  checks.push({ name: "Endpoint", ok: false, detail: checked.message });
16379
- return report(checks, json);
17210
+ return report2(checks, json);
16380
17211
  }
16381
17212
  const endpoint = checked.endpoint;
16382
17213
  checks.push({
@@ -16396,16 +17227,16 @@ async function runDoctor(parsed) {
16396
17227
  const probe = await probeMachineApi({ endpoint });
16397
17228
  if (!probe.ok) {
16398
17229
  checks.push({ name: "Machine API", ok: false, detail: probe.message });
16399
- return report(checks, json);
17230
+ return report2(checks, json);
16400
17231
  }
16401
17232
  checks.push({ name: "Machine API", ok: true, detail: probe.apiVersion });
16402
17233
  const compatible = compatibility(cliVersion(), probe.minimumCliVersion);
16403
17234
  checks.push(compatible);
16404
- if (!compatible.ok) return report(checks, json);
17235
+ if (!compatible.ok) return report2(checks, json);
16405
17236
  const signedIn = await signIn({ endpoint });
16406
17237
  if (!signedIn.ok) {
16407
17238
  checks.push({ name: "Context Gate", ok: false, detail: signedIn.message });
16408
- return report(checks, json);
17239
+ return report2(checks, json);
16409
17240
  }
16410
17241
  checks.push({
16411
17242
  name: "Context Gate",
@@ -16426,7 +17257,7 @@ async function runDoctor(parsed) {
16426
17257
  ok: false,
16427
17258
  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
17259
  });
16429
- return report(checks, json);
17260
+ return report2(checks, json);
16430
17261
  }
16431
17262
  const access = who.data.access;
16432
17263
  const projects = access.projects.map((project) => project.projectId);
@@ -16486,7 +17317,7 @@ async function runDoctor(parsed) {
16486
17317
  detail: "run `vfac guide` for the platform's own instructions, written for a machine"
16487
17318
  });
16488
17319
  checks.push(await updateAvailable(cliVersion()));
16489
- return report(checks, json);
17320
+ return report2(checks, json);
16490
17321
  }
16491
17322
  function compatibility(local, declared) {
16492
17323
  const name = "Compatibility";
@@ -16566,7 +17397,7 @@ async function updateAvailable(local) {
16566
17397
  facts
16567
17398
  };
16568
17399
  }
16569
- function report(checks, json) {
17400
+ function report2(checks, json) {
16570
17401
  const failed = checks.filter((check) => !check.ok);
16571
17402
  if (json) {
16572
17403
  process.stdout.write(`${JSON.stringify({ ok: failed.length === 0, checks })}
@@ -16942,16 +17773,14 @@ function renderProduct(product) {
16942
17773
  (profile) => `${profile.id}${profile.default ? " (default)" : ""}`
16943
17774
  );
16944
17775
  lines.push(` profile ${profiles.join(", ") || "(none declared)"}`);
16945
- const properties = product.configuration?.properties ?? {};
16946
- const required = product.configuration?.required ?? [];
16947
- if (required.length > 0) {
17776
+ const seed = seedConfiguration(product.configuration);
17777
+ if (seed.open.length > 0 || seed.requiredSecrets.length > 0) {
16948
17778
  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
- );
17779
+ for (const name of seed.requiredSecrets) {
17780
+ lines.push(` ${name} (secret \u2014 pass --secret ${name}=env:VAR)`);
17781
+ }
17782
+ for (const setting of seed.open) {
17783
+ lines.push(` ${setting.label} ${setting.type ?? ""}`.trimEnd());
16955
17784
  }
16956
17785
  }
16957
17786
  const needed = (product.dependencies ?? []).filter((dependency) => dependency.required);
@@ -17224,7 +18053,7 @@ async function runManifestInit(parsed) {
17224
18053
  }
17225
18054
  for (const setting of seed.open) {
17226
18055
  notes.push(
17227
- `spec.configuration.${setting.name} \u2014 required${setting.type ? ` (${setting.type})` : ""}`
18056
+ `spec.configuration.${setting.label} \u2014 required${setting.type ? ` (${setting.type})` : ""}`
17228
18057
  );
17229
18058
  }
17230
18059
  for (const group of seed.groups) {