@intelligems/sst 2.49.6-ig.4 → 2.49.6-ig.6
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/cli/commands/dev.js +66 -1
- package/cli/sst.js +0 -1
- package/iot.js +30 -0
- package/package.json +2 -2
- package/package.json.bak +2 -2
- package/runtime/debug-bridge-logging.d.ts +24 -0
- package/runtime/debug-bridge-logging.js +98 -0
- package/runtime/debug-file-logger.d.ts +80 -0
- package/runtime/debug-file-logger.js +148 -0
- package/runtime/event-trace-logging.d.ts +27 -0
- package/runtime/event-trace-logging.js +69 -0
- package/runtime/handlers/node.js +8 -9
- package/runtime/handlers.d.ts +2 -0
- package/runtime/handlers.js +110 -65
- package/runtime/iot.js +23 -3
- package/runtime/mono-build-config.d.ts +55 -0
- package/runtime/mono-build-config.js +80 -0
- package/runtime/request-utils.d.ts +21 -0
- package/runtime/request-utils.js +102 -0
- package/runtime/runtime.d.ts +1 -0
- package/runtime/server.js +54 -7
- package/runtime/worker-pool-logging.js +65 -70
- package/runtime/workers.d.ts +26 -0
- package/runtime/workers.js +213 -16
- package/support/bridge/live-lambda.mjs +38 -38
- package/support/nodejs-runtime/index.mjs +17 -4
- package/support/python-runtime/runtime.py +20 -20
package/runtime/handlers/node.js
CHANGED
|
@@ -11,6 +11,7 @@ import { useRuntimeWorkers } from "../workers.js";
|
|
|
11
11
|
import { Colors } from "../../cli/colors.js";
|
|
12
12
|
import { Logger } from "../../logger.js";
|
|
13
13
|
import { findAbove } from "../../util/fs.js";
|
|
14
|
+
import { useMonoBuildConfig } from "../mono-build-config.js";
|
|
14
15
|
export const useNodeHandler = () => {
|
|
15
16
|
const rebuildCache = {};
|
|
16
17
|
process.on("exit", () => {
|
|
@@ -59,12 +60,10 @@ export const useNodeHandler = () => {
|
|
|
59
60
|
await worker?.terminate();
|
|
60
61
|
},
|
|
61
62
|
build: async (input) => {
|
|
62
|
-
// Check for dev mode mono-bundle
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (input.mode === "start" && monoBundleExists) {
|
|
67
|
-
Colors.line(Colors.prefix, Colors.dim.bold("MonoBundle"), Colors.dim(`mode=${input.mode}, exists=${monoBundleExists}, handler=${input.props.handler}`));
|
|
63
|
+
// Check for dev mode mono-bundle using global config
|
|
64
|
+
const monoBuildConfig = useMonoBuildConfig();
|
|
65
|
+
if (input.mode === "start" && monoBuildConfig.enabled) {
|
|
66
|
+
Colors.line(Colors.prefix, Colors.dim.bold("MonoBundle"), Colors.dim(`mode=${input.mode}, handler=${input.props.handler}`));
|
|
68
67
|
// Symlink node_modules to mono-bundle dir for external dependencies
|
|
69
68
|
// Only create if symlink doesn't exist or points to wrong location (avoid redundant I/O)
|
|
70
69
|
const parsed = path.parse(input.props.handler);
|
|
@@ -72,7 +71,7 @@ export const useNodeHandler = () => {
|
|
|
72
71
|
const root = await findAbove(handlerFunctionsDir, "package.json");
|
|
73
72
|
if (root) {
|
|
74
73
|
const sourceNodeModules = path.resolve(root, "node_modules");
|
|
75
|
-
const monoBundleNodeModules = path.join(
|
|
74
|
+
const monoBundleNodeModules = path.join(monoBuildConfig.dir, "node_modules");
|
|
76
75
|
try {
|
|
77
76
|
const existingTarget = await fs.readlink(monoBundleNodeModules);
|
|
78
77
|
if (existingTarget === sourceNodeModules) {
|
|
@@ -98,8 +97,8 @@ export const useNodeHandler = () => {
|
|
|
98
97
|
}
|
|
99
98
|
return {
|
|
100
99
|
type: "success",
|
|
101
|
-
handler:
|
|
102
|
-
out:
|
|
100
|
+
handler: monoBuildConfig.handler,
|
|
101
|
+
out: monoBuildConfig.dir,
|
|
103
102
|
};
|
|
104
103
|
}
|
|
105
104
|
const parsed = path.parse(input.props.handler);
|
package/runtime/handlers.d.ts
CHANGED
package/runtime/handlers.js
CHANGED
|
@@ -14,6 +14,9 @@ import { usePythonHandler } from "./handlers/python.js";
|
|
|
14
14
|
import { useRustHandler } from "./handlers/rust.js";
|
|
15
15
|
import { lazy } from "../util/lazy.js";
|
|
16
16
|
import { Semaphore } from "../util/semaphore.js";
|
|
17
|
+
import { isMonoBuildPath } from "./mono-build-config.js";
|
|
18
|
+
// Build concurrency semaphore - only used for actual builds, not mono-build lookups
|
|
19
|
+
const buildSemaphore = new Semaphore(parseInt(process.env.SST_BUILD_CONCURRENCY || "4", 10));
|
|
17
20
|
export const useRuntimeHandlers = lazy(() => {
|
|
18
21
|
const handlers = [
|
|
19
22
|
useNodeHandler(),
|
|
@@ -39,13 +42,11 @@ export const useRuntimeHandlers = lazy(() => {
|
|
|
39
42
|
return result;
|
|
40
43
|
},
|
|
41
44
|
async build(functionID, mode) {
|
|
42
|
-
|
|
45
|
+
// Fast path for mono-bundle: check without semaphore since no actual build work
|
|
46
|
+
async function tryMonoBundleFastPath() {
|
|
43
47
|
const func = useFunctions().fromID(functionID);
|
|
44
48
|
if (!func)
|
|
45
|
-
return
|
|
46
|
-
type: "error",
|
|
47
|
-
errors: [`Function with ID "${functionID}" not found`],
|
|
48
|
-
};
|
|
49
|
+
return null;
|
|
49
50
|
const handler = result.for(func.runtime);
|
|
50
51
|
const out = path.join(project.paths.artifacts, functionID);
|
|
51
52
|
// Check for mono-bundle mode by doing a preliminary build call
|
|
@@ -67,56 +68,84 @@ export const useRuntimeHandlers = lazy(() => {
|
|
|
67
68
|
sourcemap: monoBundleCheck.sourcemap,
|
|
68
69
|
};
|
|
69
70
|
}
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
71
|
+
return null; // Not mono-bundle, need full build
|
|
72
|
+
}
|
|
73
|
+
// Full build with semaphore protection for actual compilation work
|
|
74
|
+
async function fullBuild() {
|
|
75
|
+
const func = useFunctions().fromID(functionID);
|
|
76
|
+
if (!func)
|
|
77
|
+
return {
|
|
78
|
+
type: "error",
|
|
79
|
+
errors: [`Function with ID "${functionID}" not found`],
|
|
80
|
+
};
|
|
81
|
+
const handler = result.for(func.runtime);
|
|
82
|
+
const out = path.join(project.paths.artifacts, functionID);
|
|
83
|
+
// Acquire semaphore only for actual build work
|
|
84
|
+
const unlock = await buildSemaphore.lock();
|
|
85
|
+
try {
|
|
86
|
+
// Non-mono-bundle: follow original flow
|
|
87
|
+
await fs.rm(out, { recursive: true, force: true });
|
|
88
|
+
await fs.mkdir(out, { recursive: true });
|
|
89
|
+
bus.publish("function.build.started", { functionID });
|
|
90
|
+
if (func.hooks?.beforeBuild)
|
|
91
|
+
await func.hooks.beforeBuild(func, out);
|
|
92
|
+
const built = await handler.build({
|
|
84
93
|
functionID,
|
|
85
|
-
|
|
94
|
+
out,
|
|
95
|
+
mode,
|
|
96
|
+
props: func,
|
|
86
97
|
});
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
98
|
+
if (built.type === "error") {
|
|
99
|
+
bus.publish("function.build.failed", {
|
|
100
|
+
functionID,
|
|
101
|
+
errors: built.errors,
|
|
102
|
+
});
|
|
103
|
+
return built;
|
|
104
|
+
}
|
|
105
|
+
if (func.copyFiles) {
|
|
106
|
+
await Promise.all(func.copyFiles.map(async (entry) => {
|
|
107
|
+
const fromPath = path.join(project.paths.root, entry.from);
|
|
108
|
+
const to = entry.to || entry.from;
|
|
109
|
+
if (path.isAbsolute(to))
|
|
110
|
+
throw new Error(`Copy destination path "${to}" must be relative`);
|
|
111
|
+
const toPath = path.join(out, to);
|
|
112
|
+
if (mode === "deploy")
|
|
113
|
+
await fs.cp(fromPath, toPath, {
|
|
114
|
+
recursive: true,
|
|
115
|
+
});
|
|
116
|
+
if (mode === "start") {
|
|
117
|
+
try {
|
|
118
|
+
const dir = path.dirname(toPath);
|
|
119
|
+
await fs.mkdir(dir, { recursive: true });
|
|
120
|
+
await fs.symlink(fromPath, toPath);
|
|
121
|
+
}
|
|
122
|
+
catch (ex) {
|
|
123
|
+
Logger.debug("Failed to symlink", fromPath, toPath, ex);
|
|
124
|
+
}
|
|
108
125
|
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
128
|
+
if (func.hooks?.afterBuild)
|
|
129
|
+
await func.hooks.afterBuild(func, out);
|
|
130
|
+
bus.publish("function.build.success", { functionID });
|
|
131
|
+
return {
|
|
132
|
+
...built,
|
|
133
|
+
out,
|
|
134
|
+
sourcemap: built.sourcemap,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
unlock();
|
|
111
139
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
140
|
+
}
|
|
141
|
+
async function task() {
|
|
142
|
+
// Try mono-bundle fast path first (no semaphore needed)
|
|
143
|
+
const monoBundleResult = await tryMonoBundleFastPath();
|
|
144
|
+
if (monoBundleResult) {
|
|
145
|
+
return monoBundleResult;
|
|
146
|
+
}
|
|
147
|
+
// Fall back to full build with semaphore protection
|
|
148
|
+
return fullBuild();
|
|
120
149
|
}
|
|
121
150
|
if (pendingBuilds.has(functionID)) {
|
|
122
151
|
Logger.debug("Waiting on pending build", functionID);
|
|
@@ -138,27 +167,43 @@ export const useRuntimeHandlers = lazy(() => {
|
|
|
138
167
|
export const useFunctionBuilder = lazy(() => {
|
|
139
168
|
const artifacts = new Map();
|
|
140
169
|
const handlers = useRuntimeHandlers();
|
|
141
|
-
|
|
170
|
+
// Track pending builds to prevent duplicate concurrent builds for same function
|
|
171
|
+
const pendingArtifactBuilds = new Map();
|
|
142
172
|
const result = {
|
|
143
173
|
artifact: (functionID) => {
|
|
174
|
+
// Fast path: already cached - return immediately without any async work
|
|
144
175
|
if (artifacts.has(functionID))
|
|
145
176
|
return artifacts.get(functionID);
|
|
146
177
|
return result.build(functionID);
|
|
147
178
|
},
|
|
148
179
|
build: async (functionID) => {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const result = await handlers.build(functionID, "start");
|
|
152
|
-
if (!result)
|
|
153
|
-
return;
|
|
154
|
-
if (result.type === "error")
|
|
155
|
-
return;
|
|
156
|
-
artifacts.set(functionID, result);
|
|
180
|
+
// Fast path: already cached (check again in case of concurrent calls)
|
|
181
|
+
if (artifacts.has(functionID))
|
|
157
182
|
return artifacts.get(functionID);
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
183
|
+
// Deduplication: if build already in progress for this function, wait for it
|
|
184
|
+
const pending = pendingArtifactBuilds.get(functionID);
|
|
185
|
+
if (pending)
|
|
186
|
+
return pending;
|
|
187
|
+
const buildTask = async () => {
|
|
188
|
+
try {
|
|
189
|
+
// handlers.build() handles semaphore internally:
|
|
190
|
+
// - mono-build: no semaphore (fast path)
|
|
191
|
+
// - non-mono-build: semaphore protected
|
|
192
|
+
const buildResult = await handlers.build(functionID, "start");
|
|
193
|
+
if (!buildResult)
|
|
194
|
+
return;
|
|
195
|
+
if (buildResult.type === "error")
|
|
196
|
+
return;
|
|
197
|
+
artifacts.set(functionID, buildResult);
|
|
198
|
+
return artifacts.get(functionID);
|
|
199
|
+
}
|
|
200
|
+
finally {
|
|
201
|
+
pendingArtifactBuilds.delete(functionID);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
const promise = buildTask();
|
|
205
|
+
pendingArtifactBuilds.set(functionID, promise);
|
|
206
|
+
return promise;
|
|
162
207
|
},
|
|
163
208
|
};
|
|
164
209
|
const watcher = useWatcher();
|
|
@@ -169,7 +214,7 @@ export const useFunctionBuilder = lazy(() => {
|
|
|
169
214
|
// Optimization: For mono-build, the artifact path is stable and build is handled externally.
|
|
170
215
|
// We can skip the potentially expensive shouldBuild check and rebuild call.
|
|
171
216
|
const existing = artifacts.get(functionID);
|
|
172
|
-
if (existing
|
|
217
|
+
if (existing && isMonoBuildPath(existing.out))
|
|
173
218
|
continue;
|
|
174
219
|
const handler = handlers.for(info.runtime);
|
|
175
220
|
if (!handler?.shouldBuild({
|
package/runtime/iot.js
CHANGED
|
@@ -2,20 +2,40 @@ import { useBus } from "../bus.js";
|
|
|
2
2
|
import { useIOT } from "../iot.js";
|
|
3
3
|
import { lazy } from "../util/lazy.js";
|
|
4
4
|
import { logInvokeTrace } from "./worker-pool-logging.js";
|
|
5
|
+
import { logIot } from "./debug-bridge-logging.js";
|
|
6
|
+
import { logEventTrace } from "./event-trace-logging.js";
|
|
5
7
|
export const useIOTBridge = lazy(async () => {
|
|
6
8
|
const bus = useBus();
|
|
7
9
|
const iot = await useIOT();
|
|
8
10
|
const topic = `${iot.prefix}/events`;
|
|
9
11
|
bus.subscribe("function.success", async (evt) => {
|
|
10
|
-
|
|
12
|
+
const { workerID, requestID } = evt.properties;
|
|
13
|
+
logIot(`reqId=${requestID?.slice(0, 8)} Publishing function.success to worker ${workerID.slice(0, 8)}`);
|
|
14
|
+
const startTime = Date.now();
|
|
15
|
+
await iot.publish(topic + "/" + workerID, "function.success", evt.properties);
|
|
16
|
+
logIot(`reqId=${requestID?.slice(0, 8)} function.success published in ${Date.now() - startTime}ms`);
|
|
11
17
|
});
|
|
12
18
|
bus.subscribe("function.error", async (evt) => {
|
|
13
|
-
|
|
19
|
+
const { workerID, requestID } = evt.properties;
|
|
20
|
+
logIot(`reqId=${requestID?.slice(0, 8)} Publishing function.error to worker ${workerID.slice(0, 8)}`);
|
|
21
|
+
const startTime = Date.now();
|
|
22
|
+
await iot.publish(topic + "/" + workerID, "function.error", evt.properties);
|
|
23
|
+
logIot(`reqId=${requestID?.slice(0, 8)} function.error published in ${Date.now() - startTime}ms`);
|
|
14
24
|
});
|
|
15
25
|
bus.subscribe("function.ack", async (evt) => {
|
|
16
|
-
const workerID = evt.properties
|
|
26
|
+
const { workerID, requestID, functionID } = evt.properties;
|
|
27
|
+
logIot(`reqId=${requestID?.slice(0, 8)} Publishing function.ack to worker ${workerID.slice(0, 8)}`);
|
|
17
28
|
logInvokeTrace("IOT_ACK_START", workerID, `worker=${workerID.slice(0, 8)}`);
|
|
29
|
+
const startTime = Date.now();
|
|
18
30
|
await iot.publish(topic + "/" + workerID, "function.ack", evt.properties);
|
|
31
|
+
const elapsed = Date.now() - startTime;
|
|
32
|
+
logIot(`reqId=${requestID?.slice(0, 8)} function.ack published in ${elapsed}ms`);
|
|
19
33
|
logInvokeTrace("IOT_ACK_DONE", workerID, `worker=${workerID.slice(0, 8)}`);
|
|
34
|
+
logEventTrace("IOT_ACK", {
|
|
35
|
+
requestID,
|
|
36
|
+
functionID,
|
|
37
|
+
workerID,
|
|
38
|
+
elapsed,
|
|
39
|
+
});
|
|
20
40
|
});
|
|
21
41
|
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global mono build configuration for SST dev mode.
|
|
3
|
+
*
|
|
4
|
+
* Mono build mode bundles all Lambda handlers into a single file (.mono-build/index.mjs)
|
|
5
|
+
* instead of building each handler individually. This significantly speeds up dev mode
|
|
6
|
+
* by sharing compilation work across all handlers.
|
|
7
|
+
*
|
|
8
|
+
* Detection happens once at startup and the result is cached for the session.
|
|
9
|
+
*/
|
|
10
|
+
export declare const useMonoBuildConfig: () => {
|
|
11
|
+
/**
|
|
12
|
+
* Whether mono build mode is enabled (detected at startup).
|
|
13
|
+
* When true, all Node.js handlers use the shared .mono-build bundle.
|
|
14
|
+
*/
|
|
15
|
+
enabled: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* The mono bundle directory (.mono-build)
|
|
18
|
+
*/
|
|
19
|
+
dir: string;
|
|
20
|
+
/**
|
|
21
|
+
* The mono bundle entry file (.mono-build/index.mjs)
|
|
22
|
+
*/
|
|
23
|
+
entryFile: string;
|
|
24
|
+
/**
|
|
25
|
+
* The handler string to use for mono build mode
|
|
26
|
+
*/
|
|
27
|
+
handler: string;
|
|
28
|
+
/**
|
|
29
|
+
* Check if a build output path represents a mono build.
|
|
30
|
+
* This is useful when you have a build result and need to determine its type.
|
|
31
|
+
*/
|
|
32
|
+
isMonoBuildPath(buildOut: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Get the pool key for a function based on mono build status.
|
|
35
|
+
* For mono build: shared key (all functions share workers)
|
|
36
|
+
* For non-mono build: per-function key
|
|
37
|
+
*/
|
|
38
|
+
getPoolKey(functionID: string, runtime: string, buildOut: string): {
|
|
39
|
+
key: string;
|
|
40
|
+
isShared: boolean;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Quick check for mono build mode without full config initialization.
|
|
45
|
+
* Use this for simple boolean checks where you don't need the full config.
|
|
46
|
+
*/
|
|
47
|
+
export declare function isMonoBuildEnabled(): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Get the mono build directory path.
|
|
50
|
+
*/
|
|
51
|
+
export declare function getMonoBuildDir(): string;
|
|
52
|
+
/**
|
|
53
|
+
* Check if a path represents a mono build output.
|
|
54
|
+
*/
|
|
55
|
+
export declare function isMonoBuildPath(buildOut: string): boolean;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import fsSync from "fs";
|
|
3
|
+
import { useProject } from "../project.js";
|
|
4
|
+
import { lazy } from "../util/lazy.js";
|
|
5
|
+
import { Logger } from "../logger.js";
|
|
6
|
+
/**
|
|
7
|
+
* Global mono build configuration for SST dev mode.
|
|
8
|
+
*
|
|
9
|
+
* Mono build mode bundles all Lambda handlers into a single file (.mono-build/index.mjs)
|
|
10
|
+
* instead of building each handler individually. This significantly speeds up dev mode
|
|
11
|
+
* by sharing compilation work across all handlers.
|
|
12
|
+
*
|
|
13
|
+
* Detection happens once at startup and the result is cached for the session.
|
|
14
|
+
*/
|
|
15
|
+
export const useMonoBuildConfig = lazy(() => {
|
|
16
|
+
const project = useProject();
|
|
17
|
+
const monoBundleDir = path.join(project.paths.root, ".mono-build");
|
|
18
|
+
const monoBundlePath = path.join(monoBundleDir, "index.mjs");
|
|
19
|
+
// Check once at startup if mono build exists
|
|
20
|
+
const enabled = fsSync.existsSync(monoBundlePath);
|
|
21
|
+
if (enabled) {
|
|
22
|
+
Logger.debug("Mono build mode enabled:", monoBundlePath);
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
/**
|
|
26
|
+
* Whether mono build mode is enabled (detected at startup).
|
|
27
|
+
* When true, all Node.js handlers use the shared .mono-build bundle.
|
|
28
|
+
*/
|
|
29
|
+
enabled,
|
|
30
|
+
/**
|
|
31
|
+
* The mono bundle directory (.mono-build)
|
|
32
|
+
*/
|
|
33
|
+
dir: monoBundleDir,
|
|
34
|
+
/**
|
|
35
|
+
* The mono bundle entry file (.mono-build/index.mjs)
|
|
36
|
+
*/
|
|
37
|
+
entryFile: monoBundlePath,
|
|
38
|
+
/**
|
|
39
|
+
* The handler string to use for mono build mode
|
|
40
|
+
*/
|
|
41
|
+
handler: "index.handler",
|
|
42
|
+
/**
|
|
43
|
+
* Check if a build output path represents a mono build.
|
|
44
|
+
* This is useful when you have a build result and need to determine its type.
|
|
45
|
+
*/
|
|
46
|
+
isMonoBuildPath(buildOut) {
|
|
47
|
+
return enabled && buildOut.includes(".mono-build");
|
|
48
|
+
},
|
|
49
|
+
/**
|
|
50
|
+
* Get the pool key for a function based on mono build status.
|
|
51
|
+
* For mono build: shared key (all functions share workers)
|
|
52
|
+
* For non-mono build: per-function key
|
|
53
|
+
*/
|
|
54
|
+
getPoolKey(functionID, runtime, buildOut) {
|
|
55
|
+
if (enabled && buildOut.includes(".mono-build")) {
|
|
56
|
+
return { key: `${runtime}:mono-build`, isShared: true };
|
|
57
|
+
}
|
|
58
|
+
return { key: `${runtime}:${functionID}`, isShared: false };
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
/**
|
|
63
|
+
* Quick check for mono build mode without full config initialization.
|
|
64
|
+
* Use this for simple boolean checks where you don't need the full config.
|
|
65
|
+
*/
|
|
66
|
+
export function isMonoBuildEnabled() {
|
|
67
|
+
return useMonoBuildConfig().enabled;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get the mono build directory path.
|
|
71
|
+
*/
|
|
72
|
+
export function getMonoBuildDir() {
|
|
73
|
+
return useMonoBuildConfig().dir;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Check if a path represents a mono build output.
|
|
77
|
+
*/
|
|
78
|
+
export function isMonoBuildPath(buildOut) {
|
|
79
|
+
return useMonoBuildConfig().isMonoBuildPath(buildOut);
|
|
80
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts a request path from a Lambda event for logging purposes.
|
|
3
|
+
* Handles API Gateway v1, v2, and other event formats.
|
|
4
|
+
*/
|
|
5
|
+
export declare function getRequestPath(event: unknown): string;
|
|
6
|
+
/**
|
|
7
|
+
* Extracts correlation ID from headers for request tracing.
|
|
8
|
+
* Checks common correlation header names.
|
|
9
|
+
*
|
|
10
|
+
* Frontend can send: X-Correlation-ID, X-Request-ID, or X-Trace-ID
|
|
11
|
+
*/
|
|
12
|
+
export declare function getCorrelationId(event: unknown): string | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Extracts API Gateway Request ID from requestContext.
|
|
15
|
+
* This ID is visible in browser dev tools (x-amzn-requestid response header).
|
|
16
|
+
*/
|
|
17
|
+
export declare function getApiGatewayRequestId(event: unknown): string | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Extracts HTTP method from event
|
|
20
|
+
*/
|
|
21
|
+
export declare function getHttpMethod(event: unknown): string | undefined;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts a request path from a Lambda event for logging purposes.
|
|
3
|
+
* Handles API Gateway v1, v2, and other event formats.
|
|
4
|
+
*/
|
|
5
|
+
export function getRequestPath(event) {
|
|
6
|
+
if (!event || typeof event !== "object") {
|
|
7
|
+
return "[unknown]";
|
|
8
|
+
}
|
|
9
|
+
const evt = event;
|
|
10
|
+
// API Gateway v2 (HTTP API)
|
|
11
|
+
if (typeof evt.rawPath === "string") {
|
|
12
|
+
return evt.rawPath;
|
|
13
|
+
}
|
|
14
|
+
// API Gateway v1 (REST API)
|
|
15
|
+
if (typeof evt.path === "string") {
|
|
16
|
+
return evt.path;
|
|
17
|
+
}
|
|
18
|
+
// Warmup requests
|
|
19
|
+
if ("ding" in evt || "warmer" in evt || evt.__sst_warmup === true) {
|
|
20
|
+
return "[warmup]";
|
|
21
|
+
}
|
|
22
|
+
// SQS, SNS, or other event types - no path
|
|
23
|
+
return "[event]";
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Extracts correlation ID from headers for request tracing.
|
|
27
|
+
* Checks common correlation header names.
|
|
28
|
+
*
|
|
29
|
+
* Frontend can send: X-Correlation-ID, X-Request-ID, or X-Trace-ID
|
|
30
|
+
*/
|
|
31
|
+
export function getCorrelationId(event) {
|
|
32
|
+
if (!event || typeof event !== "object") {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
const evt = event;
|
|
36
|
+
// Get headers - handle both v1 and v2 API Gateway formats
|
|
37
|
+
let headers;
|
|
38
|
+
// API Gateway v2 uses lowercase headers
|
|
39
|
+
if (evt.headers && typeof evt.headers === "object") {
|
|
40
|
+
headers = evt.headers;
|
|
41
|
+
}
|
|
42
|
+
if (!headers)
|
|
43
|
+
return undefined;
|
|
44
|
+
// Check common correlation header names (case-insensitive)
|
|
45
|
+
const correlationHeaders = [
|
|
46
|
+
"x-correlation-id",
|
|
47
|
+
"x-request-id",
|
|
48
|
+
"x-trace-id",
|
|
49
|
+
"correlation-id",
|
|
50
|
+
"request-id",
|
|
51
|
+
];
|
|
52
|
+
// Normalize header keys to lowercase for comparison
|
|
53
|
+
const normalizedHeaders = {};
|
|
54
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
55
|
+
normalizedHeaders[key.toLowerCase()] = value;
|
|
56
|
+
}
|
|
57
|
+
for (const headerName of correlationHeaders) {
|
|
58
|
+
const value = normalizedHeaders[headerName];
|
|
59
|
+
if (value) {
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Extracts API Gateway Request ID from requestContext.
|
|
67
|
+
* This ID is visible in browser dev tools (x-amzn-requestid response header).
|
|
68
|
+
*/
|
|
69
|
+
export function getApiGatewayRequestId(event) {
|
|
70
|
+
if (!event || typeof event !== "object") {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
const evt = event;
|
|
74
|
+
// Check requestContext.requestId (both v1 and v2)
|
|
75
|
+
const requestContext = evt.requestContext;
|
|
76
|
+
if (requestContext?.requestId && typeof requestContext.requestId === "string") {
|
|
77
|
+
return requestContext.requestId;
|
|
78
|
+
}
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Extracts HTTP method from event
|
|
83
|
+
*/
|
|
84
|
+
export function getHttpMethod(event) {
|
|
85
|
+
if (!event || typeof event !== "object") {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
const evt = event;
|
|
89
|
+
// API Gateway v2
|
|
90
|
+
const requestContext = evt.requestContext;
|
|
91
|
+
if (requestContext?.http && typeof requestContext.http === "object") {
|
|
92
|
+
const http = requestContext.http;
|
|
93
|
+
if (typeof http.method === "string") {
|
|
94
|
+
return http.method;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// API Gateway v1
|
|
98
|
+
if (typeof evt.httpMethod === "string") {
|
|
99
|
+
return evt.httpMethod;
|
|
100
|
+
}
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|