@intentius/chant-lexicon-terraform 0.124.54 → 0.124.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -4
- package/dist/ci-setup.d.ts +11 -0
- package/dist/ci-setup.d.ts.map +1 -0
- package/dist/ci-setup.js +41 -0
- package/dist/components/capability-plugin.d.ts +40 -6
- package/dist/components/capability-plugin.d.ts.map +1 -1
- package/dist/components/capability-plugin.js +174 -30
- package/dist/components/compose-group.d.ts +1 -1
- package/dist/components/compose-group.d.ts.map +1 -1
- package/dist/components/compose-group.js +1 -1
- package/dist/components/release-inputs.d.ts +29 -0
- package/dist/components/release-inputs.d.ts.map +1 -0
- package/dist/components/release-inputs.js +57 -0
- package/dist/components/trace-facts.d.ts +16 -0
- package/dist/components/trace-facts.d.ts.map +1 -0
- package/dist/components/trace-facts.js +19 -0
- package/dist/cone.d.ts +53 -0
- package/dist/cone.d.ts.map +1 -0
- package/dist/cone.js +119 -0
- package/dist/cost-hold.d.ts +27 -0
- package/dist/cost-hold.d.ts.map +1 -0
- package/dist/cost-hold.js +21 -0
- package/dist/drift-rows.d.ts +61 -0
- package/dist/drift-rows.d.ts.map +1 -0
- package/dist/drift-rows.js +108 -0
- package/dist/exit-codes.d.ts +55 -0
- package/dist/exit-codes.d.ts.map +1 -0
- package/dist/exit-codes.js +48 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/integrity.json +7 -4
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/audit-catalog.js +27 -3
- package/dist/lint/audit-lineage.d.ts +14 -2
- package/dist/lint/audit-lineage.d.ts.map +1 -1
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lint/post-synth/index.js +4 -0
- package/dist/lint/post-synth/record-store.d.ts +54 -0
- package/dist/lint/post-synth/record-store.d.ts.map +1 -0
- package/dist/lint/post-synth/record-store.js +104 -0
- package/dist/lint/post-synth/tf001.d.ts +2 -2
- package/dist/lint/post-synth/tf001.js +2 -2
- package/dist/lint/post-synth/tf024.d.ts +35 -11
- package/dist/lint/post-synth/tf024.d.ts.map +1 -1
- package/dist/lint/post-synth/tf024.js +92 -20
- package/dist/lint/post-synth/tf046.d.ts +64 -0
- package/dist/lint/post-synth/tf046.d.ts.map +1 -0
- package/dist/lint/post-synth/tf046.js +175 -0
- package/dist/lint/post-synth/tf047.d.ts +36 -0
- package/dist/lint/post-synth/tf047.d.ts.map +1 -0
- package/dist/lint/post-synth/tf047.js +94 -0
- package/dist/lock-rows.d.ts +36 -0
- package/dist/lock-rows.d.ts.map +1 -0
- package/dist/lock-rows.js +60 -0
- package/dist/lock-timeout.d.ts +22 -0
- package/dist/lock-timeout.d.ts.map +1 -0
- package/dist/lock-timeout.js +32 -0
- package/dist/manifest.json +1 -1
- package/dist/module-catalog/index.d.ts +69 -0
- package/dist/module-catalog/index.d.ts.map +1 -0
- package/dist/module-catalog/index.js +110 -0
- package/dist/module-catalog/refresh-cli.d.ts +4 -0
- package/dist/module-catalog/refresh-cli.d.ts.map +1 -0
- package/dist/module-catalog/refresh-cli.js +64 -0
- package/dist/module-catalog/static-tier.d.ts +6 -0
- package/dist/module-catalog/static-tier.d.ts.map +1 -0
- package/dist/module-catalog/static-tier.js +1650 -0
- package/dist/module-catalog/types.d.ts +65 -0
- package/dist/module-catalog/types.d.ts.map +1 -0
- package/dist/module-catalog/types.js +33 -0
- package/dist/okf/index.md +3 -1
- package/dist/okf/rules/TF024.md +2 -2
- package/dist/okf/rules/TF046.md +11 -0
- package/dist/okf/rules/TF047.md +11 -0
- package/dist/op/activities/terraform.d.ts +29 -0
- package/dist/op/activities/terraform.d.ts.map +1 -1
- package/dist/op/activities/terraform.js +19 -4
- package/dist/pin/consumers.d.ts +35 -0
- package/dist/pin/consumers.d.ts.map +1 -0
- package/dist/pin/consumers.js +70 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +5 -0
- package/dist/registry/address.d.ts +63 -0
- package/dist/registry/address.d.ts.map +1 -0
- package/dist/registry/address.js +65 -0
- package/dist/registry/catalog-read.d.ts +14 -0
- package/dist/registry/catalog-read.d.ts.map +1 -0
- package/dist/registry/catalog-read.js +58 -0
- package/dist/registry/deprecated.d.ts +37 -0
- package/dist/registry/deprecated.d.ts.map +1 -0
- package/dist/registry/deprecated.js +43 -0
- package/dist/registry/docs.d.ts +96 -0
- package/dist/registry/docs.d.ts.map +1 -0
- package/dist/registry/docs.js +210 -0
- package/dist/registry/edge/edge.d.ts +46 -0
- package/dist/registry/edge/edge.d.ts.map +1 -0
- package/dist/registry/edge/edge.js +194 -0
- package/dist/registry/edge/index.d.ts +11 -0
- package/dist/registry/edge/index.d.ts.map +1 -0
- package/dist/registry/edge/index.js +10 -0
- package/dist/registry/edge/lambda-edge.d.ts +76 -0
- package/dist/registry/edge/lambda-edge.d.ts.map +1 -0
- package/dist/registry/edge/lambda-edge.js +95 -0
- package/dist/registry/edge/sigv4-web.d.ts +33 -0
- package/dist/registry/edge/sigv4-web.d.ts.map +1 -0
- package/dist/registry/edge/sigv4-web.js +56 -0
- package/dist/registry/edge/token-source.d.ts +73 -0
- package/dist/registry/edge/token-source.d.ts.map +1 -0
- package/dist/registry/edge/token-source.js +115 -0
- package/dist/registry/edge/worker.d.ts +45 -0
- package/dist/registry/edge/worker.d.ts.map +1 -0
- package/dist/registry/edge/worker.js +26 -0
- package/dist/registry/handler.d.ts +25 -0
- package/dist/registry/handler.d.ts.map +1 -0
- package/dist/registry/handler.js +75 -0
- package/dist/registry/headers-file.d.ts +21 -0
- package/dist/registry/headers-file.d.ts.map +1 -0
- package/dist/registry/headers-file.js +57 -0
- package/dist/registry/index.d.ts +31 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +30 -0
- package/dist/registry/mirror-paths.d.ts +9 -0
- package/dist/registry/mirror-paths.d.ts.map +1 -0
- package/dist/registry/mirror-paths.js +8 -0
- package/dist/registry/mirror.d.ts +130 -0
- package/dist/registry/mirror.d.ts.map +1 -0
- package/dist/registry/mirror.js +319 -0
- package/dist/registry/proxies.d.ts +73 -0
- package/dist/registry/proxies.d.ts.map +1 -0
- package/dist/registry/proxies.js +111 -0
- package/dist/registry/reader.d.ts +57 -0
- package/dist/registry/reader.d.ts.map +1 -0
- package/dist/registry/reader.js +198 -0
- package/dist/registry/semver.d.ts +16 -0
- package/dist/registry/semver.d.ts.map +1 -0
- package/dist/registry/semver.js +24 -0
- package/dist/registry/serve.d.ts +24 -0
- package/dist/registry/serve.d.ts.map +1 -0
- package/dist/registry/serve.js +64 -0
- package/dist/registry/source-forms.d.ts +79 -0
- package/dist/registry/source-forms.d.ts.map +1 -0
- package/dist/registry/source-forms.js +202 -0
- package/dist/registry/targets/artifactory.d.ts +28 -0
- package/dist/registry/targets/artifactory.d.ts.map +1 -0
- package/dist/registry/targets/artifactory.js +206 -0
- package/dist/registry/targets/fake-artifactory.d.ts +30 -0
- package/dist/registry/targets/fake-artifactory.d.ts.map +1 -0
- package/dist/registry/targets/fake-artifactory.js +142 -0
- package/dist/registry/targets/fake-gitlab.d.ts +39 -0
- package/dist/registry/targets/fake-gitlab.d.ts.map +1 -0
- package/dist/registry/targets/fake-gitlab.js +136 -0
- package/dist/registry/targets/fake-hcp.d.ts +50 -0
- package/dist/registry/targets/fake-hcp.d.ts.map +1 -0
- package/dist/registry/targets/fake-hcp.js +202 -0
- package/dist/registry/targets/fake-http.d.ts +31 -0
- package/dist/registry/targets/fake-http.d.ts.map +1 -0
- package/dist/registry/targets/fake-http.js +87 -0
- package/dist/registry/targets/fake-nexus.d.ts +27 -0
- package/dist/registry/targets/fake-nexus.d.ts.map +1 -0
- package/dist/registry/targets/fake-nexus.js +137 -0
- package/dist/registry/targets/gitlab.d.ts +53 -0
- package/dist/registry/targets/gitlab.d.ts.map +1 -0
- package/dist/registry/targets/gitlab.js +187 -0
- package/dist/registry/targets/hcp.d.ts +29 -0
- package/dist/registry/targets/hcp.d.ts.map +1 -0
- package/dist/registry/targets/hcp.js +310 -0
- package/dist/registry/targets/hosted.d.ts +62 -0
- package/dist/registry/targets/hosted.d.ts.map +1 -0
- package/dist/registry/targets/hosted.js +74 -0
- package/dist/registry/targets/index.d.ts +18 -0
- package/dist/registry/targets/index.d.ts.map +1 -0
- package/dist/registry/targets/index.js +17 -0
- package/dist/registry/targets/module-target.d.ts +68 -0
- package/dist/registry/targets/module-target.d.ts.map +1 -0
- package/dist/registry/targets/module-target.js +63 -0
- package/dist/registry/targets/nexus.d.ts +43 -0
- package/dist/registry/targets/nexus.d.ts.map +1 -0
- package/dist/registry/targets/nexus.js +250 -0
- package/dist/registry/targets/zip.d.ts +55 -0
- package/dist/registry/targets/zip.d.ts.map +1 -0
- package/dist/registry/targets/zip.js +278 -0
- package/dist/registry/writer.d.ts +101 -0
- package/dist/registry/writer.d.ts.map +1 -0
- package/dist/registry/writer.js +109 -0
- package/dist/rules/record-store.ts +122 -0
- package/dist/rules/tf001.ts +2 -2
- package/dist/rules/tf024.ts +105 -22
- package/dist/rules/tf046.ts +204 -0
- package/dist/rules/tf047.ts +114 -0
- package/dist/state/azure-blob.d.ts +8 -67
- package/dist/state/azure-blob.d.ts.map +1 -1
- package/dist/state/azure-blob.js +6 -199
- package/dist/state/gcs.d.ts +7 -61
- package/dist/state/gcs.d.ts.map +1 -1
- package/dist/state/gcs.js +5 -222
- package/dist/state/object-store.d.ts +4 -33
- package/dist/state/object-store.d.ts.map +1 -1
- package/dist/state/object-store.js +4 -17
- package/dist/state/s3.d.ts +11 -58
- package/dist/state/s3.d.ts.map +1 -1
- package/dist/state/s3.js +10 -161
- package/dist/state/store.js +2 -2
- package/dist/state-ops.d.ts +255 -0
- package/dist/state-ops.d.ts.map +1 -0
- package/dist/state-ops.js +305 -0
- package/dist/state-reads.d.ts +57 -0
- package/dist/state-reads.d.ts.map +1 -0
- package/dist/state-reads.js +104 -0
- package/dist/used-sources.d.ts +54 -0
- package/dist/used-sources.d.ts.map +1 -0
- package/dist/used-sources.js +157 -0
- package/package.json +19 -3
- package/src/__fixtures__/drift-record/README.md +5 -0
- package/src/__fixtures__/drift-rows/live-root-plan.json +70 -0
- package/src/__fixtures__/drift-rows/refresh-plan.json +61 -0
- package/src/__fixtures__/used-sources/root/extra.tofu +1 -0
- package/src/__fixtures__/used-sources/root/main.tf +31 -0
- package/src/__fixtures__/used-sources/root/modules/net/main.tf +14 -0
- package/src/__fixtures__/used-sources/root/modules/net/versions.tf.json +7 -0
- package/src/__fixtures__/used-sources/unit/terragrunt.hcl +3 -0
- package/src/ci-setup.ts +42 -0
- package/src/components/capability-plugin.test.ts +26 -14
- package/src/components/capability-plugin.ts +185 -31
- package/src/components/compose-group.ts +1 -1
- package/src/components/pr-loop-compose.tofu.e2e.test.ts +1 -1
- package/src/components/pr-loop.tofu.test.ts +1 -1
- package/src/components/release-inputs.ts +69 -0
- package/src/components/trace-facts.test.ts +21 -0
- package/src/components/trace-facts.ts +27 -0
- package/src/cone.ts +138 -0
- package/src/cost-hold.ts +29 -0
- package/src/describe-resources.engine-facts.test.ts +2 -3
- package/src/drift-rows.test.ts +135 -0
- package/src/drift-rows.ts +137 -0
- package/src/exit-codes.test.ts +23 -0
- package/src/exit-codes.ts +59 -0
- package/src/index.ts +3 -0
- package/src/lint/audit-catalog.ts +29 -3
- package/src/lint/audit-lineage.ts +14 -2
- package/src/lint/post-synth/fixtures/TF024/live-with-record-store/main.tf +18 -0
- package/src/lint/post-synth/fixtures/TF024/sidecar-with-backend/estate.chdf.hcl +1 -0
- package/src/lint/post-synth/fixtures/TF024/sidecar-with-backend/main.tf +10 -0
- package/src/lint/post-synth/fixtures/TF046/azurerm-no-resource-group/main.tf +15 -0
- package/src/lint/post-synth/fixtures/TF046/backend-config-only/main.tf +14 -0
- package/src/lint/post-synth/fixtures/TF046/derived-s3/backend.tf +7 -0
- package/src/lint/post-synth/fixtures/TF046/derived-s3/main.tf +9 -0
- package/src/lint/post-synth/fixtures/TF046/explicit-local/main.tf +16 -0
- package/src/lint/post-synth/fixtures/TF046/explicit-s3-beside-local-backend/main.tf +15 -0
- package/src/lint/post-synth/fixtures/TF046/implied-local/main.tf +9 -0
- package/src/lint/post-synth/fixtures/TF046/sidecar-implied/estate.chdf.hcl +1 -0
- package/src/lint/post-synth/fixtures/TF046/sidecar-implied/main.tf +5 -0
- package/src/lint/post-synth/fixtures/TF047/clear/main.tf +12 -0
- package/src/lint/post-synth/fixtures/TF047/refused/main.tf +12 -0
- package/src/lint/post-synth/fixtures/TF047/unknown-module/main.tf +12 -0
- package/src/lint/post-synth/fixtures/TF047/unknown-version/main.tf +12 -0
- package/src/lint/post-synth/index.ts +4 -0
- package/src/lint/post-synth/record-store.ts +122 -0
- package/src/lint/post-synth/tf001.ts +2 -2
- package/src/lint/post-synth/tf024.test.ts +26 -5
- package/src/lint/post-synth/tf024.ts +105 -22
- package/src/lint/post-synth/tf046.test.ts +137 -0
- package/src/lint/post-synth/tf046.ts +204 -0
- package/src/lint/post-synth/tf047.test.ts +56 -0
- package/src/lint/post-synth/tf047.ts +114 -0
- package/src/lock-rows.test.ts +70 -0
- package/src/lock-rows.ts +82 -0
- package/src/lock-timeout.ts +34 -0
- package/src/module-catalog/index.test.ts +93 -0
- package/src/module-catalog/index.ts +159 -0
- package/src/module-catalog/refresh-cli.ts +68 -0
- package/src/module-catalog/static-tier.ts +1659 -0
- package/src/module-catalog/types.ts +88 -0
- package/src/op/activities/terraform.ts +46 -5
- package/src/op-wave-step.test.ts +109 -0
- package/src/pin/__fixtures__/consumers/live/dev/main.tf +4 -0
- package/src/pin/__fixtures__/consumers/live/prod/main.tf +21 -0
- package/src/pin/__fixtures__/consumers/units/vpc/terragrunt.hcl +7 -0
- package/src/pin/consumers.test.ts +57 -0
- package/src/pin/consumers.ts +95 -0
- package/src/plugin.ts +6 -0
- package/src/registry/__fixtures__/docs/documented/.terraform-docs.yml +3 -0
- package/src/registry/__fixtures__/docs/documented/README.md +1 -0
- package/src/registry/__fixtures__/docs/documented/main.tf +8 -0
- package/src/registry/__fixtures__/docs/json/main.tf.json +14 -0
- package/src/registry/__fixtures__/docs/vpc/README.md +3 -0
- package/src/registry/__fixtures__/docs/vpc/examples/complete/main.tf +5 -0
- package/src/registry/__fixtures__/docs/vpc/examples/simple/README.md +1 -0
- package/src/registry/__fixtures__/docs/vpc/examples/simple/main.tf +4 -0
- package/src/registry/__fixtures__/docs/vpc/main.tf +14 -0
- package/src/registry/__fixtures__/docs/vpc/outputs.tf +9 -0
- package/src/registry/__fixtures__/docs/vpc/variables.tf +16 -0
- package/src/registry/__fixtures__/proxied-root/main.tf +12 -0
- package/src/registry/address.ts +100 -0
- package/src/registry/catalog-read.ts +56 -0
- package/src/registry/deprecated.test.ts +139 -0
- package/src/registry/deprecated.ts +76 -0
- package/src/registry/docs.test.ts +140 -0
- package/src/registry/docs.ts +299 -0
- package/src/registry/edge/bundle.test.ts +50 -0
- package/src/registry/edge/edge.test.ts +176 -0
- package/src/registry/edge/edge.ts +211 -0
- package/src/registry/edge/index.ts +10 -0
- package/src/registry/edge/lambda-edge.ts +127 -0
- package/src/registry/edge/sigv4-web.ts +79 -0
- package/src/registry/edge/token-source.test.ts +258 -0
- package/src/registry/edge/token-source.ts +169 -0
- package/src/registry/edge/worker.ts +61 -0
- package/src/registry/handler.ts +89 -0
- package/src/registry/headers-file.ts +61 -0
- package/src/registry/index.ts +31 -0
- package/src/registry/mirror-paths.ts +10 -0
- package/src/registry/mirror.test.ts +198 -0
- package/src/registry/mirror.ts +395 -0
- package/src/registry/proxies.test.ts +153 -0
- package/src/registry/proxies.ts +132 -0
- package/src/registry/reader.ts +225 -0
- package/src/registry/registry.test.ts +262 -0
- package/src/registry/semver.ts +34 -0
- package/src/registry/serve.ts +82 -0
- package/src/registry/source-forms.test.ts +87 -0
- package/src/registry/source-forms.ts +216 -0
- package/src/registry/targets/artifactory.test.ts +248 -0
- package/src/registry/targets/artifactory.ts +257 -0
- package/src/registry/targets/fake-artifactory.ts +158 -0
- package/src/registry/targets/fake-gitlab.ts +161 -0
- package/src/registry/targets/fake-hcp.ts +233 -0
- package/src/registry/targets/fake-http.ts +97 -0
- package/src/registry/targets/fake-nexus.ts +152 -0
- package/src/registry/targets/gitlab.test.ts +208 -0
- package/src/registry/targets/gitlab.ts +223 -0
- package/src/registry/targets/hcp.test.ts +230 -0
- package/src/registry/targets/hcp.ts +360 -0
- package/src/registry/targets/hosted.ts +117 -0
- package/src/registry/targets/index.ts +17 -0
- package/src/registry/targets/module-target.ts +131 -0
- package/src/registry/targets/nexus.test.ts +220 -0
- package/src/registry/targets/nexus.ts +314 -0
- package/src/registry/targets/zip.test.ts +65 -0
- package/src/registry/targets/zip.ts +287 -0
- package/src/registry/writer.ts +155 -0
- package/src/state/azure-blob.ts +6 -221
- package/src/state/gcs.ts +5 -252
- package/src/state/object-store.ts +4 -32
- package/src/state/s3.ts +10 -182
- package/src/state/store.ts +2 -2
- package/src/state-ops.test.ts +182 -0
- package/src/state-ops.ts +462 -0
- package/src/state-reads.ts +116 -0
- package/src/used-sources.test.ts +75 -0
- package/src/used-sources.ts +204 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a module sits in a Terraform module registry, and how a registry
|
|
3
|
+
* source (`<host>/<namespace>/<name>/<system>`) is split into those parts.
|
|
4
|
+
* Lifted from terragucci's `publish/registry.ts` (18f6431595) for chant#4565.
|
|
5
|
+
* No Node imports, so an edge function can carry it.
|
|
6
|
+
*/
|
|
7
|
+
export const DISCOVERY = ".well-known/terraform.json";
|
|
8
|
+
export const MODULES_V1 = "/v1/modules/";
|
|
9
|
+
export const DEFAULT_SYSTEM = "generic";
|
|
10
|
+
/** A registry namespace or module name: letters, digits, `-` and `_`, starting and ending on a letter or digit. */
|
|
11
|
+
export const REGISTRY_NAME = /^[0-9A-Za-z](?:[0-9A-Za-z_-]{0,62}[0-9A-Za-z])?$/;
|
|
12
|
+
/** A registry module's system (its target provider): lower-case letters and digits. */
|
|
13
|
+
export const REGISTRY_SYSTEM = /^[0-9a-z]{1,64}$/;
|
|
14
|
+
/** A module name the protocol cannot carry. */
|
|
15
|
+
export class RegistryNameError extends Error {
|
|
16
|
+
constructor(message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = "RegistryNameError";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** The host a registry's sources name: its URL without the scheme, lower-cased. */
|
|
22
|
+
export const registryHost = (reg) => new URL(reg.url).host.toLowerCase();
|
|
23
|
+
/** The namespace a module's path goes under: the longest `namespaces` prefix it starts with, else `namespace`. */
|
|
24
|
+
export function namespaceFor(reg, rel) {
|
|
25
|
+
let best;
|
|
26
|
+
for (const [prefix, ns] of Object.entries(reg.namespaces ?? {})) {
|
|
27
|
+
const p = prefix.replace(/^\.\//, "");
|
|
28
|
+
const dir = p.endsWith("/") ? p : `${p}/`;
|
|
29
|
+
if ((rel.startsWith(dir) || rel === p.replace(/\/+$/, "")) && (!best || p.length > best.prefix.length))
|
|
30
|
+
best = { prefix: p, ns };
|
|
31
|
+
}
|
|
32
|
+
return best?.ns ?? reg.namespace;
|
|
33
|
+
}
|
|
34
|
+
/** A module's place in the registry: its namespace, its name (the last segment of its path) and its system. */
|
|
35
|
+
export function registryAddress(reg, rel) {
|
|
36
|
+
const namespace = namespaceFor(reg, rel);
|
|
37
|
+
const name = rel.replace(/\/+$/, "").split("/").pop() ?? "";
|
|
38
|
+
const system = reg.system ?? DEFAULT_SYSTEM;
|
|
39
|
+
if (!REGISTRY_NAME.test(name))
|
|
40
|
+
throw new RegistryNameError(`${rel}: a registry module's name is letters, digits, - and _, and ${JSON.stringify(name)} is not; rename the directory`);
|
|
41
|
+
return { namespace, name, system, source: `${registryHost(reg)}/${namespace}/${name}/${system}`, path: `v1/modules/${namespace}/${name}/${system}` };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A registry source split into its host and module, or undefined when the
|
|
45
|
+
* source is not one: `host/ns/name/system`, with an optional `//subdir` and
|
|
46
|
+
* query. The host must look like one (a dot, a port, or `localhost`), so a
|
|
47
|
+
* three-part public registry source with no host is not taken for one.
|
|
48
|
+
* `hosts` names bare hosts a CLI `host` block declares (a Nexus repository's
|
|
49
|
+
* name, chant#4605), which are taken as hosts too.
|
|
50
|
+
*/
|
|
51
|
+
export function parseRegistrySource(source, hosts = []) {
|
|
52
|
+
const base = source.split("?")[0].split("//")[0];
|
|
53
|
+
const m = /^([a-z0-9.-]+(?::\d+)?)\/([^/]+)\/([^/]+)\/([^/]+)$/i.exec(base);
|
|
54
|
+
const known = (h) => hosts.some((k) => k.toLowerCase() === h.toLowerCase());
|
|
55
|
+
if (!m || (!m[1].includes(".") && !m[1].includes(":") && m[1] !== "localhost" && !known(m[1])))
|
|
56
|
+
return undefined;
|
|
57
|
+
return { host: m[1].toLowerCase(), namespace: m[2], name: m[3], system: m[4] };
|
|
58
|
+
}
|
|
59
|
+
/** A key under a registry's prefix in its bucket or directory. */
|
|
60
|
+
export const keyFor = (prefix, path) => {
|
|
61
|
+
const p = (prefix ?? "").replace(/^\/+|\/+$/g, "");
|
|
62
|
+
return p ? `${p}/${path}` : path;
|
|
63
|
+
};
|
|
64
|
+
/** The tarball's name beside its version. */
|
|
65
|
+
export const tarballName = (addr, version) => `${addr.name}-${version}.tar.gz`;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ReleaseCatalog } from "@intentius/chant/lifecycle/release-catalog";
|
|
2
|
+
import { type RegistryReadOptions } from "./reader.js";
|
|
3
|
+
/** Where the catalog sits under a registry's root. */
|
|
4
|
+
export declare const CATALOG_PATH = "catalog/index.json";
|
|
5
|
+
/** The catalog URL for a registry: a host (`modules.acme.dev`) or a base URL (`https://modules.acme.dev/` or `https://cdn.acme.dev/registry/`). */
|
|
6
|
+
export declare function catalogUrl(registry: string, scheme?: "https" | "http"): string;
|
|
7
|
+
/**
|
|
8
|
+
* Fetch and validate a registry's catalog. Throws `RegistryError` when the
|
|
9
|
+
* request fails or is refused (naming where a token goes, never a value),
|
|
10
|
+
* and when the answer is not a valid catalog. A registry with no catalog
|
|
11
|
+
* (404) gives null.
|
|
12
|
+
*/
|
|
13
|
+
export declare function readRegistryCatalog(registry: string, options?: RegistryReadOptions): Promise<ReleaseCatalog | null>;
|
|
14
|
+
//# sourceMappingURL=catalog-read.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog-read.d.ts","sourceRoot":"","sources":["../../src/registry/catalog-read.ts"],"names":[],"mappings":"AAaA,OAAO,EAA4C,KAAK,cAAc,EAAE,MAAM,4CAA4C,CAAC;AAC3H,OAAO,EAAgD,KAAK,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAElG,sDAAsD;AACtD,eAAO,MAAM,YAAY,uBAAuB,CAAC;AAEjD,mJAAmJ;AACnJ,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,GAAG,MAAgB,GAAG,MAAM,CAGvF;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAwB7H"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading a registry's release catalog (chant#4599, terragucci#1289 bundle
|
|
3
|
+
* B): `catalog/index.json` beside the registry's files, the document
|
|
4
|
+
* `renderCatalogSite` writes (`@intentius/chant/lifecycle/release-catalog-site`).
|
|
5
|
+
*
|
|
6
|
+
* The request carries the token the CLIs would send the host (chant#4563,
|
|
7
|
+
* `readToken` in ./reader.ts), so a registry behind the edge function's
|
|
8
|
+
* `auth: token` check answers it. Per ruling B (2026-10-27) this reader is
|
|
9
|
+
* how a token-protected catalog is read; the edge function gains no cookie
|
|
10
|
+
* and no open path, and a browser reaches the catalog over the
|
|
11
|
+
* network-private path.
|
|
12
|
+
*/
|
|
13
|
+
import { credentialsHost, tokenVariable } from "@intentius/chant/terraform/credentials";
|
|
14
|
+
import { parseReleaseCatalog, ReleaseCatalogError } from "@intentius/chant/lifecycle/release-catalog";
|
|
15
|
+
import { readToken, RegistryError } from "./reader.js";
|
|
16
|
+
/** Where the catalog sits under a registry's root. */
|
|
17
|
+
export const CATALOG_PATH = "catalog/index.json";
|
|
18
|
+
/** The catalog URL for a registry: a host (`modules.acme.dev`) or a base URL (`https://modules.acme.dev/` or `https://cdn.acme.dev/registry/`). */
|
|
19
|
+
export function catalogUrl(registry, scheme = "https") {
|
|
20
|
+
const base = registry.includes("://") ? registry : `${scheme}://${registry}`;
|
|
21
|
+
return new URL(CATALOG_PATH, base.endsWith("/") ? base : `${base}/`).toString();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Fetch and validate a registry's catalog. Throws `RegistryError` when the
|
|
25
|
+
* request fails or is refused (naming where a token goes, never a value),
|
|
26
|
+
* and when the answer is not a valid catalog. A registry with no catalog
|
|
27
|
+
* (404) gives null.
|
|
28
|
+
*/
|
|
29
|
+
export async function readRegistryCatalog(registry, options = {}) {
|
|
30
|
+
const url = catalogUrl(registry, options.scheme);
|
|
31
|
+
const host = new URL(url).host.toLowerCase();
|
|
32
|
+
const token = readToken(host, options);
|
|
33
|
+
const fetchFn = options.fetch ?? ((u, init) => fetch(u, init));
|
|
34
|
+
let res;
|
|
35
|
+
try {
|
|
36
|
+
res = await fetchFn(url, { method: "GET", redirect: "follow", headers: { Accept: "application/json", ...(token ? { Authorization: `Bearer ${token}` } : {}) } });
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
throw new RegistryError(`${url}: ${e.message}`);
|
|
40
|
+
}
|
|
41
|
+
if (res.status === 404)
|
|
42
|
+
return null;
|
|
43
|
+
if (res.status === 401 || res.status === 403) {
|
|
44
|
+
const variable = tokenVariable(host);
|
|
45
|
+
const where = variable ? `${variable} or a credentials "${credentialsHost(host)}" block` : `a credentials "${credentialsHost(host) ?? host}" block in credentials.tfrc.json`;
|
|
46
|
+
throw new RegistryError(token ? `${url} answered ${res.status}: the registry refused the token for ${host}` : `${url} answered ${res.status}: the catalog wants a token; set ${where}`);
|
|
47
|
+
}
|
|
48
|
+
if (!res.ok)
|
|
49
|
+
throw new RegistryError(`${url} answered ${res.status}`);
|
|
50
|
+
try {
|
|
51
|
+
return parseReleaseCatalog(await res.text());
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
if (e instanceof ReleaseCatalogError)
|
|
55
|
+
throw new RegistryError(`${url} is not a release catalog: ${e.problems.slice(0, 3).join("; ")}`);
|
|
56
|
+
throw e;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The deprecated-version check (chant#4599, terragucci#1289 bundle B): each
|
|
3
|
+
* module call (`moduleConsumers`, ../pin/consumers.ts) against the release
|
|
4
|
+
* catalog (`readRegistryCatalog`, ./catalog-read.ts). A call pinned to a
|
|
5
|
+
* deprecated version gets a warning, and one pinned to a yanked version a
|
|
6
|
+
* refusal, each naming the reason and the version to move to when the
|
|
7
|
+
* catalog has one.
|
|
8
|
+
*
|
|
9
|
+
* A catalog component is matched by the module address its `terraform`
|
|
10
|
+
* fields name (`terraformCatalogFields`, ./docs.ts), else by its name, both
|
|
11
|
+
* normalised as the scan normalises a call's source. A call's pin is matched
|
|
12
|
+
* to a version by the version (a leading `v` ignored) or by the digest. A
|
|
13
|
+
* call with no pin, or one naming a module or version the catalog does not
|
|
14
|
+
* list, is left alone: this check speaks only for what the catalog says.
|
|
15
|
+
*/
|
|
16
|
+
import type { ReleaseCatalog } from "@intentius/chant/lifecycle/release-catalog";
|
|
17
|
+
import { type ModuleConsumer } from "../pin/consumers.js";
|
|
18
|
+
/** One call on a version the catalog marks. */
|
|
19
|
+
export interface DeprecatedPin {
|
|
20
|
+
consumer: ModuleConsumer;
|
|
21
|
+
component: string;
|
|
22
|
+
/** The version as the catalog lists it (its version, or its digest when it has none). */
|
|
23
|
+
version: string;
|
|
24
|
+
status: "deprecated" | "yanked";
|
|
25
|
+
reason: string | null;
|
|
26
|
+
replacement: string | null;
|
|
27
|
+
/** One line for a person: where, what, why, and what to move to. */
|
|
28
|
+
message: string;
|
|
29
|
+
}
|
|
30
|
+
/** The check's result: warnings for deprecated pins, refusals for yanked ones. */
|
|
31
|
+
export interface DeprecatedPinsResult {
|
|
32
|
+
warnings: DeprecatedPin[];
|
|
33
|
+
refusals: DeprecatedPin[];
|
|
34
|
+
}
|
|
35
|
+
/** Check every call against the catalog. */
|
|
36
|
+
export declare function deprecatedPins(consumers: readonly ModuleConsumer[], catalog: ReleaseCatalog): DeprecatedPinsResult;
|
|
37
|
+
//# sourceMappingURL=deprecated.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deprecated.d.ts","sourceRoot":"","sources":["../../src/registry/deprecated.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAoC,cAAc,EAAE,MAAM,4CAA4C,CAAC;AACnH,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAE9E,+CAA+C;AAC/C,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,cAAc,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,yFAAyF;IACzF,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,YAAY,GAAG,QAAQ,CAAC;IAChC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,kFAAkF;AAClF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAaD,4CAA4C;AAC5C,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,cAAc,EAAE,EAAE,OAAO,EAAE,cAAc,GAAG,oBAAoB,CA0BlH"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { normalizeModuleSource } from "../pin/consumers.js";
|
|
2
|
+
const bare = (v) => v.replace(/^v(?=\d)/, "");
|
|
3
|
+
function addressOf(c) {
|
|
4
|
+
const tf = c.fields.terraform;
|
|
5
|
+
return normalizeModuleSource(typeof tf?.address === "string" ? tf.address : c.name);
|
|
6
|
+
}
|
|
7
|
+
function versionFor(c, pin) {
|
|
8
|
+
return c.versions.find((v) => (v.version !== null && bare(v.version) === bare(pin)) || v.digest === pin);
|
|
9
|
+
}
|
|
10
|
+
/** Check every call against the catalog. */
|
|
11
|
+
export function deprecatedPins(consumers, catalog) {
|
|
12
|
+
const byAddress = new Map();
|
|
13
|
+
for (const c of catalog.components)
|
|
14
|
+
byAddress.set(addressOf(c), c);
|
|
15
|
+
const out = { warnings: [], refusals: [] };
|
|
16
|
+
for (const consumer of consumers) {
|
|
17
|
+
if (consumer.pin === null)
|
|
18
|
+
continue;
|
|
19
|
+
const component = byAddress.get(consumer.module);
|
|
20
|
+
if (!component)
|
|
21
|
+
continue;
|
|
22
|
+
const v = versionFor(component, consumer.pin);
|
|
23
|
+
if (!v || v.status.state === "active")
|
|
24
|
+
continue;
|
|
25
|
+
const version = v.version ?? v.digest;
|
|
26
|
+
const said = [`${consumer.file} ${consumer.address} pins ${component.name} ${version}, which is ${v.status.state}`];
|
|
27
|
+
if (v.status.reason)
|
|
28
|
+
said.push(`: ${v.status.reason}`);
|
|
29
|
+
if (v.status.replacement)
|
|
30
|
+
said.push(`; move to ${v.status.replacement}`);
|
|
31
|
+
const finding = {
|
|
32
|
+
consumer,
|
|
33
|
+
component: component.name,
|
|
34
|
+
version,
|
|
35
|
+
status: v.status.state,
|
|
36
|
+
reason: v.status.reason,
|
|
37
|
+
replacement: v.status.replacement,
|
|
38
|
+
message: said.join(""),
|
|
39
|
+
};
|
|
40
|
+
(v.status.state === "yanked" ? out.refusals : out.warnings).push(finding);
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { Hcl2Json } from "@intentius/chant/terraform/hcl2json";
|
|
2
|
+
import type { CatalogSiteSections } from "@intentius/chant/lifecycle/release-catalog-site";
|
|
3
|
+
/** The terraform-docs config file names a module may carry, in the order they are looked for. */
|
|
4
|
+
export declare const TERRAFORM_DOCS_CONFIGS: readonly [".terraform-docs.yml", ".terraform-docs.yaml"];
|
|
5
|
+
/** One module input, a `variable` block. */
|
|
6
|
+
export interface ModuleInput {
|
|
7
|
+
name: string;
|
|
8
|
+
/** The type constraint as written, `string` or `map(object({...}))`, or null when none is declared. */
|
|
9
|
+
type: string | null;
|
|
10
|
+
/** True when the variable has no default. */
|
|
11
|
+
required: boolean;
|
|
12
|
+
/** The default as the parse reads it. Absent when required, and when the variable is sensitive. */
|
|
13
|
+
default?: unknown;
|
|
14
|
+
description: string | null;
|
|
15
|
+
sensitive: boolean;
|
|
16
|
+
/** The file that declares it. */
|
|
17
|
+
file: string;
|
|
18
|
+
}
|
|
19
|
+
/** One module output. */
|
|
20
|
+
export interface ModuleOutput {
|
|
21
|
+
name: string;
|
|
22
|
+
description: string | null;
|
|
23
|
+
sensitive: boolean;
|
|
24
|
+
file: string;
|
|
25
|
+
}
|
|
26
|
+
/** One entry of `required_providers`. */
|
|
27
|
+
export interface ModuleProvider {
|
|
28
|
+
name: string;
|
|
29
|
+
source: string | null;
|
|
30
|
+
version: string | null;
|
|
31
|
+
}
|
|
32
|
+
/** One directory under `examples/`. */
|
|
33
|
+
export interface ModuleExample {
|
|
34
|
+
name: string;
|
|
35
|
+
/** From the module's directory, `examples/<name>`. */
|
|
36
|
+
path: string;
|
|
37
|
+
readme: string | null;
|
|
38
|
+
/** Its configuration files, sorted. */
|
|
39
|
+
files: string[];
|
|
40
|
+
}
|
|
41
|
+
/** Everything a catalog page shows about one module version. */
|
|
42
|
+
export interface ModuleDocs {
|
|
43
|
+
readme: string | null;
|
|
44
|
+
inputs: ModuleInput[];
|
|
45
|
+
outputs: ModuleOutput[];
|
|
46
|
+
providers: ModuleProvider[];
|
|
47
|
+
requiredVersion: string | null;
|
|
48
|
+
examples: ModuleExample[];
|
|
49
|
+
/** `terraform-docs` when the body is terraform-docs's markdown, `hcl` when the page is built from the parse alone. */
|
|
50
|
+
docsFrom: "terraform-docs" | "hcl";
|
|
51
|
+
/** terraform-docs's output, when `docsFrom` is `terraform-docs`. */
|
|
52
|
+
markdown: string | null;
|
|
53
|
+
/** The terraform-docs config file found, from the module's directory. */
|
|
54
|
+
config: string | null;
|
|
55
|
+
/** Why the parse was used when a config file was found. */
|
|
56
|
+
note: string | null;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Runs terraform-docs in `dir` with `config` (a path from `dir`) and returns
|
|
60
|
+
* its markdown, or null when no terraform-docs binary is installed. Injected,
|
|
61
|
+
* so a test fakes it and a product chooses how the binary is found.
|
|
62
|
+
*/
|
|
63
|
+
export type TerraformDocsRunner = (args: {
|
|
64
|
+
dir: string;
|
|
65
|
+
config: string;
|
|
66
|
+
}) => Promise<string | null>;
|
|
67
|
+
export interface ModuleDocsOptions {
|
|
68
|
+
/** The HCL parser; loaded on first use when absent. */
|
|
69
|
+
parser?: Hcl2Json;
|
|
70
|
+
/** Runs terraform-docs when the module has a config file. Without one the parse is used. */
|
|
71
|
+
terraformDocs?: TerraformDocsRunner;
|
|
72
|
+
/** The binary the module is written for (`terraform`, `tofu`), which decides the files read. */
|
|
73
|
+
binary?: string;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Read one module's docs from `dir`. Throws what the HCL parse throws for a
|
|
77
|
+
* file it cannot read; a terraform-docs failure is never thrown, it falls
|
|
78
|
+
* back to the parse and says so in `note`.
|
|
79
|
+
*/
|
|
80
|
+
export declare function moduleDocs(dir: string, options?: ModuleDocsOptions): Promise<ModuleDocs>;
|
|
81
|
+
/**
|
|
82
|
+
* The release catalog's `terraform` fields for a module version: its
|
|
83
|
+
* address (`<host>/<namespace>/<name>/<system>`), the docs' structured
|
|
84
|
+
* parts, and the markdown when terraform-docs wrote it. Everything here is
|
|
85
|
+
* JSON, as the catalog's schema requires.
|
|
86
|
+
*/
|
|
87
|
+
export declare function terraformCatalogFields(docs: ModuleDocs, address: string): Record<string, unknown>;
|
|
88
|
+
/**
|
|
89
|
+
* The site sections (`renderCatalogSite`'s `sections`) for Terraform
|
|
90
|
+
* modules: usage, inputs, outputs, requirements, examples and the README or
|
|
91
|
+
* terraform-docs's markdown on each version page, read from the version's
|
|
92
|
+
* `terraform` fields, and the usage line on the component page from the
|
|
93
|
+
* component's. A component without `terraform` fields gets none.
|
|
94
|
+
*/
|
|
95
|
+
export declare function terraformCatalogSections(): CatalogSiteSections;
|
|
96
|
+
//# sourceMappingURL=docs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../src/registry/docs.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,KAAK,EAAsB,mBAAmB,EAAE,MAAM,iDAAiD,CAAC;AAG/G,iGAAiG;AACjG,eAAO,MAAM,sBAAsB,YAAI,qBAAqB,EAAE,sBAAsB,CAAU,CAAC;AAE/F,4CAA4C;AAC5C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,uGAAuG;IACvG,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,6CAA6C;IAC7C,QAAQ,EAAE,OAAO,CAAC;IAClB,mGAAmG;IACnG,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,yBAAyB;AACzB,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,yCAAyC;AACzC,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,uCAAuC;AACvC,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,uCAAuC;IACvC,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,gEAAgE;AAChE,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,sHAAsH;IACtH,QAAQ,EAAE,gBAAgB,GAAG,KAAK,CAAC;IACnC,oEAAoE;IACpE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,yEAAyE;IACzE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAEpG,MAAM,WAAW,iBAAiB;IAChC,uDAAuD;IACvD,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,4FAA4F;IAC5F,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,gGAAgG;IAChG,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAsED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,UAAU,CAAC,CAuClG;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAYjG;AA2CD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,IAAI,mBAAmB,CAW9D"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The module docs extractor (chant#4598, terragucci#1289 bundle B): what a
|
|
3
|
+
* module's catalog page shows, read from the module's own directory.
|
|
4
|
+
*
|
|
5
|
+
* {@link moduleDocs} returns the README, the inputs (type, default,
|
|
6
|
+
* description, sensitivity, whether one is required), the outputs, the
|
|
7
|
+
* required providers and Terraform version, and the `examples/` tree. The
|
|
8
|
+
* structured fields always come from chant's own HCL parse
|
|
9
|
+
* (`parseTerraformRootDir`, ../hcl/parse.ts), `.tf` and `.tf.json` alike.
|
|
10
|
+
*
|
|
11
|
+
* When the module carries a `.terraform-docs.yml` (or `.terraform-docs.yaml`)
|
|
12
|
+
* and the caller passes a terraform-docs runner, the runner is called with
|
|
13
|
+
* that file and its markdown is kept as the page's body, since the module's
|
|
14
|
+
* author chose that layout. Without the file, without a runner, or when the
|
|
15
|
+
* runner finds no binary or fails, the page is built from the parse alone.
|
|
16
|
+
* `docsFrom` says which happened and `note` says why.
|
|
17
|
+
*
|
|
18
|
+
* {@link terraformCatalogFields} turns the docs into the release catalog's
|
|
19
|
+
* `terraform` fields (`@intentius/chant/lifecycle/release-catalog`), and
|
|
20
|
+
* {@link terraformCatalogSections} into the site's sections for a component
|
|
21
|
+
* and a version.
|
|
22
|
+
*/
|
|
23
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
24
|
+
import { join } from "node:path";
|
|
25
|
+
import { OUTPUT_TYPE, TERRAFORM_TYPE, VARIABLE_TYPE, listTerraformFiles, parseTerraformRootDir } from "../hcl/parse.js";
|
|
26
|
+
/** The terraform-docs config file names a module may carry, in the order they are looked for. */
|
|
27
|
+
export const TERRAFORM_DOCS_CONFIGS = [".terraform-docs.yml", ".terraform-docs.yaml"];
|
|
28
|
+
const README_NAMES = ["README.md", "readme.md", "Readme.md", "README"];
|
|
29
|
+
function readme(dir) {
|
|
30
|
+
for (const name of README_NAMES) {
|
|
31
|
+
const p = join(dir, name);
|
|
32
|
+
if (existsSync(p) && statSync(p).isFile())
|
|
33
|
+
return readFileSync(p, "utf-8");
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
/** `${string}` as hcl2json writes an expression, or `string` as a `.tf.json` file does: the expression either way. */
|
|
38
|
+
function expression(v) {
|
|
39
|
+
if (typeof v !== "string")
|
|
40
|
+
return null;
|
|
41
|
+
const m = /^\$\{([\s\S]*)\}$/.exec(v.trim());
|
|
42
|
+
return (m ? m[1] : v).trim();
|
|
43
|
+
}
|
|
44
|
+
const text = (v) => (typeof v === "string" ? v : null);
|
|
45
|
+
function firstBody(v) {
|
|
46
|
+
const b = Array.isArray(v) ? v[0] : v;
|
|
47
|
+
return b && typeof b === "object" && !Array.isArray(b) ? b : undefined;
|
|
48
|
+
}
|
|
49
|
+
function providersOf(body) {
|
|
50
|
+
const out = [];
|
|
51
|
+
const blocks = Array.isArray(body.required_providers) ? body.required_providers : body.required_providers ? [body.required_providers] : [];
|
|
52
|
+
for (const block of blocks) {
|
|
53
|
+
if (!block || typeof block !== "object")
|
|
54
|
+
continue;
|
|
55
|
+
for (const [name, spec] of Object.entries(block)) {
|
|
56
|
+
// A bare string is the old `aws = "~> 4.0"` form: a version with no source.
|
|
57
|
+
if (typeof spec === "string") {
|
|
58
|
+
out.push({ name, source: null, version: spec });
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const s = firstBody(spec) ?? {};
|
|
62
|
+
out.push({ name, source: text(s.source), version: text(s.version) });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
function examples(dir, binary) {
|
|
68
|
+
const root = join(dir, "examples");
|
|
69
|
+
if (!existsSync(root) || !statSync(root).isDirectory())
|
|
70
|
+
return [];
|
|
71
|
+
return readdirSync(root, { withFileTypes: true })
|
|
72
|
+
.filter((d) => d.isDirectory())
|
|
73
|
+
.map((d) => d.name)
|
|
74
|
+
.sort()
|
|
75
|
+
.map((name) => {
|
|
76
|
+
const path = join(root, name);
|
|
77
|
+
return { name, path: `examples/${name}`, readme: readme(path), files: [...listTerraformFiles(path, binary)].sort() };
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
async function fromTerraformDocs(dir, runner) {
|
|
81
|
+
const config = TERRAFORM_DOCS_CONFIGS.find((name) => existsSync(join(dir, name))) ?? null;
|
|
82
|
+
if (!config)
|
|
83
|
+
return { docsFrom: "hcl", markdown: null, config: null, note: null };
|
|
84
|
+
if (!runner)
|
|
85
|
+
return { docsFrom: "hcl", markdown: null, config, note: `${config} found, and no terraform-docs runner was given` };
|
|
86
|
+
try {
|
|
87
|
+
const markdown = await runner({ dir, config });
|
|
88
|
+
if (markdown === null)
|
|
89
|
+
return { docsFrom: "hcl", markdown: null, config, note: `${config} found, and no terraform-docs binary is installed` };
|
|
90
|
+
return { docsFrom: "terraform-docs", markdown, config, note: null };
|
|
91
|
+
}
|
|
92
|
+
catch (e) {
|
|
93
|
+
return { docsFrom: "hcl", markdown: null, config, note: `terraform-docs failed with ${config}: ${e.message.split("\n", 1)[0]}` };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Read one module's docs from `dir`. Throws what the HCL parse throws for a
|
|
98
|
+
* file it cannot read; a terraform-docs failure is never thrown, it falls
|
|
99
|
+
* back to the parse and says so in `note`.
|
|
100
|
+
*/
|
|
101
|
+
export async function moduleDocs(dir, options = {}) {
|
|
102
|
+
const entities = [...(await parseTerraformRootDir(dir, "module", options.parser, options.binary ? { binary: options.binary } : undefined)).values()];
|
|
103
|
+
const inputs = [];
|
|
104
|
+
const outputs = [];
|
|
105
|
+
const providers = [];
|
|
106
|
+
let requiredVersion = null;
|
|
107
|
+
for (const e of entities) {
|
|
108
|
+
const { address, body, file } = e.props;
|
|
109
|
+
if (e.entityType === VARIABLE_TYPE) {
|
|
110
|
+
const sensitive = body.sensitive === true;
|
|
111
|
+
const required = !("default" in body);
|
|
112
|
+
inputs.push({
|
|
113
|
+
name: address.slice("var.".length),
|
|
114
|
+
type: expression(body.type),
|
|
115
|
+
required,
|
|
116
|
+
...(!required && !sensitive ? { default: body.default } : {}),
|
|
117
|
+
description: text(body.description),
|
|
118
|
+
sensitive,
|
|
119
|
+
file,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
else if (e.entityType === OUTPUT_TYPE) {
|
|
123
|
+
outputs.push({ name: address.slice("output.".length), description: text(body.description), sensitive: body.sensitive === true, file });
|
|
124
|
+
}
|
|
125
|
+
else if (e.entityType === TERRAFORM_TYPE) {
|
|
126
|
+
providers.push(...providersOf(body));
|
|
127
|
+
requiredVersion ?? (requiredVersion = text(body.required_version));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
inputs.sort((a, b) => a.name.localeCompare(b.name));
|
|
131
|
+
outputs.sort((a, b) => a.name.localeCompare(b.name));
|
|
132
|
+
providers.sort((a, b) => a.name.localeCompare(b.name));
|
|
133
|
+
return {
|
|
134
|
+
readme: readme(dir),
|
|
135
|
+
inputs,
|
|
136
|
+
outputs,
|
|
137
|
+
providers,
|
|
138
|
+
requiredVersion,
|
|
139
|
+
examples: examples(dir, options.binary),
|
|
140
|
+
...(await fromTerraformDocs(dir, options.terraformDocs)),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* The release catalog's `terraform` fields for a module version: its
|
|
145
|
+
* address (`<host>/<namespace>/<name>/<system>`), the docs' structured
|
|
146
|
+
* parts, and the markdown when terraform-docs wrote it. Everything here is
|
|
147
|
+
* JSON, as the catalog's schema requires.
|
|
148
|
+
*/
|
|
149
|
+
export function terraformCatalogFields(docs, address) {
|
|
150
|
+
return {
|
|
151
|
+
address,
|
|
152
|
+
inputs: docs.inputs.map(({ file: _file, ...i }) => i),
|
|
153
|
+
outputs: docs.outputs.map(({ file: _file, ...o }) => o),
|
|
154
|
+
providers: docs.providers,
|
|
155
|
+
requiredVersion: docs.requiredVersion,
|
|
156
|
+
examples: docs.examples.map((x) => ({ name: x.name, path: x.path, files: x.files })),
|
|
157
|
+
docsFrom: docs.docsFrom,
|
|
158
|
+
...(docs.readme !== null ? { readme: docs.readme } : {}),
|
|
159
|
+
...(docs.markdown !== null ? { markdown: docs.markdown } : {}),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const show = (v) => (v === undefined ? "" : typeof v === "string" ? JSON.stringify(v) : JSON.stringify(v) ?? "");
|
|
163
|
+
function versionSections(f, version) {
|
|
164
|
+
const out = [];
|
|
165
|
+
if (f.address) {
|
|
166
|
+
const pin = version ? `\n version = "${version}"` : "";
|
|
167
|
+
out.push({ title: "Usage", body: { kind: "code", text: `module "${f.address.split("/").at(-2) ?? "this"}" {\n source = "${f.address}"${pin}\n}` } });
|
|
168
|
+
}
|
|
169
|
+
if (f.inputs?.length) {
|
|
170
|
+
out.push({
|
|
171
|
+
title: "Inputs",
|
|
172
|
+
body: { kind: "table", columns: ["Name", "Type", "Default", "Required", "Description"], rows: f.inputs.map((i) => [i.name, i.type ?? "", i.sensitive ? "(sensitive)" : i.required ? "" : show(i.default), i.required ? "yes" : "no", i.description ?? ""]) },
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
if (f.outputs?.length) {
|
|
176
|
+
out.push({ title: "Outputs", body: { kind: "table", columns: ["Name", "Description", "Sensitive"], rows: f.outputs.map((o) => [o.name, o.description ?? "", o.sensitive ? "yes" : "no"]) } });
|
|
177
|
+
}
|
|
178
|
+
if (f.providers?.length || f.requiredVersion) {
|
|
179
|
+
const rows = (f.providers ?? []).map((p) => [p.name, p.source ?? "", p.version ?? ""]);
|
|
180
|
+
if (f.requiredVersion)
|
|
181
|
+
rows.unshift(["terraform", "", f.requiredVersion]);
|
|
182
|
+
out.push({ title: "Requirements", body: { kind: "table", columns: ["Name", "Source", "Version"], rows } });
|
|
183
|
+
}
|
|
184
|
+
if (f.examples?.length)
|
|
185
|
+
out.push({ title: "Examples", body: { kind: "table", columns: ["Example", "Path"], rows: f.examples.map((x) => [x.name, x.path]) } });
|
|
186
|
+
// terraform-docs's markdown is shown as text: the site renders no markdown, and escaping keeps a module's README from writing HTML into the page.
|
|
187
|
+
const body = f.markdown ?? f.readme;
|
|
188
|
+
if (body)
|
|
189
|
+
out.push({ title: f.markdown ? "Documentation" : "README", body: { kind: "code", text: body } });
|
|
190
|
+
return out;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* The site sections (`renderCatalogSite`'s `sections`) for Terraform
|
|
194
|
+
* modules: usage, inputs, outputs, requirements, examples and the README or
|
|
195
|
+
* terraform-docs's markdown on each version page, read from the version's
|
|
196
|
+
* `terraform` fields, and the usage line on the component page from the
|
|
197
|
+
* component's. A component without `terraform` fields gets none.
|
|
198
|
+
*/
|
|
199
|
+
export function terraformCatalogSections() {
|
|
200
|
+
return {
|
|
201
|
+
component: (c) => {
|
|
202
|
+
const f = c.fields.terraform;
|
|
203
|
+
return f?.address ? versionSections({ address: f.address }, c.latest && !c.latest.includes(":") ? c.latest : null) : [];
|
|
204
|
+
},
|
|
205
|
+
version: (_c, v) => {
|
|
206
|
+
const f = v.fields.terraform;
|
|
207
|
+
return f ? versionSections(f, v.version) : [];
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type RegistryFiles } from "../handler.js";
|
|
2
|
+
/** Read auth: the tokens accepted and how tarball URLs are signed. */
|
|
3
|
+
export interface EdgeAuth {
|
|
4
|
+
/** Accepted bearer tokens. Keep two while one is rotated out. */
|
|
5
|
+
tokens: string[];
|
|
6
|
+
/** The key tarball URLs are signed with. Default: the first token. */
|
|
7
|
+
signingKey?: string;
|
|
8
|
+
/** Seconds a signed tarball URL works. Default 300. */
|
|
9
|
+
ttl?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface RegistryEdgeOptions {
|
|
12
|
+
/** Where the registry's files are read: the bucket behind the function. */
|
|
13
|
+
files: RegistryFiles;
|
|
14
|
+
/** The registry's prefix in the bucket. */
|
|
15
|
+
prefix?: string;
|
|
16
|
+
/** Read auth. Absent (the default) means anyone who can reach the host reads. */
|
|
17
|
+
auth?: EdgeAuth;
|
|
18
|
+
/** Milliseconds since the epoch; a test fixes it. Default `Date.now`. */
|
|
19
|
+
now?: () => number;
|
|
20
|
+
/** Called once per request: method, path and the status sent. Never given a token. */
|
|
21
|
+
log?: (line: string) => void;
|
|
22
|
+
}
|
|
23
|
+
/** The query parameters a signed tarball URL carries. */
|
|
24
|
+
export declare const SIGNED_EXPIRES = "expires";
|
|
25
|
+
export declare const SIGNED_SIGNATURE = "signature";
|
|
26
|
+
/** Default lifetime of a signed tarball URL, in seconds. */
|
|
27
|
+
export declare const DEFAULT_SIGNED_TTL = 300;
|
|
28
|
+
/** A location with `expires` and `signature` added, valid for `ttl` seconds from `nowMs`. */
|
|
29
|
+
export declare function signLocation(location: string, key: string, nowMs: number, ttl?: number): Promise<string>;
|
|
30
|
+
/** Whether a URL carries a signature this key made for its path that has not expired. */
|
|
31
|
+
export declare function verifySignedUrl(url: URL, key: string, nowMs: number): Promise<boolean>;
|
|
32
|
+
/** Whether a request carries one of the accepted bearer tokens. */
|
|
33
|
+
export declare function bearerAccepted(request: Request, tokens: string[]): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The part of the function that decides before the bucket is read: a 401, a
|
|
36
|
+
* download answer, or undefined for "serve this file from the bucket as it is".
|
|
37
|
+
* Lambda@Edge uses this on its own and lets CloudFront fetch the rest.
|
|
38
|
+
*/
|
|
39
|
+
export declare function registryEdgeGate(options: RegistryEdgeOptions): (request: Request) => Promise<Response | undefined>;
|
|
40
|
+
/** The whole function: the gate, then every other file served from the bucket by `registryHandler`. */
|
|
41
|
+
export declare function registryEdge(options: RegistryEdgeOptions): (request: Request) => Promise<Response>;
|
|
42
|
+
/** Registry files read from an origin over HTTP: a public bucket endpoint, a website endpoint, or a test server. */
|
|
43
|
+
export declare function originFiles(base: string, fetchFn?: (url: string, init?: RequestInit) => Promise<Response>): RegistryFiles;
|
|
44
|
+
/** Accepted tokens from a secret's text: one per line or comma separated, blanks dropped. */
|
|
45
|
+
export declare const tokensFromSecret: (text: string | undefined) => string[];
|
|
46
|
+
//# sourceMappingURL=edge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge.d.ts","sourceRoot":"","sources":["../../../src/registry/edge/edge.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAIjE,sEAAsE;AACtE,MAAM,WAAW,QAAQ;IACvB,iEAAiE;IACjE,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,mBAAmB;IAClC,2EAA2E;IAC3E,KAAK,EAAE,aAAa,CAAC;IACrB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,yEAAyE;IACzE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,sFAAsF;IACtF,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,yDAAyD;AACzD,eAAO,MAAM,cAAc,YAAY,CAAC;AACxC,eAAO,MAAM,gBAAgB,cAAc,CAAC;AAC5C,4DAA4D;AAC5D,eAAO,MAAM,kBAAkB,MAAM,CAAC;AA6BtC,6FAA6F;AAC7F,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,SAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ1H;AAED,yFAAyF;AACzF,wBAAsB,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAK5F;AAED,mEAAmE;AACnE,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAQ1E;AAOD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,mBAAmB,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAuClH;AA4BD,uGAAuG;AACvG,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAIlG;AAED,oHAAoH;AACpH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAyB,GAAG,aAAa,CAQhJ;AAED,6FAA6F;AAC7F,eAAO,MAAM,gBAAgB,SAAU,MAAM,GAAG,SAAS,KAAG,MAAM,EAAyE,CAAC"}
|