@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.
- package/.turbo/turbo-build.log +7 -1
- package/dist/agentic/guides/module-facts.json +5509 -599
- package/dist/agentic/guides/module-facts.v2.json +5595 -599
- package/dist/agentic/guides/modules/ai_assistant/index.md +1 -1
- package/dist/agentic/guides/modules/api_docs/index.md +1 -1
- package/dist/agentic/guides/modules/api_keys/di-registrations-rich.md +11 -0
- package/dist/agentic/guides/modules/api_keys/di-service-tokens.md +11 -0
- package/dist/agentic/guides/modules/api_keys/exact-override-targets.md +1 -0
- package/dist/agentic/guides/modules/api_keys/index.md +4 -2
- package/dist/agentic/guides/modules/attachments/di-registrations-rich.md +6 -5
- package/dist/agentic/guides/modules/attachments/di-service-tokens.md +5 -4
- package/dist/agentic/guides/modules/attachments/exact-override-targets.md +6 -5
- package/dist/agentic/guides/modules/attachments/index.md +2 -2
- package/dist/agentic/guides/modules/audit_logs/index.md +1 -1
- package/dist/agentic/guides/modules/auth/di-registrations-rich.md +3 -2
- package/dist/agentic/guides/modules/auth/di-service-tokens.md +3 -2
- package/dist/agentic/guides/modules/auth/exact-override-targets.md +3 -2
- package/dist/agentic/guides/modules/auth/index.md +1 -1
- package/dist/agentic/guides/modules/business_rules/index.md +1 -1
- package/dist/agentic/guides/modules/catalog/incoming-installed-contributions.md +3 -0
- package/dist/agentic/guides/modules/catalog/index.md +2 -2
- package/dist/agentic/guides/modules/channel_apns/index.md +1 -1
- package/dist/agentic/guides/modules/channel_discord/acl-features.md +13 -0
- package/dist/agentic/guides/modules/channel_discord/ai-agents.md +11 -0
- package/dist/agentic/guides/modules/channel_discord/backend-pages.md +11 -0
- package/dist/agentic/guides/modules/channel_discord/cli-commands.md +13 -0
- package/dist/agentic/guides/modules/channel_discord/contribution-resolutions.md +14 -0
- package/dist/agentic/guides/modules/channel_discord/di-registrations-rich.md +12 -0
- package/dist/agentic/guides/modules/channel_discord/exact-override-targets.md +26 -0
- package/dist/agentic/guides/modules/channel_discord/index.md +22 -0
- package/dist/agentic/guides/modules/channel_discord/owned-contract-module-metadata.md +11 -0
- package/dist/agentic/guides/modules/channel_discord/setup.md +11 -0
- package/dist/agentic/guides/modules/channel_discord/umes-contributions.md +14 -0
- package/dist/agentic/guides/modules/channel_discord/workers.md +12 -0
- package/dist/agentic/guides/modules/channel_expo/index.md +1 -1
- package/dist/agentic/guides/modules/channel_fcm/index.md +1 -1
- package/dist/agentic/guides/modules/channel_gmail/index.md +1 -1
- package/dist/agentic/guides/modules/channel_imap/index.md +1 -1
- package/dist/agentic/guides/modules/checkout/index.md +1 -1
- package/dist/agentic/guides/modules/communication_channels/di-registrations-rich.md +9 -8
- package/dist/agentic/guides/modules/communication_channels/exact-override-targets.md +9 -8
- package/dist/agentic/guides/modules/communication_channels/incoming-installed-contributions.md +2 -0
- package/dist/agentic/guides/modules/communication_channels/index.md +3 -3
- package/dist/agentic/guides/modules/configs/index.md +1 -1
- package/dist/agentic/guides/modules/content/index.md +1 -1
- package/dist/agentic/guides/modules/currencies/index.md +1 -1
- package/dist/agentic/guides/modules/customer_accounts/index.md +1 -1
- package/dist/agentic/guides/modules/customers/active-extension-bindings.md +6 -3
- package/dist/agentic/guides/modules/customers/contribution-resolutions.md +1 -0
- package/dist/agentic/guides/modules/customers/events.md +2 -1
- package/dist/agentic/guides/modules/customers/incoming-installed-contributions.md +7 -0
- package/dist/agentic/guides/modules/customers/index.md +4 -4
- package/dist/agentic/guides/modules/customers/umes-contributions.md +1 -0
- package/dist/agentic/guides/modules/customers/umes-hosts.md +1 -0
- package/dist/agentic/guides/modules/dashboards/index.md +1 -1
- package/dist/agentic/guides/modules/data_sync/exact-override-targets.md +2 -2
- package/dist/agentic/guides/modules/data_sync/index.md +1 -1
- package/dist/agentic/guides/modules/data_sync/workers.md +2 -2
- package/dist/agentic/guides/modules/design_system/index.md +1 -1
- package/dist/agentic/guides/modules/devices/index.md +1 -1
- package/dist/agentic/guides/modules/dictionaries/index.md +1 -1
- package/dist/agentic/guides/modules/directory/di-registrations-rich.md +12 -0
- package/dist/agentic/guides/modules/directory/di-service-tokens.md +12 -0
- package/dist/agentic/guides/modules/directory/exact-override-targets.md +2 -0
- package/dist/agentic/guides/modules/directory/index.md +5 -3
- package/dist/agentic/guides/modules/documents/acl-features.md +17 -0
- package/dist/agentic/guides/modules/documents/backend-pages.md +13 -0
- package/dist/agentic/guides/modules/documents/contribution-resolutions.md +27 -0
- package/dist/agentic/guides/modules/documents/di-registrations-rich.md +21 -0
- package/dist/agentic/guides/modules/documents/domain-commands.md +37 -0
- package/dist/agentic/guides/modules/documents/encryption.md +11 -0
- package/dist/agentic/guides/modules/documents/entities.md +21 -0
- package/dist/agentic/guides/modules/documents/events.md +25 -0
- package/dist/agentic/guides/modules/documents/exact-override-targets.md +39 -0
- package/dist/agentic/guides/modules/documents/index.md +25 -0
- package/dist/agentic/guides/modules/documents/notifications.md +13 -0
- package/dist/agentic/guides/modules/documents/owned-contract-module-metadata.md +11 -0
- package/dist/agentic/guides/modules/documents/setup.md +11 -0
- package/dist/agentic/guides/modules/documents/umes-contributions.md +27 -0
- package/dist/agentic/guides/modules/documents/umes-hosts.md +36 -0
- package/dist/agentic/guides/modules/entities/index.md +1 -1
- package/dist/agentic/guides/modules/eudr/index.md +1 -1
- package/dist/agentic/guides/modules/events/index.md +1 -1
- package/dist/agentic/guides/modules/feature_toggles/index.md +1 -1
- package/dist/agentic/guides/modules/gateway_stripe/exact-override-targets.md +1 -1
- package/dist/agentic/guides/modules/gateway_stripe/index.md +1 -1
- package/dist/agentic/guides/modules/gateway_stripe/workers.md +1 -1
- package/dist/agentic/guides/modules/generators/index.md +1 -1
- package/dist/agentic/guides/modules/inbox_ops/index.md +1 -1
- package/dist/agentic/guides/modules/integrations/index.md +1 -1
- package/dist/agentic/guides/modules/messages/index.md +1 -1
- package/dist/agentic/guides/modules/notifications/index.md +1 -1
- package/dist/agentic/guides/modules/onboarding/index.md +1 -1
- package/dist/agentic/guides/modules/payment_gateways/index.md +1 -1
- package/dist/agentic/guides/modules/perspectives/index.md +1 -1
- package/dist/agentic/guides/modules/planner/index.md +1 -1
- package/dist/agentic/guides/modules/portal/index.md +1 -1
- package/dist/agentic/guides/modules/progress/index.md +1 -1
- package/dist/agentic/guides/modules/push_notifications/index.md +1 -1
- package/dist/agentic/guides/modules/query_index/index.md +1 -1
- package/dist/agentic/guides/modules/record_locks/index.md +1 -1
- package/dist/agentic/guides/modules/resources/index.md +1 -1
- package/dist/agentic/guides/modules/sales/active-extension-bindings.md +1 -0
- package/dist/agentic/guides/modules/sales/contribution-resolutions.md +1 -0
- package/dist/agentic/guides/modules/sales/exact-override-targets.md +5 -4
- package/dist/agentic/guides/modules/sales/host-extension-points.md +1 -1
- package/dist/agentic/guides/modules/sales/incoming-installed-contributions.md +2 -0
- package/dist/agentic/guides/modules/sales/index.md +3 -3
- package/dist/agentic/guides/modules/sales/setup.md +1 -1
- package/dist/agentic/guides/modules/sales/umes-contributions.md +1 -0
- package/dist/agentic/guides/modules/sales/umes-diagnostics.md +1 -0
- package/dist/agentic/guides/modules/sales/umes-hosts.md +9 -0
- package/dist/agentic/guides/modules/scheduler/cli-commands.md +1 -0
- package/dist/agentic/guides/modules/scheduler/exact-override-targets.md +3 -1
- package/dist/agentic/guides/modules/scheduler/index.md +2 -2
- package/dist/agentic/guides/modules/scheduler/workers.md +2 -1
- package/dist/agentic/guides/modules/search/index.md +1 -1
- package/dist/agentic/guides/modules/security/index.md +1 -1
- package/dist/agentic/guides/modules/security/owned-contract-module-metadata.md +1 -1
- package/dist/agentic/guides/modules/shipping_carriers/index.md +1 -1
- package/dist/agentic/guides/modules/sso/index.md +1 -1
- package/dist/agentic/guides/modules/staff/index.md +1 -1
- package/dist/agentic/guides/modules/storage_s3/index.md +1 -1
- package/dist/agentic/guides/modules/sync_akeneo/index.md +1 -1
- package/dist/agentic/guides/modules/sync_excel/index.md +1 -1
- package/dist/agentic/guides/modules/system_status_overlays/index.md +1 -1
- package/dist/agentic/guides/modules/translations/index.md +1 -1
- package/dist/agentic/guides/modules/warranty_claims/index.md +1 -1
- package/dist/agentic/guides/modules/webhooks/index.md +1 -1
- package/dist/agentic/guides/modules/wms/index.md +1 -1
- package/dist/agentic/guides/modules/workflows/index.md +1 -1
- package/dist/agentic/guides/reference-module-facts.json +2 -2
- package/dist/agentic/guides/reference-modules/example/index.md +1 -1
- package/dist/agentic/guides/upstream/manifest.json +1 -1
- package/dist/agentic/shared/AGENTS.md.template +3 -3
- package/dist/agentic/shared/ai/harness/README.md +2 -2
- package/dist/agentic/shared/ai/harness/RELEASE.md +8 -6
- package/dist/agentic/shared/ai/harness/cases.json +83 -0
- package/dist/agentic/shared/ai/harness/cases.schema.json +4 -4
- package/dist/agentic/shared/ai/harness/validators.json +1 -1
- package/dist/agentic/shared/scripts/run-agent-harness-release.mjs +37 -11
- package/dist/lib/generators/ast/index.js +4 -1
- package/dist/lib/generators/ast/index.js.map +2 -2
- package/dist/lib/generators/ast/source-file.js +8 -1
- package/dist/lib/generators/ast/source-file.js.map +2 -2
- package/dist/lib/generators/ast/writers.js +9 -1
- package/dist/lib/generators/ast/writers.js.map +2 -2
- package/dist/lib/generators/module-registry.js +424 -189
- package/dist/lib/generators/module-registry.js.map +2 -2
- package/dist/lib/generators/route-manifest-shards.js +4 -1
- package/dist/lib/generators/route-manifest-shards.js.map +2 -2
- package/dist/lib/spawn.js +3 -2
- package/dist/lib/spawn.js.map +2 -2
- package/dist/lib/testing/integration.js +162 -24
- package/dist/lib/testing/integration.js.map +2 -2
- package/dist/mercato.js +14 -0
- package/dist/mercato.js.map +2 -2
- package/package.json +8 -7
- package/src/__tests__/resolve-queue-abandon-hook.test.ts +39 -0
- package/src/lib/__tests__/spawn.test.ts +23 -0
- package/src/lib/generators/__tests__/__snapshots__/bootstrap-registrations.test.ts.snap +14 -0
- package/src/lib/generators/__tests__/__snapshots__/command-loaders.test.ts.snap +23 -0
- package/src/lib/generators/__tests__/ast-writers-escaping.test.ts +64 -0
- package/src/lib/generators/__tests__/bootstrap-registrations.test.ts +176 -0
- package/src/lib/generators/__tests__/command-loaders.test.ts +239 -0
- package/src/lib/generators/__tests__/generated-registry-imports.test.ts +128 -0
- package/src/lib/generators/__tests__/metadata-extraction.test.ts +44 -0
- package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +32 -8
- package/src/lib/generators/__tests__/module-facts.customers.fixture.test.ts +4 -1
- package/src/lib/generators/__tests__/module-facts.extension-hosts.test.ts +1 -1
- package/src/lib/generators/__tests__/module-subset.test.ts +96 -72
- package/src/lib/generators/__tests__/output-snapshots.test.ts +1 -1
- package/src/lib/generators/__tests__/registry-variant-parity.test.ts +258 -0
- package/src/lib/generators/__tests__/route-manifest-shards.test.ts +15 -0
- package/src/lib/generators/__tests__/structural-contracts.test.ts +7 -3
- package/src/lib/generators/ast/index.ts +2 -1
- package/src/lib/generators/ast/source-file.ts +13 -0
- package/src/lib/generators/ast/writers.ts +26 -1
- package/src/lib/generators/module-registry.ts +534 -227
- package/src/lib/generators/route-manifest-shards.ts +8 -1
- package/src/lib/spawn.ts +5 -4
- package/src/lib/testing/__tests__/integration.test.ts +435 -0
- package/src/lib/testing/integration.ts +254 -32
- package/src/mercato.ts +27 -0
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
conditionalExpression,
|
|
33
33
|
createGeneratedSourceFile,
|
|
34
34
|
elementAccess,
|
|
35
|
+
escapeGeneratedJsonLiteral,
|
|
35
36
|
returnStatement,
|
|
36
37
|
getSourceText,
|
|
37
38
|
identifier,
|
|
@@ -44,6 +45,7 @@ import {
|
|
|
44
45
|
optionalPropertyAccess,
|
|
45
46
|
parenthesized,
|
|
46
47
|
propertyAccess,
|
|
48
|
+
renderGeneratedValue,
|
|
47
49
|
spreadExpression,
|
|
48
50
|
type GeneratedObjectEntry,
|
|
49
51
|
variableStatement,
|
|
@@ -133,7 +135,7 @@ type DashboardWidgetEntry = {
|
|
|
133
135
|
importPath: string
|
|
134
136
|
}
|
|
135
137
|
|
|
136
|
-
type CommandLoaderGenerationEntry = {
|
|
138
|
+
export type CommandLoaderGenerationEntry = {
|
|
137
139
|
moduleId: string
|
|
138
140
|
key: string
|
|
139
141
|
importPath: string
|
|
@@ -159,7 +161,7 @@ type RuntimeApiMethodMetadata = {
|
|
|
159
161
|
type PageRouteGenerationResult = {
|
|
160
162
|
eagerRoutes: string[]
|
|
161
163
|
runtimeRoutes: string[]
|
|
162
|
-
manifestRoutes:
|
|
164
|
+
manifestRoutes: WriterFunction[]
|
|
163
165
|
manifestShardRoutes: RouteManifestShardEntry[]
|
|
164
166
|
metadataRoutes: RouteManifestShardEntry[]
|
|
165
167
|
routePatterns: string[]
|
|
@@ -190,7 +192,7 @@ function assertUniqueBackendRoutePattern(
|
|
|
190
192
|
type ApiRouteGenerationResult = {
|
|
191
193
|
eagerApis: string[]
|
|
192
194
|
runtimeApis: string[]
|
|
193
|
-
manifestApis:
|
|
195
|
+
manifestApis: WriterFunction[]
|
|
194
196
|
manifestShardApis: RouteManifestShardEntry[]
|
|
195
197
|
metadataApis: RouteManifestShardEntry[]
|
|
196
198
|
}
|
|
@@ -244,8 +246,19 @@ type SerializableWorkerMetadata = {
|
|
|
244
246
|
id?: string
|
|
245
247
|
queue?: string
|
|
246
248
|
concurrency?: number
|
|
249
|
+
schedulerSafe?: boolean
|
|
250
|
+
schedulerRequiredFeatures?: string[]
|
|
247
251
|
lockDuration?: number
|
|
248
252
|
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
|
|
249
262
|
}
|
|
250
263
|
|
|
251
264
|
type PageMetadataManifestLoadResult = {
|
|
@@ -577,6 +590,55 @@ function extractObjectPropertiesFromAst(sourceFile: string, exportName: string):
|
|
|
577
590
|
return Object.keys(result).length > 0 ? result : null
|
|
578
591
|
}
|
|
579
592
|
|
|
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
|
+
|
|
580
642
|
export function extractNamedObjectLiteralExport(sourceFile: string, exportName: string): Record<string, unknown> | null {
|
|
581
643
|
const literal = extractNamedObjectLiteralSource(sourceFile, exportName)
|
|
582
644
|
if (literal) {
|
|
@@ -1136,11 +1198,7 @@ function requiresRuntimePageMetadataFromSourceFile(sourceFile: string): boolean
|
|
|
1136
1198
|
}
|
|
1137
1199
|
|
|
1138
1200
|
function toLiteral(value: unknown): string {
|
|
1139
|
-
return JSON.stringify(value)
|
|
1140
|
-
.replace(/</g, '\\u003c')
|
|
1141
|
-
.replace(/>/g, '\\u003e')
|
|
1142
|
-
.replace(/\u2028/g, '\\u2028')
|
|
1143
|
-
.replace(/\u2029/g, '\\u2029')
|
|
1201
|
+
return escapeGeneratedJsonLiteral(JSON.stringify(value))
|
|
1144
1202
|
}
|
|
1145
1203
|
|
|
1146
1204
|
const GENERATED_MODULE_RELATIVE_PREFIXES = ['@/', '../../src/modules/', './'] as const
|
|
@@ -1382,32 +1440,81 @@ function collectCommandLoaderEntries(
|
|
|
1382
1440
|
return entries
|
|
1383
1441
|
}
|
|
1384
1442
|
|
|
1385
|
-
function renderCommandLoadersFile(entries: CommandLoaderGenerationEntry[]): string {
|
|
1443
|
+
export function renderCommandLoadersFile(entries: CommandLoaderGenerationEntry[]): string {
|
|
1386
1444
|
const seenCommandIds = new Map<string, string>()
|
|
1387
|
-
const
|
|
1445
|
+
const loaderEntries: WriterFunction[] = []
|
|
1388
1446
|
|
|
1389
1447
|
for (const entry of entries) {
|
|
1390
|
-
const
|
|
1448
|
+
const loadValue = arrowFunction({
|
|
1449
|
+
body: importExpression(sanitizeGeneratedModuleSpecifier(entry.importPath)),
|
|
1450
|
+
})
|
|
1391
1451
|
for (const id of entry.ids) {
|
|
1392
1452
|
const previous = seenCommandIds.get(id)
|
|
1393
1453
|
if (previous && previous !== entry.key) {
|
|
1394
1454
|
throw new Error(`[generate] Duplicate command id "${id}" discovered in "${previous}" and "${entry.key}"`)
|
|
1395
1455
|
}
|
|
1396
1456
|
seenCommandIds.set(id, entry.key)
|
|
1397
|
-
|
|
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
|
+
]))
|
|
1398
1463
|
}
|
|
1399
|
-
|
|
1464
|
+
loaderEntries.push(objectLiteral([
|
|
1465
|
+
{ name: 'moduleId', value: entry.moduleId },
|
|
1466
|
+
{ name: 'key', value: entry.key },
|
|
1467
|
+
{ name: 'load', value: loadValue },
|
|
1468
|
+
]))
|
|
1400
1469
|
}
|
|
1401
1470
|
|
|
1402
|
-
|
|
1403
|
-
|
|
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
|
+
})
|
|
1404
1479
|
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
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
|
+
})
|
|
1408
1495
|
|
|
1409
|
-
|
|
1410
|
-
|
|
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)
|
|
1411
1518
|
}
|
|
1412
1519
|
|
|
1413
1520
|
function serializeGeneratedImport(statement: GeneratedImportStatement): string {
|
|
@@ -1663,8 +1770,38 @@ function buildPageRouteProps(metaExpr: string, routePath: string): string {
|
|
|
1663
1770
|
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`
|
|
1664
1771
|
}
|
|
1665
1772
|
|
|
1666
|
-
function buildPageRouteManifestSpread(metaExpr: string, routePath: string):
|
|
1667
|
-
return
|
|
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
|
+
})
|
|
1668
1805
|
}
|
|
1669
1806
|
|
|
1670
1807
|
function normalizeBreadcrumb(raw: unknown): SerializablePageMetadata['breadcrumb'] {
|
|
@@ -1777,6 +1914,14 @@ function normalizeWorkerMetadata(raw: unknown): SerializableWorkerMetadata | nul
|
|
|
1777
1914
|
if (typeof source.concurrency === 'number') normalized.concurrency = source.concurrency
|
|
1778
1915
|
if (typeof source.lockDuration === 'number') normalized.lockDuration = source.lockDuration
|
|
1779
1916
|
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
|
+
}
|
|
1780
1925
|
return Object.keys(normalized).length > 0 ? normalized : null
|
|
1781
1926
|
}
|
|
1782
1927
|
|
|
@@ -1788,8 +1933,16 @@ async function loadSubscriberMetadata(sourceFile: string): Promise<SerializableS
|
|
|
1788
1933
|
|
|
1789
1934
|
async function loadWorkerMetadata(sourceFile: string): Promise<SerializableWorkerMetadata | null> {
|
|
1790
1935
|
const sourceModule = await loadModuleExportsFromSource<Record<string, unknown>>(sourceFile)
|
|
1791
|
-
|
|
1936
|
+
const metadata = normalizeWorkerMetadata(sourceModule?.metadata)
|
|
1792
1937
|
?? 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
|
|
1793
1946
|
}
|
|
1794
1947
|
|
|
1795
1948
|
async function discoverSubscribers(
|
|
@@ -1974,7 +2127,7 @@ async function processPageFiles(options: {
|
|
|
1974
2127
|
const metaPrefix = type === 'frontend' ? 'M' : 'BM'
|
|
1975
2128
|
const eagerRoutes: string[] = []
|
|
1976
2129
|
const runtimeRoutes: string[] = []
|
|
1977
|
-
const manifestRoutes:
|
|
2130
|
+
const manifestRoutes: WriterFunction[] = []
|
|
1978
2131
|
const manifestShardRoutes: RouteManifestShardEntry[] = []
|
|
1979
2132
|
const metadataRoutes: RouteManifestShardEntry[] = []
|
|
1980
2133
|
const routePatterns: string[] = []
|
|
@@ -2049,18 +2202,25 @@ async function processPageFiles(options: {
|
|
|
2049
2202
|
const manifestBaseProps = buildPageRouteManifestSpread(manifestMetaExpr, routePath)
|
|
2050
2203
|
eagerRoutes.push(`{ ${baseProps}, Component: ${buildLazyRouteComponentExpression(importPath)} }`)
|
|
2051
2204
|
runtimeRoutes.push(`{ ${runtimeBaseProps}, Component: ${buildLazyRouteComponentExpression(importPath)} }`)
|
|
2052
|
-
const
|
|
2053
|
-
|
|
2054
|
-
|
|
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)
|
|
2055
2215
|
manifestShardRoutes.push({
|
|
2056
2216
|
path: routePath,
|
|
2057
|
-
declaration:
|
|
2058
|
-
imports:
|
|
2217
|
+
declaration: renderGeneratedValue(manifestEntry),
|
|
2218
|
+
imports: shardImports,
|
|
2059
2219
|
})
|
|
2060
2220
|
metadataRoutes.push({
|
|
2061
2221
|
path: routePath,
|
|
2062
|
-
declaration:
|
|
2063
|
-
imports:
|
|
2222
|
+
declaration: renderGeneratedValue(objectLiteral(metadataEntries)),
|
|
2223
|
+
imports: shardImports,
|
|
2064
2224
|
})
|
|
2065
2225
|
routePatterns.push(routePath)
|
|
2066
2226
|
}
|
|
@@ -2140,18 +2300,25 @@ async function processPageFiles(options: {
|
|
|
2140
2300
|
const manifestBaseProps = buildPageRouteManifestSpread(manifestMetaExpr, routePath)
|
|
2141
2301
|
eagerRoutes.push(`{ ${baseProps}, Component: ${buildLazyRouteComponentExpression(importPath)} }`)
|
|
2142
2302
|
runtimeRoutes.push(`{ ${runtimeBaseProps}, Component: ${buildLazyRouteComponentExpression(importPath)} }`)
|
|
2143
|
-
const
|
|
2144
|
-
|
|
2145
|
-
|
|
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)
|
|
2146
2313
|
manifestShardRoutes.push({
|
|
2147
2314
|
path: routePath,
|
|
2148
|
-
declaration:
|
|
2149
|
-
imports:
|
|
2315
|
+
declaration: renderGeneratedValue(manifestEntry),
|
|
2316
|
+
imports: shardImports,
|
|
2150
2317
|
})
|
|
2151
2318
|
metadataRoutes.push({
|
|
2152
2319
|
path: routePath,
|
|
2153
|
-
declaration:
|
|
2154
|
-
imports:
|
|
2320
|
+
declaration: renderGeneratedValue(objectLiteral(metadataEntries)),
|
|
2321
|
+
imports: shardImports,
|
|
2155
2322
|
})
|
|
2156
2323
|
routePatterns.push(routePath)
|
|
2157
2324
|
}
|
|
@@ -2183,7 +2350,7 @@ async function processApiRoutes(options: {
|
|
|
2183
2350
|
|
|
2184
2351
|
const eagerApis: string[] = []
|
|
2185
2352
|
const runtimeApis: string[] = []
|
|
2186
|
-
const manifestApis:
|
|
2353
|
+
const manifestApis: WriterFunction[] = []
|
|
2187
2354
|
const manifestShardApis: RouteManifestShardEntry[] = []
|
|
2188
2355
|
const metadataApis: RouteManifestShardEntry[] = []
|
|
2189
2356
|
|
|
@@ -2216,11 +2383,19 @@ async function processApiRoutes(options: {
|
|
|
2216
2383
|
eagerImports.push(buildImportStatement(`* as ${importName}`, importPath))
|
|
2217
2384
|
eagerApis.push(`{ path: ((${importName} as any).metadata?.path ?? ${toLiteral(routePath)}), metadata: (${importName} as any).metadata, handlers: ${importName} as any${docsPart} }`)
|
|
2218
2385
|
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(', ')} } }`)
|
|
2219
|
-
const
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
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)) })
|
|
2224
2399
|
}
|
|
2225
2400
|
|
|
2226
2401
|
// Single files (plain scripts, not route.*, not tests, skip method dirs)
|
|
@@ -2252,11 +2427,19 @@ async function processApiRoutes(options: {
|
|
|
2252
2427
|
eagerImports.push(buildImportStatement(`* as ${importName}`, importPath))
|
|
2253
2428
|
eagerApis.push(`{ path: ((${importName} as any).metadata?.path ?? ${toLiteral(routePath)}), metadata: (${importName} as any).metadata, handlers: ${importName} as any${docsPart} }`)
|
|
2254
2429
|
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(', ')} } }`)
|
|
2255
|
-
const
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
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)) })
|
|
2260
2443
|
}
|
|
2261
2444
|
|
|
2262
2445
|
// Legacy per-method
|
|
@@ -2298,11 +2481,20 @@ async function processApiRoutes(options: {
|
|
|
2298
2481
|
eagerImports.push(buildImportStatement(`${importName}, * as ${metaName}`, importPath))
|
|
2299
2482
|
eagerApis.push(`{ method: ${toLiteral(method)}, path: (${metaName}.metadata?.path ?? ${toLiteral(routePath)}), handler: ${importName}, metadata: ${metaName}.metadata${docsPart} }`)
|
|
2300
2483
|
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} }`)
|
|
2301
|
-
const
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
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)) })
|
|
2306
2498
|
}
|
|
2307
2499
|
}
|
|
2308
2500
|
|
|
@@ -2315,62 +2507,17 @@ async function processApiRoutes(options: {
|
|
|
2315
2507
|
}
|
|
2316
2508
|
}
|
|
2317
2509
|
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
return subscribers
|
|
2327
|
-
}
|
|
2328
|
-
|
|
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
|
-
}
|
|
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'
|
|
2339
2518
|
|
|
2340
|
-
function
|
|
2341
|
-
discovered
|
|
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
|
|
2519
|
+
function workersDeclareAbandonHook(discovered: DiscoveredWorker[]): boolean {
|
|
2520
|
+
return discovered.some(({ metadata }) => metadata.hasJobAbandonedHook === true)
|
|
2374
2521
|
}
|
|
2375
2522
|
|
|
2376
2523
|
/**
|
|
@@ -2517,6 +2664,64 @@ function buildModuleDashboardWidgetsValue(entries: DashboardWidgetEntry[]): Writ
|
|
|
2517
2664
|
)
|
|
2518
2665
|
}
|
|
2519
2666
|
|
|
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
|
+
|
|
2520
2725
|
function buildModuleIntegrationListValue(
|
|
2521
2726
|
importName: string,
|
|
2522
2727
|
pluralMember: string,
|
|
@@ -2564,9 +2769,15 @@ function buildModulesInfoExpression(): WriterFunction {
|
|
|
2564
2769
|
function renderAstModuleRegistryFile(options: {
|
|
2565
2770
|
fileName: string
|
|
2566
2771
|
generator: string
|
|
2567
|
-
|
|
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[]
|
|
2568
2778
|
moduleEntries: WriterFunction[]
|
|
2569
2779
|
includeCreateElementImport?: boolean
|
|
2780
|
+
includeAbandonHookImport?: boolean
|
|
2570
2781
|
}): string {
|
|
2571
2782
|
const sourceFile = createGeneratedSourceFile(options.fileName)
|
|
2572
2783
|
addAutoGeneratedComment(sourceFile, options.generator)
|
|
@@ -2582,10 +2793,11 @@ function renderAstModuleRegistryFile(options: {
|
|
|
2582
2793
|
namedImports: [
|
|
2583
2794
|
{ name: 'createLazyModuleSubscriber' },
|
|
2584
2795
|
{ name: 'createLazyModuleWorker' },
|
|
2796
|
+
...(options.includeAbandonHookImport ? [{ name: ABANDON_HOOK_FACTORY }] : []),
|
|
2585
2797
|
{ name: 'Module', isTypeOnly: true },
|
|
2586
2798
|
],
|
|
2587
2799
|
})
|
|
2588
|
-
addImportStatements(sourceFile, options.imports)
|
|
2800
|
+
addImportStatements(sourceFile, options.imports.map((entry) => serializeGeneratedImport(entry)))
|
|
2589
2801
|
|
|
2590
2802
|
sourceFile.addVariableStatement({
|
|
2591
2803
|
declarationKind: VariableDeclarationKind.Const,
|
|
@@ -2616,6 +2828,52 @@ function renderAstModuleRegistryFile(options: {
|
|
|
2616
2828
|
return getSourceText(sourceFile)
|
|
2617
2829
|
}
|
|
2618
2830
|
|
|
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
|
+
|
|
2619
2877
|
function renderAstLegacyAliasFile(options: {
|
|
2620
2878
|
fileName: string
|
|
2621
2879
|
exportName: string
|
|
@@ -2679,62 +2937,10 @@ function renderEnabledModuleIdsFile(moduleIds: readonly string[]): string {
|
|
|
2679
2937
|
return getSourceText(sourceFile)
|
|
2680
2938
|
}
|
|
2681
2939
|
|
|
2682
|
-
function renderLegacyCompatibleArray(entries: readonly string[]): string {
|
|
2683
|
-
return `[
|
|
2684
|
-
${entries.join(',\n ')}
|
|
2685
|
-
]`
|
|
2686
|
-
}
|
|
2687
|
-
|
|
2688
2940
|
function buildLazyRouteComponentExpression(importPath: string): string {
|
|
2689
2941
|
return `async (props: any) => { const mod = await ${buildDynamicImportExpression(importPath)}; const Component = (mod.default ?? mod) as any; return createElement(Component, props) }`
|
|
2690
2942
|
}
|
|
2691
2943
|
|
|
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
|
-
|
|
2738
2944
|
function buildRuntimeRouteComponent(importPath: string): WriterFunction {
|
|
2739
2945
|
return arrowFunction({
|
|
2740
2946
|
async: true,
|
|
@@ -3009,8 +3215,21 @@ function processWorkersAst(discovered: DiscoveredWorker[]): WriterFunction[] {
|
|
|
3009
3215
|
{ name: 'id', value: workerId },
|
|
3010
3216
|
{ name: 'queue', value: metadata.queue },
|
|
3011
3217
|
{ 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
|
+
: []),
|
|
3012
3222
|
...(metadata.lockDuration === undefined ? [] : [{ name: 'lockDuration', value: metadata.lockDuration }]),
|
|
3013
3223
|
...(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
|
+
: []),
|
|
3014
3233
|
{
|
|
3015
3234
|
name: 'handler',
|
|
3016
3235
|
value: callExpression(identifier('createLazyModuleWorker'), [
|
|
@@ -3370,11 +3589,11 @@ async function generateModuleRegistryFromDiscovery(options: ModuleRegistryRender
|
|
|
3370
3589
|
const runtimeImports: string[] = []
|
|
3371
3590
|
const frontendRouteManifestImports: string[] = []
|
|
3372
3591
|
const backendRouteManifestImports: string[] = []
|
|
3373
|
-
const moduleDecls:
|
|
3374
|
-
const runtimeModuleDecls:
|
|
3375
|
-
const frontendRouteManifestDecls:
|
|
3376
|
-
const backendRouteManifestDecls:
|
|
3377
|
-
const apiRouteManifestDecls:
|
|
3592
|
+
const moduleDecls: WriterFunction[] = []
|
|
3593
|
+
const runtimeModuleDecls: WriterFunction[] = []
|
|
3594
|
+
const frontendRouteManifestDecls: WriterFunction[] = []
|
|
3595
|
+
const backendRouteManifestDecls: WriterFunction[] = []
|
|
3596
|
+
const apiRouteManifestDecls: WriterFunction[] = []
|
|
3378
3597
|
const backendRouteShardEntries: RouteManifestShardEntry[] = []
|
|
3379
3598
|
const backendRouteMetadataEntries: RouteManifestShardEntry[] = []
|
|
3380
3599
|
const apiRouteShardEntries: RouteManifestShardEntry[] = []
|
|
@@ -3384,6 +3603,7 @@ async function generateModuleRegistryFromDiscovery(options: ModuleRegistryRender
|
|
|
3384
3603
|
const requiresByModule = new Map<string, string[]>()
|
|
3385
3604
|
const commandLoaderEntries: CommandLoaderGenerationEntry[] = []
|
|
3386
3605
|
let hasRouteComponents = false
|
|
3606
|
+
let hasAbandonHookWorkers = false
|
|
3387
3607
|
|
|
3388
3608
|
// UMES conflict detection: collect file paths during module processing
|
|
3389
3609
|
const umesConflictSources: Array<{
|
|
@@ -3406,9 +3626,9 @@ async function generateModuleRegistryFromDiscovery(options: ModuleRegistryRender
|
|
|
3406
3626
|
const runtimeBackendRoutes: string[] = []
|
|
3407
3627
|
const runtimeApis: string[] = []
|
|
3408
3628
|
let cliImportName: string | null = null
|
|
3409
|
-
const translations:
|
|
3410
|
-
const subscribers:
|
|
3411
|
-
const workers:
|
|
3629
|
+
const translations: GeneratedObjectEntry[] = []
|
|
3630
|
+
const subscribers: WriterFunction[] = []
|
|
3631
|
+
const workers: WriterFunction[] = []
|
|
3412
3632
|
let infoImportName: string | null = null
|
|
3413
3633
|
let extensionsImportName: string | null = null
|
|
3414
3634
|
let fieldsImportName: string | null = null
|
|
@@ -3622,7 +3842,7 @@ async function generateModuleRegistryFromDiscovery(options: ModuleRegistryRender
|
|
|
3622
3842
|
}
|
|
3623
3843
|
|
|
3624
3844
|
// 16. Translations
|
|
3625
|
-
translations.push(...
|
|
3845
|
+
translations.push(...processTranslationsAst({
|
|
3626
3846
|
discovered: discovered.translations,
|
|
3627
3847
|
modId,
|
|
3628
3848
|
appImportBase,
|
|
@@ -3632,10 +3852,12 @@ async function generateModuleRegistryFromDiscovery(options: ModuleRegistryRender
|
|
|
3632
3852
|
}))
|
|
3633
3853
|
|
|
3634
3854
|
// 17. Subscribers
|
|
3635
|
-
subscribers.push(...
|
|
3855
|
+
subscribers.push(...processSubscribersAst(await discovered.getSubscribers()))
|
|
3636
3856
|
|
|
3637
3857
|
// 18. Workers
|
|
3638
|
-
|
|
3858
|
+
const discoveredWorkersForModule = await discovered.getWorkers()
|
|
3859
|
+
if (workersDeclareAbandonHook(discoveredWorkersForModule)) hasAbandonHookWorkers = true
|
|
3860
|
+
workers.push(...processWorkersAst(discoveredWorkersForModule))
|
|
3639
3861
|
|
|
3640
3862
|
// Build combined customFieldSets expression
|
|
3641
3863
|
{
|
|
@@ -3658,44 +3880,120 @@ async function generateModuleRegistryFromDiscovery(options: ModuleRegistryRender
|
|
|
3658
3880
|
}
|
|
3659
3881
|
}
|
|
3660
3882
|
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
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))
|
|
3699
3997
|
}
|
|
3700
3998
|
|
|
3701
3999
|
// === UMES Conflict Detection ===
|
|
@@ -3796,33 +4094,37 @@ async function generateModuleRegistryFromDiscovery(options: ModuleRegistryRender
|
|
|
3796
4094
|
}
|
|
3797
4095
|
}
|
|
3798
4096
|
|
|
3799
|
-
const output =
|
|
4097
|
+
const output = renderAstModuleRegistryFile({
|
|
3800
4098
|
fileName: 'modules.generated.ts',
|
|
4099
|
+
generator: 'registry',
|
|
3801
4100
|
imports,
|
|
3802
4101
|
moduleEntries: moduleDecls,
|
|
3803
4102
|
includeCreateElementImport: hasRouteComponents,
|
|
4103
|
+
includeAbandonHookImport: hasAbandonHookWorkers,
|
|
3804
4104
|
})
|
|
3805
|
-
const runtimeOutput =
|
|
4105
|
+
const runtimeOutput = renderAstModuleRegistryFile({
|
|
3806
4106
|
fileName: 'modules.runtime.generated.ts',
|
|
4107
|
+
generator: 'registry',
|
|
3807
4108
|
imports: runtimeImports,
|
|
3808
4109
|
moduleEntries: runtimeModuleDecls,
|
|
3809
4110
|
includeCreateElementImport: true,
|
|
4111
|
+
includeAbandonHookImport: hasAbandonHookWorkers,
|
|
3810
4112
|
})
|
|
3811
|
-
const frontendRoutesOutput =
|
|
4113
|
+
const frontendRoutesOutput = renderAstManifestFile({
|
|
3812
4114
|
fileName: 'frontend-routes.generated.ts',
|
|
3813
4115
|
typeName: 'FrontendRouteManifestEntry',
|
|
3814
4116
|
exportName: 'frontendRoutes',
|
|
3815
4117
|
imports: frontendRouteManifestImports,
|
|
3816
4118
|
entries: frontendRouteManifestDecls,
|
|
3817
4119
|
})
|
|
3818
|
-
const backendRoutesOutput =
|
|
4120
|
+
const backendRoutesOutput = renderAstManifestFile({
|
|
3819
4121
|
fileName: 'backend-routes.generated.ts',
|
|
3820
4122
|
typeName: 'BackendRouteManifestEntry',
|
|
3821
4123
|
exportName: 'backendRoutes',
|
|
3822
4124
|
imports: backendRouteManifestImports,
|
|
3823
4125
|
entries: backendRouteManifestDecls,
|
|
3824
4126
|
})
|
|
3825
|
-
const apiRoutesOutput =
|
|
4127
|
+
const apiRoutesOutput = renderAstManifestFile({
|
|
3826
4128
|
fileName: 'api-routes.generated.ts',
|
|
3827
4129
|
typeName: 'ApiRouteManifestEntry',
|
|
3828
4130
|
exportName: 'apiRoutes',
|
|
@@ -3962,15 +4264,11 @@ async function generateModuleRegistryFromDiscovery(options: ModuleRegistryRender
|
|
|
3962
4264
|
allRegImports.push(...reg.registrationImports)
|
|
3963
4265
|
allCalls.push(reg.buildCall(reg.entriesExportName))
|
|
3964
4266
|
}
|
|
3965
|
-
const
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
export function runBootstrapRegistrations(): void {
|
|
3971
|
-
${body}
|
|
3972
|
-
}
|
|
3973
|
-
`
|
|
4267
|
+
const bootstrapRegsOutput = renderBootstrapRegistrationsFile({
|
|
4268
|
+
entryImports: allEntryImports,
|
|
4269
|
+
registrationImports: allRegImports,
|
|
4270
|
+
calls: allCalls,
|
|
4271
|
+
})
|
|
3974
4272
|
writeGeneratedFile({ outFile: bootstrapRegsOutFile, checksumFile: bootstrapRegsChecksumFile, content: bootstrapRegsOutput, structureChecksum, result, quiet })
|
|
3975
4273
|
}
|
|
3976
4274
|
|
|
@@ -4010,6 +4308,7 @@ async function generateModuleRegistryAppFromDiscovery(options: ModuleRegistryRen
|
|
|
4010
4308
|
const requiresByModule = new Map<string, string[]>()
|
|
4011
4309
|
const commandLoaderEntries: CommandLoaderGenerationEntry[] = []
|
|
4012
4310
|
let hasRouteComponents = false
|
|
4311
|
+
let hasAbandonHookWorkers = false
|
|
4013
4312
|
const seenBackendRoutePatterns = new Map<string, string>()
|
|
4014
4313
|
|
|
4015
4314
|
for (const discovered of discovery.modules) {
|
|
@@ -4171,7 +4470,9 @@ async function generateModuleRegistryAppFromDiscovery(options: ModuleRegistryRen
|
|
|
4171
4470
|
|
|
4172
4471
|
subscribers.push(...processSubscribersAst(await discovered.getSubscribers()))
|
|
4173
4472
|
|
|
4174
|
-
|
|
4473
|
+
const discoveredWorkersForModule = await discovered.getWorkers()
|
|
4474
|
+
if (workersDeclareAbandonHook(discoveredWorkersForModule)) hasAbandonHookWorkers = true
|
|
4475
|
+
workers.push(...processWorkersAst(discoveredWorkersForModule))
|
|
4175
4476
|
|
|
4176
4477
|
{
|
|
4177
4478
|
dashboardWidgetsValue = buildModuleDashboardWidgetsValue(discovered.dashboardWidgetEntries)
|
|
@@ -4295,18 +4596,21 @@ async function generateModuleRegistryAppFromDiscovery(options: ModuleRegistryRen
|
|
|
4295
4596
|
imports,
|
|
4296
4597
|
moduleEntries: moduleDecls,
|
|
4297
4598
|
includeCreateElementImport: hasRouteComponents,
|
|
4599
|
+
includeAbandonHookImport: hasAbandonHookWorkers,
|
|
4298
4600
|
})
|
|
4299
4601
|
const bootstrapOutput = renderAstModuleRegistryFile({
|
|
4300
4602
|
fileName: 'modules.bootstrap.generated.ts',
|
|
4301
4603
|
generator: 'registry (bootstrap version)',
|
|
4302
4604
|
imports: bootstrapImports,
|
|
4303
4605
|
moduleEntries: bootstrapModuleDecls,
|
|
4606
|
+
includeAbandonHookImport: hasAbandonHookWorkers,
|
|
4304
4607
|
})
|
|
4305
4608
|
const i18nOutput = renderAstModuleRegistryFile({
|
|
4306
4609
|
fileName: 'modules.i18n.generated.ts',
|
|
4307
4610
|
generator: 'registry (i18n version)',
|
|
4308
4611
|
imports: i18nImports,
|
|
4309
4612
|
moduleEntries: i18nModuleDecls,
|
|
4613
|
+
includeAbandonHookImport: hasAbandonHookWorkers,
|
|
4310
4614
|
})
|
|
4311
4615
|
const legacyOutput = renderAstLegacyAliasFile({
|
|
4312
4616
|
fileName: 'bootstrap-modules.generated.ts',
|
|
@@ -4397,6 +4701,7 @@ async function generateModuleRegistryAppFromDiscovery(options: ModuleRegistryRen
|
|
|
4397
4701
|
* Excludes: frontend routes, backend routes, API handlers, injection widgets
|
|
4398
4702
|
*/
|
|
4399
4703
|
async function generateModuleRegistryCliFromDiscovery(options: ModuleRegistryRenderOptions): Promise<GeneratorResult> {
|
|
4704
|
+
let hasAbandonHookWorkers = false
|
|
4400
4705
|
const { resolver, quiet = false } = options
|
|
4401
4706
|
const { discovery } = options
|
|
4402
4707
|
const result = createGeneratorResult()
|
|
@@ -4548,6 +4853,7 @@ async function generateModuleRegistryCliFromDiscovery(options: ModuleRegistryRen
|
|
|
4548
4853
|
// Workers
|
|
4549
4854
|
const discoveredWorkers = await discovered.getWorkers()
|
|
4550
4855
|
const workerGenerationEntries = collectWorkerGenerationEntries(discoveredWorkers, modId)
|
|
4856
|
+
if (workersDeclareAbandonHook(discoveredWorkers)) hasAbandonHookWorkers = true
|
|
4551
4857
|
workers.push(...processWorkersAst(discoveredWorkers))
|
|
4552
4858
|
devSupervisorWorkers.push(...workerGenerationEntries.map(({ importPath: _importPath, ...worker }) => worker))
|
|
4553
4859
|
|
|
@@ -4671,6 +4977,7 @@ async function generateModuleRegistryCliFromDiscovery(options: ModuleRegistryRen
|
|
|
4671
4977
|
generator: 'registry (CLI version)',
|
|
4672
4978
|
imports,
|
|
4673
4979
|
moduleEntries: moduleDecls,
|
|
4980
|
+
includeAbandonHookImport: hasAbandonHookWorkers,
|
|
4674
4981
|
})
|
|
4675
4982
|
const legacyOutput = renderAstLegacyAliasFile({
|
|
4676
4983
|
fileName: 'cli-modules.generated.ts',
|