@astram/engine 0.1.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/LICENSE +21 -0
- package/dist/adapters/mariadb-audit-sink.d.ts +24 -0
- package/dist/adapters/mariadb-audit-sink.d.ts.map +1 -0
- package/dist/adapters/mariadb-audit-sink.js +40 -0
- package/dist/adapters/mariadb-audit-sink.js.map +1 -0
- package/dist/adapters/mariadb-unit-of-work.d.ts +27 -0
- package/dist/adapters/mariadb-unit-of-work.d.ts.map +1 -0
- package/dist/adapters/mariadb-unit-of-work.js +58 -0
- package/dist/adapters/mariadb-unit-of-work.js.map +1 -0
- package/dist/decorators/access-bundle.decorator.d.ts +66 -0
- package/dist/decorators/access-bundle.decorator.d.ts.map +1 -0
- package/dist/decorators/access-bundle.decorator.js +76 -0
- package/dist/decorators/access-bundle.decorator.js.map +1 -0
- package/dist/decorators/object-type.decorator.d.ts +116 -0
- package/dist/decorators/object-type.decorator.d.ts.map +1 -0
- package/dist/decorators/object-type.decorator.js +146 -0
- package/dist/decorators/object-type.decorator.js.map +1 -0
- package/dist/errors.d.ts +46 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +67 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +80 -0
- package/dist/index.js.map +1 -0
- package/dist/module/engine.module.d.ts +90 -0
- package/dist/module/engine.module.d.ts.map +1 -0
- package/dist/module/engine.module.js +218 -0
- package/dist/module/engine.module.js.map +1 -0
- package/dist/module/tokens.d.ts +12 -0
- package/dist/module/tokens.d.ts.map +1 -0
- package/dist/module/tokens.js +14 -0
- package/dist/pipe/context.d.ts +55 -0
- package/dist/pipe/context.d.ts.map +1 -0
- package/dist/pipe/context.js +18 -0
- package/dist/pipe/context.js.map +1 -0
- package/dist/pipe/record-engine.d.ts +72 -0
- package/dist/pipe/record-engine.d.ts.map +1 -0
- package/dist/pipe/record-engine.js +279 -0
- package/dist/pipe/record-engine.js.map +1 -0
- package/dist/pipe/records-service.d.ts +47 -0
- package/dist/pipe/records-service.d.ts.map +1 -0
- package/dist/pipe/records-service.js +108 -0
- package/dist/pipe/stages/audit.d.ts +15 -0
- package/dist/pipe/stages/audit.d.ts.map +1 -0
- package/dist/pipe/stages/audit.js +42 -0
- package/dist/pipe/stages/audit.js.map +1 -0
- package/dist/pipe/stages/derive-write-context.d.ts +22 -0
- package/dist/pipe/stages/derive-write-context.d.ts.map +1 -0
- package/dist/pipe/stages/derive-write-context.js +61 -0
- package/dist/pipe/stages/derive-write-context.js.map +1 -0
- package/dist/pipe/stages/fetch-owner.d.ts +28 -0
- package/dist/pipe/stages/fetch-owner.d.ts.map +1 -0
- package/dist/pipe/stages/fetch-owner.js +90 -0
- package/dist/pipe/stages/fetch-owner.js.map +1 -0
- package/dist/pipe/stages/junction-sync.d.ts +14 -0
- package/dist/pipe/stages/junction-sync.d.ts.map +1 -0
- package/dist/pipe/stages/junction-sync.js +63 -0
- package/dist/pipe/stages/junction-sync.js.map +1 -0
- package/dist/pipe/stages/permission-gate.d.ts +11 -0
- package/dist/pipe/stages/permission-gate.d.ts.map +1 -0
- package/dist/pipe/stages/permission-gate.js +53 -0
- package/dist/pipe/stages/permission-gate.js.map +1 -0
- package/dist/pipe/stages/validate.d.ts +11 -0
- package/dist/pipe/stages/validate.d.ts.map +1 -0
- package/dist/pipe/stages/validate.js +89 -0
- package/dist/pipe/stages/validate.js.map +1 -0
- package/dist/pipe/stages/write.d.ts +12 -0
- package/dist/pipe/stages/write.d.ts.map +1 -0
- package/dist/pipe/stages/write.js +109 -0
- package/dist/pipe/stages/write.js.map +1 -0
- package/dist/ports.d.ts +37 -0
- package/dist/ports.d.ts.map +1 -0
- package/dist/ports.js +11 -0
- package/dist/ports.js.map +1 -0
- package/dist/registry.d.ts +42 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +184 -0
- package/dist/registry.js.map +1 -0
- package/dist/types.d.ts +207 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +15 -0
- package/dist/types.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Stage: write. Persists the validated columns to the real physical table via the
|
|
4
|
+
* bound transactional query fn (parameterized — never string-built). Handles the
|
|
5
|
+
* create (INSERT with a pre-allocated uuid PK), update (UPDATE by id), and delete
|
|
6
|
+
* (DELETE by id). This is the ONLY stage that writes the base row; extension-field
|
|
7
|
+
* sidecar writes attach here too when the table is extensible.
|
|
8
|
+
*
|
|
9
|
+
* uuid generation uses node:crypto (guard/engine own node built-ins; core does not).
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.writeStage = writeStage;
|
|
13
|
+
const node_crypto_1 = require("node:crypto");
|
|
14
|
+
const errors_js_1 = require("../../errors.js");
|
|
15
|
+
const registry_js_1 = require("../../registry.js");
|
|
16
|
+
function qualified(ctx) {
|
|
17
|
+
const schema = ctx.registration.schema;
|
|
18
|
+
const t = ctx.registration.table;
|
|
19
|
+
return schema ? `\`${schema}\`.\`${t}\`` : `\`${t}\``;
|
|
20
|
+
}
|
|
21
|
+
/** The physical column of a `tenantId`-trusted field, if the table has one. */
|
|
22
|
+
function tenantColumn(ctx) {
|
|
23
|
+
const f = ctx.registration.fields.find((x) => x.trusted === 'tenantId');
|
|
24
|
+
return f ? (0, registry_js_1.columnOf)(f) : null;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Build a WHERE clause + params for a record id (uuid or composite). CRITICAL
|
|
28
|
+
* cross-tenant safety: when the table is tenant-owned (has a `tenantId`-trusted
|
|
29
|
+
* column), the WHERE is ALSO constrained to the ACTOR's tenant — so an update/
|
|
30
|
+
* delete can NEVER touch another tenant's row even if the id is known and the gate
|
|
31
|
+
* (which sees the actor's own tenant on a record-scoped resource) would allow it.
|
|
32
|
+
* This is the write-path twin of the read-path scoping (defense-in-depth).
|
|
33
|
+
*/
|
|
34
|
+
function whereById(ctx, id) {
|
|
35
|
+
const pk = ctx.registration.primaryKey;
|
|
36
|
+
let sql;
|
|
37
|
+
let params;
|
|
38
|
+
if (pk.kind === 'uuid') {
|
|
39
|
+
if (typeof id !== 'string') {
|
|
40
|
+
throw new errors_js_1.RegistrationError(`uuid PK table '${ctx.registration.typeKey}' needs a string id`);
|
|
41
|
+
}
|
|
42
|
+
sql = `\`${pk.column}\` = ?`;
|
|
43
|
+
params = [id];
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
if (typeof id === 'string') {
|
|
47
|
+
throw new errors_js_1.RegistrationError(`composite PK table '${ctx.registration.typeKey}' needs a key map, not a string id`);
|
|
48
|
+
}
|
|
49
|
+
sql = pk.columns.map((c) => `\`${c}\` = ?`).join(' AND ');
|
|
50
|
+
params = pk.columns.map((c) => id[c]);
|
|
51
|
+
}
|
|
52
|
+
const tenantCol = tenantColumn(ctx);
|
|
53
|
+
if (tenantCol) {
|
|
54
|
+
sql += ` AND \`${tenantCol}\` = ?`;
|
|
55
|
+
params = [...params, ctx.actor.tenantId];
|
|
56
|
+
}
|
|
57
|
+
return { sql, params };
|
|
58
|
+
}
|
|
59
|
+
function writeStage() {
|
|
60
|
+
return {
|
|
61
|
+
name: 'write',
|
|
62
|
+
async run(ctx) {
|
|
63
|
+
const table = qualified(ctx);
|
|
64
|
+
if (ctx.op === 'create') {
|
|
65
|
+
const pk = ctx.registration.primaryKey;
|
|
66
|
+
// Pre-allocate the uuid PK (composite-PK tables set their key columns via fields).
|
|
67
|
+
if (pk.kind === 'uuid') {
|
|
68
|
+
const pkCol = (0, registry_js_1.uuidPkColumn)(pk);
|
|
69
|
+
if (ctx.columns[pkCol] === undefined)
|
|
70
|
+
ctx.columns[pkCol] = (0, node_crypto_1.randomUUID)();
|
|
71
|
+
ctx.resultId = ctx.columns[pkCol];
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
const key = {};
|
|
75
|
+
for (const c of pk.columns)
|
|
76
|
+
key[c] = String(ctx.columns[c]);
|
|
77
|
+
ctx.resultId = key;
|
|
78
|
+
}
|
|
79
|
+
const cols = Object.keys(ctx.columns);
|
|
80
|
+
if (cols.length === 0) {
|
|
81
|
+
throw new errors_js_1.RegistrationError(`create '${ctx.registration.typeKey}': no columns to insert`);
|
|
82
|
+
}
|
|
83
|
+
const placeholders = cols.map(() => '?').join(', ');
|
|
84
|
+
const sql = `INSERT INTO ${table} (${cols.map((c) => `\`${c}\``).join(', ')}) VALUES (${placeholders})`;
|
|
85
|
+
await ctx.query(sql, cols.map((c) => ctx.columns[c]));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (ctx.op === 'update') {
|
|
89
|
+
if (ctx.id === undefined)
|
|
90
|
+
throw new errors_js_1.RegistrationError('update requires an id');
|
|
91
|
+
const cols = Object.keys(ctx.columns);
|
|
92
|
+
if (cols.length === 0)
|
|
93
|
+
return; // nothing to patch
|
|
94
|
+
const setSql = cols.map((c) => `\`${c}\` = ?`).join(', ');
|
|
95
|
+
const where = whereById(ctx, ctx.id);
|
|
96
|
+
const sql = `UPDATE ${table} SET ${setSql} WHERE ${where.sql}`;
|
|
97
|
+
await ctx.query(sql, [...cols.map((c) => ctx.columns[c]), ...where.params]);
|
|
98
|
+
ctx.resultId = ctx.id;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// delete
|
|
102
|
+
if (ctx.id === undefined)
|
|
103
|
+
throw new errors_js_1.RegistrationError('delete requires an id');
|
|
104
|
+
const where = whereById(ctx, ctx.id);
|
|
105
|
+
await ctx.query(`DELETE FROM ${table} WHERE ${where.sql}`, where.params);
|
|
106
|
+
ctx.resultId = ctx.id;
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write.js","sourceRoot":"","sources":["../../../src/pipe/stages/write.ts"],"names":[],"mappings":"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"}
|
package/dist/ports.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ports the engine depends on — injected adapters, so the engine's pipe logic
|
|
3
|
+
* is pure over its collaborators (ADR-0003 port/adapter grain). Concrete adapters
|
|
4
|
+
* (the sealed mariadb pool, the decision-authority binding, the audit sink) are
|
|
5
|
+
* wired in the engine's Nest module.
|
|
6
|
+
*
|
|
7
|
+
* Core types are TYPE-ONLY imports (erased at build — no runtime require of the
|
|
8
|
+
* ESM-only core).
|
|
9
|
+
*/
|
|
10
|
+
import type { Principal, Action, Resource, DecisionContext, Decision, AuditEvent } from '@astram/core';
|
|
11
|
+
/** Raw parameterized query — the ONE DB primitive the pipe uses (sealed pool). */
|
|
12
|
+
export type QueryFn = (sql: string, params?: readonly unknown[]) => Promise<unknown>;
|
|
13
|
+
/** Opens a transaction and runs `body` with a bound {@link QueryFn}; commit/rollback. */
|
|
14
|
+
export interface UnitOfWork {
|
|
15
|
+
run<T>(body: (query: QueryFn) => Promise<T>): Promise<T>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The authorization port — the permission-gate stage calls this. Bound to the
|
|
19
|
+
* framework's single {@link import('@astram/core').DecisionAuthority} (guard's
|
|
20
|
+
* `SHIELD_DECISION_AUTHORITY`). The engine NEVER makes its own allow/deny logic;
|
|
21
|
+
* it asks `decide()` and obeys.
|
|
22
|
+
*/
|
|
23
|
+
export interface DecisionPort {
|
|
24
|
+
decide(principal: Principal, action: Action, resource: Resource, context: DecisionContext): Promise<Decision>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The audit port — the audit stage calls `record()` (append-only). It receives the
|
|
28
|
+
* pipe's bound {@link QueryFn} so the audit row commits IN THE SAME TRANSACTION as
|
|
29
|
+
* the data write: there is never data without its audit row, and never an audit row
|
|
30
|
+
* for a rolled-back write. (This differs from core's connection-less `AuditSink`
|
|
31
|
+
* seam by design — atomicity with the record write is a paved-road invariant.)
|
|
32
|
+
*/
|
|
33
|
+
export interface AuditPort {
|
|
34
|
+
record(event: AuditEvent, query: QueryFn): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
export type { Principal, Action, Resource, DecisionContext, Decision, AuditEvent };
|
|
37
|
+
//# sourceMappingURL=ports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,SAAS,EACT,MAAM,EACN,QAAQ,EACR,eAAe,EACf,QAAQ,EACR,UAAU,EACX,MAAM,cAAc,CAAC;AAEtB,kFAAkF;AAClF,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAErF,yFAAyF;AACzF,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC1D;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,CACJ,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,QAAQ,CAAC,CAAC;CACtB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1D;AAED,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC"}
|
package/dist/ports.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The ports the engine depends on — injected adapters, so the engine's pipe logic
|
|
4
|
+
* is pure over its collaborators (ADR-0003 port/adapter grain). Concrete adapters
|
|
5
|
+
* (the sealed mariadb pool, the decision-authority binding, the audit sink) are
|
|
6
|
+
* wired in the engine's Nest module.
|
|
7
|
+
*
|
|
8
|
+
* Core types are TYPE-ONLY imports (erased at build — no runtime require of the
|
|
9
|
+
* ESM-only core).
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.js","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The table registry — the in-memory source of truth for every registered table's
|
|
3
|
+
* shape (the "SchemaOverview" of the Directus pattern). Features register a
|
|
4
|
+
* {@link ObjectType} at boot (code-registered) — the engine resolves by
|
|
5
|
+
* `typeKey` on every operation. One code path serves all tables because the schema
|
|
6
|
+
* is data.
|
|
7
|
+
*
|
|
8
|
+
* Runtime-registered (no-code) tables will register through the SAME registry later
|
|
9
|
+
* (the no-code phase); the pipe does not distinguish how a registration arrived.
|
|
10
|
+
*/
|
|
11
|
+
import type { FieldSpec, PkStrategy, ObjectType } from './types.js';
|
|
12
|
+
export declare class TableRegistry {
|
|
13
|
+
private readonly byType;
|
|
14
|
+
/** Register a table. Throws on a duplicate typeKey or an invalid registration. */
|
|
15
|
+
register(reg: ObjectType): void;
|
|
16
|
+
/** Resolve a registration, or throw a RegistrationError if unknown. */
|
|
17
|
+
require(typeKey: string): ObjectType;
|
|
18
|
+
has(typeKey: string): boolean;
|
|
19
|
+
all(): readonly ObjectType[];
|
|
20
|
+
}
|
|
21
|
+
/** The physical column name for a field (explicit `column` or the field key). */
|
|
22
|
+
export declare function columnOf(field: FieldSpec): string;
|
|
23
|
+
/** The single uuid PK column name, or throw for a composite PK. */
|
|
24
|
+
export declare function uuidPkColumn(pk: PkStrategy): string;
|
|
25
|
+
/**
|
|
26
|
+
* Whether the type declares person-ownership (`access.owned === true`). An owned type
|
|
27
|
+
* has exactly one `trusted:'actorId'` field (enforced by chokepoint 1) — its column is
|
|
28
|
+
* the owner column. Only owned types pre-fetch their owner before the gate.
|
|
29
|
+
*/
|
|
30
|
+
export declare function isOwned(reg: ObjectType): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* The physical column of the `trusted:'actorId'` (owner) field, or `null` if the type
|
|
33
|
+
* has none. For an owned type chokepoint 1 guarantees exactly one exists.
|
|
34
|
+
*/
|
|
35
|
+
export declare function ownerColumn(reg: ObjectType): string | null;
|
|
36
|
+
/** The physical column of the `trusted:'tenantId'` field, or `null` if the type has none. */
|
|
37
|
+
export declare function tenantColumnOf(reg: ObjectType): string | null;
|
|
38
|
+
/** The physical column of the field bearing `role`, or null if unclaimed. */
|
|
39
|
+
export declare function columnForRole(reg: ObjectType, role: FieldSpec['role']): string | null;
|
|
40
|
+
/** Boot-enforce that exactly-one field claims `role`; throw if none. */
|
|
41
|
+
export declare function requireRole(reg: ObjectType, role: NonNullable<FieldSpec['role']>): void;
|
|
42
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEpE,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;IAExD,kFAAkF;IAClF,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI;IAQ/B,uEAAuE;IACvE,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU;IAWpC,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAI7B,GAAG,IAAI,SAAS,UAAU,EAAE;CAG7B;AAED,iFAAiF;AACjF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAEjD;AAED,mEAAmE;AACnE,wBAAgB,YAAY,CAAC,EAAE,EAAE,UAAU,GAAG,MAAM,CAKnD;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAEhD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAG1D;AAED,6FAA6F;AAC7F,wBAAgB,cAAc,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAG7D;AAED,6EAA6E;AAC7E,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,IAAI,CAGrF;AAED,wEAAwE;AACxE,wBAAgB,WAAW,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,CAMvF"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The table registry — the in-memory source of truth for every registered table's
|
|
4
|
+
* shape (the "SchemaOverview" of the Directus pattern). Features register a
|
|
5
|
+
* {@link ObjectType} at boot (code-registered) — the engine resolves by
|
|
6
|
+
* `typeKey` on every operation. One code path serves all tables because the schema
|
|
7
|
+
* is data.
|
|
8
|
+
*
|
|
9
|
+
* Runtime-registered (no-code) tables will register through the SAME registry later
|
|
10
|
+
* (the no-code phase); the pipe does not distinguish how a registration arrived.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.TableRegistry = void 0;
|
|
14
|
+
exports.columnOf = columnOf;
|
|
15
|
+
exports.uuidPkColumn = uuidPkColumn;
|
|
16
|
+
exports.isOwned = isOwned;
|
|
17
|
+
exports.ownerColumn = ownerColumn;
|
|
18
|
+
exports.tenantColumnOf = tenantColumnOf;
|
|
19
|
+
exports.columnForRole = columnForRole;
|
|
20
|
+
exports.requireRole = requireRole;
|
|
21
|
+
const errors_js_1 = require("./errors.js");
|
|
22
|
+
class TableRegistry {
|
|
23
|
+
byType = new Map();
|
|
24
|
+
/** Register a table. Throws on a duplicate typeKey or an invalid registration. */
|
|
25
|
+
register(reg) {
|
|
26
|
+
validate(reg);
|
|
27
|
+
if (this.byType.has(reg.typeKey)) {
|
|
28
|
+
throw new errors_js_1.RegistrationError(`duplicate table registration for typeKey '${reg.typeKey}'`);
|
|
29
|
+
}
|
|
30
|
+
this.byType.set(reg.typeKey, reg);
|
|
31
|
+
}
|
|
32
|
+
/** Resolve a registration, or throw a RegistrationError if unknown. */
|
|
33
|
+
require(typeKey) {
|
|
34
|
+
const reg = this.byType.get(typeKey);
|
|
35
|
+
if (!reg) {
|
|
36
|
+
throw new errors_js_1.RegistrationError(`no table registered for typeKey '${typeKey}' — a feature must register it ` +
|
|
37
|
+
`before the engine can operate on it.`);
|
|
38
|
+
}
|
|
39
|
+
return reg;
|
|
40
|
+
}
|
|
41
|
+
has(typeKey) {
|
|
42
|
+
return this.byType.has(typeKey);
|
|
43
|
+
}
|
|
44
|
+
all() {
|
|
45
|
+
return [...this.byType.values()];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.TableRegistry = TableRegistry;
|
|
49
|
+
/** The physical column name for a field (explicit `column` or the field key). */
|
|
50
|
+
function columnOf(field) {
|
|
51
|
+
return field.column ?? field.key;
|
|
52
|
+
}
|
|
53
|
+
/** The single uuid PK column name, or throw for a composite PK. */
|
|
54
|
+
function uuidPkColumn(pk) {
|
|
55
|
+
if (pk.kind !== 'uuid') {
|
|
56
|
+
throw new errors_js_1.RegistrationError('expected a uuid primary key');
|
|
57
|
+
}
|
|
58
|
+
return pk.column;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Whether the type declares person-ownership (`access.owned === true`). An owned type
|
|
62
|
+
* has exactly one `trusted:'actorId'` field (enforced by chokepoint 1) — its column is
|
|
63
|
+
* the owner column. Only owned types pre-fetch their owner before the gate.
|
|
64
|
+
*/
|
|
65
|
+
function isOwned(reg) {
|
|
66
|
+
return reg.access.owned === true;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The physical column of the `trusted:'actorId'` (owner) field, or `null` if the type
|
|
70
|
+
* has none. For an owned type chokepoint 1 guarantees exactly one exists.
|
|
71
|
+
*/
|
|
72
|
+
function ownerColumn(reg) {
|
|
73
|
+
const f = reg.fields.find((x) => x.trusted === 'actorId');
|
|
74
|
+
return f ? columnOf(f) : null;
|
|
75
|
+
}
|
|
76
|
+
/** The physical column of the `trusted:'tenantId'` field, or `null` if the type has none. */
|
|
77
|
+
function tenantColumnOf(reg) {
|
|
78
|
+
const f = reg.fields.find((x) => x.trusted === 'tenantId');
|
|
79
|
+
return f ? columnOf(f) : null;
|
|
80
|
+
}
|
|
81
|
+
/** The physical column of the field bearing `role`, or null if unclaimed. */
|
|
82
|
+
function columnForRole(reg, role) {
|
|
83
|
+
const f = reg.fields.find((x) => x.role === role);
|
|
84
|
+
return f?.column ?? f?.key ?? null;
|
|
85
|
+
}
|
|
86
|
+
/** Boot-enforce that exactly-one field claims `role`; throw if none. */
|
|
87
|
+
function requireRole(reg, role) {
|
|
88
|
+
if (!reg.fields.some((f) => f.role === role)) {
|
|
89
|
+
throw new errors_js_1.RegistrationError(`registration '${reg.typeKey}': requires a field with role '${role}'.`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function validate(reg) {
|
|
93
|
+
if (!reg.typeKey)
|
|
94
|
+
throw new errors_js_1.RegistrationError('registration.typeKey is required');
|
|
95
|
+
if (!reg.table)
|
|
96
|
+
throw new errors_js_1.RegistrationError(`registration '${reg.typeKey}': table is required`);
|
|
97
|
+
if (reg.fields.length === 0) {
|
|
98
|
+
throw new errors_js_1.RegistrationError(`registration '${reg.typeKey}': at least one field is required`);
|
|
99
|
+
}
|
|
100
|
+
const keys = new Set();
|
|
101
|
+
for (const f of reg.fields) {
|
|
102
|
+
if (keys.has(f.key)) {
|
|
103
|
+
throw new errors_js_1.RegistrationError(`registration '${reg.typeKey}': duplicate field key '${f.key}'`);
|
|
104
|
+
}
|
|
105
|
+
keys.add(f.key);
|
|
106
|
+
}
|
|
107
|
+
if (reg.primaryKey.kind === 'composite' && reg.primaryKey.columns.length < 2) {
|
|
108
|
+
throw new errors_js_1.RegistrationError(`registration '${reg.typeKey}': composite primary key needs >= 2 columns`);
|
|
109
|
+
}
|
|
110
|
+
validateAccess(reg);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Chokepoint 1 (§4.1 of the framework-owns-data-layer design). Every registered
|
|
114
|
+
* object type MUST declare its access explicitly — this closes tenant-leak-by-omission
|
|
115
|
+
* and ungoverned-record holes at BOOT (dev/CI), never as a silent prod hole:
|
|
116
|
+
*
|
|
117
|
+
* 1. `access` is REQUIRED — absent → boot fails (no silent default).
|
|
118
|
+
* 2. `system` is MUTUALLY EXCLUSIVE with `tenantScoped`/`owned` — a contradictory
|
|
119
|
+
* combo → boot fails.
|
|
120
|
+
* 3. `owned:true` REQUIRES a `trusted:'actorId'` owner column — else boot fails.
|
|
121
|
+
* 4. `tenantScoped:true` REQUIRES a `trusted:'tenantId'` column — else boot fails.
|
|
122
|
+
*
|
|
123
|
+
* NOTE: a `system` type MAY still carry a `trusted:'actorId'` field (a `created_by`
|
|
124
|
+
* audit column, e.g. company_membership/membership_posting) — `system` only forbids
|
|
125
|
+
* being DECLARED owned/tenantScoped, NOT having such a column. (No record-rule check
|
|
126
|
+
* here — that is Phase 2's completeness sweep.)
|
|
127
|
+
*/
|
|
128
|
+
function validateAccess(reg) {
|
|
129
|
+
const access = reg.access;
|
|
130
|
+
if (!access) {
|
|
131
|
+
throw new errors_js_1.RegistrationError(`registration '${reg.typeKey}': an access declaration is required ` +
|
|
132
|
+
`(access: { plane, tenantScoped?, owned?, system? }) — no silent default.`);
|
|
133
|
+
}
|
|
134
|
+
// SECURITY (root-cap axis): `plane` is REQUIRED + explicit. Omitting it would default a
|
|
135
|
+
// new administrative type to `data` and wrongly deny root, with NO error — the exact
|
|
136
|
+
// posture-by-omission hole `access` itself already forbids. Only the two literal
|
|
137
|
+
// CapabilityPlane values are accepted; absent/other fails loud at boot. (`decide()`
|
|
138
|
+
// still keeps `data` as its runtime fail-safe floor; this enforces the CHOICE at boot.)
|
|
139
|
+
if (access.plane !== 'control' && access.plane !== 'data') {
|
|
140
|
+
throw new errors_js_1.RegistrationError(`registration '${reg.typeKey}': access.plane is required and must be ` +
|
|
141
|
+
`'control' or 'data', got ${access.plane === undefined ? 'undefined' : `'${String(access.plane)}'`}. ` +
|
|
142
|
+
`control = who-may-act administration (root allowed); data = business content ` +
|
|
143
|
+
`(root denied). No silent default.`);
|
|
144
|
+
}
|
|
145
|
+
// SECURITY: gate on real booleans, NOT `=== true`. A truthy-non-boolean flag
|
|
146
|
+
// (`owned: 1`, `system: 'yes'`) is a MALFORMED declaration — rejecting it here closes
|
|
147
|
+
// the bypass where such a value would (a) escape the mutual-exclusion / column-required
|
|
148
|
+
// checks below (which key off `=== true`) yet (b) mark the type "governed", producing a
|
|
149
|
+
// type with no owner/tenant column and thus ZERO isolation. Only `true`/`false`/absent
|
|
150
|
+
// are valid; anything else fails loud at boot.
|
|
151
|
+
const flag = (name) => {
|
|
152
|
+
const v = access[name];
|
|
153
|
+
if (v !== undefined && typeof v !== 'boolean') {
|
|
154
|
+
throw new errors_js_1.RegistrationError(`registration '${reg.typeKey}': access.${name} must be a boolean, got ` +
|
|
155
|
+
`${typeof v} (${String(v)}).`);
|
|
156
|
+
}
|
|
157
|
+
return v === true;
|
|
158
|
+
};
|
|
159
|
+
const tenantScoped = flag('tenantScoped');
|
|
160
|
+
const owned = flag('owned');
|
|
161
|
+
const system = flag('system');
|
|
162
|
+
// No silent "global by empty object": {} / all-false is "declared nothing" — the same
|
|
163
|
+
// hole as an absent declaration. At least ONE posture must be an explicit choice. Truly
|
|
164
|
+
// global (no tenant, no owner) is expressed with `system: true` (grant-governed) — a
|
|
165
|
+
// DELIBERATE decision, never the accidental default of an empty/all-false object.
|
|
166
|
+
if (!tenantScoped && !owned && !system) {
|
|
167
|
+
throw new errors_js_1.RegistrationError(`registration '${reg.typeKey}': access declares no posture ` +
|
|
168
|
+
`(need at least one of tenantScoped/owned/system=true). A global, ungoverned ` +
|
|
169
|
+
`type must be declared system:true — 'global' is a decision, not an empty object.`);
|
|
170
|
+
}
|
|
171
|
+
if (system && (tenantScoped || owned)) {
|
|
172
|
+
throw new errors_js_1.RegistrationError(`registration '${reg.typeKey}': access.system is mutually exclusive with ` +
|
|
173
|
+
`tenantScoped/owned.`);
|
|
174
|
+
}
|
|
175
|
+
const hasTrusted = (source) => reg.fields.some((f) => f.trusted === source);
|
|
176
|
+
if (owned && !hasTrusted('actorId')) {
|
|
177
|
+
throw new errors_js_1.RegistrationError(`registration '${reg.typeKey}': access.owned requires a field with ` +
|
|
178
|
+
`trusted:'actorId' (the owner column).`);
|
|
179
|
+
}
|
|
180
|
+
if (tenantScoped && !hasTrusted('tenantId')) {
|
|
181
|
+
throw new errors_js_1.RegistrationError(`registration '${reg.typeKey}': access.tenantScoped requires a field with ` +
|
|
182
|
+
`trusted:'tenantId' (the tenant column).`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types for @astram/engine — the paved road.
|
|
3
|
+
*
|
|
4
|
+
* A feature contributes exactly ONE thing: a {@link ObjectType} (metadata =
|
|
5
|
+
* the contract). From it the engine derives the physical table shape, validation,
|
|
6
|
+
* the `decide()` resource identity, and the audit event. The feature then calls the
|
|
7
|
+
* {@link RecordEngine} facade for all CRUD — it never touches a pool/query-runner.
|
|
8
|
+
*
|
|
9
|
+
* Core types (`Principal`, `Action`, `Resource`) are imported TYPE-ONLY from
|
|
10
|
+
* `@astram/core` (erased at build; the CJS↔ESM lesson — no runtime require of the
|
|
11
|
+
* ESM-only core). Re-exported from the engine barrel so a consumer has ONE import
|
|
12
|
+
* surface.
|
|
13
|
+
*/
|
|
14
|
+
import type { Principal, CapabilityPlane } from '@astram/core';
|
|
15
|
+
/** Supported field value kinds the pipe can validate + persist (Phase 1 set). */
|
|
16
|
+
export type FieldType = 'string' | 'number' | 'boolean' | 'uuid' | 'timestamp' | 'json';
|
|
17
|
+
/** One field on a registered table (core column). */
|
|
18
|
+
export interface FieldSpec {
|
|
19
|
+
/** Stable field key (business-meaningful; maps to a physical column). */
|
|
20
|
+
readonly key: string;
|
|
21
|
+
/** Physical column name (defaults to `key` if omitted). */
|
|
22
|
+
readonly column?: string;
|
|
23
|
+
readonly type: FieldType;
|
|
24
|
+
/** Required on create (validate stage rejects a missing value). */
|
|
25
|
+
readonly required?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* A server-trusted attribute: on write the engine OVERWRITES any client-supplied
|
|
28
|
+
* value with a value derived from the actor/context (anti-forgery). The resolver
|
|
29
|
+
* key names which trusted source fills it (e.g. `tenantId`, `actorId`). Client
|
|
30
|
+
* input for this field is IGNORED.
|
|
31
|
+
*/
|
|
32
|
+
readonly trusted?: TrustedSource;
|
|
33
|
+
/**
|
|
34
|
+
* An identity-role marker (see {@link IdentityRole}) — lets the framework find a
|
|
35
|
+
* field by ROLE regardless of the app's chosen column name. Orthogonal to
|
|
36
|
+
* `trusted`: `trusted` marks WRITE-CONTEXT roles (tenantId/actorId, enforced by
|
|
37
|
+
* the derive-write-context stage); `role` marks IDENTITY roles (username,
|
|
38
|
+
* password, etc.) and carries no write-time behavior of its own.
|
|
39
|
+
*/
|
|
40
|
+
readonly role?: IdentityRole;
|
|
41
|
+
}
|
|
42
|
+
/** Server-trusted write-context sources (derive-write-context stage). */
|
|
43
|
+
export type TrustedSource = 'tenantId' | 'actorId';
|
|
44
|
+
/** Identity/field ROLE vocabulary — marks WHAT a field IS, so the framework finds it
|
|
45
|
+
* by role regardless of the app's chosen column name. `trusted` (tenantId/actorId) are
|
|
46
|
+
* the WRITE-CONTEXT roles and remain separate; these are the IDENTITY roles. */
|
|
47
|
+
export type IdentityRole = 'primaryKey' | 'username' | 'password' | 'status' | 'active' | 'effectiveFrom' | 'effectiveUntil';
|
|
48
|
+
/**
|
|
49
|
+
* The REQUIRED access declaration on every {@link ObjectType} — two orthogonal flags
|
|
50
|
+
* plus a `system` escape. Replaces "tenant/owner by omission" (a forgotten column =
|
|
51
|
+
* a silent cross-tenant or ungoverned hole) with "by explicit, boot-enforced
|
|
52
|
+
* declaration". Enforced at registration (chokepoint 1 in {@link TableRegistry}):
|
|
53
|
+
*
|
|
54
|
+
* - `owned: true` REQUIRES exactly-one field with `trusted:'actorId'` (the owner
|
|
55
|
+
* column) — else registration throws.
|
|
56
|
+
* - `tenantScoped: true` REQUIRES exactly-one field with `trusted:'tenantId'` — else
|
|
57
|
+
* registration throws.
|
|
58
|
+
* - `system: true` is MUTUALLY EXCLUSIVE with `tenantScoped`/`owned`. A `system`
|
|
59
|
+
* type may still carry a `trusted:'actorId'` field (e.g. a `created_by` audit
|
|
60
|
+
* column) — `system` only forbids being DECLARED owned/tenantScoped, not having
|
|
61
|
+
* such a column.
|
|
62
|
+
*
|
|
63
|
+
* `tenantScoped` and `owned` are independent (a normal data record is BOTH). An
|
|
64
|
+
* absent `access` fails registration (no silent default).
|
|
65
|
+
*
|
|
66
|
+
* `plane` is a SECOND, ORTHOGONAL axis (do not confuse it with the ownership flags
|
|
67
|
+
* above). The ownership flags answer "who reaches an individual ROW?" (gate 6, the
|
|
68
|
+
* record owner-baseline). `plane` answers "may the capped ROOT super-user perform this?"
|
|
69
|
+
* (gate 5, the root cap). They do NOT correlate: a `system` type may be `control`
|
|
70
|
+
* (`role` — administration, root allowed) OR `data` (`company` — business content, root
|
|
71
|
+
* denied). `plane` is REQUIRED and boot-validated for the same reason `access` itself is
|
|
72
|
+
* — a plane is a security posture, and posture-by-omission is a silent hole (a forgotten
|
|
73
|
+
* `plane` would default a new administrative type to `data` and wrongly deny root, with
|
|
74
|
+
* no error). It is DECLARED per type (uniform across the type's actions); the framework
|
|
75
|
+
* defines only the `CapabilityPlane` vocabulary — the consumer app owns the assignment.
|
|
76
|
+
*/
|
|
77
|
+
export interface AccessDeclaration {
|
|
78
|
+
/** true → tenant-isolated; a `trusted:'tenantId'` field is REQUIRED. */
|
|
79
|
+
readonly tenantScoped?: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* true → records are person-owned; a `trusted:'actorId'` (owner) field is REQUIRED.
|
|
82
|
+
* Owner = the CREATING principal (DAC).
|
|
83
|
+
*/
|
|
84
|
+
readonly owned?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* true → identity/infrastructure type (company/user/role): neither person-owned nor
|
|
87
|
+
* simply tenant-scoped; grant-governed, framework-managed. MUTUALLY EXCLUSIVE with
|
|
88
|
+
* `tenantScoped`/`owned`.
|
|
89
|
+
*/
|
|
90
|
+
readonly system?: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* REQUIRED capability plane (the root-cap axis; see the interface note above).
|
|
93
|
+
* `'control'` → identity/access administration; the capped root MAY perform it without
|
|
94
|
+
* a grant. `'data'` → tenant business content; root is DENIED by construction
|
|
95
|
+
* (`root_data_plane_denied`), even fully granted. Absent → registration fails
|
|
96
|
+
* (chokepoint 1) — no silent `data` default at the declaration layer. (`decide()`
|
|
97
|
+
* keeps `data` as its RUNTIME fail-safe floor, but boot requires an explicit choice.)
|
|
98
|
+
*/
|
|
99
|
+
readonly plane: CapabilityPlane;
|
|
100
|
+
}
|
|
101
|
+
/** Primary-key strategy for a registered table. */
|
|
102
|
+
export type PkStrategy = {
|
|
103
|
+
readonly kind: 'uuid';
|
|
104
|
+
readonly column: string;
|
|
105
|
+
} | {
|
|
106
|
+
readonly kind: 'composite';
|
|
107
|
+
readonly columns: readonly string[];
|
|
108
|
+
};
|
|
109
|
+
/** A composite-key junction table this object owns/syncs. */
|
|
110
|
+
export interface JunctionSpec {
|
|
111
|
+
/** Stable name (business key, e.g. `user_roles`). */
|
|
112
|
+
readonly name: string;
|
|
113
|
+
/** Physical junction table. */
|
|
114
|
+
readonly table: string;
|
|
115
|
+
/** Column holding THIS record's id (the local side). */
|
|
116
|
+
readonly localColumn: string;
|
|
117
|
+
/** Column holding the referenced id (the foreign side). */
|
|
118
|
+
readonly foreignColumn: string;
|
|
119
|
+
/** Optional extra payload columns (e.g. `is_active`) with server-set defaults. */
|
|
120
|
+
readonly payloadDefaults?: Readonly<Record<string, unknown>>;
|
|
121
|
+
}
|
|
122
|
+
/** A feature's declaration — the complete contract for one table on the pipe. */
|
|
123
|
+
export interface ObjectType {
|
|
124
|
+
/** Stable object-type key — the `decide()` resource typeKey + audit type key. */
|
|
125
|
+
readonly typeKey: string;
|
|
126
|
+
/** Physical table name. */
|
|
127
|
+
readonly table: string;
|
|
128
|
+
/** Schema/database the table lives in (defaults to the engine's app schema). */
|
|
129
|
+
readonly schema?: string;
|
|
130
|
+
readonly primaryKey: PkStrategy;
|
|
131
|
+
readonly fields: readonly FieldSpec[];
|
|
132
|
+
readonly junctions?: readonly JunctionSpec[];
|
|
133
|
+
/** Opt into the runtime extension-field sidecar for this table. */
|
|
134
|
+
readonly extensible?: boolean;
|
|
135
|
+
/**
|
|
136
|
+
* REQUIRED access declaration (tenantScoped/owned/system). Absent → registration
|
|
137
|
+
* fails (chokepoint 1). See {@link AccessDeclaration}.
|
|
138
|
+
*/
|
|
139
|
+
readonly access: AccessDeclaration;
|
|
140
|
+
}
|
|
141
|
+
/** Input to a create/update — field keys → values (+ optional junction refs). */
|
|
142
|
+
export interface RecordInput {
|
|
143
|
+
/** Core + extension field values, keyed by field key. */
|
|
144
|
+
readonly fields: Readonly<Record<string, unknown>>;
|
|
145
|
+
/**
|
|
146
|
+
* Multi-reference junction values, keyed by junction name → array of foreign ids
|
|
147
|
+
* (DELETE+INSERT synced by the junction-sync stage).
|
|
148
|
+
*/
|
|
149
|
+
readonly junctions?: Readonly<Record<string, readonly string[]>>;
|
|
150
|
+
}
|
|
151
|
+
/** A record id — a uuid string, or a composite key map for junction-style tables. */
|
|
152
|
+
export type RecordId = string | Readonly<Record<string, string>>;
|
|
153
|
+
/** A read-projected record (UUID→label resolution + unauthorized columns hidden). */
|
|
154
|
+
export interface RecordView {
|
|
155
|
+
readonly id: RecordId;
|
|
156
|
+
readonly fields: Readonly<Record<string, unknown>>;
|
|
157
|
+
readonly junctions?: Readonly<Record<string, readonly string[]>>;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Free-text search across one or more registered field keys — a single search box
|
|
161
|
+
* that ORs a `LIKE '%term%'` over each named field. Field keys are whitelisted via
|
|
162
|
+
* the registration (unknown → RegistrationError); the term is bound as a `?` param
|
|
163
|
+
* (never interpolated), and LIKE wildcards in the term are escaped so a literal `%`
|
|
164
|
+
* or `_` the user types matches literally instead of acting as a wildcard.
|
|
165
|
+
*/
|
|
166
|
+
export interface SearchQuery {
|
|
167
|
+
/** Registered field keys to search (each resolved to its column + whitelisted). */
|
|
168
|
+
readonly fields: readonly string[];
|
|
169
|
+
/** The search term — bound as `%term%` per field (wildcards escaped). */
|
|
170
|
+
readonly term: string;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* A list query — tenant-scoped filtering + simple paging, plus optional search
|
|
174
|
+
* (LIKE across fields) and sort (ORDER BY). All field references are registered
|
|
175
|
+
* field KEYS, resolved+whitelisted to columns; all user values are bound `?` params.
|
|
176
|
+
*/
|
|
177
|
+
export interface ListQuery {
|
|
178
|
+
readonly limit?: number;
|
|
179
|
+
readonly offset?: number;
|
|
180
|
+
/** Equality filters on core field keys. */
|
|
181
|
+
readonly where?: Readonly<Record<string, unknown>>;
|
|
182
|
+
/** Free-text LIKE search across named field keys (ANDed with `where` + tenant). */
|
|
183
|
+
readonly search?: SearchQuery;
|
|
184
|
+
/** A registered field KEY to ORDER BY (unknown → RegistrationError). */
|
|
185
|
+
readonly sortBy?: string;
|
|
186
|
+
/** Sort direction; anything other than 'desc' is treated as ascending. */
|
|
187
|
+
readonly sortOrder?: 'asc' | 'desc';
|
|
188
|
+
}
|
|
189
|
+
/** A page of read-projected records. */
|
|
190
|
+
export interface RecordPage {
|
|
191
|
+
readonly rows: readonly RecordView[];
|
|
192
|
+
readonly total: number;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* The ONLY data-access surface a feature is given. Every method flows through the
|
|
196
|
+
* full pipe (permission gate + audit are mandatory). No pool, query runner, or
|
|
197
|
+
* repository is ever handed to feature scope — that is the physical wall.
|
|
198
|
+
*/
|
|
199
|
+
export interface RecordEngine {
|
|
200
|
+
create(typeKey: string, input: RecordInput, actor: Principal): Promise<RecordId>;
|
|
201
|
+
update(typeKey: string, id: RecordId, input: RecordInput, actor: Principal): Promise<void>;
|
|
202
|
+
delete(typeKey: string, id: RecordId, actor: Principal): Promise<void>;
|
|
203
|
+
get(typeKey: string, id: RecordId, actor: Principal): Promise<RecordView | null>;
|
|
204
|
+
list(typeKey: string, query: ListQuery, actor: Principal): Promise<RecordPage>;
|
|
205
|
+
}
|
|
206
|
+
export type { Principal };
|
|
207
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|