@flareapp/node 0.1.0 → 0.2.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.
Files changed (39) hide show
  1. package/README.md +20 -2
  2. package/dist/index.cjs +848 -0
  3. package/dist/index.d.cts +138 -0
  4. package/dist/index.d.mts +138 -0
  5. package/dist/index.mjs +763 -0
  6. package/package.json +5 -2
  7. package/.oxlintrc.json +0 -7
  8. package/.release-it.json +0 -13
  9. package/CHANGELOG.md +0 -22
  10. package/src/Flare.ts +0 -224
  11. package/src/context/body.ts +0 -185
  12. package/src/context/collectNode.ts +0 -116
  13. package/src/context/headers.ts +0 -90
  14. package/src/context/process.ts +0 -25
  15. package/src/index.ts +0 -27
  16. package/src/process/fatal.ts +0 -54
  17. package/src/process/handlers.ts +0 -109
  18. package/src/scope/AsyncLocalStorageScopeProvider.ts +0 -86
  19. package/src/scope/NodeScope.ts +0 -8
  20. package/src/stacktrace/DiskFileReader.ts +0 -57
  21. package/src/types.ts +0 -37
  22. package/tests/asyncScopeProvider.test.ts +0 -43
  23. package/tests/body.test.ts +0 -129
  24. package/tests/diskFileReader.test.ts +0 -36
  25. package/tests/fatalHandlers.test.ts +0 -140
  26. package/tests/flush.test.ts +0 -11
  27. package/tests/headers.test.ts +0 -86
  28. package/tests/integration.test.ts +0 -63
  29. package/tests/lifecycle.test.ts +0 -71
  30. package/tests/nodeContextCollector.test.ts +0 -106
  31. package/tests/nodeExports.test.ts +0 -11
  32. package/tests/nodeScope.test.ts +0 -19
  33. package/tests/processAttributes.test.ts +0 -15
  34. package/tests/processHandlers.test.ts +0 -47
  35. package/tests/regexFlagSanitization.test.ts +0 -88
  36. package/tests/scopeIsolation.test.ts +0 -47
  37. package/tests/setFrameworkScope.test.ts +0 -86
  38. package/tsconfig.json +0 -9
  39. package/vitest.config.ts +0 -18
