@standards-kit/conform 0.1.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/dist/artifactregistry-QQWBMEQN.js +38 -0
- package/dist/artifactregistry-QQWBMEQN.js.map +1 -0
- package/dist/chunk-J5S6GRGW.js +314 -0
- package/dist/chunk-J5S6GRGW.js.map +1 -0
- package/dist/chunk-KHO6NIAI.js +1367 -0
- package/dist/chunk-KHO6NIAI.js.map +1 -0
- package/dist/chunk-M7G73Q6P.js +662 -0
- package/dist/chunk-M7G73Q6P.js.map +1 -0
- package/dist/chunk-P7TIZJ4C.js +85 -0
- package/dist/chunk-P7TIZJ4C.js.map +1 -0
- package/dist/chunk-RXA4FO7L.js +279 -0
- package/dist/chunk-RXA4FO7L.js.map +1 -0
- package/dist/cli.js +7432 -0
- package/dist/cli.js.map +1 -0
- package/dist/cloudrun-O36R23SH.js +31 -0
- package/dist/cloudrun-O36R23SH.js.map +1 -0
- package/dist/cloudwatch-KSZ4A256.js +56 -0
- package/dist/cloudwatch-KSZ4A256.js.map +1 -0
- package/dist/dynamodb-5KVESCVJ.js +51 -0
- package/dist/dynamodb-5KVESCVJ.js.map +1 -0
- package/dist/ec2-HKPE6GZV.js +151 -0
- package/dist/ec2-HKPE6GZV.js.map +1 -0
- package/dist/ecs-OS3NJZTA.js +141 -0
- package/dist/ecs-OS3NJZTA.js.map +1 -0
- package/dist/elasticache-7TCRHYYM.js +151 -0
- package/dist/elasticache-7TCRHYYM.js.map +1 -0
- package/dist/elb-PEDLXW5R.js +151 -0
- package/dist/elb-PEDLXW5R.js.map +1 -0
- package/dist/generate-D4MFMOHP.js +28 -0
- package/dist/generate-D4MFMOHP.js.map +1 -0
- package/dist/iam-7H5HFWVQ.js +96 -0
- package/dist/iam-7H5HFWVQ.js.map +1 -0
- package/dist/iam-DJI64AGK.js +39 -0
- package/dist/iam-DJI64AGK.js.map +1 -0
- package/dist/index.js +7980 -0
- package/dist/index.js.map +1 -0
- package/dist/infra-UXM5XQX3.js +566 -0
- package/dist/infra-UXM5XQX3.js.map +1 -0
- package/dist/lambda-NFB5UILT.js +60 -0
- package/dist/lambda-NFB5UILT.js.map +1 -0
- package/dist/manifest-7AIL2FK2.js +23 -0
- package/dist/manifest-7AIL2FK2.js.map +1 -0
- package/dist/mcp-O5O7XVFG.js +204 -0
- package/dist/mcp-O5O7XVFG.js.map +1 -0
- package/dist/rds-KLG5O5SI.js +151 -0
- package/dist/rds-KLG5O5SI.js.map +1 -0
- package/dist/registry-V65CC7IN.js +15 -0
- package/dist/registry-V65CC7IN.js.map +1 -0
- package/dist/s3-2DH7PRVR.js +49 -0
- package/dist/s3-2DH7PRVR.js.map +1 -0
- package/dist/scan-EELS42BP.js +593 -0
- package/dist/scan-EELS42BP.js.map +1 -0
- package/dist/secretmanager-RDL62EFW.js +31 -0
- package/dist/secretmanager-RDL62EFW.js.map +1 -0
- package/dist/secretsmanager-MOOIHLAO.js +50 -0
- package/dist/secretsmanager-MOOIHLAO.js.map +1 -0
- package/dist/sns-Y36LVTWA.js +50 -0
- package/dist/sns-Y36LVTWA.js.map +1 -0
- package/dist/sqs-RRS3GRHK.js +61 -0
- package/dist/sqs-RRS3GRHK.js.map +1 -0
- package/dist/src-KZRTG3EU.js +45 -0
- package/dist/src-KZRTG3EU.js.map +1 -0
- package/dist/standards-RXK5G4IG.js +37 -0
- package/dist/standards-RXK5G4IG.js.map +1 -0
- package/dist/sync-RLYBGYNY.js +877 -0
- package/dist/sync-RLYBGYNY.js.map +1 -0
- package/dist/validate-AABLVQJS.js +327 -0
- package/dist/validate-AABLVQJS.js.map +1 -0
- package/dist/validator-6PL5I5EC.js +156 -0
- package/dist/validator-6PL5I5EC.js.map +1 -0
- package/package.json +91 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// src/infra/checkers/gcp/artifactregistry.ts
|
|
2
|
+
import { ArtifactRegistryClient } from "@google-cloud/artifact-registry";
|
|
3
|
+
var client = null;
|
|
4
|
+
function getClient() {
|
|
5
|
+
client ??= new ArtifactRegistryClient();
|
|
6
|
+
return client;
|
|
7
|
+
}
|
|
8
|
+
function result(raw, resourceId, exists, error) {
|
|
9
|
+
return {
|
|
10
|
+
arn: raw,
|
|
11
|
+
exists,
|
|
12
|
+
error,
|
|
13
|
+
service: "artifactregistry",
|
|
14
|
+
resourceType: "repositories",
|
|
15
|
+
resourceId
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
var ArtifactRegistryChecker = {
|
|
19
|
+
async check(resource) {
|
|
20
|
+
const { project, location, resourceId, raw } = resource;
|
|
21
|
+
const repoName = `projects/${project}/locations/${location}/repositories/${resourceId}`;
|
|
22
|
+
try {
|
|
23
|
+
const arClient = getClient();
|
|
24
|
+
await arClient.getRepository({ name: repoName });
|
|
25
|
+
return result(raw, resourceId, true);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
const err = error;
|
|
28
|
+
if (err.code === 5 || err.message?.includes("NOT_FOUND")) {
|
|
29
|
+
return result(raw, resourceId, false);
|
|
30
|
+
}
|
|
31
|
+
return result(raw, resourceId, false, err.message ?? "Unknown error");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
export {
|
|
36
|
+
ArtifactRegistryChecker
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=artifactregistry-QQWBMEQN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/infra/checkers/gcp/artifactregistry.ts"],"sourcesContent":["/**\n * GCP Artifact Registry resource checker\n */\n\nimport { ArtifactRegistryClient } from \"@google-cloud/artifact-registry\";\n\nimport type { ParsedGcpResource, ResourceCheckResult } from \"../../types.js\";\nimport type { GcpResourceChecker } from \"../types.js\";\n\nlet client: ArtifactRegistryClient | null = null;\n\nfunction getClient(): ArtifactRegistryClient {\n client ??= new ArtifactRegistryClient();\n return client;\n}\n\nfunction result(\n raw: string,\n resourceId: string,\n exists: boolean,\n error?: string\n): ResourceCheckResult {\n return {\n arn: raw,\n exists,\n error,\n service: \"artifactregistry\",\n resourceType: \"repositories\",\n resourceId,\n };\n}\n\nexport const ArtifactRegistryChecker: GcpResourceChecker = {\n async check(resource: ParsedGcpResource): Promise<ResourceCheckResult> {\n const { project, location, resourceId, raw } = resource;\n const repoName = `projects/${project}/locations/${location}/repositories/${resourceId}`;\n\n try {\n const arClient = getClient();\n await arClient.getRepository({ name: repoName });\n return result(raw, resourceId, true);\n } catch (error) {\n const err = error as { code?: number; message?: string };\n if (err.code === 5 || err.message?.includes(\"NOT_FOUND\")) {\n return result(raw, resourceId, false);\n }\n return result(raw, resourceId, false, err.message ?? \"Unknown error\");\n }\n },\n};\n"],"mappings":";AAIA,SAAS,8BAA8B;AAKvC,IAAI,SAAwC;AAE5C,SAAS,YAAoC;AAC3C,aAAW,IAAI,uBAAuB;AACtC,SAAO;AACT;AAEA,SAAS,OACP,KACA,YACA,QACA,OACqB;AACrB,SAAO;AAAA,IACL,KAAK;AAAA,IACL;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT,cAAc;AAAA,IACd;AAAA,EACF;AACF;AAEO,IAAM,0BAA8C;AAAA,EACzD,MAAM,MAAM,UAA2D;AACrE,UAAM,EAAE,SAAS,UAAU,YAAY,IAAI,IAAI;AAC/C,UAAM,WAAW,YAAY,OAAO,cAAc,QAAQ,iBAAiB,UAAU;AAErF,QAAI;AACF,YAAM,WAAW,UAAU;AAC3B,YAAM,SAAS,cAAc,EAAE,MAAM,SAAS,CAAC;AAC/C,aAAO,OAAO,KAAK,YAAY,IAAI;AAAA,IACrC,SAAS,OAAO;AACd,YAAM,MAAM;AACZ,UAAI,IAAI,SAAS,KAAK,IAAI,SAAS,SAAS,WAAW,GAAG;AACxD,eAAO,OAAO,KAAK,YAAY,KAAK;AAAA,MACtC;AACA,aAAO,OAAO,KAAK,YAAY,OAAO,IAAI,WAAW,eAAe;AAAA,IACtE;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ManifestError,
|
|
3
|
+
detectAccountFromResource,
|
|
4
|
+
isMultiAccountManifest,
|
|
5
|
+
isValidArn,
|
|
6
|
+
isValidGcpResource
|
|
7
|
+
} from "./chunk-M7G73Q6P.js";
|
|
8
|
+
|
|
9
|
+
// src/infra/generate.ts
|
|
10
|
+
import * as fs from "fs";
|
|
11
|
+
import * as readline from "readline";
|
|
12
|
+
var DEFAULT_MANIFEST_NAME = "infra-manifest.json";
|
|
13
|
+
function cleanResourceIdentifier(value) {
|
|
14
|
+
const pipeIndex = value.indexOf("|");
|
|
15
|
+
if (pipeIndex !== -1) {
|
|
16
|
+
return value.substring(0, pipeIndex);
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
function isValidResource(value) {
|
|
21
|
+
if (typeof value !== "string") {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const cleaned = cleanResourceIdentifier(value);
|
|
25
|
+
return isValidArn(cleaned) || isValidGcpResource(cleaned);
|
|
26
|
+
}
|
|
27
|
+
function extractResourcesFromOutputs(outputs) {
|
|
28
|
+
const resources = [];
|
|
29
|
+
const arnFields = [
|
|
30
|
+
"arn",
|
|
31
|
+
"id",
|
|
32
|
+
"bucketArn",
|
|
33
|
+
"functionArn",
|
|
34
|
+
"secretArn",
|
|
35
|
+
"roleArn",
|
|
36
|
+
"clusterArn",
|
|
37
|
+
"serviceArn",
|
|
38
|
+
"tableArn",
|
|
39
|
+
"topicArn",
|
|
40
|
+
"queueArn",
|
|
41
|
+
"logGroupArn",
|
|
42
|
+
"policyArn",
|
|
43
|
+
// GCP-specific
|
|
44
|
+
"name",
|
|
45
|
+
"selfLink"
|
|
46
|
+
];
|
|
47
|
+
for (const field of arnFields) {
|
|
48
|
+
const value = outputs[field];
|
|
49
|
+
if (isValidResource(value)) {
|
|
50
|
+
resources.push(cleanResourceIdentifier(value));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
for (const [key, value] of Object.entries(outputs)) {
|
|
54
|
+
if (!arnFields.includes(key) && isValidResource(value)) {
|
|
55
|
+
resources.push(cleanResourceIdentifier(value));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return resources;
|
|
59
|
+
}
|
|
60
|
+
function extractProjectName(resources) {
|
|
61
|
+
for (const resource of resources) {
|
|
62
|
+
if (resource.urn) {
|
|
63
|
+
const parts = resource.urn.split("::");
|
|
64
|
+
if (parts.length >= 2) {
|
|
65
|
+
return parts[1];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return void 0;
|
|
70
|
+
}
|
|
71
|
+
function parseStackExport(stackExport, project) {
|
|
72
|
+
if (!stackExport || typeof stackExport !== "object") {
|
|
73
|
+
throw new Error("Invalid stack export: expected an object");
|
|
74
|
+
}
|
|
75
|
+
const typed = stackExport;
|
|
76
|
+
const deployment = typed.deployment;
|
|
77
|
+
if (!deployment?.resources) {
|
|
78
|
+
throw new Error("Invalid stack export: missing deployment.resources");
|
|
79
|
+
}
|
|
80
|
+
const resources = [];
|
|
81
|
+
for (const resource of deployment.resources) {
|
|
82
|
+
if (resource.outputs) {
|
|
83
|
+
const extracted = extractResourcesFromOutputs(resource.outputs);
|
|
84
|
+
resources.push(...extracted);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const uniqueResources = [...new Set(resources)];
|
|
88
|
+
const projectName = project ?? extractProjectName(deployment.resources) ?? "unknown";
|
|
89
|
+
return {
|
|
90
|
+
project: projectName,
|
|
91
|
+
resources: uniqueResources
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
async function readStdin() {
|
|
95
|
+
if (process.stdin.isTTY) {
|
|
96
|
+
throw new Error("No input provided. Pipe Pulumi stack export: pulumi stack export | conform infra generate");
|
|
97
|
+
}
|
|
98
|
+
return new Promise((resolve, reject) => {
|
|
99
|
+
let data = "";
|
|
100
|
+
const rl = readline.createInterface({
|
|
101
|
+
input: process.stdin,
|
|
102
|
+
terminal: false
|
|
103
|
+
});
|
|
104
|
+
rl.on("line", (line) => {
|
|
105
|
+
data += `${line}
|
|
106
|
+
`;
|
|
107
|
+
});
|
|
108
|
+
rl.on("close", () => {
|
|
109
|
+
resolve(data);
|
|
110
|
+
});
|
|
111
|
+
rl.on("error", reject);
|
|
112
|
+
setTimeout(() => {
|
|
113
|
+
if (!data) {
|
|
114
|
+
rl.close();
|
|
115
|
+
reject(new Error("Timeout waiting for stdin. Pipe Pulumi stack export: pulumi stack export | conform infra generate"));
|
|
116
|
+
}
|
|
117
|
+
}, 5e3);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
async function generateManifestFromStdin(options = {}) {
|
|
121
|
+
const content = await readStdin();
|
|
122
|
+
let stackExport;
|
|
123
|
+
try {
|
|
124
|
+
stackExport = JSON.parse(content);
|
|
125
|
+
} catch {
|
|
126
|
+
throw new Error("Invalid JSON input. Expected Pulumi stack export format.");
|
|
127
|
+
}
|
|
128
|
+
return parseStackExport(stackExport, options.project);
|
|
129
|
+
}
|
|
130
|
+
function generateManifestFromFile(filePath, options = {}) {
|
|
131
|
+
if (!fs.existsSync(filePath)) {
|
|
132
|
+
throw new Error(`File not found: ${filePath}`);
|
|
133
|
+
}
|
|
134
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
135
|
+
let stackExport;
|
|
136
|
+
try {
|
|
137
|
+
stackExport = JSON.parse(content);
|
|
138
|
+
} catch {
|
|
139
|
+
throw new Error(`Invalid JSON in file ${filePath}`);
|
|
140
|
+
}
|
|
141
|
+
return parseStackExport(stackExport, options.project);
|
|
142
|
+
}
|
|
143
|
+
function writeManifest(manifest, options = {}) {
|
|
144
|
+
const json = JSON.stringify(manifest, null, 2);
|
|
145
|
+
if (options.stdout) {
|
|
146
|
+
process.stdout.write(`${json}
|
|
147
|
+
`);
|
|
148
|
+
} else {
|
|
149
|
+
const outputPath = options.output ?? DEFAULT_MANIFEST_NAME;
|
|
150
|
+
fs.writeFileSync(outputPath, `${json}
|
|
151
|
+
`, "utf-8");
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function extractDeploymentResources(deployment) {
|
|
155
|
+
const resources = [];
|
|
156
|
+
for (const resource of deployment.resources ?? []) {
|
|
157
|
+
if (resource.outputs) {
|
|
158
|
+
const extracted = extractResourcesFromOutputs(resource.outputs);
|
|
159
|
+
resources.push(...extracted);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return [...new Set(resources)];
|
|
163
|
+
}
|
|
164
|
+
function groupResourcesByAccount(resources) {
|
|
165
|
+
const accountsMap = /* @__PURE__ */ new Map();
|
|
166
|
+
for (const resource of resources) {
|
|
167
|
+
const accountKey = detectAccountFromResource(resource);
|
|
168
|
+
const existing = accountsMap.get(accountKey);
|
|
169
|
+
if (existing) {
|
|
170
|
+
existing.resources.push(resource);
|
|
171
|
+
} else {
|
|
172
|
+
accountsMap.set(accountKey, { resources: [resource] });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return accountsMap;
|
|
176
|
+
}
|
|
177
|
+
function buildAccountsRecord(accountsMap, alias) {
|
|
178
|
+
const accounts = {};
|
|
179
|
+
const entries = Array.from(accountsMap.entries());
|
|
180
|
+
if (alias && entries.length === 1) {
|
|
181
|
+
const [key, value] = entries[0];
|
|
182
|
+
accounts[key] = { alias, resources: value.resources };
|
|
183
|
+
} else {
|
|
184
|
+
for (const [key, value] of entries) {
|
|
185
|
+
accounts[key] = value;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return accounts;
|
|
189
|
+
}
|
|
190
|
+
function parseStackExportMultiAccount(stackExport, options = {}) {
|
|
191
|
+
if (!stackExport || typeof stackExport !== "object") {
|
|
192
|
+
throw new Error("Invalid stack export: expected an object");
|
|
193
|
+
}
|
|
194
|
+
const typed = stackExport;
|
|
195
|
+
const deployment = typed.deployment;
|
|
196
|
+
if (!deployment?.resources) {
|
|
197
|
+
throw new Error("Invalid stack export: missing deployment.resources");
|
|
198
|
+
}
|
|
199
|
+
const uniqueResources = extractDeploymentResources(deployment);
|
|
200
|
+
const projectName = options.project ?? extractProjectName(deployment.resources);
|
|
201
|
+
if (options.accountId) {
|
|
202
|
+
return {
|
|
203
|
+
version: 2,
|
|
204
|
+
project: projectName,
|
|
205
|
+
accounts: {
|
|
206
|
+
[options.accountId]: { alias: options.account, resources: uniqueResources }
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const accountsMap = groupResourcesByAccount(uniqueResources);
|
|
211
|
+
const accounts = buildAccountsRecord(accountsMap, options.account);
|
|
212
|
+
return { version: 2, project: projectName, accounts };
|
|
213
|
+
}
|
|
214
|
+
function readExistingManifest(filePath) {
|
|
215
|
+
if (!fs.existsSync(filePath)) {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
219
|
+
try {
|
|
220
|
+
return JSON.parse(content);
|
|
221
|
+
} catch {
|
|
222
|
+
throw new ManifestError(`Invalid JSON in existing manifest: ${filePath}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function convertLegacyToMultiAccount(legacy) {
|
|
226
|
+
const legacyAccounts = {};
|
|
227
|
+
for (const resource of legacy.resources) {
|
|
228
|
+
const key = detectAccountFromResource(resource);
|
|
229
|
+
if (key in legacyAccounts) {
|
|
230
|
+
legacyAccounts[key].resources.push(resource);
|
|
231
|
+
} else {
|
|
232
|
+
legacyAccounts[key] = { resources: [resource] };
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return { version: 2, project: legacy.project, accounts: legacyAccounts };
|
|
236
|
+
}
|
|
237
|
+
function mergeIntoManifest(existing, newResources, accountKey, alias) {
|
|
238
|
+
const multiAccount = isMultiAccountManifest(existing) ? { ...existing, accounts: { ...existing.accounts } } : convertLegacyToMultiAccount(existing);
|
|
239
|
+
const hasExisting = accountKey in multiAccount.accounts;
|
|
240
|
+
const existingResources = hasExisting ? multiAccount.accounts[accountKey].resources : [];
|
|
241
|
+
const existingAlias = hasExisting ? multiAccount.accounts[accountKey].alias : void 0;
|
|
242
|
+
const mergedResources = [.../* @__PURE__ */ new Set([...existingResources, ...newResources])];
|
|
243
|
+
multiAccount.accounts[accountKey] = {
|
|
244
|
+
alias: alias ?? existingAlias,
|
|
245
|
+
resources: mergedResources
|
|
246
|
+
};
|
|
247
|
+
return multiAccount;
|
|
248
|
+
}
|
|
249
|
+
async function generateMultiAccountFromStdin(options = {}) {
|
|
250
|
+
const content = await readStdin();
|
|
251
|
+
let stackExport;
|
|
252
|
+
try {
|
|
253
|
+
stackExport = JSON.parse(content);
|
|
254
|
+
} catch {
|
|
255
|
+
throw new Error("Invalid JSON input. Expected Pulumi stack export format.");
|
|
256
|
+
}
|
|
257
|
+
return parseStackExportMultiAccount(stackExport, options);
|
|
258
|
+
}
|
|
259
|
+
function generateMultiAccountFromFile(filePath, options = {}) {
|
|
260
|
+
if (!fs.existsSync(filePath)) {
|
|
261
|
+
throw new Error(`File not found: ${filePath}`);
|
|
262
|
+
}
|
|
263
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
264
|
+
let stackExport;
|
|
265
|
+
try {
|
|
266
|
+
stackExport = JSON.parse(content);
|
|
267
|
+
} catch {
|
|
268
|
+
throw new Error(`Invalid JSON in file ${filePath}`);
|
|
269
|
+
}
|
|
270
|
+
return parseStackExportMultiAccount(stackExport, options);
|
|
271
|
+
}
|
|
272
|
+
async function generateNewManifest(inputPath, options) {
|
|
273
|
+
if (inputPath) {
|
|
274
|
+
return generateMultiAccountFromFile(inputPath, options);
|
|
275
|
+
}
|
|
276
|
+
return generateMultiAccountFromStdin(options);
|
|
277
|
+
}
|
|
278
|
+
function mergeManifests(existing, newManifest, projectOverride) {
|
|
279
|
+
let merged = isMultiAccountManifest(existing) ? { ...existing, accounts: { ...existing.accounts } } : convertLegacyToMultiAccount(existing);
|
|
280
|
+
for (const [key, account] of Object.entries(newManifest.accounts)) {
|
|
281
|
+
merged = mergeIntoManifest(merged, account.resources, key, account.alias);
|
|
282
|
+
}
|
|
283
|
+
if (projectOverride) {
|
|
284
|
+
merged.project = projectOverride;
|
|
285
|
+
}
|
|
286
|
+
return merged;
|
|
287
|
+
}
|
|
288
|
+
async function generateWithMerge(inputPath, options) {
|
|
289
|
+
const outputPath = options.output ?? DEFAULT_MANIFEST_NAME;
|
|
290
|
+
const newManifest = await generateNewManifest(inputPath, options);
|
|
291
|
+
if (!options.merge) {
|
|
292
|
+
return newManifest;
|
|
293
|
+
}
|
|
294
|
+
const existing = readExistingManifest(outputPath);
|
|
295
|
+
if (!existing) {
|
|
296
|
+
return newManifest;
|
|
297
|
+
}
|
|
298
|
+
return mergeManifests(existing, newManifest, options.project);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export {
|
|
302
|
+
DEFAULT_MANIFEST_NAME,
|
|
303
|
+
parseStackExport,
|
|
304
|
+
generateManifestFromStdin,
|
|
305
|
+
generateManifestFromFile,
|
|
306
|
+
writeManifest,
|
|
307
|
+
parseStackExportMultiAccount,
|
|
308
|
+
readExistingManifest,
|
|
309
|
+
mergeIntoManifest,
|
|
310
|
+
generateMultiAccountFromStdin,
|
|
311
|
+
generateMultiAccountFromFile,
|
|
312
|
+
generateWithMerge
|
|
313
|
+
};
|
|
314
|
+
//# sourceMappingURL=chunk-J5S6GRGW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/infra/generate.ts"],"sourcesContent":["/**\n * Manifest generation from Pulumi stack exports\n *\n * Parses Pulumi stack export JSON and extracts resource ARNs/identifiers\n * to generate an infra-manifest.json file.\n */\n\nimport * as fs from \"node:fs\";\nimport * as readline from \"node:readline\";\n\nimport { isValidArn } from \"./arn.js\";\nimport { isValidGcpResource } from \"./gcp.js\";\nimport {\n detectAccountFromResource,\n isMultiAccountManifest,\n ManifestError,\n} from \"./manifest.js\";\nimport type { LegacyManifest, Manifest, ManifestAccount, MultiAccountManifest } from \"./types.js\";\n\n/**\n * Pulumi stack export structure (minimal typing for what we need)\n */\ninterface PulumiResource {\n urn?: string;\n type?: string;\n outputs?: Record<string, unknown>;\n}\n\ninterface PulumiDeployment {\n resources?: PulumiResource[];\n}\n\ninterface PulumiStackExport {\n deployment?: PulumiDeployment;\n}\n\n/** Default manifest filename */\nexport const DEFAULT_MANIFEST_NAME = \"infra-manifest.json\";\n\n/**\n * Options for manifest generation\n */\nexport interface GenerateManifestOptions {\n /** Project name (extracted from stack if not provided) */\n project?: string;\n /** Output file path (defaults to infra-manifest.json) */\n output?: string;\n /** If true, output to stdout instead of file */\n stdout?: boolean;\n /** Account alias (e.g., \"prod-aws\") for multi-account manifests */\n account?: string;\n /** Explicit account ID (e.g., \"aws:111111111111\") */\n accountId?: string;\n /** Merge into existing manifest instead of overwriting */\n merge?: boolean;\n}\n\n/**\n * Clean a resource identifier by stripping Pulumi internal suffixes\n *\n * Pulumi sometimes appends internal metadata to ARNs like:\n * arn:aws:secretsmanager:...:secret:name|terraform-20260123...\n *\n * The pipe and everything after it should be stripped.\n */\nfunction cleanResourceIdentifier(value: string): string {\n const pipeIndex = value.indexOf(\"|\");\n if (pipeIndex !== -1) {\n return value.substring(0, pipeIndex);\n }\n return value;\n}\n\n/**\n * Check if a value is a valid resource identifier (AWS ARN or GCP path)\n */\nfunction isValidResource(value: unknown): value is string {\n if (typeof value !== \"string\") {\n return false;\n }\n // Clean the value before validating (strip Pulumi suffixes)\n const cleaned = cleanResourceIdentifier(value);\n return isValidArn(cleaned) || isValidGcpResource(cleaned);\n}\n\n/**\n * Extract ARNs and resource identifiers from Pulumi resource outputs\n */\nfunction extractResourcesFromOutputs(outputs: Record<string, unknown>): string[] {\n const resources: string[] = [];\n\n // Common ARN/resource output field names\n const arnFields = [\n \"arn\",\n \"id\",\n \"bucketArn\",\n \"functionArn\",\n \"secretArn\",\n \"roleArn\",\n \"clusterArn\",\n \"serviceArn\",\n \"tableArn\",\n \"topicArn\",\n \"queueArn\",\n \"logGroupArn\",\n \"policyArn\",\n // GCP-specific\n \"name\",\n \"selfLink\",\n ];\n\n for (const field of arnFields) {\n const value = outputs[field];\n if (isValidResource(value)) {\n resources.push(cleanResourceIdentifier(value));\n }\n }\n\n // Also check for any other fields that look like ARNs or GCP resources\n for (const [key, value] of Object.entries(outputs)) {\n if (!arnFields.includes(key) && isValidResource(value)) {\n resources.push(cleanResourceIdentifier(value));\n }\n }\n\n return resources;\n}\n\n/**\n * Extract project name from Pulumi resource URNs\n *\n * URN format: urn:pulumi:stack::project::type::name\n */\nfunction extractProjectName(resources: PulumiResource[]): string | undefined {\n for (const resource of resources) {\n if (resource.urn) {\n const parts = resource.urn.split(\"::\");\n if (parts.length >= 2) {\n return parts[1]; // project is the second part\n }\n }\n }\n return undefined;\n}\n\n/**\n * Parse Pulumi stack export JSON and extract manifest\n */\nexport function parseStackExport(stackExport: unknown, project?: string): Manifest {\n if (!stackExport || typeof stackExport !== \"object\") {\n throw new Error(\"Invalid stack export: expected an object\");\n }\n\n const typed = stackExport as PulumiStackExport;\n const deployment = typed.deployment;\n\n if (!deployment?.resources) {\n throw new Error(\"Invalid stack export: missing deployment.resources\");\n }\n\n const resources: string[] = [];\n\n // Extract resources from each Pulumi resource's outputs\n for (const resource of deployment.resources) {\n if (resource.outputs) {\n const extracted = extractResourcesFromOutputs(resource.outputs);\n resources.push(...extracted);\n }\n }\n\n // Deduplicate resources\n const uniqueResources = [...new Set(resources)];\n\n // Determine project name\n const projectName = project ?? extractProjectName(deployment.resources) ?? \"unknown\";\n\n return {\n project: projectName,\n resources: uniqueResources,\n };\n}\n\n/**\n * Read all content from stdin\n */\nasync function readStdin(): Promise<string> {\n // Check if stdin is a TTY (interactive terminal)\n if (process.stdin.isTTY) {\n throw new Error(\"No input provided. Pipe Pulumi stack export: pulumi stack export | conform infra generate\");\n }\n\n return new Promise((resolve, reject) => {\n let data = \"\";\n const rl = readline.createInterface({\n input: process.stdin,\n terminal: false,\n });\n\n rl.on(\"line\", (line) => {\n data += `${line}\\n`;\n });\n\n rl.on(\"close\", () => {\n resolve(data);\n });\n\n rl.on(\"error\", reject);\n\n // Timeout after 5 seconds if no data\n setTimeout(() => {\n if (!data) {\n rl.close();\n reject(new Error(\"Timeout waiting for stdin. Pipe Pulumi stack export: pulumi stack export | conform infra generate\"));\n }\n }, 5000);\n });\n}\n\n/**\n * Generate manifest from stdin (Pulumi stack export)\n */\nexport async function generateManifestFromStdin(options: GenerateManifestOptions = {}): Promise<Manifest> {\n const content = await readStdin();\n\n let stackExport: unknown;\n try {\n stackExport = JSON.parse(content);\n } catch {\n throw new Error(\"Invalid JSON input. Expected Pulumi stack export format.\");\n }\n\n return parseStackExport(stackExport, options.project);\n}\n\n/**\n * Generate manifest from a file\n */\nexport function generateManifestFromFile(filePath: string, options: GenerateManifestOptions = {}): Manifest {\n if (!fs.existsSync(filePath)) {\n throw new Error(`File not found: ${filePath}`);\n }\n\n const content = fs.readFileSync(filePath, \"utf-8\");\n\n let stackExport: unknown;\n try {\n stackExport = JSON.parse(content);\n } catch {\n throw new Error(`Invalid JSON in file ${filePath}`);\n }\n\n return parseStackExport(stackExport, options.project);\n}\n\n/**\n * Write manifest to file or stdout\n *\n * @param manifest - The manifest to write\n * @param options - Output options (defaults to writing infra-manifest.json)\n */\nexport function writeManifest(manifest: Manifest, options: { output?: string; stdout?: boolean } = {}): void {\n const json = JSON.stringify(manifest, null, 2);\n\n if (options.stdout) {\n process.stdout.write(`${json}\\n`);\n } else {\n const outputPath = options.output ?? DEFAULT_MANIFEST_NAME;\n fs.writeFileSync(outputPath, `${json}\\n`, \"utf-8\");\n }\n}\n\n/**\n * Extract and deduplicate resources from a Pulumi deployment\n */\nfunction extractDeploymentResources(deployment: PulumiDeployment): string[] {\n const resources: string[] = [];\n for (const resource of deployment.resources ?? []) {\n if (resource.outputs) {\n const extracted = extractResourcesFromOutputs(resource.outputs);\n resources.push(...extracted);\n }\n }\n return [...new Set(resources)];\n}\n\n/**\n * Group resources by detected account\n */\nfunction groupResourcesByAccount(resources: string[]): Map<string, ManifestAccount> {\n const accountsMap = new Map<string, ManifestAccount>();\n\n for (const resource of resources) {\n const accountKey = detectAccountFromResource(resource);\n const existing = accountsMap.get(accountKey);\n if (existing) {\n existing.resources.push(resource);\n } else {\n accountsMap.set(accountKey, { resources: [resource] });\n }\n }\n\n return accountsMap;\n}\n\n/**\n * Convert accounts map to record, optionally applying alias\n */\nfunction buildAccountsRecord(\n accountsMap: Map<string, ManifestAccount>,\n alias?: string\n): Record<string, ManifestAccount> {\n const accounts: Record<string, ManifestAccount> = {};\n const entries = Array.from(accountsMap.entries());\n\n if (alias && entries.length === 1) {\n const [key, value] = entries[0];\n accounts[key] = { alias, resources: value.resources };\n } else {\n for (const [key, value] of entries) {\n accounts[key] = value;\n }\n }\n\n return accounts;\n}\n\n/**\n * Parse Pulumi stack export and create multi-account manifest\n * Groups resources by detected account\n */\nexport function parseStackExportMultiAccount(\n stackExport: unknown,\n options: GenerateManifestOptions = {}\n): MultiAccountManifest {\n if (!stackExport || typeof stackExport !== \"object\") {\n throw new Error(\"Invalid stack export: expected an object\");\n }\n\n const typed = stackExport as PulumiStackExport;\n const deployment = typed.deployment;\n\n if (!deployment?.resources) {\n throw new Error(\"Invalid stack export: missing deployment.resources\");\n }\n\n const uniqueResources = extractDeploymentResources(deployment);\n const projectName = options.project ?? extractProjectName(deployment.resources);\n\n // If explicit account ID provided, use it\n if (options.accountId) {\n return {\n version: 2,\n project: projectName,\n accounts: {\n [options.accountId]: { alias: options.account, resources: uniqueResources },\n },\n };\n }\n\n // Group resources by auto-detected account\n const accountsMap = groupResourcesByAccount(uniqueResources);\n const accounts = buildAccountsRecord(accountsMap, options.account);\n\n return { version: 2, project: projectName, accounts };\n}\n\n/**\n * Read existing manifest from file\n * Returns null if file doesn't exist\n */\nexport function readExistingManifest(filePath: string): Manifest | null {\n if (!fs.existsSync(filePath)) {\n return null;\n }\n\n const content = fs.readFileSync(filePath, \"utf-8\");\n try {\n return JSON.parse(content) as Manifest;\n } catch {\n throw new ManifestError(`Invalid JSON in existing manifest: ${filePath}`);\n }\n}\n\n/**\n * Convert legacy manifest to multi-account format\n */\nfunction convertLegacyToMultiAccount(legacy: LegacyManifest): MultiAccountManifest {\n const legacyAccounts: Record<string, ManifestAccount> = {};\n for (const resource of legacy.resources) {\n const key = detectAccountFromResource(resource);\n if (key in legacyAccounts) {\n legacyAccounts[key].resources.push(resource);\n } else {\n legacyAccounts[key] = { resources: [resource] };\n }\n }\n return { version: 2, project: legacy.project, accounts: legacyAccounts };\n}\n\n/**\n * Merge new resources into an existing manifest\n */\nexport function mergeIntoManifest(\n existing: Manifest,\n newResources: string[],\n accountKey: string,\n alias?: string\n): MultiAccountManifest {\n // Convert existing to multi-account if it's legacy format\n const multiAccount: MultiAccountManifest = isMultiAccountManifest(existing)\n ? { ...existing, accounts: { ...existing.accounts } }\n : convertLegacyToMultiAccount(existing);\n\n // Add or update the target account\n const hasExisting = accountKey in multiAccount.accounts;\n const existingResources = hasExisting ? multiAccount.accounts[accountKey].resources : [];\n const existingAlias = hasExisting ? multiAccount.accounts[accountKey].alias : undefined;\n const mergedResources = [...new Set([...existingResources, ...newResources])];\n\n multiAccount.accounts[accountKey] = {\n alias: alias ?? existingAlias,\n resources: mergedResources,\n };\n\n return multiAccount;\n}\n\n/**\n * Generate multi-account manifest from stdin (Pulumi stack export)\n */\nexport async function generateMultiAccountFromStdin(\n options: GenerateManifestOptions = {}\n): Promise<MultiAccountManifest> {\n const content = await readStdin();\n\n let stackExport: unknown;\n try {\n stackExport = JSON.parse(content);\n } catch {\n throw new Error(\"Invalid JSON input. Expected Pulumi stack export format.\");\n }\n\n return parseStackExportMultiAccount(stackExport, options);\n}\n\n/**\n * Generate multi-account manifest from a file\n */\nexport function generateMultiAccountFromFile(\n filePath: string,\n options: GenerateManifestOptions = {}\n): MultiAccountManifest {\n if (!fs.existsSync(filePath)) {\n throw new Error(`File not found: ${filePath}`);\n }\n\n const content = fs.readFileSync(filePath, \"utf-8\");\n\n let stackExport: unknown;\n try {\n stackExport = JSON.parse(content);\n } catch {\n throw new Error(`Invalid JSON in file ${filePath}`);\n }\n\n return parseStackExportMultiAccount(stackExport, options);\n}\n\n/**\n * Generate new manifest from input\n */\nasync function generateNewManifest(\n inputPath: string | undefined,\n options: GenerateManifestOptions\n): Promise<MultiAccountManifest> {\n if (inputPath) {\n return generateMultiAccountFromFile(inputPath, options);\n }\n return generateMultiAccountFromStdin(options);\n}\n\n/**\n * Merge new manifest accounts into existing manifest\n */\nfunction mergeManifests(\n existing: Manifest,\n newManifest: MultiAccountManifest,\n projectOverride?: string\n): MultiAccountManifest {\n // Start with existing converted to multi-account\n let merged: MultiAccountManifest = isMultiAccountManifest(existing)\n ? { ...existing, accounts: { ...existing.accounts } }\n : convertLegacyToMultiAccount(existing);\n\n // Merge in new accounts\n for (const [key, account] of Object.entries(newManifest.accounts)) {\n merged = mergeIntoManifest(merged, account.resources, key, account.alias);\n }\n\n // Apply project override if provided\n if (projectOverride) {\n merged.project = projectOverride;\n }\n\n return merged;\n}\n\n/**\n * Handle merge operation for manifest generation\n */\nexport async function generateWithMerge(\n inputPath: string | undefined,\n options: GenerateManifestOptions\n): Promise<Manifest> {\n const outputPath = options.output ?? DEFAULT_MANIFEST_NAME;\n const newManifest = await generateNewManifest(inputPath, options);\n\n // If not merging, just return the new manifest\n if (!options.merge) {\n return newManifest;\n }\n\n // Read existing manifest\n const existing = readExistingManifest(outputPath);\n if (!existing) {\n return newManifest;\n }\n\n return mergeManifests(existing, newManifest, options.project);\n}\n"],"mappings":"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","names":[]}
|