@glasstrace/sdk 1.16.0 → 1.18.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 (68) hide show
  1. package/README.md +44 -3
  2. package/dist/async-context/index.cjs.map +1 -1
  3. package/dist/async-context/index.js +2 -2
  4. package/dist/{capture-error-B0txjNut.d.cts → capture-error-B8qiXFeC.d.cts} +2 -2
  5. package/dist/{capture-error-Dc01rYNR.d.ts → capture-error-BTI6mCH2.d.ts} +2 -2
  6. package/dist/{chunk-WOYJAG7H.js → chunk-BJOZBAP7.js} +3 -3
  7. package/dist/{chunk-AFTCLH77.js → chunk-CN5EP25B.js} +2 -2
  8. package/dist/{chunk-KM4UNN3Q.js → chunk-EVX6D2TX.js} +2 -2
  9. package/dist/{chunk-BGJKEFBN.js → chunk-M3QGJUEI.js} +2 -2
  10. package/dist/{chunk-GBVMPMVV.js → chunk-MKT54VEH.js} +2 -2
  11. package/dist/{chunk-GBVMPMVV.js.map → chunk-MKT54VEH.js.map} +1 -1
  12. package/dist/{chunk-OHSX224U.js → chunk-O7IJP2TQ.js} +2 -2
  13. package/dist/{chunk-T7B752NF.js → chunk-SONZOTBP.js} +11 -3
  14. package/dist/{chunk-T7B752NF.js.map → chunk-SONZOTBP.js.map} +1 -1
  15. package/dist/{chunk-774XIOZG.js → chunk-UOAG72NR.js} +2 -2
  16. package/dist/{chunk-DW3CZDS6.js → chunk-YKE6HJLW.js} +2 -2
  17. package/dist/{chunk-EWW3TZ52.js → chunk-Z2DSC3YI.js} +61 -11
  18. package/dist/{chunk-EWW3TZ52.js.map → chunk-Z2DSC3YI.js.map} +1 -1
  19. package/dist/cli/init.cjs +4 -4
  20. package/dist/cli/init.cjs.map +1 -1
  21. package/dist/cli/init.js +7 -7
  22. package/dist/cli/mcp-add.cjs +1 -1
  23. package/dist/cli/mcp-add.cjs.map +1 -1
  24. package/dist/cli/mcp-add.js +3 -3
  25. package/dist/cli/uninit.js +3 -3
  26. package/dist/cli/upgrade-instructions.cjs +1 -1
  27. package/dist/cli/upgrade-instructions.js +3 -3
  28. package/dist/cli/validate.cjs.map +1 -1
  29. package/dist/cli/validate.js +2 -2
  30. package/dist/{correlation-id-CelUvw7j.d.cts → correlation-id-CClOq8Wn.d.cts} +1 -1
  31. package/dist/{correlation-id-B9YYmoZw.d.ts → correlation-id-Ct86Ug4s.d.ts} +1 -1
  32. package/dist/edge-entry.cjs.map +1 -1
  33. package/dist/edge-entry.d.cts +2 -2
  34. package/dist/edge-entry.d.ts +2 -2
  35. package/dist/edge-entry.js +4 -4
  36. package/dist/{import-graph-Dka_Fm7j.d.ts → import-graph-DZjTJdJ5.d.ts} +1 -1
  37. package/dist/{import-graph-DBLGNjcI.d.cts → import-graph-DyQfZU2f.d.cts} +1 -1
  38. package/dist/index.cjs +202 -7
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/{index.d-3-cJoY8y.d.cts → index.d-DhatN7mq.d.cts} +1 -1
  41. package/dist/{index.d-3-cJoY8y.d.ts → index.d-DhatN7mq.d.ts} +1 -1
  42. package/dist/index.d.cts +243 -5
  43. package/dist/index.d.ts +243 -5
  44. package/dist/index.js +151 -6
  45. package/dist/index.js.map +1 -1
  46. package/dist/middleware/index.cjs.map +1 -1
  47. package/dist/middleware/index.js +2 -2
  48. package/dist/node-entry.cjs +5 -5
  49. package/dist/node-entry.cjs.map +1 -1
  50. package/dist/node-entry.d.cts +4 -4
  51. package/dist/node-entry.d.ts +4 -4
  52. package/dist/node-entry.js +7 -7
  53. package/dist/node-subpath.cjs.map +1 -1
  54. package/dist/node-subpath.d.cts +2 -2
  55. package/dist/node-subpath.d.ts +2 -2
  56. package/dist/node-subpath.js +3 -3
  57. package/dist/{source-map-uploader-UJPZCUFN.js → source-map-uploader-U7SLSKIZ.js} +3 -3
  58. package/dist/trpc/index.cjs.map +1 -1
  59. package/dist/trpc/index.js +1 -1
  60. package/package.json +1 -1
  61. /package/dist/{chunk-WOYJAG7H.js.map → chunk-BJOZBAP7.js.map} +0 -0
  62. /package/dist/{chunk-AFTCLH77.js.map → chunk-CN5EP25B.js.map} +0 -0
  63. /package/dist/{chunk-KM4UNN3Q.js.map → chunk-EVX6D2TX.js.map} +0 -0
  64. /package/dist/{chunk-BGJKEFBN.js.map → chunk-M3QGJUEI.js.map} +0 -0
  65. /package/dist/{chunk-OHSX224U.js.map → chunk-O7IJP2TQ.js.map} +0 -0
  66. /package/dist/{chunk-774XIOZG.js.map → chunk-UOAG72NR.js.map} +0 -0
  67. /package/dist/{chunk-DW3CZDS6.js.map → chunk-YKE6HJLW.js.map} +0 -0
  68. /package/dist/{source-map-uploader-UJPZCUFN.js.map → source-map-uploader-U7SLSKIZ.js.map} +0 -0
