@varde-flyt/vfac 0.15.3 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +32 -7
  2. package/dist/vfac.mjs +675 -31
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -228,13 +228,38 @@ holding on to:
228
228
  | `valueFrom.resourceOutput` | read a value another resource publishes |
229
229
  | `dependencyBindings` | use that resource as the PROVIDER for a declared dependency |
230
230
 
231
- A connection is established once. **A manifest can make one and cannot move
232
- one**: pointing an existing connection at a different resource is refused rather
233
- than performed, because a file you re-apply on every push must not be able to
234
- silently move a running service onto a different identity provider. The
235
- credential the connection delivers never appears in your manifest, in a plan, or
236
- in any `vfac` output — the providing service issues it and the platform stores
237
- it.
231
+ An entry means one of three things, and only one of them releases anything:
232
+
233
+ | The manifest says | What `vfac apply` does |
234
+ | --- | --- |
235
+ | nothing for the key | leaves that connection exactly as it is |
236
+ | `resourceKey: …` | connects to that resource, or keeps the connection it has |
237
+ | `state: disconnected` | releases the connection, in the same apply as your settings |
238
+
239
+ ```yaml
240
+ spec:
241
+ configuration:
242
+ signIn: Disabled # whatever setting made the connection necessary
243
+ dependencyBindings:
244
+ <dependency-key>:
245
+ state: disconnected
246
+ ```
247
+
248
+ Leaving a key out never releases anything, so a manifest written before this
249
+ field existed re-applies unchanged. `vfac plan` lists a release under
250
+ `connections to disconnect` before you approve it. A dependency the Product
251
+ requires cannot be disconnected; one a setting makes required can be, in the same
252
+ file that turns the setting off. A released connection is torn down by its
253
+ provider afterwards, and the providing resource can only be deleted once that
254
+ teardown is done.
255
+
256
+ **A manifest never moves a connection in one apply**: pointing an existing
257
+ connection at a different resource is refused rather than performed, because a
258
+ file you re-apply on every push must not be able to silently move a running
259
+ service onto a different identity provider. Disconnect it and apply, then name
260
+ the new resource and apply again. The credential the connection delivers never
261
+ appears in your manifest, in a plan, or in any `vfac` output — the providing
262
+ service issues it and the platform stores it.
238
263
 
239
264
  ## For an agent or a pipeline
240
265
 
package/dist/vfac.mjs CHANGED
@@ -12337,12 +12337,12 @@ var CustomerConfigurationSchema = CustomerConfigurationObjectSchema.superRefine(
12337
12337
  });
12338
12338
  }
12339
12339
  }
12340
- const duplicates = (config.required ?? []).filter((name, index, all) => all.indexOf(name) !== index);
12341
- if (duplicates.length > 0) {
12340
+ const duplicates2 = (config.required ?? []).filter((name, index, all) => all.indexOf(name) !== index);
12341
+ if (duplicates2.length > 0) {
12342
12342
  ctx.addIssue({
12343
12343
  code: external_exports.ZodIssueCode.custom,
12344
12344
  path: ["required"],
12345
- message: `Duplicate required entries: ${[...new Set(duplicates)].join(", ")}.`
12345
+ message: `Duplicate required entries: ${[...new Set(duplicates2)].join(", ")}.`
12346
12346
  });
12347
12347
  }
12348
12348
  checkDependentRequired(config, ctx);
@@ -14322,6 +14322,517 @@ function settingContainer(container, requiredByParent) {
14322
14322
  };
14323
14323
  }
14324
14324
 
