@astram/engine 0.5.3 → 0.7.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/dist/adapters/mariadb-unit-of-work.d.ts +8 -0
- package/dist/adapters/mariadb-unit-of-work.d.ts.map +1 -1
- package/dist/adapters/mariadb-unit-of-work.js +1 -0
- package/dist/decorators/object-type.decorator.d.ts +10 -0
- package/dist/decorators/object-type.decorator.d.ts.map +1 -1
- package/dist/decorators/object-type.decorator.js +5 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/pipe/context.d.ts +8 -0
- package/dist/pipe/context.d.ts.map +1 -1
- package/dist/pipe/filter-sql.d.ts +66 -0
- package/dist/pipe/filter-sql.d.ts.map +1 -0
- package/dist/pipe/filter-sql.js +266 -0
- package/dist/pipe/record-engine.d.ts +34 -1
- package/dist/pipe/record-engine.d.ts.map +1 -1
- package/dist/pipe/record-engine.js +256 -87
- package/dist/pipe/records-service.d.ts +19 -1
- package/dist/pipe/records-service.d.ts.map +1 -1
- package/dist/pipe/records-service.js +32 -0
- package/dist/pipe/stages/audit.d.ts.map +1 -1
- package/dist/pipe/stages/audit.js +8 -1
- package/dist/pipe/stages/fetch-record.d.ts +11 -1
- package/dist/pipe/stages/fetch-record.d.ts.map +1 -1
- package/dist/pipe/stages/fetch-record.js +18 -9
- package/dist/pipe/stages/permission-gate.d.ts +8 -0
- package/dist/pipe/stages/permission-gate.d.ts.map +1 -1
- package/dist/pipe/stages/permission-gate.js +15 -9
- package/dist/pipe/stages/validate.d.ts +6 -0
- package/dist/pipe/stages/validate.d.ts.map +1 -1
- package/dist/pipe/stages/validate.js +22 -8
- package/dist/registry.d.ts +13 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +66 -0
- package/dist/types.d.ts +112 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -34,6 +34,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
34
34
|
exports.recordFacts = recordFacts;
|
|
35
35
|
exports.decisionAttributes = decisionAttributes;
|
|
36
36
|
exports.recordNotReachable = recordNotReachable;
|
|
37
|
+
exports.fetchRecordFacts = fetchRecordFacts;
|
|
37
38
|
exports.fetchRecordStage = fetchRecordStage;
|
|
38
39
|
const errors_js_1 = require("../../errors.js");
|
|
39
40
|
const registry_js_1 = require("../../registry.js");
|
|
@@ -76,6 +77,20 @@ function decisionAttributes(reg, facts) {
|
|
|
76
77
|
function recordNotReachable(reg) {
|
|
77
78
|
return new errors_js_1.AuthorizationDeniedError('no_record_permit', `no reachable '${reg.typeKey}' record for this operation`);
|
|
78
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Read one record's decision facts through `whereById` (pk + actor tenant + ADR-0030 write
|
|
82
|
+
* scope), `LIMIT 1`, with `FOR UPDATE` when `lock` is set. No row (missing ≡ foreign) ⇒
|
|
83
|
+
* `recordNotReachable`. Shared by `fetchRecordStage` (lock) and `RecordEngine.authorize`
|
|
84
|
+
* (lock only inside a transaction). The row itself is never returned, only its facts.
|
|
85
|
+
*/
|
|
86
|
+
async function fetchRecordFacts(reg, actor, id, query, opts) {
|
|
87
|
+
const where = (0, where_by_id_js_1.whereById)(reg, actor, id);
|
|
88
|
+
const rows = (await query(`SELECT * FROM ${(0, where_by_id_js_1.qualifiedTable)(reg)} WHERE ${where.sql} LIMIT 1${opts.lock ? ' FOR UPDATE' : ''}`, where.params));
|
|
89
|
+
const first = Array.isArray(rows) ? rows[0] : undefined;
|
|
90
|
+
if (first === undefined || first === null)
|
|
91
|
+
throw recordNotReachable(reg);
|
|
92
|
+
return recordFacts(reg, first);
|
|
93
|
+
}
|
|
79
94
|
function fetchRecordStage() {
|
|
80
95
|
return {
|
|
81
96
|
name: 'fetch-record',
|
|
@@ -84,15 +99,9 @@ function fetchRecordStage() {
|
|
|
84
99
|
return;
|
|
85
100
|
if (ctx.id === undefined)
|
|
86
101
|
throw new errors_js_1.RegistrationError(`${ctx.op} requires an id`);
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const first = Array.isArray(rows)
|
|
91
|
-
? rows[0]
|
|
92
|
-
: undefined;
|
|
93
|
-
if (first === undefined || first === null)
|
|
94
|
-
throw recordNotReachable(reg);
|
|
95
|
-
const facts = recordFacts(reg, first);
|
|
102
|
+
const facts = await fetchRecordFacts(ctx.registration, ctx.actor, ctx.id, ctx.query, {
|
|
103
|
+
lock: true,
|
|
104
|
+
});
|
|
96
105
|
ctx.existing = facts.fields;
|
|
97
106
|
if (facts.ownerId !== undefined)
|
|
98
107
|
ctx.ownerId = facts.ownerId;
|
|
@@ -6,8 +6,16 @@
|
|
|
6
6
|
* own allow/deny logic; it asks `decide()` and obeys.
|
|
7
7
|
*/
|
|
8
8
|
import type { DecisionPort } from '../../ports.js';
|
|
9
|
+
import type { Action, Resource, DecisionContext } from '../../ports.js';
|
|
10
|
+
import type { Principal } from '../../types.js';
|
|
9
11
|
import type { Stage } from '../context.js';
|
|
10
12
|
export declare function permissionGateStage(decision: DecisionPort): Stage;
|
|
13
|
+
/**
|
|
14
|
+
* Ask `decide()` once; throw on deny (AuthorizationDeniedError) or a throw (unavailable).
|
|
15
|
+
* Shared by the write stages and `RecordEngine.authorize` (custom object actions, D3); `label`
|
|
16
|
+
* names the operation in the `EngineUnavailableError` message only.
|
|
17
|
+
*/
|
|
18
|
+
export declare function decideOrThrow(decision: DecisionPort, actor: Principal, action: Action, resource: Resource, context: DecisionContext, label: string): Promise<void>;
|
|
11
19
|
/**
|
|
12
20
|
* Stage: type pre-gate (update/delete only; runs FIRST, before fetch-record's row lock).
|
|
13
21
|
* Security-hardening Phase 8 #7.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permission-gate.d.ts","sourceRoot":"","sources":["../../../src/pipe/stages/permission-gate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"permission-gate.d.ts","sourceRoot":"","sources":["../../../src/pipe/stages/permission-gate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,KAAK,EAAyB,MAAM,eAAe,CAAC;AAQlE,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,YAAY,GAAG,KAAK,CAiCjE;AAOD;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,YAAY,EACtB,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAef;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,GAAG,KAAK,CAoB9D"}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
exports.permissionGateStage = permissionGateStage;
|
|
11
|
+
exports.decideOrThrow = decideOrThrow;
|
|
11
12
|
exports.typePreGateStage = typePreGateStage;
|
|
12
13
|
const errors_js_1 = require("../../errors.js");
|
|
13
14
|
const fetch_record_js_1 = require("./fetch-record.js");
|
|
@@ -44,21 +45,28 @@ function permissionGateStage(decision) {
|
|
|
44
45
|
}
|
|
45
46
|
: {}),
|
|
46
47
|
};
|
|
47
|
-
await decideOrThrow(decision, ctx, action, resource, context);
|
|
48
|
+
await decideOrThrow(decision, ctx.actor, action, resource, context, gateLabel(ctx));
|
|
48
49
|
},
|
|
49
50
|
};
|
|
50
51
|
}
|
|
51
|
-
/**
|
|
52
|
-
|
|
52
|
+
/** The `EngineUnavailableError` label of a pipe stage: `<op> <typeKey>`. */
|
|
53
|
+
function gateLabel(ctx) {
|
|
54
|
+
return `${ctx.op} ${ctx.registration.typeKey}`;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Ask `decide()` once; throw on deny (AuthorizationDeniedError) or a throw (unavailable).
|
|
58
|
+
* Shared by the write stages and `RecordEngine.authorize` (custom object actions, D3); `label`
|
|
59
|
+
* names the operation in the `EngineUnavailableError` message only.
|
|
60
|
+
*/
|
|
61
|
+
async function decideOrThrow(decision, actor, action, resource, context, label) {
|
|
53
62
|
let outcome;
|
|
54
63
|
try {
|
|
55
|
-
outcome = await decision.decide(
|
|
64
|
+
outcome = await decision.decide(actor, action, resource, context);
|
|
56
65
|
}
|
|
57
66
|
catch (err) {
|
|
58
67
|
// A thrown decision = transient/unavailable (ADR-0016). Fail-closed: do NOT
|
|
59
68
|
// proceed; signal retryable.
|
|
60
|
-
throw new errors_js_1.EngineUnavailableError(`decision authority unavailable for ${
|
|
61
|
-
`${err.message}`);
|
|
69
|
+
throw new errors_js_1.EngineUnavailableError(`decision authority unavailable for ${label}: ${err.message}`);
|
|
62
70
|
}
|
|
63
71
|
if (outcome.effect !== 'allow') {
|
|
64
72
|
throw new errors_js_1.AuthorizationDeniedError(outcome.reason, outcome.detail);
|
|
@@ -103,9 +111,7 @@ function typePreGateStage(decision) {
|
|
|
103
111
|
typeKey: ctx.registration.typeKey,
|
|
104
112
|
tenantId: ctx.actor.tenantId,
|
|
105
113
|
};
|
|
106
|
-
await decideOrThrow(decision, ctx, { key: actionKey(ctx.op) }, resource, {
|
|
107
|
-
isAuthenticated: true,
|
|
108
|
-
});
|
|
114
|
+
await decideOrThrow(decision, ctx.actor, { key: actionKey(ctx.op) }, resource, { isAuthenticated: true }, gateLabel(ctx));
|
|
109
115
|
},
|
|
110
116
|
};
|
|
111
117
|
}
|
|
@@ -7,5 +7,11 @@
|
|
|
7
7
|
* Runs AFTER derive-write-context so trusted (server) values are validated too.
|
|
8
8
|
*/
|
|
9
9
|
import type { Stage } from '../context.js';
|
|
10
|
+
import type { FieldSpec } from '../../types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Validate + coerce one value against a field type. Returns the coerced value. Also used by
|
|
13
|
+
* the list filter builder (`buildFilterClauses`) for every `where` operand.
|
|
14
|
+
*/
|
|
15
|
+
export declare function coerce(field: FieldSpec, value: unknown): unknown;
|
|
10
16
|
export declare function validateStage(): Stage;
|
|
11
17
|
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../src/pipe/stages/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,KAAK,EAAgB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../src/pipe/stages/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,KAAK,EAAgB,MAAM,eAAe,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,gBAAgB,CAAC;AAW3D;;;GAGG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CA6ChE;AAED,wBAAgB,aAAa,IAAI,KAAK,CAmCrC"}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* Runs AFTER derive-write-context so trusted (server) values are validated too.
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.coerce = coerce;
|
|
11
12
|
exports.validateStage = validateStage;
|
|
12
13
|
const errors_js_1 = require("../../errors.js");
|
|
13
14
|
const registry_js_1 = require("../../registry.js");
|
|
@@ -20,7 +21,10 @@ const SUPPORTED = new Set([
|
|
|
20
21
|
'timestamp',
|
|
21
22
|
'json',
|
|
22
23
|
]);
|
|
23
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* Validate + coerce one value against a field type. Returns the coerced value. Also used by
|
|
26
|
+
* the list filter builder (`buildFilterClauses`) for every `where` operand.
|
|
27
|
+
*/
|
|
24
28
|
function coerce(field, value) {
|
|
25
29
|
switch (field.type) {
|
|
26
30
|
case 'string':
|
|
@@ -31,6 +35,10 @@ function coerce(field, value) {
|
|
|
31
35
|
if (typeof value !== 'number' || Number.isNaN(value)) {
|
|
32
36
|
throw new errors_js_1.ValidationError(field.key, 'expected a number');
|
|
33
37
|
}
|
|
38
|
+
// ±Infinity is not a storable column value and not a meaningful filter operand.
|
|
39
|
+
if (!Number.isFinite(value)) {
|
|
40
|
+
throw new errors_js_1.ValidationError(field.key, 'expected a finite number');
|
|
41
|
+
}
|
|
34
42
|
return value;
|
|
35
43
|
case 'boolean':
|
|
36
44
|
if (typeof value === 'boolean')
|
|
@@ -44,14 +52,20 @@ function coerce(field, value) {
|
|
|
44
52
|
throw new errors_js_1.ValidationError(field.key, 'expected a uuid');
|
|
45
53
|
return canonical;
|
|
46
54
|
}
|
|
47
|
-
case 'timestamp':
|
|
55
|
+
case 'timestamp': {
|
|
56
|
+
// An invalid Date (NaN / ±Infinity epoch) is rejected here, never bound as a parameter.
|
|
57
|
+
let date = null;
|
|
48
58
|
if (value instanceof Date)
|
|
49
|
-
|
|
50
|
-
if (typeof value === 'number')
|
|
51
|
-
|
|
52
|
-
if (typeof value === 'string' && !Number.isNaN(Date.parse(value)))
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
date = value;
|
|
60
|
+
else if (typeof value === 'number')
|
|
61
|
+
date = new Date(value);
|
|
62
|
+
else if (typeof value === 'string' && !Number.isNaN(Date.parse(value)))
|
|
63
|
+
date = new Date(value);
|
|
64
|
+
if (date === null || Number.isNaN(date.getTime())) {
|
|
65
|
+
throw new errors_js_1.ValidationError(field.key, 'expected a timestamp (Date | epoch ms | ISO string)');
|
|
66
|
+
}
|
|
67
|
+
return date;
|
|
68
|
+
}
|
|
55
69
|
case 'json':
|
|
56
70
|
return JSON.stringify(value);
|
|
57
71
|
default:
|
package/dist/registry.d.ts
CHANGED
|
@@ -70,4 +70,17 @@ export declare function writeScopeClause(reg: ObjectType, actor: Principal): {
|
|
|
70
70
|
export declare function isCreateWalled(reg: ObjectType): boolean;
|
|
71
71
|
/** Boot-enforce that exactly-one field claims `role`; throw if none. */
|
|
72
72
|
export declare function requireRole(reg: ObjectType, role: NonNullable<FieldSpec['role']>): void;
|
|
73
|
+
/** What the action vocabulary reads from a registration (typeKey only names it in errors). */
|
|
74
|
+
type ActionDeclaration = Pick<ObjectType, 'actions' | 'crudActions'> & {
|
|
75
|
+
readonly typeKey?: string;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* The type's action vocabulary (ADR-0038): `read, create, update, delete` then the declared
|
|
79
|
+
* extras in declared order, or exactly the declared list when `crudActions === false`.
|
|
80
|
+
* Validates first (throws `RegistrationError`), so a registration read without
|
|
81
|
+
* `TableRegistry.register` (e.g. identity `extraObjects`) is held to the same rules.
|
|
82
|
+
* Framework-facing: the guard registry bridge, identity lookups and `authorize()` read it.
|
|
83
|
+
*/
|
|
84
|
+
export declare function effectiveActions(reg: ActionDeclaration): readonly string[];
|
|
85
|
+
export {};
|
|
73
86
|
//# sourceMappingURL=registry.d.ts.map
|
package/dist/registry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE/E,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;AAaD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,UAAU,EACf,KAAK,EAAE,SAAS,GACf;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAA;CAAE,GAAG,IAAI,CAYpD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAEvD;AAED,wEAAwE;AACxE,wBAAgB,WAAW,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,CAMvF;AAmDD,8FAA8F;AAC9F,KAAK,iBAAiB,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,GAAG,aAAa,CAAC,GAAG;IACrE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAgEF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,iBAAiB,GAAG,SAAS,MAAM,EAAE,CAI1E"}
|
package/dist/registry.js
CHANGED
|
@@ -20,6 +20,8 @@ exports.columnForRole = columnForRole;
|
|
|
20
20
|
exports.writeScopeClause = writeScopeClause;
|
|
21
21
|
exports.isCreateWalled = isCreateWalled;
|
|
22
22
|
exports.requireRole = requireRole;
|
|
23
|
+
exports.effectiveActions = effectiveActions;
|
|
24
|
+
const core_1 = require("@astram/core");
|
|
23
25
|
const errors_js_1 = require("./errors.js");
|
|
24
26
|
const types_js_1 = require("./types.js");
|
|
25
27
|
class TableRegistry {
|
|
@@ -178,6 +180,70 @@ function validate(reg) {
|
|
|
178
180
|
throw new errors_js_1.RegistrationError(`registration '${reg.typeKey}': composite primary key needs >= 2 columns`);
|
|
179
181
|
}
|
|
180
182
|
validateAccess(reg);
|
|
183
|
+
validateActions(reg);
|
|
184
|
+
}
|
|
185
|
+
/** The CRUD vocabulary, in the `IDENTITY_ACTIONS` order (guard `identity-actions.ts`). */
|
|
186
|
+
const CRUD_ACTIONS = ['read', 'create', 'update', 'delete'];
|
|
187
|
+
/** Names no type may declare: the wildcard and the bare `@Classified` default (ADR-0038). */
|
|
188
|
+
const RESERVED_ACTIONS = new Set(['*', 'access']);
|
|
189
|
+
/** The narrowest action column (`perm_action`, `rpr_action`, `rs_action`) is VARCHAR(64). */
|
|
190
|
+
const MAX_ACTION_LENGTH = 64;
|
|
191
|
+
/**
|
|
192
|
+
* Boot-time validation of the declared action vocabulary (ADR-0038). Pure, no IO. Every
|
|
193
|
+
* failure is a `RegistrationError` naming the type and the action, so a bad name fails boot
|
|
194
|
+
* instead of silently denying at request time (Shield #2).
|
|
195
|
+
*/
|
|
196
|
+
function validateActions(reg) {
|
|
197
|
+
const type = reg.typeKey ?? '(unnamed)';
|
|
198
|
+
const crud = reg.crudActions;
|
|
199
|
+
if (crud !== undefined && typeof crud !== 'boolean') {
|
|
200
|
+
throw new errors_js_1.RegistrationError(`registration '${type}': crudActions must be a boolean, got ${typeof crud} (${String(crud)}).`);
|
|
201
|
+
}
|
|
202
|
+
const actions = reg.actions;
|
|
203
|
+
if (actions !== undefined && !Array.isArray(actions)) {
|
|
204
|
+
throw new errors_js_1.RegistrationError(`registration '${type}': actions must be an array of action names, got ${typeof actions}.`);
|
|
205
|
+
}
|
|
206
|
+
const list = actions ?? [];
|
|
207
|
+
if (crud === false && list.length === 0) {
|
|
208
|
+
throw new errors_js_1.RegistrationError(`registration '${type}': crudActions:false requires a non-empty actions list ` +
|
|
209
|
+
`(a type with no actions could never be granted).`);
|
|
210
|
+
}
|
|
211
|
+
const seen = new Set();
|
|
212
|
+
for (const a of list) {
|
|
213
|
+
if (typeof a !== 'string') {
|
|
214
|
+
throw new errors_js_1.RegistrationError(`registration '${type}': action ${String(a)} must be a string, got ${typeof a}.`);
|
|
215
|
+
}
|
|
216
|
+
if (RESERVED_ACTIONS.has(a)) {
|
|
217
|
+
throw new errors_js_1.RegistrationError(`registration '${type}': action '${a}' is reserved ('*' is the wildcard, 'access' the ` +
|
|
218
|
+
`@Classified default).`);
|
|
219
|
+
}
|
|
220
|
+
if (a.length > MAX_ACTION_LENGTH) {
|
|
221
|
+
throw new errors_js_1.RegistrationError(`registration '${type}': action '${a}' is longer than ${MAX_ACTION_LENGTH} characters.`);
|
|
222
|
+
}
|
|
223
|
+
if (!core_1.SEGMENT_FORMAT.test(a)) {
|
|
224
|
+
throw new errors_js_1.RegistrationError(`registration '${type}': action '${a}' must be snake_case (${String(core_1.SEGMENT_FORMAT)}).`);
|
|
225
|
+
}
|
|
226
|
+
if (crud !== false && CRUD_ACTIONS.includes(a)) {
|
|
227
|
+
throw new errors_js_1.RegistrationError(`registration '${type}': action '${a}' is a CRUD verb, already granted by default; ` +
|
|
228
|
+
`list it only with crudActions:false.`);
|
|
229
|
+
}
|
|
230
|
+
if (seen.has(a)) {
|
|
231
|
+
throw new errors_js_1.RegistrationError(`registration '${type}': duplicate action '${a}'.`);
|
|
232
|
+
}
|
|
233
|
+
seen.add(a);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* The type's action vocabulary (ADR-0038): `read, create, update, delete` then the declared
|
|
238
|
+
* extras in declared order, or exactly the declared list when `crudActions === false`.
|
|
239
|
+
* Validates first (throws `RegistrationError`), so a registration read without
|
|
240
|
+
* `TableRegistry.register` (e.g. identity `extraObjects`) is held to the same rules.
|
|
241
|
+
* Framework-facing: the guard registry bridge, identity lookups and `authorize()` read it.
|
|
242
|
+
*/
|
|
243
|
+
function effectiveActions(reg) {
|
|
244
|
+
validateActions(reg);
|
|
245
|
+
const declared = reg.actions ?? [];
|
|
246
|
+
return reg.crudActions === false ? [...declared] : [...CRUD_ACTIONS, ...declared];
|
|
181
247
|
}
|
|
182
248
|
/**
|
|
183
249
|
* Chokepoint 1 (§4.1 of the framework-owns-data-layer design). Every registered
|
package/dist/types.d.ts
CHANGED
|
@@ -212,6 +212,19 @@ export interface ObjectType {
|
|
|
212
212
|
* still applies).
|
|
213
213
|
*/
|
|
214
214
|
readonly readScope?: ReadScope;
|
|
215
|
+
/**
|
|
216
|
+
* OPTIONAL extra actions beyond CRUD (ADR-0038), e.g. `['activate']`. Each is a
|
|
217
|
+
* permission-key segment (`SEGMENT_FORMAT`, at most 64 characters), unique, never `*` or
|
|
218
|
+
* `access`, and never a CRUD name unless `crudActions` is `false`. Validated at boot; read
|
|
219
|
+
* through `effectiveActions(reg)`.
|
|
220
|
+
*/
|
|
221
|
+
readonly actions?: readonly string[];
|
|
222
|
+
/**
|
|
223
|
+
* OPTIONAL, default `true`: the type's vocabulary starts with `read, create, update, delete`.
|
|
224
|
+
* `false` is for authority-only types (never CRUD'd through the engine): the vocabulary is
|
|
225
|
+
* exactly `actions`, which must then be non-empty (ADR-0038).
|
|
226
|
+
*/
|
|
227
|
+
readonly crudActions?: boolean;
|
|
215
228
|
}
|
|
216
229
|
/** Input to a create/update — field keys → values (+ optional junction refs). */
|
|
217
230
|
export interface RecordInput {
|
|
@@ -244,6 +257,41 @@ export interface SearchQuery {
|
|
|
244
257
|
/** The search term — bound as `%term%` per field (wildcards escaped). */
|
|
245
258
|
readonly term: string;
|
|
246
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* The `list()` filter operators (list-query-operators SPEC v2 §10). Exactly these keys; no
|
|
262
|
+
* `ne`/`not_in`/`contains` (follow-up spec). Operator × type: `eq`/`in` on every type but
|
|
263
|
+
* `json`; `gt|gte|lt|lte|between` on `string`, `number`, `uuid`, `timestamp`; `isNull` /
|
|
264
|
+
* `isNotNull` on every type.
|
|
265
|
+
*/
|
|
266
|
+
export type FilterOperator = 'eq' | 'in' | 'gt' | 'gte' | 'lt' | 'lte' | 'between' | 'isNull' | 'isNotNull';
|
|
267
|
+
/** A filter operand before coercion (coerced per field type like a write; `?`-bound). */
|
|
268
|
+
export type FilterScalar = string | number | boolean | Date | null;
|
|
269
|
+
/**
|
|
270
|
+
* One `where` entry: a scalar (equality; `null` ⇒ `IS NULL`) or exactly ONE operator object.
|
|
271
|
+
* An unknown operator, several operators in one object, or a malformed operand throws
|
|
272
|
+
* (never degrades to "no filter").
|
|
273
|
+
*/
|
|
274
|
+
export type FilterValue = FilterScalar | {
|
|
275
|
+
readonly eq: FilterScalar;
|
|
276
|
+
} | {
|
|
277
|
+
readonly in: readonly FilterScalar[];
|
|
278
|
+
} | {
|
|
279
|
+
readonly gt: FilterScalar;
|
|
280
|
+
} | {
|
|
281
|
+
readonly gte: FilterScalar;
|
|
282
|
+
} | {
|
|
283
|
+
readonly lt: FilterScalar;
|
|
284
|
+
} | {
|
|
285
|
+
readonly lte: FilterScalar;
|
|
286
|
+
} | {
|
|
287
|
+
readonly between: readonly [FilterScalar, FilterScalar];
|
|
288
|
+
} | {
|
|
289
|
+
readonly isNull: true;
|
|
290
|
+
} | {
|
|
291
|
+
readonly isNotNull: true;
|
|
292
|
+
};
|
|
293
|
+
/** A list `where`: registered field KEY → filter value, ANDed in object key order. */
|
|
294
|
+
export type WhereClause = Readonly<Record<string, FilterValue>>;
|
|
247
295
|
/**
|
|
248
296
|
* A list query — tenant-scoped filtering + simple paging, plus optional search
|
|
249
297
|
* (LIKE across fields) and sort (ORDER BY). All field references are registered
|
|
@@ -252,8 +300,8 @@ export interface SearchQuery {
|
|
|
252
300
|
export interface ListQuery {
|
|
253
301
|
readonly limit?: number;
|
|
254
302
|
readonly offset?: number;
|
|
255
|
-
/**
|
|
256
|
-
readonly where?:
|
|
303
|
+
/** Filters on registered field keys: scalar equality or one operator (`FilterValue`). */
|
|
304
|
+
readonly where?: WhereClause;
|
|
257
305
|
/** Free-text LIKE search across named field keys (ANDed with `where` + tenant). */
|
|
258
306
|
readonly search?: SearchQuery;
|
|
259
307
|
/**
|
|
@@ -270,6 +318,42 @@ export interface RecordPage {
|
|
|
270
318
|
readonly rows: readonly RecordView[];
|
|
271
319
|
readonly total: number;
|
|
272
320
|
}
|
|
321
|
+
/**
|
|
322
|
+
* An `aggregate()` function (list-query-operators SPEC v2 §10): `count` (no field ⇒ `COUNT(*)`),
|
|
323
|
+
* `sum`/`avg` on `number` fields, `min`/`max` on `string`, `number`, `uuid`, `timestamp`.
|
|
324
|
+
*/
|
|
325
|
+
export type AggregateFn = 'count' | 'sum' | 'min' | 'max' | 'avg';
|
|
326
|
+
/** One measure: `fn` over a registered field KEY, returned under `as` (`^[a-z_][a-z0-9_]{0,63}$`). */
|
|
327
|
+
export interface AggregateMeasure {
|
|
328
|
+
readonly fn: AggregateFn;
|
|
329
|
+
readonly field?: string;
|
|
330
|
+
readonly as: string;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* An aggregate over exactly the rows `list()` would return for the same `where`/`search`
|
|
334
|
+
* (shared read WHERE, Shield #8). `groupBy` ≤ 3 non-json field keys; 1..10 `measures`;
|
|
335
|
+
* `limit` caps the number of groups (default 100, max 1000).
|
|
336
|
+
*/
|
|
337
|
+
export interface AggregateQuery {
|
|
338
|
+
readonly where?: WhereClause;
|
|
339
|
+
readonly search?: SearchQuery;
|
|
340
|
+
readonly groupBy?: readonly string[];
|
|
341
|
+
readonly measures: readonly AggregateMeasure[];
|
|
342
|
+
readonly limit?: number;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* One group: `key` maps each `groupBy` field key to its value (`{}` when ungrouped); `values`
|
|
346
|
+
* maps each measure alias to its result. `count`/`sum`/`avg` are numbers (or `null`); `min`/`max`
|
|
347
|
+
* keep the column's value (`string` for string/uuid, `Date` for timestamp, `number`) or `null`.
|
|
348
|
+
*/
|
|
349
|
+
export interface AggregateGroup {
|
|
350
|
+
readonly key: Readonly<Record<string, unknown>>;
|
|
351
|
+
readonly values: Readonly<Record<string, number | string | Date | null>>;
|
|
352
|
+
}
|
|
353
|
+
/** Ungrouped ⇒ exactly one group; grouped with zero rows ⇒ `groups: []`. Ordered by group keys. */
|
|
354
|
+
export interface AggregateResult {
|
|
355
|
+
readonly groups: readonly AggregateGroup[];
|
|
356
|
+
}
|
|
273
357
|
/**
|
|
274
358
|
* The ONLY data-access surface a feature is given. Every method flows through the
|
|
275
359
|
* full pipe (permission gate + audit are mandatory). No pool, query runner, or
|
|
@@ -306,6 +390,32 @@ export interface RecordEngine {
|
|
|
306
390
|
* outside one it throws `EngineUnavailableError` (a lock released at once serialises nothing).
|
|
307
391
|
*/
|
|
308
392
|
lockForUpdate(typeKey: string, id: RecordId, actor: Principal): Promise<void>;
|
|
393
|
+
/**
|
|
394
|
+
* Record-level decision for one declared action, with no write (custom object actions,
|
|
395
|
+
* ADR-0038). Resolves on allow, throws on deny; returns no row data and writes no audit row.
|
|
396
|
+
* An `action` outside the type's declared vocabulary throws `RegistrationError` before any SQL
|
|
397
|
+
* or decision. Then the type pre-gate, the tenant-scoped fetch (`whereById`; `FOR UPDATE` inside
|
|
398
|
+
* `transaction(fn)`, plain `SELECT` outside) and the record-level `decide()` on the row's facts
|
|
399
|
+
* and tenant. Missing and foreign rows give the same `AuthorizationDeniedError`
|
|
400
|
+
* (`no_record_permit`); a decision throw is `EngineUnavailableError`. A deny does not make an
|
|
401
|
+
* open transaction rollback-only.
|
|
402
|
+
*/
|
|
403
|
+
authorize(typeKey: string, id: RecordId, action: string, actor: Principal): Promise<void>;
|
|
404
|
+
/**
|
|
405
|
+
* Aggregate (count/sum/avg/min/max, optional groupBy) over exactly the rows `list()` returns
|
|
406
|
+
* for the same `where`/`search`: gated as object-type `read` like `list`, then one SELECT with
|
|
407
|
+
* the list's read WHERE. Read-only; writes no audit row.
|
|
408
|
+
*/
|
|
409
|
+
aggregate(typeKey: string, query: AggregateQuery, actor: Principal): Promise<AggregateResult>;
|
|
410
|
+
/**
|
|
411
|
+
* Create 1..500 records all-or-nothing in ONE transaction: each row runs the full single-row
|
|
412
|
+
* `create` pipe (gate, derive, validate, write, junction-sync, create wall, audit), in order.
|
|
413
|
+
* The first failing row throws and the whole batch rolls back; inside `transaction(fn)` it
|
|
414
|
+
* joins the outer one. Each row's audit event carries the call's `batchId` and its 0-based
|
|
415
|
+
* `batchIndex`. An empty, non-array or over-cap `inputs` throws `ValidationError('inputs')`
|
|
416
|
+
* before any transaction opens. Returns the ids in input order.
|
|
417
|
+
*/
|
|
418
|
+
createMany(typeKey: string, inputs: readonly RecordInput[], actor: Principal): Promise<RecordId[]>;
|
|
309
419
|
}
|
|
310
420
|
export type { Principal };
|
|
311
421
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astram/engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Astram framework — the paved road. ONE unbypassable write pipe (resolve-schema → permission gate → derive-write-context → validate → compute → write → junction-sync → audit → emit-event) over real physical tables, that every feature's CRUD flows through. Feature code receives ONLY the RecordEngine facade — never a raw pool/DataSource/QueryRunner (the physical wall). Security-gate + audit are mandatory stages. Missing capability → a typed CapabilityNotSupportedError (the gap-flag STOP). Builds on @astram/core (metadata contract, decide(), audit) + @astram/guard (decision authority).",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"mariadb": "^3.5.4",
|
|
35
|
-
"@astram/core": "1.
|
|
35
|
+
"@astram/core": "1.6.0"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@nestjs/common": "^11",
|