@open-mercato/cli 0.7.0 → 0.7.1-develop.7102.1.b41f7e3e51

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 (184) hide show
  1. package/.turbo/turbo-build.log +7 -1
  2. package/dist/agentic/guides/module-facts.json +5509 -599
  3. package/dist/agentic/guides/module-facts.v2.json +5595 -599
  4. package/dist/agentic/guides/modules/ai_assistant/index.md +1 -1
  5. package/dist/agentic/guides/modules/api_docs/index.md +1 -1
  6. package/dist/agentic/guides/modules/api_keys/di-registrations-rich.md +11 -0
  7. package/dist/agentic/guides/modules/api_keys/di-service-tokens.md +11 -0
  8. package/dist/agentic/guides/modules/api_keys/exact-override-targets.md +1 -0
  9. package/dist/agentic/guides/modules/api_keys/index.md +4 -2
  10. package/dist/agentic/guides/modules/attachments/di-registrations-rich.md +6 -5
  11. package/dist/agentic/guides/modules/attachments/di-service-tokens.md +5 -4
  12. package/dist/agentic/guides/modules/attachments/exact-override-targets.md +6 -5
  13. package/dist/agentic/guides/modules/attachments/index.md +2 -2
  14. package/dist/agentic/guides/modules/audit_logs/index.md +1 -1
  15. package/dist/agentic/guides/modules/auth/di-registrations-rich.md +3 -2
  16. package/dist/agentic/guides/modules/auth/di-service-tokens.md +3 -2
  17. package/dist/agentic/guides/modules/auth/exact-override-targets.md +3 -2
  18. package/dist/agentic/guides/modules/auth/index.md +1 -1
  19. package/dist/agentic/guides/modules/business_rules/index.md +1 -1
  20. package/dist/agentic/guides/modules/catalog/incoming-installed-contributions.md +3 -0
  21. package/dist/agentic/guides/modules/catalog/index.md +2 -2
  22. package/dist/agentic/guides/modules/channel_apns/index.md +1 -1
  23. package/dist/agentic/guides/modules/channel_discord/acl-features.md +13 -0
  24. package/dist/agentic/guides/modules/channel_discord/ai-agents.md +11 -0
  25. package/dist/agentic/guides/modules/channel_discord/backend-pages.md +11 -0
  26. package/dist/agentic/guides/modules/channel_discord/cli-commands.md +13 -0
  27. package/dist/agentic/guides/modules/channel_discord/contribution-resolutions.md +14 -0
  28. package/dist/agentic/guides/modules/channel_discord/di-registrations-rich.md +12 -0
  29. package/dist/agentic/guides/modules/channel_discord/exact-override-targets.md +26 -0
  30. package/dist/agentic/guides/modules/channel_discord/index.md +22 -0
  31. package/dist/agentic/guides/modules/channel_discord/owned-contract-module-metadata.md +11 -0
  32. package/dist/agentic/guides/modules/channel_discord/setup.md +11 -0
  33. package/dist/agentic/guides/modules/channel_discord/umes-contributions.md +14 -0
  34. package/dist/agentic/guides/modules/channel_discord/workers.md +12 -0
  35. package/dist/agentic/guides/modules/channel_expo/index.md +1 -1
  36. package/dist/agentic/guides/modules/channel_fcm/index.md +1 -1
  37. package/dist/agentic/guides/modules/channel_gmail/index.md +1 -1
  38. package/dist/agentic/guides/modules/channel_imap/index.md +1 -1
  39. package/dist/agentic/guides/modules/checkout/index.md +1 -1
  40. package/dist/agentic/guides/modules/communication_channels/di-registrations-rich.md +9 -8
  41. package/dist/agentic/guides/modules/communication_channels/exact-override-targets.md +9 -8
  42. package/dist/agentic/guides/modules/communication_channels/incoming-installed-contributions.md +2 -0
  43. package/dist/agentic/guides/modules/communication_channels/index.md +3 -3
  44. package/dist/agentic/guides/modules/configs/index.md +1 -1
  45. package/dist/agentic/guides/modules/content/index.md +1 -1
  46. package/dist/agentic/guides/modules/currencies/index.md +1 -1
  47. package/dist/agentic/guides/modules/customer_accounts/index.md +1 -1
  48. package/dist/agentic/guides/modules/customers/active-extension-bindings.md +6 -3
  49. package/dist/agentic/guides/modules/customers/contribution-resolutions.md +1 -0
  50. package/dist/agentic/guides/modules/customers/events.md +2 -1
  51. package/dist/agentic/guides/modules/customers/incoming-installed-contributions.md +7 -0
  52. package/dist/agentic/guides/modules/customers/index.md +4 -4
  53. package/dist/agentic/guides/modules/customers/umes-contributions.md +1 -0
  54. package/dist/agentic/guides/modules/customers/umes-hosts.md +1 -0
  55. package/dist/agentic/guides/modules/dashboards/index.md +1 -1
  56. package/dist/agentic/guides/modules/data_sync/exact-override-targets.md +2 -2
  57. package/dist/agentic/guides/modules/data_sync/index.md +1 -1
  58. package/dist/agentic/guides/modules/data_sync/workers.md +2 -2
  59. package/dist/agentic/guides/modules/design_system/index.md +1 -1
  60. package/dist/agentic/guides/modules/devices/index.md +1 -1
  61. package/dist/agentic/guides/modules/dictionaries/index.md +1 -1
  62. package/dist/agentic/guides/modules/directory/di-registrations-rich.md +12 -0
  63. package/dist/agentic/guides/modules/directory/di-service-tokens.md +12 -0
  64. package/dist/agentic/guides/modules/directory/exact-override-targets.md +2 -0
  65. package/dist/agentic/guides/modules/directory/index.md +5 -3
  66. package/dist/agentic/guides/modules/documents/acl-features.md +17 -0
  67. package/dist/agentic/guides/modules/documents/backend-pages.md +13 -0
  68. package/dist/agentic/guides/modules/documents/contribution-resolutions.md +27 -0
  69. package/dist/agentic/guides/modules/documents/di-registrations-rich.md +21 -0
  70. package/dist/agentic/guides/modules/documents/domain-commands.md +37 -0
  71. package/dist/agentic/guides/modules/documents/encryption.md +11 -0
  72. package/dist/agentic/guides/modules/documents/entities.md +21 -0
  73. package/dist/agentic/guides/modules/documents/events.md +25 -0
  74. package/dist/agentic/guides/modules/documents/exact-override-targets.md +39 -0
  75. package/dist/agentic/guides/modules/documents/index.md +25 -0
  76. package/dist/agentic/guides/modules/documents/notifications.md +13 -0
  77. package/dist/agentic/guides/modules/documents/owned-contract-module-metadata.md +11 -0
  78. package/dist/agentic/guides/modules/documents/setup.md +11 -0
  79. package/dist/agentic/guides/modules/documents/umes-contributions.md +27 -0
  80. package/dist/agentic/guides/modules/documents/umes-hosts.md +36 -0
  81. package/dist/agentic/guides/modules/entities/index.md +1 -1
  82. package/dist/agentic/guides/modules/eudr/index.md +1 -1
  83. package/dist/agentic/guides/modules/events/index.md +1 -1
  84. package/dist/agentic/guides/modules/feature_toggles/index.md +1 -1
  85. package/dist/agentic/guides/modules/gateway_stripe/exact-override-targets.md +1 -1
  86. package/dist/agentic/guides/modules/gateway_stripe/index.md +1 -1
  87. package/dist/agentic/guides/modules/gateway_stripe/workers.md +1 -1
  88. package/dist/agentic/guides/modules/generators/index.md +1 -1
  89. package/dist/agentic/guides/modules/inbox_ops/index.md +1 -1
  90. package/dist/agentic/guides/modules/integrations/index.md +1 -1
  91. package/dist/agentic/guides/modules/messages/index.md +1 -1
  92. package/dist/agentic/guides/modules/notifications/index.md +1 -1
  93. package/dist/agentic/guides/modules/onboarding/index.md +1 -1
  94. package/dist/agentic/guides/modules/payment_gateways/index.md +1 -1
  95. package/dist/agentic/guides/modules/perspectives/index.md +1 -1
  96. package/dist/agentic/guides/modules/planner/index.md +1 -1
  97. package/dist/agentic/guides/modules/portal/index.md +1 -1
  98. package/dist/agentic/guides/modules/progress/index.md +1 -1
  99. package/dist/agentic/guides/modules/push_notifications/index.md +1 -1
  100. package/dist/agentic/guides/modules/query_index/index.md +1 -1
  101. package/dist/agentic/guides/modules/record_locks/index.md +1 -1
  102. package/dist/agentic/guides/modules/resources/index.md +1 -1
  103. package/dist/agentic/guides/modules/sales/active-extension-bindings.md +1 -0
  104. package/dist/agentic/guides/modules/sales/contribution-resolutions.md +1 -0
  105. package/dist/agentic/guides/modules/sales/exact-override-targets.md +5 -4
  106. package/dist/agentic/guides/modules/sales/host-extension-points.md +1 -1
  107. package/dist/agentic/guides/modules/sales/incoming-installed-contributions.md +2 -0
  108. package/dist/agentic/guides/modules/sales/index.md +3 -3
  109. package/dist/agentic/guides/modules/sales/setup.md +1 -1
  110. package/dist/agentic/guides/modules/sales/umes-contributions.md +1 -0
  111. package/dist/agentic/guides/modules/sales/umes-diagnostics.md +1 -0
  112. package/dist/agentic/guides/modules/sales/umes-hosts.md +9 -0
  113. package/dist/agentic/guides/modules/scheduler/cli-commands.md +1 -0
  114. package/dist/agentic/guides/modules/scheduler/exact-override-targets.md +3 -1
  115. package/dist/agentic/guides/modules/scheduler/index.md +2 -2
  116. package/dist/agentic/guides/modules/scheduler/workers.md +2 -1
  117. package/dist/agentic/guides/modules/search/index.md +1 -1
  118. package/dist/agentic/guides/modules/security/index.md +1 -1
  119. package/dist/agentic/guides/modules/security/owned-contract-module-metadata.md +1 -1
  120. package/dist/agentic/guides/modules/shipping_carriers/index.md +1 -1
  121. package/dist/agentic/guides/modules/sso/index.md +1 -1
  122. package/dist/agentic/guides/modules/staff/index.md +1 -1
  123. package/dist/agentic/guides/modules/storage_s3/index.md +1 -1
  124. package/dist/agentic/guides/modules/sync_akeneo/index.md +1 -1
  125. package/dist/agentic/guides/modules/sync_excel/index.md +1 -1
  126. package/dist/agentic/guides/modules/system_status_overlays/index.md +1 -1
  127. package/dist/agentic/guides/modules/translations/index.md +1 -1
  128. package/dist/agentic/guides/modules/warranty_claims/index.md +1 -1
  129. package/dist/agentic/guides/modules/webhooks/index.md +1 -1
  130. package/dist/agentic/guides/modules/wms/index.md +1 -1
  131. package/dist/agentic/guides/modules/workflows/index.md +1 -1
  132. package/dist/agentic/guides/reference-module-facts.json +2 -2
  133. package/dist/agentic/guides/reference-modules/example/index.md +1 -1
  134. package/dist/agentic/guides/upstream/manifest.json +1 -1
  135. package/dist/agentic/shared/AGENTS.md.template +3 -3
  136. package/dist/agentic/shared/ai/harness/README.md +2 -2
  137. package/dist/agentic/shared/ai/harness/RELEASE.md +8 -6
  138. package/dist/agentic/shared/ai/harness/cases.json +83 -0
  139. package/dist/agentic/shared/ai/harness/cases.schema.json +4 -4
  140. package/dist/agentic/shared/ai/harness/validators.json +1 -1
  141. package/dist/agentic/shared/scripts/run-agent-harness-release.mjs +37 -11
  142. package/dist/lib/generators/ast/index.js +4 -1
  143. package/dist/lib/generators/ast/index.js.map +2 -2
  144. package/dist/lib/generators/ast/source-file.js +8 -1
  145. package/dist/lib/generators/ast/source-file.js.map +2 -2
  146. package/dist/lib/generators/ast/writers.js +9 -1
  147. package/dist/lib/generators/ast/writers.js.map +2 -2
  148. package/dist/lib/generators/module-registry.js +424 -189
  149. package/dist/lib/generators/module-registry.js.map +2 -2
  150. package/dist/lib/generators/route-manifest-shards.js +4 -1
  151. package/dist/lib/generators/route-manifest-shards.js.map +2 -2
  152. package/dist/lib/spawn.js +3 -2
  153. package/dist/lib/spawn.js.map +2 -2
  154. package/dist/lib/testing/integration.js +162 -24
  155. package/dist/lib/testing/integration.js.map +2 -2
  156. package/dist/mercato.js +14 -0
  157. package/dist/mercato.js.map +2 -2
  158. package/package.json +8 -7
  159. package/src/__tests__/resolve-queue-abandon-hook.test.ts +39 -0
  160. package/src/lib/__tests__/spawn.test.ts +23 -0
  161. package/src/lib/generators/__tests__/__snapshots__/bootstrap-registrations.test.ts.snap +14 -0
  162. package/src/lib/generators/__tests__/__snapshots__/command-loaders.test.ts.snap +23 -0
  163. package/src/lib/generators/__tests__/ast-writers-escaping.test.ts +64 -0
  164. package/src/lib/generators/__tests__/bootstrap-registrations.test.ts +176 -0
  165. package/src/lib/generators/__tests__/command-loaders.test.ts +239 -0
  166. package/src/lib/generators/__tests__/generated-registry-imports.test.ts +128 -0
  167. package/src/lib/generators/__tests__/metadata-extraction.test.ts +44 -0
  168. package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +32 -8
  169. package/src/lib/generators/__tests__/module-facts.customers.fixture.test.ts +4 -1
  170. package/src/lib/generators/__tests__/module-facts.extension-hosts.test.ts +1 -1
  171. package/src/lib/generators/__tests__/module-subset.test.ts +96 -72
  172. package/src/lib/generators/__tests__/output-snapshots.test.ts +1 -1
  173. package/src/lib/generators/__tests__/registry-variant-parity.test.ts +258 -0
  174. package/src/lib/generators/__tests__/route-manifest-shards.test.ts +15 -0
  175. package/src/lib/generators/__tests__/structural-contracts.test.ts +7 -3
  176. package/src/lib/generators/ast/index.ts +2 -1
  177. package/src/lib/generators/ast/source-file.ts +13 -0
  178. package/src/lib/generators/ast/writers.ts +26 -1
  179. package/src/lib/generators/module-registry.ts +534 -227
  180. package/src/lib/generators/route-manifest-shards.ts +8 -1
  181. package/src/lib/spawn.ts +5 -4
  182. package/src/lib/testing/__tests__/integration.test.ts +435 -0
  183. package/src/lib/testing/integration.ts +254 -32
  184. package/src/mercato.ts +27 -0
