@intentius/chant-lexicon-cpln 0.124.31 → 0.124.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/api.js +88 -0
  2. package/dist/codegen/docs-cli.js +6 -0
  3. package/dist/codegen/docs.js +85 -0
  4. package/dist/codegen/generate-cli.js +25 -0
  5. package/dist/codegen/generate-lexicon.js +50 -0
  6. package/dist/codegen/generate-typescript.js +48 -0
  7. package/dist/codegen/generate.js +86 -0
  8. package/dist/codegen/naming.js +43 -0
  9. package/dist/codegen/package.js +31 -0
  10. package/dist/composites/catalog.js +556 -0
  11. package/dist/composites/cron-job.js +52 -0
  12. package/dist/composites/gvc-environment.js +49 -0
  13. package/dist/composites/index.js +6 -0
  14. package/dist/composites/public-domain.js +70 -0
  15. package/dist/composites/secret-access.js +82 -0
  16. package/dist/composites/serverless-service.js +58 -0
  17. package/dist/composites/stateful-service.js +87 -0
  18. package/dist/coverage.js +104 -0
  19. package/dist/describe-resources.js +264 -0
  20. package/dist/detect.js +52 -0
  21. package/dist/entity-props.js +100 -0
  22. package/dist/generated/index.js +169 -0
  23. package/dist/generated/runtime.js +4 -0
  24. package/dist/index.js +14 -0
  25. package/dist/init-templates.js +125 -0
  26. package/dist/integrity.json +2 -2
  27. package/dist/kinds.js +134 -0
  28. package/dist/lint/audit-catalog.js +90 -0
  29. package/dist/lint/audit-lineage.js +32 -0
  30. package/dist/lint/post-synth/cpl010-unrestricted-outbound.js +34 -0
  31. package/dist/lint/post-synth/cpl011-internal-firewall-scope.js +32 -0
  32. package/dist/lint/post-synth/cpl012-no-literal-credential-env.js +51 -0
  33. package/dist/lint/post-synth/cpl013-qualified-identity-principal.js +40 -0
  34. package/dist/lint/post-synth/cpl014-qualified-secret-ref.js +47 -0
  35. package/dist/lint/post-synth/cpl020-serverless-single-port.js +51 -0
  36. package/dist/lint/post-synth/cpl021-cron-job-shape.js +55 -0
  37. package/dist/lint/post-synth/cpl022-container-ports.js +53 -0
  38. package/dist/lint/post-synth/cpl023-container-resources.js +73 -0
  39. package/dist/lint/post-synth/cpl024-probe-handler.js +43 -0
  40. package/dist/lint/post-synth/cpl025-autoscaling-shape.js +84 -0
  41. package/dist/lint/post-synth/cpl026-scale-to-zero.js +59 -0
  42. package/dist/lint/post-synth/cpl027-capacity-ai-conflict.js +67 -0
  43. package/dist/lint/post-synth/cpl028-volume-set.js +155 -0
  44. package/dist/lint/post-synth/cpl029-link-targets.js +140 -0
  45. package/dist/lint/post-synth/cpl030-domain-routing.js +114 -0
  46. package/dist/lint/post-synth/cpl040-pinned-image.js +41 -0
  47. package/dist/lint/post-synth/cpl041-image-reference-form.js +52 -0
  48. package/dist/lint/post-synth/cpl042-gvc-placement.js +49 -0
  49. package/dist/lint/post-synth/cpl043-policy-scope.js +0 -0
  50. package/dist/lint/post-synth/helpers.js +102 -0
  51. package/dist/lint/post-synth/index.js +42 -0
  52. package/dist/lint/rules/cpl001-no-secret-literals.js +73 -0
  53. package/dist/lint/rules/cpl002-prefer-resource-reference.js +60 -0
  54. package/dist/lint/rules/index.js +6 -0
  55. package/dist/lsp/completions.d.ts.map +1 -1
  56. package/dist/lsp/completions.js +24 -0
  57. package/dist/lsp/hover.d.ts.map +1 -1
  58. package/dist/lsp/hover.js +106 -0
  59. package/dist/manifest.json +1 -1
  60. package/dist/ownership.js +25 -0
  61. package/dist/package-cli.js +21 -0
  62. package/dist/plugin.js +141 -0
  63. package/dist/reference-catalog.js +99 -0
  64. package/dist/serializer.js +217 -0
  65. package/dist/spec/fetch.d.ts.map +1 -1
  66. package/dist/spec/fetch.js +70 -0
  67. package/dist/spec/parse.js +338 -0
  68. package/dist/spec/snapshot-cli.js +60 -0
  69. package/dist/validate-cli.js +7 -0
  70. package/dist/validate.js +88 -0
  71. package/package.json +7 -7
  72. package/src/codegen/package.ts +2 -2
  73. package/src/lsp/completions.ts +2 -1
  74. package/src/lsp/hover.ts +2 -1
  75. package/src/spec/fetch.ts +2 -2
