@varde-flyt/vfac 0.16.0 → 0.18.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 +55 -8
  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
@@ -14216,7 +14216,7 @@ function exampleResourceKey(name) {
14216
14216
  const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^[^a-z]+/, "").replace(/-+$/, "").slice(0, 63);
14217
14217
  return RESOURCE_KEY_PATTERN.test(slug) ? slug : "";
14218
14218
  }
14219
- var DEPLOYMENT_EXAMPLE_GUARANTEE = "Product-owned configuration is complete and E2E-proven; tenant-specific secrets and Resource/provider selections are supplied by the tenant.";
14219
+ var DEPLOYMENT_EXAMPLE_GUARANTEE = "Product-owned configuration is complete and E2E-proven; tenant-specific secrets, tenant-specific values and Resource/provider selections are supplied by the tenant.";
14220
14220
 
14221
14221
  // ../product-configuration/dist/configuration-requirements.js
14222
14222
  function configurationRequirements(configuration) {
@@ -15077,6 +15077,7 @@ function isBlockedHost(hostname) {
15077
15077
  }
15078
15078
 
15079
15079
  // ../product-contracts/dist/deployment-example.schema.js
15080
+ var MAX_TENANT_INPUTS = 8;
15080
15081
  var DeploymentExampleSchema = external_exports.object({
15081
15082
  /** The heading a customer reads above the example. */
15082
15083
  title: external_exports.string().min(3).max(80),
@@ -15105,12 +15106,29 @@ var DeploymentExampleSchema = external_exports.object({
15105
15106
  * created and again only to set a new one — and never written into a
15106
15107
  * manifest. A secret resent on an unchanged re-apply is written again.
15107
15108
  */
15108
- secretInputs: external_exports.array(external_exports.string().regex(CONFIGURATION_PROPERTY_NAME_REGEX)).max(MAX_CONFIGURATION_CHILDREN).optional()
15109
- }).strict().describe("The recommended deployment for this release. Required for a NEW release; optional in the schema because releases published before the field existed are immutable. " + DEPLOYMENT_EXAMPLE_GUARANTEE + " Secret settings are named in secretInputs and never given a value here.");
15109
+ secretInputs: external_exports.array(external_exports.string().regex(CONFIGURATION_PROPERTY_NAME_REGEX)).max(MAX_CONFIGURATION_CHILDREN).optional(),
15110
+ /**
15111
+ * The ordinary — NOT secret — settings the tenant answers, BY NAME.
15112
+ *
15113
+ * A value only the tenant can know and nothing may default: the address of
15114
+ * the person who administers the Resource first. The example carries NO
15115
+ * value for one — the document a reader copies carries it present and
15116
+ * empty, and the tenant types it — so an example applied unchanged is
15117
+ * refused for that setting by name rather than deployed with a placeholder.
15118
+ * Unlike a secret, the value belongs in the manifest.
15119
+ *
15120
+ * Each must be a setting every deployment must answer, with no default, that
15121
+ * no other rule reads: whether the example is complete cannot depend on a
15122
+ * value the publisher never gave.
15123
+ */
15124
+ tenantInputs: external_exports.array(external_exports.string().regex(CONFIGURATION_PROPERTY_NAME_REGEX)).max(MAX_TENANT_INPUTS).optional()
15125
+ }).strict().describe("The recommended deployment for this release. Required for a NEW release; optional in the schema because releases published before the field existed are immutable. " + DEPLOYMENT_EXAMPLE_GUARANTEE + " Secret settings are named in secretInputs and never given a value here; so are the ordinary settings the tenant answers, named in tenantInputs.");
15110
15126
  var SECRET_INPUT_SEGMENT = CONFIGURATION_PROPERTY_NAME_REGEX.source.slice(1, -1);
15111
- var SecretInputV2Schema = external_exports.string().regex(new RegExp(`^(?:${SECRET_INPUT_SEGMENT}|(?:/${SECRET_INPUT_SEGMENT}){2,${MAX_CONFIGURATION_DEPTH}})$`)).refine((raw) => secretPathOf(raw) !== null, {
15112
- message: 'A secret input is a root setting name, or a configuration path such as "/group/setting" with no list index.'
15127
+ var settingInputV2Schema = (what) => external_exports.string().regex(new RegExp(`^(?:${SECRET_INPUT_SEGMENT}|(?:/${SECRET_INPUT_SEGMENT}){2,${MAX_CONFIGURATION_DEPTH}})$`)).refine((raw) => secretPathOf(raw) !== null, {
15128
+ message: `A ${what} is a root setting name, or a configuration path such as "/group/setting" with no list index.`
15113
15129
  });
15130
+ var SecretInputV2Schema = settingInputV2Schema("secret input");
15131
+ var TenantInputV2Schema = settingInputV2Schema("tenant input");
15114
15132
  var DeploymentExampleV2Schema = DeploymentExampleSchema.extend({
15115
15133
  /**
15116
15134
  * The secret settings a deployment of this example supplies, BY CANONICAL
@@ -15120,7 +15138,12 @@ var DeploymentExampleV2Schema = DeploymentExampleSchema.extend({
15120
15138
  * here, so a reader knows every `--secret` flag the create needs before
15121
15139
  * running it. The values are the tenant's and never written into a manifest.
15122
15140
  */
15123
- secretInputs: external_exports.array(SecretInputV2Schema).max(MAX_CONFIGURATION_CHILDREN).optional()
15141
+ secretInputs: external_exports.array(SecretInputV2Schema).max(MAX_CONFIGURATION_CHILDREN).optional(),
15142
+ /**
15143
+ * The ordinary settings the tenant answers, BY CANONICAL PATH —
15144
+ * `/administrator/email`. See the v1 field; only the spelling differs.
15145
+ */
15146
+ tenantInputs: external_exports.array(TenantInputV2Schema).max(MAX_TENANT_INPUTS).optional()
15124
15147
  });
15125
15148
 
15126
15149
  // ../product-contracts/dist/portfolio.js
@@ -16473,7 +16496,7 @@ function looksLikeReference(value) {
16473
16496
  }
16474
16497
 
16475
16498
  // ../product-contracts/dist/dependency-binding.js
16476
- var DependencyBindingSelectorSchema = external_exports.object({
16499
+ var DependencyBindingConnectSchema = external_exports.object({
16477
16500
  /** The providing Resource's `metadata.key`. */
16478
16501
  resourceKey: ResourceKeySchema,
16479
16502
  /**
@@ -16492,6 +16515,11 @@ var DependencyBindingSelectorSchema = external_exports.object({
16492
16515
  */
16493
16516
  projectId: external_exports.string().min(1).max(64).optional()
16494
16517
  }).strict();
16518
+ var DependencyBindingDisconnectSchema = external_exports.object({ state: external_exports.literal("disconnected") }).strict();
16519
+ var DependencyBindingSelectorSchema = external_exports.union([
16520
+ DependencyBindingConnectSchema,
16521
+ DependencyBindingDisconnectSchema
16522
+ ]);
16495
16523
  var DependencyBindingsSchema = external_exports.record(ServiceBindingKeySchema, DependencyBindingSelectorSchema);
16496
16524
 
16497
16525
  // ../product-contracts/dist/product-instance-template.schema.js
@@ -16593,10 +16621,17 @@ var ProductInstanceTemplateSchema = external_exports.object({
16593
16621
  * schema; this names another Resource, and the create envelope made the
16594
16622
  * identical split for the identical reason.
16595
16623
  *
16624
+ * THREE MEANINGS, AND ONLY ONE OF THEM RELEASES ANYTHING:
16625
+ *
16626
+ * key omitted leave that connection alone
16627
+ * { resourceKey, … } connect to (or keep) this provider
16628
+ * { state: disconnected } release the connection it holds
16629
+ *
16596
16630
  * OPTIONAL, PERMANENTLY. Every manifest written before this field
16597
16631
  * existed omits it, and absence has to keep meaning "leave the
16598
16632
  * connections alone" — otherwise re-applying an unchanged file would
16599
- * release a binding somebody made in the portal.
16633
+ * release a binding somebody made in the portal. A release is
16634
+ * therefore never inferred from a missing key; it is stated.
16600
16635
  *
16601
16636
  * The keys the platform will ACCEPT here are narrower than the shape:
16602
16637
  * only a dependency whose interface the registry calls
@@ -19227,6 +19262,18 @@ function printPlan(plan, json) {
19227
19262
  }
19228
19263
  }
19229
19264
  }
19265
+ const disconnects = plan.dependencyDisconnects ?? [];
19266
+ if (disconnects.length > 0) {
19267
+ lines.push(" connections to disconnect:");
19268
+ for (const disconnect of disconnects) {
19269
+ lines.push(` ${disconnect.dependencyKey}`);
19270
+ if (disconnect.interfaceId !== void 0) {
19271
+ lines.push(
19272
+ ` interface: ${disconnect.interfaceId}${disconnect.interfaceVersion ? `/${disconnect.interfaceVersion}` : ""}`
19273
+ );
19274
+ }
19275
+ }
19276
+ }
19230
19277
  if ((plan.secretsToWrite ?? []).length > 0) {
19231
19278
  lines.push(` secrets to set: ${plan.secretsToWrite.join(", ")}`);
19232
19279
  for (const line of secretWriteConsequence(plan)) lines.push(` ${line}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@varde-flyt/vfac",
3
- "version": "0.16.0",
3
+ "version": "0.18.0",
4
4
  "description": "Deploy and manage Varde Flyt Products from a manifest.",
5
5
  "repository": {
6
6
  "type": "git",