@glasstrace/sdk 1.3.9 → 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/README.md +68 -0
- package/dist/capture-error-BmQz7xF6.d.cts +455 -0
- package/dist/capture-error-CTgSYxek.d.ts +455 -0
- package/dist/{chunk-XS5W3SPL.js → chunk-4WI7B5FQ.js} +91 -5
- package/dist/chunk-4WI7B5FQ.js.map +1 -0
- package/dist/{chunk-GYTCZSAV.js → chunk-MFYOQOD7.js} +2 -2
- package/dist/{chunk-4WUHMLMM.js → chunk-MMKFFF2L.js} +2 -2
- package/dist/{chunk-FKBCEOJ5.js → chunk-Q42BY5BA.js} +2 -2
- package/dist/{chunk-DBKG6SRI.js → chunk-QU26IKIJ.js} +2 -2
- package/dist/{chunk-ADUD4PEK.js → chunk-TANUWTFO.js} +3 -3
- package/dist/{chunk-LJMZXJ45.js → chunk-VJQIFY33.js} +3 -3
- package/dist/{chunk-JH7EGRC5.js → chunk-VQDYXXVS.js} +9 -9
- package/dist/cli/init.cjs +13 -2
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.js +6 -6
- package/dist/cli/mcp-add.cjs +12 -1
- package/dist/cli/mcp-add.cjs.map +1 -1
- package/dist/cli/mcp-add.js +2 -2
- package/dist/cli/uninit.js +3 -3
- package/dist/cli/validate.cjs +12 -1
- package/dist/cli/validate.cjs.map +1 -1
- package/dist/cli/validate.js +2 -2
- package/dist/{edge-entry-BSKA1l_0.d.ts → edge-entry-AWO70gje.d.ts} +1 -1
- package/dist/{edge-entry-DyMWa6JK.d.cts → edge-entry-DaeG7D7S.d.cts} +1 -1
- package/dist/edge-entry.cjs +43 -2
- package/dist/edge-entry.cjs.map +1 -1
- package/dist/edge-entry.d.cts +2 -2
- package/dist/edge-entry.d.ts +2 -2
- package/dist/edge-entry.js +2 -2
- package/dist/index.cjs +369 -8
- package/dist/index.cjs.map +1 -1
- package/dist/{index.d-Bo_Rxund.d.cts → index.d-Dq33YwFT.d.cts} +91 -1
- package/dist/{index.d-Bo_Rxund.d.ts → index.d-Dq33YwFT.d.ts} +91 -1
- package/dist/index.d.cts +91 -431
- package/dist/index.d.ts +91 -431
- package/dist/index.js +290 -6
- package/dist/index.js.map +1 -1
- package/dist/node-entry.cjs +49 -7
- package/dist/node-entry.cjs.map +1 -1
- package/dist/node-entry.d.cts +3 -3
- package/dist/node-entry.d.ts +3 -3
- package/dist/node-entry.js +7 -7
- package/dist/node-subpath.cjs +12 -1
- package/dist/node-subpath.cjs.map +1 -1
- package/dist/node-subpath.d.cts +1 -1
- package/dist/node-subpath.d.ts +1 -1
- package/dist/node-subpath.js +3 -3
- package/dist/{source-map-uploader-FSTHCYDR.js → source-map-uploader-PB3M4PPP.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-XS5W3SPL.js.map +0 -1
- /package/dist/{chunk-GYTCZSAV.js.map → chunk-MFYOQOD7.js.map} +0 -0
- /package/dist/{chunk-4WUHMLMM.js.map → chunk-MMKFFF2L.js.map} +0 -0
- /package/dist/{chunk-FKBCEOJ5.js.map → chunk-Q42BY5BA.js.map} +0 -0
- /package/dist/{chunk-DBKG6SRI.js.map → chunk-QU26IKIJ.js.map} +0 -0
- /package/dist/{chunk-ADUD4PEK.js.map → chunk-TANUWTFO.js.map} +0 -0
- /package/dist/{chunk-LJMZXJ45.js.map → chunk-VJQIFY33.js.map} +0 -0
- /package/dist/{chunk-JH7EGRC5.js.map → chunk-VQDYXXVS.js.map} +0 -0
- /package/dist/{source-map-uploader-FSTHCYDR.js.map → source-map-uploader-PB3M4PPP.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -12,13 +12,15 @@ import {
|
|
|
12
12
|
registerGlasstrace,
|
|
13
13
|
waitForReady,
|
|
14
14
|
withGlasstraceConfig
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-VQDYXXVS.js";
|
|
16
16
|
import {
|
|
17
17
|
GlasstraceSpanProcessor,
|
|
18
18
|
SdkError,
|
|
19
19
|
captureCorrelationId
|
|
20
|
-
} from "./chunk-
|
|
21
|
-
import
|
|
20
|
+
} from "./chunk-Q42BY5BA.js";
|
|
21
|
+
import {
|
|
22
|
+
trace
|
|
23
|
+
} from "./chunk-DQ25VOKK.js";
|
|
22
24
|
import "./chunk-YG3X7TUI.js";
|
|
23
25
|
import {
|
|
24
26
|
getActiveConfig,
|
|
@@ -27,7 +29,7 @@ import {
|
|
|
27
29
|
performInit,
|
|
28
30
|
saveCachedConfig,
|
|
29
31
|
sendInitRequest
|
|
30
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-TANUWTFO.js";
|
|
31
33
|
import {
|
|
32
34
|
isAnonymousMode,
|
|
33
35
|
isProductionDisabled,
|
|
@@ -37,11 +39,292 @@ import {
|
|
|
37
39
|
import {
|
|
38
40
|
getOrCreateAnonKey,
|
|
39
41
|
readAnonKey
|
|
40
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-MFYOQOD7.js";
|
|
41
43
|
import {
|
|
44
|
+
GLASSTRACE_ATTRIBUTE_NAMES,
|
|
45
|
+
SIDE_EFFECT_OMISSION_REASONS,
|
|
46
|
+
SIDE_EFFECT_OPERATION_KINDS,
|
|
47
|
+
SIDE_EFFECT_OPERATION_PHASES,
|
|
48
|
+
SIDE_EFFECT_OPERATION_STATUSES,
|
|
49
|
+
SIDE_EFFECT_SEMANTIC_FIELD_KEYS,
|
|
42
50
|
deriveSessionId
|
|
43
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-4WI7B5FQ.js";
|
|
44
52
|
import "./chunk-NSBPE2FW.js";
|
|
53
|
+
|
|
54
|
+
// src/side-effect/allowlist.ts
|
|
55
|
+
var MAX_SIDE_EFFECT_OPERATION_LABEL_LENGTH = 96;
|
|
56
|
+
var MAX_SIDE_EFFECT_FIELD_VALUE_LENGTH = 80;
|
|
57
|
+
var MAX_SIDE_EFFECT_OPERATIONS_PER_SPAN = 5;
|
|
58
|
+
var OPERATION_KIND_SET = new Set(
|
|
59
|
+
SIDE_EFFECT_OPERATION_KINDS
|
|
60
|
+
);
|
|
61
|
+
var SEMANTIC_FIELD_KEY_SET = new Set(
|
|
62
|
+
SIDE_EFFECT_SEMANTIC_FIELD_KEYS
|
|
63
|
+
);
|
|
64
|
+
var OPERATION_STATUS_SET = new Set(
|
|
65
|
+
SIDE_EFFECT_OPERATION_STATUSES
|
|
66
|
+
);
|
|
67
|
+
var OPERATION_PHASE_SET = new Set(
|
|
68
|
+
SIDE_EFFECT_OPERATION_PHASES
|
|
69
|
+
);
|
|
70
|
+
var OMISSION_REASON_SET = new Set(
|
|
71
|
+
SIDE_EFFECT_OMISSION_REASONS
|
|
72
|
+
);
|
|
73
|
+
var TOKEN_REGEX = /^[A-Za-z0-9][A-Za-z0-9_.:-]*$/;
|
|
74
|
+
var LOCALE_REGEX = /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8}){0,3}$/;
|
|
75
|
+
var TIMEZONE_REGEX = /^(?:UTC|GMT|[A-Za-z][A-Za-z0-9_+-]*(?:\/[A-Za-z0-9_+-]+){1,3})$/;
|
|
76
|
+
var URL_SCHEME = /:\/\//;
|
|
77
|
+
var URL_SCHEME_RELATIVE = /^\/\//;
|
|
78
|
+
var EMAIL_LIKE = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/i;
|
|
79
|
+
var QUERY_LIKE = /\?/;
|
|
80
|
+
var FRAGMENT_LIKE = /#/;
|
|
81
|
+
var HEADER_LIKE = /\b(authorization|set-cookie|cookie)\b\s*[:=]/i;
|
|
82
|
+
var HEADER_TOKEN_LIKE = /\b(authorization|set-cookie|cookie)\b\s+\S+=/i;
|
|
83
|
+
var BEARER_LIKE = /bearer\s+\S+/i;
|
|
84
|
+
var TOKEN_KV_LIKE = /["']?(password|passwd|token|api[_-]?key|secret|client_secret)["']?\s*[:=]/i;
|
|
85
|
+
var PROSE_LIKE = /[\r\n\t]|\s{2,}/;
|
|
86
|
+
var UUID_LIKE = /[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/i;
|
|
87
|
+
var GT_KEY_LIKE = /gt_(dev|anon|live)_[A-Za-z0-9_-]+/;
|
|
88
|
+
function detectUnsafePattern(value) {
|
|
89
|
+
if (value.trim() !== value) return "raw_payload";
|
|
90
|
+
if (PROSE_LIKE.test(value)) return "raw_payload";
|
|
91
|
+
if (URL_SCHEME.test(value)) return "raw_payload";
|
|
92
|
+
if (URL_SCHEME_RELATIVE.test(value)) return "raw_payload";
|
|
93
|
+
if (QUERY_LIKE.test(value)) return "raw_payload";
|
|
94
|
+
if (FRAGMENT_LIKE.test(value)) return "raw_payload";
|
|
95
|
+
if (BEARER_LIKE.test(value)) return "secret";
|
|
96
|
+
if (HEADER_TOKEN_LIKE.test(value)) return "secret";
|
|
97
|
+
if (TOKEN_KV_LIKE.test(value)) return "secret";
|
|
98
|
+
if (HEADER_LIKE.test(value)) return "secret";
|
|
99
|
+
if (UUID_LIKE.test(value)) return "secret";
|
|
100
|
+
if (GT_KEY_LIKE.test(value)) return "secret";
|
|
101
|
+
if (EMAIL_LIKE.test(value)) return "pii";
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
function passesFieldValidator(key, value) {
|
|
105
|
+
if (key === "locale") return LOCALE_REGEX.test(value);
|
|
106
|
+
if (key === "timezone") return TIMEZONE_REGEX.test(value);
|
|
107
|
+
return TOKEN_REGEX.test(value);
|
|
108
|
+
}
|
|
109
|
+
function checkOperationLabel(value) {
|
|
110
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
111
|
+
return { accepted: false, reason: "raw_payload" };
|
|
112
|
+
}
|
|
113
|
+
if (value.length > MAX_SIDE_EFFECT_OPERATION_LABEL_LENGTH) {
|
|
114
|
+
return { accepted: false, reason: "value_too_long" };
|
|
115
|
+
}
|
|
116
|
+
const unsafe = detectUnsafePattern(value);
|
|
117
|
+
if (unsafe) {
|
|
118
|
+
return { accepted: false, reason: unsafe };
|
|
119
|
+
}
|
|
120
|
+
if (!TOKEN_REGEX.test(value)) {
|
|
121
|
+
return { accepted: false, reason: "raw_payload" };
|
|
122
|
+
}
|
|
123
|
+
return { accepted: true, value };
|
|
124
|
+
}
|
|
125
|
+
function checkSemanticFieldValue(key, value) {
|
|
126
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
127
|
+
return { accepted: false, reason: "raw_payload" };
|
|
128
|
+
}
|
|
129
|
+
if (value.length > MAX_SIDE_EFFECT_FIELD_VALUE_LENGTH) {
|
|
130
|
+
return { accepted: false, reason: "value_too_long" };
|
|
131
|
+
}
|
|
132
|
+
const unsafe = detectUnsafePattern(value);
|
|
133
|
+
if (unsafe) {
|
|
134
|
+
return { accepted: false, reason: unsafe };
|
|
135
|
+
}
|
|
136
|
+
if (!passesFieldValidator(key, value)) {
|
|
137
|
+
return { accepted: false, reason: "raw_payload" };
|
|
138
|
+
}
|
|
139
|
+
return { accepted: true, value };
|
|
140
|
+
}
|
|
141
|
+
function checkSemanticFieldKey(key) {
|
|
142
|
+
return typeof key === "string" && SEMANTIC_FIELD_KEY_SET.has(key);
|
|
143
|
+
}
|
|
144
|
+
function checkOperationKind(kind) {
|
|
145
|
+
return typeof kind === "string" && OPERATION_KIND_SET.has(kind);
|
|
146
|
+
}
|
|
147
|
+
function checkOperationStatus(status) {
|
|
148
|
+
return typeof status === "string" && OPERATION_STATUS_SET.has(status);
|
|
149
|
+
}
|
|
150
|
+
function checkOperationPhase(phase) {
|
|
151
|
+
return typeof phase === "string" && OPERATION_PHASE_SET.has(phase);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/side-effect/emit.ts
|
|
155
|
+
var spanState = /* @__PURE__ */ new WeakMap();
|
|
156
|
+
function getOrCreateState(span) {
|
|
157
|
+
let state = spanState.get(span);
|
|
158
|
+
if (!state) {
|
|
159
|
+
state = { operationsRecorded: 0, omissions: /* @__PURE__ */ new Map() };
|
|
160
|
+
spanState.set(span, state);
|
|
161
|
+
}
|
|
162
|
+
return state;
|
|
163
|
+
}
|
|
164
|
+
function getRecordingActiveSpan() {
|
|
165
|
+
let span;
|
|
166
|
+
try {
|
|
167
|
+
span = trace.getActiveSpan();
|
|
168
|
+
} catch {
|
|
169
|
+
return void 0;
|
|
170
|
+
}
|
|
171
|
+
if (!span) return void 0;
|
|
172
|
+
try {
|
|
173
|
+
if (typeof span.isRecording === "function" && !span.isRecording()) {
|
|
174
|
+
return void 0;
|
|
175
|
+
}
|
|
176
|
+
} catch {
|
|
177
|
+
return void 0;
|
|
178
|
+
}
|
|
179
|
+
return span;
|
|
180
|
+
}
|
|
181
|
+
function recordOmissionOnActiveSpan(reason) {
|
|
182
|
+
const span = getRecordingActiveSpan();
|
|
183
|
+
if (!span) return;
|
|
184
|
+
recordOmissionOnSpan(span, reason);
|
|
185
|
+
}
|
|
186
|
+
function recordOmissionOnSpan(span, reason) {
|
|
187
|
+
const state = getOrCreateState(span);
|
|
188
|
+
const previous = state.omissions.get(reason) ?? 0;
|
|
189
|
+
const next = previous + 1;
|
|
190
|
+
state.omissions.set(reason, next);
|
|
191
|
+
const attribute = OMISSION_ATTRIBUTE_BY_REASON[reason];
|
|
192
|
+
try {
|
|
193
|
+
span.setAttribute(attribute, next);
|
|
194
|
+
} catch {
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
var OMISSION_ATTRIBUTE_BY_REASON = {
|
|
198
|
+
pii: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_OMITTED_PII,
|
|
199
|
+
secret: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_OMITTED_SECRET,
|
|
200
|
+
raw_payload: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_OMITTED_RAW_PAYLOAD,
|
|
201
|
+
unsupported_key: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_OMITTED_UNSUPPORTED_KEY,
|
|
202
|
+
value_too_long: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_OMITTED_VALUE_TOO_LONG,
|
|
203
|
+
not_emitted: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_OMITTED_NOT_EMITTED,
|
|
204
|
+
capture_disabled: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_OMITTED_CAPTURE_DISABLED
|
|
205
|
+
};
|
|
206
|
+
var FIELD_ATTRIBUTE_BY_KEY = {
|
|
207
|
+
templateKey: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_FIELD_TEMPLATE_KEY,
|
|
208
|
+
providerOperation: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_FIELD_PROVIDER_OPERATION,
|
|
209
|
+
role: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_FIELD_ROLE,
|
|
210
|
+
locale: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_FIELD_LOCALE,
|
|
211
|
+
timezone: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_FIELD_TIMEZONE,
|
|
212
|
+
status: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_FIELD_STATUS,
|
|
213
|
+
phase: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_FIELD_PHASE
|
|
214
|
+
};
|
|
215
|
+
function attachOperation(input) {
|
|
216
|
+
const span = getRecordingActiveSpan();
|
|
217
|
+
if (!span) return { kind: "no_active_span" };
|
|
218
|
+
const state = getOrCreateState(span);
|
|
219
|
+
if (state.operationsRecorded >= MAX_SIDE_EFFECT_OPERATIONS_PER_SPAN) {
|
|
220
|
+
return { kind: "over_budget", span };
|
|
221
|
+
}
|
|
222
|
+
state.operationsRecorded += 1;
|
|
223
|
+
try {
|
|
224
|
+
span.setAttribute(GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_KIND, input.kind);
|
|
225
|
+
span.setAttribute(
|
|
226
|
+
GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_OPERATION,
|
|
227
|
+
input.operation
|
|
228
|
+
);
|
|
229
|
+
if (input.status !== void 0) {
|
|
230
|
+
span.setAttribute(
|
|
231
|
+
GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_STATUS,
|
|
232
|
+
input.status
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
if (input.phase !== void 0) {
|
|
236
|
+
span.setAttribute(
|
|
237
|
+
GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_PHASE,
|
|
238
|
+
input.phase
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
} catch {
|
|
242
|
+
}
|
|
243
|
+
return { kind: "attached", span };
|
|
244
|
+
}
|
|
245
|
+
function attachField(span, key, value) {
|
|
246
|
+
const attribute = FIELD_ATTRIBUTE_BY_KEY[key];
|
|
247
|
+
try {
|
|
248
|
+
span.setAttribute(attribute, value);
|
|
249
|
+
} catch {
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function recordOmission(span, reason) {
|
|
253
|
+
recordOmissionOnSpan(span, reason);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// src/side-effect/index.ts
|
|
257
|
+
function recordSideEffect(input) {
|
|
258
|
+
try {
|
|
259
|
+
runRecordSideEffect(input);
|
|
260
|
+
} catch {
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function runRecordSideEffect(input) {
|
|
264
|
+
if (!input || typeof input !== "object") return;
|
|
265
|
+
let captureEnabled;
|
|
266
|
+
try {
|
|
267
|
+
captureEnabled = getActiveConfig().sideEffectEvidence === true;
|
|
268
|
+
} catch {
|
|
269
|
+
captureEnabled = false;
|
|
270
|
+
}
|
|
271
|
+
if (!captureEnabled) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
const candidate = input;
|
|
275
|
+
if (!checkOperationKind(candidate.kind)) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const labelOutcome = checkOperationLabel(candidate.operation);
|
|
279
|
+
if (!labelOutcome.accepted) {
|
|
280
|
+
recordOmissionOnActiveSpan(labelOutcome.reason);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
let acceptedStatus;
|
|
284
|
+
if (candidate.status !== void 0) {
|
|
285
|
+
if (checkOperationStatus(candidate.status)) {
|
|
286
|
+
acceptedStatus = candidate.status;
|
|
287
|
+
} else {
|
|
288
|
+
recordOmissionOnActiveSpan("unsupported_key");
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
let acceptedPhase;
|
|
292
|
+
if (candidate.phase !== void 0) {
|
|
293
|
+
if (checkOperationPhase(candidate.phase)) {
|
|
294
|
+
acceptedPhase = candidate.phase;
|
|
295
|
+
} else {
|
|
296
|
+
recordOmissionOnActiveSpan("unsupported_key");
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
const outcome = attachOperation({
|
|
300
|
+
kind: candidate.kind,
|
|
301
|
+
operation: labelOutcome.value,
|
|
302
|
+
status: acceptedStatus,
|
|
303
|
+
phase: acceptedPhase
|
|
304
|
+
});
|
|
305
|
+
if (outcome.kind === "no_active_span") {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (outcome.kind === "over_budget") {
|
|
309
|
+
recordOmission(outcome.span, "value_too_long");
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
const fields = candidate.fields;
|
|
313
|
+
if (fields && typeof fields === "object") {
|
|
314
|
+
for (const [rawKey, rawValue] of Object.entries(fields)) {
|
|
315
|
+
if (!checkSemanticFieldKey(rawKey)) {
|
|
316
|
+
recordOmission(outcome.span, "unsupported_key");
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
const valueOutcome = checkSemanticFieldValue(rawKey, rawValue);
|
|
320
|
+
if (!valueOutcome.accepted) {
|
|
321
|
+
recordOmission(outcome.span, valueOutcome.reason);
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
attachField(outcome.span, rawKey, valueOutcome.value);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
45
328
|
export {
|
|
46
329
|
GlasstraceExporter,
|
|
47
330
|
GlasstraceSpanProcessor,
|
|
@@ -66,6 +349,7 @@ export {
|
|
|
66
349
|
performInit,
|
|
67
350
|
readAnonKey,
|
|
68
351
|
readEnvVars,
|
|
352
|
+
recordSideEffect,
|
|
69
353
|
registerGlasstrace,
|
|
70
354
|
resolveConfig,
|
|
71
355
|
saveCachedConfig,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/side-effect/allowlist.ts","../src/side-effect/emit.ts","../src/side-effect/index.ts"],"sourcesContent":["/**\n * Side-effect evidence allowlist enforcement (SDK-049).\n *\n * Two enforcement layers, both pure functions with no I/O. The SDK\n * runs these before any `glasstrace.side_effect.*` attribute is\n * attached to a span; the product's storage filter (ING-023) is a\n * second defense, not the primary boundary. A value rejected here\n * never reaches the OTel exporter, so it cannot leak through any\n * downstream telemetry path.\n *\n * Layer 1 (input shape):\n * - Reject non-string scalars where strings are required.\n * - Reject lengths exceeding the SCHEMA-036 budgets\n * (operation label > 96 chars, field value > 80 chars) ⇒\n * `value_too_long`.\n * - Reject unsafe-pattern matches (URL, email, headers, tokens,\n * UUIDs, prose-shaped whitespace) ⇒ category-mapped reason.\n *\n * Layer 2 (per-field validators):\n * - `templateKey | providerOperation | role | status | phase` and\n * the operation label require a compact token regex.\n * - `locale` requires a BCP-47-shaped token.\n * - `timezone` requires an IANA-shaped token.\n *\n * The regexes mirror the product-side schema in\n * `glasstrace-product/shared/types/agent-evidence.ts:585-619`\n * verbatim so any value the SDK admits is guaranteed to pass the\n * product's storage-time filter.\n *\n * @drift-check ../../glasstrace-product/shared/types/agent-evidence.ts\n */\n\nimport type {\n SideEffectOmissionReason,\n SideEffectOperationKind,\n SideEffectOperationPhase,\n SideEffectOperationStatus,\n SideEffectSemanticFieldKey,\n} from \"@glasstrace/protocol\";\nimport {\n SIDE_EFFECT_OMISSION_REASONS,\n SIDE_EFFECT_OPERATION_KINDS,\n SIDE_EFFECT_OPERATION_PHASES,\n SIDE_EFFECT_OPERATION_STATUSES,\n SIDE_EFFECT_SEMANTIC_FIELD_KEYS,\n} from \"@glasstrace/protocol\";\n\n/**\n * Maximum length, in characters, of a side-effect operation label.\n * Mirrors `AGENT_EVIDENCE_MAX_SIDE_EFFECT_OPERATION_LABEL_LENGTH`\n * in the product-side schema.\n */\nexport const MAX_SIDE_EFFECT_OPERATION_LABEL_LENGTH = 96;\n\n/**\n * Maximum length, in characters, of a side-effect semantic field\n * value. Mirrors `AGENT_EVIDENCE_MAX_SIDE_EFFECT_FIELD_VALUE_LENGTH`.\n */\nexport const MAX_SIDE_EFFECT_FIELD_VALUE_LENGTH = 80;\n\n/**\n * Maximum number of side-effect operations recorded on a single span\n * before further calls are dropped under the `value_too_long`\n * omission reason. Mirrors\n * `AGENT_EVIDENCE_MAX_SIDE_EFFECT_OPERATIONS`.\n */\nexport const MAX_SIDE_EFFECT_OPERATIONS_PER_SPAN = 5;\n\nconst OPERATION_KIND_SET: ReadonlySet<string> = new Set(\n SIDE_EFFECT_OPERATION_KINDS,\n);\nconst SEMANTIC_FIELD_KEY_SET: ReadonlySet<string> = new Set(\n SIDE_EFFECT_SEMANTIC_FIELD_KEYS,\n);\nconst OPERATION_STATUS_SET: ReadonlySet<string> = new Set(\n SIDE_EFFECT_OPERATION_STATUSES,\n);\nconst OPERATION_PHASE_SET: ReadonlySet<string> = new Set(\n SIDE_EFFECT_OPERATION_PHASES,\n);\nconst OMISSION_REASON_SET: ReadonlySet<string> = new Set(\n SIDE_EFFECT_OMISSION_REASONS,\n);\n\n/**\n * Returns `true` when `reason` is one of the SCHEMA-036 omission\n * reasons. Exposed for tests and any future caller that needs to\n * narrow an arbitrary string to a `SideEffectOmissionReason` before\n * passing it to the emission helpers; the SDK's own emission path\n * always works with statically known literals.\n */\nexport function isKnownOmissionReason(\n reason: string,\n): reason is SideEffectOmissionReason {\n return OMISSION_REASON_SET.has(reason);\n}\n\n// Compact-token regex shared by templateKey/providerOperation/role/status/phase\n// and the operation label. The trailing length guard is enforced\n// separately so the budget rejection produces `value_too_long`\n// (rather than this regex's anchor failure being mis-categorized).\nconst TOKEN_REGEX = /^[A-Za-z0-9][A-Za-z0-9_.:-]*$/;\n\n// BCP-47-shaped locale, mirroring SCHEMA-036's\n// SideEffectLocaleValueSchema regex.\nconst LOCALE_REGEX = /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8}){0,3}$/;\n\n// IANA-shaped timezone, mirroring SCHEMA-036's\n// SideEffectTimezoneValueSchema regex.\nconst TIMEZONE_REGEX =\n /^(?:UTC|GMT|[A-Za-z][A-Za-z0-9_+-]*(?:\\/[A-Za-z0-9_+-]+){1,3})$/;\n\n// Unsafe-pattern detectors. Each rejects independently; the first\n// match determines the omission reason. The patterns and the reason\n// mapping are calibrated against the product's\n// SideEffectUnsafeTextSchema (agent-evidence.ts:563-583) and the\n// SDK-049 §Safety Requirements list.\n\nconst URL_SCHEME = /:\\/\\//;\nconst URL_SCHEME_RELATIVE = /^\\/\\//;\nconst EMAIL_LIKE = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}/i;\nconst QUERY_LIKE = /\\?/;\nconst FRAGMENT_LIKE = /#/;\nconst HEADER_LIKE = /\\b(authorization|set-cookie|cookie)\\b\\s*[:=]/i;\nconst HEADER_TOKEN_LIKE =\n /\\b(authorization|set-cookie|cookie)\\b\\s+\\S+=/i;\nconst BEARER_LIKE = /bearer\\s+\\S+/i;\nconst TOKEN_KV_LIKE =\n /[\"']?(password|passwd|token|api[_-]?key|secret|client_secret)[\"']?\\s*[:=]/i;\nconst PROSE_LIKE = /[\\r\\n\\t]|\\s{2,}/;\nconst UUID_LIKE =\n /[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/i;\nconst GT_KEY_LIKE = /gt_(dev|anon|live)_[A-Za-z0-9_-]+/;\n\n/**\n * Inspect a value for unsafe patterns. Returns the matched omission\n * reason or `null` if the value is shape-clean. The function does NOT\n * apply the per-field token regex; that is Layer 2 below.\n *\n * Detector order matters: URL-shape detectors run before token-shape\n * detectors so a URL with an embedded `token=` query parameter is\n * categorized as `raw_payload` (the structural shape that brought it\n * here was the URL, not the token). Header and bearer-shaped values\n * are categorized as `secret` because they always carry credential\n * material. Email is `pii`. Whitespace anomalies are `raw_payload`.\n */\nfunction detectUnsafePattern(value: string): SideEffectOmissionReason | null {\n // Whitespace anomalies fall into raw_payload because they indicate\n // prose or copy-pasted user content rather than a compact label.\n if (value.trim() !== value) return \"raw_payload\";\n if (PROSE_LIKE.test(value)) return \"raw_payload\";\n\n // URL-shape detectors run first so a credential-bearing query\n // string is categorized by its structural shape (raw_payload)\n // rather than the credential token inside it. The product's\n // SCHEMA-036 filter rejects URL shapes regardless of category, so\n // either choice is safe; the test fixture documents that URL-shape\n // wins.\n if (URL_SCHEME.test(value)) return \"raw_payload\";\n if (URL_SCHEME_RELATIVE.test(value)) return \"raw_payload\";\n if (QUERY_LIKE.test(value)) return \"raw_payload\";\n if (FRAGMENT_LIKE.test(value)) return \"raw_payload\";\n\n // Header-shaped and credential-shaped values route to `secret`\n // because they always carry authentication material. Bearer- and\n // token-key-value-like values are detected even when no header\n // prefix is present.\n if (BEARER_LIKE.test(value)) return \"secret\";\n if (HEADER_TOKEN_LIKE.test(value)) return \"secret\";\n if (TOKEN_KV_LIKE.test(value)) return \"secret\";\n if (HEADER_LIKE.test(value)) return \"secret\";\n if (UUID_LIKE.test(value)) return \"secret\";\n if (GT_KEY_LIKE.test(value)) return \"secret\";\n\n // PII detector.\n if (EMAIL_LIKE.test(value)) return \"pii\";\n\n return null;\n}\n\n/**\n * Run Layer 2 per-field validation. Returns `true` when the value\n * matches the field's regex. Length and shape rejection from Layer 1\n * is the caller's responsibility — by the time this runs the value\n * is known to be a non-empty string within the per-field length\n * budget and free of unsafe patterns.\n */\nfunction passesFieldValidator(\n key: SideEffectSemanticFieldKey,\n value: string,\n): boolean {\n if (key === \"locale\") return LOCALE_REGEX.test(value);\n if (key === \"timezone\") return TIMEZONE_REGEX.test(value);\n return TOKEN_REGEX.test(value);\n}\n\n/**\n * Outcome of allowlist enforcement on a single value.\n */\nexport type ValueOutcome =\n | { accepted: true; value: string }\n | { accepted: false; reason: SideEffectOmissionReason };\n\n/**\n * Check the operation label (`recordSideEffect({ operation })`).\n *\n * Order matters: type rejection ⇒ length budget ⇒ unsafe pattern ⇒\n * field validator. This ordering lets each rejection class produce a\n * meaningful omission reason without the regex anchor failure\n * masking an upstream length or shape problem.\n */\nexport function checkOperationLabel(value: unknown): ValueOutcome {\n if (typeof value !== \"string\" || value.length === 0) {\n return { accepted: false, reason: \"raw_payload\" };\n }\n if (value.length > MAX_SIDE_EFFECT_OPERATION_LABEL_LENGTH) {\n return { accepted: false, reason: \"value_too_long\" };\n }\n const unsafe = detectUnsafePattern(value);\n if (unsafe) {\n return { accepted: false, reason: unsafe };\n }\n if (!TOKEN_REGEX.test(value)) {\n // A value that survives unsafe-pattern detection but still fails\n // the compact-token regex is malformed (e.g., starts with a hyphen\n // or contains a slash). Categorize as `raw_payload` because the\n // value carries shape inconsistent with a normalized label.\n return { accepted: false, reason: \"raw_payload\" };\n }\n return { accepted: true, value };\n}\n\n/**\n * Check a semantic field value for one of the allowlisted keys. The\n * key must already be known to be allowlisted (Layer 1 filters\n * unsupported keys via {@link checkSemanticFieldKey}).\n */\nexport function checkSemanticFieldValue(\n key: SideEffectSemanticFieldKey,\n value: unknown,\n): ValueOutcome {\n if (typeof value !== \"string\" || value.length === 0) {\n return { accepted: false, reason: \"raw_payload\" };\n }\n if (value.length > MAX_SIDE_EFFECT_FIELD_VALUE_LENGTH) {\n return { accepted: false, reason: \"value_too_long\" };\n }\n const unsafe = detectUnsafePattern(value);\n if (unsafe) {\n return { accepted: false, reason: unsafe };\n }\n if (!passesFieldValidator(key, value)) {\n return { accepted: false, reason: \"raw_payload\" };\n }\n return { accepted: true, value };\n}\n\n/**\n * Returns `true` when `key` is allowlisted as a semantic field key.\n * The narrowing predicate lets callers safely route the value to\n * {@link checkSemanticFieldValue}.\n */\nexport function checkSemanticFieldKey(\n key: unknown,\n): key is SideEffectSemanticFieldKey {\n return typeof key === \"string\" && SEMANTIC_FIELD_KEY_SET.has(key);\n}\n\n/**\n * Returns `true` when `kind` is one of the v1 allowlisted operation\n * kinds.\n */\nexport function checkOperationKind(\n kind: unknown,\n): kind is SideEffectOperationKind {\n return typeof kind === \"string\" && OPERATION_KIND_SET.has(kind);\n}\n\n/**\n * Returns `true` when `status` is one of the v1 allowlisted operation\n * statuses. Distinct from the per-field `status` check because the\n * top-level operation status is enforced as an enum membership only —\n * no compact-token regex applies (the SCHEMA-036 enum is the\n * exhaustive accepted set).\n */\nexport function checkOperationStatus(\n status: unknown,\n): status is SideEffectOperationStatus {\n return typeof status === \"string\" && OPERATION_STATUS_SET.has(status);\n}\n\n/**\n * Returns `true` when `phase` is one of the v1 allowlisted operation\n * phases.\n */\nexport function checkOperationPhase(\n phase: unknown,\n): phase is SideEffectOperationPhase {\n return typeof phase === \"string\" && OPERATION_PHASE_SET.has(phase);\n}\n","/**\n * Side-effect evidence attribute emission and per-span counter state\n * (SDK-049).\n *\n * Pure observer: this module never executes a side effect, never\n * creates a span, never throws to the caller. All failure modes route\n * to silent no-ops or omission counters. Per-span state is held in a\n * `WeakMap` keyed by the OTel `Span` object so counters auto-clean\n * when the span is garbage-collected after export.\n */\n\nimport * as otelApi from \"@opentelemetry/api\";\nimport {\n GLASSTRACE_ATTRIBUTE_NAMES,\n type SideEffectOmissionReason,\n type SideEffectOperationKind,\n type SideEffectOperationPhase,\n type SideEffectOperationStatus,\n type SideEffectSemanticFieldKey,\n} from \"@glasstrace/protocol\";\nimport { MAX_SIDE_EFFECT_OPERATIONS_PER_SPAN } from \"./allowlist.js\";\n\n/**\n * Per-span side-effect bookkeeping. `operationsRecorded` enforces the\n * SCHEMA-036 budget of 5 operations per span; `omissions` carries one\n * counter per omission reason so final `glasstrace.side_effect.omitted.*`\n * attributes can be flushed on the span as integer counts.\n */\ninterface SpanSideEffectState {\n operationsRecorded: number;\n omissions: Map<SideEffectOmissionReason, number>;\n}\n\nconst spanState: WeakMap<otelApi.Span, SpanSideEffectState> = new WeakMap();\n\nfunction getOrCreateState(span: otelApi.Span): SpanSideEffectState {\n let state = spanState.get(span);\n if (!state) {\n state = { operationsRecorded: 0, omissions: new Map() };\n spanState.set(span, state);\n }\n return state;\n}\n\n/**\n * Returns the currently-active span when it is recording and not yet\n * ended. Returns `undefined` when there is no active span, when the\n * active span is a `NonRecordingSpan`, or when the active span has\n * already ended. Callers treat all such cases as silent no-op\n * conditions: there is no span on which to attach evidence.\n */\nfunction getRecordingActiveSpan(): otelApi.Span | undefined {\n let span: otelApi.Span | undefined;\n try {\n span = otelApi.trace.getActiveSpan();\n } catch {\n // Defensive: an OTel API surface error must not propagate to the\n // user's side-effect call site.\n return undefined;\n }\n if (!span) return undefined;\n\n // `isRecording()` returns false for both NonRecordingSpan and ended\n // spans on the standard SDK; honor that as the no-op signal. The\n // method is part of the OTel API contract so a missing impl\n // indicates a host shim — fall through to the conservative no-op.\n try {\n if (typeof span.isRecording === \"function\" && !span.isRecording()) {\n return undefined;\n }\n } catch {\n return undefined;\n }\n return span;\n}\n\n/**\n * Record a single omission count on the active span without emitting\n * the rejected value. Rejection metadata only ever leaks the integer\n * count, never the original input.\n *\n * No-op when there is no recording active span — the rejected value\n * still doesn't reach the wire because emission is gated on a span\n * being available.\n */\nexport function recordOmissionOnActiveSpan(\n reason: SideEffectOmissionReason,\n): void {\n const span = getRecordingActiveSpan();\n if (!span) return;\n recordOmissionOnSpan(span, reason);\n}\n\nfunction recordOmissionOnSpan(\n span: otelApi.Span,\n reason: SideEffectOmissionReason,\n): void {\n const state = getOrCreateState(span);\n const previous = state.omissions.get(reason) ?? 0;\n const next = previous + 1;\n state.omissions.set(reason, next);\n\n const attribute = OMISSION_ATTRIBUTE_BY_REASON[reason];\n try {\n span.setAttribute(attribute, next);\n } catch {\n // OTel may reject the attribute write (slot exhaustion, ended\n // span). The counter still advances in-memory; further emission\n // attempts are harmless no-ops.\n }\n}\n\nconst OMISSION_ATTRIBUTE_BY_REASON: Readonly<\n Record<SideEffectOmissionReason, string>\n> = {\n pii: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_OMITTED_PII,\n secret: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_OMITTED_SECRET,\n raw_payload: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_OMITTED_RAW_PAYLOAD,\n unsupported_key:\n GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_OMITTED_UNSUPPORTED_KEY,\n value_too_long:\n GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_OMITTED_VALUE_TOO_LONG,\n not_emitted: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_OMITTED_NOT_EMITTED,\n capture_disabled:\n GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_OMITTED_CAPTURE_DISABLED,\n};\n\nconst FIELD_ATTRIBUTE_BY_KEY: Readonly<\n Record<SideEffectSemanticFieldKey, string>\n> = {\n templateKey: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_FIELD_TEMPLATE_KEY,\n providerOperation:\n GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_FIELD_PROVIDER_OPERATION,\n role: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_FIELD_ROLE,\n locale: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_FIELD_LOCALE,\n timezone: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_FIELD_TIMEZONE,\n status: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_FIELD_STATUS,\n phase: GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_FIELD_PHASE,\n};\n\n/**\n * Outcome of attempting to attach an operation summary. The\n * `over_budget` and `no_active_span` discriminants let the public API\n * route the call's bookkeeping (omission count vs. silent drop)\n * without re-querying span state.\n */\nexport type AttachOutcome =\n | { kind: \"attached\"; span: otelApi.Span }\n | { kind: \"no_active_span\" }\n | { kind: \"over_budget\"; span: otelApi.Span };\n\n/**\n * Attach the top-level operation attributes to the active span and\n * advance the per-span operation counter. The caller is responsible\n * for invoking {@link attachField} for each accepted semantic field\n * and {@link recordOmission} for each rejected value.\n *\n * Returns the span when emission proceeded, `no_active_span` when no\n * recording span is active, or `over_budget` when the per-span\n * operation budget (5) is exhausted. The caller routes `over_budget`\n * to a `value_too_long` omission via {@link recordOmission}.\n */\nexport function attachOperation(input: {\n kind: SideEffectOperationKind;\n operation: string;\n status?: SideEffectOperationStatus;\n phase?: SideEffectOperationPhase;\n}): AttachOutcome {\n const span = getRecordingActiveSpan();\n if (!span) return { kind: \"no_active_span\" };\n\n const state = getOrCreateState(span);\n if (state.operationsRecorded >= MAX_SIDE_EFFECT_OPERATIONS_PER_SPAN) {\n return { kind: \"over_budget\", span };\n }\n state.operationsRecorded += 1;\n\n try {\n span.setAttribute(GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_KIND, input.kind);\n span.setAttribute(\n GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_OPERATION,\n input.operation,\n );\n if (input.status !== undefined) {\n span.setAttribute(\n GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_STATUS,\n input.status,\n );\n }\n if (input.phase !== undefined) {\n span.setAttribute(\n GLASSTRACE_ATTRIBUTE_NAMES.SIDE_EFFECT_PHASE,\n input.phase,\n );\n }\n } catch {\n // Slot exhaustion or ended-span write — ignore. The counter has\n // already advanced; subsequent calls within the same span are\n // budget-bounded as expected.\n }\n return { kind: \"attached\", span };\n}\n\n/**\n * Attach a single allowlisted semantic field to the span. The caller\n * has already routed the field key and value through the allowlist\n * helpers; this function only writes the attribute.\n */\nexport function attachField(\n span: otelApi.Span,\n key: SideEffectSemanticFieldKey,\n value: string,\n): void {\n const attribute = FIELD_ATTRIBUTE_BY_KEY[key];\n try {\n span.setAttribute(attribute, value);\n } catch {\n // Slot exhaustion — ignore.\n }\n}\n\n/**\n * Record an omission directly on a known span. Used by the public\n * API after {@link attachOperation} returns `over_budget` so the\n * count is registered on the same span that observed the operation.\n */\nexport function recordOmission(\n span: otelApi.Span,\n reason: SideEffectOmissionReason,\n): void {\n recordOmissionOnSpan(span, reason);\n}\n","/**\n * Public side-effect evidence emission API (SDK-049).\n *\n * Exposes {@link recordSideEffect} as a single user-callable function\n * that attaches allowlisted, non-sensitive semantic metadata about a\n * side-effect operation (email, calendar_link, webhook, external_api,\n * queue, after_callback) to the current active OTel span.\n *\n * The behavior contract is observational only: this function never\n * executes a side effect, never retries, never delays, never throws.\n * All failure modes (no active span, ended span, NonRecordingSpan,\n * capture-config disabled, allowlist rejection, per-span budget\n * exhausted, OTel attribute slot exhaustion) silently route to a\n * no-op or to an omission-counter increment that carries no rejected\n * input.\n */\n\nimport type {\n SideEffectOperationKind,\n SideEffectOperationPhase,\n SideEffectOperationStatus,\n SideEffectSemanticFieldKey,\n} from \"@glasstrace/protocol\";\nimport {\n checkOperationKind,\n checkOperationLabel,\n checkOperationPhase,\n checkOperationStatus,\n checkSemanticFieldKey,\n checkSemanticFieldValue,\n} from \"./allowlist.js\";\nimport {\n attachField,\n attachOperation,\n recordOmission,\n recordOmissionOnActiveSpan,\n} from \"./emit.js\";\nimport { getActiveConfig } from \"../init-client.js\";\n\n/**\n * Input shape for {@link recordSideEffect}.\n *\n * All fields except `kind` and `operation` are optional. The SDK\n * silently drops unknown fields and unsafe values, surfacing only an\n * integer omission count under the matching\n * `glasstrace.side_effect.omitted.*` attribute on the active span.\n */\nexport interface RecordSideEffectInput {\n /**\n * One of the allowlisted v1 operation kinds. Calls with any other\n * value (typo, unsupported kind, non-string) silently drop without\n * recording an omission, because there is no kind to attach the\n * counter to.\n */\n kind: SideEffectOperationKind;\n\n /**\n * Compact, normalized operation label (max 96 chars). Must match\n * `^[A-Za-z0-9][A-Za-z0-9_.:-]*$`. Free-form prose, URLs, query\n * strings, and email-shaped values are silently dropped and routed\n * to the matching omission counter.\n */\n operation: string;\n\n /**\n * Optional operation lifecycle status. Defaults to omitted. Values\n * outside the v1 allowlist are silently dropped.\n */\n status?: SideEffectOperationStatus;\n\n /**\n * Optional operation execution phase (request / post_response /\n * background / unknown). Defaults to omitted.\n */\n phase?: SideEffectOperationPhase;\n\n /**\n * Optional allowlisted semantic fields. Keys outside the v1\n * allowlist (`templateKey`, `providerOperation`, `role`, `locale`,\n * `timezone`, `status`, `phase`) and values matching unsafe\n * patterns (URLs, emails, tokens, headers, prose-shaped\n * whitespace) are silently dropped and routed to the matching\n * omission counter.\n */\n fields?: Partial<Record<SideEffectSemanticFieldKey, string>>;\n}\n\n/**\n * Record allowlisted side-effect evidence on the current active OTel\n * span (SDK-049).\n *\n * Behavior is observational only: this function never executes,\n * retries, or duplicates a side effect. The default capture-config\n * flag `sideEffectEvidence` is `false`; callers must opt in via\n * account configuration before any attribute reaches the wire.\n *\n * Edge cases (all silent no-ops):\n * - capture-config flag is `false` ⇒ no-op (no allowlist evaluation)\n * - input is not a plain object ⇒ no-op\n * - `kind` is not in the v1 allowlist ⇒ no-op\n * - no active span ⇒ no-op\n * - active span has already ended or is `NonRecordingSpan` ⇒ no-op\n * - per-span operation budget exhausted (5 ops max) ⇒ records a\n * `value_too_long` omission count, no operation attributes\n * - OTel attribute slot exhaustion ⇒ silently drops the attribute\n * write\n *\n * The SDK guards only callers of this function. Direct\n * `span.setAttribute(\"glasstrace.side_effect.<...>\", ...)` writes\n * bypass the SDK and rely on the product's storage filter (ING-023)\n * as the second defense layer; this is intentional defense-in-depth,\n * not a gap.\n *\n * @example Recording a successful cancellation email\n * ```ts\n * import { recordSideEffect } from \"@glasstrace/sdk\";\n *\n * await mailer.send({ to: recipient, template: \"EventCanceledEmail\" });\n * recordSideEffect({\n * kind: \"email\",\n * operation: \"email.send\",\n * status: \"succeeded\",\n * phase: \"request\",\n * fields: {\n * templateKey: \"EventCanceledEmail\",\n * role: \"invitee\",\n * locale: \"en-US\",\n * timezone: \"Europe/Paris\",\n * },\n * });\n * ```\n */\nexport function recordSideEffect(input: RecordSideEffectInput): void {\n try {\n runRecordSideEffect(input);\n } catch {\n // Defense-in-depth: any unexpected throw inside the function\n // (e.g., a host shim mis-implementing OTel API) must not\n // propagate to the user's code path. Behavior-neutrality requires\n // recordSideEffect to be observationally invisible.\n }\n}\n\nfunction runRecordSideEffect(input: unknown): void {\n if (!input || typeof input !== \"object\") return;\n\n // Capture-config gate: read at every call so config rotation takes\n // effect on the next emission without restart. The disk read is\n // cached inside getActiveConfig() so this stays cheap on the hot\n // path.\n let captureEnabled: boolean;\n try {\n captureEnabled = getActiveConfig().sideEffectEvidence === true;\n } catch {\n captureEnabled = false;\n }\n if (!captureEnabled) {\n // Note: we deliberately do NOT increment a `capture_disabled`\n // omission counter for every call. With the flag off, the SDK's\n // contract is \"no allowlist evaluation runs and no allocation\n // happens\" — surfacing a per-call counter would require attaching\n // to a span and would defeat that goal. The\n // `capture_disabled` reason exists for the receiver-side path\n // where ingestion drops attributes due to product-side flag\n // changes after the SDK emitted them.\n return;\n }\n\n const candidate = input as Partial<RecordSideEffectInput>;\n\n if (!checkOperationKind(candidate.kind)) {\n // No `kind` to attach a counter under — silent drop.\n return;\n }\n\n const labelOutcome = checkOperationLabel(candidate.operation);\n if (!labelOutcome.accepted) {\n recordOmissionOnActiveSpan(labelOutcome.reason);\n return;\n }\n\n let acceptedStatus: SideEffectOperationStatus | undefined;\n if (candidate.status !== undefined) {\n if (checkOperationStatus(candidate.status)) {\n acceptedStatus = candidate.status;\n } else {\n recordOmissionOnActiveSpan(\"unsupported_key\");\n }\n }\n\n let acceptedPhase: SideEffectOperationPhase | undefined;\n if (candidate.phase !== undefined) {\n if (checkOperationPhase(candidate.phase)) {\n acceptedPhase = candidate.phase;\n } else {\n recordOmissionOnActiveSpan(\"unsupported_key\");\n }\n }\n\n const outcome = attachOperation({\n kind: candidate.kind,\n operation: labelOutcome.value,\n status: acceptedStatus,\n phase: acceptedPhase,\n });\n\n if (outcome.kind === \"no_active_span\") {\n // No span to record an omission against either — silent drop.\n return;\n }\n if (outcome.kind === \"over_budget\") {\n recordOmission(outcome.span, \"value_too_long\");\n return;\n }\n\n // Process semantic fields. Each rejection routes to an omission\n // count on the same span; accepted values become field attributes.\n const fields = candidate.fields;\n if (fields && typeof fields === \"object\") {\n for (const [rawKey, rawValue] of Object.entries(fields)) {\n if (!checkSemanticFieldKey(rawKey)) {\n recordOmission(outcome.span, \"unsupported_key\");\n continue;\n }\n const valueOutcome = checkSemanticFieldValue(rawKey, rawValue);\n if (!valueOutcome.accepted) {\n recordOmission(outcome.span, valueOutcome.reason);\n continue;\n }\n attachField(outcome.span, rawKey, valueOutcome.value);\n }\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/node-entry.cjs
CHANGED
|
@@ -14594,7 +14594,18 @@ var init_dist = __esm({
|
|
|
14594
14594
|
fullConsoleOutput: external_exports.boolean(),
|
|
14595
14595
|
importGraph: external_exports.boolean(),
|
|
14596
14596
|
consoleErrors: external_exports.boolean().optional().default(false),
|
|
14597
|
-
errorResponseBodies: external_exports.boolean().optional().default(false)
|
|
14597
|
+
errorResponseBodies: external_exports.boolean().optional().default(false),
|
|
14598
|
+
/**
|
|
14599
|
+
* Account opt-in for side-effect evidence emission (SDK-049).
|
|
14600
|
+
*
|
|
14601
|
+
* When `false` (default), `recordSideEffect()` is a silent no-op:
|
|
14602
|
+
* no allowlist evaluation runs and no `glasstrace.side_effect.*`
|
|
14603
|
+
* attribute reaches the wire. When `true`, allowlisted side-effect
|
|
14604
|
+
* metadata is attached to the active OTel span subject to the
|
|
14605
|
+
* client-side allowlist enforcement layered with the product's
|
|
14606
|
+
* storage-time filter as defense-in-depth.
|
|
14607
|
+
*/
|
|
14608
|
+
sideEffectEvidence: external_exports.boolean().optional().default(false)
|
|
14598
14609
|
});
|
|
14599
14610
|
SdkCachedConfigSchema = external_exports.object({
|
|
14600
14611
|
response: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
@@ -14654,7 +14665,37 @@ var init_dist = __esm({
|
|
|
14654
14665
|
TRIGGER_TYPE: "glasstrace.trigger.type",
|
|
14655
14666
|
ELEMENT_FINGERPRINT: "glasstrace.element.fingerprint",
|
|
14656
14667
|
ELEMENT_CONFIDENCE: "glasstrace.element.confidence",
|
|
14657
|
-
TAB_ID: "glasstrace.tab.id"
|
|
14668
|
+
TAB_ID: "glasstrace.tab.id",
|
|
14669
|
+
// Side-effect evidence (SDK-049 / SCHEMA-036).
|
|
14670
|
+
// Top-level operation attributes attached to the active span when a
|
|
14671
|
+
// side-effect is recorded via `recordSideEffect()`. The wire-string
|
|
14672
|
+
// set aligns verbatim with the product-side filter in
|
|
14673
|
+
// `packages/ingestion/src/services/trace-writer.ts`.
|
|
14674
|
+
SIDE_EFFECT_KIND: "glasstrace.side_effect.kind",
|
|
14675
|
+
SIDE_EFFECT_OPERATION: "glasstrace.side_effect.operation",
|
|
14676
|
+
SIDE_EFFECT_STATUS: "glasstrace.side_effect.status",
|
|
14677
|
+
SIDE_EFFECT_PHASE: "glasstrace.side_effect.phase",
|
|
14678
|
+
// Allowlisted semantic field attributes — one per allowlisted key.
|
|
14679
|
+
// Wire keys are camelCase to match the SCHEMA-036 enum members
|
|
14680
|
+
// exactly; the SDK constant names are SCREAMING_SNAKE per the rest
|
|
14681
|
+
// of GLASSTRACE_ATTRIBUTE_NAMES.
|
|
14682
|
+
SIDE_EFFECT_FIELD_TEMPLATE_KEY: "glasstrace.side_effect.field.templateKey",
|
|
14683
|
+
SIDE_EFFECT_FIELD_PROVIDER_OPERATION: "glasstrace.side_effect.field.providerOperation",
|
|
14684
|
+
SIDE_EFFECT_FIELD_ROLE: "glasstrace.side_effect.field.role",
|
|
14685
|
+
SIDE_EFFECT_FIELD_LOCALE: "glasstrace.side_effect.field.locale",
|
|
14686
|
+
SIDE_EFFECT_FIELD_TIMEZONE: "glasstrace.side_effect.field.timezone",
|
|
14687
|
+
SIDE_EFFECT_FIELD_STATUS: "glasstrace.side_effect.field.status",
|
|
14688
|
+
SIDE_EFFECT_FIELD_PHASE: "glasstrace.side_effect.field.phase",
|
|
14689
|
+
// Omission reason attributes — one per allowlisted reason. The
|
|
14690
|
+
// attribute value carries an integer count; the rejected value is
|
|
14691
|
+
// never echoed.
|
|
14692
|
+
SIDE_EFFECT_OMITTED_PII: "glasstrace.side_effect.omitted.pii",
|
|
14693
|
+
SIDE_EFFECT_OMITTED_SECRET: "glasstrace.side_effect.omitted.secret",
|
|
14694
|
+
SIDE_EFFECT_OMITTED_RAW_PAYLOAD: "glasstrace.side_effect.omitted.raw_payload",
|
|
14695
|
+
SIDE_EFFECT_OMITTED_UNSUPPORTED_KEY: "glasstrace.side_effect.omitted.unsupported_key",
|
|
14696
|
+
SIDE_EFFECT_OMITTED_VALUE_TOO_LONG: "glasstrace.side_effect.omitted.value_too_long",
|
|
14697
|
+
SIDE_EFFECT_OMITTED_NOT_EMITTED: "glasstrace.side_effect.omitted.not_emitted",
|
|
14698
|
+
SIDE_EFFECT_OMITTED_CAPTURE_DISABLED: "glasstrace.side_effect.omitted.capture_disabled"
|
|
14658
14699
|
};
|
|
14659
14700
|
DEFAULT_CAPTURE_CONFIG = {
|
|
14660
14701
|
requestBodies: false,
|
|
@@ -14663,7 +14704,8 @@ var init_dist = __esm({
|
|
|
14663
14704
|
fullConsoleOutput: false,
|
|
14664
14705
|
importGraph: false,
|
|
14665
14706
|
consoleErrors: false,
|
|
14666
|
-
errorResponseBodies: false
|
|
14707
|
+
errorResponseBodies: false,
|
|
14708
|
+
sideEffectEvidence: false
|
|
14667
14709
|
};
|
|
14668
14710
|
MAX_SOURCE_MAP_FILE_PATH_LENGTH = 512;
|
|
14669
14711
|
MAX_SOURCE_MAP_FILE_SIZE = 50 * 1024 * 1024;
|
|
@@ -22753,7 +22795,7 @@ function registerGlasstrace(options) {
|
|
|
22753
22795
|
setCoreState(CoreState.REGISTERING);
|
|
22754
22796
|
startRuntimeStateWriter({
|
|
22755
22797
|
projectRoot: process.cwd(),
|
|
22756
|
-
sdkVersion: "1.
|
|
22798
|
+
sdkVersion: "1.4.0"
|
|
22757
22799
|
});
|
|
22758
22800
|
const config2 = resolveConfig(options);
|
|
22759
22801
|
if (config2.verbose) {
|
|
@@ -22920,8 +22962,8 @@ async function backgroundInit(config2, anonKeyForInit, generation) {
|
|
|
22920
22962
|
if (config2.verbose) {
|
|
22921
22963
|
console.info("[glasstrace] Background init firing.");
|
|
22922
22964
|
}
|
|
22923
|
-
const healthReport = collectHealthReport("1.
|
|
22924
|
-
const initResult = await performInit(config2, anonKeyForInit, "1.
|
|
22965
|
+
const healthReport = collectHealthReport("1.4.0");
|
|
22966
|
+
const initResult = await performInit(config2, anonKeyForInit, "1.4.0", healthReport);
|
|
22925
22967
|
if (generation !== registrationGeneration) return;
|
|
22926
22968
|
const currentState = getCoreState();
|
|
22927
22969
|
if (currentState === CoreState.SHUTTING_DOWN || currentState === CoreState.SHUTDOWN) {
|
|
@@ -22944,7 +22986,7 @@ async function backgroundInit(config2, anonKeyForInit, generation) {
|
|
|
22944
22986
|
}
|
|
22945
22987
|
maybeInstallConsoleCapture();
|
|
22946
22988
|
if (didLastInitSucceed()) {
|
|
22947
|
-
startHeartbeat(config2, anonKeyForInit, "1.
|
|
22989
|
+
startHeartbeat(config2, anonKeyForInit, "1.4.0", generation, (newApiKey, accountId) => {
|
|
22948
22990
|
setAuthState(AuthState.CLAIMING);
|
|
22949
22991
|
emitLifecycleEvent("auth:claim_started", { accountId });
|
|
22950
22992
|
setResolvedApiKey(newApiKey);
|