@open-mercato/cli 0.4.2-canary-eb5f87d5f9 → 0.4.2-canary-5035717565

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/cli",
3
- "version": "0.4.2-canary-eb5f87d5f9",
3
+ "version": "0.4.2-canary-5035717565",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "exports": {
@@ -55,7 +55,7 @@
55
55
  "@mikro-orm/core": "^6.6.2",
56
56
  "@mikro-orm/migrations": "^6.6.2",
57
57
  "@mikro-orm/postgresql": "^6.6.2",
58
- "@open-mercato/shared": "0.4.2-canary-eb5f87d5f9",
58
+ "@open-mercato/shared": "0.4.2-canary-5035717565",
59
59
  "pg": "^8.16.3",
60
60
  "typescript": "^5.9.3"
61
61
  },
@@ -77,8 +77,7 @@ async function loadModuleEntities(entry: ModuleEntry, resolver: PackageResolver)
77
77
  path.join(roots.appBase, 'db'),
78
78
  path.join(roots.pkgBase, 'db'),
79
79
  ]
80
- // Check both .ts (src/monorepo) and .js (dist/production) extensions
81
- const candidates = ['entities.ts', 'entities.js', 'schema.ts', 'schema.js']
80
+ const candidates = ['entities.ts', 'schema.ts']
82
81
 
