@cross-deck/node 0.1.0 → 1.1.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,316 @@
1
+ import { p as CrossdeckServer } from '../crossdeck-server-BXQaFjVx.mjs';
2
+ import 'node:events';
3
+
4
+ /**
5
+ * Express auto-events — `request.handled` middleware + uncaught-route
6
+ * error capture.
7
+ *
8
+ * Two middleware factories, registered separately because Express
9
+ * differentiates them by arity:
10
+ *
11
+ * import { crossdeckExpress, crossdeckExpressErrorHandler } from
12
+ * "@cross-deck/node/auto-events";
13
+ *
14
+ * app.use(crossdeckExpress(server)); // request middleware
15
+ * app.use(routes); // your routes
16
+ * app.use(crossdeckExpressErrorHandler(server)); // LAST — error middleware
17
+ *
18
+ * `crossdeckExpress` emits `request.handled` on response 'finish'
19
+ * with the matched route pattern (not the full URL — high-cardinality
20
+ * URL paths kill dashboards), method, statusCode, and durationMs.
21
+ *
22
+ * `crossdeckExpressErrorHandler` catches errors thrown in route
23
+ * handlers (sync OR async — Express 5 supports async handlers
24
+ * natively; Express 4 needs the caller to forward via `next(err)`).
25
+ * The error is shipped with request context (url, method, matched
26
+ * route) attached so the dashboard can group by route.
27
+ *
28
+ * Compatible with both Express 4 and Express 5. The middleware
29
+ * signatures are stable across both versions.
30
+ *
31
+ * No `import` from `express`. The adapter speaks shape-only against
32
+ * Express's request / response objects — customers don't pay a
33
+ * forced dependency on Express just to install the Crossdeck SDK.
34
+ * If `express` is missing at install, this module still compiles.
35
+ */
36
+
37
+ /**
38
+ * Shape of an Express request object — enough fields for the
39
+ * middleware to do its job without depending on Express's types.
40
+ */
41
+ interface ExpressRequestLike {
42
+ method: string;
43
+ url: string;
44
+ path?: string;
45
+ route?: {
46
+ path?: string | RegExp | Array<string | RegExp>;
47
+ };
48
+ originalUrl?: string;
49
+ headers?: Record<string, string | string[] | undefined>;
50
+ }
51
+ /** Shape of an Express response object. */
52
+ interface ExpressResponseLike {
53
+ statusCode: number;
54
+ once(event: "finish" | "close", listener: () => void): unknown;
55
+ /**
56
+ * Optional — Express's response exposes this for reading headers
57
+ * the framework / middleware chain set. Used by the middleware to
58
+ * surface `responseBytes` on the `request.handled` event. If your
59
+ * adapter doesn't have it, the field is simply omitted.
60
+ */
61
+ getHeader?(name: string): string | string[] | number | undefined;
62
+ }
63
+ type ExpressNext = (err?: unknown) => void;
64
+ interface CrossdeckExpressOptions {
65
+ /**
66
+ * Routes to skip. Tested against `req.route?.path` if available, else
67
+ * `req.path` / `req.url`. Defaults to a single self-skip for
68
+ * `/crossdeck/*` so the SDK doesn't emit telemetry about its own
69
+ * health endpoints.
70
+ */
71
+ skipPaths?: Array<string | RegExp>;
72
+ /**
73
+ * Optional identity extractor — runs once per request. Whatever it
74
+ * returns is attached to the `request.handled` event so the
75
+ * dashboard can pivot by user. Typical implementation: read
76
+ * `req.user.id` populated by your auth middleware.
77
+ *
78
+ * crossdeckExpress(server, {
79
+ * getIdentity: (req) => ({ developerUserId: req.user?.id }),
80
+ * })
81
+ */
82
+ getIdentity?: (req: ExpressRequestLike) => {
83
+ developerUserId?: string;
84
+ anonymousId?: string;
85
+ crossdeckCustomerId?: string;
86
+ } | null | undefined;
87
+ /**
88
+ * Attach `{ url, method, route }` as `context.request` on captured
89
+ * errors. Default `true`. Set `false` if you have a separate
90
+ * mechanism for capturing request context (Pino bindings, etc).
91
+ */
92
+ captureErrorsWithRequestContext?: boolean;
93
+ }
94
+ /**
95
+ * Express middleware that emits `request.handled` per request.
96
+ * Register BEFORE your routes:
97
+ *
98
+ * app.use(crossdeckExpress(server));
99
+ *
100
+ * Behaviour:
101
+ * - Listens on `res.once('finish')` so we capture the FINAL
102
+ * statusCode after any post-route middleware (compression, etc).
103
+ * Also listens on `res.once('close')` to cover client-aborted
104
+ * requests where 'finish' never fires.
105
+ * - Idempotent per request: dispatches once regardless of which
106
+ * terminal event fires first.
107
+ * - `route` property is the matched route PATTERN (`/users/:id`),
108
+ * not the full URL — keeps dashboard cardinality manageable. Falls
109
+ * back to `req.path` when no route matched (404s).
110
+ * - Errors thrown by `getIdentity` are swallowed and the event still
111
+ * ships without identity — telemetry must NEVER break the request
112
+ * pipeline.
113
+ */
114
+ declare function crossdeckExpress(server: CrossdeckServer, options?: CrossdeckExpressOptions): (req: ExpressRequestLike, res: ExpressResponseLike, next: ExpressNext) => void;
115
+ /**
116
+ * Express error middleware (4-arg signature). Register LAST, after
117
+ * all routes + after the request middleware:
118
+ *
119
+ * app.use(crossdeckExpressErrorHandler(server));
120
+ *
121
+ * Captures the error with request context, then forwards to the next
122
+ * error handler — Crossdeck observes, the framework still produces the
123
+ * normal 500 response.
124
+ *
125
+ * In Express 5 (async handlers natively forward errors), this middleware
126
+ * sees errors from any handler. In Express 4, customers must wrap async
127
+ * route handlers in a `next(err)` adapter — that's not a Crossdeck
128
+ * limitation; it's how Express 4 works.
129
+ */
130
+ declare function crossdeckExpressErrorHandler(server: CrossdeckServer, options?: CrossdeckExpressOptions): (err: unknown, req: ExpressRequestLike, _res: ExpressResponseLike, next: ExpressNext) => void;
131
+ declare function shouldSkipRequest(req: ExpressRequestLike, skipPaths: Array<string | RegExp>): boolean;
132
+ declare function extractRoutePattern(req: ExpressRequestLike): string;
133
+
134
+ /**
135
+ * AWS Lambda handler wrapper — emits `function.invoked` /
136
+ * `function.completed` / `function.failed` with Lambda lifecycle
137
+ * metadata, and (crucially) `await server.flush()` BEFORE the handler
138
+ * returns.
139
+ *
140
+ * Why flush-before-return is non-optional on Lambda: the runtime
141
+ * freezes the process between invocations. Any event queued but not
142
+ * sent over the wire vanishes — silently — when the function returns.
143
+ * `flush-on-exit` doesn't fire because the process isn't exiting;
144
+ * it's hibernating. Without the wrapper's explicit flush, you'd lose
145
+ * the very telemetry you installed the SDK for.
146
+ *
147
+ * import { wrapLambdaHandler } from "@cross-deck/node/auto-events";
148
+ *
149
+ * export const handler = wrapLambdaHandler(server, async (event, ctx) => {
150
+ * // your handler
151
+ * });
152
+ *
153
+ * The wrapper preserves the handler's TypeScript signature via
154
+ * generic parameters so the wrapped handler is type-equivalent to
155
+ * the original.
156
+ *
157
+ * Cold-start detection is per-module-instance: the first invocation
158
+ * gets `coldStart: true`, subsequent invocations of the SAME warm
159
+ * container get `coldStart: false`. AWS spawns multiple containers
160
+ * for concurrent invocations — each container's first invocation is
161
+ * a cold start, so this is a per-container signal, not per-account.
162
+ */
163
+
164
+ /**
165
+ * Minimal shape of the AWS Lambda invocation context. We don't pull
166
+ * `@types/aws-lambda` as a dependency — that would force every
167
+ * non-Lambda caller to install Lambda types just to import the SDK.
168
+ * The fields we read are the stable subset every Lambda runtime
169
+ * provides.
170
+ */
171
+ interface LambdaContextLike {
172
+ awsRequestId?: string;
173
+ functionName?: string;
174
+ functionVersion?: string;
175
+ invokedFunctionArn?: string;
176
+ memoryLimitInMB?: number | string;
177
+ logGroupName?: string;
178
+ logStreamName?: string;
179
+ /** Time remaining in the invocation, in ms. Useful for context. */
180
+ getRemainingTimeInMillis?: () => number;
181
+ }
182
+ type LambdaHandlerLike<TEvent, TResult> = (event: TEvent, context: LambdaContextLike) => Promise<TResult> | TResult;
183
+ interface WrapLambdaOptions {
184
+ /**
185
+ * Override the per-container cold-start flag. Module-level
186
+ * detection is sufficient for production; tests use this to
187
+ * deterministically reset cold-start across runs.
188
+ */
189
+ resetColdStart?: boolean;
190
+ /**
191
+ * Optional identity extractor — read auth context from `event`
192
+ * (e.g. `event.requestContext?.authorizer?.principalId` on an API
193
+ * Gateway invocation) and attach to the emitted events.
194
+ */
195
+ getIdentity?: (event: unknown, context: LambdaContextLike) => {
196
+ developerUserId?: string;
197
+ anonymousId?: string;
198
+ crossdeckCustomerId?: string;
199
+ } | null | undefined;
200
+ }
201
+ /**
202
+ * Wrap a Lambda handler. Returns a handler with the same signature.
203
+ *
204
+ * Lifecycle emitted:
205
+ * - `function.invoked` on entry — requestId, functionName, coldStart
206
+ * - `function.completed` on success — durationMs, memoryUsedMb, statusCode
207
+ * - `function.failed` on throw — errorType, errorMessage, durationMs
208
+ *
209
+ * Failures also call `server.captureError(err)` so the error pipeline
210
+ * sees it with `error.handled` shape (frames + fingerprint +
211
+ * breadcrumbs). The thrown error is re-thrown after capture so Lambda
212
+ * itself still sees the failure and reports it to CloudWatch.
213
+ *
214
+ * `await server.flush()` runs in the `finally` block of every
215
+ * invocation — bounded best-effort, so a transient backend outage
216
+ * doesn't keep the function alive past the platform's SIGKILL.
217
+ */
218
+ declare function wrapLambdaHandler<TEvent, TResult>(server: CrossdeckServer, handler: LambdaHandlerLike<TEvent, TResult>, options?: WrapLambdaOptions): LambdaHandlerLike<TEvent, TResult>;
219
+
220
+ /**
221
+ * Firebase Cloud Functions wrapper — generic across v1 + v2, also
222
+ * usable for Google Cloud Run Functions and Cloud Run services
223
+ * (anything that exposes a Node handler and freezes / tears down
224
+ * between invocations).
225
+ *
226
+ * Why generic: Firebase has many handler signatures across v1 and v2:
227
+ * v1 https.onRequest: (req, res) => void
228
+ * v1 https.onCall: (data, context) => Promise<result>
229
+ * v1 firestore.onWrite: (snapshot, context) => Promise<void>
230
+ * v1 pubsub.onPublish: (message, context) => Promise<void>
231
+ * v2 onRequest: (req, res) => Promise<void>
232
+ * v2 onCall: (request) => Promise<result>
233
+ * v2 onDocumentWritten: (event) => Promise<void>
234
+ *
235
+ * Rather than ship one wrapper per signature (which would force a
236
+ * dependency on `firebase-functions` types and break when Google
237
+ * adds new triggers), this wrapper is **shape-preserving** — it
238
+ * accepts ANY function and returns one with the same signature.
239
+ * Lifecycle telemetry is emitted around the call; metadata extraction
240
+ * is plug-in via `getMetadata`.
241
+ *
242
+ * import { wrapFunction } from "@cross-deck/node/auto-events";
243
+ * import { onRequest } from "firebase-functions/v2/https";
244
+ *
245
+ * export const myFunction = onRequest(wrapFunction(server, async (req, res) => {
246
+ * // your handler
247
+ * }));
248
+ *
249
+ * Cold-start detection: same per-container logic as Lambda. The
250
+ * first invocation of a fresh container is a cold start; subsequent
251
+ * invocations of the same warm container are not.
252
+ *
253
+ * Flush-before-return: same critical contract as Lambda. Firebase
254
+ * tears down idle containers; queued events vanish if the SDK doesn't
255
+ * flush before the handler returns.
256
+ */
257
+
258
+ interface WrapFunctionOptions {
259
+ /**
260
+ * Override the per-container cold-start flag. Module-level
261
+ * detection is sufficient for production; tests use this to
262
+ * deterministically reset cold-start across runs.
263
+ */
264
+ resetColdStart?: boolean;
265
+ /**
266
+ * Optional metadata extractor — read trigger-specific fields off
267
+ * the handler arguments and attach them to the emitted events.
268
+ * Default: no extra metadata.
269
+ *
270
+ * Return `{ identity, properties }` so identity hints route onto
271
+ * the event envelope (for dashboard pivot) and properties merge
272
+ * into the event's `properties` bag:
273
+ *
274
+ * wrapFunction(server, handler, {
275
+ * getMetadata: (args) => ({
276
+ * identity: { developerUserId: args[0].auth?.uid },
277
+ * properties: { docPath: args[0].ref?.path, region: "us-central1" },
278
+ * }),
279
+ * })
280
+ */
281
+ getMetadata?: (args: unknown[]) => WrapFunctionMetadata | null | undefined;
282
+ /**
283
+ * Label for the `runtime` event property. Defaults to
284
+ * `"firebase-functions"`. Override to distinguish triggers in
285
+ * dashboards (e.g. `"firebase-https"` vs `"firebase-firestore"`).
286
+ */
287
+ runtime?: string;
288
+ }
289
+ interface WrapFunctionMetadata {
290
+ /** Identity hint attached to the event envelope (developerUserId / anonymousId / crossdeckCustomerId). */
291
+ identity?: {
292
+ developerUserId?: string;
293
+ anonymousId?: string;
294
+ crossdeckCustomerId?: string;
295
+ };
296
+ /** Additional properties merged into emitted event properties. */
297
+ properties?: Record<string, unknown>;
298
+ }
299
+ /**
300
+ * Shape-preserving wrap for a Firebase / Cloud Run handler. Returns
301
+ * a function with the SAME signature as the input.
302
+ *
303
+ * Lifecycle emitted:
304
+ * - `function.invoked` on entry — runtime, coldStart, ...metadata
305
+ * - `function.completed` on success — durationMs, memoryUsedMb
306
+ * - `function.failed` on throw — errorType, errorMessage, durationMs
307
+ *
308
+ * Failures also call `server.captureError(err)`. Errors are re-thrown
309
+ * so Firebase still sees the failure and reports it to Cloud Logging.
310
+ *
311
+ * `await server.flush()` runs in `finally` — same as Lambda. Firebase
312
+ * containers freeze / tear down between invocations.
313
+ */
314
+ declare function wrapFunction<TArgs extends unknown[], TResult>(server: CrossdeckServer, handler: (...args: TArgs) => Promise<TResult> | TResult, options?: WrapFunctionOptions): (...args: TArgs) => Promise<TResult>;
315
+
316
+ export { type CrossdeckExpressOptions, type ExpressNext, type ExpressRequestLike, type ExpressResponseLike, type LambdaContextLike, type LambdaHandlerLike, type WrapFunctionMetadata, type WrapFunctionOptions, type WrapLambdaOptions, crossdeckExpress, crossdeckExpressErrorHandler, extractRoutePattern, shouldSkipRequest, wrapFunction, wrapLambdaHandler };
@@ -0,0 +1,316 @@
1
+ import { p as CrossdeckServer } from '../crossdeck-server-BXQaFjVx.js';
2
+ import 'node:events';
3
+
4
+ /**
5
+ * Express auto-events — `request.handled` middleware + uncaught-route
6
+ * error capture.
7
+ *
8
+ * Two middleware factories, registered separately because Express
9
+ * differentiates them by arity:
10
+ *
11
+ * import { crossdeckExpress, crossdeckExpressErrorHandler } from
12
+ * "@cross-deck/node/auto-events";
13
+ *
14
+ * app.use(crossdeckExpress(server)); // request middleware
15
+ * app.use(routes); // your routes
16
+ * app.use(crossdeckExpressErrorHandler(server)); // LAST — error middleware
17
+ *
18
+ * `crossdeckExpress` emits `request.handled` on response 'finish'
19
+ * with the matched route pattern (not the full URL — high-cardinality
20
+ * URL paths kill dashboards), method, statusCode, and durationMs.
21
+ *
22
+ * `crossdeckExpressErrorHandler` catches errors thrown in route
23
+ * handlers (sync OR async — Express 5 supports async handlers
24
+ * natively; Express 4 needs the caller to forward via `next(err)`).
25
+ * The error is shipped with request context (url, method, matched
26
+ * route) attached so the dashboard can group by route.
27
+ *
28
+ * Compatible with both Express 4 and Express 5. The middleware
29
+ * signatures are stable across both versions.
30
+ *
31
+ * No `import` from `express`. The adapter speaks shape-only against
32
+ * Express's request / response objects — customers don't pay a
33
+ * forced dependency on Express just to install the Crossdeck SDK.
34
+ * If `express` is missing at install, this module still compiles.
35
+ */
36
+
37
+ /**
38
+ * Shape of an Express request object — enough fields for the
39
+ * middleware to do its job without depending on Express's types.
40
+ */
41
+ interface ExpressRequestLike {
42
+ method: string;
43
+ url: string;
44
+ path?: string;
45
+ route?: {
46
+ path?: string | RegExp | Array<string | RegExp>;
47
+ };
48
+ originalUrl?: string;
49
+ headers?: Record<string, string | string[] | undefined>;
50
+ }
51
+ /** Shape of an Express response object. */
52
+ interface ExpressResponseLike {
53
+ statusCode: number;
54
+ once(event: "finish" | "close", listener: () => void): unknown;
55
+ /**
56
+ * Optional — Express's response exposes this for reading headers
57
+ * the framework / middleware chain set. Used by the middleware to
58
+ * surface `responseBytes` on the `request.handled` event. If your
59
+ * adapter doesn't have it, the field is simply omitted.
60
+ */
61
+ getHeader?(name: string): string | string[] | number | undefined;
62
+ }
63
+ type ExpressNext = (err?: unknown) => void;
64
+ interface CrossdeckExpressOptions {
65
+ /**
66
+ * Routes to skip. Tested against `req.route?.path` if available, else
67
+ * `req.path` / `req.url`. Defaults to a single self-skip for
68
+ * `/crossdeck/*` so the SDK doesn't emit telemetry about its own
69
+ * health endpoints.
70
+ */
71
+ skipPaths?: Array<string | RegExp>;
72
+ /**
73
+ * Optional identity extractor — runs once per request. Whatever it
74
+ * returns is attached to the `request.handled` event so the
75
+ * dashboard can pivot by user. Typical implementation: read
76
+ * `req.user.id` populated by your auth middleware.
77
+ *
78
+ * crossdeckExpress(server, {
79
+ * getIdentity: (req) => ({ developerUserId: req.user?.id }),
80
+ * })
81
+ */
82
+ getIdentity?: (req: ExpressRequestLike) => {
83
+ developerUserId?: string;
84
+ anonymousId?: string;
85
+ crossdeckCustomerId?: string;
86
+ } | null | undefined;
87
+ /**
88
+ * Attach `{ url, method, route }` as `context.request` on captured
89
+ * errors. Default `true`. Set `false` if you have a separate
90
+ * mechanism for capturing request context (Pino bindings, etc).
91
+ */
92
+ captureErrorsWithRequestContext?: boolean;
93
+ }
94
+ /**
95
+ * Express middleware that emits `request.handled` per request.
96
+ * Register BEFORE your routes:
97
+ *
98
+ * app.use(crossdeckExpress(server));
99
+ *
100
+ * Behaviour:
101
+ * - Listens on `res.once('finish')` so we capture the FINAL
102
+ * statusCode after any post-route middleware (compression, etc).
103
+ * Also listens on `res.once('close')` to cover client-aborted
104
+ * requests where 'finish' never fires.
105
+ * - Idempotent per request: dispatches once regardless of which
106
+ * terminal event fires first.
107
+ * - `route` property is the matched route PATTERN (`/users/:id`),
108
+ * not the full URL — keeps dashboard cardinality manageable. Falls
109
+ * back to `req.path` when no route matched (404s).
110
+ * - Errors thrown by `getIdentity` are swallowed and the event still
111
+ * ships without identity — telemetry must NEVER break the request
112
+ * pipeline.
113
+ */
114
+ declare function crossdeckExpress(server: CrossdeckServer, options?: CrossdeckExpressOptions): (req: ExpressRequestLike, res: ExpressResponseLike, next: ExpressNext) => void;
115
+ /**
116
+ * Express error middleware (4-arg signature). Register LAST, after
117
+ * all routes + after the request middleware:
118
+ *
119
+ * app.use(crossdeckExpressErrorHandler(server));
120
+ *
121
+ * Captures the error with request context, then forwards to the next
122
+ * error handler — Crossdeck observes, the framework still produces the
123
+ * normal 500 response.
124
+ *
125
+ * In Express 5 (async handlers natively forward errors), this middleware
126
+ * sees errors from any handler. In Express 4, customers must wrap async
127
+ * route handlers in a `next(err)` adapter — that's not a Crossdeck
128
+ * limitation; it's how Express 4 works.
129
+ */
130
+ declare function crossdeckExpressErrorHandler(server: CrossdeckServer, options?: CrossdeckExpressOptions): (err: unknown, req: ExpressRequestLike, _res: ExpressResponseLike, next: ExpressNext) => void;
131
+ declare function shouldSkipRequest(req: ExpressRequestLike, skipPaths: Array<string | RegExp>): boolean;
132
+ declare function extractRoutePattern(req: ExpressRequestLike): string;
133
+
134
+ /**
135
+ * AWS Lambda handler wrapper — emits `function.invoked` /
136
+ * `function.completed` / `function.failed` with Lambda lifecycle
137
+ * metadata, and (crucially) `await server.flush()` BEFORE the handler
138
+ * returns.
139
+ *
140
+ * Why flush-before-return is non-optional on Lambda: the runtime
141
+ * freezes the process between invocations. Any event queued but not
142
+ * sent over the wire vanishes — silently — when the function returns.
143
+ * `flush-on-exit` doesn't fire because the process isn't exiting;
144
+ * it's hibernating. Without the wrapper's explicit flush, you'd lose
145
+ * the very telemetry you installed the SDK for.
146
+ *
147
+ * import { wrapLambdaHandler } from "@cross-deck/node/auto-events";
148
+ *
149
+ * export const handler = wrapLambdaHandler(server, async (event, ctx) => {
150
+ * // your handler
151
+ * });
152
+ *
153
+ * The wrapper preserves the handler's TypeScript signature via
154
+ * generic parameters so the wrapped handler is type-equivalent to
155
+ * the original.
156
+ *
157
+ * Cold-start detection is per-module-instance: the first invocation
158
+ * gets `coldStart: true`, subsequent invocations of the SAME warm
159
+ * container get `coldStart: false`. AWS spawns multiple containers
160
+ * for concurrent invocations — each container's first invocation is
161
+ * a cold start, so this is a per-container signal, not per-account.
162
+ */
163
+
164
+ /**
165
+ * Minimal shape of the AWS Lambda invocation context. We don't pull
166
+ * `@types/aws-lambda` as a dependency — that would force every
167
+ * non-Lambda caller to install Lambda types just to import the SDK.
168
+ * The fields we read are the stable subset every Lambda runtime
169
+ * provides.
170
+ */
171
+ interface LambdaContextLike {
172
+ awsRequestId?: string;
173
+ functionName?: string;
174
+ functionVersion?: string;
175
+ invokedFunctionArn?: string;
176
+ memoryLimitInMB?: number | string;
177
+ logGroupName?: string;
178
+ logStreamName?: string;
179
+ /** Time remaining in the invocation, in ms. Useful for context. */
180
+ getRemainingTimeInMillis?: () => number;
181
+ }
182
+ type LambdaHandlerLike<TEvent, TResult> = (event: TEvent, context: LambdaContextLike) => Promise<TResult> | TResult;
183
+ interface WrapLambdaOptions {
184
+ /**
185
+ * Override the per-container cold-start flag. Module-level
186
+ * detection is sufficient for production; tests use this to
187
+ * deterministically reset cold-start across runs.
188
+ */
189
+ resetColdStart?: boolean;
190
+ /**
191
+ * Optional identity extractor — read auth context from `event`
192
+ * (e.g. `event.requestContext?.authorizer?.principalId` on an API
193
+ * Gateway invocation) and attach to the emitted events.
194
+ */
195
+ getIdentity?: (event: unknown, context: LambdaContextLike) => {
196
+ developerUserId?: string;
197
+ anonymousId?: string;
198
+ crossdeckCustomerId?: string;
199
+ } | null | undefined;
200
+ }
201
+ /**
202
+ * Wrap a Lambda handler. Returns a handler with the same signature.
203
+ *
204
+ * Lifecycle emitted:
205
+ * - `function.invoked` on entry — requestId, functionName, coldStart
206
+ * - `function.completed` on success — durationMs, memoryUsedMb, statusCode
207
+ * - `function.failed` on throw — errorType, errorMessage, durationMs
208
+ *
209
+ * Failures also call `server.captureError(err)` so the error pipeline
210
+ * sees it with `error.handled` shape (frames + fingerprint +
211
+ * breadcrumbs). The thrown error is re-thrown after capture so Lambda
212
+ * itself still sees the failure and reports it to CloudWatch.
213
+ *
214
+ * `await server.flush()` runs in the `finally` block of every
215
+ * invocation — bounded best-effort, so a transient backend outage
216
+ * doesn't keep the function alive past the platform's SIGKILL.
217
+ */
218
+ declare function wrapLambdaHandler<TEvent, TResult>(server: CrossdeckServer, handler: LambdaHandlerLike<TEvent, TResult>, options?: WrapLambdaOptions): LambdaHandlerLike<TEvent, TResult>;
219
+
220
+ /**
221
+ * Firebase Cloud Functions wrapper — generic across v1 + v2, also
222
+ * usable for Google Cloud Run Functions and Cloud Run services
223
+ * (anything that exposes a Node handler and freezes / tears down
224
+ * between invocations).
225
+ *
226
+ * Why generic: Firebase has many handler signatures across v1 and v2:
227
+ * v1 https.onRequest: (req, res) => void
228
+ * v1 https.onCall: (data, context) => Promise<result>
229
+ * v1 firestore.onWrite: (snapshot, context) => Promise<void>
230
+ * v1 pubsub.onPublish: (message, context) => Promise<void>
231
+ * v2 onRequest: (req, res) => Promise<void>
232
+ * v2 onCall: (request) => Promise<result>
233
+ * v2 onDocumentWritten: (event) => Promise<void>
234
+ *
235
+ * Rather than ship one wrapper per signature (which would force a
236
+ * dependency on `firebase-functions` types and break when Google
237
+ * adds new triggers), this wrapper is **shape-preserving** — it
238
+ * accepts ANY function and returns one with the same signature.
239
+ * Lifecycle telemetry is emitted around the call; metadata extraction
240
+ * is plug-in via `getMetadata`.
241
+ *
242
+ * import { wrapFunction } from "@cross-deck/node/auto-events";
243
+ * import { onRequest } from "firebase-functions/v2/https";
244
+ *
245
+ * export const myFunction = onRequest(wrapFunction(server, async (req, res) => {
246
+ * // your handler
247
+ * }));
248
+ *
249
+ * Cold-start detection: same per-container logic as Lambda. The
250
+ * first invocation of a fresh container is a cold start; subsequent
251
+ * invocations of the same warm container are not.
252
+ *
253
+ * Flush-before-return: same critical contract as Lambda. Firebase
254
+ * tears down idle containers; queued events vanish if the SDK doesn't
255
+ * flush before the handler returns.
256
+ */
257
+
258
+ interface WrapFunctionOptions {
259
+ /**
260
+ * Override the per-container cold-start flag. Module-level
261
+ * detection is sufficient for production; tests use this to
262
+ * deterministically reset cold-start across runs.
263
+ */
264
+ resetColdStart?: boolean;
265
+ /**
266
+ * Optional metadata extractor — read trigger-specific fields off
267
+ * the handler arguments and attach them to the emitted events.
268
+ * Default: no extra metadata.
269
+ *
270
+ * Return `{ identity, properties }` so identity hints route onto
271
+ * the event envelope (for dashboard pivot) and properties merge
272
+ * into the event's `properties` bag:
273
+ *
274
+ * wrapFunction(server, handler, {
275
+ * getMetadata: (args) => ({
276
+ * identity: { developerUserId: args[0].auth?.uid },
277
+ * properties: { docPath: args[0].ref?.path, region: "us-central1" },
278
+ * }),
279
+ * })
280
+ */
281
+ getMetadata?: (args: unknown[]) => WrapFunctionMetadata | null | undefined;
282
+ /**
283
+ * Label for the `runtime` event property. Defaults to
284
+ * `"firebase-functions"`. Override to distinguish triggers in
285
+ * dashboards (e.g. `"firebase-https"` vs `"firebase-firestore"`).
286
+ */
287
+ runtime?: string;
288
+ }
289
+ interface WrapFunctionMetadata {
290
+ /** Identity hint attached to the event envelope (developerUserId / anonymousId / crossdeckCustomerId). */
291
+ identity?: {
292
+ developerUserId?: string;
293
+ anonymousId?: string;
294
+ crossdeckCustomerId?: string;
295
+ };
296
+ /** Additional properties merged into emitted event properties. */
297
+ properties?: Record<string, unknown>;
298
+ }
299
+ /**
300
+ * Shape-preserving wrap for a Firebase / Cloud Run handler. Returns
301
+ * a function with the SAME signature as the input.
302
+ *
303
+ * Lifecycle emitted:
304
+ * - `function.invoked` on entry — runtime, coldStart, ...metadata
305
+ * - `function.completed` on success — durationMs, memoryUsedMb
306
+ * - `function.failed` on throw — errorType, errorMessage, durationMs
307
+ *
308
+ * Failures also call `server.captureError(err)`. Errors are re-thrown
309
+ * so Firebase still sees the failure and reports it to Cloud Logging.
310
+ *
311
+ * `await server.flush()` runs in `finally` — same as Lambda. Firebase
312
+ * containers freeze / tear down between invocations.
313
+ */
314
+ declare function wrapFunction<TArgs extends unknown[], TResult>(server: CrossdeckServer, handler: (...args: TArgs) => Promise<TResult> | TResult, options?: WrapFunctionOptions): (...args: TArgs) => Promise<TResult>;
315
+
316
+ export { type CrossdeckExpressOptions, type ExpressNext, type ExpressRequestLike, type ExpressResponseLike, type LambdaContextLike, type LambdaHandlerLike, type WrapFunctionMetadata, type WrapFunctionOptions, type WrapLambdaOptions, crossdeckExpress, crossdeckExpressErrorHandler, extractRoutePattern, shouldSkipRequest, wrapFunction, wrapLambdaHandler };