@forklaunch/core 0.19.5 → 1.0.2
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/lib/{apiDefinition.types-DnUkFmfT.d.mts → apiDefinition.types-Br0fDuBQ.d.mts} +42 -8
- package/lib/{apiDefinition.types-DnUkFmfT.d.ts → apiDefinition.types-Br0fDuBQ.d.ts} +42 -8
- package/lib/cache/index.d.mts +3 -133
- package/lib/cache/index.d.ts +3 -133
- package/lib/http/index.d.mts +88 -3
- package/lib/http/index.d.ts +88 -3
- package/lib/http/index.js +168 -25
- package/lib/http/index.js.map +1 -1
- package/lib/http/index.mjs +166 -25
- package/lib/http/index.mjs.map +1 -1
- package/lib/mappers/index.js.map +1 -1
- package/lib/mappers/index.mjs.map +1 -1
- package/lib/persistence/index.d.mts +271 -1
- package/lib/persistence/index.d.ts +271 -1
- package/lib/persistence/index.js +463 -0
- package/lib/persistence/index.js.map +1 -1
- package/lib/persistence/index.mjs +427 -0
- package/lib/persistence/index.mjs.map +1 -1
- package/lib/ttlCache.interface-DClm-lSa.d.mts +133 -0
- package/lib/ttlCache.interface-DClm-lSa.d.ts +133 -0
- package/lib/ws/index.d.mts +1 -1
- package/lib/ws/index.d.ts +1 -1
- package/package.json +9 -9
package/lib/mappers/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/mappers/index.ts","../../src/mappers/mapper.ts","../../src/mappers/guards/isSchemaResolutionFunction.ts","../../src/mappers/mapServiceSchemas.ts"],"sourcesContent":["export * from './mapper';\nexport * from './mapServiceSchemas';\n","import { Prettify } from '@forklaunch/common';\nimport {\n AnySchemaValidator,\n IdiomaticSchema,\n prettyPrintParseErrors,\n Schema,\n SchemaValidator\n} from '@forklaunch/validator';\nimport { EntitySchema, InferEntity } from '@mikro-orm/core';\n\nexport function requestMapper<\n SV extends AnySchemaValidator,\n DomainSchema extends IdiomaticSchema<SV>,\n Entity extends EntitySchema,\n AdditionalArgs extends unknown[] = []\n>({\n schemaValidator,\n schema,\n entity,\n mapperDefinition\n}: {\n schemaValidator: SV;\n schema: DomainSchema;\n entity: Entity;\n mapperDefinition: {\n toEntity: (\n dto: Schema<DomainSchema, SV>,\n ...args: AdditionalArgs\n ) => Promise<InferEntity<Entity>>;\n };\n}): {\n entity: Entity;\n schema: DomainSchema;\n} & typeof mapperDefinition {\n const sv = schemaValidator as SchemaValidator;\n return {\n ...mapperDefinition,\n entity,\n schema,\n\n toEntity: async (\n dto: Schema<DomainSchema, SV>,\n ...args: AdditionalArgs\n ) => {\n const parsedSchema = sv.parse(sv.schemify(schema), dto);\n if (!parsedSchema.ok) {\n throw new Error(prettyPrintParseErrors(parsedSchema.errors, 'DTO'));\n }\n return mapperDefinition.toEntity(\n dto as Schema<DomainSchema, SV>,\n ...(args as AdditionalArgs)\n );\n }\n };\n}\n\nexport function responseMapper<\n SV extends AnySchemaValidator,\n DomainSchema extends IdiomaticSchema<SV>,\n Entity extends EntitySchema,\n AdditionalArgs extends unknown[] = []\n>({\n schemaValidator,\n schema,\n entity,\n mapperDefinition\n}: {\n schemaValidator: SV;\n schema: DomainSchema;\n entity: Entity;\n mapperDefinition: {\n toDto: (\n entity: InferEntity<Entity>,\n ...args: AdditionalArgs\n ) => Promise<Schema<DomainSchema, SV>>;\n };\n}): Prettify<\n {\n entity: Entity;\n schema: DomainSchema;\n } & typeof mapperDefinition\n> {\n const sv = schemaValidator as SchemaValidator;\n return {\n ...mapperDefinition,\n entity,\n schema,\n\n toDto: async (
|
|
1
|
+
{"version":3,"sources":["../../src/mappers/index.ts","../../src/mappers/mapper.ts","../../src/mappers/guards/isSchemaResolutionFunction.ts","../../src/mappers/mapServiceSchemas.ts"],"sourcesContent":["export * from './mapper';\nexport * from './mapServiceSchemas';\n","import { Prettify } from '@forklaunch/common';\nimport {\n AnySchemaValidator,\n IdiomaticSchema,\n prettyPrintParseErrors,\n Schema,\n SchemaValidator\n} from '@forklaunch/validator';\nimport { EntitySchema, InferEntity } from '@mikro-orm/core';\n\nexport function requestMapper<\n SV extends AnySchemaValidator,\n DomainSchema extends IdiomaticSchema<SV>,\n Entity extends EntitySchema,\n AdditionalArgs extends unknown[] = []\n>({\n schemaValidator,\n schema,\n entity,\n mapperDefinition\n}: {\n schemaValidator: SV;\n schema: DomainSchema;\n entity: Entity;\n mapperDefinition: {\n toEntity: (\n dto: Schema<DomainSchema, SV>,\n ...args: AdditionalArgs\n ) => Promise<InferEntity<Entity>>;\n };\n}): {\n entity: Entity;\n schema: DomainSchema;\n} & typeof mapperDefinition {\n const sv = schemaValidator as SchemaValidator;\n return {\n ...mapperDefinition,\n entity,\n schema,\n\n toEntity: async (\n dto: Schema<DomainSchema, SV>,\n ...args: AdditionalArgs\n ) => {\n const parsedSchema = sv.parse(sv.schemify(schema), dto);\n if (!parsedSchema.ok) {\n throw new Error(prettyPrintParseErrors(parsedSchema.errors, 'DTO'));\n }\n return mapperDefinition.toEntity(\n dto as Schema<DomainSchema, SV>,\n ...(args as AdditionalArgs)\n );\n }\n };\n}\n\nexport function responseMapper<\n SV extends AnySchemaValidator,\n DomainSchema extends IdiomaticSchema<SV>,\n Entity extends EntitySchema,\n AdditionalArgs extends unknown[] = []\n>({\n schemaValidator,\n schema,\n entity,\n mapperDefinition\n}: {\n schemaValidator: SV;\n schema: DomainSchema;\n entity: Entity;\n mapperDefinition: {\n toDto: (\n entity: InferEntity<Entity>,\n ...args: AdditionalArgs\n ) => Promise<Schema<DomainSchema, SV>>;\n };\n}): Prettify<\n {\n entity: Entity;\n schema: DomainSchema;\n } & typeof mapperDefinition\n> {\n const sv = schemaValidator as SchemaValidator;\n return {\n ...mapperDefinition,\n entity,\n schema,\n\n toDto: async (entity: InferEntity<Entity>, ...args: AdditionalArgs) => {\n const domain = await mapperDefinition.toDto(entity, ...args);\n const parsedSchema = sv.parse(sv.schemify(schema), domain);\n if (!parsedSchema.ok) {\n throw new Error(prettyPrintParseErrors(parsedSchema.errors, 'DTO'));\n }\n return domain;\n }\n };\n}\n","import { SchemaResolutionFunction } from '../types/schemaResolution.types';\n\n/**\n * Type guard to determine if a value is a SchemaResolutionFunction.\n *\n * @template Args - The type of the options object expected by the schema resolution function.\n * @param {unknown} value - The value to check.\n * @returns {value is SchemaResolutionFunction<Args>} True if the value is a function (assumed to be a SchemaResolutionFunction), false otherwise.\n */\nexport function isSchemaResolutionFunction<\n Args extends Record<string, unknown>\n>(value: unknown): value is SchemaResolutionFunction<Args> {\n return typeof value === 'function';\n}\n","import { AnySchemaValidator, IdiomaticSchema } from '@forklaunch/validator';\nimport { isSchemaResolutionFunction } from './guards/isSchemaResolutionFunction';\nimport { SchemaResolutionFunction } from './types/schemaResolution.types';\n\n/**\n * Maps a set of service schema factories or pre-instantiated schemas to their resolved schemas using the provided arguments.\n *\n * This utility allows you to provide an object whose values are either:\n * - Schema factory functions (SchemaResolutionFunction) that accept an options object (Args) and return a schema or schema group.\n * - Already-instantiated schemas (IdiomaticSchema).\n *\n * Each factory function will be called with the provided `args` object, and the result will be included in the returned mapping.\n * If a value is already a schema (not a function), it is returned as-is.\n *\n * @template SV - The schema validator type.\n * @template T - An object whose values are either schema factory functions or instantiated schemas.\n * @template Args - The type of the options object passed to each factory function (e.g., { validator, uuidId, ... }).\n *\n * @param {T} schemas - An object mapping schema names to either factory functions (SchemaResolutionFunction) or instantiated schemas (IdiomaticSchema).\n * @param {Args} args - The options object to be passed to each schema factory function.\n * @returns {{ [K in keyof T]: T[K] extends SchemaResolutionFunction<Args> ? ReturnType<T[K]> : T[K] }} An object mapping each schema name to its resolved schema.\n *\n * @example\n * const schemas = {\n * UserSchemas: (opts) => createUserSchemas(opts),\n * ProductSchemas: (opts) => createProductSchemas(opts),\n * AlreadyInstantiated: someSchemaObject\n * };\n * const mapped = mapServiceSchemas(schemas, { validator: myValidator });\n * // mapped.UserSchemas and mapped.ProductSchemas are instantiated, mapped.AlreadyInstantiated is passed through\n */\nexport function mapServiceSchemas<\n SV extends AnySchemaValidator,\n T extends Record<\n string,\n SchemaResolutionFunction<Args> | IdiomaticSchema<SV>\n >,\n Args extends Record<string, unknown>\n>(\n schemas: T,\n args: Args\n): {\n [K in keyof T]: T[K] extends SchemaResolutionFunction<Args>\n ? ReturnType<T[K]>\n : T[K];\n} {\n return Object.fromEntries(\n Object.entries(schemas).map(([key, value]) => [\n key,\n isSchemaResolutionFunction(value) ? value(args) : value\n ])\n ) as {\n [K in keyof T]: T[K] extends SchemaResolutionFunction<Args>\n ? ReturnType<T[K]>\n : T[K];\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACCA,uBAMO;AAGA,SAAS,cAKd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAa4B;AAC1B,QAAM,KAAK;AACX,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA;AAAA,IAEA,UAAU,OACR,QACG,SACA;AACH,YAAM,eAAe,GAAG,MAAM,GAAG,SAAS,MAAM,GAAG,GAAG;AACtD,UAAI,CAAC,aAAa,IAAI;AACpB,cAAM,IAAI,UAAM,yCAAuB,aAAa,QAAQ,KAAK,CAAC;AAAA,MACpE;AACA,aAAO,iBAAiB;AAAA,QACtB;AAAA,QACA,GAAI;AAAA,MACN;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,eAKd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAeE;AACA,QAAM,KAAK;AACX,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA;AAAA,IAEA,OAAO,OAAOA,YAAgC,SAAyB;AACrE,YAAM,SAAS,MAAM,iBAAiB,MAAMA,SAAQ,GAAG,IAAI;AAC3D,YAAM,eAAe,GAAG,MAAM,GAAG,SAAS,MAAM,GAAG,MAAM;AACzD,UAAI,CAAC,aAAa,IAAI;AACpB,cAAM,IAAI,UAAM,yCAAuB,aAAa,QAAQ,KAAK,CAAC;AAAA,MACpE;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;ACxFO,SAAS,2BAEd,OAAyD;AACzD,SAAO,OAAO,UAAU;AAC1B;;;ACkBO,SAAS,kBAQd,SACA,MAKA;AACA,SAAO,OAAO;AAAA,IACZ,OAAO,QAAQ,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AAAA,MAC5C;AAAA,MACA,2BAA2B,KAAK,IAAI,MAAM,IAAI,IAAI;AAAA,IACpD,CAAC;AAAA,EACH;AAKF;","names":["entity"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/mappers/mapper.ts","../../src/mappers/guards/isSchemaResolutionFunction.ts","../../src/mappers/mapServiceSchemas.ts"],"sourcesContent":["import { Prettify } from '@forklaunch/common';\nimport {\n AnySchemaValidator,\n IdiomaticSchema,\n prettyPrintParseErrors,\n Schema,\n SchemaValidator\n} from '@forklaunch/validator';\nimport { EntitySchema, InferEntity } from '@mikro-orm/core';\n\nexport function requestMapper<\n SV extends AnySchemaValidator,\n DomainSchema extends IdiomaticSchema<SV>,\n Entity extends EntitySchema,\n AdditionalArgs extends unknown[] = []\n>({\n schemaValidator,\n schema,\n entity,\n mapperDefinition\n}: {\n schemaValidator: SV;\n schema: DomainSchema;\n entity: Entity;\n mapperDefinition: {\n toEntity: (\n dto: Schema<DomainSchema, SV>,\n ...args: AdditionalArgs\n ) => Promise<InferEntity<Entity>>;\n };\n}): {\n entity: Entity;\n schema: DomainSchema;\n} & typeof mapperDefinition {\n const sv = schemaValidator as SchemaValidator;\n return {\n ...mapperDefinition,\n entity,\n schema,\n\n toEntity: async (\n dto: Schema<DomainSchema, SV>,\n ...args: AdditionalArgs\n ) => {\n const parsedSchema = sv.parse(sv.schemify(schema), dto);\n if (!parsedSchema.ok) {\n throw new Error(prettyPrintParseErrors(parsedSchema.errors, 'DTO'));\n }\n return mapperDefinition.toEntity(\n dto as Schema<DomainSchema, SV>,\n ...(args as AdditionalArgs)\n );\n }\n };\n}\n\nexport function responseMapper<\n SV extends AnySchemaValidator,\n DomainSchema extends IdiomaticSchema<SV>,\n Entity extends EntitySchema,\n AdditionalArgs extends unknown[] = []\n>({\n schemaValidator,\n schema,\n entity,\n mapperDefinition\n}: {\n schemaValidator: SV;\n schema: DomainSchema;\n entity: Entity;\n mapperDefinition: {\n toDto: (\n entity: InferEntity<Entity>,\n ...args: AdditionalArgs\n ) => Promise<Schema<DomainSchema, SV>>;\n };\n}): Prettify<\n {\n entity: Entity;\n schema: DomainSchema;\n } & typeof mapperDefinition\n> {\n const sv = schemaValidator as SchemaValidator;\n return {\n ...mapperDefinition,\n entity,\n schema,\n\n toDto: async (
|
|
1
|
+
{"version":3,"sources":["../../src/mappers/mapper.ts","../../src/mappers/guards/isSchemaResolutionFunction.ts","../../src/mappers/mapServiceSchemas.ts"],"sourcesContent":["import { Prettify } from '@forklaunch/common';\nimport {\n AnySchemaValidator,\n IdiomaticSchema,\n prettyPrintParseErrors,\n Schema,\n SchemaValidator\n} from '@forklaunch/validator';\nimport { EntitySchema, InferEntity } from '@mikro-orm/core';\n\nexport function requestMapper<\n SV extends AnySchemaValidator,\n DomainSchema extends IdiomaticSchema<SV>,\n Entity extends EntitySchema,\n AdditionalArgs extends unknown[] = []\n>({\n schemaValidator,\n schema,\n entity,\n mapperDefinition\n}: {\n schemaValidator: SV;\n schema: DomainSchema;\n entity: Entity;\n mapperDefinition: {\n toEntity: (\n dto: Schema<DomainSchema, SV>,\n ...args: AdditionalArgs\n ) => Promise<InferEntity<Entity>>;\n };\n}): {\n entity: Entity;\n schema: DomainSchema;\n} & typeof mapperDefinition {\n const sv = schemaValidator as SchemaValidator;\n return {\n ...mapperDefinition,\n entity,\n schema,\n\n toEntity: async (\n dto: Schema<DomainSchema, SV>,\n ...args: AdditionalArgs\n ) => {\n const parsedSchema = sv.parse(sv.schemify(schema), dto);\n if (!parsedSchema.ok) {\n throw new Error(prettyPrintParseErrors(parsedSchema.errors, 'DTO'));\n }\n return mapperDefinition.toEntity(\n dto as Schema<DomainSchema, SV>,\n ...(args as AdditionalArgs)\n );\n }\n };\n}\n\nexport function responseMapper<\n SV extends AnySchemaValidator,\n DomainSchema extends IdiomaticSchema<SV>,\n Entity extends EntitySchema,\n AdditionalArgs extends unknown[] = []\n>({\n schemaValidator,\n schema,\n entity,\n mapperDefinition\n}: {\n schemaValidator: SV;\n schema: DomainSchema;\n entity: Entity;\n mapperDefinition: {\n toDto: (\n entity: InferEntity<Entity>,\n ...args: AdditionalArgs\n ) => Promise<Schema<DomainSchema, SV>>;\n };\n}): Prettify<\n {\n entity: Entity;\n schema: DomainSchema;\n } & typeof mapperDefinition\n> {\n const sv = schemaValidator as SchemaValidator;\n return {\n ...mapperDefinition,\n entity,\n schema,\n\n toDto: async (entity: InferEntity<Entity>, ...args: AdditionalArgs) => {\n const domain = await mapperDefinition.toDto(entity, ...args);\n const parsedSchema = sv.parse(sv.schemify(schema), domain);\n if (!parsedSchema.ok) {\n throw new Error(prettyPrintParseErrors(parsedSchema.errors, 'DTO'));\n }\n return domain;\n }\n };\n}\n","import { SchemaResolutionFunction } from '../types/schemaResolution.types';\n\n/**\n * Type guard to determine if a value is a SchemaResolutionFunction.\n *\n * @template Args - The type of the options object expected by the schema resolution function.\n * @param {unknown} value - The value to check.\n * @returns {value is SchemaResolutionFunction<Args>} True if the value is a function (assumed to be a SchemaResolutionFunction), false otherwise.\n */\nexport function isSchemaResolutionFunction<\n Args extends Record<string, unknown>\n>(value: unknown): value is SchemaResolutionFunction<Args> {\n return typeof value === 'function';\n}\n","import { AnySchemaValidator, IdiomaticSchema } from '@forklaunch/validator';\nimport { isSchemaResolutionFunction } from './guards/isSchemaResolutionFunction';\nimport { SchemaResolutionFunction } from './types/schemaResolution.types';\n\n/**\n * Maps a set of service schema factories or pre-instantiated schemas to their resolved schemas using the provided arguments.\n *\n * This utility allows you to provide an object whose values are either:\n * - Schema factory functions (SchemaResolutionFunction) that accept an options object (Args) and return a schema or schema group.\n * - Already-instantiated schemas (IdiomaticSchema).\n *\n * Each factory function will be called with the provided `args` object, and the result will be included in the returned mapping.\n * If a value is already a schema (not a function), it is returned as-is.\n *\n * @template SV - The schema validator type.\n * @template T - An object whose values are either schema factory functions or instantiated schemas.\n * @template Args - The type of the options object passed to each factory function (e.g., { validator, uuidId, ... }).\n *\n * @param {T} schemas - An object mapping schema names to either factory functions (SchemaResolutionFunction) or instantiated schemas (IdiomaticSchema).\n * @param {Args} args - The options object to be passed to each schema factory function.\n * @returns {{ [K in keyof T]: T[K] extends SchemaResolutionFunction<Args> ? ReturnType<T[K]> : T[K] }} An object mapping each schema name to its resolved schema.\n *\n * @example\n * const schemas = {\n * UserSchemas: (opts) => createUserSchemas(opts),\n * ProductSchemas: (opts) => createProductSchemas(opts),\n * AlreadyInstantiated: someSchemaObject\n * };\n * const mapped = mapServiceSchemas(schemas, { validator: myValidator });\n * // mapped.UserSchemas and mapped.ProductSchemas are instantiated, mapped.AlreadyInstantiated is passed through\n */\nexport function mapServiceSchemas<\n SV extends AnySchemaValidator,\n T extends Record<\n string,\n SchemaResolutionFunction<Args> | IdiomaticSchema<SV>\n >,\n Args extends Record<string, unknown>\n>(\n schemas: T,\n args: Args\n): {\n [K in keyof T]: T[K] extends SchemaResolutionFunction<Args>\n ? ReturnType<T[K]>\n : T[K];\n} {\n return Object.fromEntries(\n Object.entries(schemas).map(([key, value]) => [\n key,\n isSchemaResolutionFunction(value) ? value(args) : value\n ])\n ) as {\n [K in keyof T]: T[K] extends SchemaResolutionFunction<Args>\n ? ReturnType<T[K]>\n : T[K];\n };\n}\n"],"mappings":";AACA;AAAA,EAGE;AAAA,OAGK;AAGA,SAAS,cAKd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAa4B;AAC1B,QAAM,KAAK;AACX,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA;AAAA,IAEA,UAAU,OACR,QACG,SACA;AACH,YAAM,eAAe,GAAG,MAAM,GAAG,SAAS,MAAM,GAAG,GAAG;AACtD,UAAI,CAAC,aAAa,IAAI;AACpB,cAAM,IAAI,MAAM,uBAAuB,aAAa,QAAQ,KAAK,CAAC;AAAA,MACpE;AACA,aAAO,iBAAiB;AAAA,QACtB;AAAA,QACA,GAAI;AAAA,MACN;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,eAKd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAeE;AACA,QAAM,KAAK;AACX,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA;AAAA,IAEA,OAAO,OAAOA,YAAgC,SAAyB;AACrE,YAAM,SAAS,MAAM,iBAAiB,MAAMA,SAAQ,GAAG,IAAI;AAC3D,YAAM,eAAe,GAAG,MAAM,GAAG,SAAS,MAAM,GAAG,MAAM;AACzD,UAAI,CAAC,aAAa,IAAI;AACpB,cAAM,IAAI,MAAM,uBAAuB,aAAa,QAAQ,KAAK,CAAC;AAAA,MACpE;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;ACxFO,SAAS,2BAEd,OAAyD;AACzD,SAAO,OAAO,UAAU;AAC1B;;;ACkBO,SAAS,kBAQd,SACA,MAKA;AACA,SAAO,OAAO;AAAA,IACZ,OAAO,QAAQ,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AAAA,MAC5C;AAAA,MACA,2BAA2B,KAAK,IAAI,MAAM,IAAI,IAAI;AAAA,IACpD,CAAC;AAAA,EACH;AAKF;","names":["entity"]}
|
|
@@ -1,2 +1,272 @@
|
|
|
1
|
+
import * as _mikro_orm_core from '@mikro-orm/core';
|
|
2
|
+
import { PropertyChain, PropertyBuilders, UniversalPropertyOptionsBuilder, EntityMetadataWithProperties, EventSubscriber, EventArgs, EntityManager, FilterDef, MikroORM, TransactionEventArgs } from '@mikro-orm/core';
|
|
3
|
+
export { InferEntity } from '@mikro-orm/core';
|
|
1
4
|
|
|
2
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Classification levels for entity field compliance.
|
|
7
|
+
* Drives encryption (phi/pci), audit log redaction (all non-none), and compliance reporting.
|
|
8
|
+
*/
|
|
9
|
+
declare const ComplianceLevel: {
|
|
10
|
+
readonly pii: "pii";
|
|
11
|
+
readonly phi: "phi";
|
|
12
|
+
readonly pci: "pci";
|
|
13
|
+
readonly none: "none";
|
|
14
|
+
};
|
|
15
|
+
type ComplianceLevel = (typeof ComplianceLevel)[keyof typeof ComplianceLevel];
|
|
16
|
+
/**
|
|
17
|
+
* Brand symbol — makes ClassifiedProperty structurally distinct from
|
|
18
|
+
* plain PropertyChain at the TypeScript level.
|
|
19
|
+
*/
|
|
20
|
+
declare const CLASSIFIED: unique symbol;
|
|
21
|
+
/**
|
|
22
|
+
* Brand-only type used by `AssertAllClassified` to check that every
|
|
23
|
+
* property has been classified. Both `ClassifiedScalarProperty` and
|
|
24
|
+
* `ClassifiedRelationChain` extend this.
|
|
25
|
+
*/
|
|
26
|
+
interface ClassifiedProperty {
|
|
27
|
+
readonly [CLASSIFIED]: true;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A scalar property classified via `.compliance()`.
|
|
31
|
+
* Extends `PropertyChain<Value, Options>` so that `defineEntity` (and
|
|
32
|
+
* therefore `InferEntity`) can still read the value/options types for
|
|
33
|
+
* entity type inference. The `[CLASSIFIED]` brand prevents unclassified
|
|
34
|
+
* properties from being accepted by `defineComplianceEntity`.
|
|
35
|
+
*/
|
|
36
|
+
type ClassifiedScalarProperty<Value, Options> = PropertyChain<Value, Options> & ClassifiedProperty;
|
|
37
|
+
/**
|
|
38
|
+
* Look up the compliance level for a single field on an entity.
|
|
39
|
+
* Returns `'none'` if the entity or field is not registered.
|
|
40
|
+
*/
|
|
41
|
+
declare function getComplianceMetadata(entityName: string, fieldName: string): ComplianceLevel;
|
|
42
|
+
/**
|
|
43
|
+
* Get all compliance fields for an entity.
|
|
44
|
+
* Returns undefined if the entity is not registered.
|
|
45
|
+
*/
|
|
46
|
+
declare function getEntityComplianceFields(entityName: string): Map<string, ComplianceLevel> | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* Check whether an entity has any fields requiring encryption (phi or pci).
|
|
49
|
+
*/
|
|
50
|
+
declare function entityHasEncryptedFields(entityName: string): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Recursively remaps every method on PropertyChain<V,O> so those returning
|
|
53
|
+
* PropertyChain<V2,O2> instead return ForklaunchPropertyChain<V2,O2>.
|
|
54
|
+
* This preserves the `.compliance()` method through chained calls like
|
|
55
|
+
* `.nullable().unique()`.
|
|
56
|
+
*/
|
|
57
|
+
interface ForklaunchPropertyChain<Value, Options> extends RemapReturns<Value, Options> {
|
|
58
|
+
/**
|
|
59
|
+
* Classify this field's compliance level. Must be called on every scalar
|
|
60
|
+
* field passed to `defineComplianceEntity`.
|
|
61
|
+
* Returns a `ClassifiedScalarProperty` that preserves the PropertyChain
|
|
62
|
+
* type info for `InferEntity` to work.
|
|
63
|
+
*/
|
|
64
|
+
compliance(level: ComplianceLevel): ClassifiedScalarProperty<Value, Options>;
|
|
65
|
+
}
|
|
66
|
+
type RemapReturns<Value, Options> = {
|
|
67
|
+
[K in keyof PropertyChain<Value, Options>]: PropertyChain<Value, Options>[K] extends (...args: infer A) => PropertyChain<infer V2, infer O2> ? (...args: A) => ForklaunchPropertyChain<V2, O2> : PropertyChain<Value, Options>[K];
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Keys on PropertyBuilders that return relation builders.
|
|
71
|
+
* These are auto-classified as 'none' — the fp proxy wraps them
|
|
72
|
+
* to return ClassifiedProperty directly.
|
|
73
|
+
*/
|
|
74
|
+
type RelationBuilderKeys = 'manyToOne' | 'oneToMany' | 'manyToMany' | 'oneToOne' | 'embedded';
|
|
75
|
+
/**
|
|
76
|
+
* The type of `fp` — mirrors `PropertyBuilders` but:
|
|
77
|
+
* - Scalar methods return `ForklaunchPropertyChain` (must call `.compliance()`)
|
|
78
|
+
* - Relation methods return `ClassifiedProperty` directly (auto 'none')
|
|
79
|
+
*/
|
|
80
|
+
type ForklaunchPropertyBuilders = {
|
|
81
|
+
[K in Exclude<keyof PropertyBuilders, RelationBuilderKeys>]: PropertyBuilders[K] extends (...args: infer A) => UniversalPropertyOptionsBuilder<infer V, infer O, infer _IK> ? (...args: A) => ForklaunchPropertyChain<V, O> : PropertyBuilders[K] extends (...args: infer A) => PropertyChain<infer V, infer O> ? (...args: A) => ForklaunchPropertyChain<V, O> : PropertyBuilders[K];
|
|
82
|
+
} & {
|
|
83
|
+
[K in RelationBuilderKeys]: PropertyBuilders[K] extends (...args: infer A) => PropertyChain<infer V, infer O> ? (...args: A) => ClassifiedRelationChain<V, O> : PropertyBuilders[K];
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* A relation builder that is already classified (as 'none') but still
|
|
87
|
+
* supports chaining relation-specific methods like `.mappedBy()`, `.nullable()`.
|
|
88
|
+
* All chain methods return ClassifiedRelationChain (preserving the brand).
|
|
89
|
+
*/
|
|
90
|
+
type ClassifiedRelationChain<Value, Options> = {
|
|
91
|
+
[K in keyof PropertyChain<Value, Options>]: PropertyChain<Value, Options>[K] extends (...args: infer A) => PropertyChain<infer V2, infer O2> ? (...args: A) => ClassifiedRelationChain<V2, O2> : PropertyChain<Value, Options>[K];
|
|
92
|
+
} & ClassifiedProperty;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* ForkLaunch property builder. Drop-in replacement for MikroORM's `p`
|
|
96
|
+
* that adds `.compliance(level)` to every scalar property builder
|
|
97
|
+
* and auto-classifies relation builders as 'none'.
|
|
98
|
+
*
|
|
99
|
+
* - Scalar fields: `fp.string().compliance('pii')` — must call `.compliance()`
|
|
100
|
+
* - Relation fields: `fp.manyToOne(Target)` — auto-classified, no `.compliance()` needed
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```typescript
|
|
104
|
+
* import { defineComplianceEntity, fp } from '@forklaunch/core/persistence';
|
|
105
|
+
*
|
|
106
|
+
* const User = defineComplianceEntity({
|
|
107
|
+
* name: 'User',
|
|
108
|
+
* properties: {
|
|
109
|
+
* id: fp.uuid().primary().compliance('none'),
|
|
110
|
+
* email: fp.string().unique().compliance('pii'),
|
|
111
|
+
* medicalRecord: fp.string().nullable().compliance('phi'),
|
|
112
|
+
* organization: () => fp.manyToOne(Organization).nullable(),
|
|
113
|
+
* }
|
|
114
|
+
* });
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
declare const fp: ForklaunchPropertyBuilders;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Wrapper around MikroORM's `defineEntity` that enforces compliance
|
|
121
|
+
* classification on every field at both compile-time and runtime.
|
|
122
|
+
*
|
|
123
|
+
* The return type is inferred directly from `defineEntity` — `InferEntity`
|
|
124
|
+
* works because `ClassifiedScalarProperty<V,O>` extends `PropertyChain<V,O>`.
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* const User = defineComplianceEntity({
|
|
129
|
+
* name: 'User',
|
|
130
|
+
* properties: {
|
|
131
|
+
* id: fp.uuid().primary().compliance('none'),
|
|
132
|
+
* email: fp.string().unique().compliance('pii'),
|
|
133
|
+
* organization: () => fp.manyToOne(Organization).nullable(),
|
|
134
|
+
* }
|
|
135
|
+
* });
|
|
136
|
+
* export type User = InferEntity<typeof User>;
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
declare function defineComplianceEntity<const TName extends string, const TTableName extends string, const TProperties extends Record<string, ClassifiedProperty>, const TPK extends (keyof TProperties)[] | undefined = undefined, const TBase = never, const TRepository = never, const TForceObject extends boolean = false>(meta: EntityMetadataWithProperties<TName, TTableName, TProperties, TPK, TBase, TRepository, TForceObject>): _mikro_orm_core.EntitySchemaWithMeta<TName, TTableName, _mikro_orm_core.InferEntityFromProperties<TProperties, TPK, TBase, TRepository, TForceObject>, TBase, TProperties, _mikro_orm_core.EntityCtor<_mikro_orm_core.InferEntityFromProperties<TProperties, TPK, TBase, TRepository, TForceObject>>>;
|
|
140
|
+
|
|
141
|
+
declare class FieldEncryptor {
|
|
142
|
+
private readonly masterKey;
|
|
143
|
+
constructor(masterKey: string);
|
|
144
|
+
/**
|
|
145
|
+
* Derive a per-tenant 32-byte key using HKDF-SHA256.
|
|
146
|
+
* The master key is used as input key material and the tenantId as info context.
|
|
147
|
+
*/
|
|
148
|
+
deriveKey(tenantId: string): Buffer;
|
|
149
|
+
/**
|
|
150
|
+
* Encrypt a plaintext string for a specific tenant.
|
|
151
|
+
*
|
|
152
|
+
* @returns Format: `v1:{base64(iv)}:{base64(authTag)}:{base64(ciphertext)}`
|
|
153
|
+
*/
|
|
154
|
+
encrypt(plaintext: string | null): string | null;
|
|
155
|
+
encrypt(plaintext: string | null, tenantId: string): string | null;
|
|
156
|
+
/**
|
|
157
|
+
* Decrypt a ciphertext string produced by {@link encrypt}.
|
|
158
|
+
*/
|
|
159
|
+
decrypt(ciphertext: string | null): string | null;
|
|
160
|
+
decrypt(ciphertext: string | null, tenantId: string): string | null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* MikroORM EventSubscriber that enforces field-level encryption for
|
|
165
|
+
* compliance-classified fields (PHI and PCI).
|
|
166
|
+
*
|
|
167
|
+
* - **onBeforeCreate / onBeforeUpdate**: Encrypts PHI/PCI fields before
|
|
168
|
+
* database persistence. Throws `EncryptionRequiredError` if the encryption
|
|
169
|
+
* key is unavailable.
|
|
170
|
+
* - **onLoad**: Decrypts PHI/PCI fields after loading from the database.
|
|
171
|
+
* Pre-migration plaintext (no `v1:` prefix) is returned as-is with a
|
|
172
|
+
* console warning to support rolling deployments.
|
|
173
|
+
*
|
|
174
|
+
* The tenant ID for key derivation is read from the EntityManager's filter
|
|
175
|
+
* parameters (set by the tenant context middleware).
|
|
176
|
+
*/
|
|
177
|
+
declare class ComplianceEventSubscriber implements EventSubscriber {
|
|
178
|
+
private readonly encryptor;
|
|
179
|
+
constructor(encryptor: FieldEncryptor);
|
|
180
|
+
beforeCreate(args: EventArgs<unknown>): Promise<void>;
|
|
181
|
+
beforeUpdate(args: EventArgs<unknown>): Promise<void>;
|
|
182
|
+
onLoad(args: EventArgs<unknown>): Promise<void>;
|
|
183
|
+
private encryptFields;
|
|
184
|
+
private decryptFields;
|
|
185
|
+
/**
|
|
186
|
+
* Read the tenant ID from the EntityManager's filter parameters.
|
|
187
|
+
* The tenant context middleware sets this when forking the EM per request.
|
|
188
|
+
*/
|
|
189
|
+
private getTenantId;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Wraps an EntityManager to block `nativeInsert`, `nativeUpdate`, and
|
|
193
|
+
* `nativeDelete` on entities that have PHI or PCI compliance fields.
|
|
194
|
+
*
|
|
195
|
+
* This prevents bypassing the ComplianceEventSubscriber's encryption
|
|
196
|
+
* by using raw queries. Call this in the tenant context middleware when
|
|
197
|
+
* creating the request-scoped EM.
|
|
198
|
+
*
|
|
199
|
+
* @returns A Proxy-wrapped EntityManager that throws on native query
|
|
200
|
+
* operations targeting compliance entities.
|
|
201
|
+
*/
|
|
202
|
+
declare function wrapEmWithNativeQueryBlocking<T extends EntityManager>(em: T): T;
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* The name used to register the tenant isolation filter.
|
|
206
|
+
*/
|
|
207
|
+
declare const TENANT_FILTER_NAME = "tenant";
|
|
208
|
+
/**
|
|
209
|
+
* Creates the tenant filter definition.
|
|
210
|
+
*
|
|
211
|
+
* The filter adds `WHERE organizationId = :tenantId` to all queries
|
|
212
|
+
* on entities that have an `organizationId` or `organization` property.
|
|
213
|
+
* Entities without either property are unaffected (empty condition).
|
|
214
|
+
*/
|
|
215
|
+
declare function createTenantFilterDef(): FilterDef;
|
|
216
|
+
/**
|
|
217
|
+
* Registers the global tenant isolation filter on the ORM's entity manager.
|
|
218
|
+
* Call this once at application bootstrap after `MikroORM.init()`.
|
|
219
|
+
*
|
|
220
|
+
* After calling this, every fork of the EM will inherit the filter.
|
|
221
|
+
* Set the tenant ID per-request via:
|
|
222
|
+
*
|
|
223
|
+
* ```ts
|
|
224
|
+
* em.setFilterParams('tenant', { tenantId: 'org-123' });
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
declare function setupTenantFilter(orm: MikroORM): void;
|
|
228
|
+
/**
|
|
229
|
+
* Returns a forked EntityManager with the tenant filter disabled.
|
|
230
|
+
*
|
|
231
|
+
* Use this only from code paths that have verified super-admin permissions.
|
|
232
|
+
* Queries executed through the returned EM will return cross-tenant data.
|
|
233
|
+
*/
|
|
234
|
+
declare function getSuperAdminContext(em: EntityManager): EntityManager;
|
|
235
|
+
|
|
236
|
+
interface RlsConfig {
|
|
237
|
+
/**
|
|
238
|
+
* Whether to enable PostgreSQL Row-Level Security.
|
|
239
|
+
* Defaults to `true` when the driver is PostgreSQL, `false` otherwise.
|
|
240
|
+
* Set to `false` to opt out even on PostgreSQL.
|
|
241
|
+
*/
|
|
242
|
+
enabled?: boolean;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* MikroORM EventSubscriber that executes `SET LOCAL app.tenant_id = :tenantId`
|
|
246
|
+
* at the start of every transaction when PostgreSQL RLS is enabled.
|
|
247
|
+
*
|
|
248
|
+
* This ensures that even if the MikroORM global filter is somehow bypassed,
|
|
249
|
+
* the database-level RLS policy enforces tenant isolation.
|
|
250
|
+
*
|
|
251
|
+
* The tenant ID is read from the EntityManager's filter parameters
|
|
252
|
+
* (set by the tenant context middleware).
|
|
253
|
+
*/
|
|
254
|
+
declare class RlsEventSubscriber implements EventSubscriber {
|
|
255
|
+
afterTransactionStart(args: TransactionEventArgs): Promise<void>;
|
|
256
|
+
private getTenantId;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Sets up PostgreSQL Row-Level Security integration.
|
|
260
|
+
*
|
|
261
|
+
* 1. Registers the `RlsEventSubscriber` to run `SET LOCAL app.tenant_id`
|
|
262
|
+
* at the start of every transaction.
|
|
263
|
+
* 2. Validates that RLS policies exist on tenant-scoped tables (warns if missing).
|
|
264
|
+
*
|
|
265
|
+
* Call this at application bootstrap after `MikroORM.init()` and `setupTenantFilter()`.
|
|
266
|
+
*
|
|
267
|
+
* @param orm - The initialized MikroORM instance
|
|
268
|
+
* @param config - RLS configuration (enabled defaults to auto-detect PostgreSQL)
|
|
269
|
+
*/
|
|
270
|
+
declare function setupRls(orm: MikroORM, config?: RlsConfig): void;
|
|
271
|
+
|
|
272
|
+
export { type ClassifiedProperty, type ClassifiedRelationChain, type ClassifiedScalarProperty, ComplianceEventSubscriber, ComplianceLevel, ComplianceLevel as ComplianceLevelType, type ForklaunchPropertyBuilders, type ForklaunchPropertyChain, type RlsConfig, RlsEventSubscriber, TENANT_FILTER_NAME, createTenantFilterDef, defineComplianceEntity, entityHasEncryptedFields, fp, getComplianceMetadata, getEntityComplianceFields, getSuperAdminContext, setupRls, setupTenantFilter, wrapEmWithNativeQueryBlocking };
|
|
@@ -1,2 +1,272 @@
|
|
|
1
|
+
import * as _mikro_orm_core from '@mikro-orm/core';
|
|
2
|
+
import { PropertyChain, PropertyBuilders, UniversalPropertyOptionsBuilder, EntityMetadataWithProperties, EventSubscriber, EventArgs, EntityManager, FilterDef, MikroORM, TransactionEventArgs } from '@mikro-orm/core';
|
|
3
|
+
export { InferEntity } from '@mikro-orm/core';
|
|
1
4
|
|
|
2
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Classification levels for entity field compliance.
|
|
7
|
+
* Drives encryption (phi/pci), audit log redaction (all non-none), and compliance reporting.
|
|
8
|
+
*/
|
|
9
|
+
declare const ComplianceLevel: {
|
|
10
|
+
readonly pii: "pii";
|
|
11
|
+
readonly phi: "phi";
|
|
12
|
+
readonly pci: "pci";
|
|
13
|
+
readonly none: "none";
|
|
14
|
+
};
|
|
15
|
+
type ComplianceLevel = (typeof ComplianceLevel)[keyof typeof ComplianceLevel];
|
|
16
|
+
/**
|
|
17
|
+
* Brand symbol — makes ClassifiedProperty structurally distinct from
|
|
18
|
+
* plain PropertyChain at the TypeScript level.
|
|
19
|
+
*/
|
|
20
|
+
declare const CLASSIFIED: unique symbol;
|
|
21
|
+
/**
|
|
22
|
+
* Brand-only type used by `AssertAllClassified` to check that every
|
|
23
|
+
* property has been classified. Both `ClassifiedScalarProperty` and
|
|
24
|
+
* `ClassifiedRelationChain` extend this.
|
|
25
|
+
*/
|
|
26
|
+
interface ClassifiedProperty {
|
|
27
|
+
readonly [CLASSIFIED]: true;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A scalar property classified via `.compliance()`.
|
|
31
|
+
* Extends `PropertyChain<Value, Options>` so that `defineEntity` (and
|
|
32
|
+
* therefore `InferEntity`) can still read the value/options types for
|
|
33
|
+
* entity type inference. The `[CLASSIFIED]` brand prevents unclassified
|
|
34
|
+
* properties from being accepted by `defineComplianceEntity`.
|
|
35
|
+
*/
|
|
36
|
+
type ClassifiedScalarProperty<Value, Options> = PropertyChain<Value, Options> & ClassifiedProperty;
|
|
37
|
+
/**
|
|
38
|
+
* Look up the compliance level for a single field on an entity.
|
|
39
|
+
* Returns `'none'` if the entity or field is not registered.
|
|
40
|
+
*/
|
|
41
|
+
declare function getComplianceMetadata(entityName: string, fieldName: string): ComplianceLevel;
|
|
42
|
+
/**
|
|
43
|
+
* Get all compliance fields for an entity.
|
|
44
|
+
* Returns undefined if the entity is not registered.
|
|
45
|
+
*/
|
|
46
|
+
declare function getEntityComplianceFields(entityName: string): Map<string, ComplianceLevel> | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* Check whether an entity has any fields requiring encryption (phi or pci).
|
|
49
|
+
*/
|
|
50
|
+
declare function entityHasEncryptedFields(entityName: string): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Recursively remaps every method on PropertyChain<V,O> so those returning
|
|
53
|
+
* PropertyChain<V2,O2> instead return ForklaunchPropertyChain<V2,O2>.
|
|
54
|
+
* This preserves the `.compliance()` method through chained calls like
|
|
55
|
+
* `.nullable().unique()`.
|
|
56
|
+
*/
|
|
57
|
+
interface ForklaunchPropertyChain<Value, Options> extends RemapReturns<Value, Options> {
|
|
58
|
+
/**
|
|
59
|
+
* Classify this field's compliance level. Must be called on every scalar
|
|
60
|
+
* field passed to `defineComplianceEntity`.
|
|
61
|
+
* Returns a `ClassifiedScalarProperty` that preserves the PropertyChain
|
|
62
|
+
* type info for `InferEntity` to work.
|
|
63
|
+
*/
|
|
64
|
+
compliance(level: ComplianceLevel): ClassifiedScalarProperty<Value, Options>;
|
|
65
|
+
}
|
|
66
|
+
type RemapReturns<Value, Options> = {
|
|
67
|
+
[K in keyof PropertyChain<Value, Options>]: PropertyChain<Value, Options>[K] extends (...args: infer A) => PropertyChain<infer V2, infer O2> ? (...args: A) => ForklaunchPropertyChain<V2, O2> : PropertyChain<Value, Options>[K];
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Keys on PropertyBuilders that return relation builders.
|
|
71
|
+
* These are auto-classified as 'none' — the fp proxy wraps them
|
|
72
|
+
* to return ClassifiedProperty directly.
|
|
73
|
+
*/
|
|
74
|
+
type RelationBuilderKeys = 'manyToOne' | 'oneToMany' | 'manyToMany' | 'oneToOne' | 'embedded';
|
|
75
|
+
/**
|
|
76
|
+
* The type of `fp` — mirrors `PropertyBuilders` but:
|
|
77
|
+
* - Scalar methods return `ForklaunchPropertyChain` (must call `.compliance()`)
|
|
78
|
+
* - Relation methods return `ClassifiedProperty` directly (auto 'none')
|
|
79
|
+
*/
|
|
80
|
+
type ForklaunchPropertyBuilders = {
|
|
81
|
+
[K in Exclude<keyof PropertyBuilders, RelationBuilderKeys>]: PropertyBuilders[K] extends (...args: infer A) => UniversalPropertyOptionsBuilder<infer V, infer O, infer _IK> ? (...args: A) => ForklaunchPropertyChain<V, O> : PropertyBuilders[K] extends (...args: infer A) => PropertyChain<infer V, infer O> ? (...args: A) => ForklaunchPropertyChain<V, O> : PropertyBuilders[K];
|
|
82
|
+
} & {
|
|
83
|
+
[K in RelationBuilderKeys]: PropertyBuilders[K] extends (...args: infer A) => PropertyChain<infer V, infer O> ? (...args: A) => ClassifiedRelationChain<V, O> : PropertyBuilders[K];
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* A relation builder that is already classified (as 'none') but still
|
|
87
|
+
* supports chaining relation-specific methods like `.mappedBy()`, `.nullable()`.
|
|
88
|
+
* All chain methods return ClassifiedRelationChain (preserving the brand).
|
|
89
|
+
*/
|
|
90
|
+
type ClassifiedRelationChain<Value, Options> = {
|
|
91
|
+
[K in keyof PropertyChain<Value, Options>]: PropertyChain<Value, Options>[K] extends (...args: infer A) => PropertyChain<infer V2, infer O2> ? (...args: A) => ClassifiedRelationChain<V2, O2> : PropertyChain<Value, Options>[K];
|
|
92
|
+
} & ClassifiedProperty;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* ForkLaunch property builder. Drop-in replacement for MikroORM's `p`
|
|
96
|
+
* that adds `.compliance(level)` to every scalar property builder
|
|
97
|
+
* and auto-classifies relation builders as 'none'.
|
|
98
|
+
*
|
|
99
|
+
* - Scalar fields: `fp.string().compliance('pii')` — must call `.compliance()`
|
|
100
|
+
* - Relation fields: `fp.manyToOne(Target)` — auto-classified, no `.compliance()` needed
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```typescript
|
|
104
|
+
* import { defineComplianceEntity, fp } from '@forklaunch/core/persistence';
|
|
105
|
+
*
|
|
106
|
+
* const User = defineComplianceEntity({
|
|
107
|
+
* name: 'User',
|
|
108
|
+
* properties: {
|
|
109
|
+
* id: fp.uuid().primary().compliance('none'),
|
|
110
|
+
* email: fp.string().unique().compliance('pii'),
|
|
111
|
+
* medicalRecord: fp.string().nullable().compliance('phi'),
|
|
112
|
+
* organization: () => fp.manyToOne(Organization).nullable(),
|
|
113
|
+
* }
|
|
114
|
+
* });
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
declare const fp: ForklaunchPropertyBuilders;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Wrapper around MikroORM's `defineEntity` that enforces compliance
|
|
121
|
+
* classification on every field at both compile-time and runtime.
|
|
122
|
+
*
|
|
123
|
+
* The return type is inferred directly from `defineEntity` — `InferEntity`
|
|
124
|
+
* works because `ClassifiedScalarProperty<V,O>` extends `PropertyChain<V,O>`.
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* const User = defineComplianceEntity({
|
|
129
|
+
* name: 'User',
|
|
130
|
+
* properties: {
|
|
131
|
+
* id: fp.uuid().primary().compliance('none'),
|
|
132
|
+
* email: fp.string().unique().compliance('pii'),
|
|
133
|
+
* organization: () => fp.manyToOne(Organization).nullable(),
|
|
134
|
+
* }
|
|
135
|
+
* });
|
|
136
|
+
* export type User = InferEntity<typeof User>;
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
declare function defineComplianceEntity<const TName extends string, const TTableName extends string, const TProperties extends Record<string, ClassifiedProperty>, const TPK extends (keyof TProperties)[] | undefined = undefined, const TBase = never, const TRepository = never, const TForceObject extends boolean = false>(meta: EntityMetadataWithProperties<TName, TTableName, TProperties, TPK, TBase, TRepository, TForceObject>): _mikro_orm_core.EntitySchemaWithMeta<TName, TTableName, _mikro_orm_core.InferEntityFromProperties<TProperties, TPK, TBase, TRepository, TForceObject>, TBase, TProperties, _mikro_orm_core.EntityCtor<_mikro_orm_core.InferEntityFromProperties<TProperties, TPK, TBase, TRepository, TForceObject>>>;
|
|
140
|
+
|
|
141
|
+
declare class FieldEncryptor {
|
|
142
|
+
private readonly masterKey;
|
|
143
|
+
constructor(masterKey: string);
|
|
144
|
+
/**
|
|
145
|
+
* Derive a per-tenant 32-byte key using HKDF-SHA256.
|
|
146
|
+
* The master key is used as input key material and the tenantId as info context.
|
|
147
|
+
*/
|
|
148
|
+
deriveKey(tenantId: string): Buffer;
|
|
149
|
+
/**
|
|
150
|
+
* Encrypt a plaintext string for a specific tenant.
|
|
151
|
+
*
|
|
152
|
+
* @returns Format: `v1:{base64(iv)}:{base64(authTag)}:{base64(ciphertext)}`
|
|
153
|
+
*/
|
|
154
|
+
encrypt(plaintext: string | null): string | null;
|
|
155
|
+
encrypt(plaintext: string | null, tenantId: string): string | null;
|
|
156
|
+
/**
|
|
157
|
+
* Decrypt a ciphertext string produced by {@link encrypt}.
|
|
158
|
+
*/
|
|
159
|
+
decrypt(ciphertext: string | null): string | null;
|
|
160
|
+
decrypt(ciphertext: string | null, tenantId: string): string | null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* MikroORM EventSubscriber that enforces field-level encryption for
|
|
165
|
+
* compliance-classified fields (PHI and PCI).
|
|
166
|
+
*
|
|
167
|
+
* - **onBeforeCreate / onBeforeUpdate**: Encrypts PHI/PCI fields before
|
|
168
|
+
* database persistence. Throws `EncryptionRequiredError` if the encryption
|
|
169
|
+
* key is unavailable.
|
|
170
|
+
* - **onLoad**: Decrypts PHI/PCI fields after loading from the database.
|
|
171
|
+
* Pre-migration plaintext (no `v1:` prefix) is returned as-is with a
|
|
172
|
+
* console warning to support rolling deployments.
|
|
173
|
+
*
|
|
174
|
+
* The tenant ID for key derivation is read from the EntityManager's filter
|
|
175
|
+
* parameters (set by the tenant context middleware).
|
|
176
|
+
*/
|
|
177
|
+
declare class ComplianceEventSubscriber implements EventSubscriber {
|
|
178
|
+
private readonly encryptor;
|
|
179
|
+
constructor(encryptor: FieldEncryptor);
|
|
180
|
+
beforeCreate(args: EventArgs<unknown>): Promise<void>;
|
|
181
|
+
beforeUpdate(args: EventArgs<unknown>): Promise<void>;
|
|
182
|
+
onLoad(args: EventArgs<unknown>): Promise<void>;
|
|
183
|
+
private encryptFields;
|
|
184
|
+
private decryptFields;
|
|
185
|
+
/**
|
|
186
|
+
* Read the tenant ID from the EntityManager's filter parameters.
|
|
187
|
+
* The tenant context middleware sets this when forking the EM per request.
|
|
188
|
+
*/
|
|
189
|
+
private getTenantId;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Wraps an EntityManager to block `nativeInsert`, `nativeUpdate`, and
|
|
193
|
+
* `nativeDelete` on entities that have PHI or PCI compliance fields.
|
|
194
|
+
*
|
|
195
|
+
* This prevents bypassing the ComplianceEventSubscriber's encryption
|
|
196
|
+
* by using raw queries. Call this in the tenant context middleware when
|
|
197
|
+
* creating the request-scoped EM.
|
|
198
|
+
*
|
|
199
|
+
* @returns A Proxy-wrapped EntityManager that throws on native query
|
|
200
|
+
* operations targeting compliance entities.
|
|
201
|
+
*/
|
|
202
|
+
declare function wrapEmWithNativeQueryBlocking<T extends EntityManager>(em: T): T;
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* The name used to register the tenant isolation filter.
|
|
206
|
+
*/
|
|
207
|
+
declare const TENANT_FILTER_NAME = "tenant";
|
|
208
|
+
/**
|
|
209
|
+
* Creates the tenant filter definition.
|
|
210
|
+
*
|
|
211
|
+
* The filter adds `WHERE organizationId = :tenantId` to all queries
|
|
212
|
+
* on entities that have an `organizationId` or `organization` property.
|
|
213
|
+
* Entities without either property are unaffected (empty condition).
|
|
214
|
+
*/
|
|
215
|
+
declare function createTenantFilterDef(): FilterDef;
|
|
216
|
+
/**
|
|
217
|
+
* Registers the global tenant isolation filter on the ORM's entity manager.
|
|
218
|
+
* Call this once at application bootstrap after `MikroORM.init()`.
|
|
219
|
+
*
|
|
220
|
+
* After calling this, every fork of the EM will inherit the filter.
|
|
221
|
+
* Set the tenant ID per-request via:
|
|
222
|
+
*
|
|
223
|
+
* ```ts
|
|
224
|
+
* em.setFilterParams('tenant', { tenantId: 'org-123' });
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
declare function setupTenantFilter(orm: MikroORM): void;
|
|
228
|
+
/**
|
|
229
|
+
* Returns a forked EntityManager with the tenant filter disabled.
|
|
230
|
+
*
|
|
231
|
+
* Use this only from code paths that have verified super-admin permissions.
|
|
232
|
+
* Queries executed through the returned EM will return cross-tenant data.
|
|
233
|
+
*/
|
|
234
|
+
declare function getSuperAdminContext(em: EntityManager): EntityManager;
|
|
235
|
+
|
|
236
|
+
interface RlsConfig {
|
|
237
|
+
/**
|
|
238
|
+
* Whether to enable PostgreSQL Row-Level Security.
|
|
239
|
+
* Defaults to `true` when the driver is PostgreSQL, `false` otherwise.
|
|
240
|
+
* Set to `false` to opt out even on PostgreSQL.
|
|
241
|
+
*/
|
|
242
|
+
enabled?: boolean;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* MikroORM EventSubscriber that executes `SET LOCAL app.tenant_id = :tenantId`
|
|
246
|
+
* at the start of every transaction when PostgreSQL RLS is enabled.
|
|
247
|
+
*
|
|
248
|
+
* This ensures that even if the MikroORM global filter is somehow bypassed,
|
|
249
|
+
* the database-level RLS policy enforces tenant isolation.
|
|
250
|
+
*
|
|
251
|
+
* The tenant ID is read from the EntityManager's filter parameters
|
|
252
|
+
* (set by the tenant context middleware).
|
|
253
|
+
*/
|
|
254
|
+
declare class RlsEventSubscriber implements EventSubscriber {
|
|
255
|
+
afterTransactionStart(args: TransactionEventArgs): Promise<void>;
|
|
256
|
+
private getTenantId;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Sets up PostgreSQL Row-Level Security integration.
|
|
260
|
+
*
|
|
261
|
+
* 1. Registers the `RlsEventSubscriber` to run `SET LOCAL app.tenant_id`
|
|
262
|
+
* at the start of every transaction.
|
|
263
|
+
* 2. Validates that RLS policies exist on tenant-scoped tables (warns if missing).
|
|
264
|
+
*
|
|
265
|
+
* Call this at application bootstrap after `MikroORM.init()` and `setupTenantFilter()`.
|
|
266
|
+
*
|
|
267
|
+
* @param orm - The initialized MikroORM instance
|
|
268
|
+
* @param config - RLS configuration (enabled defaults to auto-detect PostgreSQL)
|
|
269
|
+
*/
|
|
270
|
+
declare function setupRls(orm: MikroORM, config?: RlsConfig): void;
|
|
271
|
+
|
|
272
|
+
export { type ClassifiedProperty, type ClassifiedRelationChain, type ClassifiedScalarProperty, ComplianceEventSubscriber, ComplianceLevel, ComplianceLevel as ComplianceLevelType, type ForklaunchPropertyBuilders, type ForklaunchPropertyChain, type RlsConfig, RlsEventSubscriber, TENANT_FILTER_NAME, createTenantFilterDef, defineComplianceEntity, entityHasEncryptedFields, fp, getComplianceMetadata, getEntityComplianceFields, getSuperAdminContext, setupRls, setupTenantFilter, wrapEmWithNativeQueryBlocking };
|