@intentius/chant-lexicon-cpln 0.53.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.
- package/README.md +85 -0
- package/dist/api.d.ts +77 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/codegen/docs-cli.d.ts +3 -0
- package/dist/codegen/docs-cli.d.ts.map +1 -0
- package/dist/codegen/docs.d.ts +17 -0
- package/dist/codegen/docs.d.ts.map +1 -0
- package/dist/codegen/generate-cli.d.ts +6 -0
- package/dist/codegen/generate-cli.d.ts.map +1 -0
- package/dist/codegen/generate-lexicon.d.ts +36 -0
- package/dist/codegen/generate-lexicon.d.ts.map +1 -0
- package/dist/codegen/generate-typescript.d.ts +11 -0
- package/dist/codegen/generate-typescript.d.ts.map +1 -0
- package/dist/codegen/generate.d.ts +15 -0
- package/dist/codegen/generate.d.ts.map +1 -0
- package/dist/codegen/naming.d.ts +30 -0
- package/dist/codegen/naming.d.ts.map +1 -0
- package/dist/codegen/package.d.ts +17 -0
- package/dist/codegen/package.d.ts.map +1 -0
- package/dist/composites/cron-job.d.ts +60 -0
- package/dist/composites/cron-job.d.ts.map +1 -0
- package/dist/composites/gvc-environment.d.ts +47 -0
- package/dist/composites/gvc-environment.d.ts.map +1 -0
- package/dist/composites/index.d.ts +13 -0
- package/dist/composites/index.d.ts.map +1 -0
- package/dist/composites/public-domain.d.ts +68 -0
- package/dist/composites/public-domain.d.ts.map +1 -0
- package/dist/composites/secret-access.d.ts +78 -0
- package/dist/composites/secret-access.d.ts.map +1 -0
- package/dist/composites/serverless-service.d.ts +78 -0
- package/dist/composites/serverless-service.d.ts.map +1 -0
- package/dist/composites/stateful-service.d.ts +79 -0
- package/dist/composites/stateful-service.d.ts.map +1 -0
- package/dist/coverage.d.ts +59 -0
- package/dist/coverage.d.ts.map +1 -0
- package/dist/describe-resources.d.ts +71 -0
- package/dist/describe-resources.d.ts.map +1 -0
- package/dist/detect.d.ts +14 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/entity-props.d.ts +34 -0
- package/dist/entity-props.d.ts.map +1 -0
- package/dist/generated/index.d.ts +151 -0
- package/dist/generated/index.d.ts.map +1 -0
- package/dist/generated/runtime.d.ts +5 -0
- package/dist/generated/runtime.d.ts.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/init-templates.d.ts +16 -0
- package/dist/init-templates.d.ts.map +1 -0
- package/dist/integrity.json +35 -0
- package/dist/kinds.d.ts +70 -0
- package/dist/kinds.d.ts.map +1 -0
- package/dist/lint/audit-catalog.d.ts +19 -0
- package/dist/lint/audit-catalog.d.ts.map +1 -0
- package/dist/lint/post-synth/cpl010-unrestricted-outbound.d.ts +12 -0
- package/dist/lint/post-synth/cpl010-unrestricted-outbound.d.ts.map +1 -0
- package/dist/lint/post-synth/cpl011-internal-firewall-scope.d.ts +11 -0
- package/dist/lint/post-synth/cpl011-internal-firewall-scope.d.ts.map +1 -0
- package/dist/lint/post-synth/cpl012-no-literal-credential-env.d.ts +11 -0
- package/dist/lint/post-synth/cpl012-no-literal-credential-env.d.ts.map +1 -0
- package/dist/lint/post-synth/cpl013-qualified-identity-principal.d.ts +11 -0
- package/dist/lint/post-synth/cpl013-qualified-identity-principal.d.ts.map +1 -0
- package/dist/lint/post-synth/cpl014-qualified-secret-ref.d.ts +14 -0
- package/dist/lint/post-synth/cpl014-qualified-secret-ref.d.ts.map +1 -0
- package/dist/lint/post-synth/cpl020-serverless-single-port.d.ts +10 -0
- package/dist/lint/post-synth/cpl020-serverless-single-port.d.ts.map +1 -0
- package/dist/lint/post-synth/cpl021-cron-job-shape.d.ts +11 -0
- package/dist/lint/post-synth/cpl021-cron-job-shape.d.ts.map +1 -0
- package/dist/lint/post-synth/cpl022-container-ports.d.ts +10 -0
- package/dist/lint/post-synth/cpl022-container-ports.d.ts.map +1 -0
- package/dist/lint/post-synth/cpl023-container-resources.d.ts +12 -0
- package/dist/lint/post-synth/cpl023-container-resources.d.ts.map +1 -0
- package/dist/lint/post-synth/cpl024-probe-handler.d.ts +10 -0
- package/dist/lint/post-synth/cpl024-probe-handler.d.ts.map +1 -0
- package/dist/lint/post-synth/cpl025-autoscaling-shape.d.ts +11 -0
- package/dist/lint/post-synth/cpl025-autoscaling-shape.d.ts.map +1 -0
- package/dist/lint/post-synth/cpl026-scale-to-zero.d.ts +11 -0
- package/dist/lint/post-synth/cpl026-scale-to-zero.d.ts.map +1 -0
- package/dist/lint/post-synth/cpl027-capacity-ai-conflict.d.ts +13 -0
- package/dist/lint/post-synth/cpl027-capacity-ai-conflict.d.ts.map +1 -0
- package/dist/lint/post-synth/cpl028-volume-set.d.ts +3 -0
- package/dist/lint/post-synth/cpl028-volume-set.d.ts.map +1 -0
- package/dist/lint/post-synth/cpl029-link-targets.d.ts +3 -0
- package/dist/lint/post-synth/cpl029-link-targets.d.ts.map +1 -0
- package/dist/lint/post-synth/cpl030-domain-routing.d.ts +3 -0
- package/dist/lint/post-synth/cpl030-domain-routing.d.ts.map +1 -0
- package/dist/lint/post-synth/cpl040-pinned-image.d.ts +11 -0
- package/dist/lint/post-synth/cpl040-pinned-image.d.ts.map +1 -0
- package/dist/lint/post-synth/cpl041-image-reference-form.d.ts +12 -0
- package/dist/lint/post-synth/cpl041-image-reference-form.d.ts.map +1 -0
- package/dist/lint/post-synth/cpl042-gvc-placement.d.ts +10 -0
- package/dist/lint/post-synth/cpl042-gvc-placement.d.ts.map +1 -0
- package/dist/lint/post-synth/cpl043-policy-scope.d.ts +16 -0
- package/dist/lint/post-synth/cpl043-policy-scope.d.ts.map +1 -0
- package/dist/lint/post-synth/helpers.d.ts +45 -0
- package/dist/lint/post-synth/helpers.d.ts.map +1 -0
- package/dist/lint/post-synth/index.d.ts +3 -0
- package/dist/lint/post-synth/index.d.ts.map +1 -0
- package/dist/lint/rules/cpl001-no-secret-literals.d.ts +3 -0
- package/dist/lint/rules/cpl001-no-secret-literals.d.ts.map +1 -0
- package/dist/lint/rules/cpl002-prefer-resource-reference.d.ts +3 -0
- package/dist/lint/rules/cpl002-prefer-resource-reference.d.ts.map +1 -0
- package/dist/lint/rules/index.d.ts +6 -0
- package/dist/lint/rules/index.d.ts.map +1 -0
- package/dist/lsp/completions.d.ts +7 -0
- package/dist/lsp/completions.d.ts.map +1 -0
- package/dist/lsp/hover.d.ts +4 -0
- package/dist/lsp/hover.d.ts.map +1 -0
- package/dist/manifest.json +6 -0
- package/dist/meta.json +2110 -0
- package/dist/okf/index.md +39 -0
- package/dist/okf/rules/CPL001.md +22 -0
- package/dist/okf/rules/CPL002.md +22 -0
- package/dist/okf/rules/CPL010.md +15 -0
- package/dist/okf/rules/CPL011.md +15 -0
- package/dist/okf/rules/CPL012.md +15 -0
- package/dist/okf/rules/CPL013.md +15 -0
- package/dist/okf/rules/CPL014.md +16 -0
- package/dist/okf/rules/CPL020.md +11 -0
- package/dist/okf/rules/CPL021.md +15 -0
- package/dist/okf/rules/CPL022.md +15 -0
- package/dist/okf/rules/CPL023.md +15 -0
- package/dist/okf/rules/CPL024.md +15 -0
- package/dist/okf/rules/CPL025.md +15 -0
- package/dist/okf/rules/CPL026.md +15 -0
- package/dist/okf/rules/CPL027.md +15 -0
- package/dist/okf/rules/CPL028.md +15 -0
- package/dist/okf/rules/CPL029.md +15 -0
- package/dist/okf/rules/CPL030.md +16 -0
- package/dist/okf/rules/CPL040.md +15 -0
- package/dist/okf/rules/CPL041.md +15 -0
- package/dist/okf/rules/CPL042.md +11 -0
- package/dist/okf/rules/CPL043.md +15 -0
- package/dist/okf/types/Domain.md +22 -0
- package/dist/okf/types/Gvc.md +21 -0
- package/dist/okf/types/Identity.md +29 -0
- package/dist/okf/types/IpSet.md +21 -0
- package/dist/okf/types/Policy.md +28 -0
- package/dist/okf/types/Secret.md +23 -0
- package/dist/okf/types/VolumeSet.md +22 -0
- package/dist/okf/types/Workload.md +38 -0
- package/dist/ownership.d.ts +23 -0
- package/dist/ownership.d.ts.map +1 -0
- package/dist/package-cli.d.ts +3 -0
- package/dist/package-cli.d.ts.map +1 -0
- package/dist/plugin.d.ts +3 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/reference-catalog.d.ts +24 -0
- package/dist/reference-catalog.d.ts.map +1 -0
- package/dist/rules/cpl001-no-secret-literals.ts +83 -0
- package/dist/rules/cpl002-prefer-resource-reference.ts +71 -0
- package/dist/rules/cpl010-unrestricted-outbound.ts +41 -0
- package/dist/rules/cpl011-internal-firewall-scope.ts +38 -0
- package/dist/rules/cpl012-no-literal-credential-env.ts +58 -0
- package/dist/rules/cpl013-qualified-identity-principal.ts +45 -0
- package/dist/rules/cpl014-qualified-secret-ref.ts +54 -0
- package/dist/rules/cpl020-serverless-single-port.ts +58 -0
- package/dist/rules/cpl021-cron-job-shape.ts +64 -0
- package/dist/rules/cpl022-container-ports.ts +64 -0
- package/dist/rules/cpl023-container-resources.ts +87 -0
- package/dist/rules/cpl024-probe-handler.ts +51 -0
- package/dist/rules/cpl025-autoscaling-shape.ts +100 -0
- package/dist/rules/cpl026-scale-to-zero.ts +67 -0
- package/dist/rules/cpl027-capacity-ai-conflict.ts +78 -0
- package/dist/rules/cpl028-volume-set.ts +183 -0
- package/dist/rules/cpl029-link-targets.ts +155 -0
- package/dist/rules/cpl030-domain-routing.ts +137 -0
- package/dist/rules/cpl040-pinned-image.ts +48 -0
- package/dist/rules/cpl041-image-reference-form.ts +60 -0
- package/dist/rules/cpl042-gvc-placement.ts +57 -0
- package/dist/rules/cpl043-policy-scope.ts +0 -0
- package/dist/rules/helpers.ts +114 -0
- package/dist/serializer.d.ts +69 -0
- package/dist/serializer.d.ts.map +1 -0
- package/dist/skills/chant-cpln-secrets.md +110 -0
- package/dist/skills/chant-cpln-workloads.md +78 -0
- package/dist/skills/chant-cpln.md +103 -0
- package/dist/spec/fetch.d.ts +43 -0
- package/dist/spec/fetch.d.ts.map +1 -0
- package/dist/spec/parse.d.ts +79 -0
- package/dist/spec/parse.d.ts.map +1 -0
- package/dist/spec/snapshot-cli.d.ts +18 -0
- package/dist/spec/snapshot-cli.d.ts.map +1 -0
- package/dist/types/index.d.ts +1649 -0
- package/dist/validate-cli.d.ts +3 -0
- package/dist/validate-cli.d.ts.map +1 -0
- package/dist/validate.d.ts +26 -0
- package/dist/validate.d.ts.map +1 -0
- package/package.json +74 -0
- package/src/api.ts +134 -0
- package/src/codegen/docs-cli.ts +7 -0
- package/src/codegen/docs.ts +94 -0
- package/src/codegen/generate-cli.ts +34 -0
- package/src/codegen/generate-lexicon.ts +88 -0
- package/src/codegen/generate-typescript.ts +71 -0
- package/src/codegen/generate.ts +110 -0
- package/src/codegen/naming.ts +61 -0
- package/src/codegen/package.ts +35 -0
- package/src/composites/composites.test.ts +252 -0
- package/src/composites/cron-job.ts +124 -0
- package/src/composites/gvc-environment.ts +104 -0
- package/src/composites/index.ts +17 -0
- package/src/composites/public-domain.ts +144 -0
- package/src/composites/secret-access.ts +136 -0
- package/src/composites/serverless-service.ts +143 -0
- package/src/composites/stateful-service.ts +179 -0
- package/src/coverage.ts +149 -0
- package/src/describe-resources.test.ts +252 -0
- package/src/describe-resources.ts +310 -0
- package/src/detect.ts +58 -0
- package/src/entity-props.ts +98 -0
- package/src/generated/index.d.ts +1649 -0
- package/src/generated/index.ts +156 -0
- package/src/generated/lexicon-cpln.json +2110 -0
- package/src/generated/runtime.ts +4 -0
- package/src/index.ts +31 -0
- package/src/init-templates.ts +131 -0
- package/src/kinds.ts +173 -0
- package/src/lint/audit-catalog.ts +230 -0
- package/src/lint/post-synth/cpl010-unrestricted-outbound.ts +41 -0
- package/src/lint/post-synth/cpl011-internal-firewall-scope.ts +38 -0
- package/src/lint/post-synth/cpl012-no-literal-credential-env.ts +58 -0
- package/src/lint/post-synth/cpl013-qualified-identity-principal.ts +45 -0
- package/src/lint/post-synth/cpl014-qualified-secret-ref.ts +54 -0
- package/src/lint/post-synth/cpl020-serverless-single-port.ts +58 -0
- package/src/lint/post-synth/cpl021-cron-job-shape.ts +64 -0
- package/src/lint/post-synth/cpl022-container-ports.ts +64 -0
- package/src/lint/post-synth/cpl023-container-resources.ts +87 -0
- package/src/lint/post-synth/cpl024-probe-handler.ts +51 -0
- package/src/lint/post-synth/cpl025-autoscaling-shape.ts +100 -0
- package/src/lint/post-synth/cpl026-scale-to-zero.ts +67 -0
- package/src/lint/post-synth/cpl027-capacity-ai-conflict.ts +78 -0
- package/src/lint/post-synth/cpl028-volume-set.ts +183 -0
- package/src/lint/post-synth/cpl029-link-targets.ts +155 -0
- package/src/lint/post-synth/cpl030-domain-routing.ts +137 -0
- package/src/lint/post-synth/cpl040-pinned-image.ts +48 -0
- package/src/lint/post-synth/cpl041-image-reference-form.ts +60 -0
- package/src/lint/post-synth/cpl042-gvc-placement.ts +57 -0
- package/src/lint/post-synth/cpl043-policy-scope.ts +0 -0
- package/src/lint/post-synth/helpers.ts +114 -0
- package/src/lint/post-synth/index.ts +45 -0
- package/src/lint/post-synth/post-synth.test.ts +737 -0
- package/src/lint/rules/cpl001-no-secret-literals.ts +83 -0
- package/src/lint/rules/cpl002-prefer-resource-reference.ts +71 -0
- package/src/lint/rules/index.ts +9 -0
- package/src/lint/rules/rules.test.ts +108 -0
- package/src/lsp/completions.test.ts +44 -0
- package/src/lsp/completions.ts +27 -0
- package/src/lsp/hover.test.ts +53 -0
- package/src/lsp/hover.ts +130 -0
- package/src/ownership.ts +28 -0
- package/src/package-cli.ts +25 -0
- package/src/plugin.test.ts +131 -0
- package/src/plugin.ts +187 -0
- package/src/reference-catalog.ts +110 -0
- package/src/serializer.test.ts +233 -0
- package/src/serializer.ts +253 -0
- package/src/skills/chant-cpln-secrets.md +110 -0
- package/src/skills/chant-cpln-workloads.md +78 -0
- package/src/skills/chant-cpln.md +103 -0
- package/src/spec/cpln-openapi.snapshot.json +10651 -0
- package/src/spec/fetch.ts +77 -0
- package/src/spec/parse.ts +451 -0
- package/src/spec/snapshot-cli.ts +71 -0
- package/src/validate-cli.ts +8 -0
- package/src/validate.ts +102 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-cli.d.ts","sourceRoot":"","sources":["../src/validate-cli.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate the generated `lexicon-cpln` artifacts.
|
|
3
|
+
*
|
|
4
|
+
* The required-names list is the point of this file: it asserts that a
|
|
5
|
+
* regeneration still produced the surface consumers import. A rename upstream,
|
|
6
|
+
* a parser change that stops following a `$ref`, or a loosened subtree would
|
|
7
|
+
* otherwise leave a smaller generated surface that builds fine and is missing
|
|
8
|
+
* classes nobody notices until an import fails.
|
|
9
|
+
*
|
|
10
|
+
* The list spans all three layers deliberately — the eight resources, property
|
|
11
|
+
* types reached through several different mechanisms (inline nesting, a `$ref`,
|
|
12
|
+
* a `oneOf` branch, an array item), and the deepest shapes the parser walks to.
|
|
13
|
+
* A shallow list of the eight resource names would pass while the entire
|
|
14
|
+
* property-type tree vanished.
|
|
15
|
+
*/
|
|
16
|
+
import { type ValidateResult } from "@intentius/chant/codegen/validate";
|
|
17
|
+
export type { ValidateCheck, ValidateResult } from "@intentius/chant/codegen/validate";
|
|
18
|
+
/**
|
|
19
|
+
* Validate the generated artifacts.
|
|
20
|
+
*/
|
|
21
|
+
export declare function validate(opts?: {
|
|
22
|
+
basePath?: string;
|
|
23
|
+
}): Promise<ValidateResult>;
|
|
24
|
+
/** The names `validate` asserts, exported so a test can assert the list itself is deep. */
|
|
25
|
+
export declare const REQUIRED_ARTIFACT_NAMES: readonly string[];
|
|
26
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAGlG,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAkEvF;;GAEG;AACH,wBAAsB,QAAQ,CAAC,IAAI,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAQpF;AAED,2FAA2F;AAC3F,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAAmB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@intentius/chant-lexicon-cpln",
|
|
3
|
+
"version": "0.53.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"files": [
|
|
6
|
+
"src/",
|
|
7
|
+
"dist/"
|
|
8
|
+
],
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"development": "./src/index.ts",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./src/index.ts"
|
|
17
|
+
},
|
|
18
|
+
"./*": {
|
|
19
|
+
"development": "./src/*.ts",
|
|
20
|
+
"types": "./dist/*.d.ts",
|
|
21
|
+
"default": "./src/*.ts"
|
|
22
|
+
},
|
|
23
|
+
"./detect": {
|
|
24
|
+
"development": "./src/detect.ts",
|
|
25
|
+
"types": "./dist/detect.d.ts",
|
|
26
|
+
"default": "./src/detect.ts"
|
|
27
|
+
},
|
|
28
|
+
"./lint/post-synth": {
|
|
29
|
+
"development": "./src/lint/post-synth/index.ts",
|
|
30
|
+
"types": "./dist/lint/post-synth/index.d.ts",
|
|
31
|
+
"default": "./src/lint/post-synth/index.ts"
|
|
32
|
+
},
|
|
33
|
+
"./manifest": "./dist/manifest.json",
|
|
34
|
+
"./meta": "./dist/meta.json",
|
|
35
|
+
"./types": "./dist/types/index.d.ts"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"generate": "tsx src/codegen/generate-cli.ts",
|
|
39
|
+
"bundle": "tsx src/package-cli.ts",
|
|
40
|
+
"validate": "tsx src/validate-cli.ts",
|
|
41
|
+
"docs": "tsx src/codegen/docs-cli.ts",
|
|
42
|
+
"snapshot": "tsx src/spec/snapshot-cli.ts",
|
|
43
|
+
"prepack": "npm run generate && npm run bundle && npm run validate && npm run build",
|
|
44
|
+
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && find dist -type f \\( -name \"*.js\" -o -name \"*.js.map\" \\) -delete"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@intentius/chant": "*",
|
|
48
|
+
"tsc-alias": "^1.8.17",
|
|
49
|
+
"typescript": "^5.9.3"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"@intentius/chant": "^0.53.0",
|
|
53
|
+
"typescript": "^5.9.3"
|
|
54
|
+
},
|
|
55
|
+
"description": "Control Plane (cpln) lexicon for chant — declarative IaC in TypeScript",
|
|
56
|
+
"license": "Apache-2.0",
|
|
57
|
+
"repository": {
|
|
58
|
+
"type": "git",
|
|
59
|
+
"url": "https://github.com/INTENTIUS/chant.git",
|
|
60
|
+
"directory": "lexicons/cpln"
|
|
61
|
+
},
|
|
62
|
+
"homepage": "https://intentius.io/chant",
|
|
63
|
+
"bugs": {
|
|
64
|
+
"url": "https://github.com/INTENTIUS/chant/issues"
|
|
65
|
+
},
|
|
66
|
+
"keywords": [
|
|
67
|
+
"infrastructure-as-code",
|
|
68
|
+
"iac",
|
|
69
|
+
"typescript",
|
|
70
|
+
"control-plane",
|
|
71
|
+
"cpln",
|
|
72
|
+
"chant"
|
|
73
|
+
]
|
|
74
|
+
}
|
package/src/api.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal Control Plane REST client.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately small: observation needs list-and-index, nothing more. The API
|
|
5
|
+
* is uniform enough that one `get` covers every kind — `/org/{org}/{kind}` for
|
|
6
|
+
* org-scoped, `/org/{org}/{kind}` again for the GVC-scoped rollup, which is why
|
|
7
|
+
* `collectionPath` in `kinds.ts` returns the org-wide form when no GVC is given.
|
|
8
|
+
* That rollup is what makes observation one request per kind instead of one per
|
|
9
|
+
* GVC.
|
|
10
|
+
*
|
|
11
|
+
* Credentials follow the CLI's own conventions so `chant plan` reads the org a
|
|
12
|
+
* `cpln` command in the same shell would write to. Nothing here reads a secret
|
|
13
|
+
* value: `-reveal` is never called.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { collectionPath, type CplnKind } from "./kinds";
|
|
17
|
+
|
|
18
|
+
/** Default API root. `CPLN_ENDPOINT` overrides it for a private deployment. */
|
|
19
|
+
export const DEFAULT_ENDPOINT = "https://api.cpln.io";
|
|
20
|
+
|
|
21
|
+
/** A resource as the API returns it. Only the envelope is typed. */
|
|
22
|
+
export interface CplnResource {
|
|
23
|
+
id?: string;
|
|
24
|
+
name?: string;
|
|
25
|
+
kind?: string;
|
|
26
|
+
version?: number;
|
|
27
|
+
description?: string;
|
|
28
|
+
tags?: Record<string, unknown>;
|
|
29
|
+
created?: string;
|
|
30
|
+
lastModified?: string;
|
|
31
|
+
links?: Array<{ rel?: string; href?: string }>;
|
|
32
|
+
spec?: Record<string, unknown>;
|
|
33
|
+
status?: Record<string, unknown>;
|
|
34
|
+
[key: string]: unknown;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** A list response. Control Plane returns `{ kind: "...list", items: [...] }`. */
|
|
38
|
+
interface CplnList {
|
|
39
|
+
items?: CplnResource[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface CplnConfig {
|
|
43
|
+
endpoint: string;
|
|
44
|
+
org: string;
|
|
45
|
+
token: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Thrown when there is no org or no token to read with. */
|
|
49
|
+
export class CplnCredentialsError extends Error {}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Resolve endpoint, org and token from the environment.
|
|
53
|
+
*
|
|
54
|
+
* `CPLN_TOKEN` is the documented CI variable and the one Control Plane's own
|
|
55
|
+
* guidance prefers over `--token`, which leaks into process listings and logs.
|
|
56
|
+
* A service account key works in the same header.
|
|
57
|
+
*/
|
|
58
|
+
export function resolveConfig(overrides: Partial<CplnConfig> = {}): CplnConfig {
|
|
59
|
+
const endpoint = overrides.endpoint ?? process.env.CPLN_ENDPOINT ?? DEFAULT_ENDPOINT;
|
|
60
|
+
const org = overrides.org ?? process.env.CPLN_ORG ?? "";
|
|
61
|
+
const token = overrides.token ?? process.env.CPLN_TOKEN ?? "";
|
|
62
|
+
|
|
63
|
+
if (!org) {
|
|
64
|
+
throw new CplnCredentialsError(
|
|
65
|
+
"No Control Plane org. Set CPLN_ORG (or pass `org`) — it is the top-level isolation boundary and " +
|
|
66
|
+
"every read path is scoped to it.",
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
if (!token) {
|
|
70
|
+
throw new CplnCredentialsError(
|
|
71
|
+
"No Control Plane token. Set CPLN_TOKEN to a service account key or a JWT. Prefer the env var over " +
|
|
72
|
+
"`--token`, which leaks into logs.",
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return { endpoint: endpoint.replace(/\/+$/, ""), org, token };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Build the `Authorization` header value.
|
|
81
|
+
*
|
|
82
|
+
* The spec declares two schemes on the same header: `serviceAccountKey` is an
|
|
83
|
+
* apiKey sent bare, `jwt` is an HTTP bearer token. They are distinguishable by
|
|
84
|
+
* shape — a JWT is three base64url segments and always starts `eyJ` — so a
|
|
85
|
+
* caller does not have to say which kind of credential they have. An
|
|
86
|
+
* already-prefixed value is passed through so an explicit `Bearer …` works too.
|
|
87
|
+
*/
|
|
88
|
+
export function authorization(token: string): string {
|
|
89
|
+
if (token.startsWith("Bearer ")) return token;
|
|
90
|
+
return /^eyJ[A-Za-z0-9_-]+\./.test(token) ? `Bearer ${token}` : token;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** The transport, injectable so tests need no network. */
|
|
94
|
+
export interface CplnHttp {
|
|
95
|
+
get(path: string): Promise<unknown>;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** The default transport — `fetch` with the documented auth header. */
|
|
99
|
+
export function defaultCplnHttp(config: CplnConfig): CplnHttp {
|
|
100
|
+
return {
|
|
101
|
+
async get(path: string): Promise<unknown> {
|
|
102
|
+
const response = await fetch(`${config.endpoint}${path}`, {
|
|
103
|
+
headers: { Authorization: authorization(config.token), Accept: "application/json" },
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (response.status === 404) return undefined;
|
|
107
|
+
if (!response.ok) {
|
|
108
|
+
throw new Error(`GET ${path} → ${response.status} ${response.statusText}`);
|
|
109
|
+
}
|
|
110
|
+
return response.json();
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* List every resource of one kind in the org, keyed by name.
|
|
117
|
+
*
|
|
118
|
+
* Returns an empty map for a 404, which the API uses for a kind the org has
|
|
119
|
+
* none of as well as for one the token cannot see — the distinction is not
|
|
120
|
+
* available here, and the observer treats an absent resource as absent either
|
|
121
|
+
* way.
|
|
122
|
+
*/
|
|
123
|
+
export async function listKind(
|
|
124
|
+
http: CplnHttp,
|
|
125
|
+
config: CplnConfig,
|
|
126
|
+
kind: CplnKind,
|
|
127
|
+
): Promise<Map<string, CplnResource>> {
|
|
128
|
+
const body = (await http.get(collectionPath(kind, config.org))) as CplnList | undefined;
|
|
129
|
+
const index = new Map<string, CplnResource>();
|
|
130
|
+
for (const item of body?.items ?? []) {
|
|
131
|
+
if (typeof item.name === "string") index.set(item.name, item);
|
|
132
|
+
}
|
|
133
|
+
return index;
|
|
134
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cpln documentation generator.
|
|
3
|
+
*
|
|
4
|
+
* Calls the core `docsPipeline` with cpln-specific config. The prose pages live
|
|
5
|
+
* as authored `.mdx` under `docs/pages/`, each naming its Diátaxis quadrant in
|
|
6
|
+
* frontmatter (chant #1731 / #1733); the pipeline reads them, expands markers
|
|
7
|
+
* and builds the sidebar from the quadrant. Only the overview and the output
|
|
8
|
+
* format description stay here, because they are interpolated into generated
|
|
9
|
+
* pages rather than being pages of their own.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { dirname, join } from "path";
|
|
13
|
+
import { fileURLToPath } from "url";
|
|
14
|
+
import { docsPipeline, writeDocsSite, type DocsConfig } from "@intentius/chant/codegen/docs";
|
|
15
|
+
|
|
16
|
+
const pkgDir = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
|
|
17
|
+
|
|
18
|
+
const overview = `The **cpln** lexicon declares [Control Plane](https://controlplane.com) infrastructure as typed chant resources. Control Plane is a hybrid multi-cloud platform: workloads deploy into Global Virtual Clouds that span AWS, GCP, Azure and private clouds, with geo-routing, TLS termination and identity-based cloud access handled by the platform.
|
|
19
|
+
|
|
20
|
+
Types are generated from Control Plane's served OpenAPI document (\`https://api.cpln.io/openapi.json\`), so they track the real API.
|
|
21
|
+
|
|
22
|
+
Install it with:
|
|
23
|
+
|
|
24
|
+
\`\`\`bash
|
|
25
|
+
npm install --save-dev @intentius/chant-lexicon-cpln
|
|
26
|
+
\`\`\`
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
\`\`\`typescript
|
|
31
|
+
import { Gvc, Workload } from "@intentius/chant-lexicon-cpln";
|
|
32
|
+
|
|
33
|
+
export const gvc = new Gvc({
|
|
34
|
+
name: "prod",
|
|
35
|
+
spec: {
|
|
36
|
+
staticPlacement: {
|
|
37
|
+
locationLinks: ["/org/acme/location/aws-us-east-1"], // placement is GVC-level
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export const web = new Workload({
|
|
43
|
+
name: "web",
|
|
44
|
+
gvc: "prod",
|
|
45
|
+
spec: {
|
|
46
|
+
type: "serverless", // exactly one HTTP port
|
|
47
|
+
containers: [{
|
|
48
|
+
name: "main",
|
|
49
|
+
image: "nginx:1.27",
|
|
50
|
+
ports: [{ number: 8080, protocol: "http" }],
|
|
51
|
+
}],
|
|
52
|
+
firewallConfig: {
|
|
53
|
+
external: { inboundAllowCIDR: ["0.0.0.0/0"] }, // closed by default
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
\`\`\`
|
|
58
|
+
|
|
59
|
+
## The loop
|
|
60
|
+
|
|
61
|
+
1. \`chant build\` — synthesize and lint. The CPL rules catch the silent failures Control Plane does not report: an unqualified identity link, a scale-to-zero that never happens, a secret reference with no field.
|
|
62
|
+
2. \`cpln apply --file dist/cpln.yaml --ready\` — reconcile. Ordering across documents is resolved by \`cpln apply\` itself.
|
|
63
|
+
3. \`chant plan\` — read the live org back and diff. Ownership comes from the \`tags\` marker chant stamps at synthesis.
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
const outputFormat = "multi-document Control Plane YAML for `cpln apply --file`";
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Generate the documentation site for the cpln lexicon.
|
|
70
|
+
*/
|
|
71
|
+
export async function generateDocs(options?: { verbose?: boolean }): Promise<void> {
|
|
72
|
+
const config: DocsConfig = {
|
|
73
|
+
name: "cpln",
|
|
74
|
+
displayName: "Control Plane",
|
|
75
|
+
description: "Control Plane (cpln) GVCs, workloads, identities and secrets as typed estate.",
|
|
76
|
+
distDir: join(pkgDir, "dist"),
|
|
77
|
+
outDir: join(pkgDir, "docs"),
|
|
78
|
+
srcDir: join(pkgDir, "src"),
|
|
79
|
+
basePath: process.env.DOCS_BASE_PATH ?? "/chant/lexicons/cpln/",
|
|
80
|
+
overview,
|
|
81
|
+
outputFormat,
|
|
82
|
+
serviceFromType: (type: string) => type.split("::")[1] ?? type,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const result = docsPipeline(config);
|
|
86
|
+
writeDocsSite(config, result);
|
|
87
|
+
|
|
88
|
+
if (options?.verbose) {
|
|
89
|
+
console.error(
|
|
90
|
+
`Generated docs: ${result.stats.resources} resources, ${result.stats.properties} properties, ` +
|
|
91
|
+
`${result.stats.services} services, ${result.stats.rules} rules`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* CLI entry point for cpln lexicon generation.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { dirname } from "path";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
8
|
+
import { generate, writeGeneratedFiles } from "./generate";
|
|
9
|
+
|
|
10
|
+
const pkgDir = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
|
|
11
|
+
|
|
12
|
+
async function main(): Promise<void> {
|
|
13
|
+
const verbose = process.argv.includes("--verbose") || process.argv.includes("-v");
|
|
14
|
+
const force = process.argv.includes("--force") || process.argv.includes("-f");
|
|
15
|
+
|
|
16
|
+
console.error("Generating cpln lexicon...");
|
|
17
|
+
|
|
18
|
+
const result = await generate({ verbose, force });
|
|
19
|
+
writeGeneratedFiles(result, pkgDir);
|
|
20
|
+
|
|
21
|
+
console.error(
|
|
22
|
+
`Generated ${result.resources} resources, ${result.properties} property types, ${result.enums} enums`,
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
if (result.warnings.length > 0) {
|
|
26
|
+
console.error(`${result.warnings.length} warnings:`);
|
|
27
|
+
for (const w of result.warnings) console.error(` ${w.file}: ${w.error}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
main().catch((err) => {
|
|
32
|
+
console.error("Generation failed:", err);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lexicon registry generator — produces `lexicon-cpln.json`, the metadata the
|
|
3
|
+
* evaluator and lint engine read for each cpln resource and property type.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { PropertyConstraints } from "@intentius/chant/codegen/json-schema";
|
|
7
|
+
import {
|
|
8
|
+
buildRegistry,
|
|
9
|
+
serializeRegistry,
|
|
10
|
+
type RegistryResource,
|
|
11
|
+
} from "@intentius/chant/codegen/generate-registry";
|
|
12
|
+
import { cplnShortName, kindByTypeName } from "../kinds";
|
|
13
|
+
import type { CplnParseResult } from "../spec/parse";
|
|
14
|
+
import type { NamingStrategy } from "./naming";
|
|
15
|
+
|
|
16
|
+
export interface CplnLexiconEntry {
|
|
17
|
+
resourceType: string;
|
|
18
|
+
kind: "resource" | "property";
|
|
19
|
+
lexicon: "cpln";
|
|
20
|
+
/** The `kind` discriminator a manifest carries. Resources only. */
|
|
21
|
+
cplnKind?: string;
|
|
22
|
+
/** Whether the resource is addressed under a GVC. Resources only. */
|
|
23
|
+
gvcScoped?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Per-property constraints.
|
|
26
|
+
*
|
|
27
|
+
* Named to match core's `LexiconEntry` contract rather than the bare
|
|
28
|
+
* `constraints` it would be natural to call it. The name is load-bearing
|
|
29
|
+
* twice over: `LexiconIndex.getPropertyNames` derives the LSP's property
|
|
30
|
+
* completions from *the keys of this object*, and hover reads the enums out
|
|
31
|
+
* of its values. Under any other key both features return nothing, with no
|
|
32
|
+
* error — the index simply finds no properties.
|
|
33
|
+
*
|
|
34
|
+
* Every writable property gets an entry for the same reason, even when it
|
|
35
|
+
* carries no constraints at all. A property omitted here is a property that
|
|
36
|
+
* does not complete.
|
|
37
|
+
*/
|
|
38
|
+
propertyConstraints?: Record<string, PropertyConstraints>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Generate the `lexicon-cpln.json` content.
|
|
43
|
+
*/
|
|
44
|
+
export function generateLexiconJSON(results: CplnParseResult[], naming: NamingStrategy): string {
|
|
45
|
+
const registryResources: RegistryResource[] = results.map((r) => ({
|
|
46
|
+
typeName: r.resource.typeName,
|
|
47
|
+
attributes: r.resource.attributes,
|
|
48
|
+
properties: r.resource.properties,
|
|
49
|
+
propertyTypes: [],
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
// Every writable property, with its constraints — see `propertyConstraints`.
|
|
53
|
+
const byTypeName = new Map(results.map((r) => [r.resource.typeName, r] as const));
|
|
54
|
+
|
|
55
|
+
const entries = buildRegistry<CplnLexiconEntry>(registryResources, naming, {
|
|
56
|
+
shortName: cplnShortName,
|
|
57
|
+
buildEntry: (resource) => {
|
|
58
|
+
const kind = kindByTypeName(resource.typeName);
|
|
59
|
+
const entry: CplnLexiconEntry = {
|
|
60
|
+
resourceType: resource.typeName,
|
|
61
|
+
kind: kind ? "resource" : "property",
|
|
62
|
+
lexicon: "cpln",
|
|
63
|
+
};
|
|
64
|
+
if (kind) {
|
|
65
|
+
// Carried on the entry so the serializer and observation can route a
|
|
66
|
+
// resource without re-deriving the mapping from its class name.
|
|
67
|
+
entry.cplnKind = kind.kind;
|
|
68
|
+
entry.gvcScoped = kind.gvcScoped;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const parsed = byTypeName.get(resource.typeName);
|
|
72
|
+
const constraints: Record<string, PropertyConstraints> = {};
|
|
73
|
+
for (const property of parsed?.resource.properties ?? []) {
|
|
74
|
+
constraints[property.name] = property.constraints ?? {};
|
|
75
|
+
}
|
|
76
|
+
if (Object.keys(constraints).length > 0) entry.propertyConstraints = constraints;
|
|
77
|
+
|
|
78
|
+
return entry;
|
|
79
|
+
},
|
|
80
|
+
buildPropertyEntry: (resourceType, propertyType) => ({
|
|
81
|
+
resourceType: `${resourceType}.${propertyType}`,
|
|
82
|
+
kind: "property",
|
|
83
|
+
lexicon: "cpln",
|
|
84
|
+
}),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return serializeRegistry(entries);
|
|
88
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript declaration generator — produces `index.d.ts` with the eight cpln
|
|
3
|
+
* resource classes and the property-type classes their specs reach.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
writeResourceClass,
|
|
8
|
+
writePropertyClass,
|
|
9
|
+
type DtsProperty,
|
|
10
|
+
type DtsAttribute,
|
|
11
|
+
} from "@intentius/chant/codegen/generate-typescript";
|
|
12
|
+
import type { CplnParseResult } from "../spec/parse";
|
|
13
|
+
import type { NamingStrategy } from "./naming";
|
|
14
|
+
|
|
15
|
+
interface Entry {
|
|
16
|
+
tsName: string;
|
|
17
|
+
isProperty: boolean;
|
|
18
|
+
properties: DtsProperty[];
|
|
19
|
+
attributes: DtsAttribute[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Generate the complete `.d.ts` content.
|
|
24
|
+
*/
|
|
25
|
+
export function generateTypeScriptDeclarations(results: CplnParseResult[], naming: NamingStrategy): string {
|
|
26
|
+
const lines: string[] = [];
|
|
27
|
+
lines.push("// Code generated by chant cpln generate. DO NOT EDIT.");
|
|
28
|
+
|
|
29
|
+
const entries: Entry[] = [];
|
|
30
|
+
|
|
31
|
+
for (const r of results) {
|
|
32
|
+
const tsName = naming.resolve(r.resource.typeName);
|
|
33
|
+
if (!tsName) continue;
|
|
34
|
+
|
|
35
|
+
entries.push({
|
|
36
|
+
tsName,
|
|
37
|
+
isProperty: !!r.isProperty,
|
|
38
|
+
properties: r.resource.properties.map((p) => ({
|
|
39
|
+
name: p.name,
|
|
40
|
+
type: p.tsType,
|
|
41
|
+
required: p.required,
|
|
42
|
+
description: p.description,
|
|
43
|
+
})),
|
|
44
|
+
attributes: r.resource.attributes.map((a) => ({ name: a.name, type: a.tsType })),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
entries.sort((a, b) => a.tsName.localeCompare(b.tsName));
|
|
49
|
+
|
|
50
|
+
const resources = entries.filter((e) => !e.isProperty);
|
|
51
|
+
const propertyTypes = entries.filter((e) => e.isProperty);
|
|
52
|
+
|
|
53
|
+
if (resources.length > 0) {
|
|
54
|
+
lines.push("");
|
|
55
|
+
lines.push("// --- Control Plane resource classes ---");
|
|
56
|
+
for (const re of resources) {
|
|
57
|
+
writeResourceClass(lines, re.tsName, re.properties, re.attributes);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (propertyTypes.length > 0) {
|
|
62
|
+
lines.push("");
|
|
63
|
+
lines.push("// --- Property type classes ---");
|
|
64
|
+
for (const pe of propertyTypes) {
|
|
65
|
+
writePropertyClass(lines, pe.tsName, pe.properties);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
lines.push("");
|
|
70
|
+
return lines.join("\n");
|
|
71
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cpln generation pipeline — core `generatePipeline` with cpln's fetch, parse,
|
|
3
|
+
* naming and emit callbacks.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync, readFileSync } from "fs";
|
|
7
|
+
import { dirname, join } from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
import {
|
|
10
|
+
generatePipeline,
|
|
11
|
+
writeGeneratedArtifacts,
|
|
12
|
+
type GenerateOptions,
|
|
13
|
+
type GenerateResult,
|
|
14
|
+
type GeneratePipelineConfig,
|
|
15
|
+
} from "@intentius/chant/codegen/generate";
|
|
16
|
+
import {
|
|
17
|
+
generateRuntimeIndex as coreGenerateRuntimeIndex,
|
|
18
|
+
type RuntimeIndexEntry,
|
|
19
|
+
type RuntimeIndexPropertyEntry,
|
|
20
|
+
} from "@intentius/chant/codegen/generate-runtime-index";
|
|
21
|
+
import { fetchSchemas } from "../spec/fetch";
|
|
22
|
+
import { parseCplnOpenAPI, type CplnParseResult } from "../spec/parse";
|
|
23
|
+
import { NamingStrategy } from "./naming";
|
|
24
|
+
import { generateLexiconJSON } from "./generate-lexicon";
|
|
25
|
+
import { generateTypeScriptDeclarations } from "./generate-typescript";
|
|
26
|
+
|
|
27
|
+
export type { GenerateResult };
|
|
28
|
+
|
|
29
|
+
const PKG_DIR = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
|
|
30
|
+
const SURFACE_SNAPSHOT = join(PKG_DIR, "surface.snapshot.json");
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Read the committed surface snapshot, when there is one.
|
|
34
|
+
*
|
|
35
|
+
* Absent before the first publish, which is the honest state for a lexicon
|
|
36
|
+
* with no published names to protect — the naming strategy simply has nothing
|
|
37
|
+
* to reserve.
|
|
38
|
+
*/
|
|
39
|
+
function readSurfaceSnapshot(): { entries?: Record<string, { kind?: string; resourceType?: string }> } | undefined {
|
|
40
|
+
if (!existsSync(SURFACE_SNAPSHOT)) return undefined;
|
|
41
|
+
try {
|
|
42
|
+
return JSON.parse(readFileSync(SURFACE_SNAPSHOT, "utf-8"));
|
|
43
|
+
} catch {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Run the full cpln generation pipeline.
|
|
50
|
+
*/
|
|
51
|
+
export async function generate(opts: GenerateOptions = {}): Promise<GenerateResult> {
|
|
52
|
+
const config: GeneratePipelineConfig<CplnParseResult> = {
|
|
53
|
+
fetchSchemas: (fetchOpts) => fetchSchemas({ force: fetchOpts.force }),
|
|
54
|
+
|
|
55
|
+
// The spec is one document and `parseSchema` is called once with it; the
|
|
56
|
+
// pipeline accepts an array back, so there is no need for fly's
|
|
57
|
+
// parse-once-stash-the-rest dance.
|
|
58
|
+
parseSchema: (_typeName, data) => parseCplnOpenAPI(data),
|
|
59
|
+
|
|
60
|
+
createNaming: (results) => new NamingStrategy(results, { snapshot: readSurfaceSnapshot() }),
|
|
61
|
+
|
|
62
|
+
generateRegistry: (results, naming) => generateLexiconJSON(results, naming as NamingStrategy),
|
|
63
|
+
|
|
64
|
+
generateTypes: (results, naming) => generateTypeScriptDeclarations(results, naming as NamingStrategy),
|
|
65
|
+
|
|
66
|
+
generateRuntimeIndex: (results, naming) => buildRuntimeIndex(results, naming as NamingStrategy),
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return generatePipeline(config, opts);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Write generated artifacts under `src/generated/`.
|
|
74
|
+
*/
|
|
75
|
+
export function writeGeneratedFiles(result: GenerateResult, baseDir: string): void {
|
|
76
|
+
writeGeneratedArtifacts({
|
|
77
|
+
baseDir,
|
|
78
|
+
files: {
|
|
79
|
+
"lexicon-cpln.json": result.lexiconJSON,
|
|
80
|
+
"index.d.ts": result.typesDTS,
|
|
81
|
+
"index.ts": result.indexTS,
|
|
82
|
+
"runtime.ts": `/**\n * Runtime factory constructors — re-exported from core.\n */\nexport { createResource, createProperty } from "@intentius/chant/runtime";\n`,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Build the runtime index with the factory constructor exports.
|
|
89
|
+
*/
|
|
90
|
+
function buildRuntimeIndex(results: CplnParseResult[], naming: NamingStrategy): string {
|
|
91
|
+
const resourceEntries: RuntimeIndexEntry[] = [];
|
|
92
|
+
const propertyEntries: RuntimeIndexPropertyEntry[] = [];
|
|
93
|
+
|
|
94
|
+
for (const r of results) {
|
|
95
|
+
const typeName = r.resource.typeName;
|
|
96
|
+
const tsName = naming.resolve(typeName);
|
|
97
|
+
if (!tsName) continue;
|
|
98
|
+
|
|
99
|
+
if (r.isProperty) {
|
|
100
|
+
propertyEntries.push({ tsName, resourceType: typeName });
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const attrs: Record<string, string> = {};
|
|
105
|
+
for (const attr of r.resource.attributes) attrs[attr.name] = attr.name;
|
|
106
|
+
resourceEntries.push({ tsName, resourceType: typeName, attrs });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return coreGenerateRuntimeIndex(resourceEntries, propertyEntries, { lexiconName: "cpln" });
|
|
110
|
+
}
|