@nest-yalc-2/omnikernel-module 2.2.2 → 2.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/package.json +17 -15
  2. package/src/__tests__/omni-collection.entity.spec.ts +49 -49
  3. package/src/__tests__/omni-collection.service.spec.ts +83 -83
  4. package/src/__tests__/omni-document.entity.spec.ts +55 -55
  5. package/src/__tests__/omni-document.service.spec.ts +83 -83
  6. package/src/__tests__/omni-extension-projection-constraints.spec.ts +518 -518
  7. package/src/__tests__/omni-extension-projection.registration.spec.ts +947 -946
  8. package/src/__tests__/omni-extension-projection.service.spec.ts +806 -806
  9. package/src/__tests__/omni-external-ref.entity.spec.ts +36 -36
  10. package/src/__tests__/omni-external-ref.service.spec.ts +252 -252
  11. package/src/__tests__/omni-named.entity.spec.ts +33 -33
  12. package/src/__tests__/omni-record.entity.spec.ts +32 -32
  13. package/src/__tests__/omni-record.service.spec.ts +107 -107
  14. package/src/__tests__/omni-relation-semantics.spec.ts +102 -102
  15. package/src/__tests__/omni-relation.entity.spec.ts +38 -38
  16. package/src/__tests__/omni-scope-substrate.spec.ts +81 -81
  17. package/src/__tests__/omni-scoped.backend.spec.ts +115 -115
  18. package/src/__tests__/omni-scoped.service.spec.ts +354 -354
  19. package/src/__tests__/omnikernel.diagnostics.spec.ts +94 -94
  20. package/src/__tests__/omnikernel.module.spec.ts +38 -40
  21. package/src/__tests__/omnikernel.persistence.spec.ts +154 -154
  22. package/src/__tests__/omnikernel.public-api.spec.ts +238 -238
  23. package/src/__tests__/omnikernel.query.service.spec.ts +155 -155
  24. package/src/base/omni-base.entity.ts +41 -41
  25. package/src/base/omni-external-ref.entity.ts +53 -53
  26. package/src/base/omni-named.entity.ts +16 -16
  27. package/src/base/omni-record.entity.ts +51 -51
  28. package/src/base/omni-relation.entity.ts +72 -72
  29. package/src/index.ts +49 -49
  30. package/src/omni-collection-kind.enum.ts +10 -10
  31. package/src/omni-collection.backend.ts +16 -16
  32. package/src/omni-collection.dto.ts +170 -170
  33. package/src/omni-collection.entity.ts +21 -21
  34. package/src/omni-collection.service.ts +75 -75
  35. package/src/omni-document-kind.enum.ts +12 -12
  36. package/src/omni-document.backend.ts +16 -16
  37. package/src/omni-document.dto.ts +198 -198
  38. package/src/omni-document.entity.ts +30 -30
  39. package/src/omni-document.service.ts +75 -75
  40. package/src/omni-dto.helpers.ts +21 -21
  41. package/src/omni-extension-projection.definition.ts +294 -294
  42. package/src/omni-extension-projection.resource.ts +274 -274
  43. package/src/omni-extension-projection.service.ts +452 -452
  44. package/src/omni-external-ref-binding.validator.ts +53 -53
  45. package/src/omni-external-ref-internal-type.enum.ts +11 -11
  46. package/src/omni-external-ref.backend.ts +25 -25
  47. package/src/omni-external-ref.dto.ts +128 -128
  48. package/src/omni-external-ref.service.ts +255 -255
  49. package/src/omni-migration.ts +365 -365
  50. package/src/omni-named.backend.ts +11 -11
  51. package/src/omni-named.dto.ts +86 -86
  52. package/src/omni-projection.catalog.ts +345 -345
  53. package/src/omni-projection.lifecycle.ts +53 -53
  54. package/src/omni-record-status.enum.ts +11 -11
  55. package/src/omni-record.backend.ts +19 -19
  56. package/src/omni-record.dto.ts +157 -157
  57. package/src/omni-record.service.ts +113 -113
  58. package/src/omni-relation-kind.contract.ts +49 -49
  59. package/src/omni-relation-kind.enum.ts +12 -12
  60. package/src/omni-relation-projection.definition.ts +166 -166
  61. package/src/omni-relation-projection.resource.ts +360 -360
  62. package/src/omni-relation-projection.service.ts +610 -610
  63. package/src/omni-relation-semantics.ts +52 -52
  64. package/src/omni-relation-status.enum.ts +11 -11
  65. package/src/omni-relation.backend.ts +21 -21
  66. package/src/omni-relation.dto.ts +142 -142
  67. package/src/omni-relation.service.ts +159 -159
  68. package/src/omni-scope.ts +161 -161
  69. package/src/omni-scoped.backend.ts +110 -110
  70. package/src/omni-scoped.repository.ts +34 -34
  71. package/src/omni-scoped.service.ts +338 -338
  72. package/src/omnikernel.diagnostics.ts +83 -83
  73. package/src/omnikernel.module.ts +97 -97
  74. package/src/omnikernel.query.service.ts +108 -108
