hazo_audit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGE_LOG.md +53 -0
- package/README.md +269 -0
- package/SETUP_CHECKLIST.md +186 -0
- package/bin/hazo-audit-migrate.js +169 -0
- package/config/hazo_audit_config.ini.sample +71 -0
- package/dist/client/FieldAuditIcon.d.ts +11 -0
- package/dist/client/FieldAuditIcon.d.ts.map +1 -0
- package/dist/client/FieldAuditIcon.js +46 -0
- package/dist/client/FieldAuditIcon.js.map +1 -0
- package/dist/client/FieldAuditTrail.d.ts +8 -0
- package/dist/client/FieldAuditTrail.d.ts.map +1 -0
- package/dist/client/FieldAuditTrail.js +136 -0
- package/dist/client/FieldAuditTrail.js.map +1 -0
- package/dist/client/cn.d.ts +3 -0
- package/dist/client/cn.d.ts.map +1 -0
- package/dist/client/cn.js +6 -0
- package/dist/client/cn.js.map +1 -0
- package/dist/client/index.d.ts +11 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +9 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/types.d.ts +19 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +2 -0
- package/dist/client/types.js.map +1 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/nextjs/index.d.ts +78 -0
- package/dist/nextjs/index.d.ts.map +1 -0
- package/dist/nextjs/index.js +229 -0
- package/dist/nextjs/index.js.map +1 -0
- package/dist/server/audited_crud.d.ts +42 -0
- package/dist/server/audited_crud.d.ts.map +1 -0
- package/dist/server/audited_crud.js +57 -0
- package/dist/server/audited_crud.js.map +1 -0
- package/dist/server/config.d.ts +70 -0
- package/dist/server/config.d.ts.map +1 -0
- package/dist/server/config.js +203 -0
- package/dist/server/config.js.map +1 -0
- package/dist/server/context.d.ts +28 -0
- package/dist/server/context.d.ts.map +1 -0
- package/dist/server/context.js +49 -0
- package/dist/server/context.js.map +1 -0
- package/dist/server/diff.d.ts +32 -0
- package/dist/server/diff.d.ts.map +1 -0
- package/dist/server/diff.js +152 -0
- package/dist/server/diff.js.map +1 -0
- package/dist/server/index.d.ts +23 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +26 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/outbox.d.ts +29 -0
- package/dist/server/outbox.d.ts.map +1 -0
- package/dist/server/outbox.js +53 -0
- package/dist/server/outbox.js.map +1 -0
- package/dist/server/worker.d.ts +43 -0
- package/dist/server/worker.d.ts.map +1 -0
- package/dist/server/worker.js +248 -0
- package/dist/server/worker.js.map +1 -0
- package/dist/server/wrap.d.ts +32 -0
- package/dist/server/wrap.d.ts.map +1 -0
- package/dist/server/wrap.js +232 -0
- package/dist/server/wrap.js.map +1 -0
- package/migrations/001_init.sql +149 -0
- package/package.json +101 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ActorContext } from '../index.js';
|
|
2
|
+
interface StoredContext extends ActorContext {
|
|
3
|
+
/** Audit hints stashed by createAuditedCrudService for the wrap to pick up. */
|
|
4
|
+
hints?: {
|
|
5
|
+
before_row?: Record<string, unknown> | null;
|
|
6
|
+
intent_event?: string;
|
|
7
|
+
intent_payload?: Record<string, unknown>;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Run a callback inside an audit actor context. The wrap reads this via
|
|
12
|
+
* getAuditContext() to stamp who/what caused each captured write.
|
|
13
|
+
*
|
|
14
|
+
* Missing context (e.g., scheduled jobs that forgot to wrap) is fine — the
|
|
15
|
+
* wrap defaults `actor_kind` to 'background_job'.
|
|
16
|
+
*/
|
|
17
|
+
export declare function runWithAuditContext<T>(ctx: ActorContext, fn: () => Promise<T> | T): Promise<T>;
|
|
18
|
+
export declare function getAuditContext(): ActorContext | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Internal — stash audit hints onto the current context for one operation.
|
|
21
|
+
* Used by createAuditedCrudService. Throws if no context is active so callers
|
|
22
|
+
* don't accidentally drop hints.
|
|
23
|
+
*/
|
|
24
|
+
export declare function withAuditHints<T>(hints: NonNullable<StoredContext['hints']>, fn: () => Promise<T>): Promise<T>;
|
|
25
|
+
/** Internal — read and clear hints (one-shot, consumed by the wrap). */
|
|
26
|
+
export declare function takeAuditHints(): StoredContext['hints'] | undefined;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/server/context.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,UAAU,aAAc,SAAQ,YAAY;IAC1C,+EAA+E;IAC/E,KAAK,CAAC,EAAE;QACN,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;QAC5C,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC1C,CAAC;CACH;AAID;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EACnC,GAAG,EAAE,YAAY,EACjB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,GACvB,OAAO,CAAC,CAAC,CAAC,CAMZ;AAED,wBAAgB,eAAe,IAAI,YAAY,GAAG,SAAS,CAE1D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,KAAK,EAAE,WAAW,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,EAC1C,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAaZ;AAED,wEAAwE;AACxE,wBAAgB,cAAc,IAAI,aAAa,CAAC,OAAO,CAAC,GAAG,SAAS,CAMnE"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
const storage = new AsyncLocalStorage();
|
|
4
|
+
/**
|
|
5
|
+
* Run a callback inside an audit actor context. The wrap reads this via
|
|
6
|
+
* getAuditContext() to stamp who/what caused each captured write.
|
|
7
|
+
*
|
|
8
|
+
* Missing context (e.g., scheduled jobs that forgot to wrap) is fine — the
|
|
9
|
+
* wrap defaults `actor_kind` to 'background_job'.
|
|
10
|
+
*/
|
|
11
|
+
export function runWithAuditContext(ctx, fn) {
|
|
12
|
+
const stored = {
|
|
13
|
+
...ctx,
|
|
14
|
+
correlation_id: ctx.correlation_id ?? randomUUID(),
|
|
15
|
+
};
|
|
16
|
+
return Promise.resolve(storage.run(stored, fn));
|
|
17
|
+
}
|
|
18
|
+
export function getAuditContext() {
|
|
19
|
+
return storage.getStore();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Internal — stash audit hints onto the current context for one operation.
|
|
23
|
+
* Used by createAuditedCrudService. Throws if no context is active so callers
|
|
24
|
+
* don't accidentally drop hints.
|
|
25
|
+
*/
|
|
26
|
+
export function withAuditHints(hints, fn) {
|
|
27
|
+
const current = storage.getStore();
|
|
28
|
+
const next = current
|
|
29
|
+
? { ...current, hints }
|
|
30
|
+
: {
|
|
31
|
+
actor_kind: 'background_job',
|
|
32
|
+
actor_user_id: null,
|
|
33
|
+
actor_label: null,
|
|
34
|
+
scope_id: null,
|
|
35
|
+
correlation_id: randomUUID(),
|
|
36
|
+
hints,
|
|
37
|
+
};
|
|
38
|
+
return storage.run(next, fn);
|
|
39
|
+
}
|
|
40
|
+
/** Internal — read and clear hints (one-shot, consumed by the wrap). */
|
|
41
|
+
export function takeAuditHints() {
|
|
42
|
+
const current = storage.getStore();
|
|
43
|
+
if (!current?.hints)
|
|
44
|
+
return undefined;
|
|
45
|
+
const hints = current.hints;
|
|
46
|
+
current.hints = undefined;
|
|
47
|
+
return hints;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/server/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAYzC,MAAM,OAAO,GAAG,IAAI,iBAAiB,EAAiB,CAAC;AAEvD;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAAiB,EACjB,EAAwB;IAExB,MAAM,MAAM,GAAkB;QAC5B,GAAG,GAAG;QACN,cAAc,EAAE,GAAG,CAAC,cAAc,IAAI,UAAU,EAAE;KACnD,CAAC;IACF,OAAO,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAC;AAC5B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,KAA0C,EAC1C,EAAoB;IAEpB,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IACnC,MAAM,IAAI,GAAkB,OAAO;QACjC,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE;QACvB,CAAC,CAAC;YACE,UAAU,EAAE,gBAAgB;YAC5B,aAAa,EAAE,IAAI;YACnB,WAAW,EAAE,IAAI;YACjB,QAAQ,EAAE,IAAI;YACd,cAAc,EAAE,UAAU,EAAE;YAC5B,KAAK;SACN,CAAC;IACN,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAC/B,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,cAAc;IAC5B,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IACnC,IAAI,CAAC,OAAO,EAAE,KAAK;QAAE,OAAO,SAAS,CAAC;IACtC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAC5B,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC;IAC1B,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field-level diff helper. Produces one record per changed leaf, suitable
|
|
3
|
+
* for inserting into hazo_audit_field. Uses microdiff for JSONB and a plain
|
|
4
|
+
* key-by-key comparison for top-level scalar columns.
|
|
5
|
+
*/
|
|
6
|
+
import 'server-only';
|
|
7
|
+
import type { TableOverride } from './config.js';
|
|
8
|
+
import type { PathResolver } from '../index.js';
|
|
9
|
+
export interface FieldChange {
|
|
10
|
+
field_path: string;
|
|
11
|
+
op: 'insert' | 'update' | 'delete';
|
|
12
|
+
before_value: unknown;
|
|
13
|
+
after_value: unknown;
|
|
14
|
+
is_sensitive: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Diff two rows into per-field FieldChange records.
|
|
18
|
+
*
|
|
19
|
+
* - JSONB columns (override.jsonb_columns) are decomposed via microdiff and
|
|
20
|
+
* filtered by include/exclude prefix rules, then optionally canonicalised
|
|
21
|
+
* by the named resolver.
|
|
22
|
+
* - Other columns are treated as scalars: one event per column whose value
|
|
23
|
+
* actually changed.
|
|
24
|
+
*/
|
|
25
|
+
export declare function diffRows(args: {
|
|
26
|
+
before: Record<string, unknown> | null;
|
|
27
|
+
after: Record<string, unknown> | null;
|
|
28
|
+
op: 'insert' | 'update' | 'delete';
|
|
29
|
+
override: TableOverride;
|
|
30
|
+
resolver?: PathResolver;
|
|
31
|
+
}): FieldChange[];
|
|
32
|
+
//# sourceMappingURL=diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/server/diff.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACnC,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,EAAE,OAAO,CAAC;CACvB;AAYD;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE;IAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACvC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACtC,EAAE,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACnC,QAAQ,EAAE,aAAa,CAAC;IACxB,QAAQ,CAAC,EAAE,YAAY,CAAC;CACzB,GAAG,WAAW,EAAE,CAgFhB"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field-level diff helper. Produces one record per changed leaf, suitable
|
|
3
|
+
* for inserting into hazo_audit_field. Uses microdiff for JSONB and a plain
|
|
4
|
+
* key-by-key comparison for top-level scalar columns.
|
|
5
|
+
*/
|
|
6
|
+
import 'server-only';
|
|
7
|
+
import microdiff from 'microdiff';
|
|
8
|
+
import { matchesJsonbPath } from './config.js';
|
|
9
|
+
/** True for primitives + null. JSON-stringifiable scalars only. */
|
|
10
|
+
function isScalar(v) {
|
|
11
|
+
return (v === null ||
|
|
12
|
+
typeof v === 'string' ||
|
|
13
|
+
typeof v === 'number' ||
|
|
14
|
+
typeof v === 'boolean');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Diff two rows into per-field FieldChange records.
|
|
18
|
+
*
|
|
19
|
+
* - JSONB columns (override.jsonb_columns) are decomposed via microdiff and
|
|
20
|
+
* filtered by include/exclude prefix rules, then optionally canonicalised
|
|
21
|
+
* by the named resolver.
|
|
22
|
+
* - Other columns are treated as scalars: one event per column whose value
|
|
23
|
+
* actually changed.
|
|
24
|
+
*/
|
|
25
|
+
export function diffRows(args) {
|
|
26
|
+
const { before, after, op, override, resolver } = args;
|
|
27
|
+
const sensitiveSet = new Set(override.sensitive_columns);
|
|
28
|
+
const jsonbSet = new Set(override.jsonb_columns);
|
|
29
|
+
const out = [];
|
|
30
|
+
// ── DELETE: emit one event per column from `before` (or after_row=null)
|
|
31
|
+
if (op === 'delete') {
|
|
32
|
+
const src = before ?? {};
|
|
33
|
+
for (const [col, val] of Object.entries(src)) {
|
|
34
|
+
if (jsonbSet.has(col))
|
|
35
|
+
continue; // delete summaries omit JSONB leaves
|
|
36
|
+
out.push({
|
|
37
|
+
field_path: col,
|
|
38
|
+
op,
|
|
39
|
+
before_value: val,
|
|
40
|
+
after_value: null,
|
|
41
|
+
is_sensitive: sensitiveSet.has(col),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
// ── INSERT: every column in `after` is a new value.
|
|
47
|
+
if (op === 'insert') {
|
|
48
|
+
const src = after ?? {};
|
|
49
|
+
for (const [col, val] of Object.entries(src)) {
|
|
50
|
+
if (jsonbSet.has(col)) {
|
|
51
|
+
// Decompose JSONB column into per-leaf events vs empty baseline.
|
|
52
|
+
const leaves = diffJsonb(col, undefined, val, override, resolver);
|
|
53
|
+
out.push(...leaves.map((l) => ({ ...l, op })));
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
out.push({
|
|
57
|
+
field_path: col,
|
|
58
|
+
op,
|
|
59
|
+
before_value: null,
|
|
60
|
+
after_value: val,
|
|
61
|
+
is_sensitive: sensitiveSet.has(col),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
// ── UPDATE: compare before/after; emit only changed fields.
|
|
67
|
+
const beforeMap = before ?? {};
|
|
68
|
+
const afterMap = after ?? {};
|
|
69
|
+
const keys = new Set([...Object.keys(beforeMap), ...Object.keys(afterMap)]);
|
|
70
|
+
for (const col of keys) {
|
|
71
|
+
const bv = beforeMap[col];
|
|
72
|
+
const av = afterMap[col];
|
|
73
|
+
if (jsonbSet.has(col)) {
|
|
74
|
+
const leaves = diffJsonb(col, bv, av, override, resolver);
|
|
75
|
+
out.push(...leaves.map((l) => ({ ...l, op })));
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
// Only emit for actual change. Use JSON-equality for non-scalars; deep
|
|
79
|
+
// compare via JSON stringify is fine for the small rows we expect here.
|
|
80
|
+
if (isScalar(bv) && isScalar(av)) {
|
|
81
|
+
if (bv !== av) {
|
|
82
|
+
out.push({
|
|
83
|
+
field_path: col,
|
|
84
|
+
op,
|
|
85
|
+
before_value: bv ?? null,
|
|
86
|
+
after_value: av ?? null,
|
|
87
|
+
is_sensitive: sensitiveSet.has(col),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (JSON.stringify(bv) !== JSON.stringify(av)) {
|
|
93
|
+
out.push({
|
|
94
|
+
field_path: col,
|
|
95
|
+
op,
|
|
96
|
+
before_value: bv ?? null,
|
|
97
|
+
after_value: av ?? null,
|
|
98
|
+
is_sensitive: sensitiveSet.has(col),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Decompose JSONB column changes into per-leaf events using microdiff.
|
|
106
|
+
* `column` is the SQL column; produced paths look like `column.foo.bar.0`.
|
|
107
|
+
*/
|
|
108
|
+
function diffJsonb(column, before, after, override, resolver) {
|
|
109
|
+
const sensitiveSet = new Set(override.sensitive_columns);
|
|
110
|
+
const isColumnSensitive = sensitiveSet.has(column);
|
|
111
|
+
// microdiff requires objects; wrap to give a stable root.
|
|
112
|
+
const beforeRoot = before == null ? {} : { [column]: before };
|
|
113
|
+
const afterRoot = after == null ? {} : { [column]: after };
|
|
114
|
+
const diffs = microdiff(beforeRoot, afterRoot, { cyclesFix: false });
|
|
115
|
+
const out = [];
|
|
116
|
+
for (const d of diffs) {
|
|
117
|
+
const path = d.path.map((p) => String(p)).join('.');
|
|
118
|
+
const canon = resolver ? resolver(path) : path;
|
|
119
|
+
if (!matchesJsonbPath(canon, override.jsonb_path_include, override.jsonb_path_exclude)) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (d.type === 'CREATE') {
|
|
123
|
+
out.push({
|
|
124
|
+
field_path: canon,
|
|
125
|
+
op: 'update',
|
|
126
|
+
before_value: null,
|
|
127
|
+
after_value: d.value,
|
|
128
|
+
is_sensitive: isColumnSensitive,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
else if (d.type === 'REMOVE') {
|
|
132
|
+
out.push({
|
|
133
|
+
field_path: canon,
|
|
134
|
+
op: 'update',
|
|
135
|
+
before_value: d.oldValue,
|
|
136
|
+
after_value: null,
|
|
137
|
+
is_sensitive: isColumnSensitive,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
else if (d.type === 'CHANGE') {
|
|
141
|
+
out.push({
|
|
142
|
+
field_path: canon,
|
|
143
|
+
op: 'update',
|
|
144
|
+
before_value: d.oldValue,
|
|
145
|
+
after_value: d.value,
|
|
146
|
+
is_sensitive: isColumnSensitive,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/server/diff.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hazo_audit/server — all server-only exports.
|
|
3
|
+
*
|
|
4
|
+
* Import with:
|
|
5
|
+
* import { wrapWithAudit, startAuditWorker, runWithAuditContext } from 'hazo_audit/server'
|
|
6
|
+
*
|
|
7
|
+
* Client code must never import this entry. Node.js APIs (AsyncLocalStorage,
|
|
8
|
+
* fs for INI loading) make it incompatible with browser bundles.
|
|
9
|
+
*/
|
|
10
|
+
import 'server-only';
|
|
11
|
+
export * from '../index.js';
|
|
12
|
+
export { wrapWithAudit } from './wrap.js';
|
|
13
|
+
export type { WrapWithAuditOptions } from './wrap.js';
|
|
14
|
+
export { startAuditWorker } from './worker.js';
|
|
15
|
+
export type { WorkerOptions, WorkerHandle } from './worker.js';
|
|
16
|
+
export { runWithAuditContext, getAuditContext, } from './context.js';
|
|
17
|
+
export { createAuditedCrudService, emitIntentEvent, } from './audited_crud.js';
|
|
18
|
+
export type { AuditHints, AuditedCrudService, AuditedCrudServiceOptions, } from './audited_crud.js';
|
|
19
|
+
export { loadAuditConfig, shouldCapture, getSubjectKind, getSubjectIdColumn, getOverride, matchesJsonbPath, } from './config.js';
|
|
20
|
+
export type { AuditConfig, CaptureConfig, RetentionConfig, WorkerConfig, DefaultsConfig, TableOverride, } from './config.js';
|
|
21
|
+
export { diffRows } from './diff.js';
|
|
22
|
+
export type { FieldChange } from './diff.js';
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,aAAa,CAAC;AAIrB,cAAc,aAAa,CAAC;AAG5B,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,YAAY,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAGtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG/D,OAAO,EACL,mBAAmB,EACnB,eAAe,GAChB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,wBAAwB,EACxB,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,UAAU,EACV,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,eAAe,EACf,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,WAAW,EACX,aAAa,EACb,eAAe,EACf,YAAY,EACZ,cAAc,EACd,aAAa,GACd,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hazo_audit/server — all server-only exports.
|
|
3
|
+
*
|
|
4
|
+
* Import with:
|
|
5
|
+
* import { wrapWithAudit, startAuditWorker, runWithAuditContext } from 'hazo_audit/server'
|
|
6
|
+
*
|
|
7
|
+
* Client code must never import this entry. Node.js APIs (AsyncLocalStorage,
|
|
8
|
+
* fs for INI loading) make it incompatible with browser bundles.
|
|
9
|
+
*/
|
|
10
|
+
import 'server-only';
|
|
11
|
+
// Re-export the client-safe types & permission constants so server code only
|
|
12
|
+
// needs one import.
|
|
13
|
+
export * from '../index.js';
|
|
14
|
+
// Wrap (producer)
|
|
15
|
+
export { wrapWithAudit } from './wrap.js';
|
|
16
|
+
// Worker (consumer)
|
|
17
|
+
export { startAuditWorker } from './worker.js';
|
|
18
|
+
// Actor context
|
|
19
|
+
export { runWithAuditContext, getAuditContext, } from './context.js';
|
|
20
|
+
// Audited CRUD + intent
|
|
21
|
+
export { createAuditedCrudService, emitIntentEvent, } from './audited_crud.js';
|
|
22
|
+
// Config (exposed for advanced users / tests)
|
|
23
|
+
export { loadAuditConfig, shouldCapture, getSubjectKind, getSubjectIdColumn, getOverride, matchesJsonbPath, } from './config.js';
|
|
24
|
+
// Diff helper (exposed for testing/inspection)
|
|
25
|
+
export { diffRows } from './diff.js';
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,aAAa,CAAC;AAErB,6EAA6E;AAC7E,oBAAoB;AACpB,cAAc,aAAa,CAAC;AAE5B,kBAAkB;AAClB,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAG1C,oBAAoB;AACpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C,gBAAgB;AAChB,OAAO,EACL,mBAAmB,EACnB,eAAe,GAChB,MAAM,cAAc,CAAC;AAEtB,wBAAwB;AACxB,OAAO,EACL,wBAAwB,EACxB,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAO3B,8CAA8C;AAC9C,OAAO,EACL,eAAe,EACf,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAUrB,+CAA+C;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outbox enqueue helper. Best-effort: any failure here logs + reports via
|
|
3
|
+
* onAuditFailure but NEVER throws into the user write path.
|
|
4
|
+
*/
|
|
5
|
+
import 'server-only';
|
|
6
|
+
import type { HazoConnectAdapter } from 'hazo_connect';
|
|
7
|
+
import type { ActorContext } from '../index.js';
|
|
8
|
+
export interface OutboxRow {
|
|
9
|
+
id?: string;
|
|
10
|
+
scope_id?: string | null;
|
|
11
|
+
correlation_id: string;
|
|
12
|
+
table_name: string;
|
|
13
|
+
subject_kind: string;
|
|
14
|
+
subject_id: string;
|
|
15
|
+
op: 'insert' | 'update' | 'delete';
|
|
16
|
+
before_row?: Record<string, unknown> | null;
|
|
17
|
+
after_row?: Record<string, unknown> | null;
|
|
18
|
+
actor_kind: ActorContext['actor_kind'];
|
|
19
|
+
actor_user_id?: string | null;
|
|
20
|
+
actor_label?: string | null;
|
|
21
|
+
intent_event?: string | null;
|
|
22
|
+
intent_payload?: Record<string, unknown> | null;
|
|
23
|
+
}
|
|
24
|
+
export type OutboxFailureHandler = (err: Error, ctx: {
|
|
25
|
+
table: string;
|
|
26
|
+
op: string;
|
|
27
|
+
}) => void;
|
|
28
|
+
export declare function enqueueOutbox(adapter: HazoConnectAdapter, row: OutboxRow, onFailure?: OutboxFailureHandler): Promise<void>;
|
|
29
|
+
//# sourceMappingURL=outbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../src/server/outbox.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,SAAS;IACxB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC3C,UAAU,EAAE,YAAY,CAAC,YAAY,CAAC,CAAC;IACvC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACjD;AAED,MAAM,MAAM,oBAAoB,GAAG,CACjC,GAAG,EAAE,KAAK,EACV,GAAG,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,KAC/B,IAAI,CAAC;AAiBV,wBAAsB,aAAa,CACjC,OAAO,EAAE,kBAAkB,EAC3B,GAAG,EAAE,SAAS,EACd,SAAS,CAAC,EAAE,oBAAoB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAyBf"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outbox enqueue helper. Best-effort: any failure here logs + reports via
|
|
3
|
+
* onAuditFailure but NEVER throws into the user write path.
|
|
4
|
+
*/
|
|
5
|
+
import 'server-only';
|
|
6
|
+
import { randomUUID } from 'node:crypto';
|
|
7
|
+
import { createCrudService } from 'hazo_connect/server';
|
|
8
|
+
/**
|
|
9
|
+
* Serialize a JSONB-shaped value to a JSON string when adapters can't bind
|
|
10
|
+
* objects directly (e.g., sql.js). Postgres JSONB also accepts strings so
|
|
11
|
+
* this is safe across adapters.
|
|
12
|
+
*/
|
|
13
|
+
function toJsonText(v) {
|
|
14
|
+
if (v == null)
|
|
15
|
+
return null;
|
|
16
|
+
if (typeof v === 'string')
|
|
17
|
+
return v;
|
|
18
|
+
try {
|
|
19
|
+
return JSON.stringify(v);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export async function enqueueOutbox(adapter, row, onFailure) {
|
|
26
|
+
try {
|
|
27
|
+
const svc = createCrudService(adapter, 'hazo_audit_outbox', {
|
|
28
|
+
autoId: { enabled: true, column: 'id' },
|
|
29
|
+
});
|
|
30
|
+
await svc.insert({
|
|
31
|
+
id: row.id ?? randomUUID(),
|
|
32
|
+
scope_id: row.scope_id ?? null,
|
|
33
|
+
correlation_id: row.correlation_id,
|
|
34
|
+
table_name: row.table_name,
|
|
35
|
+
subject_kind: row.subject_kind,
|
|
36
|
+
subject_id: row.subject_id,
|
|
37
|
+
op: row.op,
|
|
38
|
+
before_row: toJsonText(row.before_row),
|
|
39
|
+
after_row: toJsonText(row.after_row),
|
|
40
|
+
actor_kind: row.actor_kind,
|
|
41
|
+
actor_user_id: row.actor_user_id ?? null,
|
|
42
|
+
actor_label: row.actor_label ?? null,
|
|
43
|
+
intent_event: row.intent_event ?? null,
|
|
44
|
+
intent_payload: toJsonText(row.intent_payload),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
49
|
+
if (onFailure)
|
|
50
|
+
onFailure(e, { table: row.table_name, op: row.op });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=outbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outbox.js","sourceRoot":"","sources":["../../src/server/outbox.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,aAAa,CAAC;AACrB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAyBxD;;;;GAIG;AACH,SAAS,UAAU,CAAC,CAAU;IAC5B,IAAI,CAAC,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAC3B,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC;IACpC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAA2B,EAC3B,GAAc,EACd,SAAgC;IAEhC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,iBAAiB,CAAC,OAAO,EAAE,mBAAmB,EAAE;YAC1D,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;SACxC,CAAC,CAAC;QACH,MAAM,GAAG,CAAC,MAAM,CAAC;YACf,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,UAAU,EAAE;YAC1B,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,IAAI;YAC9B,cAAc,EAAE,GAAG,CAAC,cAAc;YAClC,UAAU,EAAE,GAAG,CAAC,UAAU;YAC1B,YAAY,EAAE,GAAG,CAAC,YAAY;YAC9B,UAAU,EAAE,GAAG,CAAC,UAAU;YAC1B,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC;YACtC,SAAS,EAAE,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC;YACpC,UAAU,EAAE,GAAG,CAAC,UAAU;YAC1B,aAAa,EAAE,GAAG,CAAC,aAAa,IAAI,IAAI;YACxC,WAAW,EAAE,GAAG,CAAC,WAAW,IAAI,IAAI;YACpC,YAAY,EAAE,GAAG,CAAC,YAAY,IAAI,IAAI;YACtC,cAAc,EAAE,UAAU,CAAC,GAAG,CAAC,cAAc,CAAC;SAC/C,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9D,IAAI,SAAS;YAAE,SAAS,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,UAAU,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;IACrE,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit worker: drains hazo_audit_outbox into hazo_audit_field /
|
|
3
|
+
* hazo_audit_intent. Uses hazo_connect's claimRows() primitive for
|
|
4
|
+
* concurrency-safe row claiming with attempt-count + backoff.
|
|
5
|
+
*/
|
|
6
|
+
import 'server-only';
|
|
7
|
+
import type { HazoConnectAdapter } from 'hazo_connect';
|
|
8
|
+
import { type AuditConfig } from './config.js';
|
|
9
|
+
import type { PathResolver } from '../index.js';
|
|
10
|
+
export interface WorkerOptions {
|
|
11
|
+
/** Adapter that owns hazo_audit_outbox (claimed via claimRows). */
|
|
12
|
+
app_adapter: HazoConnectAdapter;
|
|
13
|
+
/** Adapter that owns hazo_audit_field + hazo_audit_intent. Defaults to app_adapter. */
|
|
14
|
+
audit_adapter?: HazoConnectAdapter;
|
|
15
|
+
/** Worker poll interval in ms. Falls back to INI. */
|
|
16
|
+
poll_ms?: number;
|
|
17
|
+
/** Max attempts before dead-letter. Falls back to INI. */
|
|
18
|
+
max_attempts?: number;
|
|
19
|
+
/** Named JSONB path resolvers (same shape as wrapWithAudit). */
|
|
20
|
+
resolvers?: Record<string, PathResolver>;
|
|
21
|
+
/** Pre-loaded config (escape hatch for tests). Bypasses file IO. */
|
|
22
|
+
config?: AuditConfig;
|
|
23
|
+
/** Optional INI path override. */
|
|
24
|
+
config_path?: string;
|
|
25
|
+
/** Fired on any worker exception; never re-thrown. */
|
|
26
|
+
onWorkerError?: (err: Error, row?: unknown) => void;
|
|
27
|
+
/** Optional logger. */
|
|
28
|
+
logger?: {
|
|
29
|
+
debug?: (msg: string, data?: Record<string, unknown>) => void;
|
|
30
|
+
info?: (msg: string, data?: Record<string, unknown>) => void;
|
|
31
|
+
warn?: (msg: string, data?: Record<string, unknown>) => void;
|
|
32
|
+
error?: (msg: string, data?: Record<string, unknown>) => void;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export interface WorkerHandle {
|
|
36
|
+
stop(): Promise<void>;
|
|
37
|
+
drainOnce(): Promise<{
|
|
38
|
+
processed: number;
|
|
39
|
+
failed: number;
|
|
40
|
+
}>;
|
|
41
|
+
}
|
|
42
|
+
export declare function startAuditWorker(opts: WorkerOptions): WorkerHandle;
|
|
43
|
+
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/server/worker.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,OAAO,EAAgC,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAE7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,WAAW,EAAE,kBAAkB,CAAC;IAChC,uFAAuF;IACvF,aAAa,CAAC,EAAE,kBAAkB,CAAC;IACnC,qDAAqD;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACzC,oEAAoE;IACpE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACpD,uBAAuB;IACvB,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;QAC9D,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;QAC7D,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;QAC7D,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;KAC/D,CAAC;CACH;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,SAAS,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC7D;AAqND,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,YAAY,CAoFlE"}
|