@@ -0,0 +1,128 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+ import os from 'node:os'
4
+ import type { PackageResolver, ModuleEntry } from '../../resolver'
5
+ import { generateModuleRegistry, generateModuleRegistryApp, generateModuleRegistryCli } from '../module-registry'
6
+
7
+ /**
8
+ * The generator's output, not its helpers.
9
+ *
10
+ * A worker declaring `metadata.onJobAbandoned` makes the registry emit
11
+ * `createLazyModuleWorkerAbandonHook(...)`, and the import for it used to be opted into per render
12
+ * site. Forgetting it at one of the six sites produced a file referencing an undeclared name, which
13
+ * type-checks nowhere and surfaces only at `build:app` — every unit test stayed green because no
14
+ * fixture declared such a worker.
15
+ *
16
+ * These assertions are driven off the files the generators actually wrote, so a renderer added later
17
+ * is covered without anyone remembering to extend this test.
18
+ */
19
+
20
+ let tmpDir: string
21
+
22
+ function touchFile(filePath: string, content = ''): void {
23
+ fs.mkdirSync(path.dirname(filePath), { recursive: true })
24
+ fs.writeFileSync(filePath, content)
25
+ }
26
+
27
+ function createMockResolver(dir: string, enabled: ModuleEntry[]): PackageResolver {
28
+ const outputDir = path.join(dir, 'output', 'generated')
29
+ fs.mkdirSync(outputDir, { recursive: true })
30
+ return {
31
+ isMonorepo: () => true,
32
+ getRootDir: () => dir,
33
+ getAppDir: () => path.join(dir, 'app'),
34
+ getOutputDir: () => outputDir,
35
+ getModulesConfigPath: () => path.join(dir, 'app', 'src', 'modules.ts'),
36
+ discoverPackages: () => [],
37
+ loadEnabledModules: () => enabled,
38
+ getModulePaths: (entry: ModuleEntry) => ({
39
+ appBase: path.join(dir, 'app', 'src', 'modules', entry.id),
40
+ pkgBase: path.join(dir, 'packages', 'core', 'src', 'modules', entry.id),
41
+ }),
42
+ getModuleImportBase: (entry: ModuleEntry) => ({
43
+ appBase: `@/modules/${entry.id}`,
44
+ pkgBase: `@open-mercato/core/modules/${entry.id}`,
45
+ }),
46
+ getPackageOutputDir: () => outputDir,
47
+ getPackageRoot: () => path.join(dir, 'packages', 'core'),
48
+ }
49
+ }
50
+
51
+ function scaffoldWorkerModule(dir: string, modId: string, workerSource: string): void {
52
+ touchFile(path.join(dir, 'packages', 'core', 'src', 'modules', modId, 'workers', 'sync.ts'), workerSource)
53
+ }
54
+
55
+ function generatedFiles(dir: string): Array<{ name: string; content: string }> {
56
+ const outputDir = path.join(dir, 'output', 'generated')
57
+ if (!fs.existsSync(outputDir)) return []
58
+ return fs.readdirSync(outputDir)
59
+ .filter((name) => name.endsWith('.ts'))
60
+ .map((name) => ({ name, content: fs.readFileSync(path.join(outputDir, name), 'utf8') }))
61
+ }
62
+
63
+ const ABANDON_HOOK_FACTORY = 'createLazyModuleWorkerAbandonHook'
64
+
65
+ const WORKER_WITH_HOOK = `
66
+ import { failAbandonedRun } from '../lib/abandoned-run'
67
+ export const metadata = {
68
+ queue: 'demo-queue',
69
+ id: 'demo:worker',
70
+ concurrency: 2,
71
+ onJobAbandoned: failAbandonedRun,
72
+ }
73
+ export default async function handle() {}
74
+ `
75
+
76
+ const WORKER_WITHOUT_HOOK = `
77
+ export const metadata = {
78
+ queue: 'demo-queue',
79
+ id: 'demo:worker',
80
+ concurrency: 2,
81
+ }
82
+ export default async function handle() {}
83
+ `
84
+
85
+ async function generateAll(resolver: PackageResolver): Promise<void> {
86
+ await generateModuleRegistry({ resolver, quiet: true })
87
+ await generateModuleRegistryApp({ resolver, quiet: true })
88
+ await generateModuleRegistryCli({ resolver, quiet: true })
89
+ }
90
+
91
+ beforeEach(() => {
92
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'generated-registry-imports-'))
93
+ })
94
+
95
+ afterEach(() => {
96
+ fs.rmSync(tmpDir, { recursive: true, force: true })
97
+ })
98
+
99
+ describe('generated registries — emitted identifiers are imported', () => {
100
+ it('imports the abandon-hook helper in every file that calls it', async () => {
101
+ scaffoldWorkerModule(tmpDir, 'demo', WORKER_WITH_HOOK)
102
+ const resolver = createMockResolver(tmpDir, [{ id: 'demo', from: 'pkg' } as ModuleEntry])
103
+
104
+ await generateAll(resolver)
105
+
106
+ const files = generatedFiles(tmpDir)
107
+ const callers = files.filter(({ content }) => content.includes(`${ABANDON_HOOK_FACTORY}(`))
108
+ // The fixture must actually exercise the path, or this test passes vacuously — which is exactly
109
+ // how the missing import survived every existing suite.
110
+ expect(callers.length).toBeGreaterThan(0)
111
+
112
+ for (const { name, content } of callers) {
113
+ expect(`${name}: ${content.includes(`import { `) && new RegExp(`import \\{[^}]*\\b${ABANDON_HOOK_FACTORY}\\b[^}]*\\} from`, 's').test(content)}`)
114
+ .toBe(`${name}: true`)
115
+ }
116
+ })
117
+
118
+ it('leaves output untouched for a module set whose workers declare no hook', async () => {
119
+ scaffoldWorkerModule(tmpDir, 'demo', WORKER_WITHOUT_HOOK)
120
+ const resolver = createMockResolver(tmpDir, [{ id: 'demo', from: 'pkg' } as ModuleEntry])
121
+
122
+ await generateAll(resolver)
123
+
124
+ for (const { name, content } of generatedFiles(tmpDir)) {
125
+ expect(`${name}: ${content.includes(ABANDON_HOOK_FACTORY)}`).toBe(`${name}: false`)
126
+ }
127
+ })
128
+ })
@@ -4,6 +4,7 @@ import os from 'node:os'
4
4
  import {
5
5
  extractNamedObjectLiteralExport,
6
6
  hasNamedExport,
7
+ namedObjectLiteralDeclaresProperty,
7
8
  resolveNamedObjectExport,
8
9
  } from '../module-registry'
