@varde-flyt/vfac 0.12.0 → 0.13.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 +465 -22
  2. package/package.json +1 -1
package/dist/vfac.mjs CHANGED
@@ -7928,13 +7928,33 @@ function renderOperation(operationId, data) {
7928
7928
  );
7929
7929
  }
7930
7930
  }
7931
+ const product = failure["product"];
7932
+ const reportedSettings = [];
7933
+ if (Array.isArray(product)) {
7934
+ for (const entry of product) {
7935
+ const one = asRecord(entry);
7936
+ if (one === null) continue;
7937
+ const component = one["component"];
7938
+ const code = one["code"];
7939
+ if (typeof component !== "string" || typeof code !== "string") continue;
7940
+ if (component === "" || code === "") continue;
7941
+ const setting = one["setting"];
7942
+ if (typeof setting === "string" && setting !== "") {
7943
+ reportedSettings.push(setting);
7944
+ lines.push(` ${component} refused to start: ${code} (setting: ${setting})`);
7945
+ } else {
7946
+ lines.push(` ${component} refused to start: ${code}`);
7947
+ }
7948
+ }
7949
+ }
7931
7950
  const retryable = failure["retryable"];
7932
7951
  if (typeof retryable === "boolean") {
7933
7952
  const again = data["type"] === "PRODUCT_INSTANCE_UPGRADE" ? "`vfac lifecycle upgrade <pri_\u2026>`" : "`vfac lifecycle reconcile <pri_\u2026>`";
7934
7953
  const changeIt = data["type"] === "PRODUCT_INSTANCE_UPGRADE" ? "" : ", or `vfac apply` a corrected manifest";
7935
7954
  const cause2 = failure["cause"];
7955
+ const named = reportedSettings[0];
7936
7956
  lines.push(
7937
- retryable ? ` Another attempt can succeed: ${again}.` : cause2 === "unknown" ? ` The platform could not classify this, so there is nothing here to correct. Read the whole record with \`vfac get operation ${operationId} --output json\`, and quote that id if you ask for help. Repeating it unchanged fails the same way.` : ` Repeating this unchanged fails again \u2014 fix what the lines above name, then ${again}${changeIt}.`
7957
+ retryable ? ` Another attempt can succeed: ${again}.` : named !== void 0 ? ` Correct \`${named}\` in your manifest, then \`vfac apply\` it${data["type"] === "PRODUCT_INSTANCE_UPGRADE" ? ` and ${again}` : ""}. Repeating it unchanged fails the same way.` : cause2 === "unknown" ? ` The platform could not classify this, so there is nothing here to correct. Read the whole record with \`vfac get operation ${operationId} --output json\`, and quote that id if you ask for help. Repeating it unchanged fails the same way.` : ` Repeating this unchanged fails again \u2014 fix what the lines above name, then ${again}${changeIt}.`
7938
7958
  );
7939
7959
  }
7940
7960
  }
@@ -12245,6 +12265,29 @@ var ValueConditionSchema = external_exports.object({
12245
12265
  anyOf: external_exports.array(external_exports.string().min(1).max(200)).min(1).max(20),
12246
12266
  anyRow: external_exports.boolean().optional()
12247
12267
  }).strict();
12268
+ var ValueReferenceSchema = external_exports.object({
12269
+ /**
12270
+ * The setting whose VALUES form the allowed set — a canonical declaration
12271
+ * path, or a bare root key, which is the same string at depth 1.
12272
+ */
12273
+ path: external_exports.string().min(1).max(400)
12274
+ }).strict();
12275
+ var RowCardinalitySchema = external_exports.object({
12276
+ /** A setting declared DIRECTLY on the row. */
12277
+ property: PropertyNameSchema,
12278
+ /** The answers that count toward the limit. Non-empty. */
12279
+ anyOf: external_exports.array(external_exports.string().min(1).max(200)).min(1).max(20),
12280
+ /**
12281
+ * How many rows may answer that way. Zero is meaningful and allowed — "no
12282
+ * row may, under this release" is a thing an upgrade needs to be able to
12283
+ * say.
12284
+ */
12285
+ max: external_exports.number().int().min(0).max(99)
12286
+ }).strict();
12287
+ var RowRulesSchema = external_exports.object({
12288
+ unique: external_exports.array(PropertyNameSchema).min(1).max(MAX_CONFIGURATION_CHILDREN).optional(),
12289
+ atMost: external_exports.array(RowCardinalitySchema).min(1).max(10).optional()
12290
+ }).strict();
12248
12291
  var TitleSchema = external_exports.string().min(2).max(80).describe("Customer-facing field label.");
