@talosprotocol/contracts 1.1.15 → 1.2.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/dist/index.cjs +5 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
package/dist/index.cjs
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
Base64UrlError: () => Base64UrlError,
|
|
24
|
+
VERSION: () => VERSION,
|
|
24
25
|
assertCursorInvariant: () => assertCursorInvariant,
|
|
25
26
|
base64urlDecodeToBytes: () => base64urlDecodeToBytes,
|
|
26
27
|
base64urlDecodeToUtf8: () => base64urlDecodeToUtf8,
|
|
@@ -310,9 +311,13 @@ function createEvidenceBundle(params) {
|
|
|
310
311
|
gateway_snapshot: redactedSnapshot
|
|
311
312
|
};
|
|
312
313
|
}
|
|
314
|
+
|
|
315
|
+
// src/index.ts
|
|
316
|
+
var VERSION = "1.2.0";
|
|
313
317
|
// Annotate the CommonJS export names for ESM import in node:
|
|
314
318
|
0 && (module.exports = {
|
|
315
319
|
Base64UrlError,
|
|
320
|
+
VERSION,
|
|
316
321
|
assertCursorInvariant,
|
|
317
322
|
base64urlDecodeToBytes,
|
|
318
323
|
base64urlDecodeToUtf8,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/infrastructure/base64url.ts","../src/infrastructure/uuidv7.ts","../src/domain/logic/cursor.ts","../src/domain/logic/continuity.ts","../src/domain/logic/ordering.ts","../src/domain/logic/redaction.ts","../src/domain/logic/evidence-bundle.ts"],"sourcesContent":["// typescript/src/index.ts\n// Public API exports for @talosprotocol/contracts\n//\n// This file is the ONLY supported import path for this package.\n// All exports are stable and backward-compatible.\n\n// ============================================================================\n// Infrastructure: Encoding & Validation Utilities\n// ============================================================================\n\nexport {\n Base64UrlError,\n base64urlEncodeBytes,\n base64urlDecodeToBytes,\n base64urlEncodeUtf8,\n base64urlDecodeToUtf8,\n} from \"./infrastructure/base64url.js\";\n\nexport { isUuidV7, isCanonicalLowerUuid } from \"./infrastructure/uuidv7.js\";\n\n// ============================================================================\n// Domain Types\n// ============================================================================\n\n// Cursor types\nexport type {\n CursorValidationReason,\n CursorValidationResult,\n DecodedCursor,\n} from \"./domain/types/cursor.types.js\";\n\n// Event types\nexport type {\n AuditEvent,\n GatewayStatus,\n Outcome,\n RedactionLevel,\n AuditFilters,\n Comparator,\n} from \"./domain/types/event.types.js\";\n\n// Bundle types\nexport type {\n EvidenceBundleMetadata,\n IntegritySummary,\n EvidenceBundle,\n CursorGap,\n ContinuityCheckResult,\n} from \"./domain/types/bundle.types.js\";\n\n// ============================================================================\n// Domain Logic\n// ============================================================================\n\n// Cursor operations\nexport {\n deriveCursor,\n decodeCursor,\n compareCursor,\n assertCursorInvariant,\n} from \"./domain/logic/cursor.js\";\n\n// Continuity checking\nexport { checkCursorContinuity } from \"./domain/logic/continuity.js\";\n\n// Event ordering\nexport { orderingCompare } from \"./domain/logic/ordering.js\";\n\n// Redaction\nexport {\n redactEvent,\n redactGatewaySnapshot,\n} from \"./domain/logic/redaction.js\";\n\n// Evidence bundle creation\nexport { createEvidenceBundle } from \"./domain/logic/evidence-bundle.js\";\n","// src/infrastructure/base64url.ts\n// Strict base64url encoding/decoding - NO btoa/atob, NO padding\n// Uses TextEncoder/TextDecoder for universal runtime support (Node, Browser, Edge)\n\nexport class Base64UrlError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"Base64UrlError\";\n }\n}\n\nconst ALPHABET =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\nconst RE_VALID = /^[A-Za-z0-9\\-_]*$/;\n\nfunction toUtf8Bytes(s: string): Uint8Array {\n return new TextEncoder().encode(s);\n}\n\nfunction fromUtf8Bytes(b: Uint8Array): string {\n return new TextDecoder(\"utf-8\", { fatal: true }).decode(b);\n}\n\nexport function base64urlEncodeBytes(bytes: Uint8Array): string {\n if (bytes.length === 0) return \"\";\n\n let out = \"\";\n let i = 0;\n\n while (i + 3 <= bytes.length) {\n const n = (bytes[i]! << 16) | (bytes[i + 1]! << 8) | bytes[i + 2]!;\n out += ALPHABET[(n >>> 18) & 63]!;\n out += ALPHABET[(n >>> 12) & 63]!;\n out += ALPHABET[(n >>> 6) & 63]!;\n out += ALPHABET[n & 63]!;\n i += 3;\n }\n\n const rem = bytes.length - i;\n if (rem === 1) {\n const n = bytes[i]!;\n out += ALPHABET[(n >>> 2) & 63]!;\n out += ALPHABET[(n << 4) & 63]!;\n // no padding\n } else if (rem === 2) {\n const n = (bytes[i]! << 8) | bytes[i + 1]!;\n out += ALPHABET[(n >>> 10) & 63]!;\n out += ALPHABET[(n >>> 4) & 63]!;\n out += ALPHABET[(n << 2) & 63]!;\n // no padding\n }\n\n return out;\n}\n\nfunction decodeChar(c: string): number {\n const idx = ALPHABET.indexOf(c);\n if (idx === -1) throw new Base64UrlError(`Invalid base64url character: ${c}`);\n return idx;\n}\n\nexport function base64urlDecodeToBytes(s: string): Uint8Array {\n if (s.length === 0) return new Uint8Array(0);\n\n // Reject padding and non-url alphabet\n if (s.includes(\"=\")) throw new Base64UrlError(\"Padding is not allowed\");\n if (!RE_VALID.test(s))\n throw new Base64UrlError(\"Non-base64url characters present\");\n // length mod 4 == 1 is impossible for canonical base64url\n if (s.length % 4 === 1) throw new Base64UrlError(\"Invalid base64url length\");\n\n const out: number[] = [];\n let i = 0;\n\n while (i < s.length) {\n const remain = s.length - i;\n\n if (remain >= 4) {\n const a = decodeChar(s[i]!),\n b = decodeChar(s[i + 1]!),\n c = decodeChar(s[i + 2]!),\n d = decodeChar(s[i + 3]!);\n const n = (a << 18) | (b << 12) | (c << 6) | d;\n out.push((n >>> 16) & 255, (n >>> 8) & 255, n & 255);\n i += 4;\n continue;\n }\n\n if (remain === 2) {\n const a = decodeChar(s[i]!),\n b = decodeChar(s[i + 1]!);\n const n = (a << 18) | (b << 12);\n out.push((n >>> 16) & 255);\n i += 2;\n continue;\n }\n\n if (remain === 3) {\n const a = decodeChar(s[i]!),\n b = decodeChar(s[i + 1]!),\n c = decodeChar(s[i + 2]!);\n const n = (a << 18) | (b << 12) | (c << 6);\n out.push((n >>> 16) & 255, (n >>> 8) & 255);\n i += 3;\n continue;\n }\n\n throw new Base64UrlError(\"Invalid base64url length\");\n }\n\n const decoded = new Uint8Array(out);\n // Strict canonical check: re-encode must match exactly\n const recoded = base64urlEncodeBytes(decoded);\n if (recoded !== s) throw new Base64UrlError(\"Non-canonical base64url form\");\n return decoded;\n}\n\nexport function base64urlEncodeUtf8(s: string): string {\n return base64urlEncodeBytes(toUtf8Bytes(s));\n}\n\nexport function base64urlDecodeToUtf8(s: string): string {\n return fromUtf8Bytes(base64urlDecodeToBytes(s));\n}\n","// src/infrastructure/uuidv7.ts\n// Strict UUIDv7 validation - pure regex, no external dependencies\n\nconst RE_UUID =\n /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;\n\nexport function isUuidV7(id: string): boolean {\n if (!RE_UUID.test(id)) return false;\n // version nibble is first nibble of 3rd group (position 14)\n const version = id[14]!;\n if (version !== \"7\") return false;\n\n // variant is first nibble of 4th group (position 19): 8, 9, a, b\n const variant = id[19]!.toLowerCase();\n return (\n variant === \"8\" || variant === \"9\" || variant === \"a\" || variant === \"b\"\n );\n}\n\nexport function isCanonicalLowerUuid(id: string): boolean {\n return id === id.toLowerCase();\n}\n","// src/domain/logic/cursor.ts\n// Cursor derivation, validation, and comparison\n// D4=B: invalid frames do not throw from validation, return INVALID_FRAME\n\nimport {\n base64urlDecodeToUtf8,\n base64urlEncodeUtf8,\n} from \"../../infrastructure/base64url.js\";\nimport { isUuidV7, isCanonicalLowerUuid } from \"../../infrastructure/uuidv7.js\";\nimport type {\n CursorValidationResult,\n DecodedCursor,\n} from \"../types/cursor.types.js\";\n\nfunction isValidUnixSecondsInt(n: unknown): n is number {\n return (\n typeof n === \"number\" &&\n Number.isInteger(n) &&\n n >= 0 &&\n Number.isSafeInteger(n)\n );\n}\n\nfunction isCanonicalTimestampString(s: string): boolean {\n if (!/^\\d+$/.test(s)) return false;\n if (s.length > 1 && s.startsWith(\"0\")) return false;\n return true;\n}\n\n/**\n * Derive a cursor from timestamp and event_id.\n * cursor = base64url(utf8(\"{timestamp}:{event_id}\"))\n */\nexport function deriveCursor(timestamp: number, eventId: string): string {\n if (!isValidUnixSecondsInt(timestamp)) {\n throw new Error(\"timestamp must be unix seconds integer\");\n }\n const plain = `${String(timestamp)}:${eventId}`;\n return base64urlEncodeUtf8(plain);\n}\n\n/**\n * Decode a cursor to {timestamp, event_id}.\n * Throws on invalid cursor format.\n */\nexport function decodeCursor(cursor: string): DecodedCursor {\n const raw = base64urlDecodeToUtf8(cursor);\n const colonIndex = raw.indexOf(\":\");\n if (colonIndex === -1)\n throw new Error(\"cursor frame must be '{timestamp}:{event_id}'\");\n\n const tsStr = raw.slice(0, colonIndex);\n const eventId = raw.slice(colonIndex + 1);\n\n if (!isCanonicalTimestampString(tsStr))\n throw new Error(\"timestamp is not canonical base-10\");\n const tsNum = Number(tsStr);\n if (!isValidUnixSecondsInt(tsNum))\n throw new Error(\"timestamp is out of range\");\n\n if (!isUuidV7(eventId)) throw new Error(\"event_id is not uuidv7\");\n if (!isCanonicalLowerUuid(eventId))\n throw new Error(\"event_id must be lowercase canonical uuid\");\n\n return { timestamp: tsNum, event_id: eventId };\n}\n\n/**\n * Compare two cursors.\n * Returns -1 if a < b, 0 if equal, 1 if a > b.\n * Compares by (timestamp, event_id) lexicographically.\n */\nexport function compareCursor(a: string, b: string): -1 | 0 | 1 {\n const da = decodeCursor(a);\n const db = decodeCursor(b);\n\n if (da.timestamp < db.timestamp) return -1;\n if (da.timestamp > db.timestamp) return 1;\n\n if (da.event_id < db.event_id) return -1;\n if (da.event_id > db.event_id) return 1;\n return 0;\n}\n\n/**\n * Validate that an event's cursor matches the derived cursor.\n * D4=B: Does not throw on invalid frames, returns { ok: false, reason: \"INVALID_FRAME\" }\n */\nexport function assertCursorInvariant(event: {\n timestamp: unknown;\n event_id: unknown;\n cursor: unknown;\n}): CursorValidationResult {\n // Frame checks first (D4=B: do not throw)\n if (\n !isValidUnixSecondsInt(event.timestamp) ||\n typeof event.event_id !== \"string\" ||\n typeof event.cursor !== \"string\"\n ) {\n return { ok: false, derived: \"\", reason: \"INVALID_FRAME\" };\n }\n\n // Decode cursor must be strict base64url + canonical frame. If it fails, INVALID_FRAME.\n try {\n decodeCursor(event.cursor);\n } catch {\n const derived = deriveCursor(event.timestamp, event.event_id);\n return { ok: false, derived, reason: \"INVALID_FRAME\" };\n }\n\n // Derived cursor must match exactly (CURSOR_MISMATCH)\n const derived = deriveCursor(event.timestamp, event.event_id);\n if (event.cursor !== derived)\n return { ok: false, derived, reason: \"CURSOR_MISMATCH\" };\n\n // Also enforce event_id canonical uuidv7 for the event frame\n if (!isUuidV7(event.event_id) || !isCanonicalLowerUuid(event.event_id)) {\n return { ok: false, derived, reason: \"INVALID_FRAME\" };\n }\n\n return { ok: true, derived };\n}\n","// src/domain/logic/continuity.ts\n// Cursor continuity checking\n\nimport { compareCursor } from \"./cursor.js\";\nimport type {\n CursorGap,\n ContinuityCheckResult,\n} from \"../types/bundle.types.js\";\n\n/**\n * Check cursor continuity based on contract-defined rules.\n * Gap detection requires cursor predecessor relation, not heuristics.\n * For v1.1, we verify strict ordering.\n */\nexport function checkCursorContinuity(\n events: Array<{ cursor: string; timestamp: number }>,\n _expectedPredecessor?: (cursor: string) => string | null,\n): ContinuityCheckResult {\n if (events.length <= 1) {\n return { status: \"CONTINUOUS\", gaps: [] };\n }\n\n const gaps: CursorGap[] = [];\n\n // Verify ordering\n for (let i = 0; i < events.length - 1; i++) {\n const current = events[i];\n const next = events[i + 1];\n\n // If next cursor is Lexicographically smaller than current, strict ordering violation\n const cmp = compareCursor(current.cursor, next.cursor);\n if (cmp === 1) {\n // Order violation isn't exactly a \"gap\" but a \"discontinuity\"\n // For v1.1, we treat this as a Gap for the sake of the interface\n gaps.push({\n from_cursor: current.cursor,\n to_cursor: next.cursor,\n detected_at: Date.now(),\n });\n }\n }\n\n // FUTURE(v1.2): Implement actual rigorous predecessor check using Merkle links if available\n // For now, we only check monotonic ordering.\n\n if (gaps.length > 0) {\n return { status: \"GAP_DETECTED\", gaps };\n }\n\n return { status: \"CONTINUOUS\", gaps: [] };\n}\n","// src/domain/logic/ordering.ts\n// Event ordering comparison: (timestamp DESC, event_id DESC)\n\n/**\n * Compare two events for ordering.\n * Returns -1 if a should come before b (a is \"greater\" in DESC order)\n * Returns 0 if equal\n * Returns 1 if a should come after b\n *\n * Ordering: timestamp DESC, then event_id DESC\n */\nexport function orderingCompare(\n a: { timestamp: number; event_id: string },\n b: { timestamp: number; event_id: string },\n): -1 | 0 | 1 {\n // DESC timestamp: higher timestamp comes first\n if (a.timestamp > b.timestamp) return -1;\n if (a.timestamp < b.timestamp) return 1;\n\n // DESC event_id: higher event_id comes first (lexicographic)\n if (a.event_id > b.event_id) return -1;\n if (a.event_id < b.event_id) return 1;\n return 0;\n}\n","// src/domain/logic/redaction.ts\n// Event and gateway snapshot redaction\n\nimport type {\n AuditEvent,\n GatewayStatus,\n RedactionLevel,\n} from \"../types/event.types.js\";\n\nexport function redactEvent(\n event: AuditEvent,\n level: RedactionLevel,\n): AuditEvent {\n if (level === \"none\") return event;\n\n const copy = structuredClone(event); // Deep copy\n\n // Common redactions for safe_default\n if (level === \"safe_default\" || level === \"strict\") {\n if (copy.request && typeof copy.request === \"object\") {\n const req = copy.request as {\n headers?: Record<string, string>;\n [key: string]: unknown;\n };\n if (req.headers) {\n // Strip auth headers\n delete req.headers[\"authorization\"];\n delete req.headers[\"cookie\"];\n delete req.headers[\"x-api-key\"];\n }\n }\n }\n\n if (level === \"strict\") {\n // Strict redaction - strip inputs and all headers\n if (copy.input) {\n copy.input = \"[REDACTED]\";\n }\n if (copy.request && typeof copy.request === \"object\") {\n const req = copy.request as {\n headers?: Record<string, string>;\n [key: string]: unknown;\n };\n req.headers = undefined; // Remove all headers\n }\n }\n\n return copy;\n}\n\nexport function redactGatewaySnapshot(\n snapshot: GatewayStatus,\n): Partial<GatewayStatus> {\n const copy = { ...snapshot };\n\n // Always strip these\n delete copy.internal_endpoints;\n delete copy.keys;\n delete copy.tokens;\n\n return copy;\n}\n","// src/domain/logic/evidence-bundle.ts\n// Evidence bundle creation - pure deterministic transformation\n\nimport { compareCursor } from \"./cursor.js\";\nimport { redactEvent, redactGatewaySnapshot } from \"./redaction.js\";\nimport type {\n AuditEvent,\n GatewayStatus,\n RedactionLevel,\n AuditFilters,\n Outcome,\n} from \"../types/event.types.js\";\nimport type {\n EvidenceBundle,\n IntegritySummary,\n} from \"../types/bundle.types.js\";\n\nexport interface CreateEvidenceBundleParams {\n events: AuditEvent[];\n redactionLevel?: RedactionLevel;\n gatewaySnapshot?: GatewayStatus;\n filters?: AuditFilters;\n cursorRange?: { start?: string; end?: string };\n dashboardVersion: string;\n}\n\nexport function createEvidenceBundle(\n params: CreateEvidenceBundleParams,\n): EvidenceBundle {\n const level = params.redactionLevel ?? \"safe_default\";\n\n // 1. Sort events by cursor (canonical order)\n const sortedEvents = [...params.events].sort((a, b) =>\n compareCursor(a.cursor, b.cursor),\n );\n\n // 2. Apply redaction based on level\n const redactedEvents = sortedEvents.map((e) => redactEvent(e, level));\n\n // 3. Compute integrity summary\n const by_outcome: Record<Outcome, number> = { OK: 0, DENY: 0, ERROR: 0 };\n const by_denial_reason: Record<string, number> = {};\n\n redactedEvents.forEach((e) => {\n const outcome = (e.outcome as Outcome) || \"OK\";\n by_outcome[outcome] = (by_outcome[outcome] || 0) + 1;\n\n if (typeof e.reason === \"string\") {\n by_denial_reason[e.reason] = (by_denial_reason[e.reason] || 0) + 1;\n }\n });\n\n const summary: IntegritySummary = {\n total_events: redactedEvents.length,\n by_outcome,\n by_denial_reason,\n cursor_continuity: \"UNKNOWN\", // Continuity check is computationally expensive for large bundles\n };\n\n // 4. Strip secrets from gateway snapshot\n const redactedSnapshot = params.gatewaySnapshot\n ? redactGatewaySnapshot(params.gatewaySnapshot)\n : undefined;\n\n return {\n metadata: {\n schema_version: \"1.1.0\",\n generated_at: new Date().toISOString(),\n source: {\n dashboard_version: params.dashboardVersion,\n gateway_version: params.gatewaySnapshot?.version,\n },\n filters_applied: params.filters,\n cursor_range: params.cursorRange,\n redaction_level: level,\n },\n events: redactedEvents,\n integrity_summary: summary,\n gateway_snapshot: redactedSnapshot,\n };\n}\n"],"mappings":"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","names":["derived"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/infrastructure/base64url.ts","../src/infrastructure/uuidv7.ts","../src/domain/logic/cursor.ts","../src/domain/logic/continuity.ts","../src/domain/logic/ordering.ts","../src/domain/logic/redaction.ts","../src/domain/logic/evidence-bundle.ts"],"sourcesContent":["// typescript/src/index.ts\n// Public API exports for @talosprotocol/contracts\n//\n// This file is the ONLY supported import path for this package.\n// All exports are stable and backward-compatible.\n\n// ============================================================================\n// Infrastructure: Encoding & Validation Utilities\n// ============================================================================\n\nexport {\n Base64UrlError,\n base64urlEncodeBytes,\n base64urlDecodeToBytes,\n base64urlEncodeUtf8,\n base64urlDecodeToUtf8,\n} from \"./infrastructure/base64url\";\n\nexport { isUuidV7, isCanonicalLowerUuid } from \"./infrastructure/uuidv7\";\n\n// ============================================================================\n// Domain Types\n// ============================================================================\n\n// Cursor types\nexport type {\n CursorValidationReason,\n CursorValidationResult,\n DecodedCursor,\n} from \"./domain/types/cursor.types\";\n\n// Event types\nexport type {\n AuditEvent,\n GatewayStatus,\n Outcome,\n RedactionLevel,\n AuditFilters,\n Comparator,\n} from \"./domain/types/event.types\";\n\n// Bundle types\nexport type {\n EvidenceBundleMetadata,\n IntegritySummary,\n EvidenceBundle,\n CursorGap,\n ContinuityCheckResult,\n} from \"./domain/types/bundle.types\";\n\n// ============================================================================\n// Domain Logic\n// ============================================================================\n\n// Cursor operations\nexport {\n deriveCursor,\n decodeCursor,\n compareCursor,\n assertCursorInvariant,\n} from \"./domain/logic/cursor\";\n\n// Continuity checking\nexport { checkCursorContinuity } from \"./domain/logic/continuity\";\n\n// Event ordering\nexport { orderingCompare } from \"./domain/logic/ordering\";\n\n// Redaction\nexport {\n redactEvent,\n redactGatewaySnapshot,\n} from \"./domain/logic/redaction\";\n\n// Evidence bundle creation\nexport { createEvidenceBundle } from \"./domain/logic/evidence-bundle\";\n\nexport const VERSION = \"1.2.0\";\n","// src/infrastructure/base64url.ts\n// Strict base64url encoding/decoding - NO btoa/atob, NO padding\n// Uses TextEncoder/TextDecoder for universal runtime support (Node, Browser, Edge)\n\nexport class Base64UrlError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"Base64UrlError\";\n }\n}\n\nconst ALPHABET =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\nconst RE_VALID = /^[A-Za-z0-9\\-_]*$/;\n\nfunction toUtf8Bytes(s: string): Uint8Array {\n return new TextEncoder().encode(s);\n}\n\nfunction fromUtf8Bytes(b: Uint8Array): string {\n return new TextDecoder(\"utf-8\", { fatal: true }).decode(b);\n}\n\nexport function base64urlEncodeBytes(bytes: Uint8Array): string {\n if (bytes.length === 0) return \"\";\n\n let out = \"\";\n let i = 0;\n\n while (i + 3 <= bytes.length) {\n const n = (bytes[i]! << 16) | (bytes[i + 1]! << 8) | bytes[i + 2]!;\n out += ALPHABET[(n >>> 18) & 63]!;\n out += ALPHABET[(n >>> 12) & 63]!;\n out += ALPHABET[(n >>> 6) & 63]!;\n out += ALPHABET[n & 63]!;\n i += 3;\n }\n\n const rem = bytes.length - i;\n if (rem === 1) {\n const n = bytes[i]!;\n out += ALPHABET[(n >>> 2) & 63]!;\n out += ALPHABET[(n << 4) & 63]!;\n // no padding\n } else if (rem === 2) {\n const n = (bytes[i]! << 8) | bytes[i + 1]!;\n out += ALPHABET[(n >>> 10) & 63]!;\n out += ALPHABET[(n >>> 4) & 63]!;\n out += ALPHABET[(n << 2) & 63]!;\n // no padding\n }\n\n return out;\n}\n\nfunction decodeChar(c: string): number {\n const idx = ALPHABET.indexOf(c);\n if (idx === -1) throw new Base64UrlError(`Invalid base64url character: ${c}`);\n return idx;\n}\n\nexport function base64urlDecodeToBytes(s: string): Uint8Array {\n if (s.length === 0) return new Uint8Array(0);\n\n // Reject padding and non-url alphabet\n if (s.includes(\"=\")) throw new Base64UrlError(\"Padding is not allowed\");\n if (!RE_VALID.test(s))\n throw new Base64UrlError(\"Non-base64url characters present\");\n // length mod 4 == 1 is impossible for canonical base64url\n if (s.length % 4 === 1) throw new Base64UrlError(\"Invalid base64url length\");\n\n const out: number[] = [];\n let i = 0;\n\n while (i < s.length) {\n const remain = s.length - i;\n\n if (remain >= 4) {\n const a = decodeChar(s[i]!),\n b = decodeChar(s[i + 1]!),\n c = decodeChar(s[i + 2]!),\n d = decodeChar(s[i + 3]!);\n const n = (a << 18) | (b << 12) | (c << 6) | d;\n out.push((n >>> 16) & 255, (n >>> 8) & 255, n & 255);\n i += 4;\n continue;\n }\n\n if (remain === 2) {\n const a = decodeChar(s[i]!),\n b = decodeChar(s[i + 1]!);\n const n = (a << 18) | (b << 12);\n out.push((n >>> 16) & 255);\n i += 2;\n continue;\n }\n\n if (remain === 3) {\n const a = decodeChar(s[i]!),\n b = decodeChar(s[i + 1]!),\n c = decodeChar(s[i + 2]!);\n const n = (a << 18) | (b << 12) | (c << 6);\n out.push((n >>> 16) & 255, (n >>> 8) & 255);\n i += 3;\n continue;\n }\n\n throw new Base64UrlError(\"Invalid base64url length\");\n }\n\n const decoded = new Uint8Array(out);\n // Strict canonical check: re-encode must match exactly\n const recoded = base64urlEncodeBytes(decoded);\n if (recoded !== s) throw new Base64UrlError(\"Non-canonical base64url form\");\n return decoded;\n}\n\nexport function base64urlEncodeUtf8(s: string): string {\n return base64urlEncodeBytes(toUtf8Bytes(s));\n}\n\nexport function base64urlDecodeToUtf8(s: string): string {\n return fromUtf8Bytes(base64urlDecodeToBytes(s));\n}\n","// src/infrastructure/uuidv7.ts\n// Strict UUIDv7 validation - pure regex, no external dependencies\n\nconst RE_UUID =\n /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;\n\nexport function isUuidV7(id: string): boolean {\n if (!RE_UUID.test(id)) return false;\n // version nibble is first nibble of 3rd group (position 14)\n const version = id[14]!;\n if (version !== \"7\") return false;\n\n // variant is first nibble of 4th group (position 19): 8, 9, a, b\n const variant = id[19]!.toLowerCase();\n return (\n variant === \"8\" || variant === \"9\" || variant === \"a\" || variant === \"b\"\n );\n}\n\nexport function isCanonicalLowerUuid(id: string): boolean {\n return id === id.toLowerCase();\n}\n","// src/domain/logic/cursor.ts\n// Cursor derivation, validation, and comparison\n// D4=B: invalid frames do not throw from validation, return INVALID_FRAME\n\nimport {\n base64urlDecodeToUtf8,\n base64urlEncodeUtf8,\n} from \"../../infrastructure/base64url\";\nimport { isUuidV7, isCanonicalLowerUuid } from \"../../infrastructure/uuidv7\";\nimport type {\n CursorValidationResult,\n DecodedCursor,\n} from \"../types/cursor.types\";\n\nfunction isValidUnixSecondsInt(n: unknown): n is number {\n return (\n typeof n === \"number\" &&\n Number.isInteger(n) &&\n n >= 0 &&\n Number.isSafeInteger(n)\n );\n}\n\nfunction isCanonicalTimestampString(s: string): boolean {\n if (!/^\\d+$/.test(s)) return false;\n if (s.length > 1 && s.startsWith(\"0\")) return false;\n return true;\n}\n\n/**\n * Derive a cursor from timestamp and event_id.\n * cursor = base64url(utf8(\"{timestamp}:{event_id}\"))\n */\nexport function deriveCursor(timestamp: number, eventId: string): string {\n if (!isValidUnixSecondsInt(timestamp)) {\n throw new Error(\"timestamp must be unix seconds integer\");\n }\n const plain = `${String(timestamp)}:${eventId}`;\n return base64urlEncodeUtf8(plain);\n}\n\n/**\n * Decode a cursor to {timestamp, event_id}.\n * Throws on invalid cursor format.\n */\nexport function decodeCursor(cursor: string): DecodedCursor {\n const raw = base64urlDecodeToUtf8(cursor);\n const colonIndex = raw.indexOf(\":\");\n if (colonIndex === -1)\n throw new Error(\"cursor frame must be '{timestamp}:{event_id}'\");\n\n const tsStr = raw.slice(0, colonIndex);\n const eventId = raw.slice(colonIndex + 1);\n\n if (!isCanonicalTimestampString(tsStr))\n throw new Error(\"timestamp is not canonical base-10\");\n const tsNum = Number(tsStr);\n if (!isValidUnixSecondsInt(tsNum))\n throw new Error(\"timestamp is out of range\");\n\n if (!isUuidV7(eventId)) throw new Error(\"event_id is not uuidv7\");\n if (!isCanonicalLowerUuid(eventId))\n throw new Error(\"event_id must be lowercase canonical uuid\");\n\n return { timestamp: tsNum, event_id: eventId };\n}\n\n/**\n * Compare two cursors.\n * Returns -1 if a < b, 0 if equal, 1 if a > b.\n * Compares by (timestamp, event_id) lexicographically.\n */\nexport function compareCursor(a: string, b: string): -1 | 0 | 1 {\n const da = decodeCursor(a);\n const db = decodeCursor(b);\n\n if (da.timestamp < db.timestamp) return -1;\n if (da.timestamp > db.timestamp) return 1;\n\n if (da.event_id < db.event_id) return -1;\n if (da.event_id > db.event_id) return 1;\n return 0;\n}\n\n/**\n * Validate that an event's cursor matches the derived cursor.\n * D4=B: Does not throw on invalid frames, returns { ok: false, reason: \"INVALID_FRAME\" }\n */\nexport function assertCursorInvariant(event: {\n timestamp: unknown;\n event_id: unknown;\n cursor: unknown;\n}): CursorValidationResult {\n // Frame checks first (D4=B: do not throw)\n if (\n !isValidUnixSecondsInt(event.timestamp) ||\n typeof event.event_id !== \"string\" ||\n typeof event.cursor !== \"string\"\n ) {\n return { ok: false, derived: \"\", reason: \"INVALID_FRAME\" };\n }\n\n // Decode cursor must be strict base64url + canonical frame. If it fails, INVALID_FRAME.\n try {\n decodeCursor(event.cursor);\n } catch {\n const derived = deriveCursor(event.timestamp, event.event_id);\n return { ok: false, derived, reason: \"INVALID_FRAME\" };\n }\n\n // Derived cursor must match exactly (CURSOR_MISMATCH)\n const derived = deriveCursor(event.timestamp, event.event_id);\n if (event.cursor !== derived)\n return { ok: false, derived, reason: \"CURSOR_MISMATCH\" };\n\n // Also enforce event_id canonical uuidv7 for the event frame\n if (!isUuidV7(event.event_id) || !isCanonicalLowerUuid(event.event_id)) {\n return { ok: false, derived, reason: \"INVALID_FRAME\" };\n }\n\n return { ok: true, derived };\n}\n","// src/domain/logic/continuity.ts\n// Cursor continuity checking\n\nimport { compareCursor } from \"./cursor\";\nimport type {\n CursorGap,\n ContinuityCheckResult,\n} from \"../types/bundle.types\";\n\n/**\n * Check cursor continuity based on contract-defined rules.\n * Gap detection requires cursor predecessor relation, not heuristics.\n * For v1.1, we verify strict ordering.\n */\nexport function checkCursorContinuity(\n events: Array<{ cursor: string; timestamp: number }>,\n _expectedPredecessor?: (cursor: string) => string | null,\n): ContinuityCheckResult {\n if (events.length <= 1) {\n return { status: \"CONTINUOUS\", gaps: [] };\n }\n\n const gaps: CursorGap[] = [];\n\n // Verify ordering\n for (let i = 0; i < events.length - 1; i++) {\n const current = events[i];\n const next = events[i + 1];\n\n // If next cursor is Lexicographically smaller than current, strict ordering violation\n const cmp = compareCursor(current.cursor, next.cursor);\n if (cmp === 1) {\n // Order violation isn't exactly a \"gap\" but a \"discontinuity\"\n // For v1.1, we treat this as a Gap for the sake of the interface\n gaps.push({\n from_cursor: current.cursor,\n to_cursor: next.cursor,\n detected_at: Date.now(),\n });\n }\n }\n\n // FUTURE(v1.2): Implement actual rigorous predecessor check using Merkle links if available\n // For now, we only check monotonic ordering.\n\n if (gaps.length > 0) {\n return { status: \"GAP_DETECTED\", gaps };\n }\n\n return { status: \"CONTINUOUS\", gaps: [] };\n}\n","// src/domain/logic/ordering.ts\n// Event ordering comparison: (timestamp DESC, event_id DESC)\n\n/**\n * Compare two events for ordering.\n * Returns -1 if a should come before b (a is \"greater\" in DESC order)\n * Returns 0 if equal\n * Returns 1 if a should come after b\n *\n * Ordering: timestamp DESC, then event_id DESC\n */\nexport function orderingCompare(\n a: { timestamp: number; event_id: string },\n b: { timestamp: number; event_id: string },\n): -1 | 0 | 1 {\n // DESC timestamp: higher timestamp comes first\n if (a.timestamp > b.timestamp) return -1;\n if (a.timestamp < b.timestamp) return 1;\n\n // DESC event_id: higher event_id comes first (lexicographic)\n if (a.event_id > b.event_id) return -1;\n if (a.event_id < b.event_id) return 1;\n return 0;\n}\n","// src/domain/logic/redaction.ts\n// Event and gateway snapshot redaction\n\nimport type {\n AuditEvent,\n GatewayStatus,\n RedactionLevel,\n} from \"../types/event.types\";\n\nexport function redactEvent(\n event: AuditEvent,\n level: RedactionLevel,\n): AuditEvent {\n if (level === \"none\") return event;\n\n const copy = structuredClone(event); // Deep copy\n\n // Common redactions for safe_default\n if (level === \"safe_default\" || level === \"strict\") {\n if (copy.request && typeof copy.request === \"object\") {\n const req = copy.request as {\n headers?: Record<string, string>;\n [key: string]: unknown;\n };\n if (req.headers) {\n // Strip auth headers\n delete req.headers[\"authorization\"];\n delete req.headers[\"cookie\"];\n delete req.headers[\"x-api-key\"];\n }\n }\n }\n\n if (level === \"strict\") {\n // Strict redaction - strip inputs and all headers\n if (copy.input) {\n copy.input = \"[REDACTED]\";\n }\n if (copy.request && typeof copy.request === \"object\") {\n const req = copy.request as {\n headers?: Record<string, string>;\n [key: string]: unknown;\n };\n req.headers = undefined; // Remove all headers\n }\n }\n\n return copy;\n}\n\nexport function redactGatewaySnapshot(\n snapshot: GatewayStatus,\n): Partial<GatewayStatus> {\n const copy = { ...snapshot };\n\n // Always strip these\n delete copy.internal_endpoints;\n delete copy.keys;\n delete copy.tokens;\n\n return copy;\n}\n","// src/domain/logic/evidence-bundle.ts\n// Evidence bundle creation - pure deterministic transformation\n\nimport { compareCursor } from \"./cursor\";\nimport { redactEvent, redactGatewaySnapshot } from \"./redaction\";\nimport type {\n AuditEvent,\n GatewayStatus,\n RedactionLevel,\n AuditFilters,\n Outcome,\n} from \"../types/event.types\";\nimport type {\n EvidenceBundle,\n IntegritySummary,\n} from \"../types/bundle.types\";\n\nexport interface CreateEvidenceBundleParams {\n events: AuditEvent[];\n redactionLevel?: RedactionLevel;\n gatewaySnapshot?: GatewayStatus;\n filters?: AuditFilters;\n cursorRange?: { start?: string; end?: string };\n dashboardVersion: string;\n}\n\nexport function createEvidenceBundle(\n params: CreateEvidenceBundleParams,\n): EvidenceBundle {\n const level = params.redactionLevel ?? \"safe_default\";\n\n // 1. Sort events by cursor (canonical order)\n const sortedEvents = [...params.events].sort((a, b) =>\n compareCursor(a.cursor, b.cursor),\n );\n\n // 2. Apply redaction based on level\n const redactedEvents = sortedEvents.map((e) => redactEvent(e, level));\n\n // 3. Compute integrity summary\n const by_outcome: Record<Outcome, number> = { OK: 0, DENY: 0, ERROR: 0 };\n const by_denial_reason: Record<string, number> = {};\n\n redactedEvents.forEach((e) => {\n const outcome = (e.outcome as Outcome) || \"OK\";\n by_outcome[outcome] = (by_outcome[outcome] || 0) + 1;\n\n if (typeof e.reason === \"string\") {\n by_denial_reason[e.reason] = (by_denial_reason[e.reason] || 0) + 1;\n }\n });\n\n const summary: IntegritySummary = {\n total_events: redactedEvents.length,\n by_outcome,\n by_denial_reason,\n cursor_continuity: \"UNKNOWN\", // Continuity check is computationally expensive for large bundles\n };\n\n // 4. Strip secrets from gateway snapshot\n const redactedSnapshot = params.gatewaySnapshot\n ? redactGatewaySnapshot(params.gatewaySnapshot)\n : undefined;\n\n return {\n metadata: {\n schema_version: \"1.1.0\",\n generated_at: new Date().toISOString(),\n source: {\n dashboard_version: params.dashboardVersion,\n gateway_version: params.gatewaySnapshot?.version,\n },\n filters_applied: params.filters,\n cursor_range: params.cursorRange,\n redaction_level: level,\n },\n events: redactedEvents,\n integrity_summary: summary,\n gateway_snapshot: redactedSnapshot,\n };\n}\n"],"mappings":"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","names":["derived"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -155,4 +155,6 @@ interface CreateEvidenceBundleParams {
|
|
|
155
155
|
}
|
|
156
156
|
declare function createEvidenceBundle(params: CreateEvidenceBundleParams): EvidenceBundle;
|
|
157
157
|
|
|
158
|
-
|
|
158
|
+
declare const VERSION = "1.2.0";
|
|
159
|
+
|
|
160
|
+
export { type AuditEvent, type AuditFilters, Base64UrlError, type Comparator, type ContinuityCheckResult, type CursorGap, type CursorValidationReason, type CursorValidationResult, type DecodedCursor, type EvidenceBundle, type EvidenceBundleMetadata, type GatewayStatus, type IntegritySummary, type Outcome, type RedactionLevel, VERSION, assertCursorInvariant, base64urlDecodeToBytes, base64urlDecodeToUtf8, base64urlEncodeBytes, base64urlEncodeUtf8, checkCursorContinuity, compareCursor, createEvidenceBundle, decodeCursor, deriveCursor, isCanonicalLowerUuid, isUuidV7, orderingCompare, redactEvent, redactGatewaySnapshot };
|
package/dist/index.d.ts
CHANGED
|
@@ -155,4 +155,6 @@ interface CreateEvidenceBundleParams {
|
|
|
155
155
|
}
|
|
156
156
|
declare function createEvidenceBundle(params: CreateEvidenceBundleParams): EvidenceBundle;
|
|
157
157
|
|
|
158
|
-
|
|
158
|
+
declare const VERSION = "1.2.0";
|
|
159
|
+
|
|
160
|
+
export { type AuditEvent, type AuditFilters, Base64UrlError, type Comparator, type ContinuityCheckResult, type CursorGap, type CursorValidationReason, type CursorValidationResult, type DecodedCursor, type EvidenceBundle, type EvidenceBundleMetadata, type GatewayStatus, type IntegritySummary, type Outcome, type RedactionLevel, VERSION, assertCursorInvariant, base64urlDecodeToBytes, base64urlDecodeToUtf8, base64urlEncodeBytes, base64urlEncodeUtf8, checkCursorContinuity, compareCursor, createEvidenceBundle, decodeCursor, deriveCursor, isCanonicalLowerUuid, isUuidV7, orderingCompare, redactEvent, redactGatewaySnapshot };
|
package/dist/index.js
CHANGED
|
@@ -269,8 +269,12 @@ function createEvidenceBundle(params) {
|
|
|
269
269
|
gateway_snapshot: redactedSnapshot
|
|
270
270
|
};
|
|
271
271
|
}
|
|
272
|
+
|
|
273
|
+
// src/index.ts
|
|
274
|
+
var VERSION = "1.2.0";
|
|
272
275
|
export {
|
|
273
276
|
Base64UrlError,
|
|
277
|
+
VERSION,
|
|
274
278
|
assertCursorInvariant,
|
|
275
279
|
base64urlDecodeToBytes,
|
|
276
280
|
base64urlDecodeToUtf8,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/infrastructure/base64url.ts","../src/infrastructure/uuidv7.ts","../src/domain/logic/cursor.ts","../src/domain/logic/continuity.ts","../src/domain/logic/ordering.ts","../src/domain/logic/redaction.ts","../src/domain/logic/evidence-bundle.ts"],"sourcesContent":["// src/infrastructure/base64url.ts\n// Strict base64url encoding/decoding - NO btoa/atob, NO padding\n// Uses TextEncoder/TextDecoder for universal runtime support (Node, Browser, Edge)\n\nexport class Base64UrlError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"Base64UrlError\";\n }\n}\n\nconst ALPHABET =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\nconst RE_VALID = /^[A-Za-z0-9\\-_]*$/;\n\nfunction toUtf8Bytes(s: string): Uint8Array {\n return new TextEncoder().encode(s);\n}\n\nfunction fromUtf8Bytes(b: Uint8Array): string {\n return new TextDecoder(\"utf-8\", { fatal: true }).decode(b);\n}\n\nexport function base64urlEncodeBytes(bytes: Uint8Array): string {\n if (bytes.length === 0) return \"\";\n\n let out = \"\";\n let i = 0;\n\n while (i + 3 <= bytes.length) {\n const n = (bytes[i]! << 16) | (bytes[i + 1]! << 8) | bytes[i + 2]!;\n out += ALPHABET[(n >>> 18) & 63]!;\n out += ALPHABET[(n >>> 12) & 63]!;\n out += ALPHABET[(n >>> 6) & 63]!;\n out += ALPHABET[n & 63]!;\n i += 3;\n }\n\n const rem = bytes.length - i;\n if (rem === 1) {\n const n = bytes[i]!;\n out += ALPHABET[(n >>> 2) & 63]!;\n out += ALPHABET[(n << 4) & 63]!;\n // no padding\n } else if (rem === 2) {\n const n = (bytes[i]! << 8) | bytes[i + 1]!;\n out += ALPHABET[(n >>> 10) & 63]!;\n out += ALPHABET[(n >>> 4) & 63]!;\n out += ALPHABET[(n << 2) & 63]!;\n // no padding\n }\n\n return out;\n}\n\nfunction decodeChar(c: string): number {\n const idx = ALPHABET.indexOf(c);\n if (idx === -1) throw new Base64UrlError(`Invalid base64url character: ${c}`);\n return idx;\n}\n\nexport function base64urlDecodeToBytes(s: string): Uint8Array {\n if (s.length === 0) return new Uint8Array(0);\n\n // Reject padding and non-url alphabet\n if (s.includes(\"=\")) throw new Base64UrlError(\"Padding is not allowed\");\n if (!RE_VALID.test(s))\n throw new Base64UrlError(\"Non-base64url characters present\");\n // length mod 4 == 1 is impossible for canonical base64url\n if (s.length % 4 === 1) throw new Base64UrlError(\"Invalid base64url length\");\n\n const out: number[] = [];\n let i = 0;\n\n while (i < s.length) {\n const remain = s.length - i;\n\n if (remain >= 4) {\n const a = decodeChar(s[i]!),\n b = decodeChar(s[i + 1]!),\n c = decodeChar(s[i + 2]!),\n d = decodeChar(s[i + 3]!);\n const n = (a << 18) | (b << 12) | (c << 6) | d;\n out.push((n >>> 16) & 255, (n >>> 8) & 255, n & 255);\n i += 4;\n continue;\n }\n\n if (remain === 2) {\n const a = decodeChar(s[i]!),\n b = decodeChar(s[i + 1]!);\n const n = (a << 18) | (b << 12);\n out.push((n >>> 16) & 255);\n i += 2;\n continue;\n }\n\n if (remain === 3) {\n const a = decodeChar(s[i]!),\n b = decodeChar(s[i + 1]!),\n c = decodeChar(s[i + 2]!);\n const n = (a << 18) | (b << 12) | (c << 6);\n out.push((n >>> 16) & 255, (n >>> 8) & 255);\n i += 3;\n continue;\n }\n\n throw new Base64UrlError(\"Invalid base64url length\");\n }\n\n const decoded = new Uint8Array(out);\n // Strict canonical check: re-encode must match exactly\n const recoded = base64urlEncodeBytes(decoded);\n if (recoded !== s) throw new Base64UrlError(\"Non-canonical base64url form\");\n return decoded;\n}\n\nexport function base64urlEncodeUtf8(s: string): string {\n return base64urlEncodeBytes(toUtf8Bytes(s));\n}\n\nexport function base64urlDecodeToUtf8(s: string): string {\n return fromUtf8Bytes(base64urlDecodeToBytes(s));\n}\n","// src/infrastructure/uuidv7.ts\n// Strict UUIDv7 validation - pure regex, no external dependencies\n\nconst RE_UUID =\n /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;\n\nexport function isUuidV7(id: string): boolean {\n if (!RE_UUID.test(id)) return false;\n // version nibble is first nibble of 3rd group (position 14)\n const version = id[14]!;\n if (version !== \"7\") return false;\n\n // variant is first nibble of 4th group (position 19): 8, 9, a, b\n const variant = id[19]!.toLowerCase();\n return (\n variant === \"8\" || variant === \"9\" || variant === \"a\" || variant === \"b\"\n );\n}\n\nexport function isCanonicalLowerUuid(id: string): boolean {\n return id === id.toLowerCase();\n}\n","// src/domain/logic/cursor.ts\n// Cursor derivation, validation, and comparison\n// D4=B: invalid frames do not throw from validation, return INVALID_FRAME\n\nimport {\n base64urlDecodeToUtf8,\n base64urlEncodeUtf8,\n} from \"../../infrastructure/base64url.js\";\nimport { isUuidV7, isCanonicalLowerUuid } from \"../../infrastructure/uuidv7.js\";\nimport type {\n CursorValidationResult,\n DecodedCursor,\n} from \"../types/cursor.types.js\";\n\nfunction isValidUnixSecondsInt(n: unknown): n is number {\n return (\n typeof n === \"number\" &&\n Number.isInteger(n) &&\n n >= 0 &&\n Number.isSafeInteger(n)\n );\n}\n\nfunction isCanonicalTimestampString(s: string): boolean {\n if (!/^\\d+$/.test(s)) return false;\n if (s.length > 1 && s.startsWith(\"0\")) return false;\n return true;\n}\n\n/**\n * Derive a cursor from timestamp and event_id.\n * cursor = base64url(utf8(\"{timestamp}:{event_id}\"))\n */\nexport function deriveCursor(timestamp: number, eventId: string): string {\n if (!isValidUnixSecondsInt(timestamp)) {\n throw new Error(\"timestamp must be unix seconds integer\");\n }\n const plain = `${String(timestamp)}:${eventId}`;\n return base64urlEncodeUtf8(plain);\n}\n\n/**\n * Decode a cursor to {timestamp, event_id}.\n * Throws on invalid cursor format.\n */\nexport function decodeCursor(cursor: string): DecodedCursor {\n const raw = base64urlDecodeToUtf8(cursor);\n const colonIndex = raw.indexOf(\":\");\n if (colonIndex === -1)\n throw new Error(\"cursor frame must be '{timestamp}:{event_id}'\");\n\n const tsStr = raw.slice(0, colonIndex);\n const eventId = raw.slice(colonIndex + 1);\n\n if (!isCanonicalTimestampString(tsStr))\n throw new Error(\"timestamp is not canonical base-10\");\n const tsNum = Number(tsStr);\n if (!isValidUnixSecondsInt(tsNum))\n throw new Error(\"timestamp is out of range\");\n\n if (!isUuidV7(eventId)) throw new Error(\"event_id is not uuidv7\");\n if (!isCanonicalLowerUuid(eventId))\n throw new Error(\"event_id must be lowercase canonical uuid\");\n\n return { timestamp: tsNum, event_id: eventId };\n}\n\n/**\n * Compare two cursors.\n * Returns -1 if a < b, 0 if equal, 1 if a > b.\n * Compares by (timestamp, event_id) lexicographically.\n */\nexport function compareCursor(a: string, b: string): -1 | 0 | 1 {\n const da = decodeCursor(a);\n const db = decodeCursor(b);\n\n if (da.timestamp < db.timestamp) return -1;\n if (da.timestamp > db.timestamp) return 1;\n\n if (da.event_id < db.event_id) return -1;\n if (da.event_id > db.event_id) return 1;\n return 0;\n}\n\n/**\n * Validate that an event's cursor matches the derived cursor.\n * D4=B: Does not throw on invalid frames, returns { ok: false, reason: \"INVALID_FRAME\" }\n */\nexport function assertCursorInvariant(event: {\n timestamp: unknown;\n event_id: unknown;\n cursor: unknown;\n}): CursorValidationResult {\n // Frame checks first (D4=B: do not throw)\n if (\n !isValidUnixSecondsInt(event.timestamp) ||\n typeof event.event_id !== \"string\" ||\n typeof event.cursor !== \"string\"\n ) {\n return { ok: false, derived: \"\", reason: \"INVALID_FRAME\" };\n }\n\n // Decode cursor must be strict base64url + canonical frame. If it fails, INVALID_FRAME.\n try {\n decodeCursor(event.cursor);\n } catch {\n const derived = deriveCursor(event.timestamp, event.event_id);\n return { ok: false, derived, reason: \"INVALID_FRAME\" };\n }\n\n // Derived cursor must match exactly (CURSOR_MISMATCH)\n const derived = deriveCursor(event.timestamp, event.event_id);\n if (event.cursor !== derived)\n return { ok: false, derived, reason: \"CURSOR_MISMATCH\" };\n\n // Also enforce event_id canonical uuidv7 for the event frame\n if (!isUuidV7(event.event_id) || !isCanonicalLowerUuid(event.event_id)) {\n return { ok: false, derived, reason: \"INVALID_FRAME\" };\n }\n\n return { ok: true, derived };\n}\n","// src/domain/logic/continuity.ts\n// Cursor continuity checking\n\nimport { compareCursor } from \"./cursor.js\";\nimport type {\n CursorGap,\n ContinuityCheckResult,\n} from \"../types/bundle.types.js\";\n\n/**\n * Check cursor continuity based on contract-defined rules.\n * Gap detection requires cursor predecessor relation, not heuristics.\n * For v1.1, we verify strict ordering.\n */\nexport function checkCursorContinuity(\n events: Array<{ cursor: string; timestamp: number }>,\n _expectedPredecessor?: (cursor: string) => string | null,\n): ContinuityCheckResult {\n if (events.length <= 1) {\n return { status: \"CONTINUOUS\", gaps: [] };\n }\n\n const gaps: CursorGap[] = [];\n\n // Verify ordering\n for (let i = 0; i < events.length - 1; i++) {\n const current = events[i];\n const next = events[i + 1];\n\n // If next cursor is Lexicographically smaller than current, strict ordering violation\n const cmp = compareCursor(current.cursor, next.cursor);\n if (cmp === 1) {\n // Order violation isn't exactly a \"gap\" but a \"discontinuity\"\n // For v1.1, we treat this as a Gap for the sake of the interface\n gaps.push({\n from_cursor: current.cursor,\n to_cursor: next.cursor,\n detected_at: Date.now(),\n });\n }\n }\n\n // FUTURE(v1.2): Implement actual rigorous predecessor check using Merkle links if available\n // For now, we only check monotonic ordering.\n\n if (gaps.length > 0) {\n return { status: \"GAP_DETECTED\", gaps };\n }\n\n return { status: \"CONTINUOUS\", gaps: [] };\n}\n","// src/domain/logic/ordering.ts\n// Event ordering comparison: (timestamp DESC, event_id DESC)\n\n/**\n * Compare two events for ordering.\n * Returns -1 if a should come before b (a is \"greater\" in DESC order)\n * Returns 0 if equal\n * Returns 1 if a should come after b\n *\n * Ordering: timestamp DESC, then event_id DESC\n */\nexport function orderingCompare(\n a: { timestamp: number; event_id: string },\n b: { timestamp: number; event_id: string },\n): -1 | 0 | 1 {\n // DESC timestamp: higher timestamp comes first\n if (a.timestamp > b.timestamp) return -1;\n if (a.timestamp < b.timestamp) return 1;\n\n // DESC event_id: higher event_id comes first (lexicographic)\n if (a.event_id > b.event_id) return -1;\n if (a.event_id < b.event_id) return 1;\n return 0;\n}\n","// src/domain/logic/redaction.ts\n// Event and gateway snapshot redaction\n\nimport type {\n AuditEvent,\n GatewayStatus,\n RedactionLevel,\n} from \"../types/event.types.js\";\n\nexport function redactEvent(\n event: AuditEvent,\n level: RedactionLevel,\n): AuditEvent {\n if (level === \"none\") return event;\n\n const copy = structuredClone(event); // Deep copy\n\n // Common redactions for safe_default\n if (level === \"safe_default\" || level === \"strict\") {\n if (copy.request && typeof copy.request === \"object\") {\n const req = copy.request as {\n headers?: Record<string, string>;\n [key: string]: unknown;\n };\n if (req.headers) {\n // Strip auth headers\n delete req.headers[\"authorization\"];\n delete req.headers[\"cookie\"];\n delete req.headers[\"x-api-key\"];\n }\n }\n }\n\n if (level === \"strict\") {\n // Strict redaction - strip inputs and all headers\n if (copy.input) {\n copy.input = \"[REDACTED]\";\n }\n if (copy.request && typeof copy.request === \"object\") {\n const req = copy.request as {\n headers?: Record<string, string>;\n [key: string]: unknown;\n };\n req.headers = undefined; // Remove all headers\n }\n }\n\n return copy;\n}\n\nexport function redactGatewaySnapshot(\n snapshot: GatewayStatus,\n): Partial<GatewayStatus> {\n const copy = { ...snapshot };\n\n // Always strip these\n delete copy.internal_endpoints;\n delete copy.keys;\n delete copy.tokens;\n\n return copy;\n}\n","// src/domain/logic/evidence-bundle.ts\n// Evidence bundle creation - pure deterministic transformation\n\nimport { compareCursor } from \"./cursor.js\";\nimport { redactEvent, redactGatewaySnapshot } from \"./redaction.js\";\nimport type {\n AuditEvent,\n GatewayStatus,\n RedactionLevel,\n AuditFilters,\n Outcome,\n} from \"../types/event.types.js\";\nimport type {\n EvidenceBundle,\n IntegritySummary,\n} from \"../types/bundle.types.js\";\n\nexport interface CreateEvidenceBundleParams {\n events: AuditEvent[];\n redactionLevel?: RedactionLevel;\n gatewaySnapshot?: GatewayStatus;\n filters?: AuditFilters;\n cursorRange?: { start?: string; end?: string };\n dashboardVersion: string;\n}\n\nexport function createEvidenceBundle(\n params: CreateEvidenceBundleParams,\n): EvidenceBundle {\n const level = params.redactionLevel ?? \"safe_default\";\n\n // 1. Sort events by cursor (canonical order)\n const sortedEvents = [...params.events].sort((a, b) =>\n compareCursor(a.cursor, b.cursor),\n );\n\n // 2. Apply redaction based on level\n const redactedEvents = sortedEvents.map((e) => redactEvent(e, level));\n\n // 3. Compute integrity summary\n const by_outcome: Record<Outcome, number> = { OK: 0, DENY: 0, ERROR: 0 };\n const by_denial_reason: Record<string, number> = {};\n\n redactedEvents.forEach((e) => {\n const outcome = (e.outcome as Outcome) || \"OK\";\n by_outcome[outcome] = (by_outcome[outcome] || 0) + 1;\n\n if (typeof e.reason === \"string\") {\n by_denial_reason[e.reason] = (by_denial_reason[e.reason] || 0) + 1;\n }\n });\n\n const summary: IntegritySummary = {\n total_events: redactedEvents.length,\n by_outcome,\n by_denial_reason,\n cursor_continuity: \"UNKNOWN\", // Continuity check is computationally expensive for large bundles\n };\n\n // 4. Strip secrets from gateway snapshot\n const redactedSnapshot = params.gatewaySnapshot\n ? redactGatewaySnapshot(params.gatewaySnapshot)\n : undefined;\n\n return {\n metadata: {\n schema_version: \"1.1.0\",\n generated_at: new Date().toISOString(),\n source: {\n dashboard_version: params.dashboardVersion,\n gateway_version: params.gatewaySnapshot?.version,\n },\n filters_applied: params.filters,\n cursor_range: params.cursorRange,\n redaction_level: level,\n },\n events: redactedEvents,\n integrity_summary: summary,\n gateway_snapshot: redactedSnapshot,\n };\n}\n"],"mappings":"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","names":["derived"]}
|
|
1
|
+
{"version":3,"sources":["../src/infrastructure/base64url.ts","../src/infrastructure/uuidv7.ts","../src/domain/logic/cursor.ts","../src/domain/logic/continuity.ts","../src/domain/logic/ordering.ts","../src/domain/logic/redaction.ts","../src/domain/logic/evidence-bundle.ts","../src/index.ts"],"sourcesContent":["// src/infrastructure/base64url.ts\n// Strict base64url encoding/decoding - NO btoa/atob, NO padding\n// Uses TextEncoder/TextDecoder for universal runtime support (Node, Browser, Edge)\n\nexport class Base64UrlError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"Base64UrlError\";\n }\n}\n\nconst ALPHABET =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\nconst RE_VALID = /^[A-Za-z0-9\\-_]*$/;\n\nfunction toUtf8Bytes(s: string): Uint8Array {\n return new TextEncoder().encode(s);\n}\n\nfunction fromUtf8Bytes(b: Uint8Array): string {\n return new TextDecoder(\"utf-8\", { fatal: true }).decode(b);\n}\n\nexport function base64urlEncodeBytes(bytes: Uint8Array): string {\n if (bytes.length === 0) return \"\";\n\n let out = \"\";\n let i = 0;\n\n while (i + 3 <= bytes.length) {\n const n = (bytes[i]! << 16) | (bytes[i + 1]! << 8) | bytes[i + 2]!;\n out += ALPHABET[(n >>> 18) & 63]!;\n out += ALPHABET[(n >>> 12) & 63]!;\n out += ALPHABET[(n >>> 6) & 63]!;\n out += ALPHABET[n & 63]!;\n i += 3;\n }\n\n const rem = bytes.length - i;\n if (rem === 1) {\n const n = bytes[i]!;\n out += ALPHABET[(n >>> 2) & 63]!;\n out += ALPHABET[(n << 4) & 63]!;\n // no padding\n } else if (rem === 2) {\n const n = (bytes[i]! << 8) | bytes[i + 1]!;\n out += ALPHABET[(n >>> 10) & 63]!;\n out += ALPHABET[(n >>> 4) & 63]!;\n out += ALPHABET[(n << 2) & 63]!;\n // no padding\n }\n\n return out;\n}\n\nfunction decodeChar(c: string): number {\n const idx = ALPHABET.indexOf(c);\n if (idx === -1) throw new Base64UrlError(`Invalid base64url character: ${c}`);\n return idx;\n}\n\nexport function base64urlDecodeToBytes(s: string): Uint8Array {\n if (s.length === 0) return new Uint8Array(0);\n\n // Reject padding and non-url alphabet\n if (s.includes(\"=\")) throw new Base64UrlError(\"Padding is not allowed\");\n if (!RE_VALID.test(s))\n throw new Base64UrlError(\"Non-base64url characters present\");\n // length mod 4 == 1 is impossible for canonical base64url\n if (s.length % 4 === 1) throw new Base64UrlError(\"Invalid base64url length\");\n\n const out: number[] = [];\n let i = 0;\n\n while (i < s.length) {\n const remain = s.length - i;\n\n if (remain >= 4) {\n const a = decodeChar(s[i]!),\n b = decodeChar(s[i + 1]!),\n c = decodeChar(s[i + 2]!),\n d = decodeChar(s[i + 3]!);\n const n = (a << 18) | (b << 12) | (c << 6) | d;\n out.push((n >>> 16) & 255, (n >>> 8) & 255, n & 255);\n i += 4;\n continue;\n }\n\n if (remain === 2) {\n const a = decodeChar(s[i]!),\n b = decodeChar(s[i + 1]!);\n const n = (a << 18) | (b << 12);\n out.push((n >>> 16) & 255);\n i += 2;\n continue;\n }\n\n if (remain === 3) {\n const a = decodeChar(s[i]!),\n b = decodeChar(s[i + 1]!),\n c = decodeChar(s[i + 2]!);\n const n = (a << 18) | (b << 12) | (c << 6);\n out.push((n >>> 16) & 255, (n >>> 8) & 255);\n i += 3;\n continue;\n }\n\n throw new Base64UrlError(\"Invalid base64url length\");\n }\n\n const decoded = new Uint8Array(out);\n // Strict canonical check: re-encode must match exactly\n const recoded = base64urlEncodeBytes(decoded);\n if (recoded !== s) throw new Base64UrlError(\"Non-canonical base64url form\");\n return decoded;\n}\n\nexport function base64urlEncodeUtf8(s: string): string {\n return base64urlEncodeBytes(toUtf8Bytes(s));\n}\n\nexport function base64urlDecodeToUtf8(s: string): string {\n return fromUtf8Bytes(base64urlDecodeToBytes(s));\n}\n","// src/infrastructure/uuidv7.ts\n// Strict UUIDv7 validation - pure regex, no external dependencies\n\nconst RE_UUID =\n /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;\n\nexport function isUuidV7(id: string): boolean {\n if (!RE_UUID.test(id)) return false;\n // version nibble is first nibble of 3rd group (position 14)\n const version = id[14]!;\n if (version !== \"7\") return false;\n\n // variant is first nibble of 4th group (position 19): 8, 9, a, b\n const variant = id[19]!.toLowerCase();\n return (\n variant === \"8\" || variant === \"9\" || variant === \"a\" || variant === \"b\"\n );\n}\n\nexport function isCanonicalLowerUuid(id: string): boolean {\n return id === id.toLowerCase();\n}\n","// src/domain/logic/cursor.ts\n// Cursor derivation, validation, and comparison\n// D4=B: invalid frames do not throw from validation, return INVALID_FRAME\n\nimport {\n base64urlDecodeToUtf8,\n base64urlEncodeUtf8,\n} from \"../../infrastructure/base64url\";\nimport { isUuidV7, isCanonicalLowerUuid } from \"../../infrastructure/uuidv7\";\nimport type {\n CursorValidationResult,\n DecodedCursor,\n} from \"../types/cursor.types\";\n\nfunction isValidUnixSecondsInt(n: unknown): n is number {\n return (\n typeof n === \"number\" &&\n Number.isInteger(n) &&\n n >= 0 &&\n Number.isSafeInteger(n)\n );\n}\n\nfunction isCanonicalTimestampString(s: string): boolean {\n if (!/^\\d+$/.test(s)) return false;\n if (s.length > 1 && s.startsWith(\"0\")) return false;\n return true;\n}\n\n/**\n * Derive a cursor from timestamp and event_id.\n * cursor = base64url(utf8(\"{timestamp}:{event_id}\"))\n */\nexport function deriveCursor(timestamp: number, eventId: string): string {\n if (!isValidUnixSecondsInt(timestamp)) {\n throw new Error(\"timestamp must be unix seconds integer\");\n }\n const plain = `${String(timestamp)}:${eventId}`;\n return base64urlEncodeUtf8(plain);\n}\n\n/**\n * Decode a cursor to {timestamp, event_id}.\n * Throws on invalid cursor format.\n */\nexport function decodeCursor(cursor: string): DecodedCursor {\n const raw = base64urlDecodeToUtf8(cursor);\n const colonIndex = raw.indexOf(\":\");\n if (colonIndex === -1)\n throw new Error(\"cursor frame must be '{timestamp}:{event_id}'\");\n\n const tsStr = raw.slice(0, colonIndex);\n const eventId = raw.slice(colonIndex + 1);\n\n if (!isCanonicalTimestampString(tsStr))\n throw new Error(\"timestamp is not canonical base-10\");\n const tsNum = Number(tsStr);\n if (!isValidUnixSecondsInt(tsNum))\n throw new Error(\"timestamp is out of range\");\n\n if (!isUuidV7(eventId)) throw new Error(\"event_id is not uuidv7\");\n if (!isCanonicalLowerUuid(eventId))\n throw new Error(\"event_id must be lowercase canonical uuid\");\n\n return { timestamp: tsNum, event_id: eventId };\n}\n\n/**\n * Compare two cursors.\n * Returns -1 if a < b, 0 if equal, 1 if a > b.\n * Compares by (timestamp, event_id) lexicographically.\n */\nexport function compareCursor(a: string, b: string): -1 | 0 | 1 {\n const da = decodeCursor(a);\n const db = decodeCursor(b);\n\n if (da.timestamp < db.timestamp) return -1;\n if (da.timestamp > db.timestamp) return 1;\n\n if (da.event_id < db.event_id) return -1;\n if (da.event_id > db.event_id) return 1;\n return 0;\n}\n\n/**\n * Validate that an event's cursor matches the derived cursor.\n * D4=B: Does not throw on invalid frames, returns { ok: false, reason: \"INVALID_FRAME\" }\n */\nexport function assertCursorInvariant(event: {\n timestamp: unknown;\n event_id: unknown;\n cursor: unknown;\n}): CursorValidationResult {\n // Frame checks first (D4=B: do not throw)\n if (\n !isValidUnixSecondsInt(event.timestamp) ||\n typeof event.event_id !== \"string\" ||\n typeof event.cursor !== \"string\"\n ) {\n return { ok: false, derived: \"\", reason: \"INVALID_FRAME\" };\n }\n\n // Decode cursor must be strict base64url + canonical frame. If it fails, INVALID_FRAME.\n try {\n decodeCursor(event.cursor);\n } catch {\n const derived = deriveCursor(event.timestamp, event.event_id);\n return { ok: false, derived, reason: \"INVALID_FRAME\" };\n }\n\n // Derived cursor must match exactly (CURSOR_MISMATCH)\n const derived = deriveCursor(event.timestamp, event.event_id);\n if (event.cursor !== derived)\n return { ok: false, derived, reason: \"CURSOR_MISMATCH\" };\n\n // Also enforce event_id canonical uuidv7 for the event frame\n if (!isUuidV7(event.event_id) || !isCanonicalLowerUuid(event.event_id)) {\n return { ok: false, derived, reason: \"INVALID_FRAME\" };\n }\n\n return { ok: true, derived };\n}\n","// src/domain/logic/continuity.ts\n// Cursor continuity checking\n\nimport { compareCursor } from \"./cursor\";\nimport type {\n CursorGap,\n ContinuityCheckResult,\n} from \"../types/bundle.types\";\n\n/**\n * Check cursor continuity based on contract-defined rules.\n * Gap detection requires cursor predecessor relation, not heuristics.\n * For v1.1, we verify strict ordering.\n */\nexport function checkCursorContinuity(\n events: Array<{ cursor: string; timestamp: number }>,\n _expectedPredecessor?: (cursor: string) => string | null,\n): ContinuityCheckResult {\n if (events.length <= 1) {\n return { status: \"CONTINUOUS\", gaps: [] };\n }\n\n const gaps: CursorGap[] = [];\n\n // Verify ordering\n for (let i = 0; i < events.length - 1; i++) {\n const current = events[i];\n const next = events[i + 1];\n\n // If next cursor is Lexicographically smaller than current, strict ordering violation\n const cmp = compareCursor(current.cursor, next.cursor);\n if (cmp === 1) {\n // Order violation isn't exactly a \"gap\" but a \"discontinuity\"\n // For v1.1, we treat this as a Gap for the sake of the interface\n gaps.push({\n from_cursor: current.cursor,\n to_cursor: next.cursor,\n detected_at: Date.now(),\n });\n }\n }\n\n // FUTURE(v1.2): Implement actual rigorous predecessor check using Merkle links if available\n // For now, we only check monotonic ordering.\n\n if (gaps.length > 0) {\n return { status: \"GAP_DETECTED\", gaps };\n }\n\n return { status: \"CONTINUOUS\", gaps: [] };\n}\n","// src/domain/logic/ordering.ts\n// Event ordering comparison: (timestamp DESC, event_id DESC)\n\n/**\n * Compare two events for ordering.\n * Returns -1 if a should come before b (a is \"greater\" in DESC order)\n * Returns 0 if equal\n * Returns 1 if a should come after b\n *\n * Ordering: timestamp DESC, then event_id DESC\n */\nexport function orderingCompare(\n a: { timestamp: number; event_id: string },\n b: { timestamp: number; event_id: string },\n): -1 | 0 | 1 {\n // DESC timestamp: higher timestamp comes first\n if (a.timestamp > b.timestamp) return -1;\n if (a.timestamp < b.timestamp) return 1;\n\n // DESC event_id: higher event_id comes first (lexicographic)\n if (a.event_id > b.event_id) return -1;\n if (a.event_id < b.event_id) return 1;\n return 0;\n}\n","// src/domain/logic/redaction.ts\n// Event and gateway snapshot redaction\n\nimport type {\n AuditEvent,\n GatewayStatus,\n RedactionLevel,\n} from \"../types/event.types\";\n\nexport function redactEvent(\n event: AuditEvent,\n level: RedactionLevel,\n): AuditEvent {\n if (level === \"none\") return event;\n\n const copy = structuredClone(event); // Deep copy\n\n // Common redactions for safe_default\n if (level === \"safe_default\" || level === \"strict\") {\n if (copy.request && typeof copy.request === \"object\") {\n const req = copy.request as {\n headers?: Record<string, string>;\n [key: string]: unknown;\n };\n if (req.headers) {\n // Strip auth headers\n delete req.headers[\"authorization\"];\n delete req.headers[\"cookie\"];\n delete req.headers[\"x-api-key\"];\n }\n }\n }\n\n if (level === \"strict\") {\n // Strict redaction - strip inputs and all headers\n if (copy.input) {\n copy.input = \"[REDACTED]\";\n }\n if (copy.request && typeof copy.request === \"object\") {\n const req = copy.request as {\n headers?: Record<string, string>;\n [key: string]: unknown;\n };\n req.headers = undefined; // Remove all headers\n }\n }\n\n return copy;\n}\n\nexport function redactGatewaySnapshot(\n snapshot: GatewayStatus,\n): Partial<GatewayStatus> {\n const copy = { ...snapshot };\n\n // Always strip these\n delete copy.internal_endpoints;\n delete copy.keys;\n delete copy.tokens;\n\n return copy;\n}\n","// src/domain/logic/evidence-bundle.ts\n// Evidence bundle creation - pure deterministic transformation\n\nimport { compareCursor } from \"./cursor\";\nimport { redactEvent, redactGatewaySnapshot } from \"./redaction\";\nimport type {\n AuditEvent,\n GatewayStatus,\n RedactionLevel,\n AuditFilters,\n Outcome,\n} from \"../types/event.types\";\nimport type {\n EvidenceBundle,\n IntegritySummary,\n} from \"../types/bundle.types\";\n\nexport interface CreateEvidenceBundleParams {\n events: AuditEvent[];\n redactionLevel?: RedactionLevel;\n gatewaySnapshot?: GatewayStatus;\n filters?: AuditFilters;\n cursorRange?: { start?: string; end?: string };\n dashboardVersion: string;\n}\n\nexport function createEvidenceBundle(\n params: CreateEvidenceBundleParams,\n): EvidenceBundle {\n const level = params.redactionLevel ?? \"safe_default\";\n\n // 1. Sort events by cursor (canonical order)\n const sortedEvents = [...params.events].sort((a, b) =>\n compareCursor(a.cursor, b.cursor),\n );\n\n // 2. Apply redaction based on level\n const redactedEvents = sortedEvents.map((e) => redactEvent(e, level));\n\n // 3. Compute integrity summary\n const by_outcome: Record<Outcome, number> = { OK: 0, DENY: 0, ERROR: 0 };\n const by_denial_reason: Record<string, number> = {};\n\n redactedEvents.forEach((e) => {\n const outcome = (e.outcome as Outcome) || \"OK\";\n by_outcome[outcome] = (by_outcome[outcome] || 0) + 1;\n\n if (typeof e.reason === \"string\") {\n by_denial_reason[e.reason] = (by_denial_reason[e.reason] || 0) + 1;\n }\n });\n\n const summary: IntegritySummary = {\n total_events: redactedEvents.length,\n by_outcome,\n by_denial_reason,\n cursor_continuity: \"UNKNOWN\", // Continuity check is computationally expensive for large bundles\n };\n\n // 4. Strip secrets from gateway snapshot\n const redactedSnapshot = params.gatewaySnapshot\n ? redactGatewaySnapshot(params.gatewaySnapshot)\n : undefined;\n\n return {\n metadata: {\n schema_version: \"1.1.0\",\n generated_at: new Date().toISOString(),\n source: {\n dashboard_version: params.dashboardVersion,\n gateway_version: params.gatewaySnapshot?.version,\n },\n filters_applied: params.filters,\n cursor_range: params.cursorRange,\n redaction_level: level,\n },\n events: redactedEvents,\n integrity_summary: summary,\n gateway_snapshot: redactedSnapshot,\n };\n}\n","// typescript/src/index.ts\n// Public API exports for @talosprotocol/contracts\n//\n// This file is the ONLY supported import path for this package.\n// All exports are stable and backward-compatible.\n\n// ============================================================================\n// Infrastructure: Encoding & Validation Utilities\n// ============================================================================\n\nexport {\n Base64UrlError,\n base64urlEncodeBytes,\n base64urlDecodeToBytes,\n base64urlEncodeUtf8,\n base64urlDecodeToUtf8,\n} from \"./infrastructure/base64url\";\n\nexport { isUuidV7, isCanonicalLowerUuid } from \"./infrastructure/uuidv7\";\n\n// ============================================================================\n// Domain Types\n// ============================================================================\n\n// Cursor types\nexport type {\n CursorValidationReason,\n CursorValidationResult,\n DecodedCursor,\n} from \"./domain/types/cursor.types\";\n\n// Event types\nexport type {\n AuditEvent,\n GatewayStatus,\n Outcome,\n RedactionLevel,\n AuditFilters,\n Comparator,\n} from \"./domain/types/event.types\";\n\n// Bundle types\nexport type {\n EvidenceBundleMetadata,\n IntegritySummary,\n EvidenceBundle,\n CursorGap,\n ContinuityCheckResult,\n} from \"./domain/types/bundle.types\";\n\n// ============================================================================\n// Domain Logic\n// ============================================================================\n\n// Cursor operations\nexport {\n deriveCursor,\n decodeCursor,\n compareCursor,\n assertCursorInvariant,\n} from \"./domain/logic/cursor\";\n\n// Continuity checking\nexport { checkCursorContinuity } from \"./domain/logic/continuity\";\n\n// Event ordering\nexport { orderingCompare } from \"./domain/logic/ordering\";\n\n// Redaction\nexport {\n redactEvent,\n redactGatewaySnapshot,\n} from \"./domain/logic/redaction\";\n\n// Evidence bundle creation\nexport { createEvidenceBundle } from \"./domain/logic/evidence-bundle\";\n\nexport const VERSION = \"1.2.0\";\n"],"mappings":"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","names":["derived"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@talosprotocol/contracts",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Talos Protocol contracts - schemas, types, and helper functions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"sideEffects": false,
|
|
17
17
|
"scripts": {
|
|
18
18
|
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
|
|
19
|
+
"prepare": "npm run build",
|
|
19
20
|
"test": "vitest run",
|
|
20
21
|
"test:watch": "vitest",
|
|
21
22
|
"lint": "eslint . --ext .ts,.tsx,.js,.jsx",
|
|
@@ -41,7 +42,7 @@
|
|
|
41
42
|
"security"
|
|
42
43
|
],
|
|
43
44
|
"author": "Talos Protocol",
|
|
44
|
-
"license": "
|
|
45
|
+
"license": "Apache-2.0",
|
|
45
46
|
"publishConfig": {
|
|
46
47
|
"access": "public"
|
|
47
48
|
},
|
|
@@ -70,5 +71,9 @@
|
|
|
70
71
|
"tsup": "^8.0.1",
|
|
71
72
|
"typescript": "^5.3.0",
|
|
72
73
|
"vitest": "^1.0.0"
|
|
74
|
+
},
|
|
75
|
+
"dependencies": {
|
|
76
|
+
"ajv": "^8.17.1",
|
|
77
|
+
"ajv-formats": "^3.0.1"
|
|
73
78
|
}
|
|
74
79
|
}
|