9
10
 
@@ -218,3 +219,46 @@ describe('hasNamedExport', () => {
218
219
  expect(hasNamedExport(file, 'metadata')).toBe(false)
219
220
  })
220
221
  })
222
+
223
+ describe('namedObjectLiteralDeclaresProperty', () => {
224
+ // A worker declaring `onJobAbandoned: someImportedFn` is the case that broke: the value resolvers
225
+ // cannot evaluate an imported identifier, so "is it declared" has to be answered from the syntax.
226
+ // Getting this wrong is silent — the generator simply never emits the hook.
227
+ it('sees a property whose value is an unresolvable identifier', () => {
228
+ const file = writeSource('worker.ts', `
229
+ import { failAbandonedRun } from '../lib/abandoned-run'
230
+ export const metadata = {
231
+ queue: 'data-sync-import',
232
+ concurrency: 5,
233
+ onJobAbandoned: failAbandonedRun,
234
+ }
235
+ `)
236
+ expect(namedObjectLiteralDeclaresProperty(file, 'metadata', 'onJobAbandoned')).toBe(true)
237
+ expect(extractNamedObjectLiteralExport(file, 'metadata')?.onJobAbandoned).toBeUndefined()
238
+ })
239
+
240
+ it('sees a property declared as an inline function', () => {
241
+ const file = writeSource('worker.ts', `
242
+ export const metadata = {
243
+ queue: 'q',
244
+ onJobAbandoned: async () => {},
245
+ }
246
+ `)
247
+ expect(namedObjectLiteralDeclaresProperty(file, 'metadata', 'onJobAbandoned')).toBe(true)
248
+ })
249
+
250
+ it('reports absence for a worker that declares no hook', () => {
251
+ const file = writeSource('worker.ts', `
252
+ export const metadata = { queue: 'q', concurrency: 1 }
253
+ `)
254
+ expect(namedObjectLiteralDeclaresProperty(file, 'metadata', 'onJobAbandoned')).toBe(false)
255
+ })
256
+
257
+ it('reports absence for a metadata export that is not an object literal', () => {
258
+ const file = writeSource('worker.ts', `
259
+ const base = buildMetadata()
260
+ export const metadata = base
261
+ `)
262
+ expect(namedObjectLiteralDeclaresProperty(file, 'metadata', 'onJobAbandoned')).toBe(false)
263
+ })
264
+ })
@@ -149,22 +149,46 @@ describe('module-facts BC resolve guard (T2)', () => {
149
149
  // wms, staff, integrations and checkout were therefore invisible to every fact
150
150
  // consumer — `integrations` published no contributions at all. Reading them costs
151
151
  // ~28KB, which is the fix working, not drift.
152
- // The additive EUDR module contributes its real routes, ACL, events,
153
- // entities, and extension surfaces without changing the extraction shape.
152
+ // The additive EUDR and Documents modules contribute their real routes, ACL,
153
+ // events, entities, and extension surfaces without changing the extraction
154
+ // shape.
154
155
  //
155
156
  // JSON cap raised a fifth time by the devices/push-notifications stack: the
156
157
  // `devices` and `push_notifications` modules plus the `channel-fcm`,
157
158
  // `channel-apns` and `channel-expo` provider packages add their own facts,
158
159
  // provenance entries and override targets to every render. The
159
- // `warranty_claims` module (see above) lands alongside it, so the cap
160
- // absorbs both additions.
161
- expect(Buffer.byteLength(completeJson)).toBeLessThan(4_000_000)
162
- expect(Buffer.byteLength(completeJson) - Buffer.byteLength(legacyJson)).toBeLessThan(1_800_000)
160
+ // `warranty_claims` module (see above) and the additive Documents module
161
+ // land alongside it.
162
+ //
163
+ // Raised again here for a reason unlike every raise above: those all tracked
164
+ // a schema or extraction change, this one tracks ordinary repo growth. The
165
+ // additive `channel_discord` module costs ~15.9KB JSON and ~9.2KB markdown
166
+ // (`channel_gmail` costs 6,886 / `channel_imap` 6,798; the difference is a
167
+ // gateway worker, a CLI command, a signed route and a subscriber, spread
168
+ // proportionally across overrideTargets, extensionSurfaces, factSources and
169
+ // ownedContracts — no duplicated payloads).
170
+ //
171
+ // The numbers below are MEASURED ON THE MERGED TREE, not transcribed from
172
+ // either side of the merge: neither side's cap covers the union. A cap that
173
+ // sits a fraction of a percent above the current measurement has stopped
174
+ // detecting blow-ups and started rejecting the next additive module of any
175
+ // size, whichever PR happens to add it — so keep bounded headroom over the
176
+ // measured size. A real blow-up here is multiplicative (a duplicated
177
+ // provenance payload, a contribution body copied per resolution), not one
178
+ // provider's worth of references.
179
+ expect(Buffer.byteLength(completeJson)).toBeLessThan(4_700_000)
180
+ // Measured on the merged tree (`yarn jest module-facts.bc-guard`):
181
+ // completeJson 4,120,374 · legacy delta 1,795,214 · markdown 1,558,590 ·
182
+ // directory markdown 1,709,511.
183
+ // The delta cap is raised alongside the JSON one for the same reason: at
184
+ // 1,800,000 it sat 0.27% above the measurement, which is not a guard.
185
+ expect(Buffer.byteLength(completeJson) - Buffer.byteLength(legacyJson)).toBeLessThan(2_050_000)
163
186
  // Markdown cap raised with the source-link contract: entities, events, ACL
164
187
  // features, DI tokens, search entities, notifications, UMES hosts and UMES
165
188
  // contributions all render a resolved Source cell, and contribution
166
- // resolutions render as their own source-linked section.
167
- expect(markdownBytes).toBeLessThan(1_750_000)
189
+ // resolutions render as their own source-linked section — plus the additive
190
+ // `channel_discord` module's own render, per the growth note above.
191
+ expect(markdownBytes).toBeLessThan(1_800_000)
168
192
  expect(directoryMarkdownBytes).toBeLessThan(2_050_000)
169
193
  })
