@nest-yalc-2/omnikernel-module 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +193 -0
  3. package/package.json +52 -0
  4. package/src/__tests__/omni-collection.entity.spec.ts +49 -0
  5. package/src/__tests__/omni-collection.service.spec.ts +83 -0
  6. package/src/__tests__/omni-document.entity.spec.ts +55 -0
  7. package/src/__tests__/omni-document.service.spec.ts +83 -0
  8. package/src/__tests__/omni-extension-projection-constraints.spec.ts +518 -0
  9. package/src/__tests__/omni-extension-projection.registration.spec.ts +946 -0
  10. package/src/__tests__/omni-extension-projection.service.spec.ts +806 -0
  11. package/src/__tests__/omni-external-ref.entity.spec.ts +36 -0
  12. package/src/__tests__/omni-external-ref.service.spec.ts +252 -0
  13. package/src/__tests__/omni-named.entity.spec.ts +33 -0
  14. package/src/__tests__/omni-record.entity.spec.ts +32 -0
  15. package/src/__tests__/omni-record.service.spec.ts +107 -0
  16. package/src/__tests__/omni-relation-semantics.spec.ts +102 -0
  17. package/src/__tests__/omni-relation.entity.spec.ts +38 -0
  18. package/src/__tests__/omni-scope-substrate.spec.ts +81 -0
  19. package/src/__tests__/omni-scoped.backend.spec.ts +115 -0
  20. package/src/__tests__/omni-scoped.service.spec.ts +354 -0
  21. package/src/__tests__/omnikernel.diagnostics.spec.ts +94 -0
  22. package/src/__tests__/omnikernel.module.spec.ts +40 -0
  23. package/src/__tests__/omnikernel.persistence.spec.ts +154 -0
  24. package/src/__tests__/omnikernel.public-api.spec.ts +238 -0
  25. package/src/__tests__/omnikernel.query.service.spec.ts +155 -0
  26. package/src/__tests__/tsconfig.json +6 -0
  27. package/src/base/omni-base.entity.d.ts +9 -0
  28. package/src/base/omni-base.entity.js +53 -0
  29. package/src/base/omni-base.entity.js.map +1 -0
  30. package/src/base/omni-base.entity.ts +41 -0
  31. package/src/base/omni-external-ref.entity.d.ts +13 -0
  32. package/src/base/omni-external-ref.entity.js +72 -0
  33. package/src/base/omni-external-ref.entity.js.map +1 -0
  34. package/src/base/omni-external-ref.entity.ts +53 -0
  35. package/src/base/omni-named.entity.d.ts +6 -0
  36. package/src/base/omni-named.entity.js +35 -0
  37. package/src/base/omni-named.entity.js.map +1 -0
  38. package/src/base/omni-named.entity.ts +16 -0
  39. package/src/base/omni-record.entity.d.ts +13 -0
  40. package/src/base/omni-record.entity.js +71 -0
  41. package/src/base/omni-record.entity.js.map +1 -0
  42. package/src/base/omni-record.entity.ts +51 -0
  43. package/src/base/omni-relation.entity.d.ts +15 -0
  44. package/src/base/omni-relation.entity.js +91 -0
  45. package/src/base/omni-relation.entity.js.map +1 -0
  46. package/src/base/omni-relation.entity.ts +72 -0
  47. package/src/index.d.ts +49 -0
  48. package/src/index.js +66 -0
  49. package/src/index.js.map +1 -0
  50. package/src/index.ts +49 -0
  51. package/src/omni-collection-kind.enum.d.ts +4 -0
  52. package/src/omni-collection-kind.enum.js +13 -0
  53. package/src/omni-collection-kind.enum.js.map +1 -0
  54. package/src/omni-collection-kind.enum.ts +10 -0
  55. package/src/omni-collection.backend.d.ts +2 -0
  56. package/src/omni-collection.backend.js +15 -0
  57. package/src/omni-collection.backend.js.map +1 -0
  58. package/src/omni-collection.backend.ts +16 -0
  59. package/src/omni-collection.dto.d.ts +32 -0
  60. package/src/omni-collection.dto.js +184 -0
  61. package/src/omni-collection.dto.js.map +1 -0
  62. package/src/omni-collection.dto.ts +170 -0
  63. package/src/omni-collection.entity.d.ts +7 -0
  64. package/src/omni-collection.entity.js +41 -0
  65. package/src/omni-collection.entity.js.map +1 -0
  66. package/src/omni-collection.entity.ts +21 -0
  67. package/src/omni-collection.service.d.ts +14 -0
  68. package/src/omni-collection.service.js +24 -0
  69. package/src/omni-collection.service.js.map +1 -0
  70. package/src/omni-collection.service.ts +75 -0
  71. package/src/omni-document-kind.enum.d.ts +6 -0
  72. package/src/omni-document-kind.enum.js +15 -0
  73. package/src/omni-document-kind.enum.js.map +1 -0
  74. package/src/omni-document-kind.enum.ts +12 -0
  75. package/src/omni-document.backend.d.ts +2 -0
  76. package/src/omni-document.backend.js +15 -0
  77. package/src/omni-document.backend.js.map +1 -0
  78. package/src/omni-document.backend.ts +16 -0
  79. package/src/omni-document.dto.d.ts +35 -0
  80. package/src/omni-document.dto.js +213 -0
  81. package/src/omni-document.dto.js.map +1 -0
  82. package/src/omni-document.dto.ts +198 -0
  83. package/src/omni-document.entity.d.ts +10 -0
  84. package/src/omni-document.entity.js +53 -0
  85. package/src/omni-document.entity.js.map +1 -0
  86. package/src/omni-document.entity.ts +30 -0
  87. package/src/omni-document.service.d.ts +14 -0
  88. package/src/omni-document.service.js +24 -0
  89. package/src/omni-document.service.js.map +1 -0
  90. package/src/omni-document.service.ts +75 -0
  91. package/src/omni-dto.helpers.d.ts +1 -0
  92. package/src/omni-dto.helpers.js +10 -0
  93. package/src/omni-dto.helpers.js.map +1 -0
  94. package/src/omni-dto.helpers.ts +21 -0
  95. package/src/omni-extension-projection.definition.d.ts +17 -0
  96. package/src/omni-extension-projection.definition.js +160 -0
  97. package/src/omni-extension-projection.definition.js.map +1 -0
  98. package/src/omni-extension-projection.definition.ts +294 -0
  99. package/src/omni-extension-projection.resource.d.ts +42 -0
  100. package/src/omni-extension-projection.resource.js +127 -0
  101. package/src/omni-extension-projection.resource.js.map +1 -0
  102. package/src/omni-extension-projection.resource.ts +274 -0
  103. package/src/omni-extension-projection.service.d.ts +33 -0
  104. package/src/omni-extension-projection.service.js +332 -0
  105. package/src/omni-extension-projection.service.js.map +1 -0
  106. package/src/omni-extension-projection.service.ts +452 -0
  107. package/src/omni-external-ref-binding.validator.d.ts +14 -0
  108. package/src/omni-external-ref-binding.validator.js +34 -0
  109. package/src/omni-external-ref-binding.validator.js.map +1 -0
  110. package/src/omni-external-ref-binding.validator.ts +53 -0
  111. package/src/omni-external-ref-internal-type.enum.d.ts +5 -0
  112. package/src/omni-external-ref-internal-type.enum.js +14 -0
  113. package/src/omni-external-ref-internal-type.enum.js.map +1 -0
  114. package/src/omni-external-ref-internal-type.enum.ts +11 -0
  115. package/src/omni-external-ref.backend.d.ts +2 -0
  116. package/src/omni-external-ref.backend.js +19 -0
  117. package/src/omni-external-ref.backend.js.map +1 -0
  118. package/src/omni-external-ref.backend.ts +25 -0
  119. package/src/omni-external-ref.dto.d.ts +26 -0
  120. package/src/omni-external-ref.dto.js +140 -0
  121. package/src/omni-external-ref.dto.js.map +1 -0
  122. package/src/omni-external-ref.dto.ts +128 -0
  123. package/src/omni-external-ref.service.d.ts +33 -0
  124. package/src/omni-external-ref.service.js +133 -0
  125. package/src/omni-external-ref.service.js.map +1 -0
  126. package/src/omni-external-ref.service.ts +255 -0
  127. package/src/omni-migration.d.ts +28 -0
  128. package/src/omni-migration.js +239 -0
  129. package/src/omni-migration.js.map +1 -0
  130. package/src/omni-migration.ts +365 -0
  131. package/src/omni-named.backend.d.ts +2 -0
  132. package/src/omni-named.backend.js +13 -0
  133. package/src/omni-named.backend.js.map +1 -0
  134. package/src/omni-named.backend.ts +11 -0
  135. package/src/omni-named.dto.d.ts +19 -0
  136. package/src/omni-named.dto.js +94 -0
  137. package/src/omni-named.dto.js.map +1 -0
  138. package/src/omni-named.dto.ts +86 -0
  139. package/src/omni-projection.catalog.d.ts +49 -0
  140. package/src/omni-projection.catalog.js +177 -0
  141. package/src/omni-projection.catalog.js.map +1 -0
  142. package/src/omni-projection.catalog.ts +345 -0
  143. package/src/omni-projection.lifecycle.d.ts +20 -0
  144. package/src/omni-projection.lifecycle.js +3 -0
  145. package/src/omni-projection.lifecycle.js.map +1 -0
  146. package/src/omni-projection.lifecycle.ts +53 -0
  147. package/src/omni-record-status.enum.d.ts +5 -0
  148. package/src/omni-record-status.enum.js +14 -0
  149. package/src/omni-record-status.enum.js.map +1 -0
  150. package/src/omni-record-status.enum.ts +11 -0
  151. package/src/omni-record.backend.d.ts +2 -0
  152. package/src/omni-record.backend.js +13 -0
  153. package/src/omni-record.backend.js.map +1 -0
  154. package/src/omni-record.backend.ts +19 -0
  155. package/src/omni-record.dto.d.ts +29 -0
  156. package/src/omni-record.dto.js +169 -0
  157. package/src/omni-record.dto.js.map +1 -0
  158. package/src/omni-record.dto.ts +157 -0
  159. package/src/omni-record.service.d.ts +18 -0
  160. package/src/omni-record.service.js +45 -0
  161. package/src/omni-record.service.js.map +1 -0
  162. package/src/omni-record.service.ts +113 -0
  163. package/src/omni-relation-kind.contract.d.ts +9 -0
  164. package/src/omni-relation-kind.contract.js +33 -0
  165. package/src/omni-relation-kind.contract.js.map +1 -0
  166. package/src/omni-relation-kind.contract.ts +49 -0
  167. package/src/omni-relation-kind.enum.d.ts +6 -0
  168. package/src/omni-relation-kind.enum.js +15 -0
  169. package/src/omni-relation-kind.enum.js.map +1 -0
  170. package/src/omni-relation-kind.enum.ts +12 -0
  171. package/src/omni-relation-projection.definition.d.ts +25 -0
  172. package/src/omni-relation-projection.definition.js +83 -0
  173. package/src/omni-relation-projection.definition.js.map +1 -0
  174. package/src/omni-relation-projection.definition.ts +166 -0
  175. package/src/omni-relation-projection.resource.d.ts +51 -0
  176. package/src/omni-relation-projection.resource.js +203 -0
  177. package/src/omni-relation-projection.resource.js.map +1 -0
  178. package/src/omni-relation-projection.resource.ts +360 -0
  179. package/src/omni-relation-projection.service.d.ts +47 -0
  180. package/src/omni-relation-projection.service.js +385 -0
  181. package/src/omni-relation-projection.service.js.map +1 -0
  182. package/src/omni-relation-projection.service.ts +610 -0
  183. package/src/omni-relation-semantics.d.ts +11 -0
  184. package/src/omni-relation-semantics.js +34 -0
  185. package/src/omni-relation-semantics.js.map +1 -0
  186. package/src/omni-relation-semantics.ts +52 -0
  187. package/src/omni-relation-status.enum.d.ts +5 -0
  188. package/src/omni-relation-status.enum.js +14 -0
  189. package/src/omni-relation-status.enum.js.map +1 -0
  190. package/src/omni-relation-status.enum.ts +11 -0
  191. package/src/omni-relation.backend.d.ts +2 -0
  192. package/src/omni-relation.backend.js +17 -0
  193. package/src/omni-relation.backend.js.map +1 -0
  194. package/src/omni-relation.backend.ts +21 -0
  195. package/src/omni-relation.dto.d.ts +28 -0
  196. package/src/omni-relation.dto.js +153 -0
  197. package/src/omni-relation.dto.js.map +1 -0
  198. package/src/omni-relation.dto.ts +142 -0
  199. package/src/omni-relation.service.d.ts +19 -0
  200. package/src/omni-relation.service.js +76 -0
  201. package/src/omni-relation.service.js.map +1 -0
  202. package/src/omni-relation.service.ts +159 -0
  203. package/src/omni-scope.d.ts +33 -0
  204. package/src/omni-scope.js +91 -0
  205. package/src/omni-scope.js.map +1 -0
  206. package/src/omni-scope.ts +161 -0
  207. package/src/omni-scoped.backend.d.ts +21 -0
  208. package/src/omni-scoped.backend.js +47 -0
  209. package/src/omni-scoped.backend.js.map +1 -0
  210. package/src/omni-scoped.backend.ts +110 -0
  211. package/src/omni-scoped.repository.d.ts +10 -0
  212. package/src/omni-scoped.repository.js +26 -0
  213. package/src/omni-scoped.repository.js.map +1 -0
  214. package/src/omni-scoped.repository.ts +34 -0
  215. package/src/omni-scoped.service.d.ts +36 -0
  216. package/src/omni-scoped.service.js +171 -0
  217. package/src/omni-scoped.service.js.map +1 -0
  218. package/src/omni-scoped.service.ts +338 -0
  219. package/src/omnikernel.diagnostics.d.ts +16 -0
  220. package/src/omnikernel.diagnostics.js +50 -0
  221. package/src/omnikernel.diagnostics.js.map +1 -0
  222. package/src/omnikernel.diagnostics.ts +83 -0
  223. package/src/omnikernel.module.d.ts +5 -0
  224. package/src/omnikernel.module.js +87 -0
  225. package/src/omnikernel.module.js.map +1 -0
  226. package/src/omnikernel.module.ts +97 -0
  227. package/src/omnikernel.query.service.d.ts +17 -0
  228. package/src/omnikernel.query.service.js +85 -0
  229. package/src/omnikernel.query.service.js.map +1 -0
  230. package/src/omnikernel.query.service.ts +108 -0
