@open-mercato/cli 0.6.8-develop.7099.1.7ba2771d06 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/.turbo/turbo-build.log +1 -7
  2. package/dist/agentic/guides/module-facts.json +598 -5508
  3. package/dist/agentic/guides/module-facts.v2.json +598 -5594
  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/exact-override-targets.md +0 -1
  7. package/dist/agentic/guides/modules/api_keys/index.md +2 -4
  8. package/dist/agentic/guides/modules/attachments/di-registrations-rich.md +5 -6
  9. package/dist/agentic/guides/modules/attachments/di-service-tokens.md +4 -5
  10. package/dist/agentic/guides/modules/attachments/exact-override-targets.md +5 -6
  11. package/dist/agentic/guides/modules/attachments/index.md +2 -2
  12. package/dist/agentic/guides/modules/audit_logs/index.md +1 -1
  13. package/dist/agentic/guides/modules/auth/di-registrations-rich.md +2 -3
  14. package/dist/agentic/guides/modules/auth/di-service-tokens.md +2 -3
  15. package/dist/agentic/guides/modules/auth/exact-override-targets.md +2 -3
  16. package/dist/agentic/guides/modules/auth/index.md +1 -1
  17. package/dist/agentic/guides/modules/business_rules/index.md +1 -1
  18. package/dist/agentic/guides/modules/catalog/incoming-installed-contributions.md +0 -3
  19. package/dist/agentic/guides/modules/catalog/index.md +2 -2
  20. package/dist/agentic/guides/modules/channel_apns/index.md +1 -1
  21. package/dist/agentic/guides/modules/channel_expo/index.md +1 -1
  22. package/dist/agentic/guides/modules/channel_fcm/index.md +1 -1
  23. package/dist/agentic/guides/modules/channel_gmail/index.md +1 -1
  24. package/dist/agentic/guides/modules/channel_imap/index.md +1 -1
  25. package/dist/agentic/guides/modules/checkout/index.md +1 -1
  26. package/dist/agentic/guides/modules/communication_channels/di-registrations-rich.md +8 -9
  27. package/dist/agentic/guides/modules/communication_channels/exact-override-targets.md +8 -9
  28. package/dist/agentic/guides/modules/communication_channels/incoming-installed-contributions.md +0 -2
  29. package/dist/agentic/guides/modules/communication_channels/index.md +3 -3
  30. package/dist/agentic/guides/modules/configs/index.md +1 -1
  31. package/dist/agentic/guides/modules/content/index.md +1 -1
  32. package/dist/agentic/guides/modules/currencies/index.md +1 -1
  33. package/dist/agentic/guides/modules/customer_accounts/index.md +1 -1
  34. package/dist/agentic/guides/modules/customers/active-extension-bindings.md +3 -6
  35. package/dist/agentic/guides/modules/customers/contribution-resolutions.md +0 -1
  36. package/dist/agentic/guides/modules/customers/events.md +1 -2
  37. package/dist/agentic/guides/modules/customers/incoming-installed-contributions.md +0 -7
  38. package/dist/agentic/guides/modules/customers/index.md +4 -4
  39. package/dist/agentic/guides/modules/customers/umes-contributions.md +0 -1
  40. package/dist/agentic/guides/modules/customers/umes-hosts.md +0 -1
  41. package/dist/agentic/guides/modules/dashboards/index.md +1 -1
  42. package/dist/agentic/guides/modules/data_sync/exact-override-targets.md +2 -2
  43. package/dist/agentic/guides/modules/data_sync/index.md +1 -1
  44. package/dist/agentic/guides/modules/data_sync/workers.md +2 -2
  45. package/dist/agentic/guides/modules/design_system/index.md +1 -1
  46. package/dist/agentic/guides/modules/devices/index.md +1 -1
  47. package/dist/agentic/guides/modules/dictionaries/index.md +1 -1
  48. package/dist/agentic/guides/modules/directory/exact-override-targets.md +0 -2
  49. package/dist/agentic/guides/modules/directory/index.md +3 -5
  50. package/dist/agentic/guides/modules/entities/index.md +1 -1
  51. package/dist/agentic/guides/modules/eudr/index.md +1 -1
  52. package/dist/agentic/guides/modules/events/index.md +1 -1
  53. package/dist/agentic/guides/modules/feature_toggles/index.md +1 -1
  54. package/dist/agentic/guides/modules/gateway_stripe/exact-override-targets.md +1 -1
  55. package/dist/agentic/guides/modules/gateway_stripe/index.md +1 -1
  56. package/dist/agentic/guides/modules/gateway_stripe/workers.md +1 -1
  57. package/dist/agentic/guides/modules/generators/index.md +1 -1
  58. package/dist/agentic/guides/modules/inbox_ops/index.md +1 -1
  59. package/dist/agentic/guides/modules/integrations/index.md +1 -1
  60. package/dist/agentic/guides/modules/messages/index.md +1 -1
  61. package/dist/agentic/guides/modules/notifications/index.md +1 -1
  62. package/dist/agentic/guides/modules/onboarding/index.md +1 -1
  63. package/dist/agentic/guides/modules/payment_gateways/index.md +1 -1
  64. package/dist/agentic/guides/modules/perspectives/index.md +1 -1
  65. package/dist/agentic/guides/modules/planner/index.md +1 -1
  66. package/dist/agentic/guides/modules/portal/index.md +1 -1
  67. package/dist/agentic/guides/modules/progress/index.md +1 -1
  68. package/dist/agentic/guides/modules/push_notifications/index.md +1 -1
  69. package/dist/agentic/guides/modules/query_index/index.md +1 -1
  70. package/dist/agentic/guides/modules/record_locks/index.md +1 -1
  71. package/dist/agentic/guides/modules/resources/index.md +1 -1
  72. package/dist/agentic/guides/modules/sales/active-extension-bindings.md +0 -1
  73. package/dist/agentic/guides/modules/sales/contribution-resolutions.md +0 -1
  74. package/dist/agentic/guides/modules/sales/exact-override-targets.md +4 -5
  75. package/dist/agentic/guides/modules/sales/host-extension-points.md +1 -1
  76. package/dist/agentic/guides/modules/sales/incoming-installed-contributions.md +0 -2
  77. package/dist/agentic/guides/modules/sales/index.md +3 -3
  78. package/dist/agentic/guides/modules/sales/setup.md +1 -1
  79. package/dist/agentic/guides/modules/sales/umes-contributions.md +0 -1
  80. package/dist/agentic/guides/modules/sales/umes-diagnostics.md +0 -1
  81. package/dist/agentic/guides/modules/sales/umes-hosts.md +0 -9
  82. package/dist/agentic/guides/modules/scheduler/cli-commands.md +0 -1
  83. package/dist/agentic/guides/modules/scheduler/exact-override-targets.md +1 -3
  84. package/dist/agentic/guides/modules/scheduler/index.md +2 -2
  85. package/dist/agentic/guides/modules/scheduler/workers.md +1 -2
  86. package/dist/agentic/guides/modules/search/index.md +1 -1
  87. package/dist/agentic/guides/modules/security/index.md +1 -1
  88. package/dist/agentic/guides/modules/security/owned-contract-module-metadata.md +1 -1
  89. package/dist/agentic/guides/modules/shipping_carriers/index.md +1 -1
  90. package/dist/agentic/guides/modules/sso/index.md +1 -1
  91. package/dist/agentic/guides/modules/staff/index.md +1 -1
  92. package/dist/agentic/guides/modules/storage_s3/index.md +1 -1
  93. package/dist/agentic/guides/modules/sync_akeneo/index.md +1 -1
  94. package/dist/agentic/guides/modules/sync_excel/index.md +1 -1
  95. package/dist/agentic/guides/modules/system_status_overlays/index.md +1 -1
  96. package/dist/agentic/guides/modules/translations/index.md +1 -1
  97. package/dist/agentic/guides/modules/warranty_claims/index.md +1 -1
  98. package/dist/agentic/guides/modules/webhooks/index.md +1 -1
  99. package/dist/agentic/guides/modules/wms/index.md +1 -1
  100. package/dist/agentic/guides/modules/workflows/index.md +1 -1
  101. package/dist/agentic/guides/reference-module-facts.json +2 -2
  102. package/dist/agentic/guides/reference-modules/example/index.md +1 -1
  103. package/dist/agentic/guides/upstream/manifest.json +1 -1
  104. package/dist/agentic/shared/AGENTS.md.template +3 -3
  105. package/dist/agentic/shared/ai/harness/README.md +2 -2
  106. package/dist/agentic/shared/ai/harness/RELEASE.md +6 -8
  107. package/dist/agentic/shared/ai/harness/cases.json +0 -83
  108. package/dist/agentic/shared/ai/harness/cases.schema.json +4 -4
  109. package/dist/agentic/shared/ai/harness/validators.json +1 -1
  110. package/dist/agentic/shared/ai/skills/om-auto-upgrade-0.6.7-to-0.7.0/SKILL.md +12 -3
  111. package/dist/agentic/shared/scripts/run-agent-harness-release.mjs +11 -37
  112. package/dist/lib/generators/ast/index.js +1 -4
  113. package/dist/lib/generators/ast/index.js.map +2 -2
  114. package/dist/lib/generators/ast/source-file.js +1 -8
  115. package/dist/lib/generators/ast/source-file.js.map +2 -2
  116. package/dist/lib/generators/ast/writers.js +1 -9
  117. package/dist/lib/generators/ast/writers.js.map +2 -2
  118. package/dist/lib/generators/module-registry.js +189 -424
  119. package/dist/lib/generators/module-registry.js.map +2 -2
  120. package/dist/lib/generators/route-manifest-shards.js +1 -4
  121. package/dist/lib/generators/route-manifest-shards.js.map +2 -2
  122. package/dist/lib/spawn.js +2 -3
  123. package/dist/lib/spawn.js.map +2 -2
  124. package/dist/lib/testing/integration.js +24 -162
  125. package/dist/lib/testing/integration.js.map +2 -2
  126. package/dist/mercato.js +0 -14
  127. package/dist/mercato.js.map +2 -2
  128. package/package.json +7 -8
  129. package/src/lib/__tests__/spawn.test.ts +0 -23
  130. package/src/lib/generators/__tests__/metadata-extraction.test.ts +0 -44
  131. package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +8 -32
  132. package/src/lib/generators/__tests__/module-facts.customers.fixture.test.ts +1 -4
  133. package/src/lib/generators/__tests__/module-facts.extension-hosts.test.ts +1 -1
  134. package/src/lib/generators/__tests__/module-subset.test.ts +72 -96
  135. package/src/lib/generators/__tests__/output-snapshots.test.ts +1 -1
  136. package/src/lib/generators/__tests__/route-manifest-shards.test.ts +0 -15
  137. package/src/lib/generators/__tests__/structural-contracts.test.ts +3 -7
  138. package/src/lib/generators/ast/index.ts +1 -2
  139. package/src/lib/generators/ast/source-file.ts +0 -13
  140. package/src/lib/generators/ast/writers.ts +1 -26
  141. package/src/lib/generators/module-registry.ts +227 -534
  142. package/src/lib/generators/route-manifest-shards.ts +1 -8
  143. package/src/lib/spawn.ts +4 -5
  144. package/src/lib/testing/__tests__/integration.test.ts +0 -435
  145. package/src/lib/testing/integration.ts +32 -254
  146. package/src/mercato.ts +0 -27
  147. package/dist/agentic/guides/modules/api_keys/di-registrations-rich.md +0 -11
  148. package/dist/agentic/guides/modules/api_keys/di-service-tokens.md +0 -11
  149. package/dist/agentic/guides/modules/channel_discord/acl-features.md +0 -13
  150. package/dist/agentic/guides/modules/channel_discord/ai-agents.md +0 -11
  151. package/dist/agentic/guides/modules/channel_discord/backend-pages.md +0 -11
  152. package/dist/agentic/guides/modules/channel_discord/cli-commands.md +0 -13
  153. package/dist/agentic/guides/modules/channel_discord/contribution-resolutions.md +0 -14
  154. package/dist/agentic/guides/modules/channel_discord/di-registrations-rich.md +0 -12
  155. package/dist/agentic/guides/modules/channel_discord/exact-override-targets.md +0 -26
  156. package/dist/agentic/guides/modules/channel_discord/index.md +0 -22
  157. package/dist/agentic/guides/modules/channel_discord/owned-contract-module-metadata.md +0 -11
  158. package/dist/agentic/guides/modules/channel_discord/setup.md +0 -11
  159. package/dist/agentic/guides/modules/channel_discord/umes-contributions.md +0 -14
  160. package/dist/agentic/guides/modules/channel_discord/workers.md +0 -12
  161. package/dist/agentic/guides/modules/directory/di-registrations-rich.md +0 -12
  162. package/dist/agentic/guides/modules/directory/di-service-tokens.md +0 -12
  163. package/dist/agentic/guides/modules/documents/acl-features.md +0 -17
  164. package/dist/agentic/guides/modules/documents/backend-pages.md +0 -13
  165. package/dist/agentic/guides/modules/documents/contribution-resolutions.md +0 -27
  166. package/dist/agentic/guides/modules/documents/di-registrations-rich.md +0 -21
  167. package/dist/agentic/guides/modules/documents/domain-commands.md +0 -37
  168. package/dist/agentic/guides/modules/documents/encryption.md +0 -11
  169. package/dist/agentic/guides/modules/documents/entities.md +0 -21
  170. package/dist/agentic/guides/modules/documents/events.md +0 -25
  171. package/dist/agentic/guides/modules/documents/exact-override-targets.md +0 -39
  172. package/dist/agentic/guides/modules/documents/index.md +0 -25
  173. package/dist/agentic/guides/modules/documents/notifications.md +0 -13
  174. package/dist/agentic/guides/modules/documents/owned-contract-module-metadata.md +0 -11
  175. package/dist/agentic/guides/modules/documents/setup.md +0 -11
  176. package/dist/agentic/guides/modules/documents/umes-contributions.md +0 -27
  177. package/dist/agentic/guides/modules/documents/umes-hosts.md +0 -36
  178. package/src/__tests__/resolve-queue-abandon-hook.test.ts +0 -39
  179. package/src/lib/generators/__tests__/__snapshots__/bootstrap-registrations.test.ts.snap +0 -14
  180. package/src/lib/generators/__tests__/__snapshots__/command-loaders.test.ts.snap +0 -23
  181. package/src/lib/generators/__tests__/ast-writers-escaping.test.ts +0 -64
  182. package/src/lib/generators/__tests__/bootstrap-registrations.test.ts +0 -176
  183. package/src/lib/generators/__tests__/command-loaders.test.ts +0 -239
  184. package/src/lib/generators/__tests__/generated-registry-imports.test.ts +0 -128
  185. package/src/lib/generators/__tests__/registry-variant-parity.test.ts +0 -258
