@skuba-lib/api 2.1.2 → 2.2.0-add-cdk-NodejsFunction-20260613135656
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/cdk/nodejsFunction/package.json +6 -0
- package/cdk/package.json +6 -0
- package/lib/buildkite/index.cjs +1 -1
- package/lib/buildkite/index.d.cts +2 -2
- package/lib/buildkite/index.d.mts +2 -2
- package/lib/buildkite/index.mjs +1 -1
- package/lib/{buildkite-D8p6SH33.mjs → buildkite-CJ4a_6Cv.mjs} +3 -3
- package/lib/{buildkite-EYIbmVQ4.cjs → buildkite-Dyd__LF3.cjs} +4 -3
- package/lib/cdk/bridges/rolldown.mjs +49 -0
- package/lib/cdk/index.cjs +6 -0
- package/lib/cdk/index.d.cts +4 -0
- package/lib/cdk/index.d.mts +4 -0
- package/lib/cdk/index.mjs +5 -0
- package/lib/cdk/nodejsFunction/index.cjs +371 -0
- package/lib/cdk/nodejsFunction/index.d.cts +32 -0
- package/lib/cdk/nodejsFunction/index.d.mts +32 -0
- package/lib/cdk/nodejsFunction/index.mjs +365 -0
- package/lib/chunk-DakpK96I.cjs +43 -0
- package/lib/{error-DJ-upcVW.cjs → error-DwkbVu3p.cjs} +2 -43
- package/lib/{exec-DHKlT34S.cjs → exec-BsoJ4pFc.cjs} +5 -4
- package/lib/{exec-DuOGnPmx.mjs → exec-CbhqWxN4.mjs} +1 -1
- package/lib/git/index.cjs +1 -1
- package/lib/git/index.d.cts +2 -2
- package/lib/git/index.d.mts +2 -2
- package/lib/git/index.mjs +1 -1
- package/lib/{git-DBepQ7NY.mjs → git-BSWQdkgU.mjs} +2 -2
- package/lib/{git-CemmnhZX.cjs → git-CEnn1Qrs.cjs} +8 -7
- package/lib/github/index.cjs +2 -2
- package/lib/github/index.d.cts +2 -2
- package/lib/github/index.d.mts +2 -2
- package/lib/github/index.mjs +2 -2
- package/lib/{github-YNn-2h6W.cjs → github-CzH1PaKH.cjs} +6 -5
- package/lib/{github-CEOIl2xv.mjs → github-IYwiDs5D.mjs} +3 -3
- package/lib/{index-9k0uNPiA.d.mts → index-D4JUiy2k.d.cts} +1 -1
- package/lib/{index-CvCXMWrj.d.cts → index-DbjgHTTm.d.mts} +1 -1
- package/lib/index.cjs +4 -4
- package/lib/index.d.cts +4 -4
- package/lib/index.d.mts +4 -4
- package/lib/index.mjs +4 -4
- package/lib/net/index.cjs +1 -1
- package/lib/net/index.d.cts +1 -1
- package/lib/net/index.d.mts +1 -1
- package/lib/net/index.mjs +1 -1
- package/lib/{net-CBYpNAZ9.cjs → net-BGgnkCnN.cjs} +4 -4
- package/lib/{net-xHFhmM3Z.mjs → net-ZfhL_QSV.mjs} +2 -2
- package/package.json +33 -2
- /package/lib/{chunk-CfYAbeIz.mjs → chunk-D7D4PA-g.mjs} +0 -0
- /package/lib/{error-CTBQVZXj.mjs → error-BYLsj5YU.mjs} +0 -0
- /package/lib/{index-BGJOKUEn.d.cts → index-C9WMO_Y9.d.cts} +0 -0
- /package/lib/{index-BLuWPbMv.d.mts → index-C9WMO_Y9.d.mts} +0 -0
- /package/lib/{index-B-uDtu2-.d.mts → index-CNxgBJm_.d.cts} +0 -0
- /package/lib/{index-BHREYHx-.d.cts → index-CNxgBJm_.d.mts} +0 -0
- /package/lib/{index-BF1A__qo.d.cts → index-Kyp8ZCwF.d.cts} +0 -0
- /package/lib/{index-BFKo2N5d.d.mts → index-Kyp8ZCwF.d.mts} +0 -0
package/cdk/package.json
ADDED
package/lib/buildkite/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_buildkite = require("../buildkite-
|
|
2
|
+
const require_buildkite = require("../buildkite-Dyd__LF3.cjs");
|
|
3
3
|
exports.annotate = require_buildkite.annotate;
|
|
4
4
|
exports.md = require_buildkite.md;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as annotate, n as md, r as AnnotationStyle } from "../index-
|
|
2
|
-
export { AnnotationStyle, annotate, md };
|
|
1
|
+
import { i as annotate, n as md, r as AnnotationStyle } from "../index-CNxgBJm_.cjs";
|
|
2
|
+
export { type AnnotationStyle, annotate, md };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as annotate, n as md, r as AnnotationStyle } from "../index-
|
|
2
|
-
export { AnnotationStyle, annotate, md };
|
|
1
|
+
import { i as annotate, n as md, r as AnnotationStyle } from "../index-CNxgBJm_.mjs";
|
|
2
|
+
export { type AnnotationStyle, annotate, md };
|
package/lib/buildkite/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as md, r as annotate } from "../buildkite-
|
|
1
|
+
import { n as md, r as annotate } from "../buildkite-CJ4a_6Cv.mjs";
|
|
2
2
|
export { annotate, md };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as __exportAll } from "./chunk-
|
|
2
|
-
import { n as log } from "./error-
|
|
3
|
-
import { n as exec, r as hasCommand } from "./exec-
|
|
1
|
+
import { t as __exportAll } from "./chunk-D7D4PA-g.mjs";
|
|
2
|
+
import { n as log } from "./error-BYLsj5YU.mjs";
|
|
3
|
+
import { n as exec, r as hasCommand } from "./exec-CbhqWxN4.mjs";
|
|
4
4
|
//#region src/buildkite/annotate.ts
|
|
5
5
|
const isAnnotationEnabled = async () => Boolean(process.env.BUILDKITE && process.env.BUILDKITE_AGENT_ACCESS_TOKEN && process.env.BUILDKITE_JOB_ID && await hasCommand("buildkite-agent"));
|
|
6
6
|
const MAX_SIZE = 1024 * 1024;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
1
|
+
const require_chunk = require("./chunk-DakpK96I.cjs");
|
|
2
|
+
const require_error = require("./error-DwkbVu3p.cjs");
|
|
3
|
+
const require_exec = require("./exec-BsoJ4pFc.cjs");
|
|
3
4
|
//#region src/buildkite/annotate.ts
|
|
4
5
|
const isAnnotationEnabled = async () => Boolean(process.env.BUILDKITE && process.env.BUILDKITE_AGENT_ACCESS_TOKEN && process.env.BUILDKITE_JOB_ID && await require_exec.hasCommand("buildkite-agent"));
|
|
5
6
|
const MAX_SIZE = 1024 * 1024;
|
|
@@ -36,7 +37,7 @@ const annotate = async (markdown, opts = {}) => {
|
|
|
36
37
|
const md = { terminal: (code) => `\`\`\`term\n${code.replace(/```/g, "\\`\\`\\`")}\n\`\`\`` };
|
|
37
38
|
//#endregion
|
|
38
39
|
//#region src/buildkite/index.ts
|
|
39
|
-
var buildkite_exports = /* @__PURE__ */
|
|
40
|
+
var buildkite_exports = /* @__PURE__ */ require_chunk.__exportAll({
|
|
40
41
|
annotate: () => annotate,
|
|
41
42
|
md: () => md
|
|
42
43
|
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { rolldown } from "rolldown";
|
|
5
|
+
//#region src/cdk/nodejsFunction/util.ts
|
|
6
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
|
+
const BUNDLE_META_FILENAME = ".lambda-bundle-meta";
|
|
8
|
+
const ESM_FORMATS = new Set([
|
|
9
|
+
"es",
|
|
10
|
+
"esm",
|
|
11
|
+
"module"
|
|
12
|
+
]);
|
|
13
|
+
const isEsmFormat = (format) => ESM_FORMATS.has(format ?? "");
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/cdk/nodejsFunction/bridges/rolldown.mts
|
|
16
|
+
const [configPath, entry, outputDir] = process.argv.slice(2, 5);
|
|
17
|
+
if (!configPath || !entry || !outputDir) throw new Error("Expected arguments: <configPath> <entry> <outputDir>");
|
|
18
|
+
const asRecord = (value) => isRecord(value) ? value : void 0;
|
|
19
|
+
const asString = (value) => typeof value === "string" ? value : void 0;
|
|
20
|
+
const userConfig = asRecord(asRecord(await import(pathToFileURL(configPath).href))?.default);
|
|
21
|
+
if (!userConfig) throw new Error(`Config file must export a default config object: ${configPath}`);
|
|
22
|
+
const rawOutput = userConfig.output;
|
|
23
|
+
if (Array.isArray(rawOutput) && rawOutput.length !== 1) throw new Error("`output` must be a single output object: NodejsFunction emits a single index.js/index.mjs handler.");
|
|
24
|
+
const baseRaw = asRecord(Array.isArray(rawOutput) ? rawOutput[0] : rawOutput) ?? {};
|
|
25
|
+
if (baseRaw.preserveModules) throw new Error("`output.preserveModules` is not supported: NodejsFunction emits a single index.js/index.mjs handler.");
|
|
26
|
+
if (baseRaw.entryFileNames !== void 0) throw new Error("`output.entryFileNames` is not supported: NodejsFunction names the handler index.js/index.mjs itself.");
|
|
27
|
+
if (userConfig.input !== void 0) throw new Error("`input` is not supported: NodejsFunction sets the bundle entry itself.");
|
|
28
|
+
const format = asString(baseRaw.format) ?? "es";
|
|
29
|
+
const { file: _file, ...baseRest } = baseRaw;
|
|
30
|
+
const { output: _output, ...userConfigRest } = userConfig;
|
|
31
|
+
const inputOptions = {
|
|
32
|
+
...userConfigRest,
|
|
33
|
+
input: entry
|
|
34
|
+
};
|
|
35
|
+
const outputOptions = {
|
|
36
|
+
...baseRest,
|
|
37
|
+
format,
|
|
38
|
+
dir: outputDir,
|
|
39
|
+
entryFileNames: isEsmFormat(format) ? "index.mjs" : "index.js"
|
|
40
|
+
};
|
|
41
|
+
const bundle = await rolldown(inputOptions);
|
|
42
|
+
try {
|
|
43
|
+
await bundle.write(outputOptions);
|
|
44
|
+
} finally {
|
|
45
|
+
await bundle.close();
|
|
46
|
+
}
|
|
47
|
+
writeFileSync(join(outputDir, BUNDLE_META_FILENAME), JSON.stringify({ format }));
|
|
48
|
+
//#endregion
|
|
49
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/cdk/normaliseTemplate/index.ts
|
|
3
|
+
const normaliseTemplateJsonString = (json) => json.replace(/"S3Key":"([0-9a-f]+)\.zip"/g, (_, hash) => `"S3Key":"${"x".repeat(hash.length)}.zip"`).replace(/workerCurrentVersion([0-9a-zA-Z]+)"/g, (_, hash) => `workerCurrentVersion${"x".repeat(hash.length)}"`).replaceAll(/"Value":"\d+\.\d+\.\d+-([^"]+)"/g, (_, hash) => `"Value": "x.x.x-${"x".repeat(hash.length)}"`).replaceAll(/"Value":"v\d+\.\d+\.\d+"/g, (_) => "\"Value\": \"vx.x.x\"").replace(/"DD_TAGS":"git.commit.sha:([0-9a-f]+),git.repository_url:([^\"]+)",/g, "").replaceAll(/(layer:Datadog-[^-]+-.+?:)\d+/g, (_, layer) => `${layer}x`);
|
|
4
|
+
const normaliseTemplate = (template) => JSON.parse(normaliseTemplateJsonString(JSON.stringify(template)));
|
|
5
|
+
//#endregion
|
|
6
|
+
exports.normaliseTemplate = normaliseTemplate;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
//#region src/cdk/normaliseTemplate/index.ts
|
|
2
|
+
const normaliseTemplateJsonString = (json) => json.replace(/"S3Key":"([0-9a-f]+)\.zip"/g, (_, hash) => `"S3Key":"${"x".repeat(hash.length)}.zip"`).replace(/workerCurrentVersion([0-9a-zA-Z]+)"/g, (_, hash) => `workerCurrentVersion${"x".repeat(hash.length)}"`).replaceAll(/"Value":"\d+\.\d+\.\d+-([^"]+)"/g, (_, hash) => `"Value": "x.x.x-${"x".repeat(hash.length)}"`).replaceAll(/"Value":"v\d+\.\d+\.\d+"/g, (_) => "\"Value\": \"vx.x.x\"").replace(/"DD_TAGS":"git.commit.sha:([0-9a-f]+),git.repository_url:([^\"]+)",/g, "").replaceAll(/(layer:Datadog-[^-]+-.+?:)\d+/g, (_, layer) => `${layer}x`);
|
|
3
|
+
const normaliseTemplate = (template) => JSON.parse(normaliseTemplateJsonString(JSON.stringify(template)));
|
|
4
|
+
//#endregion
|
|
5
|
+
export { normaliseTemplate };
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_chunk = require("../../chunk-DakpK96I.cjs");
|
|
3
|
+
let node_fs = require("node:fs");
|
|
4
|
+
node_fs = require_chunk.__toESM(node_fs, 1);
|
|
5
|
+
let node_path = require("node:path");
|
|
6
|
+
node_path = require_chunk.__toESM(node_path, 1);
|
|
7
|
+
let aws_cdk_lib_aws_lambda = require("aws-cdk-lib/aws-lambda");
|
|
8
|
+
aws_cdk_lib_aws_lambda = require_chunk.__toESM(aws_cdk_lib_aws_lambda, 1);
|
|
9
|
+
let node_child_process = require("node:child_process");
|
|
10
|
+
let aws_cdk_lib = require("aws-cdk-lib");
|
|
11
|
+
aws_cdk_lib = require_chunk.__toESM(aws_cdk_lib, 1);
|
|
12
|
+
let node_url = require("node:url");
|
|
13
|
+
let yaml = require("yaml");
|
|
14
|
+
let node_module = require("node:module");
|
|
15
|
+
//#region src/cdk/nodejsFunction/errors.ts
|
|
16
|
+
var ValidationError = class extends Error {
|
|
17
|
+
constructor(message) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = "ValidationError";
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/cdk/nodejsFunction/bridge-path.ts
|
|
24
|
+
const findPackageRoot = (moduleUrl) => {
|
|
25
|
+
let dir = node_path.dirname((0, node_url.fileURLToPath)(moduleUrl));
|
|
26
|
+
for (;;) {
|
|
27
|
+
if (node_fs.existsSync(node_path.join(dir, "package.json"))) return dir;
|
|
28
|
+
const parent = node_path.dirname(dir);
|
|
29
|
+
if (parent === dir) throw new ValidationError("Unable to locate the package root to resolve the bundler bridge script.");
|
|
30
|
+
dir = parent;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const BRIDGE_OUTPUT_SEGMENTS = [
|
|
34
|
+
"lib",
|
|
35
|
+
"cdk",
|
|
36
|
+
"bridges",
|
|
37
|
+
"rolldown.mjs"
|
|
38
|
+
];
|
|
39
|
+
let bridgePath;
|
|
40
|
+
const resolveRolldownBridge = () => bridgePath ??= node_path.join(findPackageRoot(require("url").pathToFileURL(__filename).href), ...BRIDGE_OUTPUT_SEGMENTS);
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/cdk/nodejsFunction/util.ts
|
|
43
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
44
|
+
const BUNDLE_META_FILENAME = ".lambda-bundle-meta";
|
|
45
|
+
const ESM_FORMATS = new Set([
|
|
46
|
+
"es",
|
|
47
|
+
"esm",
|
|
48
|
+
"module"
|
|
49
|
+
]);
|
|
50
|
+
const isEsmFormat = (format) => ESM_FORMATS.has(format ?? "");
|
|
51
|
+
const parseJsonFile = (filePath) => {
|
|
52
|
+
try {
|
|
53
|
+
return JSON.parse(node_fs.readFileSync(filePath, "utf-8"));
|
|
54
|
+
} catch (err) {
|
|
55
|
+
throw new ValidationError(`Failed to parse ${filePath} as JSON: ${err.message}`);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const findUp = (name, directory = process.cwd()) => {
|
|
59
|
+
const absoluteDirectory = node_path.resolve(directory);
|
|
60
|
+
const file = node_path.join(absoluteDirectory, name);
|
|
61
|
+
if (node_fs.existsSync(file)) return file;
|
|
62
|
+
const { root } = node_path.parse(absoluteDirectory);
|
|
63
|
+
if (absoluteDirectory === root) return;
|
|
64
|
+
return findUp(name, node_path.dirname(absoluteDirectory));
|
|
65
|
+
};
|
|
66
|
+
const extractDependencies = (pkgPath, modules) => {
|
|
67
|
+
const result = {};
|
|
68
|
+
const base = (0, node_url.pathToFileURL)(pkgPath);
|
|
69
|
+
for (const mod of modules) {
|
|
70
|
+
let modPkgPath;
|
|
71
|
+
try {
|
|
72
|
+
modPkgPath = (0, node_module.findPackageJSON)(mod, base);
|
|
73
|
+
} catch {
|
|
74
|
+
modPkgPath = void 0;
|
|
75
|
+
}
|
|
76
|
+
const parsed = modPkgPath ? parseJsonFile(modPkgPath) : void 0;
|
|
77
|
+
const version = isRecord(parsed) && typeof parsed.version === "string" ? parsed.version.trim() : void 0;
|
|
78
|
+
if (!version) throw new ValidationError(`Cannot extract version for module '${mod}'. Check that it's referenced in your package.json or installed.`);
|
|
79
|
+
result[mod] = version;
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
};
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/cdk/nodejsFunction/package-manager.ts
|
|
85
|
+
const PNPM_LOCK = "pnpm-lock.yaml";
|
|
86
|
+
const PNPM_WORKSPACE_FILES = [
|
|
87
|
+
"pnpm-workspace.yaml",
|
|
88
|
+
".npmrc",
|
|
89
|
+
".pnpmfile.cjs",
|
|
90
|
+
".pnpmfile.mjs"
|
|
91
|
+
];
|
|
92
|
+
const PNPM_INSTALL_COMMAND = [
|
|
93
|
+
"pnpm",
|
|
94
|
+
"install",
|
|
95
|
+
"--config.node-linker=hoisted",
|
|
96
|
+
"--config.package-import-method=clone-or-copy",
|
|
97
|
+
"--no-frozen-lockfile"
|
|
98
|
+
];
|
|
99
|
+
const isInside = (parent, child) => {
|
|
100
|
+
const rel = node_path.relative(node_path.resolve(parent), node_path.resolve(child));
|
|
101
|
+
return rel === "" || !rel.startsWith("..") && !node_path.isAbsolute(rel);
|
|
102
|
+
};
|
|
103
|
+
const assertPnpm = (name, source, pkgPath) => {
|
|
104
|
+
if (name !== "pnpm") throw new ValidationError(`Unsupported package manager '${name}' declared in ${source} of ${pkgPath}. NodejsFunction only supports pnpm.`);
|
|
105
|
+
};
|
|
106
|
+
const readPackageManagerMeta = (projectRoot) => {
|
|
107
|
+
const pkgPath = node_path.join(projectRoot, "package.json");
|
|
108
|
+
const nearestPackageJson = node_fs.existsSync(pkgPath) ? pkgPath : void 0;
|
|
109
|
+
if (!nearestPackageJson) return {
|
|
110
|
+
nearestPackageJson,
|
|
111
|
+
packageManagerField: void 0
|
|
112
|
+
};
|
|
113
|
+
const parsed = parseJsonFile(pkgPath);
|
|
114
|
+
if (!isRecord(parsed)) return {
|
|
115
|
+
nearestPackageJson,
|
|
116
|
+
packageManagerField: void 0
|
|
117
|
+
};
|
|
118
|
+
if (isRecord(parsed.devEngines)) {
|
|
119
|
+
const devEnginesPm = parsed.devEngines.packageManager;
|
|
120
|
+
if (isRecord(devEnginesPm) && typeof devEnginesPm.name === "string") assertPnpm(devEnginesPm.name, "the `devEngines.packageManager` field", pkgPath);
|
|
121
|
+
}
|
|
122
|
+
if (typeof parsed.packageManager === "string") {
|
|
123
|
+
const m = /^([^@\s]+)@/.exec(parsed.packageManager);
|
|
124
|
+
if (m) {
|
|
125
|
+
assertPnpm(m[1], "the `packageManager` field", pkgPath);
|
|
126
|
+
return {
|
|
127
|
+
nearestPackageJson,
|
|
128
|
+
packageManagerField: parsed.packageManager
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
nearestPackageJson,
|
|
134
|
+
packageManagerField: void 0
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
const injectIgnoreScripts = (outputDir) => {
|
|
138
|
+
const target = node_path.join(outputDir, ".npmrc");
|
|
139
|
+
const existing = node_fs.existsSync(target) ? node_fs.readFileSync(target, "utf8") : "";
|
|
140
|
+
if (/^ignore-scripts\s*=/m.test(existing)) return;
|
|
141
|
+
const sep = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
142
|
+
node_fs.writeFileSync(target, `${existing}${sep}ignore-scripts=true\n`);
|
|
143
|
+
};
|
|
144
|
+
const copyPatchFile = (relativePath, projectRoot, outputDir, copiedFiles) => {
|
|
145
|
+
const src = node_path.join(projectRoot, relativePath);
|
|
146
|
+
const dest = node_path.join(outputDir, relativePath);
|
|
147
|
+
if (!isInside(projectRoot, src) || !isInside(outputDir, dest)) throw new ValidationError(`Refusing to copy pnpm patch '${relativePath}': it resolves outside the project root or output directory.`);
|
|
148
|
+
if (!node_fs.existsSync(src)) return;
|
|
149
|
+
const realSrc = node_fs.realpathSync(src);
|
|
150
|
+
if (!isInside(node_fs.realpathSync(projectRoot), realSrc)) throw new ValidationError(`Refusing to copy pnpm patch '${relativePath}': it resolves (via symlink) outside the project root.`);
|
|
151
|
+
node_fs.mkdirSync(node_path.dirname(dest), { recursive: true });
|
|
152
|
+
node_fs.copyFileSync(realSrc, dest);
|
|
153
|
+
copiedFiles.push(dest);
|
|
154
|
+
};
|
|
155
|
+
const filterPnpmWorkspaceYaml = (content, nodeModules, projectRoot, outputDir) => {
|
|
156
|
+
let doc;
|
|
157
|
+
try {
|
|
158
|
+
doc = (0, yaml.parse)(content);
|
|
159
|
+
} catch (err) {
|
|
160
|
+
throw new ValidationError(`Failed to parse pnpm-workspace.yaml: ${err.message}`);
|
|
161
|
+
}
|
|
162
|
+
if (!isRecord(doc) || !("patchedDependencies" in doc)) return {
|
|
163
|
+
content,
|
|
164
|
+
copiedFiles: []
|
|
165
|
+
};
|
|
166
|
+
const patched = doc.patchedDependencies;
|
|
167
|
+
const copiedFiles = [];
|
|
168
|
+
if (isRecord(patched)) for (const [key, value] of Object.entries(patched)) {
|
|
169
|
+
const pkgName = key.replace(/(?<=.)@[^@]+$/, "");
|
|
170
|
+
if (!nodeModules.includes(pkgName)) delete patched[key];
|
|
171
|
+
else if (typeof value === "string" && value) copyPatchFile(value, projectRoot, outputDir, copiedFiles);
|
|
172
|
+
}
|
|
173
|
+
if (!isRecord(patched) || Object.keys(patched).length === 0) delete doc.patchedDependencies;
|
|
174
|
+
return {
|
|
175
|
+
content: (0, yaml.stringify)(doc),
|
|
176
|
+
copiedFiles
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
const copyWorkspaceFiles = (projectRoot, outputDir, nodeModules = [], ignoreScripts = false) => {
|
|
180
|
+
const stagedFiles = [];
|
|
181
|
+
for (const file of PNPM_WORKSPACE_FILES) {
|
|
182
|
+
const src = node_path.join(projectRoot, file);
|
|
183
|
+
if (!node_fs.existsSync(src)) continue;
|
|
184
|
+
const dest = node_path.join(outputDir, file);
|
|
185
|
+
if (file === "pnpm-workspace.yaml") {
|
|
186
|
+
const filtered = filterPnpmWorkspaceYaml(node_fs.readFileSync(src, "utf8"), nodeModules, projectRoot, outputDir);
|
|
187
|
+
node_fs.writeFileSync(dest, filtered.content);
|
|
188
|
+
stagedFiles.push(...filtered.copiedFiles);
|
|
189
|
+
} else node_fs.copyFileSync(src, dest);
|
|
190
|
+
}
|
|
191
|
+
if (ignoreScripts) injectIgnoreScripts(outputDir);
|
|
192
|
+
return stagedFiles;
|
|
193
|
+
};
|
|
194
|
+
//#endregion
|
|
195
|
+
//#region src/cdk/nodejsFunction/bundling.ts
|
|
196
|
+
const SPAWN_MAX_BUFFER = 256 * 1024 * 1024;
|
|
197
|
+
const describeExit = (result) => result.signal != null ? `killed by signal ${result.signal}` : `exited with status ${result.status}`;
|
|
198
|
+
const stderrTail = (stderr) => {
|
|
199
|
+
if (!stderr) return "";
|
|
200
|
+
const text = typeof stderr === "string" ? stderr : stderr.toString("utf-8");
|
|
201
|
+
if (!text) return "";
|
|
202
|
+
const tail = text.trimEnd().split("\n").slice(-20).join("\n");
|
|
203
|
+
return tail ? `\n\n${tail}` : "";
|
|
204
|
+
};
|
|
205
|
+
const checkSpawnResult = (result, prefix) => {
|
|
206
|
+
if (result.error) {
|
|
207
|
+
if (result.error.code === "ETIMEDOUT") throw new ValidationError(`${prefix} timed out.${stderrTail(result.stderr)}`);
|
|
208
|
+
throw result.error;
|
|
209
|
+
}
|
|
210
|
+
if (result.status !== 0) throw new ValidationError(`${prefix} ${describeExit(result)}.${stderrTail(result.stderr)}`);
|
|
211
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
212
|
+
};
|
|
213
|
+
const writeOutputPackageJson = (outputDir, fields) => {
|
|
214
|
+
const pkg = {};
|
|
215
|
+
if (fields.dependencies) pkg.dependencies = fields.dependencies;
|
|
216
|
+
if (fields.isEsm) pkg.type = "module";
|
|
217
|
+
if (fields.packageManager) pkg.packageManager = fields.packageManager;
|
|
218
|
+
if (Object.keys(pkg).length === 0) return;
|
|
219
|
+
node_fs.writeFileSync(node_path.join(outputDir, "package.json"), JSON.stringify(pkg, null, 2));
|
|
220
|
+
};
|
|
221
|
+
const removeEmptyParentDirs = (startDir, outputDir) => {
|
|
222
|
+
let dir = startDir;
|
|
223
|
+
while (dir.startsWith(outputDir + node_path.sep)) {
|
|
224
|
+
try {
|
|
225
|
+
node_fs.rmdirSync(dir);
|
|
226
|
+
} catch {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
dir = node_path.dirname(dir);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
var Bundling = class Bundling {
|
|
233
|
+
props;
|
|
234
|
+
static bundle(props) {
|
|
235
|
+
return aws_cdk_lib_aws_lambda.Code.fromAsset(props.projectRoot, {
|
|
236
|
+
assetHash: props.assetHash,
|
|
237
|
+
assetHashType: props.assetHash ? aws_cdk_lib.AssetHashType.CUSTOM : aws_cdk_lib.AssetHashType.OUTPUT,
|
|
238
|
+
bundling: new Bundling(props)
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
image;
|
|
242
|
+
local;
|
|
243
|
+
constructor(props) {
|
|
244
|
+
this.props = props;
|
|
245
|
+
this.image = aws_cdk_lib.DockerImage.fromRegistry("dummy");
|
|
246
|
+
this.local = this.buildLocalBundling();
|
|
247
|
+
}
|
|
248
|
+
buildLocalBundling() {
|
|
249
|
+
return { tryBundle: (outputDir) => {
|
|
250
|
+
this.runHooks(this.props.commandHooks?.beforeBundling(this.props.projectRoot, outputDir) ?? [], this.props.projectRoot);
|
|
251
|
+
this.runBundler(outputDir);
|
|
252
|
+
const isEsm = this.readBundleMeta(outputDir);
|
|
253
|
+
const { nodeModules } = this.props;
|
|
254
|
+
if (nodeModules?.length) this.installNodeModules(outputDir, isEsm, nodeModules);
|
|
255
|
+
else writeOutputPackageJson(outputDir, { isEsm });
|
|
256
|
+
this.runHooks(this.props.commandHooks?.afterBundling(this.props.projectRoot, outputDir) ?? [], this.props.projectRoot);
|
|
257
|
+
return true;
|
|
258
|
+
} };
|
|
259
|
+
}
|
|
260
|
+
runHooks(commands, cwd) {
|
|
261
|
+
for (const cmd of commands) this.spawn(cmd, [], cwd, `Command hook '${cmd}'`, { shell: true });
|
|
262
|
+
}
|
|
263
|
+
runBundler(outputDir) {
|
|
264
|
+
this.spawn("node", [
|
|
265
|
+
resolveRolldownBridge(),
|
|
266
|
+
this.props.bundlerConfig,
|
|
267
|
+
this.props.entry,
|
|
268
|
+
outputDir
|
|
269
|
+
], this.props.projectRoot, "Bundler 'rolldown'");
|
|
270
|
+
}
|
|
271
|
+
readBundleMeta(outputDir) {
|
|
272
|
+
const metaPath = node_path.join(outputDir, BUNDLE_META_FILENAME);
|
|
273
|
+
if (!node_fs.existsSync(metaPath)) return false;
|
|
274
|
+
try {
|
|
275
|
+
const parsed = parseJsonFile(metaPath);
|
|
276
|
+
return isEsmFormat(isRecord(parsed) && typeof parsed.format === "string" ? parsed.format : void 0);
|
|
277
|
+
} finally {
|
|
278
|
+
node_fs.unlinkSync(metaPath);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
installNodeModules(outputDir, isEsm, nodeModules) {
|
|
282
|
+
const { nearestPackageJson, packageManagerField } = readPackageManagerMeta(this.props.projectRoot);
|
|
283
|
+
if (!nearestPackageJson) throw new ValidationError("Cannot find a package.json. Using nodeModules requires a package.json.");
|
|
284
|
+
this.runHooks(this.props.commandHooks?.beforeInstall(this.props.projectRoot, outputDir) ?? [], this.props.projectRoot);
|
|
285
|
+
writeOutputPackageJson(outputDir, {
|
|
286
|
+
dependencies: extractDependencies(nearestPackageJson, nodeModules),
|
|
287
|
+
isEsm,
|
|
288
|
+
packageManager: packageManagerField
|
|
289
|
+
});
|
|
290
|
+
let stagedFiles = [];
|
|
291
|
+
try {
|
|
292
|
+
stagedFiles = copyWorkspaceFiles(this.props.projectRoot, outputDir, nodeModules, this.props.ignoreScripts);
|
|
293
|
+
if (node_fs.existsSync(this.props.depsLockFilePath)) node_fs.copyFileSync(this.props.depsLockFilePath, node_path.join(outputDir, PNPM_LOCK));
|
|
294
|
+
const [installBin, ...installArgs] = PNPM_INSTALL_COMMAND;
|
|
295
|
+
this.spawn(installBin, installArgs, outputDir, "Package manager 'pnpm' install");
|
|
296
|
+
} finally {
|
|
297
|
+
node_fs.rmSync(node_path.join(outputDir, PNPM_LOCK), { force: true });
|
|
298
|
+
for (const file of PNPM_WORKSPACE_FILES) node_fs.rmSync(node_path.join(outputDir, file), { force: true });
|
|
299
|
+
for (const file of stagedFiles) {
|
|
300
|
+
node_fs.rmSync(file, { force: true });
|
|
301
|
+
removeEmptyParentDirs(node_path.dirname(file), outputDir);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
spawn(bin, args, cwd, prefix, opts) {
|
|
306
|
+
checkSpawnResult((0, node_child_process.spawnSync)(bin, args, {
|
|
307
|
+
env: process.env,
|
|
308
|
+
stdio: [
|
|
309
|
+
"ignore",
|
|
310
|
+
"inherit",
|
|
311
|
+
"pipe"
|
|
312
|
+
],
|
|
313
|
+
cwd,
|
|
314
|
+
timeout: this.props.timeout,
|
|
315
|
+
encoding: "utf-8",
|
|
316
|
+
maxBuffer: SPAWN_MAX_BUFFER,
|
|
317
|
+
shell: opts?.shell
|
|
318
|
+
}), prefix);
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
//#endregion
|
|
322
|
+
//#region src/cdk/nodejsFunction/function.ts
|
|
323
|
+
var NodejsFunction = class extends aws_cdk_lib_aws_lambda.Function {
|
|
324
|
+
constructor(scope, id, props) {
|
|
325
|
+
if (props.runtime && props.runtime.family !== aws_cdk_lib_aws_lambda.RuntimeFamily.NODEJS) throw new ValidationError("Only NODEJS runtimes are supported.");
|
|
326
|
+
const runtime = props.runtime ?? aws_cdk_lib_aws_lambda.Runtime.NODEJS_LATEST;
|
|
327
|
+
const entry = validateEntry(props.entry);
|
|
328
|
+
const depsLockFilePath = findLockFile(props.depsLockFilePath);
|
|
329
|
+
const projectRoot = node_path.resolve(props.projectRoot ?? node_path.dirname(depsLockFilePath));
|
|
330
|
+
const bundlerConfig = findBundlerConfig(props.bundling.bundlerConfig, projectRoot);
|
|
331
|
+
const handlerFn = resolveHandlerFn(props.handler);
|
|
332
|
+
const { entry: _entry, bundling, depsLockFilePath: _depsLockFilePath, projectRoot: _projectRoot, handler: _handler, runtime: _runtime, ...functionOptions } = props;
|
|
333
|
+
super(scope, id, {
|
|
334
|
+
...functionOptions,
|
|
335
|
+
runtime,
|
|
336
|
+
code: Bundling.bundle({
|
|
337
|
+
...bundling,
|
|
338
|
+
bundlerConfig,
|
|
339
|
+
entry,
|
|
340
|
+
runtime,
|
|
341
|
+
depsLockFilePath,
|
|
342
|
+
projectRoot
|
|
343
|
+
}),
|
|
344
|
+
handler: `index.${handlerFn}`
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
const resolveHandlerFn = (handler) => {
|
|
349
|
+
const handlerName = (handler ?? "handler").trim();
|
|
350
|
+
const handlerFn = handlerName.slice(handlerName.lastIndexOf(".") + 1);
|
|
351
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(handlerFn)) throw new ValidationError(`Invalid handler '${handler}'. Expected an exported function name, optionally prefixed with a file part (e.g. 'handler' or 'index.handler').`);
|
|
352
|
+
return handlerFn;
|
|
353
|
+
};
|
|
354
|
+
const validateEntry = (entry) => {
|
|
355
|
+
const base = node_path.basename(entry);
|
|
356
|
+
if (/\.d\.(c|m)?ts$/i.test(base) || !/\.(js|ts|mjs|mts|cts|cjs)$/i.test(base)) throw new ValidationError("Only JavaScript or TypeScript entry files are supported.");
|
|
357
|
+
return node_path.resolve(entry);
|
|
358
|
+
};
|
|
359
|
+
const findBundlerConfig = (bundlerConfig, projectRoot) => node_path.isAbsolute(bundlerConfig) ? bundlerConfig : node_path.resolve(projectRoot, bundlerConfig);
|
|
360
|
+
const findLockFile = (depsLockFilePath) => {
|
|
361
|
+
if (depsLockFilePath) {
|
|
362
|
+
const resolved = node_path.resolve(depsLockFilePath);
|
|
363
|
+
if (!node_fs.existsSync(resolved)) throw new ValidationError(`Cannot find a pnpm-lock.yaml file at '${depsLockFilePath}'.`);
|
|
364
|
+
return resolved;
|
|
365
|
+
}
|
|
366
|
+
const lockFile = findUp(PNPM_LOCK);
|
|
367
|
+
if (!lockFile) throw new ValidationError("Cannot find a pnpm-lock.yaml file. Please specify it with `depsLockFilePath`.");
|
|
368
|
+
return lockFile;
|
|
369
|
+
};
|
|
370
|
+
//#endregion
|
|
371
|
+
exports.NodejsFunction = NodejsFunction;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as lambda from "aws-cdk-lib/aws-lambda";
|
|
2
|
+
import { Construct } from "constructs";
|
|
3
|
+
|
|
4
|
+
//#region src/cdk/nodejsFunction/types.d.ts
|
|
5
|
+
interface ICommandHooks {
|
|
6
|
+
beforeBundling(inputDir: string, outputDir: string): string[];
|
|
7
|
+
afterBundling(inputDir: string, outputDir: string): string[];
|
|
8
|
+
beforeInstall(inputDir: string, outputDir: string): string[];
|
|
9
|
+
}
|
|
10
|
+
interface BundlingOptions {
|
|
11
|
+
bundlerConfig: string;
|
|
12
|
+
nodeModules?: string[];
|
|
13
|
+
commandHooks?: ICommandHooks;
|
|
14
|
+
assetHash?: string;
|
|
15
|
+
timeout?: number;
|
|
16
|
+
ignoreScripts?: boolean;
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/cdk/nodejsFunction/function.d.ts
|
|
20
|
+
interface NodejsFunctionProps extends lambda.FunctionOptions {
|
|
21
|
+
readonly entry: string;
|
|
22
|
+
readonly handler?: string;
|
|
23
|
+
readonly runtime?: lambda.Runtime;
|
|
24
|
+
readonly depsLockFilePath?: string;
|
|
25
|
+
readonly projectRoot?: string;
|
|
26
|
+
readonly bundling: BundlingOptions;
|
|
27
|
+
}
|
|
28
|
+
declare class NodejsFunction extends lambda.Function {
|
|
29
|
+
constructor(scope: Construct, id: string, props: NodejsFunctionProps);
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { type BundlingOptions, type ICommandHooks, NodejsFunction, type NodejsFunctionProps };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as lambda from "aws-cdk-lib/aws-lambda";
|
|
2
|
+
import { Construct } from "constructs";
|
|
3
|
+
|
|
4
|
+
//#region src/cdk/nodejsFunction/types.d.ts
|
|
5
|
+
interface ICommandHooks {
|
|
6
|
+
beforeBundling(inputDir: string, outputDir: string): string[];
|
|
7
|
+
afterBundling(inputDir: string, outputDir: string): string[];
|
|
8
|
+
beforeInstall(inputDir: string, outputDir: string): string[];
|
|
9
|
+
}
|
|
10
|
+
interface BundlingOptions {
|
|
11
|
+
bundlerConfig: string;
|
|
12
|
+
nodeModules?: string[];
|
|
13
|
+
commandHooks?: ICommandHooks;
|
|
14
|
+
assetHash?: string;
|
|
15
|
+
timeout?: number;
|
|
16
|
+
ignoreScripts?: boolean;
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/cdk/nodejsFunction/function.d.ts
|
|
20
|
+
interface NodejsFunctionProps extends lambda.FunctionOptions {
|
|
21
|
+
readonly entry: string;
|
|
22
|
+
readonly handler?: string;
|
|
23
|
+
readonly runtime?: lambda.Runtime;
|
|
24
|
+
readonly depsLockFilePath?: string;
|
|
25
|
+
readonly projectRoot?: string;
|
|
26
|
+
readonly bundling: BundlingOptions;
|
|
27
|
+
}
|
|
28
|
+
declare class NodejsFunction extends lambda.Function {
|
|
29
|
+
constructor(scope: Construct, id: string, props: NodejsFunctionProps);
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { type BundlingOptions, type ICommandHooks, NodejsFunction, type NodejsFunctionProps };
|