14325
+ // ../log-access-contract/dist/operations.js
14326
+ var ALL_LOG_ACCESS_OPS = Object.freeze([
14327
+ "append",
14328
+ "query",
14329
+ "aggregate",
14330
+ "analyze"
14331
+ ]);
14332
+
14333
+ // ../log-access-contract/dist/limits.js
14334
+ var MAX_RECORDS_PER_APPEND = 500;
14335
+ var MAX_RECORD_BYTES = 16384;
14336
+ var MAX_APPEND_BYTES = 1048576;
14337
+ var MAX_APPEND_BODY_BYTES = 2 * MAX_APPEND_BYTES;
14338
+ var MAX_ATTRIBUTION_LENGTH = 128;
14339
+ var MAX_RECORD_FIELDS = 32;
14340
+ var MAX_FIELD_NAME_LENGTH = 64;
14341
+ var MAX_FIELD_VALUE_LENGTH = 2048;
14342
+ var MAX_QUERY_LIMIT = 1e3;
14343
+ var MAX_QUERY_WINDOW_SECONDS = 86400;
14344
+ var MAX_QUERY_FILTERS = 8;
14345
+ var MAX_AGGREGATE_WINDOW_SECONDS = 604800;
14346
+ var MIN_AGGREGATE_BUCKET_SECONDS = 60;
14347
+ var MAX_AGGREGATE_BUCKETS = 1440;
14348
+ var MAX_AGGREGATE_GROUPS = 100;
14349
+ var MAX_RECORDS_PER_TIMESTAMP = 5e3;
14350
+ var MAX_ANALYSIS_WINDOW_SECONDS = 604800;
14351
+ var MAX_ANALYSIS_PREDICATES = 8;
14352
+ var MAX_ANALYSIS_PREDICATE_VALUES = 16;
14353
+ var MAX_ANALYSIS_VALUE_BYTES = 16384;
14354
+ var MAX_ANALYSIS_GROUP_BY_FIELDS = 2;
14355
+ var MAX_ANALYSIS_GROUPS = 100;
14356
+ var MIN_ANALYSIS_BUCKET_SECONDS = MIN_AGGREGATE_BUCKET_SECONDS;
14357
+ var MAX_ANALYSIS_BUCKETS = MAX_AGGREGATE_BUCKETS;
14358
+ var MAX_ANALYSIS_CELLS = 1e4;
14359
+
14360
+ // ../log-access-contract/dist/wire.js
14361
+ var LOG_ACCESS_REQUEST_HEADERS = Object.freeze({
14362
+ signature: "x-vfac-signature",
14363
+ operationId: "x-vfac-operation-id",
14364
+ requestedAt: "x-vfac-requested-at",
14365
+ productInstanceId: "x-vfac-product-instance-id"
14366
+ });
14367
+ var SIGNING_CANONICAL_FIELDS = Object.freeze([
14368
+ "method",
14369
+ "path",
14370
+ "operationId",
14371
+ "requestedAt",
14372
+ "sha256(body)"
14373
+ ]);
14374
+ var LOG_SEVERITIES = Object.freeze(["debug", "info", "warn", "error", "fatal"]);
14375
+ var RESERVED_RECORD_FIELDS = Object.freeze([
14376
+ "message",
14377
+ "severity",
14378
+ "observedAt",
14379
+ "receivedAt",
14380
+ "attributedTo"
14381
+ ]);
14382
+ var LOG_FIELD_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_.-]*$/;
14383
+ var UNGROUPABLE_RECORD_FIELDS = Object.freeze([
14384
+ "message",
14385
+ "observedAt",
14386
+ "receivedAt"
14387
+ ]);
14388
+ var ANALYSIS_TEXT_OPERATORS = Object.freeze([
14389
+ "eq",
14390
+ "neq",
14391
+ "contains",
14392
+ "notContains"
14393
+ ]);
14394
+ var ANALYSIS_LIST_OPERATORS = Object.freeze(["in", "notIn"]);
14395
+ var ANALYSIS_PRESENCE_OPERATORS = Object.freeze(["exists", "notExists"]);
14396
+ var ANALYSIS_NUMERIC_OPERATORS = Object.freeze(["gt", "gte", "lt", "lte"]);
14397
+ var ANALYSIS_PREDICATE_OPERATORS = Object.freeze([
14398
+ ...ANALYSIS_TEXT_OPERATORS,
14399
+ ...ANALYSIS_LIST_OPERATORS,
14400
+ ...ANALYSIS_PRESENCE_OPERATORS,
14401
+ ...ANALYSIS_NUMERIC_OPERATORS
14402
+ ]);
14403
+ var ANALYSIS_COUNTING_MEASURES = Object.freeze(["count", "rate"]);
14404
+ var ANALYSIS_FIELD_MEASURES = Object.freeze([
14405
+ "sum",
14406
+ "avg",
14407
+ "min",
14408
+ "max",
14409
+ "percentile"
14410
+ ]);
14411
+ var ANALYSIS_MEASURES = Object.freeze([
14412
+ ...ANALYSIS_COUNTING_MEASURES,
14413
+ ...ANALYSIS_FIELD_MEASURES
14414
+ ]);
14415
+ var ANALYSIS_PERCENTILES = Object.freeze(["p50", "p90", "p95", "p99"]);
14416
+ var ANALYSIS_ORDERS = Object.freeze(["desc", "asc"]);
14417
+ var ANALYSIS_RESULT_KINDS = Object.freeze(["scalar", "groups", "timeseries"]);
14418
+
14419
+ // ../log-access-contract/dist/refusals.js
14420
+ function listed(names, conjunction = "or") {
14421
+ const quoted = names.map((name) => `\`${name}\``);
14422
+ return quoted.length < 2 ? quoted.join("") : `${quoted.slice(0, -1).join(", ")} ${conjunction} ${quoted[quoted.length - 1]}`;
14423
+ }
14424
+ var ROUTES = ALL_LOG_ACCESS_OPS.map((op) => `/${op}`);
14425
+ var LOG_ACCESS_REFUSALS = Object.freeze({
14426
+ missing_records: {
14427
+ status: 400,
14428
+ meaning: "no `records`, or one that is not a non-empty list"
14429
+ },
14430
+ too_many_records: {
14431
+ status: 400,
14432
+ meaning: `more than ${MAX_RECORDS_PER_APPEND} records in one append`
14433
+ },
14434
+ append_too_large: {
14435
+ status: 400,
14436
+ meaning: `the records exceed ${MAX_APPEND_BYTES} bytes as compact JSON, or the request body exceeds ${MAX_APPEND_BODY_BYTES} bytes`
14437
+ },
14438
+ record_too_large: {
14439
+ status: 400,
14440
+ meaning: `one record exceeds ${MAX_RECORD_BYTES} bytes. A log line, not a document`
14441
+ },
14442
+ too_many_fields: {
14443
+ status: 400,
14444
+ meaning: `one record carries more than ${MAX_RECORD_FIELDS} fields`
14445
+ },
14446
+ invalid_field: {
14447
+ status: 400,
14448
+ meaning: "a field name or value is empty, over-long, or carries a control character. Field names are structured keys and never stream labels, so none of them changes what your records are filed under"
14449
+ },
14450
+ reserved_field: {
14451
+ status: 400,
14452
+ meaning: `a field is named ${listed(RESERVED_RECORD_FIELDS)}. Those are the record\u2019s own keys and are stored beside your fields, so a field of the same name would overwrite one or be overwritten by it \u2014 rename the field`
14453
+ },
14454
+ invalid_attribution: {
14455
+ status: 400,
14456
+ meaning: `\`attributedTo\` is not one identifier of at most ${MAX_ATTRIBUTION_LENGTH} letters, digits, \`.\`, \`_\`, \`:\` or \`-\` starting with a letter or digit`
14457
+ },
14458
+ invalid_timestamp: {
14459
+ status: 400,
14460
+ meaning: "`observedAt` is not an RFC 3339 instant. The service stamps its own receipt time as well, and that one is what ordering uses"
14461
+ },
14462
+ invalid_severity: {
14463
+ status: 400,
14464
+ meaning: "`severity` is not one of the levels this contract defines"
14465
+ },
14466
+ invalid_window: {
14467
+ status: 400,
14468
+ meaning: `\`from\` and \`to\` are not an RFC 3339 range, are inverted, or span more than ${MAX_QUERY_WINDOW_SECONDS} seconds. Not the retention window \u2014 how much ONE query scans`
14469
+ },
14470
+ invalid_limit: {
14471
+ status: 400,
14472
+ meaning: `\`limit\` is not a whole number in 1\u2026${MAX_QUERY_LIMIT}`
14473
+ },
14474
+ invalid_filters: {
14475
+ status: 400,
14476
+ meaning: `\`filters\` is not a list of \`{field, equals}\`, or carries more than ${MAX_QUERY_FILTERS} entries`
14477
+ },
14478
+ invalid_cursor: {
14479
+ status: 400,
14480
+ meaning: "the `cursor` did not come from this service, or is not for this query. A page is resumed with the cursor the previous page returned, verbatim, and never composed"
14481
+ },
14482
+ invalid_aggregate: {
14483
+ status: 400,
14484
+ meaning: `the aggregate is not one this service answers: a window over ${MAX_AGGREGATE_WINDOW_SECONDS} seconds, a \`bucketSeconds\` under ${MIN_AGGREGATE_BUCKET_SECONDS} or not dividing the window into at most ${MAX_AGGREGATE_BUCKETS} whole buckets, a \`groupBy\` that is not a field identifier or names \`message\`, \`observedAt\` or \`receivedAt\`, a \`limit\` outside 1\u2026${MAX_AGGREGATE_GROUPS} or without \`groupBy\`, or both \`bucketSeconds\` and \`groupBy\``
14485
+ },
14486
+ invalid_analysis: {
14487
+ 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}`
14489
+ },
14490
+ analysis_timed_out: {
14491
+ status: 504,
14492
+ meaning: "the provider did not answer within the time one analysis gets. The same analysis is likely to time out again, so narrow the window, the selection or the grouping rather than repeating it \u2014 unlike `log_store_error`, this says the question was too expensive rather than that the provider faltered"
14493
+ },
14494
+ analysis_too_broad: {
14495
+ status: 400,
14496
+ meaning: "ranking the groups met more distinct values than can be ranked in one answer. Not transient \u2014 narrow the window or the selection, or group by a field with fewer values"
14497
+ },
14498
+ ingest_suspended: {
14499
+ status: 403,
14500
+ meaning: "this Product Instance may read its records and may not add one. Its Tenant is being drained or is not active \u2014 see `writable` on the authorization record"
14501
+ },
14502
+ query_not_granted: {
14503
+ status: 403,
14504
+ meaning: "this Product Instance may add records and may not read them back"
14505
+ },
14506
+ log_store_error: {
14507
+ status: 502,
14508
+ meaning: "the provider refused or did not answer. Not yours to fix, and the one refusal here worth retrying"
14509
+ },
14510
+ timestamp_group_too_large: {
14511
+ status: 502,
14512
+ meaning: `more than ${MAX_RECORDS_PER_TIMESTAMP} records share one timestamp, so this page cannot be answered without skipping some. Refused rather than answered incompletely; not yours to fix`
14513
+ },
14514
+ log_store_capacity: {
14515
+ status: 503,
14516
+ meaning: "the provider is rate-limiting or over quota. Retry with backoff; records already accepted are kept"
14517
+ }
14518
+ });
14519
+ var HANDLER_REFUSALS = Object.freeze({
14520
+ invalid_body: { status: 400, meaning: "the request body is not JSON" },
14521
+ invalid_op: {
14522
+ status: 400,
14523
+ meaning: `no \`op\`, or one this route does not serve \u2014 ${listed(ROUTES, "and")} each serve the operation they are named after`
14524
+ },
14525
+ forbidden: {
14526
+ status: 403,
14527
+ meaning: "the credential verified and does not hold this operation\u2019s permission. Not an authentication failure \u2014 see 401 for those"
14528
+ },
14529
+ log_access_unconfigured: {
14530
+ status: 503,
14531
+ meaning: "this deployment cannot present a working provider credential \u2014 it has none, or the provider refused the one it has \u2014 so it can broker nothing. An operator problem, not a caller one, and not worth retrying at a caller\u2019s pace: it clears when an operator or the platform replaces the credential"
14532
+ }
14533
+ });
14534
+ var AUTHENTICATION_REFUSALS = Object.freeze({
14535
+ unauthorized: {
14536
+ status: 401,
14537
+ meaning: "the signature, the headers, the clock, or an instance that does not exist. Which one is never said"
14538
+ },
14539
+ operation_replay: {
14540
+ status: 409,
14541
+ meaning: "that operation id has been used inside the replay window. Sign a retry with a NEW one"
14542
+ },
14543
+ unavailable: {
14544
+ status: 503,
14545
+ meaning: "the platform could not read this instance\u2019s credential. Transient; retry"
14546
+ },
14547
+ replay_store_unavailable: {
14548
+ status: 503,
14549
+ meaning: "the replay store did not answer, so this request cannot be proved to be a first use. Refused rather than accepted; retry with a fresh operation id"
14550
+ }
14551
+ });
14552
+ var ALL_REFUSALS = Object.freeze({
14553
+ ...LOG_ACCESS_REFUSALS,
14554
+ ...HANDLER_REFUSALS
14555
+ });
14556
+
14557
+ // ../log-access-contract/dist/log-semantics.js
14558
+ var LOG_EVENT_FIELD = "event";
14559
+ var LOG_SCHEMA_REF_FIELD = "vfac_log_schema";
14560
+ var LOG_SEMANTICS_FORMAT_V1 = "log-semantics.vardeflyt.no/v1";
14561
+
14562
+ // ../log-access-contract/dist/analysis-examples.js
14563
+ var ANALYZE_REQUEST_EXAMPLES = Object.freeze([
14564
+ {
14565
+ id: "p95-per-operation-over-time",
14566
+ asks: "The 95th percentile of `duration_ms` per operation, in five-minute buckets, over the production records that are errors or failed",
14567
+ request: {
14568
+ op: "analyze",
14569
+ from: "2026-09-21T00:00:00Z",
14570
+ to: "2026-09-22T00:00:00Z",
14571
+ where: {
14572
+ all: [{ field: "environment", op: "eq", value: "prod" }],
14573
+ any: [
14574
+ { field: "severity", op: "eq", value: "error" },
14575
+ { field: "outcome", op: "eq", value: "failed" }
14576
+ ]
14577
+ },
14578
+ measure: { op: "percentile", field: "duration_ms", percentile: "p95" },
14579
+ groupBy: ["operation"],
14580
+ bucketSeconds: 300,
14581
+ limit: 20
14582
+ }
14583
+ },
14584
+ {
14585
+ id: "error-count",
14586
+ asks: "How many records in a day were errors or worse",
14587
+ request: {
14588
+ op: "analyze",
14589
+ from: "2026-09-21T00:00:00Z",
14590
+ to: "2026-09-22T00:00:00Z",
14591
+ where: { all: [{ field: "severity", op: "in", values: ["error", "fatal"] }] },
14592
+ measure: { op: "count" }
14593
+ }
14594
+ },
14595
+ {
14596
+ id: "slowest-routes",
14597
+ asks: "The ten slowest checkout routes of the week per environment, counting only requests that took a second or more",
14598
+ request: {
14599
+ op: "analyze",
14600
+ from: "2026-09-15T00:00:00Z",
14601
+ to: "2026-09-22T00:00:00Z",
14602
+ where: {
14603
+ all: [
14604
+ { field: "duration_ms", op: "gte", value: 1e3 },
14605
+ { field: "route", op: "contains", value: "/checkout" }
14606
+ ]
14607
+ },
14608
+ measure: { op: "max", field: "duration_ms" },
14609
+ groupBy: ["route", "environment"],
14610
+ limit: 10,
14611
+ order: "desc"
14612
+ }
14613
+ },
14614
+ {
14615
+ id: "grouped-alternatives",
14616
+ asks: "How many records in each environment either failed or took a second or more, counting only the two checkout routes",
14617
+ request: {
14618
+ op: "analyze",
14619
+ from: "2026-09-21T00:00:00Z",
14620
+ to: "2026-09-22T00:00:00Z",
14621
+ where: {
14622
+ all: [
14623
+ {
14624
+ any: [
14625
+ { field: "route", op: "eq", value: "/checkout" },
14626
+ { field: "route", op: "eq", value: "/checkout/pay" }
14627
+ ]
14628
+ },
14629
+ {
14630
+ any: [
14631
+ { field: "outcome", op: "eq", value: "failed" },
14632
+ { field: "duration_ms", op: "gte", value: 1e3 }
14633
+ ]
14634
+ }
14635
+ ]
14636
+ },
14637
+ measure: { op: "count" },
14638
+ groupBy: ["environment"],
14639
+ limit: 5
14640
+ }
14641
+ },
14642
+ {
14643
+ id: "hourly-rate",
14644
+ asks: "Records per second, hour by hour, over a day",
14645
+ request: {
14646
+ op: "analyze",
14647
+ from: "2026-09-21T00:00:00Z",
14648
+ to: "2026-09-22T00:00:00Z",
14649
+ measure: { op: "rate" },
14650
+ bucketSeconds: 3600
14651
+ }
14652
+ }
14653
+ ]);
14654
+ var ANALYZE_RESPONSE_EXAMPLES = Object.freeze({
14655
+ scalar: { ok: true, kind: "scalar", value: 124 },
14656
+ groups: {
14657
+ ok: true,
14658
+ kind: "groups",
14659
+ groups: [
14660
+ { dimensions: { route: "/checkout/pay", environment: "prod" }, value: 4210 },
14661
+ { dimensions: { route: "/checkout", environment: null }, value: 1830 }
14662
+ ],
14663
+ truncated: true
14664
+ },
14665
+ timeseries: {
14666
+ ok: true,
14667
+ kind: "timeseries",
14668
+ bucketSeconds: 300,
14669
+ series: [
14670
+ {
14671
+ dimensions: { operation: "deploy" },
14672
+ points: [
14673
+ { start: "2026-09-21T00:00:00.000Z", value: 431 },
14674
+ { start: "2026-09-21T00:05:00.000Z", value: null }
14675
+ ]
14676
+ }
14677
+ ],
14678
+ truncated: false
14679
+ }
14680
+ });
14681
+
14682
+ // ../log-access-contract/dist/workspace-ingest.js
14683
+ var WORKSPACE_INGEST_STATUSES = Object.freeze({
14684
+ 202: "accepted in full \u2014 the body is `{accepted}` and it equals the number of records sent. Anything short of that is not an acknowledgement",
14685
+ 400: "the batch is malformed; `error` is a record refusal code. Do not retry it unchanged",
14686
+ 401: "the credential is not valid for this address. Stop sending until the binding changes",
14687
+ 403: "the credential does not hold `logs.write`. Stop sending until the binding changes",
14688
+ 429: "the workspace is over its rate. Back off, honouring `Retry-After` when present",
14689
+ 503: "the workspace cannot take records now. Retry with backoff"
14690
+ });
14691
+
14692
+ // ../product-configuration/dist/log-semantics.js
14693
+ var MAX_LOG_SEMANTICS_EVENTS = 128;
14694
+ var MAX_LOG_SEMANTICS_FIELDS = 64;
14695
+ var MAX_LOG_EVENT_FIELDS = MAX_RECORD_FIELDS - 2;
14696
+ var MAX_LOG_FIELD_VALUES = 32;
14697
+ var MAX_LOG_CORRELATION_FIELDS = 8;
14698
+ var MAX_LOG_INTERPRETATION_NOTES = 12;
14699
+ var MAX_LOG_EVENT_NAME_LENGTH = 128;
14700
+ var MAX_LOG_SEMANTICS_BYTES = 131072;
14701
+ var LOG_FIELD_TYPES = Object.freeze(["string", "number", "boolean"]);
14702
+ var LOG_EVENT_DELIVERY = Object.freeze(["at-least-once", "best-effort"]);
14703
+ var LOG_EVENT_NAME_PATTERN = /^[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)*$/;
14704
+ function hasControlCharacter(value) {
14705
+ for (let i = 0; i < value.length; i += 1) {
14706
+ const code = value.charCodeAt(i);
14707
+ if (code < 32 || code === 127)
14708
+ return true;
14709
+ }
14710
+ return false;
14711
+ }
14712
+ var TextSchema = (min, max) => external_exports.string().min(min).max(max).refine((value) => !hasControlCharacter(value), {
14713
+ message: "Descriptor text is one line, with no control characters."
14714
+ });
14715
+ var FieldNameSchema = external_exports.string().min(1).max(MAX_FIELD_NAME_LENGTH).regex(LOG_FIELD_NAME_PATTERN, "A field name follows the ingest wire rule.");
14716
+ var LogFieldValueSchema = external_exports.object({
14717
+ /** One value the field can hold, exactly as a record carries it. */
14718
+ value: TextSchema(1, 128),
14719
+ /** What that value means. */
14720
+ meaning: TextSchema(3, 300)
14721
+ }).strict();
14722
+ var LogFieldSchema = external_exports.object({
14723
+ name: FieldNameSchema,
14724
+ type: external_exports.enum(LOG_FIELD_TYPES),
14725
+ meaning: TextSchema(10, 400),
14726
+ /** The unit a number is measured in: `ms`, `bytes`, `EUR`. Numbers only. */
14727
+ unit: TextSchema(1, 32).optional(),
14728
+ /** Values a string field holds, each explained. Strings only. */
14729
+ values: external_exports.array(LogFieldValueSchema).min(1).max(MAX_LOG_FIELD_VALUES).optional(),
14730
+ /**
14731
+ * `true`: `values` is every value the field can hold, so a record carrying
14732
+ * another is out of contract. Absent: `values` explains the known ones.
14733
+ */
14734
+ closed: external_exports.literal(true).optional()
14735
+ }).strict();
14736
+ var LogEventFieldSchema = external_exports.object({
14737
+ /** A declared field this event's records carry. */
14738
+ field: FieldNameSchema,
14739
+ /** Whether every record of this event carries it. */
14740
+ required: external_exports.boolean()
14741
+ }).strict();
14742
+ var LogEventSchema = external_exports.object({
14743
+ name: external_exports.string().min(1).max(MAX_LOG_EVENT_NAME_LENGTH).regex(LOG_EVENT_NAME_PATTERN, "An event name is dotted lowercase segments."),
14744
+ meaning: TextSchema(10, 400),
14745
+ /** When the producer actually emits it — the condition, not the intent. */
14746
+ whenProduced: TextSchema(10, 400),
14747
+ delivery: external_exports.enum(LOG_EVENT_DELIVERY).optional(),
14748
+ /** The declared fields its records carry, beyond `event` and `vfac_log_schema`. */
14749
+ fields: external_exports.array(LogEventFieldSchema).max(MAX_LOG_EVENT_FIELDS)
14750
+ }).strict();
14751
+ var LogCorrelationSchema = external_exports.object({
14752
+ /** A declared field records can be joined on. */
14753
+ field: FieldNameSchema,
14754
+ meaning: TextSchema(10, 300)
14755
+ }).strict();
14756
+ var LogInterpretationSchema = external_exports.object({
14757
+ /** What it means when a field an event may carry is absent. */
14758
+ absentField: TextSchema(10, 600),
14759
+ /** Whether a record can arrive more than once, and how to tell. */
14760
+ duplicates: TextSchema(10, 600),
14761
+ correlation: external_exports.array(LogCorrelationSchema).min(1).max(MAX_LOG_CORRELATION_FIELDS).optional(),
14762
+ notes: external_exports.array(TextSchema(10, 400)).min(1).max(MAX_LOG_INTERPRETATION_NOTES).optional()
14763
+ }).strict();
14764
+ var LogSemanticsDescriptorObjectSchema = external_exports.object({
14765
+ format: external_exports.literal(LOG_SEMANTICS_FORMAT_V1),
14766
+ fields: external_exports.array(LogFieldSchema).min(1).max(MAX_LOG_SEMANTICS_FIELDS),
14767
+ events: external_exports.array(LogEventSchema).min(1).max(MAX_LOG_SEMANTICS_EVENTS),
14768
+ interpretation: LogInterpretationSchema
14769
+ }).strict();
14770
+ var UNDECLARABLE = /* @__PURE__ */ new Set([
14771
+ ...RESERVED_RECORD_FIELDS,
14772
+ LOG_EVENT_FIELD,
14773
+ LOG_SCHEMA_REF_FIELD
14774
+ ]);
14775
+ function duplicates(values) {
14776
+ const seen = /* @__PURE__ */ new Set();
14777
+ const repeated = /* @__PURE__ */ new Set();
14778
+ for (const value of values)
14779
+ (seen.has(value) ? repeated : seen).add(value);
14780
+ return [...repeated];
14781
+ }
14782
+ var LogSemanticsDescriptorSchema = LogSemanticsDescriptorObjectSchema.superRefine((descriptor, ctx) => {
14783
+ const issue = (path, message) => ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path, message });
14784
+ const declared = new Set(descriptor.fields.map((field) => field.name));
14785
+ for (const name of duplicates(descriptor.fields.map((field) => field.name))) {
14786
+ issue(["fields"], `Field "${name}" is declared more than once.`);
14787
+ }
14788
+ for (const name of duplicates(descriptor.events.map((event) => event.name))) {
14789
+ issue(["events"], `Event "${name}" is declared more than once.`);
14790
+ }
14791
+ descriptor.fields.forEach((field, i) => {
14792
+ if (UNDECLARABLE.has(field.name)) {
14793
+ issue(["fields", i, "name"], `"${field.name}" is the record's own key or a convention field, and is not declared.`);
14794
+ }
14795
+ if (field.unit !== void 0 && field.type !== "number") {
14796
+ issue(["fields", i, "unit"], "Only a number field has a unit.");
14797
+ }
14798
+ if (field.values !== void 0 && field.type !== "string") {
14799
+ issue(["fields", i, "values"], "Only a string field lists values.");
14800
+ }
14801
+ if (field.closed !== void 0 && field.values === void 0) {
14802
+ issue(["fields", i, "closed"], "A closed field lists its values.");
14803
+ }
14804
+ for (const value of duplicates((field.values ?? []).map((entry) => entry.value))) {
14805
+ issue(["fields", i, "values"], `Value "${value}" is explained more than once.`);
14806
+ }
14807
+ });
14808
+ descriptor.events.forEach((event, i) => {
14809
+ for (const name of duplicates(event.fields.map((entry) => entry.field))) {
14810
+ issue(["events", i, "fields"], `Event "${event.name}" lists "${name}" more than once.`);
14811
+ }
14812
+ event.fields.forEach((entry, j) => {
14813
+ if (!declared.has(entry.field)) {
14814
+ issue(["events", i, "fields", j, "field"], `Event "${event.name}" names "${entry.field}", which is not a declared field.`);
14815
+ }
14816
+ });
14817
+ });
14818
+ const correlation = descriptor.interpretation.correlation ?? [];
14819
+ for (const name of duplicates(correlation.map((entry) => entry.field))) {
14820
+ issue(["interpretation", "correlation"], `Correlation field "${name}" is listed twice.`);
14821
+ }
14822
+ correlation.forEach((entry, i) => {
14823
+ if (!declared.has(entry.field)) {
14824
+ issue(["interpretation", "correlation", i, "field"], `"${entry.field}" is not a declared field.`);
14825
+ }
14826
+ });
14827
+ const bytes = logSemanticsByteLength(descriptor);
14828
+ if (bytes > MAX_LOG_SEMANTICS_BYTES) {
14829
+ issue([], `The descriptor is ${bytes} bytes as canonical JSON, over the ${MAX_LOG_SEMANTICS_BYTES}-byte bound.`);
14830
+ }
14831
+ });
14832
+ function logSemanticsByteLength(descriptor) {
14833
+ return new TextEncoder().encode(canonicalJson(descriptor)).length;
14834
+ }
14835
+
14325
14836
  // ../product-configuration/dist/manifest-yaml.js
