@emarketeer/ts-microservice-commons 8.2.2 → 9.0.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/build-handlers.js +149 -48
- package/dist/cdk/cjs/index.js +760 -557
- package/dist/cdk/cjs/index.js.map +1 -1
- package/dist/cdk/index.js +739 -536
- package/dist/cdk/index.js.map +1 -1
- package/dist/lib/em-commons.js +44 -43
- package/dist/lib/jest.config.js +2 -2
- package/dist/types/cdk/__tests__/config.test.d.ts +1 -0
- package/dist/types/cdk/__tests__/dlq-alarm.test.d.ts +1 -0
- package/dist/types/cdk/__tests__/handler-path.test.d.ts +1 -0
- package/dist/types/cdk/__tests__/logs.test.d.ts +1 -0
- package/dist/types/cdk/__tests__/rds-vpc.test.d.ts +1 -0
- package/dist/types/cdk/constructs/dlq-alarm.d.ts +6 -0
- package/dist/types/cdk/constructs/lambda-with-http-api.d.ts +2 -2
- package/dist/types/cdk/constructs/lambda-with-queue.d.ts +28 -3
- package/dist/types/cdk/constructs/stack.d.ts +142 -33
- package/dist/types/cdk/constructs/topic-queue-consumer.d.ts +5 -0
- package/dist/types/cdk/types/common.d.ts +18 -26
- package/dist/types/cdk/utils/config.d.ts +11 -62
- package/dist/types/cdk/utils/handler-path.d.ts +26 -4
- package/dist/types/cdk/utils/iam.d.ts +1 -64
- package/dist/types/cdk/utils/logs.d.ts +11 -9
- package/dist/types/cdk/utils/rds-vpc.d.ts +8 -1
- package/dist/types/cdk/utils/serverless-migration.d.ts +187 -3
- package/dist/types/find-entry-points.d.ts +20 -0
- package/dist/types/find-entry-points.test.d.ts +1 -0
- package/dist/types/utils.d.ts +1 -1
- package/package.json +9 -3
- package/dist/types/cdk/jest.config.d.ts +0 -2
- package/dist/types/esbuild-plugins.d.ts +0 -5
package/dist/build-handlers.js
CHANGED
|
@@ -4,11 +4,108 @@ var path = require('path');
|
|
|
4
4
|
var esbuild = require('esbuild');
|
|
5
5
|
var fs = require('fs');
|
|
6
6
|
|
|
7
|
+
function _interopNamespace(e) {
|
|
8
|
+
if (e && e.__esModule) return e;
|
|
9
|
+
var n = Object.create(null);
|
|
10
|
+
if (e) {
|
|
11
|
+
Object.keys(e).forEach(function (k) {
|
|
12
|
+
if (k !== 'default') {
|
|
13
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
14
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: function () { return e[k]; }
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
n["default"] = e;
|
|
22
|
+
return Object.freeze(n);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
26
|
+
var esbuild__namespace = /*#__PURE__*/_interopNamespace(esbuild);
|
|
27
|
+
var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Patterns that indicate a file exports a Lambda handler named `handler`.
|
|
31
|
+
*
|
|
32
|
+
* Covers:
|
|
33
|
+
* export const/let/var handler = ...
|
|
34
|
+
* export function handler / export async function handler
|
|
35
|
+
* export { handler } and export { foo as handler } (the export *name* is
|
|
36
|
+
* `handler`; the negative lookahead excludes `handler as somethingElse`
|
|
37
|
+
* where `handler` is only the source identifier)
|
|
38
|
+
* module.exports.handler = ... / exports.handler = ... (CJS interop)
|
|
39
|
+
*
|
|
40
|
+
* Intentionally excludes `export default function handler` — the esbuild wrapper
|
|
41
|
+
* accesses `unwrappedHandler.handler` (named export). Default exports compile to
|
|
42
|
+
* `module.exports.default`, so `.handler` is `undefined` at runtime.
|
|
43
|
+
*
|
|
44
|
+
* Not covered: `export * from './impl'` — resolving re-exports would require
|
|
45
|
+
* following the import graph. Files that match the source-naming convention
|
|
46
|
+
* but contain no recognised pattern produce a `console.warn` from
|
|
47
|
+
* `findEntryPoints`.
|
|
48
|
+
*/
|
|
49
|
+
const HANDLER_EXPORT_PATTERNS = [
|
|
50
|
+
/export\s+(const|let|var|function|async\s+function)\s+handler\b/,
|
|
51
|
+
/export\s*\{[^}]*\bhandler\b(?!\s*as\b)[^}]*\}/,
|
|
52
|
+
/(?:module\.)?exports\.handler\s*=/,
|
|
53
|
+
];
|
|
54
|
+
function containsHandlerExport(content) {
|
|
55
|
+
return HANDLER_EXPORT_PATTERNS.some(pattern => pattern.test(content));
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Scans `handlersDir` recursively for TypeScript source files that export a
|
|
59
|
+
* Lambda handler. Returns esbuild-compatible `{ in, out }` entry point pairs.
|
|
60
|
+
*
|
|
61
|
+
* `in` is the absolute file path. `out` is relative to `handlersDir`:
|
|
62
|
+
* a file at `<handlersDir>/subdir/my-handler.ts` produces `out: 'subdir/my-handler/index'`.
|
|
63
|
+
* esbuild combines this with its `outdir` to produce the final path.
|
|
64
|
+
*
|
|
65
|
+
* Files matching the handler-source naming convention but containing no handler
|
|
66
|
+
* export (e.g. `export * from './impl'` re-exports, which cannot be detected
|
|
67
|
+
* without resolving the import graph) emit a `console.warn` so the omission
|
|
68
|
+
* surfaces in CI rather than being discovered at deploy time.
|
|
69
|
+
*
|
|
70
|
+
* Throws if a file cannot be read, with the full path included in the error message.
|
|
71
|
+
*/
|
|
72
|
+
async function findEntryPoints(handlersDir) {
|
|
73
|
+
// Explicit { encoding: 'utf8' } selects the string[] overload of readdir
|
|
74
|
+
// (the recursive overload otherwise has an ambiguous return type).
|
|
75
|
+
const files = (await fs__namespace.promises.readdir(handlersDir, { recursive: true, encoding: 'utf8' })).filter(f => f.endsWith('.ts') && !f.endsWith('.d.ts') && !f.includes('.test.') && !f.includes('.spec.'));
|
|
76
|
+
const results = await Promise.all(files.map(async (f) => {
|
|
77
|
+
const fullPath = path__namespace.join(handlersDir, f);
|
|
78
|
+
let content;
|
|
79
|
+
try {
|
|
80
|
+
content = await fs__namespace.promises.readFile(fullPath, 'utf8');
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
throw new Error(`findEntryPoints: could not read ${fullPath}: ${err.message}`);
|
|
84
|
+
}
|
|
85
|
+
if (!containsHandlerExport(content)) {
|
|
86
|
+
console.warn(`findEntryPoints: skipping ${fullPath} — no recognised handler export found. `
|
|
87
|
+
+ 'Supported shapes: export const/let/var/function handler, export { handler } / { foo as handler }, '
|
|
88
|
+
+ '(module.)exports.handler = …. Re-exports (export * from …) are not detected.');
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
in: fullPath,
|
|
93
|
+
out: path__namespace.join(path__namespace.dirname(f), path__namespace.basename(f, '.ts'), 'index'),
|
|
94
|
+
};
|
|
95
|
+
}));
|
|
96
|
+
return results.filter((entry) => entry !== undefined);
|
|
97
|
+
}
|
|
98
|
+
|
|
7
99
|
const { recapDevHandlerWrapper, defaultPlugins } = require('./esbuild-plugins');
|
|
8
100
|
const args = process.argv.slice(2);
|
|
9
101
|
const handlersDirIndex = args.indexOf('--handlers-dir');
|
|
10
102
|
const outDirIndex = args.indexOf('--out-dir');
|
|
11
103
|
const targetIndex = args.indexOf('--target');
|
|
104
|
+
// --allow-empty: exit 0 instead of 1 when no handlers are found. Used by
|
|
105
|
+
// services that may legitimately have zero Lambda handlers (libraries,
|
|
106
|
+
// infra-only packages) so a shared `em-commons build-handlers` step can be
|
|
107
|
+
// run unconditionally in their CI without breaking the pipeline.
|
|
108
|
+
const allowEmpty = args.includes('--allow-empty');
|
|
12
109
|
function resolveArg(index, flag, fallback) {
|
|
13
110
|
if (index === -1) {
|
|
14
111
|
return fallback;
|
|
@@ -24,63 +121,65 @@ const handlersDir = resolveArg(handlersDirIndex, '--handlers-dir', 'src/handlers
|
|
|
24
121
|
const outDir = resolveArg(outDirIndex, '--out-dir', 'dist/handlers');
|
|
25
122
|
const target = resolveArg(targetIndex, '--target', 'node24');
|
|
26
123
|
const rootDir = process.cwd();
|
|
27
|
-
const absoluteHandlersDir =
|
|
28
|
-
const absoluteOutDir =
|
|
29
|
-
if (!absoluteHandlersDir.startsWith(rootDir +
|
|
124
|
+
const absoluteHandlersDir = path__namespace.resolve(rootDir, handlersDir);
|
|
125
|
+
const absoluteOutDir = path__namespace.resolve(rootDir, outDir);
|
|
126
|
+
if (!absoluteHandlersDir.startsWith(rootDir + path__namespace.sep)) {
|
|
30
127
|
console.error(`--handlers-dir must be inside the project root: ${absoluteHandlersDir}`);
|
|
31
128
|
process.exit(1);
|
|
32
129
|
}
|
|
33
|
-
if (!absoluteOutDir.startsWith(rootDir +
|
|
130
|
+
if (!absoluteOutDir.startsWith(rootDir + path__namespace.sep)) {
|
|
34
131
|
console.error(`--out-dir must be inside the project root: ${absoluteOutDir}`);
|
|
35
132
|
process.exit(1);
|
|
36
133
|
}
|
|
37
|
-
if (!
|
|
134
|
+
if (!fs__namespace.existsSync(absoluteHandlersDir)) {
|
|
38
135
|
console.error(`Handlers directory not found: ${absoluteHandlersDir}`);
|
|
39
136
|
process.exit(1);
|
|
40
137
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
console.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
138
|
+
async function main() {
|
|
139
|
+
const entryPoints = await findEntryPoints(absoluteHandlersDir);
|
|
140
|
+
if (entryPoints.length === 0) {
|
|
141
|
+
if (allowEmpty) {
|
|
142
|
+
// Log the resolved absolute path so a misrouted --handlers-dir (typo'd
|
|
143
|
+
// to a real-but-empty sibling directory) is visible in CI output.
|
|
144
|
+
console.warn(`No handlers found in ${handlersDir} (resolved: ${absoluteHandlersDir}), `
|
|
145
|
+
+ 'skipping build (--allow-empty)');
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
console.error(`No handlers found in ${handlersDir}. Pass --allow-empty to suppress this error.`);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
console.log(`Building ${entryPoints.length} handler(s) from ${handlersDir}...`);
|
|
152
|
+
await esbuild__namespace.build({
|
|
153
|
+
entryPoints,
|
|
154
|
+
outdir: absoluteOutDir,
|
|
155
|
+
bundle: true,
|
|
156
|
+
platform: 'node',
|
|
157
|
+
target,
|
|
158
|
+
format: 'cjs',
|
|
159
|
+
sourcemap: false,
|
|
160
|
+
minify: true,
|
|
161
|
+
external: [
|
|
162
|
+
'@aws-sdk/*',
|
|
163
|
+
'aws-sdk',
|
|
164
|
+
// Unused Knex/DB drivers — matches serverless-esbuild external list
|
|
165
|
+
'mysql',
|
|
166
|
+
'pg',
|
|
167
|
+
'pg-native',
|
|
168
|
+
'sqlite3',
|
|
169
|
+
'mssql',
|
|
170
|
+
'oracledb',
|
|
171
|
+
'better-sqlite3',
|
|
172
|
+
'pg-sqlite3',
|
|
173
|
+
'tedious',
|
|
174
|
+
'pg-query-stream',
|
|
175
|
+
'libsql',
|
|
176
|
+
'mariadb'
|
|
177
|
+
],
|
|
178
|
+
plugins: [recapDevHandlerWrapper, ...defaultPlugins]
|
|
179
|
+
});
|
|
81
180
|
entryPoints.forEach(({ out }) => console.log(` ${outDir}/${out}.js`));
|
|
82
|
-
}
|
|
83
|
-
|
|
181
|
+
}
|
|
182
|
+
main().catch(err => {
|
|
84
183
|
if (err?.errors?.length) {
|
|
85
184
|
err.errors.forEach((e) => {
|
|
86
185
|
const loc = e.location
|
|
@@ -90,7 +189,9 @@ esbuild.build({
|
|
|
90
189
|
});
|
|
91
190
|
}
|
|
92
191
|
else {
|
|
93
|
-
|
|
192
|
+
// Print the full stack (and `cause` chain) so non-esbuild errors are
|
|
193
|
+
// diagnosable in CI output rather than being collapsed to the message.
|
|
194
|
+
console.error(err instanceof Error ? (err.stack ?? err) : err);
|
|
94
195
|
}
|
|
95
196
|
process.exit(1);
|
|
96
197
|
});
|