@@ -32,7 +32,6 @@ import {
32
32
  conditionalExpression,
33
33
  createGeneratedSourceFile,
34
34
  elementAccess,
35
- escapeGeneratedJsonLiteral,
36
35
  returnStatement,
37
36
  getSourceText,
38
37
  identifier,
@@ -45,7 +44,6 @@ import {
45
44
  optionalPropertyAccess,
46
45
  parenthesized,
47
46
  propertyAccess,
48
- renderGeneratedValue,
49
47
  spreadExpression,
50
48
  type GeneratedObjectEntry,
51
49
  variableStatement,
@@ -135,7 +133,7 @@ type DashboardWidgetEntry = {
135
133
  importPath: string
136
134
  }
137
135
 
138
- export type CommandLoaderGenerationEntry = {
136
+ type CommandLoaderGenerationEntry = {
139
137
  moduleId: string
140
138
  key: string
141
139
  importPath: string
@@ -161,7 +159,7 @@ type RuntimeApiMethodMetadata = {
161
159
  type PageRouteGenerationResult = {
162
160
  eagerRoutes: string[]
163
161
  runtimeRoutes: string[]
164
- manifestRoutes: WriterFunction[]
162
+ manifestRoutes: string[]
165
163
  manifestShardRoutes: RouteManifestShardEntry[]
166
164
  metadataRoutes: RouteManifestShardEntry[]
167
165
  routePatterns: string[]
@@ -192,7 +190,7 @@ function assertUniqueBackendRoutePattern(
192
190
  type ApiRouteGenerationResult = {
193
191
  eagerApis: string[]
194
192
  runtimeApis: string[]
195
- manifestApis: WriterFunction[]
193
+ manifestApis: string[]
196
194
  manifestShardApis: RouteManifestShardEntry[]
197
195
  metadataApis: RouteManifestShardEntry[]
198
196
  }
@@ -246,19 +244,8 @@ type SerializableWorkerMetadata = {
246
244
  id?: string
247
245
  queue?: string
248
246
  concurrency?: number
249
- schedulerSafe?: boolean
250
- schedulerRequiredFeatures?: string[]
251
247
  lockDuration?: number
252
248
  maxStalledCount?: number
253
- /**
254
- * Whether the worker's metadata declares an `onJobAbandoned` callback.
255
- *
256
- * A function cannot be serialized into the registry the way the scalar options are, so the flag is
257
- * resolved here at build time and the callback itself is emitted as a lazy import beside the
258
- * handler. Recorded only when the source module could actually be loaded — the object-literal
259
- * fallback below cannot see whether a referenced identifier is a function.
260
- */
261
- hasJobAbandonedHook?: boolean
262
249
  }
263
250
 
264
251
  type PageMetadataManifestLoadResult = {
@@ -590,55 +577,6 @@ function extractObjectPropertiesFromAst(sourceFile: string, exportName: string):
590
577
  return Object.keys(result).length > 0 ? result : null
591
578
  }
592
579
 
593
- /**
594
- * Whether an exported object literal declares a property, regardless of what it evaluates to.
595
- *
596
- * Needed for callbacks: the value resolvers above can read literals and local constants, but a
597
- * property whose value is an imported function stays unresolvable, so "did the author declare it"
598
- * cannot be answered by inspecting the extracted value. Answering it from the syntax instead is what
599
- * lets the generator emit a lazy accessor for a hook it can see but cannot serialize.
600
- */
601
- export function namedObjectLiteralDeclaresProperty(sourceFile: string, exportName: string, propertyName: string): boolean {
602
- let source = ''
603
- try {
604
- source = fs.readFileSync(sourceFile, 'utf8')
605
- } catch {
606
- return false
607
- }
608
-
609
- const parsed = ts.createSourceFile(sourceFile, source, ts.ScriptTarget.Latest, true, inferScriptKind(sourceFile))
610
- const exportedNames = new Set<string>()
611
- for (const statement of parsed.statements) {
612
- if (
613
- ts.isExportDeclaration(statement)
614
- && statement.exportClause
615
- && ts.isNamedExports(statement.exportClause)
616
- && !statement.moduleSpecifier
617
- ) {
618
- for (const element of statement.exportClause.elements) exportedNames.add(element.name.text)
619
- }
620
- }
621
-
622
- for (const statement of parsed.statements) {
623
- if (!ts.isVariableStatement(statement)) continue
624
- const exportsDirectly = (ts.canHaveModifiers(statement) ? ts.getModifiers(statement) : undefined)
625
- ?.some((modifier: ts.Modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)
626
- for (const declaration of statement.declarationList.declarations) {
627
- if (!ts.isIdentifier(declaration.name) || declaration.name.text !== exportName) continue
628
- if (!exportsDirectly && !exportedNames.has(exportName)) continue
629
- const objectLiteral = unwrapObjectLiteralExpression(declaration.initializer)
630
- if (!objectLiteral) return false
631
- return objectLiteral.properties.some((property) => {
632
- const name = property.name
633
- if (!name) return false
634
- const key = ts.isIdentifier(name) ? name.text : ts.isStringLiteral(name) ? name.text : null
635
- return key === propertyName
636
- })
637
- }
638
- }
639
- return false
640
- }
641
-
642
580
  export function extractNamedObjectLiteralExport(sourceFile: string, exportName: string): Record<string, unknown> | null {
643
581
  const literal = extractNamedObjectLiteralSource(sourceFile, exportName)
644
582
  if (literal) {
@@ -1198,7 +1136,11 @@ function requiresRuntimePageMetadataFromSourceFile(sourceFile: string): boolean
1198
1136
  }
1199
1137
 
1200
1138
  function toLiteral(value: unknown): string {
1201
- return escapeGeneratedJsonLiteral(JSON.stringify(value))
1139
+ return JSON.stringify(value)
1140
+ .replace(/</g, '\\u003c')
1141
+ .replace(/>/g, '\\u003e')
1142
+ .replace(/\u2028/g, '\\u2028')
1143
+ .replace(/\u2029/g, '\\u2029')
1202
1144
  }
1203
1145
 
1204
1146
  const GENERATED_MODULE_RELATIVE_PREFIXES = ['@/', '../../src/modules/', './'] as const
@@ -1440,81 +1382,32 @@ function collectCommandLoaderEntries(
1440
1382
  return entries
1441
1383
  }
1442
1384
 
1443
- export function renderCommandLoadersFile(entries: CommandLoaderGenerationEntry[]): string {
1385
+ function renderCommandLoadersFile(entries: CommandLoaderGenerationEntry[]): string {
1444
1386
  const seenCommandIds = new Map<string, string>()
1445
- const loaderEntries: WriterFunction[] = []
1387
+ const rendered: string[] = []
1446
1388
 
1447
1389
  for (const entry of entries) {
1448
- const loadValue = arrowFunction({
1449
- body: importExpression(sanitizeGeneratedModuleSpecifier(entry.importPath)),
1450
- })
1390
+ const loadExpr = `() => ${buildDynamicImportExpression(entry.importPath)}`
1451
1391
  for (const id of entry.ids) {
1452
1392
  const previous = seenCommandIds.get(id)
1453
1393
  if (previous && previous !== entry.key) {
1454
1394
  throw new Error(`[generate] Duplicate command id "${id}" discovered in "${previous}" and "${entry.key}"`)
1455
1395
  }
1456
1396
  seenCommandIds.set(id, entry.key)
1457
- loaderEntries.push(objectLiteral([
1458
- { name: 'moduleId', value: entry.moduleId },
1459
- { name: 'id', value: id },
1460
- { name: 'key', value: entry.key },
1461
- { name: 'load', value: loadValue },
1462
- ]))
1397
+ rendered.push(` { moduleId: ${toLiteral(entry.moduleId)}, id: ${toLiteral(id)}, key: ${toLiteral(entry.key)}, load: ${loadExpr} },`)
1463
1398
  }
1464
- loaderEntries.push(objectLiteral([
1465
- { name: 'moduleId', value: entry.moduleId },
1466
- { name: 'key', value: entry.key },
1467
- { name: 'load', value: loadValue },
1468
- ]))
1399
+ rendered.push(` { moduleId: ${toLiteral(entry.moduleId)}, key: ${toLiteral(entry.key)}, load: ${loadExpr} },`)
1469
1400
  }
1470
1401
 
1471
- const sourceFile = createGeneratedSourceFile('command-loaders.generated.ts')
1472
- addAutoGeneratedComment(sourceFile, 'command-loaders')
1473
-
1474
- addImportSpec(sourceFile, {
1475
- moduleSpecifier: '@open-mercato/shared/lib/commands',
1476
- isTypeOnly: true,
1477
- namedImports: [{ name: 'CommandLoader' }],
1478
- })
1402
+ return `// AUTO-GENERATED by mercato generate command-loaders
1403
+ import type { CommandLoader } from '@open-mercato/shared/lib/commands'
1479
1404
 
1480
- sourceFile.addVariableStatement({
1481
- declarationKind: VariableDeclarationKind.Const,
1482
- isExported: true,
1483
- declarations: [
1484
- {
1485
- name: 'commandLoaderEntries',
1486
- type: 'CommandLoader[]',
1487
- initializer: arrayLiteral(loaderEntries, writeValue),
1488
- },
1489
- ],
1490
- })
1491
- sourceFile.addExportAssignment({
1492
- isExportEquals: false,
1493
- expression: 'commandLoaderEntries',
1494
- })
1405
+ export const commandLoaderEntries: CommandLoader[] = [
1406
+ ${rendered.join('\n')}
1407
+ ]
1495
1408
 
1496
- return getSourceText(sourceFile)
1497
- }
1498
-
1499
- export function renderBootstrapRegistrationsFile(options: {
1500
- entryImports: string[]
1501
- registrationImports: string[]
1502
- calls: string[]
1503
- }): string {
1504
- const uniqueImports = [...new Set([...options.entryImports, ...options.registrationImports])]
1505
-
1506
- const sourceFile = createGeneratedSourceFile('bootstrap-registrations.generated.ts')
1507
- addAutoGeneratedComment(sourceFile, 'registry')
1508
- addImportStatements(sourceFile, uniqueImports)
1509
-
1510
- sourceFile.addFunction({
1511
- name: 'runBootstrapRegistrations',
1512
- isExported: true,
1513
- returnType: 'void',
1514
- statements: options.calls,
1515
- })
1516
-
1517
- return getSourceText(sourceFile)
1409
+ export default commandLoaderEntries
1410
+ `
1518
1411
  }
1519
1412
 
1520
1413
  function serializeGeneratedImport(statement: GeneratedImportStatement): string {
@@ -1770,38 +1663,8 @@ function buildPageRouteProps(metaExpr: string, routePath: string): string {
1770
1663
  return `pattern: ${toLiteral(routePath || '/')}, requireAuth: (${metaExpr})?.requireAuth, requireRoles: (${metaExpr})?.requireRoles, requireFeatures: (${metaExpr})?.requireFeatures, requireCustomerAuth: (${metaExpr})?.requireCustomerAuth, requireCustomerFeatures: (${metaExpr})?.requireCustomerFeatures, nav: (${metaExpr})?.nav, title: (${metaExpr})?.pageTitle ?? (${metaExpr})?.title, titleKey: (${metaExpr})?.pageTitleKey ?? (${metaExpr})?.titleKey, group: (${metaExpr})?.pageGroup ?? (${metaExpr})?.group, groupKey: (${metaExpr})?.pageGroupKey ?? (${metaExpr})?.groupKey, icon: (${metaExpr})?.icon, order: (${metaExpr})?.pageOrder ?? (${metaExpr})?.order, priority: (${metaExpr})?.pagePriority ?? (${metaExpr})?.priority, navHidden: (${metaExpr})?.navHidden, visible: (${metaExpr})?.visible, enabled: (${metaExpr})?.enabled, breadcrumb: (${metaExpr})?.breadcrumb, pageContext: (${metaExpr})?.pageContext, placement: (${metaExpr})?.placement`
1771
1664
  }
1772
1665
 
1773
- function buildPageRouteManifestSpread(metaExpr: string, routePath: string): WriterFunction {
1774
- return callExpression(identifier('resolvePageRouteMetadata'), [routePath || '/', identifier(metaExpr)])
1775
- }
1776
-
1777
- function buildManifestRouteLoader(importPath: string): WriterFunction {
1778
- return arrowFunction({
1779
- async: true,
1780
- body: block((writer) => {
1781
- writeStatements(writer, [
1782
- variableStatement({
1783
- name: 'mod',
1784
- initializer: awaitExpression(importExpression(sanitizeGeneratedModuleSpecifier(importPath))),
1785
- }),
1786
- returnStatement(
1787
- asExpression(
1788
- parenthesized(nullishCoalesce([
1789
- propertyAccess(identifier('mod'), 'default'),
1790
- identifier('mod'),
1791
- ])),
1792
- 'any',
1793
- ),
1794
- ),
1795
- ])
1796
- }),
1797
- })
1798
- }
1799
-
1800
- function buildManifestApiLoader(importPath: string): WriterFunction {
1801
- return arrowFunction({
1802
- async: true,
1803
- body: importExpression(sanitizeGeneratedModuleSpecifier(importPath)),
1804
- })
1666
+ function buildPageRouteManifestSpread(metaExpr: string, routePath: string): string {
1667
+ return `...resolvePageRouteMetadata(${toLiteral(routePath || '/')}, ${metaExpr})`
1805
1668
  }
1806
1669
 
1807
1670
  function normalizeBreadcrumb(raw: unknown): SerializablePageMetadata['breadcrumb'] {
@@ -1914,14 +1777,6 @@ function normalizeWorkerMetadata(raw: unknown): SerializableWorkerMetadata | nul
1914
1777
  if (typeof source.concurrency === 'number') normalized.concurrency = source.concurrency
1915
1778
  if (typeof source.lockDuration === 'number') normalized.lockDuration = source.lockDuration
1916
1779
  if (typeof source.maxStalledCount === 'number') normalized.maxStalledCount = source.maxStalledCount
1917
- if (typeof source.onJobAbandoned === 'function') normalized.hasJobAbandonedHook = true
1918
- if (source.schedulerSafe === true) normalized.schedulerSafe = true
1919
- if (Array.isArray(source.schedulerRequiredFeatures)) {
1920
- const features = source.schedulerRequiredFeatures.filter(
1921
- (feature): feature is string => typeof feature === 'string' && feature.length > 0,
1922
- )
1923
- if (features.length > 0) normalized.schedulerRequiredFeatures = features
1924
- }
1925
1780
  return Object.keys(normalized).length > 0 ? normalized : null
1926
1781
  }
1927
1782
 
@@ -1933,16 +1788,8 @@ async function loadSubscriberMetadata(sourceFile: string): Promise<SerializableS
1933
1788
 
1934
1789
  async function loadWorkerMetadata(sourceFile: string): Promise<SerializableWorkerMetadata | null> {
1935
1790
  const sourceModule = await loadModuleExportsFromSource<Record<string, unknown>>(sourceFile)
1936
- const metadata = normalizeWorkerMetadata(sourceModule?.metadata)
1791
+ return normalizeWorkerMetadata(sourceModule?.metadata)
1937
1792
  ?? normalizeWorkerMetadata(extractNamedObjectLiteralExport(sourceFile, 'metadata'))
1938
- if (!metadata) return null
1939
- // Resolved from the syntax, not the extracted value: a worker module that cannot be imported at
1940
- // build time falls back to the literal extractor, which cannot tell an imported function from an
1941
- // unresolvable identifier. Missing this is silent — the hook is simply never installed.
1942
- if (!metadata.hasJobAbandonedHook && namedObjectLiteralDeclaresProperty(sourceFile, 'metadata', 'onJobAbandoned')) {
1943
- metadata.hasJobAbandonedHook = true
1944
- }
1945
- return metadata
1946
1793
  }
1947
1794
 
1948
1795
  async function discoverSubscribers(
@@ -2127,7 +1974,7 @@ async function processPageFiles(options: {
2127
1974
  const metaPrefix = type === 'frontend' ? 'M' : 'BM'
2128
1975
  const eagerRoutes: string[] = []
2129
1976
  const runtimeRoutes: string[] = []
2130
- const manifestRoutes: WriterFunction[] = []
1977
+ const manifestRoutes: string[] = []
2131
1978
  const manifestShardRoutes: RouteManifestShardEntry[] = []
2132
1979
  const metadataRoutes: RouteManifestShardEntry[] = []
2133
1980
  const routePatterns: string[] = []
@@ -2202,25 +2049,18 @@ async function processPageFiles(options: {
2202
2049
  const manifestBaseProps = buildPageRouteManifestSpread(manifestMetaExpr, routePath)
2203
2050
  eagerRoutes.push(`{ ${baseProps}, Component: ${buildLazyRouteComponentExpression(importPath)} }`)
2204
2051
  runtimeRoutes.push(`{ ${runtimeBaseProps}, Component: ${buildLazyRouteComponentExpression(importPath)} }`)
2205
- const metadataEntries: GeneratedObjectEntry[] = [
2206
- { name: 'moduleId', value: modId },
2207
- { kind: 'spread', value: manifestBaseProps },
2208
- ]
2209
- const manifestEntry = objectLiteral([
2210
- ...metadataEntries,
2211
- { name: 'load', value: buildManifestRouteLoader(importPath) },
2212
- ])
2213
- const shardImports = manifestImportStatement ? [manifestImportStatement] : []
2214
- manifestRoutes.push(manifestEntry)
2052
+ const metadataDeclaration = `{ moduleId: ${toLiteral(modId)}, ${manifestBaseProps} }`
2053
+ const manifestDeclaration = `{ moduleId: ${toLiteral(modId)}, ${manifestBaseProps}, load: async () => { const mod = await ${buildDynamicImportExpression(importPath)}; return (mod.default ?? mod) as any } }`
2054
+ manifestRoutes.push(manifestDeclaration)
2215
2055
  manifestShardRoutes.push({
2216
2056
  path: routePath,
2217
- declaration: renderGeneratedValue(manifestEntry),
2218
- imports: shardImports,
2057
+ declaration: manifestDeclaration,
2058
+ imports: manifestImportStatement ? [manifestImportStatement] : [],
2219
2059
  })
2220
2060
  metadataRoutes.push({
2221
2061
  path: routePath,
2222
- declaration: renderGeneratedValue(objectLiteral(metadataEntries)),
2223
- imports: shardImports,
2062
+ declaration: metadataDeclaration,
2063
+ imports: manifestImportStatement ? [manifestImportStatement] : [],
2224
2064
  })
2225
2065
  routePatterns.push(routePath)
2226
2066
  }
@@ -2300,25 +2140,18 @@ async function processPageFiles(options: {
2300
2140
  const manifestBaseProps = buildPageRouteManifestSpread(manifestMetaExpr, routePath)
2301
2141
  eagerRoutes.push(`{ ${baseProps}, Component: ${buildLazyRouteComponentExpression(importPath)} }`)
2302
2142
  runtimeRoutes.push(`{ ${runtimeBaseProps}, Component: ${buildLazyRouteComponentExpression(importPath)} }`)
2303
- const metadataEntries: GeneratedObjectEntry[] = [
2304
- { name: 'moduleId', value: modId },
2305
- { kind: 'spread', value: manifestBaseProps },
2306
- ]
2307
- const manifestEntry = objectLiteral([
2308
- ...metadataEntries,
2309
- { name: 'load', value: buildManifestRouteLoader(importPath) },
2310
- ])
2311
- const shardImports = manifestImportStatement ? [manifestImportStatement] : []
2312
- manifestRoutes.push(manifestEntry)
2143
+ const metadataDeclaration = `{ moduleId: ${toLiteral(modId)}, ${manifestBaseProps} }`
2144
+ const manifestDeclaration = `{ moduleId: ${toLiteral(modId)}, ${manifestBaseProps}, load: async () => { const mod = await ${buildDynamicImportExpression(importPath)}; return (mod.default ?? mod) as any } }`
2145
+ manifestRoutes.push(manifestDeclaration)
2313
2146
  manifestShardRoutes.push({
2314
2147
  path: routePath,
2315
- declaration: renderGeneratedValue(manifestEntry),
2316
- imports: shardImports,
2148
+ declaration: manifestDeclaration,
2149
+ imports: manifestImportStatement ? [manifestImportStatement] : [],
2317
2150
  })
2318
2151
  metadataRoutes.push({
2319
2152
  path: routePath,
2320
- declaration: renderGeneratedValue(objectLiteral(metadataEntries)),
2321
- imports: shardImports,
2153
+ declaration: metadataDeclaration,
2154
+ imports: manifestImportStatement ? [manifestImportStatement] : [],
2322
2155
  })
2323
2156
  routePatterns.push(routePath)
2324
2157
  }
@@ -2350,7 +2183,7 @@ async function processApiRoutes(options: {
2350
2183
 
2351
2184
  const eagerApis: string[] = []
2352
2185
  const runtimeApis: string[] = []
2353
- const manifestApis: WriterFunction[] = []
2186
+ const manifestApis: string[] = []
2354
2187
  const manifestShardApis: RouteManifestShardEntry[] = []
2355
2188
  const metadataApis: RouteManifestShardEntry[] = []
2356
2189
 
@@ -2383,19 +2216,11 @@ async function processApiRoutes(options: {
2383
2216
  eagerImports.push(buildImportStatement(`* as ${importName}`, importPath))
2384
2217
  eagerApis.push(`{ path: ((${importName} as any).metadata?.path ?? ${toLiteral(routePath)}), metadata: (${importName} as any).metadata, handlers: ${importName} as any${docsPart} }`)
2385
2218
  runtimeApis.push(`{ path: ${toLiteral(resolvedPath)}, metadata: ${metadataLiteral}, handlers: { ${exportedMethods.map((method) => `${method}: async (req: Request, ctx?: any) => { const mod = await ${buildDynamicImportExpression(importPath)}; return (mod as any).${method}(req, ctx) }`).join(', ')} } }`)
2386
- const metadataEntries: GeneratedObjectEntry[] = [
2387
- { name: 'moduleId', value: modId },
2388
- { name: 'kind', value: 'route-file' },
2389
- { name: 'path', value: resolvedPath },
2390
- { name: 'methods', value: arrayLiteral(exportedMethods, writeValue) },
2391
- ]
2392
- const manifestEntry = objectLiteral([
2393
- ...metadataEntries,
2394
- { name: 'load', value: buildManifestApiLoader(importPath) },
2395
- ])
2396
- manifestApis.push(manifestEntry)
2397
- manifestShardApis.push({ path: resolvedPath, declaration: renderGeneratedValue(manifestEntry) })
2398
- metadataApis.push({ path: resolvedPath, declaration: renderGeneratedValue(objectLiteral(metadataEntries)) })
2219
+ const metadataDeclaration = `{ moduleId: ${toLiteral(modId)}, kind: ${toLiteral('route-file')}, path: ${toLiteral(resolvedPath)}, methods: [${exportedMethods.map((method) => toLiteral(method)).join(', ')}] }`
2220
+ const manifestDeclaration = `{ moduleId: ${toLiteral(modId)}, kind: ${toLiteral('route-file')}, path: ${toLiteral(resolvedPath)}, methods: [${exportedMethods.map((method) => toLiteral(method)).join(', ')}], load: async () => ${buildDynamicImportExpression(importPath)} }`
2221
+ manifestApis.push(manifestDeclaration)
2222
+ manifestShardApis.push({ path: resolvedPath, declaration: manifestDeclaration })
2223
+ metadataApis.push({ path: resolvedPath, declaration: metadataDeclaration })
2399
2224
  }
2400
2225
 
2401
2226
  // Single files (plain scripts, not route.*, not tests, skip method dirs)
@@ -2427,19 +2252,11 @@ async function processApiRoutes(options: {
2427
2252
  eagerImports.push(buildImportStatement(`* as ${importName}`, importPath))
2428
2253
  eagerApis.push(`{ path: ((${importName} as any).metadata?.path ?? ${toLiteral(routePath)}), metadata: (${importName} as any).metadata, handlers: ${importName} as any${docsPart} }`)
2429
2254
  runtimeApis.push(`{ path: ${toLiteral(resolvedPath)}, metadata: ${metadataLiteral}, handlers: { ${exportedMethods.map((entryMethod) => `${entryMethod}: async (req: Request, ctx?: any) => { const mod = await ${buildDynamicImportExpression(importPath)}; return (mod as any).${entryMethod}(req, ctx) }`).join(', ')} } }`)
2430
- const metadataEntries: GeneratedObjectEntry[] = [
2431
- { name: 'moduleId', value: modId },
2432
- { name: 'kind', value: 'route-file' },
2433
- { name: 'path', value: resolvedPath },
2434
- { name: 'methods', value: arrayLiteral(exportedMethods, writeValue) },
2435
- ]
2436
- const manifestEntry = objectLiteral([
2437
- ...metadataEntries,
2438
- { name: 'load', value: buildManifestApiLoader(importPath) },
2439
- ])
2440
- manifestApis.push(manifestEntry)
2441
- manifestShardApis.push({ path: resolvedPath, declaration: renderGeneratedValue(manifestEntry) })
2442
- metadataApis.push({ path: resolvedPath, declaration: renderGeneratedValue(objectLiteral(metadataEntries)) })
2255
+ const metadataDeclaration = `{ moduleId: ${toLiteral(modId)}, kind: ${toLiteral('route-file')}, path: ${toLiteral(resolvedPath)}, methods: [${exportedMethods.map((entryMethod) => toLiteral(entryMethod)).join(', ')}] }`
2256
+ const manifestDeclaration = `{ moduleId: ${toLiteral(modId)}, kind: ${toLiteral('route-file')}, path: ${toLiteral(resolvedPath)}, methods: [${exportedMethods.map((entryMethod) => toLiteral(entryMethod)).join(', ')}], load: async () => ${buildDynamicImportExpression(importPath)} }`
2257
+ manifestApis.push(manifestDeclaration)
2258
+ manifestShardApis.push({ path: resolvedPath, declaration: manifestDeclaration })
2259
+ metadataApis.push({ path: resolvedPath, declaration: metadataDeclaration })
2443
2260
  }
2444
2261
 
2445
2262
  // Legacy per-method
@@ -2481,20 +2298,11 @@ async function processApiRoutes(options: {
2481
2298
  eagerImports.push(buildImportStatement(`${importName}, * as ${metaName}`, importPath))
2482
2299
  eagerApis.push(`{ method: ${toLiteral(method)}, path: (${metaName}.metadata?.path ?? ${toLiteral(routePath)}), handler: ${importName}, metadata: ${metaName}.metadata${docsPart} }`)
2483
2300
  runtimeApis.push(`{ method: ${toLiteral(method)}, path: ${toLiteral(resolvedPath)}, handler: async (req: Request, ctx?: any) => { const mod = await ${buildDynamicImportExpression(importPath)}; const handler = ((mod as any).default ?? (mod as any).${method} ?? (mod as any).handler) as any; return handler(req, ctx) }, metadata: ${metadataLiteral} }`)
2484
- const metadataEntries: GeneratedObjectEntry[] = [
2485
- { name: 'moduleId', value: modId },
2486
- { name: 'kind', value: 'legacy' },
2487
- { name: 'method', value: method },
2488
- { name: 'path', value: resolvedPath },
2489
- { name: 'methods', value: arrayLiteral([method], writeValue) },
2490
- ]
2491
- const manifestEntry = objectLiteral([
2492
- ...metadataEntries,
2493
- { name: 'load', value: buildManifestApiLoader(importPath) },
2494
- ])
2495
- manifestApis.push(manifestEntry)
2496
- manifestShardApis.push({ path: resolvedPath, declaration: renderGeneratedValue(manifestEntry) })
2497
- metadataApis.push({ path: resolvedPath, declaration: renderGeneratedValue(objectLiteral(metadataEntries)) })
2301
+ const metadataDeclaration = `{ moduleId: ${toLiteral(modId)}, kind: ${toLiteral('legacy')}, method: ${toLiteral(method)}, path: ${toLiteral(resolvedPath)}, methods: [${toLiteral(method)}] }`
2302
+ const manifestDeclaration = `{ moduleId: ${toLiteral(modId)}, kind: ${toLiteral('legacy')}, method: ${toLiteral(method)}, path: ${toLiteral(resolvedPath)}, methods: [${toLiteral(method)}], load: async () => ${buildDynamicImportExpression(importPath)} }`
2303
+ manifestApis.push(manifestDeclaration)
2304
+ manifestShardApis.push({ path: resolvedPath, declaration: manifestDeclaration })
2305
+ metadataApis.push({ path: resolvedPath, declaration: metadataDeclaration })
2498
2306
  }
2499
2307
  }
2500
2308
 
@@ -2507,17 +2315,62 @@ async function processApiRoutes(options: {
2507
2315
  }
2508
2316
  }
2509
2317
 
2510
- /**
2511
- * Registry helper emitted for a worker that declares `metadata.onJobAbandoned`.
2512
- *
2513
- * Named once because two things must stay in step: the call the worker entry renders, and the import
2514
- * the file needs for it. A generated file that references this without importing it type-checks
2515
- * nowhere, and only fails at `build:app` — see `generated-registry-imports.test.ts`.
2516
- */
2517
- const ABANDON_HOOK_FACTORY = 'createLazyModuleWorkerAbandonHook'
2318
+ function processSubscribers(discovered: DiscoveredSubscriber[]): string[] {
2319
+ const subscribers: string[] = []
2320
+ for (const { id, importPath, metadata } of discovered) {
2321
+ const subscriberId = metadata?.id ?? id
2322
+ subscribers.push(
2323
+ `{ id: ${toLiteral(subscriberId)}, event: ${toLiteral(metadata?.event ?? '')}, persistent: ${metadata?.persistent === undefined ? 'undefined' : toLiteral(metadata.persistent)}, sync: ${metadata?.sync === undefined ? 'undefined' : toLiteral(metadata.sync)}, priority: ${metadata?.priority === undefined ? 'undefined' : toLiteral(metadata.priority)}, handler: createLazyModuleSubscriber(() => ${buildDynamicImportExpression(importPath)}, ${toLiteral(subscriberId)}) }`
2324
+ )
2325
+ }
2326
+ return subscribers
2327
+ }
2518
2328
 
2519
- function workersDeclareAbandonHook(discovered: DiscoveredWorker[]): boolean {
2520
- return discovered.some(({ metadata }) => metadata.hasJobAbandonedHook === true)
2329
+ function processWorkers(discovered: DiscoveredWorker[]): string[] {
2330
+ const workers: string[] = []
2331
+ for (const { id, importPath, metadata } of discovered) {
2332
+ const workerId = metadata.id ?? id
2333
+ workers.push(
2334
+ `{ id: ${toLiteral(workerId)}, queue: ${toLiteral(metadata.queue)}, concurrency: ${toLiteral(metadata.concurrency ?? 1)}${metadata.lockDuration === undefined ? '' : `, lockDuration: ${toLiteral(metadata.lockDuration)}`}${metadata.maxStalledCount === undefined ? '' : `, maxStalledCount: ${toLiteral(metadata.maxStalledCount)}`}, handler: createLazyModuleWorker(() => ${buildDynamicImportExpression(importPath)}, ${toLiteral(workerId)}) }`
2335
+ )
2336
+ }
2337
+ return workers
2338
+ }
2339
+
2340
+ function processTranslations(options: {
2341
+ discovered: DiscoveredTranslation[]
2342
+ modId: string
2343
+ appImportBase: string
2344
+ pkgImportBase: string
2345
+ imports: string[]
2346
+ extraImports?: string[]
2347
+ }): string[] {
2348
+ const { discovered, modId, appImportBase, pkgImportBase, imports, extraImports } = options
2349
+ const translations: string[] = []
2350
+ for (const { locale, coreHas, appHas } of discovered) {
2351
+ if (coreHas && appHas) {
2352
+ const cName = `T_${toVar(modId)}_${toVar(locale)}_C`
2353
+ const aName = `T_${toVar(modId)}_${toVar(locale)}_A`
2354
+ imports.push(buildImportStatement(cName, `${pkgImportBase}/i18n/${locale}.json`))
2355
+ imports.push(buildImportStatement(aName, `${appImportBase}/i18n/${locale}.json`))
2356
+ extraImports?.push(buildImportStatement(cName, `${pkgImportBase}/i18n/${locale}.json`))
2357
+ extraImports?.push(buildImportStatement(aName, `${appImportBase}/i18n/${locale}.json`))
2358
+ translations.push(
2359
+ `'${locale}': { ...( ${cName} as unknown as Record<string,string> ), ...( ${aName} as unknown as Record<string,string> ) }`
2360
+ )
2361
+ } else if (appHas) {
2362
+ const aName = `T_${toVar(modId)}_${toVar(locale)}_A`
2363
+ imports.push(buildImportStatement(aName, `${appImportBase}/i18n/${locale}.json`))
2364
+ extraImports?.push(buildImportStatement(aName, `${appImportBase}/i18n/${locale}.json`))
2365
+ translations.push(`'${locale}': ${aName} as unknown as Record<string,string>`)
2366
+ } else if (coreHas) {
2367
+ const cName = `T_${toVar(modId)}_${toVar(locale)}_C`
2368
+ imports.push(buildImportStatement(cName, `${pkgImportBase}/i18n/${locale}.json`))
2369
+ extraImports?.push(buildImportStatement(cName, `${pkgImportBase}/i18n/${locale}.json`))
2370
+ translations.push(`'${locale}': ${cName} as unknown as Record<string,string>`)
2371
+ }
2372
+ }
2373
+ return translations
2521
2374
  }
2522
2375
 
2523
2376
  /**
@@ -2664,64 +2517,6 @@ function buildModuleDashboardWidgetsValue(entries: DashboardWidgetEntry[]): Writ
2664
2517
  )
2665
2518
  }
2666
2519
 
2667
- /**
2668
- * Wraps expressions that are still produced as pre-rendered strings by collaborators this
2669
- * change does not convert (page routes, api handlers, dashboard widgets) so they can sit
2670
- * inside an AST-built array without being re-quoted as string literals. It disappears once
2671
- * those producers emit writers themselves — see the follow-up noted in the spec's Non-Goals.
2672
- */
2673
- function preRenderedExpressionList(expressions: readonly string[]): WriterFunction {
2674
- return arrayLiteral(expressions.map((expression) => identifier(expression)), writeValue)
2675
- }
2676
-
2677
- /**
2678
- * `(X.<first> ?? X.<second>)` — the member-fallback shape the main registry path emits.
2679
- * It is deliberately not `namespaceFallback`, which the app path uses: that helper walks
2680
- * the members in an IIFE and treats only `!= null` as present, which is a different
2681
- * expression. Converging the two is a behaviour change, not a formatting one.
2682
- */
2683
- function legacyNamespaceMemberFallback(importName: string, members: readonly [string, string]): WriterFunction {
2684
- return parenthesized(nullishCoalesce([
2685
- propertyAccess(identifier(importName), members[0]),
2686
- propertyAccess(identifier(importName), members[1]),
2687
- ]))
2688
- }
2689
-
2690
- function buildLegacyModuleListValue(options: {
2691
- importName: string
2692
- members: readonly [string, string]
2693
- castType: string
2694
- }): WriterFunction {
2695
- return logicalOr([
2696
- asExpression(legacyNamespaceMemberFallback(options.importName, options.members), options.castType),
2697
- emptyArray(),
2698
- ])
2699
- }
2700
-
2701
- function buildLegacyModuleSetupValue(importName: string): WriterFunction {
2702
- return logicalOr([
2703
- legacyNamespaceMemberFallback(importName, ['default', 'setup']),
2704
- identifier('undefined'),
2705
- ])
2706
- }
2707
-
2708
- function buildLegacyIntegrationListValue(
2709
- importName: string,
2710
- pluralMember: string,
2711
- singularMember: string,
2712
- castType: string,
2713
- ): WriterFunction {
2714
- const singular = propertyAccess(identifier(importName), singularMember)
2715
-
2716
- return asExpression(
2717
- parenthesized(nullishCoalesce([
2718
- propertyAccess(identifier(importName), pluralMember),
2719
- parenthesized(conditionalExpression(singular, arrayLiteral([singular], writeValue), emptyArray())),
2720
- ])),
2721
- castType,
2722
- )
2723
- }
2724
-
2725
2520
  function buildModuleIntegrationListValue(
2726
2521
  importName: string,
2727
2522
  pluralMember: string,
@@ -2769,15 +2564,9 @@ function buildModulesInfoExpression(): WriterFunction {
2769
2564
  function renderAstModuleRegistryFile(options: {
2770
2565
  fileName: string
2771
2566
  generator: string
2772
- /**
2773
- * Import statements, or the structured specs generator extensions push onto the shared
2774
- * import array via `processStandaloneConfig`. Both shapes reach this emitter at runtime,
2775
- * so both are normalised here rather than at each call site.
2776
- */
2777
- imports: GeneratedImportStatement[]
2567
+ imports: string[]
2778
2568
  moduleEntries: WriterFunction[]
2779
2569
  includeCreateElementImport?: boolean
2780
- includeAbandonHookImport?: boolean
2781
2570
  }): string {
2782
2571
  const sourceFile = createGeneratedSourceFile(options.fileName)
2783
2572
  addAutoGeneratedComment(sourceFile, options.generator)
@@ -2793,11 +2582,10 @@ function renderAstModuleRegistryFile(options: {
2793
2582
  namedImports: [
2794
2583
  { name: 'createLazyModuleSubscriber' },
2795
2584
  { name: 'createLazyModuleWorker' },
2796
- ...(options.includeAbandonHookImport ? [{ name: ABANDON_HOOK_FACTORY }] : []),
2797
2585
  { name: 'Module', isTypeOnly: true },
2798
2586
  ],
2799
2587
  })
2800
- addImportStatements(sourceFile, options.imports.map((entry) => serializeGeneratedImport(entry)))
2588
+ addImportStatements(sourceFile, options.imports)
2801
2589
 
2802
2590
  sourceFile.addVariableStatement({
2803
2591
  declarationKind: VariableDeclarationKind.Const,
@@ -2828,52 +2616,6 @@ function renderAstModuleRegistryFile(options: {
2828
2616
  return getSourceText(sourceFile)
2829
2617
  }
2830
2618
 
2831
- function renderAstManifestFile(options: {
2832
- fileName: string
2833
- typeName: string
2834
- exportName: string
2835
- imports?: string[]
2836
- entries: WriterFunction[]
2837
- }): string {
2838
- const sourceFile = createGeneratedSourceFile(options.fileName)
2839
- addAutoGeneratedComment(sourceFile, 'registry')
2840
-
2841
- const usesPageRouteMetadataHelper = options.typeName === 'FrontendRouteManifestEntry'
2842
- || options.typeName === 'BackendRouteManifestEntry'
2843
- addImportSpec(sourceFile, usesPageRouteMetadataHelper
2844
- ? {
2845
- moduleSpecifier: '@open-mercato/shared/modules/registry',
2846
- namedImports: [
2847
- { name: 'resolvePageRouteMetadata' },
2848
- { name: options.typeName, isTypeOnly: true },
2849
- ],
2850
- }
2851
- : {
2852
- moduleSpecifier: '@open-mercato/shared/modules/registry',
2853
- isTypeOnly: true,
2854
- namedImports: [{ name: options.typeName }],
2855
- })
2856
- addImportStatements(sourceFile, options.imports ?? [])
2857
-
2858
- sourceFile.addVariableStatement({
2859
- declarationKind: VariableDeclarationKind.Const,
2860
- isExported: true,
2861
- declarations: [
2862
- {
2863
- name: options.exportName,
2864
- type: `${options.typeName}[]`,
2865
- initializer: arrayLiteral(options.entries, writeValue),
2866
- },
2867
- ],
2868
- })
2869
- sourceFile.addExportAssignment({
2870
- isExportEquals: false,
2871
- expression: options.exportName,
2872
- })
2873
-
2874
- return getSourceText(sourceFile)
2875
- }
2876
-
2877
2619
  function renderAstLegacyAliasFile(options: {
2878
2620
  fileName: string
2879
2621
  exportName: string
@@ -2937,10 +2679,62 @@ function renderEnabledModuleIdsFile(moduleIds: readonly string[]): string {
2937
2679
  return getSourceText(sourceFile)
2938
2680
  }
2939
2681
 
2682
+ function renderLegacyCompatibleArray(entries: readonly string[]): string {
2683
+ return `[
2684
+ ${entries.join(',\n ')}
2685
+ ]`
2686
+ }
2687
+
2940
2688
  function buildLazyRouteComponentExpression(importPath: string): string {
2941
2689
  return `async (props: any) => { const mod = await ${buildDynamicImportExpression(importPath)}; const Component = (mod.default ?? mod) as any; return createElement(Component, props) }`
2942
2690
  }
2943
2691
 
2692
+ function renderAstLegacyModuleRegistryOutput(options: {
2693
+ fileName: string
2694
+ imports: GeneratedImportStatement[]
2695
+ moduleEntries: string[]
2696
+ includeCreateElementImport?: boolean
2697
+ }): string {
2698
+ const importSection = [
2699
+ ...(options.includeCreateElementImport ? ["import { createElement } from 'react'"] : []),
2700
+ "import { createLazyModuleSubscriber, createLazyModuleWorker, type Module } from '@open-mercato/shared/modules/registry'",
2701
+ ...options.imports.map((entry) => serializeGeneratedImport(entry)),
2702
+ ].join('\n')
2703
+
2704
+ return `// AUTO-GENERATED by mercato generate registry
2705
+ ${importSection}
2706
+
2707
+ export const modules: Module[] = ${renderLegacyCompatibleArray(options.moduleEntries)}
2708
+ export const modulesInfo = modules.map(m => ({ id: m.id, ...(m.info || {}) }))
2709
+ export default modules
2710
+ `
2711
+ }
2712
+
2713
+ function renderAstLegacyManifestOutput(options: {
2714
+ fileName: string
2715
+ typeName: string
2716
+ exportName: string
2717
+ imports?: GeneratedImportStatement[]
2718
+ entries: string[]
2719
+ }): string {
2720
+ const usesPageRouteMetadataHelper = options.typeName === 'FrontendRouteManifestEntry'
2721
+ || options.typeName === 'BackendRouteManifestEntry'
2722
+ const importSection = [
2723
+ usesPageRouteMetadataHelper
2724
+ ? `import { resolvePageRouteMetadata, type ${options.typeName} } from '@open-mercato/shared/modules/registry'`
2725
+ : `import type { ${options.typeName} } from '@open-mercato/shared/modules/registry'`,
2726
+ ...(options.imports ?? []).map((entry) => serializeGeneratedImport(entry)),
2727
+ ].join('\n')
2728
+
2729
+ return `// AUTO-GENERATED by mercato generate registry
2730
+ ${importSection}
2731
+
2732
+ export const ${options.exportName}: ${options.typeName}[] = ${renderLegacyCompatibleArray(options.entries)}
2733
+
2734
+ export default ${options.exportName}
2735
+ `
2736
+ }
2737
+
2944
2738
  function buildRuntimeRouteComponent(importPath: string): WriterFunction {
2945
2739
  return arrowFunction({
2946
2740
  async: true,
@@ -3215,21 +3009,8 @@ function processWorkersAst(discovered: DiscoveredWorker[]): WriterFunction[] {
3215
3009
  { name: 'id', value: workerId },
3216
3010
  { name: 'queue', value: metadata.queue },
3217
3011
  { name: 'concurrency', value: metadata.concurrency ?? 1 },
3218
- ...(metadata.schedulerSafe === true ? [{ name: 'schedulerSafe', value: true }] : []),
3219
- ...(metadata.schedulerRequiredFeatures
3220
- ? [{ name: 'schedulerRequiredFeatures', value: arrayLiteral(metadata.schedulerRequiredFeatures, writeValue) }]
3221
- : []),
3222
3012
  ...(metadata.lockDuration === undefined ? [] : [{ name: 'lockDuration', value: metadata.lockDuration }]),
3223
3013
  ...(metadata.maxStalledCount === undefined ? [] : [{ name: 'maxStalledCount', value: metadata.maxStalledCount }]),
3224
- ...(metadata.hasJobAbandonedHook
3225
- ? [{
3226
- name: 'onJobAbandoned',
3227
- value: callExpression(identifier(ABANDON_HOOK_FACTORY), [
3228
- arrowFunction({ body: importExpression(importPath) }),
3229
- workerId,
3230
- ]),
3231
- }]
3232
- : []),
3233
3014
  {
3234
3015
  name: 'handler',
3235
3016
  value: callExpression(identifier('createLazyModuleWorker'), [
@@ -3589,11 +3370,11 @@ async function generateModuleRegistryFromDiscovery(options: ModuleRegistryRender
3589
3370
  const runtimeImports: string[] = []
3590
3371
  const frontendRouteManifestImports: string[] = []
3591
3372
  const backendRouteManifestImports: string[] = []
3592
- const moduleDecls: WriterFunction[] = []
3593
- const runtimeModuleDecls: WriterFunction[] = []
3594
- const frontendRouteManifestDecls: WriterFunction[] = []
3595
- const backendRouteManifestDecls: WriterFunction[] = []
3596
- const apiRouteManifestDecls: WriterFunction[] = []
3373
+ const moduleDecls: string[] = []
3374
+ const runtimeModuleDecls: string[] = []
3375
+ const frontendRouteManifestDecls: string[] = []
3376
+ const backendRouteManifestDecls: string[] = []
3377
+ const apiRouteManifestDecls: string[] = []
3597
3378
  const backendRouteShardEntries: RouteManifestShardEntry[] = []
3598
3379
  const backendRouteMetadataEntries: RouteManifestShardEntry[] = []
3599
3380
  const apiRouteShardEntries: RouteManifestShardEntry[] = []
@@ -3603,7 +3384,6 @@ async function generateModuleRegistryFromDiscovery(options: ModuleRegistryRender
3603
3384
  const requiresByModule = new Map<string, string[]>()
3604
3385
  const commandLoaderEntries: CommandLoaderGenerationEntry[] = []
3605
3386
  let hasRouteComponents = false
3606
- let hasAbandonHookWorkers = false
3607
3387
 
3608
3388
  // UMES conflict detection: collect file paths during module processing
3609
3389
  const umesConflictSources: Array<{
@@ -3626,9 +3406,9 @@ async function generateModuleRegistryFromDiscovery(options: ModuleRegistryRender
3626
3406
  const runtimeBackendRoutes: string[] = []
3627
3407
  const runtimeApis: string[] = []
3628
3408
  let cliImportName: string | null = null
3629
- const translations: GeneratedObjectEntry[] = []
3630
- const subscribers: WriterFunction[] = []
3631
- const workers: WriterFunction[] = []
3409
+ const translations: string[] = []
3410
+ const subscribers: string[] = []
3411
+ const workers: string[] = []
3632
3412
  let infoImportName: string | null = null
3633
3413
  let extensionsImportName: string | null = null
3634
3414
  let fieldsImportName: string | null = null
@@ -3842,7 +3622,7 @@ async function generateModuleRegistryFromDiscovery(options: ModuleRegistryRender
3842
3622
  }
3843
3623
 
3844
3624
  // 16. Translations
3845
- translations.push(...processTranslationsAst({
3625
+ translations.push(...processTranslations({
3846
3626
  discovered: discovered.translations,
3847
3627
  modId,
3848
3628
  appImportBase,
@@ -3852,12 +3632,10 @@ async function generateModuleRegistryFromDiscovery(options: ModuleRegistryRender
3852
3632
  }))
3853
3633
 
3854
3634
  // 17. Subscribers
3855
- subscribers.push(...processSubscribersAst(await discovered.getSubscribers()))
3635
+ subscribers.push(...processSubscribers(await discovered.getSubscribers()))
3856
3636
 
3857
3637
  // 18. Workers
3858
- const discoveredWorkersForModule = await discovered.getWorkers()
3859
- if (workersDeclareAbandonHook(discoveredWorkersForModule)) hasAbandonHookWorkers = true
3860
- workers.push(...processWorkersAst(discoveredWorkersForModule))
3638
+ workers.push(...processWorkers(await discovered.getWorkers()))
3861
3639
 
3862
3640
  // Build combined customFieldSets expression
3863
3641
  {
@@ -3880,120 +3658,44 @@ async function generateModuleRegistryFromDiscovery(options: ModuleRegistryRender
3880
3658
  }
3881
3659
  }
3882
3660
 
3883
- const identityEntries: GeneratedObjectEntry[] = [{ name: 'id', value: modId }]
3884
- if (infoImportName) {
3885
- identityEntries.push({ name: 'info', value: propertyAccess(identifier(infoImportName), 'metadata') })
3886
- }
3887
-
3888
- const sharedTailEntries: GeneratedObjectEntry[] = []
3889
- if (translations.length > 0) {
3890
- sharedTailEntries.push({ name: 'translations', value: objectLiteral(translations) })
3891
- }
3892
- if (subscribers.length > 0) {
3893
- sharedTailEntries.push({ name: 'subscribers', value: arrayLiteral(subscribers, writeValue) })
3894
- }
3895
- if (workers.length > 0) {
3896
- sharedTailEntries.push({ name: 'workers', value: arrayLiteral(workers, writeValue) })
3897
- }
3898
- if (extensionsImportName) {
3899
- sharedTailEntries.push({
3900
- name: 'entityExtensions',
3901
- value: buildLegacyModuleListValue({
3902
- importName: extensionsImportName,
3903
- members: ['default', 'extensions'],
3904
- castType: "import('@open-mercato/shared/modules/entities').EntityExtension[]",
3905
- }),
3906
- })
3907
- }
3908
- sharedTailEntries.push({ name: 'customFieldSets', value: identifier(customFieldSetsExpr) })
3909
- if (featuresImportName) {
3910
- sharedTailEntries.push({
3911
- name: 'features',
3912
- value: buildLegacyModuleListValue({
3913
- importName: featuresImportName,
3914
- members: ['default', 'features'],
3915
- castType: 'any',
3916
- }),
3917
- })
3918
- }
3919
- if (customEntitiesImportName) {
3920
- sharedTailEntries.push({
3921
- name: 'customEntities',
3922
- value: buildLegacyModuleListValue({
3923
- importName: customEntitiesImportName,
3924
- members: ['default', 'entities'],
3925
- castType: 'any',
3926
- }),
3927
- })
3928
- }
3929
-
3930
- const setupAndBeyondEntries: GeneratedObjectEntry[] = []
3931
- if (setupImportName) {
3932
- setupAndBeyondEntries.push({ name: 'setup', value: buildLegacyModuleSetupValue(setupImportName) })
3933
- }
3934
- if (encryptionImportName) {
3935
- setupAndBeyondEntries.push({
3936
- name: 'defaultEncryptionMaps',
3937
- value: buildLegacyModuleListValue({
3938
- importName: encryptionImportName,
3939
- members: ['default', 'defaultEncryptionMaps'],
3940
- castType: "import('@open-mercato/shared/modules/encryption').ModuleEncryptionMap[]",
3941
- }),
3942
- })
3943
- }
3944
- if (integrationExports) {
3945
- setupAndBeyondEntries.push({
3946
- name: 'integrations',
3947
- value: buildLegacyIntegrationListValue(
3948
- integrationExports,
3949
- 'integrations',
3950
- 'integration',
3951
- "import('@open-mercato/shared/modules/integrations/types').IntegrationDefinition[]",
3952
- ),
3953
- })
3954
- setupAndBeyondEntries.push({
3955
- name: 'bundles',
3956
- value: buildLegacyIntegrationListValue(
3957
- integrationExports,
3958
- 'bundles',
3959
- 'bundle',
3960
- "import('@open-mercato/shared/modules/integrations/types').IntegrationBundle[]",
3961
- ),
3962
- })
3963
- }
3964
-
3965
- const eagerEntries: GeneratedObjectEntry[] = [...identityEntries]
3966
- if (frontendRoutes.length > 0) {
3967
- eagerEntries.push({ name: 'frontendRoutes', value: preRenderedExpressionList(frontendRoutes) })
3968
- }
3969
- if (backendRoutes.length > 0) {
3970
- eagerEntries.push({ name: 'backendRoutes', value: preRenderedExpressionList(backendRoutes) })
3971
- }
3972
- if (apis.length > 0) {
3973
- eagerEntries.push({ name: 'apis', value: preRenderedExpressionList(apis) })
3974
- }
3975
- if (cliImportName) {
3976
- eagerEntries.push({ name: 'cli', value: identifier(cliImportName) })
3977
- }
3978
- eagerEntries.push(...sharedTailEntries)
3979
- if (dashboardWidgets.length > 0) {
3980
- eagerEntries.push({ name: 'dashboardWidgets', value: preRenderedExpressionList(dashboardWidgets) })
3981
- }
3982
- eagerEntries.push(...setupAndBeyondEntries)
3983
- moduleDecls.push(objectLiteral(eagerEntries))
3984
-
3985
- const runtimeEntries: GeneratedObjectEntry[] = [...identityEntries]
3986
- if (runtimeFrontendRoutes.length > 0) {
3987
- runtimeEntries.push({ name: 'frontendRoutes', value: preRenderedExpressionList(runtimeFrontendRoutes) })
3988
- }
3989
- if (runtimeBackendRoutes.length > 0) {
3990
- runtimeEntries.push({ name: 'backendRoutes', value: preRenderedExpressionList(runtimeBackendRoutes) })
3991
- }
3992
- if (runtimeApis.length > 0) {
3993
- runtimeEntries.push({ name: 'apis', value: preRenderedExpressionList(runtimeApis) })
3994
- }
3995
- runtimeEntries.push(...sharedTailEntries, ...setupAndBeyondEntries)
3996
- runtimeModuleDecls.push(objectLiteral(runtimeEntries))
3661
+ moduleDecls.push(`{
3662
+ id: ${toLiteral(modId)},
3663
+ ${infoImportName ? `info: ${infoImportName}.metadata,` : ''}
3664
+ ${frontendRoutes.length ? `frontendRoutes: [${frontendRoutes.join(', ')}],` : ''}
3665
+ ${backendRoutes.length ? `backendRoutes: [${backendRoutes.join(', ')}],` : ''}
3666
+ ${apis.length ? `apis: [${apis.join(', ')}],` : ''}
3667
+ ${cliImportName ? `cli: ${cliImportName},` : ''}
3668
+ ${translations.length ? `translations: { ${translations.join(', ')} },` : ''}
3669
+ ${subscribers.length ? `subscribers: [${subscribers.join(', ')}],` : ''}
3670
+ ${workers.length ? `workers: [${workers.join(', ')}],` : ''}
3671
+ ${extensionsImportName ? `entityExtensions: ((${extensionsImportName}.default ?? ${extensionsImportName}.extensions) as import('@open-mercato/shared/modules/entities').EntityExtension[]) || [],` : ''}
3672
+ customFieldSets: ${customFieldSetsExpr},
3673
+ ${featuresImportName ? `features: ((${featuresImportName}.default ?? ${featuresImportName}.features) as any) || [],` : ''}
3674
+ ${customEntitiesImportName ? `customEntities: ((${customEntitiesImportName}.default ?? ${customEntitiesImportName}.entities) as any) || [],` : ''}
3675
+ ${dashboardWidgets.length ? `dashboardWidgets: [${dashboardWidgets.join(', ')}],` : ''}
3676
+ ${setupImportName ? `setup: (${setupImportName}.default ?? ${setupImportName}.setup) || undefined,` : ''}
3677
+ ${encryptionImportName ? `defaultEncryptionMaps: ((${encryptionImportName}.default ?? ${encryptionImportName}.defaultEncryptionMaps) as import('@open-mercato/shared/modules/encryption').ModuleEncryptionMap[]) || [],` : ''}
3678
+ ${integrationExports ? `integrations: ((${integrationExports}.integrations ?? (${integrationExports}.integration ? [${integrationExports}.integration] : [])) as import('@open-mercato/shared/modules/integrations/types').IntegrationDefinition[]),` : ''}
3679
+ ${integrationExports ? `bundles: ((${integrationExports}.bundles ?? (${integrationExports}.bundle ? [${integrationExports}.bundle] : [])) as import('@open-mercato/shared/modules/integrations/types').IntegrationBundle[]),` : ''}
3680
+ }`)
3681
+ runtimeModuleDecls.push(`{
3682
+ id: ${toLiteral(modId)},
3683
+ ${infoImportName ? `info: ${infoImportName}.metadata,` : ''}
3684
+ ${runtimeFrontendRoutes.length ? `frontendRoutes: [${runtimeFrontendRoutes.join(', ')}],` : ''}
3685
+ ${runtimeBackendRoutes.length ? `backendRoutes: [${runtimeBackendRoutes.join(', ')}],` : ''}
3686
+ ${runtimeApis.length ? `apis: [${runtimeApis.join(', ')}],` : ''}
3687
+ ${translations.length ? `translations: { ${translations.join(', ')} },` : ''}
3688
+ ${subscribers.length ? `subscribers: [${subscribers.join(', ')}],` : ''}
3689
+ ${workers.length ? `workers: [${workers.join(', ')}],` : ''}
3690
+ ${extensionsImportName ? `entityExtensions: ((${extensionsImportName}.default ?? ${extensionsImportName}.extensions) as import('@open-mercato/shared/modules/entities').EntityExtension[]) || [],` : ''}
3691
+ customFieldSets: ${customFieldSetsExpr},
3692
+ ${featuresImportName ? `features: ((${featuresImportName}.default ?? ${featuresImportName}.features) as any) || [],` : ''}
3693
+ ${customEntitiesImportName ? `customEntities: ((${customEntitiesImportName}.default ?? ${customEntitiesImportName}.entities) as any) || [],` : ''}
3694
+ ${setupImportName ? `setup: (${setupImportName}.default ?? ${setupImportName}.setup) || undefined,` : ''}
3695
+ ${encryptionImportName ? `defaultEncryptionMaps: ((${encryptionImportName}.default ?? ${encryptionImportName}.defaultEncryptionMaps) as import('@open-mercato/shared/modules/encryption').ModuleEncryptionMap[]) || [],` : ''}
3696
+ ${integrationExports ? `integrations: ((${integrationExports}.integrations ?? (${integrationExports}.integration ? [${integrationExports}.integration] : [])) as import('@open-mercato/shared/modules/integrations/types').IntegrationDefinition[]),` : ''}
3697
+ ${integrationExports ? `bundles: ((${integrationExports}.bundles ?? (${integrationExports}.bundle ? [${integrationExports}.bundle] : [])) as import('@open-mercato/shared/modules/integrations/types').IntegrationBundle[]),` : ''}
3698
+ }`)
3997
3699
  }
3998
3700
 
3999
3701
  // === UMES Conflict Detection ===
@@ -4094,37 +3796,33 @@ async function generateModuleRegistryFromDiscovery(options: ModuleRegistryRender
4094
3796
  }
4095
3797
  }
4096
3798
 
4097
- const output = renderAstModuleRegistryFile({
3799
+ const output = renderAstLegacyModuleRegistryOutput({
4098
3800
  fileName: 'modules.generated.ts',
4099
- generator: 'registry',
4100
3801
  imports,
4101
3802
  moduleEntries: moduleDecls,
4102
3803
  includeCreateElementImport: hasRouteComponents,
4103
- includeAbandonHookImport: hasAbandonHookWorkers,
4104
3804
  })
4105
- const runtimeOutput = renderAstModuleRegistryFile({
3805
+ const runtimeOutput = renderAstLegacyModuleRegistryOutput({
4106
3806
  fileName: 'modules.runtime.generated.ts',
4107
- generator: 'registry',
4108
3807
  imports: runtimeImports,
4109
3808
  moduleEntries: runtimeModuleDecls,
4110
3809
  includeCreateElementImport: true,
4111
- includeAbandonHookImport: hasAbandonHookWorkers,
4112
3810
  })
4113
- const frontendRoutesOutput = renderAstManifestFile({
3811
+ const frontendRoutesOutput = renderAstLegacyManifestOutput({
4114
3812
  fileName: 'frontend-routes.generated.ts',
4115
3813
  typeName: 'FrontendRouteManifestEntry',
4116
3814
  exportName: 'frontendRoutes',
4117
3815
  imports: frontendRouteManifestImports,
4118
3816
  entries: frontendRouteManifestDecls,
4119
3817
  })
4120
- const backendRoutesOutput = renderAstManifestFile({
3818
+ const backendRoutesOutput = renderAstLegacyManifestOutput({
4121
3819
  fileName: 'backend-routes.generated.ts',
4122
3820
  typeName: 'BackendRouteManifestEntry',
4123
3821
  exportName: 'backendRoutes',
4124
3822
  imports: backendRouteManifestImports,
4125
3823
  entries: backendRouteManifestDecls,
4126
3824
  })
4127
- const apiRoutesOutput = renderAstManifestFile({
3825
+ const apiRoutesOutput = renderAstLegacyManifestOutput({
4128
3826
  fileName: 'api-routes.generated.ts',
4129
3827
  typeName: 'ApiRouteManifestEntry',
4130
3828
  exportName: 'apiRoutes',
@@ -4264,11 +3962,15 @@ async function generateModuleRegistryFromDiscovery(options: ModuleRegistryRender
4264
3962
  allRegImports.push(...reg.registrationImports)
4265
3963
  allCalls.push(reg.buildCall(reg.entriesExportName))
4266
3964
  }
4267
- const bootstrapRegsOutput = renderBootstrapRegistrationsFile({
4268
- entryImports: allEntryImports,
4269
- registrationImports: allRegImports,
4270
- calls: allCalls,
4271
- })
3965
+ const uniqueImports = [...new Set([...allEntryImports, ...allRegImports])]
3966
+ const importSection = uniqueImports.join('\n')
3967
+ const body = ` ${allCalls.join('\n ')}`
3968
+ const bootstrapRegsOutput = `// AUTO-GENERATED by mercato generate registry
3969
+ ${importSection ? `${importSection}\n` : ''}
3970
+ export function runBootstrapRegistrations(): void {
3971
+ ${body}
3972
+ }
3973
+ `
4272
3974
  writeGeneratedFile({ outFile: bootstrapRegsOutFile, checksumFile: bootstrapRegsChecksumFile, content: bootstrapRegsOutput, structureChecksum, result, quiet })
4273
3975
  }
4274
3976
 
@@ -4308,7 +4010,6 @@ async function generateModuleRegistryAppFromDiscovery(options: ModuleRegistryRen
4308
4010
  const requiresByModule = new Map<string, string[]>()
4309
4011
  const commandLoaderEntries: CommandLoaderGenerationEntry[] = []
4310
4012
  let hasRouteComponents = false
4311
- let hasAbandonHookWorkers = false
4312
4013
  const seenBackendRoutePatterns = new Map<string, string>()
4313
4014
 
4314
4015
  for (const discovered of discovery.modules) {
@@ -4470,9 +4171,7 @@ async function generateModuleRegistryAppFromDiscovery(options: ModuleRegistryRen
4470
4171
 
4471
4172
  subscribers.push(...processSubscribersAst(await discovered.getSubscribers()))
4472
4173
 
4473
- const discoveredWorkersForModule = await discovered.getWorkers()
4474
- if (workersDeclareAbandonHook(discoveredWorkersForModule)) hasAbandonHookWorkers = true
4475
- workers.push(...processWorkersAst(discoveredWorkersForModule))
4174
+ workers.push(...processWorkersAst(await discovered.getWorkers()))
4476
4175
 
4477
4176
  {
4478
4177
  dashboardWidgetsValue = buildModuleDashboardWidgetsValue(discovered.dashboardWidgetEntries)
@@ -4596,21 +4295,18 @@ async function generateModuleRegistryAppFromDiscovery(options: ModuleRegistryRen
4596
4295
  imports,
4597
4296
  moduleEntries: moduleDecls,
4598
4297
  includeCreateElementImport: hasRouteComponents,
4599
- includeAbandonHookImport: hasAbandonHookWorkers,
4600
4298
  })
4601
4299
  const bootstrapOutput = renderAstModuleRegistryFile({
4602
4300
  fileName: 'modules.bootstrap.generated.ts',
4603
4301
  generator: 'registry (bootstrap version)',
4604
4302
  imports: bootstrapImports,
4605
4303
  moduleEntries: bootstrapModuleDecls,
4606
- includeAbandonHookImport: hasAbandonHookWorkers,
4607
4304
  })
4608
4305
  const i18nOutput = renderAstModuleRegistryFile({
4609
4306
  fileName: 'modules.i18n.generated.ts',
4610
4307
  generator: 'registry (i18n version)',
4611
4308
  imports: i18nImports,
4612
4309
  moduleEntries: i18nModuleDecls,
4613
- includeAbandonHookImport: hasAbandonHookWorkers,
4614
4310
  })
4615
4311
  const legacyOutput = renderAstLegacyAliasFile({
4616
4312
  fileName: 'bootstrap-modules.generated.ts',
@@ -4701,7 +4397,6 @@ async function generateModuleRegistryAppFromDiscovery(options: ModuleRegistryRen
4701
4397
  * Excludes: frontend routes, backend routes, API handlers, injection widgets
4702
4398
  */
4703
4399
  async function generateModuleRegistryCliFromDiscovery(options: ModuleRegistryRenderOptions): Promise<GeneratorResult> {
4704
- let hasAbandonHookWorkers = false
4705
4400
  const { resolver, quiet = false } = options
4706
4401
  const { discovery } = options
4707
4402
  const result = createGeneratorResult()
@@ -4853,7 +4548,6 @@ async function generateModuleRegistryCliFromDiscovery(options: ModuleRegistryRen
4853
4548
  // Workers
4854
4549
  const discoveredWorkers = await discovered.getWorkers()
4855
4550
  const workerGenerationEntries = collectWorkerGenerationEntries(discoveredWorkers, modId)
4856
- if (workersDeclareAbandonHook(discoveredWorkers)) hasAbandonHookWorkers = true
4857
4551
  workers.push(...processWorkersAst(discoveredWorkers))
4858
4552
  devSupervisorWorkers.push(...workerGenerationEntries.map(({ importPath: _importPath, ...worker }) => worker))
4859
4553
 
@@ -4977,7 +4671,6 @@ async function generateModuleRegistryCliFromDiscovery(options: ModuleRegistryRen
4977
4671
  generator: 'registry (CLI version)',
4978
4672
  imports,
4979
4673
  moduleEntries: moduleDecls,
4980
- includeAbandonHookImport: hasAbandonHookWorkers,
4981
4674
  })
4982
4675
  const legacyOutput = renderAstLegacyAliasFile({
4983
4676
  fileName: 'cli-modules.generated.ts',