170
194
 
@@ -28,7 +28,10 @@ describe('module-facts customers fixture (T1 anti-drift guard)', () => {
28
28
  })
29
29
 
30
30
  it('locks customers events, acl, search, and notification surfaces', () => {
31
- expect(facts.events).toHaveLength(49)
31
+ // 50 since #5114 added `customers.person.company_assignment.detached` — the profile-only
32
+ // sibling of the person_company_link CRUD events. Bump deliberately: this guard exists to
33
+ // make an event-surface change visible, not to be silenced.
34
+ expect(facts.events).toHaveLength(50)
32
35
  expect(facts.aclFeatures).toHaveLength(21)
33
36
  expect(facts.searchEntities).toEqual([
34
37
  'customers:customer_person_profile',
@@ -103,6 +103,6 @@ describe('module-facts extension host extraction', () => {
103
103
  ])
104
104
 
105
105
  const sales = extractModuleFacts({ moduleId: 'sales', coreSrcRoot })
106
- expect(sales.hostTokens.tableIds).toEqual(['sales.orders', 'sales.quotes'])
106
+ expect(sales.hostTokens.tableIds).toEqual(['sales.orders', 'sales.payments', 'sales.quotes'])
107
107
  })
108
108
  })
@@ -186,65 +186,65 @@ describe('generateModuleRegistry with module subsets', () => {
186
186
  expect(output).toContain('export default modules')
187
187
  })
