@open-mercato/shared 0.6.3-develop.3663.1.63517660ba → 0.6.3-develop.3680.1.62e9da67c2

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.
@@ -1,16 +1,25 @@
1
1
  import { applyModuleOverridesToModules } from "@open-mercato/shared/modules/overrides";
2
- let _modules = null;
2
+ const GLOBAL_KEY = "__openMercatoModulesRegistry__";
3
+ function getGlobalModules() {
4
+ return globalThis[GLOBAL_KEY] ?? null;
5
+ }
6
+ function setGlobalModules(modules) {
7
+ ;
8
+ globalThis[GLOBAL_KEY] = modules;
9
+ }
3
10
  function registerModules(modules) {
4
- if (_modules !== null && process.env.NODE_ENV === "development") {
11
+ const existing = getGlobalModules();
12
+ if (existing !== null && process.env.NODE_ENV === "development") {
5
13
  console.debug("[Bootstrap] Modules re-registered (this may occur during HMR)");
6
14
  }
7
- _modules = applyModuleOverridesToModules(modules);
15
+ setGlobalModules(applyModuleOverridesToModules(modules));
8
16
  }
9
17
  function getModules() {
10
- if (!_modules) {
18
+ const modules = getGlobalModules();
19
+ if (!modules) {
11
20
  throw new Error("[Bootstrap] Modules not registered. Call registerModules() at bootstrap.");
12
21
  }
13
- return _modules;
22
+ return modules;
14
23
  }
15
24
  export {
16
25
  getModules,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/modules/registry.ts"],
4
- "sourcesContent": ["import type { Module } from '@open-mercato/shared/modules/registry'\nimport { applyModuleOverridesToModules } from '@open-mercato/shared/modules/overrides'\n\n// Registration pattern for publishable packages\nlet _modules: Module[] | null = null\n\nexport function registerModules(modules: Module[]) {\n if (_modules !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] Modules re-registered (this may occur during HMR)')\n }\n _modules = applyModuleOverridesToModules(modules)\n}\n\nexport function getModules(): Module[] {\n if (!_modules) {\n throw new Error('[Bootstrap] Modules not registered. Call registerModules() at bootstrap.')\n }\n return _modules\n}\n"],
5
- "mappings": "AACA,SAAS,qCAAqC;AAG9C,IAAI,WAA4B;AAEzB,SAAS,gBAAgB,SAAmB;AACjD,MAAI,aAAa,QAAQ,QAAQ,IAAI,aAAa,eAAe;AAC/D,YAAQ,MAAM,+DAA+D;AAAA,EAC/E;AACA,aAAW,8BAA8B,OAAO;AAClD;AAEO,SAAS,aAAuB;AACrC,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,MAAM,0EAA0E;AAAA,EAC5F;AACA,SAAO;AACT;",
4
+ "sourcesContent": ["import type { Module } from '@open-mercato/shared/modules/registry'\nimport { applyModuleOverridesToModules } from '@open-mercato/shared/modules/overrides'\n\n// Registration pattern for publishable packages.\n// Use globalThis to survive tsx/esbuild module duplication where the same\n// registry.ts file can be loaded as multiple module instances when mixing\n// dynamic and static imports \u2014 for example a standalone integration test\n// bootstraps via the source path while a worker handler resolves it through\n// node_modules/@open-mercato/shared/dist/. Mirrors the same workaround used\n// by `getDiRegistrars()` in `../di/container.ts`.\nconst GLOBAL_KEY = '__openMercatoModulesRegistry__'\n\nfunction getGlobalModules(): Module[] | null {\n return (globalThis as any)[GLOBAL_KEY] ?? null\n}\n\nfunction setGlobalModules(modules: Module[]): void {\n ;(globalThis as any)[GLOBAL_KEY] = modules\n}\n\nexport function registerModules(modules: Module[]) {\n const existing = getGlobalModules()\n if (existing !== null && process.env.NODE_ENV === 'development') {\n console.debug('[Bootstrap] Modules re-registered (this may occur during HMR)')\n }\n setGlobalModules(applyModuleOverridesToModules(modules))\n}\n\nexport function getModules(): Module[] {\n const modules = getGlobalModules()\n if (!modules) {\n throw new Error('[Bootstrap] Modules not registered. Call registerModules() at bootstrap.')\n }\n return modules\n}\n"],
5
+ "mappings": "AACA,SAAS,qCAAqC;AAS9C,MAAM,aAAa;AAEnB,SAAS,mBAAoC;AAC3C,SAAQ,WAAmB,UAAU,KAAK;AAC5C;AAEA,SAAS,iBAAiB,SAAyB;AACjD;AAAC,EAAC,WAAmB,UAAU,IAAI;AACrC;AAEO,SAAS,gBAAgB,SAAmB;AACjD,QAAM,WAAW,iBAAiB;AAClC,MAAI,aAAa,QAAQ,QAAQ,IAAI,aAAa,eAAe;AAC/D,YAAQ,MAAM,+DAA+D;AAAA,EAC/E;AACA,mBAAiB,8BAA8B,OAAO,CAAC;AACzD;AAEO,SAAS,aAAuB;AACrC,QAAM,UAAU,iBAAiB;AACjC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,0EAA0E;AAAA,EAC5F;AACA,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,4 @@
1
- const APP_VERSION = "0.6.3-develop.3663.1.63517660ba";
1
+ const APP_VERSION = "0.6.3-develop.3680.1.62e9da67c2";
2
2
  const appVersion = APP_VERSION;
3
3
  export {
4
4
  APP_VERSION,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/version.ts"],
4
- "sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.6.3-develop.3663.1.63517660ba'\nexport const appVersion = APP_VERSION\n"],
4
+ "sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.6.3-develop.3680.1.62e9da67c2'\nexport const appVersion = APP_VERSION\n"],
5
5
  "mappings": "AACO,MAAM,cAAc;AACpB,MAAM,aAAa;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/shared",
3
- "version": "0.6.3-develop.3663.1.63517660ba",
3
+ "version": "0.6.3-develop.3680.1.62e9da67c2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -92,7 +92,7 @@
92
92
  "@mikro-orm/core": "^7.0.17",
93
93
  "@mikro-orm/decorators": "^7.0.17",
94
94
  "@mikro-orm/postgresql": "^7.0.17",
95
- "@open-mercato/cache": "0.6.3-develop.3663.1.63517660ba",
95
+ "@open-mercato/cache": "0.6.3-develop.3680.1.62e9da67c2",
96
96
  "dotenv": "^17.4.2",
97
97
  "rate-limiter-flexible": "^11.1.0",
98
98
  "re2js": "2.8.2",
@@ -0,0 +1,114 @@
1
+ import type { Module } from '@open-mercato/shared/modules/registry'
2
+
3
+ const GLOBAL_KEY = '__openMercatoModulesRegistry__'
4
+
5
+ function clearGlobalRegistry(): void {
6
+ delete (globalThis as any)[GLOBAL_KEY]
7
+ }
8
+
9
+ function clearRegistryModuleCache(): void {
10
+ const matchers = [/packages\/shared\/src\/lib\/modules\/registry\.ts$/]
11
+ for (const key of Object.keys(require.cache)) {
12
+ if (matchers.some((re) => re.test(key))) {
13
+ delete require.cache[key]
14
+ }
15
+ }
16
+ }
17
+
18
+ function loadRegistry(): typeof import('../registry') {
19
+ clearRegistryModuleCache()
20
+ return require('../registry') as typeof import('../registry')
21
+ }
22
+
23
+ describe('shared modules registry', () => {
24
+ const sampleModules: Module[] = [
25
+ { id: 'auth' } as Module,
26
+ { id: 'customers' } as Module,
27
+ ]
28
+
29
+ let nodeEnvSnapshot: string | undefined
30
+
31
+ beforeEach(() => {
32
+ nodeEnvSnapshot = process.env.NODE_ENV
33
+ clearGlobalRegistry()
34
+ })
35
+
36
+ afterEach(() => {
37
+ clearGlobalRegistry()
38
+ if (nodeEnvSnapshot === undefined) {
39
+ delete process.env.NODE_ENV
40
+ } else {
41
+ process.env.NODE_ENV = nodeEnvSnapshot
42
+ }
43
+ clearRegistryModuleCache()
44
+ })
45
+
46
+ it('returns registered modules from the same module instance', () => {
47
+ const registry = loadRegistry()
48
+ registry.registerModules(sampleModules)
49
+ expect(registry.getModules().map((m) => m.id)).toEqual(['auth', 'customers'])
50
+ })
51
+
52
+ it('throws a helpful error when getModules is called before bootstrap', () => {
53
+ const registry = loadRegistry()
54
+ expect(() => registry.getModules()).toThrow(
55
+ '[Bootstrap] Modules not registered. Call registerModules() at bootstrap.',
56
+ )
57
+ })
58
+
59
+ it('survives module duplication: a re-required registry instance still sees modules registered by the first', () => {
60
+ // First load: bootstrap registers modules via the "source" path.
61
+ const first = loadRegistry()
62
+ first.registerModules(sampleModules)
63
+ expect(first.getModules().map((m) => m.id)).toEqual(['auth', 'customers'])
64
+
65
+ // Second load: simulate tsx/esbuild re-loading the same file under a
66
+ // different module identity (e.g. dist/ vs src/). This is the exact
67
+ // failure mode hit by the standalone TC-CRM-068/069 worker handlers —
68
+ // they create a fresh container, which calls getModules() through a
69
+ // different registry instance than the test's bootstrap registered into.
70
+ // With the module-local `let _modules` variant, this re-require would
71
+ // throw `[Bootstrap] Modules not registered`. With the globalThis
72
+ // variant it returns the same list.
73
+ const second = loadRegistry()
74
+ expect(second.getModules().map((m) => m.id)).toEqual(['auth', 'customers'])
75
+ })
76
+
77
+ it('reads from globalThis so external setters can prime the registry', () => {
78
+ // Simulates the case where a bootstrap script in a sibling process or
79
+ // sibling module instance already wrote to `globalThis` before this
80
+ // package's registry module was even loaded.
81
+ ;(globalThis as any).__openMercatoModulesRegistry__ = sampleModules
82
+ const registry = loadRegistry()
83
+ expect(registry.getModules().map((m) => m.id)).toEqual(['auth', 'customers'])
84
+ })
85
+
86
+ it('emits the HMR debug log on re-registration in development', () => {
87
+ process.env.NODE_ENV = 'development'
88
+ const debugSpy = jest.spyOn(console, 'debug').mockImplementation(() => {})
89
+ try {
90
+ const registry = loadRegistry()
91
+ registry.registerModules(sampleModules)
92
+ expect(debugSpy).not.toHaveBeenCalled()
93
+ registry.registerModules(sampleModules)
94
+ expect(debugSpy).toHaveBeenCalledWith(
95
+ '[Bootstrap] Modules re-registered (this may occur during HMR)',
96
+ )
97
+ } finally {
98
+ debugSpy.mockRestore()
99
+ }
100
+ })
101
+
102
+ it('does not emit the HMR debug log when NODE_ENV is not development', () => {
103
+ process.env.NODE_ENV = 'production'
104
+ const debugSpy = jest.spyOn(console, 'debug').mockImplementation(() => {})
105
+ try {
106
+ const registry = loadRegistry()
107
+ registry.registerModules(sampleModules)
108
+ registry.registerModules(sampleModules)
109
+ expect(debugSpy).not.toHaveBeenCalled()
110
+ } finally {
111
+ debugSpy.mockRestore()
112
+ }
113
+ })
114
+ })
@@ -1,19 +1,35 @@
1
1
  import type { Module } from '@open-mercato/shared/modules/registry'
2
2
  import { applyModuleOverridesToModules } from '@open-mercato/shared/modules/overrides'
3
3
 
4
- // Registration pattern for publishable packages
5
- let _modules: Module[] | null = null
4
+ // Registration pattern for publishable packages.
5
+ // Use globalThis to survive tsx/esbuild module duplication where the same
6
+ // registry.ts file can be loaded as multiple module instances when mixing
7
+ // dynamic and static imports — for example a standalone integration test
8
+ // bootstraps via the source path while a worker handler resolves it through
9
+ // node_modules/@open-mercato/shared/dist/. Mirrors the same workaround used
10
+ // by `getDiRegistrars()` in `../di/container.ts`.
11
+ const GLOBAL_KEY = '__openMercatoModulesRegistry__'
12
+
13
+ function getGlobalModules(): Module[] | null {
14
+ return (globalThis as any)[GLOBAL_KEY] ?? null
15
+ }
16
+
17
+ function setGlobalModules(modules: Module[]): void {
18
+ ;(globalThis as any)[GLOBAL_KEY] = modules
19
+ }
6
20
 
7
21
  export function registerModules(modules: Module[]) {
8
- if (_modules !== null && process.env.NODE_ENV === 'development') {
22
+ const existing = getGlobalModules()
23
+ if (existing !== null && process.env.NODE_ENV === 'development') {
9
24
  console.debug('[Bootstrap] Modules re-registered (this may occur during HMR)')
10
25
  }
11
- _modules = applyModuleOverridesToModules(modules)
26
+ setGlobalModules(applyModuleOverridesToModules(modules))
12
27
  }
13
28
 
14
29
  export function getModules(): Module[] {
15
- if (!_modules) {
30
+ const modules = getGlobalModules()
31
+ if (!modules) {
16
32
  throw new Error('[Bootstrap] Modules not registered. Call registerModules() at bootstrap.')
17
33
  }
18
- return _modules
34
+ return modules
19
35
  }