@open-mercato/shared 0.6.7-develop.6860.1.f796b22e80 → 0.6.7

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 (199) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +6 -57
  3. package/build.mjs +4 -2
  4. package/dist/lib/auth/server.js +2 -15
  5. package/dist/lib/auth/server.js.map +2 -2
  6. package/dist/lib/bootstrap/dynamicLoader.js +43 -117
  7. package/dist/lib/bootstrap/dynamicLoader.js.map +2 -2
  8. package/dist/lib/commands/command-bus.js +1 -5
  9. package/dist/lib/commands/command-bus.js.map +2 -2
  10. package/dist/lib/commands/command-interceptor-runner.js +2 -2
  11. package/dist/lib/commands/command-interceptor-runner.js.map +2 -2
  12. package/dist/lib/crud/enricher-runner.js +11 -2
  13. package/dist/lib/crud/enricher-runner.js.map +2 -2
  14. package/dist/lib/crud/factory.js +4 -63
  15. package/dist/lib/crud/factory.js.map +2 -2
  16. package/dist/lib/crud/interceptor-runner.js +2 -2
  17. package/dist/lib/crud/interceptor-runner.js.map +2 -2
  18. package/dist/lib/crud/mutation-guard-registry.js +2 -2
  19. package/dist/lib/crud/mutation-guard-registry.js.map +2 -2
  20. package/dist/lib/crud/types.js +0 -4
  21. package/dist/lib/crud/types.js.map +3 -3
  22. package/dist/lib/data/engine.js +11 -37
  23. package/dist/lib/data/engine.js.map +2 -2
  24. package/dist/lib/db/mikro.js +12 -3
  25. package/dist/lib/db/mikro.js.map +2 -2
  26. package/dist/lib/db/pg-errors.js +0 -42
  27. package/dist/lib/db/pg-errors.js.map +2 -2
  28. package/dist/lib/di/container.js +6 -48
  29. package/dist/lib/di/container.js.map +2 -2
  30. package/dist/lib/email/send.js +0 -56
  31. package/dist/lib/email/send.js.map +2 -2
  32. package/dist/lib/encryption/subscriber.js +10 -2
  33. package/dist/lib/encryption/subscriber.js.map +2 -2
  34. package/dist/lib/encryption/tenantDataEncryptionService.js +17 -52
  35. package/dist/lib/encryption/tenantDataEncryptionService.js.map +2 -2
  36. package/dist/lib/i18n/config.js +1 -1
  37. package/dist/lib/i18n/config.js.map +2 -2
  38. package/dist/lib/i18n/server.js +4 -4
  39. package/dist/lib/i18n/server.js.map +3 -3
  40. package/dist/lib/logger/index.js +3 -47
  41. package/dist/lib/logger/index.js.map +3 -3
  42. package/dist/lib/modules/registry.js +1 -5
  43. package/dist/lib/modules/registry.js.map +2 -2
  44. package/dist/lib/query/engine.js +40 -54
  45. package/dist/lib/query/engine.js.map +2 -2
  46. package/dist/lib/query/types.js.map +1 -1
  47. package/dist/lib/redis/connection.js +2 -7
  48. package/dist/lib/redis/connection.js.map +2 -2
  49. package/dist/lib/search/config.js +2 -61
  50. package/dist/lib/search/config.js.map +2 -2
  51. package/dist/lib/search/tokenize.js +11 -23
  52. package/dist/lib/search/tokenize.js.map +2 -2
  53. package/dist/lib/version.js +1 -1
  54. package/dist/lib/version.js.map +1 -1
  55. package/dist/lib/webhooks/index.js +1 -13
  56. package/dist/lib/webhooks/index.js.map +2 -2
  57. package/dist/lib/webhooks/verify.js +7 -12
  58. package/dist/lib/webhooks/verify.js.map +2 -2
  59. package/dist/modules/analytics.js.map +2 -2
  60. package/dist/modules/overrides.js +1 -50
  61. package/dist/modules/overrides.js.map +2 -2
  62. package/dist/modules/payment_gateways/types.js +1 -8
  63. package/dist/modules/payment_gateways/types.js.map +2 -2
  64. package/dist/modules/registry.js +0 -1
  65. package/dist/modules/registry.js.map +2 -2
  66. package/dist/security/enabledModulesRegistry.js +11 -56
  67. package/dist/security/enabledModulesRegistry.js.map +2 -2
  68. package/jest.config.cjs +1 -2
  69. package/package.json +9 -19
  70. package/src/lib/ai/llm-provider.ts +0 -33
  71. package/src/lib/auth/__tests__/server.test.ts +0 -23
  72. package/src/lib/auth/server.ts +2 -23
  73. package/src/lib/bootstrap/__tests__/dynamicLoader.commandInterceptors.test.ts +8 -63
  74. package/src/lib/bootstrap/__tests__/dynamicLoader.tsconfig.test.ts +1 -21
  75. package/src/lib/bootstrap/dynamicLoader.ts +65 -213
  76. package/src/lib/commands/__tests__/command-interceptor-runner.test.ts +0 -28
  77. package/src/lib/commands/command-bus.ts +1 -5
  78. package/src/lib/commands/command-interceptor-runner.ts +2 -2
  79. package/src/lib/crud/__tests__/crud-factory.test.ts +0 -298
  80. package/src/lib/crud/__tests__/mutation-guard-registry.test.ts +0 -26
  81. package/src/lib/crud/enricher-runner.ts +11 -2
  82. package/src/lib/crud/factory.ts +1 -69
  83. package/src/lib/crud/interceptor-runner.ts +2 -2
  84. package/src/lib/crud/mutation-guard-registry.ts +2 -2
  85. package/src/lib/crud/types.ts +0 -3
  86. package/src/lib/data/__tests__/engine.bulk-suppress.test.ts +3 -18
  87. package/src/lib/data/engine.ts +16 -50
  88. package/src/lib/db/__tests__/mikro.test.ts +25 -0
  89. package/src/lib/db/mikro.ts +38 -13
  90. package/src/lib/db/pg-errors.ts +0 -58
  91. package/src/lib/di/container.ts +8 -72
  92. package/src/lib/email/__tests__/send.test.ts +1 -37
  93. package/src/lib/email/send.ts +0 -79
  94. package/src/lib/encryption/__tests__/subscriber.test.ts +2 -54
  95. package/src/lib/encryption/__tests__/tenantDataEncryptionService.test.ts +0 -58
  96. package/src/lib/encryption/subscriber.ts +14 -2
  97. package/src/lib/encryption/tenantDataEncryptionService.ts +18 -64
  98. package/src/lib/i18n/config.ts +2 -2
  99. package/src/lib/i18n/server.ts +2 -6
  100. package/src/lib/logger/__tests__/logger.test.ts +0 -77
  101. package/src/lib/logger/index.ts +1 -59
  102. package/src/lib/modules/registry.ts +0 -10
  103. package/src/lib/query/__tests__/engine.test.ts +13 -65
  104. package/src/lib/query/engine.ts +58 -75
  105. package/src/lib/query/types.ts +5 -4
  106. package/src/lib/redis/__tests__/connection.test.ts +0 -16
  107. package/src/lib/redis/connection.ts +7 -10
  108. package/src/lib/search/__tests__/config.test.ts +0 -177
  109. package/src/lib/search/config.ts +8 -106
  110. package/src/lib/search/tokenize.ts +11 -33
  111. package/src/lib/webhooks/__tests__/verify.test.ts +1 -20
  112. package/src/lib/webhooks/index.ts +1 -19
  113. package/src/lib/webhooks/verify.ts +9 -17
  114. package/src/modules/__tests__/registry.test.ts +2 -10
  115. package/src/modules/analytics.ts +0 -7
  116. package/src/modules/customer-auth.ts +0 -1
  117. package/src/modules/encryption.ts +0 -3
  118. package/src/modules/navigation/backendChrome.ts +0 -15
  119. package/src/modules/overrides.ts +0 -103
  120. package/src/modules/payment_gateways/types.ts +0 -41
  121. package/src/modules/registry.ts +0 -3
  122. package/src/security/enabledModulesRegistry.ts +12 -64
  123. package/dist/lib/ai/safety-identifier.js +0 -32
  124. package/dist/lib/ai/safety-identifier.js.map +0 -7
  125. package/dist/lib/auth/organizationScope.js +0 -34
  126. package/dist/lib/auth/organizationScope.js.map +0 -7
  127. package/dist/lib/bootstrap/clientOnlyModules.js +0 -55
  128. package/dist/lib/bootstrap/clientOnlyModules.js.map +0 -7
  129. package/dist/lib/data/consistency.js +0 -19
  130. package/dist/lib/data/consistency.js.map +0 -7
  131. package/dist/lib/db/entityMetadata.js +0 -26
  132. package/dist/lib/db/entityMetadata.js.map +0 -7
  133. package/dist/lib/logger/extension.js +0 -29
  134. package/dist/lib/logger/extension.js.map +0 -7
  135. package/dist/lib/modules/surfaceFingerprint.js +0 -47
  136. package/dist/lib/modules/surfaceFingerprint.js.map +0 -7
  137. package/dist/lib/query/ciphertext-search-warning.js +0 -45
  138. package/dist/lib/query/ciphertext-search-warning.js.map +0 -7
  139. package/dist/lib/search/auto-indexing.js +0 -14
  140. package/dist/lib/search/auto-indexing.js.map +0 -7
  141. package/dist/lib/search/availability.js +0 -107
  142. package/dist/lib/search/availability.js.map +0 -7
  143. package/dist/lib/search/tokenLookup.js +0 -46
  144. package/dist/lib/search/tokenLookup.js.map +0 -7
  145. package/dist/lib/telemetry/runtime.js +0 -35
  146. package/dist/lib/telemetry/runtime.js.map +0 -7
  147. package/dist/lib/webhooks/body.js +0 -57
  148. package/dist/lib/webhooks/body.js.map +0 -7
  149. package/dist/lib/webhooks/inbound-types.js +0 -1
  150. package/dist/lib/webhooks/inbound-types.js.map +0 -7
  151. package/dist/modules/widgets/extension-points.js +0 -96
  152. package/dist/modules/widgets/extension-points.js.map +0 -7
  153. package/dist/security/featurePolicy.js +0 -62
  154. package/dist/security/featurePolicy.js.map +0 -7
  155. package/scripts/versionSource.cjs +0 -65
  156. package/src/lib/__tests__/versionSource.test.ts +0 -46
  157. package/src/lib/ai/__tests__/llm-provider-contract.test.ts +0 -59
  158. package/src/lib/ai/__tests__/safety-identifier.test.ts +0 -71
  159. package/src/lib/ai/safety-identifier.ts +0 -73
  160. package/src/lib/auth/__tests__/organizationScope.test.ts +0 -107
  161. package/src/lib/auth/organizationScope.ts +0 -65
  162. package/src/lib/bootstrap/__tests__/clientOnlyModules.test.ts +0 -189
  163. package/src/lib/bootstrap/__tests__/dynamicLoader.aliasResolver.test.ts +0 -98
  164. package/src/lib/bootstrap/__tests__/dynamicLoader.appDi.test.ts +0 -211
  165. package/src/lib/bootstrap/__tests__/dynamicLoader.generatedCacheRecovery.test.ts +0 -192
  166. package/src/lib/bootstrap/clientOnlyModules.ts +0 -85
  167. package/src/lib/crud/__tests__/crud-factory.cache-user-scope.test.ts +0 -171
  168. package/src/lib/data/__tests__/consistency.test.ts +0 -38
  169. package/src/lib/data/consistency.ts +0 -17
  170. package/src/lib/db/__tests__/entityMetadata.test.ts +0 -38
  171. package/src/lib/db/__tests__/pg-errors.test.ts +0 -48
  172. package/src/lib/db/entityMetadata.ts +0 -43
  173. package/src/lib/di/__tests__/container-app-di-absent.test.ts +0 -155
  174. package/src/lib/di/__tests__/container-app-di.test.ts +0 -173
  175. package/src/lib/di/__tests__/registrar-error-log.test.ts +0 -154
  176. package/src/lib/i18n/__tests__/config.test.ts +0 -8
  177. package/src/lib/i18n/__tests__/server-dictionary-cache.test.ts +0 -30
  178. package/src/lib/i18n/__tests__/server-unbootstrapped.test.ts +0 -29
  179. package/src/lib/logger/extension.ts +0 -59
  180. package/src/lib/modules/__tests__/surfaceFingerprint.test.ts +0 -122
  181. package/src/lib/modules/surfaceFingerprint.ts +0 -87
  182. package/src/lib/query/__tests__/ciphertext-search-warning.test.ts +0 -178
  183. package/src/lib/query/ciphertext-search-warning.ts +0 -95
  184. package/src/lib/search/__tests__/availability.test.ts +0 -211
  185. package/src/lib/search/__tests__/tokenLookup.test.ts +0 -206
  186. package/src/lib/search/__tests__/tokenize.test.ts +0 -49
  187. package/src/lib/search/auto-indexing.ts +0 -22
  188. package/src/lib/search/availability.ts +0 -232
  189. package/src/lib/search/tokenLookup.ts +0 -133
  190. package/src/lib/telemetry/runtime.ts +0 -72
  191. package/src/lib/webhooks/__tests__/body.test.ts +0 -95
  192. package/src/lib/webhooks/body.ts +0 -70
  193. package/src/lib/webhooks/inbound-types.ts +0 -125
  194. package/src/modules/__tests__/nav-group-order-override.test.ts +0 -183
  195. package/src/modules/payment_gateways/__tests__/types.test.ts +0 -37
  196. package/src/modules/widgets/__tests__/extension-points.test.ts +0 -101
  197. package/src/modules/widgets/extension-points.ts +0 -524
  198. package/src/security/__tests__/featurePolicy.test.ts +0 -166
  199. package/src/security/featurePolicy.ts +0 -89
