@cross-deck/node 0.1.0 → 1.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.
@@ -0,0 +1,354 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/auto-events/index.ts
21
+ var auto_events_exports = {};
22
+ __export(auto_events_exports, {
23
+ crossdeckExpress: () => crossdeckExpress,
24
+ crossdeckExpressErrorHandler: () => crossdeckExpressErrorHandler,
25
+ extractRoutePattern: () => extractRoutePattern,
26
+ shouldSkipRequest: () => shouldSkipRequest,
27
+ wrapFunction: () => wrapFunction,
28
+ wrapLambdaHandler: () => wrapLambdaHandler
29
+ });
30
+ module.exports = __toCommonJS(auto_events_exports);
31
+
32
+ // src/auto-events/express.ts
33
+ var DEFAULT_SKIP_PATHS = [/^\/crossdeck($|\/)/];
34
+ function crossdeckExpress(server, options = {}) {
35
+ const skipPaths = options.skipPaths ?? DEFAULT_SKIP_PATHS;
36
+ return function crossdeckExpressMiddleware(req, res, next) {
37
+ if (shouldSkipRequest(req, skipPaths)) {
38
+ next();
39
+ return;
40
+ }
41
+ const start = Date.now();
42
+ let dispatched = false;
43
+ const emit = () => {
44
+ if (dispatched) return;
45
+ dispatched = true;
46
+ let identity = void 0;
47
+ try {
48
+ identity = options.getIdentity?.(req);
49
+ } catch {
50
+ }
51
+ try {
52
+ const props = {
53
+ route: extractRoutePattern(req),
54
+ method: req.method,
55
+ statusCode: res.statusCode,
56
+ durationMs: Date.now() - start
57
+ };
58
+ try {
59
+ const ua = readHeader(req, "user-agent");
60
+ if (ua) props.userAgent = ua;
61
+ } catch {
62
+ }
63
+ try {
64
+ const cl = typeof res.getHeader === "function" ? res.getHeader("content-length") : void 0;
65
+ if (typeof cl === "number") {
66
+ props.responseBytes = cl;
67
+ } else if (typeof cl === "string") {
68
+ const parsed = Number(cl);
69
+ if (Number.isFinite(parsed)) props.responseBytes = parsed;
70
+ }
71
+ } catch {
72
+ }
73
+ server.track({
74
+ name: "request.handled",
75
+ developerUserId: identity?.developerUserId,
76
+ anonymousId: identity?.anonymousId,
77
+ crossdeckCustomerId: identity?.crossdeckCustomerId,
78
+ properties: props
79
+ });
80
+ } catch {
81
+ }
82
+ };
83
+ res.once("finish", emit);
84
+ res.once("close", emit);
85
+ next();
86
+ };
87
+ }
88
+ function crossdeckExpressErrorHandler(server, options = {}) {
89
+ const attachContext = options.captureErrorsWithRequestContext !== false;
90
+ return function crossdeckExpressErrorMiddleware(err, req, _res, next) {
91
+ try {
92
+ if (attachContext) {
93
+ server.captureError(err, {
94
+ context: {
95
+ request: {
96
+ url: req.originalUrl ?? req.url,
97
+ method: req.method,
98
+ route: extractRoutePattern(req)
99
+ }
100
+ }
101
+ });
102
+ } else {
103
+ server.captureError(err);
104
+ }
105
+ } catch {
106
+ }
107
+ next(err);
108
+ };
109
+ }
110
+ function shouldSkipRequest(req, skipPaths) {
111
+ const candidates = [req.path, req.url].filter((s) => typeof s === "string");
112
+ for (const candidate of candidates) {
113
+ for (const pattern of skipPaths) {
114
+ if (typeof pattern === "string" && candidate.startsWith(pattern)) return true;
115
+ if (pattern instanceof RegExp && pattern.test(candidate)) return true;
116
+ }
117
+ }
118
+ return false;
119
+ }
120
+ function readHeader(req, name) {
121
+ if (!req.headers) return void 0;
122
+ const v = req.headers[name];
123
+ if (typeof v === "string") return v;
124
+ if (Array.isArray(v)) return v[0];
125
+ return void 0;
126
+ }
127
+ function extractRoutePattern(req) {
128
+ const routePath = req.route?.path;
129
+ if (typeof routePath === "string") return routePath;
130
+ if (routePath instanceof RegExp) return routePath.source;
131
+ if (Array.isArray(routePath)) {
132
+ return routePath.map((p) => typeof p === "string" ? p : p instanceof RegExp ? p.source : "").filter(Boolean).join("|");
133
+ }
134
+ return req.path ?? req.url ?? "<unknown>";
135
+ }
136
+
137
+ // src/auto-events/lambda.ts
138
+ var containerColdStart = true;
139
+ function wrapLambdaHandler(server, handler, options = {}) {
140
+ if (options.resetColdStart === true) containerColdStart = true;
141
+ return async function wrappedLambdaHandler(event, context) {
142
+ const start = Date.now();
143
+ const coldStart = containerColdStart;
144
+ containerColdStart = false;
145
+ const identity = safeExtractIdentity(options.getIdentity, event, context);
146
+ server.track({
147
+ name: "function.invoked",
148
+ developerUserId: identity?.developerUserId,
149
+ anonymousId: identity?.anonymousId,
150
+ crossdeckCustomerId: identity?.crossdeckCustomerId,
151
+ properties: {
152
+ runtime: "aws-lambda",
153
+ requestId: context.awsRequestId,
154
+ functionName: context.functionName,
155
+ functionVersion: context.functionVersion,
156
+ coldStart,
157
+ memoryLimitMb: numericOrUndefined(context.memoryLimitInMB),
158
+ remainingMs: safeRemainingMs(context)
159
+ }
160
+ });
161
+ try {
162
+ const result = await handler(event, context);
163
+ const completedProps = {
164
+ runtime: "aws-lambda",
165
+ requestId: context.awsRequestId,
166
+ functionName: context.functionName,
167
+ durationMs: Date.now() - start,
168
+ memoryUsedMb: rssMb()
169
+ };
170
+ if (isHttpStyleResponse(result)) {
171
+ completedProps.statusCode = result.statusCode;
172
+ if (typeof result.body === "string") {
173
+ completedProps.responseBytes = Buffer.byteLength(result.body, "utf8");
174
+ }
175
+ }
176
+ server.track({
177
+ name: "function.completed",
178
+ developerUserId: identity?.developerUserId,
179
+ anonymousId: identity?.anonymousId,
180
+ crossdeckCustomerId: identity?.crossdeckCustomerId,
181
+ properties: completedProps
182
+ });
183
+ return result;
184
+ } catch (err) {
185
+ try {
186
+ server.captureError(err, {
187
+ context: {
188
+ lambda: {
189
+ requestId: context.awsRequestId,
190
+ functionName: context.functionName,
191
+ functionVersion: context.functionVersion
192
+ }
193
+ }
194
+ });
195
+ } catch {
196
+ }
197
+ try {
198
+ server.track({
199
+ name: "function.failed",
200
+ developerUserId: identity?.developerUserId,
201
+ anonymousId: identity?.anonymousId,
202
+ crossdeckCustomerId: identity?.crossdeckCustomerId,
203
+ properties: {
204
+ runtime: "aws-lambda",
205
+ requestId: context.awsRequestId,
206
+ functionName: context.functionName,
207
+ errorType: err instanceof Error ? err.name : null,
208
+ errorMessage: err instanceof Error ? err.message : String(err),
209
+ durationMs: Date.now() - start
210
+ }
211
+ });
212
+ } catch {
213
+ }
214
+ throw err;
215
+ } finally {
216
+ try {
217
+ await server.flush();
218
+ } catch {
219
+ }
220
+ }
221
+ };
222
+ }
223
+ function safeExtractIdentity(extractor, event, context) {
224
+ if (!extractor) return void 0;
225
+ try {
226
+ return extractor(event, context);
227
+ } catch {
228
+ return void 0;
229
+ }
230
+ }
231
+ function safeRemainingMs(context) {
232
+ try {
233
+ return typeof context.getRemainingTimeInMillis === "function" ? context.getRemainingTimeInMillis() : void 0;
234
+ } catch {
235
+ return void 0;
236
+ }
237
+ }
238
+ function numericOrUndefined(value) {
239
+ if (typeof value === "number") return value;
240
+ if (typeof value === "string") {
241
+ const n = Number(value);
242
+ return Number.isFinite(n) ? n : void 0;
243
+ }
244
+ return void 0;
245
+ }
246
+ function rssMb() {
247
+ try {
248
+ return Math.round(process.memoryUsage().rss / 1024 / 1024);
249
+ } catch {
250
+ return 0;
251
+ }
252
+ }
253
+ function isHttpStyleResponse(value) {
254
+ return Boolean(
255
+ value && typeof value === "object" && typeof value.statusCode === "number"
256
+ );
257
+ }
258
+
259
+ // src/auto-events/firebase.ts
260
+ var containerColdStart2 = true;
261
+ function wrapFunction(server, handler, options = {}) {
262
+ if (options.resetColdStart === true) containerColdStart2 = true;
263
+ const runtime = options.runtime ?? "firebase-functions";
264
+ return async function wrappedFirebaseHandler(...args) {
265
+ const start = Date.now();
266
+ const coldStart = containerColdStart2;
267
+ containerColdStart2 = false;
268
+ const metadata = safeExtractMetadata(options.getMetadata, args);
269
+ const identity = metadata?.identity ?? {};
270
+ const extraProps = metadata?.properties ?? {};
271
+ server.track({
272
+ name: "function.invoked",
273
+ developerUserId: identity.developerUserId,
274
+ anonymousId: identity.anonymousId,
275
+ crossdeckCustomerId: identity.crossdeckCustomerId,
276
+ properties: {
277
+ runtime,
278
+ coldStart,
279
+ ...extraProps
280
+ }
281
+ });
282
+ try {
283
+ const result = await handler(...args);
284
+ server.track({
285
+ name: "function.completed",
286
+ developerUserId: identity.developerUserId,
287
+ anonymousId: identity.anonymousId,
288
+ crossdeckCustomerId: identity.crossdeckCustomerId,
289
+ properties: {
290
+ runtime,
291
+ durationMs: Date.now() - start,
292
+ memoryUsedMb: rssMb2(),
293
+ ...extraProps
294
+ }
295
+ });
296
+ return result;
297
+ } catch (err) {
298
+ try {
299
+ server.captureError(err, {
300
+ context: { firebase: extraProps }
301
+ });
302
+ } catch {
303
+ }
304
+ try {
305
+ server.track({
306
+ name: "function.failed",
307
+ developerUserId: identity.developerUserId,
308
+ anonymousId: identity.anonymousId,
309
+ crossdeckCustomerId: identity.crossdeckCustomerId,
310
+ properties: {
311
+ runtime,
312
+ errorType: err instanceof Error ? err.name : null,
313
+ errorMessage: err instanceof Error ? err.message : String(err),
314
+ durationMs: Date.now() - start,
315
+ ...extraProps
316
+ }
317
+ });
318
+ } catch {
319
+ }
320
+ throw err;
321
+ } finally {
322
+ try {
323
+ await server.flush();
324
+ } catch {
325
+ }
326
+ }
327
+ };
328
+ }
329
+ function safeExtractMetadata(extractor, args) {
330
+ if (!extractor) return void 0;
331
+ try {
332
+ const out = extractor(args);
333
+ return out ?? void 0;
334
+ } catch {
335
+ return void 0;
336
+ }
337
+ }
338
+ function rssMb2() {
339
+ try {
340
+ return Math.round(process.memoryUsage().rss / 1024 / 1024);
341
+ } catch {
342
+ return 0;
343
+ }
344
+ }
345
+ // Annotate the CommonJS export names for ESM import in node:
346
+ 0 && (module.exports = {
347
+ crossdeckExpress,
348
+ crossdeckExpressErrorHandler,
349
+ extractRoutePattern,
350
+ shouldSkipRequest,
351
+ wrapFunction,
352
+ wrapLambdaHandler
353
+ });
354
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/auto-events/index.ts","../../src/auto-events/express.ts","../../src/auto-events/lambda.ts","../../src/auto-events/firebase.ts"],"sourcesContent":["/**\n * @cross-deck/node/auto-events — framework adapters barrel.\n *\n * Each adapter is a thin wrapper around the core `CrossdeckServer`\n * API. They don't pull their framework's runtime types as a hard\n * dependency — the imports are shape-only — so a customer using\n * Express but not Firebase doesn't pay for `firebase-functions`\n * types, and vice versa.\n *\n * The three adapters approved for v1.0.0 (per the SDK_TRUTH.md\n * capability matrix Q1 decision):\n * - Express + ExpressErrorHandler — `request.handled` + route-error capture\n * - Lambda — `function.invoked` / `completed` / `failed` + flush-before-return\n * - Firebase / Cloud Run — generic `wrapFunction` for any handler shape\n *\n * Fastify is deferred to v0.3.0 (Q1 decision). Cloudflare Workers and\n * Vercel Edge are deferred to v0.4+ (no `process.on(...)` lifecycle\n * in Workers — flush-on-exit needs a runtime-specific pattern).\n */\n\nexport {\n crossdeckExpress,\n crossdeckExpressErrorHandler,\n shouldSkipRequest,\n extractRoutePattern,\n} from \"./express\";\nexport type {\n CrossdeckExpressOptions,\n ExpressNext,\n ExpressRequestLike,\n ExpressResponseLike,\n} from \"./express\";\n\nexport { wrapLambdaHandler } from \"./lambda\";\nexport type {\n LambdaContextLike,\n LambdaHandlerLike,\n WrapLambdaOptions,\n} from \"./lambda\";\n\nexport { wrapFunction } from \"./firebase\";\nexport type { WrapFunctionOptions, WrapFunctionMetadata } from \"./firebase\";\n","/**\n * Express auto-events — `request.handled` middleware + uncaught-route\n * error capture.\n *\n * Two middleware factories, registered separately because Express\n * differentiates them by arity:\n *\n * import { crossdeckExpress, crossdeckExpressErrorHandler } from\n * \"@cross-deck/node/auto-events\";\n *\n * app.use(crossdeckExpress(server)); // request middleware\n * app.use(routes); // your routes\n * app.use(crossdeckExpressErrorHandler(server)); // LAST — error middleware\n *\n * `crossdeckExpress` emits `request.handled` on response 'finish'\n * with the matched route pattern (not the full URL — high-cardinality\n * URL paths kill dashboards), method, statusCode, and durationMs.\n *\n * `crossdeckExpressErrorHandler` catches errors thrown in route\n * handlers (sync OR async — Express 5 supports async handlers\n * natively; Express 4 needs the caller to forward via `next(err)`).\n * The error is shipped with request context (url, method, matched\n * route) attached so the dashboard can group by route.\n *\n * Compatible with both Express 4 and Express 5. The middleware\n * signatures are stable across both versions.\n *\n * No `import` from `express`. The adapter speaks shape-only against\n * Express's request / response objects — customers don't pay a\n * forced dependency on Express just to install the Crossdeck SDK.\n * If `express` is missing at install, this module still compiles.\n */\n\nimport type { CrossdeckServer } from \"../crossdeck-server\";\n\n/**\n * Shape of an Express request object — enough fields for the\n * middleware to do its job without depending on Express's types.\n */\nexport interface ExpressRequestLike {\n method: string;\n url: string;\n path?: string;\n route?: { path?: string | RegExp | Array<string | RegExp> };\n originalUrl?: string;\n headers?: Record<string, string | string[] | undefined>;\n}\n\n/** Shape of an Express response object. */\nexport interface ExpressResponseLike {\n statusCode: number;\n once(event: \"finish\" | \"close\", listener: () => void): unknown;\n /**\n * Optional — Express's response exposes this for reading headers\n * the framework / middleware chain set. Used by the middleware to\n * surface `responseBytes` on the `request.handled` event. If your\n * adapter doesn't have it, the field is simply omitted.\n */\n getHeader?(name: string): string | string[] | number | undefined;\n}\n\nexport type ExpressNext = (err?: unknown) => void;\n\nexport interface CrossdeckExpressOptions {\n /**\n * Routes to skip. Tested against `req.route?.path` if available, else\n * `req.path` / `req.url`. Defaults to a single self-skip for\n * `/crossdeck/*` so the SDK doesn't emit telemetry about its own\n * health endpoints.\n */\n skipPaths?: Array<string | RegExp>;\n /**\n * Optional identity extractor — runs once per request. Whatever it\n * returns is attached to the `request.handled` event so the\n * dashboard can pivot by user. Typical implementation: read\n * `req.user.id` populated by your auth middleware.\n *\n * crossdeckExpress(server, {\n * getIdentity: (req) => ({ developerUserId: req.user?.id }),\n * })\n */\n getIdentity?: (req: ExpressRequestLike) => {\n developerUserId?: string;\n anonymousId?: string;\n crossdeckCustomerId?: string;\n } | null | undefined;\n /**\n * Attach `{ url, method, route }` as `context.request` on captured\n * errors. Default `true`. Set `false` if you have a separate\n * mechanism for capturing request context (Pino bindings, etc).\n */\n captureErrorsWithRequestContext?: boolean;\n}\n\nconst DEFAULT_SKIP_PATHS: Array<string | RegExp> = [/^\\/crossdeck($|\\/)/];\n\n/**\n * Express middleware that emits `request.handled` per request.\n * Register BEFORE your routes:\n *\n * app.use(crossdeckExpress(server));\n *\n * Behaviour:\n * - Listens on `res.once('finish')` so we capture the FINAL\n * statusCode after any post-route middleware (compression, etc).\n * Also listens on `res.once('close')` to cover client-aborted\n * requests where 'finish' never fires.\n * - Idempotent per request: dispatches once regardless of which\n * terminal event fires first.\n * - `route` property is the matched route PATTERN (`/users/:id`),\n * not the full URL — keeps dashboard cardinality manageable. Falls\n * back to `req.path` when no route matched (404s).\n * - Errors thrown by `getIdentity` are swallowed and the event still\n * ships without identity — telemetry must NEVER break the request\n * pipeline.\n */\nexport function crossdeckExpress(\n server: CrossdeckServer,\n options: CrossdeckExpressOptions = {},\n) {\n const skipPaths = options.skipPaths ?? DEFAULT_SKIP_PATHS;\n\n return function crossdeckExpressMiddleware(\n req: ExpressRequestLike,\n res: ExpressResponseLike,\n next: ExpressNext,\n ): void {\n if (shouldSkipRequest(req, skipPaths)) {\n next();\n return;\n }\n\n const start = Date.now();\n let dispatched = false;\n\n const emit = (): void => {\n if (dispatched) return;\n dispatched = true;\n let identity: ReturnType<NonNullable<CrossdeckExpressOptions[\"getIdentity\"]>> = undefined;\n try {\n identity = options.getIdentity?.(req);\n } catch {\n // identity extraction must never break the request pipeline\n }\n try {\n const props: Record<string, unknown> = {\n route: extractRoutePattern(req),\n method: req.method,\n statusCode: res.statusCode,\n durationMs: Date.now() - start,\n };\n // Defensive: every header read is in a try/swallow because a\n // misbehaving framework middleware may have mutated the\n // response in ways that throw on access.\n try {\n const ua = readHeader(req, \"user-agent\");\n if (ua) props.userAgent = ua;\n } catch {\n // skip\n }\n try {\n const cl = typeof res.getHeader === \"function\" ? res.getHeader(\"content-length\") : undefined;\n if (typeof cl === \"number\") {\n props.responseBytes = cl;\n } else if (typeof cl === \"string\") {\n const parsed = Number(cl);\n if (Number.isFinite(parsed)) props.responseBytes = parsed;\n }\n } catch {\n // skip\n }\n server.track({\n name: \"request.handled\",\n developerUserId: identity?.developerUserId,\n anonymousId: identity?.anonymousId,\n crossdeckCustomerId: identity?.crossdeckCustomerId,\n properties: props,\n });\n } catch {\n // SDK telemetry must never throw out of the response pipeline.\n }\n };\n\n res.once(\"finish\", emit);\n res.once(\"close\", emit);\n\n next();\n };\n}\n\n/**\n * Express error middleware (4-arg signature). Register LAST, after\n * all routes + after the request middleware:\n *\n * app.use(crossdeckExpressErrorHandler(server));\n *\n * Captures the error with request context, then forwards to the next\n * error handler — Crossdeck observes, the framework still produces the\n * normal 500 response.\n *\n * In Express 5 (async handlers natively forward errors), this middleware\n * sees errors from any handler. In Express 4, customers must wrap async\n * route handlers in a `next(err)` adapter — that's not a Crossdeck\n * limitation; it's how Express 4 works.\n */\nexport function crossdeckExpressErrorHandler(\n server: CrossdeckServer,\n options: CrossdeckExpressOptions = {},\n) {\n const attachContext = options.captureErrorsWithRequestContext !== false;\n\n return function crossdeckExpressErrorMiddleware(\n err: unknown,\n req: ExpressRequestLike,\n _res: ExpressResponseLike,\n next: ExpressNext,\n ): void {\n try {\n if (attachContext) {\n server.captureError(err, {\n context: {\n request: {\n url: req.originalUrl ?? req.url,\n method: req.method,\n route: extractRoutePattern(req),\n },\n },\n });\n } else {\n server.captureError(err);\n }\n } catch {\n // SDK observation must not block the framework's error pipeline.\n }\n next(err);\n };\n}\n\n// ---------- helpers (exported for testing) ----------\n\nexport function shouldSkipRequest(\n req: ExpressRequestLike,\n skipPaths: Array<string | RegExp>,\n): boolean {\n const candidates = [req.path, req.url].filter((s): s is string => typeof s === \"string\");\n for (const candidate of candidates) {\n for (const pattern of skipPaths) {\n if (typeof pattern === \"string\" && candidate.startsWith(pattern)) return true;\n if (pattern instanceof RegExp && pattern.test(candidate)) return true;\n }\n }\n return false;\n}\n\nfunction readHeader(req: ExpressRequestLike, name: string): string | undefined {\n if (!req.headers) return undefined;\n const v = req.headers[name];\n if (typeof v === \"string\") return v;\n if (Array.isArray(v)) return v[0];\n return undefined;\n}\n\nexport function extractRoutePattern(req: ExpressRequestLike): string {\n const routePath = req.route?.path;\n if (typeof routePath === \"string\") return routePath;\n if (routePath instanceof RegExp) return routePath.source;\n if (Array.isArray(routePath)) {\n return routePath\n .map((p) => (typeof p === \"string\" ? p : p instanceof RegExp ? p.source : \"\"))\n .filter(Boolean)\n .join(\"|\");\n }\n // Fallback for 404s + middleware-only requests where `req.route` is\n // undefined. Use `req.path` (the URL path without query string)\n // when present, else `req.url` as a last resort.\n return req.path ?? req.url ?? \"<unknown>\";\n}\n","/**\n * AWS Lambda handler wrapper — emits `function.invoked` /\n * `function.completed` / `function.failed` with Lambda lifecycle\n * metadata, and (crucially) `await server.flush()` BEFORE the handler\n * returns.\n *\n * Why flush-before-return is non-optional on Lambda: the runtime\n * freezes the process between invocations. Any event queued but not\n * sent over the wire vanishes — silently — when the function returns.\n * `flush-on-exit` doesn't fire because the process isn't exiting;\n * it's hibernating. Without the wrapper's explicit flush, you'd lose\n * the very telemetry you installed the SDK for.\n *\n * import { wrapLambdaHandler } from \"@cross-deck/node/auto-events\";\n *\n * export const handler = wrapLambdaHandler(server, async (event, ctx) => {\n * // your handler\n * });\n *\n * The wrapper preserves the handler's TypeScript signature via\n * generic parameters so the wrapped handler is type-equivalent to\n * the original.\n *\n * Cold-start detection is per-module-instance: the first invocation\n * gets `coldStart: true`, subsequent invocations of the SAME warm\n * container get `coldStart: false`. AWS spawns multiple containers\n * for concurrent invocations — each container's first invocation is\n * a cold start, so this is a per-container signal, not per-account.\n */\n\nimport type { CrossdeckServer } from \"../crossdeck-server\";\n\n/**\n * Minimal shape of the AWS Lambda invocation context. We don't pull\n * `@types/aws-lambda` as a dependency — that would force every\n * non-Lambda caller to install Lambda types just to import the SDK.\n * The fields we read are the stable subset every Lambda runtime\n * provides.\n */\nexport interface LambdaContextLike {\n awsRequestId?: string;\n functionName?: string;\n functionVersion?: string;\n invokedFunctionArn?: string;\n memoryLimitInMB?: number | string;\n logGroupName?: string;\n logStreamName?: string;\n /** Time remaining in the invocation, in ms. Useful for context. */\n getRemainingTimeInMillis?: () => number;\n}\n\nexport type LambdaHandlerLike<TEvent, TResult> = (\n event: TEvent,\n context: LambdaContextLike,\n) => Promise<TResult> | TResult;\n\nexport interface WrapLambdaOptions {\n /**\n * Override the per-container cold-start flag. Module-level\n * detection is sufficient for production; tests use this to\n * deterministically reset cold-start across runs.\n */\n resetColdStart?: boolean;\n /**\n * Optional identity extractor — read auth context from `event`\n * (e.g. `event.requestContext?.authorizer?.principalId` on an API\n * Gateway invocation) and attach to the emitted events.\n */\n getIdentity?: (event: unknown, context: LambdaContextLike) => {\n developerUserId?: string;\n anonymousId?: string;\n crossdeckCustomerId?: string;\n } | null | undefined;\n}\n\nlet containerColdStart = true;\n\n/**\n * Wrap a Lambda handler. Returns a handler with the same signature.\n *\n * Lifecycle emitted:\n * - `function.invoked` on entry — requestId, functionName, coldStart\n * - `function.completed` on success — durationMs, memoryUsedMb, statusCode\n * - `function.failed` on throw — errorType, errorMessage, durationMs\n *\n * Failures also call `server.captureError(err)` so the error pipeline\n * sees it with `error.handled` shape (frames + fingerprint +\n * breadcrumbs). The thrown error is re-thrown after capture so Lambda\n * itself still sees the failure and reports it to CloudWatch.\n *\n * `await server.flush()` runs in the `finally` block of every\n * invocation — bounded best-effort, so a transient backend outage\n * doesn't keep the function alive past the platform's SIGKILL.\n */\nexport function wrapLambdaHandler<TEvent, TResult>(\n server: CrossdeckServer,\n handler: LambdaHandlerLike<TEvent, TResult>,\n options: WrapLambdaOptions = {},\n): LambdaHandlerLike<TEvent, TResult> {\n if (options.resetColdStart === true) containerColdStart = true;\n\n return async function wrappedLambdaHandler(event, context): Promise<TResult> {\n const start = Date.now();\n const coldStart = containerColdStart;\n containerColdStart = false;\n const identity = safeExtractIdentity(options.getIdentity, event, context);\n\n server.track({\n name: \"function.invoked\",\n developerUserId: identity?.developerUserId,\n anonymousId: identity?.anonymousId,\n crossdeckCustomerId: identity?.crossdeckCustomerId,\n properties: {\n runtime: \"aws-lambda\",\n requestId: context.awsRequestId,\n functionName: context.functionName,\n functionVersion: context.functionVersion,\n coldStart,\n memoryLimitMb: numericOrUndefined(context.memoryLimitInMB),\n remainingMs: safeRemainingMs(context),\n },\n });\n\n try {\n const result = await handler(event, context);\n const completedProps: Record<string, unknown> = {\n runtime: \"aws-lambda\",\n requestId: context.awsRequestId,\n functionName: context.functionName,\n durationMs: Date.now() - start,\n memoryUsedMb: rssMb(),\n };\n // API Gateway / Function URL responses are\n // `{ statusCode, body, headers? }`. When the handler returns\n // that shape, surface statusCode + body size on the completed\n // event so the dashboard can pivot by HTTP outcome. Non-HTTP\n // handlers (queue / cron) return arbitrary shapes; we silently\n // skip those keys.\n if (isHttpStyleResponse(result)) {\n completedProps.statusCode = result.statusCode;\n if (typeof result.body === \"string\") {\n completedProps.responseBytes = Buffer.byteLength(result.body, \"utf8\");\n }\n }\n server.track({\n name: \"function.completed\",\n developerUserId: identity?.developerUserId,\n anonymousId: identity?.anonymousId,\n crossdeckCustomerId: identity?.crossdeckCustomerId,\n properties: completedProps,\n });\n return result;\n } catch (err) {\n try {\n server.captureError(err, {\n context: {\n lambda: {\n requestId: context.awsRequestId,\n functionName: context.functionName,\n functionVersion: context.functionVersion,\n },\n },\n });\n } catch {\n // self-protection — error capture must never block re-throw\n }\n try {\n server.track({\n name: \"function.failed\",\n developerUserId: identity?.developerUserId,\n anonymousId: identity?.anonymousId,\n crossdeckCustomerId: identity?.crossdeckCustomerId,\n properties: {\n runtime: \"aws-lambda\",\n requestId: context.awsRequestId,\n functionName: context.functionName,\n errorType: err instanceof Error ? err.name : null,\n errorMessage: err instanceof Error ? err.message : String(err),\n durationMs: Date.now() - start,\n },\n });\n } catch {\n // swallow — same self-protection\n }\n throw err;\n } finally {\n // CRITICAL — Lambda freezes the process between invocations.\n // Without this, queued events vanish silently the moment the\n // handler returns. `flush()` is best-effort + bounded by the\n // queue's own timeout policy.\n try {\n await server.flush();\n } catch {\n // Flush failure is observable via diagnostics.events.lastError.\n }\n }\n };\n}\n\nfunction safeExtractIdentity(\n extractor: WrapLambdaOptions[\"getIdentity\"] | undefined,\n event: unknown,\n context: LambdaContextLike,\n): ReturnType<NonNullable<WrapLambdaOptions[\"getIdentity\"]>> {\n if (!extractor) return undefined;\n try {\n return extractor(event, context);\n } catch {\n return undefined;\n }\n}\n\nfunction safeRemainingMs(context: LambdaContextLike): number | undefined {\n try {\n return typeof context.getRemainingTimeInMillis === \"function\"\n ? context.getRemainingTimeInMillis()\n : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction numericOrUndefined(value: number | string | undefined): number | undefined {\n if (typeof value === \"number\") return value;\n if (typeof value === \"string\") {\n const n = Number(value);\n return Number.isFinite(n) ? n : undefined;\n }\n return undefined;\n}\n\nfunction rssMb(): number {\n try {\n return Math.round(process.memoryUsage().rss / 1024 / 1024);\n } catch {\n return 0;\n }\n}\n\n/**\n * Duck-type detection for API Gateway / Function URL / ALB-style\n * Lambda responses. Real Lambda handlers can return ANY shape — only\n * HTTP-style returns expose statusCode + body. We don't import\n * `@types/aws-lambda` to avoid the dep cost; this duck-type is good\n * enough.\n */\nfunction isHttpStyleResponse(\n value: unknown,\n): value is { statusCode: number; body?: string; headers?: Record<string, string> } {\n return Boolean(\n value &&\n typeof value === \"object\" &&\n typeof (value as { statusCode?: unknown }).statusCode === \"number\",\n );\n}\n","/**\n * Firebase Cloud Functions wrapper — generic across v1 + v2, also\n * usable for Google Cloud Run Functions and Cloud Run services\n * (anything that exposes a Node handler and freezes / tears down\n * between invocations).\n *\n * Why generic: Firebase has many handler signatures across v1 and v2:\n * v1 https.onRequest: (req, res) => void\n * v1 https.onCall: (data, context) => Promise<result>\n * v1 firestore.onWrite: (snapshot, context) => Promise<void>\n * v1 pubsub.onPublish: (message, context) => Promise<void>\n * v2 onRequest: (req, res) => Promise<void>\n * v2 onCall: (request) => Promise<result>\n * v2 onDocumentWritten: (event) => Promise<void>\n *\n * Rather than ship one wrapper per signature (which would force a\n * dependency on `firebase-functions` types and break when Google\n * adds new triggers), this wrapper is **shape-preserving** — it\n * accepts ANY function and returns one with the same signature.\n * Lifecycle telemetry is emitted around the call; metadata extraction\n * is plug-in via `getMetadata`.\n *\n * import { wrapFunction } from \"@cross-deck/node/auto-events\";\n * import { onRequest } from \"firebase-functions/v2/https\";\n *\n * export const myFunction = onRequest(wrapFunction(server, async (req, res) => {\n * // your handler\n * }));\n *\n * Cold-start detection: same per-container logic as Lambda. The\n * first invocation of a fresh container is a cold start; subsequent\n * invocations of the same warm container are not.\n *\n * Flush-before-return: same critical contract as Lambda. Firebase\n * tears down idle containers; queued events vanish if the SDK doesn't\n * flush before the handler returns.\n */\n\nimport type { CrossdeckServer } from \"../crossdeck-server\";\n\nexport interface WrapFunctionOptions {\n /**\n * Override the per-container cold-start flag. Module-level\n * detection is sufficient for production; tests use this to\n * deterministically reset cold-start across runs.\n */\n resetColdStart?: boolean;\n /**\n * Optional metadata extractor — read trigger-specific fields off\n * the handler arguments and attach them to the emitted events.\n * Default: no extra metadata.\n *\n * Return `{ identity, properties }` so identity hints route onto\n * the event envelope (for dashboard pivot) and properties merge\n * into the event's `properties` bag:\n *\n * wrapFunction(server, handler, {\n * getMetadata: (args) => ({\n * identity: { developerUserId: args[0].auth?.uid },\n * properties: { docPath: args[0].ref?.path, region: \"us-central1\" },\n * }),\n * })\n */\n getMetadata?: (args: unknown[]) => WrapFunctionMetadata | null | undefined;\n /**\n * Label for the `runtime` event property. Defaults to\n * `\"firebase-functions\"`. Override to distinguish triggers in\n * dashboards (e.g. `\"firebase-https\"` vs `\"firebase-firestore\"`).\n */\n runtime?: string;\n}\n\nexport interface WrapFunctionMetadata {\n /** Identity hint attached to the event envelope (developerUserId / anonymousId / crossdeckCustomerId). */\n identity?: {\n developerUserId?: string;\n anonymousId?: string;\n crossdeckCustomerId?: string;\n };\n /** Additional properties merged into emitted event properties. */\n properties?: Record<string, unknown>;\n}\n\nlet containerColdStart = true;\n\n/**\n * Shape-preserving wrap for a Firebase / Cloud Run handler. Returns\n * a function with the SAME signature as the input.\n *\n * Lifecycle emitted:\n * - `function.invoked` on entry — runtime, coldStart, ...metadata\n * - `function.completed` on success — durationMs, memoryUsedMb\n * - `function.failed` on throw — errorType, errorMessage, durationMs\n *\n * Failures also call `server.captureError(err)`. Errors are re-thrown\n * so Firebase still sees the failure and reports it to Cloud Logging.\n *\n * `await server.flush()` runs in `finally` — same as Lambda. Firebase\n * containers freeze / tear down between invocations.\n */\nexport function wrapFunction<TArgs extends unknown[], TResult>(\n server: CrossdeckServer,\n handler: (...args: TArgs) => Promise<TResult> | TResult,\n options: WrapFunctionOptions = {},\n): (...args: TArgs) => Promise<TResult> {\n if (options.resetColdStart === true) containerColdStart = true;\n const runtime = options.runtime ?? \"firebase-functions\";\n\n return async function wrappedFirebaseHandler(...args: TArgs): Promise<TResult> {\n const start = Date.now();\n const coldStart = containerColdStart;\n containerColdStart = false;\n const metadata = safeExtractMetadata(options.getMetadata, args);\n const identity = metadata?.identity ?? {};\n const extraProps = metadata?.properties ?? {};\n\n server.track({\n name: \"function.invoked\",\n developerUserId: identity.developerUserId,\n anonymousId: identity.anonymousId,\n crossdeckCustomerId: identity.crossdeckCustomerId,\n properties: {\n runtime,\n coldStart,\n ...extraProps,\n },\n });\n\n try {\n const result = await handler(...args);\n server.track({\n name: \"function.completed\",\n developerUserId: identity.developerUserId,\n anonymousId: identity.anonymousId,\n crossdeckCustomerId: identity.crossdeckCustomerId,\n properties: {\n runtime,\n durationMs: Date.now() - start,\n memoryUsedMb: rssMb(),\n ...extraProps,\n },\n });\n return result;\n } catch (err) {\n try {\n server.captureError(err, {\n context: { firebase: extraProps },\n });\n } catch {\n // self-protection — error capture must never block re-throw\n }\n try {\n server.track({\n name: \"function.failed\",\n developerUserId: identity.developerUserId,\n anonymousId: identity.anonymousId,\n crossdeckCustomerId: identity.crossdeckCustomerId,\n properties: {\n runtime,\n errorType: err instanceof Error ? err.name : null,\n errorMessage: err instanceof Error ? err.message : String(err),\n durationMs: Date.now() - start,\n ...extraProps,\n },\n });\n } catch {\n // swallow — same self-protection\n }\n throw err;\n } finally {\n try {\n await server.flush();\n } catch {\n // Flush failure is observable via diagnostics.events.lastError.\n }\n }\n };\n}\n\nfunction safeExtractMetadata(\n extractor: WrapFunctionOptions[\"getMetadata\"] | undefined,\n args: unknown[],\n): WrapFunctionMetadata | undefined {\n if (!extractor) return undefined;\n try {\n const out = extractor(args);\n return out ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction rssMb(): number {\n try {\n return Math.round(process.memoryUsage().rss / 1024 / 1024);\n } catch {\n return 0;\n }\n}\n"],"mappings":"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","names":["containerColdStart","rssMb"]}