@open-mercato/cli 0.6.8-develop.7016.1.4ed7b1e49d → 0.6.8-develop.7020.1.d14d757582

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 (79) hide show
  1. package/dist/agentic/guides/module-facts.json +130 -130
  2. package/dist/agentic/guides/module-facts.v2.json +130 -130
  3. package/dist/agentic/guides/modules/ai_assistant/index.md +1 -1
  4. package/dist/agentic/guides/modules/api_docs/index.md +1 -1
  5. package/dist/agentic/guides/modules/api_keys/index.md +1 -1
  6. package/dist/agentic/guides/modules/attachments/index.md +1 -1
  7. package/dist/agentic/guides/modules/audit_logs/index.md +1 -1
  8. package/dist/agentic/guides/modules/auth/index.md +1 -1
  9. package/dist/agentic/guides/modules/business_rules/index.md +1 -1
  10. package/dist/agentic/guides/modules/catalog/index.md +1 -1
  11. package/dist/agentic/guides/modules/channel_apns/index.md +1 -1
  12. package/dist/agentic/guides/modules/channel_expo/index.md +1 -1
  13. package/dist/agentic/guides/modules/channel_fcm/index.md +1 -1
  14. package/dist/agentic/guides/modules/channel_gmail/index.md +1 -1
  15. package/dist/agentic/guides/modules/channel_imap/index.md +1 -1
  16. package/dist/agentic/guides/modules/checkout/index.md +1 -1
  17. package/dist/agentic/guides/modules/communication_channels/index.md +1 -1
  18. package/dist/agentic/guides/modules/configs/index.md +1 -1
  19. package/dist/agentic/guides/modules/content/index.md +1 -1
  20. package/dist/agentic/guides/modules/currencies/index.md +1 -1
  21. package/dist/agentic/guides/modules/customer_accounts/index.md +1 -1
  22. package/dist/agentic/guides/modules/customers/index.md +1 -1
  23. package/dist/agentic/guides/modules/dashboards/index.md +1 -1
  24. package/dist/agentic/guides/modules/data_sync/exact-override-targets.md +2 -2
  25. package/dist/agentic/guides/modules/data_sync/index.md +1 -1
  26. package/dist/agentic/guides/modules/data_sync/workers.md +2 -2
  27. package/dist/agentic/guides/modules/design_system/index.md +1 -1
  28. package/dist/agentic/guides/modules/devices/index.md +1 -1
  29. package/dist/agentic/guides/modules/dictionaries/index.md +1 -1
  30. package/dist/agentic/guides/modules/directory/index.md +1 -1
  31. package/dist/agentic/guides/modules/documents/index.md +1 -1
  32. package/dist/agentic/guides/modules/entities/index.md +1 -1
  33. package/dist/agentic/guides/modules/eudr/index.md +1 -1
  34. package/dist/agentic/guides/modules/events/index.md +1 -1
  35. package/dist/agentic/guides/modules/feature_toggles/index.md +1 -1
  36. package/dist/agentic/guides/modules/gateway_stripe/index.md +1 -1
  37. package/dist/agentic/guides/modules/generators/index.md +1 -1
  38. package/dist/agentic/guides/modules/inbox_ops/index.md +1 -1
  39. package/dist/agentic/guides/modules/integrations/index.md +1 -1
  40. package/dist/agentic/guides/modules/messages/index.md +1 -1
  41. package/dist/agentic/guides/modules/notifications/index.md +1 -1
  42. package/dist/agentic/guides/modules/onboarding/index.md +1 -1
  43. package/dist/agentic/guides/modules/payment_gateways/index.md +1 -1
  44. package/dist/agentic/guides/modules/perspectives/index.md +1 -1
  45. package/dist/agentic/guides/modules/planner/index.md +1 -1
  46. package/dist/agentic/guides/modules/portal/index.md +1 -1
  47. package/dist/agentic/guides/modules/progress/index.md +1 -1
  48. package/dist/agentic/guides/modules/push_notifications/index.md +1 -1
  49. package/dist/agentic/guides/modules/query_index/index.md +1 -1
  50. package/dist/agentic/guides/modules/record_locks/index.md +1 -1
  51. package/dist/agentic/guides/modules/resources/index.md +1 -1
  52. package/dist/agentic/guides/modules/sales/index.md +1 -1
  53. package/dist/agentic/guides/modules/scheduler/index.md +1 -1
  54. package/dist/agentic/guides/modules/search/index.md +1 -1
  55. package/dist/agentic/guides/modules/security/index.md +1 -1
  56. package/dist/agentic/guides/modules/shipping_carriers/index.md +1 -1
  57. package/dist/agentic/guides/modules/sso/index.md +1 -1
  58. package/dist/agentic/guides/modules/staff/index.md +1 -1
  59. package/dist/agentic/guides/modules/storage_s3/index.md +1 -1
  60. package/dist/agentic/guides/modules/sync_akeneo/index.md +1 -1
  61. package/dist/agentic/guides/modules/sync_excel/index.md +1 -1
  62. package/dist/agentic/guides/modules/system_status_overlays/index.md +1 -1
  63. package/dist/agentic/guides/modules/translations/index.md +1 -1
  64. package/dist/agentic/guides/modules/warranty_claims/index.md +1 -1
  65. package/dist/agentic/guides/modules/webhooks/index.md +1 -1
  66. package/dist/agentic/guides/modules/wms/index.md +1 -1
  67. package/dist/agentic/guides/modules/workflows/index.md +1 -1
  68. package/dist/agentic/guides/reference-module-facts.json +1 -1
  69. package/dist/agentic/guides/upstream/manifest.json +1 -1
  70. package/dist/lib/generators/module-registry.js +77 -11
  71. package/dist/lib/generators/module-registry.js.map +2 -2
  72. package/dist/mercato.js +14 -0
  73. package/dist/mercato.js.map +2 -2
  74. package/package.json +6 -6
  75. package/src/__tests__/resolve-queue-abandon-hook.test.ts +39 -0
  76. package/src/lib/generators/__tests__/generated-registry-imports.test.ts +128 -0
  77. package/src/lib/generators/__tests__/metadata-extraction.test.ts +44 -0
  78. package/src/lib/generators/module-registry.ts +118 -5
  79. package/src/mercato.ts +27 -0