12249
12292
  var V1_LEAF_BOUNDS = {
12250
12293
  help: 400,
@@ -12318,7 +12361,16 @@ function leafShapes(bounds) {
12318
12361
  * what `requiredSecrets` reports, which is one answer with one reader.
12319
12362
  * Opening it wider is a separate decision with its own blast radius.
12320
12363
  */
12321
- "x-required-when": ValueConditionSchema.optional()
12364
+ "x-required-when": ValueConditionSchema.optional(),
12365
+ /**
12366
+ * The values this one must be chosen from — see {@link ValueReferenceSchema}.
12367
+ *
12368
+ * ON A STRING ONLY, and not on a secret: the platform never reads a
12369
+ * secret's value, so a membership test on one could never be evaluated.
12370
+ * The publish rules say so rather than leaving it to be discovered, the
12371
+ * same way they already do for a condition pointed at a secret.
12372
+ */
12373
+ "x-value-in": ValueReferenceSchema.optional()
12322
12374
  }).strict();
12323
12375
  withJsonSchemaAnnex(string, SECRET_COUPLING_ANNEX);
12324
12376
  const number = external_exports.object({
@@ -12329,14 +12381,23 @@ function leafShapes(bounds) {
12329
12381
  /** Both bounds required — "numeric limits" in the guide is not optional. */
12330
12382
  minimum: external_exports.number(),
12331
12383
  maximum: external_exports.number(),
12332
- default: external_exports.number().optional()
12384
+ default: external_exports.number().optional(),
12385
+ /**
12386
+ * WIDENED FROM SECRETS TO EVERY ROOT SETTING, which this shape's own
12387
+ * comment on the string leaf used to record as a known gap: "a
12388
+ * value-conditioned requirement for an ORDINARY setting is a real gap
12389
+ * too". It is the same keyword, the same evaluator and the same publish
12390
+ * rules; what changes is which settings may carry it.
12391
+ */
12392
+ "x-required-when": ValueConditionSchema.optional()
12333
12393
  }).strict();
12334
12394
  const boolean = external_exports.object({
12335
12395
  type: external_exports.literal("boolean"),
12336
12396
  title: TitleSchema,
12337
12397
  description: help.optional(),
12338
12398
  group: ConfigurationGroupSchema,
12339
- default: external_exports.boolean().optional()
12399
+ default: external_exports.boolean().optional(),
12400
+ "x-required-when": ValueConditionSchema.optional()
12340
12401
  }).strict();
12341
12402
  const items = external_exports.object({
12342
12403
  type: external_exports.literal("string"),
@@ -12353,7 +12414,8 @@ function leafShapes(bounds) {
12353
12414
  /** Required: an unbounded list is an unbounded write. */
12354
12415
  maxItems: external_exports.number().int().min(1).max(100),
12355
12416
  uniqueItems: external_exports.boolean().optional(),
12356
- default: external_exports.array(external_exports.string().max(200)).max(100).optional()
12417
+ default: external_exports.array(external_exports.string().max(200)).max(100).optional(),
12418
+ "x-required-when": ValueConditionSchema.optional()
12357
12419
  }).strict();
12358
12420
  return { string, number, boolean, items, array };
12359
12421
  }
@@ -12373,9 +12435,9 @@ var ConfigurationPropertySchema = external_exports.discriminatedUnion("type", [
12373
12435
  var NestedStringPropertySchema = V2_LEAVES.string.omit({ "x-secret": true, writeOnly: true, "x-required-when": true }).extend({ group: ConfigurationGroupSchema.optional() });
12374
12436
  var V2_NESTED_LEAVES = {
12375
12437
  string: NestedStringPropertySchema,
12376
- number: V2_LEAVES.number.extend({ group: ConfigurationGroupSchema.optional() }),
12377
- boolean: V2_LEAVES.boolean.extend({ group: ConfigurationGroupSchema.optional() }),
12378
- array: V2_LEAVES.array.extend({ group: ConfigurationGroupSchema.optional() }),
12438
+ number: V2_LEAVES.number.omit({ "x-required-when": true }).extend({ group: ConfigurationGroupSchema.optional() }),
12439
+ boolean: V2_LEAVES.boolean.omit({ "x-required-when": true }).extend({ group: ConfigurationGroupSchema.optional() }),
12440
+ array: V2_LEAVES.array.omit({ "x-required-when": true }).extend({ group: ConfigurationGroupSchema.optional() }),
12379
12441
  items: V2_LEAVES.items
12380
12442
  };
12381
12443
  function childBagShape(child) {
@@ -12419,7 +12481,23 @@ function propertyUnion(levelsBelow, leafSet) {
12419
12481
  // discriminator, which is also what keeps a v1 document's
12420
12482
  // `items: {type: 'string'}` reading exactly as it always did.
12421
12483
  leafSet.array.extend({
12422
- items: external_exports.discriminatedUnion("type", [V2_LEAVES.items, groupItems])
12484
+ items: external_exports.discriminatedUnion("type", [V2_LEAVES.items, groupItems]),
12485
+ /**
12486
+ * Rules over the ROWS of this list — see {@link RowRulesSchema}.
12487
+ *
12488
+ * HERE RATHER THAN ON THE LEAF SHAPE, and that placement is the whole
12489
+ * enforcement. `leafShapes().array` is also a v1 list and a last-level
12490
+ * list, both of which hold STRINGS: a rule naming a field of a row would
12491
+ * name nothing there, and `uniqueItems` is already the right keyword for
12492
+ * comparing whole scalar entries. This branch is the only one in the chain
12493
+ * where a list can hold groups, so declaring the keyword here makes "rows
12494
+ * have fields" a property of the shape rather than a rule to remember.
12495
+ *
12496
+ * The refinement still has to refuse a rule on a list of STRINGS, because
12497
+ * `items` is a discriminated union INSIDE this branch — the shape narrows
12498
+ * where a row can exist, not whether this particular list has one.
12499
+ */
12500
+ "x-row-rules": RowRulesSchema.optional()
12423
12501
  }),
12424
12502
  /**
12425
12503
  * A group of related settings.
@@ -13122,6 +13200,9 @@ function canonicalJson(value) {
13122
13200
  }
13123
13201
 
13124
13202
  // ../product-configuration/dist/configuration-tree.js
13203
+ function immediateContainer(node) {
13204
+ return node.containers[node.containers.length - 1] ?? null;
13205
+ }
13125
13206
  function underList(node) {
13126
13207
  return node.containers.some((container) => container.kind === "array");
13127
13208
  }
@@ -13183,6 +13264,11 @@ function walkDeclarations(properties, ancestors, base, onLeaf, onContainer = ()
13183
13264
  walkDeclarations(children.properties, [...ancestors, container], segments, onLeaf, onContainer);
13184
13265
  }
13185
13266
  }
13267
+ function declaredDefault(property) {
13268
+ if (property.type === "object")
13269
+ return void 0;
13270
+ return property.default;
13271
+ }
13186
13272
  function leafAt(schema, path) {
13187
13273
  const declaration = declarationPathOf(path);
13188
13274
  return configurationLeaves(schema).find((leaf) => leaf.path === declaration) ?? null;
@@ -13195,6 +13281,29 @@ function declaresPath(schema, path) {
13195
13281
  function isPlainObject(value) {
13196
13282
  return typeof value === "object" && value !== null && !Array.isArray(value);
13197
13283
  }
13284
+ function expandDeclarationPath(configuration, declaration) {
13285
+ const out = [];
13286
+ const walk = (cursor, remaining, soFar) => {
13287
+ if (remaining.length === 0) {
13288
+ const path = configurationPath(...soFar);
13289
+ if (path !== null)
13290
+ out.push(path);
13291
+ return;
13292
+ }
13293
+ if (Array.isArray(cursor)) {
13294
+ cursor.forEach((element, index) => walk(element, remaining, [...soFar, index]));
13295
+ return;
13296
+ }
13297
+ if (!isPlainObject(cursor))
13298
+ return;
13299
+ const [head, ...rest] = remaining;
13300
+ if (head === void 0)
13301
+ return;
13302
+ walk(cursor[String(head)], rest, [...soFar, head]);
13303
+ };
13304
+ walk(configuration, segmentsOf(declaration), []);
13305
+ return out;
13306
+ }
13198
13307
  function valueAt(configuration, path) {
13199
13308
  let cursor = configuration;
13200
13309
  for (const segment of segmentsOf(path)) {
@@ -13290,6 +13399,7 @@ function validateSubmittedConfiguration(schema, submitted, presence = {}) {
13290
13399
  const secrets = {};
13291
13400
  const configuration = validateObject({ properties: schema.properties, required: schema.required }, input, null, walk, { secrets, supplied });
13292
13401
  drain(walk, crossFieldIssues(schema, supplied, presence, configuration));
13402
+ drain(walk, rowAndReferenceIssues(schema, presence, configuration));
13293
13403
  if (walk.suppressed > 0) {
13294
13404
  walk.issues.push({
13295
13405
  field: "",
@@ -13533,6 +13643,106 @@ function crossFieldIssues(schema, supplied, presence, configuration = {}) {
13533
13643
  }
13534
13644
  return issues;
13535
13645
  }
13646
+ function referenceOf(property) {
13647
+ return property?.["x-value-in"];
13648
+ }
13649
+ function rowRulesOf(property) {
13650
+ return property?.["x-row-rules"];
13651
+ }
13652
+ function comparableValue(value) {
13653
+ if (value === void 0 || value === null)
13654
+ return null;
13655
+ if (typeof value === "string")
13656
+ return value === "" ? null : JSON.stringify(value);
13657
+ if (typeof value === "number" || typeof value === "boolean")
13658
+ return JSON.stringify(value);
13659
+ return null;
13660
+ }
13661
+ function rowAndReferenceIssues(schema, presence, configuration) {
13662
+ const issues = [];
13663
+ const referenced = new Set(presence.referenced ?? []);
13664
+ for (const leaf of configurationLeaves(schema)) {
13665
+ const reference = referenceOf(leaf.property);
13666
+ if (reference === void 0)
13667
+ continue;
13668
+ const targetPath = parseConfigurationPath(reference.path);
13669
+ if (targetPath === null)
13670
+ continue;
13671
+ const allowed = new Set(valuesAt(configuration, targetPath).filter((value) => typeof value === "string"));
13672
+ for (const valuePath of expandDeclarationPath(configuration, leaf.path)) {
13673
+ if (referenced.has(valuePath))
13674
+ continue;
13675
+ const held = valueAt(configuration, valuePath);
13676
+ if (typeof held !== "string" || held === "")
13677
+ continue;
13678
+ if (allowed.has(held))
13679
+ continue;
13680
+ issues.push({
13681
+ field: valuePath,
13682
+ message: `This must be one of the values configured under ${labelOfPath(schema, targetPath)}. ` + (allowed.size === 0 ? "Nothing is configured there yet." : "No entry there currently holds this one.")
13683
+ });
13684
+ }
13685
+ }
13686
+ for (const container of configurationContainers(schema)) {
13687
+ if (container.kind !== "array")
13688
+ continue;
13689
+ const rules = rowRulesOf(container.property);
13690
+ if (rules === void 0)
13691
+ continue;
13692
+ for (const listPath of expandDeclarationPath(configuration, container.path)) {
13693
+ const rows = valueAt(configuration, listPath);
13694
+ if (!Array.isArray(rows))
13695
+ continue;
13696
+ const fieldOf = (index, name) => configurationPath(...pathSegments(listPath), index, name) ?? listPath;
13697
+ for (const name of rules.unique ?? []) {
13698
+ const firstAt = /* @__PURE__ */ new Map();
13699
+ rows.forEach((row, index) => {
13700
+ const held = comparableValue(isPlainObject2(row) ? row[name] : void 0);
13701
+ if (held === null)
13702
+ return;
13703
+ const earlier = firstAt.get(held);
13704
+ if (earlier === void 0) {
13705
+ firstAt.set(held, index);
13706
+ return;
13707
+ }
13708
+ issues.push({
13709
+ field: fieldOf(index, name),
13710
+ message: `${labelOfChild(container, name)} must be different in every entry; entry ${earlier + 1} already uses this one.`
13711
+ });
13712
+ });
13713
+ }
13714
+ for (const rule of rules.atMost ?? []) {
13715
+ let counted = 0;
13716
+ rows.forEach((row, index) => {
13717
+ const held = isPlainObject2(row) ? row[rule.property] : void 0;
13718
+ if (typeof held !== "string" || !rule.anyOf.includes(held))
13719
+ return;
13720
+ counted += 1;
13721
+ if (counted <= rule.max)
13722
+ return;
13723
+ issues.push({
13724
+ field: fieldOf(index, rule.property),
13725
+ message: rule.max === 0 ? `No entry may be set to this.` : `At most ${rule.max} ${rule.max === 1 ? "entry" : "entries"} may be set to this, and ${counted - 1} already ${counted - 1 === 1 ? "is" : "are"}.`
13726
+ });
13727
+ });
13728
+ }
13729
+ }
13730
+ }
13731
+ return issues;
13732
+ }
13733
+ function labelOfPath(schema, path) {
13734
+ const leaf = leafAt(schema, path);
13735
+ const titleOf = (node) => node?.property.title;
13736
+ const field = titleOf(leaf) ?? printableConfigurationPath(path);
13737
+ const container = leaf?.containers[0];
13738
+ const outer = container === void 0 ? void 0 : titleOf(container);
13739
+ return outer === void 0 ? `"${field}"` : `"${field}" in "${outer}"`;
13740
+ }
13741
+ function labelOfChild(container, name) {
13742
+ const items = container.property.items;
13743
+ const title = items?.properties?.[name]?.title;
13744
+ return title === void 0 ? `"${name}"` : `"${title}"`;
13745
+ }
13536
13746
  function conditionOf(property) {
13537
13747
  return property?.["x-required-when"];
13538
13748
  }
@@ -13541,19 +13751,45 @@ function labelOf(schema, name) {
13541
13751
  return property?.title === void 0 ? `"${name}"` : `"${property.title}"`;
13542
13752
  }
13543
13753
  function withoutCrossFieldRules(schema) {
13544
- const conditioned = Object.entries(schema.properties).some(([, property]) => property["x-required-when"] !== void 0);
13545
- if (conditioned) {
13546
- const properties = Object.fromEntries(Object.entries(schema.properties).map(([name, property]) => {
13547
- const { "x-required-when": _dropped, ...bare } = property;
13548
- return [name, bare];
13549
- }));
13550
- return withoutCrossFieldRules({ ...schema, properties });
13551
- }
13552
- if (schema.dependentRequired === void 0)
13553
- return schema;
13554
- const { dependentRequired: _removed, ...rest } = schema;
13754
+ const properties = withoutPropertyRules(schema.properties);
13755
+ const stripped = properties === null ? schema : { ...schema, properties };
13756
+ if (stripped.dependentRequired === void 0)
13757
+ return stripped;
13758
+ const { dependentRequired: _removed, ...rest } = stripped;
13555
13759
  return rest;
13556
13760
  }
13761
+ var PROPERTY_RULE_KEYWORDS = ["x-required-when", "x-value-in", "x-row-rules"];
13762
+ function withoutPropertyRules(properties) {
13763
+ let out = null;
13764
+ const edit = (name) => {
13765
+ out ??= { ...properties };
13766
+ const copy = { ...out[name] };
13767
+ out[name] = copy;
13768
+ return copy;
13769
+ };
13770
+ for (const [name, property] of Object.entries(properties)) {
13771
+ if (!isPlainObject2(property))
13772
+ continue;
13773
+ for (const keyword of PROPERTY_RULE_KEYWORDS) {
13774
+ if (property[keyword] === void 0)
13775
+ continue;
13776
+ delete edit(name)[keyword];
13777
+ }
13778
+ const children = out?.[name] ?? property;
13779
+ if (isPlainObject2(children["properties"])) {
13780
+ const bare = withoutPropertyRules(children["properties"]);
13781
+ if (bare !== null)
13782
+ edit(name)["properties"] = bare;
13783
+ }
13784
+ const items = children["items"];
13785
+ if (isPlainObject2(items) && isPlainObject2(items["properties"])) {
13786
+ const bare = withoutPropertyRules(items["properties"]);
13787
+ if (bare !== null)
13788
+ edit(name)["items"] = { ...items, properties: bare };
13789
+ }
13790
+ }
13791
+ return out;
13792
+ }
13557
13793
  function defaultOf(property) {
13558
13794
  switch (property.type) {
13559
13795
  case "string":
@@ -14760,11 +14996,19 @@ function checkProductDefinition(definition, ctx) {
14760
14996
  if (condition === void 0)
14761
14997
  continue;
14762
14998
  const at = ["configuration", "properties", name, "x-required-when"];
14763
- if (!isSecretProperty(property)) {
14999
+ if ((definition.configuration.required ?? []).includes(name)) {
15000
+ ctx.addIssue({
15001
+ code: external_exports.ZodIssueCode.custom,
15002
+ path: [...at],
15003
+ message: `"${name}" is already required, so "x-required-when" can only narrow a rule that is already unconditional. Drop one: \`required\` is always, \`x-required-when\` is the condition.`
15004
+ });
15005
+ continue;
15006
+ }
15007
+ if (declaredDefault(property) !== void 0) {
14764
15008
  ctx.addIssue({
14765
15009
  code: external_exports.ZodIssueCode.custom,
14766
15010
  path: [...at],
14767
- message: `"${name}" is not a secret, so "x-required-when" has nothing to report through. Declare it "x-secret": true with "writeOnly": true, or use "dependentRequired" to require one ordinary setting alongside another.`
15011
+ message: `"${name}" declares a default, so it is never absent and this condition could never make it required. Remove the default, or the condition.`
14768
15012
  });
14769
15013
  continue;
14770
15014
  }
@@ -14840,6 +15084,8 @@ function checkProductDefinition(definition, ctx) {
14840
15084
  });
14841
15085
  }
14842
15086
  }
15087
+ checkValueReferences(definition.configuration, ctx);
15088
+ checkRowRules(definition.configuration, ctx);
14843
15089
  const provides = definition.serviceInterfaces?.provides ?? [];
14844
15090
  const provideKeys = provides.map((provide) => provide.key);
14845
15091
  const duplicateProvideKeys = provideKeys.filter((key, i) => provideKeys.indexOf(key) !== i);
@@ -14951,6 +15197,203 @@ function checkProductDefinition(definition, ctx) {
14951
15197
  });
14952
15198
  }
