@edraj/sauron-node 1.0.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/README.md +1148 -88
- package/dist/client.d.ts +113 -4
- package/dist/client.js +261 -9
- package/dist/index.d.ts +23 -1
- package/dist/index.js +24 -0
- package/dist/scope.js +5 -0
- package/dist/transport.d.ts +2 -1
- package/dist/transport.js +5 -2
- package/dist/types.d.ts +52 -4
- package/dist/workflow.d.ts +26 -0
- package/dist/workflow.js +51 -0
- package/package.json +1 -1
package/dist/types.d.ts
CHANGED
|
@@ -68,6 +68,30 @@ export interface ScopeData {
|
|
|
68
68
|
contexts: Record<string, unknown>;
|
|
69
69
|
extra: Record<string, unknown>;
|
|
70
70
|
breadcrumbs: Breadcrumb[];
|
|
71
|
+
/**
|
|
72
|
+
* The workflow bounded by `startWorkflow`/`endWorkflow`/`cancelWorkflow` on
|
|
73
|
+
* THIS scope, or `null` if none. Request-isolated: the async-local child
|
|
74
|
+
* scope created by `withScope`/`runWithAsyncScope` gets its own copy, so
|
|
75
|
+
* concurrent requests never observe each other's workflow.
|
|
76
|
+
*/
|
|
77
|
+
workflow: ActiveWorkflow | null;
|
|
78
|
+
}
|
|
79
|
+
/** Exactly six reachable outcomes for a workflow lifecycle call. No seventh. */
|
|
80
|
+
export type WorkflowStatus = 'ok' | 'already_active' | 'not_active' | 'name_mismatch' | 'invalid_name' | 'disabled';
|
|
81
|
+
/** Return value of `startWorkflow` / `endWorkflow` / `cancelWorkflow`. */
|
|
82
|
+
export interface WorkflowResult {
|
|
83
|
+
status: WorkflowStatus;
|
|
84
|
+
/** Present when `status` is `'ok'`. */
|
|
85
|
+
workflowId?: string;
|
|
86
|
+
}
|
|
87
|
+
/** The workflow currently bounded on a scope. */
|
|
88
|
+
export interface ActiveWorkflow {
|
|
89
|
+
/** Fresh client-generated UUID v4, minted by `startWorkflow`. */
|
|
90
|
+
workflowId: string;
|
|
91
|
+
/** Trimmed, caller-supplied name (see `normalizeWorkflowName`). */
|
|
92
|
+
name: string;
|
|
93
|
+
/** ISO-8601 timestamp of when the workflow started. */
|
|
94
|
+
startedAt: string;
|
|
71
95
|
}
|
|
72
96
|
/** An error item (manual `captureException` / `captureMessage`). */
|
|
73
97
|
export interface ErrorItem {
|
|
@@ -84,6 +108,14 @@ export interface ErrorItem {
|
|
|
84
108
|
fingerprint: string[] | null;
|
|
85
109
|
user: ErrorUser | null;
|
|
86
110
|
session_id: string | null;
|
|
111
|
+
/**
|
|
112
|
+
* Id/name of the workflow this error occurred within, if the SDK bounded
|
|
113
|
+
* one via `startWorkflow`. Optional-not-nullable: apps that never use
|
|
114
|
+
* workflows must be byte-identical to before this field existed, so an
|
|
115
|
+
* absent workflow means the key is OMITTED from the item, never `null`.
|
|
116
|
+
*/
|
|
117
|
+
workflow_id?: string;
|
|
118
|
+
workflow_name?: string;
|
|
87
119
|
screen: string | null;
|
|
88
120
|
}
|
|
89
121
|
/** A product-analytics event (PostHog-style `track`). */
|
|
@@ -94,6 +126,9 @@ export interface EventItem {
|
|
|
94
126
|
properties: Record<string, unknown>;
|
|
95
127
|
timestamp: string;
|
|
96
128
|
session_id: string | null;
|
|
129
|
+
/** See {@link ErrorItem.workflow_id} — same omit-not-null convention. */
|
|
130
|
+
workflow_id?: string;
|
|
131
|
+
workflow_name?: string;
|
|
97
132
|
screen: string | null;
|
|
98
133
|
tags?: Record<string, string>;
|
|
99
134
|
contexts?: Record<string, unknown>;
|
|
@@ -122,6 +157,9 @@ export interface TransactionItem {
|
|
|
122
157
|
http_status?: number;
|
|
123
158
|
url?: string;
|
|
124
159
|
distinct_id?: string;
|
|
160
|
+
/** See {@link ErrorItem.workflow_id} — same omit-not-null convention. */
|
|
161
|
+
workflow_id?: string;
|
|
162
|
+
workflow_name?: string;
|
|
125
163
|
timestamp: string;
|
|
126
164
|
}
|
|
127
165
|
/** Caller input for {@link TransactionItem} via `trackTransaction`. */
|
|
@@ -129,7 +167,20 @@ export interface TransactionInput {
|
|
|
129
167
|
name: string;
|
|
130
168
|
/** Operation class: `navigation | http | resource | screen_load | custom`. Default `custom`. */
|
|
131
169
|
op?: string;
|
|
132
|
-
duration_ms
|
|
170
|
+
duration_ms?: number;
|
|
171
|
+
/**
|
|
172
|
+
* Accepted alias for {@link duration_ms}.
|
|
173
|
+
*
|
|
174
|
+
* The browser SDK's equivalent input takes `durationMs` (`sdks/js`), so a
|
|
175
|
+
* snippet moved between the two — or any plain-JavaScript caller, where the
|
|
176
|
+
* type checker is not there to object — used to produce a transaction with no
|
|
177
|
+
* duration at all. The item still validated and still shipped, just without
|
|
178
|
+
* the one field it exists to carry, which is the worst possible outcome: no
|
|
179
|
+
* error anywhere and a silently useless performance record.
|
|
180
|
+
*
|
|
181
|
+
* Supply exactly one. `duration_ms` wins if both are present.
|
|
182
|
+
*/
|
|
183
|
+
durationMs?: number;
|
|
133
184
|
status?: string;
|
|
134
185
|
http_method?: string;
|
|
135
186
|
http_status?: number;
|
|
@@ -169,7 +220,6 @@ export interface EnvelopeHeader {
|
|
|
169
220
|
dsn: string;
|
|
170
221
|
sdk: SdkInfo;
|
|
171
222
|
sent_at: string;
|
|
172
|
-
environment: string;
|
|
173
223
|
release: string | null;
|
|
174
224
|
}
|
|
175
225
|
/** The complete, serializable envelope posted to the ingest gateway. */
|
|
@@ -223,7 +273,6 @@ export interface TransportOptions {
|
|
|
223
273
|
export interface InitOptions {
|
|
224
274
|
/** `https://<public_key>@<host>/<project_id>` */
|
|
225
275
|
dsn: string;
|
|
226
|
-
environment?: string;
|
|
227
276
|
release?: string | null;
|
|
228
277
|
/** Default tags seeded into the global scope at init. */
|
|
229
278
|
tags?: Record<string, string>;
|
|
@@ -269,7 +318,6 @@ export interface InitOptions {
|
|
|
269
318
|
/** Fully-resolved options with all defaults applied. */
|
|
270
319
|
export interface ResolvedOptions {
|
|
271
320
|
dsn: string;
|
|
272
|
-
environment: string;
|
|
273
321
|
release: string | null;
|
|
274
322
|
tags: Record<string, string>;
|
|
275
323
|
contexts: Record<string, unknown>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ActiveWorkflow } from './types.js';
|
|
2
|
+
/** Cap on a workflow name, after trimming. */
|
|
3
|
+
export declare const WORKFLOW_NAME_MAX = 120;
|
|
4
|
+
/** Cap on a cancel reason. */
|
|
5
|
+
export declare const WORKFLOW_REASON_MAX = 120;
|
|
6
|
+
/**
|
|
7
|
+
* Returns the trimmed name, or `null` when invalid (not a string, empty after
|
|
8
|
+
* trimming, or over {@link WORKFLOW_NAME_MAX} characters). Trims BEFORE
|
|
9
|
+
* checking length/emptiness so an all-whitespace or over-long-but-padded name
|
|
10
|
+
* is rejected rather than silently truncated.
|
|
11
|
+
*/
|
|
12
|
+
export declare function normalizeWorkflowName(name: unknown): string | null;
|
|
13
|
+
/**
|
|
14
|
+
* Normalize a cancel reason: default to `'user'` for a non-string or
|
|
15
|
+
* all-whitespace value, else trim and cap at {@link WORKFLOW_REASON_MAX}.
|
|
16
|
+
* The internal `force`-supersede path also routes its literal `'superseded'`
|
|
17
|
+
* reason through this, so every reason on the wire is consistently shaped.
|
|
18
|
+
*/
|
|
19
|
+
export declare function normalizeReason(reason: unknown): string;
|
|
20
|
+
/**
|
|
21
|
+
* The active workflow on the CURRENT scope — the `AsyncLocalStorage` child
|
|
22
|
+
* inside `withScope`/`runWithAsyncScope`, else the process-wide global scope —
|
|
23
|
+
* or `null` if none. Client-agnostic: reflects scope state regardless of
|
|
24
|
+
* whether an SDK client is initialized, same as `getCurrentScope()` itself.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getWorkflow(): ActiveWorkflow | null;
|
package/dist/workflow.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow name/reason normalization — pure helpers, no state of their own.
|
|
3
|
+
*
|
|
4
|
+
* Unlike the browser SDK (`sdks/js/src/workflow.ts`), the active workflow is
|
|
5
|
+
* NOT held in a module-level variable here: a module global would leak one
|
|
6
|
+
* HTTP request's workflow into every other concurrent request's telemetry.
|
|
7
|
+
* Instead it lives on the per-request {@link Scope} (`scope.data.workflow`),
|
|
8
|
+
* isolated by `AsyncLocalStorage` exactly like `user`/`tags`/`breadcrumbs`
|
|
9
|
+
* already are (see `scope.ts`). `getWorkflow()` below is a bare read of that
|
|
10
|
+
* scope field — it takes no client and never throws, mirroring the existing
|
|
11
|
+
* `getCurrentScope()`/`getGlobalScope()` getters.
|
|
12
|
+
*/
|
|
13
|
+
import { getCurrentScope } from './scope.js';
|
|
14
|
+
/** Cap on a workflow name, after trimming. */
|
|
15
|
+
export const WORKFLOW_NAME_MAX = 120;
|
|
16
|
+
/** Cap on a cancel reason. */
|
|
17
|
+
export const WORKFLOW_REASON_MAX = 120;
|
|
18
|
+
/**
|
|
19
|
+
* Returns the trimmed name, or `null` when invalid (not a string, empty after
|
|
20
|
+
* trimming, or over {@link WORKFLOW_NAME_MAX} characters). Trims BEFORE
|
|
21
|
+
* checking length/emptiness so an all-whitespace or over-long-but-padded name
|
|
22
|
+
* is rejected rather than silently truncated.
|
|
23
|
+
*/
|
|
24
|
+
export function normalizeWorkflowName(name) {
|
|
25
|
+
if (typeof name !== 'string')
|
|
26
|
+
return null;
|
|
27
|
+
const trimmed = name.trim();
|
|
28
|
+
if (trimmed.length === 0 || trimmed.length > WORKFLOW_NAME_MAX)
|
|
29
|
+
return null;
|
|
30
|
+
return trimmed;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Normalize a cancel reason: default to `'user'` for a non-string or
|
|
34
|
+
* all-whitespace value, else trim and cap at {@link WORKFLOW_REASON_MAX}.
|
|
35
|
+
* The internal `force`-supersede path also routes its literal `'superseded'`
|
|
36
|
+
* reason through this, so every reason on the wire is consistently shaped.
|
|
37
|
+
*/
|
|
38
|
+
export function normalizeReason(reason) {
|
|
39
|
+
if (typeof reason !== 'string' || reason.trim().length === 0)
|
|
40
|
+
return 'user';
|
|
41
|
+
return reason.trim().slice(0, WORKFLOW_REASON_MAX);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The active workflow on the CURRENT scope — the `AsyncLocalStorage` child
|
|
45
|
+
* inside `withScope`/`runWithAsyncScope`, else the process-wide global scope —
|
|
46
|
+
* or `null` if none. Client-agnostic: reflects scope state regardless of
|
|
47
|
+
* whether an SDK client is initialized, same as `getCurrentScope()` itself.
|
|
48
|
+
*/
|
|
49
|
+
export function getWorkflow() {
|
|
50
|
+
return getCurrentScope().data.workflow;
|
|
51
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edraj/sauron-node",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Sauron server-side Node/TypeScript SDK: product-analytics events + exception capture for Node backends.",
|
|
5
5
|
"homepage": "https://github.com/edraj/sauron/tree/main/sdks/node#readme",
|
|
6
6
|
"repository": {
|