14326
14837
  var import_yaml = __toESM(require_dist(), 1);
14327
14838
  function manifestYaml(document) {
@@ -14338,6 +14849,56 @@ var GovernanceFeedActorSchema = external_exports.discriminatedUnion("type", [
14338
14849
  external_exports.object({ type: external_exports.literal("resource"), id: opaqueId }),
14339
14850
  external_exports.object({ type: external_exports.literal("system") })
14340
14851
  ]);
14852
+ var connection = (action, description, outcome = "success") => ({
14853
+ category: "connections",
14854
+ action,
14855
+ outcome,
14856
+ target: "service_binding",
14857
+ description
14858
+ });
14859
+ var resource = (action, description, outcome = "success") => ({
14860
+ category: "resources",
14861
+ action,
14862
+ outcome,
14863
+ target: "resource",
14864
+ description
14865
+ });
14866
+ var GOVERNANCE_FEED_RULES = Object.freeze({
14867
+ // The portal's own connection.
14868
+ "service_binding.platform.connected": connection("connect", "The organization portal was connected to its log workspace."),
14869
+ "service_binding.platform.disconnected": connection("disconnect", "The organization portal was disconnected from its log workspace."),
14870
+ "service_binding.platform.rotation_requested": connection("rotate", "A new credential was requested for the portal\u2019s connection to its log workspace."),
14871
+ "service_binding.platform.ready": connection("complete", "The log workspace completed the portal\u2019s connection; records can be delivered."),
14872
+ "service_binding.platform.rotated": connection("rotate_complete", "The log workspace issued the portal\u2019s connection a new credential."),
14873
+ "service_binding.platform.failed": connection("complete", "The log workspace could not complete the portal\u2019s connection.", "failure"),
14874
+ "service_binding.platform.rotation_failed": connection("rotate_complete", "The log workspace could not issue the portal\u2019s connection a new credential.", "failure"),
14875
+ "service_binding.platform.released": connection("release", "The log workspace finished removing the portal\u2019s connection."),
14876
+ // Connections between Resources.
14877
+ "service_binding.disconnected": connection("disconnect", "A connection between two Resources was disconnected."),
14878
+ "service_binding.rotation_requested": connection("rotate", "A new credential was requested for a connection between two Resources."),
14879
+ "service_binding.ready": connection("complete", "The providing Resource completed a connection; the consuming Resource can use it."),
14880
+ "service_binding.rotated": connection("rotate_complete", "The providing Resource issued a connection a new credential."),
14881
+ "service_binding.failed": connection("complete", "The providing Resource could not complete a connection.", "failure"),
14882
+ "service_binding.rotation_failed": connection("rotate_complete", "The providing Resource could not issue a connection a new credential.", "failure"),
14883
+ "service_binding.released": connection("release", "The providing Resource finished removing a connection."),
14884
+ // How an operation on a Resource ended.
14885
+ "product.instance.active": resource("from-operation-type", "An operation on a Resource completed and the Resource is running; action names the operation."),
14886
+ "product.instance.suspended": resource("suspend", "A Resource was suspended."),
14887
+ "product.instance.deleted": resource("delete", "A Resource was deleted."),
14888
+ "product.instance.reconcile.failed": resource("reconcile", "An operation on a Resource failed and the platform stopped retrying it.", "failure"),
14889
+ "product.instance.upgrade.hop_committed": resource("upgrade", "A Resource finished moving to a new version."),
14890
+ "product.instance.upgrade.abandoned": resource("upgrade", "A Resource\u2019s move to a new version was abandoned; it stays on the version it had.", "failure")
14891
+ });
14892
+ var GOVERNANCE_FEED_EVENT_TYPES = Object.freeze(Object.keys(GOVERNANCE_FEED_RULES).sort());
14893
+ var GOVERNANCE_FEED_OPERATION_ACTIONS = Object.freeze({
14894
+ PRODUCT_INSTANCE_CREATE: "create",
14895
+ PRODUCT_INSTANCE_UPDATE: "configure",
14896
+ PRODUCT_INSTANCE_UPGRADE: "upgrade",
14897
+ PRODUCT_INSTANCE_SUSPEND: "suspend",
14898
+ PRODUCT_INSTANCE_RESUME: "resume",
14899
+ PRODUCT_INSTANCE_DELETE: "delete",
14900
+ PRODUCT_INSTANCE_RECONCILE: "reconcile"
14901
+ });
14341
14902
  var GovernanceFeedEventSchema = external_exports.object({
14342
14903
  /** The audit row's id. Stable, so a workspace can recognise a re-delivery. */
14343
14904
  id: opaqueId,
@@ -15094,7 +15655,7 @@ var ProductDependencySchema = external_exports.object({
15094
15655
  var ProductDependencyV2Schema = ProductDependencySchema.extend({
15095
15656
  requiredWhen: RequiredWhenV2Schema.optional()
15096
15657
  });
15097
- function hasControlCharacter(value) {
15658
+ function hasControlCharacter2(value) {
15098
15659
  for (let i = 0; i < value.length; i += 1) {
15099
15660
  const code = value.charCodeAt(i);
15100
15661
  if (code < 32 || code === 127)
@@ -15102,7 +15663,7 @@ function hasControlCharacter(value) {
15102
15663
  }
15103
15664
  return false;
15104
15665
  }
15105
- var GuidanceTextSchema = (min, max) => external_exports.string().min(min).max(max).refine((value) => !hasControlCharacter(value), {
15666
+ var GuidanceTextSchema = (min, max) => external_exports.string().min(min).max(max).refine((value) => !hasControlCharacter2(value), {
15106
15667
  message: "Guidance is one line, with no control characters."
15107
15668
  });
15108
15669
  var ConsumerGuidanceSchema = external_exports.object({
@@ -15253,6 +15814,17 @@ var ProductDefinitionObjectSchema = external_exports.object({
15253
15814
  * Read absence through the empty array at the use site instead.
15254
15815
  */
15255
15816
  serviceInterfaces: external_exports.object({ provides: external_exports.array(ServiceInterfaceProvideSchema).min(1).max(4) }).strict().optional(),
15817
+ /**
15818
+ * What this Product's own log records mean — see `log-semantics.ts` in `product-configuration`.
15819
+ *
15820
+ * `.optional()`, NEVER `.default()`. A default materialises the key,
15821
+ * `contractDigest` digests the parsed object, and every release published
15822
+ * before this field existed would be refused on republish as
15823
+ * `RELEASED_CONTRACT_MUTATED`. A NEW release that sends records to
15824
+ * `observability.logs/v1` must declare it (`validatePublishableDefinition`);
15825
+ * a record never needs it to be accepted.
15826
+ */
15827
+ observability: external_exports.object({ logs: LogSemanticsDescriptorSchema }).strict().optional(),
15256
15828
  lifecycle: LifecycleCapabilitiesSchema,
15257
15829
  policies: PolicyReferencesSchema,
15258
15830
  /**
@@ -15901,7 +16473,7 @@ function looksLikeReference(value) {
15901
16473
  }
15902
16474
 
15903
16475
  // ../product-contracts/dist/dependency-binding.js
15904
- var DependencyBindingSelectorSchema = external_exports.object({
16476
+ var DependencyBindingConnectSchema = external_exports.object({
15905
16477
  /** The providing Resource's `metadata.key`. */
15906
16478
  resourceKey: ResourceKeySchema,
15907
16479
  /**
@@ -15920,6 +16492,11 @@ var DependencyBindingSelectorSchema = external_exports.object({
15920
16492
  */
15921
16493
  projectId: external_exports.string().min(1).max(64).optional()
15922
16494
  }).strict();
16495
+ var DependencyBindingDisconnectSchema = external_exports.object({ state: external_exports.literal("disconnected") }).strict();
16496
+ var DependencyBindingSelectorSchema = external_exports.union([
16497
+ DependencyBindingConnectSchema,
16498
+ DependencyBindingDisconnectSchema
16499
+ ]);
15923
16500
  var DependencyBindingsSchema = external_exports.record(ServiceBindingKeySchema, DependencyBindingSelectorSchema);
15924
16501
 
15925
16502
  // ../product-contracts/dist/product-instance-template.schema.js
@@ -16021,10 +16598,17 @@ var ProductInstanceTemplateSchema = external_exports.object({
16021
16598
  * schema; this names another Resource, and the create envelope made the
16022
16599
  * identical split for the identical reason.
16023
16600
  *
16601
+ * THREE MEANINGS, AND ONLY ONE OF THEM RELEASES ANYTHING:
16602
+ *
16603
+ * key omitted leave that connection alone
16604
+ * { resourceKey, … } connect to (or keep) this provider
16605
+ * { state: disconnected } release the connection it holds
16606
+ *
16024
16607
  * OPTIONAL, PERMANENTLY. Every manifest written before this field
16025
16608
  * existed omits it, and absence has to keep meaning "leave the
16026
16609
  * connections alone" — otherwise re-applying an unchanged file would
16027
- * release a binding somebody made in the portal.
16610
+ * release a binding somebody made in the portal. A release is
16611
+ * therefore never inferred from a missing key; it is stated.
16028
16612
  *
16029
16613
  * The keys the platform will ACCEPT here are narrower than the shape:
16030
16614
  * only a dependency whose interface the registry calls
@@ -16311,7 +16895,15 @@ var InstanceEnvBindingSchema = external_exports.object({
16311
16895
  "productVersion",
16312
16896
  "profile",
16313
16897
  "region",
16314
- "environment"
16898
+ "environment",
16899
+ /**
16900
+ * The reference of the log-semantics descriptor this release's
16901
+ * Definition declares — the value a record carries in
16902
+ * `vfac_log_schema`. Resolved from the release the component is
16903
+ * deployed from, so during an upgrade each container carries its own
16904
+ * release's. A Definition declaring no descriptor cannot bind it.
16905
+ */
16906
+ "logSemanticsRef"
16315
16907
  ])
16316
16908
  }).strict();
16317
16909
  var PlatformEnvBindingSchema = external_exports.object({
@@ -17353,7 +17945,7 @@ function checkDeploymentBlueprint(blueprint, ctx) {
17353
17945
  message: `Artifact source "${id}" is declared more than once.`
17354
17946
  });
17355
17947
  });
17356
- const duplicateTypes = (blueprint.dynamicResources ?? []).map((resource) => resource.type).filter((type, index, all) => all.indexOf(type) !== index);
17948
+ const duplicateTypes = (blueprint.dynamicResources ?? []).map((resource2) => resource2.type).filter((type, index, all) => all.indexOf(type) !== index);
17357
17949
  [...new Set(duplicateTypes)].forEach((type) => {
17358
17950
  ctx.addIssue({
17359
17951
  code: external_exports.ZodIssueCode.custom,
@@ -17361,15 +17953,15 @@ function checkDeploymentBlueprint(blueprint, ctx) {
17361
17953
  message: `"${type}" is declared more than once. A resource type may appear at most once: everything that reads this list resolves a capability by its type.`
17362
17954
  });
17363
17955
  });
17364
- (blueprint.dynamicResources ?? []).forEach((resource, index) => {
17365
- if (!sourceIds.has(resource.bootstrap.artifactSource)) {
17956
+ (blueprint.dynamicResources ?? []).forEach((resource2, index) => {
17957
+ if (!sourceIds.has(resource2.bootstrap.artifactSource)) {
17366
17958
  ctx.addIssue({
17367
17959
  code: external_exports.ZodIssueCode.custom,
17368
17960
  path: ["dynamicResources", index, "bootstrap", "artifactSource"],
17369
- message: `No artifact source "${resource.bootstrap.artifactSource}" is declared. Declare it in "artifactSources" first.`
17961
+ message: `No artifact source "${resource2.bootstrap.artifactSource}" is declared. Declare it in "artifactSources" first.`
17370
17962
  });
17371
17963
  }
17372
- resource.consumers.forEach((consumerId, position) => {
17964
+ resource2.consumers.forEach((consumerId, position) => {
17373
17965
  if (!knownComponents.has(consumerId)) {
17374
17966
  ctx.addIssue({
17375
17967
  code: external_exports.ZodIssueCode.custom,
@@ -17387,7 +17979,7 @@ function checkDeploymentBlueprint(blueprint, ctx) {
17387
17979
  });
17388
17980
  }
17389
17981
  });
17390
- const handoff = resource.bootstrap.productHandoff;
17982
+ const handoff = resource2.bootstrap.productHandoff;
17391
17983
  if (handoff !== void 0) {
17392
17984
  const handoffPath = ["dynamicResources", index, "bootstrap", "productHandoff", "componentId"];
17393
17985
  const target = blueprint.components.find((component) => component.id === handoff.componentId);
@@ -17411,7 +18003,7 @@ function checkDeploymentBlueprint(blueprint, ctx) {
17411
18003
  message: `Component "${handoff.componentId}" declares "${target.ingress.exposure}" ingress. A Product handoff target must declare public ingress: the machine reaches it from outside the platform's network, which is the whole point of an outbound handoff.`
17412
18004
  });
17413
18005
  }
17414
- if (!resource.consumers.includes(handoff.componentId)) {
18006
+ if (!resource2.consumers.includes(handoff.componentId)) {
17415
18007
  ctx.addIssue({
17416
18008
  code: external_exports.ZodIssueCode.custom,
17417
18009
  path: handoffPath,
@@ -17998,12 +18590,12 @@ function checkDeploymentBlueprint(blueprint, ctx) {
17998
18590
  message: `\`${edges.field}\` declares versions this Product ${edges.role}, but \`lifecycle.${edges.operation}.supported\` is false.`
17999
18591
  });
18000
18592
  }
18001
- const duplicates = versions.filter((version, i) => versions.indexOf(version) !== i);
18002
- if (duplicates.length > 0) {
18593
+ const duplicates2 = versions.filter((version, i) => versions.indexOf(version) !== i);
18594
+ if (duplicates2.length > 0) {
18003
18595
  ctx.addIssue({
18004
18596
  code: external_exports.ZodIssueCode.custom,
18005
18597
  path: [...edges.path],
18006
- message: `Duplicate versions: ${[...new Set(duplicates)].join(", ")}.`
18598
+ message: `Duplicate versions: ${[...new Set(duplicates2)].join(", ")}.`
18007
18599
  });
18008
18600
  }
18009
18601
  const notOlder = versions.filter((version) => compareSemver(version, blueprint.productVersion) >= 0);
@@ -18206,6 +18798,46 @@ var PUBLISH_RULE_ANNEX = {
18206
18798
  }
18207
18799
  }
18208
18800
  }
18801
+ },
18802
+ {
18803
+ // THE SAME ASYMMETRY, for what a producer's log records mean — and only
18804
+ // for a producer: the rule is conditional on a dependency on
18805
+ // `observability.logs/v1`, the one declaration that means "sends log
18806
+ // records". Presence is all JSON Schema says; the descriptor's own rules
18807
+ // are in its schema.
18808
+ $comment: "A new release that sends records to observability.logs/v1 (a dependency on that interface) must declare definition.observability.logs: the events it produces and the fields they carry. A log record is never refused for lacking one.",
18809
+ if: {
18810
+ required: ["definition"],
18811
+ properties: {
18812
+ definition: {
18813
+ required: ["dependencies"],
18814
+ properties: {
18815
+ dependencies: {
18816
+ contains: {
18817
+ required: ["interface"],
18818
+ properties: {
18819
+ interface: {
18820
+ required: ["id", "version"],
18821
+ properties: {
18822
+ id: { const: "observability.logs" },
18823
+ version: { const: "v1" }
18824
+ }
18825
+ }
18826
+ }
18827
+ }
18828
+ }
18829
+ }
18830
+ }
18831
+ }
18832
+ },
18833
+ then: {
18834
+ properties: {
18835
+ definition: {
18836
+ required: ["observability"],
18837
+ properties: { observability: { required: ["logs"] } }
18838
+ }
18839
+ }
18840
+ }
18209
18841
  }
18210
18842
  ]
18211
18843
  };
@@ -18607,6 +19239,18 @@ function printPlan(plan, json) {
18607
19239
  }
18608
19240
  }
18609
19241
  }
19242
+ const disconnects = plan.dependencyDisconnects ?? [];
19243
+ if (disconnects.length > 0) {
19244
+ lines.push(" connections to disconnect:");
19245
+ for (const disconnect of disconnects) {
19246
+ lines.push(` ${disconnect.dependencyKey}`);
19247
+ if (disconnect.interfaceId !== void 0) {
19248
+ lines.push(
19249
+ ` interface: ${disconnect.interfaceId}${disconnect.interfaceVersion ? `/${disconnect.interfaceVersion}` : ""}`
19250
+ );
19251
+ }
19252
+ }
19253
+ }
18610
19254
  if ((plan.secretsToWrite ?? []).length > 0) {
18611
19255
  lines.push(` secrets to set: ${plan.secretsToWrite.join(", ")}`);
18612
19256
  for (const line of secretWriteConsequence(plan)) lines.push(` ${line}`);
@@ -19390,21 +20034,21 @@ async function runGet(parsed, deps = {}) {
19390
20034
  `);
19391
20035
  return 0;
19392
20036
  }
19393
- const resource = result.data["resource"] ?? {};
20037
+ const resource2 = result.data["resource"] ?? {};
19394
20038
  const lines = [
19395
- `${String(resource["productInstanceId"] ?? instanceId)} ${String(resource["status"] ?? "")} ${String(resource["displayName"] ?? "")}`,
19396
- ` product ${String(resource["productId"] ?? "")} ${String(resource["productVersion"] ?? "")}`,
19397
- ` region ${String(resource["region"] ?? "")}`,
19398
- ` profile ${String(resource["profile"] ?? "")}`
20039
+ `${String(resource2["productInstanceId"] ?? instanceId)} ${String(resource2["status"] ?? "")} ${String(resource2["displayName"] ?? "")}`,
20040
+ ` product ${String(resource2["productId"] ?? "")} ${String(resource2["productVersion"] ?? "")}`,
20041
+ ` region ${String(resource2["region"] ?? "")}`,
20042
+ ` profile ${String(resource2["profile"] ?? "")}`
19399
20043
  ];
19400
- const key = resource["manifestKey"];
20044
+ const key = resource2["manifestKey"];
19401
20045
  if (typeof key === "string") lines.push(` key ${key}`);
19402
- const outputs = resource["outputs"] ?? {};
19403
- const declared = Array.isArray(resource["outputDeclarations"]) ? resource["outputDeclarations"] : [];
20046
+ const outputs = resource2["outputs"] ?? {};
20047
+ const declared = Array.isArray(resource2["outputDeclarations"]) ? resource2["outputDeclarations"] : [];
19404
20048
  const declaredNames = declared.map((entry) => entry["key"]).filter((key2) => typeof key2 === "string");
19405
20049
  const names = [.../* @__PURE__ */ new Set([...Object.keys(outputs), ...declaredNames])].sort();
19406
20050
  const isSecret2 = (name) => declared.find((entry) => entry["key"] === name)?.["secret"] === true;
19407
- const conditions = Array.isArray(resource["conditions"]) ? resource["conditions"] : [];
20051
+ const conditions = Array.isArray(resource2["conditions"]) ? resource2["conditions"] : [];
19408
20052
  if (conditions.length > 0) {
19409
20053
  lines.push("", " Operational");
19410
20054
  const width = Math.max(
@@ -19443,7 +20087,7 @@ async function runGet(parsed, deps = {}) {
19443
20087
  }
19444
20088
  }
19445
20089
  } else {
19446
- const status = String(resource["status"] ?? "");
20090
+ const status = String(resource2["status"] ?? "");
19447
20091
  lines.push(
19448
20092
  "",
19449
20093
  status === "DELETED" ? " No outputs. This resource was deleted." : status === "FAILED" ? " No outputs. Its deployment failed \u2014 `vfac get operation` says why." : " No outputs yet. A resource publishes them when provisioning resolves them."
@@ -19510,7 +20154,7 @@ This is one page. Continue with --cursor ${result.nextCursor ?? ""}
19510
20154
  process.stderr.write("No Project. Use --project prj_\u2026 or set one in your context.\n");
19511
20155
  return 2;
19512
20156
  }
19513
- const resource = parsed.flags["resource"];
20157
+ const resource2 = parsed.flags["resource"];
19514
20158
  const result = await readAllPages({
19515
20159
  endpoint: session.ready.endpoint,
19516
20160
  token: session.ready.token,
@@ -19519,7 +20163,7 @@ This is one page. Continue with --cursor ${result.nextCursor ?? ""}
19519
20163
  cursor: parsed.flags["cursor"],
19520
20164
  pageSize: parsed.flags["page-size"],
19521
20165
  onePage: true,
19522
- ...resource === void 0 ? {} : { query: { productInstanceId: resource } }
20166
+ ...resource2 === void 0 ? {} : { query: { productInstanceId: resource2 } }
19523
20167
  });
19524
20168
  if (!result.ok) {
19525
20169
  if (result.error.status === 404 && result.error.code === "NOT_FOUND") {
@@ -19539,7 +20183,7 @@ This is one page. Continue with --cursor ${result.nextCursor ?? ""}
19539
20183
  }
19540
20184
  if (result.items.length === 0) {
19541
20185
  process.stdout.write(
19542
- resource === void 0 ? "No operations in this project.\n" : "No operations for this resource in this project.\n"
20186
+ resource2 === void 0 ? "No operations in this project.\n" : "No operations for this resource in this project.\n"
19543
20187
  );
19544
20188
  return 0;
19545
20189
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@varde-flyt/vfac",
3
- "version": "0.15.3",
3
+ "version": "0.17.0",
4
4
  "description": "Deploy and manage Varde Flyt Products from a manifest.",
5
5
  "repository": {
6
6
  "type": "git",