188
188
 
189
- it('preserves exact legacy empty output format for the refactored registry files', async () => {
189
+ it('pins the exact emitted empty output format for the refactored registry files', async () => {
190
190
  const resolver = createMockResolver(tmpDir, [])
191
191
  const result = await generateModuleRegistry({ resolver, quiet: true })
192
192
 
193
193
  expect(result.errors).toEqual([])
194
194
  expect(readGenerated(tmpDir, 'modules.generated.ts')).toBe(dedent(`
195
195
  // AUTO-GENERATED by mercato generate registry
196
- import { createLazyModuleSubscriber, createLazyModuleWorker, type Module } from '@open-mercato/shared/modules/registry'
196
+ import { createLazyModuleSubscriber, createLazyModuleWorker, type Module } from "@open-mercato/shared/modules/registry";
197
197
 
198
- export const modules: Module[] = [
199
-
200
- ]
201
- export const modulesInfo = modules.map(m => ({ id: m.id, ...(m.info || {}) }))
202
- export default modules
198
+ export const modules: Module[] = [];
199
+
200
+ export default modules;
201
+
202
+ export const modulesInfo = modules.map((m) => ({
203
+ id: m.id,
204
+ ...m.info || {}
205
+ }));
203
206
  `))
204
207
  expect(readGenerated(tmpDir, 'modules.runtime.generated.ts')).toBe(dedent(`
205
208
  // AUTO-GENERATED by mercato generate registry
206
- import { createElement } from 'react'
207
- import { createLazyModuleSubscriber, createLazyModuleWorker, type Module } from '@open-mercato/shared/modules/registry'
209
+ import { createElement } from "react";
210
+ import { createLazyModuleSubscriber, createLazyModuleWorker, type Module } from "@open-mercato/shared/modules/registry";
208
211
 
209
- export const modules: Module[] = [
210
-
211
- ]
212
- export const modulesInfo = modules.map(m => ({ id: m.id, ...(m.info || {}) }))
213
- export default modules
212
+ export const modules: Module[] = [];
213
+
214
+ export default modules;
215
+
216
+ export const modulesInfo = modules.map((m) => ({
217
+ id: m.id,
218
+ ...m.info || {}
219
+ }));
214
220
  `))
215
221
  expect(readGenerated(tmpDir, 'frontend-routes.generated.ts')).toBe(dedent(`
216
222
  // AUTO-GENERATED by mercato generate registry
217
- import { resolvePageRouteMetadata, type FrontendRouteManifestEntry } from '@open-mercato/shared/modules/registry'
223
+ import { resolvePageRouteMetadata, type FrontendRouteManifestEntry } from "@open-mercato/shared/modules/registry";
218
224
 
219
- export const frontendRoutes: FrontendRouteManifestEntry[] = [
220
-
221
- ]
225
+ export const frontendRoutes: FrontendRouteManifestEntry[] = [];
222
226
 
223
- export default frontendRoutes
227
+ export default frontendRoutes;
224
228
  `))
225
229
  expect(readGenerated(tmpDir, 'backend-routes.generated.ts')).toBe(dedent(`
226
230
  // AUTO-GENERATED by mercato generate registry
227
- import { resolvePageRouteMetadata, type BackendRouteManifestEntry } from '@open-mercato/shared/modules/registry'
231
+ import { resolvePageRouteMetadata, type BackendRouteManifestEntry } from "@open-mercato/shared/modules/registry";
228
232
 
229
- export const backendRoutes: BackendRouteManifestEntry[] = [
230
-
231
- ]
233
+ export const backendRoutes: BackendRouteManifestEntry[] = [];
232
234
 
233
- export default backendRoutes
235
+ export default backendRoutes;
234
236
  `))
235
237
  expect(readGenerated(tmpDir, 'api-routes.generated.ts')).toBe(dedent(`
236
238
  // AUTO-GENERATED by mercato generate registry
237
- import type { ApiRouteManifestEntry } from '@open-mercato/shared/modules/registry'
239
+ import type { ApiRouteManifestEntry } from "@open-mercato/shared/modules/registry";
238
240
 
239
- export const apiRoutes: ApiRouteManifestEntry[] = [
240
-
241
- ]
241
+ export const apiRoutes: ApiRouteManifestEntry[] = [];
242
242
 
243
- export default apiRoutes
243
+ export default apiRoutes;
244
244
  `))
245
245
  })
246
246
 
247
- it('preserves exact legacy populated output format for the refactored registry files', async () => {
247
+ it('pins the exact emitted populated output format for the refactored registry files', async () => {
248
248
  touchFile(
249
249
  path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'orders', 'index.ts'),
250
250
  "export const metadata = { id: 'orders', label: 'Orders' }\n",
@@ -267,75 +267,99 @@ describe('generateModuleRegistry with module subsets', () => {
267
267
 
268
268
  expect(result.errors).toEqual([])
269
269
  const modulesGenerated = readGenerated(tmpDir, 'modules.generated.ts')
270
- expect(modulesGenerated).toContain(`import { createElement } from 'react'`)
270
+ expect(modulesGenerated).toContain(`import { createElement } from "react"`)
271
271
  expect(modulesGenerated).toContain(`import * as I0_orders from "@open-mercato/core/modules/orders/index"`)
272
272
  expect(modulesGenerated).not.toContain(`import C1_orders_index`)
273
273
  expect(modulesGenerated).not.toContain(`import B5_orders_index`)
274
- expect(modulesGenerated).toContain(`frontendRoutes: [{ pattern: "/"`)
275
- expect(modulesGenerated).toContain(`backendRoutes: [{ pattern: "/backend/orders"`)
274
+ expect(modulesGenerated).toMatch(/frontendRoutes:\s*\[\s*\{ pattern: "\/"/)
275
+ expect(modulesGenerated).toMatch(/backendRoutes:\s*\[\s*\{ pattern: "\/backend\/orders"/)
276
276
  expect(modulesGenerated).toContain(`Component: async (props: any) => { const mod = await import("@open-mercato/core/modules/orders/frontend/page")`)
277
277
  expect(modulesGenerated).toContain(`Component: async (props: any) => { const mod = await import("@open-mercato/core/modules/orders/backend/page")`)
278
- expect(modulesGenerated).toContain(`apis: [{ path: ((R9_orders_orders as any).metadata?.path ?? "/orders/orders"), metadata: (R9_orders_orders as any).metadata, handlers: R9_orders_orders as any }]`)
278
+ expect(modulesGenerated).toMatch(/apis:\s*\[\s*\{ path: \(\(R9_orders_orders as any\)\.metadata\?\.path/)
279
+ expect(modulesGenerated).toContain(`{ path: ((R9_orders_orders as any).metadata?.path ?? "/orders/orders"), metadata: (R9_orders_orders as any).metadata, handlers: R9_orders_orders as any }`)
279
280
  expect(readGenerated(tmpDir, 'modules.runtime.generated.ts')).toBe(dedent(`
280
281
  // AUTO-GENERATED by mercato generate registry
281
- import { createElement } from 'react'
282
- import { createLazyModuleSubscriber, createLazyModuleWorker, type Module } from '@open-mercato/shared/modules/registry'
283
- import * as I0_orders from "@open-mercato/core/modules/orders/index"
284
- import * as MRuntime3_orders_index from "@open-mercato/core/modules/orders/frontend/page"
285
- import * as BMRuntime7_orders_index from "@open-mercato/core/modules/orders/backend/page"
282
+ import { createElement } from "react";
283
+ import { createLazyModuleSubscriber, createLazyModuleWorker, type Module } from "@open-mercato/shared/modules/registry";
284
+ import * as I0_orders from "@open-mercato/core/modules/orders/index";
285
+ import * as MRuntime3_orders_index from "@open-mercato/core/modules/orders/frontend/page";
286
+ import * as BMRuntime7_orders_index from "@open-mercato/core/modules/orders/backend/page";
286
287
 
287
288
  export const modules: Module[] = [
288
- {
289
+ {
289
290
  id: "orders",
290
291
  info: I0_orders.metadata,
291
- frontendRoutes: [{ pattern: "/", requireAuth: ((((MRuntime3_orders_index as any).metadata) as any))?.requireAuth, requireRoles: ((((MRuntime3_orders_index as any).metadata) as any))?.requireRoles, requireFeatures: ((((MRuntime3_orders_index as any).metadata) as any))?.requireFeatures, requireCustomerAuth: ((((MRuntime3_orders_index as any).metadata) as any))?.requireCustomerAuth, requireCustomerFeatures: ((((MRuntime3_orders_index as any).metadata) as any))?.requireCustomerFeatures, nav: ((((MRuntime3_orders_index as any).metadata) as any))?.nav, title: ((((MRuntime3_orders_index as any).metadata) as any))?.pageTitle ?? ((((MRuntime3_orders_index as any).metadata) as any))?.title, titleKey: ((((MRuntime3_orders_index as any).metadata) as any))?.pageTitleKey ?? ((((MRuntime3_orders_index as any).metadata) as any))?.titleKey, group: ((((MRuntime3_orders_index as any).metadata) as any))?.pageGroup ?? ((((MRuntime3_orders_index as any).metadata) as any))?.group, groupKey: ((((MRuntime3_orders_index as any).metadata) as any))?.pageGroupKey ?? ((((MRuntime3_orders_index as any).metadata) as any))?.groupKey, icon: ((((MRuntime3_orders_index as any).metadata) as any))?.icon, order: ((((MRuntime3_orders_index as any).metadata) as any))?.pageOrder ?? ((((MRuntime3_orders_index as any).metadata) as any))?.order, priority: ((((MRuntime3_orders_index as any).metadata) as any))?.pagePriority ?? ((((MRuntime3_orders_index as any).metadata) as any))?.priority, navHidden: ((((MRuntime3_orders_index as any).metadata) as any))?.navHidden, visible: ((((MRuntime3_orders_index as any).metadata) as any))?.visible, enabled: ((((MRuntime3_orders_index as any).metadata) as any))?.enabled, breadcrumb: ((((MRuntime3_orders_index as any).metadata) as any))?.breadcrumb, pageContext: ((((MRuntime3_orders_index as any).metadata) as any))?.pageContext, placement: ((((MRuntime3_orders_index as any).metadata) as any))?.placement, Component: async (props: any) => { const mod = await import("@open-mercato/core/modules/orders/frontend/page"); const Component = (mod.default ?? mod) as any; return createElement(Component, props) } }],
292
- backendRoutes: [{ pattern: "/backend/orders", requireAuth: ((((BMRuntime7_orders_index as any).metadata) as any))?.requireAuth, requireRoles: ((((BMRuntime7_orders_index as any).metadata) as any))?.requireRoles, requireFeatures: ((((BMRuntime7_orders_index as any).metadata) as any))?.requireFeatures, requireCustomerAuth: ((((BMRuntime7_orders_index as any).metadata) as any))?.requireCustomerAuth, requireCustomerFeatures: ((((BMRuntime7_orders_index as any).metadata) as any))?.requireCustomerFeatures, nav: ((((BMRuntime7_orders_index as any).metadata) as any))?.nav, title: ((((BMRuntime7_orders_index as any).metadata) as any))?.pageTitle ?? ((((BMRuntime7_orders_index as any).metadata) as any))?.title, titleKey: ((((BMRuntime7_orders_index as any).metadata) as any))?.pageTitleKey ?? ((((BMRuntime7_orders_index as any).metadata) as any))?.titleKey, group: ((((BMRuntime7_orders_index as any).metadata) as any))?.pageGroup ?? ((((BMRuntime7_orders_index as any).metadata) as any))?.group, groupKey: ((((BMRuntime7_orders_index as any).metadata) as any))?.pageGroupKey ?? ((((BMRuntime7_orders_index as any).metadata) as any))?.groupKey, icon: ((((BMRuntime7_orders_index as any).metadata) as any))?.icon, order: ((((BMRuntime7_orders_index as any).metadata) as any))?.pageOrder ?? ((((BMRuntime7_orders_index as any).metadata) as any))?.order, priority: ((((BMRuntime7_orders_index as any).metadata) as any))?.pagePriority ?? ((((BMRuntime7_orders_index as any).metadata) as any))?.priority, navHidden: ((((BMRuntime7_orders_index as any).metadata) as any))?.navHidden, visible: ((((BMRuntime7_orders_index as any).metadata) as any))?.visible, enabled: ((((BMRuntime7_orders_index as any).metadata) as any))?.enabled, breadcrumb: ((((BMRuntime7_orders_index as any).metadata) as any))?.breadcrumb, pageContext: ((((BMRuntime7_orders_index as any).metadata) as any))?.pageContext, placement: ((((BMRuntime7_orders_index as any).metadata) as any))?.placement, Component: async (props: any) => { const mod = await import("@open-mercato/core/modules/orders/backend/page"); const Component = (mod.default ?? mod) as any; return createElement(Component, props) } }],
293
- apis: [{ path: "/orders/orders", metadata: undefined, handlers: { GET: async (req: Request, ctx?: any) => { const mod = await import("@open-mercato/core/modules/orders/api/orders/route"); return (mod as any).GET(req, ctx) } } }],
294
-
295
-
296
-
297
-
298
- customFieldSets: [],
299
-
300
-
301
-
302
-
303
-
304
-
292
+ frontendRoutes: [
293
+ { pattern: "/", requireAuth: ((((MRuntime3_orders_index as any).metadata) as any))?.requireAuth, requireRoles: ((((MRuntime3_orders_index as any).metadata) as any))?.requireRoles, requireFeatures: ((((MRuntime3_orders_index as any).metadata) as any))?.requireFeatures, requireCustomerAuth: ((((MRuntime3_orders_index as any).metadata) as any))?.requireCustomerAuth, requireCustomerFeatures: ((((MRuntime3_orders_index as any).metadata) as any))?.requireCustomerFeatures, nav: ((((MRuntime3_orders_index as any).metadata) as any))?.nav, title: ((((MRuntime3_orders_index as any).metadata) as any))?.pageTitle ?? ((((MRuntime3_orders_index as any).metadata) as any))?.title, titleKey: ((((MRuntime3_orders_index as any).metadata) as any))?.pageTitleKey ?? ((((MRuntime3_orders_index as any).metadata) as any))?.titleKey, group: ((((MRuntime3_orders_index as any).metadata) as any))?.pageGroup ?? ((((MRuntime3_orders_index as any).metadata) as any))?.group, groupKey: ((((MRuntime3_orders_index as any).metadata) as any))?.pageGroupKey ?? ((((MRuntime3_orders_index as any).metadata) as any))?.groupKey, icon: ((((MRuntime3_orders_index as any).metadata) as any))?.icon, order: ((((MRuntime3_orders_index as any).metadata) as any))?.pageOrder ?? ((((MRuntime3_orders_index as any).metadata) as any))?.order, priority: ((((MRuntime3_orders_index as any).metadata) as any))?.pagePriority ?? ((((MRuntime3_orders_index as any).metadata) as any))?.priority, navHidden: ((((MRuntime3_orders_index as any).metadata) as any))?.navHidden, visible: ((((MRuntime3_orders_index as any).metadata) as any))?.visible, enabled: ((((MRuntime3_orders_index as any).metadata) as any))?.enabled, breadcrumb: ((((MRuntime3_orders_index as any).metadata) as any))?.breadcrumb, pageContext: ((((MRuntime3_orders_index as any).metadata) as any))?.pageContext, placement: ((((MRuntime3_orders_index as any).metadata) as any))?.placement, Component: async (props: any) => { const mod = await import("@open-mercato/core/modules/orders/frontend/page"); const Component = (mod.default ?? mod) as any; return createElement(Component, props) } }
294
+ ],
295
+ backendRoutes: [
296
+ { pattern: "/backend/orders", requireAuth: ((((BMRuntime7_orders_index as any).metadata) as any))?.requireAuth, requireRoles: ((((BMRuntime7_orders_index as any).metadata) as any))?.requireRoles, requireFeatures: ((((BMRuntime7_orders_index as any).metadata) as any))?.requireFeatures, requireCustomerAuth: ((((BMRuntime7_orders_index as any).metadata) as any))?.requireCustomerAuth, requireCustomerFeatures: ((((BMRuntime7_orders_index as any).metadata) as any))?.requireCustomerFeatures, nav: ((((BMRuntime7_orders_index as any).metadata) as any))?.nav, title: ((((BMRuntime7_orders_index as any).metadata) as any))?.pageTitle ?? ((((BMRuntime7_orders_index as any).metadata) as any))?.title, titleKey: ((((BMRuntime7_orders_index as any).metadata) as any))?.pageTitleKey ?? ((((BMRuntime7_orders_index as any).metadata) as any))?.titleKey, group: ((((BMRuntime7_orders_index as any).metadata) as any))?.pageGroup ?? ((((BMRuntime7_orders_index as any).metadata) as any))?.group, groupKey: ((((BMRuntime7_orders_index as any).metadata) as any))?.pageGroupKey ?? ((((BMRuntime7_orders_index as any).metadata) as any))?.groupKey, icon: ((((BMRuntime7_orders_index as any).metadata) as any))?.icon, order: ((((BMRuntime7_orders_index as any).metadata) as any))?.pageOrder ?? ((((BMRuntime7_orders_index as any).metadata) as any))?.order, priority: ((((BMRuntime7_orders_index as any).metadata) as any))?.pagePriority ?? ((((BMRuntime7_orders_index as any).metadata) as any))?.priority, navHidden: ((((BMRuntime7_orders_index as any).metadata) as any))?.navHidden, visible: ((((BMRuntime7_orders_index as any).metadata) as any))?.visible, enabled: ((((BMRuntime7_orders_index as any).metadata) as any))?.enabled, breadcrumb: ((((BMRuntime7_orders_index as any).metadata) as any))?.breadcrumb, pageContext: ((((BMRuntime7_orders_index as any).metadata) as any))?.pageContext, placement: ((((BMRuntime7_orders_index as any).metadata) as any))?.placement, Component: async (props: any) => { const mod = await import("@open-mercato/core/modules/orders/backend/page"); const Component = (mod.default ?? mod) as any; return createElement(Component, props) } }
297
+ ],
298
+ apis: [
299
+ { path: "/orders/orders", metadata: undefined, handlers: { GET: async (req: Request, ctx?: any) => { const mod = await import("@open-mercato/core/modules/orders/api/orders/route"); return (mod as any).GET(req, ctx) } } }
300
+ ],
301
+ customFieldSets: []
305
302
  }
306
- ]
307
- export const modulesInfo = modules.map(m => ({ id: m.id, ...(m.info || {}) }))
308
- export default modules
303
+ ];
304
+
305
+ export default modules;
306
+
307
+ export const modulesInfo = modules.map((m) => ({
308
+ id: m.id,
309
+ ...m.info || {}
310
+ }));
309
311
  `))
310
312
  expect(readGenerated(tmpDir, 'frontend-routes.generated.ts')).toBe(dedent(`
311
313
  // AUTO-GENERATED by mercato generate registry
312
- import { resolvePageRouteMetadata, type FrontendRouteManifestEntry } from '@open-mercato/shared/modules/registry'
314
+ import { resolvePageRouteMetadata, type FrontendRouteManifestEntry } from "@open-mercato/shared/modules/registry";
313
315
 
314
316
  export const frontendRoutes: FrontendRouteManifestEntry[] = [
315
- { moduleId: "orders", ...resolvePageRouteMetadata("/", (undefined as any)), load: async () => { const mod = await import("@open-mercato/core/modules/orders/frontend/page"); return (mod.default ?? mod) as any } }
316
- ]
317
+ {
318
+ moduleId: "orders",
319
+ ...resolvePageRouteMetadata("/", (undefined as any)),
320
+ load: async () => {
321
+ const mod = await import("@open-mercato/core/modules/orders/frontend/page");
322
+ return ((mod.default ?? mod) as any);
323
+ }
324
+ }
325
+ ];
317
326
 
318
- export default frontendRoutes
327
+ export default frontendRoutes;
319
328
  `))
320
329
  expect(readGenerated(tmpDir, 'backend-routes.generated.ts')).toBe(dedent(`
321
330
  // AUTO-GENERATED by mercato generate registry
322
- import { resolvePageRouteMetadata, type BackendRouteManifestEntry } from '@open-mercato/shared/modules/registry'
331
+ import { resolvePageRouteMetadata, type BackendRouteManifestEntry } from "@open-mercato/shared/modules/registry";
323
332
 
324
333
  export const backendRoutes: BackendRouteManifestEntry[] = [
325
- { moduleId: "orders", ...resolvePageRouteMetadata("/backend/orders", (undefined as any)), load: async () => { const mod = await import("@open-mercato/core/modules/orders/backend/page"); return (mod.default ?? mod) as any } }
326
- ]
334
+ {
335
+ moduleId: "orders",
336
+ ...resolvePageRouteMetadata("/backend/orders", (undefined as any)),
337
+ load: async () => {
338
+ const mod = await import("@open-mercato/core/modules/orders/backend/page");
339
+ return ((mod.default ?? mod) as any);
340
+ }
341
+ }
342
+ ];
327
343
 
328
- export default backendRoutes
344
+ export default backendRoutes;
329
345
  `))
330
346
  expect(readGenerated(tmpDir, 'api-routes.generated.ts')).toBe(dedent(`
331
347
  // AUTO-GENERATED by mercato generate registry
332
- import type { ApiRouteManifestEntry } from '@open-mercato/shared/modules/registry'
348
+ import type { ApiRouteManifestEntry } from "@open-mercato/shared/modules/registry";
333
349
 
334
350
  export const apiRoutes: ApiRouteManifestEntry[] = [
335
- { moduleId: "orders", kind: "route-file", path: "/orders/orders", methods: ["GET"], load: async () => import("@open-mercato/core/modules/orders/api/orders/route") }
336
- ]
351
+ {
352
+ moduleId: "orders",
353
+ kind: "route-file",
354
+ path: "/orders/orders",
355
+ methods: [
356
+ "GET"
357
+ ],
358
+ load: async () => import("@open-mercato/core/modules/orders/api/orders/route")
359
+ }
360
+ ];
337
361
 
338
- export default apiRoutes
362
+ export default apiRoutes;
339
363
  `))
340
364
  })
341
365
 
@@ -575,7 +599,7 @@ export { POST, PUT, DELETE }
575
599
  expect(result.errors).toEqual([])
576
600
  const apiRoutes = readGenerated(tmpDir, 'api-routes.generated.ts')!
577
601
  expect(apiRoutes).toContain('path: "/customers/people"')
578
- expect(apiRoutes).toContain('methods: ["GET", "POST", "PUT", "DELETE"]')
602
+ expect(apiRoutes).toMatch(/methods:\s*\[[\s\S]*?"GET",[\s\S]*?"POST",[\s\S]*?"PUT",[\s\S]*?"DELETE"[\s\S]*?\]/)
579
603
  })
580
604
 
581
605
  it('partitions API and backend loader graphs while preserving legacy manifests', async () => {
@@ -660,7 +684,7 @@ export const { metadata, GET, POST, PUT, DELETE } = crud
660
684
  expect(result.errors).toEqual([])
661
685
  const apiRoutes = readGenerated(tmpDir, 'api-routes.generated.ts')!
662
686
  expect(apiRoutes).toContain('path: "/example/todos"')
663
- expect(apiRoutes).toContain('methods: ["GET", "POST", "PUT", "DELETE"]')
687
+ expect(apiRoutes).toMatch(/methods:\s*\[[\s\S]*?"GET",[\s\S]*?"POST",[\s\S]*?"PUT",[\s\S]*?"DELETE"[\s\S]*?\]/)
664
688
  })
665
689
 
666
690
  it('uses metadata.path in api route manifests when source import fails during generation', async () => {
@@ -1047,7 +1071,7 @@ export function GET() {
1047
1071
  )
1048
1072
 
1049
1073
  expect(output).toContain('@open-mercato/core/modules/customers/i18n/en.json')
1050
- expect(output).toContain(`'en':`)
1074
+ expect(output).toMatch(/['"]en['"]:/)
1051
1075
  })
1052
1076
  })
1053
1077
 
@@ -832,7 +832,7 @@ describe('generator output compatibility', () => {
832
832
  expect(content).toContain(`import { backendRouteFacades } from "./backend-route-shards.generated"`)
833
833
  expect(content).toContain(`import { frontendRoutes } from "./frontend-routes.generated"`)
834
834
  expect(content).toContain(
835
- `import { registerBackendRouteManifests, registerFrontendRouteManifests } from '@open-mercato/shared/modules/registry'`,
835
+ `import { registerBackendRouteManifests, registerFrontendRouteManifests } from "@open-mercato/shared/modules/registry"`,
836
836
  )
837
837
  expect(content).toMatch(/export function runBootstrapRegistrations\(\): void \{[\s\S]*registerBackendRouteManifests\(backendRouteFacades\)[\s\S]*\}/)
838
838
  expect(content).toMatch(/export function runBootstrapRegistrations\(\): void \{[\s\S]*registerFrontendRouteManifests\(frontendRoutes\)[\s\S]*\}/)