@varde-flyt/vfac 0.8.0 → 0.10.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 (3) hide show
  1. package/README.md +5 -3
  2. package/dist/vfac.mjs +575 -280
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -27,9 +27,11 @@ vfac whoami
27
27
  ```
28
28
 
29
29
  `doctor` checks the endpoint, which Project this terminal has selected, the API,
30
- whether this client is new enough for the platform it is pointed at, your
31
- credential and what that credential reaches, in that order, and reports the first
32
- thing that is actually wrong. A platform declares the oldest client it supports;
30
+ whether this client is new enough for the platform it is pointed at, how this
31
+ terminal is signed in and what that credential reaches, in that order, and reports
32
+ the first thing that is actually wrong. Both ways of signing in count: a Context
33
+ Gate exchanged from `VFAC_CREDENTIAL_ID` and `VFAC_CREDENTIAL_SECRET`, or a
34
+ sign-in from `vfac login` that is still valid. A platform declares the oldest client it supports;
33
35
  below that, `doctor` fails and says so before sending your credential anywhere.
34
36
 
35
37
  The selected Project and the Projects your credential reaches are two different
package/dist/vfac.mjs CHANGED
@@ -7793,7 +7793,7 @@ async function ensureSession(args) {
7793
7793
  const endpoint = checked.endpoint;
7794
7794
  const credential = credentialFromEnvironment();
7795
7795
  if (args.forceExchange !== true && sessionUsable(stored.session, endpoint, args.now ?? /* @__PURE__ */ new Date(), credential?.keyId)) {
7796
- return { ok: true, ready: { endpoint, token: stored.session.token, stored } };
7796
+ return { ok: true, ready: { endpoint, token: stored.session.token, stored, via: "stored" } };
7797
7797
  }
7798
7798
  const signedIn = await signIn({
7799
7799
  endpoint,
@@ -7802,7 +7802,16 @@ async function ensureSession(args) {
7802
7802
  if (!signedIn.ok) return { ok: false, message: signedIn.message };
7803
7803
  const next = { ...stored, session: signedIn.session };
7804
7804
  writeStored(next);
7805
- return { ok: true, ready: { endpoint, token: signedIn.session.token, stored: next } };
7805
+ return {
7806
+ ok: true,
7807
+ ready: {
7808
+ endpoint,
7809
+ token: signedIn.session.token,
7810
+ stored: next,
7811
+ via: "exchanged",
7812
+ ...signedIn.gate ? { gate: signedIn.gate } : {}
7813
+ }
7814
+ };
7806
7815
  }
7807
7816
  function resolveProject(flag, stored) {
7808
7817
  return flag ?? stored.context?.projectId ?? null;
@@ -7922,8 +7931,9 @@ function renderOperation(operationId, data) {
7922
7931
  const retryable = failure["retryable"];
7923
7932
  if (typeof retryable === "boolean") {
7924
7933
  const again = data["type"] === "PRODUCT_INSTANCE_UPGRADE" ? "`vfac lifecycle upgrade <pri_\u2026>`" : "`vfac lifecycle reconcile <pri_\u2026>`";
7934
+ const changeIt = data["type"] === "PRODUCT_INSTANCE_UPGRADE" ? "" : ", or `vfac apply` a corrected manifest";
7925
7935
  lines.push(
7926
- retryable ? ` Another attempt can succeed: ${again}.` : ` Repeating this unchanged fails again \u2014 fix what the lines above name, then ${again}.`
7936
+ retryable ? ` Another attempt can succeed: ${again}.` : ` Repeating this unchanged fails again \u2014 fix what the lines above name, then ${again}${changeIt}.`
7927
7937
  );
7928
7938
  }
7929
7939
  }
@@ -11989,9 +11999,10 @@ var coerce = {
11989
11999
  var NEVER = INVALID;
11990
12000
 
11991
12001
  // ../product-configuration/dist/configuration-bounds.js
11992
- var MAX_CONFIGURATION_DEPTH = 2;
11993
- var MAX_CONFIGURATION_LEAVES = 40;
11994
- var MAX_CONFIGURATION_NODES = 60;
12002
+ var MAX_CONFIGURATION_DEPTH = 6;
12003
+ var MAX_CONFIGURATION_LEAVES = 256;
12004
+ var MAX_CONFIGURATION_NODES = 512;
12005
+ var MAX_CONFIGURATION_CHILDREN = 64;
11995
12006
  var MAX_CONFIGURATION_VALUE_LEAVES = 1e3;
11996
12007
  var MAX_CONFIGURATION_ISSUES = 50;
11997
12008
  var MAX_CONFIGURATION_VALUE_BYTES = 16384;
@@ -12002,6 +12013,11 @@ function withJsonSchemaAnnex(schema, annex) {
12002
12013
  ANNEXES.set(schema, annex);
12003
12014
  return schema;
12004
12015
  }
12016
+ var DEFINITION_NAMES = /* @__PURE__ */ new WeakMap();
12017
+ function withJsonSchemaDefinition(schema, name) {
12018
+ DEFINITION_NAMES.set(schema, name);
12019
+ return schema;
12020
+ }
12005
12021
 
12006
12022
  // ../product-configuration/dist/infrastructure-vocabulary.js
12007
12023
  function tokenizeIdentifier(identifier) {
@@ -12218,8 +12234,31 @@ var ConfigurationGroupSchema = external_exports.enum([
12218
12234
  ]);
12219
12235
  var CONFIGURATION_PROPERTY_NAME_REGEX = /^[a-z][a-zA-Z0-9]{1,47}$/;
12220
12236
  var PropertyNameSchema = external_exports.string().regex(CONFIGURATION_PROPERTY_NAME_REGEX, "Configuration property names must be camelCase, start with a lowercase letter, and be 2\u201348 characters.");
12237
+ var ValueConditionSchema = external_exports.object({
12238
+ /**
12239
+ * The setting, as a canonical path — or a bare root key, which is the same
12240
+ * string at depth 1. With `anyRow`, a DECLARATION path: `/servers/kind`
12241
+ * names the setting each row answers, never one row's answer.
12242
+ */
12243
+ path: external_exports.string().min(1).max(400),
12244
+ anyOf: external_exports.array(external_exports.string().min(1).max(200)).min(1).max(20),
12245
+ anyRow: external_exports.boolean().optional()
12246
+ }).strict();
12221
12247
  var TitleSchema = external_exports.string().min(2).max(80).describe("Customer-facing field label.");
12222
- var HelpTextSchema = external_exports.string().min(10).max(400).describe("Customer-facing explanation. Required for security-sensitive choices.");
12248
+ var V1_LEAF_BOUNDS = {
12249
+ help: 400,
12250
+ enumValues: 40,
12251
+ stringLength: 4096,
12252
+ defaultLength: 400
12253
+ };
12254
+ var V2_LEAF_BOUNDS = {
12255
+ help: 2e3,
12256
+ enumValues: 256,
12257
+ stringLength: 16384,
12258
+ defaultLength: 2e3
12259
+ };
12260
+ var MAX_DECLARABLE_STRING_LENGTH = V2_LEAF_BOUNDS.stringLength;
12261
+ var helpTextSchema = (bounds) => external_exports.string().min(10).max(bounds.help).describe("Customer-facing explanation. Required for security-sensitive choices.");
12223
12262
  var SECRET_COUPLING_ANNEX = {
12224
12263
  allOf: [
12225
12264
  {
@@ -12238,63 +12277,91 @@ var SECRET_COUPLING_ANNEX = {
12238
12277
  }
12239
12278
  ]
12240
12279
  };
12241
- var StringPropertySchema = external_exports.object({
12242
- type: external_exports.literal("string"),
12243
- title: TitleSchema,
12244
- description: HelpTextSchema.optional(),
12245
- group: ConfigurationGroupSchema,
12246
- enum: external_exports.array(external_exports.string().min(1).max(80)).min(1).max(40).optional(),
12247
- minLength: external_exports.number().int().min(0).max(4096).optional(),
12248
- /**
12249
- * Required unless the value is constrained by `enum`. An unbounded string
12250
- * reaching the Control Plane as desired state is an unbounded write.
12251
- */
12252
- maxLength: external_exports.number().int().min(1).max(4096).optional(),
12253
- pattern: external_exports.string().min(1).max(200).optional(),
12254
- default: external_exports.string().max(400).optional(),
12255
- /**
12256
- * docs/PRODUCT_DEFINITION_GUIDE.md "Secrets": secret input is marked
12257
- * write-only AND secret. Both flags, and the refinement below makes one
12258
- * without the other a validation error.
12259
- */
12260
- writeOnly: external_exports.boolean().optional(),
12261
- "x-secret": external_exports.boolean().optional()
12262
- }).strict();
12263
- withJsonSchemaAnnex(StringPropertySchema, SECRET_COUPLING_ANNEX);
12264
- var NumberPropertySchema = external_exports.object({
12265
- type: external_exports.enum(["integer", "number"]),
12266
- title: TitleSchema,
12267
- description: HelpTextSchema.optional(),
12268
- group: ConfigurationGroupSchema,
12269
- /** Both bounds required — "numeric limits" in the guide is not optional. */
12270
- minimum: external_exports.number(),
12271
- maximum: external_exports.number(),
12272
- default: external_exports.number().optional()
12273
- }).strict();
12274
- var BooleanPropertySchema = external_exports.object({
12275
- type: external_exports.literal("boolean"),
12276
- title: TitleSchema,
12277
- description: HelpTextSchema.optional(),
12278
- group: ConfigurationGroupSchema,
12279
- default: external_exports.boolean().optional()
12280
- }).strict();
12281
- var ArrayItemsSchema = external_exports.object({
12282
- type: external_exports.literal("string"),
12283
- enum: external_exports.array(external_exports.string().min(1).max(80)).min(1).max(40).optional(),
12284
- maxLength: external_exports.number().int().min(1).max(400).optional()
12285
- }).strict();
12286
- var ArrayPropertySchema = external_exports.object({
12287
- type: external_exports.literal("array"),
12288
- title: TitleSchema,
12289
- description: HelpTextSchema.optional(),
12290
- group: ConfigurationGroupSchema,
12291
- items: ArrayItemsSchema,
12292
- minItems: external_exports.number().int().min(0).max(100).optional(),
12293
- /** Required: an unbounded list is an unbounded write. */
12294
- maxItems: external_exports.number().int().min(1).max(100),
12295
- uniqueItems: external_exports.boolean().optional(),
12296
- default: external_exports.array(external_exports.string().max(200)).max(100).optional()
12297
- }).strict();
12280
+ function leafShapes(bounds) {
12281
+ const help = helpTextSchema(bounds);
12282
+ const enumMembers = external_exports.array(external_exports.string().min(1).max(80)).min(1).max(bounds.enumValues).optional();
12283
+ const string = external_exports.object({
12284
+ type: external_exports.literal("string"),
12285
+ title: TitleSchema,
12286
+ description: help.optional(),
12287
+ group: ConfigurationGroupSchema,
12288
+ enum: enumMembers,
12289
+ minLength: external_exports.number().int().min(0).max(bounds.stringLength).optional(),
12290
+ /**
12291
+ * Required unless the value is constrained by `enum`. An unbounded string
12292
+ * reaching the Control Plane as desired state is an unbounded write.
12293
+ */
12294
+ maxLength: external_exports.number().int().min(1).max(bounds.stringLength).optional(),
12295
+ pattern: external_exports.string().min(1).max(200).optional(),
12296
+ default: external_exports.string().max(bounds.defaultLength).optional(),
12297
+ /**
12298
+ * docs/PRODUCT_DEFINITION_GUIDE.md "Secrets": secret input is marked
12299
+ * write-only AND secret. Both flags, and the refinement below makes one
12300
+ * without the other a validation error.
12301
+ */
12302
+ writeOnly: external_exports.boolean().optional(),
12303
+ "x-secret": external_exports.boolean().optional(),
12304
+ /**
12305
+ * The setting VALUE that makes this credential necessary.
12306
+ *
12307
+ * WHY IT CANNOT BE `dependentRequired`. That mechanism is keyed on
12308
+ * PRESENCE — "if the author answered X, they must also answer Y" — and
12309
+ * says so in as many words: "PRESENCE, NEVER A VALUE. A caller passes
12310
+ * NAMES." A Product whose credential is needed only when a setting has one
12311
+ * particular value could not say so, so `vfac plan`'s `requiredSecrets`
12312
+ * could not warn before a deploy, and the refusal arrived at apply.
12313
+ *
12314
+ * ON A SECRET ONLY, and that is narrower than the mechanism could be. A
12315
+ * value-conditioned requirement for an ORDINARY setting is a real gap too,
12316
+ * but it changes what every door accepts for every property; this changes
12317
+ * what `requiredSecrets` reports, which is one answer with one reader.
12318
+ * Opening it wider is a separate decision with its own blast radius.
12319
+ */
12320
+ "x-required-when": ValueConditionSchema.optional()
12321
+ }).strict();
12322
+ withJsonSchemaAnnex(string, SECRET_COUPLING_ANNEX);
12323
+ const number = external_exports.object({
12324
+ type: external_exports.enum(["integer", "number"]),
12325
+ title: TitleSchema,
12326
+ description: help.optional(),
12327
+ group: ConfigurationGroupSchema,
12328
+ /** Both bounds required — "numeric limits" in the guide is not optional. */
12329
+ minimum: external_exports.number(),
12330
+ maximum: external_exports.number(),
12331
+ default: external_exports.number().optional()
12332
+ }).strict();
12333
+ const boolean = external_exports.object({
12334
+ type: external_exports.literal("boolean"),
12335
+ title: TitleSchema,
12336
+ description: help.optional(),
12337
+ group: ConfigurationGroupSchema,
12338
+ default: external_exports.boolean().optional()
12339
+ }).strict();
12340
+ const items = external_exports.object({
12341
+ type: external_exports.literal("string"),
12342
+ enum: enumMembers,
12343
+ maxLength: external_exports.number().int().min(1).max(bounds.defaultLength).optional()
12344
+ }).strict();
12345
+ const array = external_exports.object({
12346
+ type: external_exports.literal("array"),
12347
+ title: TitleSchema,
12348
+ description: help.optional(),
12349
+ group: ConfigurationGroupSchema,
12350
+ items,
12351
+ minItems: external_exports.number().int().min(0).max(100).optional(),
12352
+ /** Required: an unbounded list is an unbounded write. */
12353
+ maxItems: external_exports.number().int().min(1).max(100),
12354
+ uniqueItems: external_exports.boolean().optional(),
12355
+ default: external_exports.array(external_exports.string().max(200)).max(100).optional()
12356
+ }).strict();
12357
+ return { string, number, boolean, items, array };
12358
+ }
12359
+ var V1_LEAVES = leafShapes(V1_LEAF_BOUNDS);
12360
+ var V2_LEAVES = leafShapes(V2_LEAF_BOUNDS);
12361
+ var StringPropertySchema = V1_LEAVES.string;
12362
+ var NumberPropertySchema = V1_LEAVES.number;
12363
+ var BooleanPropertySchema = V1_LEAVES.boolean;
12364
+ var ArrayPropertySchema = V1_LEAVES.array;
12298
12365
  var MAX_CONFIGURATION_PROPERTIES = 40;
12299
12366
  var ConfigurationPropertySchema = external_exports.discriminatedUnion("type", [
12300
12367
  StringPropertySchema,
@@ -12302,42 +12369,81 @@ var ConfigurationPropertySchema = external_exports.discriminatedUnion("type", [
12302
12369
  BooleanPropertySchema,
12303
12370
  ArrayPropertySchema
12304
12371
  ]);
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)
12372
+ var NestedStringPropertySchema = V2_LEAVES.string.omit({ "x-secret": true, writeOnly: true, "x-required-when": true }).extend({ group: ConfigurationGroupSchema.optional() });
12373
+ var V2_NESTED_LEAVES = {
12374
+ string: NestedStringPropertySchema,
12375
+ number: V2_LEAVES.number.extend({ group: ConfigurationGroupSchema.optional() }),
12376
+ boolean: V2_LEAVES.boolean.extend({ group: ConfigurationGroupSchema.optional() }),
12377
+ array: V2_LEAVES.array.extend({ group: ConfigurationGroupSchema.optional() }),
12378
+ items: V2_LEAVES.items
12319
12379
  };
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
- ]);
12380
+ function childBagShape(child) {
12381
+ return {
12382
+ properties: external_exports.record(PropertyNameSchema, child),
12383
+ required: external_exports.array(PropertyNameSchema).max(MAX_CONFIGURATION_CHILDREN).optional(),
12384
+ /**
12385
+ * All-or-none rules, scoped to THIS container.
12386
+ *
12387
+ * ON THE CONTAINER, NEVER AS ROOT CLAUSES NAMING PATHS. The published key
12388
+ * promises that "a publisher running an off-the-shelf validator over that
12389
+ * file must get the answer the platform gives", and JSON Schema 2020-12
12390
+ * scopes `dependentRequired` to the object that CARRIES it. A root clause
12391
+ * naming `/upstream/token` would be a rule no standard validator applies —
12392
+ * the one thing this key may not become. Written here it keeps exactly the
12393
+ * meaning it has everywhere else, and the generated artifact carries it on
12394
+ * the right node.
12395
+ *
12396
+ * A REPEATABLE GROUP GETS ONE PER ROW, which is the honest reading: "if this
12397
+ * server has a URL it needs a token" is a fact about a server, not about the
12398
+ * list.
12399
+ */
12400
+ dependentRequired: external_exports.record(PropertyNameSchema, external_exports.array(PropertyNameSchema).min(1).max(MAX_CONFIGURATION_CHILDREN)).optional().describe("Settings inside this group that must be supplied together."),
12401
+ additionalProperties: external_exports.literal(false)
12402
+ };
12403
+ }
12404
+ function propertyUnion(levelsBelow, leafSet) {
12405
+ const leaves = [leafSet.string, leafSet.number, leafSet.boolean];
12406
+ if (levelsBelow <= 0) {
12407
+ return [...leaves, leafSet.array];
12408
+ }
12409
+ const level = MAX_CONFIGURATION_DEPTH - levelsBelow + 1;
12410
+ const child = withJsonSchemaDefinition(external_exports.discriminatedUnion("type", propertyUnion(levelsBelow - 1, V2_NESTED_LEAVES)), `configurationPropertyLevel${level}`);
12411
+ const bag = childBagShape(child);
12412
+ const groupItems = external_exports.object({ type: external_exports.literal("object"), ...bag }).strict();
12413
+ return [
12414
+ ...leaves,
12415
+ // ONE `array` BRANCH, NOT TWO, because a discriminated union may not carry
12416
+ // two options with the same discriminator value ("duplicate value array" —
12417
+ // zod refuses it at construction). The item shape is the inner
12418
+ // discriminator, which is also what keeps a v1 document's
12419
+ // `items: {type: 'string'}` reading exactly as it always did.
12420
+ leafSet.array.extend({
12421
+ items: external_exports.discriminatedUnion("type", [V2_LEAVES.items, groupItems])
12422
+ }),
12423
+ /**
12424
+ * A group of related settings.
12425
+ *
12426
+ * NO `default`. A container cannot carry one, structurally — `.strict()`
12427
+ * refuses the key — and that removes a whole class of question rather than
12428
+ * answering it: what a whole-subtree default means when the customer
12429
+ * supplied half of it, how it interacts with `dependentRequired`'s
12430
+ * load-bearing "a defaulted setting is never absent", and what
12431
+ * `seedConfiguration` should write into a served example and a file on
12432
+ * disk. Defaults are a LEAF concept here.
12433
+ */
12434
+ external_exports.object({
12435
+ type: external_exports.literal("object"),
12436
+ title: TitleSchema,
12437
+ description: helpTextSchema(V2_LEAF_BOUNDS).optional(),
12438
+ // INHERITED BELOW THE ROOT, for the reason `V2_NESTED_LEAVES` states: a
12439
+ // group is a bucket for a whole subtree, and repeating it is the only
12440
+ // value that could be written.
12441
+ group: levelsBelow === MAX_CONFIGURATION_DEPTH - 1 ? ConfigurationGroupSchema : ConfigurationGroupSchema.optional(),
12442
+ ...bag
12443
+ }).strict()
12444
+ ];
12445
+ }
12446
+ var RootConfigurationPropertySchema = external_exports.discriminatedUnion("type", propertyUnion(MAX_CONFIGURATION_DEPTH - 1, V2_LEAVES));
12341
12447
  var CustomerConfigurationObjectSchema = external_exports.object({
12342
12448
  type: external_exports.literal("object"),
12343
12449
  properties: external_exports.record(PropertyNameSchema, ConfigurationPropertySchema).describe("Customer-editable settings, keyed by camelCase property name."),
@@ -12568,19 +12674,19 @@ function checkDeclaredProperty(name, property, path, ctx, atRoot) {
12568
12674
  }
12569
12675
  }
12570
12676
  }
12571
- function checkDependentRequired(config, ctx) {
12677
+ function checkDependentRequired(config, ctx, at = []) {
12572
12678
  const clauses = Object.entries(config.dependentRequired ?? {});
12573
12679
  if (clauses.length === 0)
12574
12680
  return;
12575
12681
  if (clauses.length > MAX_CONFIGURATION_PROPERTIES) {
12576
12682
  ctx.addIssue({
12577
12683
  code: external_exports.ZodIssueCode.custom,
12578
- path: ["dependentRequired"],
12684
+ path: [...at, "dependentRequired"],
12579
12685
  message: `A Product may declare at most ${MAX_CONFIGURATION_PROPERTIES} dependent-required groups; found ${clauses.length}.`
12580
12686
  });
12581
12687
  }
12582
12688
  for (const [trigger, dependents] of clauses) {
12583
- const path = ["dependentRequired", trigger];
12689
+ const path = [...at, "dependentRequired", trigger];
12584
12690
  const declaredTrigger = config.properties[trigger];
12585
12691
  if (declaredTrigger === void 0) {
12586
12692
  ctx.addIssue({
@@ -12692,7 +12798,7 @@ function checkOneSecretPerGroup(config, ctx) {
12692
12798
  var CustomerConfigurationV2ObjectSchema = external_exports.object({
12693
12799
  type: external_exports.literal("object"),
12694
12800
  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(),
12801
+ required: external_exports.array(PropertyNameSchema).max(MAX_CONFIGURATION_CHILDREN).optional(),
12696
12802
  /**
12697
12803
  * ROOT PROPERTIES ONLY, in v2.0.
12698
12804
  *
@@ -12704,23 +12810,25 @@ var CustomerConfigurationV2ObjectSchema = external_exports.object({
12704
12810
  * nested object may not carry its own clause either — that is expressible
12705
12811
  * and honest, and it is deliberately left for when something needs it.
12706
12812
  */
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."),
12813
+ dependentRequired: external_exports.record(PropertyNameSchema, external_exports.array(PropertyNameSchema).min(1).max(MAX_CONFIGURATION_CHILDREN)).optional().describe("Settings that must be supplied together, keyed by the triggering property."),
12708
12814
  additionalProperties: external_exports.literal(false)
12709
12815
  }).strict();
12710
12816
  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 };
12817
+ const children = childrenOf(property);
12818
+ if (children === null)
12819
+ return { leaves: 1, nodes: 1, instantiated: 1 };
12820
+ let leaves = 0;
12821
+ let nodes = 1;
12822
+ let instantiated = 0;
12823
+ for (const child of Object.values(children.properties)) {
12824
+ const inner = countNodes(child);
12825
+ leaves += inner.leaves;
12826
+ nodes += inner.nodes;
12827
+ instantiated += inner.instantiated;
12828
+ }
12829
+ if (property.type === "array")
12830
+ instantiated *= property.maxItems;
12831
+ return { leaves, nodes, instantiated };
12724
12832
  }
12725
12833
  function childrenOf(property) {
12726
12834
  if (property.type === "object")
@@ -12730,34 +12838,26 @@ function childrenOf(property) {
12730
12838
  return null;
12731
12839
  }
12732
12840
  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;
12841
+ return minimumGroupBytes(config.properties, config.required ?? []);
12749
12842
  }
12750
12843
  function minimumGroupBytes(properties, required) {
12751
12844
  const names = new Set(required);
12752
12845
  let total = 2;
12753
12846
  for (const [name, property] of Object.entries(properties)) {
12754
- if (!names.has(name) && declaredLeafDefault(property) === void 0)
12847
+ const isContainer = childrenOf(property) !== null;
12848
+ if (isContainer ? !names.has(name) : !names.has(name) && declaredLeafDefault(property) === void 0) {
12755
12849
  continue;
12756
- total += name.length + 3 + minimumLeafBytes(property);
12850
+ }
12851
+ total += name.length + 3 + minimumPropertyBytes(property);
12757
12852
  }
12758
12853
  return total;
12759
12854
  }
12760
- function minimumLeafBytes(property) {
12855
+ function minimumPropertyBytes(property) {
12856
+ const children = childrenOf(property);
12857
+ if (children !== null) {
12858
+ const inner = minimumGroupBytes(children.properties, children.required ?? []);
12859
+ return property.type === "array" ? 2 + (property.minItems ?? 0) * inner : inner;
12860
+ }
12761
12861
  switch (property.type) {
12762
12862
  case "string": {
12763
12863
  const shortestEnum = property.enum?.reduce((a, b) => a.length <= b.length ? a : b);
@@ -12778,13 +12878,85 @@ function minimumLeafBytes(property) {
12778
12878
  function declaredLeafDefault(property) {
12779
12879
  return property.type === "object" ? void 0 : property.default;
12780
12880
  }
12881
+ function checkPropertyDeclaration(name, property, path, ctx, atRoot) {
12882
+ const children = childrenOf(property);
12883
+ if (children === null) {
12884
+ checkDeclaredProperty(name, property, path, ctx, atRoot);
12885
+ return;
12886
+ }
12887
+ const infrastructure = infrastructureVocabularyViolation(name);
12888
+ if (infrastructure) {
12889
+ ctx.addIssue({
12890
+ code: external_exports.ZodIssueCode.custom,
12891
+ path,
12892
+ message: `${infrastructure}. Customer settings describe outcomes, not infrastructure.`
12893
+ });
12894
+ }
12895
+ if (atRoot) {
12896
+ const platformOwned = platformOwnedFieldViolation(name);
12897
+ if (platformOwned) {
12898
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path, message: `${platformOwned}.` });
12899
+ }
12900
+ }
12901
+ const childNames = Object.keys(children.properties);
12902
+ if (childNames.length === 0) {
12903
+ ctx.addIssue({
12904
+ code: external_exports.ZodIssueCode.custom,
12905
+ path: [...path, "properties"],
12906
+ message: `"${name}" declares no settings. A group with nothing in it renders as an empty section.`
12907
+ });
12908
+ }
12909
+ if (childNames.length > MAX_CONFIGURATION_CHILDREN) {
12910
+ ctx.addIssue({
12911
+ code: external_exports.ZodIssueCode.custom,
12912
+ path: [...path, "properties"],
12913
+ message: `"${name}" may declare at most ${MAX_CONFIGURATION_CHILDREN} settings; found ${childNames.length}.`
12914
+ });
12915
+ }
12916
+ if (property.type === "array" && property.default !== void 0) {
12917
+ ctx.addIssue({
12918
+ code: external_exports.ZodIssueCode.custom,
12919
+ path: [...path, "default"],
12920
+ message: `"${name}" is a list of groups and cannot declare a default. Declare defaults on the settings inside it.`
12921
+ });
12922
+ }
12923
+ if (property.type === "array" && property.minItems !== void 0 && property.minItems > property.maxItems) {
12924
+ ctx.addIssue({
12925
+ code: external_exports.ZodIssueCode.custom,
12926
+ path,
12927
+ message: `"${name}" has minItems greater than maxItems.`
12928
+ });
12929
+ }
12930
+ for (const childName of childNames) {
12931
+ checkPropertyDeclaration(childName, children.properties[childName], [...path, "properties", childName], ctx, false);
12932
+ }
12933
+ for (const required of children.required ?? []) {
12934
+ if (!Object.hasOwn(children.properties, required)) {
12935
+ ctx.addIssue({
12936
+ code: external_exports.ZodIssueCode.custom,
12937
+ path: [...path, "required"],
12938
+ message: `"${required}" is listed as required in "${name}" but is not one of its settings.`
12939
+ });
12940
+ }
12941
+ }
12942
+ const scoped = children;
12943
+ if (scoped.dependentRequired !== void 0) {
12944
+ checkDependentRequired(
12945
+ { properties: children.properties, dependentRequired: scoped.dependentRequired },
12946
+ ctx,
12947
+ // An ARRAY's children live under `items`, which is where the key it
12948
+ // constrains actually sits in the document.
12949
+ property.type === "array" ? [...path, "items"] : path
12950
+ );
12951
+ }
12952
+ }
12781
12953
  var CustomerConfigurationV2Schema = CustomerConfigurationV2ObjectSchema.superRefine((config, ctx) => {
12782
12954
  const names = Object.keys(config.properties);
12783
- if (names.length > MAX_CONFIGURATION_PROPERTIES) {
12955
+ if (names.length > MAX_CONFIGURATION_CHILDREN) {
12784
12956
  ctx.addIssue({
12785
12957
  code: external_exports.ZodIssueCode.custom,
12786
12958
  path: ["properties"],
12787
- message: `A Product may declare at most ${MAX_CONFIGURATION_PROPERTIES} customer settings; found ${names.length}.`
12959
+ message: `A Product may declare at most ${MAX_CONFIGURATION_CHILDREN} root settings; found ${names.length}. Group them, or declare fewer.`
12788
12960
  });
12789
12961
  }
12790
12962
  let leaves = 0;
@@ -12797,64 +12969,7 @@ var CustomerConfigurationV2Schema = CustomerConfigurationV2ObjectSchema.superRef
12797
12969
  leaves += counted.leaves;
12798
12970
  nodes += counted.nodes;
12799
12971
  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
- }
12972
+ checkPropertyDeclaration(name, property, path, ctx, true);
12858
12973
  }
12859
12974
  if (leaves > MAX_CONFIGURATION_LEAVES) {
12860
12975
  ctx.addIssue({
@@ -12874,7 +12989,7 @@ var CustomerConfigurationV2Schema = CustomerConfigurationV2ObjectSchema.superRef
12874
12989
  ctx.addIssue({
12875
12990
  code: external_exports.ZodIssueCode.custom,
12876
12991
  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.`
