@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.
Files changed (3) hide show
  1. package/README.md +6 -4
  2. package/dist/vfac.mjs +132 -25
  3. 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 alone, or supply all of them. A provider connection is the usual shape —
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
- reads a file with no Project and no credential — cannot tell whether the
196
- credential in a group is configured, and does not refuse a document over it.
197
- `vfac plan` knows what the apply would carry and what the resource already holds,
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) || // A MEMBER, NOT A CONTAINER: a referenced leaf is recorded at this very
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) || sink !== null && Object.hasOwn(sink.secrets, 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 known = (name) => !unknown.has(name);
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) || !held(trigger))
13764
+ if (!known(trigger) || !answered(trigger))
13742
13765
  continue;
13743
13766
  for (const dependent of dependents) {
13744
- if (!known(dependent) || held(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 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"
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 is processed. `tenant-region` means it never leaves the
15724
- * region the customer selected; the other values are a data-residency
15725
- * decision the customer must see before deploying.
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 any tenant content can leave the tenant boundary at all (an
15763
- * external model provider, a SaaS tool). The Blueprint's egress rules are
15764
- * the authoritative detail; this is the one-bit fact the review screen
15765
- * needs.
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 data cannot be global without leaving the tenant boundary.'
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 3 vCPU and 4 GiB memory, with local instance storage"
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. What a document OMITS is left alone rather than
18680
- * compared — a manifest naming no connections answers this beside a resource
18681
- * that holds several — so this never means the file mirrors stored state.
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* — `project.read` and `resource.read`, no writes at all —
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@varde-flyt/vfac",
3
- "version": "0.18.1",
3
+ "version": "0.18.2",
4
4
  "description": "Deploy and manage Varde Flyt Products from a manifest.",
5
5
  "repository": {
6
6
  "type": "git",