@@ -0,0 +1,138 @@
1
+ import { AttributeValue, Attributes, Config, ContextCollector, DEFAULT_URL_DENYLIST, EntryPointHandler, FileReader, Flare, Flare as Flare$1, FlushFn, FlushScheduler, Framework, GlobalScopeProvider, Glow, Logger, MessageLevel, NullFileReader, OverriddenGrouping, Report, Scope, Scope as Scope$1, ScopeProvider, SdkInfo, SpanEvent, StackFrame, convertToError, redactUrlQuery, resolveDenylist } from "@flareapp/core";
2
+
3
+ //#region src/types.d.ts
4
+ type RequestContext = {
5
+ method?: string;
6
+ path?: string;
7
+ url?: string;
8
+ headers?: Record<string, string | string[] | undefined>;
9
+ body?: unknown;
10
+ };
11
+ type User = {
12
+ id?: string | number;
13
+ email?: string;
14
+ username?: string;
15
+ ipAddress?: string;
16
+ };
17
+ type FatalMode = 'off' | 'report' | 'report-and-exit';
18
+ type NodeOptions = {
19
+ uncaughtExceptionMode?: FatalMode;
20
+ unhandledRejectionMode?: FatalMode;
21
+ shutdownTimeoutMs?: number;
22
+ headerDenylist?: RegExp;
23
+ headerAllowlist?: RegExp | null;
24
+ replaceDefaultHeaderDenylist?: boolean;
25
+ captureRequestBody?: boolean;
26
+ bodyMaxBytes?: number;
27
+ bodyAllowedContentTypes?: RegExp;
28
+ bodyKeyDenylist?: RegExp;
29
+ };
30
+ //#endregion
31
+ //#region src/scope/NodeScope.d.ts
32
+ declare class NodeScope extends Scope$1 {
33
+ request: RequestContext;
34
+ user: User | null;
35
+ }
36
+ //#endregion
37
+ //#region src/Flare.d.ts
38
+ /**
39
+ * Node.js-specific `Flare` singleton, exposed from `@flareapp/node` as `flare`.
40
+ *
41
+ * Subclasses core's `Flare` and wires the Node-only seams in its constructor:
42
+ *
43
+ * - `AsyncLocalStorageScopeProvider` so each `runWithContext(...)` callback
44
+ * gets its own `NodeScope` (glows, attributes, user, entry-point, request),
45
+ * isolated from concurrent requests.
46
+ * - `makeNodeContextCollector(...)` to project the current `NodeScope` and
47
+ * process info into report attributes (http.request.*, url.path, etc).
48
+ * - `DiskFileReader` to read source files for stack-trace snippets via
49
+ * `node:fs/promises` instead of the browser's `fetch`.
50
+ * - `ProcessHandlerManager` to attach/detach `uncaughtException` and
51
+ * `unhandledRejection` listeners based on the current `NodeOptions`.
52
+ *
53
+ * Also adds Node-only API surface on top of core: `configureNode(...)`,
54
+ * `runWithContext(...)`, `mergeContext(...)`, `setUser(...)`, `getContext()`,
55
+ * `removeProcessListeners()`. Inherited core methods (`light`, `configure`,
56
+ * `addContext`, `glow`, etc.) return `this`, so chaining keeps the
57
+ * `NodeFlare` type and `configureNode(...)` stays callable mid-chain.
58
+ */
59
+ declare class NodeFlare extends Flare$1 {
60
+ private nodeOptions;
61
+ private isLit;
62
+ private nodeScopeProvider;
63
+ private handlerManager;
64
+ constructor();
65
+ /**
66
+ * Set the API key (and optional debug flag), then reconcile process
67
+ * listeners with the current `nodeOptions`. Reconcile runs on EVERY call,
68
+ * not just the first, so `light()` is the right escape hatch to re-attach
69
+ * after `removeProcessListeners()`.
70
+ */
71
+ light(key?: string, debug?: boolean): this;
72
+ /**
73
+ * Merge Node-only options (fatal-handler modes, header/body redaction
74
+ * config, shutdown timeout) into the active configuration. Safe to call
75
+ * before or after `light()`:
76
+ *
77
+ * - Before `light()`: options are stored; listeners are attached when
78
+ * `light()` runs.
79
+ * - After `light()`: options are stored AND listeners are reconciled
80
+ * immediately, so flipping a mode to `'off'` detaches the handler and
81
+ * flipping it back to `'report'`/`'report-and-exit'` re-attaches.
82
+ *
83
+ * Regex options (`headerAllowlist`, `bodyAllowedContentTypes`,
84
+ * `bodyKeyDenylist`) are passed through `sanitizeRegex` to strip stateful
85
+ * `g`/`y` flags; without that, `RegExp.prototype.test` would skip matches
86
+ * across keys.
87
+ */
88
+ configureNode(partial: Partial<NodeOptions>): NodeFlare;
89
+ /**
90
+ * Run `fn` inside a fresh `NodeScope` carrying the supplied request
91
+ * metadata. Inside `fn` (and any async work it awaits), `flare.glow(...)`,
92
+ * `flare.addContext(...)`, `flare.setUser(...)`, and `flare.report(...)`
93
+ * see a scope that is isolated from other concurrent requests.
94
+ *
95
+ * Mirrors a typical web-framework middleware: call once per request,
96
+ * wrapping the request handler, and the SDK will attribute any error
97
+ * reported inside the chain to the right request.
98
+ */
99
+ runWithContext<T>(request: RequestContext, fn: () => T): T;
100
+ /**
101
+ * Patch the request metadata on the active scope after `runWithContext(...)`
102
+ * has already started. Useful when fields become known partway through a
103
+ * request (e.g., the resolved absolute URL after proxy headers are parsed).
104
+ *
105
+ * Outside any `runWithContext(...)` callback, this writes to the fallback
106
+ * scope; the patch is visible to subsequent reports issued from outside a
107
+ * request scope but is NOT inherited by future `runWithContext(...)` calls.
108
+ */
109
+ mergeContext(partial: Partial<RequestContext>): void;
110
+ /**
111
+ * Attach an authenticated user to the active scope. Inside a request scope
112
+ * this is per-request; outside it lands on the fallback scope. The fields
113
+ * are projected to OTel-style keys (`enduser.id`, `enduser.email`,
114
+ * `enduser.username`, `client.address`) by the Node context collector.
115
+ */
116
+ setUser(user: User | null): void;
117
+ /**
118
+ * Returns the request scope when called inside `runWithContext(...)`, or
119
+ * `null` outside. Intentionally returns `null` (not the fallback scope)
120
+ * when no request is active, so callers can distinguish "we are inside a
121
+ * request" from "we are not". Primarily useful for debugging.
122
+ */
123
+ getContext(): NodeScope | null;
124
+ /**
125
+ * Detach the `uncaughtException` and `unhandledRejection` listeners
126
+ * without changing `nodeOptions`. Intended for tests and for graceful
127
+ * shutdown paths where you want to take ownership of process exit
128
+ * yourself.
129
+ *
130
+ * Calling `light()` afterwards re-attaches based on the current options.
131
+ */
132
+ removeProcessListeners(): void;
133
+ }
134
+ //#endregion
135
+ //#region src/index.d.ts
136
+ declare const flare: NodeFlare;
137
+ //#endregion
138
+ export { type AttributeValue, type Attributes, type Config, type ContextCollector, DEFAULT_URL_DENYLIST, type EntryPointHandler, type FatalMode, type FileReader, Flare, type FlushFn, type FlushScheduler, type Framework, GlobalScopeProvider, type Glow, Logger, type MessageLevel, NodeFlare, type NodeOptions, NodeScope, NullFileReader, type OverriddenGrouping, type Report, type RequestContext, Scope, type ScopeProvider, type SdkInfo, type SpanEvent, type StackFrame, type User, convertToError, flare, redactUrlQuery, resolveDenylist };
@@ -0,0 +1,138 @@
1
+ import { AttributeValue, Attributes, Config, ContextCollector, DEFAULT_URL_DENYLIST, EntryPointHandler, FileReader, Flare, Flare as Flare$1, FlushFn, FlushScheduler, Framework, GlobalScopeProvider, Glow, Logger, MessageLevel, NullFileReader, OverriddenGrouping, Report, Scope, Scope as Scope$1, ScopeProvider, SdkInfo, SpanEvent, StackFrame, convertToError, redactUrlQuery, resolveDenylist } from "@flareapp/core";
2
+
3
+ //#region src/types.d.ts
4
+ type RequestContext = {
5
+ method?: string;
6
+ path?: string;
7
+ url?: string;
8
+ headers?: Record<string, string | string[] | undefined>;
9
+ body?: unknown;
10
+ };
11
+ type User = {
12
+ id?: string | number;
13
+ email?: string;
14
+ username?: string;
15
+ ipAddress?: string;
16
+ };
17
+ type FatalMode = 'off' | 'report' | 'report-and-exit';
18
+ type NodeOptions = {
19
+ uncaughtExceptionMode?: FatalMode;
20
+ unhandledRejectionMode?: FatalMode;
21
+ shutdownTimeoutMs?: number;
22
+ headerDenylist?: RegExp;
23
+ headerAllowlist?: RegExp | null;
24
+ replaceDefaultHeaderDenylist?: boolean;
25
+ captureRequestBody?: boolean;
26
+ bodyMaxBytes?: number;
27
+ bodyAllowedContentTypes?: RegExp;
28
+ bodyKeyDenylist?: RegExp;
29
+ };
30
+ //#endregion
31
+ //#region src/scope/NodeScope.d.ts
32
+ declare class NodeScope extends Scope$1 {
33
+ request: RequestContext;
34
+ user: User | null;
35
+ }
36
+ //#endregion
37
+ //#region src/Flare.d.ts
38
+ /**
39
+ * Node.js-specific `Flare` singleton, exposed from `@flareapp/node` as `flare`.
40
+ *
41
+ * Subclasses core's `Flare` and wires the Node-only seams in its constructor:
42
+ *
43
+ * - `AsyncLocalStorageScopeProvider` so each `runWithContext(...)` callback
44
+ * gets its own `NodeScope` (glows, attributes, user, entry-point, request),
45
+ * isolated from concurrent requests.
46
+ * - `makeNodeContextCollector(...)` to project the current `NodeScope` and
47
+ * process info into report attributes (http.request.*, url.path, etc).
48
+ * - `DiskFileReader` to read source files for stack-trace snippets via
49
+ * `node:fs/promises` instead of the browser's `fetch`.
50
+ * - `ProcessHandlerManager` to attach/detach `uncaughtException` and
51
+ * `unhandledRejection` listeners based on the current `NodeOptions`.
52
+ *
53
+ * Also adds Node-only API surface on top of core: `configureNode(...)`,
54
+ * `runWithContext(...)`, `mergeContext(...)`, `setUser(...)`, `getContext()`,
55
+ * `removeProcessListeners()`. Inherited core methods (`light`, `configure`,
56
+ * `addContext`, `glow`, etc.) return `this`, so chaining keeps the
57
+ * `NodeFlare` type and `configureNode(...)` stays callable mid-chain.
58
+ */
59
+ declare class NodeFlare extends Flare$1 {
60
+ private nodeOptions;
61
+ private isLit;
62
+ private nodeScopeProvider;
63
+ private handlerManager;
64
+ constructor();
65
+ /**
66
+ * Set the API key (and optional debug flag), then reconcile process
67
+ * listeners with the current `nodeOptions`. Reconcile runs on EVERY call,
68
+ * not just the first, so `light()` is the right escape hatch to re-attach
69
+ * after `removeProcessListeners()`.
70
+ */
71
+ light(key?: string, debug?: boolean): this;
72
+ /**
73
+ * Merge Node-only options (fatal-handler modes, header/body redaction
74
+ * config, shutdown timeout) into the active configuration. Safe to call
75
+ * before or after `light()`:
76
+ *
77
+ * - Before `light()`: options are stored; listeners are attached when
78
+ * `light()` runs.
79
+ * - After `light()`: options are stored AND listeners are reconciled
80
+ * immediately, so flipping a mode to `'off'` detaches the handler and
81
+ * flipping it back to `'report'`/`'report-and-exit'` re-attaches.
82
+ *
83
+ * Regex options (`headerAllowlist`, `bodyAllowedContentTypes`,
84
+ * `bodyKeyDenylist`) are passed through `sanitizeRegex` to strip stateful
85
+ * `g`/`y` flags; without that, `RegExp.prototype.test` would skip matches
86
+ * across keys.
87
+ */
88
+ configureNode(partial: Partial<NodeOptions>): NodeFlare;
89
+ /**
90
+ * Run `fn` inside a fresh `NodeScope` carrying the supplied request
91
+ * metadata. Inside `fn` (and any async work it awaits), `flare.glow(...)`,
92
+ * `flare.addContext(...)`, `flare.setUser(...)`, and `flare.report(...)`
93
+ * see a scope that is isolated from other concurrent requests.
94
+ *
95
+ * Mirrors a typical web-framework middleware: call once per request,
96
+ * wrapping the request handler, and the SDK will attribute any error
97
+ * reported inside the chain to the right request.
98
+ */
99
+ runWithContext<T>(request: RequestContext, fn: () => T): T;
100
+ /**
101
+ * Patch the request metadata on the active scope after `runWithContext(...)`
102
+ * has already started. Useful when fields become known partway through a
103
+ * request (e.g., the resolved absolute URL after proxy headers are parsed).
104
+ *
105
+ * Outside any `runWithContext(...)` callback, this writes to the fallback
106
+ * scope; the patch is visible to subsequent reports issued from outside a
107
+ * request scope but is NOT inherited by future `runWithContext(...)` calls.
108
+ */
109
+ mergeContext(partial: Partial<RequestContext>): void;
110
+ /**
111
+ * Attach an authenticated user to the active scope. Inside a request scope
112
+ * this is per-request; outside it lands on the fallback scope. The fields
113
+ * are projected to OTel-style keys (`enduser.id`, `enduser.email`,
114
+ * `enduser.username`, `client.address`) by the Node context collector.
115
+ */
116
+ setUser(user: User | null): void;
117
+ /**
118
+ * Returns the request scope when called inside `runWithContext(...)`, or
119
+ * `null` outside. Intentionally returns `null` (not the fallback scope)
120
+ * when no request is active, so callers can distinguish "we are inside a
121
+ * request" from "we are not". Primarily useful for debugging.
122
+ */
123
+ getContext(): NodeScope | null;
124
+ /**
125
+ * Detach the `uncaughtException` and `unhandledRejection` listeners
126
+ * without changing `nodeOptions`. Intended for tests and for graceful
127
+ * shutdown paths where you want to take ownership of process exit
128
+ * yourself.
129
+ *
130
+ * Calling `light()` afterwards re-attaches based on the current options.
131
+ */
132
+ removeProcessListeners(): void;
133
+ }
134
+ //#endregion
135
+ //#region src/index.d.ts
136
+ declare const flare: NodeFlare;
137
+ //#endregion
138
+ export { type AttributeValue, type Attributes, type Config, type ContextCollector, DEFAULT_URL_DENYLIST, type EntryPointHandler, type FatalMode, type FileReader, Flare, type FlushFn, type FlushScheduler, type Framework, GlobalScopeProvider, type Glow, Logger, type MessageLevel, NodeFlare, type NodeOptions, NodeScope, NullFileReader, type OverriddenGrouping, type Report, type RequestContext, Scope, type ScopeProvider, type SdkInfo, type SpanEvent, type StackFrame, type User, convertToError, flare, redactUrlQuery, resolveDenylist };