@edraj/sauron-node 1.0.0 → 1.3.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 +35 -0
- package/README.md +1148 -88
- package/dist/client.d.ts +113 -4
- package/dist/client.js +247 -8
- 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 +38 -3
- 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`. */
|
|
@@ -169,7 +207,6 @@ export interface EnvelopeHeader {
|
|
|
169
207
|
dsn: string;
|
|
170
208
|
sdk: SdkInfo;
|
|
171
209
|
sent_at: string;
|
|
172
|
-
environment: string;
|
|
173
210
|
release: string | null;
|
|
174
211
|
}
|
|
175
212
|
/** The complete, serializable envelope posted to the ingest gateway. */
|
|
@@ -223,7 +260,6 @@ export interface TransportOptions {
|
|
|
223
260
|
export interface InitOptions {
|
|
224
261
|
/** `https://<public_key>@<host>/<project_id>` */
|
|
225
262
|
dsn: string;
|
|
226
|
-
environment?: string;
|
|
227
263
|
release?: string | null;
|
|
228
264
|
/** Default tags seeded into the global scope at init. */
|
|
229
265
|
tags?: Record<string, string>;
|
|
@@ -269,7 +305,6 @@ export interface InitOptions {
|
|
|
269
305
|
/** Fully-resolved options with all defaults applied. */
|
|
270
306
|
export interface ResolvedOptions {
|
|
271
307
|
dsn: string;
|
|
272
|
-
environment: string;
|
|
273
308
|
release: string | null;
|
|
274
309
|
tags: Record<string, string>;
|
|
275
310
|
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.3.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": {
|