@continuous-excellence/ze-great-dashboard-aws 0.13.7 → 0.14.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/cli.js CHANGED
@@ -11,136 +11,495 @@ import { readFile as readFile4 } from "node:fs/promises";
11
11
  import { promisify as promisify2 } from "node:util";
12
12
  import { parse as parse2 } from "yaml";
13
13
 
14
- // packages/aws/src/index.ts
15
- import { execFile } from "node:child_process";
16
- import { cp, mkdir as mkdir2, readFile as readFile3, rm, writeFile as writeFile2 } from "node:fs/promises";
17
- import { join as join2, resolve as resolve2 } from "node:path";
18
- import { fileURLToPath as fileURLToPath2 } from "node:url";
19
- import { promisify } from "node:util";
14
+ // packages/aws/src/bootstrap.ts
15
+ import { readFile as readFile2 } from "node:fs/promises";
16
+ import { fileURLToPath } from "node:url";
20
17
 
21
- // node_modules/fflate/esm/index.mjs
22
- import { createRequire } from "module";
23
- var require2 = createRequire("/");
24
- var _a;
25
- var Worker;
26
- var isMarkedAsUntransferable;
27
- try {
28
- _a = require2("worker_threads"), Worker = _a.Worker, isMarkedAsUntransferable = _a.isMarkedAsUntransferable;
29
- } catch (e) {
18
+ // packages/aws/src/compute-mode.ts
19
+ function valid(value2, label) {
20
+ if (value2 !== "lambda" && value2 !== "ecs")
21
+ throw new Error(`${label} must be lambda or ecs (received ${value2})`);
22
+ return value2;
30
23
  }
31
- var u8 = Uint8Array;
32
- var u16 = Uint16Array;
33
- var i32 = Int32Array;
34
- var fleb = new u8([
35
- 0,
36
- 0,
37
- 0,
38
- 0,
39
- 0,
40
- 0,
41
- 0,
42
- 0,
43
- 1,
44
- 1,
45
- 1,
46
- 1,
47
- 2,
48
- 2,
49
- 2,
50
- 2,
51
- 3,
52
- 3,
53
- 3,
54
- 3,
55
- 4,
56
- 4,
57
- 4,
58
- 4,
59
- 5,
60
- 5,
61
- 5,
62
- 5,
63
- 0,
64
- /* unused */
65
- 0,
66
- 0,
67
- /* impossible */
68
- 0
69
- ]);
70
- var fdeb = new u8([
71
- 0,
72
- 0,
73
- 0,
74
- 0,
75
- 1,
76
- 1,
77
- 2,
78
- 2,
79
- 3,
80
- 3,
81
- 4,
82
- 4,
83
- 5,
84
- 5,
85
- 6,
86
- 6,
87
- 7,
88
- 7,
89
- 8,
90
- 8,
91
- 9,
92
- 9,
93
- 10,
94
- 10,
95
- 11,
96
- 11,
97
- 12,
98
- 12,
99
- 13,
100
- 13,
101
- /* unused */
102
- 0,
103
- 0
24
+ function resolveComputeMode(input = {}) {
25
+ const persisted = input.persisted === void 0 ? void 0 : valid(input.persisted, "persisted mode");
26
+ const artifact = input.artifact === void 0 ? void 0 : valid(input.artifact, "ComputeMode");
27
+ const explicit = input.explicit === void 0 ? void 0 : valid(input.explicit, "mode");
28
+ const selected = persisted ?? explicit ?? artifact ?? "lambda";
29
+ if (artifact !== void 0 && artifact !== selected)
30
+ throw new Error(
31
+ `ComputeMode ${artifact} disagrees with persisted mode ${selected}; regenerate the bootstrap and parameter artifacts to change deployment mode`
32
+ );
33
+ if (explicit !== void 0 && explicit !== selected)
34
+ throw new Error(
35
+ `explicit mode ${explicit} disagrees with persisted mode ${selected}; regenerate the bootstrap and parameter artifacts to change deployment mode`
36
+ );
37
+ return selected;
38
+ }
39
+ function computeMode(config) {
40
+ return resolveComputeMode({ persisted: config.mode });
41
+ }
42
+
43
+ // packages/aws/src/release.ts
44
+ import { createHash } from "node:crypto";
45
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
46
+ import { join, resolve } from "node:path";
47
+ import { parse, stringify } from "yaml";
48
+ import { z as z2 } from "zod";
49
+
50
+ // packages/aws/src/internal-board.ts
51
+ import { z } from "zod";
52
+ var durationSchema = z.string().regex(/^\d+(?:ms|s|m|h)$/).refine((value2) => Number.parseInt(value2, 10) > 0).brand();
53
+ var positionSchema = z.union([
54
+ z.object({
55
+ x: z.number().int().min(0),
56
+ y: z.number().int().min(0),
57
+ w: z.number().int().min(1).max(12),
58
+ h: z.number().int().min(1)
59
+ }),
60
+ z.object({ x: z.literal(0), y: z.literal(0), w: z.literal(0), h: z.literal(0) })
104
61
  ]);
105
- var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
106
- var freb = function(eb, start) {
107
- var b = new u16(31);
108
- for (var i2 = 0; i2 < 31; ++i2) {
109
- b[i2] = start += 1 << eb[i2 - 1];
110
- }
111
- var r = new i32(b[30]);
112
- for (var i2 = 1; i2 < 30; ++i2) {
113
- for (var j = b[i2]; j < b[i2 + 1]; ++j) {
114
- r[j] = j - b[i2] << 5 | i2;
115
- }
62
+ var panelSchema = z.looseObject({
63
+ // Presentation-only; ids still address proxy calls and the generated allowlist.
64
+ label: z.string().min(1).optional(),
65
+ id: z.string().min(1),
66
+ type: z.string().min(1),
67
+ source: z.string().min(1).optional(),
68
+ // Cosmetic roles remain open so newer board files can safely fall back on older runtimes.
69
+ display: z.string().min(1).optional(),
70
+ position: positionSchema.optional(),
71
+ refresh: durationSchema.optional(),
72
+ link: z.url().optional(),
73
+ url: z.url().optional(),
74
+ json_path: z.string().regex(/^\$(?:\.[A-Za-z_][A-Za-z0-9_]*|\[\d+\])*$/).optional()
75
+ });
76
+ var sourceSchema = z.looseObject({
77
+ type: z.string().min(1),
78
+ token_env: z.string().min(1).optional()
79
+ });
80
+ var boardSchema = z.object({
81
+ refresh: durationSchema.optional(),
82
+ panels: z.array(panelSchema).min(1).superRefine((panels, ctx) => {
83
+ const seen = /* @__PURE__ */ new Map();
84
+ panels.forEach((panel, index) => {
85
+ const firstIndex = seen.get(panel.id);
86
+ if (firstIndex === void 0) seen.set(panel.id, index);
87
+ else
88
+ ctx.addIssue({
89
+ code: "custom",
90
+ path: [index, "id"],
91
+ message: `duplicate panel id "${panel.id}" (already used by panel at index ${firstIndex})`
92
+ });
93
+ });
94
+ })
95
+ });
96
+ var boardConfigSchema = z.object({
97
+ sources: z.record(z.string().min(1), sourceSchema).default({}),
98
+ boards: z.record(z.string().min(1), boardSchema),
99
+ auth: z.object({
100
+ issuer: z.url(),
101
+ client_id: z.string().min(1).optional(),
102
+ allow: z.object({
103
+ groups: z.array(z.string().min(1)).optional(),
104
+ subjects: z.array(z.string().min(1)).optional()
105
+ }).optional()
106
+ }).optional()
107
+ });
108
+
109
+ // packages/aws/src/release.ts
110
+ var CORE_RUNTIME_VERSION = "1.0.0";
111
+ var CANONICAL_ASSET_DOMAIN = "https://public-assets.zegreatrob.com";
112
+ function clientAssetUrl(version, domain = CANONICAL_ASSET_DOMAIN) {
113
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(version))
114
+ throw new Error(`Invalid dashboard version: ${version}`);
115
+ return `${domain.replace(/\/+$/, "")}/dashboard/${version}`;
116
+ }
117
+ async function validateBoardConfig(path) {
118
+ const source = await readFile(resolve(path), "utf8");
119
+ const result = boardConfigSchema.safeParse(parse(source));
120
+ if (!result.success)
121
+ throw new Error(`Invalid board configuration:
122
+ ${z2.prettifyError(result.error)}`);
123
+ const yaml = stringify(result.data, { sortMapEntries: true });
124
+ return {
125
+ yaml,
126
+ sha256: sha256(yaml),
127
+ usesCredentials: Object.values(result.data.sources).some((source2) => Boolean(source2.token_env))
128
+ };
129
+ }
130
+ async function assembleRelease(input) {
131
+ const board = await validateBoardConfig(input.boardConfigPath);
132
+ if (board.usesCredentials && !input.secretReference)
133
+ throw new Error(
134
+ "Board config uses token_env; SecretReference must name a Secrets Manager credential-map or Parameter Store SecureString ARN"
135
+ );
136
+ const outputDir = resolve(input.outputDir);
137
+ await mkdir(outputDir, { recursive: true });
138
+ await writeFile(join(outputDir, "board.yaml"), board.yaml);
139
+ const metadata = {
140
+ dashboardVersion: input.version,
141
+ clientAssetUrl: clientAssetUrl(input.version, input.assetDomain),
142
+ serverRuntimeVersion: CORE_RUNTIME_VERSION,
143
+ supportedProviders: input.providers ?? ["aws-lambda"],
144
+ artifactChecksums: { "board.yaml": board.sha256 },
145
+ runtimeCompatibility: { node: ">=22" }
146
+ };
147
+ const metadataText = `${JSON.stringify(metadata, null, 2)}
148
+ `;
149
+ await writeFile(join(outputDir, "release.json"), metadataText);
150
+ const files = { "board.yaml": board.sha256, "release.json": sha256(metadataText) };
151
+ await writeFile(
152
+ join(outputDir, "SHA256SUMS"),
153
+ `${Object.entries(files).map(([name, digest]) => `${digest} ${name}`).join("\n")}
154
+ `
155
+ );
156
+ return { metadata, files };
157
+ }
158
+ function sha256(value2) {
159
+ return createHash("sha256").update(value2).digest("hex");
160
+ }
161
+
162
+ // packages/aws/src/bootstrap.ts
163
+ var templates = {
164
+ core: {
165
+ lambda: "../bootstrap/core-v1.yml",
166
+ ecs: "../bootstrap/core-ecs-v1.yml"
167
+ },
168
+ "github-oidc": {
169
+ lambda: "../bootstrap/github-oidc-v2.yml",
170
+ ecs: "../bootstrap/github-oidc-ecs-v1.yml"
116
171
  }
117
- return { b, r };
118
172
  };
119
- var _a = freb(fleb, 2);
120
- var fl = _a.b;
121
- var revfl = _a.r;
122
- fl[28] = 258, revfl[258] = 28;
123
- var _b = freb(fdeb, 0);
124
- var fd = _b.b;
125
- var revfd = _b.r;
126
- var rev = new u16(32768);
127
- for (i = 0; i < 32768; ++i) {
128
- x = (i & 43690) >> 1 | (i & 21845) << 1;
129
- x = (x & 52428) >> 2 | (x & 13107) << 2;
130
- x = (x & 61680) >> 4 | (x & 3855) << 4;
131
- rev[i] = ((x & 65280) >> 8 | (x & 255) << 8) >> 1;
173
+ function requireComputeMode(persisted, explicit) {
174
+ return resolveComputeMode({ persisted: persisted.mode, explicit });
132
175
  }
133
- var x;
134
- var i;
135
- var hMap = (function(cd, mb, r) {
136
- var s = cd.length;
137
- var i2 = 0;
138
- var l = new u16(mb);
139
- for (; i2 < s; ++i2) {
140
- if (cd[i2])
141
- ++l[cd[i2] - 1];
142
- }
143
- var le = new u16(mb);
176
+ async function bootstrapTemplatePath(kind, mode = "lambda") {
177
+ if (!Object.hasOwn(templates, kind)) throw new Error(`Unknown bootstrap template: ${kind}`);
178
+ return fileURLToPath(new URL(templates[kind][mode], import.meta.url));
179
+ }
180
+ async function bootstrapTemplate(kind, mode = "lambda") {
181
+ return readFile2(await bootstrapTemplatePath(kind, mode), "utf8");
182
+ }
183
+ function bootstrapContractVersion(template) {
184
+ const version = template.match(/BootstrapContractVersion:\s*\{\s*Value:\s*'([^']+)'\s*}/)?.[1];
185
+ if (!version) throw new Error("Bootstrap template has no BootstrapContractVersion output");
186
+ return version;
187
+ }
188
+ function bootstrapTemplateRevision(template) {
189
+ const revision = template.match(/BootstrapTemplateRevision:\s*\{\s*Value:\s*'([^']+)'\s*}/)?.[1];
190
+ if (!revision) throw new Error("Bootstrap template has no BootstrapTemplateRevision output");
191
+ return revision;
192
+ }
193
+ function templateRevisionIfPresent(template) {
194
+ return template.match(/BootstrapTemplateRevision:\s*\{\s*Value:\s*'([^']+)'\s*}/)?.[1];
195
+ }
196
+ function inspectTemplate(kind, path, template) {
197
+ const resourcesBlock = template.match(/^Resources:\n([\s\S]*?)(?=^[A-Za-z][A-Za-z0-9]*:\s*$)/m)?.[1] ?? "";
198
+ const resources = [
199
+ ...resourcesBlock.matchAll(/^ {2}([A-Za-z][A-Za-z0-9]+):\n {4}Type: ([^\n]+)$/gm)
200
+ ].map((match) => ({ logicalId: match[1], type: match[2] }));
201
+ const iamActions = [...template.matchAll(/^\s+Action: (?:\[([^\]]+)\]|([^\n]+))$/gm)].flatMap(([, list, scalar]) => (list ?? scalar ?? "").split(",").map((action) => action.trim())).filter(Boolean);
202
+ return {
203
+ kind,
204
+ path,
205
+ contractVersion: bootstrapContractVersion(template),
206
+ templateRevision: templateRevisionIfPresent(template),
207
+ sha256: sha256(template),
208
+ resources,
209
+ iamActions: [...new Set(iamActions)]
210
+ };
211
+ }
212
+ async function bootstrapPlan(config) {
213
+ const packageManifest = JSON.parse(
214
+ await readFile2(new URL("../package.json", import.meta.url), "utf8")
215
+ );
216
+ const packageTemplates = await Promise.all(
217
+ ["core", "github-oidc"].map(async (kind) => {
218
+ const mode = computeMode(config);
219
+ const [path, template] = await Promise.all([
220
+ bootstrapTemplatePath(kind, mode),
221
+ bootstrapTemplate(kind, mode)
222
+ ]);
223
+ return inspectTemplate(kind, path, template);
224
+ })
225
+ );
226
+ return {
227
+ packageVersion: typeof packageManifest.version === "string" ? packageManifest.version : "unknown",
228
+ packageTemplates,
229
+ configuration: config,
230
+ notes: [
231
+ "Templates are owned by the installed npm package; package.json and package-lock.json pin the source version.",
232
+ "Generated CloudFormation parameter files and describe-stacks captures are deployment artifacts, not source configuration.",
233
+ "This plan performs no AWS or GitHub mutations."
234
+ ]
235
+ };
236
+ }
237
+ function capturedStack(input) {
238
+ const stack = input && typeof input === "object" && "Stacks" in input ? input.Stacks?.[0] : input;
239
+ return stack && typeof stack === "object" ? stack : {};
240
+ }
241
+ function outputValues(stack) {
242
+ return Object.fromEntries(
243
+ (stack.Outputs ?? []).map(({ OutputKey, OutputValue }) => [OutputKey, OutputValue])
244
+ );
245
+ }
246
+ function bootstrapConsistency(kind, config, stackInput, expectedContract, expectedRevision, coreOutputs = {}) {
247
+ const stack = capturedStack(stackInput);
248
+ const mismatches = [];
249
+ const expectedStackName = kind === "core" ? config.core?.stackName : config.githubOidc?.stackName;
250
+ if (stack.StackName !== expectedStackName)
251
+ mismatches.push(
252
+ `stack name is ${stack.StackName ?? "missing"}; expected ${expectedStackName ?? "missing"}`
253
+ );
254
+ const observedRegion = stack.StackId?.match(/^arn:[^:]+:cloudformation:([^:]+):/)?.[1];
255
+ if (observedRegion !== config.region)
256
+ mismatches.push(
257
+ `Region is ${observedRegion ?? "missing"}; expected ${config.region ?? "missing"}`
258
+ );
259
+ if (!["CREATE_COMPLETE", "UPDATE_COMPLETE"].includes(stack.StackStatus ?? ""))
260
+ mismatches.push(
261
+ `stack status is ${stack.StackStatus ?? "missing"}; expected a successful stable status`
262
+ );
263
+ const outputs2 = outputValues(stack);
264
+ if (outputs2.BootstrapContractVersion !== expectedContract)
265
+ mismatches.push(
266
+ `contract is ${outputs2.BootstrapContractVersion ?? "missing"}; expected ${expectedContract}`
267
+ );
268
+ if (expectedRevision && outputs2.BootstrapTemplateRevision !== expectedRevision)
269
+ mismatches.push(
270
+ `template revision is ${outputs2.BootstrapTemplateRevision ?? "missing"}; expected ${expectedRevision}`
271
+ );
272
+ const requiredOutputs = kind === "core" ? ["ArtifactBucketName", "ApplicationStackName", "CloudFormationExecutionRoleArn"] : ["GitHubDeployRoleArn"];
273
+ for (const key of requiredOutputs) if (!outputs2[key]) mismatches.push(`${key} output is missing`);
274
+ if (kind === "core") {
275
+ if (outputs2.ArtifactBucketName !== config.core?.artifactBucketName)
276
+ mismatches.push(
277
+ `ArtifactBucketName output is ${outputs2.ArtifactBucketName ?? "missing"}; expected ${config.core?.artifactBucketName ?? "missing"}`
278
+ );
279
+ if (outputs2.ApplicationStackName !== config.core?.applicationStackName)
280
+ mismatches.push(
281
+ `ApplicationStackName output is ${outputs2.ApplicationStackName ?? "missing"}; expected ${config.core?.applicationStackName ?? "missing"}`
282
+ );
283
+ }
284
+ const parameters = Object.fromEntries(
285
+ (stack.Parameters ?? []).map(({ ParameterKey, ParameterValue }) => [
286
+ ParameterKey,
287
+ ParameterValue
288
+ ])
289
+ );
290
+ const expectedParameters = kind === "core" ? {
291
+ ArtifactBucketName: config.core?.artifactBucketName,
292
+ ApplicationStackName: config.core?.applicationStackName,
293
+ DashboardFunctionName: config.core?.dashboardFunctionName,
294
+ ComputeMode: config.mode,
295
+ RuntimeSecretArn: config.core?.runtimeSecretArn ?? "",
296
+ ArtifactKmsKeyArn: config.core?.artifactKmsKeyArn ?? ""
297
+ } : {
298
+ GitHubOidcProviderArn: config.githubOidc?.providerArn,
299
+ GitHubRepository: config.githubOidc?.repository,
300
+ GitHubOwnerId: config.githubOidc?.ownerId,
301
+ GitHubRepositoryId: config.githubOidc?.repositoryId,
302
+ GitHubEnvironment: config.githubOidc?.environment,
303
+ CoreBootstrapStackName: config.core?.stackName,
304
+ ApplicationStackName: config.core?.applicationStackName,
305
+ ArtifactBucketName: config.core?.artifactBucketName,
306
+ ComputeMode: config.mode,
307
+ CloudFormationExecutionRoleArn: coreOutputs.CloudFormationExecutionRoleArn,
308
+ ConsumerGatewayStackName: config.githubOidc?.consumerGatewayStackName ?? ""
309
+ };
310
+ for (const [key, expected] of Object.entries(expectedParameters))
311
+ if (expected !== void 0 && parameters[key] !== expected)
312
+ mismatches.push(`${key} is ${parameters[key] ?? "missing"}; expected ${expected}`);
313
+ return { ok: mismatches.length === 0, mismatches };
314
+ }
315
+ function mergeBootstrapParameters(requested, deployed) {
316
+ const supplied = new Map(requested.map((entry) => [entry.ParameterKey, entry.ParameterValue]));
317
+ const duplicates = requested.map((entry) => entry.ParameterKey);
318
+ if (new Set(duplicates).size !== duplicates.length)
319
+ throw new Error("Bootstrap parameter file contains duplicate parameters");
320
+ return deployed.map(({ ParameterKey, ParameterValue }) => ({
321
+ ParameterKey,
322
+ ParameterValue: supplied.get(ParameterKey) ?? ParameterValue
323
+ })).concat(
324
+ requested.filter(
325
+ ({ ParameterKey }) => !deployed.some((entry) => entry.ParameterKey === ParameterKey)
326
+ )
327
+ );
328
+ }
329
+ function deployedBootstrapStack(input, expectedContractVersion) {
330
+ const stack = input && typeof input === "object" && "Stacks" in input ? input.Stacks?.[0] : input;
331
+ if (!stack || typeof stack !== "object") throw new Error("Deployed stack JSON contains no stack");
332
+ const deployed = stack;
333
+ const contract = deployed.Outputs?.find(
334
+ (output) => output.OutputKey === "BootstrapContractVersion"
335
+ )?.OutputValue;
336
+ if (contract !== expectedContractVersion)
337
+ throw new Error(
338
+ `Bootstrap contract mismatch (deployed: ${contract ?? "missing"}; expected: ${expectedContractVersion}); follow the documented migration procedure`
339
+ );
340
+ if (deployed.Parameters && !Array.isArray(deployed.Parameters))
341
+ throw new Error("Deployed stack JSON has invalid Parameters");
342
+ return deployed;
343
+ }
344
+ function coreBootstrapOutputs(stack) {
345
+ const values = Object.fromEntries(
346
+ (stack.Outputs ?? []).filter(
347
+ (output) => Boolean(output.OutputValue)
348
+ ).map((output) => [output.OutputKey, output.OutputValue])
349
+ );
350
+ const required2 = ["ArtifactBucketName", "ApplicationStackName", "CloudFormationExecutionRoleArn"];
351
+ const missing = required2.filter((key) => !values[key]);
352
+ if (missing.length) throw new Error(`Core stack JSON is missing outputs: ${missing.join(", ")}`);
353
+ return values;
354
+ }
355
+ function requiredBootstrapParameters(kind, values) {
356
+ const keys = kind === "core" ? ["ArtifactBucketName", "ApplicationStackName", "DashboardFunctionName"] : [
357
+ "GitHubOidcProviderArn",
358
+ "GitHubRepository",
359
+ "GitHubOwnerId",
360
+ "GitHubRepositoryId",
361
+ "GitHubEnvironment",
362
+ "CoreBootstrapStackName",
363
+ "ApplicationStackName",
364
+ "ArtifactBucketName",
365
+ "CloudFormationExecutionRoleArn"
366
+ ];
367
+ const missing = keys.filter((key) => !values[key]);
368
+ if (missing.length) throw new Error(`Missing required bootstrap values: ${missing.join(", ")}`);
369
+ const optional2 = kind === "core" ? ["RuntimeSecretArn", "ArtifactKmsKeyArn", "ComputeMode"] : ["ConsumerGatewayStackName", "ComputeMode"];
370
+ return [...keys, ...optional2].filter((key) => values[key] !== void 0).map((ParameterKey) => ({ ParameterKey, ParameterValue: values[ParameterKey] ?? "" }));
371
+ }
372
+
373
+ // packages/aws/src/index.ts
374
+ import { execFile } from "node:child_process";
375
+ import { cp, mkdir as mkdir2, readFile as readFile3, rm, writeFile as writeFile2 } from "node:fs/promises";
376
+ import { join as join2, resolve as resolve2 } from "node:path";
377
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
378
+ import { promisify } from "node:util";
379
+
380
+ // node_modules/fflate/esm/index.mjs
381
+ import { createRequire } from "module";
382
+ var require2 = createRequire("/");
383
+ var _a;
384
+ var Worker;
385
+ var isMarkedAsUntransferable;
386
+ try {
387
+ _a = require2("worker_threads"), Worker = _a.Worker, isMarkedAsUntransferable = _a.isMarkedAsUntransferable;
388
+ } catch (e) {
389
+ }
390
+ var u8 = Uint8Array;
391
+ var u16 = Uint16Array;
392
+ var i32 = Int32Array;
393
+ var fleb = new u8([
394
+ 0,
395
+ 0,
396
+ 0,
397
+ 0,
398
+ 0,
399
+ 0,
400
+ 0,
401
+ 0,
402
+ 1,
403
+ 1,
404
+ 1,
405
+ 1,
406
+ 2,
407
+ 2,
408
+ 2,
409
+ 2,
410
+ 3,
411
+ 3,
412
+ 3,
413
+ 3,
414
+ 4,
415
+ 4,
416
+ 4,
417
+ 4,
418
+ 5,
419
+ 5,
420
+ 5,
421
+ 5,
422
+ 0,
423
+ /* unused */
424
+ 0,
425
+ 0,
426
+ /* impossible */
427
+ 0
428
+ ]);
429
+ var fdeb = new u8([
430
+ 0,
431
+ 0,
432
+ 0,
433
+ 0,
434
+ 1,
435
+ 1,
436
+ 2,
437
+ 2,
438
+ 3,
439
+ 3,
440
+ 4,
441
+ 4,
442
+ 5,
443
+ 5,
444
+ 6,
445
+ 6,
446
+ 7,
447
+ 7,
448
+ 8,
449
+ 8,
450
+ 9,
451
+ 9,
452
+ 10,
453
+ 10,
454
+ 11,
455
+ 11,
456
+ 12,
457
+ 12,
458
+ 13,
459
+ 13,
460
+ /* unused */
461
+ 0,
462
+ 0
463
+ ]);
464
+ var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
465
+ var freb = function(eb, start) {
466
+ var b = new u16(31);
467
+ for (var i2 = 0; i2 < 31; ++i2) {
468
+ b[i2] = start += 1 << eb[i2 - 1];
469
+ }
470
+ var r = new i32(b[30]);
471
+ for (var i2 = 1; i2 < 30; ++i2) {
472
+ for (var j = b[i2]; j < b[i2 + 1]; ++j) {
473
+ r[j] = j - b[i2] << 5 | i2;
474
+ }
475
+ }
476
+ return { b, r };
477
+ };
478
+ var _a = freb(fleb, 2);
479
+ var fl = _a.b;
480
+ var revfl = _a.r;
481
+ fl[28] = 258, revfl[258] = 28;
482
+ var _b = freb(fdeb, 0);
483
+ var fd = _b.b;
484
+ var revfd = _b.r;
485
+ var rev = new u16(32768);
486
+ for (i = 0; i < 32768; ++i) {
487
+ x = (i & 43690) >> 1 | (i & 21845) << 1;
488
+ x = (x & 52428) >> 2 | (x & 13107) << 2;
489
+ x = (x & 61680) >> 4 | (x & 3855) << 4;
490
+ rev[i] = ((x & 65280) >> 8 | (x & 255) << 8) >> 1;
491
+ }
492
+ var x;
493
+ var i;
494
+ var hMap = (function(cd, mb, r) {
495
+ var s = cd.length;
496
+ var i2 = 0;
497
+ var l = new u16(mb);
498
+ for (; i2 < s; ++i2) {
499
+ if (cd[i2])
500
+ ++l[cd[i2] - 1];
501
+ }
502
+ var le = new u16(mb);
144
503
  for (i2 = 1; i2 < mb; ++i2) {
145
504
  le[i2] = le[i2 - 1] + l[i2 - 1] << 1;
146
505
  }
@@ -401,659 +760,342 @@ var wblk = function(dat, out, final, syms, lf, df, eb, li, bs, bl, p) {
401
760
  wbits(out, p, clct[i2] >> 5 & 127), p += clct[i2] >> 12;
402
761
  }
403
762
  }
404
- } else {
405
- lm = flm, ll = flt, dm = fdm, dl = fdt;
406
- }
407
- for (var i2 = 0; i2 < li; ++i2) {
408
- var sym = syms[i2];
409
- if (sym > 255) {
410
- var len = sym >> 18 & 31;
411
- wbits16(out, p, lm[len + 257]), p += ll[len + 257];
412
- if (len > 7)
413
- wbits(out, p, sym >> 23 & 31), p += fleb[len];
414
- var dst = sym & 31;
415
- wbits16(out, p, dm[dst]), p += dl[dst];
416
- if (dst > 3)
417
- wbits16(out, p, sym >> 5 & 8191), p += fdeb[dst];
418
- } else {
419
- wbits16(out, p, lm[sym]), p += ll[sym];
420
- }
421
- }
422
- wbits16(out, p, lm[256]);
423
- return p + ll[256];
424
- };
425
- var deo = /* @__PURE__ */ new i32([65540, 131080, 131088, 131104, 262176, 1048704, 1048832, 2114560, 2117632]);
426
- var et = /* @__PURE__ */ new u8(0);
427
- var dflt = function(dat, lvl, plvl, pre, post, st) {
428
- var s = st.z || dat.length;
429
- var o = new u8(pre + s + 5 * (1 + Math.ceil(s / 7e3)) + post);
430
- var w = o.subarray(pre, o.length - post);
431
- var lst = st.l;
432
- var pos = (st.r || 0) & 7;
433
- if (lvl) {
434
- if (pos)
435
- w[0] = st.r >> 3;
436
- var opt = deo[lvl - 1];
437
- var n = opt >> 13, c = opt & 8191;
438
- var msk_1 = (1 << plvl) - 1;
439
- var prev = st.p || new u16(32768), head = st.h || new u16(msk_1 + 1);
440
- var bs1_1 = Math.ceil(plvl / 3), bs2_1 = 2 * bs1_1;
441
- var hsh = function(i3) {
442
- return (dat[i3] ^ dat[i3 + 1] << bs1_1 ^ dat[i3 + 2] << bs2_1) & msk_1;
443
- };
444
- var syms = new i32(25e3);
445
- var lf = new u16(288), df = new u16(32);
446
- var lc_1 = 0, eb = 0, i2 = st.i || 0, li = 0, wi = st.w || 0, bs = 0;
447
- for (; i2 + 2 < s; ++i2) {
448
- var hv = hsh(i2);
449
- var imod = i2 & 32767, pimod = head[hv];
450
- prev[imod] = pimod;
451
- head[hv] = imod;
452
- if (wi <= i2) {
453
- var rem = s - i2;
454
- if ((lc_1 > 7e3 || li > 24576) && (rem > 423 || !lst)) {
455
- pos = wblk(dat, w, 0, syms, lf, df, eb, li, bs, i2 - bs, pos);
456
- li = lc_1 = eb = 0, bs = i2;
457
- for (var j = 0; j < 286; ++j)
458
- lf[j] = 0;
459
- for (var j = 0; j < 30; ++j)
460
- df[j] = 0;
461
- }
462
- var l = 2, d = 0, ch_1 = c, dif = imod - pimod & 32767;
463
- if (rem > 2 && hv == hsh(i2 - dif)) {
464
- var maxn = Math.min(n, rem) - 1;
465
- var maxd = Math.min(32767, i2);
466
- var ml = Math.min(258, rem);
467
- while (dif <= maxd && --ch_1 && imod != pimod) {
468
- if (dat[i2 + l] == dat[i2 + l - dif]) {
469
- var nl = 0;
470
- for (; nl < ml && dat[i2 + nl] == dat[i2 + nl - dif]; ++nl)
471
- ;
472
- if (nl > l) {
473
- l = nl, d = dif;
474
- if (nl > maxn)
475
- break;
476
- var mmd = Math.min(dif, nl - 2);
477
- var md = 0;
478
- for (var j = 0; j < mmd; ++j) {
479
- var ti = i2 - dif + j & 32767;
480
- var pti = prev[ti];
481
- var cd = ti - pti & 32767;
482
- if (cd > md)
483
- md = cd, pimod = ti;
484
- }
485
- }
486
- }
487
- imod = pimod, pimod = prev[imod];
488
- dif += imod - pimod & 32767;
489
- }
490
- }
491
- if (d) {
492
- syms[li++] = 268435456 | revfl[l] << 18 | revfd[d];
493
- var lin = revfl[l] & 31, din = revfd[d] & 31;
494
- eb += fleb[lin] + fdeb[din];
495
- ++lf[257 + lin];
496
- ++df[din];
497
- wi = i2 + l;
498
- ++lc_1;
499
- } else {
500
- syms[li++] = dat[i2];
501
- ++lf[dat[i2]];
502
- }
503
- }
504
- }
505
- for (i2 = Math.max(i2, wi); i2 < s; ++i2) {
506
- syms[li++] = dat[i2];
507
- ++lf[dat[i2]];
508
- }
509
- pos = wblk(dat, w, lst, syms, lf, df, eb, li, bs, i2 - bs, pos);
510
- if (!lst) {
511
- st.r = pos & 7 | w[pos / 8 | 0] << 3;
512
- pos -= 7;
513
- st.h = head, st.p = prev, st.i = i2, st.w = wi;
514
- }
515
- } else {
516
- for (var i2 = st.w || 0; i2 < s + lst; i2 += 65535) {
517
- var e = i2 + 65535;
518
- if (e >= s) {
519
- w[pos / 8 | 0] = lst;
520
- e = s;
521
- }
522
- pos = wfblk(w, pos + 1, dat.subarray(i2, e));
523
- }
524
- st.i = s;
525
- }
526
- return slc(o, 0, pre + shft(pos) + post);
527
- };
528
- var crct = /* @__PURE__ */ (function() {
529
- var t = new Int32Array(256);
530
- for (var i2 = 0; i2 < 256; ++i2) {
531
- var c = i2, k = 9;
532
- while (--k)
533
- c = (c & 1 && -306674912) ^ c >>> 1;
534
- t[i2] = c;
535
- }
536
- return t;
537
- })();
538
- var crc = function() {
539
- var c = -1;
540
- return {
541
- p: function(d) {
542
- var cr = c;
543
- for (var i2 = 0; i2 < d.length; ++i2)
544
- cr = crct[cr & 255 ^ d[i2]] ^ cr >>> 8;
545
- c = cr;
546
- },
547
- d: function() {
548
- return ~c;
549
- }
550
- };
551
- };
552
- var dopt = function(dat, opt, pre, post, st) {
553
- if (!st) {
554
- st = { l: 1 };
555
- if (opt.dictionary) {
556
- var dict = opt.dictionary.subarray(-32768);
557
- var newDat = new u8(dict.length + dat.length);
558
- newDat.set(dict);
559
- newDat.set(dat, dict.length);
560
- dat = newDat;
561
- st.w = dict.length;
562
- }
563
- }
564
- return dflt(dat, opt.level == null ? 6 : opt.level, opt.mem == null ? st.l ? Math.ceil(Math.max(8, Math.min(13, Math.log(dat.length))) * 1.5) : 20 : 12 + opt.mem, pre, post, st);
565
- };
566
- var mrg = function(a, b) {
567
- var o = {};
568
- for (var k in a)
569
- o[k] = a[k];
570
- for (var k in b)
571
- o[k] = b[k];
572
- return o;
573
- };
574
- var wbytes = function(d, b, v) {
575
- for (; v; ++b)
576
- d[b] = v, v >>>= 8;
577
- };
578
- function deflateSync(data, opts) {
579
- return dopt(data, opts || {}, 0, 0);
580
- }
581
- var fltn = function(d, p, t, o) {
582
- for (var k in d) {
583
- var val = d[k], n = p + k, op = o;
584
- if (Array.isArray(val))
585
- op = mrg(o, val[1]), val = val[0];
586
- if (ArrayBuffer.isView(val))
587
- t[n] = [val, op];
588
- else {
589
- t[n += "/"] = [new u8(0), op];
590
- fltn(val, n, t, o);
591
- }
592
- }
593
- };
594
- var te = typeof TextEncoder != "undefined" && /* @__PURE__ */ new TextEncoder();
595
- var td = typeof TextDecoder != "undefined" && /* @__PURE__ */ new TextDecoder();
596
- var tds = 0;
597
- try {
598
- td.decode(et, { stream: true });
599
- tds = 1;
600
- } catch (e) {
601
- }
602
- function strToU8(str, latin1) {
603
- if (latin1) {
604
- var ar_1 = new u8(str.length);
605
- for (var i2 = 0; i2 < str.length; ++i2)
606
- ar_1[i2] = str.charCodeAt(i2);
607
- return ar_1;
608
- }
609
- if (te)
610
- return te.encode(str);
611
- var l = str.length;
612
- var ar = new u8(str.length + (str.length >> 1));
613
- var ai = 0;
614
- var w = function(v) {
615
- ar[ai++] = v;
616
- };
617
- for (var i2 = 0; i2 < l; ++i2) {
618
- if (ai + 5 > ar.length) {
619
- var n = new u8(ai + 8 + (l - i2 << 1));
620
- n.set(ar);
621
- ar = n;
622
- }
623
- var c = str.charCodeAt(i2);
624
- if (c < 128 || latin1)
625
- w(c);
626
- else if (c < 2048)
627
- w(192 | c >> 6), w(128 | c & 63);
628
- else if (c > 55295 && c < 57344)
629
- c = 65536 + (c & 1023 << 10) | str.charCodeAt(++i2) & 1023, w(240 | c >> 18), w(128 | c >> 12 & 63), w(128 | c >> 6 & 63), w(128 | c & 63);
630
- else
631
- w(224 | c >> 12), w(128 | c >> 6 & 63), w(128 | c & 63);
632
- }
633
- return slc(ar, 0, ai);
634
- }
635
- var exfl = function(ex) {
636
- var le = 0;
637
- if (ex) {
638
- for (var k in ex) {
639
- var l = ex[k].length;
640
- if (l > 65535)
641
- err(9);
642
- le += l + 4;
643
- }
644
- }
645
- return le;
646
- };
647
- var wzh = function(d, b, f, fn, u, c, ce, co) {
648
- var fl2 = fn.length, ex = f.extra, col = co && co.length;
649
- var exl = exfl(ex);
650
- wbytes(d, b, ce != null ? 33639248 : 67324752), b += 4;
651
- if (ce != null)
652
- d[b++] = 20, d[b++] = f.os;
653
- d[b] = 20, b += 2;
654
- d[b++] = f.flag << 1 | (c < 0 && 8), d[b++] = u && 8;
655
- d[b++] = f.compression & 255, d[b++] = f.compression >> 8;
656
- var dt = new Date(f.mtime == null ? Date.now() : f.mtime), y = dt.getFullYear() - 1980;
657
- if (y < 0 || y > 119)
658
- err(10);
659
- wbytes(d, b, y << 25 | dt.getMonth() + 1 << 21 | dt.getDate() << 16 | dt.getHours() << 11 | dt.getMinutes() << 5 | dt.getSeconds() >> 1), b += 4;
660
- if (c != -1) {
661
- wbytes(d, b, f.crc);
662
- wbytes(d, b + 4, c < 0 ? -c - 2 : c);
663
- wbytes(d, b + 8, f.size);
664
- }
665
- wbytes(d, b + 12, fl2);
666
- wbytes(d, b + 14, exl), b += 16;
667
- if (ce != null) {
668
- wbytes(d, b, col);
669
- wbytes(d, b + 6, f.attrs);
670
- wbytes(d, b + 10, ce), b += 14;
763
+ } else {
764
+ lm = flm, ll = flt, dm = fdm, dl = fdt;
671
765
  }
672
- d.set(fn, b);
673
- b += fl2;
674
- if (exl) {
675
- for (var k in ex) {
676
- var exf = ex[k], l = exf.length;
677
- wbytes(d, b, +k);
678
- wbytes(d, b + 2, l);
679
- d.set(exf, b + 4), b += 4 + l;
766
+ for (var i2 = 0; i2 < li; ++i2) {
767
+ var sym = syms[i2];
768
+ if (sym > 255) {
769
+ var len = sym >> 18 & 31;
770
+ wbits16(out, p, lm[len + 257]), p += ll[len + 257];
771
+ if (len > 7)
772
+ wbits(out, p, sym >> 23 & 31), p += fleb[len];
773
+ var dst = sym & 31;
774
+ wbits16(out, p, dm[dst]), p += dl[dst];
775
+ if (dst > 3)
776
+ wbits16(out, p, sym >> 5 & 8191), p += fdeb[dst];
777
+ } else {
778
+ wbits16(out, p, lm[sym]), p += ll[sym];
680
779
  }
681
780
  }
682
- if (col)
683
- d.set(co, b), b += col;
684
- return b;
685
- };
686
- var wzf = function(o, b, c, d, e) {
687
- wbytes(o, b, 101010256);
688
- wbytes(o, b + 8, c);
689
- wbytes(o, b + 10, c);
690
- wbytes(o, b + 12, d);
691
- wbytes(o, b + 16, e);
781
+ wbits16(out, p, lm[256]);
782
+ return p + ll[256];
692
783
  };
693
- function zipSync(data, opts) {
694
- if (!opts)
695
- opts = {};
696
- var r = {};
697
- var files = [];
698
- fltn(data, "", r, opts);
699
- var o = 0;
700
- var tot = 0;
701
- for (var fn in r) {
702
- var _a2 = r[fn], file = _a2[0], p = _a2[1];
703
- var compression = p.level == 0 ? 0 : 8;
704
- var f = strToU8(fn), s = f.length;
705
- var com = p.comment, m = com && strToU8(com), ms = m && m.length;
706
- var exl = exfl(p.extra);
707
- if (s > 65535)
708
- err(11);
709
- var d = compression ? deflateSync(file, p) : file, l = d.length;
710
- var c = crc();
711
- c.p(file);
712
- files.push(mrg(p, {
713
- size: file.length,
714
- crc: c.d(),
715
- c: d,
716
- f,
717
- m,
718
- u: s != fn.length || m && com.length != ms,
719
- o,
720
- compression
721
- }));
722
- o += 30 + s + exl + l;
723
- tot += 76 + 2 * (s + exl) + (ms || 0) + l;
724
- }
725
- var out = new u8(tot + 22), oe = o, cdl = tot - o;
726
- for (var i2 = 0; i2 < files.length; ++i2) {
727
- var f = files[i2];
728
- wzh(out, f.o, f, f.f, f.u, f.c.length);
729
- var badd = 30 + f.f.length + exfl(f.extra);
730
- out.set(f.c, f.o + badd);
731
- wzh(out, o, f, f.f, f.u, f.c.length, f.o, f.m), o += 16 + badd + (f.m ? f.m.length : 0);
732
- }
733
- wzf(out, o, files.length, cdl, oe);
734
- return out;
735
- }
736
-
737
- // packages/aws/src/release.ts
738
- import { createHash } from "node:crypto";
739
- import { mkdir, readFile, writeFile } from "node:fs/promises";
740
- import { join, resolve } from "node:path";
741
- import { parse, stringify } from "yaml";
742
- import { z as z2 } from "zod";
743
-
744
- // packages/aws/src/internal-board.ts
745
- import { z } from "zod";
746
- var durationSchema = z.string().regex(/^\d+(?:ms|s|m|h)$/).refine((value2) => Number.parseInt(value2, 10) > 0).brand();
747
- var positionSchema = z.union([
748
- z.object({
749
- x: z.number().int().min(0),
750
- y: z.number().int().min(0),
751
- w: z.number().int().min(1).max(12),
752
- h: z.number().int().min(1)
753
- }),
754
- z.object({ x: z.literal(0), y: z.literal(0), w: z.literal(0), h: z.literal(0) })
755
- ]);
756
- var panelSchema = z.looseObject({
757
- // Presentation-only; ids still address proxy calls and the generated allowlist.
758
- label: z.string().min(1).optional(),
759
- id: z.string().min(1),
760
- type: z.string().min(1),
761
- source: z.string().min(1).optional(),
762
- // Cosmetic roles remain open so newer board files can safely fall back on older runtimes.
763
- display: z.string().min(1).optional(),
764
- position: positionSchema.optional(),
765
- refresh: durationSchema.optional(),
766
- link: z.url().optional(),
767
- url: z.url().optional(),
768
- json_path: z.string().regex(/^\$(?:\.[A-Za-z_][A-Za-z0-9_]*|\[\d+\])*$/).optional()
769
- });
770
- var sourceSchema = z.looseObject({
771
- type: z.string().min(1),
772
- token_env: z.string().min(1).optional()
773
- });
774
- var boardSchema = z.object({
775
- refresh: durationSchema.optional(),
776
- panels: z.array(panelSchema).min(1).superRefine((panels, ctx) => {
777
- const seen = /* @__PURE__ */ new Map();
778
- panels.forEach((panel, index) => {
779
- const firstIndex = seen.get(panel.id);
780
- if (firstIndex === void 0) seen.set(panel.id, index);
781
- else
782
- ctx.addIssue({
783
- code: "custom",
784
- path: [index, "id"],
785
- message: `duplicate panel id "${panel.id}" (already used by panel at index ${firstIndex})`
786
- });
787
- });
788
- })
789
- });
790
- var boardConfigSchema = z.object({
791
- sources: z.record(z.string().min(1), sourceSchema).default({}),
792
- boards: z.record(z.string().min(1), boardSchema),
793
- auth: z.object({
794
- issuer: z.url(),
795
- client_id: z.string().min(1).optional(),
796
- allow: z.object({
797
- groups: z.array(z.string().min(1)).optional(),
798
- subjects: z.array(z.string().min(1)).optional()
799
- }).optional()
800
- }).optional()
801
- });
802
-
803
- // packages/aws/src/release.ts
804
- var CORE_RUNTIME_VERSION = "1.0.0";
805
- var CANONICAL_ASSET_DOMAIN = "https://public-assets.zegreatrob.com";
806
- function clientAssetUrl(version, domain = CANONICAL_ASSET_DOMAIN) {
807
- if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(version))
808
- throw new Error(`Invalid dashboard version: ${version}`);
809
- return `${domain.replace(/\/+$/, "")}/dashboard/${version}`;
810
- }
811
- async function validateBoardConfig(path) {
812
- const source = await readFile(resolve(path), "utf8");
813
- const result = boardConfigSchema.safeParse(parse(source));
814
- if (!result.success)
815
- throw new Error(`Invalid board configuration:
816
- ${z2.prettifyError(result.error)}`);
817
- const yaml = stringify(result.data, { sortMapEntries: true });
818
- return {
819
- yaml,
820
- sha256: sha256(yaml),
821
- usesCredentials: Object.values(result.data.sources).some((source2) => Boolean(source2.token_env))
822
- };
823
- }
824
- async function assembleRelease(input) {
825
- const board = await validateBoardConfig(input.boardConfigPath);
826
- if (board.usesCredentials && !input.secretReference)
827
- throw new Error(
828
- "Board config uses token_env; SecretReference must name a Secrets Manager credential-map or Parameter Store SecureString ARN"
829
- );
830
- const outputDir = resolve(input.outputDir);
831
- await mkdir(outputDir, { recursive: true });
832
- await writeFile(join(outputDir, "board.yaml"), board.yaml);
833
- const metadata = {
834
- dashboardVersion: input.version,
835
- clientAssetUrl: clientAssetUrl(input.version, input.assetDomain),
836
- serverRuntimeVersion: CORE_RUNTIME_VERSION,
837
- supportedProviders: input.providers ?? ["aws-lambda"],
838
- artifactChecksums: { "board.yaml": board.sha256 },
839
- runtimeCompatibility: { node: ">=22" }
840
- };
841
- const metadataText = `${JSON.stringify(metadata, null, 2)}
842
- `;
843
- await writeFile(join(outputDir, "release.json"), metadataText);
844
- const files = { "board.yaml": board.sha256, "release.json": sha256(metadataText) };
845
- await writeFile(
846
- join(outputDir, "SHA256SUMS"),
847
- `${Object.entries(files).map(([name, digest]) => `${digest} ${name}`).join("\n")}
848
- `
849
- );
850
- return { metadata, files };
851
- }
852
- function sha256(value2) {
853
- return createHash("sha256").update(value2).digest("hex");
854
- }
855
-
856
- // packages/aws/src/bootstrap.ts
857
- import { readFile as readFile2 } from "node:fs/promises";
858
- import { fileURLToPath } from "node:url";
859
- var templates = {
860
- core: "../bootstrap/core-v1.yml",
861
- "github-oidc": "../bootstrap/github-oidc-v2.yml"
784
+ var deo = /* @__PURE__ */ new i32([65540, 131080, 131088, 131104, 262176, 1048704, 1048832, 2114560, 2117632]);
785
+ var et = /* @__PURE__ */ new u8(0);
786
+ var dflt = function(dat, lvl, plvl, pre, post, st) {
787
+ var s = st.z || dat.length;
788
+ var o = new u8(pre + s + 5 * (1 + Math.ceil(s / 7e3)) + post);
789
+ var w = o.subarray(pre, o.length - post);
790
+ var lst = st.l;
791
+ var pos = (st.r || 0) & 7;
792
+ if (lvl) {
793
+ if (pos)
794
+ w[0] = st.r >> 3;
795
+ var opt = deo[lvl - 1];
796
+ var n = opt >> 13, c = opt & 8191;
797
+ var msk_1 = (1 << plvl) - 1;
798
+ var prev = st.p || new u16(32768), head = st.h || new u16(msk_1 + 1);
799
+ var bs1_1 = Math.ceil(plvl / 3), bs2_1 = 2 * bs1_1;
800
+ var hsh = function(i3) {
801
+ return (dat[i3] ^ dat[i3 + 1] << bs1_1 ^ dat[i3 + 2] << bs2_1) & msk_1;
802
+ };
803
+ var syms = new i32(25e3);
804
+ var lf = new u16(288), df = new u16(32);
805
+ var lc_1 = 0, eb = 0, i2 = st.i || 0, li = 0, wi = st.w || 0, bs = 0;
806
+ for (; i2 + 2 < s; ++i2) {
807
+ var hv = hsh(i2);
808
+ var imod = i2 & 32767, pimod = head[hv];
809
+ prev[imod] = pimod;
810
+ head[hv] = imod;
811
+ if (wi <= i2) {
812
+ var rem = s - i2;
813
+ if ((lc_1 > 7e3 || li > 24576) && (rem > 423 || !lst)) {
814
+ pos = wblk(dat, w, 0, syms, lf, df, eb, li, bs, i2 - bs, pos);
815
+ li = lc_1 = eb = 0, bs = i2;
816
+ for (var j = 0; j < 286; ++j)
817
+ lf[j] = 0;
818
+ for (var j = 0; j < 30; ++j)
819
+ df[j] = 0;
820
+ }
821
+ var l = 2, d = 0, ch_1 = c, dif = imod - pimod & 32767;
822
+ if (rem > 2 && hv == hsh(i2 - dif)) {
823
+ var maxn = Math.min(n, rem) - 1;
824
+ var maxd = Math.min(32767, i2);
825
+ var ml = Math.min(258, rem);
826
+ while (dif <= maxd && --ch_1 && imod != pimod) {
827
+ if (dat[i2 + l] == dat[i2 + l - dif]) {
828
+ var nl = 0;
829
+ for (; nl < ml && dat[i2 + nl] == dat[i2 + nl - dif]; ++nl)
830
+ ;
831
+ if (nl > l) {
832
+ l = nl, d = dif;
833
+ if (nl > maxn)
834
+ break;
835
+ var mmd = Math.min(dif, nl - 2);
836
+ var md = 0;
837
+ for (var j = 0; j < mmd; ++j) {
838
+ var ti = i2 - dif + j & 32767;
839
+ var pti = prev[ti];
840
+ var cd = ti - pti & 32767;
841
+ if (cd > md)
842
+ md = cd, pimod = ti;
843
+ }
844
+ }
845
+ }
846
+ imod = pimod, pimod = prev[imod];
847
+ dif += imod - pimod & 32767;
848
+ }
849
+ }
850
+ if (d) {
851
+ syms[li++] = 268435456 | revfl[l] << 18 | revfd[d];
852
+ var lin = revfl[l] & 31, din = revfd[d] & 31;
853
+ eb += fleb[lin] + fdeb[din];
854
+ ++lf[257 + lin];
855
+ ++df[din];
856
+ wi = i2 + l;
857
+ ++lc_1;
858
+ } else {
859
+ syms[li++] = dat[i2];
860
+ ++lf[dat[i2]];
861
+ }
862
+ }
863
+ }
864
+ for (i2 = Math.max(i2, wi); i2 < s; ++i2) {
865
+ syms[li++] = dat[i2];
866
+ ++lf[dat[i2]];
867
+ }
868
+ pos = wblk(dat, w, lst, syms, lf, df, eb, li, bs, i2 - bs, pos);
869
+ if (!lst) {
870
+ st.r = pos & 7 | w[pos / 8 | 0] << 3;
871
+ pos -= 7;
872
+ st.h = head, st.p = prev, st.i = i2, st.w = wi;
873
+ }
874
+ } else {
875
+ for (var i2 = st.w || 0; i2 < s + lst; i2 += 65535) {
876
+ var e = i2 + 65535;
877
+ if (e >= s) {
878
+ w[pos / 8 | 0] = lst;
879
+ e = s;
880
+ }
881
+ pos = wfblk(w, pos + 1, dat.subarray(i2, e));
882
+ }
883
+ st.i = s;
884
+ }
885
+ return slc(o, 0, pre + shft(pos) + post);
862
886
  };
863
- async function bootstrapTemplatePath(kind) {
864
- if (!Object.hasOwn(templates, kind)) throw new Error(`Unknown bootstrap template: ${kind}`);
865
- return fileURLToPath(new URL(templates[kind], import.meta.url));
866
- }
867
- async function bootstrapTemplate(kind) {
868
- return readFile2(await bootstrapTemplatePath(kind), "utf8");
869
- }
870
- function bootstrapContractVersion(template) {
871
- const version = template.match(/BootstrapContractVersion:\s*\{\s*Value:\s*'([^']+)'\s*}/)?.[1];
872
- if (!version) throw new Error("Bootstrap template has no BootstrapContractVersion output");
873
- return version;
874
- }
875
- function bootstrapTemplateRevision(template) {
876
- const revision = template.match(/BootstrapTemplateRevision:\s*\{\s*Value:\s*'([^']+)'\s*}/)?.[1];
877
- if (!revision) throw new Error("Bootstrap template has no BootstrapTemplateRevision output");
878
- return revision;
879
- }
880
- function templateRevisionIfPresent(template) {
881
- return template.match(/BootstrapTemplateRevision:\s*\{\s*Value:\s*'([^']+)'\s*}/)?.[1];
882
- }
883
- function inspectTemplate(kind, path, template) {
884
- const resourcesBlock = template.match(/^Resources:\n([\s\S]*?)(?=^[A-Za-z][A-Za-z0-9]*:\s*$)/m)?.[1] ?? "";
885
- const resources = [
886
- ...resourcesBlock.matchAll(/^ {2}([A-Za-z][A-Za-z0-9]+):\n {4}Type: ([^\n]+)$/gm)
887
- ].map((match) => ({ logicalId: match[1], type: match[2] }));
888
- const iamActions = [...template.matchAll(/^\s+Action: (?:\[([^\]]+)\]|([^\n]+))$/gm)].flatMap(([, list, scalar]) => (list ?? scalar ?? "").split(",").map((action) => action.trim())).filter(Boolean);
889
- return {
890
- kind,
891
- path,
892
- contractVersion: bootstrapContractVersion(template),
893
- templateRevision: templateRevisionIfPresent(template),
894
- sha256: sha256(template),
895
- resources,
896
- iamActions: [...new Set(iamActions)]
897
- };
898
- }
899
- async function bootstrapPlan(config) {
900
- const packageManifest = JSON.parse(
901
- await readFile2(new URL("../package.json", import.meta.url), "utf8")
902
- );
903
- const packageTemplates = await Promise.all(
904
- ["core", "github-oidc"].map(async (kind) => {
905
- const [path, template] = await Promise.all([
906
- bootstrapTemplatePath(kind),
907
- bootstrapTemplate(kind)
908
- ]);
909
- return inspectTemplate(kind, path, template);
910
- })
911
- );
887
+ var crct = /* @__PURE__ */ (function() {
888
+ var t = new Int32Array(256);
889
+ for (var i2 = 0; i2 < 256; ++i2) {
890
+ var c = i2, k = 9;
891
+ while (--k)
892
+ c = (c & 1 && -306674912) ^ c >>> 1;
893
+ t[i2] = c;
894
+ }
895
+ return t;
896
+ })();
897
+ var crc = function() {
898
+ var c = -1;
912
899
  return {
913
- packageVersion: typeof packageManifest.version === "string" ? packageManifest.version : "unknown",
914
- packageTemplates,
915
- configuration: config,
916
- notes: [
917
- "Templates are owned by the installed npm package; package.json and package-lock.json pin the source version.",
918
- "Generated CloudFormation parameter files and describe-stacks captures are deployment artifacts, not source configuration.",
919
- "This plan performs no AWS or GitHub mutations."
920
- ]
900
+ p: function(d) {
901
+ var cr = c;
902
+ for (var i2 = 0; i2 < d.length; ++i2)
903
+ cr = crct[cr & 255 ^ d[i2]] ^ cr >>> 8;
904
+ c = cr;
905
+ },
906
+ d: function() {
907
+ return ~c;
908
+ }
921
909
  };
922
- }
923
- function capturedStack(input) {
924
- const stack = input && typeof input === "object" && "Stacks" in input ? input.Stacks?.[0] : input;
925
- return stack && typeof stack === "object" ? stack : {};
926
- }
927
- function outputValues(stack) {
928
- return Object.fromEntries(
929
- (stack.Outputs ?? []).map(({ OutputKey, OutputValue }) => [OutputKey, OutputValue])
930
- );
931
- }
932
- function bootstrapConsistency(kind, config, stackInput, expectedContract, expectedRevision, coreOutputs = {}) {
933
- const stack = capturedStack(stackInput);
934
- const mismatches = [];
935
- const expectedStackName = kind === "core" ? config.core?.stackName : config.githubOidc?.stackName;
936
- if (stack.StackName !== expectedStackName)
937
- mismatches.push(
938
- `stack name is ${stack.StackName ?? "missing"}; expected ${expectedStackName ?? "missing"}`
939
- );
940
- const observedRegion = stack.StackId?.match(/^arn:[^:]+:cloudformation:([^:]+):/)?.[1];
941
- if (observedRegion !== config.region)
942
- mismatches.push(
943
- `Region is ${observedRegion ?? "missing"}; expected ${config.region ?? "missing"}`
944
- );
945
- if (!["CREATE_COMPLETE", "UPDATE_COMPLETE"].includes(stack.StackStatus ?? ""))
946
- mismatches.push(
947
- `stack status is ${stack.StackStatus ?? "missing"}; expected a successful stable status`
948
- );
949
- const outputs2 = outputValues(stack);
950
- if (outputs2.BootstrapContractVersion !== expectedContract)
951
- mismatches.push(
952
- `contract is ${outputs2.BootstrapContractVersion ?? "missing"}; expected ${expectedContract}`
953
- );
954
- if (expectedRevision && outputs2.BootstrapTemplateRevision !== expectedRevision)
955
- mismatches.push(
956
- `template revision is ${outputs2.BootstrapTemplateRevision ?? "missing"}; expected ${expectedRevision}`
957
- );
958
- const requiredOutputs = kind === "core" ? ["ArtifactBucketName", "ApplicationStackName", "CloudFormationExecutionRoleArn"] : ["GitHubDeployRoleArn"];
959
- for (const key of requiredOutputs) if (!outputs2[key]) mismatches.push(`${key} output is missing`);
960
- if (kind === "core") {
961
- if (outputs2.ArtifactBucketName !== config.core?.artifactBucketName)
962
- mismatches.push(
963
- `ArtifactBucketName output is ${outputs2.ArtifactBucketName ?? "missing"}; expected ${config.core?.artifactBucketName ?? "missing"}`
964
- );
965
- if (outputs2.ApplicationStackName !== config.core?.applicationStackName)
966
- mismatches.push(
967
- `ApplicationStackName output is ${outputs2.ApplicationStackName ?? "missing"}; expected ${config.core?.applicationStackName ?? "missing"}`
968
- );
910
+ };
911
+ var dopt = function(dat, opt, pre, post, st) {
912
+ if (!st) {
913
+ st = { l: 1 };
914
+ if (opt.dictionary) {
915
+ var dict = opt.dictionary.subarray(-32768);
916
+ var newDat = new u8(dict.length + dat.length);
917
+ newDat.set(dict);
918
+ newDat.set(dat, dict.length);
919
+ dat = newDat;
920
+ st.w = dict.length;
921
+ }
969
922
  }
970
- const parameters = Object.fromEntries(
971
- (stack.Parameters ?? []).map(({ ParameterKey, ParameterValue }) => [
972
- ParameterKey,
973
- ParameterValue
974
- ])
975
- );
976
- const expectedParameters = kind === "core" ? {
977
- ArtifactBucketName: config.core?.artifactBucketName,
978
- ApplicationStackName: config.core?.applicationStackName,
979
- DashboardFunctionName: config.core?.dashboardFunctionName,
980
- RuntimeSecretArn: config.core?.runtimeSecretArn ?? "",
981
- ArtifactKmsKeyArn: config.core?.artifactKmsKeyArn ?? ""
982
- } : {
983
- GitHubOidcProviderArn: config.githubOidc?.providerArn,
984
- GitHubRepository: config.githubOidc?.repository,
985
- GitHubOwnerId: config.githubOidc?.ownerId,
986
- GitHubRepositoryId: config.githubOidc?.repositoryId,
987
- GitHubEnvironment: config.githubOidc?.environment,
988
- CoreBootstrapStackName: config.core?.stackName,
989
- ApplicationStackName: config.core?.applicationStackName,
990
- ArtifactBucketName: config.core?.artifactBucketName,
991
- CloudFormationExecutionRoleArn: coreOutputs.CloudFormationExecutionRoleArn,
992
- ConsumerGatewayStackName: config.githubOidc?.consumerGatewayStackName ?? ""
993
- };
994
- for (const [key, expected] of Object.entries(expectedParameters))
995
- if (expected !== void 0 && parameters[key] !== expected)
996
- mismatches.push(`${key} is ${parameters[key] ?? "missing"}; expected ${expected}`);
997
- return { ok: mismatches.length === 0, mismatches };
998
- }
999
- function mergeBootstrapParameters(requested, deployed) {
1000
- const supplied = new Map(requested.map((entry) => [entry.ParameterKey, entry.ParameterValue]));
1001
- const duplicates = requested.map((entry) => entry.ParameterKey);
1002
- if (new Set(duplicates).size !== duplicates.length)
1003
- throw new Error("Bootstrap parameter file contains duplicate parameters");
1004
- return deployed.map(({ ParameterKey, ParameterValue }) => ({
1005
- ParameterKey,
1006
- ParameterValue: supplied.get(ParameterKey) ?? ParameterValue
1007
- })).concat(
1008
- requested.filter(
1009
- ({ ParameterKey }) => !deployed.some((entry) => entry.ParameterKey === ParameterKey)
1010
- )
1011
- );
923
+ return dflt(dat, opt.level == null ? 6 : opt.level, opt.mem == null ? st.l ? Math.ceil(Math.max(8, Math.min(13, Math.log(dat.length))) * 1.5) : 20 : 12 + opt.mem, pre, post, st);
924
+ };
925
+ var mrg = function(a, b) {
926
+ var o = {};
927
+ for (var k in a)
928
+ o[k] = a[k];
929
+ for (var k in b)
930
+ o[k] = b[k];
931
+ return o;
932
+ };
933
+ var wbytes = function(d, b, v) {
934
+ for (; v; ++b)
935
+ d[b] = v, v >>>= 8;
936
+ };
937
+ function deflateSync(data, opts) {
938
+ return dopt(data, opts || {}, 0, 0);
1012
939
  }
1013
- function deployedBootstrapStack(input, expectedContractVersion) {
1014
- const stack = input && typeof input === "object" && "Stacks" in input ? input.Stacks?.[0] : input;
1015
- if (!stack || typeof stack !== "object") throw new Error("Deployed stack JSON contains no stack");
1016
- const deployed = stack;
1017
- const contract = deployed.Outputs?.find(
1018
- (output) => output.OutputKey === "BootstrapContractVersion"
1019
- )?.OutputValue;
1020
- if (contract !== expectedContractVersion)
1021
- throw new Error(
1022
- `Bootstrap contract mismatch (deployed: ${contract ?? "missing"}; expected: ${expectedContractVersion}); follow the documented migration procedure`
1023
- );
1024
- if (deployed.Parameters && !Array.isArray(deployed.Parameters))
1025
- throw new Error("Deployed stack JSON has invalid Parameters");
1026
- return deployed;
940
+ var fltn = function(d, p, t, o) {
941
+ for (var k in d) {
942
+ var val = d[k], n = p + k, op = o;
943
+ if (Array.isArray(val))
944
+ op = mrg(o, val[1]), val = val[0];
945
+ if (ArrayBuffer.isView(val))
946
+ t[n] = [val, op];
947
+ else {
948
+ t[n += "/"] = [new u8(0), op];
949
+ fltn(val, n, t, o);
950
+ }
951
+ }
952
+ };
953
+ var te = typeof TextEncoder != "undefined" && /* @__PURE__ */ new TextEncoder();
954
+ var td = typeof TextDecoder != "undefined" && /* @__PURE__ */ new TextDecoder();
955
+ var tds = 0;
956
+ try {
957
+ td.decode(et, { stream: true });
958
+ tds = 1;
959
+ } catch (e) {
1027
960
  }
1028
- function coreBootstrapOutputs(stack) {
1029
- const values = Object.fromEntries(
1030
- (stack.Outputs ?? []).filter(
1031
- (output) => Boolean(output.OutputValue)
1032
- ).map((output) => [output.OutputKey, output.OutputValue])
1033
- );
1034
- const required2 = ["ArtifactBucketName", "ApplicationStackName", "CloudFormationExecutionRoleArn"];
1035
- const missing = required2.filter((key) => !values[key]);
1036
- if (missing.length) throw new Error(`Core stack JSON is missing outputs: ${missing.join(", ")}`);
1037
- return values;
961
+ function strToU8(str, latin1) {
962
+ if (latin1) {
963
+ var ar_1 = new u8(str.length);
964
+ for (var i2 = 0; i2 < str.length; ++i2)
965
+ ar_1[i2] = str.charCodeAt(i2);
966
+ return ar_1;
967
+ }
968
+ if (te)
969
+ return te.encode(str);
970
+ var l = str.length;
971
+ var ar = new u8(str.length + (str.length >> 1));
972
+ var ai = 0;
973
+ var w = function(v) {
974
+ ar[ai++] = v;
975
+ };
976
+ for (var i2 = 0; i2 < l; ++i2) {
977
+ if (ai + 5 > ar.length) {
978
+ var n = new u8(ai + 8 + (l - i2 << 1));
979
+ n.set(ar);
980
+ ar = n;
981
+ }
982
+ var c = str.charCodeAt(i2);
983
+ if (c < 128 || latin1)
984
+ w(c);
985
+ else if (c < 2048)
986
+ w(192 | c >> 6), w(128 | c & 63);
987
+ else if (c > 55295 && c < 57344)
988
+ c = 65536 + (c & 1023 << 10) | str.charCodeAt(++i2) & 1023, w(240 | c >> 18), w(128 | c >> 12 & 63), w(128 | c >> 6 & 63), w(128 | c & 63);
989
+ else
990
+ w(224 | c >> 12), w(128 | c >> 6 & 63), w(128 | c & 63);
991
+ }
992
+ return slc(ar, 0, ai);
1038
993
  }
1039
- function requiredBootstrapParameters(kind, values) {
1040
- const keys = kind === "core" ? ["ArtifactBucketName", "ApplicationStackName", "DashboardFunctionName"] : [
1041
- "GitHubOidcProviderArn",
1042
- "GitHubRepository",
1043
- "GitHubOwnerId",
1044
- "GitHubRepositoryId",
1045
- "GitHubEnvironment",
1046
- "CoreBootstrapStackName",
1047
- "ApplicationStackName",
1048
- "ArtifactBucketName",
1049
- "CloudFormationExecutionRoleArn"
1050
- ];
1051
- const missing = keys.filter((key) => !values[key]);
1052
- if (missing.length) throw new Error(`Missing required bootstrap values: ${missing.join(", ")}`);
1053
- const optional2 = kind === "core" ? ["RuntimeSecretArn", "ArtifactKmsKeyArn"] : ["ConsumerGatewayStackName"];
1054
- return [...keys, ...optional2].filter((key) => values[key] !== void 0).map((ParameterKey) => ({ ParameterKey, ParameterValue: values[ParameterKey] ?? "" }));
994
+ var exfl = function(ex) {
995
+ var le = 0;
996
+ if (ex) {
997
+ for (var k in ex) {
998
+ var l = ex[k].length;
999
+ if (l > 65535)
1000
+ err(9);
1001
+ le += l + 4;
1002
+ }
1003
+ }
1004
+ return le;
1005
+ };
1006
+ var wzh = function(d, b, f, fn, u, c, ce, co) {
1007
+ var fl2 = fn.length, ex = f.extra, col = co && co.length;
1008
+ var exl = exfl(ex);
1009
+ wbytes(d, b, ce != null ? 33639248 : 67324752), b += 4;
1010
+ if (ce != null)
1011
+ d[b++] = 20, d[b++] = f.os;
1012
+ d[b] = 20, b += 2;
1013
+ d[b++] = f.flag << 1 | (c < 0 && 8), d[b++] = u && 8;
1014
+ d[b++] = f.compression & 255, d[b++] = f.compression >> 8;
1015
+ var dt = new Date(f.mtime == null ? Date.now() : f.mtime), y = dt.getFullYear() - 1980;
1016
+ if (y < 0 || y > 119)
1017
+ err(10);
1018
+ wbytes(d, b, y << 25 | dt.getMonth() + 1 << 21 | dt.getDate() << 16 | dt.getHours() << 11 | dt.getMinutes() << 5 | dt.getSeconds() >> 1), b += 4;
1019
+ if (c != -1) {
1020
+ wbytes(d, b, f.crc);
1021
+ wbytes(d, b + 4, c < 0 ? -c - 2 : c);
1022
+ wbytes(d, b + 8, f.size);
1023
+ }
1024
+ wbytes(d, b + 12, fl2);
1025
+ wbytes(d, b + 14, exl), b += 16;
1026
+ if (ce != null) {
1027
+ wbytes(d, b, col);
1028
+ wbytes(d, b + 6, f.attrs);
1029
+ wbytes(d, b + 10, ce), b += 14;
1030
+ }
1031
+ d.set(fn, b);
1032
+ b += fl2;
1033
+ if (exl) {
1034
+ for (var k in ex) {
1035
+ var exf = ex[k], l = exf.length;
1036
+ wbytes(d, b, +k);
1037
+ wbytes(d, b + 2, l);
1038
+ d.set(exf, b + 4), b += 4 + l;
1039
+ }
1040
+ }
1041
+ if (col)
1042
+ d.set(co, b), b += col;
1043
+ return b;
1044
+ };
1045
+ var wzf = function(o, b, c, d, e) {
1046
+ wbytes(o, b, 101010256);
1047
+ wbytes(o, b + 8, c);
1048
+ wbytes(o, b + 10, c);
1049
+ wbytes(o, b + 12, d);
1050
+ wbytes(o, b + 16, e);
1051
+ };
1052
+ function zipSync(data, opts) {
1053
+ if (!opts)
1054
+ opts = {};
1055
+ var r = {};
1056
+ var files = [];
1057
+ fltn(data, "", r, opts);
1058
+ var o = 0;
1059
+ var tot = 0;
1060
+ for (var fn in r) {
1061
+ var _a2 = r[fn], file = _a2[0], p = _a2[1];
1062
+ var compression = p.level == 0 ? 0 : 8;
1063
+ var f = strToU8(fn), s = f.length;
1064
+ var com = p.comment, m = com && strToU8(com), ms = m && m.length;
1065
+ var exl = exfl(p.extra);
1066
+ if (s > 65535)
1067
+ err(11);
1068
+ var d = compression ? deflateSync(file, p) : file, l = d.length;
1069
+ var c = crc();
1070
+ c.p(file);
1071
+ files.push(mrg(p, {
1072
+ size: file.length,
1073
+ crc: c.d(),
1074
+ c: d,
1075
+ f,
1076
+ m,
1077
+ u: s != fn.length || m && com.length != ms,
1078
+ o,
1079
+ compression
1080
+ }));
1081
+ o += 30 + s + exl + l;
1082
+ tot += 76 + 2 * (s + exl) + (ms || 0) + l;
1083
+ }
1084
+ var out = new u8(tot + 22), oe = o, cdl = tot - o;
1085
+ for (var i2 = 0; i2 < files.length; ++i2) {
1086
+ var f = files[i2];
1087
+ wzh(out, f.o, f, f.f, f.u, f.c.length);
1088
+ var badd = 30 + f.f.length + exfl(f.extra);
1089
+ out.set(f.c, f.o + badd);
1090
+ wzh(out, o, f, f.f, f.u, f.c.length, f.o, f.m), o += 16 + badd + (f.m ? f.m.length : 0);
1091
+ }
1092
+ wzf(out, o, files.length, cdl, oe);
1093
+ return out;
1055
1094
  }
1056
1095
 
1096
+ // packages/aws/src/index.ts
1097
+ import { parseDocument, Scalar } from "yaml";
1098
+
1057
1099
  // packages/aws/src/bootstrap-check.ts
1058
1100
  function formatBootstrapCheckText(result) {
1059
1101
  const lines = [`AWS bootstrap consistency (${result.packageVersion})`];
@@ -1473,14 +1515,16 @@ var githubOidcProvider = {
1473
1515
  }
1474
1516
  }
1475
1517
  };
1476
- async function contracts() {
1518
+ async function contracts(config) {
1477
1519
  return {
1478
- core: bootstrapContractVersion(await bootstrapTemplate("core")),
1479
- githubOidc: bootstrapContractVersion(await bootstrapTemplate("github-oidc"))
1520
+ core: bootstrapContractVersion(await bootstrapTemplate("core", computeMode(config))),
1521
+ githubOidc: bootstrapContractVersion(
1522
+ await bootstrapTemplate("github-oidc", computeMode(config))
1523
+ )
1480
1524
  };
1481
1525
  }
1482
1526
  async function bootstrapHandoff(input) {
1483
- const expectedContracts = await contracts();
1527
+ const expectedContracts = await contracts(input.config);
1484
1528
  const coreCapturePath = input.coreStackPath ?? workFile(input.workDir, "core-deployed-stack.json");
1485
1529
  const core = input.coreStack === void 0 ? void 0 : deployedBootstrapStack(input.coreStack, expectedContracts.core);
1486
1530
  const oidc = input.githubOidcStack === void 0 ? void 0 : deployedBootstrapStack(input.githubOidcStack, expectedContracts.githubOidc);
@@ -1490,7 +1534,7 @@ async function bootstrapHandoff(input) {
1490
1534
  ApplicationStackName: input.config.core?.applicationStackName,
1491
1535
  DashboardFunctionName: input.config.core?.dashboardFunctionName
1492
1536
  });
1493
- const templatePath = await bootstrapTemplatePath("core");
1537
+ const templatePath = await bootstrapTemplatePath("core", computeMode(input.config));
1494
1538
  return {
1495
1539
  phase: "core",
1496
1540
  expectedContracts,
@@ -1530,7 +1574,7 @@ async function bootstrapHandoff(input) {
1530
1574
  ConsumerGatewayStackName: input.config.githubOidc?.consumerGatewayStackName,
1531
1575
  ...coreBootstrapOutputs(core)
1532
1576
  });
1533
- const templatePath = await bootstrapTemplatePath("github-oidc");
1577
+ const templatePath = await bootstrapTemplatePath("github-oidc", computeMode(input.config));
1534
1578
  return {
1535
1579
  phase: "github-oidc",
1536
1580
  expectedContracts,
@@ -1597,7 +1641,7 @@ function assertEqual(actual, expected, label) {
1597
1641
  );
1598
1642
  }
1599
1643
  async function verifyBootstrap(input) {
1600
- const expected = await contracts();
1644
+ const expected = await contracts(input.config);
1601
1645
  const core = deployedBootstrapStack(input.coreStack, expected.core);
1602
1646
  const oidc = deployedBootstrapStack(input.githubOidcStack, expected.githubOidc);
1603
1647
  for (const [kind, capture] of [
@@ -1745,6 +1789,7 @@ async function discovered(input) {
1745
1789
  const repo = await optional(input.runner, "gh", ["api", `repos/${input.repository}`]);
1746
1790
  const github = parseOrUnavailable(repo) ?? {};
1747
1791
  return {
1792
+ ...input.mode ? { mode: input.mode } : {},
1748
1793
  accountId,
1749
1794
  region: region2?.trim() || void 0,
1750
1795
  ownerId: typeof github.owner?.id === "number" || typeof github.owner?.id === "string" ? String(github.owner.id) : void 0,
@@ -1760,6 +1805,8 @@ async function scaffoldBootstrapManifest(input) {
1760
1805
  const environment = value(input.environment, "environment");
1761
1806
  const providerArn = value(input.providerArn, "github OIDC provider ARN");
1762
1807
  if (!accountFromArn(providerArn)) throw new Error("github OIDC provider ARN is invalid");
1808
+ if (input.mode && input.mode !== "lambda" && input.mode !== "ecs")
1809
+ throw new Error("mode must be lambda or ecs");
1763
1810
  const found = await discovered(input);
1764
1811
  const accountId = input.accountId ?? found.accountId;
1765
1812
  const region2 = input.region ?? found.region;
@@ -1983,27 +2030,45 @@ async function bootstrapGuide(input) {
1983
2030
 
1984
2031
  // packages/aws/src/index.ts
1985
2032
  var run = promisify(execFile);
2033
+ function requireImageDigest(image) {
2034
+ if (!/^[^\s@]+@sha256:[a-f0-9]{64}$/.test(image))
2035
+ throw new Error(
2036
+ "ECS imageReference must be an immutable registry digest (image@sha256:<64 hex>)"
2037
+ );
2038
+ return image;
2039
+ }
1986
2040
  function deploymentTemplate(template, values) {
1987
- const listed = template.match(/^ {2}PackageManagedParameters: \[([^\]]*)\]\s*$/m)?.[1];
1988
- if (listed === void 0)
2041
+ const document = parseDocument(template);
2042
+ if (document.errors.length)
2043
+ throw new Error(`Invalid CloudFormation template: ${document.errors[0]}`);
2044
+ const managedValue = document.toJS().Metadata?.PackageManagedParameters;
2045
+ if (!Array.isArray(managedValue))
1989
2046
  throw new Error("CloudFormation template has no PackageManagedParameters metadata");
1990
- const managed = listed.split(",").map((key) => key.trim()).filter(Boolean);
2047
+ const managed = managedValue.filter((key) => typeof key === "string");
2048
+ if (managed.length !== managedValue.length)
2049
+ throw new Error("CloudFormation template has invalid PackageManagedParameters metadata");
1991
2050
  const missing = managed.filter((key) => !Object.hasOwn(values, key));
1992
2051
  const unknown = Object.keys(values).filter((key) => !managed.includes(key));
1993
2052
  if (missing.length || unknown.length)
1994
2053
  throw new Error(
1995
2054
  `Package-managed CloudFormation parameters are out of sync (missing: ${missing.join(", ") || "none"}; unknown: ${unknown.join(", ") || "none"})`
1996
2055
  );
1997
- return managed.reduce((rendered, key) => {
1998
- const pattern = new RegExp(`^ ${key}: \\{[^\\n]+\\}\\s*$`, "m");
1999
- const declaration = rendered.match(pattern)?.[0];
2000
- if (!declaration || /\bDefault:/.test(declaration))
2001
- throw new Error(`Unable to set the CloudFormation default for ${key}`);
2002
- return rendered.replace(
2003
- pattern,
2004
- declaration.replace(/\s*}\s*$/, `, Default: ${JSON.stringify(values[key])} }`)
2005
- );
2006
- }, template);
2056
+ for (const key of managed) {
2057
+ const path = ["Parameters", key];
2058
+ const parameter2 = document.getIn(path);
2059
+ if (!parameter2 || typeof parameter2 !== "object")
2060
+ throw new Error(`CloudFormation template has no declaration for ${key}`);
2061
+ const current = document.getIn([...path, "Default"], true);
2062
+ if (current !== void 0) {
2063
+ if (String(current) !== values[key])
2064
+ throw new Error(`Package-managed CloudFormation parameter ${key} has a conflicting default`);
2065
+ } else {
2066
+ const scalar = new Scalar(values[key]);
2067
+ scalar.type = "QUOTE_DOUBLE";
2068
+ document.setIn([...path, "Default"], scalar);
2069
+ }
2070
+ }
2071
+ return document.toString({ lineWidth: 0 });
2007
2072
  }
2008
2073
  async function packageLambda(options) {
2009
2074
  const outputDir = resolve2(options.outputDir);
@@ -2022,6 +2087,7 @@ async function packageLambda(options) {
2022
2087
  });
2023
2088
  const runtimeMetadata = {
2024
2089
  ...release.metadata,
2090
+ computeMode: "lambda",
2025
2091
  artifactChecksums: {
2026
2092
  ...release.metadata.artifactChecksums,
2027
2093
  "index.mjs": sha256(await readFile3(join2(runtimeDir, "index.mjs")))
@@ -2058,13 +2124,49 @@ async function packageLambda(options) {
2058
2124
  `);
2059
2125
  await writeFile2(
2060
2126
  join2(outputDir, "template.yml"),
2061
- deploymentTemplate(await cloudFormationTemplate(), {
2127
+ deploymentTemplate(await cloudFormationTemplate("lambda"), {
2128
+ ComputeMode: "lambda",
2062
2129
  LambdaArtifactKey: packagedRelease.artifactKey,
2063
2130
  DashboardVersion: packagedRelease.dashboardVersion
2064
2131
  })
2065
2132
  );
2066
2133
  return packagedRelease;
2067
2134
  }
2135
+ async function packageEcs(options) {
2136
+ const outputDir = resolve2(options.outputDir);
2137
+ await mkdir2(outputDir, { recursive: true });
2138
+ const imageReference = requireImageDigest(options.imageReference ?? "");
2139
+ const release = await assembleRelease({
2140
+ boardConfigPath: options.boardConfigPath,
2141
+ outputDir,
2142
+ version: options.version,
2143
+ providers: ["aws-ecs-fargate"],
2144
+ assetDomain: options.assetDomain,
2145
+ secretReference: options.secretReference
2146
+ });
2147
+ const packagedRelease = {
2148
+ ...release.metadata,
2149
+ computeMode: "ecs",
2150
+ image: imageReference,
2151
+ artifactKey: `ecs/${sha256(JSON.stringify({ ...release.metadata, image: imageReference }))}`
2152
+ };
2153
+ await writeFile2(join2(outputDir, "release.json"), `${JSON.stringify(packagedRelease, null, 2)}
2154
+ `);
2155
+ await writeFile2(
2156
+ join2(outputDir, "template.yml"),
2157
+ deploymentTemplate(await cloudFormationTemplate("ecs"), {
2158
+ ComputeMode: "ecs",
2159
+ Image: imageReference,
2160
+ DashboardVersion: packagedRelease.dashboardVersion
2161
+ })
2162
+ );
2163
+ await writeFile2(
2164
+ join2(outputDir, "SHA256SUMS"),
2165
+ `${sha256(await readFile3(join2(outputDir, "release.json")))} release.json
2166
+ `
2167
+ );
2168
+ return packagedRelease;
2169
+ }
2068
2170
  async function publishClientAssets(options) {
2069
2171
  const assetsDir = resolve2(options.assetsDir);
2070
2172
  await readFile3(join2(assetsDir, "index.html"));
@@ -2089,8 +2191,13 @@ async function publishClientAssets(options) {
2089
2191
  ]);
2090
2192
  return assetPath;
2091
2193
  }
2092
- async function cloudFormationTemplate() {
2093
- return readFile3(fileURLToPath2(new URL("../template.yml", import.meta.url)), "utf8");
2194
+ async function cloudFormationTemplate(mode = "lambda") {
2195
+ return readFile3(
2196
+ fileURLToPath2(
2197
+ new URL(mode === "ecs" ? "../template-ecs.yml" : "../template.yml", import.meta.url)
2198
+ ),
2199
+ "utf8"
2200
+ );
2094
2201
  }
2095
2202
 
2096
2203
  // packages/aws/src/doctor.ts
@@ -2117,7 +2224,11 @@ function readParameterValues(value2) {
2117
2224
  return parameters;
2118
2225
  }
2119
2226
  async function templateContract(parameters) {
2120
- const template = await cloudFormationTemplate();
2227
+ const values = Object.fromEntries(
2228
+ parameters.map((item) => [item.ParameterKey, item.ParameterValue])
2229
+ );
2230
+ const mode = computeMode({ mode: values.ComputeMode });
2231
+ const template = await cloudFormationTemplate(mode);
2121
2232
  const parametersBlock = template.match(
2122
2233
  /^Parameters:\n[\s\S]*?(?=^[A-Za-z][A-Za-z0-9]*:\s*$)/m
2123
2234
  )?.[0];
@@ -2130,9 +2241,6 @@ async function templateContract(parameters) {
2130
2241
  if (!Array.isArray(managedValue) || !managedValue.every((key) => typeof key === "string"))
2131
2242
  throw new Error("template has invalid PackageManagedParameters metadata");
2132
2243
  const managed = new Set(managedValue);
2133
- const values = Object.fromEntries(
2134
- parameters.map((item) => [item.ParameterKey, item.ParameterValue])
2135
- );
2136
2244
  const stale = Object.keys(values).filter((key) => !Object.hasOwn(definitions, key));
2137
2245
  const missing = Object.entries(definitions).filter(
2138
2246
  ([key, definition]) => !Object.hasOwn(definition, "Default") && !managed.has(key) && !Object.hasOwn(values, key)
@@ -2192,9 +2300,11 @@ async function runDoctor(options, dependencies = actualDependencies) {
2192
2300
  JSON.parse(await readFile4(options.parametersPath, "utf8"))
2193
2301
  );
2194
2302
  parameterData = await templateContract(parameters);
2195
- return `${options.parametersPath} is compatible`;
2303
+ return `${options.parametersPath} is compatible (${computeMode({ mode: parameterData.values.ComputeMode })})`;
2196
2304
  });
2197
2305
  await check("Artifact bucket", async () => {
2306
+ if (computeMode({ mode: parameterData?.values.ComputeMode }) === "ecs")
2307
+ return "not required for ECS image deployments";
2198
2308
  const bucket = parameterData?.values.LambdaArtifactBucket;
2199
2309
  if (!bucket) throw new Error("skipped because parameters do not provide LambdaArtifactBucket");
2200
2310
  if (!awsAvailable) throw new Error("skipped because the AWS CLI is unavailable");
@@ -2318,6 +2428,7 @@ function bootstrapValues(config, coreOutputs = {}) {
2318
2428
  DashboardFunctionName: option("--function-name", config.core?.dashboardFunctionName),
2319
2429
  RuntimeSecretArn: option("--runtime-secret-arn", config.core?.runtimeSecretArn ?? ""),
2320
2430
  ArtifactKmsKeyArn: option("--artifact-kms-key-arn", config.core?.artifactKmsKeyArn ?? ""),
2431
+ ComputeMode: computeMode(config),
2321
2432
  GitHubOidcProviderArn: option("--github-oidc-provider-arn", config.githubOidc?.providerArn),
2322
2433
  GitHubRepository: option("--github-repository", config.githubOidc?.repository),
2323
2434
  GitHubOwnerId: option("--github-owner-id", config.githubOidc?.ownerId),
@@ -2340,8 +2451,8 @@ function bootstrapStackName(kind, config) {
2340
2451
  function shellCommand(command) {
2341
2452
  return command.map((argument) => `'${argument.replaceAll("'", `'\\"'\\"'`)}'`).join(" ");
2342
2453
  }
2343
- async function templateParameters() {
2344
- const template = await cloudFormationTemplate();
2454
+ async function templateParameters(mode = "lambda") {
2455
+ const template = await cloudFormationTemplate(mode);
2345
2456
  const block = template.match(/^Parameters:\n[\s\S]*?(?=^[A-Za-z][A-Za-z0-9]*:\s*$)/m)?.[0];
2346
2457
  if (!block) throw new Error("Unable to read Parameters from the CloudFormation template");
2347
2458
  const parsed = parse3(block);
@@ -2368,7 +2479,9 @@ function validateConsumerParameters(supplied, template) {
2368
2479
  const unknown = Object.keys(values).filter((key) => !templateKeys.includes(key));
2369
2480
  if (unknown.length)
2370
2481
  throw new Error(`Parameter file contains unknown parameters: ${unknown.join(", ")}`);
2371
- const overridden = [...template.packageManaged].filter((key) => Object.hasOwn(values, key));
2482
+ const overridden = [...template.packageManaged].filter(
2483
+ (key) => key !== "ComputeMode" && Object.hasOwn(values, key)
2484
+ );
2372
2485
  if (overridden.length)
2373
2486
  throw new Error(
2374
2487
  `Parameter file must not set package-managed parameters: ${overridden.join(", ")}`
@@ -2392,23 +2505,26 @@ function deploymentHandoff(input) {
2392
2505
  const prefix = input.outputDir.replace(/\/+$/, "");
2393
2506
  const path = (name) => `${prefix}/${name}`;
2394
2507
  const parameterFile = path("parameters.json");
2508
+ const mode = input.release.computeMode;
2509
+ const upload = mode === "lambda" ? [
2510
+ "aws",
2511
+ "s3",
2512
+ "cp",
2513
+ path("lambda.zip"),
2514
+ `s3://${input.artifactBucket}/${input.artifactKey}`,
2515
+ "--region",
2516
+ "$AWS_REGION"
2517
+ ] : [];
2395
2518
  return {
2519
+ mode,
2396
2520
  template: "template.yml",
2397
- lambdaZip: "lambda.zip",
2521
+ ...mode === "lambda" ? { lambdaZip: "lambda.zip" } : { image: input.release.image },
2398
2522
  releaseFile: "release.json",
2399
2523
  artifact: { bucket: input.artifactBucket, key: input.artifactKey },
2400
2524
  release: input.release,
2401
2525
  parameters: "parameters.json",
2402
2526
  commands: {
2403
- upload: [
2404
- "aws",
2405
- "s3",
2406
- "cp",
2407
- path("lambda.zip"),
2408
- `s3://${input.artifactBucket}/${input.artifactKey}`,
2409
- "--region",
2410
- "$AWS_REGION"
2411
- ],
2527
+ upload,
2412
2528
  deploy: [
2413
2529
  "aws",
2414
2530
  "cloudformation",
@@ -2483,12 +2599,17 @@ try {
2483
2599
  const existingValues = Object.fromEntries(
2484
2600
  existing.map(({ ParameterKey, ParameterValue }) => [ParameterKey, ParameterValue])
2485
2601
  );
2602
+ const mode = resolveComputeMode({
2603
+ persisted: consumerConfig?.mode,
2604
+ artifact: existingValues.ComputeMode,
2605
+ explicit: option("--mode")
2606
+ });
2486
2607
  const artifactBucket = option(
2487
2608
  "--artifact-bucket",
2488
2609
  existingValues.LambdaArtifactBucket ?? consumerConfig?.core?.artifactBucketName
2489
2610
  );
2490
- if (!artifactBucket) throw new Error("--artifact-bucket is required");
2491
- const { definitions, packageManaged } = await templateParameters();
2611
+ if (mode === "lambda" && !artifactBucket) throw new Error("--artifact-bucket is required");
2612
+ const { definitions, packageManaged } = await templateParameters(mode);
2492
2613
  const templateKeys = Object.keys(definitions);
2493
2614
  const hasDefault = (key) => Object.hasOwn(definitions[key] ?? {}, "Default");
2494
2615
  const hasValue = (key) => key === "LambdaArtifactBucket" || Object.hasOwn(existingValues, key);
@@ -2506,10 +2627,10 @@ try {
2506
2627
  const includeDefaults = args.includes("--include-defaults");
2507
2628
  const functionName = option("--function-name", consumerConfig?.core?.dashboardFunctionName);
2508
2629
  const parameters = templateKeys.filter(
2509
- (key) => !packageManaged.has(key) && (includeDefaults || !hasDefault(key) || Object.hasOwn(existingValues, key) || key === "Name" && Boolean(functionName))
2630
+ (key) => (!packageManaged.has(key) || key === "ComputeMode") && (includeDefaults || !hasDefault(key) || Object.hasOwn(existingValues, key) || key === "Name" && Boolean(functionName))
2510
2631
  ).map((key) => {
2511
2632
  const definition = definitions[key];
2512
- const value2 = key === "LambdaArtifactBucket" ? artifactBucket : key === "Name" && functionName ? functionName : existingValues[key] ?? definition?.Default;
2633
+ const value2 = key === "LambdaArtifactBucket" ? artifactBucket ?? "" : key === "ComputeMode" ? mode : key === "Name" && functionName ? functionName : existingValues[key] ?? definition?.Default;
2513
2634
  if (value2 === void 0) throw new Error(`No value for CloudFormation parameter ${key}`);
2514
2635
  return parameter(key, String(value2));
2515
2636
  });
@@ -2544,6 +2665,7 @@ try {
2544
2665
  ownerId: option("--github-owner-id"),
2545
2666
  repositoryId: option("--github-repository-id"),
2546
2667
  consumerGatewayStackName: option("--consumer-gateway-stack"),
2668
+ mode: option("--mode", "lambda") ?? "lambda",
2547
2669
  runner
2548
2670
  });
2549
2671
  await writeFile3(output, `${JSON.stringify(manifest, null, 2)}
@@ -2638,11 +2760,13 @@ try {
2638
2760
  );
2639
2761
  } else if (action === "template") {
2640
2762
  const kind = bootstrapKind();
2763
+ const mode = requireComputeMode(config, option("--mode"));
2641
2764
  console.log(
2642
2765
  JSON.stringify({
2643
2766
  kind,
2644
- template: await bootstrapTemplatePath(kind),
2645
- contractVersion: bootstrapContractVersion(await bootstrapTemplate(kind))
2767
+ mode,
2768
+ template: await bootstrapTemplatePath(kind, mode),
2769
+ contractVersion: bootstrapContractVersion(await bootstrapTemplate(kind, mode))
2646
2770
  })
2647
2771
  );
2648
2772
  } else if (action === "plan") {
@@ -2683,6 +2807,7 @@ ${plan.notes.join("\n")}`);
2683
2807
  if (!result.ok) process.exitCode = 1;
2684
2808
  } else if (action === "parameters") {
2685
2809
  const kind = bootstrapKind();
2810
+ const mode = requireComputeMode(config, option("--mode"));
2686
2811
  const output = option("--output", `aws-dashboard-bootstrap-${kind}.json`) ?? `aws-dashboard-bootstrap-${kind}.json`;
2687
2812
  let coreOutputs = {};
2688
2813
  const coreStackPath = option("--core-stack-json");
@@ -2690,17 +2815,20 @@ ${plan.notes.join("\n")}`);
2690
2815
  coreOutputs = coreBootstrapOutputs(
2691
2816
  deployedBootstrapStack(
2692
2817
  JSON.parse(await readFile5(coreStackPath, "utf8")),
2693
- bootstrapContractVersion(await bootstrapTemplate("core"))
2818
+ bootstrapContractVersion(await bootstrapTemplate("core", mode))
2694
2819
  )
2695
2820
  );
2696
2821
  }
2697
- const supplied = requiredBootstrapParameters(kind, bootstrapValues(config, coreOutputs));
2822
+ const supplied = requiredBootstrapParameters(
2823
+ kind,
2824
+ bootstrapValues({ ...config, mode }, coreOutputs)
2825
+ );
2698
2826
  let parameters = supplied;
2699
2827
  const deployedStackPath = option("--deployed-stack-json");
2700
2828
  if (deployedStackPath) {
2701
2829
  const stack = deployedBootstrapStack(
2702
2830
  JSON.parse(await readFile5(deployedStackPath, "utf8")),
2703
- bootstrapContractVersion(await bootstrapTemplate(kind))
2831
+ bootstrapContractVersion(await bootstrapTemplate(kind, mode))
2704
2832
  );
2705
2833
  if (kind === "github-oidc" && !(stack.Parameters ?? []).some(
2706
2834
  ({ ParameterKey }) => ParameterKey === "ConsumerGatewayStackName"
@@ -2773,23 +2901,39 @@ ${plan.notes.join("\n")}`);
2773
2901
  throw new Error("Unable to determine the package version; pass --version explicitly");
2774
2902
  const outputDir = option("--output", "aws-dashboard-release") ?? "aws-dashboard-release";
2775
2903
  const parametersPath = option("--parameters", "aws-dashboard-parameters.json") ?? "aws-dashboard-parameters.json";
2776
- const template = await templateParameters();
2777
- const consumerParameters = validateConsumerParameters(
2778
- await existingParameters(parametersPath),
2779
- template
2780
- );
2904
+ const rawParameters = await existingParameters(parametersPath);
2905
+ const rawMode = rawParameters.find(
2906
+ ({ ParameterKey }) => ParameterKey === "ComputeMode"
2907
+ )?.ParameterValue;
2908
+ const mode = resolveComputeMode({ artifact: rawMode, explicit: option("--mode") });
2909
+ const template = await templateParameters(mode);
2910
+ const consumerParameters = validateConsumerParameters(rawParameters, template);
2781
2911
  const secretReference = consumerParameters.SecretReference ?? String(template.definitions.SecretReference?.Default ?? "");
2782
- const metadata = await packageLambda({
2912
+ const metadata = mode === "lambda" ? await packageLambda({
2783
2913
  boardConfigPath: boardConfig,
2784
2914
  outputDir,
2785
2915
  version,
2786
2916
  assetDomain: option("--asset-domain"),
2787
2917
  secretReference
2918
+ }) : await packageEcs({
2919
+ boardConfigPath: boardConfig,
2920
+ outputDir,
2921
+ version,
2922
+ assetDomain: option("--asset-domain"),
2923
+ secretReference,
2924
+ imageReference: option("--image")
2788
2925
  });
2789
2926
  const resolvedParameters = resolvedReleaseParameters(consumerParameters, template, {
2927
+ ComputeMode: mode,
2790
2928
  LambdaArtifactKey: metadata.artifactKey,
2929
+ Image: metadata.image ?? "",
2791
2930
  DashboardVersion: metadata.dashboardVersion
2792
2931
  });
2932
+ if (mode === "lambda" && !rawParameters.some(({ ParameterKey }) => ParameterKey === "ComputeMode"))
2933
+ resolvedParameters.splice(
2934
+ resolvedParameters.findIndex(({ ParameterKey }) => ParameterKey === "ComputeMode"),
2935
+ 1
2936
+ );
2793
2937
  await writeFile3(
2794
2938
  `${outputDir}/parameters.json`,
2795
2939
  `${JSON.stringify(resolvedParameters, null, 2)}
@@ -2803,7 +2947,9 @@ ${plan.notes.join("\n")}`);
2803
2947
  });
2804
2948
  await writeFile3(`${outputDir}/deployment.json`, `${JSON.stringify(handoff, null, 2)}
2805
2949
  `);
2806
- console.log(`Packaged ${outputDir}/lambda.zip and ${outputDir}/template.yml`);
2950
+ console.log(
2951
+ `Packaged ${mode === "lambda" ? `${outputDir}/lambda.zip` : metadata.image} and ${outputDir}/template.yml`
2952
+ );
2807
2953
  console.log(`Complete parameters: ${outputDir}/parameters.json`);
2808
2954
  console.log(`Deployment handoff: ${outputDir}/deployment.json`);
2809
2955
  console.log(`Upload: ${copyableCommand(handoff.commands.upload)}`);