12992
+ message: `This Product's settings could expand to ${instantiated} values. A list instantiates everything inside it once per item, so lists inside lists MULTIPLY. At most ${MAX_CONFIGURATION_VALUE_LEAVES} are allowed; reduce maxItems on the lists, or the number of settings inside them.`
12878
12993
  });
12879
12994
  }
12880
12995
  for (const name of config.required ?? []) {
@@ -12926,7 +13041,7 @@ function isSecretProperty(property) {
12926
13041
  }
12927
13042
 
12928
13043
  // ../product-configuration/dist/configuration-path.js
12929
- var MAX_CONFIGURATION_PATH_SEGMENTS = MAX_CONFIGURATION_DEPTH + 1;
13044
+ var MAX_CONFIGURATION_PATH_SEGMENTS = 2 * MAX_CONFIGURATION_DEPTH - 1;
12930
13045
  function isIndexSegment(raw) {
12931
13046
  return /^\d+$/.test(raw) && String(Number(raw)) === raw;
12932
13047
  }
@@ -12960,6 +13075,9 @@ function pathSegments(path) {
12960
13075
  const raw = path.startsWith("/") ? path.slice(1).split("/") : [path];
12961
13076
  return raw.map((segment) => isIndexSegment(segment) ? Number(segment) : segment);
12962
13077
  }
13078
+ function rootSegment(path) {
13079
+ return String(pathSegments(path)[0]);
13080
+ }
12963
13081
  function declarationPathOf(path) {
12964
13082
  const segments = pathSegments(path).filter((segment) => typeof segment !== "number");
12965
13083
  return configurationPath(...segments) ?? path;
@@ -13003,6 +13121,9 @@ function canonicalJson(value) {
13003
13121
  }
13004
13122
 
13005
13123
  // ../product-configuration/dist/configuration-tree.js
13124
+ function underList(node) {
13125
+ return node.containers.some((container) => container.kind === "array");
13126
+ }
13006
13127
  function containerChildren(property) {
13007
13128
  if (property.type === "object" && property.properties !== void 0) {
13008
13129
  return {
@@ -13028,52 +13149,38 @@ function isConfigurationContainer(property) {
13028
13149
  }
13029
13150
  function configurationLeaves(schema) {
13030
13151
  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)
13042
- continue;
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
- }
13056
- }
13152
+ walkDeclarations(schema.properties, [], [], (leaf) => out.push(leaf));
13057
13153
  return out;
13058
13154
  }
13059
13155
  function configurationContainers(schema) {
13060
13156
  const out = [];
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);
13157
+ walkDeclarations(schema.properties, [], [], () => {
13158
+ }, (container) => out.push(container));
13159
+ return out;
13160
+ }
13161
+ function walkDeclarations(properties, ancestors, base, onLeaf, onContainer = () => {
13162
+ }) {
13163
+ for (const [name, property] of Object.entries(properties)) {
13164
+ const segments = [...base, name];
13165
+ const path = configurationPath(...segments);
13066
13166
  if (path === null)
13067
13167
  continue;
13068
- out.push({
13168
+ const children = containerChildren(property);
13169
+ if (children === null) {
13170
+ onLeaf({ path, name, property, containers: ancestors });
13171
+ continue;
13172
+ }
13173
+ const container = {
13069
13174
  path,
13070
13175
  name,
13071
13176
  kind: children.kind,
13072
13177
  property,
13073
- required: children.required
13074
- });
13178
+ required: children.required,
13179
+ containers: ancestors
13180
+ };
13181
+ onContainer(container);
13182
+ walkDeclarations(children.properties, [...ancestors, container], segments, onLeaf, onContainer);
13075
13183
  }
13076
- return out;
13077
13184
  }
13078
13185
  function leafAt(schema, path) {
13079
13186
  const declaration = declarationPathOf(path);
@@ -13084,11 +13191,71 @@ function declaresPath(schema, path) {
13084
13191
  return true;
13085
13192
  return configurationContainers(schema).some((container) => container.path === declarationPathOf(path));
13086
13193
  }
13194
+ function isPlainObject(value) {
13195
+ return typeof value === "object" && value !== null && !Array.isArray(value);
13196
+ }
13197
+ function valueAt(configuration, path) {
13198
+ let cursor = configuration;
13199
+ for (const segment of segmentsOf(path)) {
13200
+ if (typeof segment === "number") {
13201
+ if (!Array.isArray(cursor))
13202
+ return void 0;
13203
+ cursor = cursor[segment];
13204
+ continue;
13205
+ }
13206
+ if (!isPlainObject(cursor))
13207
+ return void 0;
13208
+ cursor = cursor[segment];
13209
+ }
13210
+ return cursor;
13211
+ }
13212
+ function valuesAt(configuration, path) {
13213
+ let cursors = [configuration];
13214
+ for (const segment of segmentsOf(path)) {
13215
+ const next = [];
13216
+ for (const cursor of cursors) {
13217
+ if (typeof segment === "number") {
13218
+ if (Array.isArray(cursor))
13219
+ next.push(cursor[segment]);
13220
+ continue;
13221
+ }
13222
+ if (!isPlainObject(cursor))
13223
+ continue;
13224
+ const value = cursor[segment];
13225
+ if (Array.isArray(value))
13226
+ next.push(...value);
13227
+ else
13228
+ next.push(value);
13229
+ }
13230
+ cursors = next;
13231
+ }
13232
+ return cursors.filter((value) => value !== void 0);
13233
+ }
13234
+ function segmentsOf(path) {
13235
+ const raw = path.startsWith("/") ? path.slice(1).split("/") : [path];
13236
+ return raw.map((segment) => /^\d+$/.test(segment) ? Number(segment) : segment);
13237
+ }
13238
+
13239
+ // ../product-configuration/dist/value-condition.js
13240
+ function conditionSetting(condition) {
13241
+ return "path" in condition ? condition.path : condition.property;
13242
+ }
13243
+ function conditionRoot(condition) {
13244
+ const path = parseConfigurationPath(conditionSetting(condition));
13245
+ return path === null ? conditionSetting(condition) : rootSegment(path);
13246
+ }
13247
+ function conditionHolds(condition, configuration) {
13248
+ const path = parseConfigurationPath(conditionSetting(condition));
13249
+ if (path === null)
13250
+ return false;
13251
+ const matches2 = (held) => typeof held === "string" && condition.anyOf.includes(held);
13252
+ return "anyRow" in condition && condition.anyRow === true ? valuesAt(configuration, path).some(matches2) : matches2(valueAt(configuration, path));
13253
+ }
13087
13254
 
13088
13255
  // ../product-configuration/dist/customer-configuration-input.js
13089
- var ABSOLUTE_MAX_STRING = 8192;
13256
+ var ABSOLUTE_MAX_STRING = MAX_DECLARABLE_STRING_LENGTH;
13090
13257
  var MAX_REPORTED_UNDECLARED_KEYS = 10;
13091
- function isPlainObject(value) {
13258
+ function isPlainObject2(value) {
13092
13259
  return typeof value === "object" && value !== null && !Array.isArray(value);
13093
13260
  }
13094
13261
  function report(walk, field, message) {
@@ -13110,17 +13277,17 @@ function validateSubmittedConfiguration(schema, submitted, presence = {}) {
13110
13277
  referenced: new Set(presence.referenced ?? [])
13111
13278
  };
13112
13279
  const supplied = /* @__PURE__ */ new Set();
13113
- if (submitted !== void 0 && submitted !== null && !isPlainObject(submitted)) {
13280
+ if (submitted !== void 0 && submitted !== null && !isPlainObject2(submitted)) {
13114
13281
  return {
13115
13282
  ok: false,
13116
13283
  issues: [{ field: "", message: "Configuration must be an object." }],
13117
13284
  partial: {}
13118
13285
  };
13119
13286
  }
13120
- const input = isPlainObject(submitted) ? submitted : {};
13287
+ const input = isPlainObject2(submitted) ? submitted : {};
13121
13288
  const secrets = {};
13122
13289
  const configuration = validateObject({ properties: schema.properties, required: schema.required }, input, null, walk, { secrets, supplied });
13123
- drain(walk, crossFieldIssues(schema, supplied, presence));
13290
+ drain(walk, crossFieldIssues(schema, supplied, presence, configuration));
13124
13291
  if (walk.suppressed > 0) {
13125
13292
  walk.issues.push({
13126
13293
  field: "",
@@ -13228,6 +13395,18 @@ function validateObject(declaration, input, container, walk, root) {
13228
13395
  }
13229
13396
  out[name] = checked;
13230
13397
  }
13398
+ if (declaration.dependentRequired !== void 0) {
13399
+ const suppliedHere = new Set(Object.keys(out));
13400
+ for (const [trigger, dependents] of Object.entries(declaration.dependentRequired)) {
13401
+ if (!suppliedHere.has(trigger) && !containsReferenced(walk, fieldFor(trigger)))
13402
+ continue;
13403
+ for (const dependent of dependents) {
13404
+ if (suppliedHere.has(dependent) || containsReferenced(walk, fieldFor(dependent)))
13405
+ continue;
13406
+ report(walk, fieldFor(dependent), `This setting is required when ${labelOf(declaration, trigger)} is configured.`);
13407
+ }
13408
+ }
13409
+ }
13231
13410
  return out;
13232
13411
  }
13233
13412
  function containerDeclaration(property) {
@@ -13236,6 +13415,9 @@ function containerDeclaration(property) {
13236
13415
  kind: "object",
13237
13416
  properties: property.properties,
13238
13417
  required: property.required,
13418
+ // CARRIED THROUGH, or the rule the container declares is one the walk
13419
+ // never sees — it publishes cleanly and enforces nothing.
13420
+ dependentRequired: property.dependentRequired,
13239
13421
  maxItems: 1
13240
13422
  };
13241
13423
  }
@@ -13247,6 +13429,7 @@ function containerDeclaration(property) {
13247
13429
  kind: "array",
13248
13430
  properties: items.properties,
13249
13431
  required: items.required,
13432
+ dependentRequired: items.dependentRequired,
13250
13433
  maxItems: property.maxItems ?? 0,
13251
13434
  minItems: property.minItems,
13252
13435
  uniqueItems: property.uniqueItems === true
@@ -13259,7 +13442,7 @@ function validateContainer(name, property, children, value, field, walk) {
13259
13442
  if (children.kind === "object") {
13260
13443
  if (value === void 0)
13261
13444
  return void 0;
13262
- if (!isPlainObject(value)) {
13445
+ if (!isPlainObject2(value)) {
13263
13446
  report(walk, field, "Expected a group of settings.");
13264
13447
  return void 0;
13265
13448
  }
@@ -13286,7 +13469,7 @@ function validateContainer(name, property, children, value, field, walk) {
13286
13469
  report(walk, field, "This configuration has too many values.");
13287
13470
  break;
13288
13471
  }
13289
- if (!isPlainObject(element)) {
13472
+ if (!isPlainObject2(element)) {
13290
13473
  report(walk, `${field}`, `Item ${index + 1} is not a group of settings.`);
13291
13474
  continue;
13292
13475
  }
@@ -13302,9 +13485,10 @@ function validateContainer(name, property, children, value, field, walk) {
13302
13485
  }
13303
13486
  return out;
13304
13487
  }
13305
- function crossFieldIssues(schema, supplied, presence) {
13488
+ function crossFieldIssues(schema, supplied, presence, configuration = {}) {
13306
13489
  const clauses = Object.entries(schema.dependentRequired ?? {});
13307
- if (clauses.length === 0)
13490
+ const conditional = Object.entries(schema.properties).filter(([, property]) => conditionOf(property) !== void 0);
13491
+ if (clauses.length === 0 && conditional.length === 0)
13308
13492
  return [];
13309
13493
  const alsoPresent = /* @__PURE__ */ new Set([...presence.present ?? [], ...presence.referenced ?? []]);
13310
13494
  const unknown = new Set(presence.unknown ?? []);
@@ -13327,13 +13511,42 @@ function crossFieldIssues(schema, supplied, presence) {
13327
13511
  });
13328
13512
  }
13329
13513
  }
13514
+ for (const [name, property] of conditional) {
13515
+ const condition = conditionOf(property);
13516
+ if (condition === void 0 || !known(name) || held(name))
13517
+ continue;
13518
+ if (!conditionHolds(condition, configuration))
13519
+ continue;
13520
+ if (reported.has(name))
13521
+ continue;
13522
+ reported.add(name);
13523
+ issues.push({
13524
+ field: name,
13525
+ // THE SETTING THAT MADE IT NECESSARY, never the value it holds. A message
13526
+ // naming the value would be fine here and wrong one refactor later, when
13527
+ // somebody conditions a secret on another secret — which the publish rules
13528
+ // refuse precisely because nothing may read one.
13529
+ message: `This setting is required because of what ${labelOf(schema, conditionRoot(condition))} is set to.`
13530
+ });
13531
+ }
13330
13532
  return issues;
13331
13533
  }
13534
+ function conditionOf(property) {
13535
+ return property?.["x-required-when"];
13536
+ }
13332
13537
  function labelOf(schema, name) {
13333
13538
  const property = schema.properties[name];
13334
- return property === void 0 ? `"${name}"` : `"${property.title}"`;
13539
+ return property?.title === void 0 ? `"${name}"` : `"${property.title}"`;
13335
13540
  }
13336
13541
  function withoutCrossFieldRules(schema) {
13542
+ const conditioned = Object.entries(schema.properties).some(([, property]) => property["x-required-when"] !== void 0);
13543
+ if (conditioned) {
13544
+ const properties = Object.fromEntries(Object.entries(schema.properties).map(([name, property]) => {
13545
+ const { "x-required-when": _dropped, ...bare } = property;
13546
+ return [name, bare];
13547
+ }));
13548
+ return withoutCrossFieldRules({ ...schema, properties });
13549
+ }
13337
13550
  if (schema.dependentRequired === void 0)
13338
13551
  return schema;
13339
13552
  const { dependentRequired: _removed, ...rest } = schema;
@@ -13489,7 +13702,6 @@ function isSecret(declaration) {
13489
13702
  function seedConfiguration(configuration) {
13490
13703
  const properties = configuration?.properties ?? {};
13491
13704
  const required = new Set(configuration?.required ?? []);
13492
- const values = {};
13493
13705
  const open = [];
13494
13706
  const requiredSecrets = [];
13495
13707
  const groups = Object.entries(configuration?.dependentRequired ?? {}).map(([trigger, dependents]) => {
@@ -13500,8 +13712,13 @@ function seedConfiguration(configuration) {
13500
13712
  secrets: members.filter((name) => isSecret(properties[name] ?? {}))
13501
13713
  };
13502
13714
  }).sort((a, b) => a.trigger.localeCompare(b.trigger));
13715
+ const values = seedProperties(properties, required, [], open, requiredSecrets);
13716
+ return { values, open, requiredSecrets, groups };
13717
+ }
13718
+ function seedProperties(properties, required, ancestors, open, requiredSecrets) {
13719
+ const values = {};
13503
13720
  for (const [name, declaration] of Object.entries(properties)) {
13504
- if (isSecret(declaration)) {
13721
+ if (requiredSecrets !== null && isSecret(declaration)) {
13505
13722
  if (required.has(name))
13506
13723
  requiredSecrets.push(name);
13507
13724
  continue;
@@ -13510,12 +13727,14 @@ function seedConfiguration(configuration) {
13510
13727
  if (children !== null) {
13511
13728
  if (!required.has(name))
13512
13729
  continue;
13730
+ const childRequired = new Set(children.required);
13731
+ const inner = [...ancestors, name];
13513
13732
  if (children.kind === "object") {
13514
- values[name] = seedGroup(children, name, open);
13733
+ values[name] = seedProperties(children.properties, childRequired, inner, open, null);
13515
13734
  continue;
13516
13735
  }
13517
13736
  const count = children.minItems ?? 0;
13518
- values[name] = Array.from({ length: count }, () => seedGroup(children, name, open));
13737
+ values[name] = Array.from({ length: count }, () => seedProperties(children.properties, childRequired, inner, open, null));
13519
13738
  if (count > 1)
13520
13739
  dedupeOpen(open);
13521
13740
  continue;
@@ -13526,10 +13745,15 @@ function seedConfiguration(configuration) {
13526
13745
  }
13527
13746
  if (required.has(name)) {
13528
13747
  values[name] = "";
13529
- open.push({ name, label: name, type: declaration.type ?? null });
13748
+ const path = configurationPath(...ancestors, name);
13749
+ open.push({
13750
+ name: path ?? name,
13751
+ label: path === null ? name : printableConfigurationPath(path),
13752
+ type: declaration.type ?? null
13753
+ });
13530
13754
  }
13531
13755
  }
13532
- return { values, open, requiredSecrets, groups };
13756
+ return values;
13533
13757
  }
13534
13758
  function containerChildren2(declaration) {
13535
13759
  const node = declaration;
@@ -13564,26 +13788,6 @@ function dedupeOpen(open) {
13564
13788
  open.length = 0;
13565
13789
  open.push(...unique);
13566
13790
  }
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
- }
13587
13791
  function productInstanceExample(input) {
13588
13792
  return {
13589
13793
  apiVersion: RESOURCE_API_VERSION,
@@ -14506,7 +14710,7 @@ function checkProductDefinition(definition, ctx) {
14506
14710
  });
14507
14711
  return;
14508
14712
  }
14509
- if (leaf.container?.kind === "array") {
14713
+ if (underList(leaf)) {
14510
14714
  ctx.addIssue({
14511
14715
  code: external_exports.ZodIssueCode.custom,
14512
14716
  path: [...at, member],
@@ -14545,6 +14749,91 @@ function checkProductDefinition(definition, ctx) {
14545
14749
  });
14546
14750
  }
14547
14751
  });
14752
+ for (const [name, property] of Object.entries(definition.configuration.properties ?? {})) {
14753
+ const condition = property["x-required-when"];
14754
+ if (condition === void 0)
14755
+ continue;
14756
+ const at = ["configuration", "properties", name, "x-required-when"];
14757
+ if (!isSecretProperty(property)) {
14758
+ ctx.addIssue({
14759
+ code: external_exports.ZodIssueCode.custom,
14760
+ path: [...at],
14761
+ 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.`
14762
+ });
14763
+ continue;
14764
+ }
14765
+ if (new Set(condition.anyOf).size !== condition.anyOf.length) {
14766
+ ctx.addIssue({
14767
+ code: external_exports.ZodIssueCode.custom,
14768
+ path: [...at, "anyOf"],
14769
+ message: "Repeated values in `anyOf`."
14770
+ });
14771
+ }
14772
+ if (condition.path === name) {
14773
+ ctx.addIssue({
14774
+ code: external_exports.ZodIssueCode.custom,
14775
+ path: [...at, "path"],
14776
+ message: `"${name}" cannot be conditioned on its own value.`
14777
+ });
14778
+ continue;
14779
+ }
14780
+ const parsed = parseConfigurationPath(condition.path);
14781
+ const leaf = parsed === null ? null : leafAt(definition.configuration, parsed);
14782
+ if (leaf === null) {
14783
+ const isGroup = parsed !== null && declaresPath(definition.configuration, parsed);
14784
+ ctx.addIssue({
14785
+ code: external_exports.ZodIssueCode.custom,
14786
+ path: [...at, "path"],
14787
+ message: isGroup ? `"${condition.path}" is a group of settings, which holds no single value to compare. Name one setting inside it.` : `"${condition.path}" is not a declared customer configuration property.`
14788
+ });
14789
+ continue;
14790
+ }
14791
+ if (underList(leaf) && condition.anyRow !== true) {
14792
+ ctx.addIssue({
14793
+ code: external_exports.ZodIssueCode.custom,
14794
+ path: [...at, "path"],
14795
+ message: `"${condition.path}" is inside a list, so it names one value per item rather than one value. Add "anyRow": true to require the secret when ANY row chose one of these values, or name a setting the instance holds exactly once.`
14796
+ });
14797
+ continue;
14798
+ }
14799
+ if (!underList(leaf) && condition.anyRow === true) {
14800
+ ctx.addIssue({
14801
+ code: external_exports.ZodIssueCode.custom,
14802
+ path: [...at, "anyRow"],
14803
+ message: `"${condition.path}" is not inside a list, so there are no rows to quantify over.`
14804
+ });
14805
+ }
14806
+ const target = leaf.property;
14807
+ if (target.type !== "string") {
14808
+ ctx.addIssue({
14809
+ code: external_exports.ZodIssueCode.custom,
14810
+ path: [...at, "path"],
14811
+ message: `"${condition.path}" is a ${target.type} setting; a condition compares string values.`
14812
+ });
14813
+ continue;
14814
+ }
14815
+ if (isSecretProperty(target)) {
14816
+ ctx.addIssue({
14817
+ code: external_exports.ZodIssueCode.custom,
14818
+ path: [...at, "path"],
14819
+ message: `"${condition.path}" is a secret. The platform never reads a secret's value, so a condition on one could never be evaluated.`
14820
+ });
14821
+ continue;
14822
+ }
14823
+ const single = withoutCrossFieldRules({
14824
+ ...definition.configuration,
14825
+ properties: { [leaf.name]: target },
14826
+ required: [leaf.name]
14827
+ });
14828
+ const outside = condition.anyOf.filter((value) => !validateSubmittedConfiguration(single, { [leaf.name]: value }).ok);
14829
+ if (outside.length > 0) {
14830
+ ctx.addIssue({
14831
+ code: external_exports.ZodIssueCode.custom,
14832
+ path: [...at, "anyOf"],
14833
+ message: `"${condition.path}" never accepts ${outside.map((v) => `"${v}"`).join(", ")}, so this condition could never make the secret required.`
14834
+ });
14835
+ }
14836
+ }
14548
14837
  const provides = definition.serviceInterfaces?.provides ?? [];
14549
14838
  const provideKeys = provides.map((provide) => provide.key);
14550
14839
  const duplicateProvideKeys = provideKeys.filter((key, i) => provideKeys.indexOf(key) !== i);
@@ -16535,6 +16824,9 @@ function isTerminalAction(action) {
16535
16824
  }
16536
16825
  var ManifestActionSchema = external_exports.enum(MANIFEST_ACTIONS);
16537
16826
 
16827
+ // ../product-contracts/dist/contract-issue.js
16828
+ var MAX_SEGMENTS = MAX_CONFIGURATION_PATH_SEGMENTS + 2;
16829
+
16538
16830
  // ../product-contracts/dist/resource-output-reference.js
16539
16831
  var OUTPUT_KEY_RULE = "Output keys are camelCase.";
16540
16832
  var ResourceOutputReferenceSchema = external_exports.object({
@@ -16925,7 +17217,7 @@ async function runApply(verb, parsed, deps = {}) {
16925
17217
  if (!planned.ok) return printError(planned.error, json);
16926
17218
  if (verb === "plan") {
16927
17219
  printPlan(planned.data, json);
16928
- if (isUnconverged(planned.data)) {
17220
+ if (isUnconverged(planned.data) && planned.data.action === "NO_CHANGE") {
16929
17221
  if (!json) {
16930
17222
  process.stderr.write(
16931
17223
  `${planned.data.manifestKey ?? "This resource"} is ${planned.data.health?.status ?? "not serving"}. Its desired state is applied; it is not running. \`vfac get operation <id>\` says why.
@@ -16934,6 +17226,12 @@ async function runApply(verb, parsed, deps = {}) {
16934
17226
  }
16935
17227
  return 1;
16936
17228
  }
17229
+ if (isUnconverged(planned.data) && !json) {
17230
+ process.stderr.write(
17231
+ `${planned.data.manifestKey ?? "This resource"} is ${planned.data.health?.status ?? "not serving"}. This plan changes it.
17232
+ `
17233
+ );
17234
+ }
16937
17235
  return 0;
16938
17236
  }
16939
17237
  const parsedAction = ManifestActionSchema.safeParse(planned.data.action);
@@ -17233,23 +17531,20 @@ async function runDoctor(parsed) {
17233
17531
  const compatible = compatibility(cliVersion(), probe.minimumCliVersion);
17234
17532
  checks.push(compatible);
17235
17533
  if (!compatible.ok) return report2(checks, json);
17236
- const signedIn = await signIn({ endpoint });
17237
- if (!signedIn.ok) {
17238
- checks.push({ name: "Context Gate", ok: false, detail: signedIn.message });
17534
+ const session = await ensureSession({ endpointFlag: parsed.flags["endpoint"] });
17535
+ if (!session.ok) {
17536
+ checks.push({ name: "Sign-in", ok: false, detail: session.message });
17239
17537
  return report2(checks, json);
17240
17538
  }
17241
17539
  checks.push({
17242
- name: "Context Gate",
17540
+ name: "Sign-in",
17243
17541
  ok: true,
17244
- detail: signedIn.gate ? `authenticated as "${signedIn.gate.name}" (${signedIn.gate.id})` : "authenticated"
17542
+ detail: session.ready.via === "stored" ? "a stored sign-in is still valid, so no credential was spent" : session.ready.gate ? `authenticated as "${session.ready.gate.name}" (${session.ready.gate.id})` : "authenticated"
17245
17543
  });
17246
- if (!sessionUsable(stored.session, endpoint, /* @__PURE__ */ new Date())) {
17247
- writeStored({ ...stored, session: signedIn.session });
17248
- }
17249
17544
  const who = await call({
17250
17545
  endpoint,
17251
17546
  path: "/api/v1/whoami",
17252
- token: signedIn.session.token
17547
+ token: session.ready.token
17253
17548
  });
17254
17549
  if (!who.ok) {
17255
17550
  checks.push({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@varde-flyt/vfac",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "Deploy and manage Varde Flyt Products from a manifest.",
5
5
  "repository": {
6
6
  "type": "git",