@open-mercato/shared 0.6.7-develop.6860.1.f796b22e80 → 0.6.7
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 +1 -1
- package/AGENTS.md +6 -57
- package/build.mjs +4 -2
- package/dist/lib/auth/server.js +2 -15
- package/dist/lib/auth/server.js.map +2 -2
- package/dist/lib/bootstrap/dynamicLoader.js +43 -117
- package/dist/lib/bootstrap/dynamicLoader.js.map +2 -2
- package/dist/lib/commands/command-bus.js +1 -5
- package/dist/lib/commands/command-bus.js.map +2 -2
- package/dist/lib/commands/command-interceptor-runner.js +2 -2
- package/dist/lib/commands/command-interceptor-runner.js.map +2 -2
- package/dist/lib/crud/enricher-runner.js +11 -2
- package/dist/lib/crud/enricher-runner.js.map +2 -2
- package/dist/lib/crud/factory.js +4 -63
- package/dist/lib/crud/factory.js.map +2 -2
- package/dist/lib/crud/interceptor-runner.js +2 -2
- package/dist/lib/crud/interceptor-runner.js.map +2 -2
- package/dist/lib/crud/mutation-guard-registry.js +2 -2
- package/dist/lib/crud/mutation-guard-registry.js.map +2 -2
- package/dist/lib/crud/types.js +0 -4
- package/dist/lib/crud/types.js.map +3 -3
- package/dist/lib/data/engine.js +11 -37
- package/dist/lib/data/engine.js.map +2 -2
- package/dist/lib/db/mikro.js +12 -3
- package/dist/lib/db/mikro.js.map +2 -2
- package/dist/lib/db/pg-errors.js +0 -42
- package/dist/lib/db/pg-errors.js.map +2 -2
- package/dist/lib/di/container.js +6 -48
- package/dist/lib/di/container.js.map +2 -2
- package/dist/lib/email/send.js +0 -56
- package/dist/lib/email/send.js.map +2 -2
- package/dist/lib/encryption/subscriber.js +10 -2
- package/dist/lib/encryption/subscriber.js.map +2 -2
- package/dist/lib/encryption/tenantDataEncryptionService.js +17 -52
- package/dist/lib/encryption/tenantDataEncryptionService.js.map +2 -2
- package/dist/lib/i18n/config.js +1 -1
- package/dist/lib/i18n/config.js.map +2 -2
- package/dist/lib/i18n/server.js +4 -4
- package/dist/lib/i18n/server.js.map +3 -3
- package/dist/lib/logger/index.js +3 -47
- package/dist/lib/logger/index.js.map +3 -3
- package/dist/lib/modules/registry.js +1 -5
- package/dist/lib/modules/registry.js.map +2 -2
- package/dist/lib/query/engine.js +40 -54
- package/dist/lib/query/engine.js.map +2 -2
- package/dist/lib/query/types.js.map +1 -1
- package/dist/lib/redis/connection.js +2 -7
- package/dist/lib/redis/connection.js.map +2 -2
- package/dist/lib/search/config.js +2 -61
- package/dist/lib/search/config.js.map +2 -2
- package/dist/lib/search/tokenize.js +11 -23
- package/dist/lib/search/tokenize.js.map +2 -2
- package/dist/lib/version.js +1 -1
- package/dist/lib/version.js.map +1 -1
- package/dist/lib/webhooks/index.js +1 -13
- package/dist/lib/webhooks/index.js.map +2 -2
- package/dist/lib/webhooks/verify.js +7 -12
- package/dist/lib/webhooks/verify.js.map +2 -2
- package/dist/modules/analytics.js.map +2 -2
- package/dist/modules/overrides.js +1 -50
- package/dist/modules/overrides.js.map +2 -2
- package/dist/modules/payment_gateways/types.js +1 -8
- package/dist/modules/payment_gateways/types.js.map +2 -2
- package/dist/modules/registry.js +0 -1
- package/dist/modules/registry.js.map +2 -2
- package/dist/security/enabledModulesRegistry.js +11 -56
- package/dist/security/enabledModulesRegistry.js.map +2 -2
- package/jest.config.cjs +1 -2
- package/package.json +9 -19
- package/src/lib/ai/llm-provider.ts +0 -33
- package/src/lib/auth/__tests__/server.test.ts +0 -23
- package/src/lib/auth/server.ts +2 -23
- package/src/lib/bootstrap/__tests__/dynamicLoader.commandInterceptors.test.ts +8 -63
- package/src/lib/bootstrap/__tests__/dynamicLoader.tsconfig.test.ts +1 -21
- package/src/lib/bootstrap/dynamicLoader.ts +65 -213
- package/src/lib/commands/__tests__/command-interceptor-runner.test.ts +0 -28
- package/src/lib/commands/command-bus.ts +1 -5
- package/src/lib/commands/command-interceptor-runner.ts +2 -2
- package/src/lib/crud/__tests__/crud-factory.test.ts +0 -298
- package/src/lib/crud/__tests__/mutation-guard-registry.test.ts +0 -26
- package/src/lib/crud/enricher-runner.ts +11 -2
- package/src/lib/crud/factory.ts +1 -69
- package/src/lib/crud/interceptor-runner.ts +2 -2
- package/src/lib/crud/mutation-guard-registry.ts +2 -2
- package/src/lib/crud/types.ts +0 -3
- package/src/lib/data/__tests__/engine.bulk-suppress.test.ts +3 -18
- package/src/lib/data/engine.ts +16 -50
- package/src/lib/db/__tests__/mikro.test.ts +25 -0
- package/src/lib/db/mikro.ts +38 -13
- package/src/lib/db/pg-errors.ts +0 -58
- package/src/lib/di/container.ts +8 -72
- package/src/lib/email/__tests__/send.test.ts +1 -37
- package/src/lib/email/send.ts +0 -79
- package/src/lib/encryption/__tests__/subscriber.test.ts +2 -54
- package/src/lib/encryption/__tests__/tenantDataEncryptionService.test.ts +0 -58
- package/src/lib/encryption/subscriber.ts +14 -2
- package/src/lib/encryption/tenantDataEncryptionService.ts +18 -64
- package/src/lib/i18n/config.ts +2 -2
- package/src/lib/i18n/server.ts +2 -6
- package/src/lib/logger/__tests__/logger.test.ts +0 -77
- package/src/lib/logger/index.ts +1 -59
- package/src/lib/modules/registry.ts +0 -10
- package/src/lib/query/__tests__/engine.test.ts +13 -65
- package/src/lib/query/engine.ts +58 -75
- package/src/lib/query/types.ts +5 -4
- package/src/lib/redis/__tests__/connection.test.ts +0 -16
- package/src/lib/redis/connection.ts +7 -10
- package/src/lib/search/__tests__/config.test.ts +0 -177
- package/src/lib/search/config.ts +8 -106
- package/src/lib/search/tokenize.ts +11 -33
- package/src/lib/webhooks/__tests__/verify.test.ts +1 -20
- package/src/lib/webhooks/index.ts +1 -19
- package/src/lib/webhooks/verify.ts +9 -17
- package/src/modules/__tests__/registry.test.ts +2 -10
- package/src/modules/analytics.ts +0 -7
- package/src/modules/customer-auth.ts +0 -1
- package/src/modules/encryption.ts +0 -3
- package/src/modules/navigation/backendChrome.ts +0 -15
- package/src/modules/overrides.ts +0 -103
- package/src/modules/payment_gateways/types.ts +0 -41
- package/src/modules/registry.ts +0 -3
- package/src/security/enabledModulesRegistry.ts +12 -64
- package/dist/lib/ai/safety-identifier.js +0 -32
- package/dist/lib/ai/safety-identifier.js.map +0 -7
- package/dist/lib/auth/organizationScope.js +0 -34
- package/dist/lib/auth/organizationScope.js.map +0 -7
- package/dist/lib/bootstrap/clientOnlyModules.js +0 -55
- package/dist/lib/bootstrap/clientOnlyModules.js.map +0 -7
- package/dist/lib/data/consistency.js +0 -19
- package/dist/lib/data/consistency.js.map +0 -7
- package/dist/lib/db/entityMetadata.js +0 -26
- package/dist/lib/db/entityMetadata.js.map +0 -7
- package/dist/lib/logger/extension.js +0 -29
- package/dist/lib/logger/extension.js.map +0 -7
- package/dist/lib/modules/surfaceFingerprint.js +0 -47
- package/dist/lib/modules/surfaceFingerprint.js.map +0 -7
- package/dist/lib/query/ciphertext-search-warning.js +0 -45
- package/dist/lib/query/ciphertext-search-warning.js.map +0 -7
- package/dist/lib/search/auto-indexing.js +0 -14
- package/dist/lib/search/auto-indexing.js.map +0 -7
- package/dist/lib/search/availability.js +0 -107
- package/dist/lib/search/availability.js.map +0 -7
- package/dist/lib/search/tokenLookup.js +0 -46
- package/dist/lib/search/tokenLookup.js.map +0 -7
- package/dist/lib/telemetry/runtime.js +0 -35
- package/dist/lib/telemetry/runtime.js.map +0 -7
- package/dist/lib/webhooks/body.js +0 -57
- package/dist/lib/webhooks/body.js.map +0 -7
- package/dist/lib/webhooks/inbound-types.js +0 -1
- package/dist/lib/webhooks/inbound-types.js.map +0 -7
- package/dist/modules/widgets/extension-points.js +0 -96
- package/dist/modules/widgets/extension-points.js.map +0 -7
- package/dist/security/featurePolicy.js +0 -62
- package/dist/security/featurePolicy.js.map +0 -7
- package/scripts/versionSource.cjs +0 -65
- package/src/lib/__tests__/versionSource.test.ts +0 -46
- package/src/lib/ai/__tests__/llm-provider-contract.test.ts +0 -59
- package/src/lib/ai/__tests__/safety-identifier.test.ts +0 -71
- package/src/lib/ai/safety-identifier.ts +0 -73
- package/src/lib/auth/__tests__/organizationScope.test.ts +0 -107
- package/src/lib/auth/organizationScope.ts +0 -65
- package/src/lib/bootstrap/__tests__/clientOnlyModules.test.ts +0 -189
- package/src/lib/bootstrap/__tests__/dynamicLoader.aliasResolver.test.ts +0 -98
- package/src/lib/bootstrap/__tests__/dynamicLoader.appDi.test.ts +0 -211
- package/src/lib/bootstrap/__tests__/dynamicLoader.generatedCacheRecovery.test.ts +0 -192
- package/src/lib/bootstrap/clientOnlyModules.ts +0 -85
- package/src/lib/crud/__tests__/crud-factory.cache-user-scope.test.ts +0 -171
- package/src/lib/data/__tests__/consistency.test.ts +0 -38
- package/src/lib/data/consistency.ts +0 -17
- package/src/lib/db/__tests__/entityMetadata.test.ts +0 -38
- package/src/lib/db/__tests__/pg-errors.test.ts +0 -48
- package/src/lib/db/entityMetadata.ts +0 -43
- package/src/lib/di/__tests__/container-app-di-absent.test.ts +0 -155
- package/src/lib/di/__tests__/container-app-di.test.ts +0 -173
- package/src/lib/di/__tests__/registrar-error-log.test.ts +0 -154
- package/src/lib/i18n/__tests__/config.test.ts +0 -8
- package/src/lib/i18n/__tests__/server-dictionary-cache.test.ts +0 -30
- package/src/lib/i18n/__tests__/server-unbootstrapped.test.ts +0 -29
- package/src/lib/logger/extension.ts +0 -59
- package/src/lib/modules/__tests__/surfaceFingerprint.test.ts +0 -122
- package/src/lib/modules/surfaceFingerprint.ts +0 -87
- package/src/lib/query/__tests__/ciphertext-search-warning.test.ts +0 -178
- package/src/lib/query/ciphertext-search-warning.ts +0 -95
- package/src/lib/search/__tests__/availability.test.ts +0 -211
- package/src/lib/search/__tests__/tokenLookup.test.ts +0 -206
- package/src/lib/search/__tests__/tokenize.test.ts +0 -49
- package/src/lib/search/auto-indexing.ts +0 -22
- package/src/lib/search/availability.ts +0 -232
- package/src/lib/search/tokenLookup.ts +0 -133
- package/src/lib/telemetry/runtime.ts +0 -72
- package/src/lib/webhooks/__tests__/body.test.ts +0 -95
- package/src/lib/webhooks/body.ts +0 -70
- package/src/lib/webhooks/inbound-types.ts +0 -125
- package/src/modules/__tests__/nav-group-order-override.test.ts +0 -183
- package/src/modules/payment_gateways/__tests__/types.test.ts +0 -37
- package/src/modules/widgets/__tests__/extension-points.test.ts +0 -101
- package/src/modules/widgets/extension-points.ts +0 -524
- package/src/security/__tests__/featurePolicy.test.ts +0 -166
- package/src/security/featurePolicy.ts +0 -89
|
@@ -1,98 +1,16 @@
|
|
|
1
1
|
import type { BootstrapData } from './types'
|
|
2
|
-
import type { AppDiRegistrar } from '../di/container'
|
|
3
2
|
import { findAppRoot, type AppRoot } from './appResolver'
|
|
4
3
|
import { registerEntityIds } from '../encryption/entityIds'
|
|
5
|
-
import { createLogger } from '../logger'
|
|
6
4
|
import {
|
|
7
5
|
ensureMikroOrmV7GeneratedCacheCompatibility,
|
|
8
6
|
recoverMikroOrmV7GeneratedCacheFromImportError,
|
|
9
7
|
} from './generatedCacheRecovery'
|
|
10
|
-
import { CLIENT_ONLY_STUB_NAMESPACE, createClientOnlyStubPlugin } from './clientOnlyModules'
|
|
11
8
|
import path from 'node:path'
|
|
12
9
|
import fs from 'node:fs'
|
|
13
10
|
import crypto from 'node:crypto'
|
|
14
11
|
import { createRequire } from 'node:module'
|
|
15
12
|
import { pathToFileURL } from 'node:url'
|
|
16
13
|
|
|
17
|
-
const logger = createLogger('shared').child({ component: 'bootstrap' })
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Thrown when an expected generated source file is absent.
|
|
21
|
-
*
|
|
22
|
-
* Optional registries treat this as the supported compatibility case (an app
|
|
23
|
-
* that never generated the file), which is what makes it distinguishable from
|
|
24
|
-
* a file that exists but fails to compile or import.
|
|
25
|
-
*/
|
|
26
|
-
class GeneratedFileNotFoundError extends Error {
|
|
27
|
-
readonly filePath: string
|
|
28
|
-
|
|
29
|
-
constructor(filePath: string) {
|
|
30
|
-
super(`Generated file not found: ${filePath}`)
|
|
31
|
-
this.name = 'GeneratedFileNotFoundError'
|
|
32
|
-
this.filePath = filePath
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* esbuild plugins for the CLI bundle, in resolution order. The client-only stub must come
|
|
38
|
-
* first so it wins over the alias and external plugins for `*.client` dynamic imports.
|
|
39
|
-
*
|
|
40
|
-
* Exported so the wiring itself is testable: a test that only exercises
|
|
41
|
-
* `createClientOnlyStubPlugin` in isolation stays green if the plugin is dropped from this
|
|
42
|
-
* list, which would silently reintroduce #4623.
|
|
43
|
-
*/
|
|
44
|
-
export function createCliBundlePlugins(appRoot: string): import('esbuild').Plugin[] {
|
|
45
|
-
// Plugin to resolve the @/ alias the way the app tsconfig maps it:
|
|
46
|
-
// `@/.mercato/*` to the app root, every other `@/*` to the app's src/ directory.
|
|
47
|
-
const aliasPlugin: import('esbuild').Plugin = {
|
|
48
|
-
name: 'alias-resolver',
|
|
49
|
-
setup(build) {
|
|
50
|
-
build.onResolve({ filter: /^@\// }, (args) => {
|
|
51
|
-
const rest = args.path.slice('@/'.length)
|
|
52
|
-
const bases = rest.startsWith('.mercato/')
|
|
53
|
-
? [path.join(appRoot, rest)]
|
|
54
|
-
: [path.join(appRoot, 'src', rest), path.join(appRoot, rest)]
|
|
55
|
-
for (const base of bases) {
|
|
56
|
-
if (fs.existsSync(base) && fs.statSync(base).isFile()) {
|
|
57
|
-
return { path: base }
|
|
58
|
-
}
|
|
59
|
-
for (const suffix of ['.ts', '.tsx', '/index.ts', '/index.tsx']) {
|
|
60
|
-
if (fs.existsSync(base + suffix)) {
|
|
61
|
-
return { path: base + suffix }
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
// Nothing matched — hand esbuild the literal mapping so it reports the
|
|
66
|
-
// missing file against the path the app author actually wrote.
|
|
67
|
-
return { path: path.join(appRoot, rest) }
|
|
68
|
-
})
|
|
69
|
-
},
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Plugin to mark non-JSON package imports as external
|
|
73
|
-
const externalNonJsonPlugin: import('esbuild').Plugin = {
|
|
74
|
-
name: 'external-non-json',
|
|
75
|
-
setup(build) {
|
|
76
|
-
// Mark all package imports as external EXCEPT JSON files
|
|
77
|
-
// Filter matches paths that don't start with . or / (package imports like @open-mercato/shared)
|
|
78
|
-
build.onResolve({ filter: /^[^./]/ }, (args) => {
|
|
79
|
-
// Skip Windows absolute paths (e.g., C:\...) - they're local files, not packages
|
|
80
|
-
if (/^[a-zA-Z]:/.test(args.path)) {
|
|
81
|
-
return null // Let esbuild handle it
|
|
82
|
-
}
|
|
83
|
-
// If it's a JSON file, let esbuild bundle it
|
|
84
|
-
if (args.path.endsWith('.json')) {
|
|
85
|
-
return null // Let esbuild handle it
|
|
86
|
-
}
|
|
87
|
-
// Otherwise mark as external
|
|
88
|
-
return { path: args.path, external: true }
|
|
89
|
-
})
|
|
90
|
-
},
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return [createClientOnlyStubPlugin(), aliasPlugin, externalNonJsonPlugin]
|
|
94
|
-
}
|
|
95
|
-
|
|
96
14
|
const DYNAMIC_LOADER_CACHE_VERSION = 4
|
|
97
15
|
|
|
98
16
|
type DynamicLoaderCacheMetadata = {
|
|
@@ -249,7 +167,6 @@ function collectDependencyHashes(
|
|
|
249
167
|
): Record<string, string> {
|
|
250
168
|
return Object.fromEntries(
|
|
251
169
|
Object.keys(inputs)
|
|
252
|
-
.filter((inputPath) => !inputPath.startsWith(`${CLIENT_ONLY_STUB_NAMESPACE}:`))
|
|
253
170
|
.map((inputPath) => {
|
|
254
171
|
const absolutePath = path.isAbsolute(inputPath)
|
|
255
172
|
? inputPath
|
|
@@ -304,33 +221,14 @@ function cacheIsValid(
|
|
|
304
221
|
&& dependenciesAreValid(appRoot, metadata.dependencies)
|
|
305
222
|
}
|
|
306
223
|
|
|
307
|
-
/**
|
|
308
|
-
* Options for `compileAndImport`.
|
|
309
|
-
*
|
|
310
|
-
* Both paths default to the generated-registry layout (`<appRoot>/.mercato/generated/<file>.ts`
|
|
311
|
-
* compiled to a `.mjs` sibling). Sources that live elsewhere in the app — `src/di.ts` — MUST pass
|
|
312
|
-
* both explicitly: the default app root is derived by walking three directories up from the source,
|
|
313
|
-
* which only holds inside `.mercato/generated`.
|
|
314
|
-
*/
|
|
315
|
-
type CompileAndImportOptions = {
|
|
316
|
-
appRoot?: string
|
|
317
|
-
outFile?: string
|
|
318
|
-
allowRecovery?: boolean
|
|
319
|
-
}
|
|
320
|
-
|
|
321
224
|
/**
|
|
322
225
|
* Compile a TypeScript file to JavaScript using esbuild bundler.
|
|
323
226
|
* This bundles the file and all its dependencies, handling JSON imports properly.
|
|
324
|
-
* The compiled file is written next to the source file with a .mjs extension
|
|
325
|
-
* `outFile` says otherwise.
|
|
227
|
+
* The compiled file is written next to the source file with a .mjs extension.
|
|
326
228
|
*/
|
|
327
|
-
async function compileAndImport(
|
|
328
|
-
tsPath
|
|
329
|
-
|
|
330
|
-
): Promise<Record<string, unknown>> {
|
|
331
|
-
const allowRecovery = options.allowRecovery ?? true
|
|
332
|
-
const jsPath = options.outFile ?? tsPath.replace(/\.ts$/, '.mjs')
|
|
333
|
-
const appRoot = options.appRoot ?? path.dirname(path.dirname(path.dirname(tsPath)))
|
|
229
|
+
async function compileAndImport(tsPath: string, allowRecovery: boolean = true): Promise<Record<string, unknown>> {
|
|
230
|
+
const jsPath = tsPath.replace(/\.ts$/, '.mjs')
|
|
231
|
+
const appRoot = path.dirname(path.dirname(path.dirname(tsPath)))
|
|
334
232
|
const appTsconfig = path.join(appRoot, 'tsconfig.json')
|
|
335
233
|
const metadataPath = cacheMetadataPath(jsPath)
|
|
336
234
|
|
|
@@ -338,7 +236,7 @@ async function compileAndImport(
|
|
|
338
236
|
const tsconfigExists = fs.existsSync(appTsconfig)
|
|
339
237
|
|
|
340
238
|
if (!tsExists) {
|
|
341
|
-
throw new
|
|
239
|
+
throw new Error(`Generated file not found: ${tsPath}`)
|
|
342
240
|
}
|
|
343
241
|
if (!tsconfigExists) {
|
|
344
242
|
throw new Error(`App TypeScript config not found: ${appTsconfig}`)
|
|
@@ -349,10 +247,50 @@ async function compileAndImport(
|
|
|
349
247
|
const needsCompile = !cacheIsValid(appRoot, jsPath, metadataPath, expectedInputHash)
|
|
350
248
|
|
|
351
249
|
if (needsCompile) {
|
|
352
|
-
fs.mkdirSync(path.dirname(jsPath), { recursive: true })
|
|
353
250
|
// Dynamically import esbuild only when needed
|
|
354
251
|
const esbuild = await import('esbuild')
|
|
355
252
|
|
|
253
|
+
// Plugin to resolve @/ alias to app root (works for @app modules)
|
|
254
|
+
const aliasPlugin: import('esbuild').Plugin = {
|
|
255
|
+
name: 'alias-resolver',
|
|
256
|
+
setup(build) {
|
|
257
|
+
// Resolve @/ alias to app root
|
|
258
|
+
build.onResolve({ filter: /^@\// }, (args) => {
|
|
259
|
+
const resolved = path.join(appRoot, args.path.slice(2))
|
|
260
|
+
// Try with .ts extension if base path doesn't exist
|
|
261
|
+
if (!fs.existsSync(resolved) && fs.existsSync(resolved + '.ts')) {
|
|
262
|
+
return { path: resolved + '.ts' }
|
|
263
|
+
}
|
|
264
|
+
// Also check for /index.ts if it's a directory
|
|
265
|
+
if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory() && fs.existsSync(path.join(resolved, 'index.ts'))) {
|
|
266
|
+
return { path: path.join(resolved, 'index.ts') }
|
|
267
|
+
}
|
|
268
|
+
return { path: resolved }
|
|
269
|
+
})
|
|
270
|
+
},
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Plugin to mark non-JSON package imports as external
|
|
274
|
+
const externalNonJsonPlugin: import('esbuild').Plugin = {
|
|
275
|
+
name: 'external-non-json',
|
|
276
|
+
setup(build) {
|
|
277
|
+
// Mark all package imports as external EXCEPT JSON files
|
|
278
|
+
// Filter matches paths that don't start with . or / (package imports like @open-mercato/shared)
|
|
279
|
+
build.onResolve({ filter: /^[^./]/ }, (args) => {
|
|
280
|
+
// Skip Windows absolute paths (e.g., C:\...) - they're local files, not packages
|
|
281
|
+
if (/^[a-zA-Z]:/.test(args.path)) {
|
|
282
|
+
return null // Let esbuild handle it
|
|
283
|
+
}
|
|
284
|
+
// If it's a JSON file, let esbuild bundle it
|
|
285
|
+
if (args.path.endsWith('.json')) {
|
|
286
|
+
return null // Let esbuild handle it
|
|
287
|
+
}
|
|
288
|
+
// Otherwise mark as external
|
|
289
|
+
return { path: args.path, external: true }
|
|
290
|
+
})
|
|
291
|
+
},
|
|
292
|
+
}
|
|
293
|
+
|
|
356
294
|
// Use esbuild.build with bundling to handle JSON imports
|
|
357
295
|
const result = await esbuild.build({
|
|
358
296
|
entryPoints: [tsPath],
|
|
@@ -364,7 +302,7 @@ async function compileAndImport(
|
|
|
364
302
|
platform: 'node',
|
|
365
303
|
target: 'node18',
|
|
366
304
|
tsconfig: appTsconfig,
|
|
367
|
-
plugins:
|
|
305
|
+
plugins: [aliasPlugin, externalNonJsonPlugin],
|
|
368
306
|
// Allow JSON imports
|
|
369
307
|
loader: { '.json': 'json' },
|
|
370
308
|
})
|
|
@@ -395,45 +333,25 @@ async function compileAndImport(
|
|
|
395
333
|
throw error
|
|
396
334
|
}
|
|
397
335
|
|
|
398
|
-
return compileAndImport(tsPath,
|
|
336
|
+
return compileAndImport(tsPath, false)
|
|
399
337
|
}
|
|
400
338
|
}
|
|
401
339
|
|
|
402
340
|
|
|
403
341
|
/**
|
|
404
|
-
*
|
|
342
|
+
* Dynamically load bootstrap data from a resolved app directory.
|
|
343
|
+
*
|
|
344
|
+
* IMPORTANT: This only works in unbundled contexts (CLI, tsx).
|
|
345
|
+
* Do NOT use this in Next.js bundled code - use static imports instead.
|
|
405
346
|
*
|
|
406
|
-
*
|
|
407
|
-
*
|
|
408
|
-
*
|
|
409
|
-
*
|
|
410
|
-
*
|
|
411
|
-
*
|
|
347
|
+
* For CLI context, we skip loading modules.generated.ts which has Next.js dependencies.
|
|
348
|
+
* CLI commands are discovered separately via the CLI module system.
|
|
349
|
+
*
|
|
350
|
+
* @param appRoot - Optional explicit app root path. If not provided, will search from cwd.
|
|
351
|
+
* @returns The loaded bootstrap data
|
|
352
|
+
* @throws Error if app root cannot be found or generated files are missing
|
|
412
353
|
*/
|
|
413
|
-
async function
|
|
414
|
-
tsPath: string,
|
|
415
|
-
fallback: Record<string, unknown>,
|
|
416
|
-
): Promise<Record<string, unknown>> {
|
|
417
|
-
try {
|
|
418
|
-
return await compileAndImport(tsPath)
|
|
419
|
-
} catch (error) {
|
|
420
|
-
if (error instanceof GeneratedFileNotFoundError) {
|
|
421
|
-
logger.debug('Optional generated registry not present, using empty fallback', {
|
|
422
|
-
file: path.basename(tsPath),
|
|
423
|
-
})
|
|
424
|
-
return fallback
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
logger.error('Failed to load generated registry, continuing without its entries', {
|
|
428
|
-
file: path.basename(tsPath),
|
|
429
|
-
filePath: tsPath,
|
|
430
|
-
err: error,
|
|
431
|
-
})
|
|
432
|
-
return fallback
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
function resolveAppRootOrThrow(appRoot?: string): AppRoot {
|
|
354
|
+
export async function loadBootstrapData(appRoot?: string): Promise<BootstrapData> {
|
|
437
355
|
const resolved: AppRoot | null = appRoot
|
|
438
356
|
? {
|
|
439
357
|
generatedDir: path.join(appRoot, '.mercato', 'generated'),
|
|
@@ -450,68 +368,6 @@ function resolveAppRootOrThrow(appRoot?: string): AppRoot {
|
|
|
450
368
|
)
|
|
451
369
|
}
|
|
452
370
|
|
|
453
|
-
return resolved
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
/**
|
|
457
|
-
* Load the app-level DI registrar (`src/di.ts`) for the dynamic bootstrap path.
|
|
458
|
-
*
|
|
459
|
-
* The Next.js runtime imports `@/di` statically from its own `src/bootstrap.ts` and hands the
|
|
460
|
-
* registrar to `createBootstrap`. Worker, scheduler and CLI processes bootstrap through
|
|
461
|
-
* `bootstrapFromAppRoot` instead, where the `@/` alias does not exist — so without this the app's
|
|
462
|
-
* DI registrations silently never ran there, and every request container paid a failed
|
|
463
|
-
* `import('@/di')` resolution (the compatibility fallback in `lib/di/container.ts`).
|
|
464
|
-
*
|
|
465
|
-
* An absent `src/di.ts` is the supported case and resolves to `null` quietly. A file that exists
|
|
466
|
-
* but cannot be compiled, imported, or does not export `register` is reported at error level and
|
|
467
|
-
* still resolves to `null`, so a broken app DI module degrades the same way a broken generated
|
|
468
|
-
* registry does (#4327, #4491) instead of taking the whole process down.
|
|
469
|
-
*/
|
|
470
|
-
async function loadAppDiRegistrar(appDir: string): Promise<AppDiRegistrar | null> {
|
|
471
|
-
const tsPath = path.join(appDir, 'src', 'di.ts')
|
|
472
|
-
if (!fs.existsSync(tsPath)) {
|
|
473
|
-
logger.debug('App-level DI module not present, skipping its registrations', { filePath: tsPath })
|
|
474
|
-
return null
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
try {
|
|
478
|
-
const appDiModule = await compileAndImport(tsPath, {
|
|
479
|
-
appRoot: appDir,
|
|
480
|
-
outFile: path.join(appDir, '.mercato', 'generated', 'app-di.compiled.mjs'),
|
|
481
|
-
})
|
|
482
|
-
const register = appDiModule.register
|
|
483
|
-
if (typeof register !== 'function') {
|
|
484
|
-
logger.error('App-level DI module exports no register(); its registrations are skipped', {
|
|
485
|
-
filePath: tsPath,
|
|
486
|
-
})
|
|
487
|
-
return null
|
|
488
|
-
}
|
|
489
|
-
return register as AppDiRegistrar
|
|
490
|
-
} catch (error) {
|
|
491
|
-
logger.error('Failed to load the app-level DI module; its registrations are skipped', {
|
|
492
|
-
filePath: tsPath,
|
|
493
|
-
err: error,
|
|
494
|
-
})
|
|
495
|
-
return null
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
/**
|
|
500
|
-
* Dynamically load bootstrap data from a resolved app directory.
|
|
501
|
-
*
|
|
502
|
-
* IMPORTANT: This only works in unbundled contexts (CLI, tsx).
|
|
503
|
-
* Do NOT use this in Next.js bundled code - use static imports instead.
|
|
504
|
-
*
|
|
505
|
-
* For CLI context, we skip loading modules.generated.ts which has Next.js dependencies.
|
|
506
|
-
* CLI commands are discovered separately via the CLI module system.
|
|
507
|
-
*
|
|
508
|
-
* @param appRoot - Optional explicit app root path. If not provided, will search from cwd.
|
|
509
|
-
* @returns The loaded bootstrap data
|
|
510
|
-
* @throws Error if app root cannot be found or generated files are missing
|
|
511
|
-
*/
|
|
512
|
-
export async function loadBootstrapData(appRoot?: string): Promise<BootstrapData> {
|
|
513
|
-
const resolved = resolveAppRootOrThrow(appRoot)
|
|
514
|
-
|
|
515
371
|
const { generatedDir } = resolved
|
|
516
372
|
|
|
517
373
|
ensureMikroOrmV7GeneratedCacheCompatibility(resolved.appDir)
|
|
@@ -536,12 +392,10 @@ export async function loadBootstrapData(appRoot?: string): Promise<BootstrapData
|
|
|
536
392
|
compileAndImport(path.join(generatedDir, 'modules.cli.generated.ts')),
|
|
537
393
|
compileAndImport(path.join(generatedDir, 'entities.generated.ts')),
|
|
538
394
|
compileAndImport(path.join(generatedDir, 'di.generated.ts')),
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
}),
|
|
544
|
-
loadOptionalGeneratedModule(path.join(generatedDir, 'workflows.generated.ts'), { allCodeWorkflows: [] }),
|
|
395
|
+
compileAndImport(path.join(generatedDir, 'search.generated.ts')).catch(() => ({ searchModuleConfigs: [] })),
|
|
396
|
+
compileAndImport(path.join(generatedDir, 'command-loaders.generated.ts')).catch(() => ({ commandLoaderEntries: [] })),
|
|
397
|
+
compileAndImport(path.join(generatedDir, 'command-interceptors.generated.ts')).catch(() => ({ commandInterceptorEntries: [] })),
|
|
398
|
+
compileAndImport(path.join(generatedDir, 'workflows.generated.ts')).catch(() => ({ allCodeWorkflows: [] })),
|
|
545
399
|
])
|
|
546
400
|
|
|
547
401
|
return {
|
|
@@ -583,10 +437,8 @@ export async function loadBootstrapData(appRoot?: string): Promise<BootstrapData
|
|
|
583
437
|
*/
|
|
584
438
|
export async function bootstrapFromAppRoot(appRoot?: string): Promise<BootstrapData> {
|
|
585
439
|
const { createBootstrap, waitForAsyncRegistration } = await import('./factory.js')
|
|
586
|
-
const
|
|
587
|
-
const
|
|
588
|
-
const appDiRegistrar = await loadAppDiRegistrar(resolved.appDir)
|
|
589
|
-
const bootstrap = createBootstrap(data, appDiRegistrar ? { appDiRegistrar } : {})
|
|
440
|
+
const data = await loadBootstrapData(appRoot)
|
|
441
|
+
const bootstrap = createBootstrap(data)
|
|
590
442
|
bootstrap()
|
|
591
443
|
// In CLI context, wait for async registrations (UI widgets, search configs, etc.)
|
|
592
444
|
await waitForAsyncRegistration()
|
|
@@ -7,10 +7,6 @@ import {
|
|
|
7
7
|
} from '../command-interceptor-runner'
|
|
8
8
|
import type { CommandInterceptor, CommandInterceptorContext, CommandInterceptorUndoContext } from '../command-interceptor'
|
|
9
9
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
10
|
-
import {
|
|
11
|
-
applyAclFeatureOverrides,
|
|
12
|
-
resetModuleContractOverridesForTests,
|
|
13
|
-
} from '@open-mercato/shared/modules/overrides'
|
|
14
10
|
|
|
15
11
|
jest.mock('@open-mercato/shared/lib/logger', () => {
|
|
16
12
|
const mocked = {
|
|
@@ -63,10 +59,6 @@ describe('matchesCommandPattern', () => {
|
|
|
63
59
|
})
|
|
64
60
|
|
|
65
61
|
describe('runCommandInterceptorsBefore', () => {
|
|
66
|
-
afterEach(() => {
|
|
67
|
-
resetModuleContractOverridesForTests()
|
|
68
|
-
})
|
|
69
|
-
|
|
70
62
|
it('returns ok when no interceptors match', async () => {
|
|
71
63
|
const interceptor = makeInterceptor({
|
|
72
64
|
id: 'i1',
|
|
@@ -166,26 +158,6 @@ describe('runCommandInterceptorsBefore', () => {
|
|
|
166
158
|
expect(withWildcard.ok).toBe(false)
|
|
167
159
|
expect(withWildcard.error?.message).toBe('Blocked by wildcard')
|
|
168
160
|
})
|
|
169
|
-
|
|
170
|
-
it('does not run an interceptor gated by a nulled ACL feature', async () => {
|
|
171
|
-
applyAclFeatureOverrides({ 'premium.audit': null })
|
|
172
|
-
const interceptor = makeInterceptor({
|
|
173
|
-
id: 'i1',
|
|
174
|
-
features: ['premium.audit'],
|
|
175
|
-
beforeExecute: jest.fn().mockResolvedValue({ ok: false }),
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
const result = await runCommandInterceptorsBefore(
|
|
179
|
-
[interceptor],
|
|
180
|
-
'customers.create-person',
|
|
181
|
-
{},
|
|
182
|
-
baseContext,
|
|
183
|
-
['*', 'premium.audit'],
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
expect(result.ok).toBe(true)
|
|
187
|
-
expect(interceptor.beforeExecute).not.toHaveBeenCalled()
|
|
188
|
-
})
|
|
189
161
|
})
|
|
190
162
|
|
|
191
163
|
describe('runCommandInterceptorsAfter', () => {
|
|
@@ -31,7 +31,6 @@ import {
|
|
|
31
31
|
} from './command-interceptor-runner'
|
|
32
32
|
import type { CommandInterceptorContext } from './command-interceptor'
|
|
33
33
|
import { CommandInterceptorError } from './errors'
|
|
34
|
-
import { isReadProjectionAlwaysConsistent } from '@open-mercato/shared/lib/data/consistency'
|
|
35
34
|
import { createLogger } from '../logger'
|
|
36
35
|
|
|
37
36
|
const logger = createLogger('shared').child({ component: 'commands' })
|
|
@@ -698,10 +697,7 @@ export class CommandBus {
|
|
|
698
697
|
try {
|
|
699
698
|
const dataEngine = (container.resolve('dataEngine') as DataEngine)
|
|
700
699
|
await dataEngine.flushOrmEntityChanges(suppress)
|
|
701
|
-
} catch
|
|
702
|
-
if (isReadProjectionAlwaysConsistent()) {
|
|
703
|
-
throw error
|
|
704
|
-
}
|
|
700
|
+
} catch {
|
|
705
701
|
// best-effort: failures should not block command execution
|
|
706
702
|
}
|
|
707
703
|
}
|
|
@@ -3,7 +3,7 @@ import type {
|
|
|
3
3
|
CommandInterceptorContext,
|
|
4
4
|
CommandInterceptorUndoContext,
|
|
5
5
|
} from './command-interceptor'
|
|
6
|
-
import {
|
|
6
|
+
import { hasAllFeatures } from '../../security/features'
|
|
7
7
|
import { createLogger } from '../logger'
|
|
8
8
|
|
|
9
9
|
const logger = createLogger('shared').child({ component: 'commands' })
|
|
@@ -33,7 +33,7 @@ function collectMatching(
|
|
|
33
33
|
): CommandInterceptor[] {
|
|
34
34
|
return interceptors
|
|
35
35
|
.filter((i) => matchesCommandPattern(i.targetCommand, commandId))
|
|
36
|
-
.filter((i) =>
|
|
36
|
+
.filter((i) => hasAllFeatures(userFeatures, i.features))
|
|
37
37
|
.sort((a, b) => (a.priority ?? 50) - (b.priority ?? 50))
|
|
38
38
|
}
|
|
39
39
|
|