@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.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/dist/adapters/mariadb-audit-sink.d.ts +24 -0
  3. package/dist/adapters/mariadb-audit-sink.d.ts.map +1 -0
  4. package/dist/adapters/mariadb-audit-sink.js +40 -0
  5. package/dist/adapters/mariadb-audit-sink.js.map +1 -0
  6. package/dist/adapters/mariadb-unit-of-work.d.ts +27 -0
  7. package/dist/adapters/mariadb-unit-of-work.d.ts.map +1 -0
  8. package/dist/adapters/mariadb-unit-of-work.js +58 -0
  9. package/dist/adapters/mariadb-unit-of-work.js.map +1 -0
  10. package/dist/decorators/access-bundle.decorator.d.ts +66 -0
  11. package/dist/decorators/access-bundle.decorator.d.ts.map +1 -0
  12. package/dist/decorators/access-bundle.decorator.js +76 -0
  13. package/dist/decorators/access-bundle.decorator.js.map +1 -0
  14. package/dist/decorators/object-type.decorator.d.ts +116 -0
  15. package/dist/decorators/object-type.decorator.d.ts.map +1 -0
  16. package/dist/decorators/object-type.decorator.js +146 -0
  17. package/dist/decorators/object-type.decorator.js.map +1 -0
  18. package/dist/errors.d.ts +46 -0
  19. package/dist/errors.d.ts.map +1 -0
  20. package/dist/errors.js +67 -0
  21. package/dist/errors.js.map +1 -0
  22. package/dist/index.d.ts +30 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +80 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/module/engine.module.d.ts +90 -0
  27. package/dist/module/engine.module.d.ts.map +1 -0
  28. package/dist/module/engine.module.js +218 -0
  29. package/dist/module/engine.module.js.map +1 -0
  30. package/dist/module/tokens.d.ts +12 -0
  31. package/dist/module/tokens.d.ts.map +1 -0
  32. package/dist/module/tokens.js +14 -0
  33. package/dist/pipe/context.d.ts +55 -0
  34. package/dist/pipe/context.d.ts.map +1 -0
  35. package/dist/pipe/context.js +18 -0
  36. package/dist/pipe/context.js.map +1 -0
  37. package/dist/pipe/record-engine.d.ts +72 -0
  38. package/dist/pipe/record-engine.d.ts.map +1 -0
  39. package/dist/pipe/record-engine.js +279 -0
  40. package/dist/pipe/record-engine.js.map +1 -0
  41. package/dist/pipe/records-service.d.ts +47 -0
  42. package/dist/pipe/records-service.d.ts.map +1 -0
  43. package/dist/pipe/records-service.js +108 -0
  44. package/dist/pipe/stages/audit.d.ts +15 -0
  45. package/dist/pipe/stages/audit.d.ts.map +1 -0
  46. package/dist/pipe/stages/audit.js +42 -0
  47. package/dist/pipe/stages/audit.js.map +1 -0
  48. package/dist/pipe/stages/derive-write-context.d.ts +22 -0
  49. package/dist/pipe/stages/derive-write-context.d.ts.map +1 -0
  50. package/dist/pipe/stages/derive-write-context.js +61 -0
  51. package/dist/pipe/stages/derive-write-context.js.map +1 -0
  52. package/dist/pipe/stages/fetch-owner.d.ts +28 -0
  53. package/dist/pipe/stages/fetch-owner.d.ts.map +1 -0
  54. package/dist/pipe/stages/fetch-owner.js +90 -0
  55. package/dist/pipe/stages/fetch-owner.js.map +1 -0
  56. package/dist/pipe/stages/junction-sync.d.ts +14 -0
  57. package/dist/pipe/stages/junction-sync.d.ts.map +1 -0
  58. package/dist/pipe/stages/junction-sync.js +63 -0
  59. package/dist/pipe/stages/junction-sync.js.map +1 -0
  60. package/dist/pipe/stages/permission-gate.d.ts +11 -0
  61. package/dist/pipe/stages/permission-gate.d.ts.map +1 -0
  62. package/dist/pipe/stages/permission-gate.js +53 -0
  63. package/dist/pipe/stages/permission-gate.js.map +1 -0
  64. package/dist/pipe/stages/validate.d.ts +11 -0
  65. package/dist/pipe/stages/validate.d.ts.map +1 -0
  66. package/dist/pipe/stages/validate.js +89 -0
  67. package/dist/pipe/stages/validate.js.map +1 -0
  68. package/dist/pipe/stages/write.d.ts +12 -0
  69. package/dist/pipe/stages/write.d.ts.map +1 -0
  70. package/dist/pipe/stages/write.js +109 -0
  71. package/dist/pipe/stages/write.js.map +1 -0
  72. package/dist/ports.d.ts +37 -0
  73. package/dist/ports.d.ts.map +1 -0
  74. package/dist/ports.js +11 -0
  75. package/dist/ports.js.map +1 -0
  76. package/dist/registry.d.ts +42 -0
  77. package/dist/registry.d.ts.map +1 -0
  78. package/dist/registry.js +184 -0
  79. package/dist/registry.js.map +1 -0
  80. package/dist/types.d.ts +207 -0
  81. package/dist/types.d.ts.map +1 -0
  82. package/dist/types.js +15 -0
  83. package/dist/types.js.map +1 -0
  84. package/package.json +49 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Astram
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The append-only audit sink adapter — writes one row per event into the
3
+ * `audit_log` table (shield_audit schema), on the pipe's transaction. Append-only:
4
+ * this adapter exposes ONLY `record()` — no update/delete path (structural, matching
5
+ * core's audit invariant). The table is created by an @astram/migrate migration
6
+ * (P-D); the adapter assumes it exists.
7
+ *
8
+ * Actor/tenant/action/resource are stored by stable id/key; the presentation layer
9
+ * resolves ids to labels (no raw ids to end users). Sensitive values are never put
10
+ * in the event by the pipe (the audit stage carries only op + typeKey attributes).
11
+ */
12
+ import type { AuditEvent, AuditPort, QueryFn } from '../ports.js';
13
+ export interface AuditSinkConfig {
14
+ /** Schema holding the audit_log table (default shield_audit). */
15
+ readonly schema?: string;
16
+ /** Table name (default audit_log). */
17
+ readonly table?: string;
18
+ }
19
+ export declare class MariaDbAuditSink implements AuditPort {
20
+ private readonly ref;
21
+ constructor(config?: AuditSinkConfig);
22
+ record(event: AuditEvent, query: QueryFn): Promise<void>;
23
+ }
24
+ //# sourceMappingURL=mariadb-audit-sink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mariadb-audit-sink.d.ts","sourceRoot":"","sources":["../../src/adapters/mariadb-audit-sink.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAElE,MAAM,WAAW,eAAe;IAC9B,iEAAiE;IACjE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,sCAAsC;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,gBAAiB,YAAW,SAAS;IAChD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;gBAEjB,MAAM,GAAE,eAAoB;IAMlC,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CAmB/D"}
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ /**
3
+ * The append-only audit sink adapter — writes one row per event into the
4
+ * `audit_log` table (shield_audit schema), on the pipe's transaction. Append-only:
5
+ * this adapter exposes ONLY `record()` — no update/delete path (structural, matching
6
+ * core's audit invariant). The table is created by an @astram/migrate migration
7
+ * (P-D); the adapter assumes it exists.
8
+ *
9
+ * Actor/tenant/action/resource are stored by stable id/key; the presentation layer
10
+ * resolves ids to labels (no raw ids to end users). Sensitive values are never put
11
+ * in the event by the pipe (the audit stage carries only op + typeKey attributes).
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.MariaDbAuditSink = void 0;
15
+ const node_crypto_1 = require("node:crypto");
16
+ class MariaDbAuditSink {
17
+ ref;
18
+ constructor(config = {}) {
19
+ const schema = config.schema ?? 'shield_audit';
20
+ const table = config.table ?? 'audit_log';
21
+ this.ref = `\`${schema}\`.\`${table}\``;
22
+ }
23
+ async record(event, query) {
24
+ await query(`INSERT INTO ${this.ref}
25
+ (al_uuid, al_type, al_occurred_at_ms, al_actor_id, al_tenant_id,
26
+ al_action_key, al_resource_json, al_decision_json, al_attributes_json)
27
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
28
+ (0, node_crypto_1.randomUUID)(),
29
+ event.type,
30
+ event.occurredAtMs,
31
+ event.actorId,
32
+ event.tenantId,
33
+ event.action?.key ?? null,
34
+ event.resource ? JSON.stringify(event.resource) : null,
35
+ event.decision ? JSON.stringify(event.decision) : null,
36
+ event.attributes ? JSON.stringify(event.attributes) : null,
37
+ ]);
38
+ }
39
+ }
40
+ exports.MariaDbAuditSink = MariaDbAuditSink;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mariadb-audit-sink.js","sourceRoot":"","sources":["../../src/adapters/mariadb-audit-sink.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AAEH,6CAAyC;AAUzC,MAAa,gBAAgB;IACV,GAAG,CAAS;IAE7B,YAAY,SAA0B,EAAE;QACtC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,cAAc,CAAC;QAC/C,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,WAAW,CAAC;QAC1C,IAAI,CAAC,GAAG,GAAG,KAAK,MAAM,QAAQ,KAAK,IAAI,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAiB,EAAE,KAAc;QAC5C,MAAM,KAAK,CACT,eAAe,IAAI,CAAC,GAAG;;;0CAGa,EACpC;YACE,IAAA,wBAAU,GAAE;YACZ,KAAK,CAAC,IAAI;YACV,KAAK,CAAC,YAAY;YAClB,KAAK,CAAC,OAAO;YACb,KAAK,CAAC,QAAQ;YACd,KAAK,CAAC,MAAM,EAAE,GAAG,IAAI,IAAI;YACzB,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI;YACtD,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI;YACtD,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI;SAC3D,CACF,CAAC;IACJ,CAAC;CACF;AA5BD,4CA4BC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The SEALED mariadb {@link UnitOfWork} — the engine's own runtime pool (shield_core
3
+ * account: DML on shield_app, NO DDL). This pool is created INSIDE the engine module
4
+ * and is NEVER provided to feature scope — that absence IS the physical wall. A stage
5
+ * gets only the bound {@link QueryFn} for the duration of one transaction.
6
+ *
7
+ * ⛔ SECURITY — never logs the password. ADR-0018: the connection limit is a budgeted
8
+ * value from the engine config, not an ad-hoc knob.
9
+ */
10
+ import type { QueryFn, UnitOfWork } from '../ports.js';
11
+ export interface EngineDbConfig {
12
+ readonly host: string;
13
+ readonly port: number;
14
+ /** Runtime DML account (shield_core) — NOT a DDL account. */
15
+ readonly user: string;
16
+ readonly password: string;
17
+ readonly database: string;
18
+ /** ADR-0018 budgeted connection cap for this pool. */
19
+ readonly connectionLimit: number;
20
+ }
21
+ export declare class MariaDbUnitOfWork implements UnitOfWork {
22
+ private readonly pool;
23
+ constructor(config: EngineDbConfig);
24
+ run<T>(body: (query: QueryFn) => Promise<T>): Promise<T>;
25
+ close(): Promise<void>;
26
+ }
27
+ //# sourceMappingURL=mariadb-unit-of-work.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mariadb-unit-of-work.d.ts","sourceRoot":"","sources":["../../src/adapters/mariadb-unit-of-work.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEvD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,sDAAsD;IACtD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED,qBAAa,iBAAkB,YAAW,UAAU;IAClD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAO;gBAEhB,MAAM,EAAE,cAAc;IAc5B,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAqBxD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ /**
3
+ * The SEALED mariadb {@link UnitOfWork} — the engine's own runtime pool (shield_core
4
+ * account: DML on shield_app, NO DDL). This pool is created INSIDE the engine module
5
+ * and is NEVER provided to feature scope — that absence IS the physical wall. A stage
6
+ * gets only the bound {@link QueryFn} for the duration of one transaction.
7
+ *
8
+ * ⛔ SECURITY — never logs the password. ADR-0018: the connection limit is a budgeted
9
+ * value from the engine config, not an ad-hoc knob.
10
+ */
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.MariaDbUnitOfWork = void 0;
16
+ const mariadb_1 = __importDefault(require("mariadb"));
17
+ class MariaDbUnitOfWork {
18
+ pool;
19
+ constructor(config) {
20
+ this.pool = mariadb_1.default.createPool({
21
+ host: config.host,
22
+ port: config.port,
23
+ user: config.user,
24
+ password: config.password,
25
+ database: config.database,
26
+ connectionLimit: config.connectionLimit,
27
+ connectTimeout: 10_000,
28
+ timezone: 'Z',
29
+ multipleStatements: false,
30
+ });
31
+ }
32
+ async run(body) {
33
+ const conn = await this.pool.getConnection();
34
+ try {
35
+ await conn.beginTransaction();
36
+ const query = (sql, params) => params === undefined ? conn.query(sql) : conn.query(sql, params);
37
+ const result = await body(query);
38
+ await conn.commit();
39
+ return result;
40
+ }
41
+ catch (err) {
42
+ try {
43
+ await conn.rollback();
44
+ }
45
+ catch {
46
+ // subordinate to the original error
47
+ }
48
+ throw err;
49
+ }
50
+ finally {
51
+ await conn.release();
52
+ }
53
+ }
54
+ async close() {
55
+ await this.pool.end();
56
+ }
57
+ }
58
+ exports.MariaDbUnitOfWork = MariaDbUnitOfWork;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mariadb-unit-of-work.js","sourceRoot":"","sources":["../../src/adapters/mariadb-unit-of-work.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;;;;AAEH,sDAA6C;AAc7C,MAAa,iBAAiB;IACX,IAAI,CAAO;IAE5B,YAAY,MAAsB;QAChC,IAAI,CAAC,IAAI,GAAG,iBAAO,CAAC,UAAU,CAAC;YAC7B,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,eAAe,EAAE,MAAM,CAAC,eAAe;YACvC,cAAc,EAAE,MAAM;YACtB,QAAQ,EAAE,GAAG;YACb,kBAAkB,EAAE,KAAK;SAC1B,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,GAAG,CAAI,IAAoC;QAC/C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;QAC7C,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC9B,MAAM,KAAK,GAAY,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CACrC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,MAAmB,CAAC,CAAC;YAChF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC;YACjC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;YACpB,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;YACxB,CAAC;YAAC,MAAM,CAAC;gBACP,oCAAoC;YACtC,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;gBAAS,CAAC;YACT,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACvB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;IACxB,CAAC;CACF;AAzCD,8CAyCC"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * `@AccessBundle` — a metadata-only class decorator that lets a feature declare a
3
+ * NAMED, admin-grantable permission bundle (a friendly label over a set of atomic
4
+ * permission keys), and {@link reflectAccessBundle} that reads it back into a flat
5
+ * {@link AccessBundleSpec}.
6
+ *
7
+ * Mirrors `@ObjectType` in `./object-type.decorator.ts` EXACTLY: a private `Symbol`
8
+ * metadata key, `reflect-metadata` used DIRECTLY (bare `Reflect.defineMetadata` — no
9
+ * Nest dependency), and the SAME omit-when-absent assembly discipline (`description`
10
+ * is set on the result ONLY when present, never as an `undefined` key).
11
+ *
12
+ * IMPORTANT — the decorated class is a METADATA CARRIER ONLY. It is NEVER
13
+ * instantiated: {@link reflectAccessBundle} takes the CONSTRUCTOR (`Function`), reads
14
+ * one `Reflect` key off it, and never calls `new cls()`.
15
+ *
16
+ * The bundle expands to atomic permission keys at reflect time:
17
+ * `permissionKeys = opts.permissions.flatMap(p => p.actions.map(a =>
18
+ * `${opts.module}:${p.typeKey}:${a}`))`, de-duped through a `Set` so the first-seen
19
+ * order is preserved (a repeated `module:typeKey:action` collapses to one entry).
20
+ */
21
+ import 'reflect-metadata';
22
+ /**
23
+ * One permission line inside a bundle: a `typeKey` (the object/resource type) and the
24
+ * `actions` on it. Each `(typeKey, action)` pair expands to a `module:typeKey:action`
25
+ * atomic key at reflect time.
26
+ */
27
+ export interface AccessBundlePermission {
28
+ readonly typeKey: string;
29
+ readonly actions: readonly string[];
30
+ }
31
+ /** Class-level options for `@AccessBundle`. */
32
+ export interface AccessBundleOptions {
33
+ readonly key: string;
34
+ readonly module: string;
35
+ readonly label: string;
36
+ readonly description?: string;
37
+ readonly permissions: readonly AccessBundlePermission[];
38
+ }
39
+ /**
40
+ * The reflected result: the bundle's identity + its flat, de-duped, declaration-ordered
41
+ * atomic key set. `description` is OMITTED when absent (never an `undefined` key).
42
+ */
43
+ export interface AccessBundleSpec {
44
+ readonly key: string;
45
+ readonly module: string;
46
+ readonly label: string;
47
+ readonly description?: string;
48
+ readonly permissionKeys: readonly string[];
49
+ }
50
+ /**
51
+ * `@AccessBundle({ key, module, label, description?, permissions })` — declare a named
52
+ * permission bundle on a class. Stores the options verbatim under
53
+ * {@link ACCESS_BUNDLE_META} on the constructor.
54
+ */
55
+ export declare function AccessBundle(opts: AccessBundleOptions): ClassDecorator;
56
+ /**
57
+ * Read an {@link AccessBundleSpec} back out of a class decorated with `@AccessBundle`.
58
+ * Takes the CONSTRUCTOR (`Function`); never instantiates it.
59
+ *
60
+ * Throws {@link RegistrationError} when the class is undecorated or the options are
61
+ * empty (missing `key` / `module` / `label` / `permissions`). Assembles the result
62
+ * field-by-field with an explicit omit guard on `description` (omit-when-absent, mirror
63
+ * `reflectObjectType`).
64
+ */
65
+ export declare function reflectAccessBundle(cls: Function): AccessBundleSpec;
66
+ //# sourceMappingURL=access-bundle.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access-bundle.decorator.d.ts","sourceRoot":"","sources":["../../src/decorators/access-bundle.decorator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,kBAAkB,CAAC;AAW1B;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,+CAA+C;AAC/C,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,SAAS,sBAAsB,EAAE,CAAC;CACzD;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5C;AAID;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,cAAc,CAItE;AAID;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,QAAQ,GAAG,gBAAgB,CAsCnE"}
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ /**
3
+ * `@AccessBundle` — a metadata-only class decorator that lets a feature declare a
4
+ * NAMED, admin-grantable permission bundle (a friendly label over a set of atomic
5
+ * permission keys), and {@link reflectAccessBundle} that reads it back into a flat
6
+ * {@link AccessBundleSpec}.
7
+ *
8
+ * Mirrors `@ObjectType` in `./object-type.decorator.ts` EXACTLY: a private `Symbol`
9
+ * metadata key, `reflect-metadata` used DIRECTLY (bare `Reflect.defineMetadata` — no
10
+ * Nest dependency), and the SAME omit-when-absent assembly discipline (`description`
11
+ * is set on the result ONLY when present, never as an `undefined` key).
12
+ *
13
+ * IMPORTANT — the decorated class is a METADATA CARRIER ONLY. It is NEVER
14
+ * instantiated: {@link reflectAccessBundle} takes the CONSTRUCTOR (`Function`), reads
15
+ * one `Reflect` key off it, and never calls `new cls()`.
16
+ *
17
+ * The bundle expands to atomic permission keys at reflect time:
18
+ * `permissionKeys = opts.permissions.flatMap(p => p.actions.map(a =>
19
+ * `${opts.module}:${p.typeKey}:${a}`))`, de-duped through a `Set` so the first-seen
20
+ * order is preserved (a repeated `module:typeKey:action` collapses to one entry).
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.AccessBundle = AccessBundle;
24
+ exports.reflectAccessBundle = reflectAccessBundle;
25
+ require("reflect-metadata");
26
+ const errors_js_1 = require("../errors.js");
27
+ // --- Private metadata key (Symbol) — mirror OBJECT_TYPE_META -------------------
28
+ /** Class-level: the {@link AccessBundleOptions} passed to `@AccessBundle`. */
29
+ const ACCESS_BUNDLE_META = Symbol('astram:engine:accessBundle');
30
+ // --- Decorator ---------------------------------------------------------------
31
+ /**
32
+ * `@AccessBundle({ key, module, label, description?, permissions })` — declare a named
33
+ * permission bundle on a class. Stores the options verbatim under
34
+ * {@link ACCESS_BUNDLE_META} on the constructor.
35
+ */
36
+ function AccessBundle(opts) {
37
+ return (target) => {
38
+ Reflect.defineMetadata(ACCESS_BUNDLE_META, opts, target);
39
+ };
40
+ }
41
+ // --- Reflector ---------------------------------------------------------------
42
+ /**
43
+ * Read an {@link AccessBundleSpec} back out of a class decorated with `@AccessBundle`.
44
+ * Takes the CONSTRUCTOR (`Function`); never instantiates it.
45
+ *
46
+ * Throws {@link RegistrationError} when the class is undecorated or the options are
47
+ * empty (missing `key` / `module` / `label` / `permissions`). Assembles the result
48
+ * field-by-field with an explicit omit guard on `description` (omit-when-absent, mirror
49
+ * `reflectObjectType`).
50
+ */
51
+ function reflectAccessBundle(cls) {
52
+ const opts = Reflect.getMetadata(ACCESS_BUNDLE_META, cls);
53
+ if (!opts) {
54
+ throw new errors_js_1.RegistrationError(`${cls.name} is not decorated with @AccessBundle`);
55
+ }
56
+ if (!opts.key || !opts.module || !opts.label) {
57
+ throw new errors_js_1.RegistrationError(`${cls.name} @AccessBundle is missing a required 'key', 'module', or 'label'`);
58
+ }
59
+ if (!opts.permissions || opts.permissions.length === 0) {
60
+ throw new errors_js_1.RegistrationError(`${cls.name} @AccessBundle declares no permissions`);
61
+ }
62
+ // Expand to flat atomic keys, de-duped preserving first-seen order (Set keeps
63
+ // insertion order, and re-inserting an existing key is a no-op).
64
+ const permissionKeys = [
65
+ ...new Set(opts.permissions.flatMap((p) => p.actions.map((a) => `${opts.module}:${p.typeKey}:${a}`))),
66
+ ];
67
+ const out = {
68
+ key: opts.key,
69
+ module: opts.module,
70
+ label: opts.label,
71
+ };
72
+ if (opts.description !== undefined)
73
+ out.description = opts.description;
74
+ out.permissionKeys = permissionKeys;
75
+ return out;
76
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access-bundle.decorator.js","sourceRoot":"","sources":["../../src/decorators/access-bundle.decorator.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;GAmBG;;AAmDH,oCAIC;AAaD,kDAsCC;AAxGD,4BAA0B;AAE1B,4CAAiD;AAEjD,kFAAkF;AAElF,8EAA8E;AAC9E,MAAM,kBAAkB,GAAG,MAAM,CAAC,4BAA4B,CAAC,CAAC;AAmChE,gFAAgF;AAEhF;;;;GAIG;AACH,SAAgB,YAAY,CAAC,IAAyB;IACpD,OAAO,CAAC,MAAM,EAAE,EAAE;QAChB,OAAO,CAAC,cAAc,CAAC,kBAAkB,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAC3D,CAAC,CAAC;AACJ,CAAC;AAED,gFAAgF;AAEhF;;;;;;;;GAQG;AACH,SAAgB,mBAAmB,CAAC,GAAa;IAC/C,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,kBAAkB,EAAE,GAAG,CAE3C,CAAC;IACd,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,6BAAiB,CACzB,GAAG,GAAG,CAAC,IAAI,sCAAsC,CAClD,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QAC7C,MAAM,IAAI,6BAAiB,CACzB,GAAG,GAAG,CAAC,IAAI,kEAAkE,CAC9E,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,6BAAiB,CACzB,GAAG,GAAG,CAAC,IAAI,wCAAwC,CACpD,CAAC;IACJ,CAAC;IAED,8EAA8E;IAC9E,iEAAiE;IACjE,MAAM,cAAc,GAAG;QACrB,GAAG,IAAI,GAAG,CACR,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAC7B,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,EAAE,CAAC,CACzD,CACF;KACF,CAAC;IAEF,MAAM,GAAG,GAA4B;QACnC,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI,CAAC,KAAK;KAClB,CAAC;IACF,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS;QAAE,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;IACvE,GAAG,CAAC,cAAc,GAAG,cAAc,CAAC;IACpC,OAAO,GAAkC,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * `@ObjectType` / `@Field` / `@PrimaryKey` — the decorator layer that lets a
3
+ * feature author an {@link ObjectType} contract as a decorated (metadata-only)
4
+ * class instead of a plain literal, and {@link reflectObjectType} that reads it
5
+ * back into the exact same {@link ObjectType} shape.
6
+ *
7
+ * Mirrors the `@Classified` pattern in @astram/guard (SetMetadata-style
8
+ * `Reflect.defineMetadata` + a private metadata-key), but uses `reflect-metadata`
9
+ * DIRECTLY (bare `Reflect.defineMetadata` — no Nest dependency needed). The engine's
10
+ * tsconfig already has `experimentalDecorators` + `emitDecoratorMetadata` ON and
11
+ * `reflect-metadata` present, so the polyfill is active.
12
+ *
13
+ * IMPORTANT — the decorated class is a METADATA CARRIER ONLY. It is NEVER
14
+ * instantiated: {@link reflectObjectType} takes the CONSTRUCTOR (`Function`), reads
15
+ * three `Reflect` keys off it, and never calls `new cls()`. No instance is created,
16
+ * no field initializers run.
17
+ *
18
+ * GOLDEN INVARIANT — the reflected output must `deepStrictEqual` the equivalent
19
+ * literal INCLUDING the omit-when-absent convention (the literals OMIT
20
+ * `junctions` / `extensible` / `required:false` / absent `trusted` / absent
21
+ * `column` rather than setting `undefined` / `false`). The reflector therefore
22
+ * assembles the result field-by-field with explicit omit guards — NOT by spreading
23
+ * the whole options blob (a spread would carry `junctions` / `extensible` as
24
+ * `undefined` keys and break `deepStrictEqual`).
25
+ */
26
+ import 'reflect-metadata';
27
+ import type { AccessDeclaration, FieldType, IdentityRole, JunctionSpec, ObjectType as ObjectTypeContract, PkStrategy, TrustedSource } from '../types.js';
28
+ /**
29
+ * Class-level options for `@ObjectType`. Mirrors the top-level {@link ObjectType}
30
+ * shape EXCEPT `primaryKey` (declared separately via `@PrimaryKey`) and `fields`
31
+ * (declared per-member via `@Field`).
32
+ *
33
+ * `junctions` is a CLASS-LEVEL option — a junction is an object-owned side-table
34
+ * (keyed by name), not a per-field concept, so it has no home member.
35
+ */
36
+ export interface ObjectTypeOptions {
37
+ readonly typeKey: string;
38
+ readonly table: string;
39
+ readonly schema?: string;
40
+ readonly extensible?: boolean;
41
+ readonly junctions?: readonly JunctionSpec[];
42
+ /**
43
+ * REQUIRED access declaration (tenantScoped/owned/system). Reflected AS-IS onto the
44
+ * {@link ObjectType} contract; the REGISTRY's `validate` (chokepoint 1) rejects an
45
+ * absent/contradictory declaration at boot. See {@link AccessDeclaration}.
46
+ */
47
+ readonly access: AccessDeclaration;
48
+ }
49
+ /** Per-member options for `@Field` (the field's `key` is read from the member name). */
50
+ export interface FieldOptions {
51
+ readonly column?: string;
52
+ readonly type: FieldType;
53
+ readonly required?: boolean;
54
+ readonly trusted?: TrustedSource;
55
+ readonly role?: IdentityRole;
56
+ }
57
+ /**
58
+ * `@PrimaryKey` options — the {@link PkStrategy} discriminated union verbatim, so it
59
+ * is stored + assigned AS-IS (no reconstruction), keeping the `column`-vs-`columns`
60
+ * key set byte-identical to the literal's union member.
61
+ */
62
+ export type PrimaryKeyOptions = PkStrategy;
63
+ /**
64
+ * `@ObjectType({ typeKey, table, access, schema?, junctions?, extensible? })` —
65
+ * declare the class-level object-type metadata. Stores the options verbatim under
66
+ * {@link OBJECT_TYPE_META} on the constructor. `access` is REQUIRED (the registry's
67
+ * `validate` — chokepoint 1 — rejects an absent/contradictory declaration at boot).
68
+ *
69
+ * The generic, framework-neutral decorator name. (The `ObjectType` result data type is
70
+ * referenced internally as `ObjectTypeContract` so the decorator can own this name.)
71
+ */
72
+ export declare function ObjectType(opts: ObjectTypeOptions): ClassDecorator;
73
+ /**
74
+ * DECLARATION MERGING — makes `ObjectType` carry BOTH meanings under one name:
75
+ * - the `function ObjectType(...)` above is the VALUE (the class decorator), and
76
+ * - this `interface ObjectType` (extending the data-shape contract) is the TYPE.
77
+ * A `function` + `interface` of the same name declaration-merge in TypeScript, so
78
+ * `import { ObjectType }` (decorator/value) AND `import type { ObjectType }` (the
79
+ * data shape) both resolve to this single, merged export. This is the mechanism that
80
+ * lets the barrel export ONE `ObjectType` name that is simultaneously the feature-
81
+ * facing decorator and the metadata contract type. `reflectObjectType` keeps
82
+ * returning `ObjectTypeContract`, which is structurally identical to this merged type.
83
+ */
84
+ export interface ObjectType extends ObjectTypeContract {
85
+ }
86
+ /**
87
+ * `@Field({ type, column?, required?, trusted?, role? })` — declare one core field.
88
+ * The field's `key` is the decorated member's property name. Each invocation APPENDS a
89
+ * {@link FieldSpec} to the {@link FIELDS_META} array on the CONSTRUCTOR
90
+ * (`target.constructor` — `target` is the prototype for instance members).
91
+ *
92
+ * ORDER GUARANTEE: property decorators execute in SOURCE (top-to-bottom) member
93
+ * order at class-definition time, and each call read-existing → push → redefine, so
94
+ * the array accumulates in declaration order === the literal's `fields[]` order.
95
+ */
96
+ export declare function Field(opts: FieldOptions): PropertyDecorator;
97
+ /**
98
+ * `@PrimaryKey({ kind:'uuid', column } | { kind:'composite', columns:[...] })` — a
99
+ * SINGLE class-level decorator (never per-member). Stores the options object
100
+ * verbatim under {@link PK_META}; {@link reflectObjectType} assigns it directly to
101
+ * `primaryKey` (no reconstruction), so the result is byte-identical to the literal's
102
+ * {@link PkStrategy} union member. Authoring `columns` as an explicit literal array
103
+ * removes any member-ordering ambiguity for the composite case.
104
+ */
105
+ export declare function PrimaryKey(opts: PrimaryKeyOptions): ClassDecorator;
106
+ /**
107
+ * Read an {@link ObjectType} back out of a class decorated with `@ObjectType` +
108
+ * `@PrimaryKey` + one-or-more `@Field`. Takes the CONSTRUCTOR (`Function`); never
109
+ * instantiates it.
110
+ *
111
+ * The result is assembled field-by-field with explicit omit guards so it
112
+ * `deepStrictEqual`s the equivalent literal (omit-when-absent convention). Key order
113
+ * mirrors the literals for readability/JSON parity (irrelevant to `deepStrictEqual`).
114
+ */
115
+ export declare function reflectObjectType(cls: Function): ObjectTypeContract;
116
+ //# sourceMappingURL=object-type.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-type.decorator.d.ts","sourceRoot":"","sources":["../../src/decorators/object-type.decorator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EACV,iBAAiB,EAEjB,SAAS,EACT,YAAY,EACZ,YAAY,EAKZ,UAAU,IAAI,kBAAkB,EAChC,UAAU,EACV,aAAa,EACd,MAAM,aAAa,CAAC;AAarB;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IAC7C;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;CACpC;AAED,wFAAwF;AACxF,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,YAAY,CAAC;CAC9B;AAED;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,UAAU,CAAC;AAI3C;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,cAAc,CAIlE;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,UAAW,SAAQ,kBAAkB;CAAG;AAEzD;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,YAAY,GAAG,iBAAiB,CAQ3D;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,cAAc,CAIlE;AAqBD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,QAAQ,GAAG,kBAAkB,CAqCnE"}
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ /**
3
+ * `@ObjectType` / `@Field` / `@PrimaryKey` — the decorator layer that lets a
4
+ * feature author an {@link ObjectType} contract as a decorated (metadata-only)
5
+ * class instead of a plain literal, and {@link reflectObjectType} that reads it
6
+ * back into the exact same {@link ObjectType} shape.
7
+ *
8
+ * Mirrors the `@Classified` pattern in @astram/guard (SetMetadata-style
9
+ * `Reflect.defineMetadata` + a private metadata-key), but uses `reflect-metadata`
10
+ * DIRECTLY (bare `Reflect.defineMetadata` — no Nest dependency needed). The engine's
11
+ * tsconfig already has `experimentalDecorators` + `emitDecoratorMetadata` ON and
12
+ * `reflect-metadata` present, so the polyfill is active.
13
+ *
14
+ * IMPORTANT — the decorated class is a METADATA CARRIER ONLY. It is NEVER
15
+ * instantiated: {@link reflectObjectType} takes the CONSTRUCTOR (`Function`), reads
16
+ * three `Reflect` keys off it, and never calls `new cls()`. No instance is created,
17
+ * no field initializers run.
18
+ *
19
+ * GOLDEN INVARIANT — the reflected output must `deepStrictEqual` the equivalent
20
+ * literal INCLUDING the omit-when-absent convention (the literals OMIT
21
+ * `junctions` / `extensible` / `required:false` / absent `trusted` / absent
22
+ * `column` rather than setting `undefined` / `false`). The reflector therefore
23
+ * assembles the result field-by-field with explicit omit guards — NOT by spreading
24
+ * the whole options blob (a spread would carry `junctions` / `extensible` as
25
+ * `undefined` keys and break `deepStrictEqual`).
26
+ */
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.ObjectType = ObjectType;
29
+ exports.Field = Field;
30
+ exports.PrimaryKey = PrimaryKey;
31
+ exports.reflectObjectType = reflectObjectType;
32
+ require("reflect-metadata");
33
+ const errors_js_1 = require("../errors.js");
34
+ // --- Private metadata keys (Symbols) — mirror CLASSIFICATION_METADATA ---------
35
+ /** Class-level: the {@link ObjectTypeOptions} passed to `@ObjectType`. */
36
+ const OBJECT_TYPE_META = Symbol('astram:engine:objectType');
37
+ /** Class-level: the {@link PrimaryKeyOptions} passed to `@PrimaryKey`. */
38
+ const PK_META = Symbol('astram:engine:primaryKey');
39
+ /** Constructor-level: the accumulated array of {@link FieldSpec} from `@Field`. */
40
+ const FIELDS_META = Symbol('astram:engine:fields');
41
+ // --- Decorators --------------------------------------------------------------
42
+ /**
43
+ * `@ObjectType({ typeKey, table, access, schema?, junctions?, extensible? })` —
44
+ * declare the class-level object-type metadata. Stores the options verbatim under
45
+ * {@link OBJECT_TYPE_META} on the constructor. `access` is REQUIRED (the registry's
46
+ * `validate` — chokepoint 1 — rejects an absent/contradictory declaration at boot).
47
+ *
48
+ * The generic, framework-neutral decorator name. (The `ObjectType` result data type is
49
+ * referenced internally as `ObjectTypeContract` so the decorator can own this name.)
50
+ */
51
+ function ObjectType(opts) {
52
+ return (target) => {
53
+ Reflect.defineMetadata(OBJECT_TYPE_META, opts, target);
54
+ };
55
+ }
56
+ /**
57
+ * `@Field({ type, column?, required?, trusted?, role? })` — declare one core field.
58
+ * The field's `key` is the decorated member's property name. Each invocation APPENDS a
59
+ * {@link FieldSpec} to the {@link FIELDS_META} array on the CONSTRUCTOR
60
+ * (`target.constructor` — `target` is the prototype for instance members).
61
+ *
62
+ * ORDER GUARANTEE: property decorators execute in SOURCE (top-to-bottom) member
63
+ * order at class-definition time, and each call read-existing → push → redefine, so
64
+ * the array accumulates in declaration order === the literal's `fields[]` order.
65
+ */
66
+ function Field(opts) {
67
+ return (target, propertyKey) => {
68
+ const ctor = target.constructor;
69
+ const existing = Reflect.getMetadata(FIELDS_META, ctor) ?? [];
70
+ const spec = { key: String(propertyKey), ...opts };
71
+ Reflect.defineMetadata(FIELDS_META, [...existing, spec], ctor);
72
+ };
73
+ }
74
+ /**
75
+ * `@PrimaryKey({ kind:'uuid', column } | { kind:'composite', columns:[...] })` — a
76
+ * SINGLE class-level decorator (never per-member). Stores the options object
77
+ * verbatim under {@link PK_META}; {@link reflectObjectType} assigns it directly to
78
+ * `primaryKey` (no reconstruction), so the result is byte-identical to the literal's
79
+ * {@link PkStrategy} union member. Authoring `columns` as an explicit literal array
80
+ * removes any member-ordering ambiguity for the composite case.
81
+ */
82
+ function PrimaryKey(opts) {
83
+ return (target) => {
84
+ Reflect.defineMetadata(PK_META, opts, target);
85
+ };
86
+ }
87
+ // --- Reflector ---------------------------------------------------------------
88
+ /**
89
+ * Rebuild one {@link FieldSpec} from its stored spec, applying the per-field
90
+ * omit-when-absent rule so `required:false` / absent `trusted` / absent `role` /
91
+ * absent `column` never appear as keys (they must be OMITTED to `deepStrictEqual`
92
+ * the literals). Key order kept `{ key, column, type, required?, trusted?, role? }`
93
+ * to mirror the literals.
94
+ */
95
+ function reflectField(spec) {
96
+ const f = { key: spec.key };
97
+ if (spec.column !== undefined)
98
+ f.column = spec.column;
99
+ f.type = spec.type;
100
+ if (spec.required === true)
101
+ f.required = true;
102
+ if (spec.trusted !== undefined)
103
+ f.trusted = spec.trusted;
104
+ if (spec.role !== undefined)
105
+ f.role = spec.role;
106
+ return f;
107
+ }
108
+ /**
109
+ * Read an {@link ObjectType} back out of a class decorated with `@ObjectType` +
110
+ * `@PrimaryKey` + one-or-more `@Field`. Takes the CONSTRUCTOR (`Function`); never
111
+ * instantiates it.
112
+ *
113
+ * The result is assembled field-by-field with explicit omit guards so it
114
+ * `deepStrictEqual`s the equivalent literal (omit-when-absent convention). Key order
115
+ * mirrors the literals for readability/JSON parity (irrelevant to `deepStrictEqual`).
116
+ */
117
+ function reflectObjectType(cls) {
118
+ const opts = Reflect.getMetadata(OBJECT_TYPE_META, cls);
119
+ if (!opts) {
120
+ throw new errors_js_1.RegistrationError(`${cls.name} is not decorated with @ObjectType`);
121
+ }
122
+ const pk = Reflect.getMetadata(PK_META, cls);
123
+ if (!pk) {
124
+ throw new errors_js_1.RegistrationError(`${cls.name} is not decorated with @PrimaryKey`);
125
+ }
126
+ const fields = Reflect.getMetadata(FIELDS_META, cls) ?? [];
127
+ if (fields.length === 0) {
128
+ throw new errors_js_1.RegistrationError(`${cls.name} declares no @Field members`);
129
+ }
130
+ const out = { typeKey: opts.typeKey, table: opts.table };
131
+ if (opts.schema !== undefined)
132
+ out.schema = opts.schema;
133
+ out.primaryKey = pk; // assigned AS-IS — no reconstruction (union key set preserved)
134
+ out.fields = fields.map(reflectField);
135
+ if (opts.junctions !== undefined)
136
+ out.junctions = opts.junctions;
137
+ if (opts.extensible !== undefined)
138
+ out.extensible = opts.extensible;
139
+ // `access` is REQUIRED on the contract, but reflected with the SAME
140
+ // omit-when-absent discipline as junctions/extensible (assigned AS-IS). If a
141
+ // decorator omits it, the reflected literal likewise omits the key — and the
142
+ // registry's `validate` (chokepoint 1) is what rejects the absence at boot.
143
+ if (opts.access !== undefined)
144
+ out.access = opts.access;
145
+ return out;
146
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-type.decorator.js","sourceRoot":"","sources":["../../src/decorators/object-type.decorator.ts"],"names":[],"mappings":"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"}