@varde-flyt/vfac 0.3.0 → 0.3.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 (2) hide show
  1. package/dist/vfac.mjs +84 -49
  2. package/package.json +1 -1
package/dist/vfac.mjs CHANGED
@@ -12673,6 +12673,58 @@ function matches(pattern, value) {
12673
12673
  }
12674
12674
  }
12675
12675
 
12676
+ // ../product-configuration/dist/resource-api-version.js
12677
+ var RESOURCE_API_VERSION = "resources.vardeflyt.no/v1";
12678
+ var PRODUCT_INSTANCE_KIND = "ProductInstance";
12679
+
12680
+ // ../product-configuration/dist/product-instance-example.js
12681
+ function isSecret(declaration) {
12682
+ return declaration.writeOnly === true || declaration["x-secret"] === true;
12683
+ }
12684
+ function seedConfiguration(configuration) {
12685
+ const properties = configuration?.properties ?? {};
12686
+ const required = new Set(configuration?.required ?? []);
12687
+ const values = {};
12688
+ const open = [];
12689
+ const requiredSecrets = [];
12690
+ for (const [name, declaration] of Object.entries(properties)) {
12691
+ if (isSecret(declaration)) {
12692
+ if (required.has(name))
12693
+ requiredSecrets.push(name);
12694
+ continue;
12695
+ }
12696
+ if (declaration.default !== void 0) {
12697
+ values[name] = declaration.default;
12698
+ continue;
12699
+ }
12700
+ if (required.has(name)) {
12701
+ values[name] = "";
12702
+ open.push({ name, type: declaration.type ?? null });
12703
+ }
12704
+ }
12705
+ return { values, open, requiredSecrets };
12706
+ }
12707
+ function productInstanceExample(input) {
12708
+ return {
12709
+ apiVersion: RESOURCE_API_VERSION,
12710
+ kind: PRODUCT_INSTANCE_KIND,
12711
+ // The key is spread rather than set, so an underivable one is ABSENT from
12712
+ // the document instead of present and empty.
12713
+ metadata: { name: input.name, ...input.key ? { key: input.key } : {} },
12714
+ spec: {
12715
+ productId: input.productId,
12716
+ productVersion: input.productVersion,
12717
+ region: input.region,
12718
+ profile: input.profile,
12719
+ configuration: input.configuration
12720
+ }
12721
+ };
12722
+ }
12723
+ function exampleResourceKey(name) {
12724
+ const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^[^a-z]+/, "").replace(/-+$/, "").slice(0, 63);
12725
+ return /^[a-z][a-z0-9-]{1,61}[a-z0-9]$/.test(slug) ? slug : "";
12726
+ }
12727
+
12676
12728
  // ../platform-schemas/dist/index.js
12677
12729
  var TENANT_KEY_REGEX = /^[a-z][a-z0-9-]{2,23}[a-z0-9]$/;
