@graphorin/observability 0.6.0 → 0.6.1

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 CHANGED
@@ -1,5 +1,16 @@
1
1
  # @graphorin/observability
2
2
 
3
+ ## 0.6.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#140](https://github.com/o-stepper/graphorin/pull/140) [`826ee59`](https://github.com/o-stepper/graphorin/commit/826ee5933ad38693b0dd2f20a110abfecba7d23d) Thanks [@o-stepper](https://github.com/o-stepper)! - Fix the default-on `graphorin-token` redaction pattern: it was hardcoded to a stale `kru_(dev|test|prod)` token shape and never matched real framework tokens, which use the `gph` default prefix with `live|test|local` environment labels (`@graphorin/security` `DEFAULT_TOKEN_PREFIX`). The pattern now matches `gph_<env>_v1_<entropy>_<crc32>` with a loose env label (operators can extend `acceptEnvironments`); deployments that configure a custom token prefix must register their own pattern.
8
+
9
+ - [#142](https://github.com/o-stepper/graphorin/pull/142) [`436d6ca`](https://github.com/o-stepper/graphorin/commit/436d6ca5ebbd16df094e915682d3915c279a8430) Thanks [@o-stepper](https://github.com/o-stepper)! - Version constants and version-bearing strings now derive from each package's manifest at build time (`VERSION = pkg.version`; writer ids, client/server info, OTLP framework attributes, build-info metrics interpolate it). No behavioral change at the current version: the rendered strings are byte-identical. A release bump no longer edits source; the new `check-version-consistency` gate fails any reintroduced hardcoded framework version.
10
+
11
+ - Updated dependencies [[`436d6ca`](https://github.com/o-stepper/graphorin/commit/436d6ca5ebbd16df094e915682d3915c279a8430)]:
12
+ - @graphorin/core@0.6.1
13
+
3
14
  ## 0.6.0
4
15
 
5
16
  ### Minor Changes
package/README.md CHANGED
@@ -30,7 +30,11 @@ outbound network calls without an explicit user action.
30
30
  `emitGenAIAttributes(span, {...})`, `emitGenAIMessageEvents(span, [...])`,
31
31
  and `deriveGenAISystem(...)` ship the canonical
32
32
  `gen_ai.*` attribute family alongside the existing
33
- `graphorin.*` attributes - additive, never replacing.
33
+ `graphorin.*` attributes - additive, never replacing. Span names
34
+ follow the semconv `{operation} {target}` shape (`chat <model>`,
35
+ `execute_tool <tool>`, `invoke_agent <agent>`) via the exported
36
+ `spanNameFor(type, attrs)` helper, applied by the tracer
37
+ automatically.
34
38
  - **OpenInference span-kind layer.** `emitOpenInferenceKind(span)` emits
35
39
  the `openinference.span.kind` attribute via the canonical
36
40
  per-`SpanType` mapping (`agent.*` → `AGENT`,
@@ -112,4 +116,4 @@ MIT © 2026 [Oleksiy Stepurenko](https://github.com/o-stepper).
112
116
 
113
117
  ---
114
118
 
115
- **Project Graphorin** · v0.6.0 · MIT License · © 2026 Oleksiy Stepurenko · <https://github.com/o-stepper/graphorin>
119
+ **Project Graphorin** · v0.6.1 · MIT License · © 2026 Oleksiy Stepurenko · <https://github.com/o-stepper/graphorin>
@@ -1 +1 @@
1
- {"version":3,"file":"otlp-http.d.ts","names":[],"sources":["../../src/exporters/otlp-http.ts"],"sourcesContent":[],"mappings":";;;;;;;;;UA4BiB,uBAAA;;;;;;qBAMI,SAAS;;;;8BAIA;;;;;;;;;;;;;;;iBAgBd,sBAAA,OAA6B,0BAA0B"}
1
+ {"version":3,"file":"otlp-http.d.ts","names":[],"sources":["../../src/exporters/otlp-http.ts"],"sourcesContent":[],"mappings":";;;;;;AA6BA;;;AAU8B,UAVb,uBAAA,CAUa;EAAK;EAgBnB,SAAA,EAAA,CAAA,EAAA,MAAA;;;;qBApBK,SAAS;;;;8BAIA;;;;;;;;;;;;;;;iBAgBd,sBAAA,OAA6B,0BAA0B"}
@@ -1,3 +1,5 @@
1
+ import { version } from "../package.js";
2
+
1
3
  //#region src/exporters/otlp-http.ts
2
4
  /**
3
5
  * Build a minimal OTLP-over-HTTP trace exporter. Call
@@ -58,12 +60,12 @@ function toOtlpEnvelope(record, serviceName) {
58
60
  value: { stringValue: serviceName }
59
61
  }, {
60
62
  key: "graphorin.framework.version",
61
- value: { stringValue: "0.6.0" }
63
+ value: { stringValue: version }
62
64
  }] },
63
65
  scopeSpans: [{
64
66
  scope: {
65
67
  name: "@graphorin/observability",
66
- version: "0.6.0"
68
+ version
67
69
  },
68
70
  spans: [{
69
71
  traceId: record.traceId,
@@ -1 +1 @@
1
- {"version":3,"file":"otlp-http.js","names":[],"sources":["../../src/exporters/otlp-http.ts"],"sourcesContent":["/**\n * `OTLPHttpExporter` - reference implementation that POSTs span\n * records to an OpenTelemetry HTTP endpoint.\n *\n * The exporter is deliberately minimal: production hardening (retry\n * logic, batching policy tuning, gzip compression, bearer-token auth\n * rotation) is left to operators. The wire format follows the OTLP\n * `traces` resource shape and is compatible with collectors that accept\n * the `application/json` content type.\n *\n * Operators wanting the upstream OpenTelemetry SDK exporter\n * (`@opentelemetry/exporter-trace-otlp-http`) can adapt\n * {@link toOtlpEnvelope} into their own wrapper - the helper is\n * exported so the OTel adapter does not need to duplicate the wire\n * shape.\n *\n * @packageDocumentation\n */\n\nimport type { SpanAttributes, SpanAttributeValue, SpanType } from '@graphorin/core';\n\nimport type { SpanRecord, SpanRecordEvent, TraceExporter } from './types.js';\n\n/**\n * Configuration shape for {@link createOTLPHttpExporter}.\n *\n * @stable\n */\nexport interface OTLPHttpExporterOptions {\n /** Identifier reported via `exporter.id`. Defaults to `'otlp-http'`. */\n readonly id?: string;\n /** OTLP collector URL (e.g. `http://localhost:4318/v1/traces`). */\n readonly url: string;\n /** Optional headers (auth, tenant id, …). Avoid passing secrets in plain. */\n readonly headers?: Readonly<Record<string, string>>;\n /** Service name embedded in the OTLP `Resource`. */\n readonly serviceName?: string;\n /** Optional `fetch` override - useful for testing without the network. */\n readonly fetchImpl?: typeof fetch;\n /** Optional timeout (ms). Defaults to 10000. */\n readonly timeoutMs?: number;\n}\n\n/**\n * Build a minimal OTLP-over-HTTP trace exporter. Call\n * `withValidation(exporter)` before passing the result to\n * `createTracer({ exporters })`.\n *\n * The implementation issues one `POST` per finished span - no\n * batching. Operators wanting batching should wrap this exporter\n * with their own queue or use a sidecar collector.\n *\n * @stable\n */\nexport function createOTLPHttpExporter(opts: OTLPHttpExporterOptions): TraceExporter {\n const id = opts.id ?? 'otlp-http';\n const url = opts.url;\n const headers = opts.headers ?? {};\n const serviceName = opts.serviceName ?? 'graphorin';\n const fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n const timeoutMs = opts.timeoutMs ?? 10_000;\n let closed = false;\n\n if (typeof fetchImpl !== 'function') {\n throw new TypeError(\n 'createOTLPHttpExporter: no fetch implementation available. Provide ' +\n '`fetchImpl` explicitly or run on a Node.js version with global fetch ' +\n '(>= 18).',\n );\n }\n\n return {\n id,\n async export(record: SpanRecord): Promise<void> {\n if (closed) return;\n const body = toOtlpEnvelope(record, serviceName);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await fetchImpl(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json', ...headers },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n if (!res.ok) {\n throw new Error(`OTLP exporter received HTTP ${res.status} ${res.statusText}`);\n }\n } finally {\n clearTimeout(timer);\n }\n },\n async flush(): Promise<void> {\n // No internal queue; each export() awaits the POST already.\n },\n async shutdown(): Promise<void> {\n closed = true;\n },\n };\n}\n\n/**\n * @internal - exposed for unit tests\n */\nexport function toOtlpEnvelope(record: SpanRecord, serviceName: string): unknown {\n const otelStatus = record.status === 'ok' ? 1 : record.status === 'error' ? 2 : 0;\n return {\n resourceSpans: [\n {\n resource: {\n attributes: [\n { key: 'service.name', value: { stringValue: serviceName } },\n { key: 'graphorin.framework.version', value: { stringValue: '0.6.0' } },\n ],\n },\n scopeSpans: [\n {\n scope: { name: '@graphorin/observability', version: '0.6.0' },\n spans: [\n {\n traceId: record.traceId,\n spanId: record.id,\n ...(record.parentId === undefined ? {} : { parentSpanId: record.parentId }),\n name: record.name,\n kind: spanKindFor(record.type),\n startTimeUnixNano: String(record.startUnixNano),\n endTimeUnixNano: String(record.endUnixNano),\n attributes: toOtlpAttributes(record.attributes),\n events: record.events.map(toOtlpEvent),\n status: {\n code: otelStatus,\n ...(record.statusMessage === undefined ? {} : { message: record.statusMessage }),\n },\n },\n ],\n },\n ],\n },\n ],\n };\n}\n\nfunction spanKindFor(type: SpanType): number {\n // OTel SpanKind enum: 0 unspecified, 1 internal, 2 server, 3 client, 4 producer, 5 consumer.\n if (type === 'provider.generate' || type === 'provider.stream' || type === 'mcp.call') return 3;\n if (type === 'tool.execute' || type === 'memory.embed') return 1;\n return 1;\n}\n\nfunction toOtlpAttributes(attrs: SpanAttributes): unknown[] {\n return Object.entries(attrs).map(([k, v]) => ({ key: k, value: toOtlpAttributeValue(v) }));\n}\n\nfunction toOtlpEvent(event: SpanRecordEvent): unknown {\n return {\n timeUnixNano: String(event.timeUnixNano),\n name: event.name,\n attributes: toOtlpAttributes(event.attributes),\n };\n}\n\nfunction toOtlpAttributeValue(v: SpanAttributeValue): unknown {\n if (typeof v === 'string') return { stringValue: v };\n if (typeof v === 'number') {\n return Number.isInteger(v) ? { intValue: String(v) } : { doubleValue: v };\n }\n if (typeof v === 'boolean') return { boolValue: v };\n if (v === null) return { stringValue: '' };\n if (Array.isArray(v)) {\n return {\n arrayValue: {\n values: v.map((item) => toOtlpAttributeValue(item as SpanAttributeValue)),\n },\n };\n }\n return { stringValue: String(v) };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"otlp-http.js","names":["pkg.version"],"sources":["../../src/exporters/otlp-http.ts"],"sourcesContent":["import pkg from '../../package.json' with { type: 'json' };\n/**\n * `OTLPHttpExporter` - reference implementation that POSTs span\n * records to an OpenTelemetry HTTP endpoint.\n *\n * The exporter is deliberately minimal: production hardening (retry\n * logic, batching policy tuning, gzip compression, bearer-token auth\n * rotation) is left to operators. The wire format follows the OTLP\n * `traces` resource shape and is compatible with collectors that accept\n * the `application/json` content type.\n *\n * Operators wanting the upstream OpenTelemetry SDK exporter\n * (`@opentelemetry/exporter-trace-otlp-http`) can adapt\n * {@link toOtlpEnvelope} into their own wrapper - the helper is\n * exported so the OTel adapter does not need to duplicate the wire\n * shape.\n *\n * @packageDocumentation\n */\n\nimport type { SpanAttributes, SpanAttributeValue, SpanType } from '@graphorin/core';\n\nimport type { SpanRecord, SpanRecordEvent, TraceExporter } from './types.js';\n\n/**\n * Configuration shape for {@link createOTLPHttpExporter}.\n *\n * @stable\n */\nexport interface OTLPHttpExporterOptions {\n /** Identifier reported via `exporter.id`. Defaults to `'otlp-http'`. */\n readonly id?: string;\n /** OTLP collector URL (e.g. `http://localhost:4318/v1/traces`). */\n readonly url: string;\n /** Optional headers (auth, tenant id, …). Avoid passing secrets in plain. */\n readonly headers?: Readonly<Record<string, string>>;\n /** Service name embedded in the OTLP `Resource`. */\n readonly serviceName?: string;\n /** Optional `fetch` override - useful for testing without the network. */\n readonly fetchImpl?: typeof fetch;\n /** Optional timeout (ms). Defaults to 10000. */\n readonly timeoutMs?: number;\n}\n\n/**\n * Build a minimal OTLP-over-HTTP trace exporter. Call\n * `withValidation(exporter)` before passing the result to\n * `createTracer({ exporters })`.\n *\n * The implementation issues one `POST` per finished span - no\n * batching. Operators wanting batching should wrap this exporter\n * with their own queue or use a sidecar collector.\n *\n * @stable\n */\nexport function createOTLPHttpExporter(opts: OTLPHttpExporterOptions): TraceExporter {\n const id = opts.id ?? 'otlp-http';\n const url = opts.url;\n const headers = opts.headers ?? {};\n const serviceName = opts.serviceName ?? 'graphorin';\n const fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n const timeoutMs = opts.timeoutMs ?? 10_000;\n let closed = false;\n\n if (typeof fetchImpl !== 'function') {\n throw new TypeError(\n 'createOTLPHttpExporter: no fetch implementation available. Provide ' +\n '`fetchImpl` explicitly or run on a Node.js version with global fetch ' +\n '(>= 18).',\n );\n }\n\n return {\n id,\n async export(record: SpanRecord): Promise<void> {\n if (closed) return;\n const body = toOtlpEnvelope(record, serviceName);\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await fetchImpl(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json', ...headers },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n if (!res.ok) {\n throw new Error(`OTLP exporter received HTTP ${res.status} ${res.statusText}`);\n }\n } finally {\n clearTimeout(timer);\n }\n },\n async flush(): Promise<void> {\n // No internal queue; each export() awaits the POST already.\n },\n async shutdown(): Promise<void> {\n closed = true;\n },\n };\n}\n\n/**\n * @internal - exposed for unit tests\n */\nexport function toOtlpEnvelope(record: SpanRecord, serviceName: string): unknown {\n const otelStatus = record.status === 'ok' ? 1 : record.status === 'error' ? 2 : 0;\n return {\n resourceSpans: [\n {\n resource: {\n attributes: [\n { key: 'service.name', value: { stringValue: serviceName } },\n { key: 'graphorin.framework.version', value: { stringValue: pkg.version } },\n ],\n },\n scopeSpans: [\n {\n scope: { name: '@graphorin/observability', version: pkg.version },\n spans: [\n {\n traceId: record.traceId,\n spanId: record.id,\n ...(record.parentId === undefined ? {} : { parentSpanId: record.parentId }),\n name: record.name,\n kind: spanKindFor(record.type),\n startTimeUnixNano: String(record.startUnixNano),\n endTimeUnixNano: String(record.endUnixNano),\n attributes: toOtlpAttributes(record.attributes),\n events: record.events.map(toOtlpEvent),\n status: {\n code: otelStatus,\n ...(record.statusMessage === undefined ? {} : { message: record.statusMessage }),\n },\n },\n ],\n },\n ],\n },\n ],\n };\n}\n\nfunction spanKindFor(type: SpanType): number {\n // OTel SpanKind enum: 0 unspecified, 1 internal, 2 server, 3 client, 4 producer, 5 consumer.\n if (type === 'provider.generate' || type === 'provider.stream' || type === 'mcp.call') return 3;\n if (type === 'tool.execute' || type === 'memory.embed') return 1;\n return 1;\n}\n\nfunction toOtlpAttributes(attrs: SpanAttributes): unknown[] {\n return Object.entries(attrs).map(([k, v]) => ({ key: k, value: toOtlpAttributeValue(v) }));\n}\n\nfunction toOtlpEvent(event: SpanRecordEvent): unknown {\n return {\n timeUnixNano: String(event.timeUnixNano),\n name: event.name,\n attributes: toOtlpAttributes(event.attributes),\n };\n}\n\nfunction toOtlpAttributeValue(v: SpanAttributeValue): unknown {\n if (typeof v === 'string') return { stringValue: v };\n if (typeof v === 'number') {\n return Number.isInteger(v) ? { intValue: String(v) } : { doubleValue: v };\n }\n if (typeof v === 'boolean') return { boolValue: v };\n if (v === null) return { stringValue: '' };\n if (Array.isArray(v)) {\n return {\n arrayValue: {\n values: v.map((item) => toOtlpAttributeValue(item as SpanAttributeValue)),\n },\n };\n }\n return { stringValue: String(v) };\n}\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -51,8 +51,7 @@ import { GraphorinTracer, TracerOptions, asGraphorinSpan, createTracer } from ".
51
51
  *
52
52
  * @packageDocumentation
53
53
  */
54
- /** Canonical version constant. Mirrors the `package.json` version. */
55
- declare const VERSION = "0.6.0";
54
+ declare const VERSION: string;
56
55
  //#endregion
57
56
  export { ALL_BUILT_IN_PATTERNS, BUILT_IN_IMPERATIVE_PATTERNS, BUILT_IN_PATTERNS, BuiltInPatternName, Case, ConsoleExporterOptions, CostBudgetExceededCallback, CostBudgets, CostRecordInput, CostScope, CostSnapshot, CostTracker, CostTrackerOptions, DEFAULT_REPLAY_LOG_CONFIG, DEFAULT_VALIDATION_CONFIG, Dataset, EvalCaseResult, EvalReport, GenAIAttributes, GenAIMessage, GenAIMessageRole, GenAIOperationName, GenAISystem, GenAIToolType, GraphorinSpan, GraphorinTracer, IMPERATIVE_PREFILTER_SUBSTRINGS, ImperativePattern, ImperativePatternName, ScanResult as ImperativeScanResult, JSONLExporterOptions, LoggerFormat, LoggerOptions, OPEN_INFERENCE_EXCLUDED_TYPES, OPEN_INFERENCE_KIND_TABLE, OPERATION_NAME_TABLE, OPT_IN_PATTERNS, OTLPHttpExporterOptions, OpenInferenceSpanKind, PROVIDER_CLASS_TO_GEN_AI_SYSTEM, PatternCategory, PruneTracesOptions, RedactionCounters, RedactionInput, RedactionOutput, RedactionPattern, RedactionValidationError, RedactionValidator, RedactionValidatorInstance, RedactionValidatorOptions, RedactionViolation, RedactionViolationCallback, Replay, ReplayAuditBridge, ReplayAuditEvent, ReplayEvent, ReplayLogConfig, ReplayMode, ReplayOptions, ReplayRunInput, RunEvalOptions, Sampler, SamplingDecisionMaker, SamplingOptions, SamplingRule, ScoreResult, Scorer, SetAttributeOptions, SpanRecord, SpanRecordEvent, SpanTypeToOperationName, TelemetryStatus, TraceExporter, TracerOptions, UnvalidatedExporterError, VALIDATED_EXPORTER_BRAND, VERSION, ValidationConfig, WithValidationOptions, _resetGenAISystemWarningsForTesting, announceTelemetryPosture, asGraphorinSpan, compareSensitivityTiers, createConsoleExporter, createCostTracker, createJSONLExporter, createLogger, createOTLPHttpExporter, createRedactionValidator, createReplay, createSampler, createTracer, deriveGenAISystem, emitGenAIAttributes, emitGenAIMessageEvents, emitOpenInferenceKind, enableTelemetry, getCurrentSpanContext, getTelemetryStatus, getTraceLog, isValidatedExporter, newSpanId, newTraceId, openInferenceKindFor, operationNameFor, pruneTraces, runEval, scanImperativePatterns, setGenAISystemWarnSink, spanNameFor, stripImperativePatterns, tryGetValidatorCounters, withCurrentSpan, withValidation };
58
57
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAsBa,OAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAwBa"}
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { version } from "./package.js";
1
2
  import { createCostTracker } from "./cost/cost-tracker.js";
2
3
  import "./cost/index.js";
3
4
  import { runEval } from "./eval/runner.js";
@@ -52,8 +53,8 @@ import "./tracer/index.js";
52
53
  *
53
54
  * @packageDocumentation
54
55
  */
55
- /** Canonical version constant. Mirrors the `package.json` version. */
56
- const VERSION = "0.6.0";
56
+ /** Canonical version constant, derived from `package.json` at build time. */
57
+ const VERSION = version;
57
58
 
58
59
  //#endregion
59
60
  export { ALL_BUILT_IN_PATTERNS, BUILT_IN_IMPERATIVE_PATTERNS, BUILT_IN_PATTERNS, DEFAULT_REPLAY_LOG_CONFIG, DEFAULT_VALIDATION_CONFIG, IMPERATIVE_PREFILTER_SUBSTRINGS, OPEN_INFERENCE_EXCLUDED_TYPES, OPEN_INFERENCE_KIND_TABLE, OPERATION_NAME_TABLE, OPT_IN_PATTERNS, PROVIDER_CLASS_TO_GEN_AI_SYSTEM, RedactionValidationError, UnvalidatedExporterError, VALIDATED_EXPORTER_BRAND, VERSION, _resetGenAISystemWarningsForTesting, announceTelemetryPosture, asGraphorinSpan, compareSensitivityTiers, createConsoleExporter, createCostTracker, createJSONLExporter, createLogger, createOTLPHttpExporter, createRedactionValidator, createReplay, createSampler, createTracer, deriveGenAISystem, emitGenAIAttributes, emitGenAIMessageEvents, emitOpenInferenceKind, enableTelemetry, getCurrentSpanContext, getTelemetryStatus, getTraceLog, isValidatedExporter, newSpanId, newTraceId, openInferenceKindFor, operationNameFor, pruneTraces, runEval, scanImperativePatterns, setGenAISystemWarnSink, spanNameFor, stripImperativePatterns, tryGetValidatorCounters, withCurrentSpan, withValidation };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @graphorin/observability - observability primitives for the\n * Graphorin framework. Ships:\n *\n * - the typed `AISpan<T>` tracer over OpenTelemetry,\n * - the **mandatory** `withValidation()` wrapper for every exporter,\n * - the sensitivity-aware `RedactionValidator` with 14 built-in PII /\n * secret detection patterns,\n * - OpenTelemetry GenAI semantic-conventions conformance helpers,\n * - OpenInference span-kind emission,\n * - the structured `Logger` with span correlation,\n * - the append-only `JSONLExporter` for replay,\n * - sanitized-by-default `Replay` primitives,\n * - the hierarchical `CostTracker`,\n * - and a minimal inline eval runner.\n *\n * The full documentation lives in the package `README.md`.\n *\n * @packageDocumentation\n */\n\n/** Canonical version constant. Mirrors the `package.json` version. */\nexport const VERSION = '0.6.0';\n\nexport * from './cost/index.js';\nexport * from './eval/index.js';\nexport * from './exporters/index.js';\nexport * from './gen-ai/index.js';\nexport * from './logger/index.js';\nexport * from './openinference/index.js';\nexport * from './redaction/index.js';\nexport * from './replay/index.js';\nexport * from './telemetry/index.js';\nexport * from './tracer/index.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,UAAU"}
1
+ {"version":3,"file":"index.js","names":["VERSION: string","pkg.version"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @graphorin/observability - observability primitives for the\n * Graphorin framework. Ships:\n *\n * - the typed `AISpan<T>` tracer over OpenTelemetry,\n * - the **mandatory** `withValidation()` wrapper for every exporter,\n * - the sensitivity-aware `RedactionValidator` with 14 built-in PII /\n * secret detection patterns,\n * - OpenTelemetry GenAI semantic-conventions conformance helpers,\n * - OpenInference span-kind emission,\n * - the structured `Logger` with span correlation,\n * - the append-only `JSONLExporter` for replay,\n * - sanitized-by-default `Replay` primitives,\n * - the hierarchical `CostTracker`,\n * - and a minimal inline eval runner.\n *\n * The full documentation lives in the package `README.md`.\n *\n * @packageDocumentation\n */\n\n/** Canonical version constant, derived from `package.json` at build time. */\nimport pkg from '../package.json' with { type: 'json' };\n\nexport const VERSION: string = pkg.version;\n\nexport * from './cost/index.js';\nexport * from './eval/index.js';\nexport * from './exporters/index.js';\nexport * from './gen-ai/index.js';\nexport * from './logger/index.js';\nexport * from './openinference/index.js';\nexport * from './redaction/index.js';\nexport * from './replay/index.js';\nexport * from './telemetry/index.js';\nexport * from './tracer/index.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAaA,UAAkBC"}
@@ -0,0 +1,6 @@
1
+ //#region package.json
2
+ var version = "0.6.1";
3
+
4
+ //#endregion
5
+ export { version };
6
+ //# sourceMappingURL=package.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package.js","names":[],"sources":["../package.json"],"sourcesContent":["{\n \"name\": \"@graphorin/observability\",\n \"version\": \"0.6.1\",\n \"description\": \"Observability primitives for the Graphorin framework: typed AISpan tracer over OpenTelemetry, mandatory withValidation wrapper for every exporter, sensitivity-aware RedactionValidator with built-in PII / secret patterns, GenAI Semantic Conventions conformance helpers, OpenInference span-kind emission, structured logger with span correlation, append-only JSONL exporter for replay, sanitized replay primitives, hierarchical CostTracker, and a minimal inline eval runner alongside a zero-default telemetry stub.\",\n \"license\": \"MIT\",\n \"author\": \"Oleksiy Stepurenko\",\n \"homepage\": \"https://github.com/o-stepper/graphorin/tree/main/packages/observability\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/o-stepper/graphorin.git\",\n \"directory\": \"packages/observability\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/o-stepper/graphorin/issues\"\n },\n \"keywords\": [\n \"graphorin\",\n \"ai\",\n \"agents\",\n \"framework\",\n \"observability\",\n \"tracing\",\n \"opentelemetry\",\n \"otel\",\n \"gen-ai\",\n \"openinference\",\n \"redaction\",\n \"pii\",\n \"cost-tracking\",\n \"replay\",\n \"evals\",\n \"logger\"\n ],\n \"type\": \"module\",\n \"engines\": {\n \"node\": \">=22.0.0\"\n },\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./tracer\": {\n \"types\": \"./dist/tracer/index.d.ts\",\n \"import\": \"./dist/tracer/index.js\"\n },\n \"./redaction\": {\n \"types\": \"./dist/redaction/index.d.ts\",\n \"import\": \"./dist/redaction/index.js\"\n },\n \"./redaction/patterns\": {\n \"types\": \"./dist/redaction/patterns.d.ts\",\n \"import\": \"./dist/redaction/patterns.js\"\n },\n \"./redaction/imperative-patterns\": {\n \"types\": \"./dist/redaction/imperative-patterns.d.ts\",\n \"import\": \"./dist/redaction/imperative-patterns.js\"\n },\n \"./exporters\": {\n \"types\": \"./dist/exporters/index.d.ts\",\n \"import\": \"./dist/exporters/index.js\"\n },\n \"./gen-ai\": {\n \"types\": \"./dist/gen-ai/index.d.ts\",\n \"import\": \"./dist/gen-ai/index.js\"\n },\n \"./openinference\": {\n \"types\": \"./dist/openinference/index.d.ts\",\n \"import\": \"./dist/openinference/index.js\"\n },\n \"./cost\": {\n \"types\": \"./dist/cost/index.d.ts\",\n \"import\": \"./dist/cost/index.js\"\n },\n \"./replay\": {\n \"types\": \"./dist/replay/index.d.ts\",\n \"import\": \"./dist/replay/index.js\"\n },\n \"./eval\": {\n \"types\": \"./dist/eval/index.d.ts\",\n \"import\": \"./dist/eval/index.js\"\n },\n \"./logger\": {\n \"types\": \"./dist/logger/index.d.ts\",\n \"import\": \"./dist/logger/index.js\"\n },\n \"./telemetry\": {\n \"types\": \"./dist/telemetry/index.d.ts\",\n \"import\": \"./dist/telemetry/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"CHANGELOG.md\",\n \"LICENSE\"\n ],\n \"scripts\": {\n \"build\": \"tsdown\",\n \"typecheck\": \"tsc --noEmit && tsc -p tsconfig.tests.json\",\n \"test\": \"vitest run\",\n \"lint\": \"biome check .\",\n \"clean\": \"rimraf dist .turbo *.tsbuildinfo\"\n },\n \"dependencies\": {\n \"@graphorin/core\": \"workspace:*\"\n },\n \"peerDependencies\": {\n \"@opentelemetry/api\": \"^1.9.0\",\n \"@opentelemetry/sdk-node\": \"^0.217.0\",\n \"@opentelemetry/exporter-trace-otlp-http\": \"^0.215.0\"\n },\n \"peerDependenciesMeta\": {\n \"@opentelemetry/api\": {\n \"optional\": false\n },\n \"@opentelemetry/sdk-node\": {\n \"optional\": true\n },\n \"@opentelemetry/exporter-trace-otlp-http\": {\n \"optional\": true\n }\n },\n \"publishConfig\": {\n \"access\": \"public\",\n \"provenance\": true\n },\n \"devDependencies\": {\n \"@opentelemetry/api\": \"^1.9.0\",\n \"@opentelemetry/exporter-trace-otlp-http\": \"^0.215.0\",\n \"@opentelemetry/sdk-node\": \"^0.217.0\",\n \"fast-check\": \"^3.23.0\"\n }\n}\n"],"mappings":";cAEa"}
@@ -1 +1 @@
1
- {"version":3,"file":"patterns.d.ts","names":[],"sources":["../../src/redaction/patterns.ts"],"sourcesContent":[],"mappings":";;AAyBA;AAyBA;AAOA;AA+JA;AAUA;AAUA;;;;;;;;;;;;;;;;;;KAnNY,kBAAA;;;;;;;KAyBA,eAAA;;;;;;UAOK,gBAAA;;qBAEI;;kBAEH;;;;;;;;;;;;;;;;;;;;;;;;;cA2JL,4BAA4B;;;;;;;cAU5B,0BAA0B;;;;;;;cAU1B,gCAAgC"}
1
+ {"version":3,"file":"patterns.d.ts","names":[],"sources":["../../src/redaction/patterns.ts"],"sourcesContent":[],"mappings":";;AAyBA;AAyBA;AAOA;AAmKA;AAUA;AAUA;;;;;;;;;;;;;;;;;;KAvNY,kBAAA;;;;;;;KAyBA,eAAA;;;;;;UAOK,gBAAA;;qBAEI;;kBAEH;;;;;;;;;;;;;;;;;;;;;;;;;cA+JL,4BAA4B;;;;;;;cAU5B,0BAA0B;;;;;;;cAU1B,gCAAgC"}
@@ -3,8 +3,8 @@ const PATTERNS = [
3
3
  {
4
4
  name: "graphorin-token",
5
5
  category: "secret",
6
- description: "Graphorin server token (`kru_<env>_v1_<entropy>_<crc32>`).",
7
- regex: /kru_(?:dev|test|prod)_v1_[A-Za-z0-9]{20,80}_[A-Za-z0-9]{6}/g,
6
+ description: "Graphorin server token (`gph_<env>_v1_<entropy>_<crc32>`; default prefix).",
7
+ regex: /gph_[a-z0-9]{2,12}_v1_[A-Za-z0-9]{20,80}_[A-Za-z0-9]{6}/g,
8
8
  mask: "[REDACTED graphorin-token]"
9
9
  },
10
10
  {
@@ -1 +1 @@
1
- {"version":3,"file":"patterns.js","names":["PATTERNS: readonly RedactionPattern[]","BUILT_IN_PATTERNS: readonly RedactionPattern[]","OPT_IN_PATTERNS: readonly RedactionPattern[]","ALL_BUILT_IN_PATTERNS: readonly RedactionPattern[]"],"sources":["../../src/redaction/patterns.ts"],"sourcesContent":["/**\n * Built-in PII / secret detection patterns. The catalogue is intentionally\n * conservative - every pattern has both positive and negative test\n * fixtures and is documented so operators understand exactly what is\n * matched.\n *\n * The catalogue is split into two groups:\n *\n * - **secret** - credentials, API tokens, JWTs, private keys. Matches\n * are always dropped + counted, regardless of the configured tier\n * floor.\n * - **pii** - email / phone / IBAN / credit card / SSN / IP address.\n * Subject to the configured tier floor + per-pattern enable / disable\n * knobs.\n *\n * @packageDocumentation\n */\n\n/**\n * Stable pattern identifier. The catalogue is curated; user-supplied\n * patterns can use any identifier they want and will be passed through\n * the validator in addition to the built-ins.\n *\n * @stable\n */\nexport type BuiltInPatternName =\n | 'graphorin-token'\n | 'openai-key'\n | 'anthropic-key'\n | 'aws-access-key'\n | 'gcp-service-account'\n | 'github-pat'\n | 'jwt'\n | 'bearer-header'\n | 'basic-auth'\n | 'private-key-pem'\n | 'email'\n | 'creditcard'\n | 'us-ssn'\n | 'phone-e164'\n | 'iban'\n | 'ipv4'\n | 'ipv6';\n\n/**\n * Pattern category - `secret` matches always force a drop; `pii`\n * matches respect the configured `enabledPatterns` allow-list.\n *\n * @stable\n */\nexport type PatternCategory = 'secret' | 'pii';\n\n/**\n * One entry in the redaction catalogue.\n *\n * @stable\n */\nexport interface RedactionPattern {\n readonly name: string;\n readonly category: PatternCategory;\n readonly description: string;\n readonly regex: RegExp;\n /** Replacement string used when `mode === 'mask'`. */\n readonly mask?: string;\n /**\n * Optional per-match predicate (RP-21). When present, a regex hit is only\n * treated as a real match - and masked - when this returns `true` for the\n * matched substring. Used by the `creditcard` pattern to require a valid\n * Luhn checksum so look-alike digit runs (epoch-ms timestamps, order ids)\n * are not corrupted.\n */\n readonly verify?: (match: string) => boolean;\n /**\n * Optional opt-in flag. When `true` the pattern is **not** active by\n * default; operators must add it to `enabledPatterns` explicitly. Used\n * by the IPv4 / IPv6 patterns because raw IPs frequently appear in\n * non-PII log lines (host headers, debug traces, …).\n */\n readonly optIn?: boolean;\n}\n\nconst PATTERNS: readonly RedactionPattern[] = [\n {\n name: 'graphorin-token',\n category: 'secret',\n description: 'Graphorin server token (`kru_<env>_v1_<entropy>_<crc32>`).',\n regex: /kru_(?:dev|test|prod)_v1_[A-Za-z0-9]{20,80}_[A-Za-z0-9]{6}/g,\n mask: '[REDACTED graphorin-token]',\n },\n {\n name: 'openai-key',\n category: 'secret',\n description: 'OpenAI API key (`sk-...`).',\n regex: /\\bsk-[A-Za-z0-9_-]{20,}\\b/g,\n mask: '[REDACTED openai-key]',\n },\n {\n name: 'anthropic-key',\n category: 'secret',\n description: 'Anthropic API key (`sk-ant-...`).',\n regex: /\\bsk-ant-[A-Za-z0-9_-]{20,}\\b/g,\n mask: '[REDACTED anthropic-key]',\n },\n {\n name: 'aws-access-key',\n category: 'secret',\n description: 'AWS access-key id (`AKIA...`).',\n regex: /\\b(?:AKIA|ASIA)[0-9A-Z]{16}\\b/g,\n mask: '[REDACTED aws-access-key]',\n },\n {\n name: 'gcp-service-account',\n category: 'secret',\n description:\n 'GCP service-account email (`...@...iam.gserviceaccount.com`). Opt-in; the `email` pattern already redacts the address shape; this entry exists for operators wanting the explicit `gcp-service-account` counter label.',\n regex: /[A-Za-z0-9._-]+@[A-Za-z0-9-]+\\.iam\\.gserviceaccount\\.com\\b/g,\n mask: '[REDACTED gcp-service-account]',\n optIn: true,\n },\n {\n name: 'github-pat',\n category: 'secret',\n description: 'GitHub personal-access token / OAuth token / app token.',\n regex: /\\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,255}\\b/g,\n mask: '[REDACTED github-pat]',\n },\n {\n name: 'jwt',\n category: 'secret',\n description: 'JSON Web Token (`eyJ...`).',\n regex: /\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\b/g,\n mask: '[REDACTED jwt]',\n },\n {\n name: 'bearer-header',\n category: 'secret',\n description: '`Authorization: Bearer ...` header values.',\n regex: /\\bBearer\\s+[A-Za-z0-9._-]{16,}\\b/gi,\n mask: 'Bearer [REDACTED]',\n },\n {\n name: 'basic-auth',\n category: 'secret',\n description: '`Authorization: Basic ...` header values.',\n regex: /\\bBasic\\s+[A-Za-z0-9+/=]{16,}\\b/gi,\n mask: 'Basic [REDACTED]',\n },\n {\n name: 'private-key-pem',\n category: 'secret',\n description: 'PEM private-key block (`-----BEGIN ... PRIVATE KEY-----`).',\n regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\\s\\S]+?-----END [A-Z ]*PRIVATE KEY-----/g,\n mask: '[REDACTED private-key-pem]',\n },\n {\n name: 'email',\n category: 'pii',\n description: 'RFC-5322-ish email address.',\n regex: /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b/g,\n mask: '[REDACTED email]',\n },\n {\n name: 'creditcard',\n category: 'pii',\n description: 'Credit card number (13-19 digits, optional spaces / dashes; Luhn-checked).',\n regex: /\\b(?:\\d[\\s-]*?){13,19}\\b/g,\n mask: '[REDACTED creditcard]',\n // RP-21: require a valid Luhn checksum so a 13-19 digit run that is not a\n // real PAN (millisecond epoch timestamps, order numbers, …) is left alone.\n verify: isLuhnValid,\n },\n {\n name: 'us-ssn',\n category: 'pii',\n description: 'US Social Security Number (`123-45-6789`).',\n regex: /\\b\\d{3}-\\d{2}-\\d{4}\\b/g,\n mask: '[REDACTED us-ssn]',\n },\n {\n name: 'phone-e164',\n category: 'pii',\n description: 'E.164 phone number (`+11234567890`).',\n regex: /\\+\\d{7,15}\\b/g,\n mask: '[REDACTED phone-e164]',\n },\n {\n name: 'iban',\n category: 'pii',\n description: 'IBAN (2-letter country + 2 check digits + up to 30 alphanumerics).',\n regex: /\\b[A-Z]{2}\\d{2}[A-Z0-9]{11,30}\\b/g,\n mask: '[REDACTED iban]',\n },\n {\n name: 'ipv4',\n category: 'pii',\n description: 'IPv4 dotted-quad address. Opt-in (often appears legitimately in logs).',\n regex: /\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/g,\n mask: '[REDACTED ipv4]',\n optIn: true,\n },\n {\n name: 'ipv6',\n category: 'pii',\n description: 'IPv6 address (full / compressed). Opt-in.',\n regex: /\\b(?:[0-9a-fA-F]{1,4}:){2,7}[0-9a-fA-F]{1,4}\\b/g,\n mask: '[REDACTED ipv6]',\n optIn: true,\n },\n];\n\n/**\n * The 14 default-on built-in patterns (the IPv4 and IPv6 detectors are\n * opt-in and live in {@link OPT_IN_PATTERNS}).\n *\n * @stable\n */\nexport const BUILT_IN_PATTERNS: readonly RedactionPattern[] = PATTERNS.filter(\n (p) => p.optIn !== true,\n);\n\n/**\n * Patterns that are recognised by the validator but are NOT enabled by\n * default. Use them via `patterns: [...BUILT_IN_PATTERNS, ...OPT_IN_PATTERNS]`.\n *\n * @stable\n */\nexport const OPT_IN_PATTERNS: readonly RedactionPattern[] = PATTERNS.filter(\n (p) => p.optIn === true,\n);\n\n/**\n * Full registry - for tooling that wants to introspect every pattern\n * the framework knows about (e.g. CLI `graphorin redaction list`).\n *\n * @stable\n */\nexport const ALL_BUILT_IN_PATTERNS: readonly RedactionPattern[] = PATTERNS;\n\n/**\n * Luhn (mod-10) checksum validator used by the `creditcard` pattern (RP-21).\n * Strips spaces / dashes, bounds the length to 13-19 digits, and verifies the\n * checksum so a digit run that merely *looks* like a PAN is not redacted.\n */\nfunction isLuhnValid(value: string): boolean {\n const digits = value.replace(/\\D/g, '');\n if (digits.length < 13 || digits.length > 19) return false;\n let sum = 0;\n let double = false;\n for (let i = digits.length - 1; i >= 0; i -= 1) {\n let d = digits.charCodeAt(i) - 48; // '0'\n if (double) {\n d *= 2;\n if (d > 9) d -= 9;\n }\n sum += d;\n double = !double;\n }\n return sum % 10 === 0;\n}\n"],"mappings":";AAiFA,MAAMA,WAAwC;CAC5C;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aACE;EACF,OAAO;EACP,MAAM;EACN,OAAO;EACR;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EAGN,QAAQ;EACT;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACN,OAAO;EACR;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACN,OAAO;EACR;CACF;;;;;;;AAQD,MAAaC,oBAAiD,SAAS,QACpE,MAAM,EAAE,UAAU,KACpB;;;;;;;AAQD,MAAaC,kBAA+C,SAAS,QAClE,MAAM,EAAE,UAAU,KACpB;;;;;;;AAQD,MAAaC,wBAAqD;;;;;;AAOlE,SAAS,YAAY,OAAwB;CAC3C,MAAM,SAAS,MAAM,QAAQ,OAAO,GAAG;AACvC,KAAI,OAAO,SAAS,MAAM,OAAO,SAAS,GAAI,QAAO;CACrD,IAAI,MAAM;CACV,IAAI,SAAS;AACb,MAAK,IAAI,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG;EAC9C,IAAI,IAAI,OAAO,WAAW,EAAE,GAAG;AAC/B,MAAI,QAAQ;AACV,QAAK;AACL,OAAI,IAAI,EAAG,MAAK;;AAElB,SAAO;AACP,WAAS,CAAC;;AAEZ,QAAO,MAAM,OAAO"}
1
+ {"version":3,"file":"patterns.js","names":["PATTERNS: readonly RedactionPattern[]","BUILT_IN_PATTERNS: readonly RedactionPattern[]","OPT_IN_PATTERNS: readonly RedactionPattern[]","ALL_BUILT_IN_PATTERNS: readonly RedactionPattern[]"],"sources":["../../src/redaction/patterns.ts"],"sourcesContent":["/**\n * Built-in PII / secret detection patterns. The catalogue is intentionally\n * conservative - every pattern has both positive and negative test\n * fixtures and is documented so operators understand exactly what is\n * matched.\n *\n * The catalogue is split into two groups:\n *\n * - **secret** - credentials, API tokens, JWTs, private keys. Matches\n * are always dropped + counted, regardless of the configured tier\n * floor.\n * - **pii** - email / phone / IBAN / credit card / SSN / IP address.\n * Subject to the configured tier floor + per-pattern enable / disable\n * knobs.\n *\n * @packageDocumentation\n */\n\n/**\n * Stable pattern identifier. The catalogue is curated; user-supplied\n * patterns can use any identifier they want and will be passed through\n * the validator in addition to the built-ins.\n *\n * @stable\n */\nexport type BuiltInPatternName =\n | 'graphorin-token'\n | 'openai-key'\n | 'anthropic-key'\n | 'aws-access-key'\n | 'gcp-service-account'\n | 'github-pat'\n | 'jwt'\n | 'bearer-header'\n | 'basic-auth'\n | 'private-key-pem'\n | 'email'\n | 'creditcard'\n | 'us-ssn'\n | 'phone-e164'\n | 'iban'\n | 'ipv4'\n | 'ipv6';\n\n/**\n * Pattern category - `secret` matches always force a drop; `pii`\n * matches respect the configured `enabledPatterns` allow-list.\n *\n * @stable\n */\nexport type PatternCategory = 'secret' | 'pii';\n\n/**\n * One entry in the redaction catalogue.\n *\n * @stable\n */\nexport interface RedactionPattern {\n readonly name: string;\n readonly category: PatternCategory;\n readonly description: string;\n readonly regex: RegExp;\n /** Replacement string used when `mode === 'mask'`. */\n readonly mask?: string;\n /**\n * Optional per-match predicate (RP-21). When present, a regex hit is only\n * treated as a real match - and masked - when this returns `true` for the\n * matched substring. Used by the `creditcard` pattern to require a valid\n * Luhn checksum so look-alike digit runs (epoch-ms timestamps, order ids)\n * are not corrupted.\n */\n readonly verify?: (match: string) => boolean;\n /**\n * Optional opt-in flag. When `true` the pattern is **not** active by\n * default; operators must add it to `enabledPatterns` explicitly. Used\n * by the IPv4 / IPv6 patterns because raw IPs frequently appear in\n * non-PII log lines (host headers, debug traces, …).\n */\n readonly optIn?: boolean;\n}\n\nconst PATTERNS: readonly RedactionPattern[] = [\n {\n name: 'graphorin-token',\n category: 'secret',\n // Matches the DEFAULT token prefix from @graphorin/security\n // (DEFAULT_TOKEN_PREFIX = 'gph'); deployments that configure a\n // custom prefix must register their own pattern. The env label is\n // matched loosely because `acceptEnvironments` is operator-extensible.\n description: 'Graphorin server token (`gph_<env>_v1_<entropy>_<crc32>`; default prefix).',\n regex: /gph_[a-z0-9]{2,12}_v1_[A-Za-z0-9]{20,80}_[A-Za-z0-9]{6}/g,\n mask: '[REDACTED graphorin-token]',\n },\n {\n name: 'openai-key',\n category: 'secret',\n description: 'OpenAI API key (`sk-...`).',\n regex: /\\bsk-[A-Za-z0-9_-]{20,}\\b/g,\n mask: '[REDACTED openai-key]',\n },\n {\n name: 'anthropic-key',\n category: 'secret',\n description: 'Anthropic API key (`sk-ant-...`).',\n regex: /\\bsk-ant-[A-Za-z0-9_-]{20,}\\b/g,\n mask: '[REDACTED anthropic-key]',\n },\n {\n name: 'aws-access-key',\n category: 'secret',\n description: 'AWS access-key id (`AKIA...`).',\n regex: /\\b(?:AKIA|ASIA)[0-9A-Z]{16}\\b/g,\n mask: '[REDACTED aws-access-key]',\n },\n {\n name: 'gcp-service-account',\n category: 'secret',\n description:\n 'GCP service-account email (`...@...iam.gserviceaccount.com`). Opt-in; the `email` pattern already redacts the address shape; this entry exists for operators wanting the explicit `gcp-service-account` counter label.',\n regex: /[A-Za-z0-9._-]+@[A-Za-z0-9-]+\\.iam\\.gserviceaccount\\.com\\b/g,\n mask: '[REDACTED gcp-service-account]',\n optIn: true,\n },\n {\n name: 'github-pat',\n category: 'secret',\n description: 'GitHub personal-access token / OAuth token / app token.',\n regex: /\\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,255}\\b/g,\n mask: '[REDACTED github-pat]',\n },\n {\n name: 'jwt',\n category: 'secret',\n description: 'JSON Web Token (`eyJ...`).',\n regex: /\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\b/g,\n mask: '[REDACTED jwt]',\n },\n {\n name: 'bearer-header',\n category: 'secret',\n description: '`Authorization: Bearer ...` header values.',\n regex: /\\bBearer\\s+[A-Za-z0-9._-]{16,}\\b/gi,\n mask: 'Bearer [REDACTED]',\n },\n {\n name: 'basic-auth',\n category: 'secret',\n description: '`Authorization: Basic ...` header values.',\n regex: /\\bBasic\\s+[A-Za-z0-9+/=]{16,}\\b/gi,\n mask: 'Basic [REDACTED]',\n },\n {\n name: 'private-key-pem',\n category: 'secret',\n description: 'PEM private-key block (`-----BEGIN ... PRIVATE KEY-----`).',\n regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\\s\\S]+?-----END [A-Z ]*PRIVATE KEY-----/g,\n mask: '[REDACTED private-key-pem]',\n },\n {\n name: 'email',\n category: 'pii',\n description: 'RFC-5322-ish email address.',\n regex: /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b/g,\n mask: '[REDACTED email]',\n },\n {\n name: 'creditcard',\n category: 'pii',\n description: 'Credit card number (13-19 digits, optional spaces / dashes; Luhn-checked).',\n regex: /\\b(?:\\d[\\s-]*?){13,19}\\b/g,\n mask: '[REDACTED creditcard]',\n // RP-21: require a valid Luhn checksum so a 13-19 digit run that is not a\n // real PAN (millisecond epoch timestamps, order numbers, …) is left alone.\n verify: isLuhnValid,\n },\n {\n name: 'us-ssn',\n category: 'pii',\n description: 'US Social Security Number (`123-45-6789`).',\n regex: /\\b\\d{3}-\\d{2}-\\d{4}\\b/g,\n mask: '[REDACTED us-ssn]',\n },\n {\n name: 'phone-e164',\n category: 'pii',\n description: 'E.164 phone number (`+11234567890`).',\n regex: /\\+\\d{7,15}\\b/g,\n mask: '[REDACTED phone-e164]',\n },\n {\n name: 'iban',\n category: 'pii',\n description: 'IBAN (2-letter country + 2 check digits + up to 30 alphanumerics).',\n regex: /\\b[A-Z]{2}\\d{2}[A-Z0-9]{11,30}\\b/g,\n mask: '[REDACTED iban]',\n },\n {\n name: 'ipv4',\n category: 'pii',\n description: 'IPv4 dotted-quad address. Opt-in (often appears legitimately in logs).',\n regex: /\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/g,\n mask: '[REDACTED ipv4]',\n optIn: true,\n },\n {\n name: 'ipv6',\n category: 'pii',\n description: 'IPv6 address (full / compressed). Opt-in.',\n regex: /\\b(?:[0-9a-fA-F]{1,4}:){2,7}[0-9a-fA-F]{1,4}\\b/g,\n mask: '[REDACTED ipv6]',\n optIn: true,\n },\n];\n\n/**\n * The 14 default-on built-in patterns (the IPv4 and IPv6 detectors are\n * opt-in and live in {@link OPT_IN_PATTERNS}).\n *\n * @stable\n */\nexport const BUILT_IN_PATTERNS: readonly RedactionPattern[] = PATTERNS.filter(\n (p) => p.optIn !== true,\n);\n\n/**\n * Patterns that are recognised by the validator but are NOT enabled by\n * default. Use them via `patterns: [...BUILT_IN_PATTERNS, ...OPT_IN_PATTERNS]`.\n *\n * @stable\n */\nexport const OPT_IN_PATTERNS: readonly RedactionPattern[] = PATTERNS.filter(\n (p) => p.optIn === true,\n);\n\n/**\n * Full registry - for tooling that wants to introspect every pattern\n * the framework knows about (e.g. CLI `graphorin redaction list`).\n *\n * @stable\n */\nexport const ALL_BUILT_IN_PATTERNS: readonly RedactionPattern[] = PATTERNS;\n\n/**\n * Luhn (mod-10) checksum validator used by the `creditcard` pattern (RP-21).\n * Strips spaces / dashes, bounds the length to 13-19 digits, and verifies the\n * checksum so a digit run that merely *looks* like a PAN is not redacted.\n */\nfunction isLuhnValid(value: string): boolean {\n const digits = value.replace(/\\D/g, '');\n if (digits.length < 13 || digits.length > 19) return false;\n let sum = 0;\n let double = false;\n for (let i = digits.length - 1; i >= 0; i -= 1) {\n let d = digits.charCodeAt(i) - 48; // '0'\n if (double) {\n d *= 2;\n if (d > 9) d -= 9;\n }\n sum += d;\n double = !double;\n }\n return sum % 10 === 0;\n}\n"],"mappings":";AAiFA,MAAMA,WAAwC;CAC5C;EACE,MAAM;EACN,UAAU;EAKV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aACE;EACF,OAAO;EACP,MAAM;EACN,OAAO;EACR;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EAGN,QAAQ;EACT;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACP;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACN,OAAO;EACR;CACD;EACE,MAAM;EACN,UAAU;EACV,aAAa;EACb,OAAO;EACP,MAAM;EACN,OAAO;EACR;CACF;;;;;;;AAQD,MAAaC,oBAAiD,SAAS,QACpE,MAAM,EAAE,UAAU,KACpB;;;;;;;AAQD,MAAaC,kBAA+C,SAAS,QAClE,MAAM,EAAE,UAAU,KACpB;;;;;;;AAQD,MAAaC,wBAAqD;;;;;;AAOlE,SAAS,YAAY,OAAwB;CAC3C,MAAM,SAAS,MAAM,QAAQ,OAAO,GAAG;AACvC,KAAI,OAAO,SAAS,MAAM,OAAO,SAAS,GAAI,QAAO;CACrD,IAAI,MAAM;CACV,IAAI,SAAS;AACb,MAAK,IAAI,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG;EAC9C,IAAI,IAAI,OAAO,WAAW,EAAE,GAAG;AAC/B,MAAI,QAAQ;AACV,QAAK;AACL,OAAI,IAAI,EAAG,MAAK;;AAElB,SAAO;AACP,WAAS,CAAC;;AAEZ,QAAO,MAAM,OAAO"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphorin/observability",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Observability primitives for the Graphorin framework: typed AISpan tracer over OpenTelemetry, mandatory withValidation wrapper for every exporter, sensitivity-aware RedactionValidator with built-in PII / secret patterns, GenAI Semantic Conventions conformance helpers, OpenInference span-kind emission, structured logger with span correlation, append-only JSONL exporter for replay, sanitized replay primitives, hierarchical CostTracker, and a minimal inline eval runner alongside a zero-default telemetry stub.",
5
5
  "license": "MIT",
6
6
  "author": "Oleksiy Stepurenko",
@@ -100,7 +100,7 @@
100
100
  "LICENSE"
101
101
  ],
102
102
  "dependencies": {
103
- "@graphorin/core": "0.6.0"
103
+ "@graphorin/core": "0.6.1"
104
104
  },
105
105
  "peerDependencies": {
106
106
  "@opentelemetry/api": "^1.9.0",