@@ -324,4 +324,4 @@ declare const SIDE_EFFECT_OPERATION_PHASES: readonly ["request", "post_response"
324
324
  */
325
325
  type SideEffectOperationPhase = (typeof SIDE_EFFECT_OPERATION_PHASES)[number];
326
326
 
327
- export { type AnonApiKey as A, type CaptureConfig as C, type GlasstraceEnvVars as G, type ImportGraphPayload as I, type SideEffectOperationKind as S, type SideEffectOperationStatus as a, type SideEffectOperationPhase as b, type SideEffectSemanticFieldKey as c, deriveSessionId as d, type SideEffectOmissionReason as e, type SideEffectSemanticFieldStableCoreKey as f, type SourceMapUploadResponse as g, type SourceMapManifestResponse as h, isSideEffectSemanticFieldKey as i, type SdkDiagnosticCode as j, type SessionId as k, type GlasstraceOptions as l, type SdkInitResponse as m, type SdkHealthReport as n };
327
+ export { type AnonApiKey as A, type CaptureConfig as C, type GlasstraceEnvVars as G, type ImportGraphPayload as I, type SourceMapUploadResponse as S, type SourceMapManifestResponse as a, type SideEffectOperationKind as b, type SideEffectOperationStatus as c, deriveSessionId as d, type SideEffectOperationPhase as e, type SideEffectSemanticFieldKey as f, type SideEffectOmissionReason as g, type SideEffectSemanticFieldStableCoreKey as h, isSideEffectSemanticFieldKey as i, type SdkDiagnosticCode as j, type SessionId as k, type GlasstraceOptions as l, type SdkInitResponse as m, type SdkHealthReport as n };
@@ -324,4 +324,4 @@ declare const SIDE_EFFECT_OPERATION_PHASES: readonly ["request", "post_response"
324
324
  */
325
325
  type SideEffectOperationPhase = (typeof SIDE_EFFECT_OPERATION_PHASES)[number];
326
326
 
327
- export { type AnonApiKey as A, type CaptureConfig as C, type GlasstraceEnvVars as G, type ImportGraphPayload as I, type SideEffectOperationKind as S, type SideEffectOperationStatus as a, type SideEffectOperationPhase as b, type SideEffectSemanticFieldKey as c, deriveSessionId as d, type SideEffectOmissionReason as e, type SideEffectSemanticFieldStableCoreKey as f, type SourceMapUploadResponse as g, type SourceMapManifestResponse as h, isSideEffectSemanticFieldKey as i, type SdkDiagnosticCode as j, type SessionId as k, type GlasstraceOptions as l, type SdkInitResponse as m, type SdkHealthReport as n };
327
+ export { type AnonApiKey as A, type CaptureConfig as C, type GlasstraceEnvVars as G, type ImportGraphPayload as I, type SourceMapUploadResponse as S, type SourceMapManifestResponse as a, type SideEffectOperationKind as b, type SideEffectOperationStatus as c, deriveSessionId as d, type SideEffectOperationPhase as e, type SideEffectSemanticFieldKey as f, type SideEffectOmissionReason as g, type SideEffectSemanticFieldStableCoreKey as h, isSideEffectSemanticFieldKey as i, type SdkDiagnosticCode as j, type SessionId as k, type GlasstraceOptions as l, type SdkInitResponse as m, type SdkHealthReport as n };
package/dist/index.d.cts CHANGED
@@ -1,7 +1,8 @@
1
- export { C as CorrelationIdRequest, G as GlasstraceSpanProcessor, S as SdkError, a as SessionManager, c as captureCorrelationId, g as getDateString, b as getOrigin } from './correlation-id-CelUvw7j.cjs';
2
- export { F as FetchTarget, G as GlasstraceExporter, a as GlasstraceExporterOptions, I as InitClaimResult, R as ResolvedConfig, c as captureError, b as classifyFetchTarget, d as createGlasstraceSpanProcessor, g as getActiveConfig, e as getDiscoveryHandler, f as getLinkedAccountId, h as getOrCreateAnonKey, i as getStatus, j as isAnonymousMode, k as isProductionDisabled, l as isReady, m as loadCachedConfig, p as performInit, r as readAnonKey, n as readEnvVars, o as registerGlasstrace, q as resolveConfig, s as saveCachedConfig, t as sendInitRequest, w as waitForReady, u as withGlasstraceConfig } from './capture-error-B0txjNut.cjs';
3
- import { S as SideEffectOperationKind, a as SideEffectOperationStatus, b as SideEffectOperationPhase, c as SideEffectSemanticFieldKey } from './index.d-3-cJoY8y.cjs';
4
- export { e as SideEffectOmissionReason, f as SideEffectSemanticFieldStableCoreKey, d as deriveSessionId, i as isSideEffectSemanticFieldKey } from './index.d-3-cJoY8y.cjs';
1
+ export { C as CorrelationIdRequest, G as GlasstraceSpanProcessor, S as SdkError, a as SessionManager, c as captureCorrelationId, g as getDateString, b as getOrigin } from './correlation-id-CClOq8Wn.cjs';
2
+ export { F as FetchTarget, G as GlasstraceExporter, a as GlasstraceExporterOptions, I as InitClaimResult, R as ResolvedConfig, c as captureError, b as classifyFetchTarget, d as createGlasstraceSpanProcessor, g as getActiveConfig, e as getDiscoveryHandler, f as getLinkedAccountId, h as getOrCreateAnonKey, i as getStatus, j as isAnonymousMode, k as isProductionDisabled, l as isReady, m as loadCachedConfig, p as performInit, r as readAnonKey, n as readEnvVars, o as registerGlasstrace, q as resolveConfig, s as saveCachedConfig, t as sendInitRequest, w as waitForReady, u as withGlasstraceConfig } from './capture-error-B8qiXFeC.cjs';
3
+ import { b as SideEffectOperationKind, c as SideEffectOperationStatus, e as SideEffectOperationPhase, f as SideEffectSemanticFieldKey } from './index.d-DhatN7mq.cjs';
4
+ export { g as SideEffectOmissionReason, h as SideEffectSemanticFieldStableCoreKey, d as deriveSessionId, i as isSideEffectSemanticFieldKey } from './index.d-DhatN7mq.cjs';
5
+ import { Span } from './trace/span';
5
6
  export { RequestMiddlewareFunction, TracedRequestMiddlewareOptions } from './middleware/index.cjs';
6
7
  export { WithAsyncCausalityOptions } from './async-context/index.cjs';
7
8
  import './export/ReadableSpan';
@@ -90,6 +91,20 @@ interface RecordSideEffectInput {
90
91
  * prefix), so a categorical and a scalar facet can share a name.
91
92
  */
92
93
  scalars?: Record<string, unknown>;
94
+ /**
95
+ * Optional producer-asserted boolean relations (invariants) emitted on
96
+ * the categorical field channel. Keys must be camelCase ending in
97
+ * `Holds` (e.g. `timezonePreservedHolds`); values are real `boolean`s,
98
+ * coerced to `"true"`/`"false"` on the wire. A non-`Holds` key, a
99
+ * non-boolean value, or a key already attached by `fields` (a
100
+ * same-channel collision — `fields` wins) is dropped with the matching
101
+ * omission counter. Relations count against the same product-side
102
+ * per-operation field budget as `fields` (enforced at projection).
103
+ *
104
+ * Use {@link invariant} / {@link isNullInvariant} to compute the
105
+ * boolean from a comparison.
106
+ */
107
+ relations?: Record<string, boolean>;
93
108
  }
94
109
  /**
95
110
  * Record allowlisted side-effect evidence on the current active OTel
@@ -143,4 +158,227 @@ interface RecordSideEffectInput {
143
158
  */
144
159
  declare function recordSideEffect(input: RecordSideEffectInput): void;
145
160
 
146
- export { type RecordSideEffectInput, SideEffectOperationKind, SideEffectOperationPhase, SideEffectOperationStatus, SideEffectSemanticFieldKey, recordSideEffect };
161
+ /**
162
+ * Public value-capture primitive (L1 passive capture).
163
+ *
164
+ * {@link capture} emits a single allowlisted value-fidelity scalar onto a
165
+ * caller-**owned** OTel span. It is the counterpart to {@link
166
+ * recordSideEffect} for the case where the emitter owns the target span
167
+ * itself (e.g. a passive database adapter that opens a `db.<Model>.<op>`
168
+ * span) rather than attaching to the ambient active span.
169
+ *
170
+ * Why this exists: {@link recordSideEffect} is ambient-only — it resolves
171
+ * the active span via `getActiveSpan()`. A passive adapter cannot use it,
172
+ * because at its capture point the ambient span is the database client's
173
+ * own operation span, which has already ended and is non-recording. So the
174
+ * adapter must own a fresh recording span and emit onto it explicitly.
175
+ *
176
+ * The behavior contract mirrors {@link recordSideEffect}: observational
177
+ * only. `capture` never executes a side effect, never reads or mutates the
178
+ * captured value's source, and **never throws**. Every failure mode
179
+ * (capture-config disabled, ended / `NonRecordingSpan` target, allowlist
180
+ * rejection, OTel attribute-slot exhaustion) routes to a silent no-op or to
181
+ * an omission-counter increment that carries no rejected input.
182
+ *
183
+ * Capture is **strict-mode only**: timestamp-shaped and unhashed-identifier
184
+ * values are rejected at emit so they never reach the wire. The `full`
185
+ * fidelity relaxation is not reachable through this primitive.
186
+ */
187
+
188
+ /** Options for {@link capture}. */
189
+ interface CaptureOptions {
190
+ /**
191
+ * The caller-owned, recording span to attach the scalar to. `capture`
192
+ * writes only to this span; it never resolves or touches the ambient
193
+ * active span. The caller is responsible for `end()`-ing it.
194
+ */
195
+ span: Span;
196
+ }
197
+ /**
198
+ * Emit a single allowlisted value-fidelity scalar onto a caller-owned span.
199
+ *
200
+ * The scalar `name` must match the value-fidelity scalar key pattern
201
+ * (`*Ms` / `*Amount` / `*Bytes` / `*Ratio` / `*Id` / `*Value` / `*Flag`)
202
+ * and its value must match the suffix type — e.g. a `*Flag` key requires a
203
+ * `boolean`. Mismatches, timestamp-shaped values, and unhashed `*Id`s are
204
+ * rejected under strict mode and recorded as an omission count on the
205
+ * supplied span (never the active span).
206
+ *
207
+ * Edge cases (all silent no-ops, never throws):
208
+ * - capture-config flag `sideEffectEvidence` is `false` ⇒ no-op, **no
209
+ * counter** (mirrors {@link recordSideEffect}: with capture disabled the
210
+ * SDK does no allowlist evaluation and writes nothing).
211
+ * - the supplied span has already ended or is a `NonRecordingSpan` ⇒
212
+ * no-op, **no counter** (its omission counter would itself be a dropped
213
+ * span write). The owning adapter passes a fresh recording span, so this
214
+ * is a caller-misuse guard.
215
+ * - the value fails strict allowlist validation ⇒ an omission count is
216
+ * recorded on the supplied span; the rejected value is never emitted.
217
+ * - OTel attribute-slot exhaustion ⇒ the attribute write is silently
218
+ * dropped.
219
+ *
220
+ * @example Project a boolean result field onto an owned database span
221
+ * ```ts
222
+ * import { capture } from "@glasstrace/sdk";
223
+ *
224
+ * const span = tracer.startSpan("db.Poll.findUnique");
225
+ * try {
226
+ * const row = await query(args);
227
+ * if (row) capture("mutedFlag", row.muted, { span });
228
+ * return row;
229
+ * } finally {
230
+ * span.end();
231
+ * }
232
+ * ```
233
+ */
234
+ declare function capture(name: string, value: unknown, options: CaptureOptions): void;
235
+
236
+ /**
237
+ * Passive Prisma value-capture adapter (L1 capture).
238
+ *
239
+ * `prismaAdapter({ allow })` returns a Prisma client extension that, for
240
+ * each allowlisted `(model, column)`, projects a boolean result field onto
241
+ * a Glasstrace value-fidelity scalar so an agent can read it back from the
242
+ * trace. It is **passive and observational**: it never executes a query
243
+ * itself, never reads or mutates the result, and never changes query
244
+ * behavior or errors.
245
+ *
246
+ * Apply it like any Prisma extension:
247
+ *
248
+ * ```ts
249
+ * import { prismaAdapter } from "@glasstrace/sdk";
250
+ *
251
+ * const prisma = new PrismaClient().$extends(
252
+ * prismaAdapter({ allow: [{ model: "Poll", column: "muted" }] }),
253
+ * );
254
+ * ```
255
+ *
256
+ * Design:
257
+ * - **OWN a span.** At the capture point the database client's own
258
+ * operation span has already ended, so the adapter opens its own
259
+ * recording `db.<Model>.<op>` span (parented under the active request
260
+ * span) and emits onto it via {@link capture}.
261
+ * - **Default-deny.** Nothing is captured unless an explicit `allow` entry
262
+ * matches AND the server-pushed `sideEffectEvidence` capture flag is on.
263
+ * An empty / unset `allow` captures nothing.
264
+ * - **Boolean only.** This adapter projects boolean columns (the strict,
265
+ * no-`captureFidelity:full` case). Non-boolean allowlisted columns route
266
+ * to a safe omission counter, never a captured value.
267
+ * - **Pure observer.** Capture work can never throw into the host query;
268
+ * the owned span is always ended; the original query error is re-thrown
269
+ * verbatim.
270
+ * - **Bounded.** `findMany` / list operations are disabled (no per-row
271
+ * capture). The adapter never widens the app's `select`.
272
+ *
273
+ * This module has **no dependency on `@prisma/client`** — it is typed
274
+ * structurally against Prisma's client-extension shape (mirroring the
275
+ * Drizzle adapter), so it adds no runtime dependency and ships on the edge-
276
+ * safe root barrel. On a runtime with no active request span (e.g. an edge
277
+ * runtime with no AsyncLocalStorage), it captures nothing.
278
+ */
279
+ /** The arguments Prisma passes to a `$allOperations` query-extension callback. */
280
+ interface PrismaAllOperationsArgs {
281
+ /** The Prisma model name (PascalCase, e.g. `Poll`), or `undefined` for raw ops. */
282
+ model?: string;
283
+ /** The Prisma operation (e.g. `findUnique`, `findMany`, `update`). */
284
+ operation: string;
285
+ /** The operation arguments, forwarded unchanged to `query`. */
286
+ args: unknown;
287
+ /** Executes the underlying operation. Called exactly once. */
288
+ query: (args: unknown) => Promise<unknown>;
289
+ }
290
+ /**
291
+ * A Prisma client extension — the object passed to `prisma.$extends(...)`.
292
+ * Structurally typed so the adapter needs no `@prisma/client` dependency.
293
+ */
294
+ interface PrismaCaptureExtension {
295
+ name: string;
296
+ query: {
297
+ $allModels: {
298
+ $allOperations(args: PrismaAllOperationsArgs): Promise<unknown>;
299
+ };
300
+ };
301
+ }
302
+ /** A single allowlisted column to project. */
303
+ interface PrismaCaptureColumn {
304
+ /** The Prisma model name, PascalCase, exactly as Prisma reports it (e.g. `Poll`). */
305
+ model: string;
306
+ /** The boolean result column to project (e.g. `muted`). */
307
+ column: string;
308
+ }
309
+ /** Options for {@link prismaAdapter}. */
310
+ interface PrismaAdapterOptions {
311
+ /**
312
+ * The default-deny allowlist. Only `(model, column)` pairs listed here are
313
+ * eligible for capture; an empty or unset list captures nothing. The
314
+ * server-side per-tenant allowlist re-enforces this independently at
315
+ * ingestion.
316
+ */
317
+ allow?: ReadonlyArray<PrismaCaptureColumn>;
318
+ }
319
+ /**
320
+ * Build a passive Prisma value-capture extension. See the module doc for
321
+ * the full behavior contract.
322
+ */
323
+ declare function prismaAdapter(options?: PrismaAdapterOptions): PrismaCaptureExtension;
324
+
325
+ /**
326
+ * Producer-sugar for computing boolean relations to emit as `*Holds`
327
+ * side-effect evidence.
328
+ *
329
+ * These helpers turn a comparison into the boolean a producer passes to
330
+ * `recordSideEffect({ relations: { …Holds: invariant(a, "eq", b) } })`.
331
+ * They are pure (no I/O, no Node built-ins) and edge-safe, so they live
332
+ * on the root barrel. The operator set is intentionally minimal and
333
+ * fixed — six binary comparisons plus a separate unary null check — and
334
+ * is not a general expression DSL.
335
+ */
336
+ /**
337
+ * The six supported binary comparison operators. `isNull` is **not** an
338
+ * operator here — use {@link isNullInvariant} for the unary case.
339
+ */
340
+ type InvariantOp = "eq" | "neq" | "lt" | "lte" | "gt" | "gte";
341
+ /**
342
+ * Evaluate a binary comparison invariant and return the boolean result.
343
+ *
344
+ * Both operands are constrained to the same primitive type. `eq`/`neq`
345
+ * use strict equality; the ordering operators (`lt`/`lte`/`gt`/`gte`)
346
+ * use the language relational operators (numeric for numbers/bigints,
347
+ * lexical for strings). Intended for producing a `*Holds` relation, e.g.
348
+ * `invariant(emittedDurationMinutes, "eq", declaredDurationMinutes)`.
349
+ *
350
+ * Operands should be comparable primitives. `NaN` follows IEEE-754
351
+ * (unequal to everything; all orderings `false`), so screen `NaN` before
352
+ * asserting a relation. Passing a non-primitive (e.g. a `Symbol`, or an
353
+ * object with a throwing `valueOf`) to an ordering operator throws per
354
+ * JS semantics — the type signature prevents this for typed callers.
355
+ *
356
+ * @param left - The left operand.
357
+ * @param op - One of the six {@link InvariantOp} comparisons.
358
+ * @param right - The right operand (same primitive type as `left`).
359
+ * @returns The boolean result of `left <op> right`.
360
+ *
361
+ * @example
362
+ * recordSideEffect({
363
+ * kind: "calendar_link",
364
+ * operation: "invite.create",
365
+ * relations: {
366
+ * durationMatchesHolds: invariant(emittedMinutes, "eq", declaredMinutes),
367
+ * },
368
+ * });
369
+ */
370
+ declare function invariant<T extends number | string | bigint | boolean>(left: T, op: InvariantOp, right: T): boolean;
371
+ /**
372
+ * Unary null/undefined invariant — `true` when `value` is `null` or
373
+ * `undefined`. Kept separate from {@link invariant} because nullishness
374
+ * is a unary predicate, not a binary comparison (there is no `isNull`
375
+ * operator). Use for a `*Holds` relation asserting a value's absence,
376
+ * e.g. `relations: { recipientMissingHolds: isNullInvariant(recipient) }`.
377
+ *
378
+ * @param value - The value to test.
379
+ * @returns `true` when `value` is `null` or `undefined`, else `false`
380
+ * (falsy-but-present values like `0`, `""`, `false`, `NaN` are `false`).
381
+ */
382
+ declare function isNullInvariant(value: unknown): boolean;
383
+
384
+ export { type CaptureOptions, type InvariantOp, type PrismaAdapterOptions, type PrismaCaptureColumn, type PrismaCaptureExtension, type RecordSideEffectInput, SideEffectOperationKind, SideEffectOperationPhase, SideEffectOperationStatus, SideEffectSemanticFieldKey, capture, invariant, isNullInvariant, prismaAdapter, recordSideEffect };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- export { C as CorrelationIdRequest, G as GlasstraceSpanProcessor, S as SdkError, a as SessionManager, c as captureCorrelationId, g as getDateString, b as getOrigin } from './correlation-id-B9YYmoZw.js';
2
- export { F as FetchTarget, G as GlasstraceExporter, a as GlasstraceExporterOptions, I as InitClaimResult, R as ResolvedConfig, c as captureError, b as classifyFetchTarget, d as createGlasstraceSpanProcessor, g as getActiveConfig, e as getDiscoveryHandler, f as getLinkedAccountId, h as getOrCreateAnonKey, i as getStatus, j as isAnonymousMode, k as isProductionDisabled, l as isReady, m as loadCachedConfig, p as performInit, r as readAnonKey, n as readEnvVars, o as registerGlasstrace, q as resolveConfig, s as saveCachedConfig, t as sendInitRequest, w as waitForReady, u as withGlasstraceConfig } from './capture-error-Dc01rYNR.js';
3
- import { S as SideEffectOperationKind, a as SideEffectOperationStatus, b as SideEffectOperationPhase, c as SideEffectSemanticFieldKey } from './index.d-3-cJoY8y.js';
4
- export { e as SideEffectOmissionReason, f as SideEffectSemanticFieldStableCoreKey, d as deriveSessionId, i as isSideEffectSemanticFieldKey } from './index.d-3-cJoY8y.js';
1
+ export { C as CorrelationIdRequest, G as GlasstraceSpanProcessor, S as SdkError, a as SessionManager, c as captureCorrelationId, g as getDateString, b as getOrigin } from './correlation-id-Ct86Ug4s.js';
2
+ export { F as FetchTarget, G as GlasstraceExporter, a as GlasstraceExporterOptions, I as InitClaimResult, R as ResolvedConfig, c as captureError, b as classifyFetchTarget, d as createGlasstraceSpanProcessor, g as getActiveConfig, e as getDiscoveryHandler, f as getLinkedAccountId, h as getOrCreateAnonKey, i as getStatus, j as isAnonymousMode, k as isProductionDisabled, l as isReady, m as loadCachedConfig, p as performInit, r as readAnonKey, n as readEnvVars, o as registerGlasstrace, q as resolveConfig, s as saveCachedConfig, t as sendInitRequest, w as waitForReady, u as withGlasstraceConfig } from './capture-error-BTI6mCH2.js';
3
+ import { b as SideEffectOperationKind, c as SideEffectOperationStatus, e as SideEffectOperationPhase, f as SideEffectSemanticFieldKey } from './index.d-DhatN7mq.js';
4
+ export { g as SideEffectOmissionReason, h as SideEffectSemanticFieldStableCoreKey, d as deriveSessionId, i as isSideEffectSemanticFieldKey } from './index.d-DhatN7mq.js';
5
+ import { Span } from './trace/span';
5
6
  export { RequestMiddlewareFunction, TracedRequestMiddlewareOptions } from './middleware/index.js';
6
7
  export { WithAsyncCausalityOptions } from './async-context/index.js';
7
8
  import './export/ReadableSpan';
@@ -90,6 +91,20 @@ interface RecordSideEffectInput {
90
91
  * prefix), so a categorical and a scalar facet can share a name.
91
92
  */
92
93
  scalars?: Record<string, unknown>;
94
+ /**
95
+ * Optional producer-asserted boolean relations (invariants) emitted on
96
+ * the categorical field channel. Keys must be camelCase ending in
97
+ * `Holds` (e.g. `timezonePreservedHolds`); values are real `boolean`s,
98
+ * coerced to `"true"`/`"false"` on the wire. A non-`Holds` key, a
99
+ * non-boolean value, or a key already attached by `fields` (a
100
+ * same-channel collision — `fields` wins) is dropped with the matching
101
+ * omission counter. Relations count against the same product-side
102
+ * per-operation field budget as `fields` (enforced at projection).
103
+ *
104
+ * Use {@link invariant} / {@link isNullInvariant} to compute the
105
+ * boolean from a comparison.
106
+ */
107
+ relations?: Record<string, boolean>;
93
108
  }
