@astram/engine 0.6.0 → 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.
Files changed (34) hide show
  1. package/dist/decorators/object-type.decorator.d.ts +10 -0
  2. package/dist/decorators/object-type.decorator.d.ts.map +1 -1
  3. package/dist/decorators/object-type.decorator.js +5 -0
  4. package/dist/index.d.ts +2 -2
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +2 -1
  7. package/dist/pipe/context.d.ts +8 -0
  8. package/dist/pipe/context.d.ts.map +1 -1
  9. package/dist/pipe/filter-sql.d.ts +66 -0
  10. package/dist/pipe/filter-sql.d.ts.map +1 -0
  11. package/dist/pipe/filter-sql.js +266 -0
  12. package/dist/pipe/record-engine.d.ts +34 -1
  13. package/dist/pipe/record-engine.d.ts.map +1 -1
  14. package/dist/pipe/record-engine.js +256 -87
  15. package/dist/pipe/records-service.d.ts +19 -1
  16. package/dist/pipe/records-service.d.ts.map +1 -1
  17. package/dist/pipe/records-service.js +32 -0
  18. package/dist/pipe/stages/audit.d.ts.map +1 -1
  19. package/dist/pipe/stages/audit.js +8 -1
  20. package/dist/pipe/stages/fetch-record.d.ts +11 -1
  21. package/dist/pipe/stages/fetch-record.d.ts.map +1 -1
  22. package/dist/pipe/stages/fetch-record.js +18 -9
  23. package/dist/pipe/stages/permission-gate.d.ts +8 -0
  24. package/dist/pipe/stages/permission-gate.d.ts.map +1 -1
  25. package/dist/pipe/stages/permission-gate.js +15 -9
  26. package/dist/pipe/stages/validate.d.ts +6 -0
  27. package/dist/pipe/stages/validate.d.ts.map +1 -1
  28. package/dist/pipe/stages/validate.js +22 -8
  29. package/dist/registry.d.ts +13 -0
  30. package/dist/registry.d.ts.map +1 -1
  31. package/dist/registry.js +66 -0
  32. package/dist/types.d.ts +112 -2
  33. package/dist/types.d.ts.map +1 -1
  34. 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 reg = ctx.registration;
88
- const where = (0, where_by_id_js_1.whereById)(reg, ctx.actor, ctx.id);
89
- const rows = (await ctx.query(`SELECT * FROM ${(0, where_by_id_js_1.qualifiedTable)(reg)} WHERE ${where.sql} LIMIT 1 FOR UPDATE`, where.params));
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;AAEnD,OAAO,KAAK,EAAE,KAAK,EAAyB,MAAM,eAAe,CAAC;AAQlE,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,YAAY,GAAG,KAAK,CAiCjE;AA2BD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,GAAG,KAAK,CAe9D"}
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
- /** Ask `decide()` once; throw on deny (AuthorizationDeniedError) or a throw (unavailable). */
52
- async function decideOrThrow(decision, ctx, action, resource, context) {
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(ctx.actor, action, resource, context);
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 ${ctx.op} ${ctx.registration.typeKey}: ` +
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;AAiDzD,wBAAgB,aAAa,IAAI,KAAK,CAmCrC"}
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
- /** Validate + coerce one value against a field type. Returns the coerced value. */
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
- return value;
50
- if (typeof value === 'number')
51
- return new Date(value);
52
- if (typeof value === 'string' && !Number.isNaN(Date.parse(value)))
53
- return new Date(value);
54
- throw new errors_js_1.ValidationError(field.key, 'expected a timestamp (Date | epoch ms | ISO string)');
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:
@@ -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
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,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"}
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
- /** Equality filters on core field keys. */
256
- readonly where?: Readonly<Record<string, unknown>>;
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
@@ -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.6.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.5.0"
35
+ "@astram/core": "1.6.0"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@nestjs/common": "^11",