@@ -320,6 +320,38 @@ function extractObjectPropertiesFromAst(sourceFile, exportName) {
320
320
  }
321
321
  return Object.keys(result).length > 0 ? result : null;
322
322
  }
323
+ function namedObjectLiteralDeclaresProperty(sourceFile, exportName, propertyName) {
324
+ let source = "";
325
+ try {
326
+ source = fs.readFileSync(sourceFile, "utf8");
327
+ } catch {
328
+ return false;
329
+ }
330
+ const parsed = ts.createSourceFile(sourceFile, source, ts.ScriptTarget.Latest, true, inferScriptKind(sourceFile));
331
+ const exportedNames = /* @__PURE__ */ new Set();
332
+ for (const statement of parsed.statements) {
333
+ if (ts.isExportDeclaration(statement) && statement.exportClause && ts.isNamedExports(statement.exportClause) && !statement.moduleSpecifier) {
334
+ for (const element of statement.exportClause.elements) exportedNames.add(element.name.text);
335
+ }
336
+ }
337
+ for (const statement of parsed.statements) {
338
+ if (!ts.isVariableStatement(statement)) continue;
339
+ const exportsDirectly = (ts.canHaveModifiers(statement) ? ts.getModifiers(statement) : void 0)?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword);
340
+ for (const declaration of statement.declarationList.declarations) {
341
+ if (!ts.isIdentifier(declaration.name) || declaration.name.text !== exportName) continue;
342
+ if (!exportsDirectly && !exportedNames.has(exportName)) continue;
343
+ const objectLiteral2 = unwrapObjectLiteralExpression(declaration.initializer);
344
+ if (!objectLiteral2) return false;
345
+ return objectLiteral2.properties.some((property) => {
346
+ const name = property.name;
347
+ if (!name) return false;
348
+ const key = ts.isIdentifier(name) ? name.text : ts.isStringLiteral(name) ? name.text : null;
349
+ return key === propertyName;
350
+ });
351
+ }
352
+ }
353
+ return false;
354
+ }
323
355
  function extractNamedObjectLiteralExport(sourceFile, exportName) {
324
356
  const literal = extractNamedObjectLiteralSource(sourceFile, exportName);
325
357
  if (literal) {
@@ -1238,6 +1270,7 @@ function normalizeWorkerMetadata(raw) {
1238
1270
  if (typeof source.concurrency === "number") normalized.concurrency = source.concurrency;
1239
1271
  if (typeof source.lockDuration === "number") normalized.lockDuration = source.lockDuration;
1240
1272
  if (typeof source.maxStalledCount === "number") normalized.maxStalledCount = source.maxStalledCount;
1273
+ if (typeof source.onJobAbandoned === "function") normalized.hasJobAbandonedHook = true;
1241
1274
  return Object.keys(normalized).length > 0 ? normalized : null;
1242
1275
  }
1243
1276
  async function loadSubscriberMetadata(sourceFile) {
@@ -1246,7 +1279,12 @@ async function loadSubscriberMetadata(sourceFile) {
1246
1279
  }
1247
1280
  async function loadWorkerMetadata(sourceFile) {
1248
1281
  const sourceModule = await loadModuleExportsFromSource(sourceFile);
1249
- return normalizeWorkerMetadata(sourceModule?.metadata) ?? normalizeWorkerMetadata(extractNamedObjectLiteralExport(sourceFile, "metadata"));
1282
+ const metadata = normalizeWorkerMetadata(sourceModule?.metadata) ?? normalizeWorkerMetadata(extractNamedObjectLiteralExport(sourceFile, "metadata"));
1283
+ if (!metadata) return null;
1284
+ if (!metadata.hasJobAbandonedHook && namedObjectLiteralDeclaresProperty(sourceFile, "metadata", "onJobAbandoned")) {
1285
+ metadata.hasJobAbandonedHook = true;
1286
+ }
1287
+ return metadata;
1250
1288
  }
1251
1289
  async function discoverSubscribers(roots, imps, modId) {
1252
1290
  const subscribers = [];
@@ -1708,12 +1746,16 @@ function processSubscribers(discovered) {
1708
1746
  }
1709
1747
  return subscribers;
1710
1748
  }
1749
+ const ABANDON_HOOK_FACTORY = "createLazyModuleWorkerAbandonHook";
1750
+ function workersDeclareAbandonHook(discovered) {
1751
+ return discovered.some(({ metadata }) => metadata.hasJobAbandonedHook === true);
1752
+ }
1711
1753
  function processWorkers(discovered) {
1712
1754
  const workers = [];
1713
1755
  for (const { id, importPath, metadata } of discovered) {
1714
1756
  const workerId = metadata.id ?? id;
1715
1757
  workers.push(
1716
- `{ id: ${toLiteral(workerId)}, queue: ${toLiteral(metadata.queue)}, concurrency: ${toLiteral(metadata.concurrency ?? 1)}${metadata.lockDuration === void 0 ? "" : `, lockDuration: ${toLiteral(metadata.lockDuration)}`}${metadata.maxStalledCount === void 0 ? "" : `, maxStalledCount: ${toLiteral(metadata.maxStalledCount)}`}, handler: createLazyModuleWorker(() => ${buildDynamicImportExpression(importPath)}, ${toLiteral(workerId)}) }`
1758
+ `{ id: ${toLiteral(workerId)}, queue: ${toLiteral(metadata.queue)}, concurrency: ${toLiteral(metadata.concurrency ?? 1)}${metadata.lockDuration === void 0 ? "" : `, lockDuration: ${toLiteral(metadata.lockDuration)}`}${metadata.maxStalledCount === void 0 ? "" : `, maxStalledCount: ${toLiteral(metadata.maxStalledCount)}`}${metadata.hasJobAbandonedHook ? `, onJobAbandoned: ${ABANDON_HOOK_FACTORY}(() => ${buildDynamicImportExpression(importPath)}, ${toLiteral(workerId)})` : ""}, handler: createLazyModuleWorker(() => ${buildDynamicImportExpression(importPath)}, ${toLiteral(workerId)}) }`
1717
1759
  );
1718
1760
  }
1719
1761
  return workers;
@@ -1920,6 +1962,7 @@ function renderAstModuleRegistryFile(options) {
1920
1962
  namedImports: [
1921
1963
  { name: "createLazyModuleSubscriber" },
1922
1964
  { name: "createLazyModuleWorker" },
1965
+ ...options.includeAbandonHookImport ? [{ name: ABANDON_HOOK_FACTORY }] : [],
1923
1966
  { name: "Module", isTypeOnly: true }
1924
1967
  ]
1925
1968
  });
@@ -2010,9 +2053,10 @@ function buildLazyRouteComponentExpression(importPath) {
2010
2053
  return `async (props: any) => { const mod = await ${buildDynamicImportExpression(importPath)}; const Component = (mod.default ?? mod) as any; return createElement(Component, props) }`;
2011
2054
  }
2012
2055
  function renderAstLegacyModuleRegistryOutput(options) {
2056
+ const needsAbandonHookImport = options.includeAbandonHookImport || options.moduleEntries.some((entry) => entry.includes(ABANDON_HOOK_FACTORY));
2013
2057
  const importSection = [
2014
2058
  ...options.includeCreateElementImport ? ["import { createElement } from 'react'"] : [],
2015
- "import { createLazyModuleSubscriber, createLazyModuleWorker, type Module } from '@open-mercato/shared/modules/registry'",
2059
+ needsAbandonHookImport ? "import { createLazyModuleSubscriber, createLazyModuleWorker, createLazyModuleWorkerAbandonHook, type Module } from '@open-mercato/shared/modules/registry'" : "import { createLazyModuleSubscriber, createLazyModuleWorker, type Module } from '@open-mercato/shared/modules/registry'",
2016
2060
  ...options.imports.map((entry) => serializeGeneratedImport(entry))
2017
2061
  ].join("\n");
2018
2062
  return `// AUTO-GENERATED by mercato generate registry
@@ -2279,6 +2323,13 @@ function processWorkersAst(discovered) {
2279
2323
  { name: "concurrency", value: metadata.concurrency ?? 1 },
2280
2324
  ...metadata.lockDuration === void 0 ? [] : [{ name: "lockDuration", value: metadata.lockDuration }],
2281
2325
  ...metadata.maxStalledCount === void 0 ? [] : [{ name: "maxStalledCount", value: metadata.maxStalledCount }],
2326
+ ...metadata.hasJobAbandonedHook ? [{
2327
+ name: "onJobAbandoned",
2328
+ value: callExpression(identifier(ABANDON_HOOK_FACTORY), [
2329
+ arrowFunction({ body: importExpression(importPath) }),
2330
+ workerId
2331
+ ])
2332
+ }] : [],
2282
2333
  {
2283
2334
  name: "handler",
2284
2335
  value: callExpression(identifier("createLazyModuleWorker"), [
@@ -2588,6 +2639,7 @@ async function generateModuleRegistryFromDiscovery(options) {
2588
2639
  const requiresByModule = /* @__PURE__ */ new Map();
2589
2640
  const commandLoaderEntries = [];
2590
2641
  let hasRouteComponents = false;
2642
+ let hasAbandonHookWorkers = false;
2591
2643
  const umesConflictSources = [];
2592
2644
  const seenBackendRoutePatterns = /* @__PURE__ */ new Map();
2593
2645
  for (const discovered of discovery.modules) {
@@ -2791,7 +2843,9 @@ async function generateModuleRegistryFromDiscovery(options) {
2791
2843
  extraImports: runtimeImports
2792
2844
  }));
2793
2845
  subscribers.push(...processSubscribers(await discovered.getSubscribers()));
2794
- workers.push(...processWorkers(await discovered.getWorkers()));
2846
+ const discoveredWorkersForModule = await discovered.getWorkers();
2847
+ if (workersDeclareAbandonHook(discoveredWorkersForModule)) hasAbandonHookWorkers = true;
2848
+ workers.push(...processWorkers(discoveredWorkersForModule));
2795
2849
  {
2796
2850
  const parts = [];
2797
2851
  if (fieldsImportName)
@@ -2943,13 +2997,15 @@ async function generateModuleRegistryFromDiscovery(options) {
2943
2997
  fileName: "modules.generated.ts",
2944
2998
  imports,
2945
2999
  moduleEntries: moduleDecls,
2946
- includeCreateElementImport: hasRouteComponents
3000
+ includeCreateElementImport: hasRouteComponents,
3001
+ includeAbandonHookImport: hasAbandonHookWorkers
2947
3002
  });
2948
3003
  const runtimeOutput = renderAstLegacyModuleRegistryOutput({
2949
3004
  fileName: "modules.runtime.generated.ts",
2950
3005
  imports: runtimeImports,
2951
3006
  moduleEntries: runtimeModuleDecls,
2952
- includeCreateElementImport: true
3007
+ includeCreateElementImport: true,
3008
+ includeAbandonHookImport: hasAbandonHookWorkers
2953
3009
  });
2954
3010
  const frontendRoutesOutput = renderAstLegacyManifestOutput({
2955
3011
  fileName: "frontend-routes.generated.ts",
@@ -3137,6 +3193,7 @@ async function generateModuleRegistryAppFromDiscovery(options) {
3137
3193
  const requiresByModule = /* @__PURE__ */ new Map();
3138
3194
  const commandLoaderEntries = [];
3139
3195
  let hasRouteComponents = false;
3196
+ let hasAbandonHookWorkers = false;
3140
3197
  const seenBackendRoutePatterns = /* @__PURE__ */ new Map();
3141
3198
  for (const discovered of discovery.modules) {
3142
3199
  const { modId, roots, imps, appImportBase } = discovered;
@@ -3282,7 +3339,9 @@ async function generateModuleRegistryAppFromDiscovery(options) {
3282
3339
  i18nLocaleShards.set(locale, shard);
3283
3340
  }
3284
3341
  subscribers.push(...processSubscribersAst(await discovered.getSubscribers()));
3285
- workers.push(...processWorkersAst(await discovered.getWorkers()));
3342
+ const discoveredWorkersForModule = await discovered.getWorkers();
3343
+ if (workersDeclareAbandonHook(discoveredWorkersForModule)) hasAbandonHookWorkers = true;
3344
+ workers.push(...processWorkersAst(discoveredWorkersForModule));
3286
3345
  {
3287
3346
  dashboardWidgetsValue = buildModuleDashboardWidgetsValue(discovered.dashboardWidgetEntries);
3288
3347
  }
@@ -3399,19 +3458,22 @@ async function generateModuleRegistryAppFromDiscovery(options) {
3399
3458
  generator: "registry (app version)",
3400
3459
  imports,
3401
3460
  moduleEntries: moduleDecls,
3402
- includeCreateElementImport: hasRouteComponents
3461
+ includeCreateElementImport: hasRouteComponents,
3462
+ includeAbandonHookImport: hasAbandonHookWorkers
3403
3463
  });
3404
3464
  const bootstrapOutput = renderAstModuleRegistryFile({
3405
3465
  fileName: "modules.bootstrap.generated.ts",
3406
3466
  generator: "registry (bootstrap version)",
3407
3467
  imports: bootstrapImports,
3408
- moduleEntries: bootstrapModuleDecls
3468
+ moduleEntries: bootstrapModuleDecls,
3469
+ includeAbandonHookImport: hasAbandonHookWorkers
3409
3470
  });
3410
3471
  const i18nOutput = renderAstModuleRegistryFile({
3411
3472
  fileName: "modules.i18n.generated.ts",
3412
3473
  generator: "registry (i18n version)",
3413
3474
  imports: i18nImports,
3414
- moduleEntries: i18nModuleDecls
3475
+ moduleEntries: i18nModuleDecls,
3476
+ includeAbandonHookImport: hasAbandonHookWorkers
3415
3477
  });
3416
3478
  const legacyOutput = renderAstLegacyAliasFile({
3417
3479
  fileName: "bootstrap-modules.generated.ts",
@@ -3486,6 +3548,7 @@ async function generateModuleRegistryAppFromDiscovery(options) {
3486
3548
  return result;
3487
3549
  }
3488
3550
  async function generateModuleRegistryCliFromDiscovery(options) {
3551
+ let hasAbandonHookWorkers = false;
3489
3552
  const { resolver, quiet = false } = options;
3490
3553
  const { discovery } = options;
3491
3554
  const result = createGeneratorResult();
@@ -3604,6 +3667,7 @@ async function generateModuleRegistryCliFromDiscovery(options) {
3604
3667
  subscribers.push(...processSubscribersAst(await discovered.getSubscribers()));
3605
3668
  const discoveredWorkers = await discovered.getWorkers();
3606
3669
  const workerGenerationEntries = collectWorkerGenerationEntries(discoveredWorkers, modId);
3670
+ if (workersDeclareAbandonHook(discoveredWorkers)) hasAbandonHookWorkers = true;
3607
3671
  workers.push(...processWorkersAst(discoveredWorkers));
3608
3672
  devSupervisorWorkers.push(...workerGenerationEntries.map(({ importPath: _importPath, ...worker }) => worker));
3609
3673
  {
@@ -3720,7 +3784,8 @@ async function generateModuleRegistryCliFromDiscovery(options) {
3720
3784
  fileName: "modules.cli.generated.ts",
3721
3785
  generator: "registry (CLI version)",
3722
3786
  imports,
3723
- moduleEntries: moduleDecls
3787
+ moduleEntries: moduleDecls,
3788
+ includeAbandonHookImport: hasAbandonHookWorkers
3724
3789
  });
3725
3790
  const legacyOutput = renderAstLegacyAliasFile({
3726
3791
  fileName: "cli-modules.generated.ts",
@@ -3806,6 +3871,7 @@ export {
3806
3871
  generateModuleRegistryApp,
3807
3872
  generateModuleRegistryCli,
3808
3873
  hasNamedExport,
3874
+ namedObjectLiteralDeclaresProperty,
3809
3875
  resetConventionWarnings,
3810
3876
  resolveNamedObjectExport,
3811
3877
  warnIfPageMetaMissingMetadataExport,