@intentius/chant-lexicon-gcp 0.0.15

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 (122) hide show
  1. package/dist/integrity.json +36 -0
  2. package/dist/manifest.json +12 -0
  3. package/dist/meta.json +10919 -0
  4. package/dist/rules/gcp-helpers.ts +117 -0
  5. package/dist/rules/hardcoded-project.ts +58 -0
  6. package/dist/rules/hardcoded-region.ts +56 -0
  7. package/dist/rules/public-iam.ts +43 -0
  8. package/dist/rules/wgc101.ts +56 -0
  9. package/dist/rules/wgc102.ts +35 -0
  10. package/dist/rules/wgc103.ts +45 -0
  11. package/dist/rules/wgc104.ts +42 -0
  12. package/dist/rules/wgc105.ts +46 -0
  13. package/dist/rules/wgc106.ts +36 -0
  14. package/dist/rules/wgc107.ts +39 -0
  15. package/dist/rules/wgc108.ts +41 -0
  16. package/dist/rules/wgc109.ts +39 -0
  17. package/dist/rules/wgc110.ts +38 -0
  18. package/dist/rules/wgc111.ts +54 -0
  19. package/dist/rules/wgc112.ts +56 -0
  20. package/dist/rules/wgc113.ts +42 -0
  21. package/dist/rules/wgc201.ts +36 -0
  22. package/dist/rules/wgc202.ts +39 -0
  23. package/dist/rules/wgc203.ts +44 -0
  24. package/dist/rules/wgc204.ts +39 -0
  25. package/dist/rules/wgc301.ts +34 -0
  26. package/dist/rules/wgc302.ts +34 -0
  27. package/dist/rules/wgc303.ts +37 -0
  28. package/dist/skills/chant-gcp-patterns.md +367 -0
  29. package/dist/skills/chant-gcp-security.md +276 -0
  30. package/dist/skills/chant-gcp.md +108 -0
  31. package/dist/types/index.d.ts +26529 -0
  32. package/package.json +35 -0
  33. package/src/actions/index.ts +52 -0
  34. package/src/codegen/docs-cli.ts +7 -0
  35. package/src/codegen/docs.ts +820 -0
  36. package/src/codegen/generate-cli.ts +24 -0
  37. package/src/codegen/generate.ts +252 -0
  38. package/src/codegen/naming.test.ts +49 -0
  39. package/src/codegen/naming.ts +132 -0
  40. package/src/codegen/package.ts +66 -0
  41. package/src/composites/cloud-function.ts +117 -0
  42. package/src/composites/cloud-run-service.ts +124 -0
  43. package/src/composites/cloud-sql-instance.ts +126 -0
  44. package/src/composites/composites.test.ts +432 -0
  45. package/src/composites/gcs-bucket.ts +111 -0
  46. package/src/composites/gke-cluster.ts +125 -0
  47. package/src/composites/index.ts +20 -0
  48. package/src/composites/managed-certificate.ts +79 -0
  49. package/src/composites/private-service.ts +95 -0
  50. package/src/composites/pubsub-pipeline.ts +102 -0
  51. package/src/composites/secure-project.ts +128 -0
  52. package/src/composites/vpc-network.ts +165 -0
  53. package/src/coverage.test.ts +27 -0
  54. package/src/coverage.ts +51 -0
  55. package/src/default-labels.test.ts +111 -0
  56. package/src/default-labels.ts +93 -0
  57. package/src/generated/index.d.ts +26529 -0
  58. package/src/generated/index.ts +1723 -0
  59. package/src/generated/lexicon-gcp.json +10919 -0
  60. package/src/generated/runtime.ts +4 -0
  61. package/src/import/generator.test.ts +125 -0
  62. package/src/import/generator.ts +82 -0
  63. package/src/import/parser.test.ts +167 -0
  64. package/src/import/parser.ts +80 -0
  65. package/src/import/roundtrip.test.ts +66 -0
  66. package/src/index.ts +54 -0
  67. package/src/lint/post-synth/gcp-helpers.ts +117 -0
  68. package/src/lint/post-synth/index.ts +20 -0
  69. package/src/lint/post-synth/post-synth.test.ts +693 -0
  70. package/src/lint/post-synth/wgc101.ts +56 -0
  71. package/src/lint/post-synth/wgc102.ts +35 -0
  72. package/src/lint/post-synth/wgc103.ts +45 -0
  73. package/src/lint/post-synth/wgc104.ts +42 -0
  74. package/src/lint/post-synth/wgc105.ts +46 -0
  75. package/src/lint/post-synth/wgc106.ts +36 -0
  76. package/src/lint/post-synth/wgc107.ts +39 -0
  77. package/src/lint/post-synth/wgc108.ts +41 -0
  78. package/src/lint/post-synth/wgc109.ts +39 -0
  79. package/src/lint/post-synth/wgc110.ts +38 -0
  80. package/src/lint/post-synth/wgc111.ts +54 -0
  81. package/src/lint/post-synth/wgc112.ts +56 -0
  82. package/src/lint/post-synth/wgc113.ts +42 -0
  83. package/src/lint/post-synth/wgc201.ts +36 -0
  84. package/src/lint/post-synth/wgc202.ts +39 -0
  85. package/src/lint/post-synth/wgc203.ts +44 -0
  86. package/src/lint/post-synth/wgc204.ts +39 -0
  87. package/src/lint/post-synth/wgc301.ts +34 -0
  88. package/src/lint/post-synth/wgc302.ts +34 -0
  89. package/src/lint/post-synth/wgc303.ts +37 -0
  90. package/src/lint/rules/hardcoded-project.ts +58 -0
  91. package/src/lint/rules/hardcoded-region.ts +56 -0
  92. package/src/lint/rules/index.ts +3 -0
  93. package/src/lint/rules/public-iam.ts +43 -0
  94. package/src/lint/rules/rules.test.ts +63 -0
  95. package/src/lsp/completions.test.ts +67 -0
  96. package/src/lsp/completions.ts +17 -0
  97. package/src/lsp/hover.test.ts +66 -0
  98. package/src/lsp/hover.ts +54 -0
  99. package/src/package-cli.ts +24 -0
  100. package/src/plugin.test.ts +250 -0
  101. package/src/plugin.ts +405 -0
  102. package/src/pseudo.test.ts +40 -0
  103. package/src/pseudo.ts +19 -0
  104. package/src/serializer.test.ts +250 -0
  105. package/src/serializer.ts +232 -0
  106. package/src/skills/chant-gcp-patterns.md +367 -0
  107. package/src/skills/chant-gcp-security.md +276 -0
  108. package/src/skills/chant-gcp.md +108 -0
  109. package/src/spec/fetch.test.ts +16 -0
  110. package/src/spec/fetch.ts +121 -0
  111. package/src/spec/parse.test.ts +163 -0
  112. package/src/spec/parse.ts +432 -0
  113. package/src/testdata/compute-instance.yaml +93 -0
  114. package/src/testdata/iam-policy-member.yaml +66 -0
  115. package/src/testdata/manifests/compute-instance.yaml +18 -0
  116. package/src/testdata/manifests/full-app.yaml +34 -0
  117. package/src/testdata/manifests/storage-bucket.yaml +12 -0
  118. package/src/testdata/storage-bucket.yaml +100 -0
  119. package/src/validate-cli.ts +13 -0
  120. package/src/validate.test.ts +38 -0
  121. package/src/validate.ts +30 -0
  122. package/src/variables.ts +15 -0
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * CLI entry point for GCP lexicon generation.
4
+ */
5
+
6
+ import { dirname } from "path";
7
+ import { fileURLToPath } from "url";
8
+ import { generate, writeGeneratedFiles } from "./generate";
9
+
10
+ // src/codegen/generate-cli.ts → dirname x3 → package root
11
+ const pkgDir = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
12
+
13
+ const verbose = process.argv.includes("--verbose") || !process.argv.includes("--quiet");
14
+ const force = process.argv.includes("--force");
15
+
16
+ const result = await generate({ verbose, force });
17
+ writeGeneratedFiles(result, pkgDir);
18
+
19
+ console.error(
20
+ `Generated ${result.resources} resources, ${result.properties} property types, ${result.enums} enums`,
21
+ );
22
+ if (result.warnings.length > 0) {
23
+ console.error(`${result.warnings.length} warnings`);
24
+ }
@@ -0,0 +1,252 @@
1
+ /**
2
+ * GCP Config Connector generation pipeline — uses core generatePipeline
3
+ * with GCP-specific fetch, parse, naming, and generation callbacks.
4
+ */
5
+
6
+ import {
7
+ generatePipeline,
8
+ writeGeneratedArtifacts,
9
+ type GenerateOptions,
10
+ type GenerateResult,
11
+ type GeneratePipelineConfig,
12
+ } from "@intentius/chant/codegen/generate";
13
+ import { fetchCRDBundle } from "../spec/fetch";
14
+ import { parseGcpCRD, type GcpParseResult } from "../spec/parse";
15
+ import { NamingStrategy, propertyTypeName } from "./naming";
16
+ import {
17
+ generateRuntimeIndex as coreGenerateRuntimeIndex,
18
+ type RuntimeIndexEntry,
19
+ type RuntimeIndexPropertyEntry,
20
+ } from "@intentius/chant/codegen/generate-runtime-index";
21
+
22
+ export type { GenerateOptions, GenerateResult };
23
+
24
+ const gcpPipelineConfig: GeneratePipelineConfig<GcpParseResult> = {
25
+ fetchSchemas: async (opts) => {
26
+ return fetchCRDBundle(opts.force);
27
+ },
28
+
29
+ parseSchema: (_typeName, data) => {
30
+ const results = parseGcpCRD(data);
31
+ // Each CRD file may contain one or more resources
32
+ // We return the first one; additional ones get collected in augmentResults
33
+ if (results.length === 0) return null;
34
+ return results[0];
35
+ },
36
+
37
+ createNaming: (results) => new NamingStrategy(results),
38
+
39
+ augmentResults: (results, _opts, log) => {
40
+ const warnings: Array<{ file: string; error: string }> = [];
41
+ log(`Total: ${results.length} GCP Config Connector resource schemas`);
42
+ return { results, warnings };
43
+ },
44
+
45
+ generateRegistry: (results, naming) => {
46
+ return generateLexiconJSON(results, naming as NamingStrategy);
47
+ },
48
+
49
+ generateTypes: (results, naming) => {
50
+ return generateTypeScriptDeclarations(results, naming as NamingStrategy);
51
+ },
52
+
53
+ generateRuntimeIndex: (results, naming) => {
54
+ return generateRuntimeIndex(results, naming as NamingStrategy);
55
+ },
56
+ };
57
+
58
+ /**
59
+ * Run the full GCP generation pipeline.
60
+ */
61
+ export async function generate(opts: GenerateOptions = {}): Promise<GenerateResult> {
62
+ return generatePipeline(gcpPipelineConfig, opts);
63
+ }
64
+
65
+ /**
66
+ * Write generated artifacts to disk.
67
+ */
68
+ export function writeGeneratedFiles(result: GenerateResult, baseDir: string): void {
69
+ writeGeneratedArtifacts({
70
+ baseDir,
71
+ files: {
72
+ "lexicon-gcp.json": result.lexiconJSON,
73
+ "index.d.ts": result.typesDTS,
74
+ "index.ts": result.indexTS,
75
+ "runtime.ts": [
76
+ "/**",
77
+ " * Runtime factory constructors — re-exported from core.",
78
+ " */",
79
+ 'export { createResource, createProperty } from "@intentius/chant/runtime";',
80
+ "",
81
+ ].join("\n"),
82
+ },
83
+ });
84
+ }
85
+
86
+ /**
87
+ * Extract the property-type suffix from a GCP `::` name and flatten to valid TS identifier.
88
+ * "GCP::Alloydb::Backup::EncryptionConfig", "GCP::Alloydb::Backup" → "EncryptionConfig"
89
+ * "GCP::Acm::AccessLevel::Custom::Expr", "GCP::Acm::AccessLevel" → "Custom_Expr"
90
+ */
91
+ function gcpDefName(ptName: string, resourceType: string): string {
92
+ const prefix = `${resourceType}::`;
93
+ const raw = ptName.startsWith(prefix) ? ptName.slice(prefix.length) : ptName.split("::").pop()!;
94
+ return raw.replace(/::/g, "_");
95
+ }
96
+
97
+ // ── Lexicon JSON generation ─────────────────────────────────────────
98
+
99
+ function generateLexiconJSON(results: GcpParseResult[], naming: NamingStrategy): string {
100
+ const entries: Record<string, unknown> = {};
101
+
102
+ for (const r of results) {
103
+ const cfnType = r.resource.typeName;
104
+ const tsName = naming.resolve(cfnType);
105
+ if (!tsName) continue;
106
+
107
+ const apiVersion = `${r.gvk.group}/${r.gvk.version}`;
108
+
109
+ entries[tsName] = {
110
+ resourceType: cfnType,
111
+ kind: "resource",
112
+ apiVersion,
113
+ gvkKind: r.gvk.kind,
114
+ group: r.gvk.group,
115
+ properties: r.resource.properties.length,
116
+ attrs: Object.fromEntries(
117
+ r.resource.attributes.map((a) => [a.name, a.tsType]),
118
+ ),
119
+ };
120
+
121
+ // Add property types
122
+ for (const pt of r.propertyTypes) {
123
+ const defName = gcpDefName(pt.name, cfnType);
124
+ const ptName = propertyTypeName(tsName, defName);
125
+ entries[ptName] = {
126
+ resourceType: `${cfnType}.${pt.specType}`,
127
+ kind: "property",
128
+ };
129
+ }
130
+ }
131
+
132
+ return JSON.stringify(entries, null, 2);
133
+ }
134
+
135
+ // ── TypeScript declarations generation ──────────────────────────────
136
+
137
+ function generateTypeScriptDeclarations(results: GcpParseResult[], naming: NamingStrategy): string {
138
+ const lines: string[] = [
139
+ "/**",
140
+ " * GCP Config Connector resource type declarations.",
141
+ " *",
142
+ " * Auto-generated — do not edit.",
143
+ " */",
144
+ "",
145
+ ];
146
+
147
+ const emittedTypes = new Set<string>();
148
+
149
+ for (const r of results) {
150
+ const cfnType = r.resource.typeName;
151
+ const tsName = naming.resolve(cfnType);
152
+ if (!tsName) continue;
153
+
154
+ // Resource class interface
155
+ const desc = (r.resource.description ?? cfnType).replace(/\*\//g, "*\\/");
156
+ lines.push(`/** ${desc} */`);
157
+ lines.push(`export declare class ${tsName} {`);
158
+ lines.push(` constructor(props: ${tsName}Props);`);
159
+
160
+ // Attributes
161
+ for (const attr of r.resource.attributes) {
162
+ lines.push(` readonly ${attr.name}: ${attr.tsType};`);
163
+ }
164
+
165
+ lines.push(`}`);
166
+ lines.push("");
167
+
168
+ // Props interface
169
+ lines.push(`export interface ${tsName}Props {`);
170
+ for (const prop of r.resource.properties) {
171
+ const optional = prop.required ? "" : "?";
172
+ const safeName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(prop.name) ? prop.name : JSON.stringify(prop.name);
173
+ const propDesc = prop.description ? ` /** ${prop.description.replace(/\*\//g, "*\\/")} */\n` : "";
174
+ lines.push(`${propDesc} ${safeName}${optional}: ${prop.tsType};`);
175
+ }
176
+ lines.push(`}`);
177
+ lines.push("");
178
+
179
+ // Aliases
180
+ for (const alias of naming.aliases(cfnType)) {
181
+ lines.push(`/** Alias for ${tsName} */`);
182
+ lines.push(`export declare const ${alias}: typeof ${tsName};`);
183
+ lines.push("");
184
+ }
185
+
186
+ // Property types (skip duplicates from singularization collisions)
187
+ for (const pt of r.propertyTypes) {
188
+ const defName = gcpDefName(pt.name, cfnType);
189
+ const ptName = propertyTypeName(tsName, defName);
190
+ if (emittedTypes.has(ptName)) continue;
191
+ emittedTypes.add(ptName);
192
+
193
+ lines.push(`export declare class ${ptName} {`);
194
+ lines.push(` constructor(props: ${ptName}Props);`);
195
+ lines.push(`}`);
196
+ lines.push("");
197
+
198
+ lines.push(`export interface ${ptName}Props {`);
199
+ for (const prop of pt.properties) {
200
+ const optional = prop.required ? "" : "?";
201
+ const safeName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(prop.name) ? prop.name : JSON.stringify(prop.name);
202
+ lines.push(` ${safeName}${optional}: ${prop.tsType};`);
203
+ }
204
+ lines.push(`}`);
205
+ lines.push("");
206
+ }
207
+ }
208
+
209
+ return lines.join("\n");
210
+ }
211
+
212
+ // ── Runtime index generation ────────────────────────────────────────
213
+
214
+ function generateRuntimeIndex(results: GcpParseResult[], naming: NamingStrategy): string {
215
+ const resourceEntries: RuntimeIndexEntry[] = [];
216
+ const propertyEntries: RuntimeIndexPropertyEntry[] = [];
217
+ const emittedProperties = new Set<string>();
218
+
219
+ for (const r of results) {
220
+ const cfnType = r.resource.typeName;
221
+ const tsName = naming.resolve(cfnType);
222
+ if (!tsName) continue;
223
+
224
+ const attrs: Record<string, string> = {};
225
+ for (const a of r.resource.attributes) {
226
+ attrs[a.name] = a.name;
227
+ }
228
+
229
+ resourceEntries.push({ tsName, resourceType: cfnType, attrs });
230
+
231
+ for (const alias of naming.aliases(cfnType)) {
232
+ resourceEntries.push({ tsName: alias, resourceType: cfnType, attrs });
233
+ }
234
+
235
+ for (const pt of r.propertyTypes) {
236
+ const defName = gcpDefName(pt.name, cfnType);
237
+ const ptName = propertyTypeName(tsName, defName);
238
+ if (emittedProperties.has(ptName)) continue;
239
+ emittedProperties.add(ptName);
240
+ const ptCfnType = `${cfnType}.${pt.specType}`;
241
+ propertyEntries.push({ tsName: ptName, resourceType: ptCfnType });
242
+ }
243
+ }
244
+
245
+ return coreGenerateRuntimeIndex(resourceEntries, propertyEntries, {
246
+ lexiconName: "gcp",
247
+ pseudoReExports: {
248
+ names: ["GCP", "ProjectId", "Region", "Zone"],
249
+ from: "../pseudo",
250
+ },
251
+ });
252
+ }
@@ -0,0 +1,49 @@
1
+ import { describe, test, expect } from "bun:test";
2
+ import { NamingStrategy } from "./naming";
3
+ import type { GcpParseResult } from "../spec/parse";
4
+
5
+ function makeResult(typeName: string): GcpParseResult {
6
+ return {
7
+ resource: {
8
+ typeName,
9
+ properties: [],
10
+ attributes: [],
11
+ deprecatedProperties: [],
12
+ },
13
+ propertyTypes: [],
14
+ enums: [],
15
+ gvk: { group: "", version: "v1beta1", kind: typeName.split("::").pop()! },
16
+ };
17
+ }
18
+
19
+ describe("NamingStrategy", () => {
20
+ test("resolves priority names", () => {
21
+ const results = [
22
+ makeResult("GCP::Compute::Instance"),
23
+ makeResult("GCP::Storage::Bucket"),
24
+ makeResult("GCP::Container::Cluster"),
25
+ ];
26
+ const naming = new NamingStrategy(results);
27
+
28
+ expect(naming.resolve("GCP::Compute::Instance")).toBe("ComputeInstance");
29
+ expect(naming.resolve("GCP::Storage::Bucket")).toBe("StorageBucket");
30
+ expect(naming.resolve("GCP::Container::Cluster")).toBe("GKECluster");
31
+ });
32
+
33
+ test("provides aliases", () => {
34
+ const results = [makeResult("GCP::Container::Cluster")];
35
+ const naming = new NamingStrategy(results);
36
+
37
+ const aliases = naming.aliases("GCP::Container::Cluster");
38
+ expect(aliases).toContain("GKE");
39
+ });
40
+
41
+ test("resolves non-priority types by short name", () => {
42
+ const results = [makeResult("GCP::Accesscontextmanager::AccessLevel")];
43
+ const naming = new NamingStrategy(results);
44
+
45
+ const name = naming.resolve("GCP::Accesscontextmanager::AccessLevel");
46
+ expect(name).toBeDefined();
47
+ expect(name!.length).toBeGreaterThan(0);
48
+ });
49
+ });
@@ -0,0 +1,132 @@
1
+ /**
2
+ * GCP-specific naming configuration for the core NamingStrategy.
3
+ *
4
+ * Maps Config Connector resource types to concise TypeScript class names.
5
+ */
6
+
7
+ import {
8
+ NamingStrategy as CoreNamingStrategy,
9
+ type NamingConfig,
10
+ type NamingInput,
11
+ } from "@intentius/chant/codegen/naming";
12
+
13
+ export { propertyTypeName, extractDefName } from "@intentius/chant/codegen/naming";
14
+
15
+ import type { GcpParseResult } from "../spec/parse";
16
+ import { gcpShortName, gcpServiceName } from "../spec/parse";
17
+
18
+ /**
19
+ * Fixed TypeScript class names for key GCP resources.
20
+ */
21
+ const priorityNames: Record<string, string> = {
22
+ "GCP::Compute::Instance": "ComputeInstance",
23
+ "GCP::Compute::Network": "VPCNetwork",
24
+ "GCP::Compute::Subnetwork": "Subnetwork",
25
+ "GCP::Compute::Firewall": "Firewall",
26
+ "GCP::Compute::Address": "ComputeAddress",
27
+ "GCP::Compute::Disk": "ComputeDisk",
28
+ "GCP::Compute::ForwardingRule": "ForwardingRule",
29
+ "GCP::Compute::HealthCheck": "ComputeHealthCheck",
30
+ "GCP::Compute::Router": "Router",
31
+ "GCP::Compute::RouterNAT": "RouterNAT",
32
+ "GCP::Compute::BackendService": "BackendService",
33
+ "GCP::Compute::TargetHTTPProxy": "TargetHTTPProxy",
34
+ "GCP::Compute::URLMap": "URLMap",
35
+ "GCP::Compute::SSLCertificate": "SSLCertificate",
36
+ "GCP::Storage::Bucket": "StorageBucket",
37
+ "GCP::Sql::Instance": "SQLInstance",
38
+ "GCP::Sql::Database": "SQLDatabase",
39
+ "GCP::Sql::User": "SQLUser",
40
+ "GCP::Container::Cluster": "GKECluster",
41
+ "GCP::Container::NodePool": "NodePool",
42
+ "GCP::Iam::ServiceAccount": "GCPServiceAccount",
43
+ "GCP::Iam::PolicyMember": "IAMPolicyMember",
44
+ "GCP::Iam::Policy": "IAMPolicy",
45
+ "GCP::Iam::AuditConfig": "IAMAuditConfig",
46
+ "GCP::Run::Service": "CloudRunService",
47
+ "GCP::Run::Job": "CloudRunJob",
48
+ "GCP::Pubsub::Topic": "PubSubTopic",
49
+ "GCP::Pubsub::Subscription": "PubSubSubscription",
50
+ "GCP::Bigquery::Dataset": "BigQueryDataset",
51
+ "GCP::Bigquery::Table": "BigQueryTable",
52
+ "GCP::Dns::ManagedZone": "DNSManagedZone",
53
+ "GCP::Dns::RecordSet": "DNSRecordSet",
54
+ "GCP::Logging::Metric": "LoggingMetric",
55
+ "GCP::Monitoring::AlertPolicy": "AlertPolicy",
56
+ "GCP::Secretmanager::Secret": "SecretManagerSecret",
57
+ "GCP::Secretmanager::SecretVersion": "SecretManagerSecretVersion",
58
+ "GCP::Artifactregistry::Repository": "ArtifactRegistryRepository",
59
+ "GCP::Cloudfunctions::Function": "CloudFunction",
60
+ "GCP::Kms::KeyRing": "KMSKeyRing",
61
+ "GCP::Kms::CryptoKey": "KMSCryptoKey",
62
+ "GCP::Redis::Instance": "RedisInstance",
63
+ "GCP::Memcache::Instance": "MemcacheInstance",
64
+ "GCP::Spanner::Instance": "SpannerInstance",
65
+ "GCP::Spanner::Database": "SpannerDatabase",
66
+ "GCP::Dataflow::Job": "DataflowJob",
67
+ "GCP::Bigtable::Instance": "BigtableInstance",
68
+ "GCP::Bigtable::Table": "BigtableTable",
69
+ };
70
+
71
+ /**
72
+ * Additional TypeScript names beyond the primary priority name.
73
+ */
74
+ const priorityAliases: Record<string, string[]> = {
75
+ "GCP::Compute::Network": ["VPC"],
76
+ "GCP::Container::Cluster": ["GKE"],
77
+ "GCP::Storage::Bucket": ["GCSBucket"],
78
+ "GCP::Sql::Instance": ["CloudSQL"],
79
+ "GCP::Iam::ServiceAccount": ["GSA"],
80
+ "GCP::Run::Service": ["CloudRun"],
81
+ };
82
+
83
+ /**
84
+ * Service name abbreviations for collision-resolved names.
85
+ */
86
+ const serviceAbbreviations: Record<string, string> = {
87
+ Compute: "Compute",
88
+ Storage: "Gcs",
89
+ Container: "Gke",
90
+ Sql: "Sql",
91
+ Iam: "Iam",
92
+ Bigquery: "BQ",
93
+ Pubsub: "PubSub",
94
+ Dns: "Dns",
95
+ Logging: "Log",
96
+ Monitoring: "Mon",
97
+ Secretmanager: "Sm",
98
+ Artifactregistry: "Ar",
99
+ Cloudfunctions: "Gcf",
100
+ Kms: "Kms",
101
+ Redis: "Redis",
102
+ Run: "Run",
103
+ Spanner: "Spanner",
104
+ Dataflow: "Df",
105
+ Bigtable: "Bt",
106
+ Memcache: "Mc",
107
+ };
108
+
109
+ const gcpNamingConfig: NamingConfig = {
110
+ priorityNames,
111
+ priorityAliases,
112
+ priorityPropertyAliases: {},
113
+ serviceAbbreviations,
114
+ shortName: gcpShortName,
115
+ serviceName: (typeName: string) => {
116
+ const parts = typeName.split("::");
117
+ return parts.length >= 2 ? parts[1] : "Other";
118
+ },
119
+ };
120
+
121
+ /**
122
+ * GCP-specific NamingStrategy — wraps the core algorithm with GCP data tables.
123
+ */
124
+ export class NamingStrategy extends CoreNamingStrategy {
125
+ constructor(results: GcpParseResult[]) {
126
+ const inputs: NamingInput[] = results.map((r) => ({
127
+ typeName: r.resource.typeName,
128
+ propertyTypes: r.propertyTypes,
129
+ }));
130
+ super(inputs, gcpNamingConfig);
131
+ }
132
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * GCP lexicon packaging — delegates to core packagePipeline
3
+ * with GCP-specific manifest building and skill collection.
4
+ */
5
+
6
+ import { createRequire } from "module";
7
+ import { readFileSync } from "fs";
8
+ const require = createRequire(import.meta.url);
9
+ import { dirname } from "path";
10
+ import { fileURLToPath } from "url";
11
+ import {
12
+ packagePipeline,
13
+ collectSkills,
14
+ type PackageOptions,
15
+ type PackageResult,
16
+ } from "@intentius/chant/codegen/package";
17
+ import { generate } from "./generate";
18
+
19
+ export type { PackageOptions, PackageResult };
20
+
21
+ // src/codegen/package.ts → dirname x2 → src/ (srcDir for rule collection)
22
+ const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
23
+
24
+ /**
25
+ * Package the GCP lexicon into a distributable BundleSpec.
26
+ */
27
+ export async function packageLexicon(opts: PackageOptions = {}): Promise<PackageResult> {
28
+ const pkgJson = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf-8"));
29
+
30
+ return packagePipeline(
31
+ {
32
+ generate: (genOpts) => generate({ verbose: genOpts.verbose, force: genOpts.force }),
33
+
34
+ buildManifest: (_genResult) => {
35
+ const { gcpPlugin } = require("../plugin");
36
+
37
+ const pseudoParams: string[] = gcpPlugin.pseudoParameters?.() ?? [];
38
+ const pseudoParameters: Record<string, string> = {};
39
+ for (const p of pseudoParams) {
40
+ const shortName = p.split("::").pop()!;
41
+ pseudoParameters[shortName] = p;
42
+ }
43
+
44
+ return {
45
+ name: "gcp",
46
+ version: pkgJson.version ?? "0.0.0",
47
+ chantVersion: ">=0.1.0",
48
+ namespace: "GCP",
49
+ intrinsics: [],
50
+ pseudoParameters,
51
+ };
52
+ },
53
+
54
+ srcDir: pkgDir,
55
+
56
+ collectSkills: () => {
57
+ const { gcpPlugin } = require("../plugin");
58
+ const skillDefs = gcpPlugin.skills?.() ?? [];
59
+ return collectSkills(skillDefs);
60
+ },
61
+
62
+ version: pkgJson.version ?? "0.0.0",
63
+ },
64
+ opts,
65
+ );
66
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * CloudFunctionWithTrigger composite — CloudFunction + source bucket + optional PubSub/HTTP trigger + invoker IAM.
3
+ */
4
+
5
+ export interface CloudFunctionWithTriggerProps {
6
+ /** Function name. */
7
+ name: string;
8
+ /** Runtime (e.g., "nodejs20", "python312"). */
9
+ runtime: string;
10
+ /** Entry point function name. */
11
+ entryPoint: string;
12
+ /** GCP region. */
13
+ region?: string;
14
+ /** Available memory (default: "256M"). */
15
+ availableMemoryMb?: string;
16
+ /** Timeout in seconds (default: 60). */
17
+ timeout?: number;
18
+ /** Trigger type (default: "http"). */
19
+ triggerType?: "http" | "pubsub";
20
+ /** PubSub topic name (required if triggerType is "pubsub"). */
21
+ triggerTopic?: string;
22
+ /** Allow unauthenticated HTTP invocations (default: false). */
23
+ publicAccess?: boolean;
24
+ /** Environment variables. */
25
+ environmentVariables?: Record<string, string>;
26
+ /** Additional labels. */
27
+ labels?: Record<string, string>;
28
+ /** Namespace for all resources. */
29
+ namespace?: string;
30
+ }
31
+
32
+ export interface CloudFunctionWithTriggerResult {
33
+ function: Record<string, unknown>;
34
+ sourceBucket: Record<string, unknown>;
35
+ invokerIam?: Record<string, unknown>;
36
+ }
37
+
38
+ export function CloudFunctionWithTrigger(props: CloudFunctionWithTriggerProps): CloudFunctionWithTriggerResult {
39
+ const {
40
+ name,
41
+ runtime,
42
+ entryPoint,
43
+ region,
44
+ availableMemoryMb = "256M",
45
+ timeout = 60,
46
+ triggerType = "http",
47
+ triggerTopic,
48
+ publicAccess = false,
49
+ environmentVariables,
50
+ labels: extraLabels = {},
51
+ namespace,
52
+ } = props;
53
+
54
+ const commonLabels: Record<string, string> = {
55
+ "app.kubernetes.io/name": name,
56
+ "app.kubernetes.io/managed-by": "chant",
57
+ ...extraLabels,
58
+ };
59
+
60
+ const sourceBucket: Record<string, unknown> = {
61
+ metadata: {
62
+ name: `${name}-source`,
63
+ ...(namespace && { namespace }),
64
+ labels: { ...commonLabels, "app.kubernetes.io/component": "source" },
65
+ },
66
+ location: region ?? "US",
67
+ uniformBucketLevelAccess: true,
68
+ };
69
+
70
+ const eventTrigger = triggerType === "pubsub" && triggerTopic
71
+ ? { eventType: "google.cloud.pubsub.topic.v1.messagePublished", pubsubTopic: triggerTopic }
72
+ : undefined;
73
+
74
+ const fn: Record<string, unknown> = {
75
+ metadata: {
76
+ name,
77
+ ...(namespace && { namespace }),
78
+ labels: { ...commonLabels, "app.kubernetes.io/component": "function" },
79
+ },
80
+ ...(region && { location: region }),
81
+ runtime,
82
+ entryPoint,
83
+ availableMemoryMb,
84
+ timeout: `${timeout}s`,
85
+ buildConfig: {
86
+ sourceRef: {
87
+ storageBucketRef: { name: `${name}-source` },
88
+ },
89
+ },
90
+ ...(eventTrigger && { eventTrigger }),
91
+ ...(environmentVariables && { environmentVariables }),
92
+ };
93
+
94
+ const result: CloudFunctionWithTriggerResult = {
95
+ function: fn,
96
+ sourceBucket,
97
+ };
98
+
99
+ if (publicAccess && triggerType === "http") {
100
+ result.invokerIam = {
101
+ metadata: {
102
+ name: `${name}-invoker`,
103
+ ...(namespace && { namespace }),
104
+ labels: { ...commonLabels, "app.kubernetes.io/component": "iam" },
105
+ },
106
+ member: "allUsers",
107
+ role: "roles/cloudfunctions.invoker",
108
+ resourceRef: {
109
+ apiVersion: "cloudfunctions.cnrm.cloud.google.com/v1beta1",
110
+ kind: "CloudFunctionsFunction",
111
+ name,
112
+ },
113
+ };
114
+ }
115
+
116
+ return result;
117
+ }