@@ -1,38 +0,0 @@
1
- import {
2
- __resetAlwaysConsistentCacheForTests,
3
- isReadProjectionAlwaysConsistent,
4
- parseAlwaysConsistentEnv,
5
- } from '../consistency'
6
-
7
- describe('read projection consistency flag', () => {
8
- const originalEnv = process.env.OM_CACHE_SAFETY_ALWAYS_CONSISTENT
9
-
10
- afterEach(() => {
11
- if (originalEnv === undefined) {
12
- delete process.env.OM_CACHE_SAFETY_ALWAYS_CONSISTENT
13
- } else {
14
- process.env.OM_CACHE_SAFETY_ALWAYS_CONSISTENT = originalEnv
15
- }
16
- __resetAlwaysConsistentCacheForTests()
17
- })
18
-
19
- it.each([undefined, null, '', ' ', 'off', 'false', '0', 'no', 'disabled', 'none', 'unexpected'])(
20
- 'parses %p as OFF',
21
- (raw) => {
22
- expect(parseAlwaysConsistentEnv(raw)).toBe(false)
23
- },
24
- )
25
-
26
- it.each(['on', 'true', '1', 'yes', 'enabled'])('parses %p as ON', (raw) => {
27
- expect(parseAlwaysConsistentEnv(raw)).toBe(true)
28
- })
29
-
30
- it('memoizes the env value until reset for tests', () => {
31
- process.env.OM_CACHE_SAFETY_ALWAYS_CONSISTENT = 'on'
32
- expect(isReadProjectionAlwaysConsistent()).toBe(true)
33
- process.env.OM_CACHE_SAFETY_ALWAYS_CONSISTENT = 'off'
34
- expect(isReadProjectionAlwaysConsistent()).toBe(true)
35
- __resetAlwaysConsistentCacheForTests()
36
- expect(isReadProjectionAlwaysConsistent()).toBe(false)
37
- })
38
- })
@@ -1,17 +0,0 @@
1
- import { parseBooleanWithDefault } from '../boolean'
2
-
3
- let alwaysConsistentFlag: boolean | null = null
4
-
5
- export function parseAlwaysConsistentEnv(raw: string | undefined | null): boolean {
6
- return parseBooleanWithDefault(raw, false)
7
- }
8
-
9
- export function isReadProjectionAlwaysConsistent(): boolean {
10
- if (alwaysConsistentFlag !== null) return alwaysConsistentFlag
11
- alwaysConsistentFlag = parseAlwaysConsistentEnv(process.env.OM_CACHE_SAFETY_ALWAYS_CONSISTENT)
12
- return alwaysConsistentFlag
13
- }
14
-
15
- export function __resetAlwaysConsistentCacheForTests(): void {
16
- alwaysConsistentFlag = null
17
- }
@@ -1,38 +0,0 @@
1
- import { listEntityMetadata, listEntityMetadataFromRegistry } from '../entityMetadata'
2
-
3
- const personMeta = { className: 'Person', tableName: 'people' }
4
- const orderMeta = { className: 'Order', tableName: 'orders' }
5
-
6
- describe('listEntityMetadataFromRegistry', () => {
7
- it('reads the MikroORM v7 Map returned by getAll()', () => {
8
- const registry = { getAll: () => new Map([[Object, personMeta], [Array, orderMeta]]) }
9
- expect(listEntityMetadataFromRegistry(registry)).toEqual([personMeta, orderMeta])
10
- })
11
-
12
- it('reads the MikroORM v6 dictionary returned by getAll()', () => {
13
- const registry = { getAll: () => ({ Person: personMeta, Order: orderMeta }) }
14
- expect(listEntityMetadataFromRegistry(registry)).toEqual([personMeta, orderMeta])
15
- })
16
-
17
- it('falls back to the metadata property when getAll() yields nothing', () => {
18
- const registry = { getAll: () => new Map(), metadata: { Person: personMeta } }
19
- expect(listEntityMetadataFromRegistry(registry)).toEqual([personMeta])
20
- })
21
-
22
- it('returns an empty array for a missing registry', () => {
23
- expect(listEntityMetadataFromRegistry(null)).toEqual([])
24
- expect(listEntityMetadataFromRegistry({})).toEqual([])
25
- })
26
- })
27
-
28
- describe('listEntityMetadata', () => {
29
- it('resolves the registry through getMetadata()', () => {
30
- const em = { getMetadata: () => ({ getAll: () => new Map([[Object, personMeta]]) }) }
31
- expect(listEntityMetadata(em)).toEqual([personMeta])
32
- })
33
-
34
- it('returns an empty array when the source exposes no registry', () => {
35
- expect(listEntityMetadata({})).toEqual([])
36
- expect(listEntityMetadata(undefined)).toEqual([])
37
- })
38
- })
@@ -1,48 +0,0 @@
1
- import { isTransientDbError, isUniqueViolation } from '../pg-errors'
2
-
3
- describe('isTransientDbError', () => {
4
- it('is true for the max_connections SQLSTATE', () => {
5
- expect(isTransientDbError({ code: '53300' })).toBe(true)
6
- })
7
-
8
- it('is true for connection-family SQLSTATEs', () => {
9
- for (const code of ['53400', '57P01', '57P02', '57P03', '08000', '08001', '08003', '08006']) {
10
- expect(isTransientDbError({ code })).toBe(true)
11
- }
12
- })
13
-
14
- it('is true for driver/ORM-wrapped connection messages that drop the SQLSTATE', () => {
15
- expect(isTransientDbError(new Error('sorry, too many clients already'))).toBe(true)
16
- expect(isTransientDbError(new Error('Knex: Timeout acquiring a connection. The pool is probably full.'))).toBe(true)
17
- expect(isTransientDbError(new Error('Unable to acquire a connection'))).toBe(true)
18
- expect(isTransientDbError(new Error('Connection terminated unexpectedly'))).toBe(true)
19
- expect(isTransientDbError(new Error('the database system is starting up'))).toBe(true)
20
- })
21
-
22
- it('is false for bare socket errors with no Postgres signal (avoids false positives)', () => {
23
- // Generic socket failures can come from any outbound connection (HTTP, cache,
24
- // queue), so they must NOT be attributed to the database.
25
- expect(isTransientDbError({ code: 'ECONNREFUSED' })).toBe(false)
26
- expect(isTransientDbError({ code: 'ETIMEDOUT' })).toBe(false)
27
- expect(isTransientDbError(Object.assign(new Error('read ECONNRESET'), { code: 'ECONNRESET' }))).toBe(false)
28
- })
29
-
30
- it('is false for a unique violation and other query-level errors', () => {
31
- expect(isTransientDbError({ code: '23505' })).toBe(false)
32
- expect(isTransientDbError({ code: '40P01' })).toBe(false) // deadlock — not a connection failure
33
- expect(isTransientDbError({ code: '40001' })).toBe(false) // serialization — not a connection failure
34
- })
35
-
36
- it('is false for non-DB and empty errors', () => {
37
- expect(isTransientDbError(new Error('something unrelated broke'))).toBe(false)
38
- expect(isTransientDbError(null)).toBe(false)
39
- expect(isTransientDbError(undefined)).toBe(false)
40
- expect(isTransientDbError('a string')).toBe(false)
41
- })
42
-
43
- it('does not overlap with isUniqueViolation', () => {
44
- const uniqueErr = { code: '23505', message: 'duplicate key value violates unique constraint' }
45
- expect(isUniqueViolation(uniqueErr)).toBe(true)
46
- expect(isTransientDbError(uniqueErr)).toBe(false)
47
- })
48
- })
@@ -1,43 +0,0 @@
1
- import type { EntityMetadata } from '@mikro-orm/core'
2
-
3
- type MetadataRegistryLike = {
4
- getAll?: () => unknown
5
- metadata?: unknown
6
- }
7
-
8
- type MetadataHolder = {
9
- getMetadata?: () => unknown
10
- }
11
-
12
- /**
13
- * MikroORM v7 returns a `Map` from `MetadataStorage.getAll()`; v6 returned a plain
14
- * dictionary. `Object.values(someMap)` evaluates to `[]`, so a v6-era read of the
15
- * registry silently reports zero entities instead of failing loudly.
16
- */
17
- function toMetadataList(value: unknown): EntityMetadata[] {
18
- if (!value) return []
19
- if (value instanceof Map) return Array.from(value.values()) as EntityMetadata[]
20
- if (Array.isArray(value)) return value as EntityMetadata[]
21
- if (typeof value === 'object') return Object.values(value as Record<string, EntityMetadata>)
22
- return []
23
- }
24
-
25
- export function listEntityMetadataFromRegistry(registry: unknown): EntityMetadata[] {
26
- const source = registry as MetadataRegistryLike | null | undefined
27
- if (!source) return []
28
- if (typeof source.getAll === 'function') {
29
- const entries = toMetadataList(source.getAll())
30
- if (entries.length) return entries
31
- }
32
- return toMetadataList(source.metadata)
33
- }
34
-
35
- /**
36
- * List every discovered entity metadata from an `EntityManager` or `MikroORM` instance.
37
- * Returns an empty array when the source exposes no metadata registry.
38
- */
39
- export function listEntityMetadata(source: unknown): EntityMetadata[] {
40
- const holder = source as MetadataHolder | null | undefined
41
- if (!holder || typeof holder.getMetadata !== 'function') return []
42
- return listEntityMetadataFromRegistry(holder.getMetadata())
43
- }
@@ -1,155 +0,0 @@
1
- // The app-level DI override module (`@/di`) is OPTIONAL: most apps ship no
2
- // src/di.ts, and packages contexts cannot resolve the alias at all. Its
3
- // absence must not produce warnings — only real load/register failures do
4
- // (covered in container-app-di.test.ts). This file deliberately does NOT
5
- // mock '@/di', so the import fails with module-not-found, exercising the
6
- // quiet branch.
7
-
8
- jest.mock(
9
- '@open-mercato/shared/lib/db/mikro',
10
- () => {
11
- const baseEm: any = {
12
- fork: () => baseEm,
13
- getRepository: () => ({ find: async () => [], findOne: async () => null }),
14
- }
15
- return {
16
- __esModule: true,
17
- getOrm: async () => ({ em: baseEm }),
18
- getOrmEntities: () => [],
19
- registerOrmEntities: () => {},
20
- }
21
- },
22
- { virtual: false },
23
- )
24
-
25
- jest.mock(
26
- '@mikro-orm/core',
27
- () => ({
28
- __esModule: true,
29
- RequestContext: { getEntityManager: () => null },
30
- }),
31
- { virtual: true },
32
- )
33
-
34
- jest.mock(
35
- '@open-mercato/shared/lib/query/engine',
36
- () => ({ __esModule: true, BasicQueryEngine: class {} }),
37
- { virtual: false },
38
- )
39
-
40
- jest.mock(
41
- '@open-mercato/shared/lib/data/engine',
42
- () => ({ __esModule: true, DefaultDataEngine: class {} }),
43
- { virtual: false },
44
- )
45
-
46
- jest.mock(
47
- '@open-mercato/shared/lib/commands',
48
- () => ({
49
- __esModule: true,
50
- commandRegistry: {},
51
- CommandBus: class { constructor() {} },
52
- }),
53
- { virtual: false },
54
- )
55
-
56
- jest.mock(
57
- '@open-mercato/shared/modules/overrides',
58
- () => ({
59
- __esModule: true,
60
- applyDiOverridesToContainer: () => {},
61
- }),
62
- { virtual: false },
63
- )
64
-
65
- // Keep this suite hermetic: without this mock the REAL core bootstrap module
66
- // loads (and runs its module-level side effects) whenever generated files
67
- // exist on disk, polluting globalThis state shared with sibling test files.
68
- jest.mock(
69
- '@open-mercato/core/bootstrap',
70
- () => ({
71
- __esModule: true,
72
- bootstrap: async () => {},
73
- }),
74
- { virtual: true },
75
- )
76
-
77
- jest.mock(
78
- '@open-mercato/shared/lib/encryption/subscriber',
79
- () => ({
80
- __esModule: true,
81
- registerTenantEncryptionSubscriber: () => {},
82
- }),
83
- { virtual: true },
84
- )
85
-
86
- const mockWarn = jest.fn()
87
- const mockDebug = jest.fn()
88
- jest.mock('../../logger', () => ({
89
- createLogger: () => ({
90
- debug: (...args: unknown[]) => mockDebug(...args),
91
- info: jest.fn(),
92
- warn: (...args: unknown[]) => mockWarn(...args),
93
- error: jest.fn(),
94
- child() { return this },
95
- }),
96
- }))
97
-
98
- const {
99
- createRequestContainer,
100
- registerAppDiRegistrar,
101
- registerDiRegistrars,
102
- resetBootstrapCache,
103
- } = require('@open-mercato/shared/lib/di/container')
104
-
105
- describe('app-level DI override hook when @/di is absent', () => {
106
- beforeEach(() => {
107
- resetBootstrapCache()
108
- registerAppDiRegistrar(null)
109
- registerDiRegistrars([])
110
- mockWarn.mockClear()
111
- mockDebug.mockClear()
112
- })
113
-
114
- it('creates the container without warning', async () => {
115
- const firstContainer = await createRequestContainer()
116
- const secondContainer = await createRequestContainer()
117
- expect(firstContainer).toBeDefined()
118
- expect(secondContainer).toBeDefined()
119
- expect(mockWarn).not.toHaveBeenCalled()
120
- })
121
-
122
- // Worker and CLI processes create one request container per job against built package
123
- // output, where the app's `@/` alias does not exist. Retrying the doomed import per
124
- // container repeats a failed module resolution — and its log line — once per job.
125
- it('stops retrying the unresolvable @/di import after the first container', async () => {
126
- await createRequestContainer()
127
- await createRequestContainer()
128
- await createRequestContainer()
129
-
130
- const unresolvableLogs = mockDebug.mock.calls.filter(
131
- ([message]) => message === 'App-level DI override module (@/di) not resolvable; skipping',
132
- )
133
- expect(unresolvableLogs).toHaveLength(1)
134
- })
135
-
136
- it('warns once when @/di fails because a nested app alias is missing', async () => {
137
- const nestedAliasError = Object.assign(new Error("Cannot find module '@/di/helpers'"), {
138
- code: 'MODULE_NOT_FOUND',
139
- })
140
- jest.doMock('@/di', () => {
141
- throw nestedAliasError
142
- }, { virtual: true })
143
-
144
- const firstContainer = await createRequestContainer()
145
- const secondContainer = await createRequestContainer()
146
-
147
- expect(firstContainer).toBeDefined()
148
- expect(secondContainer).toBeDefined()
149
- expect(mockWarn).toHaveBeenCalledTimes(1)
150
- expect(mockWarn).toHaveBeenCalledWith(
151
- 'App-level DI override module (@/di) failed to load; its registrations are skipped',
152
- { err: nestedAliasError },
153
- )
154
- })
155
- })
@@ -1,173 +0,0 @@
1
- // Tests for the app-level DI override hook (`src/di.ts` → `@/di`) in
2
- // `createRequestContainer()`. The hook used to swallow EVERY failure with
3
- // bare `catch {}` blocks, so a src/di.ts whose register() threw — or a @/di
4
- // module that failed to load for any reason other than being absent — was
5
- // skipped without a trace and the documented override point silently did
6
- // nothing. Absence of the optional module must stay quiet; real failures
7
- // must be logged.
8
-
9
- import { asValue } from 'awilix'
10
-
11
- jest.mock(
12
- '@open-mercato/shared/lib/db/mikro',
13
- () => {
14
- const baseEm: any = {
15
- fork: () => baseEm,
16
- getRepository: () => ({ find: async () => [], findOne: async () => null }),
17
- }
18
- return {
19
- __esModule: true,
20
- getOrm: async () => ({ em: baseEm }),
21
- getOrmEntities: () => [],
22
- registerOrmEntities: () => {},
23
- }
24
- },
25
- { virtual: false },
26
- )
27
-
28
- jest.mock(
29
- '@mikro-orm/core',
30
- () => ({
31
- __esModule: true,
32
- RequestContext: { getEntityManager: () => null },
33
- }),
34
- { virtual: true },
35
- )
36
-
37
- jest.mock(
38
- '@open-mercato/shared/lib/query/engine',
39
- () => ({ __esModule: true, BasicQueryEngine: class {} }),
40
- { virtual: false },
41
- )
42
-
43
- jest.mock(
44
- '@open-mercato/shared/lib/data/engine',
45
- () => ({ __esModule: true, DefaultDataEngine: class {} }),
46
- { virtual: false },
47
- )
48
-
49
- jest.mock(
50
- '@open-mercato/shared/lib/commands',
51
- () => ({
52
- __esModule: true,
53
- commandRegistry: {},
54
- CommandBus: class { constructor() {} },
55
- }),
56
- { virtual: false },
57
- )
58
-
59
- jest.mock(
60
- '@open-mercato/shared/modules/overrides',
61
- () => ({
62
- __esModule: true,
63
- applyDiOverridesToContainer: () => {},
64
- }),
65
- { virtual: false },
66
- )
67
-
68
- // Keep this suite hermetic: without this mock the REAL core bootstrap module
69
- // loads (and runs its module-level side effects) whenever generated files
70
- // exist on disk, polluting globalThis state shared with sibling test files.
71
- jest.mock(
72
- '@open-mercato/core/bootstrap',
73
- () => ({
74
- __esModule: true,
75
- bootstrap: async () => {},
76
- }),
77
- { virtual: true },
78
- )
79
-
80
- jest.mock(
81
- '@open-mercato/shared/lib/encryption/subscriber',
82
- () => ({
83
- __esModule: true,
84
- registerTenantEncryptionSubscriber: () => {},
85
- }),
86
- { virtual: true },
87
- )
88
-
89
- const mockWarn = jest.fn()
90
- jest.mock('../../logger', () => ({
91
- createLogger: () => ({
92
- debug: jest.fn(),
93
- info: jest.fn(),
94
- warn: (...args: unknown[]) => mockWarn(...args),
95
- error: jest.fn(),
96
- child() { return this },
97
- }),
98
- }))
99
-
100
- const mockAppDiRegister = jest.fn()
101
- jest.mock(
102
- '@/di',
103
- () => ({
104
- __esModule: true,
105
- register: (...args: unknown[]) => mockAppDiRegister(...args),
106
- }),
107
- { virtual: true },
108
- )
109
-
110
- const {
111
- createRequestContainer,
112
- registerAppDiRegistrar,
113
- registerDiRegistrars,
114
- resetBootstrapCache,
115
- } = require('@open-mercato/shared/lib/di/container')
116
-
117
- describe('app-level DI override hook (@/di)', () => {
118
- beforeEach(() => {
119
- resetBootstrapCache()
120
- registerAppDiRegistrar(null)
121
- registerDiRegistrars([])
122
- mockWarn.mockClear()
123
- mockAppDiRegister.mockReset()
124
- })
125
-
126
- it('calls register() with the request container and applies its registrations', async () => {
127
- mockAppDiRegister.mockImplementation((container: any) => {
128
- container.register({ appOverrideProbe: asValue('from-app-di') })
129
- })
130
- const container = await createRequestContainer()
131
- expect(mockAppDiRegister).toHaveBeenCalledTimes(1)
132
- expect(mockAppDiRegister).toHaveBeenCalledWith(container)
133
- expect(container.resolve('appOverrideProbe')).toBe('from-app-di')
134
- expect(mockWarn).not.toHaveBeenCalled()
135
- })
136
-
137
- it('awaits an async register()', async () => {
138
- mockAppDiRegister.mockImplementation(async (container: any) => {
139
- await Promise.resolve()
140
- container.register({ appOverrideProbe: asValue('async-app-di') })
141
- })
142
- const container = await createRequestContainer()
143
- expect(container.resolve('appOverrideProbe')).toBe('async-app-di')
144
- expect(mockWarn).not.toHaveBeenCalled()
145
- })
146
-
147
- it('warns instead of silently swallowing when register() throws', async () => {
148
- mockAppDiRegister.mockImplementation(() => {
149
- throw new Error('boom from app di')
150
- })
151
- const firstContainer = await createRequestContainer()
152
- const secondContainer = await createRequestContainer()
153
- expect(firstContainer).toBeDefined()
154
- expect(secondContainer).toBeDefined()
155
- expect(mockAppDiRegister).toHaveBeenCalledTimes(2)
156
- expect(mockWarn).toHaveBeenCalledTimes(1)
157
- const [message, fields] = mockWarn.mock.calls[0]
158
- expect(String(message)).toContain('register()')
159
- expect((fields as { err: Error }).err.message).toBe('boom from app di')
160
- })
161
-
162
- it('warns when an async register() rejects', async () => {
163
- mockAppDiRegister.mockImplementation(async () => {
164
- throw new Error('async boom from app di')
165
- })
166
- const firstContainer = await createRequestContainer()
167
- const secondContainer = await createRequestContainer()
168
- expect(firstContainer).toBeDefined()
169
- expect(secondContainer).toBeDefined()
170
- expect(mockAppDiRegister).toHaveBeenCalledTimes(2)
171
- expect(mockWarn).toHaveBeenCalledTimes(1)
172
- })
173
- })
@@ -1,154 +0,0 @@
1
- // A module DI registrar that throws is skipped fail-open (one broken module
2
- // must not take down every request container), but the failure MUST be logged —
3
- // a silent catch leaves the module's services missing with no trace until an
4
- // unrelated Awilix resolution error surfaces much later.
5
-
6
- import { asValue } from 'awilix'
7
- import type { AwilixContainer } from 'awilix'
8
-
9
- const mockLoggerError = jest.fn()
10
-
11
- jest.mock('@open-mercato/shared/lib/logger', () => {
12
- const makeLogger = (): Record<string, unknown> => ({
13
- error: (...args: unknown[]) => mockLoggerError(...args),
14
- warn: jest.fn(),
15
- info: jest.fn(),
16
- debug: jest.fn(),
17
- trace: jest.fn(),
18
- child: () => makeLogger(),
19
- })
20
- return {
21
- __esModule: true,
22
- createLogger: () => makeLogger(),
23
- getLogLevel: () => 'info',
24
- isLevelEnabled: () => false,
25
- }
26
- })
27
-
28
- jest.mock(
29
- '@open-mercato/shared/lib/db/mikro',
30
- () => {
31
- const baseEm: any = {
32
- fork: () => baseEm,
33
- getRepository: () => ({ find: async () => [], findOne: async () => null }),
34
- }
35
- return {
36
- __esModule: true,
37
- getOrm: async () => ({ em: baseEm }),
38
- getOrmEntities: () => [],
39
- registerOrmEntities: () => {},
40
- }
41
- },
42
- { virtual: false },
43
- )
44
-
45
- jest.mock(
46
- '@mikro-orm/core',
47
- () => ({
48
- __esModule: true,
49
- RequestContext: { getEntityManager: () => null },
50
- }),
51
- { virtual: true },
52
- )
53
-
54
- jest.mock(
55
- '@open-mercato/shared/lib/query/engine',
56
- () => ({ __esModule: true, BasicQueryEngine: class {} }),
57
- { virtual: false },
58
- )
59
-
60
- jest.mock(
61
- '@open-mercato/shared/lib/data/engine',
62
- () => ({ __esModule: true, DefaultDataEngine: class {} }),
63
- { virtual: false },
64
- )
65
-
66
- jest.mock(
67
- '@open-mercato/shared/lib/commands',
68
- () => ({
69
- __esModule: true,
70
- commandRegistry: {},
71
- CommandBus: class { constructor() {} },
72
- }),
73
- { virtual: false },
74
- )
75
-
76
- jest.mock(
77
- '@open-mercato/shared/modules/overrides',
78
- () => ({
79
- __esModule: true,
80
- applyDiOverridesToContainer: () => {},
81
- applyModuleOverridesToModules: (modules: unknown[]) => modules,
82
- applyComponentOverridesToEntries: (entries: unknown[]) => entries,
83
- }),
84
- { virtual: false },
85
- )
86
-
87
- jest.mock(
88
- '@open-mercato/core/bootstrap',
89
- () => ({
90
- __esModule: true,
91
- bootstrap: async () => {},
92
- }),
93
- { virtual: true },
94
- )
95
-
96
- jest.mock(
97
- '@open-mercato/shared/lib/encryption/subscriber',
98
- () => ({
99
- __esModule: true,
100
- registerTenantEncryptionSubscriber: () => {},
101
- }),
102
- { virtual: true },
103
- )
104
-
105
- const {
106
- createRequestContainer,
107
- registerAppDiRegistrar,
108
- registerDiRegistrars,
109
- resetBootstrapCache,
110
- } = require('@open-mercato/shared/lib/di/container')
111
-
112
- describe('createRequestContainer — module DI registrar failures', () => {
113
- beforeEach(() => {
114
- resetBootstrapCache()
115
- registerAppDiRegistrar(null)
116
- mockLoggerError.mockClear()
117
- })
118
-
119
- afterAll(() => {
120
- registerDiRegistrars([])
121
- })
122
-
123
- it('logs the error and continues with the remaining registrars', async () => {
124
- const boom = new Error('di exploded')
125
- const workingRegistrar = jest.fn((container: AwilixContainer) => {
126
- container.register({ registrarProbeService: asValue('registered') })
127
- })
128
- registerDiRegistrars([
129
- () => { throw boom },
130
- workingRegistrar,
131
- ])
132
-
133
- const container = await createRequestContainer()
134
-
135
- expect(workingRegistrar).toHaveBeenCalledTimes(1)
136
- expect(container.resolve('registrarProbeService')).toBe('registered')
137
- expect(mockLoggerError).toHaveBeenCalledWith('Module DI registrar failed', {
138
- registrarIndex: 0,
139
- err: boom,
140
- })
141
- })
142
-
143
- it('does not log when every registrar succeeds', async () => {
144
- registerDiRegistrars([
145
- (container: AwilixContainer) => {
146
- container.register({ registrarProbeService: asValue('ok') })
147
- },
148
- ])
149
-
150
- await createRequestContainer()
151
-
152
- expect(mockLoggerError).not.toHaveBeenCalled()
153
- })
154
- })
@@ -1,8 +0,0 @@
1
- import { defaultLocale, locales, type Locale } from '../config'
2
-
3
- describe('supported application locales', () => {
4
- it('registers Korean without changing the default locale', () => {
5
- expect(locales).toEqual<Locale[]>(['en', 'pl', 'es', 'de', 'ko'])
6
- expect(defaultLocale).toBe('en')
7
- })
8
- })