@open-mercato/cli 0.10.1-develop.7836.1.0ef0f453c6 → 0.10.1-develop.7841.1.af87ac614d
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/dist/agentic/guides/module-facts.json +172 -163
- package/dist/agentic/guides/module-facts.v2.json +172 -163
- package/dist/agentic/guides/modules/agent_orchestrator/index.md +1 -1
- 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/index.md +1 -1
- package/dist/agentic/guides/modules/attachments/index.md +1 -1
- package/dist/agentic/guides/modules/audit_logs/index.md +1 -1
- package/dist/agentic/guides/modules/auth/index.md +1 -1
- package/dist/agentic/guides/modules/availability/index.md +1 -1
- package/dist/agentic/guides/modules/business_rules/index.md +1 -1
- package/dist/agentic/guides/modules/carrier_inpost/index.md +1 -1
- package/dist/agentic/guides/modules/cart/index.md +1 -1
- package/dist/agentic/guides/modules/catalog/index.md +1 -1
- package/dist/agentic/guides/modules/channel_apns/index.md +1 -1
- package/dist/agentic/guides/modules/channel_discord/index.md +1 -1
- 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/channel_resend/index.md +1 -1
- package/dist/agentic/guides/modules/channel_ses/index.md +1 -1
- package/dist/agentic/guides/modules/channel_smtp/index.md +1 -1
- package/dist/agentic/guides/modules/checkout/index.md +1 -1
- package/dist/agentic/guides/modules/communication_channels/index.md +1 -1
- 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/customer_groups/index.md +1 -1
- package/dist/agentic/guides/modules/customers/index.md +1 -1
- package/dist/agentic/guides/modules/dashboards/index.md +1 -1
- package/dist/agentic/guides/modules/data_sync/index.md +1 -1
- 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/index.md +1 -1
- package/dist/agentic/guides/modules/documents/index.md +1 -1
- package/dist/agentic/guides/modules/ecommerce/index.md +1 -1
- 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/eudr/owned-contract-module-metadata.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/forms/index.md +1 -1
- package/dist/agentic/guides/modules/gateway_autopay/index.md +1 -1
- package/dist/agentic/guides/modules/gateway_stripe/index.md +1 -1
- package/dist/agentic/guides/modules/gateway_tpay/index.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/phone_calls/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/promotions/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/index.md +1 -1
- package/dist/agentic/guides/modules/scheduler/index.md +1 -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/seeds/index.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/active-extension-bindings.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/sync_excel/owned-contract-module-metadata.md +1 -1
- package/dist/agentic/guides/modules/system_status_overlays/index.md +1 -1
- package/dist/agentic/guides/modules/telemetry/index.md +1 -1
- package/dist/agentic/guides/modules/tillio/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 +1 -1
- package/dist/agentic/guides/upstream/manifest.json +1 -1
- package/dist/lib/generators/module-registry.js +18 -18
- package/dist/lib/generators/module-registry.js.map +2 -2
- package/package.json +7 -7
- package/src/lib/generators/__tests__/module-registry-requires-enforcement.test.ts +130 -0
- package/src/lib/generators/module-registry.ts +20 -21
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/cli",
|
|
3
|
-
"version": "0.10.1-develop.
|
|
3
|
+
"version": "0.10.1-develop.7841.1.af87ac614d",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -60,10 +60,10 @@
|
|
|
60
60
|
"@mikro-orm/decorators": "^7.1.14",
|
|
61
61
|
"@mikro-orm/migrations": "^7.1.14",
|
|
62
62
|
"@mikro-orm/postgresql": "^7.1.14",
|
|
63
|
-
"@open-mercato/events": "0.10.1-develop.
|
|
64
|
-
"@open-mercato/queue": "0.10.1-develop.
|
|
65
|
-
"@open-mercato/shared": "0.10.1-develop.
|
|
66
|
-
"@open-mercato/telemetry": "0.10.1-develop.
|
|
63
|
+
"@open-mercato/events": "0.10.1-develop.7841.1.af87ac614d",
|
|
64
|
+
"@open-mercato/queue": "0.10.1-develop.7841.1.af87ac614d",
|
|
65
|
+
"@open-mercato/shared": "0.10.1-develop.7841.1.af87ac614d",
|
|
66
|
+
"@open-mercato/telemetry": "0.10.1-develop.7841.1.af87ac614d",
|
|
67
67
|
"cross-spawn": "^7.0.6",
|
|
68
68
|
"minisearch": "7.2.0",
|
|
69
69
|
"pg": "8.23.0",
|
|
@@ -75,10 +75,10 @@
|
|
|
75
75
|
"typescript-js": "npm:typescript@6.0.3"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
|
-
"@open-mercato/shared": "0.10.1-develop.
|
|
78
|
+
"@open-mercato/shared": "0.10.1-develop.7841.1.af87ac614d"
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
|
-
"@open-mercato/shared": "0.10.1-develop.
|
|
81
|
+
"@open-mercato/shared": "0.10.1-develop.7841.1.af87ac614d",
|
|
82
82
|
"@types/jest": "^30.0.0",
|
|
83
83
|
"jest": "^30.4.2",
|
|
84
84
|
"ts-jest": "^29.4.12"
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `metadata.requires` must actually block `yarn generate` when the required module is
|
|
3
|
+
* not enabled — it is not advisory text.
|
|
4
|
+
*
|
|
5
|
+
* The CLI package is built as an ES module (`packages/cli/package.json` `"type": "module"`),
|
|
6
|
+
* where the bare `require()` this check used to read `metadata.requires` is undefined and
|
|
7
|
+
* throws a `ReferenceError` on every module, every time. That throw was swallowed by a
|
|
8
|
+
* `catch {}`, so the dependency map stayed empty and the check below it never found
|
|
9
|
+
* anything to report: a module could declare `requires: ['catalog']`, `catalog` could be
|
|
10
|
+
* disabled, and `yarn generate` would still succeed. Unit tests missed this because Jest
|
|
11
|
+
* runs the generator under CommonJS, where `require()` is defined and the bug cannot
|
|
12
|
+
* reproduce.
|
|
13
|
+
*
|
|
14
|
+
* The fix reads `metadata.requires` through `loadModuleExportsFromSource` (a real
|
|
15
|
+
* `import()`) with an AST-based static fallback — the same mechanism this file already
|
|
16
|
+
* uses for route metadata — so there is no `require()` left to break under ESM. This test
|
|
17
|
+
* drives the real generator against real fixture files on disk rather than asserting on
|
|
18
|
+
* the implementation, so it fails again if a future change reintroduces `require()`.
|
|
19
|
+
*/
|
|
20
|
+
import fs from 'node:fs'
|
|
21
|
+
import path from 'node:path'
|
|
22
|
+
import os from 'node:os'
|
|
23
|
+
import type { PackageResolver, ModuleEntry } from '../../resolver'
|
|
24
|
+
import { generateModuleRegistry } from '../module-registry'
|
|
25
|
+
|
|
26
|
+
let tmpDir: string
|
|
27
|
+
let outputDir: string
|
|
28
|
+
|
|
29
|
+
function touchFile(filePath: string, content: string): void {
|
|
30
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
|
31
|
+
fs.writeFileSync(filePath, content)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function pkgModulePath(modId: string, ...segments: string[]): string {
|
|
35
|
+
return path.join(tmpDir, 'packages', 'core', 'src', 'modules', modId, ...segments)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function createMockResolver(enabled: ModuleEntry[]): PackageResolver {
|
|
39
|
+
return {
|
|
40
|
+
isMonorepo: () => true,
|
|
41
|
+
getRootDir: () => tmpDir,
|
|
42
|
+
getAppDir: () => path.join(tmpDir, 'app'),
|
|
43
|
+
getOutputDir: () => outputDir,
|
|
44
|
+
getModulesConfigPath: () => path.join(tmpDir, 'app', 'src', 'modules.ts'),
|
|
45
|
+
discoverPackages: () => [],
|
|
46
|
+
loadEnabledModules: () => enabled,
|
|
47
|
+
getModulePaths: (entry: ModuleEntry) => ({
|
|
48
|
+
appBase: path.join(tmpDir, 'app', 'src', 'modules', entry.id),
|
|
49
|
+
pkgBase: path.join(tmpDir, 'packages', 'core', 'src', 'modules', entry.id),
|
|
50
|
+
}),
|
|
51
|
+
getModuleImportBase: (entry: ModuleEntry) => ({
|
|
52
|
+
appBase: `@/modules/${entry.id}`,
|
|
53
|
+
pkgBase: `@open-mercato/core/modules/${entry.id}`,
|
|
54
|
+
}),
|
|
55
|
+
getPackageOutputDir: () => outputDir,
|
|
56
|
+
getPackageRoot: () => path.join(tmpDir, 'packages', 'core'),
|
|
57
|
+
} as unknown as PackageResolver
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function scaffoldDependentFixture(): void {
|
|
61
|
+
touchFile(pkgModulePath('leaf', 'index.ts'), "export const metadata = { id: 'leaf', label: 'Leaf' }\n")
|
|
62
|
+
touchFile(
|
|
63
|
+
pkgModulePath('dependent', 'index.ts'),
|
|
64
|
+
"export const metadata = { id: 'dependent', label: 'Dependent', requires: ['leaf'] }\n",
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function mockProcessExit(): jest.SpyInstance {
|
|
69
|
+
return jest.spyOn(process, 'exit').mockImplementation(((code?: number) => {
|
|
70
|
+
throw new Error(`process.exit(${code})`)
|
|
71
|
+
}) as unknown as typeof process.exit)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
describe('module registry enforces metadata.requires', () => {
|
|
75
|
+
beforeEach(() => {
|
|
76
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'registry-requires-'))
|
|
77
|
+
outputDir = path.join(tmpDir, 'app', '.mercato', 'generated')
|
|
78
|
+
fs.mkdirSync(outputDir, { recursive: true })
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
afterEach(() => {
|
|
82
|
+
fs.rmSync(tmpDir, { recursive: true, force: true })
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('refuses generation when a declared dependency is not enabled', async () => {
|
|
86
|
+
scaffoldDependentFixture()
|
|
87
|
+
const resolver = createMockResolver([{ id: 'dependent', from: '@open-mercato/core' }])
|
|
88
|
+
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation()
|
|
89
|
+
const exitSpy = mockProcessExit()
|
|
90
|
+
|
|
91
|
+
await expect(generateModuleRegistry({ resolver, quiet: true })).rejects.toThrow('process.exit(1)')
|
|
92
|
+
|
|
93
|
+
expect(exitSpy).toHaveBeenCalledWith(1)
|
|
94
|
+
expect(consoleErrorSpy.mock.calls.flat().join('\n')).toContain('Module "dependent" requires: leaf')
|
|
95
|
+
|
|
96
|
+
consoleErrorSpy.mockRestore()
|
|
97
|
+
exitSpy.mockRestore()
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('generates cleanly once the declared dependency is enabled', async () => {
|
|
101
|
+
scaffoldDependentFixture()
|
|
102
|
+
const resolver = createMockResolver([
|
|
103
|
+
{ id: 'leaf', from: '@open-mercato/core' },
|
|
104
|
+
{ id: 'dependent', from: '@open-mercato/core' },
|
|
105
|
+
])
|
|
106
|
+
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation()
|
|
107
|
+
const exitSpy = mockProcessExit()
|
|
108
|
+
|
|
109
|
+
const result = await generateModuleRegistry({ resolver, quiet: true })
|
|
110
|
+
|
|
111
|
+
expect(result.errors).toEqual([])
|
|
112
|
+
expect(exitSpy).not.toHaveBeenCalled()
|
|
113
|
+
|
|
114
|
+
consoleErrorSpy.mockRestore()
|
|
115
|
+
exitSpy.mockRestore()
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
describe('module registry has no require() left to break under the ESM CLI build', () => {
|
|
120
|
+
it('reads metadata.requires without the CommonJS require() function', () => {
|
|
121
|
+
// Jest runs this generator under CommonJS, where `require()` is defined — the two
|
|
122
|
+
// tests above would pass even against the old, ESM-broken implementation. This
|
|
123
|
+
// static check is what actually guards against a regression: `@open-mercato/cli`
|
|
124
|
+
// is built as an ES module, so a bare `require()` call reintroduced here would throw
|
|
125
|
+
// under the real build regardless of what Jest reports.
|
|
126
|
+
const sourceFile = path.join(__dirname, '..', 'module-registry.ts')
|
|
127
|
+
const source = fs.readFileSync(sourceFile, 'utf8')
|
|
128
|
+
expect([...source.matchAll(/\brequire\(/g)]).toEqual([])
|
|
129
|
+
})
|
|
130
|
+
})
|
|
@@ -1722,6 +1722,20 @@ function resolveApiPathFromMetadata(metadata: unknown, fallbackPath: string): st
|
|
|
1722
1722
|
return typeof candidate === 'string' && candidate.length > 0 ? candidate : fallbackPath
|
|
1723
1723
|
}
|
|
1724
1724
|
|
|
1725
|
+
function resolveModuleRequiresFromMetadata(metadata: unknown): string[] | undefined {
|
|
1726
|
+
if (!metadata || typeof metadata !== 'object') return undefined
|
|
1727
|
+
const requires = (metadata as Record<string, unknown>).requires
|
|
1728
|
+
if (!Array.isArray(requires)) return undefined
|
|
1729
|
+
const resolved = requires.filter((value): value is string => typeof value === 'string')
|
|
1730
|
+
return resolved.length > 0 ? resolved : undefined
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
async function resolveModuleRequiresFromIndexFile(indexFileAbsolutePath: string): Promise<string[] | undefined> {
|
|
1734
|
+
const loadedModule = await loadModuleExportsFromSource<Record<string, unknown>>(indexFileAbsolutePath)
|
|
1735
|
+
const metadata = loadedModule?.metadata ?? extractNamedObjectLiteralExport(indexFileAbsolutePath, 'metadata')
|
|
1736
|
+
return resolveModuleRequiresFromMetadata(metadata)
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1725
1739
|
function detectExportedHttpMethods(sourceFile: string): HttpMethod[] {
|
|
1726
1740
|
let source = ''
|
|
1727
1741
|
try {
|
|
@@ -3691,13 +3705,8 @@ async function generateModuleRegistryFromDiscovery(options: ModuleRegistryRender
|
|
|
3691
3705
|
const importPath = sanitizeGeneratedModuleSpecifier(indexResolved.importPath)
|
|
3692
3706
|
imports.push(buildImportStatement(`* as ${infoImportName}`, importPath))
|
|
3693
3707
|
runtimeImports.push(buildImportStatement(`* as ${infoImportName}`, importPath))
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
const mod = require(indexResolved.absolutePath)
|
|
3697
|
-
const reqs: string[] | undefined =
|
|
3698
|
-
mod?.metadata && Array.isArray(mod.metadata.requires) ? mod.metadata.requires : undefined
|
|
3699
|
-
if (reqs && reqs.length) requiresByModule.set(modId, reqs)
|
|
3700
|
-
} catch {}
|
|
3708
|
+
const reqs = await resolveModuleRequiresFromIndexFile(indexResolved.absolutePath)
|
|
3709
|
+
if (reqs && reqs.length) requiresByModule.set(modId, reqs)
|
|
3701
3710
|
}
|
|
3702
3711
|
|
|
3703
3712
|
// 2. Pages: frontend
|
|
@@ -4414,13 +4423,8 @@ async function generateModuleRegistryAppFromDiscovery(options: ModuleRegistryRen
|
|
|
4414
4423
|
const importPath = sanitizeGeneratedModuleSpecifier(indexResolved.importPath)
|
|
4415
4424
|
imports.push(buildImportStatement(`* as ${infoImportName}`, importPath))
|
|
4416
4425
|
bootstrapImports.push(buildImportStatement(`* as ${infoImportName}`, importPath))
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
const mod = require(indexResolved.absolutePath)
|
|
4420
|
-
const reqs: string[] | undefined =
|
|
4421
|
-
mod?.metadata && Array.isArray(mod.metadata.requires) ? mod.metadata.requires : undefined
|
|
4422
|
-
if (reqs && reqs.length) requiresByModule.set(modId, reqs)
|
|
4423
|
-
} catch {}
|
|
4426
|
+
const reqs = await resolveModuleRequiresFromIndexFile(indexResolved.absolutePath)
|
|
4427
|
+
if (reqs && reqs.length) requiresByModule.set(modId, reqs)
|
|
4424
4428
|
}
|
|
4425
4429
|
|
|
4426
4430
|
{
|
|
@@ -4849,13 +4853,8 @@ async function generateModuleRegistryCliFromDiscovery(options: ModuleRegistryRen
|
|
|
4849
4853
|
infoImportName = `I${importIdRef.value++}_${toVar(modId)}`
|
|
4850
4854
|
const importPath = sanitizeGeneratedModuleSpecifier(indexResolved.importPath)
|
|
4851
4855
|
imports.push(buildImportStatement(`* as ${infoImportName}`, importPath))
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
const mod = require(indexResolved.absolutePath)
|
|
4855
|
-
const reqs: string[] | undefined =
|
|
4856
|
-
mod?.metadata && Array.isArray(mod.metadata.requires) ? mod.metadata.requires : undefined
|
|
4857
|
-
if (reqs && reqs.length) requiresByModule.set(modId, reqs)
|
|
4858
|
-
} catch {}
|
|
4856
|
+
const reqs = await resolveModuleRequiresFromIndexFile(indexResolved.absolutePath)
|
|
4857
|
+
if (reqs && reqs.length) requiresByModule.set(modId, reqs)
|
|
4859
4858
|
}
|
|
4860
4859
|
|
|
4861
4860
|
// Module setup configuration: setup.ts
|