@varde-flyt/vfac 0.14.1 → 0.15.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -9
- package/dist/vfac.mjs +1195 -387
- package/package.json +1 -1
package/dist/vfac.mjs
CHANGED
|
@@ -12081,11 +12081,13 @@ function leafShapes(bounds) {
|
|
|
12081
12081
|
* particular value could not say so, so `vfac plan`'s `requiredSecrets`
|
|
12082
12082
|
* could not warn before a deploy, and the refusal arrived at apply.
|
|
12083
12083
|
*
|
|
12084
|
-
* ON A SECRET ONLY, and
|
|
12085
|
-
*
|
|
12086
|
-
*
|
|
12087
|
-
*
|
|
12088
|
-
*
|
|
12084
|
+
* IT BEGAN ON A ROOT SECRET ONLY, and was widened twice, each time as a
|
|
12085
|
+
* decision of its own: to every ROOT setting, which the number shape below
|
|
12086
|
+
* records, and then to a setting in a GROUP at any depth, which is where a
|
|
12087
|
+
* credential belonging to one sign-in method actually lives. It is still
|
|
12088
|
+
* refused on a setting BELOW A LIST, structurally — the leaf set the chain
|
|
12089
|
+
* uses there does not declare the key — because a setting there has one
|
|
12090
|
+
* path per row, and a condition makes one setting required.
|
|
12089
12091
|
*/
|
|
12090
12092
|
"x-required-when": ValueConditionSchema.optional(),
|
|
12091
12093
|
/**
|
|
@@ -12109,11 +12111,12 @@ function leafShapes(bounds) {
|
|
|
12109
12111
|
maximum: external_exports.number(),
|
|
12110
12112
|
default: external_exports.number().optional(),
|
|
12111
12113
|
/**
|
|
12112
|
-
* WIDENED FROM SECRETS TO EVERY ROOT SETTING, which
|
|
12113
|
-
* comment
|
|
12114
|
-
*
|
|
12115
|
-
*
|
|
12116
|
-
*
|
|
12114
|
+
* WIDENED FROM SECRETS TO EVERY ROOT SETTING, which the string leaf's
|
|
12115
|
+
* comment used to record as a known gap: "a value-conditioned requirement
|
|
12116
|
+
* for an ORDINARY setting is a real gap too". It is the same keyword, the
|
|
12117
|
+
* same evaluator and the same publish rules; what changes is which
|
|
12118
|
+
* settings may carry it — and a setting in a group may too, since the
|
|
12119
|
+
* chain's group leaf set keeps this key and only its list set omits it.
|
|
12117
12120
|
*/
|
|
12118
12121
|
"x-required-when": ValueConditionSchema.optional()
|
|
12119
12122
|
}).strict();
|
|
@@ -12158,14 +12161,33 @@ var ConfigurationPropertySchema = external_exports.discriminatedUnion("type", [
|
|
|
12158
12161
|
BooleanPropertySchema,
|
|
12159
12162
|
ArrayPropertySchema
|
|
12160
12163
|
]);
|
|
12161
|
-
var
|
|
12162
|
-
var
|
|
12163
|
-
string:
|
|
12164
|
-
number: V2_LEAVES.number.
|
|
12165
|
-
boolean: V2_LEAVES.boolean.
|
|
12166
|
-
array: V2_LEAVES.array.
|
|
12164
|
+
var INHERITED_GROUP = { group: ConfigurationGroupSchema.optional() };
|
|
12165
|
+
var V2_GROUP_LEAVES = {
|
|
12166
|
+
string: withJsonSchemaAnnex(V2_LEAVES.string.extend(INHERITED_GROUP), SECRET_COUPLING_ANNEX),
|
|
12167
|
+
number: V2_LEAVES.number.extend(INHERITED_GROUP),
|
|
12168
|
+
boolean: V2_LEAVES.boolean.extend(INHERITED_GROUP),
|
|
12169
|
+
array: V2_LEAVES.array.extend(INHERITED_GROUP),
|
|
12167
12170
|
items: V2_LEAVES.items
|
|
12168
12171
|
};
|
|
12172
|
+
var V2_LIST_LEAVES = {
|
|
12173
|
+
string: withJsonSchemaAnnex(V2_LEAVES.string.omit({ "x-required-when": true }).extend(INHERITED_GROUP), SECRET_COUPLING_ANNEX),
|
|
12174
|
+
number: V2_LEAVES.number.omit({ "x-required-when": true }).extend(INHERITED_GROUP),
|
|
12175
|
+
boolean: V2_LEAVES.boolean.omit({ "x-required-when": true }).extend(INHERITED_GROUP),
|
|
12176
|
+
array: V2_LEAVES.array.omit({ "x-required-when": true }).extend(INHERITED_GROUP),
|
|
12177
|
+
items: V2_LEAVES.items
|
|
12178
|
+
};
|
|
12179
|
+
var LIST_OF_GROUPS_CONDITION_ANNEX = {
|
|
12180
|
+
allOf: [
|
|
12181
|
+
{
|
|
12182
|
+
$comment: "A list of groups inside a group may not carry x-required-when: the condition makes a setting required, and a list of groups is not one.",
|
|
12183
|
+
if: {
|
|
12184
|
+
properties: { items: { properties: { type: { const: "object" } }, required: ["type"] } },
|
|
12185
|
+
required: ["items"]
|
|
12186
|
+
},
|
|
12187
|
+
then: { not: { required: ["x-required-when"] } }
|
|
12188
|
+
}
|
|
12189
|
+
]
|
|
12190
|
+
};
|
|
12169
12191
|
function childBagShape(child) {
|
|
12170
12192
|
return {
|
|
12171
12193
|
properties: external_exports.record(PropertyNameSchema, child),
|
|
@@ -12190,41 +12212,49 @@ function childBagShape(child) {
|
|
|
12190
12212
|
additionalProperties: external_exports.literal(false)
|
|
12191
12213
|
};
|
|
12192
12214
|
}
|
|
12193
|
-
|
|
12215
|
+
var LEAF_SETS = { root: V2_LEAVES, group: V2_GROUP_LEAVES, list: V2_LIST_LEAVES };
|
|
12216
|
+
var LEVELS = /* @__PURE__ */ new Map();
|
|
12217
|
+
function levelUnion(levelsBelow, position) {
|
|
12218
|
+
const key = `${position}:${levelsBelow}`;
|
|
12219
|
+
const built = LEVELS.get(key);
|
|
12220
|
+
if (built !== void 0)
|
|
12221
|
+
return built;
|
|
12222
|
+
const level = MAX_CONFIGURATION_DEPTH - levelsBelow;
|
|
12223
|
+
const union = withJsonSchemaDefinition(external_exports.discriminatedUnion("type", propertyUnion(levelsBelow, position)), position === "group" ? `configurationPropertyLevel${level}` : `configurationListPropertyLevel${level}`);
|
|
12224
|
+
LEVELS.set(key, union);
|
|
12225
|
+
return union;
|
|
12226
|
+
}
|
|
12227
|
+
function propertyUnion(levelsBelow, position) {
|
|
12228
|
+
const leafSet = LEAF_SETS[position];
|
|
12194
12229
|
const leaves = [leafSet.string, leafSet.number, leafSet.boolean];
|
|
12195
12230
|
if (levelsBelow <= 0) {
|
|
12196
12231
|
return [...leaves, leafSet.array];
|
|
12197
12232
|
}
|
|
12198
|
-
const
|
|
12199
|
-
const
|
|
12200
|
-
const
|
|
12201
|
-
const
|
|
12233
|
+
const groupChild = levelUnion(levelsBelow - 1, position === "list" ? "list" : "group");
|
|
12234
|
+
const rowChild = levelUnion(levelsBelow - 1, "list");
|
|
12235
|
+
const groupItems = external_exports.object({ type: external_exports.literal("object"), ...childBagShape(rowChild) }).strict();
|
|
12236
|
+
const list = leafSet.array.extend({
|
|
12237
|
+
items: external_exports.discriminatedUnion("type", [V2_LEAVES.items, groupItems]),
|
|
12238
|
+
/**
|
|
12239
|
+
* Rules over the ROWS of this list — see {@link RowRulesSchema}.
|
|
12240
|
+
*
|
|
12241
|
+
* HERE RATHER THAN ON THE LEAF SHAPE, and that placement is the whole
|
|
12242
|
+
* enforcement. `leafShapes().array` is also a v1 list and a last-level
|
|
12243
|
+
* list, both of which hold STRINGS: a rule naming a field of a row would
|
|
12244
|
+
* name nothing there, and `uniqueItems` is already the right keyword for
|
|
12245
|
+
* comparing whole scalar entries. This branch is the only one in the chain
|
|
12246
|
+
* where a list can hold groups, so declaring the keyword here makes "rows
|
|
12247
|
+
* have fields" a property of the shape rather than a rule to remember.
|
|
12248
|
+
*
|
|
12249
|
+
* The refinement still has to refuse a rule on a list of STRINGS, because
|
|
12250
|
+
* `items` is a discriminated union INSIDE this branch — the shape narrows
|
|
12251
|
+
* where a row can exist, not whether this particular list has one.
|
|
12252
|
+
*/
|
|
12253
|
+
"x-row-rules": RowRulesSchema.optional()
|
|
12254
|
+
});
|
|
12202
12255
|
return [
|
|
12203
12256
|
...leaves,
|
|
12204
|
-
|
|
12205
|
-
// two options with the same discriminator value ("duplicate value array" —
|
|
12206
|
-
// zod refuses it at construction). The item shape is the inner
|
|
12207
|
-
// discriminator, which is also what keeps a v1 document's
|
|
12208
|
-
// `items: {type: 'string'}` reading exactly as it always did.
|
|
12209
|
-
leafSet.array.extend({
|
|
12210
|
-
items: external_exports.discriminatedUnion("type", [V2_LEAVES.items, groupItems]),
|
|
12211
|
-
/**
|
|
12212
|
-
* Rules over the ROWS of this list — see {@link RowRulesSchema}.
|
|
12213
|
-
*
|
|
12214
|
-
* HERE RATHER THAN ON THE LEAF SHAPE, and that placement is the whole
|
|
12215
|
-
* enforcement. `leafShapes().array` is also a v1 list and a last-level
|
|
12216
|
-
* list, both of which hold STRINGS: a rule naming a field of a row would
|
|
12217
|
-
* name nothing there, and `uniqueItems` is already the right keyword for
|
|
12218
|
-
* comparing whole scalar entries. This branch is the only one in the chain
|
|
12219
|
-
* where a list can hold groups, so declaring the keyword here makes "rows
|
|
12220
|
-
* have fields" a property of the shape rather than a rule to remember.
|
|
12221
|
-
*
|
|
12222
|
-
* The refinement still has to refuse a rule on a list of STRINGS, because
|
|
12223
|
-
* `items` is a discriminated union INSIDE this branch — the shape narrows
|
|
12224
|
-
* where a row can exist, not whether this particular list has one.
|
|
12225
|
-
*/
|
|
12226
|
-
"x-row-rules": RowRulesSchema.optional()
|
|
12227
|
-
}),
|
|
12257
|
+
position === "group" ? withJsonSchemaAnnex(list, LIST_OF_GROUPS_CONDITION_ANNEX) : list,
|
|
12228
12258
|
/**
|
|
12229
12259
|
* A group of related settings.
|
|
12230
12260
|
*
|
|
@@ -12235,20 +12265,23 @@ function propertyUnion(levelsBelow, leafSet) {
|
|
|
12235
12265
|
* load-bearing "a defaulted setting is never absent", and what
|
|
12236
12266
|
* `seedConfiguration` should write into a served example and a file on
|
|
12237
12267
|
* disk. Defaults are a LEAF concept here.
|
|
12268
|
+
*
|
|
12269
|
+
* NO `x-required-when` EITHER, at any position: it makes a setting required,
|
|
12270
|
+
* and a group is not a setting.
|
|
12238
12271
|
*/
|
|
12239
12272
|
external_exports.object({
|
|
12240
12273
|
type: external_exports.literal("object"),
|
|
12241
12274
|
title: TitleSchema,
|
|
12242
12275
|
description: helpTextSchema(V2_LEAF_BOUNDS).optional(),
|
|
12243
|
-
// INHERITED BELOW THE ROOT, for the reason `
|
|
12276
|
+
// INHERITED BELOW THE ROOT, for the reason `INHERITED_GROUP` states: a
|
|
12244
12277
|
// group is a bucket for a whole subtree, and repeating it is the only
|
|
12245
12278
|
// value that could be written.
|
|
12246
|
-
group:
|
|
12247
|
-
...
|
|
12279
|
+
group: position === "root" ? ConfigurationGroupSchema : ConfigurationGroupSchema.optional(),
|
|
12280
|
+
...childBagShape(groupChild)
|
|
12248
12281
|
}).strict()
|
|
12249
12282
|
];
|
|
12250
12283
|
}
|
|
12251
|
-
var RootConfigurationPropertySchema = external_exports.discriminatedUnion("type", propertyUnion(MAX_CONFIGURATION_DEPTH - 1,
|
|
12284
|
+
var RootConfigurationPropertySchema = external_exports.discriminatedUnion("type", propertyUnion(MAX_CONFIGURATION_DEPTH - 1, "root"));
|
|
12252
12285
|
var CustomerConfigurationObjectSchema = external_exports.object({
|
|
12253
12286
|
type: external_exports.literal("object"),
|
|
12254
12287
|
properties: external_exports.record(PropertyNameSchema, ConfigurationPropertySchema).describe("Customer-editable settings, keyed by camelCase property name."),
|
|
@@ -12314,7 +12347,7 @@ var CustomerConfigurationSchema = CustomerConfigurationObjectSchema.superRefine(
|
|
|
12314
12347
|
}
|
|
12315
12348
|
checkDependentRequired(config, ctx);
|
|
12316
12349
|
});
|
|
12317
|
-
function checkDeclaredProperty(name, property, path, ctx, atRoot) {
|
|
12350
|
+
function checkDeclaredProperty(name, property, path, ctx, atRoot, underList2 = false) {
|
|
12318
12351
|
const infrastructure = infrastructureVocabularyViolation(name);
|
|
12319
12352
|
if (infrastructure) {
|
|
12320
12353
|
ctx.addIssue({
|
|
@@ -12348,13 +12381,20 @@ function checkDeclaredProperty(name, property, path, ctx, atRoot) {
|
|
|
12348
12381
|
ctx.addIssue({
|
|
12349
12382
|
code: external_exports.ZodIssueCode.custom,
|
|
12350
12383
|
path,
|
|
12351
|
-
// THE REMEDIATION HAS TO BE ONE THE CONTRACT ALLOWS.
|
|
12352
|
-
// answer is to declare the two markers. Below
|
|
12353
|
-
//
|
|
12354
|
-
//
|
|
12355
|
-
//
|
|
12356
|
-
//
|
|
12357
|
-
message:
|
|
12384
|
+
// THE REMEDIATION HAS TO BE ONE THE CONTRACT ALLOWS. In a group, as at
|
|
12385
|
+
// the root, the answer is to declare the two markers. Below a LIST that
|
|
12386
|
+
// answer is impossible — a secret is refused there, below — so telling a
|
|
12387
|
+
// publisher to add them sends them to write a second invalid document,
|
|
12388
|
+
// and an agent following the validator loops. A validator's errors are
|
|
12389
|
+
// part of the agent-facing contract.
|
|
12390
|
+
message: underList2 ? `${credential}, and a secret cannot be declared inside a list in this contract format \u2014 move it out of the list and declare it "x-secret": true with "writeOnly": true, or rename it if it is not one.` : `${credential} and must be declared "x-secret": true with "writeOnly": true, or renamed if it is not one.`
|
|
12391
|
+
});
|
|
12392
|
+
}
|
|
12393
|
+
if (underList2 && (secret || property.writeOnly === true)) {
|
|
12394
|
+
ctx.addIssue({
|
|
12395
|
+
code: external_exports.ZodIssueCode.custom,
|
|
12396
|
+
path,
|
|
12397
|
+
message: `"${name}": a secret cannot be declared inside a list in this contract format. Declare it in a group or at the root.`
|
|
12358
12398
|
});
|
|
12359
12399
|
}
|
|
12360
12400
|
if (secret && property.writeOnly !== true) {
|
|
@@ -12549,9 +12589,9 @@ function checkDependentRequired(config, ctx, at = []) {
|
|
|
12549
12589
|
}
|
|
12550
12590
|
}
|
|
12551
12591
|
}
|
|
12552
|
-
checkOneSecretPerGroup(config, ctx);
|
|
12592
|
+
checkOneSecretPerGroup(config, ctx, at);
|
|
12553
12593
|
}
|
|
12554
|
-
function checkOneSecretPerGroup(config, ctx) {
|
|
12594
|
+
function checkOneSecretPerGroup(config, ctx, at = []) {
|
|
12555
12595
|
const clauses = Object.entries(config.dependentRequired ?? {});
|
|
12556
12596
|
if (clauses.length === 0)
|
|
12557
12597
|
return;
|
|
@@ -12595,11 +12635,238 @@ function checkOneSecretPerGroup(config, ctx) {
|
|
|
12595
12635
|
code: external_exports.ZodIssueCode.custom,
|
|
12596
12636
|
// The component has no single name, so the issue is reported against the
|
|
12597
12637
|
// map rather than against whichever member happened to be visited first.
|
|
12598
|
-
path: ["dependentRequired"],
|
|
12638
|
+
path: [...at, "dependentRequired"],
|
|
12599
12639
|
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.`
|
|
12600
12640
|
});
|
|
12601
12641
|
}
|
|
12602
12642
|
}
|
|
12643
|
+
function checkOptionalGroupActivation(config, ctx) {
|
|
12644
|
+
const context = { root: config, acceptsOutside: /* @__PURE__ */ new Map() };
|
|
12645
|
+
const check = (container, at, segments) => {
|
|
12646
|
+
const required = new Set(container.required ?? []);
|
|
12647
|
+
for (const [name, property] of Object.entries(container.properties)) {
|
|
12648
|
+
if (property.type !== "object")
|
|
12649
|
+
continue;
|
|
12650
|
+
const path = [...at, "properties", name];
|
|
12651
|
+
const inner = [...segments, name];
|
|
12652
|
+
if (!required.has(name)) {
|
|
12653
|
+
const { secrets, conditional } = activationSecrets(context, property, inner);
|
|
12654
|
+
if (secrets.length > 1) {
|
|
12655
|
+
ctx.addIssue({
|
|
12656
|
+
code: external_exports.ZodIssueCode.custom,
|
|
12657
|
+
path,
|
|
12658
|
+
message: `"${name}" is optional, and adding it to a resource that already exists requires more than one secret (${secrets.join(", ")}). Only one secret can be supplied per change, so an optional group must ask for at most one \u2014 otherwise a resource created without it can never add it. Require at most one of them, or make the group required, with every group above it, so it is supplied when the resource is created.` + conditionalActivationSentence(conditional)
|
|
12659
|
+
});
|
|
12660
|
+
}
|
|
12661
|
+
}
|
|
12662
|
+
check(property, path, inner);
|
|
12663
|
+
}
|
|
12664
|
+
};
|
|
12665
|
+
check(config, [], []);
|
|
12666
|
+
}
|
|
12667
|
+
function conditionalActivationSentence(conditional) {
|
|
12668
|
+
if (conditional.length === 0)
|
|
12669
|
+
return "";
|
|
12670
|
+
const one = conditional.length === 1;
|
|
12671
|
+
return ` ${conditional.join(", ")} ${one ? "is" : "are"} counted although ${one ? "it is" : "they are"} conditional: adding the group always turns ${one ? "its" : "their"} "x-required-when" on, because the setting ${one ? "that condition reads" : "each condition reads"} is then present and no value it accepts outside "anyOf" was found. Let ${one ? "that" : "such a"} setting be left out, or declare an "enum" for it that includes a value outside "anyOf".`;
|
|
12672
|
+
}
|
|
12673
|
+
function declarationKey(segments) {
|
|
12674
|
+
return `/${segments.join("/")}`;
|
|
12675
|
+
}
|
|
12676
|
+
function activationSecrets(context, group, segments) {
|
|
12677
|
+
const carriers = conditionCarriersBelow(group, segments);
|
|
12678
|
+
const unavoidable = /* @__PURE__ */ new Set();
|
|
12679
|
+
const unconditioned = walkActivation(group, segments, unavoidable);
|
|
12680
|
+
let walk = unconditioned;
|
|
12681
|
+
for (; ; ) {
|
|
12682
|
+
const found = carriers.filter((carrier) => !unavoidable.has(carrier.key) && conditionUnavoidable(context, segments, carrier.condition, walk));
|
|
12683
|
+
if (found.length === 0)
|
|
12684
|
+
break;
|
|
12685
|
+
for (const carrier of found)
|
|
12686
|
+
unavoidable.add(carrier.key);
|
|
12687
|
+
walk = walkActivation(group, segments, unavoidable);
|
|
12688
|
+
}
|
|
12689
|
+
const secrets = [...walk.secrets].sort();
|
|
12690
|
+
return {
|
|
12691
|
+
secrets,
|
|
12692
|
+
conditional: secrets.filter((path) => unavoidable.has(path) && !unconditioned.secrets.has(path))
|
|
12693
|
+
};
|
|
12694
|
+
}
|
|
12695
|
+
function walkActivation(group, segments, unavoidable) {
|
|
12696
|
+
const secrets = /* @__PURE__ */ new Set();
|
|
12697
|
+
const demanded = /* @__PURE__ */ new Set();
|
|
12698
|
+
const sent = /* @__PURE__ */ new Set();
|
|
12699
|
+
const onTheWay = /* @__PURE__ */ new Set();
|
|
12700
|
+
for (const carrier of unavoidable) {
|
|
12701
|
+
const parts = carrier.slice(1).split("/");
|
|
12702
|
+
for (let depth = segments.length + 1; depth <= parts.length; depth += 1) {
|
|
12703
|
+
onTheWay.add(declarationKey(parts.slice(0, depth)));
|
|
12704
|
+
}
|
|
12705
|
+
}
|
|
12706
|
+
const visit = (node, nodeSegments) => {
|
|
12707
|
+
sent.add(declarationKey(nodeSegments));
|
|
12708
|
+
const pending = [
|
|
12709
|
+
...node.required ?? [],
|
|
12710
|
+
...Object.keys(node.properties).filter((name) => onTheWay.has(declarationKey([...nodeSegments, name])))
|
|
12711
|
+
];
|
|
12712
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12713
|
+
while (pending.length > 0) {
|
|
12714
|
+
const name = pending.pop();
|
|
12715
|
+
if (seen.has(name))
|
|
12716
|
+
continue;
|
|
12717
|
+
seen.add(name);
|
|
12718
|
+
if (!Object.hasOwn(node.properties, name))
|
|
12719
|
+
continue;
|
|
12720
|
+
const property = node.properties[name];
|
|
12721
|
+
const key = declarationKey([...nodeSegments, name]);
|
|
12722
|
+
demanded.add(key);
|
|
12723
|
+
if (isSecretProperty(property)) {
|
|
12724
|
+
secrets.add(key);
|
|
12725
|
+
} else if (property.type === "object") {
|
|
12726
|
+
visit(property, [...nodeSegments, name]);
|
|
12727
|
+
}
|
|
12728
|
+
const clauses = node.dependentRequired;
|
|
12729
|
+
if (clauses !== void 0 && Object.hasOwn(clauses, name))
|
|
12730
|
+
pending.push(...clauses[name]);
|
|
12731
|
+
}
|
|
12732
|
+
};
|
|
12733
|
+
visit(group, segments);
|
|
12734
|
+
return { secrets, demanded, sent };
|
|
12735
|
+
}
|
|
12736
|
+
function conditionCarriersBelow(group, segments) {
|
|
12737
|
+
const out = [];
|
|
12738
|
+
const collect = (node, nodeSegments) => {
|
|
12739
|
+
for (const [name, property] of Object.entries(node.properties)) {
|
|
12740
|
+
const here = [...nodeSegments, name];
|
|
12741
|
+
if (property.type === "object") {
|
|
12742
|
+
collect(property, here);
|
|
12743
|
+
continue;
|
|
12744
|
+
}
|
|
12745
|
+
const condition = property["x-required-when"];
|
|
12746
|
+
if (condition !== void 0)
|
|
12747
|
+
out.push({ key: declarationKey(here), condition });
|
|
12748
|
+
}
|
|
12749
|
+
};
|
|
12750
|
+
collect(group, segments);
|
|
12751
|
+
return out;
|
|
12752
|
+
}
|
|
12753
|
+
function conditionUnavoidable(context, group, condition, walk) {
|
|
12754
|
+
const target = resolveConditionTarget(context.root, condition.path);
|
|
12755
|
+
if (target === null)
|
|
12756
|
+
return false;
|
|
12757
|
+
const { property } = target;
|
|
12758
|
+
if (property.type !== "string" || isSecretProperty(property))
|
|
12759
|
+
return false;
|
|
12760
|
+
const groupOrAbove = (segments) => segments.length <= group.length && segments.every((segment, i) => group[i] === segment);
|
|
12761
|
+
const inside = (segments) => segments.length > group.length && group.every((segment, i) => segments[i] === segment);
|
|
12762
|
+
let belowList = false;
|
|
12763
|
+
for (const container of target.containers) {
|
|
12764
|
+
if (container.kind === "group" && groupOrAbove(container.segments))
|
|
12765
|
+
continue;
|
|
12766
|
+
if (container.kind === "list" && condition.anyRow !== true)
|
|
12767
|
+
return false;
|
|
12768
|
+
const key = declarationKey(container.segments);
|
|
12769
|
+
const present2 = !belowList && inside(container.segments) ? container.kind === "group" ? walk.sent.has(key) : walk.demanded.has(key) : container.required;
|
|
12770
|
+
if (!present2)
|
|
12771
|
+
return false;
|
|
12772
|
+
if (container.kind === "list") {
|
|
12773
|
+
if (container.minItems < 1)
|
|
12774
|
+
return false;
|
|
12775
|
+
belowList = true;
|
|
12776
|
+
}
|
|
12777
|
+
}
|
|
12778
|
+
const defaulted = property.default !== void 0;
|
|
12779
|
+
const present = !belowList && inside(target.segments) ? walk.demanded.has(declarationKey(target.segments)) || defaulted && walk.sent.has(declarationKey(target.segments.slice(0, -1))) : target.required || defaulted;
|
|
12780
|
+
if (!present)
|
|
12781
|
+
return false;
|
|
12782
|
+
let outside = context.acceptsOutside.get(condition);
|
|
12783
|
+
if (outside === void 0) {
|
|
12784
|
+
outside = acceptsValueOutside(property, condition.anyOf);
|
|
12785
|
+
context.acceptsOutside.set(condition, outside);
|
|
12786
|
+
}
|
|
12787
|
+
return !outside;
|
|
12788
|
+
}
|
|
12789
|
+
function resolveConditionTarget(root, path) {
|
|
12790
|
+
const pointer = path.startsWith("/");
|
|
12791
|
+
const segments = pointer ? path.slice(1).split("/") : [path];
|
|
12792
|
+
if (pointer && segments.length < 2)
|
|
12793
|
+
return null;
|
|
12794
|
+
if (!segments.every((segment) => CONFIGURATION_PROPERTY_NAME_REGEX.test(segment)))
|
|
12795
|
+
return null;
|
|
12796
|
+
const containers = [];
|
|
12797
|
+
let holder = root;
|
|
12798
|
+
for (const [index, name] of segments.entries()) {
|
|
12799
|
+
if (!Object.hasOwn(holder.properties, name))
|
|
12800
|
+
return null;
|
|
12801
|
+
const property = holder.properties[name];
|
|
12802
|
+
const here = segments.slice(0, index + 1);
|
|
12803
|
+
const required = (holder.required ?? []).includes(name);
|
|
12804
|
+
const last = index === segments.length - 1;
|
|
12805
|
+
if (property.type === "object") {
|
|
12806
|
+
if (last)
|
|
12807
|
+
return null;
|
|
12808
|
+
containers.push({ segments: here, kind: "group", required, minItems: 0 });
|
|
12809
|
+
holder = property;
|
|
12810
|
+
} else if (property.type === "array" && property.items.type === "object") {
|
|
12811
|
+
if (last)
|
|
12812
|
+
return null;
|
|
12813
|
+
containers.push({ segments: here, kind: "list", required, minItems: property.minItems ?? 0 });
|
|
12814
|
+
holder = property.items;
|
|
12815
|
+
} else {
|
|
12816
|
+
return last ? { segments, containers, property, required } : null;
|
|
12817
|
+
}
|
|
12818
|
+
}
|
|
12819
|
+
return null;
|
|
12820
|
+
}
|
|
12821
|
+
function acceptsValueOutside(property, anyOf) {
|
|
12822
|
+
let pattern = null;
|
|
12823
|
+
if (property.pattern !== void 0) {
|
|
12824
|
+
try {
|
|
12825
|
+
pattern = new RegExp(property.pattern);
|
|
12826
|
+
} catch {
|
|
12827
|
+
return false;
|
|
12828
|
+
}
|
|
12829
|
+
}
|
|
12830
|
+
const accepts = (value) => !anyOf.includes(value) && value.trim().length > 0 && (property.enum === void 0 || property.enum.includes(value)) && (property.minLength === void 0 || value.length >= property.minLength) && (property.maxLength === void 0 || value.length <= property.maxLength) && (pattern === null || pattern.test(value));
|
|
12831
|
+
if (property.enum !== void 0)
|
|
12832
|
+
return property.enum.some(accepts);
|
|
12833
|
+
if (property.default !== void 0 && accepts(property.default))
|
|
12834
|
+
return true;
|
|
12835
|
+
const floor = property.minLength ?? 0;
|
|
12836
|
+
const ceiling = Math.min(property.maxLength ?? floor + 3, floor + 3);
|
|
12837
|
+
for (let length = Math.max(floor, 1); length <= ceiling; length += 1) {
|
|
12838
|
+
for (const fill of ["x", "a", "0"]) {
|
|
12839
|
+
if (accepts(fill.repeat(length)))
|
|
12840
|
+
return true;
|
|
12841
|
+
}
|
|
12842
|
+
}
|
|
12843
|
+
for (const word of property.pattern?.match(/[A-Za-z0-9_-]+/g) ?? []) {
|
|
12844
|
+
if (accepts(word))
|
|
12845
|
+
return true;
|
|
12846
|
+
}
|
|
12847
|
+
for (const value of anyOf) {
|
|
12848
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
12849
|
+
const variant = value.slice(0, index) + sameClassCharacter(value.charAt(index)) + value.slice(index + 1);
|
|
12850
|
+
if (accepts(variant))
|
|
12851
|
+
return true;
|
|
12852
|
+
}
|
|
12853
|
+
}
|
|
12854
|
+
return false;
|
|
12855
|
+
}
|
|
12856
|
+
function sameClassCharacter(character) {
|
|
12857
|
+
const code = character.charCodeAt(0);
|
|
12858
|
+
for (const [first, last] of [
|
|
12859
|
+
["0", "9"],
|
|
12860
|
+
["a", "z"],
|
|
12861
|
+
["A", "Z"]
|
|
12862
|
+
]) {
|
|
12863
|
+
const start = first.charCodeAt(0);
|
|
12864
|
+
const end = last.charCodeAt(0);
|
|
12865
|
+
if (code >= start && code <= end)
|
|
12866
|
+
return String.fromCharCode(code === end ? start : code + 1);
|
|
12867
|
+
}
|
|
12868
|
+
return "x";
|
|
12869
|
+
}
|
|
12603
12870
|
var CustomerConfigurationV2ObjectSchema = external_exports.object({
|
|
12604
12871
|
type: external_exports.literal("object"),
|
|
12605
12872
|
properties: external_exports.record(PropertyNameSchema, RootConfigurationPropertySchema).describe("Customer-editable settings, keyed by camelCase property name."),
|
|
@@ -12683,10 +12950,10 @@ function minimumPropertyBytes(property) {
|
|
|
12683
12950
|
function declaredLeafDefault(property) {
|
|
12684
12951
|
return property.type === "object" ? void 0 : property.default;
|
|
12685
12952
|
}
|
|
12686
|
-
function checkPropertyDeclaration(name, property, path, ctx, atRoot) {
|
|
12953
|
+
function checkPropertyDeclaration(name, property, path, ctx, atRoot, underList2 = false) {
|
|
12687
12954
|
const children = childrenOf(property);
|
|
12688
12955
|
if (children === null) {
|
|
12689
|
-
checkDeclaredProperty(name, property, path, ctx, atRoot);
|
|
12956
|
+
checkDeclaredProperty(name, property, path, ctx, atRoot, underList2);
|
|
12690
12957
|
return;
|
|
12691
12958
|
}
|
|
12692
12959
|
const infrastructure = infrastructureVocabularyViolation(name);
|
|
@@ -12732,8 +12999,15 @@ function checkPropertyDeclaration(name, property, path, ctx, atRoot) {
|
|
|
12732
12999
|
message: `"${name}" has minItems greater than maxItems.`
|
|
12733
13000
|
});
|
|
12734
13001
|
}
|
|
13002
|
+
if (!atRoot && property.type === "array" && property["x-required-when"] !== void 0) {
|
|
13003
|
+
ctx.addIssue({
|
|
13004
|
+
code: external_exports.ZodIssueCode.custom,
|
|
13005
|
+
path: [...path, "x-required-when"],
|
|
13006
|
+
message: `"${name}" is a list of groups, and "x-required-when" may not be declared on one inside a group. The condition makes one setting required, and a list of groups is not a setting \u2014 declare the condition on a setting instead.`
|
|
13007
|
+
});
|
|
13008
|
+
}
|
|
12735
13009
|
for (const childName of childNames) {
|
|
12736
|
-
checkPropertyDeclaration(childName, children.properties[childName], [...path, "properties", childName], ctx, false);
|
|
13010
|
+
checkPropertyDeclaration(childName, children.properties[childName], [...path, "properties", childName], ctx, false, underList2 || property.type === "array");
|
|
12737
13011
|
}
|
|
12738
13012
|
for (const required of children.required ?? []) {
|
|
12739
13013
|
if (!Object.hasOwn(children.properties, required)) {
|
|
@@ -12816,6 +13090,7 @@ var CustomerConfigurationV2Schema = CustomerConfigurationV2ObjectSchema.superRef
|
|
|
12816
13090
|
}
|
|
12817
13091
|
checkDependentRequired(config, ctx);
|
|
12818
13092
|
checkOneSecretPerGroup(config, ctx);
|
|
13093
|
+
checkOptionalGroupActivation(config, ctx);
|
|
12819
13094
|
});
|
|
12820
13095
|
function enumerableValues(property) {
|
|
12821
13096
|
const out = [];
|
|
@@ -12876,13 +13151,16 @@ function parseConfigurationPath(raw) {
|
|
|
12876
13151
|
return null;
|
|
12877
13152
|
return configurationPath(...segments);
|
|
12878
13153
|
}
|
|
13154
|
+
function secretPathOf(raw) {
|
|
13155
|
+
const path = parseConfigurationPath(raw);
|
|
13156
|
+
if (path === null)
|
|
13157
|
+
return null;
|
|
13158
|
+
return pathSegments(path).some((segment) => typeof segment === "number") ? null : path;
|
|
13159
|
+
}
|
|
12879
13160
|
function pathSegments(path) {
|
|
12880
13161
|
const raw = path.startsWith("/") ? path.slice(1).split("/") : [path];
|
|
12881
13162
|
return raw.map((segment) => isIndexSegment(segment) ? Number(segment) : segment);
|
|
12882
13163
|
}
|
|
12883
|
-
function rootSegment(path) {
|
|
12884
|
-
return String(pathSegments(path)[0]);
|
|
12885
|
-
}
|
|
12886
13164
|
function declarationPathOf(path) {
|
|
12887
13165
|
const segments = pathSegments(path).filter((segment) => typeof segment !== "number");
|
|
12888
13166
|
return configurationPath(...segments) ?? path;
|
|
@@ -12929,9 +13207,29 @@ function canonicalJson(value) {
|
|
|
12929
13207
|
function immediateContainer(node) {
|
|
12930
13208
|
return node.containers[node.containers.length - 1] ?? null;
|
|
12931
13209
|
}
|
|
13210
|
+
function resolvedGroup(node) {
|
|
13211
|
+
for (const container of node.containers) {
|
|
13212
|
+
const declared = container.property.group;
|
|
13213
|
+
if (declared !== void 0)
|
|
13214
|
+
return declared;
|
|
13215
|
+
}
|
|
13216
|
+
return node.property.group;
|
|
13217
|
+
}
|
|
12932
13218
|
function underList(node) {
|
|
12933
13219
|
return node.containers.some((container) => container.kind === "array");
|
|
12934
13220
|
}
|
|
13221
|
+
function containersRequiredToRoot(schema, node) {
|
|
13222
|
+
const rootRequired = schema.required ?? [];
|
|
13223
|
+
return node.containers.every((container, index) => {
|
|
13224
|
+
const parent = node.containers[index - 1];
|
|
13225
|
+
return (parent === void 0 ? rootRequired : parent.required).includes(container.name);
|
|
13226
|
+
});
|
|
13227
|
+
}
|
|
13228
|
+
function requiredToRoot(schema, node) {
|
|
13229
|
+
const parent = immediateContainer(node);
|
|
13230
|
+
const declared = parent === null ? schema.required ?? [] : parent.required;
|
|
13231
|
+
return declared.includes(node.name) && containersRequiredToRoot(schema, node);
|
|
13232
|
+
}
|
|
12935
13233
|
function containerChildren(property) {
|
|
12936
13234
|
if (property.type === "object" && property.properties !== void 0) {
|
|
12937
13235
|
return {
|
|
@@ -12990,6 +13288,9 @@ function walkDeclarations(properties, ancestors, base, onLeaf, onContainer = ()
|
|
|
12990
13288
|
walkDeclarations(children.properties, [...ancestors, container], segments, onLeaf, onContainer);
|
|
12991
13289
|
}
|
|
12992
13290
|
}
|
|
13291
|
+
function declaredSecretPaths(schema) {
|
|
13292
|
+
return configurationLeaves(schema).filter((leaf) => isSecretProperty(leaf.property) && !underList(leaf)).map((leaf) => leaf.path).sort();
|
|
13293
|
+
}
|
|
12993
13294
|
function declaredDefault(property) {
|
|
12994
13295
|
if (property.type === "object")
|
|
12995
13296
|
return void 0;
|
|
@@ -13025,7 +13326,8 @@ function expandDeclarationPath(configuration, declaration) {
|
|
|
13025
13326
|
const [head, ...rest] = remaining;
|
|
13026
13327
|
if (head === void 0)
|
|
13027
13328
|
return;
|
|
13028
|
-
|
|
13329
|
+
const key = String(head);
|
|
13330
|
+
walk(Object.hasOwn(cursor, key) ? cursor[key] : void 0, rest, [...soFar, head]);
|
|
13029
13331
|
};
|
|
13030
13332
|
walk(configuration, segmentsOf(declaration), []);
|
|
13031
13333
|
return out;
|
|
@@ -13039,7 +13341,7 @@ function valueAt(configuration, path) {
|
|
|
13039
13341
|
cursor = cursor[segment];
|
|
13040
13342
|
continue;
|
|
13041
13343
|
}
|
|
13042
|
-
if (!isPlainObject(cursor))
|
|
13344
|
+
if (!isPlainObject(cursor) || !Object.hasOwn(cursor, segment))
|
|
13043
13345
|
return void 0;
|
|
13044
13346
|
cursor = cursor[segment];
|
|
13045
13347
|
}
|
|
@@ -13055,7 +13357,7 @@ function valuesAt(configuration, path) {
|
|
|
13055
13357
|
next.push(cursor[segment]);
|
|
13056
13358
|
continue;
|
|
13057
13359
|
}
|
|
13058
|
-
if (!isPlainObject(cursor))
|
|
13360
|
+
if (!isPlainObject(cursor) || !Object.hasOwn(cursor, segment))
|
|
13059
13361
|
continue;
|
|
13060
13362
|
const value = cursor[segment];
|
|
13061
13363
|
if (Array.isArray(value))
|
|
@@ -13076,10 +13378,6 @@ function segmentsOf(path) {
|
|
|
13076
13378
|
function conditionSetting(condition) {
|
|
13077
13379
|
return "path" in condition ? condition.path : condition.property;
|
|
13078
13380
|
}
|
|
13079
|
-
function conditionRoot(condition) {
|
|
13080
|
-
const path = parseConfigurationPath(conditionSetting(condition));
|
|
13081
|
-
return path === null ? conditionSetting(condition) : rootSegment(path);
|
|
13082
|
-
}
|
|
13083
13381
|
function conditionHolds(condition, configuration) {
|
|
13084
13382
|
const path = parseConfigurationPath(conditionSetting(condition));
|
|
13085
13383
|
if (path === null)
|
|
@@ -13087,6 +13385,28 @@ function conditionHolds(condition, configuration) {
|
|
|
13087
13385
|
const matches2 = (held) => typeof held === "string" && condition.anyOf.includes(held);
|
|
13088
13386
|
return "anyRow" in condition && condition.anyRow === true ? valuesAt(configuration, path).some(matches2) : matches2(valueAt(configuration, path));
|
|
13089
13387
|
}
|
|
13388
|
+
function conditionOf(property) {
|
|
13389
|
+
return property?.["x-required-when"];
|
|
13390
|
+
}
|
|
13391
|
+
function conditionCarriers(schema) {
|
|
13392
|
+
const settings = configurationLeaves(schema).flatMap((leaf) => {
|
|
13393
|
+
const condition = conditionOf(leaf.property);
|
|
13394
|
+
if (condition === void 0 || underList(leaf))
|
|
13395
|
+
return [];
|
|
13396
|
+
const { path, name, containers, property } = leaf;
|
|
13397
|
+
return [{ kind: "leaf", path, name, containers, property, condition }];
|
|
13398
|
+
});
|
|
13399
|
+
const lists = configurationContainers(schema).flatMap((container) => {
|
|
13400
|
+
const condition = conditionOf(container.property);
|
|
13401
|
+
if (condition === void 0 || container.kind !== "array" || container.containers.length > 0)
|
|
13402
|
+
return [];
|
|
13403
|
+
const { path, name, containers, property } = container;
|
|
13404
|
+
return [{ kind: "container", path, name, containers, property, condition }];
|
|
13405
|
+
});
|
|
13406
|
+
const roots = Object.keys(schema.properties);
|
|
13407
|
+
const rootIndex = (carrier) => roots.indexOf(carrier.containers[0]?.name ?? carrier.name);
|
|
13408
|
+
return [...settings, ...lists].sort((a, b) => rootIndex(a) - rootIndex(b));
|
|
13409
|
+
}
|
|
13090
13410
|
|
|
13091
13411
|
// ../product-configuration/dist/customer-configuration-input.js
|
|
13092
13412
|
var ABSOLUTE_MAX_STRING = MAX_DECLARABLE_STRING_LENGTH;
|
|
@@ -13096,6 +13416,7 @@ function isPlainObject2(value) {
|
|
|
13096
13416
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13097
13417
|
}
|
|
13098
13418
|
function report(walk, field, message) {
|
|
13419
|
+
walk.reported.add(field);
|
|
13099
13420
|
if (walk.issues.length >= MAX_CONFIGURATION_ISSUES) {
|
|
13100
13421
|
walk.suppressed += 1;
|
|
13101
13422
|
return;
|
|
@@ -13111,7 +13432,10 @@ function validateSubmittedConfiguration(schema, submitted, presence = {}) {
|
|
|
13111
13432
|
issues: [],
|
|
13112
13433
|
suppressed: 0,
|
|
13113
13434
|
leaves: 0,
|
|
13114
|
-
referenced: new Set(presence.referenced ?? [])
|
|
13435
|
+
referenced: new Set(presence.referenced ?? []),
|
|
13436
|
+
present: new Set(presence.present ?? []),
|
|
13437
|
+
unknown: new Set(presence.unknown ?? []),
|
|
13438
|
+
reported: /* @__PURE__ */ new Set()
|
|
13115
13439
|
};
|
|
13116
13440
|
const supplied = /* @__PURE__ */ new Set();
|
|
13117
13441
|
if (submitted !== void 0 && submitted !== null && !isPlainObject2(submitted)) {
|
|
@@ -13124,7 +13448,7 @@ function validateSubmittedConfiguration(schema, submitted, presence = {}) {
|
|
|
13124
13448
|
const input = isPlainObject2(submitted) ? submitted : {};
|
|
13125
13449
|
const secrets = {};
|
|
13126
13450
|
const configuration = validateObject({ properties: schema.properties, required: schema.required }, input, null, walk, { secrets, supplied });
|
|
13127
|
-
drain(walk, crossFieldIssues(schema, supplied, presence, configuration));
|
|
13451
|
+
drain(walk, crossFieldIssues(schema, supplied, presence, configuration, walk.reported));
|
|
13128
13452
|
drain(walk, rowAndReferenceIssues(schema, presence, configuration));
|
|
13129
13453
|
if (walk.suppressed > 0) {
|
|
13130
13454
|
walk.issues.push({
|
|
@@ -13149,13 +13473,17 @@ function declaresContainer(schema) {
|
|
|
13149
13473
|
return Object.values(schema.properties).some((property) => isConfigurationContainer(property));
|
|
13150
13474
|
}
|
|
13151
13475
|
function containsReferenced(walk, container) {
|
|
13152
|
-
|
|
13153
|
-
|
|
13476
|
+
return containsAny(walk.referenced, container);
|
|
13477
|
+
}
|
|
13478
|
+
function referencedAtOrInside(walk, path) {
|
|
13479
|
+
return walk.referenced.has(path) || containsReferenced(walk, path);
|
|
13480
|
+
}
|
|
13481
|
+
function containsAny(candidates, container) {
|
|
13154
13482
|
const parsedOuter = parseConfigurationPath(container);
|
|
13155
13483
|
if (parsedOuter === null)
|
|
13156
13484
|
return false;
|
|
13157
13485
|
const outer = pathSegments(parsedOuter);
|
|
13158
|
-
for (const candidate of
|
|
13486
|
+
for (const candidate of candidates) {
|
|
13159
13487
|
const parsed = parseConfigurationPath(candidate);
|
|
13160
13488
|
if (parsed === null)
|
|
13161
13489
|
continue;
|
|
@@ -13167,7 +13495,7 @@ function containsReferenced(walk, container) {
|
|
|
13167
13495
|
}
|
|
13168
13496
|
return false;
|
|
13169
13497
|
}
|
|
13170
|
-
function validateObject(declaration, input, container, walk,
|
|
13498
|
+
function validateObject(declaration, input, container, walk, sink) {
|
|
13171
13499
|
const required = new Set(declaration.required ?? []);
|
|
13172
13500
|
const out = {};
|
|
13173
13501
|
const fieldFor = (name) => {
|
|
@@ -13185,11 +13513,13 @@ function validateObject(declaration, input, container, walk, root) {
|
|
|
13185
13513
|
const field = fieldFor(name);
|
|
13186
13514
|
const children = containerDeclaration(property);
|
|
13187
13515
|
if (children !== null) {
|
|
13188
|
-
const value = input[name];
|
|
13189
|
-
const filled = validateContainer(name, property, children, value, field, walk);
|
|
13516
|
+
const value = Object.hasOwn(input, name) ? input[name] : void 0;
|
|
13517
|
+
const filled = validateContainer(name, property, children, value, field, walk, sink);
|
|
13518
|
+
if (value === void 0)
|
|
13519
|
+
absentGroupRuleIssues(children, field, walk, sink, required.has(name));
|
|
13190
13520
|
if (filled !== void 0)
|
|
13191
13521
|
out[name] = filled;
|
|
13192
|
-
else if (required.has(name) && !containsReferenced(walk, field))
|
|
13522
|
+
else if (required.has(name) && !((containsReferenced(walk, field) || containsAny(walk.present, field) || sink !== null && containsAny(Object.keys(sink.secrets), field)) && (value !== void 0 || absentGroupAnswered(children, field, walk))))
|
|
13193
13523
|
report(walk, field, "This setting is required.");
|
|
13194
13524
|
continue;
|
|
13195
13525
|
}
|
|
@@ -13204,9 +13534,13 @@ function validateObject(declaration, input, container, walk, root) {
|
|
|
13204
13534
|
const raw = input[name];
|
|
13205
13535
|
const blank = typeof raw === "string" && raw.trim().length === 0;
|
|
13206
13536
|
const present = Object.hasOwn(input, name) && raw !== void 0 && !blank;
|
|
13207
|
-
const secret =
|
|
13208
|
-
if (
|
|
13209
|
-
|
|
13537
|
+
const secret = isSecretProperty(leaf);
|
|
13538
|
+
if (secret && sink === null) {
|
|
13539
|
+
report(walk, field, "This Product declares a secret inside a list, which this contract format does not support.");
|
|
13540
|
+
continue;
|
|
13541
|
+
}
|
|
13542
|
+
if (present && sink !== null && field !== "")
|
|
13543
|
+
sink.supplied.add(field);
|
|
13210
13544
|
if (!present) {
|
|
13211
13545
|
const fallback = secret ? void 0 : defaultOf(leaf);
|
|
13212
13546
|
if (fallback !== void 0) {
|
|
@@ -13225,27 +13559,96 @@ function validateObject(declaration, input, container, walk, root) {
|
|
|
13225
13559
|
continue;
|
|
13226
13560
|
}
|
|
13227
13561
|
if (secret) {
|
|
13228
|
-
if (typeof checked === "string" && checked.length > 0)
|
|
13229
|
-
|
|
13562
|
+
if (typeof checked === "string" && checked.length > 0 && field !== "")
|
|
13563
|
+
sink.secrets[field] = checked;
|
|
13230
13564
|
else if (required.has(name))
|
|
13231
13565
|
report(walk, field, "This setting is required.");
|
|
13232
13566
|
continue;
|
|
13233
13567
|
}
|
|
13234
13568
|
out[name] = checked;
|
|
13235
13569
|
}
|
|
13236
|
-
|
|
13237
|
-
|
|
13238
|
-
|
|
13239
|
-
|
|
13570
|
+
groupRuleIssues(declaration, new Set(Object.keys(out)), container, walk, sink);
|
|
13571
|
+
return out;
|
|
13572
|
+
}
|
|
13573
|
+
function groupRuleIssues(declaration, suppliedHere, container, walk, sink) {
|
|
13574
|
+
if (declaration.dependentRequired === void 0)
|
|
13575
|
+
return;
|
|
13576
|
+
const fieldFor = (name) => (container === null ? configurationPath(name) : configurationPath(...pathSegments(container), name)) ?? "";
|
|
13577
|
+
const answered = (name) => {
|
|
13578
|
+
const path = fieldFor(name);
|
|
13579
|
+
return suppliedHere.has(name) || // A MEMBER, NOT A CONTAINER: a referenced leaf is recorded at this very
|
|
13580
|
+
// path, and a member group is answered by a reference inside it. Asking
|
|
13581
|
+
// only "inside" never counted a referenced leaf, so a referenced trigger
|
|
13582
|
+
// turned nothing on and a referenced dependent was reported missing.
|
|
13583
|
+
referencedAtOrInside(walk, path) || walk.present.has(path) || sink !== null && Object.hasOwn(sink.secrets, path);
|
|
13584
|
+
};
|
|
13585
|
+
const unseen = (name) => walk.unknown.has(fieldFor(name));
|
|
13586
|
+
const reported = /* @__PURE__ */ new Set();
|
|
13587
|
+
for (const [trigger, dependents] of Object.entries(declaration.dependentRequired)) {
|
|
13588
|
+
if (unseen(trigger) || !answered(trigger))
|
|
13589
|
+
continue;
|
|
13590
|
+
for (const dependent of dependents) {
|
|
13591
|
+
if (unseen(dependent) || answered(dependent) || reported.has(dependent))
|
|
13240
13592
|
continue;
|
|
13241
|
-
|
|
13242
|
-
|
|
13243
|
-
continue;
|
|
13244
|
-
report(walk, fieldFor(dependent), `This setting is required when ${labelOf(declaration, trigger)} is configured.`);
|
|
13245
|
-
}
|
|
13593
|
+
reported.add(dependent);
|
|
13594
|
+
report(walk, fieldFor(dependent), `This setting is required when ${labelOf(declaration, trigger)} is configured.`);
|
|
13246
13595
|
}
|
|
13247
13596
|
}
|
|
13248
|
-
|
|
13597
|
+
}
|
|
13598
|
+
function absentGroupRuleIssues(children, field, walk, sink, coveredByParent) {
|
|
13599
|
+
if (children.kind !== "object" || sink === null)
|
|
13600
|
+
return;
|
|
13601
|
+
const path = parseConfigurationPath(field);
|
|
13602
|
+
if (path === null)
|
|
13603
|
+
return;
|
|
13604
|
+
groupRuleIssues(children, /* @__PURE__ */ new Set(), path, walk, sink);
|
|
13605
|
+
const configured = containsReferenced(walk, field) || containsAny(walk.present, field);
|
|
13606
|
+
const required = new Set(children.required ?? []);
|
|
13607
|
+
for (const [name, property] of Object.entries(children.properties)) {
|
|
13608
|
+
const memberField = configurationPath(...pathSegments(path), name);
|
|
13609
|
+
if (memberField === null)
|
|
13610
|
+
continue;
|
|
13611
|
+
const demanded = configured && !coveredByParent && required.has(name) && !walk.unknown.has(memberField);
|
|
13612
|
+
const inner = containerDeclaration(property);
|
|
13613
|
+
if (inner === null) {
|
|
13614
|
+
if (!demanded || walk.referenced.has(memberField) || walk.present.has(memberField))
|
|
13615
|
+
continue;
|
|
13616
|
+
if (!isSecretProperty(property) && defaultOf(property) !== void 0)
|
|
13617
|
+
continue;
|
|
13618
|
+
report(walk, memberField, "This setting is required.");
|
|
13619
|
+
continue;
|
|
13620
|
+
}
|
|
13621
|
+
if (demanded && inner.kind === "object" && !(containsReferenced(walk, memberField) || containsAny(walk.present, memberField))) {
|
|
13622
|
+
report(walk, memberField, "This setting is required.");
|
|
13623
|
+
continue;
|
|
13624
|
+
}
|
|
13625
|
+
absentGroupRuleIssues(inner, memberField, walk, sink, coveredByParent && required.has(name));
|
|
13626
|
+
}
|
|
13627
|
+
}
|
|
13628
|
+
function absentGroupAnswered(children, field, walk) {
|
|
13629
|
+
if (children.kind !== "object")
|
|
13630
|
+
return true;
|
|
13631
|
+
const parent = parseConfigurationPath(field);
|
|
13632
|
+
if (parent === null)
|
|
13633
|
+
return false;
|
|
13634
|
+
for (const name of children.required ?? []) {
|
|
13635
|
+
const property = children.properties[name];
|
|
13636
|
+
const path = configurationPath(...pathSegments(parent), name);
|
|
13637
|
+
if (property === void 0 || path === null)
|
|
13638
|
+
return false;
|
|
13639
|
+
const inner = containerDeclaration(property);
|
|
13640
|
+
if (inner !== null) {
|
|
13641
|
+
if (!(containsReferenced(walk, path) || containsAny(walk.present, path)) || !absentGroupAnswered(inner, path, walk))
|
|
13642
|
+
return false;
|
|
13643
|
+
continue;
|
|
13644
|
+
}
|
|
13645
|
+
if (walk.referenced.has(path) || walk.present.has(path))
|
|
13646
|
+
continue;
|
|
13647
|
+
if (!isSecretProperty(property) && defaultOf(property) !== void 0)
|
|
13648
|
+
continue;
|
|
13649
|
+
return false;
|
|
13650
|
+
}
|
|
13651
|
+
return true;
|
|
13249
13652
|
}
|
|
13250
13653
|
function containerDeclaration(property) {
|
|
13251
13654
|
if (property.type === "object" && property.properties !== void 0) {
|
|
@@ -13275,7 +13678,7 @@ function containerDeclaration(property) {
|
|
|
13275
13678
|
}
|
|
13276
13679
|
return null;
|
|
13277
13680
|
}
|
|
13278
|
-
function validateContainer(name, property, children, value, field, walk) {
|
|
13681
|
+
function validateContainer(name, property, children, value, field, walk, sink) {
|
|
13279
13682
|
const path = parseConfigurationPath(field);
|
|
13280
13683
|
if (children.kind === "object") {
|
|
13281
13684
|
if (value === void 0)
|
|
@@ -13284,7 +13687,7 @@ function validateContainer(name, property, children, value, field, walk) {
|
|
|
13284
13687
|
report(walk, field, "Expected a group of settings.");
|
|
13285
13688
|
return void 0;
|
|
13286
13689
|
}
|
|
13287
|
-
const filled = validateObject(children, value, path, walk,
|
|
13690
|
+
const filled = validateObject(children, value, path, walk, sink);
|
|
13288
13691
|
return Object.keys(filled).length === 0 ? void 0 : filled;
|
|
13289
13692
|
}
|
|
13290
13693
|
if (value === void 0)
|
|
@@ -13323,10 +13726,10 @@ function validateContainer(name, property, children, value, field, walk) {
|
|
|
13323
13726
|
}
|
|
13324
13727
|
return out;
|
|
13325
13728
|
}
|
|
13326
|
-
function crossFieldIssues(schema, supplied, presence, configuration
|
|
13729
|
+
function crossFieldIssues(schema, supplied, presence, configuration, reportedByWalk) {
|
|
13327
13730
|
const clauses = Object.entries(schema.dependentRequired ?? {});
|
|
13328
|
-
const
|
|
13329
|
-
if (clauses.length === 0 &&
|
|
13731
|
+
const carriers = conditionCarriers(schema);
|
|
13732
|
+
if (clauses.length === 0 && carriers.length === 0)
|
|
13330
13733
|
return [];
|
|
13331
13734
|
const alsoPresent = /* @__PURE__ */ new Set([...presence.present ?? [], ...presence.referenced ?? []]);
|
|
13332
13735
|
const unknown = new Set(presence.unknown ?? []);
|
|
@@ -13349,22 +13752,28 @@ function crossFieldIssues(schema, supplied, presence, configuration = {}) {
|
|
|
13349
13752
|
});
|
|
13350
13753
|
}
|
|
13351
13754
|
}
|
|
13352
|
-
for (const
|
|
13353
|
-
|
|
13354
|
-
if (condition === void 0 || !known(name) || held(name))
|
|
13755
|
+
for (const { path, condition } of carriers) {
|
|
13756
|
+
if (!known(path) || held(path))
|
|
13355
13757
|
continue;
|
|
13356
|
-
|
|
13758
|
+
const target = parseConfigurationPath(conditionSetting(condition));
|
|
13759
|
+
if (target === null || !conditionHolds(condition, configuration))
|
|
13357
13760
|
continue;
|
|
13358
|
-
if (reported.has(
|
|
13761
|
+
if (reported.has(path) || reportedByWalk.has(path))
|
|
13359
13762
|
continue;
|
|
13360
|
-
reported.add(
|
|
13763
|
+
reported.add(path);
|
|
13361
13764
|
issues.push({
|
|
13362
|
-
field:
|
|
13765
|
+
field: path,
|
|
13363
13766
|
// THE SETTING THAT MADE IT NECESSARY, never the value it holds. A message
|
|
13364
13767
|
// naming the value would be fine here and wrong one refactor later, when
|
|
13365
13768
|
// somebody conditions a secret on another secret — which the publish rules
|
|
13366
13769
|
// refuse precisely because nothing may read one.
|
|
13367
|
-
|
|
13770
|
+
//
|
|
13771
|
+
// THE SETTING THE CONDITION READS, not the root it descends from. The two
|
|
13772
|
+
// were one string while a condition could only name a root setting or a
|
|
13773
|
+
// row of a root list; naming a setting in a group by its root would tell
|
|
13774
|
+
// the customer to look at a whole section. A root scalar reads exactly as
|
|
13775
|
+
// it always did, and a row's setting now names its list beside it.
|
|
13776
|
+
message: `This setting is required because of what ${labelOfPath(schema, target)} is set to.`
|
|
13368
13777
|
});
|
|
13369
13778
|
}
|
|
13370
13779
|
return issues;
|
|
@@ -13469,9 +13878,6 @@ function labelOfChild(container, name) {
|
|
|
13469
13878
|
const title = items?.properties?.[name]?.title;
|
|
13470
13879
|
return title === void 0 ? `"${name}"` : `"${title}"`;
|
|
13471
13880
|
}
|
|
13472
|
-
function conditionOf(property) {
|
|
13473
|
-
return property?.["x-required-when"];
|
|
13474
|
-
}
|
|
13475
13881
|
function labelOf(schema, name) {
|
|
13476
13882
|
const property = schema.properties[name];
|
|
13477
13883
|
return property?.title === void 0 ? `"${name}"` : `"${property.title}"`;
|
|
@@ -13516,9 +13922,6 @@ function withoutPropertyRules(properties) {
|
|
|
13516
13922
|
}
|
|
13517
13923
|
return out;
|
|
13518
13924
|
}
|
|
13519
|
-
function declaredSecretNames(schema) {
|
|
13520
|
-
return Object.entries(schema.properties).filter(([, property]) => isSecretProperty(property)).map(([name]) => name).sort();
|
|
13521
|
-
}
|
|
13522
13925
|
function defaultOf(property) {
|
|
13523
13926
|
switch (property.type) {
|
|
13524
13927
|
case "string":
|
|
@@ -13675,23 +14078,52 @@ function seedConfiguration(configuration) {
|
|
|
13675
14078
|
const required = new Set(configuration?.required ?? []);
|
|
13676
14079
|
const open = [];
|
|
13677
14080
|
const requiredSecrets = [];
|
|
13678
|
-
const groups =
|
|
14081
|
+
const groups = [];
|
|
14082
|
+
collectGroups(configuration ?? {}, [], groups);
|
|
14083
|
+
const values = seedProperties(properties, required, [], open, requiredSecrets);
|
|
14084
|
+
return { values, open, requiredSecrets, groups };
|
|
14085
|
+
}
|
|
14086
|
+
function collectGroups(node, ancestors, groups) {
|
|
14087
|
+
const properties = node.properties ?? {};
|
|
14088
|
+
const pathOf = (name) => ancestors.length === 0 ? name : configurationPath(...ancestors, name) ?? name;
|
|
14089
|
+
const labelOf2 = (name) => {
|
|
14090
|
+
const path = configurationPath(...ancestors, name);
|
|
14091
|
+
return path === null ? name : printableConfigurationPath(path);
|
|
14092
|
+
};
|
|
14093
|
+
const container = ancestors.length === 0 ? null : configurationPath(...ancestors);
|
|
14094
|
+
const within = container === null ? null : printableConfigurationPath(container);
|
|
14095
|
+
const clauses = Object.entries(node.dependentRequired ?? {}).map(([trigger, dependents]) => {
|
|
13679
14096
|
const members = [trigger, ...dependents];
|
|
14097
|
+
const settings = members.filter((name) => !isSecret(properties[name] ?? {}));
|
|
13680
14098
|
return {
|
|
13681
14099
|
trigger,
|
|
13682
|
-
|
|
13683
|
-
|
|
14100
|
+
group: {
|
|
14101
|
+
trigger: pathOf(trigger),
|
|
14102
|
+
settings: settings.map(pathOf),
|
|
14103
|
+
labels: settings.map(labelOf2),
|
|
14104
|
+
secrets: members.filter((name) => isSecret(properties[name] ?? {})).map(pathOf),
|
|
14105
|
+
within
|
|
14106
|
+
}
|
|
13684
14107
|
};
|
|
13685
14108
|
}).sort((a, b) => a.trigger.localeCompare(b.trigger));
|
|
13686
|
-
|
|
13687
|
-
|
|
14109
|
+
groups.push(...clauses.map((clause) => clause.group));
|
|
14110
|
+
for (const [name, declaration] of Object.entries(properties)) {
|
|
14111
|
+
if (isSecret(declaration))
|
|
14112
|
+
continue;
|
|
14113
|
+
const children = containerChildren2(declaration);
|
|
14114
|
+
if (children === null)
|
|
14115
|
+
continue;
|
|
14116
|
+
collectGroups(children, [...ancestors, name], groups);
|
|
14117
|
+
}
|
|
13688
14118
|
}
|
|
13689
14119
|
function seedProperties(properties, required, ancestors, open, requiredSecrets) {
|
|
13690
14120
|
const values = {};
|
|
13691
14121
|
for (const [name, declaration] of Object.entries(properties)) {
|
|
13692
|
-
if (
|
|
13693
|
-
|
|
13694
|
-
|
|
14122
|
+
if (isSecret(declaration)) {
|
|
14123
|
+
const path = ancestors.length === 0 ? name : configurationPath(...ancestors, name);
|
|
14124
|
+
if (requiredSecrets !== null && required.has(name) && path !== null) {
|
|
14125
|
+
requiredSecrets.push(path);
|
|
14126
|
+
}
|
|
13695
14127
|
continue;
|
|
13696
14128
|
}
|
|
13697
14129
|
const children = containerChildren2(declaration);
|
|
@@ -13701,7 +14133,7 @@ function seedProperties(properties, required, ancestors, open, requiredSecrets)
|
|
|
13701
14133
|
const childRequired = new Set(children.required);
|
|
13702
14134
|
const inner = [...ancestors, name];
|
|
13703
14135
|
if (children.kind === "object") {
|
|
13704
|
-
values[name] = seedProperties(children.properties, childRequired, inner, open,
|
|
14136
|
+
values[name] = seedProperties(children.properties, childRequired, inner, open, requiredSecrets);
|
|
13705
14137
|
continue;
|
|
13706
14138
|
}
|
|
13707
14139
|
const count = children.minItems ?? 0;
|
|
@@ -13732,7 +14164,8 @@ function containerChildren2(declaration) {
|
|
|
13732
14164
|
return {
|
|
13733
14165
|
kind: "object",
|
|
13734
14166
|
properties: node.properties,
|
|
13735
|
-
required: node.required ?? []
|
|
14167
|
+
required: node.required ?? [],
|
|
14168
|
+
...dependentRequiredOf(node.dependentRequired)
|
|
13736
14169
|
};
|
|
13737
14170
|
}
|
|
13738
14171
|
if (node.type === "array" && node.items !== void 0) {
|
|
@@ -13743,11 +14176,15 @@ function containerChildren2(declaration) {
|
|
|
13743
14176
|
kind: "array",
|
|
13744
14177
|
properties: items.properties,
|
|
13745
14178
|
required: items.required ?? [],
|
|
14179
|
+
...dependentRequiredOf(items.dependentRequired),
|
|
13746
14180
|
...typeof node.minItems === "number" ? { minItems: node.minItems } : {}
|
|
13747
14181
|
};
|
|
13748
14182
|
}
|
|
13749
14183
|
return null;
|
|
13750
14184
|
}
|
|
14185
|
+
function dependentRequiredOf(raw) {
|
|
14186
|
+
return typeof raw === "object" && raw !== null && !Array.isArray(raw) ? { dependentRequired: raw } : {};
|
|
14187
|
+
}
|
|
13751
14188
|
function dedupeOpen(open) {
|
|
13752
14189
|
const seen = /* @__PURE__ */ new Set();
|
|
13753
14190
|
const unique = open.filter((entry) => {
|
|
@@ -13779,6 +14216,117 @@ function exampleResourceKey(name) {
|
|
|
13779
14216
|
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^[^a-z]+/, "").replace(/-+$/, "").slice(0, 63);
|
|
13780
14217
|
return RESOURCE_KEY_PATTERN.test(slug) ? slug : "";
|
|
13781
14218
|
}
|
|
14219
|
+
var DEPLOYMENT_EXAMPLE_GUARANTEE = "Product-owned configuration is complete and E2E-proven; tenant-specific secrets and Resource/provider selections are supplied by the tenant.";
|
|
14220
|
+
|
|
14221
|
+
// ../product-configuration/dist/configuration-requirements.js
|
|
14222
|
+
function configurationRequirements(configuration) {
|
|
14223
|
+
if (configuration === void 0 || configuration === null)
|
|
14224
|
+
return [];
|
|
14225
|
+
const schema = configuration;
|
|
14226
|
+
const rootRequired = new Set(schema.required ?? []);
|
|
14227
|
+
const carriers = new Map(conditionCarriers(schema).map((carrier) => [carrier.path, carrier]));
|
|
14228
|
+
const secretPaths = new Set(configurationLeaves(schema).filter((leaf) => isSecretProperty(leaf.property)).map((leaf) => String(leaf.path)));
|
|
14229
|
+
return configurationLeaves(schema).map((leaf) => {
|
|
14230
|
+
const parent = immediateContainer(leaf);
|
|
14231
|
+
const immediatelyRequired = parent === null ? rootRequired.has(leaf.name) : parent.required.includes(leaf.name);
|
|
14232
|
+
const chainRequired = containersRequiredToRoot(schema, leaf);
|
|
14233
|
+
const always = requiredToRoot(schema, leaf);
|
|
14234
|
+
const secret = isSecretProperty(leaf.property);
|
|
14235
|
+
const condition = conditionFor(schema, leaf.path, leaf.containers, carriers);
|
|
14236
|
+
const clause = clauseEdges(schema, leaf, secretPaths);
|
|
14237
|
+
const requiredness = always ? "always" : condition !== null && condition.carrier === "setting" ? "when-value" : immediatelyRequired && !chainRequired ? "when-group" : clause.requiredBy.length > 0 ? "with-siblings" : "optional";
|
|
14238
|
+
const containers = leaf.containers.map((container, index) => settingContainer(container, index === 0 ? rootRequired : new Set(leaf.containers[index - 1]?.required ?? [])));
|
|
14239
|
+
const list = [...containers].reverse().find((container) => container.kind === "array") ?? null;
|
|
14240
|
+
return {
|
|
14241
|
+
path: leaf.path,
|
|
14242
|
+
printablePath: printableConfigurationPath(leaf.path),
|
|
14243
|
+
title: leaf.property.title,
|
|
14244
|
+
description: leaf.property.description,
|
|
14245
|
+
type: leaf.property.type,
|
|
14246
|
+
secret,
|
|
14247
|
+
requiredness,
|
|
14248
|
+
// NEVER A SECRET'S, even though the declaration rules already refuse one.
|
|
14249
|
+
// Being wrong in that direction prints a credential, which is the same
|
|
14250
|
+
// paranoia `product-instance-example.ts` documents for its own reader.
|
|
14251
|
+
defaultValue: secret ? void 0 : declaredDefault(leaf.property),
|
|
14252
|
+
allowed: secret ? [] : allowedValues(leaf.property),
|
|
14253
|
+
condition,
|
|
14254
|
+
requires: clause.requires,
|
|
14255
|
+
requiredBy: clause.requiredBy,
|
|
14256
|
+
containers,
|
|
14257
|
+
perRow: underList(leaf),
|
|
14258
|
+
list,
|
|
14259
|
+
group: resolvedGroup(leaf)
|
|
14260
|
+
};
|
|
14261
|
+
});
|
|
14262
|
+
}
|
|
14263
|
+
function allowedValues(property) {
|
|
14264
|
+
const leaf = property;
|
|
14265
|
+
return [...leaf.enum ?? leaf.items?.enum ?? []];
|
|
14266
|
+
}
|
|
14267
|
+
function conditionFor(schema, path, containers, carriers) {
|
|
14268
|
+
const own = carriers.get(path);
|
|
14269
|
+
const ancestor = [...containers].reverse().map((container) => carriers.get(container.path)).find((carrier2) => carrier2 !== void 0 && carrier2.kind === "container");
|
|
14270
|
+
const carrier = own?.kind === "leaf" ? own : ancestor;
|
|
14271
|
+
if (carrier === void 0)
|
|
14272
|
+
return null;
|
|
14273
|
+
const setting = parseConfigurationPath(conditionSetting(carrier.condition));
|
|
14274
|
+
if (setting === null)
|
|
14275
|
+
return null;
|
|
14276
|
+
return {
|
|
14277
|
+
carrier: carrier.kind === "leaf" ? "setting" : "container",
|
|
14278
|
+
carrierPath: carrier.path,
|
|
14279
|
+
setting,
|
|
14280
|
+
printableSetting: printableConfigurationPath(setting),
|
|
14281
|
+
settingLabel: labelOfPath(schema, setting),
|
|
14282
|
+
anyOf: [...carrier.condition.anyOf],
|
|
14283
|
+
anyRow: "path" in carrier.condition && carrier.condition.anyRow === true
|
|
14284
|
+
};
|
|
14285
|
+
}
|
|
14286
|
+
function clauseEdges(schema, leaf, secretPaths) {
|
|
14287
|
+
const parent = immediateContainer(leaf);
|
|
14288
|
+
const scope = parent === null ? schema : parent.kind === "array" ? parent.property.items ?? parent.property : parent.property;
|
|
14289
|
+
const requires = /* @__PURE__ */ new Set();
|
|
14290
|
+
const requiredBy = /* @__PURE__ */ new Set();
|
|
14291
|
+
for (const [trigger, dependents] of Object.entries(scope.dependentRequired ?? {})) {
|
|
14292
|
+
if (trigger === leaf.name)
|
|
14293
|
+
for (const dependent of dependents)
|
|
14294
|
+
requires.add(dependent);
|
|
14295
|
+
else if (dependents.includes(leaf.name))
|
|
14296
|
+
requiredBy.add(trigger);
|
|
14297
|
+
}
|
|
14298
|
+
const order = Object.keys(scope.properties ?? {});
|
|
14299
|
+
const members = (names) => [...names].sort((a, b) => order.indexOf(a) - order.indexOf(b)).flatMap((name) => {
|
|
14300
|
+
const path = memberPath(leaf, name);
|
|
14301
|
+
return path === null ? [] : [
|
|
14302
|
+
{
|
|
14303
|
+
path,
|
|
14304
|
+
printablePath: printableConfigurationPath(path),
|
|
14305
|
+
secret: secretPaths.has(String(path))
|
|
14306
|
+
}
|
|
14307
|
+
];
|
|
14308
|
+
});
|
|
14309
|
+
return { requires: members(requires), requiredBy: members(requiredBy) };
|
|
14310
|
+
}
|
|
14311
|
+
function memberPath(leaf, name) {
|
|
14312
|
+
const parent = immediateContainer(leaf);
|
|
14313
|
+
return parent === null ? configurationPath(name) : configurationPath(...pathSegments(parent.path), name);
|
|
14314
|
+
}
|
|
14315
|
+
function settingContainer(container, requiredByParent) {
|
|
14316
|
+
return {
|
|
14317
|
+
path: container.path,
|
|
14318
|
+
printablePath: printableConfigurationPath(container.path),
|
|
14319
|
+
title: container.property.title,
|
|
14320
|
+
kind: container.kind,
|
|
14321
|
+
required: requiredByParent.has(container.name)
|
|
14322
|
+
};
|
|
14323
|
+
}
|
|
14324
|
+
|
|
14325
|
+
// ../product-configuration/dist/manifest-yaml.js
|
|
14326
|
+
var import_yaml = __toESM(require_dist(), 1);
|
|
14327
|
+
function manifestYaml(document) {
|
|
14328
|
+
return (0, import_yaml.stringify)(document, { compat: "yaml-1.1" });
|
|
14329
|
+
}
|
|
13782
14330
|
|
|
13783
14331
|
// ../platform-schemas/dist/index.js
|
|
13784
14332
|
var TENANT_KEY_REGEX = /^[a-z][a-z0-9-]{2,23}[a-z0-9]$/;
|
|
@@ -13921,6 +14469,53 @@ function isBlockedHost(hostname) {
|
|
|
13921
14469
|
return BLOCKED_HOSTS.some((pattern) => pattern.test(hostname));
|
|
13922
14470
|
}
|
|
13923
14471
|
|
|
14472
|
+
// ../product-contracts/dist/deployment-example.schema.js
|
|
14473
|
+
var DeploymentExampleSchema = external_exports.object({
|
|
14474
|
+
/** The heading a customer reads above the example. */
|
|
14475
|
+
title: external_exports.string().min(3).max(80),
|
|
14476
|
+
/** What this deployment gives the customer, in a sentence or two. */
|
|
14477
|
+
description: external_exports.string().min(20).max(400),
|
|
14478
|
+
/**
|
|
14479
|
+
* A PLAIN STRING, for the reason the Product Instance Template's is: an
|
|
14480
|
+
* unsupported value then reaches the check that names the regions THIS
|
|
14481
|
+
* Product offers, rather than a zod enum error naming the platform's.
|
|
14482
|
+
*/
|
|
14483
|
+
region: external_exports.string().min(1).max(32),
|
|
14484
|
+
/** Plain string, same reasoning as `region`. */
|
|
14485
|
+
profile: external_exports.string().min(1).max(40),
|
|
14486
|
+
/**
|
|
14487
|
+
* The Product's own settings, in the shape `spec.configuration` takes —
|
|
14488
|
+
* nested for a v2 Product. Validated at publish against this release's
|
|
14489
|
+
* configuration schema by the same function a customer's manifest meets.
|
|
14490
|
+
*/
|
|
14491
|
+
configuration: external_exports.record(external_exports.unknown()),
|
|
14492
|
+
/**
|
|
14493
|
+
* The secret settings a deployment of this example supplies, BY NAME.
|
|
14494
|
+
*
|
|
14495
|
+
* Every secret the example's configuration makes required must be named
|
|
14496
|
+
* here, so a reader knows every `--secret` flag the create needs before
|
|
14497
|
+
* running it. The values are the tenant's — sent when the Resource is
|
|
14498
|
+
* created and again only to set a new one — and never written into a
|
|
14499
|
+
* manifest. A secret resent on an unchanged re-apply is written again.
|
|
14500
|
+
*/
|
|
14501
|
+
secretInputs: external_exports.array(external_exports.string().regex(CONFIGURATION_PROPERTY_NAME_REGEX)).max(MAX_CONFIGURATION_CHILDREN).optional()
|
|
14502
|
+
}).strict().describe("The recommended deployment for this release. Required for a NEW release; optional in the schema because releases published before the field existed are immutable. " + DEPLOYMENT_EXAMPLE_GUARANTEE + " Secret settings are named in secretInputs and never given a value here.");
|
|
14503
|
+
var SECRET_INPUT_SEGMENT = CONFIGURATION_PROPERTY_NAME_REGEX.source.slice(1, -1);
|
|
14504
|
+
var SecretInputV2Schema = external_exports.string().regex(new RegExp(`^(?:${SECRET_INPUT_SEGMENT}|(?:/${SECRET_INPUT_SEGMENT}){2,${MAX_CONFIGURATION_DEPTH}})$`)).refine((raw) => secretPathOf(raw) !== null, {
|
|
14505
|
+
message: 'A secret input is a root setting name, or a configuration path such as "/group/setting" with no list index.'
|
|
14506
|
+
});
|
|
14507
|
+
var DeploymentExampleV2Schema = DeploymentExampleSchema.extend({
|
|
14508
|
+
/**
|
|
14509
|
+
* The secret settings a deployment of this example supplies, BY CANONICAL
|
|
14510
|
+
* PATH — a bare name at the root, a pointer inside a group.
|
|
14511
|
+
*
|
|
14512
|
+
* Every secret the example's configuration makes required must be named
|
|
14513
|
+
* here, so a reader knows every `--secret` flag the create needs before
|
|
14514
|
+
* running it. The values are the tenant's and never written into a manifest.
|
|
14515
|
+
*/
|
|
14516
|
+
secretInputs: external_exports.array(SecretInputV2Schema).max(MAX_CONFIGURATION_CHILDREN).optional()
|
|
14517
|
+
});
|
|
14518
|
+
|
|
13924
14519
|
// ../product-contracts/dist/portfolio.js
|
|
13925
14520
|
var PORTFOLIO_CATEGORY_IDS = [
|
|
13926
14521
|
"clients",
|
|
@@ -14507,6 +15102,15 @@ var ProductDefinitionObjectSchema = external_exports.object({
|
|
|
14507
15102
|
// --- Customer choices -------------------------------------------------
|
|
14508
15103
|
profiles: external_exports.array(ServiceProfileSchema).min(1).max(SERVICE_PROFILE_IDS.length),
|
|
14509
15104
|
configuration: CustomerConfigurationSchema,
|
|
15105
|
+
/**
|
|
15106
|
+
* The publisher's recommended deployment of this release. See
|
|
15107
|
+
* `deployment-example.schema.ts`.
|
|
15108
|
+
*
|
|
15109
|
+
* `.optional()`, NEVER `.default(...)`, for the digest reason `conditions`
|
|
15110
|
+
* gives below — and required for a NEW release by `publish-rules.ts`, which
|
|
15111
|
+
* is the only place a rule added after releases were frozen may live.
|
|
15112
|
+
*/
|
|
15113
|
+
deploymentExample: DeploymentExampleSchema.optional(),
|
|
14510
15114
|
// --- Results, relationships, operations -------------------------------
|
|
14511
15115
|
outputs: external_exports.array(ProductOutputSchema).min(1).max(MAX_PRODUCT_OUTPUTS),
|
|
14512
15116
|
dependencies: external_exports.array(ProductDependencySchema).max(8),
|
|
@@ -14543,7 +15147,10 @@ var ProductDefinitionSchema = ProductDefinitionObjectSchema.superRefine(checkPro
|
|
|
14543
15147
|
var ProductDefinitionV2ObjectSchema = ProductDefinitionObjectSchema.extend({
|
|
14544
15148
|
apiVersion: ProductApiVersionV2Schema,
|
|
14545
15149
|
configuration: CustomerConfigurationV2Schema,
|
|
14546
|
-
dependencies: external_exports.array(ProductDependencyV2Schema).max(8)
|
|
15150
|
+
dependencies: external_exports.array(ProductDependencyV2Schema).max(8),
|
|
15151
|
+
// A v2 secret may sit inside a group, so the example names it by PATH. The v1
|
|
15152
|
+
// example keeps its root-name rule, and the v1 artifact its bytes.
|
|
15153
|
+
deploymentExample: DeploymentExampleV2Schema.optional()
|
|
14547
15154
|
});
|
|
14548
15155
|
var ProductDefinitionV2Schema = ProductDefinitionV2ObjectSchema.superRefine(checkProductDefinition);
|
|
14549
15156
|
function checkProductDefinition(definition, ctx) {
|
|
@@ -14720,12 +15327,13 @@ function checkProductDefinition(definition, ctx) {
|
|
|
14720
15327
|
});
|
|
14721
15328
|
}
|
|
14722
15329
|
});
|
|
14723
|
-
for (const
|
|
14724
|
-
const condition =
|
|
14725
|
-
|
|
14726
|
-
|
|
14727
|
-
const
|
|
14728
|
-
|
|
15330
|
+
for (const carrier of conditionCarriers(definition.configuration)) {
|
|
15331
|
+
const { condition } = carrier;
|
|
15332
|
+
const name = printableConfigurationPath(carrier.path);
|
|
15333
|
+
const named = conditionSetting(condition);
|
|
15334
|
+
const anyRow = "anyRow" in condition && condition.anyRow === true;
|
|
15335
|
+
const at = [...declarationIssuePath(carrier), "x-required-when"];
|
|
15336
|
+
if (requiredToRoot(definition.configuration, carrier)) {
|
|
14729
15337
|
ctx.addIssue({
|
|
14730
15338
|
code: external_exports.ZodIssueCode.custom,
|
|
14731
15339
|
path: [...at],
|
|
@@ -14733,7 +15341,7 @@ function checkProductDefinition(definition, ctx) {
|
|
|
14733
15341
|
});
|
|
14734
15342
|
continue;
|
|
14735
15343
|
}
|
|
14736
|
-
if (declaredDefault(property) !== void 0) {
|
|
15344
|
+
if (declaredDefault(carrier.property) !== void 0) {
|
|
14737
15345
|
ctx.addIssue({
|
|
14738
15346
|
code: external_exports.ZodIssueCode.custom,
|
|
14739
15347
|
path: [...at],
|
|
@@ -14748,7 +15356,7 @@ function checkProductDefinition(definition, ctx) {
|
|
|
14748
15356
|
message: "Repeated values in `anyOf`."
|
|
14749
15357
|
});
|
|
14750
15358
|
}
|
|
14751
|
-
if (
|
|
15359
|
+
if (named === carrier.path) {
|
|
14752
15360
|
ctx.addIssue({
|
|
14753
15361
|
code: external_exports.ZodIssueCode.custom,
|
|
14754
15362
|
path: [...at, "path"],
|
|
@@ -14756,30 +15364,30 @@ function checkProductDefinition(definition, ctx) {
|
|
|
14756
15364
|
});
|
|
14757
15365
|
continue;
|
|
14758
15366
|
}
|
|
14759
|
-
const parsed = parseConfigurationPath(
|
|
15367
|
+
const parsed = parseConfigurationPath(named);
|
|
14760
15368
|
const leaf = parsed === null ? null : leafAt(definition.configuration, parsed);
|
|
14761
15369
|
if (leaf === null) {
|
|
14762
15370
|
const isGroup = parsed !== null && declaresPath(definition.configuration, parsed);
|
|
14763
15371
|
ctx.addIssue({
|
|
14764
15372
|
code: external_exports.ZodIssueCode.custom,
|
|
14765
15373
|
path: [...at, "path"],
|
|
14766
|
-
message: isGroup ? `"${
|
|
15374
|
+
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.`
|
|
14767
15375
|
});
|
|
14768
15376
|
continue;
|
|
14769
15377
|
}
|
|
14770
|
-
if (underList(leaf) &&
|
|
15378
|
+
if (underList(leaf) && !anyRow) {
|
|
14771
15379
|
ctx.addIssue({
|
|
14772
15380
|
code: external_exports.ZodIssueCode.custom,
|
|
14773
15381
|
path: [...at, "path"],
|
|
14774
|
-
message: `"${
|
|
15382
|
+
message: `"${named}" 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.`
|
|
14775
15383
|
});
|
|
14776
15384
|
continue;
|
|
14777
15385
|
}
|
|
14778
|
-
if (!underList(leaf) &&
|
|
15386
|
+
if (!underList(leaf) && anyRow) {
|
|
14779
15387
|
ctx.addIssue({
|
|
14780
15388
|
code: external_exports.ZodIssueCode.custom,
|
|
14781
15389
|
path: [...at, "anyRow"],
|
|
14782
|
-
message: `"${
|
|
15390
|
+
message: `"${named}" is not inside a list, so there are no rows to quantify over.`
|
|
14783
15391
|
});
|
|
14784
15392
|
}
|
|
14785
15393
|
const target = leaf.property;
|
|
@@ -14787,7 +15395,7 @@ function checkProductDefinition(definition, ctx) {
|
|
|
14787
15395
|
ctx.addIssue({
|
|
14788
15396
|
code: external_exports.ZodIssueCode.custom,
|
|
14789
15397
|
path: [...at, "path"],
|
|
14790
|
-
message: `"${
|
|
15398
|
+
message: `"${named}" is a ${target.type} setting; a condition compares string values.`
|
|
14791
15399
|
});
|
|
14792
15400
|
continue;
|
|
14793
15401
|
}
|
|
@@ -14795,21 +15403,16 @@ function checkProductDefinition(definition, ctx) {
|
|
|
14795
15403
|
ctx.addIssue({
|
|
14796
15404
|
code: external_exports.ZodIssueCode.custom,
|
|
14797
15405
|
path: [...at, "path"],
|
|
14798
|
-
message: `"${
|
|
15406
|
+
message: `"${named}" is a secret. The platform never reads a secret's value, so a condition on one could never be evaluated.`
|
|
14799
15407
|
});
|
|
14800
15408
|
continue;
|
|
14801
15409
|
}
|
|
14802
|
-
const
|
|
14803
|
-
...definition.configuration,
|
|
14804
|
-
properties: { [leaf.name]: target },
|
|
14805
|
-
required: [leaf.name]
|
|
14806
|
-
});
|
|
14807
|
-
const outside = condition.anyOf.filter((value) => !validateSubmittedConfiguration(single, { [leaf.name]: value }).ok);
|
|
15410
|
+
const outside = valuesOutsideSpace(definition.configuration, leaf, condition.anyOf);
|
|
14808
15411
|
if (outside.length > 0) {
|
|
14809
15412
|
ctx.addIssue({
|
|
14810
15413
|
code: external_exports.ZodIssueCode.custom,
|
|
14811
15414
|
path: [...at, "anyOf"],
|
|
14812
|
-
message: `"${
|
|
15415
|
+
message: `"${named}" never accepts ${outside.map((v) => `"${v}"`).join(", ")}, so this condition could never make the secret required.`
|
|
14813
15416
|
});
|
|
14814
15417
|
}
|
|
14815
15418
|
}
|
|
@@ -15142,6 +15745,225 @@ var ANNOUNCED_PRODUCTS = Object.freeze([
|
|
|
15142
15745
|
}
|
|
15143
15746
|
]);
|
|
15144
15747
|
|
|
15748
|
+
// ../product-contracts/dist/contract-issue.js
|
|
15749
|
+
var PATH_SEGMENT = /^[A-Za-z_][A-Za-z0-9_-]{0,63}$/;
|
|
15750
|
+
var UNPRINTABLE_SEGMENT = "<unprintable>";
|
|
15751
|
+
var MAX_SEGMENTS = MAX_CONFIGURATION_PATH_SEGMENTS + 2;
|
|
15752
|
+
function printableContractPath(path) {
|
|
15753
|
+
const printable = path.slice(0, MAX_SEGMENTS).map((segment) => typeof segment === "number" || PATH_SEGMENT.test(segment) ? String(segment) : UNPRINTABLE_SEGMENT);
|
|
15754
|
+
return printable.join(".") || "(document)";
|
|
15755
|
+
}
|
|
15756
|
+
function manifestConfigurationField(field) {
|
|
15757
|
+
if (field === "")
|
|
15758
|
+
return "spec.configuration";
|
|
15759
|
+
const parsed = parseConfigurationPath(field);
|
|
15760
|
+
return printableContractPath([
|
|
15761
|
+
"spec",
|
|
15762
|
+
"configuration",
|
|
15763
|
+
...parsed === null ? [field] : pathSegments(parsed)
|
|
15764
|
+
]);
|
|
15765
|
+
}
|
|
15766
|
+
|
|
15767
|
+
// ../product-contracts/dist/resource-output-reference.js
|
|
15768
|
+
var OUTPUT_KEY_RULE = "Output keys are camelCase.";
|
|
15769
|
+
var ResourceOutputReferenceSchema = external_exports.object({
|
|
15770
|
+
valueFrom: external_exports.object({
|
|
15771
|
+
resourceOutput: external_exports.object({
|
|
15772
|
+
/** The producing Resource's `metadata.key`, in the same Project. */
|
|
15773
|
+
resourceKey: ResourceKeySchema,
|
|
15774
|
+
/** The key of an output that Resource's Product declares. */
|
|
15775
|
+
output: external_exports.string().regex(/^[a-z][a-zA-Z0-9]{1,47}$/, OUTPUT_KEY_RULE)
|
|
15776
|
+
}).strict()
|
|
15777
|
+
}).strict()
|
|
15778
|
+
}).strict();
|
|
15779
|
+
function looksLikeReference(value) {
|
|
15780
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && "valueFrom" in value;
|
|
15781
|
+
}
|
|
15782
|
+
|
|
15783
|
+
// ../product-contracts/dist/dependency-binding.js
|
|
15784
|
+
var DependencyBindingSelectorSchema = external_exports.object({
|
|
15785
|
+
/** The providing Resource's `metadata.key`. */
|
|
15786
|
+
resourceKey: ResourceKeySchema,
|
|
15787
|
+
/**
|
|
15788
|
+
* The Project that Resource lives in. Absent means the consumer's own.
|
|
15789
|
+
*
|
|
15790
|
+
* PRESENT BECAUSE A BINDING IS TENANT-SCOPED AND A PROJECT IS NOT A RUNTIME
|
|
15791
|
+
* BOUNDARY: a provider anywhere in the Tenant can satisfy a consumer
|
|
15792
|
+
* anywhere in it, and that has been true since bindings existed. What the
|
|
15793
|
+
* field does NOT do is grant anything — the Control Plane opens that
|
|
15794
|
+
* Project from the caller's own assertion, and a Project the caller holds
|
|
15795
|
+
* nothing in answers exactly as a Project that does not exist.
|
|
15796
|
+
*
|
|
15797
|
+
* A plain bounded string, exactly as `spec.projectId` is: an id that does
|
|
15798
|
+
* not name a readable Project is refused where the refusal can be phrased
|
|
15799
|
+
* without leaking, which is not here.
|
|
15800
|
+
*/
|
|
15801
|
+
projectId: external_exports.string().min(1).max(64).optional()
|
|
15802
|
+
}).strict();
|
|
15803
|
+
var DependencyBindingsSchema = external_exports.record(ServiceBindingKeySchema, DependencyBindingSelectorSchema);
|
|
15804
|
+
|
|
15805
|
+
// ../product-contracts/dist/product-instance-template.schema.js
|
|
15806
|
+
var ProductInstanceTemplateSchema = external_exports.object({
|
|
15807
|
+
apiVersion: ResourceApiVersionSchema,
|
|
15808
|
+
kind: kindSchema(PRODUCT_INSTANCE_KIND),
|
|
15809
|
+
metadata: external_exports.object({
|
|
15810
|
+
/**
|
|
15811
|
+
* The customer-facing Resource name — "Local service".
|
|
15812
|
+
*
|
|
15813
|
+
* Maps to `ProductInstance.displayName`, and deliberately introduces NO
|
|
15814
|
+
* new identity concept. `pri_…` remains the stable identity and
|
|
15815
|
+
* `instanceKey` remains the cloud resource key; adding a third name here
|
|
15816
|
+
* would mean a fourth thing to keep unique and a fifth to explain.
|
|
15817
|
+
* Bounded exactly as the create envelope bounds `displayName`.
|
|
15818
|
+
*/
|
|
15819
|
+
// NOT `.trim()`: this schema is PUBLISHED as JSON Schema so a customer
|
|
15820
|
+
// can validate the file before sending it, and a trim is a transform
|
|
15821
|
+
// that JSON Schema cannot express. The value is trimmed when it becomes
|
|
15822
|
+
// a create command instead, so the published document and the platform
|
|
15823
|
+
// agree on what is valid.
|
|
15824
|
+
name: external_exports.string().min(1).max(80).regex(/\S/, "The resource name must contain a non-whitespace character. A name of only spaces trims to nothing, and an empty display name is not a name."),
|
|
15825
|
+
/**
|
|
15826
|
+
* THE STABLE IDENTITY OF THIS RESOURCE INSIDE ITS PROJECT — and the one
|
|
15827
|
+
* thing that makes applying a document twice different from creating
|
|
15828
|
+
* two resources.
|
|
15829
|
+
*
|
|
15830
|
+
* THIS IS THE FOURTH NAME, AND THE HEADER ABOVE ARGUES AGAINST ONE.
|
|
15831
|
+
* That argument stands and is answered rather than ignored: a
|
|
15832
|
+
* declarative apply is not expressible without a key the AUTHOR knows
|
|
15833
|
+
* before the platform has created anything, and nothing that already
|
|
15834
|
+
* exists is one.
|
|
15835
|
+
*
|
|
15836
|
+
* `metadata.name` mutable, not unique, and rule 1 above says it is
|
|
15837
|
+
* display copy rather than identity.
|
|
15838
|
+
* `pri_…` minted by the server. It does not exist when the
|
|
15839
|
+
* first apply is written, which is the only moment
|
|
15840
|
+
* that matters.
|
|
15841
|
+
* `instanceKey` derived from `pri_…` and then frozen — the same
|
|
15842
|
+
* problem one step removed.
|
|
15843
|
+
* idempotency key a de-duplication token that "names nothing,
|
|
15844
|
+
* grants nothing, and selects nothing". Making it
|
|
15845
|
+
* select a resource would falsify the security
|
|
15846
|
+
* argument the signed-request layer rests on.
|
|
15847
|
+
*
|
|
15848
|
+
* So it gets exactly ONE job: to say which resource in this Project the
|
|
15849
|
+
* document is about. It is NEVER authorization — the Project comes from
|
|
15850
|
+
* the request, the row is loaded inside it, and a key naming nothing
|
|
15851
|
+
* there is a not-found rather than a create. And it NEVER becomes a
|
|
15852
|
+
* provider resource name; `instanceKey` owns that, and coupling the two
|
|
15853
|
+
* would let a customer's chosen string reach Scaleway.
|
|
15854
|
+
*
|
|
15855
|
+
* OPTIONAL HERE, REQUIRED AT APPLY. The document is a published `v1`
|
|
15856
|
+
* contract and validating one on its own is a legitimate thing to do
|
|
15857
|
+
* with no Project in sight — the same asymmetry `resolveTemplate`
|
|
15858
|
+
* already has for `routeProjectId`. What refuses a document without a
|
|
15859
|
+
* key is the apply path, because a create that cannot be run again is
|
|
15860
|
+
* not deployment as code.
|
|
15861
|
+
*
|
|
15862
|
+
* Shaped like a DNS label: it becomes a database predicate and a log
|
|
15863
|
+
* field, and neither wants free text.
|
|
15864
|
+
*/
|
|
15865
|
+
key: ResourceKeySchema.optional()
|
|
15866
|
+
}).strict(),
|
|
15867
|
+
spec: external_exports.object({
|
|
15868
|
+
productId: external_exports.string().min(1).max(64),
|
|
15869
|
+
/**
|
|
15870
|
+
* REQUIRED, unlike the create envelope's, which may be omitted to take
|
|
15871
|
+
* the newest deployable release.
|
|
15872
|
+
*
|
|
15873
|
+
* A template is a record of a decision, and "newest" is not one. An
|
|
15874
|
+
* optional version would mean the same document produced 1.0.0 today and
|
|
15875
|
+
* 1.1.0 after an unrelated promotion — so re-applying an exported
|
|
15876
|
+
* template would not reproduce what was exported, which is the single
|
|
15877
|
+
* property that makes templates worth having.
|
|
15878
|
+
*/
|
|
15879
|
+
productVersion: external_exports.string().min(1).max(64),
|
|
15880
|
+
/** Round-trip only — see rule 3 above. Never authorization. */
|
|
15881
|
+
projectId: external_exports.string().min(1).max(64).optional(),
|
|
15882
|
+
region: external_exports.string().min(1).max(32),
|
|
15883
|
+
profile: external_exports.string().min(1).max(40),
|
|
15884
|
+
/**
|
|
15885
|
+
* The Product's own settings.
|
|
15886
|
+
*
|
|
15887
|
+
* An OBJECT with product-specific keys, not `unknown`: the published
|
|
15888
|
+
* JSON Schema has to say at least that much, and the converter refuses
|
|
15889
|
+
* `unknown` precisely so a published contract cannot silently widen.
|
|
15890
|
+
* The keys themselves are validated against the pinned Definition's
|
|
15891
|
+
* configuration schema by `resolveTemplate`, which is where the real
|
|
15892
|
+
* shape lives — it differs per Product, so it cannot be stated here.
|
|
15893
|
+
*/
|
|
15894
|
+
configuration: external_exports.record(external_exports.unknown()).optional(),
|
|
15895
|
+
/**
|
|
15896
|
+
* Which Resource satisfies each declared service dependency.
|
|
15897
|
+
*
|
|
15898
|
+
* A RESOURCE RELATION, NOT A SETTING, which is why it is a sibling of
|
|
15899
|
+
* `configuration` rather than a key inside it. `configuration` is the
|
|
15900
|
+
* Product's own settings and is validated against the Definition's
|
|
15901
|
+
* schema; this names another Resource, and the create envelope made the
|
|
15902
|
+
* identical split for the identical reason.
|
|
15903
|
+
*
|
|
15904
|
+
* OPTIONAL, PERMANENTLY. Every manifest written before this field
|
|
15905
|
+
* existed omits it, and absence has to keep meaning "leave the
|
|
15906
|
+
* connections alone" — otherwise re-applying an unchanged file would
|
|
15907
|
+
* release a binding somebody made in the portal.
|
|
15908
|
+
*
|
|
15909
|
+
* The keys the platform will ACCEPT here are narrower than the shape:
|
|
15910
|
+
* only a dependency whose interface the registry calls
|
|
15911
|
+
* `tenant-multiple` is one a customer chooses. `resolveTemplate` says
|
|
15912
|
+
* so against the pinned Definition, so the refusal names the field.
|
|
15913
|
+
*/
|
|
15914
|
+
dependencyBindings: DependencyBindingsSchema.optional()
|
|
15915
|
+
}).strict()
|
|
15916
|
+
}).strict();
|
|
15917
|
+
function secretValuesInDocument(schema, configuration) {
|
|
15918
|
+
if (!isPlainRecord(configuration) || !isPlainRecord(schema.properties))
|
|
15919
|
+
return [];
|
|
15920
|
+
const declarations = plainDeclarations(schema.properties);
|
|
15921
|
+
return declaredSecretPaths({
|
|
15922
|
+
properties: declarations
|
|
15923
|
+
}).filter((path) => {
|
|
15924
|
+
const segments = pathSegments(path).map(String);
|
|
15925
|
+
let parent = configuration;
|
|
15926
|
+
for (const segment of segments.slice(0, -1)) {
|
|
15927
|
+
parent = isPlainRecord(parent) ? parent[segment] : void 0;
|
|
15928
|
+
}
|
|
15929
|
+
const name = segments[segments.length - 1];
|
|
15930
|
+
return isPlainRecord(parent) && Object.hasOwn(parent, name) && !looksLikeReference(parent[name]);
|
|
15931
|
+
});
|
|
15932
|
+
}
|
|
15933
|
+
function plainDeclarations(properties) {
|
|
15934
|
+
const out = {};
|
|
15935
|
+
for (const [name, property] of Object.entries(properties)) {
|
|
15936
|
+
if (!isPlainRecord(property) || typeof property["type"] !== "string")
|
|
15937
|
+
continue;
|
|
15938
|
+
const copy = { ...property };
|
|
15939
|
+
if (Object.hasOwn(copy, "properties")) {
|
|
15940
|
+
if (isPlainRecord(copy["properties"]))
|
|
15941
|
+
copy["properties"] = plainDeclarations(copy["properties"]);
|
|
15942
|
+
else
|
|
15943
|
+
delete copy["properties"];
|
|
15944
|
+
}
|
|
15945
|
+
if (Object.hasOwn(copy, "items")) {
|
|
15946
|
+
const items = copy["items"];
|
|
15947
|
+
if (!isPlainRecord(items) || typeof items["type"] !== "string")
|
|
15948
|
+
delete copy["items"];
|
|
15949
|
+
else if (Object.hasOwn(items, "properties")) {
|
|
15950
|
+
copy["items"] = isPlainRecord(items["properties"]) ? { ...items, properties: plainDeclarations(items["properties"]) } : (({ properties: _dropped, ...rest }) => rest)(items);
|
|
15951
|
+
}
|
|
15952
|
+
}
|
|
15953
|
+
out[name] = copy;
|
|
15954
|
+
}
|
|
15955
|
+
return out;
|
|
15956
|
+
}
|
|
15957
|
+
function secretValueIssue(name) {
|
|
15958
|
+
return {
|
|
15959
|
+
field: manifestConfigurationField(name),
|
|
15960
|
+
message: `"${name}" is secret input and must never appear in a template. Supply it separately when applying, so it goes to the secret manager rather than into a document.`
|
|
15961
|
+
};
|
|
15962
|
+
}
|
|
15963
|
+
function isPlainRecord(value) {
|
|
15964
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15965
|
+
}
|
|
15966
|
+
|
|
15145
15967
|
// ../product-contracts/dist/publish-rules.js
|
|
15146
15968
|
var PRODUCT_DISPLAY_NAME_PATTERN = "^[^\\u2014\\s](?:[^\\u2014]*[^\\u2014\\s])? \\u2014 [^\\u2014\\s](?:[^\\u2014]*[^\\u2014\\s])?$";
|
|
15147
15969
|
var PRODUCT_DISPLAY_NAME_REGEX = new RegExp(PRODUCT_DISPLAY_NAME_PATTERN, "u");
|
|
@@ -15464,8 +16286,12 @@ var EnvBindingV2Schema = external_exports.discriminatedUnion("source", [
|
|
|
15464
16286
|
*
|
|
15465
16287
|
* WHAT IT CARRIES: validated, non-secret configuration with resolved
|
|
15466
16288
|
* ORDINARY resource references materialised in place. Never a secret
|
|
15467
|
-
* value —
|
|
15468
|
-
*
|
|
16289
|
+
* value — a secret travels through `secrets[]`, and its value is split out
|
|
16290
|
+
* of the configuration before anything is stored. A secret may sit inside
|
|
16291
|
+
* a group, so `validateProductContracts` refuses a `path` naming a secret
|
|
16292
|
+
* or a group that contains one anywhere below it: a component bound to
|
|
16293
|
+
* that group would read it as complete while its credential is absent.
|
|
16294
|
+
* Serialised with `canonicalJson`, so the same
|
|
15469
16295
|
* logical configuration is the same bytes and the environment fingerprint
|
|
15470
16296
|
* does not move on key order alone.
|
|
15471
16297
|
*
|
|
@@ -15815,6 +16641,20 @@ var SecretWiringSchema = external_exports.object({
|
|
|
15815
16641
|
envVar: EnvVarNameSchema
|
|
15816
16642
|
}).strict()).min(1).max(12)
|
|
15817
16643
|
}).strict();
|
|
16644
|
+
var SecretWiringV2Schema = SecretWiringSchema.extend({
|
|
16645
|
+
/**
|
|
16646
|
+
* LEGACY SPELLING of a ROOT secret's path, kept so released v2 Blueprints
|
|
16647
|
+
* still read. Described here rather than in the declaration table, whose note
|
|
16648
|
+
* is what the v1 artifact publishes for the same field.
|
|
16649
|
+
*/
|
|
16650
|
+
configurationProperty: external_exports.string().regex(/^[a-z][a-zA-Z0-9]{1,47}$/).describe("Accepted only so releases published before `configurationPath` existed still read. It names a root secret and means the same as `configurationPath` with that name; a new release must use `configurationPath`, and a Blueprint may not name both.").optional(),
|
|
16651
|
+
/**
|
|
16652
|
+
* Set only for `customer-configuration`: the canonical path of the `x-secret`
|
|
16653
|
+
* setting in the Product Definition whose write-only submission populates
|
|
16654
|
+
* this secret — a bare name at the root, a pointer inside a group.
|
|
16655
|
+
*/
|
|
16656
|
+
configurationPath: ConfigurationBindingPathSchema.optional()
|
|
16657
|
+
}).strict();
|
|
15818
16658
|
var DataCategorySchema = external_exports.enum([
|
|
15819
16659
|
"prompt-content",
|
|
15820
16660
|
"model-response",
|
|
@@ -16244,7 +17084,8 @@ var DeploymentBlueprintV1ObjectSchema = DeploymentBlueprintObjectSchema.extend({
|
|
|
16244
17084
|
});
|
|
16245
17085
|
var DeploymentBlueprintV2ObjectSchema = DeploymentBlueprintObjectSchema.extend({
|
|
16246
17086
|
apiVersion: ProductApiVersionV2Schema,
|
|
16247
|
-
components: external_exports.array(RuntimeComponentV2Schema).min(1).max(MAX_RUNTIME_COMPONENTS)
|
|
17087
|
+
components: external_exports.array(RuntimeComponentV2Schema).min(1).max(MAX_RUNTIME_COMPONENTS),
|
|
17088
|
+
secrets: external_exports.array(SecretWiringV2Schema).max(20)
|
|
16248
17089
|
});
|
|
16249
17090
|
var DeploymentBlueprintV1Schema = DeploymentBlueprintV1ObjectSchema.superRefine(checkDeploymentBlueprint);
|
|
16250
17091
|
var DeploymentBlueprintV2Schema = DeploymentBlueprintV2ObjectSchema.superRefine(checkDeploymentBlueprint);
|
|
@@ -16628,18 +17469,28 @@ function checkDeploymentBlueprint(blueprint, ctx) {
|
|
|
16628
17469
|
});
|
|
16629
17470
|
}
|
|
16630
17471
|
}
|
|
16631
|
-
|
|
17472
|
+
const pathField = "configurationPath" in secret ? secret.configurationPath : void 0;
|
|
17473
|
+
const namesSetting = secret.configurationProperty !== void 0 || pathField !== void 0;
|
|
17474
|
+
const settingField = blueprint.apiVersion === PRODUCT_API_VERSION ? "configurationProperty" : "configurationPath";
|
|
17475
|
+
if (secret.configurationProperty !== void 0 && pathField !== void 0) {
|
|
17476
|
+
ctx.addIssue({
|
|
17477
|
+
code: external_exports.ZodIssueCode.custom,
|
|
17478
|
+
path,
|
|
17479
|
+
message: `Secret "${secret.key}" names both configurationProperty and configurationPath. Name the setting once, with configurationPath.`
|
|
17480
|
+
});
|
|
17481
|
+
}
|
|
17482
|
+
if (secret.source === "customer-configuration" && !namesSetting) {
|
|
16632
17483
|
ctx.addIssue({
|
|
16633
17484
|
code: external_exports.ZodIssueCode.custom,
|
|
16634
17485
|
path,
|
|
16635
|
-
message: `Secret "${secret.key}" is sourced from customer configuration but names no
|
|
17486
|
+
message: `Secret "${secret.key}" is sourced from customer configuration but names no ${settingField}.`
|
|
16636
17487
|
});
|
|
16637
17488
|
}
|
|
16638
|
-
if (secret.source !== "customer-configuration" &&
|
|
17489
|
+
if (secret.source !== "customer-configuration" && namesSetting) {
|
|
16639
17490
|
ctx.addIssue({
|
|
16640
17491
|
code: external_exports.ZodIssueCode.custom,
|
|
16641
17492
|
path,
|
|
16642
|
-
message: `Secret "${secret.key}" names a configurationProperty but is not customer-sourced.`
|
|
17493
|
+
message: `Secret "${secret.key}" names a ${pathField !== void 0 ? "configurationPath" : "configurationProperty"} but is not customer-sourced.`
|
|
16643
17494
|
});
|
|
16644
17495
|
}
|
|
16645
17496
|
if (secret.source === "service-binding" && secret.serviceBinding === void 0) {
|
|
@@ -17017,193 +17868,6 @@ function isTerminalAction(action) {
|
|
|
17017
17868
|
}
|
|
17018
17869
|
var ManifestActionSchema = external_exports.enum(MANIFEST_ACTIONS);
|
|
17019
17870
|
|
|
17020
|
-
// ../product-contracts/dist/contract-issue.js
|
|
17021
|
-
var PATH_SEGMENT = /^[A-Za-z_][A-Za-z0-9_-]{0,63}$/;
|
|
17022
|
-
var UNPRINTABLE_SEGMENT = "<unprintable>";
|
|
17023
|
-
var MAX_SEGMENTS = MAX_CONFIGURATION_PATH_SEGMENTS + 2;
|
|
17024
|
-
function printableContractPath(path) {
|
|
17025
|
-
const printable = path.slice(0, MAX_SEGMENTS).map((segment) => typeof segment === "number" || PATH_SEGMENT.test(segment) ? String(segment) : UNPRINTABLE_SEGMENT);
|
|
17026
|
-
return printable.join(".") || "(document)";
|
|
17027
|
-
}
|
|
17028
|
-
function manifestConfigurationField(field) {
|
|
17029
|
-
if (field === "")
|
|
17030
|
-
return "spec.configuration";
|
|
17031
|
-
const parsed = parseConfigurationPath(field);
|
|
17032
|
-
return printableContractPath([
|
|
17033
|
-
"spec",
|
|
17034
|
-
"configuration",
|
|
17035
|
-
...parsed === null ? [field] : pathSegments(parsed)
|
|
17036
|
-
]);
|
|
17037
|
-
}
|
|
17038
|
-
|
|
17039
|
-
// ../product-contracts/dist/resource-output-reference.js
|
|
17040
|
-
var OUTPUT_KEY_RULE = "Output keys are camelCase.";
|
|
17041
|
-
var ResourceOutputReferenceSchema = external_exports.object({
|
|
17042
|
-
valueFrom: external_exports.object({
|
|
17043
|
-
resourceOutput: external_exports.object({
|
|
17044
|
-
/** The producing Resource's `metadata.key`, in the same Project. */
|
|
17045
|
-
resourceKey: ResourceKeySchema,
|
|
17046
|
-
/** The key of an output that Resource's Product declares. */
|
|
17047
|
-
output: external_exports.string().regex(/^[a-z][a-zA-Z0-9]{1,47}$/, OUTPUT_KEY_RULE)
|
|
17048
|
-
}).strict()
|
|
17049
|
-
}).strict()
|
|
17050
|
-
}).strict();
|
|
17051
|
-
function looksLikeReference(value) {
|
|
17052
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) && "valueFrom" in value;
|
|
17053
|
-
}
|
|
17054
|
-
|
|
17055
|
-
// ../product-contracts/dist/dependency-binding.js
|
|
17056
|
-
var DependencyBindingSelectorSchema = external_exports.object({
|
|
17057
|
-
/** The providing Resource's `metadata.key`. */
|
|
17058
|
-
resourceKey: ResourceKeySchema,
|
|
17059
|
-
/**
|
|
17060
|
-
* The Project that Resource lives in. Absent means the consumer's own.
|
|
17061
|
-
*
|
|
17062
|
-
* PRESENT BECAUSE A BINDING IS TENANT-SCOPED AND A PROJECT IS NOT A RUNTIME
|
|
17063
|
-
* BOUNDARY: a provider anywhere in the Tenant can satisfy a consumer
|
|
17064
|
-
* anywhere in it, and that has been true since bindings existed. What the
|
|
17065
|
-
* field does NOT do is grant anything — the Control Plane opens that
|
|
17066
|
-
* Project from the caller's own assertion, and a Project the caller holds
|
|
17067
|
-
* nothing in answers exactly as a Project that does not exist.
|
|
17068
|
-
*
|
|
17069
|
-
* A plain bounded string, exactly as `spec.projectId` is: an id that does
|
|
17070
|
-
* not name a readable Project is refused where the refusal can be phrased
|
|
17071
|
-
* without leaking, which is not here.
|
|
17072
|
-
*/
|
|
17073
|
-
projectId: external_exports.string().min(1).max(64).optional()
|
|
17074
|
-
}).strict();
|
|
17075
|
-
var DependencyBindingsSchema = external_exports.record(ServiceBindingKeySchema, DependencyBindingSelectorSchema);
|
|
17076
|
-
|
|
17077
|
-
// ../product-contracts/dist/product-instance-template.schema.js
|
|
17078
|
-
var ProductInstanceTemplateSchema = external_exports.object({
|
|
17079
|
-
apiVersion: ResourceApiVersionSchema,
|
|
17080
|
-
kind: kindSchema(PRODUCT_INSTANCE_KIND),
|
|
17081
|
-
metadata: external_exports.object({
|
|
17082
|
-
/**
|
|
17083
|
-
* The customer-facing Resource name — "Local service".
|
|
17084
|
-
*
|
|
17085
|
-
* Maps to `ProductInstance.displayName`, and deliberately introduces NO
|
|
17086
|
-
* new identity concept. `pri_…` remains the stable identity and
|
|
17087
|
-
* `instanceKey` remains the cloud resource key; adding a third name here
|
|
17088
|
-
* would mean a fourth thing to keep unique and a fifth to explain.
|
|
17089
|
-
* Bounded exactly as the create envelope bounds `displayName`.
|
|
17090
|
-
*/
|
|
17091
|
-
// NOT `.trim()`: this schema is PUBLISHED as JSON Schema so a customer
|
|
17092
|
-
// can validate the file before sending it, and a trim is a transform
|
|
17093
|
-
// that JSON Schema cannot express. The value is trimmed when it becomes
|
|
17094
|
-
// a create command instead, so the published document and the platform
|
|
17095
|
-
// agree on what is valid.
|
|
17096
|
-
name: external_exports.string().min(1).max(80).regex(/\S/, "The resource name must contain a non-whitespace character. A name of only spaces trims to nothing, and an empty display name is not a name."),
|
|
17097
|
-
/**
|
|
17098
|
-
* THE STABLE IDENTITY OF THIS RESOURCE INSIDE ITS PROJECT — and the one
|
|
17099
|
-
* thing that makes applying a document twice different from creating
|
|
17100
|
-
* two resources.
|
|
17101
|
-
*
|
|
17102
|
-
* THIS IS THE FOURTH NAME, AND THE HEADER ABOVE ARGUES AGAINST ONE.
|
|
17103
|
-
* That argument stands and is answered rather than ignored: a
|
|
17104
|
-
* declarative apply is not expressible without a key the AUTHOR knows
|
|
17105
|
-
* before the platform has created anything, and nothing that already
|
|
17106
|
-
* exists is one.
|
|
17107
|
-
*
|
|
17108
|
-
* `metadata.name` mutable, not unique, and rule 1 above says it is
|
|
17109
|
-
* display copy rather than identity.
|
|
17110
|
-
* `pri_…` minted by the server. It does not exist when the
|
|
17111
|
-
* first apply is written, which is the only moment
|
|
17112
|
-
* that matters.
|
|
17113
|
-
* `instanceKey` derived from `pri_…` and then frozen — the same
|
|
17114
|
-
* problem one step removed.
|
|
17115
|
-
* idempotency key a de-duplication token that "names nothing,
|
|
17116
|
-
* grants nothing, and selects nothing". Making it
|
|
17117
|
-
* select a resource would falsify the security
|
|
17118
|
-
* argument the signed-request layer rests on.
|
|
17119
|
-
*
|
|
17120
|
-
* So it gets exactly ONE job: to say which resource in this Project the
|
|
17121
|
-
* document is about. It is NEVER authorization — the Project comes from
|
|
17122
|
-
* the request, the row is loaded inside it, and a key naming nothing
|
|
17123
|
-
* there is a not-found rather than a create. And it NEVER becomes a
|
|
17124
|
-
* provider resource name; `instanceKey` owns that, and coupling the two
|
|
17125
|
-
* would let a customer's chosen string reach Scaleway.
|
|
17126
|
-
*
|
|
17127
|
-
* OPTIONAL HERE, REQUIRED AT APPLY. The document is a published `v1`
|
|
17128
|
-
* contract and validating one on its own is a legitimate thing to do
|
|
17129
|
-
* with no Project in sight — the same asymmetry `resolveTemplate`
|
|
17130
|
-
* already has for `routeProjectId`. What refuses a document without a
|
|
17131
|
-
* key is the apply path, because a create that cannot be run again is
|
|
17132
|
-
* not deployment as code.
|
|
17133
|
-
*
|
|
17134
|
-
* Shaped like a DNS label: it becomes a database predicate and a log
|
|
17135
|
-
* field, and neither wants free text.
|
|
17136
|
-
*/
|
|
17137
|
-
key: ResourceKeySchema.optional()
|
|
17138
|
-
}).strict(),
|
|
17139
|
-
spec: external_exports.object({
|
|
17140
|
-
productId: external_exports.string().min(1).max(64),
|
|
17141
|
-
/**
|
|
17142
|
-
* REQUIRED, unlike the create envelope's, which may be omitted to take
|
|
17143
|
-
* the newest deployable release.
|
|
17144
|
-
*
|
|
17145
|
-
* A template is a record of a decision, and "newest" is not one. An
|
|
17146
|
-
* optional version would mean the same document produced 1.0.0 today and
|
|
17147
|
-
* 1.1.0 after an unrelated promotion — so re-applying an exported
|
|
17148
|
-
* template would not reproduce what was exported, which is the single
|
|
17149
|
-
* property that makes templates worth having.
|
|
17150
|
-
*/
|
|
17151
|
-
productVersion: external_exports.string().min(1).max(64),
|
|
17152
|
-
/** Round-trip only — see rule 3 above. Never authorization. */
|
|
17153
|
-
projectId: external_exports.string().min(1).max(64).optional(),
|
|
17154
|
-
region: external_exports.string().min(1).max(32),
|
|
17155
|
-
profile: external_exports.string().min(1).max(40),
|
|
17156
|
-
/**
|
|
17157
|
-
* The Product's own settings.
|
|
17158
|
-
*
|
|
17159
|
-
* An OBJECT with product-specific keys, not `unknown`: the published
|
|
17160
|
-
* JSON Schema has to say at least that much, and the converter refuses
|
|
17161
|
-
* `unknown` precisely so a published contract cannot silently widen.
|
|
17162
|
-
* The keys themselves are validated against the pinned Definition's
|
|
17163
|
-
* configuration schema by `resolveTemplate`, which is where the real
|
|
17164
|
-
* shape lives — it differs per Product, so it cannot be stated here.
|
|
17165
|
-
*/
|
|
17166
|
-
configuration: external_exports.record(external_exports.unknown()).optional(),
|
|
17167
|
-
/**
|
|
17168
|
-
* Which Resource satisfies each declared service dependency.
|
|
17169
|
-
*
|
|
17170
|
-
* A RESOURCE RELATION, NOT A SETTING, which is why it is a sibling of
|
|
17171
|
-
* `configuration` rather than a key inside it. `configuration` is the
|
|
17172
|
-
* Product's own settings and is validated against the Definition's
|
|
17173
|
-
* schema; this names another Resource, and the create envelope made the
|
|
17174
|
-
* identical split for the identical reason.
|
|
17175
|
-
*
|
|
17176
|
-
* OPTIONAL, PERMANENTLY. Every manifest written before this field
|
|
17177
|
-
* existed omits it, and absence has to keep meaning "leave the
|
|
17178
|
-
* connections alone" — otherwise re-applying an unchanged file would
|
|
17179
|
-
* release a binding somebody made in the portal.
|
|
17180
|
-
*
|
|
17181
|
-
* The keys the platform will ACCEPT here are narrower than the shape:
|
|
17182
|
-
* only a dependency whose interface the registry calls
|
|
17183
|
-
* `tenant-multiple` is one a customer chooses. `resolveTemplate` says
|
|
17184
|
-
* so against the pinned Definition, so the refusal names the field.
|
|
17185
|
-
*/
|
|
17186
|
-
dependencyBindings: DependencyBindingsSchema.optional()
|
|
17187
|
-
}).strict()
|
|
17188
|
-
}).strict();
|
|
17189
|
-
function secretValuesInDocument(schema, configuration) {
|
|
17190
|
-
if (!isPlainRecord(configuration) || !isPlainRecord(schema.properties))
|
|
17191
|
-
return [];
|
|
17192
|
-
const declarations = Object.fromEntries(Object.entries(schema.properties).filter(([, property]) => isPlainRecord(property)));
|
|
17193
|
-
return declaredSecretNames({
|
|
17194
|
-
properties: declarations
|
|
17195
|
-
}).filter((name) => Object.hasOwn(configuration, name) && !looksLikeReference(configuration[name]));
|
|
17196
|
-
}
|
|
17197
|
-
function secretValueIssue(name) {
|
|
17198
|
-
return {
|
|
17199
|
-
field: manifestConfigurationField(name),
|
|
17200
|
-
message: `"${name}" is secret input and must never appear in a template. Supply it separately when applying, so it goes to the secret manager rather than into a document.`
|
|
17201
|
-
};
|
|
17202
|
-
}
|
|
17203
|
-
function isPlainRecord(value) {
|
|
17204
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17205
|
-
}
|
|
17206
|
-
|
|
17207
17871
|
// ../product-contracts/dist/product-submission.schema.js
|
|
17208
17872
|
var PRODUCT_SUBMISSION_KIND = "ProductSubmission";
|
|
17209
17873
|
var SubmissionPublisherSchema = external_exports.object({
|
|
@@ -17297,13 +17961,32 @@ var PUBLISH_RULE_ANNEX = {
|
|
|
17297
17961
|
}
|
|
17298
17962
|
}
|
|
17299
17963
|
}
|
|
17964
|
+
},
|
|
17965
|
+
{
|
|
17966
|
+
// THE SAME ASYMMETRY, a third time. Presence and shape are all JSON Schema
|
|
17967
|
+
// can say: whether the region and profile are this Product's, whether the
|
|
17968
|
+
// configuration fills every required setting and whether every required
|
|
17969
|
+
// secret is named are questions about the rest of the document, answered
|
|
17970
|
+
// by preflight.
|
|
17971
|
+
$comment: "A new release must declare definition.deploymentExample: one recommended deployment, with region, profile and configuration. Preflight also checks the region and profile are this Product's, the configuration is complete against its schema, no secret carries a value, and every required secret is named in secretInputs.",
|
|
17972
|
+
properties: {
|
|
17973
|
+
definition: {
|
|
17974
|
+
required: ["deploymentExample"],
|
|
17975
|
+
properties: {
|
|
17976
|
+
deploymentExample: {
|
|
17977
|
+
required: ["title", "description", "region", "profile", "configuration"]
|
|
17978
|
+
}
|
|
17979
|
+
}
|
|
17980
|
+
}
|
|
17981
|
+
}
|
|
17300
17982
|
}
|
|
17301
17983
|
]
|
|
17302
17984
|
};
|
|
17303
17985
|
withJsonSchemaAnnex(ProductSubmissionSchema, PUBLISH_RULE_ANNEX);
|
|
17986
|
+
withJsonSchemaAnnex(ProductSubmissionV2Schema, PUBLISH_RULE_ANNEX);
|
|
17304
17987
|
|
|
17305
17988
|
// src/manifest.ts
|
|
17306
|
-
var
|
|
17989
|
+
var import_yaml2 = __toESM(require_dist(), 1);
|
|
17307
17990
|
function readManifest(path) {
|
|
17308
17991
|
let text;
|
|
17309
17992
|
try {
|
|
@@ -17313,7 +17996,7 @@ function readManifest(path) {
|
|
|
17313
17996
|
}
|
|
17314
17997
|
let parsed;
|
|
17315
17998
|
try {
|
|
17316
|
-
parsed = (0,
|
|
17999
|
+
parsed = (0, import_yaml2.parse)(text, { prettyErrors: false });
|
|
17317
18000
|
} catch (error) {
|
|
17318
18001
|
return {
|
|
17319
18002
|
ok: false,
|
|
@@ -17333,15 +18016,21 @@ function resolveSecrets(pairs, environment = process.env) {
|
|
|
17333
18016
|
if (separator <= 0) {
|
|
17334
18017
|
return {
|
|
17335
18018
|
ok: false,
|
|
17336
|
-
message: `${label} expects
|
|
18019
|
+
message: `${label} expects NAME=env:VARIABLE, and there is no name before "=".`
|
|
17337
18020
|
};
|
|
17338
18021
|
}
|
|
17339
|
-
const name = pair.slice(0, separator);
|
|
18022
|
+
const name = secretPathOf(pair.slice(0, separator));
|
|
17340
18023
|
const source = pair.slice(separator + 1);
|
|
18024
|
+
if (name === null) {
|
|
18025
|
+
return {
|
|
18026
|
+
ok: false,
|
|
18027
|
+
message: `${label} expects a setting name before "=", or a /group/setting path for a setting inside a group. A path names no list item and does not end in "/".`
|
|
18028
|
+
};
|
|
18029
|
+
}
|
|
17341
18030
|
if (!source.startsWith("env:")) {
|
|
17342
18031
|
return {
|
|
17343
18032
|
ok: false,
|
|
17344
|
-
message: `${label} must read from the environment: --secret <name>=env:VARIABLE. A value on the command line is in your shell history and in the process table.`
|
|
18033
|
+
message: `${label} must read from the environment: --secret <name-or-path>=env:VARIABLE. A value on the command line is in your shell history and in the process table.`
|
|
17345
18034
|
};
|
|
17346
18035
|
}
|
|
17347
18036
|
const variable = source.slice("env:".length);
|
|
@@ -17349,10 +18038,10 @@ function resolveSecrets(pairs, environment = process.env) {
|
|
|
17349
18038
|
return { ok: false, message: `${label} names no variable after "env:".` };
|
|
17350
18039
|
}
|
|
17351
18040
|
const value = environment[variable];
|
|
17352
|
-
if (value === void 0 || value === "") {
|
|
18041
|
+
if (value === void 0 || value.trim() === "") {
|
|
17353
18042
|
return {
|
|
17354
18043
|
ok: false,
|
|
17355
|
-
message: `${label} reads an environment variable that is not set, or
|
|
18044
|
+
message: `${label} reads an environment variable that is not set, or holds nothing but whitespace.`
|
|
17356
18045
|
};
|
|
17357
18046
|
}
|
|
17358
18047
|
secrets[name] = value;
|
|
@@ -17470,8 +18159,14 @@ async function ensureSession(args) {
|
|
|
17470
18159
|
}
|
|
17471
18160
|
};
|
|
17472
18161
|
}
|
|
18162
|
+
function resolveProjectWithSource(flag, stored) {
|
|
18163
|
+
if (flag !== void 0) return { value: flag, source: "--project" };
|
|
18164
|
+
const selected = stored.context?.projectId;
|
|
18165
|
+
if (selected !== void 0) return { value: selected, source: "the stored context" };
|
|
18166
|
+
return null;
|
|
18167
|
+
}
|
|
17473
18168
|
function resolveProject(flag, stored) {
|
|
17474
|
-
return flag
|
|
18169
|
+
return resolveProjectWithSource(flag, stored)?.value ?? null;
|
|
17475
18170
|
}
|
|
17476
18171
|
|
|
17477
18172
|
// src/operations.ts
|
|
@@ -17650,6 +18345,9 @@ function secretWriteConsequence(plan) {
|
|
|
17650
18345
|
}
|
|
17651
18346
|
return lines;
|
|
17652
18347
|
}
|
|
18348
|
+
function requiredSecretLines(names) {
|
|
18349
|
+
return names.length === 0 ? [] : [" required secrets:", ...names.map((name) => ` ${name}`)];
|
|
18350
|
+
}
|
|
17653
18351
|
function printPlan(plan, json) {
|
|
17654
18352
|
if (json) {
|
|
17655
18353
|
process.stdout.write(`${JSON.stringify(plan)}
|
|
@@ -17682,11 +18380,7 @@ function printPlan(plan, json) {
|
|
|
17682
18380
|
}
|
|
17683
18381
|
}
|
|
17684
18382
|
}
|
|
17685
|
-
|
|
17686
|
-
if (required.length > 0) {
|
|
17687
|
-
lines.push(" required secrets:");
|
|
17688
|
-
for (const name of required) lines.push(` ${name}`);
|
|
17689
|
-
}
|
|
18383
|
+
lines.push(...requiredSecretLines(plan.requiredSecrets ?? []));
|
|
17690
18384
|
if ((plan.secretsToWrite ?? []).length > 0) {
|
|
17691
18385
|
lines.push(` secrets to set: ${plan.secretsToWrite.join(", ")}`);
|
|
17692
18386
|
for (const line of secretWriteConsequence(plan)) lines.push(` ${line}`);
|
|
@@ -17770,10 +18464,15 @@ async function runApply(verb, parsed, deps = {}) {
|
|
|
17770
18464
|
body: { verb: "validate", payload: manifest2.document }
|
|
17771
18465
|
});
|
|
17772
18466
|
if (!result.ok) return printError(result.error, json);
|
|
18467
|
+
if (json) {
|
|
18468
|
+
process.stdout.write(`${JSON.stringify(result.data)}
|
|
18469
|
+
`);
|
|
18470
|
+
return 0;
|
|
18471
|
+
}
|
|
18472
|
+
const required = result.data["requiredSecrets"];
|
|
18473
|
+
const names = Array.isArray(required) ? required.filter((name) => typeof name === "string") : [];
|
|
17773
18474
|
process.stdout.write(
|
|
17774
|
-
|
|
17775
|
-
` : `${file} is a valid manifest.
|
|
17776
|
-
`
|
|
18475
|
+
[`${file} is a valid manifest.`, ...requiredSecretLines(names)].join("\n") + "\n"
|
|
17777
18476
|
);
|
|
17778
18477
|
return 0;
|
|
17779
18478
|
}
|
|
@@ -18117,13 +18816,18 @@ async function runDoctor(parsed) {
|
|
|
18117
18816
|
detail: `${endpoint} (from ${resolved.source})`,
|
|
18118
18817
|
facts: { endpoint, source: resolved.source }
|
|
18119
18818
|
});
|
|
18120
|
-
const
|
|
18819
|
+
const project = resolveProjectWithSource(parsed.flags["project"], stored);
|
|
18820
|
+
const storedProjectId = stored.context?.projectId ?? null;
|
|
18121
18821
|
checks.push({
|
|
18122
18822
|
name: "Project",
|
|
18123
18823
|
ok: true,
|
|
18124
|
-
note:
|
|
18125
|
-
detail:
|
|
18126
|
-
facts: {
|
|
18824
|
+
note: storedProjectId === null,
|
|
18825
|
+
detail: project === null ? "none selected. Commands need `--project prj_\u2026`, or run `vfac context set --project prj_\u2026` once. This is not a fault \u2014 it is what a pipeline that names the Project per command looks like \u2014 but it is NOT the same thing as which Projects your credential reaches." : project.source === "--project" ? `${project.value} (from --project, for this command only). ` + (storedProjectId === null ? "Nothing is selected in this context, so every other command needs the flag too. " : `The stored context still selects ${storedProjectId}, which is what a command with no --project uses. `) + "Which Projects the credential reaches is a separate question." : `${project.value} (from the stored context; a command with no --project uses it, and which Projects the credential reaches is a separate question)`,
|
|
18826
|
+
facts: {
|
|
18827
|
+
selectedProjectId: project?.value ?? null,
|
|
18828
|
+
source: project?.source ?? null,
|
|
18829
|
+
storedProjectId
|
|
18830
|
+
}
|
|
18127
18831
|
});
|
|
18128
18832
|
const probe = await probeMachineApi({ endpoint });
|
|
18129
18833
|
if (!probe.ok) {
|
|
@@ -18158,13 +18862,13 @@ async function runDoctor(parsed) {
|
|
|
18158
18862
|
return report2(checks, json);
|
|
18159
18863
|
}
|
|
18160
18864
|
const access = who.data.access;
|
|
18161
|
-
const projects = access.projects.map((
|
|
18865
|
+
const projects = access.projects.map((project2) => project2.projectId);
|
|
18162
18866
|
const mutating = access.projects.filter(
|
|
18163
|
-
(
|
|
18867
|
+
(project2) => project2.permissions.some((permission) => !permission.endsWith(".read"))
|
|
18164
18868
|
);
|
|
18165
18869
|
const organizationWide = access.tenantPermissions.includes("project.read");
|
|
18166
18870
|
const alsoOrganizationWide = access.tenantPermissions.length > 0 ? `Organization-wide it holds ${access.tenantPermissions.join(", ")}, which reaches no Project. ` : "";
|
|
18167
|
-
const perProject = projects.length === 0 ? "" : `. Granted per Project: ${projects.join(", ")}` + (mutating.length > 0 ? ` \u2014 you can deploy in ${mutating.map((
|
|
18871
|
+
const perProject = projects.length === 0 ? "" : `. Granted per Project: ${projects.join(", ")}` + (mutating.length > 0 ? ` \u2014 you can deploy in ${mutating.map((project2) => project2.projectId).join(", ")}` : " \u2014 read-only there");
|
|
18168
18872
|
checks.push(
|
|
18169
18873
|
organizationWide ? {
|
|
18170
18874
|
name: "Access",
|
|
@@ -18323,7 +19027,6 @@ function report2(checks, json) {
|
|
|
18323
19027
|
}
|
|
18324
19028
|
|
|
18325
19029
|
// src/commands/export.ts
|
|
18326
|
-
var import_yaml2 = __toESM(require_dist(), 1);
|
|
18327
19030
|
async function runExport(parsed) {
|
|
18328
19031
|
if (parsed.command[1] !== "product-instance" && parsed.command[1] !== "resource") {
|
|
18329
19032
|
process.stderr.write("Usage: vfac export product-instance pri_\u2026\n");
|
|
@@ -18351,7 +19054,7 @@ async function runExport(parsed) {
|
|
|
18351
19054
|
if (!result.ok) return printError(result.error, parsed.flags["output"] === "json");
|
|
18352
19055
|
process.stdout.write(
|
|
18353
19056
|
parsed.flags["output"] === "json" ? `${JSON.stringify(result.data, null, 2)}
|
|
18354
|
-
` : (
|
|
19057
|
+
` : manifestYaml(result.data)
|
|
18355
19058
|
);
|
|
18356
19059
|
const metadata = result.data["metadata"];
|
|
18357
19060
|
const hasKey = typeof metadata === "object" && metadata !== null && "key" in metadata;
|
|
@@ -18763,14 +19466,57 @@ function renderProduct(product) {
|
|
|
18763
19466
|
(profile) => `${profile.id}${profile.default ? " (default)" : ""}`
|
|
18764
19467
|
);
|
|
18765
19468
|
lines.push(` profile ${profiles.join(", ") || "(none declared)"}`);
|
|
18766
|
-
const
|
|
18767
|
-
if (
|
|
18768
|
-
lines.push("",
|
|
18769
|
-
for (const
|
|
18770
|
-
lines.push(
|
|
19469
|
+
const settings = configurationRequirements(product.configuration);
|
|
19470
|
+
if (settings.length > 0) {
|
|
19471
|
+
lines.push("", ` Settings (${settings.length})`);
|
|
19472
|
+
for (const setting of settings) {
|
|
19473
|
+
lines.push(
|
|
19474
|
+
` ${identityOf(setting)} ${setting.secret ? "secret" : setting.type} ${TOKEN[setting.requiredness]}`
|
|
19475
|
+
);
|
|
19476
|
+
const requirement = requirementOf(setting);
|
|
19477
|
+
if (requirement !== null) lines.push(...continuation(requirement));
|
|
19478
|
+
if (setting.requires.length > 0 && !mutualClause(setting)) {
|
|
19479
|
+
lines.push(
|
|
19480
|
+
...continuation(
|
|
19481
|
+
`answering it also requires ${listOf(
|
|
19482
|
+
setting.requires.map((member) => identityOf(member)),
|
|
19483
|
+
"and"
|
|
19484
|
+
)}`
|
|
19485
|
+
)
|
|
19486
|
+
);
|
|
19487
|
+
}
|
|
19488
|
+
if (setting.condition?.carrier === "container") {
|
|
19489
|
+
lines.push(
|
|
19490
|
+
...continuation(
|
|
19491
|
+
`${printableConfigurationPath(setting.condition.carrierPath)} is required when ${setting.condition.printableSetting} is ${valueList(setting.condition.anyOf)}`
|
|
19492
|
+
)
|
|
19493
|
+
);
|
|
19494
|
+
}
|
|
19495
|
+
if (setting.secret) {
|
|
19496
|
+
lines.push(...continuation(`--secret ${String(setting.path)}=env:VAR`));
|
|
19497
|
+
}
|
|
19498
|
+
if (setting.allowed.length > 0) {
|
|
19499
|
+
lines.push(...continuation(`one of ${valueList(setting.allowed)}`));
|
|
19500
|
+
}
|
|
19501
|
+
if (setting.defaultValue !== void 0) {
|
|
19502
|
+
lines.push(...continuation(`default ${JSON.stringify(setting.defaultValue)}`));
|
|
19503
|
+
}
|
|
18771
19504
|
}
|
|
18772
|
-
|
|
18773
|
-
|
|
19505
|
+
}
|
|
19506
|
+
const seed = seedConfiguration(product.configuration);
|
|
19507
|
+
const together = seed.groups.filter((group) => group.within !== null);
|
|
19508
|
+
if (together.length > 0) {
|
|
19509
|
+
lines.push("", " Settings that go together");
|
|
19510
|
+
for (const group of together) {
|
|
19511
|
+
const index = group.settings.indexOf(group.trigger);
|
|
19512
|
+
const trigger = index === -1 ? `${group.trigger} (secret)` : group.labels[index] ?? group.trigger;
|
|
19513
|
+
const needs = [
|
|
19514
|
+
...group.labels.filter((_, at) => at !== index),
|
|
19515
|
+
...group.secrets.filter((path) => path !== group.trigger).map((path) => `--secret ${path}=env:VAR`)
|
|
19516
|
+
];
|
|
19517
|
+
lines.push(
|
|
19518
|
+
` ${trigger} also needs ${needs.join(" + ")} (if ${group.within} is configured)`
|
|
19519
|
+
);
|
|
18774
19520
|
}
|
|
18775
19521
|
}
|
|
18776
19522
|
const dependencies = product.dependencies ?? [];
|
|
@@ -18807,9 +19553,66 @@ function renderProduct(product) {
|
|
|
18807
19553
|
lines.push(` ${condition.key ?? ""} ${condition.displayName ?? ""}`.trimEnd());
|
|
18808
19554
|
}
|
|
18809
19555
|
}
|
|
18810
|
-
lines.push(
|
|
19556
|
+
lines.push(
|
|
19557
|
+
"",
|
|
19558
|
+
" `--output json` carries the whole declaration. Every setting is listed above;",
|
|
19559
|
+
" descriptions and value constraints are in the JSON form."
|
|
19560
|
+
);
|
|
19561
|
+
if (settings.some((setting) => setting.secret)) {
|
|
19562
|
+
lines.push(" A credential is supplied per apply and never written into a manifest.");
|
|
19563
|
+
}
|
|
18811
19564
|
return lines.join("\n");
|
|
18812
19565
|
}
|
|
19566
|
+
var TOKEN = {
|
|
19567
|
+
always: "required",
|
|
19568
|
+
"when-value": "required-when",
|
|
19569
|
+
"when-group": "required-if",
|
|
19570
|
+
"with-siblings": "required-with",
|
|
19571
|
+
optional: "optional"
|
|
19572
|
+
};
|
|
19573
|
+
var MAX_LISTED_VALUES = 8;
|
|
19574
|
+
var identityOf = (setting) => setting.secret ? String(setting.path) : setting.printablePath;
|
|
19575
|
+
function valueList(values) {
|
|
19576
|
+
const shown = values.slice(0, MAX_LISTED_VALUES).map((value) => JSON.stringify(value));
|
|
19577
|
+
const rest = values.length - shown.length;
|
|
19578
|
+
const joined = shown.length === 2 ? shown.join(" or ") : shown.join(", ");
|
|
19579
|
+
return rest > 0 ? `${joined} and ${rest} more` : joined;
|
|
19580
|
+
}
|
|
19581
|
+
function requirementOf(setting) {
|
|
19582
|
+
const list = setting.list === null ? "" : ` ${setting.list.printablePath}`;
|
|
19583
|
+
switch (setting.requiredness) {
|
|
19584
|
+
case "always":
|
|
19585
|
+
return setting.perRow ? `in every${list} entry` : null;
|
|
19586
|
+
case "when-value": {
|
|
19587
|
+
const condition = setting.condition;
|
|
19588
|
+
if (condition === null) return null;
|
|
19589
|
+
const rows = condition.anyRow ? " in any entry" : "";
|
|
19590
|
+
return `when ${condition.printableSetting} is ${valueList(condition.anyOf)}${rows}`;
|
|
19591
|
+
}
|
|
19592
|
+
case "when-group": {
|
|
19593
|
+
const owner = setting.containers[setting.containers.length - 1];
|
|
19594
|
+
const base = `once ${owner?.printablePath ?? ""} is configured`;
|
|
19595
|
+
return setting.perRow ? `${base}, in every${list} entry` : base;
|
|
19596
|
+
}
|
|
19597
|
+
case "with-siblings": {
|
|
19598
|
+
const names = (members) => members.map((member) => identityOf(member));
|
|
19599
|
+
const triggers = names(setting.requiredBy);
|
|
19600
|
+
return mutualClause(setting) ? `with ${names(setting.requires).join(", ")}` : `once ${listOf(triggers, "or")} is set`;
|
|
19601
|
+
}
|
|
19602
|
+
case "optional":
|
|
19603
|
+
return setting.perRow ? `one per${list} entry` : null;
|
|
19604
|
+
}
|
|
19605
|
+
}
|
|
19606
|
+
function mutualClause(setting) {
|
|
19607
|
+
const requires = setting.requires.map((member) => String(member.path)).sort();
|
|
19608
|
+
const requiredBy = setting.requiredBy.map((member) => String(member.path)).sort();
|
|
19609
|
+
return requires.length > 0 && requires.join("\0") === requiredBy.join("\0");
|
|
19610
|
+
}
|
|
19611
|
+
function listOf(items, conjunction) {
|
|
19612
|
+
if (items.length <= 1) return items[0] ?? "";
|
|
19613
|
+
return `${items.slice(0, -1).join(", ")} ${conjunction} ${items[items.length - 1]}`;
|
|
19614
|
+
}
|
|
19615
|
+
var continuation = (sentence) => wrap(sentence, 72).map((line) => ` ${line}`);
|
|
18813
19616
|
function wrap(text, width) {
|
|
18814
19617
|
const lines = [];
|
|
18815
19618
|
for (const paragraph of text.split(/\n\s*\n/)) {
|
|
@@ -18992,7 +19795,6 @@ Only approve a code you are looking at right now. Waiting\u2026
|
|
|
18992
19795
|
}
|
|
18993
19796
|
|
|
18994
19797
|
// src/commands/manifest-init.ts
|
|
18995
|
-
var import_yaml3 = __toESM(require_dist(), 1);
|
|
18996
19798
|
async function runManifestInit(parsed) {
|
|
18997
19799
|
const verb = parsed.command[1];
|
|
18998
19800
|
if (verb !== "init") {
|
|
@@ -19074,7 +19876,7 @@ async function runManifestInit(parsed) {
|
|
|
19074
19876
|
);
|
|
19075
19877
|
}
|
|
19076
19878
|
for (const group of seed.groups) {
|
|
19077
|
-
const settings = group.
|
|
19879
|
+
const settings = group.labels.map((label) => `spec.configuration.${label}`);
|
|
19078
19880
|
const secrets = group.secrets.map((name2) => `--secret ${name2}=env:VAR`);
|
|
19079
19881
|
notes.push(
|
|
19080
19882
|
`${[...settings, ...secrets].join(" + ")} \u2014 all of these together, or none of them`
|
|
@@ -19097,7 +19899,7 @@ ${where}`);
|
|
|
19097
19899
|
}
|
|
19098
19900
|
process.stdout.write(
|
|
19099
19901
|
parsed.flags["output"] === "json" ? `${JSON.stringify(document, null, 2)}
|
|
19100
|
-
` : (
|
|
19902
|
+
` : manifestYaml(document)
|
|
19101
19903
|
);
|
|
19102
19904
|
if (notes.length > 0) {
|
|
19103
19905
|
process.stderr.write("\nStill yours to fill in:\n");
|
|
@@ -19290,7 +20092,9 @@ Options
|
|
|
19290
20092
|
--endpoint your organization's portal. Defaults to VFAC_ENDPOINT, then the
|
|
19291
20093
|
stored context
|
|
19292
20094
|
--secret plan and apply: NAME=env:VARIABLE \u2014 the value is read from the
|
|
19293
|
-
environment.
|
|
20095
|
+
environment. NAME is the setting's name, or its path when it
|
|
20096
|
+
sits inside a group: --secret /upstreamAuth/clientSecret=env:VAR.
|
|
20097
|
+
Plan a run that sets a secret with the same
|
|
19294
20098
|
--secret inputs the apply will carry; without them the plan
|
|
19295
20099
|
answers about a different request. A value on the command line
|
|
19296
20100
|
is in your shell history and in the process table, so it is
|
|
@@ -19309,12 +20113,16 @@ Options
|
|
|
19309
20113
|
--output json, for a pipeline that reads the answer
|
|
19310
20114
|
|
|
19311
20115
|
Diagnosing
|
|
19312
|
-
vfac doctor checks the
|
|
19313
|
-
|
|
19314
|
-
|
|
19315
|
-
|
|
19316
|
-
|
|
19317
|
-
|
|
20116
|
+
vfac doctor checks the config it could read, the endpoint, which Project this
|
|
20117
|
+
terminal has selected and which one the command will act in, the
|
|
20118
|
+
machine API, whether this client is new enough for that platform,
|
|
20119
|
+
how this terminal is signed in and what that credential can reach
|
|
20120
|
+
\u2014 in that order, so the first thing that is actually wrong is the
|
|
20121
|
+
thing it reports. Pass --project to ask about the Project a
|
|
20122
|
+
command would name, which changes nothing on disk. It ends by
|
|
20123
|
+
asking npm whether a newer vfac has been published, which is a
|
|
20124
|
+
note and never a failure. Set VFAC_NO_UPDATE_CHECK to skip that
|
|
20125
|
+
one request
|
|
19318
20126
|
vfac whoami what this credential is, and which Project scopes it has been
|
|
19319
20127
|
GRANTED. A credential scoped to one Project deploys fine and
|
|
19320
20128
|
cannot make organization-wide reads \u2014 this is where that is said.
|