83
82
  for (const base of bases) {
84
83
  for (const f of candidates) {
@@ -86,12 +85,10 @@ async function loadModuleEntities(entry: ModuleEntry, resolver: PackageResolver)
86
85
  if (fs.existsSync(p)) {
87
86
  const sub = path.basename(base)
88
87
  const fromApp = base.startsWith(roots.appBase)
89
- const ext = path.extname(f)
90
- const baseName = f.replace(ext, '')
91
88
  // For @app modules, use file:// URL since @/ alias doesn't work in Node.js runtime
92
89
  const importPath = (isAppModule && fromApp)
93
90
  ? `file://${p.replace(/\.ts$/, '.js')}`
94
- : `${fromApp ? imps.appBase : imps.pkgBase}/${sub}/${baseName}`
91
+ : `${fromApp ? imps.appBase : imps.pkgBase}/${sub}/${f.replace(/\.ts$/, '')}`
95
92
  try {
96
93
  const mod = await import(importPath)
97
94
  const entities = Object.values(mod).filter((v) => typeof v === 'function')
@@ -110,15 +107,25 @@ async function loadModuleEntities(entry: ModuleEntry, resolver: PackageResolver)
110
107
 
111
108
  function getMigrationsPath(entry: ModuleEntry, resolver: PackageResolver): string {
112
109
  const from = entry.from || '@open-mercato/core'
113
-
114
- if (from === '@app') {
115
- // For @app modules, use the app directory
116
- return path.join(resolver.getAppDir(), 'src/modules', entry.id, 'migrations')
110
+ let pkgModRoot: string
111
+
112
+ if (from === '@open-mercato/core') {
113
+ pkgModRoot = path.join(resolver.getRootDir(), 'packages/core/src/modules', entry.id)
114
+ } else if (/^@open-mercato\//.test(from)) {
115
+ const segs = from.split('/')
116
+ if (segs.length > 1 && segs[1]) {
117
+ pkgModRoot = path.join(resolver.getRootDir(), `packages/${segs[1]}/src/modules`, entry.id)
118
+ } else {
119
+ pkgModRoot = path.join(resolver.getRootDir(), 'packages/core/src/modules', entry.id)
120
+ }
121
+ } else if (from === '@app') {
122
+ // For @app modules, use the app directory not the monorepo root
123
+ pkgModRoot = path.join(resolver.getAppDir(), 'src/modules', entry.id)
124
+ } else {
125
+ pkgModRoot = path.join(resolver.getRootDir(), 'packages/core/src/modules', entry.id)
117
126
  }
118
127
 
119
- // Use resolver's getModulePaths which handles monorepo vs production mode
120
- const { pkgBase } = resolver.getModulePaths(entry)
121
- return path.join(pkgBase, 'migrations')
128
+ return path.join(pkgModRoot, 'migrations')
122
129
  }
123
130
 
124
131
  export interface DbOptions {
@@ -179,8 +179,7 @@ export async function generateEntityIds(options: EntityIdsOptions): Promise<Gene
179
179
  const appDb = path.join(roots.appBase, 'db')
180
180
  const pkgDb = path.join(roots.pkgBase, 'db')
181
181
  const bases = [appData, pkgData, appDb, pkgDb]
182
- // Check both .ts (src/monorepo) and .js (dist/production) extensions
183
- const candidates = ['entities.override.ts', 'entities.override.js', 'entities.ts', 'entities.js', 'schema.ts', 'schema.js']
182
+ const candidates = ['entities.override.ts', 'entities.ts', 'schema.ts']
184
183
  let importPath: string | null = null
185
184
  let filePath: string | null = null
186
185
 
@@ -190,8 +189,7 @@ export async function generateEntityIds(options: EntityIdsOptions): Promise<Gene
190
189
  if (fs.existsSync(p)) {
191
190
  const fromApp = base.startsWith(roots.appBase)
192
191
  const sub = path.basename(base) // 'data' | 'db'
193
- const fileBaseName = f.replace(/\.(ts|js)$/, '')
194
- importPath = `${fromApp ? imps.appBase : imps.pkgBase}/${sub}/${fileBaseName}`
192
+ importPath = `${fromApp ? imps.appBase : imps.pkgBase}/${sub}/${f.replace(/\.ts$/, '')}`
195
193
  filePath = p
196
194
  break
197
195
  }
@@ -34,13 +34,10 @@ export async function generateModuleDi(options: ModuleDiOptions): Promise<Genera
34
34
  const modId = entry.id
35
35
  const roots = resolver.getModulePaths(entry)
36
36
  const imp = resolver.getModuleImportBase(entry)
37
- // Check both .ts (src/monorepo) and .js (dist/production) extensions
38
- const appDiTs = path.join(roots.appBase, 'di.ts')
39
- const appDiJs = path.join(roots.appBase, 'di.js')
40
- const pkgDiTs = path.join(roots.pkgBase, 'di.ts')
41
- const pkgDiJs = path.join(roots.pkgBase, 'di.js')
42
- const useApp = fs.existsSync(appDiTs) || fs.existsSync(appDiJs)
43
- const usePkg = fs.existsSync(pkgDiTs) || fs.existsSync(pkgDiJs)
37
+ const appDi = path.join(roots.appBase, 'di.ts')
38
+ const pkgDi = path.join(roots.pkgBase, 'di.ts')
39
+ const useApp = fs.existsSync(appDi)
40
+ const usePkg = fs.existsSync(pkgDi)
44
41
  const importName = `D_${toVar(modId)}_${i++}`
45
42
 
46
43
  if (useApp) {
@@ -41,7 +41,7 @@ export async function generateModuleEntities(options: ModuleEntitiesOptions): Pr
41
41
  const appDb = path.join(roots.appBase, 'db')
42
42
  const pkgDb = path.join(roots.pkgBase, 'db')
43
43
  const bases = [appData, pkgData, appDb, pkgDb]
44
- const candidates = ['entities.override.ts', 'entities.override.js', 'entities.ts', 'entities.js', 'schema.ts', 'schema.js']
44
+ const candidates = ['entities.override.ts', 'entities.ts', 'schema.ts']
45
45
 
46
46
  let found: { base: string; file: string } | null = null
47
47
  for (const base of bases) {
@@ -63,13 +63,12 @@ export async function generateModuleEntities(options: ModuleEntitiesOptions): Pr
63
63
  // For @app modules, use relative path to ensure it works both in Next.js and Node.js CLI context
64
64
  // From .mercato/generated/, the relative path to src/modules/ is ../src/modules/
65
65
  let relImport: string
66
- const fileBaseName = found.file.replace(/\.(ts|js)$/, '')
67
66
  if (isAppModule && fromApp) {
68
67
  // From .mercato/generated/, go up two levels (../..) to reach the app root, then into src/modules/
69
- relImport = `../../src/modules/${modId}/${sub}/${fileBaseName}`
68
+ relImport = `../../src/modules/${modId}/${sub}/${found.file.replace(/\.ts$/, '')}`
70
69
  } else {
71
70
  const baseImport = fromApp ? imp.appBase : imp.pkgBase
72
- relImport = `${baseImport}/${sub}/${fileBaseName}`
71
+ relImport = `${baseImport}/${sub}/${found.file.replace(/\.ts$/, '')}`
73
72
  }
74
73
  imports.push(`import * as ${importName} from '${relImport}'`)
75
74
  entitySources.push({ importName, moduleId: modId })
@@ -15,21 +15,6 @@ import {
15
15
 
16
16
  type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
17
17
 
18
- /**
19
- * Find a module file, checking for both .ts (src) and .js (dist) extensions.
20
- * Supports both simple names (e.g., 'cli') and nested paths (e.g., 'data/extensions').
21
- * Returns the path if found, null otherwise.
22
- */
23
- function findModuleFile(basePath: string, ...segments: string[]): string | null {
24
- const name = segments.pop()!
25
- const dir = segments.length ? path.join(basePath, ...segments) : basePath
26
- const tsPath = path.join(dir, `${name}.ts`)
27
- if (fs.existsSync(tsPath)) return tsPath
28
- const jsPath = path.join(dir, `${name}.js`)
29
- if (fs.existsSync(jsPath)) return jsPath
30
- return null
31
- }
32
-
33
18
  export interface ModuleRegistryOptions {
34
19
  resolver: PackageResolver
35
20
  quiet?: boolean
@@ -94,9 +79,9 @@ export async function generateModuleRegistry(options: ModuleRegistryOptions): Pr
94
79
  let injectionTableImportName: string | null = null
95
80
 
96
81
  // Module metadata: index.ts (overrideable)
97
- const appIndex = findModuleFile(roots.appBase, 'index')
98
- const pkgIndex = findModuleFile(roots.pkgBase, 'index')
99
- const indexTs = appIndex ?? pkgIndex
82
+ const appIndex = path.join(roots.appBase, 'index.ts')
83
+ const pkgIndex = path.join(roots.pkgBase, 'index.ts')
84
+ const indexTs = fs.existsSync(appIndex) ? appIndex : fs.existsSync(pkgIndex) ? pkgIndex : null
100
85
  if (indexTs) {
101
86
  infoImportName = `I${importId++}_${toVar(modId)}`
102
87
  const importPath = indexTs.startsWith(roots.appBase) ? `${appImportBase}/index` : `${imps.pkgBase}/index`
@@ -205,10 +190,10 @@ export async function generateModuleRegistry(options: ModuleRegistryOptions): Pr
205
190
 
206
191
  // Entity extensions: src/modules/<module>/data/extensions.ts
207
192
  {
208
- const appFile = findModuleFile(roots.appBase, 'data', 'extensions')
209
- const pkgFile = findModuleFile(roots.pkgBase, 'data', 'extensions')
210
- const hasApp = !!appFile
211
- const hasPkg = !!pkgFile
193
+ const appFile = path.join(roots.appBase, 'data', 'extensions.ts')
194
+ const pkgFile = path.join(roots.pkgBase, 'data', 'extensions.ts')
195
+ const hasApp = fs.existsSync(appFile)
196
+ const hasPkg = fs.existsSync(pkgFile)
212
197
  if (hasApp || hasPkg) {
213
198
  const importName = `X_${toVar(modId)}_${importId++}`
214
199
  const importPath = hasApp ? `${appImportBase}/data/extensions` : `${imps.pkgBase}/data/extensions`
@@ -219,12 +204,12 @@ export async function generateModuleRegistry(options: ModuleRegistryOptions): Pr
219
204
 
220
205
  // RBAC feature declarations: module root acl.ts
221
206
  {
222
- const rootApp = findModuleFile(roots.appBase, 'acl')
223
- const rootPkg = findModuleFile(roots.pkgBase, 'acl')
224
- const hasRoot = rootApp || rootPkg
207
+ const rootApp = path.join(roots.appBase, 'acl.ts')
208
+ const rootPkg = path.join(roots.pkgBase, 'acl.ts')
209
+ const hasRoot = fs.existsSync(rootApp) || fs.existsSync(rootPkg)
225
210
  if (hasRoot) {
226
211
  const importName = `ACL_${toVar(modId)}_${importId++}`
227
- const useApp = rootApp ?? rootPkg!
212
+ const useApp = fs.existsSync(rootApp) ? rootApp : rootPkg
228
213
  const importPath = useApp.startsWith(roots.appBase) ? `${appImportBase}/acl` : `${imps.pkgBase}/acl`
229
214
  imports.push(`import * as ${importName} from '${importPath}'`)
230
215
  featuresImportName = importName
@@ -233,10 +218,10 @@ export async function generateModuleRegistry(options: ModuleRegistryOptions): Pr
233
218
 
234
219
  // Custom entities declarations: module root ce.ts
235
220
  {
236
- const appFile = findModuleFile(roots.appBase, 'ce')
237
- const pkgFile = findModuleFile(roots.pkgBase, 'ce')
238
- const hasApp = !!appFile
239
- const hasPkg = !!pkgFile
221
+ const appFile = path.join(roots.appBase, 'ce.ts')
222
+ const pkgFile = path.join(roots.pkgBase, 'ce.ts')
223
+ const hasApp = fs.existsSync(appFile)
224
+ const hasPkg = fs.existsSync(pkgFile)
240
225
  if (hasApp || hasPkg) {
241
226
  const importName = `CE_${toVar(modId)}_${importId++}`
242
227
  const importPath = hasApp ? `${appImportBase}/ce` : `${imps.pkgBase}/ce`
@@ -247,10 +232,10 @@ export async function generateModuleRegistry(options: ModuleRegistryOptions): Pr
247
232
 
248
233
  // Search module configuration: module root search.ts
249
234
  {
250
- const appFile = findModuleFile(roots.appBase, 'search')
251
- const pkgFile = findModuleFile(roots.pkgBase, 'search')
252
- const hasApp = !!appFile
253
- const hasPkg = !!pkgFile
235
+ const appFile = path.join(roots.appBase, 'search.ts')
236
+ const pkgFile = path.join(roots.pkgBase, 'search.ts')
237
+ const hasApp = fs.existsSync(appFile)
238
+ const hasPkg = fs.existsSync(pkgFile)
254
239
  if (hasApp || hasPkg) {
255
240
  const importName = `SEARCH_${toVar(modId)}_${importId++}`
256
241
  const importPath = hasApp ? `${appImportBase}/search` : `${imps.pkgBase}/search`
@@ -263,10 +248,10 @@ export async function generateModuleRegistry(options: ModuleRegistryOptions): Pr
263
248
 
264
249
  // Custom field declarations: src/modules/<module>/data/fields.ts
265
250
  {
266
- const appFile = findModuleFile(roots.appBase, 'data', 'fields')
267
- const pkgFile = findModuleFile(roots.pkgBase, 'data', 'fields')
268
- const hasApp = !!appFile
269
- const hasPkg = !!pkgFile
251
+ const appFile = path.join(roots.appBase, 'data', 'fields.ts')
252
+ const pkgFile = path.join(roots.pkgBase, 'data', 'fields.ts')
253
+ const hasApp = fs.existsSync(appFile)
254
+ const hasPkg = fs.existsSync(pkgFile)
270
255
  if (hasApp || hasPkg) {
271
256
  const importName = `F_${toVar(modId)}_${importId++}`
272
257
  const importPath = hasApp ? `${appImportBase}/data/fields` : `${imps.pkgBase}/data/fields`
@@ -369,21 +354,19 @@ export async function generateModuleRegistry(options: ModuleRegistryOptions): Pr
369
354
  const apiApp = path.join(roots.appBase, 'api')
370
355
  const apiPkg = path.join(roots.pkgBase, 'api')
371
356
  if (fs.existsSync(apiApp) || fs.existsSync(apiPkg)) {
372
- // route.ts or route.js aggregations
357
+ // route.ts aggregations
373
358
  const routeFiles: string[] = []
374
359
  const walk = (dir: string, rel: string[] = []) => {
375
360
  for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
376
361
  if (e.isDirectory()) {
377
362
  if (e.name === '__tests__' || e.name === '__mocks__') continue
378
363
  walk(path.join(dir, e.name), [...rel, e.name])
379
- } else if (e.isFile() && (e.name === 'route.ts' || e.name === 'route.js')) {
380
- routeFiles.push([...rel, e.name].join('/'))
381
- }
364
+ } else if (e.isFile() && e.name === 'route.ts') routeFiles.push([...rel, e.name].join('/'))
382
365
  }
383
366
  }
384
367
  if (fs.existsSync(apiPkg)) walk(apiPkg)
385
368
  if (fs.existsSync(apiApp)) walk(apiApp)
386
- const routeList = Array.from(new Set(routeFiles.map(f => f.replace(/\.(ts|js)$/, ''))))
369
+ const routeList = Array.from(new Set(routeFiles))
387
370
  const isDynamicRoute = (p: string) => p.split('/').some((seg) => /\[|\[\[\.\.\./.test(seg))
388
371
  routeList.sort((a, b) => {
389
372
  const ad = isDynamicRoute(a) ? 1 : 0
@@ -393,27 +376,22 @@ export async function generateModuleRegistry(options: ModuleRegistryOptions): Pr
393
376
  })
394
377
  for (const rel of routeList) {
395
378
  const segs = rel.split('/')
396
- segs.pop() // remove 'route'
379
+ segs.pop()
397
380
  const reqSegs = [modId, ...segs]
398
381
  const importName = `R${importId++}_${toVar(modId)}_${toVar(segs.join('_') || 'index')}`
399
- const appFileTs = path.join(apiApp, ...segs, 'route.ts')
400
- const appFileJs = path.join(apiApp, ...segs, 'route.js')
401
- const fromApp = fs.existsSync(appFileTs) || fs.existsSync(appFileJs)
382
+ const appFile = path.join(apiApp, ...segs, 'route.ts')
383
+ const fromApp = fs.existsSync(appFile)
402
384
  const apiSegPath = segs.join('/')
403
385
  const importPath = `${fromApp ? appImportBase : imps.pkgBase}/api${apiSegPath ? `/${apiSegPath}` : ''}/route`
404
386
  const routePath = '/' + reqSegs.filter(Boolean).join('/')
405
- const pkgFileTs = path.join(apiPkg, ...segs, 'route.ts')
406
- const pkgFileJs = path.join(apiPkg, ...segs, 'route.js')
407
- const sourceFile = fromApp
408
- ? (fs.existsSync(appFileTs) ? appFileTs : appFileJs)
409
- : (fs.existsSync(pkgFileTs) ? pkgFileTs : pkgFileJs)
387
+ const sourceFile = fromApp ? appFile : path.join(apiPkg, ...segs, 'route.ts')
410
388
  const hasOpenApi = await moduleHasExport(sourceFile, 'openApi')
411
389
  const docsPart = hasOpenApi ? `, docs: ${importName}.openApi` : ''
412
390
  imports.push(`import * as ${importName} from '${importPath}'`)
413
391
  apis.push(`{ path: '${routePath}', metadata: (${importName} as any).metadata, handlers: ${importName} as any${docsPart} }`)
414
392
  }
415
393
 
416
- // Single files (non-route API files)
394
+ // Single files
417
395
  const plainFiles: string[] = []
418
396
  const methodNames = new Set(['get', 'post', 'put', 'patch', 'delete'])
419
397
  const walkPlain = (dir: string, rel: string[] = []) => {
@@ -422,32 +400,28 @@ export async function generateModuleRegistry(options: ModuleRegistryOptions): Pr
422
400
  if (methodNames.has(e.name.toLowerCase())) continue
423
401
  if (e.name === '__tests__' || e.name === '__mocks__') continue
424
402
  walkPlain(path.join(dir, e.name), [...rel, e.name])
425
- } else if (e.isFile() && (e.name.endsWith('.ts') || e.name.endsWith('.js')) && !e.name.startsWith('route.')) {
426
- if (/\.(test|spec)\.(ts|js)$/.test(e.name)) continue
427
- if (e.name.endsWith('.d.ts')) continue
403
+ } else if (e.isFile() && e.name.endsWith('.ts') && e.name !== 'route.ts') {
404
+ if (/\.(test|spec)\.ts$/.test(e.name)) continue
428
405
  plainFiles.push([...rel, e.name].join('/'))
429
406
  }
430
407
  }
431
408
  }
432
409
  if (fs.existsSync(apiPkg)) walkPlain(apiPkg)
433
410
  if (fs.existsSync(apiApp)) walkPlain(apiApp)
434
- const plainList = Array.from(new Set(plainFiles.map(f => f.replace(/\.(ts|js)$/, ''))))
411
+ const plainList = Array.from(new Set(plainFiles))
435
412
  for (const rel of plainList) {
436
413
  const segs = rel.split('/')
437
- const pathWithoutExt = segs.pop()!
414
+ const file = segs.pop()!
415
+ const pathWithoutExt = file.replace(/\.ts$/, '')
438
416
  const fullSegs = [...segs, pathWithoutExt]
439
417
  const routePath = '/' + [modId, ...fullSegs].filter(Boolean).join('/')
440
418
  const importName = `R${importId++}_${toVar(modId)}_${toVar(fullSegs.join('_') || 'index')}`
441
- const appFileTs = path.join(apiApp, ...fullSegs) + '.ts'
442
- const appFileJs = path.join(apiApp, ...fullSegs) + '.js'
443
- const fromApp = fs.existsSync(appFileTs) || fs.existsSync(appFileJs)
419
+ const appFile = path.join(apiApp, ...fullSegs) + '.ts'
420
+ const fromApp = fs.existsSync(appFile)
444
421
  const plainSegPath = fullSegs.join('/')
445
422
  const importPath = `${fromApp ? appImportBase : imps.pkgBase}/api${plainSegPath ? `/${plainSegPath}` : ''}`
446
- const pkgFileTs = path.join(apiPkg, ...fullSegs) + '.ts'
447
- const pkgFileJs = path.join(apiPkg, ...fullSegs) + '.js'
448
- const sourceFile = fromApp
449
- ? (fs.existsSync(appFileTs) ? appFileTs : appFileJs)
450
- : (fs.existsSync(pkgFileTs) ? pkgFileTs : pkgFileJs)
423
+ const pkgFile = path.join(apiPkg, ...fullSegs) + '.ts'
424
+ const sourceFile = fromApp ? appFile : pkgFile
451
425
  const hasOpenApi = await moduleHasExport(sourceFile, 'openApi')
452
426
  const docsPart = hasOpenApi ? `, docs: ${importName}.openApi` : ''
453
427
  imports.push(`import * as ${importName} from '${importPath}'`)
@@ -466,27 +440,25 @@ export async function generateModuleRegistry(options: ModuleRegistryOptions): Pr
466
440
  if (e.isDirectory()) {
467
441
  if (e.name === '__tests__' || e.name === '__mocks__') continue
468
442
  walk2(path.join(dir, e.name), [...rel, e.name])
469
- } else if (e.isFile() && (e.name.endsWith('.ts') || e.name.endsWith('.js'))) {
470
- if (/\.(test|spec)\.(ts|js)$/.test(e.name)) continue
471
- if (e.name.endsWith('.d.ts')) continue
443
+ } else if (e.isFile() && e.name.endsWith('.ts')) {
444
+ if (/\.(test|spec)\.ts$/.test(e.name)) continue
472
445
  apiFiles.push([...rel, e.name].join('/'))
473
446
  }
474
447
  }
475
448
  }
476
449
  walk2(methodDir)
477
- const methodList = Array.from(new Set(apiFiles.map(f => f.replace(/\.(ts|js)$/, ''))))
450
+ const methodList = Array.from(new Set(apiFiles))
478
451
  for (const rel of methodList) {
479
452
  const segs = rel.split('/')
480
- const pathWithoutExt = segs.pop()!
453
+ const file = segs.pop()!
454
+ const pathWithoutExt = file.replace(/\.ts$/, '')
481
455
  const fullSegs = [...segs, pathWithoutExt]
482
456
  const routePath = '/' + [modId, ...fullSegs].filter(Boolean).join('/')
483
457
  const importName = `H${importId++}_${toVar(modId)}_${toVar(method)}_${toVar(fullSegs.join('_'))}`
484
458
  const fromApp = methodDir === appMethodDir
485
459
  const importPath = `${fromApp ? appImportBase : imps.pkgBase}/api/${method.toLowerCase()}/${fullSegs.join('/')}`
486
460
  const metaName = `RM${importId++}_${toVar(modId)}_${toVar(method)}_${toVar(fullSegs.join('_'))}`
487
- const sourceFileTs = path.join(methodDir, ...segs, `${pathWithoutExt}.ts`)
488
- const sourceFileJs = path.join(methodDir, ...segs, `${pathWithoutExt}.js`)
489
- const sourceFile = fs.existsSync(sourceFileTs) ? sourceFileTs : sourceFileJs
461
+ const sourceFile = path.join(methodDir, ...segs, file)
490
462
  const hasOpenApi = await moduleHasExport(sourceFile, 'openApi')
491
463
  const docsPart = hasOpenApi ? `, docs: ${metaName}.openApi` : ''
492
464
  imports.push(`import ${importName}, * as ${metaName} from '${importPath}'`)
@@ -496,9 +468,9 @@ export async function generateModuleRegistry(options: ModuleRegistryOptions): Pr
496
468
  }
497
469
 
498
470
  // CLI
499
- const cliApp = findModuleFile(roots.appBase, 'cli')
500
- const cliPkg = findModuleFile(roots.pkgBase, 'cli')
501
- const cliPath = cliApp ?? cliPkg
471
+ const cliApp = path.join(roots.appBase, 'cli.ts')
472
+ const cliPkg = path.join(roots.pkgBase, 'cli.ts')
473
+ const cliPath = fs.existsSync(cliApp) ? cliApp : fs.existsSync(cliPkg) ? cliPkg : null
502
474
  if (cliPath) {
503
475
  const importName = `CLI_${toVar(modId)}`
504
476
  const importPath = cliPath.startsWith(roots.appBase) ? `${appImportBase}/cli` : `${imps.pkgBase}/cli`
@@ -538,7 +510,7 @@ export async function generateModuleRegistry(options: ModuleRegistryOptions): Pr
538
510
  }
539
511
  }
540
512
 
541
- // Subscribers: src/modules/<module>/subscribers/*.ts or *.js
513
+ // Subscribers: src/modules/<module>/subscribers/*.ts
542
514
  const subApp = path.join(roots.appBase, 'subscribers')
543
515
  const subPkg = path.join(roots.pkgBase, 'subscribers')
544
516
  if (fs.existsSync(subApp) || fs.existsSync(subPkg)) {
@@ -548,9 +520,8 @@ export async function generateModuleRegistry(options: ModuleRegistryOptions): Pr
548
520
  if (e.isDirectory()) {
549
521
  if (e.name === '__tests__' || e.name === '__mocks__') continue
550
522
  walk(path.join(dir, e.name), [...rel, e.name])
551
- } else if (e.isFile() && (e.name.endsWith('.ts') || e.name.endsWith('.js'))) {
552
- if (/\.(test|spec)\.(ts|js)$/.test(e.name)) continue
553
- if (e.name.endsWith('.d.ts')) continue
523
+ } else if (e.isFile() && e.name.endsWith('.ts')) {
524
+ if (/\.(test|spec)\.ts$/.test(e.name)) continue
554
525
  found.push([...rel, e.name].join('/'))
555
526
  }
556
527
  }
@@ -561,12 +532,11 @@ export async function generateModuleRegistry(options: ModuleRegistryOptions): Pr
561
532
  for (const rel of files) {
562
533
  const segs = rel.split('/')
563
534
  const file = segs.pop()!
564
- const name = file.replace(/\.(ts|js)$/, '')
535
+ const name = file.replace(/\.ts$/, '')
565
536
  const importName = `Subscriber${importId++}_${toVar(modId)}_${toVar([...segs, name].join('_') || 'index')}`
566
537
  const metaName = `SubscriberMeta${importId++}_${toVar(modId)}_${toVar([...segs, name].join('_') || 'index')}`
567
- const appFileTs = path.join(subApp, ...segs, `${name}.ts`)
568
- const appFileJs = path.join(subApp, ...segs, `${name}.js`)
569
- const fromApp = fs.existsSync(appFileTs) || fs.existsSync(appFileJs)
538
+ const appFile = path.join(subApp, ...segs, `${name}.ts`)
539
+ const fromApp = fs.existsSync(appFile)
570
540
  const importPath = `${fromApp ? appImportBase : imps.pkgBase}/subscribers/${[...segs, name].join('/')}`
571
541
  imports.push(`import ${importName}, * as ${metaName} from '${importPath}'`)
572
542
  const sid = [modId, ...segs, name].filter(Boolean).join(':')
@@ -576,7 +546,7 @@ export async function generateModuleRegistry(options: ModuleRegistryOptions): Pr
576
546
  }
577
547
  }
578
548
 
579
- // Workers: src/modules/<module>/workers/*.ts or *.js
549
+ // Workers: src/modules/<module>/workers/*.ts
580
550
  // Only includes files that export `metadata` with a `queue` property
581
551
  {
582
552
  const wrkApp = path.join(roots.appBase, 'workers')
@@ -588,9 +558,8 @@ export async function generateModuleRegistry(options: ModuleRegistryOptions): Pr
588
558
  if (e.isDirectory()) {
589
559
  if (e.name === '__tests__' || e.name === '__mocks__') continue
590
560
  walk(path.join(dir, e.name), [...rel, e.name])
591
- } else if (e.isFile() && (e.name.endsWith('.ts') || e.name.endsWith('.js'))) {
592
- if (/\.(test|spec)\.(ts|js)$/.test(e.name)) continue
593
- if (e.name.endsWith('.d.ts')) continue
561
+ } else if (e.isFile() && e.name.endsWith('.ts')) {
562
+ if (/\.(test|spec)\.ts$/.test(e.name)) continue
594
563
  found.push([...rel, e.name].join('/'))
595
564
  }
596
565
  }
@@ -601,10 +570,9 @@ export async function generateModuleRegistry(options: ModuleRegistryOptions): Pr
601
570
  for (const rel of files) {
602
571
  const segs = rel.split('/')
603
572
  const file = segs.pop()!
604
- const name = file.replace(/\.(ts|js)$/, '')
605
- const appFileTs = path.join(wrkApp, ...segs, `${name}.ts`)
606
- const appFileJs = path.join(wrkApp, ...segs, `${name}.js`)
607
- const fromApp = fs.existsSync(appFileTs) || fs.existsSync(appFileJs)
573
+ const name = file.replace(/\.ts$/, '')
574
+ const appFile = path.join(wrkApp, ...segs, `${name}.ts`)
575
+ const fromApp = fs.existsSync(appFile)
608
576
  // Use package import path for checking exports (file path fails due to relative imports)
609
577
  const importPath = `${fromApp ? appImportBase : imps.pkgBase}/workers/${[...segs, name].join('/')}`
610
578
  // Only include files that export metadata with a queue property
@@ -962,9 +930,9 @@ export async function generateModuleRegistryCli(options: ModuleRegistryOptions):
962
930
  let customFieldSetsExpr: string = '[]'
963
931
 
964
932
  // Module metadata: index.ts (overrideable)
965
- const appIndex = findModuleFile(roots.appBase, 'index')
966
- const pkgIndex = findModuleFile(roots.pkgBase, 'index')
967
- const indexTs = appIndex ?? pkgIndex
933
+ const appIndex = path.join(roots.appBase, 'index.ts')
934
+ const pkgIndex = path.join(roots.pkgBase, 'index.ts')
935
+ const indexTs = fs.existsSync(appIndex) ? appIndex : fs.existsSync(pkgIndex) ? pkgIndex : null
968
936
  if (indexTs) {
969
937
  infoImportName = `I${importId++}_${toVar(modId)}`
970
938
  const importPath = indexTs.startsWith(roots.appBase) ? `${appImportBase}/index` : `${imps.pkgBase}/index`
@@ -981,10 +949,10 @@ export async function generateModuleRegistryCli(options: ModuleRegistryOptions):
981
949
 
982
950
  // Entity extensions: src/modules/<module>/data/extensions.ts
983
951
  {
984
- const appFile = findModuleFile(roots.appBase, 'data', 'extensions')
985
- const pkgFile = findModuleFile(roots.pkgBase, 'data', 'extensions')
986
- const hasApp = !!appFile
987
- const hasPkg = !!pkgFile
952
+ const appFile = path.join(roots.appBase, 'data', 'extensions.ts')
953
+ const pkgFile = path.join(roots.pkgBase, 'data', 'extensions.ts')
954
+ const hasApp = fs.existsSync(appFile)
955
+ const hasPkg = fs.existsSync(pkgFile)
988
956
  if (hasApp || hasPkg) {
989
957
  const importName = `X_${toVar(modId)}_${importId++}`
990
958
  const importPath = hasApp ? `${appImportBase}/data/extensions` : `${imps.pkgBase}/data/extensions`
@@ -995,12 +963,12 @@ export async function generateModuleRegistryCli(options: ModuleRegistryOptions):
995
963
 
996
964
  // RBAC feature declarations: module root acl.ts
997
965
  {
998
- const rootApp = findModuleFile(roots.appBase, 'acl')
999
- const rootPkg = findModuleFile(roots.pkgBase, 'acl')
1000
- const hasRoot = rootApp || rootPkg
966
+ const rootApp = path.join(roots.appBase, 'acl.ts')
967
+ const rootPkg = path.join(roots.pkgBase, 'acl.ts')
968
+ const hasRoot = fs.existsSync(rootApp) || fs.existsSync(rootPkg)
1001
969
  if (hasRoot) {
1002
970
  const importName = `ACL_${toVar(modId)}_${importId++}`
1003
- const useApp = rootApp ?? rootPkg!
971
+ const useApp = fs.existsSync(rootApp) ? rootApp : rootPkg
1004
972
  const importPath = useApp.startsWith(roots.appBase) ? `${appImportBase}/acl` : `${imps.pkgBase}/acl`
1005
973
  imports.push(`import * as ${importName} from '${importPath}'`)
1006
974
  featuresImportName = importName
@@ -1009,10 +977,10 @@ export async function generateModuleRegistryCli(options: ModuleRegistryOptions):
1009
977
 
1010
978
  // Custom entities declarations: module root ce.ts
1011
979
  {
1012
- const appFile = findModuleFile(roots.appBase, 'ce')
1013
- const pkgFile = findModuleFile(roots.pkgBase, 'ce')
1014
- const hasApp = !!appFile
1015
- const hasPkg = !!pkgFile
980
+ const appFile = path.join(roots.appBase, 'ce.ts')
981
+ const pkgFile = path.join(roots.pkgBase, 'ce.ts')
982
+ const hasApp = fs.existsSync(appFile)
983
+ const hasPkg = fs.existsSync(pkgFile)
1016
984
  if (hasApp || hasPkg) {
1017
985
  const importName = `CE_${toVar(modId)}_${importId++}`
1018
986
  const importPath = hasApp ? `${appImportBase}/ce` : `${imps.pkgBase}/ce`
@@ -1023,10 +991,10 @@ export async function generateModuleRegistryCli(options: ModuleRegistryOptions):
1023
991
 
1024
992
  // Vector search configuration: module root vector.ts
1025
993
  {
1026
- const appFile = findModuleFile(roots.appBase, 'vector')
1027
- const pkgFile = findModuleFile(roots.pkgBase, 'vector')
1028
- const hasApp = !!appFile
1029
- const hasPkg = !!pkgFile
994
+ const appFile = path.join(roots.appBase, 'vector.ts')
995
+ const pkgFile = path.join(roots.pkgBase, 'vector.ts')
996
+ const hasApp = fs.existsSync(appFile)
997
+ const hasPkg = fs.existsSync(pkgFile)
1030
998
  if (hasApp || hasPkg) {
1031
999
  const importName = `VECTOR_${toVar(modId)}_${importId++}`
1032
1000
  const importPath = hasApp ? `${appImportBase}/vector` : `${imps.pkgBase}/vector`
@@ -1037,10 +1005,10 @@ export async function generateModuleRegistryCli(options: ModuleRegistryOptions):
1037
1005
 
1038
1006
  // Custom field declarations: src/modules/<module>/data/fields.ts
1039
1007
  {
1040
- const appFile = findModuleFile(roots.appBase, 'data', 'fields')
1041
- const pkgFile = findModuleFile(roots.pkgBase, 'data', 'fields')
1042
- const hasApp = !!appFile
1043
- const hasPkg = !!pkgFile
1008
+ const appFile = path.join(roots.appBase, 'data', 'fields.ts')
1009
+ const pkgFile = path.join(roots.pkgBase, 'data', 'fields.ts')
1010
+ const hasApp = fs.existsSync(appFile)
1011
+ const hasPkg = fs.existsSync(pkgFile)
1044
1012
  if (hasApp || hasPkg) {
1045
1013
  const importName = `F_${toVar(modId)}_${importId++}`
1046
1014
  const importPath = hasApp ? `${appImportBase}/data/fields` : `${imps.pkgBase}/data/fields`
@@ -1050,9 +1018,9 @@ export async function generateModuleRegistryCli(options: ModuleRegistryOptions):
1050
1018
  }
1051
1019
 
1052
1020
  // CLI
1053
- const cliApp = findModuleFile(roots.appBase, 'cli')
1054
- const cliPkg = findModuleFile(roots.pkgBase, 'cli')
1055
- const cliPath = cliApp ?? cliPkg
1021
+ const cliApp = path.join(roots.appBase, 'cli.ts')
1022
+ const cliPkg = path.join(roots.pkgBase, 'cli.ts')
1023
+ const cliPath = fs.existsSync(cliApp) ? cliApp : fs.existsSync(cliPkg) ? cliPkg : null
1056
1024
  if (cliPath) {
1057
1025
  const importName = `CLI_${toVar(modId)}`
1058
1026
  const importPath = cliPath.startsWith(roots.appBase) ? `${appImportBase}/cli` : `${imps.pkgBase}/cli`
@@ -1092,7 +1060,7 @@ export async function generateModuleRegistryCli(options: ModuleRegistryOptions):
1092
1060
  }
1093
1061
  }
1094
1062
 
1095
- // Subscribers: src/modules/<module>/subscribers/*.ts or *.js
1063
+ // Subscribers: src/modules/<module>/subscribers/*.ts
1096
1064
  const subApp = path.join(roots.appBase, 'subscribers')
1097
1065
  const subPkg = path.join(roots.pkgBase, 'subscribers')
1098
1066
  if (fs.existsSync(subApp) || fs.existsSync(subPkg)) {
@@ -1102,9 +1070,8 @@ export async function generateModuleRegistryCli(options: ModuleRegistryOptions):
1102
1070
  if (e.isDirectory()) {
1103
1071
  if (e.name === '__tests__' || e.name === '__mocks__') continue
1104
1072
  walk(path.join(dir, e.name), [...rel, e.name])
1105
- } else if (e.isFile() && (e.name.endsWith('.ts') || e.name.endsWith('.js'))) {
1106
- if (/\.(test|spec)\.(ts|js)$/.test(e.name)) continue
1107
- if (e.name.endsWith('.d.ts')) continue
1073
+ } else if (e.isFile() && e.name.endsWith('.ts')) {
1074
+ if (/\.(test|spec)\.ts$/.test(e.name)) continue
1108
1075
  found.push([...rel, e.name].join('/'))
1109
1076
  }
1110
1077
  }
@@ -1115,12 +1082,11 @@ export async function generateModuleRegistryCli(options: ModuleRegistryOptions):
1115
1082
  for (const rel of files) {
1116
1083
  const segs = rel.split('/')
1117
1084
  const file = segs.pop()!
1118
- const name = file.replace(/\.(ts|js)$/, '')
1085
+ const name = file.replace(/\.ts$/, '')
1119
1086
  const importName = `Subscriber${importId++}_${toVar(modId)}_${toVar([...segs, name].join('_') || 'index')}`
1120
1087
  const metaName = `SubscriberMeta${importId++}_${toVar(modId)}_${toVar([...segs, name].join('_') || 'index')}`
1121
- const appFileTs = path.join(subApp, ...segs, `${name}.ts`)
1122
- const appFileJs = path.join(subApp, ...segs, `${name}.js`)
1123
- const fromApp = fs.existsSync(appFileTs) || fs.existsSync(appFileJs)
1088
+ const appFile = path.join(subApp, ...segs, `${name}.ts`)
1089
+ const fromApp = fs.existsSync(appFile)
1124
1090
  const importPath = `${fromApp ? appImportBase : imps.pkgBase}/subscribers/${[...segs, name].join('/')}`
1125
1091
  imports.push(`import ${importName}, * as ${metaName} from '${importPath}'`)
1126
1092
  const sid = [modId, ...segs, name].filter(Boolean).join(':')
@@ -1130,7 +1096,7 @@ export async function generateModuleRegistryCli(options: ModuleRegistryOptions):
1130
1096
  }
1131
1097
  }
1132
1098
 
1133
- // Workers: src/modules/<module>/workers/*.ts or *.js
1099
+ // Workers: src/modules/<module>/workers/*.ts
1134
1100
  // Only includes files that export `metadata` with a `queue` property
1135
1101
  {
1136
1102
  const wrkApp = path.join(roots.appBase, 'workers')
@@ -1142,9 +1108,8 @@ export async function generateModuleRegistryCli(options: ModuleRegistryOptions):
1142
1108
  if (e.isDirectory()) {
1143
1109
  if (e.name === '__tests__' || e.name === '__mocks__') continue
1144
1110
  walk(path.join(dir, e.name), [...rel, e.name])
1145
- } else if (e.isFile() && (e.name.endsWith('.ts') || e.name.endsWith('.js'))) {
1146
- if (/\.(test|spec)\.(ts|js)$/.test(e.name)) continue
1147
- if (e.name.endsWith('.d.ts')) continue
1111
+ } else if (e.isFile() && e.name.endsWith('.ts')) {
1112
+ if (/\.(test|spec)\.ts$/.test(e.name)) continue
1148
1113
  found.push([...rel, e.name].join('/'))
1149
1114
  }
1150
1115
  }
@@ -1155,10 +1120,9 @@ export async function generateModuleRegistryCli(options: ModuleRegistryOptions):
1155
1120
  for (const rel of files) {
1156
1121
  const segs = rel.split('/')
1157
1122
  const file = segs.pop()!
1158
- const name = file.replace(/\.(ts|js)$/, '')
1159
- const appFileTs = path.join(wrkApp, ...segs, `${name}.ts`)
1160
- const appFileJs = path.join(wrkApp, ...segs, `${name}.js`)
1161
- const fromApp = fs.existsSync(appFileTs) || fs.existsSync(appFileJs)
1123
+ const name = file.replace(/\.ts$/, '')
1124
+ const appFile = path.join(wrkApp, ...segs, `${name}.ts`)
1125
+ const fromApp = fs.existsSync(appFile)
1162
1126
  // Use package import path for checking exports (file path fails due to relative imports)
1163
1127
  const importPath = `${fromApp ? appImportBase : imps.pkgBase}/workers/${[...segs, name].join('/')}`
1164
1128
  // Only include files that export metadata with a queue property