12678
12730
  var RESERVED_TENANT_KEYS = /* @__PURE__ */ new Set([
@@ -12763,10 +12815,8 @@ var OperationStatusSchema = external_exports.enum([
12763
12815
 
12764
12816
  // ../product-contracts/dist/api-version.js
12765
12817
  var PRODUCT_API_VERSION = "products.vardeflyt.no/v1";
12766
- var RESOURCE_API_VERSION = "resources.vardeflyt.no/v1";
12767
12818
  var PRODUCT_DEFINITION_KIND = "ProductDefinition";
12768
12819
  var DEPLOYMENT_BLUEPRINT_KIND = "DeploymentBlueprint";
12769
- var PRODUCT_INSTANCE_KIND = "ProductInstance";
12770
12820
  var ProductApiVersionSchema = external_exports.literal(PRODUCT_API_VERSION, {
12771
12821
  errorMap: () => ({
12772
12822
  message: `apiVersion must be "${PRODUCT_API_VERSION}". An unrecognized contract format is refused rather than ignored.`
@@ -15954,6 +16004,9 @@ async function runDoctor(parsed) {
15954
16004
  }
15955
16005
  const access = who.data.access;
15956
16006
  const projects = access.projects.map((project) => project.projectId);
16007
+ const mutating = access.projects.filter(
16008
+ (project) => project.permissions.some((permission) => !permission.endsWith(".read"))
16009
+ );
15957
16010
  checks.push(
15958
16011
  access.tenantWide ? {
15959
16012
  name: "Access",
@@ -15980,7 +16033,15 @@ async function runDoctor(parsed) {
15980
16033
  // members, other Tenants' resources. An agent read the warning,
15981
16034
  // ran the listing, watched it succeed, and learned to distrust the
15982
16035
  // line — which is the worst outcome for a diagnostic.
15983
- detail: `${projects.join(", ")} \u2014 and nothing organization-wide. You can deploy in those Projects, read their catalogue, and list them. Anything ACROSS the organization \u2014 a Project you hold nothing in, or its members \u2014 is refused.`
16036
+ // AND WHAT IT MAY DO THERE, which this also got wrong. "You can
16037
+ // deploy in those Projects" is false for a gate granted *Project
16038
+ // Read* — `project.read` and `resource.read`, no writes at all —
16039
+ // and `doctor` is the FIRST command an agent is told to run, so it
16040
+ // was the first sentence an observing agent read. An agent that
16041
+ // believes it can deploy and is then refused reports a platform
16042
+ // defect, which is precisely the outcome the guide now tells it not
16043
+ // to reach for. Scope and role are two questions; this answers both.
16044
+ detail: `${projects.join(", ")} \u2014 and nothing organization-wide. ` + (mutating.length > 0 ? "You can deploy in those Projects, read their catalogue, and list them. " : "Read-only there: catalogue, manifests, resources and operations. An apply or a lifecycle command is refused, and that is what was granted rather than a fault. ") + "Anything ACROSS the organization \u2014 a Project you hold nothing in, or its members \u2014 is refused."
15984
16045
  }
15985
16046
  );
15986
16047
  checks.push({
@@ -16159,13 +16220,13 @@ async function runGet(parsed) {
16159
16220
  const declared = Array.isArray(resource["outputDeclarations"]) ? resource["outputDeclarations"] : [];
16160
16221
  const declaredNames = declared.map((entry) => entry["key"]).filter((key2) => typeof key2 === "string");
16161
16222
  const names = [.../* @__PURE__ */ new Set([...Object.keys(outputs), ...declaredNames])].sort();
16162
- const isSecret = (name) => declared.find((entry) => entry["key"] === name)?.["secret"] === true;
16223
+ const isSecret2 = (name) => declared.find((entry) => entry["key"] === name)?.["secret"] === true;
16163
16224
  if (names.length > 0) {
16164
16225
  lines.push("", " Outputs");
16165
16226
  const width = Math.max(...names.map((name) => name.length));
16166
16227
  for (const name of names) {
16167
16228
  lines.push(
16168
- ` ${name.padEnd(width)} ${isSecret(name) ? "[secret]" : String(outputs[name] ?? "")}`
16229
+ ` ${name.padEnd(width)} ${isSecret2(name) ? "[secret]" : String(outputs[name] ?? "")}`
16169
16230
  );
16170
16231
  const meaning = declared.find((entry) => entry["key"] === name);
16171
16232
  const description = meaning?.["description"];
@@ -16586,7 +16647,7 @@ async function runManifestInit(parsed) {
16586
16647
  if (!result.ok) return printError(result.error, parsed.flags["output"] === "json");
16587
16648
  const product = result.data;
16588
16649
  const notes = [];
16589
- const key = parsed.flags["key"] ?? keyFrom(name);
16650
+ const key = parsed.flags["key"] ?? exampleResourceKey(name);
16590
16651
  if (!key) {
16591
16652
  notes.push(
16592
16653
  "metadata.key \u2014 a stable identity, 3 to 63 characters, lower case. Required before apply"
@@ -16652,43 +16713,24 @@ Product Deployment as Code v1 cannot express yet. Deploy it from the portal.
16652
16713
  offered.length === 0 ? `${product.productId} ${product.version} declares no service profile. Ask the publisher.` : `This Product offers several service profiles. Pass --profile <${offered.join("|")}>.`
16653
16714
  );
16654
16715
  }
16655
- const properties = product.configuration?.properties ?? {};
16656
- const required = product.configuration?.required ?? [];
16657
- const configuration = {};
16658
- for (const [property, declaration] of Object.entries(properties)) {
16659
- const secret = declaration.writeOnly === true || declaration["x-secret"] === true;
16660
- if (secret) {
16661
- if (required.includes(property)) {
16662
- notes.push(`--secret ${property}=env:VAR \u2014 required, and never written to this file`);
16663
- }
16664
- continue;
16665
- }
16666
- if (declaration.default !== void 0) {
16667
- configuration[property] = declaration.default;
16668
- continue;
16669
- }
16670
- if (required.includes(property)) {
16671
- configuration[property] = "";
16672
- notes.push(`spec.configuration.${property} \u2014 required${typeOf(declaration)}`);
16673
- }
16716
+ const seed = seedConfiguration(product.configuration);
16717
+ for (const property of seed.requiredSecrets) {
16718
+ notes.push(`--secret ${property}=env:VAR \u2014 required, and never written to this file`);
16674
16719
  }
16675
- const document = {
16676
- apiVersion: "resources.vardeflyt.no/v1",
16677
- kind: "ProductInstance",
16678
- // The key is spread rather than set, so an underivable one is ABSENT from
16679
- // the document instead of present and empty.
16680
- metadata: { name, ...key ? { key } : {} },
16681
- spec: {
16682
- productId: product.productId,
16683
- // PINNED, ALWAYS. The contract requires a version, and a document that
16684
- // tracked "newest" would redeploy a customer's resource because somebody
16685
- // else published.
16686
- productVersion: product.version,
16687
- region,
16688
- profile,
16689
- configuration
16690
- }
16691
- };
16720
+ for (const setting of seed.open) {
16721
+ notes.push(
16722
+ `spec.configuration.${setting.name} \u2014 required${setting.type ? ` (${setting.type})` : ""}`
16723
+ );
16724
+ }
16725
+ const document = productInstanceExample({
16726
+ name,
16727
+ ...key ? { key } : {},
16728
+ productId: product.productId,
16729
+ productVersion: product.version,
16730
+ region,
16731
+ profile,
16732
+ configuration: seed.values
16733
+ });
16692
16734
  const checked = ProductInstanceTemplateSchema.safeParse(document);
16693
16735
  if (!checked.success) {
16694
16736
  const where = checked.error.issues.map((issue) => ` ${issue.path.join(".") || "(document)"}: ${issue.message}`).join("\n");
@@ -16712,13 +16754,6 @@ function refuse(message) {
16712
16754
  `);
16713
16755
  return 1;
16714
16756
  }
16715
- function typeOf(declaration) {
16716
- return declaration.type ? ` (${declaration.type})` : "";
16717
- }
16718
- function keyFrom(name) {
16719
- const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^[^a-z]+/, "").replace(/-+$/, "").slice(0, 63);
16720
- return /^[a-z][a-z0-9-]{1,61}[a-z0-9]$/.test(slug) ? slug : "";
16721
- }
16722
16757
 
16723
16758
  // src/commands/guide.ts
16724
16759
  async function runGuide(parsed) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@varde-flyt/vfac",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Deploy and manage Varde Flyt Products from a manifest.",
5
5
  "repository": {
6
6
  "type": "git",