@astram/engine 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/adapters/mariadb-audit-sink.d.ts +24 -0
- package/dist/adapters/mariadb-audit-sink.d.ts.map +1 -0
- package/dist/adapters/mariadb-audit-sink.js +40 -0
- package/dist/adapters/mariadb-audit-sink.js.map +1 -0
- package/dist/adapters/mariadb-unit-of-work.d.ts +27 -0
- package/dist/adapters/mariadb-unit-of-work.d.ts.map +1 -0
- package/dist/adapters/mariadb-unit-of-work.js +58 -0
- package/dist/adapters/mariadb-unit-of-work.js.map +1 -0
- package/dist/decorators/access-bundle.decorator.d.ts +66 -0
- package/dist/decorators/access-bundle.decorator.d.ts.map +1 -0
- package/dist/decorators/access-bundle.decorator.js +76 -0
- package/dist/decorators/access-bundle.decorator.js.map +1 -0
- package/dist/decorators/object-type.decorator.d.ts +116 -0
- package/dist/decorators/object-type.decorator.d.ts.map +1 -0
- package/dist/decorators/object-type.decorator.js +146 -0
- package/dist/decorators/object-type.decorator.js.map +1 -0
- package/dist/errors.d.ts +46 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +67 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +80 -0
- package/dist/index.js.map +1 -0
- package/dist/module/engine.module.d.ts +90 -0
- package/dist/module/engine.module.d.ts.map +1 -0
- package/dist/module/engine.module.js +218 -0
- package/dist/module/engine.module.js.map +1 -0
- package/dist/module/tokens.d.ts +12 -0
- package/dist/module/tokens.d.ts.map +1 -0
- package/dist/module/tokens.js +14 -0
- package/dist/pipe/context.d.ts +55 -0
- package/dist/pipe/context.d.ts.map +1 -0
- package/dist/pipe/context.js +18 -0
- package/dist/pipe/context.js.map +1 -0
- package/dist/pipe/record-engine.d.ts +72 -0
- package/dist/pipe/record-engine.d.ts.map +1 -0
- package/dist/pipe/record-engine.js +279 -0
- package/dist/pipe/record-engine.js.map +1 -0
- package/dist/pipe/records-service.d.ts +47 -0
- package/dist/pipe/records-service.d.ts.map +1 -0
- package/dist/pipe/records-service.js +108 -0
- package/dist/pipe/stages/audit.d.ts +15 -0
- package/dist/pipe/stages/audit.d.ts.map +1 -0
- package/dist/pipe/stages/audit.js +42 -0
- package/dist/pipe/stages/audit.js.map +1 -0
- package/dist/pipe/stages/derive-write-context.d.ts +22 -0
- package/dist/pipe/stages/derive-write-context.d.ts.map +1 -0
- package/dist/pipe/stages/derive-write-context.js +61 -0
- package/dist/pipe/stages/derive-write-context.js.map +1 -0
- package/dist/pipe/stages/fetch-owner.d.ts +28 -0
- package/dist/pipe/stages/fetch-owner.d.ts.map +1 -0
- package/dist/pipe/stages/fetch-owner.js +90 -0
- package/dist/pipe/stages/fetch-owner.js.map +1 -0
- package/dist/pipe/stages/junction-sync.d.ts +14 -0
- package/dist/pipe/stages/junction-sync.d.ts.map +1 -0
- package/dist/pipe/stages/junction-sync.js +63 -0
- package/dist/pipe/stages/junction-sync.js.map +1 -0
- package/dist/pipe/stages/permission-gate.d.ts +11 -0
- package/dist/pipe/stages/permission-gate.d.ts.map +1 -0
- package/dist/pipe/stages/permission-gate.js +53 -0
- package/dist/pipe/stages/permission-gate.js.map +1 -0
- package/dist/pipe/stages/validate.d.ts +11 -0
- package/dist/pipe/stages/validate.d.ts.map +1 -0
- package/dist/pipe/stages/validate.js +89 -0
- package/dist/pipe/stages/validate.js.map +1 -0
- package/dist/pipe/stages/write.d.ts +12 -0
- package/dist/pipe/stages/write.d.ts.map +1 -0
- package/dist/pipe/stages/write.js +109 -0
- package/dist/pipe/stages/write.js.map +1 -0
- package/dist/ports.d.ts +37 -0
- package/dist/ports.d.ts.map +1 -0
- package/dist/ports.js +11 -0
- package/dist/ports.js.map +1 -0
- package/dist/registry.d.ts +42 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +184 -0
- package/dist/registry.js.map +1 -0
- package/dist/types.d.ts +207 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +15 -0
- package/dist/types.js.map +1 -0
- package/package.json +49 -0
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed engine errors. All fail-closed: a stage that throws aborts the pipe and
|
|
3
|
+
* rolls back the transaction (no partial write, no silent degrade).
|
|
4
|
+
*/
|
|
5
|
+
/** Base for all engine-originated errors (lets consumers catch the whole family). */
|
|
6
|
+
export declare class EngineError extends Error {
|
|
7
|
+
constructor(message: string);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The GAP-FLAG STOP (§design 2.8). Thrown when a feature asks the pipe for a
|
|
11
|
+
* capability it does not implement (an unsupported field type, an operation no
|
|
12
|
+
* stage handles). It is LOUD and typed — never a silent degrade. The AI guide
|
|
13
|
+
* (ADR-0025) instructs: on this error STOP, record the gap in `.astram/gaps.md`,
|
|
14
|
+
* and do NOT work around it (the wall makes a workaround impossible anyway). The
|
|
15
|
+
* framework fills the capability properly, then the feature resumes.
|
|
16
|
+
*/
|
|
17
|
+
export declare class CapabilityNotSupportedError extends EngineError {
|
|
18
|
+
/** The missing capability (stable, greppable — e.g. `fieldType:geo`). */
|
|
19
|
+
readonly capability: string;
|
|
20
|
+
constructor(
|
|
21
|
+
/** The missing capability (stable, greppable — e.g. `fieldType:geo`). */
|
|
22
|
+
capability: string,
|
|
23
|
+
/** Human detail naming what was attempted. */
|
|
24
|
+
detail: string);
|
|
25
|
+
}
|
|
26
|
+
/** A registration/config error (a feature declared something invalid). */
|
|
27
|
+
export declare class RegistrationError extends EngineError {
|
|
28
|
+
}
|
|
29
|
+
/** The permission gate denied the operation (fail-closed). Carries the reason code. */
|
|
30
|
+
export declare class AuthorizationDeniedError extends EngineError {
|
|
31
|
+
readonly reason: string;
|
|
32
|
+
constructor(reason: string, detail?: string);
|
|
33
|
+
}
|
|
34
|
+
/** Input failed validation (types/required/unknown-field). */
|
|
35
|
+
export declare class ValidationError extends EngineError {
|
|
36
|
+
readonly field: string;
|
|
37
|
+
constructor(field: string, detail: string);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A transient/unavailable failure (the decision authority or store signalled it
|
|
41
|
+
* cannot answer right now — ADR-0016). Distinct from a deny: callers may retry.
|
|
42
|
+
* Fail-closed: the operation still does not proceed.
|
|
43
|
+
*/
|
|
44
|
+
export declare class EngineUnavailableError extends EngineError {
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,qFAAqF;AACrF,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;GAOG;AACH,qBAAa,2BAA4B,SAAQ,WAAW;IAExD,yEAAyE;IACzE,QAAQ,CAAC,UAAU,EAAE,MAAM;;IAD3B,yEAAyE;IAChE,UAAU,EAAE,MAAM;IAC3B,8CAA8C;IAC9C,MAAM,EAAE,MAAM;CAQjB;AAED,0EAA0E;AAC1E,qBAAa,iBAAkB,SAAQ,WAAW;CAAG;AAErD,uFAAuF;AACvF,qBAAa,wBAAyB,SAAQ,WAAW;IAErD,QAAQ,CAAC,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM,EACvB,MAAM,CAAC,EAAE,MAAM;CAIlB;AAED,8DAA8D;AAC9D,qBAAa,eAAgB,SAAQ,WAAW;IAE5C,QAAQ,CAAC,KAAK,EAAE,MAAM;gBAAb,KAAK,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM;CAIjB;AAED;;;;GAIG;AACH,qBAAa,sBAAuB,SAAQ,WAAW;CAAG"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Typed engine errors. All fail-closed: a stage that throws aborts the pipe and
|
|
4
|
+
* rolls back the transaction (no partial write, no silent degrade).
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.EngineUnavailableError = exports.ValidationError = exports.AuthorizationDeniedError = exports.RegistrationError = exports.CapabilityNotSupportedError = exports.EngineError = void 0;
|
|
8
|
+
/** Base for all engine-originated errors (lets consumers catch the whole family). */
|
|
9
|
+
class EngineError extends Error {
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = new.target.name;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.EngineError = EngineError;
|
|
16
|
+
/**
|
|
17
|
+
* The GAP-FLAG STOP (§design 2.8). Thrown when a feature asks the pipe for a
|
|
18
|
+
* capability it does not implement (an unsupported field type, an operation no
|
|
19
|
+
* stage handles). It is LOUD and typed — never a silent degrade. The AI guide
|
|
20
|
+
* (ADR-0025) instructs: on this error STOP, record the gap in `.astram/gaps.md`,
|
|
21
|
+
* and do NOT work around it (the wall makes a workaround impossible anyway). The
|
|
22
|
+
* framework fills the capability properly, then the feature resumes.
|
|
23
|
+
*/
|
|
24
|
+
class CapabilityNotSupportedError extends EngineError {
|
|
25
|
+
capability;
|
|
26
|
+
constructor(
|
|
27
|
+
/** The missing capability (stable, greppable — e.g. `fieldType:geo`). */
|
|
28
|
+
capability,
|
|
29
|
+
/** Human detail naming what was attempted. */
|
|
30
|
+
detail) {
|
|
31
|
+
super(`CapabilityNotSupported [${capability}]: ${detail}. ` +
|
|
32
|
+
`STOP — this is a framework gap. Record it in .astram/gaps.md and do NOT ` +
|
|
33
|
+
`work around the pipe; the capability must be added to @astram/engine.`);
|
|
34
|
+
this.capability = capability;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.CapabilityNotSupportedError = CapabilityNotSupportedError;
|
|
38
|
+
/** A registration/config error (a feature declared something invalid). */
|
|
39
|
+
class RegistrationError extends EngineError {
|
|
40
|
+
}
|
|
41
|
+
exports.RegistrationError = RegistrationError;
|
|
42
|
+
/** The permission gate denied the operation (fail-closed). Carries the reason code. */
|
|
43
|
+
class AuthorizationDeniedError extends EngineError {
|
|
44
|
+
reason;
|
|
45
|
+
constructor(reason, detail) {
|
|
46
|
+
super(`Authorization denied [${reason}]${detail ? `: ${detail}` : ''}.`);
|
|
47
|
+
this.reason = reason;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.AuthorizationDeniedError = AuthorizationDeniedError;
|
|
51
|
+
/** Input failed validation (types/required/unknown-field). */
|
|
52
|
+
class ValidationError extends EngineError {
|
|
53
|
+
field;
|
|
54
|
+
constructor(field, detail) {
|
|
55
|
+
super(`Validation failed for '${field}': ${detail}.`);
|
|
56
|
+
this.field = field;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.ValidationError = ValidationError;
|
|
60
|
+
/**
|
|
61
|
+
* A transient/unavailable failure (the decision authority or store signalled it
|
|
62
|
+
* cannot answer right now — ADR-0016). Distinct from a deny: callers may retry.
|
|
63
|
+
* Fail-closed: the operation still does not proceed.
|
|
64
|
+
*/
|
|
65
|
+
class EngineUnavailableError extends EngineError {
|
|
66
|
+
}
|
|
67
|
+
exports.EngineUnavailableError = EngineUnavailableError;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,qFAAqF;AACrF,MAAa,WAAY,SAAQ,KAAK;IACpC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;IAC9B,CAAC;CACF;AALD,kCAKC;AAED;;;;;;;GAOG;AACH,MAAa,2BAA4B,SAAQ,WAAW;IAG/C;IAFX;IACE,yEAAyE;IAChE,UAAkB;IAC3B,8CAA8C;IAC9C,MAAc;QAEd,KAAK,CACH,2BAA2B,UAAU,MAAM,MAAM,IAAI;YACnD,0EAA0E;YAC1E,uEAAuE,CAC1E,CAAC;QARO,eAAU,GAAV,UAAU,CAAQ;IAS7B,CAAC;CACF;AAbD,kEAaC;AAED,0EAA0E;AAC1E,MAAa,iBAAkB,SAAQ,WAAW;CAAG;AAArD,8CAAqD;AAErD,uFAAuF;AACvF,MAAa,wBAAyB,SAAQ,WAAW;IAE5C;IADX,YACW,MAAc,EACvB,MAAe;QAEf,KAAK,CAAC,yBAAyB,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAHhE,WAAM,GAAN,MAAM,CAAQ;IAIzB,CAAC;CACF;AAPD,4DAOC;AAED,8DAA8D;AAC9D,MAAa,eAAgB,SAAQ,WAAW;IAEnC;IADX,YACW,KAAa,EACtB,MAAc;QAEd,KAAK,CAAC,0BAA0B,KAAK,MAAM,MAAM,GAAG,CAAC,CAAC;QAH7C,UAAK,GAAL,KAAK,CAAQ;IAIxB,CAAC;CACF;AAPD,0CAOC;AAED;;;;GAIG;AACH,MAAa,sBAAuB,SAAQ,WAAW;CAAG;AAA1D,wDAA0D"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @astram/engine — the paved road. Public surface.
|
|
3
|
+
*
|
|
4
|
+
* FEATURE-FACING (what a feature uses):
|
|
5
|
+
* - `RECORD_ENGINE` token + `RecordEngine` interface — the ONE data-access facade.
|
|
6
|
+
* - `ObjectType` / `FieldSpec` / `JunctionSpec` / `RecordInput` / … — the
|
|
7
|
+
* metadata contract a feature declares.
|
|
8
|
+
* - `EngineModule.forRoot(...)` — wiring (the wall: only the facade is exported to
|
|
9
|
+
* feature scope; the pool/uow/audit stay sealed inside the module).
|
|
10
|
+
* - The typed errors — especially `CapabilityNotSupportedError` (the gap-flag STOP).
|
|
11
|
+
*
|
|
12
|
+
* FRAMEWORK-FACING (composition/tests; a FEATURE must NOT import these — the app's
|
|
13
|
+
* architectural lint rule blocks raw DB access under src/features/**):
|
|
14
|
+
* - the adapters (`MariaDbUnitOfWork`, `MariaDbAuditSink`), the `RecordEngineImpl`,
|
|
15
|
+
* the `TableRegistry`, and the ports. Exported so the framework + integration
|
|
16
|
+
* tests can compose the engine directly; NOT for feature code.
|
|
17
|
+
*/
|
|
18
|
+
export type { RecordEngine, AccessDeclaration, FieldSpec, FieldType, TrustedSource, IdentityRole, PkStrategy, JunctionSpec, RecordInput, RecordId, RecordView, ListQuery, RecordPage, Principal, } from './types.js';
|
|
19
|
+
export { EngineError, CapabilityNotSupportedError, RegistrationError, AuthorizationDeniedError, ValidationError, EngineUnavailableError, } from './errors.js';
|
|
20
|
+
export { ObjectType, Field, PrimaryKey, reflectObjectType, type ObjectTypeOptions, type FieldOptions, type PrimaryKeyOptions, } from './decorators/object-type.decorator.js';
|
|
21
|
+
export { AccessBundle, reflectAccessBundle, type AccessBundleOptions, type AccessBundlePermission, type AccessBundleSpec, } from './decorators/access-bundle.decorator.js';
|
|
22
|
+
export { EngineModule, RECORD_ENGINE, ENGINE_ALL_REGISTRATIONS, ENGINE_ALL_ACCESS_BUNDLES, type EngineModuleOptions, type ObjectTypeInput, } from './module/engine.module.js';
|
|
23
|
+
export { TableRegistry, columnOf, uuidPkColumn, isOwned, ownerColumn, tenantColumnOf, columnForRole, requireRole, } from './registry.js';
|
|
24
|
+
export { RecordEngineImpl, type RecordEngineDeps } from './pipe/record-engine.js';
|
|
25
|
+
export { RecordsService, BoundRecords, type ServiceAccountResolver, SERVICE_ACCOUNT_RESOLVER, } from './pipe/records-service.js';
|
|
26
|
+
export { MariaDbUnitOfWork, type EngineDbConfig, } from './adapters/mariadb-unit-of-work.js';
|
|
27
|
+
export { MariaDbAuditSink, type AuditSinkConfig } from './adapters/mariadb-audit-sink.js';
|
|
28
|
+
export type { QueryFn, UnitOfWork, DecisionPort, AuditPort, } from './ports.js';
|
|
29
|
+
export type { Stage, WriteContext, WriteOp, PipeBase } from './pipe/context.js';
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,YAAY,EACV,YAAY,EACZ,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,aAAa,EACb,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,WAAW,EACX,QAAQ,EACR,UAAU,EACV,SAAS,EACT,UAAU,EACV,SAAS,GACV,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,WAAW,EACX,2BAA2B,EAC3B,iBAAiB,EACjB,wBAAwB,EACxB,eAAe,EACf,sBAAsB,GACvB,MAAM,aAAa,CAAC;AAiBrB,OAAO,EACL,UAAU,EACV,KAAK,EACL,UAAU,EACV,iBAAiB,EACjB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,iBAAiB,GACvB,MAAM,uCAAuC,CAAC;AAO/C,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,gBAAgB,GACtB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,EACL,YAAY,EACZ,aAAa,EACb,wBAAwB,EACxB,yBAAyB,EACzB,KAAK,mBAAmB,EACxB,KAAK,eAAe,GACrB,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EACL,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,OAAO,EACP,WAAW,EACX,cAAc,EACd,aAAa,EACb,WAAW,GACZ,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,EACL,cAAc,EACd,YAAY,EACZ,KAAK,sBAAsB,EAC3B,wBAAwB,GACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,iBAAiB,EACjB,KAAK,cAAc,GACpB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAC1F,YAAY,EACV,OAAO,EACP,UAAU,EACV,YAAY,EACZ,SAAS,GACV,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @astram/engine — the paved road. Public surface.
|
|
4
|
+
*
|
|
5
|
+
* FEATURE-FACING (what a feature uses):
|
|
6
|
+
* - `RECORD_ENGINE` token + `RecordEngine` interface — the ONE data-access facade.
|
|
7
|
+
* - `ObjectType` / `FieldSpec` / `JunctionSpec` / `RecordInput` / … — the
|
|
8
|
+
* metadata contract a feature declares.
|
|
9
|
+
* - `EngineModule.forRoot(...)` — wiring (the wall: only the facade is exported to
|
|
10
|
+
* feature scope; the pool/uow/audit stay sealed inside the module).
|
|
11
|
+
* - The typed errors — especially `CapabilityNotSupportedError` (the gap-flag STOP).
|
|
12
|
+
*
|
|
13
|
+
* FRAMEWORK-FACING (composition/tests; a FEATURE must NOT import these — the app's
|
|
14
|
+
* architectural lint rule blocks raw DB access under src/features/**):
|
|
15
|
+
* - the adapters (`MariaDbUnitOfWork`, `MariaDbAuditSink`), the `RecordEngineImpl`,
|
|
16
|
+
* the `TableRegistry`, and the ports. Exported so the framework + integration
|
|
17
|
+
* tests can compose the engine directly; NOT for feature code.
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.MariaDbAuditSink = exports.MariaDbUnitOfWork = exports.SERVICE_ACCOUNT_RESOLVER = exports.BoundRecords = exports.RecordsService = exports.RecordEngineImpl = exports.requireRole = exports.columnForRole = exports.tenantColumnOf = exports.ownerColumn = exports.isOwned = exports.uuidPkColumn = exports.columnOf = exports.TableRegistry = exports.ENGINE_ALL_ACCESS_BUNDLES = exports.ENGINE_ALL_REGISTRATIONS = exports.RECORD_ENGINE = exports.EngineModule = exports.reflectAccessBundle = exports.AccessBundle = exports.reflectObjectType = exports.PrimaryKey = exports.Field = exports.ObjectType = exports.EngineUnavailableError = exports.ValidationError = exports.AuthorizationDeniedError = exports.RegistrationError = exports.CapabilityNotSupportedError = exports.EngineError = void 0;
|
|
21
|
+
var errors_js_1 = require("./errors.js");
|
|
22
|
+
Object.defineProperty(exports, "EngineError", { enumerable: true, get: function () { return errors_js_1.EngineError; } });
|
|
23
|
+
Object.defineProperty(exports, "CapabilityNotSupportedError", { enumerable: true, get: function () { return errors_js_1.CapabilityNotSupportedError; } });
|
|
24
|
+
Object.defineProperty(exports, "RegistrationError", { enumerable: true, get: function () { return errors_js_1.RegistrationError; } });
|
|
25
|
+
Object.defineProperty(exports, "AuthorizationDeniedError", { enumerable: true, get: function () { return errors_js_1.AuthorizationDeniedError; } });
|
|
26
|
+
Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function () { return errors_js_1.ValidationError; } });
|
|
27
|
+
Object.defineProperty(exports, "EngineUnavailableError", { enumerable: true, get: function () { return errors_js_1.EngineUnavailableError; } });
|
|
28
|
+
// The decorator layer: author an `ObjectType` as a metadata-only decorated class
|
|
29
|
+
// (`@ObjectType` + `@PrimaryKey` + `@Field`). The decorated class IS the object type —
|
|
30
|
+
// a feature passes the CLASS directly to `EngineModule.forFeature([...])` and never
|
|
31
|
+
// reflects it itself.
|
|
32
|
+
//
|
|
33
|
+
// NOTE: `ObjectType` here is the SINGLE public source of that name — it is BOTH the
|
|
34
|
+
// class DECORATOR (a value) AND the metadata contract TYPE, unified via DECLARATION
|
|
35
|
+
// MERGING inside the decorator module (a `function ObjectType` + an `interface
|
|
36
|
+
// ObjectType extends ObjectTypeContract` in the same module). So `@ObjectType(...)`
|
|
37
|
+
// (value) and `: ObjectType` (type) both resolve from this one export — the data-shape
|
|
38
|
+
// `ObjectType` is NO LONGER re-exported from ./types (that would be a duplicate name).
|
|
39
|
+
//
|
|
40
|
+
// `reflectObjectType` is FRAMEWORK/COMPOSE-FACING (the engine + tests use it to reflect
|
|
41
|
+
// a class into its `ObjectType` literal); FEATURE code never calls it — `@ObjectType`
|
|
42
|
+
// / `@Field` / `@PrimaryKey` on a class is the whole feature-facing author API.
|
|
43
|
+
var object_type_decorator_js_1 = require("./decorators/object-type.decorator.js");
|
|
44
|
+
Object.defineProperty(exports, "ObjectType", { enumerable: true, get: function () { return object_type_decorator_js_1.ObjectType; } });
|
|
45
|
+
Object.defineProperty(exports, "Field", { enumerable: true, get: function () { return object_type_decorator_js_1.Field; } });
|
|
46
|
+
Object.defineProperty(exports, "PrimaryKey", { enumerable: true, get: function () { return object_type_decorator_js_1.PrimaryKey; } });
|
|
47
|
+
Object.defineProperty(exports, "reflectObjectType", { enumerable: true, get: function () { return object_type_decorator_js_1.reflectObjectType; } });
|
|
48
|
+
// The `@AccessBundle` decorator layer: a feature authors a NAMED, admin-grantable
|
|
49
|
+
// permission bundle as a metadata-only decorated class, collected via
|
|
50
|
+
// `EngineModule.forFeature([...], { accessBundles: [...] })` into
|
|
51
|
+
// `ENGINE_ALL_ACCESS_BUNDLES`. `reflectAccessBundle` reads a class into its flat
|
|
52
|
+
// `AccessBundleSpec` (the engine reflects registered classes internally).
|
|
53
|
+
var access_bundle_decorator_js_1 = require("./decorators/access-bundle.decorator.js");
|
|
54
|
+
Object.defineProperty(exports, "AccessBundle", { enumerable: true, get: function () { return access_bundle_decorator_js_1.AccessBundle; } });
|
|
55
|
+
Object.defineProperty(exports, "reflectAccessBundle", { enumerable: true, get: function () { return access_bundle_decorator_js_1.reflectAccessBundle; } });
|
|
56
|
+
var engine_module_js_1 = require("./module/engine.module.js");
|
|
57
|
+
Object.defineProperty(exports, "EngineModule", { enumerable: true, get: function () { return engine_module_js_1.EngineModule; } });
|
|
58
|
+
Object.defineProperty(exports, "RECORD_ENGINE", { enumerable: true, get: function () { return engine_module_js_1.RECORD_ENGINE; } });
|
|
59
|
+
Object.defineProperty(exports, "ENGINE_ALL_REGISTRATIONS", { enumerable: true, get: function () { return engine_module_js_1.ENGINE_ALL_REGISTRATIONS; } });
|
|
60
|
+
Object.defineProperty(exports, "ENGINE_ALL_ACCESS_BUNDLES", { enumerable: true, get: function () { return engine_module_js_1.ENGINE_ALL_ACCESS_BUNDLES; } });
|
|
61
|
+
// --- Framework-facing (composition + tests; NOT for feature code) ------------
|
|
62
|
+
var registry_js_1 = require("./registry.js");
|
|
63
|
+
Object.defineProperty(exports, "TableRegistry", { enumerable: true, get: function () { return registry_js_1.TableRegistry; } });
|
|
64
|
+
Object.defineProperty(exports, "columnOf", { enumerable: true, get: function () { return registry_js_1.columnOf; } });
|
|
65
|
+
Object.defineProperty(exports, "uuidPkColumn", { enumerable: true, get: function () { return registry_js_1.uuidPkColumn; } });
|
|
66
|
+
Object.defineProperty(exports, "isOwned", { enumerable: true, get: function () { return registry_js_1.isOwned; } });
|
|
67
|
+
Object.defineProperty(exports, "ownerColumn", { enumerable: true, get: function () { return registry_js_1.ownerColumn; } });
|
|
68
|
+
Object.defineProperty(exports, "tenantColumnOf", { enumerable: true, get: function () { return registry_js_1.tenantColumnOf; } });
|
|
69
|
+
Object.defineProperty(exports, "columnForRole", { enumerable: true, get: function () { return registry_js_1.columnForRole; } });
|
|
70
|
+
Object.defineProperty(exports, "requireRole", { enumerable: true, get: function () { return registry_js_1.requireRole; } });
|
|
71
|
+
var record_engine_js_1 = require("./pipe/record-engine.js");
|
|
72
|
+
Object.defineProperty(exports, "RecordEngineImpl", { enumerable: true, get: function () { return record_engine_js_1.RecordEngineImpl; } });
|
|
73
|
+
var records_service_js_1 = require("./pipe/records-service.js");
|
|
74
|
+
Object.defineProperty(exports, "RecordsService", { enumerable: true, get: function () { return records_service_js_1.RecordsService; } });
|
|
75
|
+
Object.defineProperty(exports, "BoundRecords", { enumerable: true, get: function () { return records_service_js_1.BoundRecords; } });
|
|
76
|
+
Object.defineProperty(exports, "SERVICE_ACCOUNT_RESOLVER", { enumerable: true, get: function () { return records_service_js_1.SERVICE_ACCOUNT_RESOLVER; } });
|
|
77
|
+
var mariadb_unit_of_work_js_1 = require("./adapters/mariadb-unit-of-work.js");
|
|
78
|
+
Object.defineProperty(exports, "MariaDbUnitOfWork", { enumerable: true, get: function () { return mariadb_unit_of_work_js_1.MariaDbUnitOfWork; } });
|
|
79
|
+
var mariadb_audit_sink_js_1 = require("./adapters/mariadb-audit-sink.js");
|
|
80
|
+
Object.defineProperty(exports, "MariaDbAuditSink", { enumerable: true, get: function () { return mariadb_audit_sink_js_1.MariaDbAuditSink; } });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;GAgBG;;;AAmBH,yCAOqB;AANnB,wGAAA,WAAW,OAAA;AACX,wHAAA,2BAA2B,OAAA;AAC3B,8GAAA,iBAAiB,OAAA;AACjB,qHAAA,wBAAwB,OAAA;AACxB,4GAAA,eAAe,OAAA;AACf,mHAAA,sBAAsB,OAAA;AAGxB,iFAAiF;AACjF,uFAAuF;AACvF,oFAAoF;AACpF,sBAAsB;AACtB,EAAE;AACF,oFAAoF;AACpF,oFAAoF;AACpF,+EAA+E;AAC/E,oFAAoF;AACpF,uFAAuF;AACvF,uFAAuF;AACvF,EAAE;AACF,wFAAwF;AACxF,sFAAsF;AACtF,gFAAgF;AAChF,kFAQ+C;AAP7C,sHAAA,UAAU,OAAA;AACV,iHAAA,KAAK,OAAA;AACL,sHAAA,UAAU,OAAA;AACV,6HAAA,iBAAiB,OAAA;AAMnB,kFAAkF;AAClF,sEAAsE;AACtE,kEAAkE;AAClE,iFAAiF;AACjF,0EAA0E;AAC1E,sFAMiD;AAL/C,0HAAA,YAAY,OAAA;AACZ,iIAAA,mBAAmB,OAAA;AAMrB,8DAOmC;AANjC,gHAAA,YAAY,OAAA;AACZ,iHAAA,aAAa,OAAA;AACb,4HAAA,wBAAwB,OAAA;AACxB,6HAAA,yBAAyB,OAAA;AAK3B,gFAAgF;AAChF,6CAOuB;AANrB,4GAAA,aAAa,OAAA;AACb,uGAAA,QAAQ,OAAA;AACR,2GAAA,YAAY,OAAA;AACZ,sGAAA,OAAO,OAAA;AACP,0GAAA,WAAW,OAAA;AACX,6GAAA,cAAc,OAAA;AAEhB,4DAAkF;AAAzE,oHAAA,gBAAgB,OAAA;AACzB,8EAG4C;AAF1C,4HAAA,iBAAiB,OAAA;AAGnB,0EAA0F;AAAjF,yHAAA,gBAAgB,OAAA"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `EngineModule` — the paved road's Nest wiring, and the PHYSICAL WALL.
|
|
3
|
+
*
|
|
4
|
+
* It provides EXACTLY ONE thing to feature scope: {@link RECORD_ENGINE} (the
|
|
5
|
+
* RecordEngine facade). The sealed collaborators — the mariadb pool / UnitOfWork,
|
|
6
|
+
* the audit sink, the table registry — are providers INTERNAL to this module and
|
|
7
|
+
* are NOT exported. A feature module that imports EngineModule can inject the engine
|
|
8
|
+
* facade and nothing else; it is never handed a pool, a query runner, or a
|
|
9
|
+
* repository. That absence is the wall (the lint rule in the consumer app is the
|
|
10
|
+
* backstop, not the primary mechanism).
|
|
11
|
+
*
|
|
12
|
+
* The permission-gate stage binds to the framework's single decision authority
|
|
13
|
+
* (guard's `SHIELD_DECISION_AUTHORITY`) — so the engine reuses the ONE decision path,
|
|
14
|
+
* never its own authz logic.
|
|
15
|
+
*
|
|
16
|
+
* OPT-IN + config: `EngineModule.forRoot({ db, registrations })`. The engine opens a
|
|
17
|
+
* live pool, so (like AuthModule) it is imported only by an app that uses the pipe.
|
|
18
|
+
* Registrations are code-registered tables loaded at boot.
|
|
19
|
+
*/
|
|
20
|
+
import { type DynamicModule } from '@nestjs/common';
|
|
21
|
+
import type { QueryFn } from '../ports.js';
|
|
22
|
+
import { RECORD_ENGINE } from './tokens.js';
|
|
23
|
+
import { type EngineDbConfig } from '../adapters/mariadb-unit-of-work.js';
|
|
24
|
+
import { type AuditSinkConfig } from '../adapters/mariadb-audit-sink.js';
|
|
25
|
+
import type { ObjectType } from '../types.js';
|
|
26
|
+
import { type AccessBundleSpec } from '../decorators/access-bundle.decorator.js';
|
|
27
|
+
/**
|
|
28
|
+
* Input accepted by `forFeature` / `forRoot({registrations})`: either a plain
|
|
29
|
+
* {@link ObjectType} literal OR a class decorated with `@ObjectType` (`Function`).
|
|
30
|
+
* The engine normalizes every input to a reflected `ObjectType` internally, so the
|
|
31
|
+
* feature developer passes the DECORATED CLASS directly — no `reflectObjectType(...)`
|
|
32
|
+
* call in feature code.
|
|
33
|
+
*/
|
|
34
|
+
export type ObjectTypeInput = ObjectType | Function;
|
|
35
|
+
/** The ONLY token exported to feature scope — the pipe facade. Defined in
|
|
36
|
+
* `tokens.ts` (extracted there to break a circular import with
|
|
37
|
+
* `records-service.ts` — see that file's module-doc) and re-exported here
|
|
38
|
+
* (imported above + re-exported below) so existing importers of
|
|
39
|
+
* `RECORD_ENGINE` from `engine.module.js` / the barrel are unaffected. */
|
|
40
|
+
export { RECORD_ENGINE };
|
|
41
|
+
/** The MERGED registration set (read-only metadata) — exposed so the consumer app's
|
|
42
|
+
* authz layer can build its own core ModuleRegistry from the SAME collected set,
|
|
43
|
+
* without a static import. A plain (non-multi) provider whose value is a snapshot of
|
|
44
|
+
* every `forFeature` contribution + the `forRoot` base. Not the sealed pool/uow/
|
|
45
|
+
* registry — the wall is intact. */
|
|
46
|
+
export declare const ENGINE_ALL_REGISTRATIONS: unique symbol;
|
|
47
|
+
/** The MERGED access-bundle set (reflected {@link AccessBundleSpec}[]) — exposed so the
|
|
48
|
+
* consumer app's authz layer can reconcile declared bundles into its bundle catalog
|
|
49
|
+
* from the SAME collected set, without a static import. A plain (non-multi) provider
|
|
50
|
+
* whose value is a snapshot of every `forFeature(..., { accessBundles })` contribution.
|
|
51
|
+
* Empty when no feature declares a bundle. */
|
|
52
|
+
export declare const ENGINE_ALL_ACCESS_BUNDLES: unique symbol;
|
|
53
|
+
export interface EngineModuleOptions {
|
|
54
|
+
readonly db: EngineDbConfig;
|
|
55
|
+
/** Optional code-registered tables. Prefer `EngineModule.forFeature([...])` per
|
|
56
|
+
* feature module; anything passed here is merged as a base contribution. */
|
|
57
|
+
readonly registrations?: readonly ObjectTypeInput[];
|
|
58
|
+
readonly audit?: AuditSinkConfig;
|
|
59
|
+
/** Test seam: a deterministic clock. */
|
|
60
|
+
readonly nowMs?: () => number;
|
|
61
|
+
/**
|
|
62
|
+
* OPTIONAL post-delete cascade hook (design §6.2). Called after a successful delete of an
|
|
63
|
+
* `owned`-type record — the consumer wires guard's `makeShareDeleteCascade()` here to
|
|
64
|
+
* remove the record's `record_share` rows (no orphan grants). The engine passes the pipe's
|
|
65
|
+
* TX-BOUND {@link QueryFn} as the FIRST arg so the cascade runs IN THE SAME transaction as
|
|
66
|
+
* the delete (atomic commit/rollback — mirrors `AuditPort.record(event, query)`). ADR-0003:
|
|
67
|
+
* the engine never imports guard; this is the injection seam.
|
|
68
|
+
*/
|
|
69
|
+
readonly onRecordDeleted?: (query: QueryFn, typeKey: string, recordId: string, tenantId: string) => Promise<void>;
|
|
70
|
+
}
|
|
71
|
+
export declare class EngineModule {
|
|
72
|
+
/**
|
|
73
|
+
* Register a feature's object tables from WITHIN the feature's own module:
|
|
74
|
+
* `imports: [EngineModule.forFeature([...myRegistrations])]`. The feature owns its
|
|
75
|
+
* registrations here and never edits the root module. Stateless glue: no pool, not
|
|
76
|
+
* global — it only pushes into the module accumulator (see `featureRegistrations`);
|
|
77
|
+
* `forRoot` reads the accumulated set when it builds the registry.
|
|
78
|
+
*
|
|
79
|
+
* Test/multi-container note: contributions accumulate process-globally; call
|
|
80
|
+
* `EngineModule.resetFeatures()` between isolated container builds in tests.
|
|
81
|
+
*/
|
|
82
|
+
static forFeature(registrations: readonly ObjectTypeInput[], opts?: {
|
|
83
|
+
accessBundles?: readonly (AccessBundleSpec | Function)[];
|
|
84
|
+
}): DynamicModule;
|
|
85
|
+
/** Test seam: clear the accumulated `forFeature` contributions (for isolated
|
|
86
|
+
* container builds — one process normally has one container, so app code needn't). */
|
|
87
|
+
static resetFeatures(): void;
|
|
88
|
+
static forRoot(options: EngineModuleOptions): DynamicModule;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=engine.module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.module.d.ts","sourceRoot":"","sources":["../../src/module/engine.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAEL,KAAK,aAAa,EAKnB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAuC,OAAO,EAAE,MAAM,aAAa,CAAC;AAIhF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,qCAAqC,CAAC;AAC7F,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAC3F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,0CAA0C,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,QAAQ,CAAC;AAMpD;;;;2EAI2E;AAC3E,OAAO,EAAE,aAAa,EAAE,CAAC;AAEzB;;;;qCAIqC;AACrC,eAAO,MAAM,wBAAwB,eAAqC,CAAC;AAE3E;;;;+CAI+C;AAC/C,eAAO,MAAM,yBAAyB,eAAsC,CAAC;AAmC7E,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAC;IAC5B;iFAC6E;IAC7E,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACpD,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAC;IACjC,wCAAwC;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;IAC9B;;;;;;;OAOG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,CACzB,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB;AAYD,qBACa,YAAY;IACvB;;;;;;;;;OASG;IACH,MAAM,CAAC,UAAU,CACf,aAAa,EAAE,SAAS,eAAe,EAAE,EACzC,IAAI,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,SAAS,CAAC,gBAAgB,GAAG,QAAQ,CAAC,EAAE,CAAA;KAAE,GAClE,aAAa;IAMhB;2FACuF;IACvF,MAAM,CAAC,aAAa,IAAI,IAAI;IAK5B,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,mBAAmB,GAAG,aAAa;CAgG5D"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `EngineModule` — the paved road's Nest wiring, and the PHYSICAL WALL.
|
|
4
|
+
*
|
|
5
|
+
* It provides EXACTLY ONE thing to feature scope: {@link RECORD_ENGINE} (the
|
|
6
|
+
* RecordEngine facade). The sealed collaborators — the mariadb pool / UnitOfWork,
|
|
7
|
+
* the audit sink, the table registry — are providers INTERNAL to this module and
|
|
8
|
+
* are NOT exported. A feature module that imports EngineModule can inject the engine
|
|
9
|
+
* facade and nothing else; it is never handed a pool, a query runner, or a
|
|
10
|
+
* repository. That absence is the wall (the lint rule in the consumer app is the
|
|
11
|
+
* backstop, not the primary mechanism).
|
|
12
|
+
*
|
|
13
|
+
* The permission-gate stage binds to the framework's single decision authority
|
|
14
|
+
* (guard's `SHIELD_DECISION_AUTHORITY`) — so the engine reuses the ONE decision path,
|
|
15
|
+
* never its own authz logic.
|
|
16
|
+
*
|
|
17
|
+
* OPT-IN + config: `EngineModule.forRoot({ db, registrations })`. The engine opens a
|
|
18
|
+
* live pool, so (like AuthModule) it is imported only by an app that uses the pipe.
|
|
19
|
+
* Registrations are code-registered tables loaded at boot.
|
|
20
|
+
*/
|
|
21
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
22
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
23
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
24
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
25
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
26
|
+
};
|
|
27
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
28
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
29
|
+
};
|
|
30
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
31
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
32
|
+
};
|
|
33
|
+
var EngineModule_1;
|
|
34
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
+
exports.EngineModule = exports.ENGINE_ALL_ACCESS_BUNDLES = exports.ENGINE_ALL_REGISTRATIONS = exports.RECORD_ENGINE = void 0;
|
|
36
|
+
const common_1 = require("@nestjs/common");
|
|
37
|
+
const guard_1 = require("@astram/guard");
|
|
38
|
+
const registry_js_1 = require("../registry.js");
|
|
39
|
+
const record_engine_js_1 = require("../pipe/record-engine.js");
|
|
40
|
+
const records_service_js_1 = require("../pipe/records-service.js");
|
|
41
|
+
const tokens_js_1 = require("./tokens.js");
|
|
42
|
+
Object.defineProperty(exports, "RECORD_ENGINE", { enumerable: true, get: function () { return tokens_js_1.RECORD_ENGINE; } });
|
|
43
|
+
const mariadb_unit_of_work_js_1 = require("../adapters/mariadb-unit-of-work.js");
|
|
44
|
+
const mariadb_audit_sink_js_1 = require("../adapters/mariadb-audit-sink.js");
|
|
45
|
+
const object_type_decorator_js_1 = require("../decorators/object-type.decorator.js");
|
|
46
|
+
const access_bundle_decorator_js_1 = require("../decorators/access-bundle.decorator.js");
|
|
47
|
+
/** Normalizer: reflect a decorated class → `ObjectType`; pass a literal through. */
|
|
48
|
+
const toObjectType = (i) => typeof i === 'function' ? (0, object_type_decorator_js_1.reflectObjectType)(i) : i;
|
|
49
|
+
/** The MERGED registration set (read-only metadata) — exposed so the consumer app's
|
|
50
|
+
* authz layer can build its own core ModuleRegistry from the SAME collected set,
|
|
51
|
+
* without a static import. A plain (non-multi) provider whose value is a snapshot of
|
|
52
|
+
* every `forFeature` contribution + the `forRoot` base. Not the sealed pool/uow/
|
|
53
|
+
* registry — the wall is intact. */
|
|
54
|
+
exports.ENGINE_ALL_REGISTRATIONS = Symbol('ENGINE_ALL_REGISTRATIONS');
|
|
55
|
+
/** The MERGED access-bundle set (reflected {@link AccessBundleSpec}[]) — exposed so the
|
|
56
|
+
* consumer app's authz layer can reconcile declared bundles into its bundle catalog
|
|
57
|
+
* from the SAME collected set, without a static import. A plain (non-multi) provider
|
|
58
|
+
* whose value is a snapshot of every `forFeature(..., { accessBundles })` contribution.
|
|
59
|
+
* Empty when no feature declares a bundle. */
|
|
60
|
+
exports.ENGINE_ALL_ACCESS_BUNDLES = Symbol('ENGINE_ALL_ACCESS_BUNDLES');
|
|
61
|
+
/** Internal tokens (NOT exported — sealed inside the module = the wall). */
|
|
62
|
+
const ENGINE_UOW = Symbol('ENGINE_UOW');
|
|
63
|
+
const ENGINE_AUDIT = Symbol('ENGINE_AUDIT');
|
|
64
|
+
const ENGINE_REGISTRY = Symbol('ENGINE_REGISTRY');
|
|
65
|
+
/**
|
|
66
|
+
* Module-scoped accumulator for `forFeature` contributions.
|
|
67
|
+
*
|
|
68
|
+
* WHY an accumulator and not a `multi: true` DI token: this NestJS version
|
|
69
|
+
* (@nestjs/core 11.x) has NO multi-provider aggregation — the `multi` flag is
|
|
70
|
+
* silently ignored and same-token providers last-wins overwrite (proven empirically;
|
|
71
|
+
* the injector's `addCustomValue`/`addCustomFactory` never read `provider.multi`). So
|
|
72
|
+
* a DI-collected token cannot gather contributions from sibling `forFeature` modules.
|
|
73
|
+
*
|
|
74
|
+
* The accumulator sidesteps DI entirely: `forFeature(...)` is CALLED at
|
|
75
|
+
* `@Module({ imports: [...] })` array-evaluation time — which, for the whole module
|
|
76
|
+
* tree, completes BEFORE Nest instantiates any provider. So every `forFeature` has
|
|
77
|
+
* pushed its registrations before `forRoot`'s registry factory runs, guaranteeing the
|
|
78
|
+
* full set is present at DI-factory-resolve time (the moment the consumer app's authz
|
|
79
|
+
* layer needs it). Process-global: one Nest container per process is the norm; the
|
|
80
|
+
* test suite resets it between cases.
|
|
81
|
+
*/
|
|
82
|
+
const featureRegistrations = [];
|
|
83
|
+
/**
|
|
84
|
+
* Module-scoped accumulator for `forFeature`'s optional `accessBundles` contributions
|
|
85
|
+
* — mirrors {@link featureRegistrations} (same before-DI, process-global mechanics). A
|
|
86
|
+
* feature passes `@AccessBundle` classes (or reflected specs) as the 2nd `forFeature`
|
|
87
|
+
* arg; `forRoot` reflects + flattens the accumulated set into
|
|
88
|
+
* {@link ENGINE_ALL_ACCESS_BUNDLES}.
|
|
89
|
+
*/
|
|
90
|
+
const accessBundleRegistrations = [];
|
|
91
|
+
/** Lifecycle holder so the sealed pool is closed on shutdown. */
|
|
92
|
+
let EngineLifecycle = class EngineLifecycle {
|
|
93
|
+
uow;
|
|
94
|
+
constructor(uow) {
|
|
95
|
+
this.uow = uow;
|
|
96
|
+
}
|
|
97
|
+
async onModuleDestroy() {
|
|
98
|
+
const maybeClosable = this.uow;
|
|
99
|
+
if (typeof maybeClosable.close === 'function')
|
|
100
|
+
await maybeClosable.close();
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
EngineLifecycle = __decorate([
|
|
104
|
+
(0, common_1.Injectable)(),
|
|
105
|
+
__param(0, (0, common_1.Inject)(ENGINE_UOW)),
|
|
106
|
+
__metadata("design:paramtypes", [Object])
|
|
107
|
+
], EngineLifecycle);
|
|
108
|
+
let EngineModule = EngineModule_1 = class EngineModule {
|
|
109
|
+
/**
|
|
110
|
+
* Register a feature's object tables from WITHIN the feature's own module:
|
|
111
|
+
* `imports: [EngineModule.forFeature([...myRegistrations])]`. The feature owns its
|
|
112
|
+
* registrations here and never edits the root module. Stateless glue: no pool, not
|
|
113
|
+
* global — it only pushes into the module accumulator (see `featureRegistrations`);
|
|
114
|
+
* `forRoot` reads the accumulated set when it builds the registry.
|
|
115
|
+
*
|
|
116
|
+
* Test/multi-container note: contributions accumulate process-globally; call
|
|
117
|
+
* `EngineModule.resetFeatures()` between isolated container builds in tests.
|
|
118
|
+
*/
|
|
119
|
+
static forFeature(registrations, opts) {
|
|
120
|
+
featureRegistrations.push(registrations);
|
|
121
|
+
accessBundleRegistrations.push(opts?.accessBundles ?? []);
|
|
122
|
+
return { module: EngineModule_1 };
|
|
123
|
+
}
|
|
124
|
+
/** Test seam: clear the accumulated `forFeature` contributions (for isolated
|
|
125
|
+
* container builds — one process normally has one container, so app code needn't). */
|
|
126
|
+
static resetFeatures() {
|
|
127
|
+
featureRegistrations.length = 0;
|
|
128
|
+
accessBundleRegistrations.length = 0;
|
|
129
|
+
}
|
|
130
|
+
static forRoot(options) {
|
|
131
|
+
// The full set = every forFeature contribution (already accumulated at module-eval
|
|
132
|
+
// time, before DI) + an optional forRoot base (backward-compat with the old
|
|
133
|
+
// `forRoot({registrations})` API). Snapshot NOW (forRoot-call time is after all
|
|
134
|
+
// sibling forFeature calls in the same @Module imports have evaluated).
|
|
135
|
+
const allRegistrations = [
|
|
136
|
+
...featureRegistrations.flat(),
|
|
137
|
+
...(options.registrations ?? []),
|
|
138
|
+
].map(toObjectType);
|
|
139
|
+
// The registry is built from the merged set. Behavior identical to before — same
|
|
140
|
+
// register() per registration (just from the collected set, not options only).
|
|
141
|
+
const registryProvider = {
|
|
142
|
+
provide: ENGINE_REGISTRY,
|
|
143
|
+
useFactory: () => {
|
|
144
|
+
const registry = new registry_js_1.TableRegistry();
|
|
145
|
+
for (const reg of allRegistrations)
|
|
146
|
+
registry.register(reg);
|
|
147
|
+
return registry;
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
// The merged registration set, exposed (read-only) for the app's authz layer to
|
|
151
|
+
// build its own core ModuleRegistry from the same set (no static import).
|
|
152
|
+
const allRegistrationsProvider = {
|
|
153
|
+
provide: exports.ENGINE_ALL_REGISTRATIONS,
|
|
154
|
+
useValue: allRegistrations,
|
|
155
|
+
};
|
|
156
|
+
// Snapshot the accumulated access-bundle contributions (same before-DI ordering
|
|
157
|
+
// guarantee as registrations): reflect any decorated class → `AccessBundleSpec`,
|
|
158
|
+
// pass a literal spec through. Exposed read-only for the app's bundle reconciler.
|
|
159
|
+
const allBundles = accessBundleRegistrations
|
|
160
|
+
.flat()
|
|
161
|
+
.map((b) => (typeof b === 'function' ? (0, access_bundle_decorator_js_1.reflectAccessBundle)(b) : b));
|
|
162
|
+
const allBundlesProvider = {
|
|
163
|
+
provide: exports.ENGINE_ALL_ACCESS_BUNDLES,
|
|
164
|
+
useValue: allBundles,
|
|
165
|
+
};
|
|
166
|
+
const uowProvider = {
|
|
167
|
+
provide: ENGINE_UOW,
|
|
168
|
+
useFactory: () => new mariadb_unit_of_work_js_1.MariaDbUnitOfWork(options.db),
|
|
169
|
+
};
|
|
170
|
+
const auditProvider = {
|
|
171
|
+
provide: ENGINE_AUDIT,
|
|
172
|
+
useFactory: () => new mariadb_audit_sink_js_1.MariaDbAuditSink(options.audit ?? {}),
|
|
173
|
+
};
|
|
174
|
+
const engineProvider = {
|
|
175
|
+
provide: tokens_js_1.RECORD_ENGINE,
|
|
176
|
+
useFactory: (registry, uow, decision, audit) => {
|
|
177
|
+
const deps = {
|
|
178
|
+
registry,
|
|
179
|
+
uow,
|
|
180
|
+
decision,
|
|
181
|
+
audit,
|
|
182
|
+
...(options.nowMs ? { nowMs: options.nowMs } : {}),
|
|
183
|
+
...(options.onRecordDeleted ? { onRecordDeleted: options.onRecordDeleted } : {}),
|
|
184
|
+
};
|
|
185
|
+
return new record_engine_js_1.RecordEngineImpl(deps);
|
|
186
|
+
},
|
|
187
|
+
inject: [ENGINE_REGISTRY, ENGINE_UOW, guard_1.SHIELD_DECISION_AUTHORITY, ENGINE_AUDIT],
|
|
188
|
+
};
|
|
189
|
+
return {
|
|
190
|
+
module: EngineModule_1,
|
|
191
|
+
// Global so the RecordEngine facade is injectable app-wide (like the framework's
|
|
192
|
+
// decision authority) — a feature module injects RECORD_ENGINE without having to
|
|
193
|
+
// re-import a dynamic forRoot(). The WALL is unaffected: still ONLY the facade is
|
|
194
|
+
// exported; the sealed pool/uow/audit/registry remain module-internal.
|
|
195
|
+
global: true,
|
|
196
|
+
providers: [
|
|
197
|
+
registryProvider,
|
|
198
|
+
allRegistrationsProvider,
|
|
199
|
+
allBundlesProvider,
|
|
200
|
+
uowProvider,
|
|
201
|
+
auditProvider,
|
|
202
|
+
engineProvider,
|
|
203
|
+
EngineLifecycle,
|
|
204
|
+
records_service_js_1.RecordsService,
|
|
205
|
+
],
|
|
206
|
+
// ⛔ WALL: the facade + the read-only merged registration + access-bundle lists.
|
|
207
|
+
// The pool/uow/audit/registry tokens are deliberately NOT exported — feature
|
|
208
|
+
// scope cannot inject them. `RecordsService` is a request-scoped facade OVER
|
|
209
|
+
// `RECORD_ENGINE` (reads req.principal, forwards as actor) — safe to export
|
|
210
|
+
// alongside it; it exposes no pool/uow/audit/registry access itself.
|
|
211
|
+
exports: [tokens_js_1.RECORD_ENGINE, exports.ENGINE_ALL_REGISTRATIONS, exports.ENGINE_ALL_ACCESS_BUNDLES, records_service_js_1.RecordsService],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
exports.EngineModule = EngineModule;
|
|
216
|
+
exports.EngineModule = EngineModule = EngineModule_1 = __decorate([
|
|
217
|
+
(0, common_1.Module)({})
|
|
218
|
+
], EngineModule);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.module.js","sourceRoot":"","sources":["../../src/module/engine.module.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DI tokens shared between `engine.module.ts` and framework-facing consumers
|
|
3
|
+
* (e.g. `records-service.ts`) that need the token WITHOUT importing the module
|
|
4
|
+
* file itself. Extracted to break a circular import: `engine.module.ts` will
|
|
5
|
+
* import `RecordsService` (to register + export it), and `records-service.ts`
|
|
6
|
+
* needs `RECORD_ENGINE` — if the token stayed in `engine.module.ts`, that would
|
|
7
|
+
* be a cycle. Both files import the token from here instead; only
|
|
8
|
+
* `engine.module.ts` imports `RecordsService` (one direction).
|
|
9
|
+
*/
|
|
10
|
+
/** The ONLY token exported to feature scope — the pipe facade. */
|
|
11
|
+
export declare const RECORD_ENGINE: unique symbol;
|
|
12
|
+
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/module/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,kEAAkE;AAClE,eAAO,MAAM,aAAa,eAA0B,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* DI tokens shared between `engine.module.ts` and framework-facing consumers
|
|
4
|
+
* (e.g. `records-service.ts`) that need the token WITHOUT importing the module
|
|
5
|
+
* file itself. Extracted to break a circular import: `engine.module.ts` will
|
|
6
|
+
* import `RecordsService` (to register + export it), and `records-service.ts`
|
|
7
|
+
* needs `RECORD_ENGINE` — if the token stayed in `engine.module.ts`, that would
|
|
8
|
+
* be a cycle. Both files import the token from here instead; only
|
|
9
|
+
* `engine.module.ts` imports `RecordsService` (one direction).
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.RECORD_ENGINE = void 0;
|
|
13
|
+
/** The ONLY token exported to feature scope — the pipe facade. */
|
|
14
|
+
exports.RECORD_ENGINE = Symbol('RECORD_ENGINE');
|