14953
15199
  }
15200
+ function declarationIssuePath(node) {
15201
+ const out = ["configuration"];
15202
+ for (const container of node.containers) {
15203
+ out.push("properties", container.name);
15204
+ if (container.kind === "array")
15205
+ out.push("items");
15206
+ }
15207
+ out.push("properties", node.name);
15208
+ return out;
15209
+ }
15210
+ function directChildren(leaves, container) {
15211
+ return leaves.filter((leaf) => immediateContainer(leaf)?.path === container.path);
15212
+ }
15213
+ function valuesOutsideSpace(configuration, leaf, values) {
15214
+ const single = withoutCrossFieldRules({
15215
+ ...configuration,
15216
+ properties: { [leaf.name]: leaf.property },
15217
+ required: [leaf.name]
15218
+ });
15219
+ return values.filter((value) => !validateSubmittedConfiguration(single, { [leaf.name]: value }).ok);
15220
+ }
15221
+ function checkValueReferences(configuration, ctx) {
15222
+ const leaves = configurationLeaves(configuration);
15223
+ for (const leaf of leaves) {
15224
+ const reference = leaf.property["x-value-in"];
15225
+ if (reference === void 0)
15226
+ continue;
15227
+ const at = [...declarationIssuePath(leaf), "x-value-in"];
15228
+ const here = printableConfigurationPath(leaf.path);
15229
+ if (isSecretProperty(leaf.property)) {
15230
+ ctx.addIssue({
15231
+ code: external_exports.ZodIssueCode.custom,
15232
+ path: at,
15233
+ message: `"${here}" is a secret. The platform never reads a secret's value, so it could never check one against a set of values.`
15234
+ });
15235
+ continue;
15236
+ }
15237
+ if (leaf.property.enum !== void 0) {
15238
+ ctx.addIssue({
15239
+ code: external_exports.ZodIssueCode.custom,
15240
+ path: at,
15241
+ message: `"${here}" already fixes its values with "enum". A setting takes its values from the contract or from another setting, not from both.`
15242
+ });
15243
+ continue;
15244
+ }
15245
+ if (declaredDefault(leaf.property) !== void 0) {
15246
+ ctx.addIssue({
15247
+ code: external_exports.ZodIssueCode.custom,
15248
+ path: at,
15249
+ message: `"${here}" declares a default, which would be written into every row and then refused until the setting it points at happens to hold the same value. Remove the default.`
15250
+ });
15251
+ continue;
15252
+ }
15253
+ const parsed = parseConfigurationPath(reference.path);
15254
+ const target = parsed === null ? null : leafAt(configuration, parsed);
15255
+ if (target === null) {
15256
+ const isGroup = parsed !== null && declaresPath(configuration, parsed);
15257
+ ctx.addIssue({
15258
+ code: external_exports.ZodIssueCode.custom,
15259
+ path: [...at, "path"],
15260
+ message: isGroup ? `"${reference.path}" is a group of settings, which holds no value to choose from. Name one setting inside it.` : `"${reference.path}" is not a declared customer configuration property.`
15261
+ });
15262
+ continue;
15263
+ }
15264
+ if (target.path === leaf.path) {
15265
+ ctx.addIssue({
15266
+ code: external_exports.ZodIssueCode.custom,
15267
+ path: [...at, "path"],
15268
+ message: `"${here}" cannot take its values from itself.`
15269
+ });
15270
+ continue;
15271
+ }
15272
+ if (target.property.type !== "string") {
15273
+ ctx.addIssue({
15274
+ code: external_exports.ZodIssueCode.custom,
15275
+ path: [...at, "path"],
15276
+ message: `"${reference.path}" is a ${target.property.type} setting; a reference compares string values.`
15277
+ });
15278
+ continue;
15279
+ }
15280
+ if (isSecretProperty(target.property)) {
15281
+ ctx.addIssue({
15282
+ code: external_exports.ZodIssueCode.custom,
15283
+ path: [...at, "path"],
15284
+ message: `"${reference.path}" is a secret. The platform never reads a secret's value, so it could never be the set a reference is checked against.`
15285
+ });
15286
+ continue;
15287
+ }
15288
+ }
15289
+ }
15290
+ function checkRowRules(configuration, ctx) {
15291
+ const leaves = configurationLeaves(configuration);
15292
+ for (const leaf of leaves) {
15293
+ if (leaf.property["x-row-rules"] === void 0)
15294
+ continue;
15295
+ ctx.addIssue({
15296
+ code: external_exports.ZodIssueCode.custom,
15297
+ path: [...declarationIssuePath(leaf), "x-row-rules"],
15298
+ message: `"${printableConfigurationPath(leaf.path)}" is a list of text entries, whose rows have no fields to name. Use "uniqueItems" to compare whole entries, or declare "items" as a group.`
15299
+ });
15300
+ }
15301
+ for (const container of configurationContainers(configuration)) {
15302
+ const rules = container.property["x-row-rules"];
15303
+ if (rules === void 0)
15304
+ continue;
15305
+ const at = [...declarationIssuePath(container), "x-row-rules"];
15306
+ const here = printableConfigurationPath(container.path);
15307
+ if (rules.unique === void 0 && rules.atMost === void 0) {
15308
+ ctx.addIssue({
15309
+ code: external_exports.ZodIssueCode.custom,
15310
+ path: at,
15311
+ message: `"${here}" declares "x-row-rules" with no rule in it. Declare "unique", "atMost", or both.`
15312
+ });
15313
+ continue;
15314
+ }
15315
+ const children = directChildren(leaves, container);
15316
+ const childAt = (name) => children.find((child) => child.name === name);
15317
+ const maxItems = container.property.maxItems;
15318
+ const seen = /* @__PURE__ */ new Set();
15319
+ (rules.unique ?? []).forEach((name, index) => {
15320
+ if (seen.has(name)) {
15321
+ ctx.addIssue({
15322
+ code: external_exports.ZodIssueCode.custom,
15323
+ path: [...at, "unique", index],
15324
+ message: `"${name}" is named twice.`
15325
+ });
15326
+ return;
15327
+ }
15328
+ seen.add(name);
15329
+ const child = childAt(name);
15330
+ if (child === void 0) {
15331
+ ctx.addIssue({
15332
+ code: external_exports.ZodIssueCode.custom,
15333
+ path: [...at, "unique", index],
15334
+ message: `"${name}" is not a setting declared on a row of "${here}".`
15335
+ });
15336
+ return;
15337
+ }
15338
+ if (child.property.type === "array") {
15339
+ ctx.addIssue({
15340
+ code: external_exports.ZodIssueCode.custom,
15341
+ path: [...at, "unique", index],
15342
+ message: `"${name}" is a list, which holds no single value to compare across rows.`
15343
+ });
15344
+ }
15345
+ if (child.property.type === "boolean") {
15346
+ ctx.addIssue({
15347
+ code: external_exports.ZodIssueCode.custom,
15348
+ path: [...at, "unique", index],
15349
+ message: `"${name}" is a true/false setting, so requiring it to be unique would cap "${here}" at two rows rather than make anything distinct. Use "atMost" to limit how many rows may answer one way.`
15350
+ });
15351
+ }
15352
+ });
15353
+ (rules.atMost ?? []).forEach((rule, index) => {
15354
+ const ruleAt = [...at, "atMost", index];
15355
+ const child = childAt(rule.property);
15356
+ if (child === void 0) {
15357
+ ctx.addIssue({
15358
+ code: external_exports.ZodIssueCode.custom,
15359
+ path: [...ruleAt, "property"],
15360
+ message: `"${rule.property}" is not a setting declared on a row of "${here}".`
15361
+ });
15362
+ return;
15363
+ }
15364
+ if (child.property.type !== "string") {
15365
+ ctx.addIssue({
15366
+ code: external_exports.ZodIssueCode.custom,
15367
+ path: [...ruleAt, "property"],
15368
+ message: `"${rule.property}" is a ${child.property.type} setting; a row rule compares string values.`
15369
+ });
15370
+ return;
15371
+ }
15372
+ if (new Set(rule.anyOf).size !== rule.anyOf.length) {
15373
+ ctx.addIssue({
15374
+ code: external_exports.ZodIssueCode.custom,
15375
+ path: [...ruleAt, "anyOf"],
15376
+ message: "Repeated values in `anyOf`."
15377
+ });
15378
+ }
15379
+ if (maxItems !== void 0 && rule.max >= maxItems) {
15380
+ ctx.addIssue({
15381
+ code: external_exports.ZodIssueCode.custom,
15382
+ path: [...ruleAt, "max"],
15383
+ message: `"${here}" holds at most ${maxItems} rows, so a limit of ${rule.max} could never be exceeded. Lower it, or remove the rule.`
15384
+ });
15385
+ }
15386
+ const outside = valuesOutsideSpace(configuration, child, rule.anyOf);
15387
+ if (outside.length > 0) {
15388
+ ctx.addIssue({
15389
+ code: external_exports.ZodIssueCode.custom,
15390
+ path: [...ruleAt, "anyOf"],
15391
+ message: `"${rule.property}" never accepts ${outside.map((value) => `"${value}"`).join(", ")}, so this rule could never count a row.`
15392
+ });
15393
+ }
15394
+ });
15395
+ }
15396
+ }
14954
15397
 
14955
15398
  // ../product-contracts/dist/announcements.js
14956
15399
  var ANNOUNCED_PRODUCTS = Object.freeze([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@varde-flyt/vfac",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Deploy and manage Varde Flyt Products from a manifest.",
5
5
  "repository": {
6
6
  "type": "git",