94
109
  /**
95
110
  * Record allowlisted side-effect evidence on the current active OTel
@@ -143,4 +158,227 @@ interface RecordSideEffectInput {
143
158
  */
144
159
  declare function recordSideEffect(input: RecordSideEffectInput): void;
145
160
 
146
- export { type RecordSideEffectInput, SideEffectOperationKind, SideEffectOperationPhase, SideEffectOperationStatus, SideEffectSemanticFieldKey, recordSideEffect };
161
+ /**
162
+ * Public value-capture primitive (L1 passive capture).
163
+ *
164
+ * {@link capture} emits a single allowlisted value-fidelity scalar onto a
165
+ * caller-**owned** OTel span. It is the counterpart to {@link
166
+ * recordSideEffect} for the case where the emitter owns the target span
167
+ * itself (e.g. a passive database adapter that opens a `db.<Model>.<op>`
168
+ * span) rather than attaching to the ambient active span.
169
+ *
170
+ * Why this exists: {@link recordSideEffect} is ambient-only — it resolves
171
+ * the active span via `getActiveSpan()`. A passive adapter cannot use it,
172
+ * because at its capture point the ambient span is the database client's
173
+ * own operation span, which has already ended and is non-recording. So the
174
+ * adapter must own a fresh recording span and emit onto it explicitly.
175
+ *
176
+ * The behavior contract mirrors {@link recordSideEffect}: observational
177
+ * only. `capture` never executes a side effect, never reads or mutates the
178
+ * captured value's source, and **never throws**. Every failure mode
179
+ * (capture-config disabled, ended / `NonRecordingSpan` target, allowlist
180
+ * rejection, OTel attribute-slot exhaustion) routes to a silent no-op or to
181
+ * an omission-counter increment that carries no rejected input.
182
+ *
183
+ * Capture is **strict-mode only**: timestamp-shaped and unhashed-identifier
184
+ * values are rejected at emit so they never reach the wire. The `full`
185
+ * fidelity relaxation is not reachable through this primitive.
186
+ */
187
+
188
+ /** Options for {@link capture}. */
189
+ interface CaptureOptions {
190
+ /**
191
+ * The caller-owned, recording span to attach the scalar to. `capture`
192
+ * writes only to this span; it never resolves or touches the ambient
193
+ * active span. The caller is responsible for `end()`-ing it.
194
+ */
195
+ span: Span;
196
+ }
197
+ /**
198
+ * Emit a single allowlisted value-fidelity scalar onto a caller-owned span.
199
+ *
200
+ * The scalar `name` must match the value-fidelity scalar key pattern
201
+ * (`*Ms` / `*Amount` / `*Bytes` / `*Ratio` / `*Id` / `*Value` / `*Flag`)
202
+ * and its value must match the suffix type — e.g. a `*Flag` key requires a
203
+ * `boolean`. Mismatches, timestamp-shaped values, and unhashed `*Id`s are
204
+ * rejected under strict mode and recorded as an omission count on the
205
+ * supplied span (never the active span).
206
+ *
207
+ * Edge cases (all silent no-ops, never throws):
208
+ * - capture-config flag `sideEffectEvidence` is `false` ⇒ no-op, **no
209
+ * counter** (mirrors {@link recordSideEffect}: with capture disabled the
210
+ * SDK does no allowlist evaluation and writes nothing).
211
+ * - the supplied span has already ended or is a `NonRecordingSpan` ⇒
212
+ * no-op, **no counter** (its omission counter would itself be a dropped
213
+ * span write). The owning adapter passes a fresh recording span, so this
214
+ * is a caller-misuse guard.
215
+ * - the value fails strict allowlist validation ⇒ an omission count is
216
+ * recorded on the supplied span; the rejected value is never emitted.
217
+ * - OTel attribute-slot exhaustion ⇒ the attribute write is silently
218
+ * dropped.
219
+ *
220
+ * @example Project a boolean result field onto an owned database span
221
+ * ```ts
222
+ * import { capture } from "@glasstrace/sdk";
223
+ *
224
+ * const span = tracer.startSpan("db.Poll.findUnique");
225
+ * try {
226
+ * const row = await query(args);
227
+ * if (row) capture("mutedFlag", row.muted, { span });
228
+ * return row;
229
+ * } finally {
230
+ * span.end();
231
+ * }
232
+ * ```
233
+ */
234
+ declare function capture(name: string, value: unknown, options: CaptureOptions): void;
235
+
236
+ /**
237
+ * Passive Prisma value-capture adapter (L1 capture).
238
+ *
239
+ * `prismaAdapter({ allow })` returns a Prisma client extension that, for
240
+ * each allowlisted `(model, column)`, projects a boolean result field onto
241
+ * a Glasstrace value-fidelity scalar so an agent can read it back from the
242
+ * trace. It is **passive and observational**: it never executes a query
243
+ * itself, never reads or mutates the result, and never changes query
244
+ * behavior or errors.
245
+ *
246
+ * Apply it like any Prisma extension:
247
+ *
248
+ * ```ts
249
+ * import { prismaAdapter } from "@glasstrace/sdk";
250
+ *
251
+ * const prisma = new PrismaClient().$extends(
252
+ * prismaAdapter({ allow: [{ model: "Poll", column: "muted" }] }),
253
+ * );
254
+ * ```
255
+ *
256
+ * Design:
257
+ * - **OWN a span.** At the capture point the database client's own
258
+ * operation span has already ended, so the adapter opens its own
259
+ * recording `db.<Model>.<op>` span (parented under the active request
260
+ * span) and emits onto it via {@link capture}.
261
+ * - **Default-deny.** Nothing is captured unless an explicit `allow` entry
262
+ * matches AND the server-pushed `sideEffectEvidence` capture flag is on.
263
+ * An empty / unset `allow` captures nothing.
264
+ * - **Boolean only.** This adapter projects boolean columns (the strict,
265
+ * no-`captureFidelity:full` case). Non-boolean allowlisted columns route
266
+ * to a safe omission counter, never a captured value.
267
+ * - **Pure observer.** Capture work can never throw into the host query;
268
+ * the owned span is always ended; the original query error is re-thrown
269
+ * verbatim.
270
+ * - **Bounded.** `findMany` / list operations are disabled (no per-row
271
+ * capture). The adapter never widens the app's `select`.
272
+ *
273
+ * This module has **no dependency on `@prisma/client`** — it is typed
274
+ * structurally against Prisma's client-extension shape (mirroring the
275
+ * Drizzle adapter), so it adds no runtime dependency and ships on the edge-
276
+ * safe root barrel. On a runtime with no active request span (e.g. an edge
277
+ * runtime with no AsyncLocalStorage), it captures nothing.
278
+ */
279
+ /** The arguments Prisma passes to a `$allOperations` query-extension callback. */
280
+ interface PrismaAllOperationsArgs {
281
+ /** The Prisma model name (PascalCase, e.g. `Poll`), or `undefined` for raw ops. */
282
+ model?: string;
283
+ /** The Prisma operation (e.g. `findUnique`, `findMany`, `update`). */
284
+ operation: string;
285
+ /** The operation arguments, forwarded unchanged to `query`. */
286
+ args: unknown;
287
+ /** Executes the underlying operation. Called exactly once. */
288
+ query: (args: unknown) => Promise<unknown>;
289
+ }
290
+ /**
291
+ * A Prisma client extension — the object passed to `prisma.$extends(...)`.
292
+ * Structurally typed so the adapter needs no `@prisma/client` dependency.
293
+ */
294
+ interface PrismaCaptureExtension {
295
+ name: string;
296
+ query: {
297
+ $allModels: {
298
+ $allOperations(args: PrismaAllOperationsArgs): Promise<unknown>;
299
+ };
300
+ };
301
+ }
302
+ /** A single allowlisted column to project. */
303
+ interface PrismaCaptureColumn {
304
+ /** The Prisma model name, PascalCase, exactly as Prisma reports it (e.g. `Poll`). */
305
+ model: string;
306
+ /** The boolean result column to project (e.g. `muted`). */
307
+ column: string;
308
+ }
309
+ /** Options for {@link prismaAdapter}. */
310
+ interface PrismaAdapterOptions {
311
+ /**
312
+ * The default-deny allowlist. Only `(model, column)` pairs listed here are
313
+ * eligible for capture; an empty or unset list captures nothing. The
314
+ * server-side per-tenant allowlist re-enforces this independently at
315
+ * ingestion.
316
+ */
317
+ allow?: ReadonlyArray<PrismaCaptureColumn>;
318
+ }
319
+ /**
320
+ * Build a passive Prisma value-capture extension. See the module doc for
321
+ * the full behavior contract.
322
+ */
323
+ declare function prismaAdapter(options?: PrismaAdapterOptions): PrismaCaptureExtension;
324
+
325
+ /**
326
+ * Producer-sugar for computing boolean relations to emit as `*Holds`
327
+ * side-effect evidence.
328
+ *
329
+ * These helpers turn a comparison into the boolean a producer passes to
330
+ * `recordSideEffect({ relations: { …Holds: invariant(a, "eq", b) } })`.
331
+ * They are pure (no I/O, no Node built-ins) and edge-safe, so they live
332
+ * on the root barrel. The operator set is intentionally minimal and
333
+ * fixed — six binary comparisons plus a separate unary null check — and
334
+ * is not a general expression DSL.
335
+ */
336
+ /**
337
+ * The six supported binary comparison operators. `isNull` is **not** an
338
+ * operator here — use {@link isNullInvariant} for the unary case.
339
+ */
340
+ type InvariantOp = "eq" | "neq" | "lt" | "lte" | "gt" | "gte";
341
+ /**
342
+ * Evaluate a binary comparison invariant and return the boolean result.
343
+ *
344
+ * Both operands are constrained to the same primitive type. `eq`/`neq`
345
+ * use strict equality; the ordering operators (`lt`/`lte`/`gt`/`gte`)
346
+ * use the language relational operators (numeric for numbers/bigints,
347
+ * lexical for strings). Intended for producing a `*Holds` relation, e.g.
348
+ * `invariant(emittedDurationMinutes, "eq", declaredDurationMinutes)`.
349
+ *
350
+ * Operands should be comparable primitives. `NaN` follows IEEE-754
351
+ * (unequal to everything; all orderings `false`), so screen `NaN` before
352
+ * asserting a relation. Passing a non-primitive (e.g. a `Symbol`, or an
353
+ * object with a throwing `valueOf`) to an ordering operator throws per
354
+ * JS semantics — the type signature prevents this for typed callers.
355
+ *
356
+ * @param left - The left operand.
357
+ * @param op - One of the six {@link InvariantOp} comparisons.
358
+ * @param right - The right operand (same primitive type as `left`).
359
+ * @returns The boolean result of `left <op> right`.
360
+ *
361
+ * @example
362
+ * recordSideEffect({
363
+ * kind: "calendar_link",
364
+ * operation: "invite.create",
365
+ * relations: {
366
+ * durationMatchesHolds: invariant(emittedMinutes, "eq", declaredMinutes),
367
+ * },
368
+ * });
369
+ */
370
+ declare function invariant<T extends number | string | bigint | boolean>(left: T, op: InvariantOp, right: T): boolean;
371
+ /**
372
+ * Unary null/undefined invariant — `true` when `value` is `null` or
373
+ * `undefined`. Kept separate from {@link invariant} because nullishness
374
+ * is a unary predicate, not a binary comparison (there is no `isNull`
375
+ * operator). Use for a `*Holds` relation asserting a value's absence,
376
+ * e.g. `relations: { recipientMissingHolds: isNullInvariant(recipient) }`.
377
+ *
378
+ * @param value - The value to test.
379
+ * @returns `true` when `value` is `null` or `undefined`, else `false`
380
+ * (falsy-but-present values like `0`, `""`, `false`, `NaN` are `false`).
381
+ */
382
+ declare function isNullInvariant(value: unknown): boolean;
383
+
384
+ export { type CaptureOptions, type InvariantOp, type PrismaAdapterOptions, type PrismaCaptureColumn, type PrismaCaptureExtension, type RecordSideEffectInput, SideEffectOperationKind, SideEffectOperationPhase, SideEffectOperationStatus, SideEffectSemanticFieldKey, capture, invariant, isNullInvariant, prismaAdapter, recordSideEffect };