@unifyplane/logsdk 0.1.4 → 0.1.6
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/core/clock.d.ts +3 -0
- package/dist/core/clock.d.ts.map +1 -0
- package/dist/core/clock.js +21 -0
- package/dist/core/clock.js.map +1 -0
- package/dist/core/context.d.ts +10 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +100 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/fanout.d.ts +6 -0
- package/dist/core/fanout.d.ts.map +1 -0
- package/dist/core/fanout.js +35 -0
- package/dist/core/fanout.js.map +1 -0
- package/dist/core/ids.d.ts +6 -0
- package/dist/core/ids.d.ts.map +1 -0
- package/dist/core/ids.js +27 -0
- package/dist/core/ids.js.map +1 -0
- package/dist/core/message_constraints.d.ts +4 -0
- package/dist/core/message_constraints.d.ts.map +1 -0
- package/dist/core/message_constraints.js +53 -0
- package/dist/core/message_constraints.js.map +1 -0
- package/dist/core/outcomes.d.ts +5 -0
- package/dist/core/outcomes.d.ts.map +1 -0
- package/dist/core/outcomes.js +4 -0
- package/dist/core/outcomes.js.map +1 -0
- package/dist/core/record_builder.d.ts +34 -0
- package/dist/core/record_builder.d.ts.map +1 -0
- package/dist/core/record_builder.js +188 -0
- package/dist/core/record_builder.js.map +1 -0
- package/dist/core/spool.d.ts +4 -0
- package/dist/core/spool.d.ts.map +1 -0
- package/dist/core/spool.js +31 -0
- package/dist/core/spool.js.map +1 -0
- package/dist/core/types.d.ts +33 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +4 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/sinks/file_ndjson.d.ts +18 -0
- package/dist/sinks/file_ndjson.d.ts.map +1 -0
- package/dist/sinks/file_ndjson.js +29 -0
- package/dist/sinks/file_ndjson.js.map +1 -0
- package/dist/sinks/file_ndjson_sink.d.ts +7 -0
- package/dist/sinks/file_ndjson_sink.d.ts.map +1 -0
- package/dist/sinks/file_ndjson_sink.js +36 -0
- package/dist/sinks/file_ndjson_sink.js.map +1 -0
- package/dist/sinks/index.d.ts +12 -0
- package/dist/sinks/index.d.ts.map +1 -0
- package/dist/sinks/index.js +11 -0
- package/dist/sinks/index.js.map +1 -0
- package/dist/sinks/sink_types.d.ts +12 -0
- package/dist/sinks/sink_types.d.ts.map +1 -0
- package/dist/sinks/sink_types.js +2 -0
- package/dist/sinks/sink_types.js.map +1 -0
- package/dist/sinks/stdout_sink.d.ts +6 -0
- package/dist/sinks/stdout_sink.d.ts.map +1 -0
- package/dist/sinks/stdout_sink.js +15 -0
- package/dist/sinks/stdout_sink.js.map +1 -0
- package/dist/validate/api_surface_guard.d.ts +2 -0
- package/dist/validate/api_surface_guard.d.ts.map +1 -0
- package/dist/validate/api_surface_guard.js +63 -0
- package/dist/validate/api_surface_guard.js.map +1 -0
- package/dist/validate/noncompliance.d.ts +15 -0
- package/dist/validate/noncompliance.d.ts.map +1 -0
- package/dist/validate/noncompliance.js +17 -0
- package/dist/validate/noncompliance.js.map +1 -0
- package/dist/validate/schema_guard.d.ts +5 -0
- package/dist/validate/schema_guard.d.ts.map +1 -0
- package/dist/validate/schema_guard.js +151 -0
- package/dist/validate/schema_guard.js.map +1 -0
- package/package.json +1 -1
- package/src/core/fanout.ts +8 -3
- package/src/index.ts +9 -1
- package/src/sinks/index.ts +9 -5
- package/src.zip +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clock.d.ts","sourceRoot":"","sources":["../../src/core/clock.ts"],"names":[],"mappings":"AAKA,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,YAAY,IAAI,MAAM,CAerC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
let lastMonotonic = 0;
|
|
2
|
+
const hasPerformance = typeof performance !== "undefined" && typeof performance.now === "function";
|
|
3
|
+
const epochStart = Date.now();
|
|
4
|
+
export function nowUtcIso() {
|
|
5
|
+
return new Date().toISOString();
|
|
6
|
+
}
|
|
7
|
+
export function monotonicNow() {
|
|
8
|
+
let current;
|
|
9
|
+
if (hasPerformance) {
|
|
10
|
+
current = epochStart + performance.now();
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
current = Date.now();
|
|
14
|
+
}
|
|
15
|
+
if (current <= lastMonotonic) {
|
|
16
|
+
current = lastMonotonic + 1;
|
|
17
|
+
}
|
|
18
|
+
lastMonotonic = current;
|
|
19
|
+
return current;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=clock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clock.js","sourceRoot":"","sources":["../../src/core/clock.ts"],"names":[],"mappings":"AAAA,IAAI,aAAa,GAAG,CAAC,CAAC;AACtB,MAAM,cAAc,GAClB,OAAO,WAAW,KAAK,WAAW,IAAI,OAAO,WAAW,CAAC,GAAG,KAAK,UAAU,CAAC;AAC9E,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;AAE9B,MAAM,UAAU,SAAS;IACvB,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,IAAI,OAAe,CAAC;IAEpB,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO,GAAG,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;IAC3C,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,CAAC;IAED,IAAI,OAAO,IAAI,aAAa,EAAE,CAAC;QAC7B,OAAO,GAAG,aAAa,GAAG,CAAC,CAAC;IAC9B,CAAC;IAED,aAAa,GAAG,OAAO,CAAC;IACxB,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type JsonPrimitive = string | number | boolean | null;
|
|
2
|
+
export type JsonValue = JsonPrimitive | JsonValue[] | {
|
|
3
|
+
[key: string]: JsonValue;
|
|
4
|
+
};
|
|
5
|
+
export declare function initContext(context: unknown, version?: string): void;
|
|
6
|
+
export declare function getContextHash(): string;
|
|
7
|
+
export declare function getContextVersion(): string;
|
|
8
|
+
export declare function resetContextForTests(): void;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"AAIA,KAAK,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AACtD,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,SAAS,EAAE,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAkBnF,wBAAgB,WAAW,CACzB,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,MAAgC,GACxC,IAAI,CAiBN;AAED,wBAAgB,cAAc,IAAI,MAAM,CAKvC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAK1C;AAED,wBAAgB,oBAAoB,IAAI,IAAI,CAK3C"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/// <reference path="../crypto-shim.d.ts" />
|
|
2
|
+
import { createHash } from "crypto";
|
|
3
|
+
const DEFAULT_CONTEXT_VERSION = "log.context.v1";
|
|
4
|
+
const state = {
|
|
5
|
+
value: {},
|
|
6
|
+
hash: "",
|
|
7
|
+
version: DEFAULT_CONTEXT_VERSION,
|
|
8
|
+
initialized: false,
|
|
9
|
+
};
|
|
10
|
+
export function initContext(context, version = DEFAULT_CONTEXT_VERSION) {
|
|
11
|
+
if (state.initialized) {
|
|
12
|
+
throw new Error("Context already initialized");
|
|
13
|
+
}
|
|
14
|
+
if (!context || typeof context !== "object" || Array.isArray(context)) {
|
|
15
|
+
throw new Error("Context must be a non-array object");
|
|
16
|
+
}
|
|
17
|
+
const normalized = normalizeJsonValue(context, "$");
|
|
18
|
+
const canonical = stableStringify(normalized);
|
|
19
|
+
const hash = createHash("sha256").update(canonical).digest("hex");
|
|
20
|
+
state.value = deepFreeze(normalized);
|
|
21
|
+
state.hash = hash;
|
|
22
|
+
state.version = version;
|
|
23
|
+
state.initialized = true;
|
|
24
|
+
}
|
|
25
|
+
export function getContextHash() {
|
|
26
|
+
if (!state.initialized) {
|
|
27
|
+
throw new Error("Context not initialized");
|
|
28
|
+
}
|
|
29
|
+
return state.hash;
|
|
30
|
+
}
|
|
31
|
+
export function getContextVersion() {
|
|
32
|
+
if (!state.initialized) {
|
|
33
|
+
throw new Error("Context not initialized");
|
|
34
|
+
}
|
|
35
|
+
return state.version;
|
|
36
|
+
}
|
|
37
|
+
export function resetContextForTests() {
|
|
38
|
+
state.value = {};
|
|
39
|
+
state.hash = "";
|
|
40
|
+
state.version = DEFAULT_CONTEXT_VERSION;
|
|
41
|
+
state.initialized = false;
|
|
42
|
+
}
|
|
43
|
+
function deepFreeze(value) {
|
|
44
|
+
if (!value || typeof value !== "object") {
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
if (Object.isFrozen(value)) {
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
Object.freeze(value);
|
|
51
|
+
for (const key of Object.keys(value)) {
|
|
52
|
+
const child = value[key];
|
|
53
|
+
deepFreeze(child);
|
|
54
|
+
}
|
|
55
|
+
if (Array.isArray(value)) {
|
|
56
|
+
for (const item of value) {
|
|
57
|
+
deepFreeze(item);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
function normalizeJsonValue(value, path) {
|
|
63
|
+
if (value === null ||
|
|
64
|
+
typeof value === "string" ||
|
|
65
|
+
typeof value === "number" ||
|
|
66
|
+
typeof value === "boolean") {
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
if (typeof value === "bigint") {
|
|
70
|
+
return value.toString();
|
|
71
|
+
}
|
|
72
|
+
if (value instanceof Date) {
|
|
73
|
+
return value.toISOString();
|
|
74
|
+
}
|
|
75
|
+
if (Array.isArray(value)) {
|
|
76
|
+
return value.map((item, index) => normalizeJsonValue(item, `${path}[${index}]`));
|
|
77
|
+
}
|
|
78
|
+
if (typeof value === "object") {
|
|
79
|
+
const proto = Object.getPrototypeOf(value);
|
|
80
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
81
|
+
throw new Error(`Unsupported object at ${path}`);
|
|
82
|
+
}
|
|
83
|
+
const obj = value;
|
|
84
|
+
const result = {};
|
|
85
|
+
const keys = Object.keys(obj).sort();
|
|
86
|
+
for (const key of keys) {
|
|
87
|
+
const next = obj[key];
|
|
88
|
+
if (next === undefined) {
|
|
89
|
+
throw new Error(`Undefined value at ${path}.${key}`);
|
|
90
|
+
}
|
|
91
|
+
result[key] = normalizeJsonValue(next, `${path}.${key}`);
|
|
92
|
+
}
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
throw new Error(`Unsupported type at ${path}`);
|
|
96
|
+
}
|
|
97
|
+
function stableStringify(value) {
|
|
98
|
+
return JSON.stringify(value);
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAE5C,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAYpC,MAAM,uBAAuB,GAAG,gBAAgB,CAAC;AAEjD,MAAM,KAAK,GAAiB;IAC1B,KAAK,EAAE,EAAE;IACT,IAAI,EAAE,EAAE;IACR,OAAO,EAAE,uBAAuB;IAChC,WAAW,EAAE,KAAK;CACnB,CAAC;AAEF,MAAM,UAAU,WAAW,CACzB,OAAgB,EAChB,UAAkB,uBAAuB;IAEzC,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;IAED,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACtE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,UAAU,GAAG,kBAAkB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACpD,MAAM,SAAS,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAElE,KAAK,CAAC,KAAK,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IACrC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC;IACxB,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,KAAK,CAAC,OAAO,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC;IACjB,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC;IAChB,KAAK,CAAC,OAAO,GAAG,uBAAuB,CAAC;IACxC,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC;AAC5B,CAAC;AAED,SAAS,UAAU,CAAI,KAAQ;IAC7B,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAErB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAe,CAAC,EAAE,CAAC;QAC/C,MAAM,KAAK,GAAI,KAAiC,CAAC,GAAG,CAAC,CAAC;QACtD,UAAU,CAAC,KAAK,CAAC,CAAC;IACpB,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,UAAU,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAc,EAAE,IAAY;IACtD,IACE,KAAK,KAAK,IAAI;QACd,OAAO,KAAK,KAAK,QAAQ;QACzB,OAAO,KAAK,KAAK,QAAQ;QACzB,OAAO,KAAK,KAAK,SAAS,EAC1B,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC1B,CAAC;IAED,IAAI,KAAK,YAAY,IAAI,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC;IAC7B,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAC/B,kBAAkB,CAAC,IAAI,EAAE,GAAG,IAAI,IAAI,KAAK,GAAG,CAAC,CAC9C,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAC3C,IAAI,KAAK,KAAK,MAAM,CAAC,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACjD,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC;QACnD,CAAC;QAED,MAAM,GAAG,GAAG,KAAgC,CAAC;QAC7C,MAAM,MAAM,GAA8B,EAAE,CAAC;QAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QAErC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CAAC,sBAAsB,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC;YACvD,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,GAAG,kBAAkB,CAAC,IAAI,EAAE,GAAG,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC;QAC3D,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAC;AACjD,CAAC;AAED,SAAS,eAAe,CAAC,KAAgB;IACvC,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SinkEntry, StepRecord } from "../sinks/sink_types.js";
|
|
2
|
+
export type FanoutOutcome = "OK" | "DEGRADED" | "FAILED";
|
|
3
|
+
export declare function fanout(record: StepRecord, sinks: ReadonlyArray<SinkEntry>, options?: {
|
|
4
|
+
mode?: "authoritative" | "non-authoritative";
|
|
5
|
+
}): Promise<FanoutOutcome>;
|
|
6
|
+
//# sourceMappingURL=fanout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fanout.d.ts","sourceRoot":"","sources":["../../src/core/fanout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAIpE,MAAM,MAAM,aAAa,GAAG,IAAI,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEzD,wBAAsB,MAAM,CAC1B,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,EAC/B,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,eAAe,GAAG,mBAAmB,CAAA;CAAE,GACzD,OAAO,CAAC,aAAa,CAAC,CAgCxB"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { assertHasAuthoritativeSink } from "../validate/schema_guard.js";
|
|
2
|
+
import { writeEmergencySpool } from "./spool.js";
|
|
3
|
+
export async function fanout(record, sinks, options) {
|
|
4
|
+
const mode = options?.mode ?? "authoritative";
|
|
5
|
+
if (mode === "authoritative") {
|
|
6
|
+
assertHasAuthoritativeSink(sinks);
|
|
7
|
+
}
|
|
8
|
+
const frozenRecord = Object.isFrozen(record) ? record : Object.freeze(record);
|
|
9
|
+
let outcome = "OK";
|
|
10
|
+
let spooled = false;
|
|
11
|
+
for (const entry of sinks) {
|
|
12
|
+
try {
|
|
13
|
+
await entry.sink.emit(frozenRecord);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
if (mode === "authoritative" && entry.sinkClass === "authoritative") {
|
|
17
|
+
if (!spooled) {
|
|
18
|
+
try {
|
|
19
|
+
await writeEmergencySpool(frozenRecord);
|
|
20
|
+
outcome = "DEGRADED";
|
|
21
|
+
spooled = true;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
outcome = "FAILED";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (outcome === "FAILED") {
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return outcome;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=fanout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fanout.js","sourceRoot":"","sources":["../../src/core/fanout.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAIjD,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,MAAkB,EAClB,KAA+B,EAC/B,OAA0D;IAE1D,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,eAAe,CAAC;IAE9C,IAAI,IAAI,KAAK,eAAe,EAAE,CAAC;QAC7B,0BAA0B,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IACD,MAAM,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9E,IAAI,OAAO,GAAkB,IAAI,CAAC;IAClC,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,IAAI,KAAK,eAAe,IAAI,KAAK,CAAC,SAAS,KAAK,eAAe,EAAE,CAAC;gBACpE,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,IAAI,CAAC;wBACH,MAAM,mBAAmB,CAAC,YAAY,CAAC,CAAC;wBACxC,OAAO,GAAG,UAAU,CAAC;wBACrB,OAAO,GAAG,IAAI,CAAC;oBACjB,CAAC;oBAAC,MAAM,CAAC;wBACP,OAAO,GAAG,QAAQ,CAAC;oBACrB,CAAC;gBACH,CAAC;gBACD,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;oBACzB,MAAM;gBACR,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../../src/core/ids.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IAC1B,YAAY,IAAI,MAAM,CAAC;IACvB,YAAY,IAAI,MAAM,CAAC;CACxB;AASD,wBAAgB,iBAAiB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAoBnE"}
|
package/dist/core/ids.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { monotonicNow } from "./clock.js";
|
|
2
|
+
const processStart = Date.now().toString(36);
|
|
3
|
+
let instanceCounter = 0;
|
|
4
|
+
function sanitizeTag(tag) {
|
|
5
|
+
return tag.replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 32);
|
|
6
|
+
}
|
|
7
|
+
export function createIdGenerator(instanceTag) {
|
|
8
|
+
const instanceId = ++instanceCounter;
|
|
9
|
+
let sequence = 0;
|
|
10
|
+
let localCounter = 0;
|
|
11
|
+
const tag = instanceTag ? sanitizeTag(instanceTag) : "";
|
|
12
|
+
return {
|
|
13
|
+
nextSequence() {
|
|
14
|
+
const current = sequence;
|
|
15
|
+
sequence += 1;
|
|
16
|
+
return current;
|
|
17
|
+
},
|
|
18
|
+
nextRecordId() {
|
|
19
|
+
localCounter += 1;
|
|
20
|
+
const timePart = Math.floor(monotonicNow()).toString(36);
|
|
21
|
+
const counterPart = localCounter.toString(36);
|
|
22
|
+
const tagPart = tag ? `_${tag}` : "";
|
|
23
|
+
return `rec_${processStart}_${instanceId}${tagPart}_${timePart}_${counterPart}`;
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=ids.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ids.js","sourceRoot":"","sources":["../../src/core/ids.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAO1C,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAC7C,IAAI,eAAe,GAAG,CAAC,CAAC;AAExB,SAAS,WAAW,CAAC,GAAW;IAC9B,OAAO,GAAG,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,WAAoB;IACpD,MAAM,UAAU,GAAG,EAAE,eAAe,CAAC;IACrC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAExD,OAAO;QACL,YAAY;YACV,MAAM,OAAO,GAAG,QAAQ,CAAC;YACzB,QAAQ,IAAI,CAAC,CAAC;YACd,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,YAAY;YACV,YAAY,IAAI,CAAC,CAAC;YAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACzD,MAAM,WAAW,GAAG,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YAC9C,MAAM,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrC,OAAO,OAAO,YAAY,IAAI,UAAU,GAAG,OAAO,IAAI,QAAQ,IAAI,WAAW,EAAE,CAAC;QAClF,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message_constraints.d.ts","sourceRoot":"","sources":["../../src/core/message_constraints.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,kBAAkB,MAAM,CAAC;AAyC/B,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAsB3D;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const MESSAGE_MAX_LENGTH = 512;
|
|
2
|
+
const BASE64_MIN_LENGTH = 80;
|
|
3
|
+
function isJsonObjectOrArray(value) {
|
|
4
|
+
const trimmed = value.trim();
|
|
5
|
+
if (!trimmed) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
const startsWithBrace = trimmed.startsWith("{") && trimmed.endsWith("}");
|
|
9
|
+
const startsWithBracket = trimmed.startsWith("[") && trimmed.endsWith("]");
|
|
10
|
+
if (!startsWithBrace && !startsWithBracket) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
const parsed = JSON.parse(trimmed);
|
|
15
|
+
return typeof parsed === "object" && parsed !== null;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function looksLikeBase64Blob(value) {
|
|
22
|
+
const trimmed = value.trim();
|
|
23
|
+
if (trimmed.length < BASE64_MIN_LENGTH) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
if (/\s/.test(trimmed)) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
if (/^[A-Za-z0-9+/=_-]+$/.test(trimmed)) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
return /[A-Za-z0-9+/=_-]{80,}/.test(trimmed);
|
|
33
|
+
}
|
|
34
|
+
export function assertValidMessage(message) {
|
|
35
|
+
if (typeof message !== "string") {
|
|
36
|
+
throw new Error("Message must be a string.");
|
|
37
|
+
}
|
|
38
|
+
if (message.length === 0) {
|
|
39
|
+
throw new Error("Message must not be empty.");
|
|
40
|
+
}
|
|
41
|
+
if (message.length > MESSAGE_MAX_LENGTH) {
|
|
42
|
+
throw new Error(`Message exceeds maximum length of ${MESSAGE_MAX_LENGTH}.`);
|
|
43
|
+
}
|
|
44
|
+
if (isJsonObjectOrArray(message)) {
|
|
45
|
+
throw new Error("Message must not be a JSON object or array.");
|
|
46
|
+
}
|
|
47
|
+
if (looksLikeBase64Blob(message)) {
|
|
48
|
+
throw new Error("Message must not contain base64-like payloads.");
|
|
49
|
+
}
|
|
50
|
+
return message;
|
|
51
|
+
}
|
|
52
|
+
export { MESSAGE_MAX_LENGTH };
|
|
53
|
+
//# sourceMappingURL=message_constraints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message_constraints.js","sourceRoot":"","sources":["../../src/core/message_constraints.ts"],"names":[],"mappings":"AAAA,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAC/B,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAE7B,SAAS,mBAAmB,CAAC,KAAa;IACxC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACzE,MAAM,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAE3E,IAAI,CAAC,eAAe,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACnC,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAa;IACxC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,OAAO,CAAC,MAAM,GAAG,iBAAiB,EAAE,CAAC;QACvC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACvB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACxC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,uBAAuB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAgB;IACjD,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,GAAG,kBAAkB,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,qCAAqC,kBAAkB,GAAG,CAAC,CAAC;IAC9E,CAAC;IAED,IAAI,mBAAmB,CAAC,OAAO,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACjE,CAAC;IAED,IAAI,mBAAmB,CAAC,OAAO,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outcomes.d.ts","sourceRoot":"","sources":["../../src/core/outcomes.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE,EAAG,IAAa,CAAC;AAChC,eAAO,MAAM,QAAQ,EAAG,UAAmB,CAAC;AAC5C,eAAO,MAAM,MAAM,EAAG,QAAiB,CAAC;AAExC,MAAM,MAAM,OAAO,GAAG,OAAO,EAAE,GAAG,OAAO,QAAQ,GAAG,OAAO,MAAM,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outcomes.js","sourceRoot":"","sources":["../../src/core/outcomes.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,EAAE,GAAG,IAAa,CAAC;AAChC,MAAM,CAAC,MAAM,QAAQ,GAAG,UAAmB,CAAC;AAC5C,MAAM,CAAC,MAAM,MAAM,GAAG,QAAiB,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { assertValidMessage } from "./message_constraints.js";
|
|
2
|
+
import type { StepRecord } from "./types.js";
|
|
3
|
+
export declare const message_constraints: {
|
|
4
|
+
maxLength: number;
|
|
5
|
+
validate: typeof assertValidMessage;
|
|
6
|
+
};
|
|
7
|
+
export type EvidenceRef = string;
|
|
8
|
+
export type SystemOwnership = {
|
|
9
|
+
institution: string;
|
|
10
|
+
system_name: string;
|
|
11
|
+
system_type: string;
|
|
12
|
+
environment: string;
|
|
13
|
+
system_version: string;
|
|
14
|
+
instance_id?: string;
|
|
15
|
+
};
|
|
16
|
+
export type StepRecordOverrides = {
|
|
17
|
+
record_id?: string;
|
|
18
|
+
timestamp_utc?: string;
|
|
19
|
+
monotonic_time?: number;
|
|
20
|
+
message_code?: string;
|
|
21
|
+
evidence_refs?: EvidenceRef[];
|
|
22
|
+
trace_id?: string;
|
|
23
|
+
span_id?: string;
|
|
24
|
+
parent_step_id?: string;
|
|
25
|
+
surface_type?: string;
|
|
26
|
+
surface_name?: string;
|
|
27
|
+
surface_instance?: string;
|
|
28
|
+
source_file?: string;
|
|
29
|
+
source_module?: string;
|
|
30
|
+
source_function?: string;
|
|
31
|
+
source_line?: number;
|
|
32
|
+
};
|
|
33
|
+
export declare function buildStepRecord(message: string, sequence: number, system: SystemOwnership, overrides?: StepRecordOverrides): StepRecord;
|
|
34
|
+
//# sourceMappingURL=record_builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record_builder.d.ts","sourceRoot":"","sources":["../../src/core/record_builder.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,kBAAkB,EAAsB,MAAM,0BAA0B,CAAC;AAClF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAI7C,eAAO,MAAM,mBAAmB;;;CAG/B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC;AAEjC,MAAM,MAAM,eAAe,GAAG;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,WAAW,EAAE,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,eAAe,EACvB,SAAS,GAAE,mBAAwB,GAClC,UAAU,CAsDZ"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/// <reference path="../crypto-shim.d.ts" />
|
|
2
|
+
import { createHash, randomUUID } from "crypto";
|
|
3
|
+
import { getContextHash, getContextVersion } from "./context.js";
|
|
4
|
+
import { monotonicNow } from "./clock.js";
|
|
5
|
+
import { assertValidMessage, MESSAGE_MAX_LENGTH } from "./message_constraints.js";
|
|
6
|
+
const RECORD_VERSION = "log.step.v1";
|
|
7
|
+
const HASH_ALGORITHM = "sha256";
|
|
8
|
+
export const message_constraints = {
|
|
9
|
+
maxLength: MESSAGE_MAX_LENGTH,
|
|
10
|
+
validate: assertValidMessage,
|
|
11
|
+
};
|
|
12
|
+
export function buildStepRecord(message, sequence, system, overrides = {}) {
|
|
13
|
+
const validatedMessage = message_constraints.validate(message);
|
|
14
|
+
validateSequence(sequence);
|
|
15
|
+
validateSystemOwnership(system);
|
|
16
|
+
const contextHash = getContextHash();
|
|
17
|
+
const contextVersion = getContextVersion();
|
|
18
|
+
const recordId = overrides.record_id ?? createRecordId();
|
|
19
|
+
const timestampUtc = overrides.timestamp_utc ?? new Date().toISOString();
|
|
20
|
+
const monotonicTime = overrides.monotonic_time ?? monotonicNow();
|
|
21
|
+
const baseRecord = {
|
|
22
|
+
record_version: RECORD_VERSION,
|
|
23
|
+
record_id: recordId,
|
|
24
|
+
sequence,
|
|
25
|
+
timestamp_utc: timestampUtc,
|
|
26
|
+
monotonic_time: monotonicTime,
|
|
27
|
+
institution: system.institution,
|
|
28
|
+
system_name: system.system_name,
|
|
29
|
+
system_type: system.system_type,
|
|
30
|
+
environment: system.environment,
|
|
31
|
+
system_version: system.system_version,
|
|
32
|
+
message: validatedMessage,
|
|
33
|
+
context_hash: contextHash,
|
|
34
|
+
context_version: contextVersion,
|
|
35
|
+
};
|
|
36
|
+
setOptionalField(baseRecord, "instance_id", system.instance_id);
|
|
37
|
+
setOptionalField(baseRecord, "trace_id", overrides.trace_id);
|
|
38
|
+
setOptionalField(baseRecord, "span_id", overrides.span_id);
|
|
39
|
+
setOptionalField(baseRecord, "parent_step_id", overrides.parent_step_id);
|
|
40
|
+
setOptionalField(baseRecord, "surface_type", overrides.surface_type);
|
|
41
|
+
setOptionalField(baseRecord, "surface_name", overrides.surface_name);
|
|
42
|
+
setOptionalField(baseRecord, "surface_instance", overrides.surface_instance);
|
|
43
|
+
setOptionalField(baseRecord, "source_file", overrides.source_file);
|
|
44
|
+
setOptionalField(baseRecord, "source_module", overrides.source_module);
|
|
45
|
+
setOptionalField(baseRecord, "source_function", overrides.source_function);
|
|
46
|
+
setOptionalField(baseRecord, "source_line", overrides.source_line);
|
|
47
|
+
setOptionalField(baseRecord, "message_code", overrides.message_code);
|
|
48
|
+
setOptionalField(baseRecord, "evidence_refs", overrides.evidence_refs);
|
|
49
|
+
const canonical = stableStringify(normalizeJsonValueOrdered(canonicalizeForHash(baseRecord), "$"));
|
|
50
|
+
const recordHash = createHash(HASH_ALGORITHM).update(canonical).digest("hex");
|
|
51
|
+
const record = {
|
|
52
|
+
...baseRecord,
|
|
53
|
+
record_hash: recordHash,
|
|
54
|
+
hash_algorithm: HASH_ALGORITHM,
|
|
55
|
+
};
|
|
56
|
+
return deepFreeze(record);
|
|
57
|
+
}
|
|
58
|
+
function validateSequence(sequence) {
|
|
59
|
+
if (!Number.isInteger(sequence) || sequence < 0) {
|
|
60
|
+
throw new Error("Sequence must be a non-negative integer");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function validateSystemOwnership(system) {
|
|
64
|
+
const requiredFields = [
|
|
65
|
+
"institution",
|
|
66
|
+
"system_name",
|
|
67
|
+
"system_type",
|
|
68
|
+
"environment",
|
|
69
|
+
"system_version",
|
|
70
|
+
];
|
|
71
|
+
for (const field of requiredFields) {
|
|
72
|
+
const value = system[field];
|
|
73
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
74
|
+
throw new Error(`Missing required system field: ${field}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (system.instance_id !== undefined && typeof system.instance_id !== "string") {
|
|
78
|
+
throw new Error("instance_id must be a string when provided");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function createRecordId() {
|
|
82
|
+
return typeof randomUUID === "function"
|
|
83
|
+
? randomUUID()
|
|
84
|
+
: createHash("sha256")
|
|
85
|
+
.update(`${Date.now()}-${Math.random()}`)
|
|
86
|
+
.digest("hex");
|
|
87
|
+
}
|
|
88
|
+
function deepFreeze(value) {
|
|
89
|
+
if (!value || typeof value !== "object") {
|
|
90
|
+
return value;
|
|
91
|
+
}
|
|
92
|
+
if (Object.isFrozen(value)) {
|
|
93
|
+
return value;
|
|
94
|
+
}
|
|
95
|
+
Object.freeze(value);
|
|
96
|
+
for (const key of Object.keys(value)) {
|
|
97
|
+
const child = value[key];
|
|
98
|
+
deepFreeze(child);
|
|
99
|
+
}
|
|
100
|
+
if (Array.isArray(value)) {
|
|
101
|
+
for (const item of value) {
|
|
102
|
+
deepFreeze(item);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return value;
|
|
106
|
+
}
|
|
107
|
+
function canonicalizeForHash(record) {
|
|
108
|
+
const ordered = {};
|
|
109
|
+
for (const field of HASH_FIELD_ORDER) {
|
|
110
|
+
const value = record[field];
|
|
111
|
+
if (value !== undefined) {
|
|
112
|
+
ordered[field] = normalizeJsonValueOrdered(value, `${String(field)}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return ordered;
|
|
116
|
+
}
|
|
117
|
+
function normalizeJsonValueOrdered(value, path) {
|
|
118
|
+
if (value === null ||
|
|
119
|
+
typeof value === "string" ||
|
|
120
|
+
typeof value === "number" ||
|
|
121
|
+
typeof value === "boolean") {
|
|
122
|
+
return value;
|
|
123
|
+
}
|
|
124
|
+
if (typeof value === "bigint") {
|
|
125
|
+
return value.toString();
|
|
126
|
+
}
|
|
127
|
+
if (value instanceof Date) {
|
|
128
|
+
return value.toISOString();
|
|
129
|
+
}
|
|
130
|
+
if (Array.isArray(value)) {
|
|
131
|
+
return value.map((item, index) => normalizeJsonValueOrdered(item, `${path}[${index}]`));
|
|
132
|
+
}
|
|
133
|
+
if (typeof value === "object") {
|
|
134
|
+
const proto = Object.getPrototypeOf(value);
|
|
135
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
136
|
+
throw new Error(`Unsupported object at ${path}`);
|
|
137
|
+
}
|
|
138
|
+
const obj = value;
|
|
139
|
+
const result = {};
|
|
140
|
+
const keys = Object.keys(obj);
|
|
141
|
+
for (const key of keys) {
|
|
142
|
+
const next = obj[key];
|
|
143
|
+
if (next === undefined) {
|
|
144
|
+
throw new Error(`Undefined value at ${path}.${key}`);
|
|
145
|
+
}
|
|
146
|
+
result[key] = normalizeJsonValueOrdered(next, `${path}.${key}`);
|
|
147
|
+
}
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
throw new Error(`Unsupported type at ${path}`);
|
|
151
|
+
}
|
|
152
|
+
function stableStringify(value) {
|
|
153
|
+
return JSON.stringify(value);
|
|
154
|
+
}
|
|
155
|
+
function setOptionalField(record, key, value) {
|
|
156
|
+
if (value !== undefined) {
|
|
157
|
+
record[key] = value;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const HASH_FIELD_ORDER = [
|
|
161
|
+
"record_version",
|
|
162
|
+
"record_id",
|
|
163
|
+
"sequence",
|
|
164
|
+
"timestamp_utc",
|
|
165
|
+
"monotonic_time",
|
|
166
|
+
"institution",
|
|
167
|
+
"system_name",
|
|
168
|
+
"system_type",
|
|
169
|
+
"environment",
|
|
170
|
+
"system_version",
|
|
171
|
+
"instance_id",
|
|
172
|
+
"trace_id",
|
|
173
|
+
"span_id",
|
|
174
|
+
"parent_step_id",
|
|
175
|
+
"surface_type",
|
|
176
|
+
"surface_name",
|
|
177
|
+
"surface_instance",
|
|
178
|
+
"source_file",
|
|
179
|
+
"source_module",
|
|
180
|
+
"source_function",
|
|
181
|
+
"source_line",
|
|
182
|
+
"message",
|
|
183
|
+
"message_code",
|
|
184
|
+
"context_hash",
|
|
185
|
+
"context_version",
|
|
186
|
+
"evidence_refs",
|
|
187
|
+
];
|
|
188
|
+
//# sourceMappingURL=record_builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record_builder.js","sourceRoot":"","sources":["../../src/core/record_builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spool.d.ts","sourceRoot":"","sources":["../../src/core/spool.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAW7C,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB3E"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/// <reference path="../fs-shim.d.ts" />
|
|
2
|
+
import { promises as fs } from "fs";
|
|
3
|
+
import os from "os";
|
|
4
|
+
import path from "path";
|
|
5
|
+
const DEFAULT_SPOOL_NAME = "logsdk-emergency-spool.ndjson";
|
|
6
|
+
function getSpoolPath() {
|
|
7
|
+
return (process.env.LOGSDK_EMERGENCY_SPOOL_PATH ??
|
|
8
|
+
path.join(os.tmpdir(), DEFAULT_SPOOL_NAME));
|
|
9
|
+
}
|
|
10
|
+
export function getEmergencySpoolPath() {
|
|
11
|
+
return getSpoolPath();
|
|
12
|
+
}
|
|
13
|
+
export async function writeEmergencySpool(record) {
|
|
14
|
+
const spoolPath = getSpoolPath();
|
|
15
|
+
const payload = `${JSON.stringify(record)}\n`;
|
|
16
|
+
const handle = await fs.open(spoolPath, "a");
|
|
17
|
+
try {
|
|
18
|
+
const result = await handle.write(payload, undefined, "utf8");
|
|
19
|
+
if (!result || typeof result.bytesWritten !== "number") {
|
|
20
|
+
throw new Error("Emergency spool write did not report bytes.");
|
|
21
|
+
}
|
|
22
|
+
if (result.bytesWritten !== payload.length) {
|
|
23
|
+
throw new Error("Emergency spool partial write detected.");
|
|
24
|
+
}
|
|
25
|
+
await handle.sync();
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
await handle.close();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=spool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spool.js","sourceRoot":"","sources":["../../src/core/spool.ts"],"names":[],"mappings":"AAAA,wCAAwC;AAExC,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,MAAM,kBAAkB,GAAG,+BAA+B,CAAC;AAE3D,SAAS,YAAY;IACnB,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,2BAA2B;QACvC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,kBAAkB,CAAC,CAC3C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,OAAO,YAAY,EAAE,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAAkB;IAC1D,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;IACjC,MAAM,OAAO,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC;IAC9C,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAE7C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QAE9D,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;YACvD,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACjE,CAAC;QAED,IAAI,MAAM,CAAC,YAAY,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;YAC3C,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAC7D,CAAC;QAED,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;IACtB,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;AACH,CAAC"}
|