@@ -0,0 +1,115 @@
1
+ import { describe, expect, it } from "@jest/globals";
2
+ import { EventEmitter2 } from "@nestjs/event-emitter";
3
+
4
+ import { OmniNamedEntity } from '../base/omni-named.entity.js';
5
+ import { OmniRecordEntity } from '../base/omni-record.entity.js';
6
+ import { OmniRelationEntity } from '../base/omni-relation.entity.js';
7
+ import { OmniCollectionEntity } from '../omni-collection.entity.js';
8
+ import { OmniDocumentEntity } from '../omni-document.entity.js';
9
+ import { OmniExternalRefEntity } from '../base/omni-external-ref.entity.js';
10
+ import { OmniExternalRefService } from '../omni-external-ref.service.js';
11
+ import { OmniRelationService } from '../omni-relation.service.js';
12
+ import { OmniScopedService } from '../omni-scoped.service.js';
13
+ import { normalizeOmniKernelRegistrationOptions } from '../omni-scope.js';
14
+ import { omniScopedBackendProvidersFactory, omniBackendServiceToken } from '../omni-scoped.backend.js';
15
+ import { omniNamedBackendProvidersFactory } from '../omni-named.backend.js';
16
+ import { omniRecordBackendProvidersFactory } from '../omni-record.backend.js';
17
+ import { omniRelationBackendProvidersFactory } from '../omni-relation.backend.js';
18
+ import { omniCollectionBackendProvidersFactory } from '../omni-collection.backend.js';
19
+ import { omniDocumentBackendProvidersFactory } from '../omni-document.backend.js';
20
+ import { omniExternalRefBackendProvidersFactory } from '../omni-external-ref.backend.js';
21
+
22
+ const scope = {
23
+ scopeId: "scope-alpha",
24
+ cacheKey: (key: string) => `scope-alpha:${key}`,
25
+ };
26
+ const options = normalizeOmniKernelRegistrationOptions({
27
+ dbConnection: "default",
28
+ relationKinds: ["blocks"],
29
+ });
30
+
31
+ const repositoryFor = (target: unknown) => ({ target });
32
+
33
+ const factoryProvider = (providers: unknown[]) =>
34
+ providers.find(
35
+ (provider) =>
36
+ typeof provider === "object" &&
37
+ provider !== null &&
38
+ "useFactory" in provider,
39
+ ) as { useFactory: (...dependencies: unknown[]) => unknown };
40
+
41
+ describe("Omni scoped backend providers", () => {
42
+ it("builds request-scoped generic services for every ordinary resource", () => {
43
+ const cases = [
44
+ [omniNamedBackendProvidersFactory, OmniNamedEntity],
45
+ [omniRecordBackendProvidersFactory, OmniRecordEntity],
46
+ [omniCollectionBackendProvidersFactory, OmniCollectionEntity],
47
+ [omniDocumentBackendProvidersFactory, OmniDocumentEntity],
48
+ ] as const;
49
+
50
+ for (const [factory, entity] of cases) {
51
+ const backend = factory("default");
52
+ const service = factoryProvider(backend.providers).useFactory(
53
+ repositoryFor(entity),
54
+ scope,
55
+ options,
56
+ );
57
+ expect(service).toBeInstanceOf(OmniScopedService);
58
+ }
59
+ });
60
+
61
+ it("builds the relation service with a scoped record dependency", () => {
62
+ const backend = omniRelationBackendProvidersFactory("default");
63
+ const service = factoryProvider(backend.providers).useFactory(
64
+ repositoryFor(OmniRelationEntity),
65
+ scope,
66
+ options,
67
+ repositoryFor(OmniRecordEntity),
68
+ );
69
+
70
+ expect(service).toBeInstanceOf(OmniRelationService);
71
+ });
72
+
73
+ it("builds the external-reference service and exposes its service alias", () => {
74
+ const backend = omniExternalRefBackendProvidersFactory("default");
75
+ const service = factoryProvider(backend.providers).useFactory(
76
+ repositoryFor(OmniExternalRefEntity),
77
+ scope,
78
+ options,
79
+ );
80
+
81
+ expect(service).toBeInstanceOf(OmniExternalRefService);
82
+ expect(backend.providers).toEqual(
83
+ expect.arrayContaining([
84
+ expect.objectContaining({ provide: OmniExternalRefService }),
85
+ ]),
86
+ );
87
+ expect(omniBackendServiceToken(OmniExternalRefService)).toBe(
88
+ "OmniExternalRefService",
89
+ );
90
+ });
91
+
92
+ it("uses the scope-prefixed cache key in the generated request loader", () => {
93
+ const backend = omniScopedBackendProvidersFactory({
94
+ entityModel: OmniRecordEntity,
95
+ dbConnection: "default",
96
+ createService: (repository, resolvedScope) =>
97
+ new OmniScopedService(repository, resolvedScope),
98
+ });
99
+ const loaderProvider = backend.providers.find(
100
+ (provider) =>
101
+ typeof provider === "object" &&
102
+ provider !== null &&
103
+ "useFactory" in provider &&
104
+ provider !== factoryProvider(backend.providers),
105
+ ) as { useFactory: (...dependencies: unknown[]) => unknown };
106
+ const service = new OmniScopedService(
107
+ repositoryFor(OmniRecordEntity) as never,
108
+ scope,
109
+ );
110
+
111
+ expect(
112
+ loaderProvider.useFactory(service, scope, new EventEmitter2()),
113
+ ).toBeDefined();
114
+ });
115
+ });
@@ -0,0 +1,354 @@
1
+ import {
2
+ afterEach,
3
+ beforeEach,
4
+ describe,
5
+ expect,
6
+ it,
7
+ jest,
8
+ } from "@jest/globals";
9
+ import { DataSource } from "typeorm";
10
+
11
+ import { OmniNamedEntity } from '../base/omni-named.entity.js';
12
+ import { OmniRecordEntity } from '../base/omni-record.entity.js';
13
+ import { OmniRelationEntity } from '../base/omni-relation.entity.js';
14
+ import { OmniExternalRefEntity } from '../base/omni-external-ref.entity.js';
15
+ import { OmniRecordStatus } from '../omni-record-status.enum.js';
16
+ import { OmniRelationStatus } from '../omni-relation-status.enum.js';
17
+ import { OmniExternalRefInternalType } from '../omni-external-ref-internal-type.enum.js';
18
+ import { createOmniRelationKindContract } from '../omni-relation-kind.contract.js';
19
+ import { OmniRelationService } from '../omni-relation.service.js';
20
+ import { OmniExternalRefService } from '../omni-external-ref.service.js';
21
+ import { OmniExternalRefBindingValidator } from '../omni-external-ref-binding.validator.js';
22
+ import { OmniScopedService } from '../omni-scoped.service.js';
23
+
24
+ const alphaScope = {
25
+ scopeId: "scope-alpha",
26
+ cacheKey: (key: string) => `scope-alpha:${key}`,
27
+ };
28
+ const bravoScope = {
29
+ scopeId: "scope-bravo",
30
+ cacheKey: (key: string) => `scope-bravo:${key}`,
31
+ };
32
+
33
+ describe("OmniScopedService", () => {
34
+ let dataSource: DataSource;
35
+
36
+ beforeEach(async () => {
37
+ dataSource = new DataSource({
38
+ type: "sqlite",
39
+ database: ":memory:",
40
+ dropSchema: true,
41
+ synchronize: true,
42
+ entities: [
43
+ OmniNamedEntity,
44
+ OmniRecordEntity,
45
+ OmniRelationEntity,
46
+ OmniExternalRefEntity,
47
+ ],
48
+ });
49
+ await dataSource.initialize();
50
+ });
51
+
52
+ afterEach(async () => {
53
+ await dataSource.destroy();
54
+ });
55
+
56
+ it("enforces scope, payload metadata, scoped reads, and tombstones", async () => {
57
+ const repository = dataSource.getRepository(OmniRecordEntity);
58
+ const alpha = new OmniScopedService(
59
+ repository as never,
60
+ alphaScope,
61
+ "tombstone",
62
+ );
63
+ const bravo = new OmniScopedService(
64
+ repository as never,
65
+ bravoScope,
66
+ "tombstone",
67
+ );
68
+ const sharedGuid = "a0000000-0000-4000-8000-000000000001";
69
+
70
+ await expect(
71
+ alpha.createEntity({
72
+ guid: sharedGuid,
73
+ title: "attempted scope override",
74
+ kind: "generic",
75
+ status: OmniRecordStatus.Active,
76
+ scopeId: "scope-bravo",
77
+ } as never),
78
+ ).rejects.toThrow("server-owned");
79
+ await expect(
80
+ alpha.createEntity({
81
+ guid: sharedGuid,
82
+ title: "invalid payload",
83
+ kind: "generic",
84
+ status: OmniRecordStatus.Active,
85
+ payload: ["not-an-object"],
86
+ }),
87
+ ).rejects.toThrow("payload must be");
88
+ await expect(
89
+ alpha.createEntity({
90
+ guid: sharedGuid,
91
+ title: "partial schema",
92
+ kind: "generic",
93
+ status: OmniRecordStatus.Active,
94
+ payloadSchemaId: "example.record",
95
+ }),
96
+ ).rejects.toThrow("supplied together");
97
+
98
+ await alpha.createEntity({
99
+ guid: sharedGuid,
100
+ title: "alpha record",
101
+ kind: "generic",
102
+ status: OmniRecordStatus.Active,
103
+ payload: { nested: true },
104
+ payloadSchemaId: "example.record",
105
+ payloadSchemaVersion: 1,
106
+ });
107
+ await bravo.createEntity({
108
+ guid: sharedGuid,
109
+ title: "bravo record",
110
+ kind: "generic",
111
+ status: OmniRecordStatus.Active,
112
+ });
113
+
114
+ expect((await alpha.getEntity({ guid: sharedGuid }))?.title).toBe(
115
+ "alpha record",
116
+ );
117
+ expect((await bravo.getEntity({ guid: sharedGuid }))?.title).toBe(
118
+ "bravo record",
119
+ );
120
+ expect(await alpha.getEntityListExtended({})).toHaveLength(1);
121
+
122
+ await expect(
123
+ alpha.updateEntity(
124
+ { guid: sharedGuid },
125
+ { guid: "a0000000-0000-4000-8000-000000000099" },
126
+ ),
127
+ ).rejects.toThrow("guid is immutable");
128
+ await alpha.updateEntity(
129
+ { guid: sharedGuid },
130
+ { title: "alpha record updated" },
131
+ );
132
+ await alpha.deleteEntity({ guid: sharedGuid });
133
+
134
+ await expect(
135
+ alpha.getEntity({ guid: sharedGuid }, undefined, undefined, undefined, {
136
+ failOnNull: true,
137
+ }),
138
+ ).rejects.toThrow("not found");
139
+ expect((await bravo.getEntity({ guid: sharedGuid }))?.title).toBe(
140
+ "bravo record",
141
+ );
142
+ await expect(alpha.getEntity("guid = ?" as never)).rejects.toThrow(
143
+ "String where clauses",
144
+ );
145
+ });
146
+
147
+ it("applies scope recursively to extended subquery filters", async () => {
148
+ const getManyExtended = jest.fn(async (...args: any[]) => []);
149
+ const service = new OmniScopedService(
150
+ {
151
+ getCrudGenCapabilities: () => ({
152
+ extendedQueries: true,
153
+ structuredGraphqlFilters: true,
154
+ }),
155
+ getManyExtended,
156
+ getManyAndCountExtended: jest.fn(async (...args: any[]) => [[], 0]),
157
+ } as never,
158
+ alphaScope,
159
+ "tombstone",
160
+ );
161
+
162
+ await service.getEntityListExtended({
163
+ where: { filters: { title: "outer" as never } },
164
+ subQueryFilters: {
165
+ where: { filters: { title: "inner" as never } },
166
+ take: 1,
167
+ },
168
+ });
169
+
170
+ expect(getManyExtended).toHaveBeenCalledWith(
171
+ expect.objectContaining({
172
+ where: expect.objectContaining({
173
+ filters: expect.objectContaining({ scopeId: "scope-alpha" }),
174
+ }),
175
+ subQueryFilters: expect.objectContaining({
176
+ where: expect.objectContaining({
177
+ filters: expect.objectContaining({ scopeId: "scope-alpha" }),
178
+ }),
179
+ }),
180
+ }),
181
+ );
182
+ await expect(
183
+ service.getEntityListExtended({
184
+ subQueryFilters: {
185
+ where: { filters: { scopeId: "scope-bravo" as never } },
186
+ },
187
+ }),
188
+ ).rejects.toThrow("server context");
189
+ });
190
+
191
+ it("validates scoped relation endpoints and the extensible kind contract", async () => {
192
+ const recordRepository = dataSource.getRepository(OmniRecordEntity);
193
+ const relationRepository = dataSource.getRepository(OmniRelationEntity);
194
+ const records = new OmniScopedService(
195
+ recordRepository as never,
196
+ alphaScope,
197
+ "tombstone",
198
+ );
199
+ const bravoRecords = new OmniScopedService(
200
+ recordRepository as never,
201
+ bravoScope,
202
+ "tombstone",
203
+ );
204
+ const sourceId = "a0000000-0000-4000-8000-000000000010";
205
+ const targetId = "a0000000-0000-4000-8000-000000000011";
206
+ const bravoOnlyId = "a0000000-0000-4000-8000-000000000012";
207
+
208
+ await records.createEntity({
209
+ guid: sourceId,
210
+ title: "source",
211
+ kind: "generic",
212
+ status: OmniRecordStatus.Active,
213
+ });
214
+ await records.createEntity({
215
+ guid: targetId,
216
+ title: "target",
217
+ kind: "generic",
218
+ status: OmniRecordStatus.Active,
219
+ });
220
+ await bravoRecords.createEntity({
221
+ guid: bravoOnlyId,
222
+ title: "bravo target",
223
+ kind: "generic",
224
+ status: OmniRecordStatus.Active,
225
+ });
226
+
227
+ const relations = new OmniRelationService(
228
+ relationRepository as never,
229
+ alphaScope,
230
+ "hard",
231
+ recordRepository,
232
+ createOmniRelationKindContract(["blocks"]),
233
+ );
234
+ const relationId = "a0000000-0000-4000-8000-000000000013";
235
+
236
+ await relations.createEntity({
237
+ guid: relationId,
238
+ sourceRecordId: sourceId,
239
+ targetRecordId: targetId,
240
+ kind: "blocks",
241
+ status: OmniRelationStatus.Active,
242
+ });
243
+ await expect(
244
+ relations.createEntity({
245
+ guid: "a0000000-0000-4000-8000-000000000014",
246
+ sourceRecordId: sourceId,
247
+ targetRecordId: bravoOnlyId,
248
+ kind: "blocks",
249
+ status: OmniRelationStatus.Active,
250
+ }),
251
+ ).rejects.toThrow("not found");
252
+ await expect(
253
+ relations.createEntity({
254
+ guid: "a0000000-0000-4000-8000-000000000015",
255
+ sourceRecordId: sourceId,
256
+ targetRecordId: targetId,
257
+ kind: "unregistered",
258
+ status: OmniRelationStatus.Active,
259
+ }),
260
+ ).rejects.toThrow("not registered");
261
+ await expect(
262
+ relations.createEntity({
263
+ guid: "a0000000-0000-4000-8000-000000000016",
264
+ sourceRecordId: sourceId,
265
+ targetRecordId: targetId,
266
+ kind: "contains",
267
+ status: OmniRelationStatus.Active,
268
+ }),
269
+ ).rejects.toThrow("not valid for these endpoint kinds");
270
+ await expect(
271
+ relations.updateEntity(
272
+ { guid: relationId },
273
+ { sourceRecordId: targetId },
274
+ ),
275
+ ).rejects.toThrow("immutable");
276
+
277
+ await relations.updateEntity(
278
+ { guid: relationId },
279
+ { status: OmniRelationStatus.Inactive },
280
+ );
281
+ expect((await relations.getEntity({ guid: relationId }))?.status).toBe(
282
+ OmniRelationStatus.Inactive,
283
+ );
284
+ });
285
+
286
+ it("keeps external identities unique within scope while normalizing null partitions", async () => {
287
+ const repository = dataSource.getRepository(OmniExternalRefEntity);
288
+ const records = dataSource.getRepository(OmniRecordEntity);
289
+ const internalId = "a0000000-0000-4000-8000-000000000019";
290
+ await records.save([
291
+ {
292
+ scopeId: alphaScope.scopeId,
293
+ guid: internalId,
294
+ title: "alpha external target",
295
+ kind: "generic",
296
+ status: OmniRecordStatus.Active,
297
+ },
298
+ {
299
+ scopeId: bravoScope.scopeId,
300
+ guid: internalId,
301
+ title: "bravo external target",
302
+ kind: "generic",
303
+ status: OmniRecordStatus.Active,
304
+ },
305
+ ]);
306
+ const alpha = new OmniExternalRefService(
307
+ repository as never,
308
+ alphaScope,
309
+ "hard",
310
+ new OmniExternalRefBindingValidator(records, alphaScope),
311
+ );
312
+ const bravo = new OmniExternalRefService(
313
+ repository as never,
314
+ bravoScope,
315
+ "hard",
316
+ new OmniExternalRefBindingValidator(records, bravoScope),
317
+ );
318
+ const input = {
319
+ guid: "a0000000-0000-4000-8000-000000000020",
320
+ provider: "github",
321
+ externalId: "42",
322
+ internalType: OmniExternalRefInternalType.Record,
323
+ internalId,
324
+ account: null,
325
+ container: null,
326
+ };
327
+
328
+ await alpha.createEntity(input);
329
+ expect(
330
+ await alpha.findByExternalIdentity({
331
+ provider: "github",
332
+ externalId: "42",
333
+ }),
334
+ ).toEqual(
335
+ expect.objectContaining({ guid: input.guid, account: "", container: "" }),
336
+ );
337
+ await expect(
338
+ alpha.createEntity({
339
+ ...input,
340
+ guid: "a0000000-0000-4000-8000-000000000021",
341
+ }),
342
+ ).rejects.toThrow("already exists");
343
+ await bravo.createEntity({
344
+ ...input,
345
+ guid: "a0000000-0000-4000-8000-000000000022",
346
+ });
347
+ expect(
348
+ await alpha.findForInternalRecord(
349
+ OmniExternalRefInternalType.Record,
350
+ internalId,
351
+ ),
352
+ ).toHaveLength(1);
353
+ });
354
+ });
@@ -0,0 +1,94 @@
1
+ import { describe, expect, it, jest } from "@jest/globals";
2
+
3
+ import { collectOmniKernelQueryPlanEvidence } from '../omnikernel.diagnostics.js';
4
+
5
+ const sample = {
6
+ scopeId: "scope-alpha",
7
+ recordKind: "generic",
8
+ recordStatus: "active",
9
+ sourceRecordId: "record-1",
10
+ relationKind: "references",
11
+ relationStatus: "active",
12
+ };
13
+
14
+ describe("OmniKernel query-plan diagnostics", () => {
15
+ it("records sqlite index evidence for the bounded generated read shapes", async () => {
16
+ const query = jest
17
+ .fn()
18
+ .mockResolvedValueOnce([
19
+ {
20
+ detail:
21
+ "SEARCH omni-record USING INDEX omni_record_scope_kind_status_guid_idx",
22
+ },
23
+ ])
24
+ .mockResolvedValueOnce([
25
+ {
26
+ detail:
27
+ "SEARCH omni-relation USING INDEX omni_relation_scope_source_kind_status_created_guid_idx",
28
+ },
29
+ ]);
30
+
31
+ const evidence = await collectOmniKernelQueryPlanEvidence(
32
+ { options: { type: "sqlite" }, query } as never,
33
+ sample,
34
+ );
35
+
36
+ expect(evidence).toMatchObject({
37
+ dialect: "sqlite",
38
+ usesRecordGridIndex: true,
39
+ usesRelationSourceIndex: true,
40
+ });
41
+ expect(query.mock.calls[0][0]).toContain("EXPLAIN QUERY PLAN");
42
+ expect(query.mock.calls[1][1]).toEqual([
43
+ sample.scopeId,
44
+ sample.sourceRecordId,
45
+ sample.relationKind,
46
+ sample.relationStatus,
47
+ ]);
48
+ });
49
+
50
+ it("records PostgreSQL JSON-plan index evidence", async () => {
51
+ const query = jest
52
+ .fn()
53
+ .mockResolvedValueOnce([
54
+ {
55
+ "QUERY PLAN": [
56
+ {
57
+ Plan: { "Index Name": "omni_record_scope_kind_status_guid_idx" },
58
+ },
59
+ ],
60
+ },
61
+ ])
62
+ .mockResolvedValueOnce([
63
+ {
64
+ "QUERY PLAN": [
65
+ {
66
+ Plan: {
67
+ "Index Name":
68
+ "omni_relation_scope_source_kind_status_created_guid_idx",
69
+ },
70
+ },
71
+ ],
72
+ },
73
+ ]);
74
+
75
+ const evidence = await collectOmniKernelQueryPlanEvidence(
76
+ { options: { type: "postgres" }, query } as never,
77
+ sample,
78
+ );
79
+
80
+ expect(evidence.dialect).toBe("postgres");
81
+ expect(evidence.usesRecordGridIndex).toBe(true);
82
+ expect(evidence.usesRelationSourceIndex).toBe(true);
83
+ expect(query.mock.calls[0][0]).toContain("EXPLAIN (FORMAT JSON)");
84
+ });
85
+
86
+ it("rejects unsupported drivers instead of guessing their query-plan syntax", async () => {
87
+ await expect(
88
+ collectOmniKernelQueryPlanEvidence(
89
+ { options: { type: "mysql" }, query: jest.fn() } as never,
90
+ sample,
91
+ ),
92
+ ).rejects.toThrow("sqlite and postgres only");
93
+ });
94
+ });
@@ -0,0 +1,40 @@
1
+ import { describe, expect, it, jest } from '@jest/globals';
2
+ import 'reflect-metadata';
3
+ import { OmniKernelQueryService } from '../omnikernel.query.service.js';
4
+
5
+ import * as OmniNamedBackend from '../omni-named.backend.js';
6
+ import * as OmniRecordBackend from '../omni-record.backend.js';
7
+ import * as OmniRelationBackend from '../omni-relation.backend.js';
8
+ import * as OmniCollectionBackend from '../omni-collection.backend.js';
9
+ import * as OmniDocumentBackend from '../omni-document.backend.js';
10
+ import * as OmniExternalRefBackend from '../omni-external-ref.backend.js';
11
+
12
+ const omniNamedBackendProvidersFactory = jest.spyOn(OmniNamedBackend as any, 'omniNamedBackendProvidersFactory').mockReturnValue({ providers: ['named-backend'] });
13
+ const omniRecordBackendProvidersFactory = jest.spyOn(OmniRecordBackend as any, 'omniRecordBackendProvidersFactory').mockReturnValue({ providers: ['record-backend'] });
14
+ const omniRelationBackendProvidersFactory = jest.spyOn(OmniRelationBackend as any, 'omniRelationBackendProvidersFactory').mockReturnValue({ providers: ['relation-backend'] });
15
+ const omniCollectionBackendProvidersFactory = jest.spyOn(OmniCollectionBackend as any, 'omniCollectionBackendProvidersFactory').mockReturnValue({ providers: ['collection-backend'] });
16
+ const omniDocumentBackendProvidersFactory = jest.spyOn(OmniDocumentBackend as any, 'omniDocumentBackendProvidersFactory').mockReturnValue({ providers: ['document-backend'] });
17
+ const omniExternalRefBackendProvidersFactory = jest.spyOn(OmniExternalRefBackend as any, 'omniExternalRefBackendProvidersFactory').mockReturnValue({ providers: ['external-ref-backend'] });
18
+
19
+ import { OmniKernelModule } from '../omnikernel.module.js';
20
+
21
+ describe('OmniKernelModule', () => {
22
+ it('registers only backend substrate providers', () => {
23
+ const module = OmniKernelModule.register('test');
24
+
25
+ expect(module).toBeDefined();
26
+ expect(omniNamedBackendProvidersFactory).toHaveBeenCalledWith('test');
27
+ expect(module.providers).toEqual(
28
+ expect.arrayContaining([
29
+ 'named-backend',
30
+ 'record-backend',
31
+ 'relation-backend',
32
+ 'collection-backend',
33
+ 'document-backend',
34
+ 'external-ref-backend',
35
+ expect.objectContaining({ provide: OmniKernelQueryService }),
36
+ ]),
37
+ );
38
+ expect(module.controllers).toBeUndefined();
39
+ });
40
+ });