@@ -1,360 +1,360 @@
1
- import { Scope, type InjectionToken, type Provider } from '@nestjs/common';
2
- import { InputType, Int, ObjectType } from '@nestjs/graphql';
3
- import { getDataSourceToken } from '@nestjs/typeorm';
4
- import {
5
- CrudGenResourceFactory,
6
- FilterOptionType,
7
- ModelField,
8
- ModelObject,
9
- getServiceToken,
10
- type ICrudGenResourceFactoryResult,
11
- } from '@nest-yalc-2/crud-gen';
12
- import {
13
- GQLDataLoader,
14
- getDataloaderToken,
15
- getFn,
16
- } from '@nest-yalc-2/data-loader';
17
- import { UUIDScalar } from '@nest-yalc-2/graphql/scalars/uuid.scalar.js';
18
- import type { ClassType } from '@node-yalc/types/globals.js';
19
- import returnValue from '@node-yalc/utils/returnValue.js';
20
- import { Exclude, Expose } from 'class-transformer';
21
- import { GraphQLJSON } from 'graphql-type-json';
22
- import type { DataSource, ObjectLiteral } from 'typeorm';
23
- import { OmniRecordEntity } from './base/omni-record.entity.js';
24
- import { OmniRelationEntity } from './base/omni-relation.entity.js';
25
- import type {
26
- OmniProjectionReaderCatalogProvider,
27
- OmniRelationProjectionReaderRegistration,
28
- } from './omni-projection.catalog.js';
29
- import {
30
- getOmniRelationProjectionAliases,
31
- getOmniRelationProjectionAllowedKinds,
32
- type OmniRelationProjectionDefinition,
33
- } from './omni-relation-projection.definition.js';
34
- import type { OmniProjectionLifecycleProvider } from './omni-projection.lifecycle.js';
35
- import { OmniRelationProjectionService } from './omni-relation-projection.service.js';
36
- import { createOmniRelationKindContract } from './omni-relation-kind.contract.js';
37
- import { OMNI_KERNEL_OPTIONS, OmniScopeContext } from './omni-scope.js';
38
-
39
- type RelationGraphqlClass = new () => Record<string, unknown>;
40
-
41
- export interface OmniRelationProjectionGraphqlNames {
42
- object: string;
43
- create: string;
44
- patch: string;
45
- conditions: string;
46
- }
47
-
48
- export interface OmniRelationProjectionRegistrationOptions<
49
- Api extends ObjectLiteral,
50
- > {
51
- /** API-only model used by CrudGen to derive operation names and tokens. */
52
- apiModel: ClassType<Api>;
53
- definition: OmniRelationProjectionDefinition;
54
- dbConnection: string;
55
- /** Shared catalog provider, normally built from all returned registrations. */
56
- catalog?: OmniProjectionReaderCatalogProvider;
57
- /** Request-scoped app policy injected into the generated service factory. */
58
- lifecycle?: OmniProjectionLifecycleProvider<
59
- OmniRelationProjectionDefinition,
60
- OmniRelationEntity
61
- >;
62
- rest?: {
63
- path?: string;
64
- };
65
- graphql?: {
66
- prefix?: string;
67
- names?: OmniRelationProjectionGraphqlNames;
68
- };
69
- /** Optional consumer-owned ModuleRef token for generated GraphQL DI. */
70
- moduleRefToken?: InjectionToken;
71
- }
72
-
73
- export interface OmniRelationProjectionGraphqlTypes {
74
- object: RelationGraphqlClass;
75
- create: RelationGraphqlClass;
76
- patch: RelationGraphqlClass;
77
- conditions: RelationGraphqlClass;
78
- }
79
-
80
- export interface OmniRelationProjectionRegistration<Api extends ObjectLiteral> {
81
- /** omni-relation already belongs to OmniKernelModule; no new table exists. */
82
- entities: readonly [];
83
- /** Include these in OmniKernelModule.register({ relationKinds }). */
84
- relationKinds: readonly string[];
85
- definition: OmniRelationProjectionDefinition;
86
- reader: OmniRelationProjectionReaderRegistration;
87
- graphqlTypes: OmniRelationProjectionGraphqlTypes;
88
- resource: ICrudGenResourceFactoryResult<Api>;
89
- controllers: readonly ClassType[];
90
- providers: readonly Provider[];
91
- serviceToken: string;
92
- dataLoaderToken: string;
93
- }
94
-
95
- function namedClass(
96
- name: string,
97
- outputFields?: readonly (readonly [string, string])[],
98
- ): RelationGraphqlClass {
99
- return {
100
- [name]: class {
101
- constructor(data?: Record<string, unknown>) {
102
- if (!outputFields) {
103
- Object.assign(this, data);
104
- return;
105
- }
106
- for (const [publicName, destination] of outputFields) {
107
- if (data && Object.prototype.hasOwnProperty.call(data, destination)) {
108
- (this as Record<string, unknown>)[publicName] = data[destination];
109
- }
110
- }
111
- }
112
- },
113
- }[name] as RelationGraphqlClass;
114
- }
115
-
116
- function addField(
117
- target: RelationGraphqlClass,
118
- name: string,
119
- destination: string,
120
- type: unknown,
121
- nullable: boolean,
122
- ): void {
123
- ModelField({
124
- dst: destination,
125
- gqlType: returnValue(type),
126
- gqlOptions: { nullable },
127
- })(target.prototype, name);
128
- Expose()(target.prototype, name);
129
- }
130
-
131
- function defaultGraphqlNames(
132
- apiModel: ClassType,
133
- ): OmniRelationProjectionGraphqlNames {
134
- return {
135
- object: apiModel.name,
136
- create: `${apiModel.name}CreateInput`,
137
- patch: `${apiModel.name}UpdateInput`,
138
- conditions: `${apiModel.name}Condition`,
139
- };
140
- }
141
-
142
- /** Builds aliased public types while retaining native fields inside the store. */
143
- export function createOmniRelationProjectionGraphqlTypes(
144
- definition: OmniRelationProjectionDefinition,
145
- names: OmniRelationProjectionGraphqlNames,
146
- ): OmniRelationProjectionGraphqlTypes {
147
- const aliases = getOmniRelationProjectionAliases(definition);
148
- const multipleKinds =
149
- getOmniRelationProjectionAllowedKinds(definition).length > 1;
150
- const objectFields: Array<readonly [string, string]> = [
151
- ['guid', 'guid'],
152
- [aliases.source, 'sourceRecordId'],
153
- [aliases.target, 'targetRecordId'],
154
- ['revision', 'revision'],
155
- [aliases.payload, 'payload'],
156
- ];
157
- if (multipleKinds) objectFields.splice(1, 0, [aliases.kind, 'kind']);
158
- const object = namedClass(names.object, objectFields);
159
- ObjectType(names.object)(object);
160
- ModelObject({
161
- filters: { type: FilterOptionType.INCLUDE, fields: ['guid'] },
162
- })(object);
163
- Exclude()(object);
164
- addField(object, 'guid', 'guid', UUIDScalar, false);
165
- if (multipleKinds) {
166
- addField(object, aliases.kind, 'kind', String, false);
167
- }
168
- addField(object, aliases.source, 'sourceRecordId', UUIDScalar, false);
169
- addField(object, aliases.target, 'targetRecordId', UUIDScalar, false);
170
- addField(object, 'revision', 'revision', Int, false);
171
- addField(object, aliases.payload, 'payload', GraphQLJSON, true);
172
-
173
- const create = namedClass(names.create);
174
- InputType(names.create)(create);
175
- ModelObject()(create);
176
- addField(create, 'guid', 'guid', UUIDScalar, false);
177
- if (multipleKinds) {
178
- addField(create, aliases.kind, 'kind', String, false);
179
- }
180
- addField(create, aliases.source, 'sourceRecordId', UUIDScalar, false);
181
- addField(create, aliases.target, 'targetRecordId', UUIDScalar, false);
182
- addField(create, aliases.payload, 'payload', GraphQLJSON, true);
183
-
184
- const patch = namedClass(names.patch);
185
- InputType(names.patch)(patch);
186
- ModelObject()(patch);
187
- addField(patch, aliases.payload, 'payload', GraphQLJSON, true);
188
- addField(patch, 'expectedRevision', 'expectedRevision', Int, false);
189
-
190
- const conditions = namedClass(names.conditions);
191
- InputType(names.conditions)(conditions);
192
- ModelObject()(conditions);
193
- addField(conditions, 'guid', 'guid', UUIDScalar, false);
194
-
195
- return { object, create, patch, conditions };
196
- }
197
-
198
- /**
199
- * Composes one generated REST/GraphQL/dataloader surface over omni-relation.
200
- * It never creates a relation table or a handwritten transport adapter.
201
- */
202
- export function createOmniRelationProjectionRegistration<
203
- Api extends ObjectLiteral,
204
- >(
205
- options: OmniRelationProjectionRegistrationOptions<Api>,
206
- ): OmniRelationProjectionRegistration<Api> {
207
- const graphqlTypes = createOmniRelationProjectionGraphqlTypes(
208
- options.definition,
209
- options.graphql?.names ?? defaultGraphqlNames(options.apiModel),
210
- );
211
- const serviceToken = getServiceToken(options.apiModel);
212
- const dataLoaderToken = getDataloaderToken(options.apiModel);
213
- const resource = CrudGenResourceFactory<Api>({
214
- entityModel: options.apiModel,
215
- backend: false,
216
- graphql: {
217
- resolver: {
218
- dto: graphqlTypes.object as ClassType,
219
- input: {
220
- create: graphqlTypes.create as ClassType,
221
- update: graphqlTypes.patch as ClassType,
222
- conditions: graphqlTypes.conditions as ClassType,
223
- },
224
- ...(options.graphql?.prefix ? { prefix: options.graphql.prefix } : {}),
225
- ...(options.moduleRefToken !== undefined
226
- ? { moduleRefToken: options.moduleRefToken }
227
- : {}),
228
- queries: { getResource: { idName: 'guid' } },
229
- },
230
- serviceToken,
231
- dataLoaderToken,
232
- },
233
- rest: {
234
- dto: graphqlTypes.object as ClassType,
235
- serialize: true,
236
- ...(options.rest?.path ? { path: options.rest.path } : {}),
237
- idField: 'guid' as keyof Api & string,
238
- serviceToken,
239
- },
240
- });
241
- const relationKinds = Object.freeze([
242
- ...getOmniRelationProjectionAllowedKinds(options.definition),
243
- ]);
244
- const reader = Object.freeze({
245
- type: 'relation' as const,
246
- id: options.definition.id,
247
- definition: options.definition,
248
- });
249
- const serviceProvider: Provider =
250
- options.lifecycle && options.catalog
251
- ? {
252
- provide: serviceToken,
253
- scope: Scope.REQUEST,
254
- useFactory: (
255
- dataSource: DataSource,
256
- scope: OmniScopeContext,
257
- omniOptions: { deletion: { relation: 'hard' | 'tombstone' } },
258
- lifecycle: any,
259
- catalog: any,
260
- ) =>
261
- new OmniRelationProjectionService(
262
- dataSource.getRepository(OmniRelationEntity),
263
- scope,
264
- omniOptions.deletion.relation,
265
- dataSource.getRepository(OmniRecordEntity),
266
- createOmniRelationKindContract(relationKinds),
267
- options.definition,
268
- dataSource,
269
- lifecycle,
270
- catalog,
271
- ),
272
- inject: [
273
- getDataSourceToken(options.dbConnection),
274
- OmniScopeContext,
275
- OMNI_KERNEL_OPTIONS,
276
- options.lifecycle.token,
277
- options.catalog.token,
278
- ],
279
- }
280
- : options.lifecycle
281
- ? {
282
- provide: serviceToken,
283
- scope: Scope.REQUEST,
284
- useFactory: (
285
- dataSource: DataSource,
286
- scope: OmniScopeContext,
287
- omniOptions: { deletion: { relation: 'hard' | 'tombstone' } },
288
- lifecycle: any,
289
- ) =>
290
- new OmniRelationProjectionService(
291
- dataSource.getRepository(OmniRelationEntity),
292
- scope,
293
- omniOptions.deletion.relation,
294
- dataSource.getRepository(OmniRecordEntity),
295
- createOmniRelationKindContract(relationKinds),
296
- options.definition,
297
- dataSource,
298
- lifecycle,
299
- ),
300
- inject: [
301
- getDataSourceToken(options.dbConnection),
302
- OmniScopeContext,
303
- OMNI_KERNEL_OPTIONS,
304
- options.lifecycle.token,
305
- ],
306
- }
307
- : {
308
- provide: serviceToken,
309
- scope: Scope.REQUEST,
310
- useFactory: (
311
- dataSource: DataSource,
312
- scope: OmniScopeContext,
313
- omniOptions: { deletion: { relation: 'hard' | 'tombstone' } },
314
- ) =>
315
- new OmniRelationProjectionService(
316
- dataSource.getRepository(OmniRelationEntity),
317
- scope,
318
- omniOptions.deletion.relation,
319
- dataSource.getRepository(OmniRecordEntity),
320
- createOmniRelationKindContract(relationKinds),
321
- options.definition,
322
- dataSource,
323
- undefined,
324
- ),
325
- inject: [
326
- getDataSourceToken(options.dbConnection),
327
- OmniScopeContext,
328
- OMNI_KERNEL_OPTIONS,
329
- ],
330
- };
331
- const providers: Provider[] = [
332
- serviceProvider,
333
- {
334
- provide: dataLoaderToken,
335
- scope: Scope.REQUEST,
336
- useFactory: (
337
- service: OmniRelationProjectionService,
338
- scope: OmniScopeContext,
339
- ) =>
340
- new GQLDataLoader(getFn(service as never), 'guid', undefined, {
341
- cacheKeyFn: (key) => scope.cacheKey(key),
342
- }),
343
- inject: [serviceToken, OmniScopeContext],
344
- },
345
- ...resource.providers,
346
- ];
347
-
348
- return Object.freeze({
349
- entities: Object.freeze([]) as readonly [],
350
- relationKinds,
351
- definition: options.definition,
352
- reader,
353
- graphqlTypes: Object.freeze(graphqlTypes),
354
- resource,
355
- controllers: Object.freeze([...resource.controllers]),
356
- providers: Object.freeze(providers),
357
- serviceToken,
358
- dataLoaderToken,
359
- });
360
- }
1
+ import { Scope, type InjectionToken, type Provider } from '@nestjs/common';
2
+ import { InputType, Int, ObjectType } from '@nestjs/graphql';
3
+ import { getDataSourceToken } from '@nestjs/typeorm';
4
+ import {
5
+ CrudGenResourceFactory,
6
+ FilterOptionType,
7
+ ModelField,
8
+ ModelObject,
9
+ getServiceToken,
10
+ type ICrudGenResourceFactoryResult,
11
+ } from '@nest-yalc-2/crud-gen';
12
+ import {
13
+ GQLDataLoader,
14
+ getDataloaderToken,
15
+ getFn,
16
+ } from '@nest-yalc-2/data-loader';
17
+ import { UUIDScalar } from '@nest-yalc-2/graphql/scalars/uuid.scalar.js';
18
+ import type { ClassType } from '@node-yalc/types/globals.js';
19
+ import returnValue from '@node-yalc/utils/returnValue.js';
20
+ import { Exclude, Expose } from 'class-transformer';
21
+ import { GraphQLJSON } from 'graphql-type-json';
22
+ import type { DataSource, ObjectLiteral } from 'typeorm';
23
+ import { OmniRecordEntity } from './base/omni-record.entity.js';
24
+ import { OmniRelationEntity } from './base/omni-relation.entity.js';
25
+ import type {
26
+ OmniProjectionReaderCatalogProvider,
27
+ OmniRelationProjectionReaderRegistration,
28
+ } from './omni-projection.catalog.js';
29
+ import {
30
+ getOmniRelationProjectionAliases,
31
+ getOmniRelationProjectionAllowedKinds,
32
+ type OmniRelationProjectionDefinition,
33
+ } from './omni-relation-projection.definition.js';
34
+ import type { OmniProjectionLifecycleProvider } from './omni-projection.lifecycle.js';
35
+ import { OmniRelationProjectionService } from './omni-relation-projection.service.js';
36
+ import { createOmniRelationKindContract } from './omni-relation-kind.contract.js';
37
+ import { OMNI_KERNEL_OPTIONS, OmniScopeContext } from './omni-scope.js';
38
+
39
+ type RelationGraphqlClass = new () => Record<string, unknown>;
40
+
41
+ export interface OmniRelationProjectionGraphqlNames {
42
+ object: string;
43
+ create: string;
44
+ patch: string;
45
+ conditions: string;
46
+ }
47
+
48
+ export interface OmniRelationProjectionRegistrationOptions<
49
+ Api extends ObjectLiteral,
50
+ > {
51
+ /** API-only model used by CrudGen to derive operation names and tokens. */
52
+ apiModel: ClassType<Api>;
53
+ definition: OmniRelationProjectionDefinition;
54
+ dbConnection: string;
55
+ /** Shared catalog provider, normally built from all returned registrations. */
56
+ catalog?: OmniProjectionReaderCatalogProvider;
57
+ /** Request-scoped app policy injected into the generated service factory. */
58
+ lifecycle?: OmniProjectionLifecycleProvider<
59
+ OmniRelationProjectionDefinition,
60
+ OmniRelationEntity
61
+ >;
62
+ rest?: {
63
+ path?: string;
64
+ };
65
+ graphql?: {
66
+ prefix?: string;
67
+ names?: OmniRelationProjectionGraphqlNames;
68
+ };
69
+ /** Optional consumer-owned ModuleRef token for generated GraphQL DI. */
70
+ moduleRefToken?: InjectionToken;
71
+ }
72
+
73
+ export interface OmniRelationProjectionGraphqlTypes {
74
+ object: RelationGraphqlClass;
75
+ create: RelationGraphqlClass;
76
+ patch: RelationGraphqlClass;
77
+ conditions: RelationGraphqlClass;
78
+ }
79
+
80
+ export interface OmniRelationProjectionRegistration<Api extends ObjectLiteral> {
81
+ /** omni-relation already belongs to OmniKernelModule; no new table exists. */
82
+ entities: readonly [];
83
+ /** Include these in OmniKernelModule.register({ relationKinds }). */
84
+ relationKinds: readonly string[];
85
+ definition: OmniRelationProjectionDefinition;
86
+ reader: OmniRelationProjectionReaderRegistration;
87
+ graphqlTypes: OmniRelationProjectionGraphqlTypes;
88
+ resource: ICrudGenResourceFactoryResult<Api>;
89
+ controllers: readonly ClassType[];
90
+ providers: readonly Provider[];
91
+ serviceToken: string;
92
+ dataLoaderToken: string;
93
+ }
94
+
95
+ function namedClass(
96
+ name: string,
97
+ outputFields?: readonly (readonly [string, string])[],
98
+ ): RelationGraphqlClass {
99
+ return {
100
+ [name]: class {
101
+ constructor(data?: Record<string, unknown>) {
102
+ if (!outputFields) {
103
+ Object.assign(this, data);
104
+ return;
105
+ }
106
+ for (const [publicName, destination] of outputFields) {
107
+ if (data && Object.prototype.hasOwnProperty.call(data, destination)) {
108
+ (this as Record<string, unknown>)[publicName] = data[destination];
109
+ }
110
+ }
111
+ }
112
+ },
113
+ }[name] as RelationGraphqlClass;
114
+ }
115
+
116
+ function addField(
117
+ target: RelationGraphqlClass,
118
+ name: string,
119
+ destination: string,
120
+ type: unknown,
121
+ nullable: boolean,
122
+ ): void {
123
+ ModelField({
124
+ dst: destination,
125
+ gqlType: returnValue(type),
126
+ gqlOptions: { nullable },
127
+ })(target.prototype, name);
128
+ Expose()(target.prototype, name);
129
+ }
130
+
131
+ function defaultGraphqlNames(
132
+ apiModel: ClassType,
133
+ ): OmniRelationProjectionGraphqlNames {
134
+ return {
135
+ object: apiModel.name,
136
+ create: `${apiModel.name}CreateInput`,
137
+ patch: `${apiModel.name}UpdateInput`,
138
+ conditions: `${apiModel.name}Condition`,
139
+ };
140
+ }
141
+
142
+ /** Builds aliased public types while retaining native fields inside the store. */
143
+ export function createOmniRelationProjectionGraphqlTypes(
144
+ definition: OmniRelationProjectionDefinition,
145
+ names: OmniRelationProjectionGraphqlNames,
146
+ ): OmniRelationProjectionGraphqlTypes {
147
+ const aliases = getOmniRelationProjectionAliases(definition);
148
+ const multipleKinds =
149
+ getOmniRelationProjectionAllowedKinds(definition).length > 1;
150
+ const objectFields: Array<readonly [string, string]> = [
151
+ ['guid', 'guid'],
152
+ [aliases.source, 'sourceRecordId'],
153
+ [aliases.target, 'targetRecordId'],
154
+ ['revision', 'revision'],
155
+ [aliases.payload, 'payload'],
156
+ ];
157
+ if (multipleKinds) objectFields.splice(1, 0, [aliases.kind, 'kind']);
158
+ const object = namedClass(names.object, objectFields);
159
+ ObjectType(names.object)(object);
160
+ ModelObject({
161
+ filters: { type: FilterOptionType.INCLUDE, fields: ['guid'] },
162
+ })(object);
163
+ Exclude()(object);
164
+ addField(object, 'guid', 'guid', UUIDScalar, false);
165
+ if (multipleKinds) {
166
+ addField(object, aliases.kind, 'kind', String, false);
167
+ }
168
+ addField(object, aliases.source, 'sourceRecordId', UUIDScalar, false);
169
+ addField(object, aliases.target, 'targetRecordId', UUIDScalar, false);
170
+ addField(object, 'revision', 'revision', Int, false);
171
+ addField(object, aliases.payload, 'payload', GraphQLJSON, true);
172
+
173
+ const create = namedClass(names.create);
174
+ InputType(names.create)(create);
175
+ ModelObject()(create);
176
+ addField(create, 'guid', 'guid', UUIDScalar, false);
177
+ if (multipleKinds) {
178
+ addField(create, aliases.kind, 'kind', String, false);
179
+ }
180
+ addField(create, aliases.source, 'sourceRecordId', UUIDScalar, false);
181
+ addField(create, aliases.target, 'targetRecordId', UUIDScalar, false);
182
+ addField(create, aliases.payload, 'payload', GraphQLJSON, true);
183
+
184
+ const patch = namedClass(names.patch);
185
+ InputType(names.patch)(patch);
186
+ ModelObject()(patch);
187
+ addField(patch, aliases.payload, 'payload', GraphQLJSON, true);
188
+ addField(patch, 'expectedRevision', 'expectedRevision', Int, false);
189
+
190
+ const conditions = namedClass(names.conditions);
191
+ InputType(names.conditions)(conditions);
192
+ ModelObject()(conditions);
193
+ addField(conditions, 'guid', 'guid', UUIDScalar, false);
194
+
195
+ return { object, create, patch, conditions };
196
+ }
197
+
198
+ /**
199
+ * Composes one generated REST/GraphQL/dataloader surface over omni-relation.
200
+ * It never creates a relation table or a handwritten transport adapter.
201
+ */
202
+ export function createOmniRelationProjectionRegistration<
203
+ Api extends ObjectLiteral,
204
+ >(
205
+ options: OmniRelationProjectionRegistrationOptions<Api>,
206
+ ): OmniRelationProjectionRegistration<Api> {
207
+ const graphqlTypes = createOmniRelationProjectionGraphqlTypes(
208
+ options.definition,
209
+ options.graphql?.names ?? defaultGraphqlNames(options.apiModel),
210
+ );
211
+ const serviceToken = getServiceToken(options.apiModel);
212
+ const dataLoaderToken = getDataloaderToken(options.apiModel);
213
+ const resource = CrudGenResourceFactory<Api>({
214
+ entityModel: options.apiModel,
215
+ backend: false,
216
+ graphql: {
217
+ resolver: {
218
+ dto: graphqlTypes.object as ClassType,
219
+ input: {
220
+ create: graphqlTypes.create as ClassType,
221
+ update: graphqlTypes.patch as ClassType,
222
+ conditions: graphqlTypes.conditions as ClassType,
223
+ },
224
+ ...(options.graphql?.prefix ? { prefix: options.graphql.prefix } : {}),
225
+ ...(options.moduleRefToken !== undefined
226
+ ? { moduleRefToken: options.moduleRefToken }
227
+ : {}),
228
+ queries: { getResource: { idName: 'guid' } },
229
+ },
230
+ serviceToken,
231
+ dataLoaderToken,
232
+ },
233
+ rest: {
234
+ dto: graphqlTypes.object as ClassType,
235
+ serialize: true,
236
+ ...(options.rest?.path ? { path: options.rest.path } : {}),
237
+ idField: 'guid' as keyof Api & string,
238
+ serviceToken,
239
+ },
240
+ });
241
+ const relationKinds = Object.freeze([
242
+ ...getOmniRelationProjectionAllowedKinds(options.definition),
243
+ ]);
244
+ const reader = Object.freeze({
245
+ type: 'relation' as const,
246
+ id: options.definition.id,
247
+ definition: options.definition,
248
+ });
249
+ const serviceProvider: Provider =
250
+ options.lifecycle && options.catalog
251
+ ? {
252
+ provide: serviceToken,
253
+ scope: Scope.REQUEST,
254
+ useFactory: (
255
+ dataSource: DataSource,
256
+ scope: OmniScopeContext,
257
+ omniOptions: { deletion: { relation: 'hard' | 'tombstone' } },
258
+ lifecycle: any,
259
+ catalog: any,
260
+ ) =>
261
+ new OmniRelationProjectionService(
262
+ dataSource.getRepository(OmniRelationEntity),
263
+ scope,
264
+ omniOptions.deletion.relation,
265
+ dataSource.getRepository(OmniRecordEntity),
266
+ createOmniRelationKindContract(relationKinds),
267
+ options.definition,
268
+ dataSource,
269
+ lifecycle,
270
+ catalog,
271
+ ),
272
+ inject: [
273
+ getDataSourceToken(options.dbConnection),
274
+ OmniScopeContext,
275
+ OMNI_KERNEL_OPTIONS,
276
+ options.lifecycle.token,
277
+ options.catalog.token,
278
+ ],
279
+ }
280
+ : options.lifecycle
281
+ ? {
282
+ provide: serviceToken,
283
+ scope: Scope.REQUEST,
284
+ useFactory: (
285
+ dataSource: DataSource,
286
+ scope: OmniScopeContext,
287
+ omniOptions: { deletion: { relation: 'hard' | 'tombstone' } },
288
+ lifecycle: any,
289
+ ) =>
290
+ new OmniRelationProjectionService(
291
+ dataSource.getRepository(OmniRelationEntity),
292
+ scope,
293
+ omniOptions.deletion.relation,
294
+ dataSource.getRepository(OmniRecordEntity),
295
+ createOmniRelationKindContract(relationKinds),
296
+ options.definition,
297
+ dataSource,
298
+ lifecycle,
299
+ ),
300
+ inject: [
301
+ getDataSourceToken(options.dbConnection),
302
+ OmniScopeContext,
303
+ OMNI_KERNEL_OPTIONS,
304
+ options.lifecycle.token,
305
+ ],
306
+ }
307
+ : {
308
+ provide: serviceToken,
309
+ scope: Scope.REQUEST,
310
+ useFactory: (
311
+ dataSource: DataSource,
312
+ scope: OmniScopeContext,
313
+ omniOptions: { deletion: { relation: 'hard' | 'tombstone' } },
314
+ ) =>
315
+ new OmniRelationProjectionService(
316
+ dataSource.getRepository(OmniRelationEntity),
317
+ scope,
318
+ omniOptions.deletion.relation,
319
+ dataSource.getRepository(OmniRecordEntity),
320
+ createOmniRelationKindContract(relationKinds),
321
+ options.definition,
322
+ dataSource,
323
+ undefined,
324
+ ),
325
+ inject: [
326
+ getDataSourceToken(options.dbConnection),
327
+ OmniScopeContext,
328
+ OMNI_KERNEL_OPTIONS,
329
+ ],
330
+ };
331
+ const providers: Provider[] = [
332
+ serviceProvider,
333
+ {
334
+ provide: dataLoaderToken,
335
+ scope: Scope.REQUEST,
336
+ useFactory: (
337
+ service: OmniRelationProjectionService,
338
+ scope: OmniScopeContext,
339
+ ) =>
340
+ new GQLDataLoader(getFn(service as never), 'guid', undefined, {
341
+ cacheKeyFn: (key) => scope.cacheKey(key),
342
+ }),
343
+ inject: [serviceToken, OmniScopeContext],
344
+ },
345
+ ...resource.providers,
346
+ ];
347
+
348
+ return Object.freeze({
349
+ entities: Object.freeze([]) as readonly [],
350
+ relationKinds,
351
+ definition: options.definition,
352
+ reader,
353
+ graphqlTypes: Object.freeze(graphqlTypes),
354
+ resource,
355
+ controllers: Object.freeze([...resource.controllers]),
356
+ providers: Object.freeze(providers),
357
+ serviceToken,
358
+ dataLoaderToken,
359
+ });
360
+ }