@varde-flyt/vfac 0.18.1 → 0.18.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 +6 -4
- package/dist/vfac.mjs +132 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -170,7 +170,7 @@ shows one as `[secret]`. `vfac guide` is authoritative.
|
|
|
170
170
|
## Settings that go together
|
|
171
171
|
|
|
172
172
|
A Product may declare that a group of settings is all-or-nothing: leave every one
|
|
173
|
-
of them
|
|
173
|
+
of them out, or supply all of them. A provider connection is the usual shape —
|
|
174
174
|
an address without its credential is worse than neither, because the service
|
|
175
175
|
starts and then fails on its first call.
|
|
176
176
|
|
|
@@ -192,9 +192,11 @@ that fails `vfac validate`.
|
|
|
192
192
|
|
|
193
193
|
**`validate` and `plan` answer different questions here, and the difference is
|
|
194
194
|
not a bug.** A manifest may never carry a secret, so `vfac validate` — which
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
195
|
+
judges the file with no secrets beside it — cannot tell whether the credential
|
|
196
|
+
in a group is configured, and does not refuse a document over it. The one
|
|
197
|
+
exception is in the file: a credential the file answers with
|
|
198
|
+
`valueFrom.resourceOutput` IS configured, so `validate` judges its group and
|
|
199
|
+
names a missing partner. `vfac plan` knows what the apply would carry and what the resource already holds,
|
|
198
200
|
so it is where a missing member is reported. A credential the resource already
|
|
199
201
|
holds does not have to be sent again to change one of its siblings.
|
|
200
202
|
|
package/dist/vfac.mjs
CHANGED
|
@@ -13435,7 +13435,8 @@ function validateSubmittedConfiguration(schema, submitted, presence = {}) {
|
|
|
13435
13435
|
referenced: new Set(presence.referenced ?? []),
|
|
13436
13436
|
present: new Set(presence.present ?? []),
|
|
13437
13437
|
unknown: new Set(presence.unknown ?? []),
|
|
13438
|
-
reported: /* @__PURE__ */ new Set()
|
|
13438
|
+
reported: /* @__PURE__ */ new Set(),
|
|
13439
|
+
sent: /* @__PURE__ */ new Set()
|
|
13439
13440
|
};
|
|
13440
13441
|
const supplied = /* @__PURE__ */ new Set();
|
|
13441
13442
|
if (submitted !== void 0 && submitted !== null && !isPlainObject2(submitted)) {
|
|
@@ -13448,7 +13449,7 @@ function validateSubmittedConfiguration(schema, submitted, presence = {}) {
|
|
|
13448
13449
|
const input = isPlainObject2(submitted) ? submitted : {};
|
|
13449
13450
|
const secrets = {};
|
|
13450
13451
|
const configuration = validateObject({ properties: schema.properties, required: schema.required }, input, null, walk, { secrets, supplied });
|
|
13451
|
-
drain(walk, crossFieldIssues(schema, supplied, presence, configuration, walk.reported));
|
|
13452
|
+
drain(walk, crossFieldIssues(schema, supplied, walk.sent, presence, configuration, walk.reported));
|
|
13452
13453
|
drain(walk, rowAndReferenceIssues(schema, presence, configuration));
|
|
13453
13454
|
if (walk.suppressed > 0) {
|
|
13454
13455
|
walk.issues.push({
|
|
@@ -13515,6 +13516,8 @@ function validateObject(declaration, input, container, walk, sink) {
|
|
|
13515
13516
|
if (children !== null) {
|
|
13516
13517
|
const value = Object.hasOwn(input, name) ? input[name] : void 0;
|
|
13517
13518
|
const filled = validateContainer(name, property, children, value, field, walk, sink);
|
|
13519
|
+
if (field !== "" && containerSent(children, value, field, walk))
|
|
13520
|
+
walk.sent.add(field);
|
|
13518
13521
|
if (value === void 0)
|
|
13519
13522
|
absentGroupRuleIssues(children, field, walk, sink, required.has(name));
|
|
13520
13523
|
if (filled !== void 0)
|
|
@@ -13541,6 +13544,8 @@ function validateObject(declaration, input, container, walk, sink) {
|
|
|
13541
13544
|
}
|
|
13542
13545
|
if (present && sink !== null && field !== "")
|
|
13543
13546
|
sink.supplied.add(field);
|
|
13547
|
+
if (present && field !== "")
|
|
13548
|
+
walk.sent.add(field);
|
|
13544
13549
|
if (!present) {
|
|
13545
13550
|
const fallback = secret ? void 0 : defaultOf(leaf);
|
|
13546
13551
|
if (fallback !== void 0) {
|
|
@@ -13576,13 +13581,22 @@ function groupRuleIssues(declaration, suppliedHere, container, walk, sink) {
|
|
|
13576
13581
|
const fieldFor = (name) => (container === null ? configurationPath(name) : configurationPath(...pathSegments(container), name)) ?? "";
|
|
13577
13582
|
const answered = (name) => {
|
|
13578
13583
|
const path = fieldFor(name);
|
|
13579
|
-
return suppliedHere.has(name) || //
|
|
13584
|
+
return suppliedHere.has(name) || // SENT IS ANSWERED, VALID OR NOT. A member whose value failed its own
|
|
13585
|
+
// check was never stored, so `suppliedHere` alone reported it missing as
|
|
13586
|
+
// well — two refusals for one mistake, and the second one false.
|
|
13587
|
+
walk.sent.has(path) || // A MEMBER, NOT A CONTAINER: a referenced leaf is recorded at this very
|
|
13580
13588
|
// path, and a member group is answered by a reference inside it. Asking
|
|
13581
13589
|
// only "inside" never counted a referenced leaf, so a referenced trigger
|
|
13582
13590
|
// turned nothing on and a referenced dependent was reported missing.
|
|
13583
|
-
referencedAtOrInside(walk, path) || walk.present.has(path) ||
|
|
13591
|
+
referencedAtOrInside(walk, path) || walk.present.has(path) || // A GROUP MEMBER IS ANSWERED BY WHAT IS CONFIGURED INSIDE IT, as a
|
|
13592
|
+
// reference inside it already answers it: a held credential is the group,
|
|
13593
|
+
// on every apply that does not resend it.
|
|
13594
|
+
containsAny(walk.present, path) || sink !== null && Object.hasOwn(sink.secrets, path);
|
|
13595
|
+
};
|
|
13596
|
+
const unseen = (name) => {
|
|
13597
|
+
const path = fieldFor(name);
|
|
13598
|
+
return walk.unknown.has(path) && !referencedAtOrInside(walk, path);
|
|
13584
13599
|
};
|
|
13585
|
-
const unseen = (name) => walk.unknown.has(fieldFor(name));
|
|
13586
13600
|
const reported = /* @__PURE__ */ new Set();
|
|
13587
13601
|
for (const [trigger, dependents] of Object.entries(declaration.dependentRequired)) {
|
|
13588
13602
|
if (unseen(trigger) || !answered(trigger))
|
|
@@ -13678,6 +13692,13 @@ function containerDeclaration(property) {
|
|
|
13678
13692
|
}
|
|
13679
13693
|
return null;
|
|
13680
13694
|
}
|
|
13695
|
+
function containerSent(children, value, field, walk) {
|
|
13696
|
+
if (value === void 0)
|
|
13697
|
+
return false;
|
|
13698
|
+
if (children.kind !== "object" || !isPlainObject2(value))
|
|
13699
|
+
return true;
|
|
13700
|
+
return containsAny(walk.sent, field);
|
|
13701
|
+
}
|
|
13681
13702
|
function validateContainer(name, property, children, value, field, walk, sink) {
|
|
13682
13703
|
const path = parseConfigurationPath(field);
|
|
13683
13704
|
if (children.kind === "object") {
|
|
@@ -13726,22 +13747,24 @@ function validateContainer(name, property, children, value, field, walk, sink) {
|
|
|
13726
13747
|
}
|
|
13727
13748
|
return out;
|
|
13728
13749
|
}
|
|
13729
|
-
function crossFieldIssues(schema, supplied, presence, configuration, reportedByWalk) {
|
|
13750
|
+
function crossFieldIssues(schema, supplied, sent, presence, configuration, reportedByWalk) {
|
|
13730
13751
|
const clauses = Object.entries(schema.dependentRequired ?? {});
|
|
13731
13752
|
const carriers = conditionCarriers(schema);
|
|
13732
13753
|
if (clauses.length === 0 && carriers.length === 0)
|
|
13733
13754
|
return [];
|
|
13734
13755
|
const alsoPresent = /* @__PURE__ */ new Set([...presence.present ?? [], ...presence.referenced ?? []]);
|
|
13735
13756
|
const unknown = new Set(presence.unknown ?? []);
|
|
13736
|
-
const
|
|
13757
|
+
const referenced = new Set(presence.referenced ?? []);
|
|
13758
|
+
const known = (name) => !unknown.has(name) || referenced.has(name);
|
|
13737
13759
|
const held = (name) => supplied.has(name) || alsoPresent.has(name);
|
|
13760
|
+
const answered = (name) => held(name) || sent.has(name) || containsAny(alsoPresent, name);
|
|
13738
13761
|
const issues = [];
|
|
13739
13762
|
const reported = /* @__PURE__ */ new Set();
|
|
13740
13763
|
for (const [trigger, dependents] of clauses) {
|
|
13741
|
-
if (!known(trigger) || !
|
|
13764
|
+
if (!known(trigger) || !answered(trigger))
|
|
13742
13765
|
continue;
|
|
13743
13766
|
for (const dependent of dependents) {
|
|
13744
|
-
if (!known(dependent) ||
|
|
13767
|
+
if (!known(dependent) || answered(dependent))
|
|
13745
13768
|
continue;
|
|
13746
13769
|
if (reported.has(dependent))
|
|
13747
13770
|
continue;
|
|
@@ -14401,6 +14424,7 @@ var ANALYSIS_PREDICATE_OPERATORS = Object.freeze([
|
|
|
14401
14424
|
...ANALYSIS_NUMERIC_OPERATORS
|
|
14402
14425
|
]);
|
|
14403
14426
|
var ANALYSIS_COUNTING_MEASURES = Object.freeze(["count", "rate"]);
|
|
14427
|
+
var ANALYSIS_DISTINCT_MEASURES = Object.freeze(["distinctCount"]);
|
|
14404
14428
|
var ANALYSIS_FIELD_MEASURES = Object.freeze([
|
|
14405
14429
|
"sum",
|
|
14406
14430
|
"avg",
|
|
@@ -14410,6 +14434,7 @@ var ANALYSIS_FIELD_MEASURES = Object.freeze([
|
|
|
14410
14434
|
]);
|
|
14411
14435
|
var ANALYSIS_MEASURES = Object.freeze([
|
|
14412
14436
|
...ANALYSIS_COUNTING_MEASURES,
|
|
14437
|
+
...ANALYSIS_DISTINCT_MEASURES,
|
|
14413
14438
|
...ANALYSIS_FIELD_MEASURES
|
|
14414
14439
|
]);
|
|
14415
14440
|
var ANALYSIS_PERCENTILES = Object.freeze(["p50", "p90", "p95", "p99"]);
|
|
@@ -14485,7 +14510,7 @@ var LOG_ACCESS_REFUSALS = Object.freeze({
|
|
|
14485
14510
|
},
|
|
14486
14511
|
invalid_analysis: {
|
|
14487
14512
|
status: 400,
|
|
14488
|
-
meaning: `the analysis is not one this service answers: a member it does not define, at any level, or one missing that it needs; a window over ${MAX_ANALYSIS_WINDOW_SECONDS} seconds; an empty \`where\` or an empty list in it; more than ${MAX_ANALYSIS_PREDICATES} predicates counted everywhere, a group inside a group, an empty group, or an \`in\`/\`notIn\` list that is not 1\u2026${MAX_ANALYSIS_PREDICATE_VALUES} distinct values; a field that is not an identifier; a value that is empty, longer than ${MAX_FIELD_VALUE_LENGTH} characters, not well-formed Unicode or carrying a control character, or values together over ${MAX_ANALYSIS_VALUE_BYTES} bytes; a number that is not finite; a measure or a numeric predicate on ${listed(RESERVED_RECORD_FIELDS)}; more than ${MAX_ANALYSIS_GROUP_BY_FIELDS} or repeated \`groupBy\` fields, or one naming ${listed(UNGROUPABLE_RECORD_FIELDS)}; a \`limit\` missing with \`groupBy\`, present without it or outside 1\u2026${MAX_ANALYSIS_GROUPS}; an \`order\` without \`groupBy\`; a \`bucketSeconds\` under ${MIN_ANALYSIS_BUCKET_SECONDS}, a \`from\` or \`to\` that is not a whole multiple of it since 1970-01-01T00:00:00Z, or more than ${MAX_ANALYSIS_BUCKETS} buckets; or buckets times \`limit\` over ${MAX_ANALYSIS_CELLS}`
|
|
14513
|
+
meaning: `the analysis is not one this service answers: a member it does not define, at any level, or one missing that it needs; a window over ${MAX_ANALYSIS_WINDOW_SECONDS} seconds; an empty \`where\` or an empty list in it; more than ${MAX_ANALYSIS_PREDICATES} predicates counted everywhere, a group inside a group, an empty group, or an \`in\`/\`notIn\` list that is not 1\u2026${MAX_ANALYSIS_PREDICATE_VALUES} distinct values; a field that is not an identifier; a value that is empty, longer than ${MAX_FIELD_VALUE_LENGTH} characters, not well-formed Unicode or carrying a control character, or values together over ${MAX_ANALYSIS_VALUE_BYTES} bytes; a number that is not finite; a numeric measure or a numeric predicate on ${listed(RESERVED_RECORD_FIELDS)}; a \`distinctCount\` field naming ${listed(UNGROUPABLE_RECORD_FIELDS)}; more than ${MAX_ANALYSIS_GROUP_BY_FIELDS} or repeated \`groupBy\` fields, or one naming ${listed(UNGROUPABLE_RECORD_FIELDS)}; a \`limit\` missing with \`groupBy\`, present without it or outside 1\u2026${MAX_ANALYSIS_GROUPS}; an \`order\` without \`groupBy\`; a \`bucketSeconds\` under ${MIN_ANALYSIS_BUCKET_SECONDS}, a \`from\` or \`to\` that is not a whole multiple of it since 1970-01-01T00:00:00Z, or more than ${MAX_ANALYSIS_BUCKETS} buckets; or buckets times \`limit\` over ${MAX_ANALYSIS_CELLS}`
|
|
14489
14514
|
},
|
|
14490
14515
|
analysis_timed_out: {
|
|
14491
14516
|
status: 504,
|
|
@@ -14493,7 +14518,7 @@ var LOG_ACCESS_REFUSALS = Object.freeze({
|
|
|
14493
14518
|
},
|
|
14494
14519
|
analysis_too_broad: {
|
|
14495
14520
|
status: 400,
|
|
14496
|
-
meaning: "ranking the groups
|
|
14521
|
+
meaning: "ranking the groups, or counting the distinct values of a field, met more distinct values than one answer can hold. Not transient \u2014 narrow the window or the selection, or group by / count a field with fewer values"
|
|
14497
14522
|
},
|
|
14498
14523
|
ingest_suspended: {
|
|
14499
14524
|
status: 403,
|
|
@@ -14649,6 +14674,19 @@ var ANALYZE_REQUEST_EXAMPLES = Object.freeze([
|
|
|
14649
14674
|
measure: { op: "rate" },
|
|
14650
14675
|
bucketSeconds: 3600
|
|
14651
14676
|
}
|
|
14677
|
+
},
|
|
14678
|
+
{
|
|
14679
|
+
id: "distinct-requests-per-route",
|
|
14680
|
+
asks: "How many distinct requests reached each route in an hour, among the records that were errors or worse",
|
|
14681
|
+
request: {
|
|
14682
|
+
op: "analyze",
|
|
14683
|
+
from: "2026-09-21T12:00:00Z",
|
|
14684
|
+
to: "2026-09-21T13:00:00Z",
|
|
14685
|
+
where: { all: [{ field: "severity", op: "in", values: ["error", "fatal"] }] },
|
|
14686
|
+
measure: { op: "distinctCount", field: "request_id" },
|
|
14687
|
+
groupBy: ["route"],
|
|
14688
|
+
limit: 10
|
|
14689
|
+
}
|
|
14652
14690
|
}
|
|
14653
14691
|
]);
|
|
14654
14692
|
var ANALYZE_RESPONSE_EXAMPLES = Object.freeze({
|
|
@@ -15720,9 +15758,11 @@ var PolicyReferencesSchema = external_exports.object({
|
|
|
15720
15758
|
securityPolicyRef: external_exports.string().regex(/^(?:https:\/\/\S+|[A-Za-z0-9._\-/]+\.md(?:#[A-Za-z0-9-]+)?)$/, "Policy references are an https URL or a repository-relative Markdown path."),
|
|
15721
15759
|
dataPolicyRef: external_exports.string().regex(/^(?:https:\/\/\S+|[A-Za-z0-9._\-/]+\.md(?:#[A-Za-z0-9-]+)?)$/, "Policy references are an https URL or a repository-relative Markdown path."),
|
|
15722
15760
|
/**
|
|
15723
|
-
* Where tenant data
|
|
15724
|
-
*
|
|
15725
|
-
*
|
|
15761
|
+
* Where the Product processes and stores tenant data. `tenant-region`
|
|
15762
|
+
* means the Product keeps and processes it in the region the customer
|
|
15763
|
+
* selected — it says nothing about where a client the customer authorizes
|
|
15764
|
+
* runs. The other values are a data-residency decision the customer must
|
|
15765
|
+
* see before deploying.
|
|
15726
15766
|
*/
|
|
15727
15767
|
dataResidency: external_exports.enum(["tenant-region", "eu", "global"]),
|
|
15728
15768
|
/** Categories of tenant data the Product handles at all. */
|
|
@@ -15759,10 +15799,19 @@ var PolicyReferencesSchema = external_exports.object({
|
|
|
15759
15799
|
*/
|
|
15760
15800
|
defaultRetentionDays: external_exports.number().int().min(0).max(3650),
|
|
15761
15801
|
/**
|
|
15762
|
-
* Whether
|
|
15763
|
-
*
|
|
15764
|
-
*
|
|
15765
|
-
*
|
|
15802
|
+
* Whether the Product ITSELF sends tenant data out of the tenant boundary
|
|
15803
|
+
* — a call it makes, whoever chose where it goes (an external model
|
|
15804
|
+
* provider, a SaaS tool, an endpoint the customer configures). The Blueprint's egress rules are the authoritative detail and
|
|
15805
|
+
* the only input: the pair check requires this to be true exactly when an
|
|
15806
|
+
* egress rule carries a category other than `operational-metadata`. This
|
|
15807
|
+
* is the one-bit fact the review screen needs.
|
|
15808
|
+
*
|
|
15809
|
+
* What the Product returns over its OWN ingress to a caller the customer
|
|
15810
|
+
* authorized — a browser, an API client, an MCP client — is delivery, not
|
|
15811
|
+
* external processing, wherever that caller runs. No ingress kind,
|
|
15812
|
+
* authentication method, component kind or public endpoint moves this bit,
|
|
15813
|
+
* and neither does a service binding or a data store: neither is egress
|
|
15814
|
+
* (docs/SECURITY.md §6 names the one delivered address that is).
|
|
15766
15815
|
*/
|
|
15767
15816
|
externalProcessing: external_exports.boolean()
|
|
15768
15817
|
}).strict();
|
|
@@ -16240,7 +16289,7 @@ function checkProductDefinition(definition, ctx) {
|
|
|
16240
16289
|
ctx.addIssue({
|
|
16241
16290
|
code: external_exports.ZodIssueCode.custom,
|
|
16242
16291
|
path: ["policies"],
|
|
16243
|
-
message: 'dataResidency "global" contradicts externalProcessing false \u2014
|
|
16292
|
+
message: 'dataResidency "global" contradicts externalProcessing false \u2014 a Product that sends no tenant data out of the tenant boundary cannot process it globally.'
|
|
16244
16293
|
});
|
|
16245
16294
|
}
|
|
16246
16295
|
}
|
|
@@ -16919,6 +16968,17 @@ var InstanceEnvBindingSchema = external_exports.object({
|
|
|
16919
16968
|
"profile",
|
|
16920
16969
|
"region",
|
|
16921
16970
|
"environment",
|
|
16971
|
+
/**
|
|
16972
|
+
* The Product Instance's customer-facing name — `metadata.name` in a
|
|
16973
|
+
* manifest, `ProductInstance.displayName` in the platform — exactly as
|
|
16974
|
+
* stored.
|
|
16975
|
+
*
|
|
16976
|
+
* MUTABLE DISPLAY DATA, NEVER IDENTITY. A rename changes it and redeploys
|
|
16977
|
+
* the containers that bind it; nothing may name, key, scope, sign or
|
|
16978
|
+
* authorize on it — `productInstanceId` is the identity. Not the
|
|
16979
|
+
* Definition's `displayName`, which names the Product.
|
|
16980
|
+
*/
|
|
16981
|
+
"displayName",
|
|
16922
16982
|
/**
|
|
16923
16983
|
* The reference of the log-semantics descriptor this release's
|
|
16924
16984
|
* Definition declares — the value a record carries in
|
|
@@ -17269,7 +17329,33 @@ var VirtualMachineDynamicResourceSchema = external_exports.object({
|
|
|
17269
17329
|
maxCount: external_exports.number().int().min(1).max(MAX_DYNAMIC_RESOURCES_PER_INSTANCE),
|
|
17270
17330
|
operatingSystem: MachineOperatingSystemSchema,
|
|
17271
17331
|
storage: MachineStorageSchema,
|
|
17272
|
-
bootstrap: DynamicResourceBootstrapSchema
|
|
17332
|
+
bootstrap: DynamicResourceBootstrapSchema,
|
|
17333
|
+
/**
|
|
17334
|
+
* What may be done to ONE machine after it exists, beyond releasing it.
|
|
17335
|
+
*
|
|
17336
|
+
* THE SAME SHAPE AS THE BLUEPRINT'S OWN `lifecycle`: an operation is an
|
|
17337
|
+
* object carrying `supported`, so a reader who knows one knows the other.
|
|
17338
|
+
* It is not a third way of saying "supported".
|
|
17339
|
+
*
|
|
17340
|
+
* OPTIONAL, AND ABSENT MEANS WHAT IT ALWAYS MEANT: create, read and release,
|
|
17341
|
+
* and nothing else. `.optional()` and never `.default()` — a default would
|
|
17342
|
+
* materialise the key in every released Blueprint the read path re-parses,
|
|
17343
|
+
* and `contractDigest` digests the parsed object.
|
|
17344
|
+
*
|
|
17345
|
+
* A RESOURCE'S LIFECYCLE, NOT THE INSTANCE'S. Suspending a machine powers
|
|
17346
|
+
* it off and keeps it — its disk, its identity and its place in `maxCount`.
|
|
17347
|
+
* Releasing it is still the only way to give the place back, and is never
|
|
17348
|
+
* gated on this declaration. Whether the Product Instance as a whole may be
|
|
17349
|
+
* suspended is the top-level `lifecycle`, which a Product declaring dynamic
|
|
17350
|
+
* resources still may not promise.
|
|
17351
|
+
*
|
|
17352
|
+
* PAIRED. Suspend without resume strands a machine; resume without suspend
|
|
17353
|
+
* describes nothing. The cross-field rules refuse both halves.
|
|
17354
|
+
*/
|
|
17355
|
+
lifecycle: external_exports.object({
|
|
17356
|
+
suspend: external_exports.object({ supported: external_exports.boolean() }).strict(),
|
|
17357
|
+
resume: external_exports.object({ supported: external_exports.boolean() }).strict()
|
|
17358
|
+
}).strict().optional()
|
|
17273
17359
|
}).strict();
|
|
17274
17360
|
var DynamicResourceSchema = external_exports.discriminatedUnion("type", [VirtualMachineDynamicResourceSchema]);
|
|
17275
17361
|
var ComponentDependencySchema = external_exports.object({
|
|
@@ -18035,6 +18121,23 @@ function checkDeploymentBlueprint(blueprint, ctx) {
|
|
|
18035
18121
|
}
|
|
18036
18122
|
}
|
|
18037
18123
|
}
|
|
18124
|
+
const power = resource2.lifecycle;
|
|
18125
|
+
if (power !== void 0) {
|
|
18126
|
+
if (power.suspend.supported && !power.resume.supported) {
|
|
18127
|
+
ctx.addIssue({
|
|
18128
|
+
code: external_exports.ZodIssueCode.custom,
|
|
18129
|
+
path: ["dynamicResources", index, "lifecycle", "resume", "supported"],
|
|
18130
|
+
message: "A resource that supports suspend must also support resume: a machine that can be powered off and never on again has been released without giving its place back."
|
|
18131
|
+
});
|
|
18132
|
+
}
|
|
18133
|
+
if (power.resume.supported && !power.suspend.supported) {
|
|
18134
|
+
ctx.addIssue({
|
|
18135
|
+
code: external_exports.ZodIssueCode.custom,
|
|
18136
|
+
path: ["dynamicResources", index, "lifecycle", "suspend", "supported"],
|
|
18137
|
+
message: "resume is only meaningful for a resource that supports suspend: there is no suspended machine to resume."
|
|
18138
|
+
});
|
|
18139
|
+
}
|
|
18140
|
+
}
|
|
18038
18141
|
});
|
|
18039
18142
|
containers.forEach(({ component, index }) => {
|
|
18040
18143
|
const expectedRepository = imageRepository(blueprint.productId, component.id);
|
|
@@ -18658,7 +18761,7 @@ function checkHumanSessionMechanisms(ctx, at, authentication, declared) {
|
|
|
18658
18761
|
|
|
18659
18762
|
// ../product-contracts/dist/deployability.js
|
|
18660
18763
|
var VIRTUAL_MACHINE_PROFILE_INTERFACE = {
|
|
18661
|
-
developer: "Ubuntu 24.04 LTS, at least
|
|
18764
|
+
developer: "Ubuntu 24.04 LTS, at least 4 vCPU and 8 GiB memory, with local instance storage"
|
|
18662
18765
|
};
|
|
18663
18766
|
var VIRTUAL_MACHINE_PROFILES = new Set(Object.keys(VIRTUAL_MACHINE_PROFILE_INTERFACE));
|
|
18664
18767
|
|
|
@@ -18676,9 +18779,13 @@ var MANIFEST_ACTIONS = [
|
|
|
18676
18779
|
* differs and no secret would be written.
|
|
18677
18780
|
*
|
|
18678
18781
|
* NOT "the document matches it", which is how it read and is a stronger claim
|
|
18679
|
-
* than the planner makes
|
|
18680
|
-
*
|
|
18681
|
-
*
|
|
18782
|
+
* than the planner makes — and not "what a document omits is left alone"
|
|
18783
|
+
* either, which this comment said next and which is true of two things only.
|
|
18784
|
+
* Secrets the request does not carry and connections it does not name are
|
|
18785
|
+
* kept rather than compared, so a manifest naming no connections answers this
|
|
18786
|
+
* beside a resource that holds several. `spec.configuration` is compared as
|
|
18787
|
+
* the WHOLE configuration: a setting it leaves out takes its default, and that
|
|
18788
|
+
* plans as `UPDATE`.
|
|
18682
18789
|
*
|
|
18683
18790
|
* A pipeline that runs on every push and changes nothing reports it, and it
|
|
18684
18791
|
* is SUCCESS. */
|
|
@@ -19799,7 +19906,7 @@ async function runDoctor(parsed) {
|
|
|
19799
19906
|
// line — which is the worst outcome for a diagnostic.
|
|
19800
19907
|
// AND WHAT IT MAY DO THERE, which this also got wrong. "You can
|
|
19801
19908
|
// deploy in those Projects" is false for a gate granted *Project
|
|
19802
|
-
// Read* —
|
|
19909
|
+
// Read* — every permission a read, no writes at all —
|
|
19803
19910
|
// and `doctor` is the FIRST command an agent is told to run, so it
|
|
19804
19911
|
// was the first sentence an observing agent read. An agent that
|
|
19805
19912
|
// believes it can deploy and is then refused reports a platform
|