package/dist/api.js ADDED
@@ -0,0 +1,88 @@
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
+ import { collectionPath } from "./kinds.js";
16
+ /** Default API root. `CPLN_ENDPOINT` overrides it for a private deployment. */
17
+ export const DEFAULT_ENDPOINT = "https://api.cpln.io";
18
+ /** Thrown when there is no org or no token to read with. */
19
+ export class CplnCredentialsError extends Error {
20
+ }
21
+ /**
22
+ * Resolve endpoint, org and token from the environment.
23
+ *
24
+ * `CPLN_TOKEN` is the documented CI variable and the one Control Plane's own
25
+ * guidance prefers over `--token`, which leaks into process listings and logs.
26
+ * A service account key works in the same header.
27
+ */
28
+ export function resolveConfig(overrides = {}) {
29
+ const endpoint = overrides.endpoint ?? process.env.CPLN_ENDPOINT ?? DEFAULT_ENDPOINT;
30
+ const org = overrides.org ?? process.env.CPLN_ORG ?? "";
31
+ const token = overrides.token ?? process.env.CPLN_TOKEN ?? "";
32
+ if (!org) {
33
+ throw new CplnCredentialsError("No Control Plane org. Set CPLN_ORG (or pass `org`) — it is the top-level isolation boundary and " +
34
+ "every read path is scoped to it.");
35
+ }
36
+ if (!token) {
37
+ throw new CplnCredentialsError("No Control Plane token. Set CPLN_TOKEN to a service account key or a JWT. Prefer the env var over " +
38
+ "`--token`, which leaks into logs.");
39
+ }
40
+ return { endpoint: endpoint.replace(/\/+$/, ""), org, token };
41
+ }
42
+ /**
43
+ * Build the `Authorization` header value.
44
+ *
45
+ * The spec declares two schemes on the same header: `serviceAccountKey` is an
46
+ * apiKey sent bare, `jwt` is an HTTP bearer token. They are distinguishable by
47
+ * shape — a JWT is three base64url segments and always starts `eyJ` — so a
48
+ * caller does not have to say which kind of credential they have. An
49
+ * already-prefixed value is passed through so an explicit `Bearer …` works too.
50
+ */
51
+ export function authorization(token) {
52
+ if (token.startsWith("Bearer "))
53
+ return token;
54
+ return /^eyJ[A-Za-z0-9_-]+\./.test(token) ? `Bearer ${token}` : token;
55
+ }
56
+ /** The default transport — `fetch` with the documented auth header. */
57
+ export function defaultCplnHttp(config) {
58
+ return {
59
+ async get(path) {
60
+ const response = await fetch(`${config.endpoint}${path}`, {
61
+ headers: { Authorization: authorization(config.token), Accept: "application/json" },
62
+ });
63
+ if (response.status === 404)
64
+ return undefined;
65
+ if (!response.ok) {
66
+ throw new Error(`GET ${path} → ${response.status} ${response.statusText}`);
67
+ }
68
+ return response.json();
69
+ },
70
+ };
71
+ }
72
+ /**
73
+ * List every resource of one kind in the org, keyed by name.
74
+ *
75
+ * Returns an empty map for a 404, which the API uses for a kind the org has
76
+ * none of as well as for one the token cannot see — the distinction is not
77
+ * available here, and the observer treats an absent resource as absent either
78
+ * way.
79
+ */
80
+ export async function listKind(http, config, kind) {
81
+ const body = (await http.get(collectionPath(kind, config.org)));
82
+ const index = new Map();
83
+ for (const item of body?.items ?? []) {
84
+ if (typeof item.name === "string")
85
+ index.set(item.name, item);
86
+ }
87
+ return index;
88
+ }
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env tsx
2
+ import { generateDocs } from "./docs.js";
3
+ generateDocs({ verbose: true }).catch((err) => {
4
+ console.error(err);
5
+ process.exit(1);
6
+ });
@@ -0,0 +1,85 @@
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
+ import { dirname, join } from "path";
12
+ import { fileURLToPath } from "url";
13
+ import { docsPipeline, writeDocsSite } from "@intentius/chant/codegen/docs";
14
+ const pkgDir = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
15
+ 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.
16
+
17
+ Types are generated from Control Plane's served OpenAPI document (\`https://api.cpln.io/openapi.json\`), so they track the real API.
18
+
19
+ Install it with:
20
+
21
+ \`\`\`bash
22
+ npm install --save-dev @intentius/chant-lexicon-cpln
23
+ \`\`\`
24
+
25
+ ## Quick Start
26
+
27
+ \`\`\`typescript
28
+ import { Gvc, Workload } from "@intentius/chant-lexicon-cpln";
29
+
30
+ export const gvc = new Gvc({
31
+ name: "prod",
32
+ spec: {
33
+ staticPlacement: {
34
+ locationLinks: ["/org/acme/location/aws-us-east-1"], // placement is GVC-level
35
+ },
36
+ },
37
+ });
38
+
39
+ export const web = new Workload({
40
+ name: "web",
41
+ gvc: "prod",
42
+ spec: {
43
+ type: "serverless", // exactly one HTTP port
44
+ containers: [{
45
+ name: "main",
46
+ image: "nginx:1.27",
47
+ ports: [{ number: 8080, protocol: "http" }],
48
+ }],
49
+ firewallConfig: {
50
+ external: { inboundAllowCIDR: ["0.0.0.0/0"] }, // closed by default
51
+ },
52
+ },
53
+ });
54
+ \`\`\`
55
+
56
+ ## The loop
57
+
58
+ 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.
59
+ 2. \`cpln apply --file dist/cpln.yaml --ready\` — reconcile. Ordering across documents is resolved by \`cpln apply\` itself.
60
+ 3. \`chant plan\` — read the live org back and diff. Ownership comes from the \`tags\` marker chant stamps at synthesis.
61
+ `;
62
+ const outputFormat = "multi-document Control Plane YAML for `cpln apply --file`";
63
+ /**
64
+ * Generate the documentation site for the cpln lexicon.
65
+ */
66
+ export async function generateDocs(options) {
67
+ const config = {
68
+ name: "cpln",
69
+ displayName: "Control Plane",
70
+ description: "Control Plane (cpln) GVCs, workloads, identities and secrets as typed estate.",
71
+ distDir: join(pkgDir, "dist"),
72
+ outDir: join(pkgDir, "docs"),
73
+ srcDir: join(pkgDir, "src"),
74
+ basePath: process.env.DOCS_BASE_PATH ?? "/chant/lexicons/cpln/",
75
+ overview,
76
+ outputFormat,
77
+ serviceFromType: (type) => type.split("::")[1] ?? type,
78
+ };
79
+ const result = docsPipeline(config);
80
+ writeDocsSite(config, result);
81
+ if (options?.verbose) {
82
+ console.error(`Generated docs: ${result.stats.resources} resources, ${result.stats.properties} properties, ` +
83
+ `${result.stats.services} services, ${result.stats.rules} rules`);
84
+ }
85
+ }
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * CLI entry point for cpln lexicon generation.
4
+ */
5
+ import { dirname } from "path";
6
+ import { fileURLToPath } from "url";
7
+ import { generate, writeGeneratedFiles } from "./generate.js";
8
+ const pkgDir = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
9
+ async function main() {
10
+ const verbose = process.argv.includes("--verbose") || process.argv.includes("-v");
11
+ const force = process.argv.includes("--force") || process.argv.includes("-f");
12
+ console.error("Generating cpln lexicon...");
13
+ const result = await generate({ verbose, force });
14
+ writeGeneratedFiles(result, pkgDir);
15
+ console.error(`Generated ${result.resources} resources, ${result.properties} property types, ${result.enums} enums`);
16
+ if (result.warnings.length > 0) {
17
+ console.error(`${result.warnings.length} warnings:`);
18
+ for (const w of result.warnings)
19
+ console.error(` ${w.file}: ${w.error}`);
20
+ }
21
+ }
22
+ main().catch((err) => {
23
+ console.error("Generation failed:", err);
24
+ process.exit(1);
25
+ });
@@ -0,0 +1,50 @@
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
+ import { buildRegistry, serializeRegistry, } from "@intentius/chant/codegen/generate-registry";
6
+ import { cplnShortName, kindByTypeName } from "../kinds.js";
7
+ /**
8
+ * Generate the `lexicon-cpln.json` content.
9
+ */
10
+ export function generateLexiconJSON(results, naming) {
11
+ const registryResources = results.map((r) => ({
12
+ typeName: r.resource.typeName,
13
+ attributes: r.resource.attributes,
14
+ properties: r.resource.properties,
15
+ propertyTypes: [],
16
+ }));
17
+ // Every writable property, with its constraints — see `propertyConstraints`.
18
+ const byTypeName = new Map(results.map((r) => [r.resource.typeName, r]));
19
+ const entries = buildRegistry(registryResources, naming, {
20
+ shortName: cplnShortName,
21
+ buildEntry: (resource) => {
22
+ const kind = kindByTypeName(resource.typeName);
23
+ const entry = {
24
+ resourceType: resource.typeName,
25
+ kind: kind ? "resource" : "property",
26
+ lexicon: "cpln",
27
+ };
28
+ if (kind) {
29
+ // Carried on the entry so the serializer and observation can route a
30
+ // resource without re-deriving the mapping from its class name.
31
+ entry.cplnKind = kind.kind;
32
+ entry.gvcScoped = kind.gvcScoped;
33
+ }
34
+ const parsed = byTypeName.get(resource.typeName);
35
+ const constraints = {};
36
+ for (const property of parsed?.resource.properties ?? []) {
37
+ constraints[property.name] = property.constraints ?? {};
38
+ }
39
+ if (Object.keys(constraints).length > 0)
40
+ entry.propertyConstraints = constraints;
41
+ return entry;
42
+ },
43
+ buildPropertyEntry: (resourceType, propertyType) => ({
44
+ resourceType: `${resourceType}.${propertyType}`,
45
+ kind: "property",
46
+ lexicon: "cpln",
47
+ }),
48
+ });
49
+ return serializeRegistry(entries);
50
+ }
@@ -0,0 +1,48 @@
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
+ import { writeResourceClass, writePropertyClass, } from "@intentius/chant/codegen/generate-typescript";
6
+ /**
7
+ * Generate the complete `.d.ts` content.
8
+ */
9
+ export function generateTypeScriptDeclarations(results, naming) {
10
+ const lines = [];
11
+ lines.push("// Code generated by chant cpln generate. DO NOT EDIT.");
12
+ const entries = [];
13
+ for (const r of results) {
14
+ const tsName = naming.resolve(r.resource.typeName);
15
+ if (!tsName)
16
+ continue;
17
+ entries.push({
18
+ tsName,
19
+ isProperty: !!r.isProperty,
20
+ properties: r.resource.properties.map((p) => ({
21
+ name: p.name,
22
+ type: p.tsType,
23
+ required: p.required,
24
+ description: p.description,
25
+ })),
26
+ attributes: r.resource.attributes.map((a) => ({ name: a.name, type: a.tsType })),
27
+ });
28
+ }
29
+ entries.sort((a, b) => a.tsName.localeCompare(b.tsName));
30
+ const resources = entries.filter((e) => !e.isProperty);
31
+ const propertyTypes = entries.filter((e) => e.isProperty);
32
+ if (resources.length > 0) {
33
+ lines.push("");
34
+ lines.push("// --- Control Plane resource classes ---");
35
+ for (const re of resources) {
36
+ writeResourceClass(lines, re.tsName, re.properties, re.attributes);
37
+ }
38
+ }
39
+ if (propertyTypes.length > 0) {
40
+ lines.push("");
41
+ lines.push("// --- Property type classes ---");
42
+ for (const pe of propertyTypes) {
43
+ writePropertyClass(lines, pe.tsName, pe.properties);
44
+ }
45
+ }
46
+ lines.push("");
47
+ return lines.join("\n");
48
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * cpln generation pipeline — core `generatePipeline` with cpln's fetch, parse,
3
+ * naming and emit callbacks.
4
+ */
5
+ import { existsSync, readFileSync } from "fs";
6
+ import { dirname, join } from "path";
7
+ import { fileURLToPath } from "url";
8
+ import { generatePipeline, writeGeneratedArtifacts, } from "@intentius/chant/codegen/generate";
9
+ import { generateRuntimeIndex as coreGenerateRuntimeIndex, } from "@intentius/chant/codegen/generate-runtime-index";
10
+ import { fetchSchemas } from "../spec/fetch.js";
11
+ import { parseCplnOpenAPI } from "../spec/parse.js";
12
+ import { NamingStrategy } from "./naming.js";
13
+ import { generateLexiconJSON } from "./generate-lexicon.js";
14
+ import { generateTypeScriptDeclarations } from "./generate-typescript.js";
15
+ const PKG_DIR = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
16
+ const SURFACE_SNAPSHOT = join(PKG_DIR, "surface.snapshot.json");
17
+ /**
18
+ * Read the committed surface snapshot, when there is one.
19
+ *
20
+ * Absent before the first publish, which is the honest state for a lexicon
21
+ * with no published names to protect — the naming strategy simply has nothing
22
+ * to reserve.
23
+ */
24
+ function readSurfaceSnapshot() {
25
+ if (!existsSync(SURFACE_SNAPSHOT))
26
+ return undefined;
27
+ try {
28
+ return JSON.parse(readFileSync(SURFACE_SNAPSHOT, "utf-8"));
29
+ }
30
+ catch {
31
+ return undefined;
32
+ }
33
+ }
34
+ /**
35
+ * Run the full cpln generation pipeline.
36
+ */
37
+ export async function generate(opts = {}) {
38
+ const config = {
39
+ fetchSchemas: (fetchOpts) => fetchSchemas({ force: fetchOpts.force }),
40
+ // The spec is one document and `parseSchema` is called once with it; the
41
+ // pipeline accepts an array back, so there is no need for fly's
42
+ // parse-once-stash-the-rest dance.
43
+ parseSchema: (_typeName, data) => parseCplnOpenAPI(data),
44
+ createNaming: (results) => new NamingStrategy(results, { snapshot: readSurfaceSnapshot() }),
45
+ generateRegistry: (results, naming) => generateLexiconJSON(results, naming),
46
+ generateTypes: (results, naming) => generateTypeScriptDeclarations(results, naming),
47
+ generateRuntimeIndex: (results, naming) => buildRuntimeIndex(results, naming),
48
+ };
49
+ return generatePipeline(config, opts);
50
+ }
51
+ /**
52
+ * Write generated artifacts under `src/generated/`.
53
+ */
54
+ export function writeGeneratedFiles(result, baseDir) {
55
+ writeGeneratedArtifacts({
56
+ baseDir,
57
+ files: {
58
+ "lexicon-cpln.json": result.lexiconJSON,
59
+ "index.d.ts": result.typesDTS,
60
+ "index.ts": result.indexTS,
61
+ "runtime.ts": `/**\n * Runtime factory constructors — re-exported from core.\n */\nexport { createResource, createProperty } from "@intentius/chant/runtime";\n`,
62
+ },
63
+ });
64
+ }
65
+ /**
66
+ * Build the runtime index with the factory constructor exports.
67
+ */
68
+ function buildRuntimeIndex(results, naming) {
69
+ const resourceEntries = [];
70
+ const propertyEntries = [];
71
+ for (const r of results) {
72
+ const typeName = r.resource.typeName;
73
+ const tsName = naming.resolve(typeName);
74
+ if (!tsName)
75
+ continue;
76
+ if (r.isProperty) {
77
+ propertyEntries.push({ tsName, resourceType: typeName });
78
+ continue;
79
+ }
80
+ const attrs = {};
81
+ for (const attr of r.resource.attributes)
82
+ attrs[attr.name] = attr.name;
83
+ resourceEntries.push({ tsName, resourceType: typeName, attrs });
84
+ }
85
+ return coreGenerateRuntimeIndex(resourceEntries, propertyEntries, { lexiconName: "cpln" });
86
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * cpln naming strategy.
3
+ *
4
+ * Every generated type is `Cpln::Core::{Name}` with a `{Name}` the parser has
5
+ * already made unique — `$ref`'d schemas take their upstream name and inline
6
+ * shapes take their path — so the core strategy's collision phases never fire
7
+ * and `resolve` returns the last segment unchanged. The value of going through
8
+ * the core strategy anyway is phase 1a: once this lexicon publishes a surface
9
+ * snapshot, a name that has shipped stays attached to the type that shipped it
10
+ * even if an upstream reshuffle would otherwise hand it to a newcomer.
11
+ */
12
+ import { NamingStrategy as CoreNamingStrategy, reservedNamesFromSnapshot, } from "@intentius/chant/codegen/naming";
13
+ import { cplnShortName, cplnServiceName, KINDS, SERVICE } from "../kinds.js";
14
+ export { propertyTypeName, extractDefName } from "@intentius/chant/codegen/naming";
15
+ /**
16
+ * The eight kinds are pinned by hand. They are the names users type, and they
17
+ * should not move because a property type elsewhere in the spec started or
18
+ * stopped existing.
19
+ */
20
+ const priorityNames = Object.fromEntries(KINDS.map((k) => [k.typeName, k.className]));
21
+ const cplnNamingConfig = {
22
+ priorityNames,
23
+ priorityAliases: {},
24
+ priorityPropertyAliases: {},
25
+ serviceAbbreviations: { [SERVICE]: SERVICE },
26
+ shortName: cplnShortName,
27
+ serviceName: cplnServiceName,
28
+ };
29
+ /**
30
+ * cpln-specific naming strategy.
31
+ */
32
+ export class NamingStrategy extends CoreNamingStrategy {
33
+ constructor(results, options = {}) {
34
+ const inputs = results.map((r) => ({
35
+ typeName: r.resource.typeName,
36
+ propertyTypes: r.propertyTypes,
37
+ }));
38
+ super(inputs, {
39
+ ...cplnNamingConfig,
40
+ reservedNames: reservedNamesFromSnapshot(options.snapshot),
41
+ });
42
+ }
43
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Package the cpln lexicon into the distributable bundle under `dist/`.
3
+ */
4
+ import { readFileSync } from "fs";
5
+ import { dirname, join } from "path";
6
+ import { moduleSourceDir } from "@intentius/chant/module-source";
7
+ import { packagePipeline, collectSkills } from "@intentius/chant/codegen/package";
8
+ import { generate } from "./generate.js";
9
+ import { cplnPlugin } from "../plugin.js";
10
+ export async function packageLexicon(options) {
11
+ // This file is `src/codegen/package.ts`; rules and skills are collected
12
+ // relative to `src/`, so srcDir is the parent of this directory. Pointing it
13
+ // at `src/codegen` globs a directory that does not exist and silently ships
14
+ // a bundle with zero rules and zero skills.
15
+ const srcDir = dirname(moduleSourceDir(import.meta.url));
16
+ const pkgJson = JSON.parse(readFileSync(join(srcDir, "..", "package.json"), "utf-8"));
17
+ const { spec, stats } = await packagePipeline({
18
+ generate: (opts) => generate({ verbose: opts?.verbose, force: opts?.force }),
19
+ buildManifest: () => ({
20
+ name: "cpln",
21
+ version: pkgJson.version ?? "0.0.0",
22
+ chantVersion: ">=0.1.0",
23
+ namespace: "Cpln",
24
+ }),
25
+ srcDir,
26
+ collectSkills: () => collectSkills(cplnPlugin.skills?.() ?? []),
27
+ });
28
+ // Both callers (the plugin's `package()` and `package-cli`) print their own
29
+ // summary — don't double-report here.
30
+ return { spec, stats };
31
+ }