@open-mercato/core 0.6.8-develop.6918.1.7b3f32b0c8 → 0.6.8-develop.6924.1.a8d208fcdc
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/bootstrap.js +2 -1
- package/dist/bootstrap.js.map +2 -2
- package/dist/modules/catalog/search.js +8 -0
- package/dist/modules/catalog/search.js.map +2 -2
- package/dist/modules/customer_accounts/search.js +2 -0
- package/dist/modules/customer_accounts/search.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +55 -8
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/components/detail/create/dealCustomFieldControl.js +1 -1
- package/dist/modules/customers/components/detail/create/dealCustomFieldControl.js.map +2 -2
- package/dist/modules/customers/components/formConfig.js +2 -0
- package/dist/modules/customers/components/formConfig.js.map +2 -2
- package/dist/modules/inbox_ops/search.js +1 -0
- package/dist/modules/inbox_ops/search.js.map +2 -2
- package/dist/modules/messages/search.js +2 -1
- package/dist/modules/messages/search.js.map +2 -2
- package/dist/modules/planner/search.js +1 -0
- package/dist/modules/planner/search.js.map +2 -2
- package/dist/modules/resources/search.js +2 -0
- package/dist/modules/resources/search.js.map +2 -2
- package/dist/modules/sales/search.js +24 -2
- package/dist/modules/sales/search.js.map +2 -2
- package/dist/modules/staff/search.js +4 -0
- package/dist/modules/staff/search.js.map +2 -2
- package/dist/modules/wms/search.js +4 -0
- package/dist/modules/wms/search.js.map +2 -2
- package/package.json +7 -7
- package/src/bootstrap.ts +9 -2
- package/src/modules/catalog/search.ts +8 -0
- package/src/modules/customer_accounts/search.ts +2 -0
- package/src/modules/customers/commands/deals.ts +86 -14
- package/src/modules/customers/components/detail/create/dealCustomFieldControl.tsx +1 -1
- package/src/modules/customers/components/formConfig.tsx +2 -0
- package/src/modules/inbox_ops/search.ts +1 -0
- package/src/modules/messages/search.ts +2 -1
- package/src/modules/planner/search.ts +1 -0
- package/src/modules/resources/search.ts +2 -0
- package/src/modules/sales/search.ts +24 -2
- package/src/modules/staff/search.ts +4 -0
- package/src/modules/wms/search.ts +4 -0
package/dist/bootstrap.js
CHANGED
|
@@ -120,7 +120,8 @@ async function bootstrap(container) {
|
|
|
120
120
|
try {
|
|
121
121
|
const { getModules } = await import("@open-mercato/shared/lib/i18n/server");
|
|
122
122
|
loadedModules = getModules();
|
|
123
|
-
} catch {
|
|
123
|
+
} catch (err) {
|
|
124
|
+
logger.warn("Module registry unavailable; event bus starts with no module subscribers", { err });
|
|
124
125
|
}
|
|
125
126
|
const subs = loadedModules.flatMap(
|
|
126
127
|
(m) => (m.subscribers || []).map((subscriber) => ({
|
package/dist/bootstrap.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/bootstrap.ts"],
|
|
4
|
-
"sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport { asValue } from 'awilix'\nimport { getDefaultEncryptionMaps, type ModuleSubscriber } from '@open-mercato/shared/modules/registry'\nimport type { ModuleEncryptionMap } from '@open-mercato/shared/modules/encryption'\nimport { createEventBus } from '@open-mercato/events/index'\nimport { setGlobalEventBus } from '@open-mercato/shared/modules/events'\nimport { createCacheService } from '@open-mercato/cache'\nimport type { CacheStrategy } from '@open-mercato/cache'\nimport { createKmsService } from '@open-mercato/shared/lib/encryption/kms'\nimport { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport { registerTenantEncryptionSubscriber } from '@open-mercato/shared/lib/encryption/subscriber'\nimport { isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'\nimport { getSearchModuleConfigs } from '@open-mercato/shared/modules/search'\nimport {\n registerSearchModule,\n createSearchDeleteSubscriber,\n searchDeleteMetadata,\n} from '@open-mercato/search'\nimport { RateLimiterService } from '@open-mercato/shared/lib/ratelimit/service'\nimport { readRateLimitConfig } from '@open-mercato/shared/lib/ratelimit/config'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('core')\n\n// Use globalThis to survive tsx/webpack module duplication (same pattern as container.ts DI registrars)\nconst RL_GLOBAL_KEY = '__openMercatoRateLimiterService__'\nconst RL_SHUTDOWN_KEY = '__openMercatoRateLimiterShutdown__'\n\nconst CACHE_GLOBAL_KEY = '__openMercatoCacheService__'\nconst CACHE_SHUTDOWN_KEY = '__openMercatoCacheShutdown__'\n\n// Escape hatch: set OM_CACHE_SINGLETON=off to fall back to the legacy\n// per-request cache instance (e.g. to isolate a regression). Default ON.\nfunction isCacheSingletonEnabled(): boolean {\n const raw = process.env.OM_CACHE_SINGLETON\n if (raw === undefined) return true\n const normalized = raw.trim().toLowerCase()\n if (!normalized.length) return true\n return !(normalized === '0' || normalized === 'off' || normalized === 'false' || normalized === 'no')\n}\n\n/**\n * Process-wide cache service singleton, mirroring getCachedRateLimiterService.\n *\n * bootstrap() previously built a fresh cache per request container, so under\n * the default memory strategy every cross-request cache was a no-op (each\n * instance is process-local) and under sqlite each request leaked a native\n * handle. Reusing one instance is safe by construction: tenant scope resolves\n * per-call via AsyncLocalStorage (packages/cache/src/tenantContext.ts), so the\n * service holds no request-bound state.\n *\n * Returns null when disabled via the escape hatch or when creation fails, so\n * the caller falls back to a per-request instance.\n */\nexport function getCachedCacheService(): CacheStrategy | null {\n if (!isCacheSingletonEnabled()) return null\n let service = (globalThis as any)[CACHE_GLOBAL_KEY] as CacheStrategy | null ?? null\n if (!service) {\n try {\n try {\n service = createCacheService()\n } catch (err) {\n logger.warn('Cache service initialization failed; falling back to memory strategy', { err })\n service = createCacheService({ strategy: 'memory' })\n }\n ;(globalThis as any)[CACHE_GLOBAL_KEY] = service\n\n // Register shutdown hook once to close persistent handles (sqlite/redis) on process exit\n if (!(globalThis as any)[CACHE_SHUTDOWN_KEY]) {\n const shutdown = () => { service?.close?.().catch(() => {}) }\n process.once('SIGTERM', shutdown)\n process.once('SIGINT', shutdown)\n ;(globalThis as any)[CACHE_SHUTDOWN_KEY] = true\n }\n } catch (err) {\n logger.warn('Failed to create cache service', { component: 'cache', err })\n }\n }\n return service\n}\n\nexport function getCachedRateLimiterService(): RateLimiterService | null {\n let service = (globalThis as any)[RL_GLOBAL_KEY] as RateLimiterService | null ?? null\n if (!service) {\n try {\n const rateLimitConfig = readRateLimitConfig()\n service = new RateLimiterService(rateLimitConfig)\n // Fire-and-forget async init (only needed for Redis strategy;\n // memory strategy works synchronously, and Redis has an in-memory\n // insurance limiter so the first few requests are still protected)\n service.initialize().catch((err) => {\n logger.warn('Async initialization failed', { component: 'ratelimit', err })\n })\n ;(globalThis as any)[RL_GLOBAL_KEY] = service\n\n // Register shutdown hook once to disconnect Redis on process exit\n if (!(globalThis as any)[RL_SHUTDOWN_KEY]) {\n const shutdown = () => { service?.destroy().catch(() => {}) }\n process.once('SIGTERM', shutdown)\n process.once('SIGINT', shutdown)\n ;(globalThis as any)[RL_SHUTDOWN_KEY] = true\n }\n } catch (err) {\n logger.warn('Failed to create rate limiter service', { component: 'ratelimit', err })\n }\n }\n return service\n}\n\nexport async function bootstrap(container: AwilixContainer) {\n // Register the cache service. Prefer the process-wide singleton so caches\n // survive across request containers; fall back to a per-request instance\n // when the singleton is disabled or fails to build.\n let cache: any = getCachedCacheService()\n if (!cache) {\n try {\n cache = createCacheService()\n } catch (err: any) {\n logger.warn('Cache service initialization failed; falling back to memory strategy', { err })\n cache = createCacheService({ strategy: 'memory' })\n }\n }\n container.register({ cache: asValue(cache) })\n\n // Create and register the DI-aware event bus\n let eventBus: any\n try {\n // Support both QUEUE_STRATEGY and legacy EVENTS_STRATEGY env vars\n const strategyEnv = process.env.QUEUE_STRATEGY || process.env.EVENTS_STRATEGY\n const queueStrategy = strategyEnv === 'async' || strategyEnv === 'redis' ? 'async' : 'local'\n eventBus = createEventBus({ resolve: container.resolve.bind(container) as any, queueStrategy })\n } catch (err: any) {\n // Fall back to local strategy to avoid breaking the app on misconfiguration\n logger.warn('Event bus initialization failed; falling back to local strategy', { err })\n try {\n eventBus = createEventBus({ resolve: container.resolve.bind(container) as any, queueStrategy: 'local' })\n } catch {\n // In extreme cases, provide a no-op bus to avoid crashes\n eventBus = {\n emit: async () => {},\n on: () => {},\n registerModuleSubscribers: () => {},\n clearQueue: async () => ({ removed: 0 }),\n }\n }\n }\n container.register({ eventBus: asValue(eventBus) })\n // Wire the global event bus so createModuleEvents().emit works outside DI context\n setGlobalEventBus(eventBus)\n // Auto-register discovered module subscribers\n try {\n let loadedModules: any[] = []\n try {\n const { getModules } = await import('@open-mercato/shared/lib/i18n/server')\n loadedModules = getModules()\n } catch {}\n const subs = loadedModules.flatMap((m) =>\n (m.subscribers || []).map((subscriber: ModuleSubscriber) => ({\n ...subscriber,\n moduleId: subscriber.moduleId ?? m.id,\n })),\n )\n if (subs.length) (container.resolve as any)('eventBus').registerModuleSubscribers(subs)\n\n // Extract sync subscribers and register in the sync-subscriber-store\n const syncSubs = subs.filter((s: any) => s.sync === true)\n if (syncSubs.length) {\n try {\n const { registerSyncSubscribers } = await import('@open-mercato/shared/lib/crud/sync-subscriber-store')\n registerSyncSubscribers(\n syncSubs.map((s: any) => ({\n metadata: { event: s.event, sync: true as const, priority: s.priority, id: s.id },\n handler: s.handler,\n })),\n )\n } catch {\n // sync-subscriber-store may not be available\n }\n }\n } catch (err) {\n logger.error(\"Failed to register module subscribers:\", { err });\n }\n\n // KMS + tenant encryption\n const kmsService = createKmsService()\n container.register({ kmsService: asValue(kmsService) })\n let defaultEncryptionMaps: ModuleEncryptionMap[] = []\n if (isTenantDataEncryptionEnabled()) {\n try {\n const { getModules } = await import('@open-mercato/shared/lib/i18n/server')\n defaultEncryptionMaps = getDefaultEncryptionMaps(getModules())\n } catch (err) {\n logger.error('Failed to load default encryption maps', { component: 'encryption', err })\n throw err\n }\n }\n try {\n const em = container.resolve('em') as EntityManager\n const cacheService = (() => {\n try { return container.resolve('cache') as any } catch { return null }\n })()\n const tenantEncryptionService = new TenantDataEncryptionService(em, {\n cache: cacheService,\n kms: kmsService,\n defaultEncryptionMaps,\n })\n container.register({ tenantEncryptionService: asValue(tenantEncryptionService) })\n if (isTenantDataEncryptionEnabled() && kmsService.isHealthy()) {\n try {\n registerTenantEncryptionSubscriber(em, tenantEncryptionService)\n } catch (err) {\n logger.warn('Failed to register MikroORM encryption subscriber', { component: 'encryption', err })\n }\n } else if (isTenantDataEncryptionEnabled() && !kmsService.isHealthy()) {\n logger.warn('Vault/KMS unhealthy - tenant data encryption is disabled until recovery', { component: 'encryption' })\n }\n } catch (err) {\n logger.warn('Failed to initialize tenant encryption service', { component: 'encryption', err })\n }\n\n // Register rate limiter service (singleton via globalThis \u2014 reused across request containers)\n // getCachedRateLimiterService() never throws; returns null on failure\n const rateLimiterService = getCachedRateLimiterService()\n if (rateLimiterService) {\n container.register({ rateLimiterService: asValue(rateLimiterService) })\n }\n\n // Register search module\n try {\n // Get configs from global registry (registered during app bootstrap)\n const searchModuleConfigs = getSearchModuleConfigs()\n registerSearchModule(container as any, { moduleConfigs: searchModuleConfigs })\n\n // Register searchModuleConfigs in container so status API can access vector-enabled entities\n container.register({\n searchModuleConfigs: asValue(searchModuleConfigs),\n })\n\n // Register search delete event subscriber\n // Note: search.index_record is now handled by auto-discovered fulltext_upsert.ts subscriber\n try {\n const searchIndexer = container.resolve('searchIndexer') as any\n if (searchIndexer && eventBus) {\n eventBus.registerModuleSubscribers([\n {\n event: searchDeleteMetadata.event,\n persistent: searchDeleteMetadata.persistent,\n handler: createSearchDeleteSubscriber(searchIndexer),\n },\n ])\n }\n } catch {\n // searchIndexer may not be available\n }\n } catch (err) {\n logger.warn('Failed to register search module', { component: 'search', err })\n }\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport { asValue } from 'awilix'\nimport { getDefaultEncryptionMaps, type ModuleSubscriber } from '@open-mercato/shared/modules/registry'\nimport type { ModuleEncryptionMap } from '@open-mercato/shared/modules/encryption'\nimport { createEventBus } from '@open-mercato/events/index'\nimport { setGlobalEventBus } from '@open-mercato/shared/modules/events'\nimport { createCacheService } from '@open-mercato/cache'\nimport type { CacheStrategy } from '@open-mercato/cache'\nimport { createKmsService } from '@open-mercato/shared/lib/encryption/kms'\nimport { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport { registerTenantEncryptionSubscriber } from '@open-mercato/shared/lib/encryption/subscriber'\nimport { isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'\nimport { getSearchModuleConfigs } from '@open-mercato/shared/modules/search'\nimport {\n registerSearchModule,\n createSearchDeleteSubscriber,\n searchDeleteMetadata,\n} from '@open-mercato/search'\nimport { RateLimiterService } from '@open-mercato/shared/lib/ratelimit/service'\nimport { readRateLimitConfig } from '@open-mercato/shared/lib/ratelimit/config'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('core')\n\n// Use globalThis to survive tsx/webpack module duplication (same pattern as container.ts DI registrars)\nconst RL_GLOBAL_KEY = '__openMercatoRateLimiterService__'\nconst RL_SHUTDOWN_KEY = '__openMercatoRateLimiterShutdown__'\n\nconst CACHE_GLOBAL_KEY = '__openMercatoCacheService__'\nconst CACHE_SHUTDOWN_KEY = '__openMercatoCacheShutdown__'\n\n// Escape hatch: set OM_CACHE_SINGLETON=off to fall back to the legacy\n// per-request cache instance (e.g. to isolate a regression). Default ON.\nfunction isCacheSingletonEnabled(): boolean {\n const raw = process.env.OM_CACHE_SINGLETON\n if (raw === undefined) return true\n const normalized = raw.trim().toLowerCase()\n if (!normalized.length) return true\n return !(normalized === '0' || normalized === 'off' || normalized === 'false' || normalized === 'no')\n}\n\n/**\n * Process-wide cache service singleton, mirroring getCachedRateLimiterService.\n *\n * bootstrap() previously built a fresh cache per request container, so under\n * the default memory strategy every cross-request cache was a no-op (each\n * instance is process-local) and under sqlite each request leaked a native\n * handle. Reusing one instance is safe by construction: tenant scope resolves\n * per-call via AsyncLocalStorage (packages/cache/src/tenantContext.ts), so the\n * service holds no request-bound state.\n *\n * Returns null when disabled via the escape hatch or when creation fails, so\n * the caller falls back to a per-request instance.\n */\nexport function getCachedCacheService(): CacheStrategy | null {\n if (!isCacheSingletonEnabled()) return null\n let service = (globalThis as any)[CACHE_GLOBAL_KEY] as CacheStrategy | null ?? null\n if (!service) {\n try {\n try {\n service = createCacheService()\n } catch (err) {\n logger.warn('Cache service initialization failed; falling back to memory strategy', { err })\n service = createCacheService({ strategy: 'memory' })\n }\n ;(globalThis as any)[CACHE_GLOBAL_KEY] = service\n\n // Register shutdown hook once to close persistent handles (sqlite/redis) on process exit\n if (!(globalThis as any)[CACHE_SHUTDOWN_KEY]) {\n const shutdown = () => { service?.close?.().catch(() => {}) }\n process.once('SIGTERM', shutdown)\n process.once('SIGINT', shutdown)\n ;(globalThis as any)[CACHE_SHUTDOWN_KEY] = true\n }\n } catch (err) {\n logger.warn('Failed to create cache service', { component: 'cache', err })\n }\n }\n return service\n}\n\nexport function getCachedRateLimiterService(): RateLimiterService | null {\n let service = (globalThis as any)[RL_GLOBAL_KEY] as RateLimiterService | null ?? null\n if (!service) {\n try {\n const rateLimitConfig = readRateLimitConfig()\n service = new RateLimiterService(rateLimitConfig)\n // Fire-and-forget async init (only needed for Redis strategy;\n // memory strategy works synchronously, and Redis has an in-memory\n // insurance limiter so the first few requests are still protected)\n service.initialize().catch((err) => {\n logger.warn('Async initialization failed', { component: 'ratelimit', err })\n })\n ;(globalThis as any)[RL_GLOBAL_KEY] = service\n\n // Register shutdown hook once to disconnect Redis on process exit\n if (!(globalThis as any)[RL_SHUTDOWN_KEY]) {\n const shutdown = () => { service?.destroy().catch(() => {}) }\n process.once('SIGTERM', shutdown)\n process.once('SIGINT', shutdown)\n ;(globalThis as any)[RL_SHUTDOWN_KEY] = true\n }\n } catch (err) {\n logger.warn('Failed to create rate limiter service', { component: 'ratelimit', err })\n }\n }\n return service\n}\n\nexport async function bootstrap(container: AwilixContainer) {\n // Register the cache service. Prefer the process-wide singleton so caches\n // survive across request containers; fall back to a per-request instance\n // when the singleton is disabled or fails to build.\n let cache: any = getCachedCacheService()\n if (!cache) {\n try {\n cache = createCacheService()\n } catch (err: any) {\n logger.warn('Cache service initialization failed; falling back to memory strategy', { err })\n cache = createCacheService({ strategy: 'memory' })\n }\n }\n container.register({ cache: asValue(cache) })\n\n // Create and register the DI-aware event bus\n let eventBus: any\n try {\n // Support both QUEUE_STRATEGY and legacy EVENTS_STRATEGY env vars\n const strategyEnv = process.env.QUEUE_STRATEGY || process.env.EVENTS_STRATEGY\n const queueStrategy = strategyEnv === 'async' || strategyEnv === 'redis' ? 'async' : 'local'\n eventBus = createEventBus({ resolve: container.resolve.bind(container) as any, queueStrategy })\n } catch (err: any) {\n // Fall back to local strategy to avoid breaking the app on misconfiguration\n logger.warn('Event bus initialization failed; falling back to local strategy', { err })\n try {\n eventBus = createEventBus({ resolve: container.resolve.bind(container) as any, queueStrategy: 'local' })\n } catch {\n // In extreme cases, provide a no-op bus to avoid crashes. It deliberately\n // omits `dispatchQueued` so the events worker fails its job loudly instead\n // of quietly completing it with zero subscribers dispatched.\n eventBus = {\n emit: async () => {},\n on: () => {},\n registerModuleSubscribers: () => {},\n clearQueue: async () => ({ removed: 0 }),\n }\n }\n }\n container.register({ eventBus: asValue(eventBus) })\n // Wire the global event bus so createModuleEvents().emit works outside DI context\n setGlobalEventBus(eventBus)\n // Auto-register discovered module subscribers\n try {\n let loadedModules: any[] = []\n try {\n const { getModules } = await import('@open-mercato/shared/lib/i18n/server')\n loadedModules = getModules()\n } catch (err) {\n // The events worker dispatches persistent subscribers through this bus, so\n // an empty registry here means queued events silently run nothing. Swallowing\n // it made that failure invisible; log it.\n logger.warn('Module registry unavailable; event bus starts with no module subscribers', { err })\n }\n const subs = loadedModules.flatMap((m) =>\n (m.subscribers || []).map((subscriber: ModuleSubscriber) => ({\n ...subscriber,\n moduleId: subscriber.moduleId ?? m.id,\n })),\n )\n if (subs.length) (container.resolve as any)('eventBus').registerModuleSubscribers(subs)\n\n // Extract sync subscribers and register in the sync-subscriber-store\n const syncSubs = subs.filter((s: any) => s.sync === true)\n if (syncSubs.length) {\n try {\n const { registerSyncSubscribers } = await import('@open-mercato/shared/lib/crud/sync-subscriber-store')\n registerSyncSubscribers(\n syncSubs.map((s: any) => ({\n metadata: { event: s.event, sync: true as const, priority: s.priority, id: s.id },\n handler: s.handler,\n })),\n )\n } catch {\n // sync-subscriber-store may not be available\n }\n }\n } catch (err) {\n logger.error(\"Failed to register module subscribers:\", { err });\n }\n\n // KMS + tenant encryption\n const kmsService = createKmsService()\n container.register({ kmsService: asValue(kmsService) })\n let defaultEncryptionMaps: ModuleEncryptionMap[] = []\n if (isTenantDataEncryptionEnabled()) {\n try {\n const { getModules } = await import('@open-mercato/shared/lib/i18n/server')\n defaultEncryptionMaps = getDefaultEncryptionMaps(getModules())\n } catch (err) {\n logger.error('Failed to load default encryption maps', { component: 'encryption', err })\n throw err\n }\n }\n try {\n const em = container.resolve('em') as EntityManager\n const cacheService = (() => {\n try { return container.resolve('cache') as any } catch { return null }\n })()\n const tenantEncryptionService = new TenantDataEncryptionService(em, {\n cache: cacheService,\n kms: kmsService,\n defaultEncryptionMaps,\n })\n container.register({ tenantEncryptionService: asValue(tenantEncryptionService) })\n if (isTenantDataEncryptionEnabled() && kmsService.isHealthy()) {\n try {\n registerTenantEncryptionSubscriber(em, tenantEncryptionService)\n } catch (err) {\n logger.warn('Failed to register MikroORM encryption subscriber', { component: 'encryption', err })\n }\n } else if (isTenantDataEncryptionEnabled() && !kmsService.isHealthy()) {\n logger.warn('Vault/KMS unhealthy - tenant data encryption is disabled until recovery', { component: 'encryption' })\n }\n } catch (err) {\n logger.warn('Failed to initialize tenant encryption service', { component: 'encryption', err })\n }\n\n // Register rate limiter service (singleton via globalThis \u2014 reused across request containers)\n // getCachedRateLimiterService() never throws; returns null on failure\n const rateLimiterService = getCachedRateLimiterService()\n if (rateLimiterService) {\n container.register({ rateLimiterService: asValue(rateLimiterService) })\n }\n\n // Register search module\n try {\n // Get configs from global registry (registered during app bootstrap)\n const searchModuleConfigs = getSearchModuleConfigs()\n registerSearchModule(container as any, { moduleConfigs: searchModuleConfigs })\n\n // Register searchModuleConfigs in container so status API can access vector-enabled entities\n container.register({\n searchModuleConfigs: asValue(searchModuleConfigs),\n })\n\n // Register search delete event subscriber\n // Note: search.index_record is now handled by auto-discovered fulltext_upsert.ts subscriber\n try {\n const searchIndexer = container.resolve('searchIndexer') as any\n if (searchIndexer && eventBus) {\n eventBus.registerModuleSubscribers([\n {\n event: searchDeleteMetadata.event,\n persistent: searchDeleteMetadata.persistent,\n handler: createSearchDeleteSubscriber(searchIndexer),\n },\n ])\n }\n } catch {\n // searchIndexer may not be available\n }\n } catch (err) {\n logger.warn('Failed to register search module', { component: 'search', err })\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -208,6 +208,7 @@ const searchConfig = {
|
|
|
208
208
|
entities: [
|
|
209
209
|
{
|
|
210
210
|
entityId: "catalog:catalog_product",
|
|
211
|
+
aclFeatures: ["catalog.products.view"],
|
|
211
212
|
enabled: true,
|
|
212
213
|
priority: 10,
|
|
213
214
|
buildSource: async (ctx) => {
|
|
@@ -264,6 +265,7 @@ const searchConfig = {
|
|
|
264
265
|
},
|
|
265
266
|
{
|
|
266
267
|
entityId: "catalog:catalog_product_variant",
|
|
268
|
+
aclFeatures: ["catalog.products.view"],
|
|
267
269
|
enabled: true,
|
|
268
270
|
priority: 8,
|
|
269
271
|
buildSource: async (ctx) => {
|
|
@@ -292,6 +294,7 @@ const searchConfig = {
|
|
|
292
294
|
},
|
|
293
295
|
{
|
|
294
296
|
entityId: "catalog:catalog_product_category",
|
|
297
|
+
aclFeatures: ["catalog.categories.view"],
|
|
295
298
|
enabled: true,
|
|
296
299
|
priority: 7,
|
|
297
300
|
buildSource: async (ctx) => {
|
|
@@ -316,6 +319,7 @@ const searchConfig = {
|
|
|
316
319
|
},
|
|
317
320
|
{
|
|
318
321
|
entityId: "catalog:catalog_offer",
|
|
322
|
+
aclFeatures: ["sales.channels.manage"],
|
|
319
323
|
enabled: true,
|
|
320
324
|
priority: 7,
|
|
321
325
|
buildSource: async (ctx) => {
|
|
@@ -342,6 +346,7 @@ const searchConfig = {
|
|
|
342
346
|
},
|
|
343
347
|
{
|
|
344
348
|
entityId: "catalog:catalog_product_tag",
|
|
349
|
+
aclFeatures: ["catalog.products.view"],
|
|
345
350
|
enabled: true,
|
|
346
351
|
priority: 5,
|
|
347
352
|
buildSource: async (ctx) => {
|
|
@@ -363,6 +368,7 @@ const searchConfig = {
|
|
|
363
368
|
},
|
|
364
369
|
{
|
|
365
370
|
entityId: "catalog:catalog_price_kind",
|
|
371
|
+
aclFeatures: ["catalog.settings.manage"],
|
|
366
372
|
enabled: true,
|
|
367
373
|
priority: 4,
|
|
368
374
|
buildSource: async (ctx) => {
|
|
@@ -386,6 +392,7 @@ const searchConfig = {
|
|
|
386
392
|
},
|
|
387
393
|
{
|
|
388
394
|
entityId: "catalog:catalog_product_unit_conversion",
|
|
395
|
+
aclFeatures: ["catalog.products.view"],
|
|
389
396
|
enabled: true,
|
|
390
397
|
priority: 3,
|
|
391
398
|
buildSource: async (ctx) => {
|
|
@@ -414,6 +421,7 @@ const searchConfig = {
|
|
|
414
421
|
},
|
|
415
422
|
{
|
|
416
423
|
entityId: "catalog:catalog_option_schema_template",
|
|
424
|
+
aclFeatures: ["catalog.products.view"],
|
|
417
425
|
enabled: true,
|
|
418
426
|
priority: 4,
|
|
419
427
|
buildSource: async (ctx) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/catalog/search.ts"],
|
|
4
|
-
"sourcesContent": ["import type { SearchBuildContext, SearchIndexSource, SearchModuleConfig, SearchResultPresenter } from '@open-mercato/shared/modules/search'\nimport type { TranslateFn } from '@open-mercato/shared/lib/i18n/context'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\n\nconst CATALOG_PRODUCTS_URL = '/backend/catalog/products'\nconst CATALOG_CATEGORIES_URL = '/backend/catalog/categories'\nconst CATALOG_CONFIG_URL = '/backend/config/catalog'\n\nfunction normalizeText(value: unknown): string | null {\n if (value === null || value === undefined) return null\n if (typeof value === 'string') {\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : null\n }\n if (value instanceof Date) return value.toISOString()\n if (typeof value === 'number' || typeof value === 'boolean') return String(value)\n return null\n}\n\nfunction pickText(...candidates: Array<unknown>): string | null {\n for (const candidate of candidates) {\n const text = normalizeText(candidate)\n if (text) return text\n }\n return null\n}\n\nfunction readRecordText(record: Record<string, unknown>, ...keys: string[]): string | null {\n for (const key of keys) {\n const text = normalizeText(record[key])\n if (text) return text\n }\n return null\n}\n\nfunction formatSubtitle(...parts: Array<unknown>): string | undefined {\n const text = parts\n .map((part) => normalizeText(part))\n .filter((value): value is string => Boolean(value))\n if (!text.length) return undefined\n return text.join(' \u00B7 ')\n}\n\nfunction snippet(value: unknown, maxLength = 140): string | undefined {\n const text = normalizeText(value)\n if (!text) return undefined\n if (text.length <= maxLength) return text\n return `${text.slice(0, maxLength - 3)}...`\n}\n\nfunction appendLine(lines: string[], label: string, value: unknown) {\n if (value === null || value === undefined) return\n const text = Array.isArray(value)\n ? value.map((item) => normalizeText(item)).filter(Boolean).join(', ')\n : typeof value === 'object'\n ? JSON.stringify(value)\n : String(value)\n if (!text.trim()) return\n lines.push(`${label}: ${text}`)\n}\n\nfunction friendlyLabel(input: string): string {\n return input\n .replace(/^cf:/, '')\n .replace(/_/g, ' ')\n .replace(/([a-z])([A-Z])/g, (_match, firstChar, secondChar) => `${firstChar} ${secondChar}`)\n .replace(/\\b\\w/g, (character) => character.toUpperCase())\n}\n\nfunction appendCustomFieldLines(lines: string[], customFields: Record<string, unknown>) {\n for (const [key, value] of Object.entries(customFields)) {\n if (value === null || value === undefined) continue\n appendLine(lines, friendlyLabel(key), value)\n }\n}\n\nfunction buildIndexSource(\n ctx: SearchBuildContext,\n presenter: SearchResultPresenter,\n lines: string[],\n): SearchIndexSource | null {\n appendCustomFieldLines(lines, ctx.customFields)\n if (!lines.length) return null\n return {\n text: lines,\n presenter,\n checksumSource: { record: ctx.record, customFields: ctx.customFields },\n }\n}\n\nfunction buildProductUrl(productId: string | null): string | null {\n if (!productId) return null\n return `${CATALOG_PRODUCTS_URL}/${encodeURIComponent(productId)}`\n}\n\nfunction buildVariantUrl(productId: string | null, variantId: string | null): string | null {\n if (!productId || !variantId) return null\n return `${CATALOG_PRODUCTS_URL}/${encodeURIComponent(productId)}/variants/${encodeURIComponent(variantId)}`\n}\n\nfunction buildCategoryUrl(categoryId: string | null): string | null {\n if (!categoryId) return null\n return `${CATALOG_CATEGORIES_URL}/${encodeURIComponent(categoryId)}/edit`\n}\n\nfunction resolveProductId(record: Record<string, unknown>): string | null {\n // Check direct FK fields first\n const directId = readRecordText(record, 'product_id', 'productId')\n if (directId) return directId\n // Check if product is a string (FK value)\n const product = record.product\n if (typeof product === 'string') return product\n // Check if product is an object with id\n if (product && typeof product === 'object') {\n const productObj = product as Record<string, unknown>\n return readRecordText(productObj, 'id')\n }\n return null\n}\n\nfunction buildProductPresenter(\n translate: TranslateFn,\n record: Record<string, unknown>,\n): SearchResultPresenter {\n const label = translate('catalog.search.badge.product', 'Product')\n const title = pickText(\n readRecordText(record, 'title'),\n readRecordText(record, 'sku'),\n readRecordText(record, 'handle'),\n readRecordText(record, 'id'),\n ) ?? label\n const isActive = record.is_active ?? record.isActive\n const statusText = isActive === false ? translate('catalog.search.status.inactive', 'Inactive') : null\n const subtitle = formatSubtitle(\n readRecordText(record, 'subtitle'),\n readRecordText(record, 'sku'),\n readRecordText(record, 'product_type', 'productType'),\n statusText,\n ) ?? snippet(readRecordText(record, 'description'))\n return { title, subtitle, icon: 'package', badge: label }\n}\n\nfunction buildVariantPresenter(\n translate: TranslateFn,\n record: Record<string, unknown>,\n): SearchResultPresenter {\n const label = translate('catalog.search.badge.variant', 'Variant')\n const isDefault = record.is_default ?? record.isDefault\n const defaultLabel = isDefault ? translate('catalog.search.variant.default', 'Default') : null\n const title = pickText(\n readRecordText(record, 'name'),\n readRecordText(record, 'sku'),\n defaultLabel,\n readRecordText(record, 'id'),\n ) ?? label\n const isActive = record.is_active ?? record.isActive\n const statusText = isActive === false ? translate('catalog.search.status.inactive', 'Inactive') : null\n const subtitle = formatSubtitle(\n readRecordText(record, 'sku'),\n readRecordText(record, 'barcode'),\n defaultLabel,\n statusText,\n )\n return { title, subtitle, icon: 'box', badge: label }\n}\n\nfunction buildCategoryPresenter(\n translate: TranslateFn,\n record: Record<string, unknown>,\n): SearchResultPresenter {\n const label = translate('catalog.search.badge.category', 'Category')\n const title = pickText(\n readRecordText(record, 'name'),\n readRecordText(record, 'slug'),\n readRecordText(record, 'id'),\n ) ?? label\n const isActive = record.is_active ?? record.isActive\n const statusText = isActive === false ? translate('catalog.search.status.inactive', 'Inactive') : null\n const subtitle = formatSubtitle(\n readRecordText(record, 'tree_path', 'treePath'),\n readRecordText(record, 'slug'),\n statusText,\n ) ?? snippet(readRecordText(record, 'description'))\n return { title, subtitle, icon: 'folder-tree', badge: label }\n}\n\nfunction buildOfferPresenter(\n translate: TranslateFn,\n record: Record<string, unknown>,\n channelName?: string | null,\n): SearchResultPresenter {\n const label = translate('catalog.search.badge.offer', 'Channel Offer')\n const channelLabel = channelName ?? readRecordText(record, 'channel_name', 'channelName')\n const title = pickText(\n readRecordText(record, 'title'),\n readRecordText(record, 'id'),\n ) ?? label\n const titleWithChannel = channelLabel ? `${title} \u00B7 ${channelLabel}` : title\n const isActive = record.is_active ?? record.isActive\n const statusText = isActive === false ? translate('catalog.search.status.inactive', 'Inactive') : null\n const subtitle = formatSubtitle(statusText) ?? snippet(readRecordText(record, 'description'))\n return { title: titleWithChannel, subtitle, icon: 'store', badge: label }\n}\n\nfunction buildTagPresenter(\n translate: TranslateFn,\n record: Record<string, unknown>,\n): SearchResultPresenter {\n const label = translate('catalog.search.badge.tag', 'Tag')\n const title = pickText(\n readRecordText(record, 'label'),\n readRecordText(record, 'slug'),\n readRecordText(record, 'id'),\n ) ?? label\n const subtitle = formatSubtitle(readRecordText(record, 'slug'))\n return { title, subtitle, icon: 'tag', badge: label }\n}\n\nfunction buildPriceKindPresenter(\n translate: TranslateFn,\n record: Record<string, unknown>,\n): SearchResultPresenter {\n const label = translate('catalog.search.badge.priceKind', 'Price Kind')\n const title = pickText(\n readRecordText(record, 'title'),\n readRecordText(record, 'code'),\n readRecordText(record, 'id'),\n ) ?? label\n const displayMode = readRecordText(record, 'display_mode', 'displayMode')\n const isPromotion = record.is_promotion ?? record.isPromotion\n const promotionLabel = isPromotion ? translate('catalog.search.priceKind.promotion', 'Promotion') : null\n const displayModeLabel = displayMode === 'including-tax'\n ? translate('catalog.search.priceKind.includingTax', 'Incl. tax')\n : displayMode === 'excluding-tax'\n ? translate('catalog.search.priceKind.excludingTax', 'Excl. tax')\n : null\n const subtitle = formatSubtitle(\n readRecordText(record, 'code'),\n displayModeLabel,\n promotionLabel,\n )\n return { title, subtitle, icon: 'dollar-sign', badge: label }\n}\n\nfunction buildUnitConversionPresenter(\n translate: TranslateFn,\n record: Record<string, unknown>,\n): SearchResultPresenter {\n const label = translate('catalog.search.badge.unitConversion', 'Unit Conversion')\n const unitCode = readRecordText(record, 'unit_code', 'unitCode')\n const factor = readRecordText(record, 'to_base_factor', 'toBaseFactor')\n const title = unitCode ? `${unitCode} (\u00D7${factor ?? '?'})` : label\n const isActive = record.is_active ?? record.isActive\n const statusText = isActive === false ? translate('catalog.search.status.inactive', 'Inactive') : null\n const subtitle = formatSubtitle(unitCode, statusText)\n return { title, subtitle, icon: 'ruler', badge: label }\n}\n\nfunction buildOptionSchemaPresenter(\n translate: TranslateFn,\n record: Record<string, unknown>,\n): SearchResultPresenter {\n const label = translate('catalog.search.badge.optionSchema', 'Option Schema')\n const title = pickText(\n readRecordText(record, 'name'),\n readRecordText(record, 'code'),\n readRecordText(record, 'id'),\n ) ?? label\n const isActive = record.is_active ?? record.isActive\n const statusText = isActive === false ? translate('catalog.search.status.inactive', 'Inactive') : null\n const subtitle = formatSubtitle(\n readRecordText(record, 'code'),\n statusText,\n ) ?? snippet(readRecordText(record, 'description'))\n return { title, subtitle, icon: 'settings', badge: label }\n}\n\nexport const searchConfig: SearchModuleConfig = {\n entities: [\n {\n entityId: 'catalog:catalog_product',\n enabled: true,\n priority: 10,\n buildSource: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Title', record.title)\n appendLine(lines, 'Subtitle', record.subtitle)\n appendLine(lines, 'Description', record.description)\n appendLine(lines, 'SKU', record.sku)\n appendLine(lines, 'Handle', record.handle)\n appendLine(lines, 'Product type', record.product_type ?? record.productType)\n appendLine(lines, 'Base unit', record.default_unit ?? record.defaultUnit)\n appendLine(lines, 'Default sales unit', record.default_sales_unit ?? record.defaultSalesUnit)\n appendLine(\n lines,\n 'Default sales quantity',\n record.default_sales_unit_quantity ?? record.defaultSalesUnitQuantity,\n )\n appendLine(lines, 'UoM rounding mode', record.uom_rounding_mode ?? record.uomRoundingMode)\n appendLine(lines, 'UoM rounding scale', record.uom_rounding_scale ?? record.uomRoundingScale)\n appendLine(lines, 'Unit price enabled', record.unit_price_enabled ?? record.unitPriceEnabled)\n appendLine(\n lines,\n 'Unit price reference unit',\n record.unit_price_reference_unit ?? record.unitPriceReferenceUnit,\n )\n appendLine(\n lines,\n 'Unit price base quantity',\n record.unit_price_base_quantity ?? record.unitPriceBaseQuantity,\n )\n return buildIndexSource(ctx, buildProductPresenter(translate, record), lines)\n },\n formatResult: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n return buildProductPresenter(translate, ctx.record)\n },\n resolveUrl: async (ctx) => buildProductUrl(readRecordText(ctx.record, 'id')),\n fieldPolicy: {\n searchable: [\n 'title',\n 'subtitle',\n 'description',\n 'sku',\n 'handle',\n 'product_type',\n 'default_unit',\n 'default_sales_unit',\n 'unit_price_reference_unit',\n ],\n excluded: ['metadata', 'dimensions', 'tax_rate_id'],\n },\n },\n {\n entityId: 'catalog:catalog_product_variant',\n enabled: true,\n priority: 8,\n buildSource: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Name', record.name)\n appendLine(lines, 'SKU', record.sku)\n appendLine(lines, 'Barcode', record.barcode)\n appendLine(lines, 'Option values', record.option_values ?? record.optionValues)\n return buildIndexSource(ctx, buildVariantPresenter(translate, record), lines)\n },\n formatResult: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n return buildVariantPresenter(translate, ctx.record)\n },\n resolveUrl: async (ctx) => {\n const productId = resolveProductId(ctx.record)\n const variantId = readRecordText(ctx.record, 'id')\n return buildVariantUrl(productId, variantId)\n },\n fieldPolicy: {\n searchable: ['name', 'sku', 'barcode'],\n excluded: ['metadata', 'dimensions', 'option_values', 'tax_rate_id'],\n },\n },\n {\n entityId: 'catalog:catalog_product_category',\n enabled: true,\n priority: 7,\n buildSource: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Name', record.name)\n appendLine(lines, 'Slug', record.slug)\n appendLine(lines, 'Description', record.description)\n appendLine(lines, 'Path', record.tree_path ?? record.treePath)\n return buildIndexSource(ctx, buildCategoryPresenter(translate, record), lines)\n },\n formatResult: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n return buildCategoryPresenter(translate, ctx.record)\n },\n resolveUrl: async (ctx) => buildCategoryUrl(readRecordText(ctx.record, 'id')),\n fieldPolicy: {\n searchable: ['name', 'slug', 'description', 'tree_path'],\n excluded: ['metadata', 'ancestor_ids', 'child_ids', 'descendant_ids'],\n },\n },\n {\n entityId: 'catalog:catalog_offer',\n enabled: true,\n priority: 7,\n buildSource: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Title', record.title)\n appendLine(lines, 'Description', record.description)\n appendLine(lines, 'Channel', record.channel_id ?? record.channelId)\n return buildIndexSource(ctx, buildOfferPresenter(translate, record), lines)\n },\n formatResult: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n return buildOfferPresenter(translate, ctx.record)\n },\n resolveUrl: async (ctx) => {\n const productId = resolveProductId(ctx.record)\n return buildProductUrl(productId)\n },\n fieldPolicy: {\n searchable: ['title', 'description'],\n excluded: ['metadata'],\n },\n },\n {\n entityId: 'catalog:catalog_product_tag',\n enabled: true,\n priority: 5,\n buildSource: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Label', record.label)\n appendLine(lines, 'Slug', record.slug)\n return buildIndexSource(ctx, buildTagPresenter(translate, record), lines)\n },\n formatResult: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n return buildTagPresenter(translate, ctx.record)\n },\n resolveUrl: async () => CATALOG_PRODUCTS_URL,\n fieldPolicy: {\n searchable: ['label', 'slug'],\n },\n },\n {\n entityId: 'catalog:catalog_price_kind',\n enabled: true,\n priority: 4,\n buildSource: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Title', record.title)\n appendLine(lines, 'Code', record.code)\n appendLine(lines, 'Display mode', record.display_mode ?? record.displayMode)\n appendLine(lines, 'Currency', record.currency_code ?? record.currencyCode)\n return buildIndexSource(ctx, buildPriceKindPresenter(translate, record), lines)\n },\n formatResult: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n return buildPriceKindPresenter(translate, ctx.record)\n },\n resolveUrl: async () => CATALOG_CONFIG_URL,\n fieldPolicy: {\n searchable: ['title', 'code'],\n },\n },\n {\n entityId: 'catalog:catalog_product_unit_conversion',\n enabled: true,\n priority: 3,\n buildSource: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Unit code', record.unit_code ?? record.unitCode)\n appendLine(lines, 'To base factor', record.to_base_factor ?? record.toBaseFactor)\n appendLine(lines, 'Sort order', record.sort_order ?? record.sortOrder)\n const isActive = record.is_active ?? record.isActive\n appendLine(lines, 'Active', isActive)\n return buildIndexSource(ctx, buildUnitConversionPresenter(translate, record), lines)\n },\n formatResult: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n return buildUnitConversionPresenter(translate, ctx.record)\n },\n resolveUrl: async (ctx) => {\n const productId = resolveProductId(ctx.record)\n return buildProductUrl(productId)\n },\n fieldPolicy: {\n searchable: ['unit_code'],\n excluded: ['metadata'],\n },\n },\n {\n entityId: 'catalog:catalog_option_schema_template',\n enabled: true,\n priority: 4,\n buildSource: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Name', record.name)\n appendLine(lines, 'Code', record.code)\n appendLine(lines, 'Description', record.description)\n return buildIndexSource(ctx, buildOptionSchemaPresenter(translate, record), lines)\n },\n formatResult: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n return buildOptionSchemaPresenter(translate, ctx.record)\n },\n resolveUrl: async () => CATALOG_CONFIG_URL,\n fieldPolicy: {\n searchable: ['name', 'code', 'description'],\n excluded: ['schema', 'metadata'],\n },\n },\n ],\n}\n\nexport default searchConfig\nexport const config = searchConfig\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type { SearchBuildContext, SearchIndexSource, SearchModuleConfig, SearchResultPresenter } from '@open-mercato/shared/modules/search'\nimport type { TranslateFn } from '@open-mercato/shared/lib/i18n/context'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\n\nconst CATALOG_PRODUCTS_URL = '/backend/catalog/products'\nconst CATALOG_CATEGORIES_URL = '/backend/catalog/categories'\nconst CATALOG_CONFIG_URL = '/backend/config/catalog'\n\nfunction normalizeText(value: unknown): string | null {\n if (value === null || value === undefined) return null\n if (typeof value === 'string') {\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : null\n }\n if (value instanceof Date) return value.toISOString()\n if (typeof value === 'number' || typeof value === 'boolean') return String(value)\n return null\n}\n\nfunction pickText(...candidates: Array<unknown>): string | null {\n for (const candidate of candidates) {\n const text = normalizeText(candidate)\n if (text) return text\n }\n return null\n}\n\nfunction readRecordText(record: Record<string, unknown>, ...keys: string[]): string | null {\n for (const key of keys) {\n const text = normalizeText(record[key])\n if (text) return text\n }\n return null\n}\n\nfunction formatSubtitle(...parts: Array<unknown>): string | undefined {\n const text = parts\n .map((part) => normalizeText(part))\n .filter((value): value is string => Boolean(value))\n if (!text.length) return undefined\n return text.join(' \u00B7 ')\n}\n\nfunction snippet(value: unknown, maxLength = 140): string | undefined {\n const text = normalizeText(value)\n if (!text) return undefined\n if (text.length <= maxLength) return text\n return `${text.slice(0, maxLength - 3)}...`\n}\n\nfunction appendLine(lines: string[], label: string, value: unknown) {\n if (value === null || value === undefined) return\n const text = Array.isArray(value)\n ? value.map((item) => normalizeText(item)).filter(Boolean).join(', ')\n : typeof value === 'object'\n ? JSON.stringify(value)\n : String(value)\n if (!text.trim()) return\n lines.push(`${label}: ${text}`)\n}\n\nfunction friendlyLabel(input: string): string {\n return input\n .replace(/^cf:/, '')\n .replace(/_/g, ' ')\n .replace(/([a-z])([A-Z])/g, (_match, firstChar, secondChar) => `${firstChar} ${secondChar}`)\n .replace(/\\b\\w/g, (character) => character.toUpperCase())\n}\n\nfunction appendCustomFieldLines(lines: string[], customFields: Record<string, unknown>) {\n for (const [key, value] of Object.entries(customFields)) {\n if (value === null || value === undefined) continue\n appendLine(lines, friendlyLabel(key), value)\n }\n}\n\nfunction buildIndexSource(\n ctx: SearchBuildContext,\n presenter: SearchResultPresenter,\n lines: string[],\n): SearchIndexSource | null {\n appendCustomFieldLines(lines, ctx.customFields)\n if (!lines.length) return null\n return {\n text: lines,\n presenter,\n checksumSource: { record: ctx.record, customFields: ctx.customFields },\n }\n}\n\nfunction buildProductUrl(productId: string | null): string | null {\n if (!productId) return null\n return `${CATALOG_PRODUCTS_URL}/${encodeURIComponent(productId)}`\n}\n\nfunction buildVariantUrl(productId: string | null, variantId: string | null): string | null {\n if (!productId || !variantId) return null\n return `${CATALOG_PRODUCTS_URL}/${encodeURIComponent(productId)}/variants/${encodeURIComponent(variantId)}`\n}\n\nfunction buildCategoryUrl(categoryId: string | null): string | null {\n if (!categoryId) return null\n return `${CATALOG_CATEGORIES_URL}/${encodeURIComponent(categoryId)}/edit`\n}\n\nfunction resolveProductId(record: Record<string, unknown>): string | null {\n // Check direct FK fields first\n const directId = readRecordText(record, 'product_id', 'productId')\n if (directId) return directId\n // Check if product is a string (FK value)\n const product = record.product\n if (typeof product === 'string') return product\n // Check if product is an object with id\n if (product && typeof product === 'object') {\n const productObj = product as Record<string, unknown>\n return readRecordText(productObj, 'id')\n }\n return null\n}\n\nfunction buildProductPresenter(\n translate: TranslateFn,\n record: Record<string, unknown>,\n): SearchResultPresenter {\n const label = translate('catalog.search.badge.product', 'Product')\n const title = pickText(\n readRecordText(record, 'title'),\n readRecordText(record, 'sku'),\n readRecordText(record, 'handle'),\n readRecordText(record, 'id'),\n ) ?? label\n const isActive = record.is_active ?? record.isActive\n const statusText = isActive === false ? translate('catalog.search.status.inactive', 'Inactive') : null\n const subtitle = formatSubtitle(\n readRecordText(record, 'subtitle'),\n readRecordText(record, 'sku'),\n readRecordText(record, 'product_type', 'productType'),\n statusText,\n ) ?? snippet(readRecordText(record, 'description'))\n return { title, subtitle, icon: 'package', badge: label }\n}\n\nfunction buildVariantPresenter(\n translate: TranslateFn,\n record: Record<string, unknown>,\n): SearchResultPresenter {\n const label = translate('catalog.search.badge.variant', 'Variant')\n const isDefault = record.is_default ?? record.isDefault\n const defaultLabel = isDefault ? translate('catalog.search.variant.default', 'Default') : null\n const title = pickText(\n readRecordText(record, 'name'),\n readRecordText(record, 'sku'),\n defaultLabel,\n readRecordText(record, 'id'),\n ) ?? label\n const isActive = record.is_active ?? record.isActive\n const statusText = isActive === false ? translate('catalog.search.status.inactive', 'Inactive') : null\n const subtitle = formatSubtitle(\n readRecordText(record, 'sku'),\n readRecordText(record, 'barcode'),\n defaultLabel,\n statusText,\n )\n return { title, subtitle, icon: 'box', badge: label }\n}\n\nfunction buildCategoryPresenter(\n translate: TranslateFn,\n record: Record<string, unknown>,\n): SearchResultPresenter {\n const label = translate('catalog.search.badge.category', 'Category')\n const title = pickText(\n readRecordText(record, 'name'),\n readRecordText(record, 'slug'),\n readRecordText(record, 'id'),\n ) ?? label\n const isActive = record.is_active ?? record.isActive\n const statusText = isActive === false ? translate('catalog.search.status.inactive', 'Inactive') : null\n const subtitle = formatSubtitle(\n readRecordText(record, 'tree_path', 'treePath'),\n readRecordText(record, 'slug'),\n statusText,\n ) ?? snippet(readRecordText(record, 'description'))\n return { title, subtitle, icon: 'folder-tree', badge: label }\n}\n\nfunction buildOfferPresenter(\n translate: TranslateFn,\n record: Record<string, unknown>,\n channelName?: string | null,\n): SearchResultPresenter {\n const label = translate('catalog.search.badge.offer', 'Channel Offer')\n const channelLabel = channelName ?? readRecordText(record, 'channel_name', 'channelName')\n const title = pickText(\n readRecordText(record, 'title'),\n readRecordText(record, 'id'),\n ) ?? label\n const titleWithChannel = channelLabel ? `${title} \u00B7 ${channelLabel}` : title\n const isActive = record.is_active ?? record.isActive\n const statusText = isActive === false ? translate('catalog.search.status.inactive', 'Inactive') : null\n const subtitle = formatSubtitle(statusText) ?? snippet(readRecordText(record, 'description'))\n return { title: titleWithChannel, subtitle, icon: 'store', badge: label }\n}\n\nfunction buildTagPresenter(\n translate: TranslateFn,\n record: Record<string, unknown>,\n): SearchResultPresenter {\n const label = translate('catalog.search.badge.tag', 'Tag')\n const title = pickText(\n readRecordText(record, 'label'),\n readRecordText(record, 'slug'),\n readRecordText(record, 'id'),\n ) ?? label\n const subtitle = formatSubtitle(readRecordText(record, 'slug'))\n return { title, subtitle, icon: 'tag', badge: label }\n}\n\nfunction buildPriceKindPresenter(\n translate: TranslateFn,\n record: Record<string, unknown>,\n): SearchResultPresenter {\n const label = translate('catalog.search.badge.priceKind', 'Price Kind')\n const title = pickText(\n readRecordText(record, 'title'),\n readRecordText(record, 'code'),\n readRecordText(record, 'id'),\n ) ?? label\n const displayMode = readRecordText(record, 'display_mode', 'displayMode')\n const isPromotion = record.is_promotion ?? record.isPromotion\n const promotionLabel = isPromotion ? translate('catalog.search.priceKind.promotion', 'Promotion') : null\n const displayModeLabel = displayMode === 'including-tax'\n ? translate('catalog.search.priceKind.includingTax', 'Incl. tax')\n : displayMode === 'excluding-tax'\n ? translate('catalog.search.priceKind.excludingTax', 'Excl. tax')\n : null\n const subtitle = formatSubtitle(\n readRecordText(record, 'code'),\n displayModeLabel,\n promotionLabel,\n )\n return { title, subtitle, icon: 'dollar-sign', badge: label }\n}\n\nfunction buildUnitConversionPresenter(\n translate: TranslateFn,\n record: Record<string, unknown>,\n): SearchResultPresenter {\n const label = translate('catalog.search.badge.unitConversion', 'Unit Conversion')\n const unitCode = readRecordText(record, 'unit_code', 'unitCode')\n const factor = readRecordText(record, 'to_base_factor', 'toBaseFactor')\n const title = unitCode ? `${unitCode} (\u00D7${factor ?? '?'})` : label\n const isActive = record.is_active ?? record.isActive\n const statusText = isActive === false ? translate('catalog.search.status.inactive', 'Inactive') : null\n const subtitle = formatSubtitle(unitCode, statusText)\n return { title, subtitle, icon: 'ruler', badge: label }\n}\n\nfunction buildOptionSchemaPresenter(\n translate: TranslateFn,\n record: Record<string, unknown>,\n): SearchResultPresenter {\n const label = translate('catalog.search.badge.optionSchema', 'Option Schema')\n const title = pickText(\n readRecordText(record, 'name'),\n readRecordText(record, 'code'),\n readRecordText(record, 'id'),\n ) ?? label\n const isActive = record.is_active ?? record.isActive\n const statusText = isActive === false ? translate('catalog.search.status.inactive', 'Inactive') : null\n const subtitle = formatSubtitle(\n readRecordText(record, 'code'),\n statusText,\n ) ?? snippet(readRecordText(record, 'description'))\n return { title, subtitle, icon: 'settings', badge: label }\n}\n\nexport const searchConfig: SearchModuleConfig = {\n entities: [\n {\n entityId: 'catalog:catalog_product',\n aclFeatures: ['catalog.products.view'],\n enabled: true,\n priority: 10,\n buildSource: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Title', record.title)\n appendLine(lines, 'Subtitle', record.subtitle)\n appendLine(lines, 'Description', record.description)\n appendLine(lines, 'SKU', record.sku)\n appendLine(lines, 'Handle', record.handle)\n appendLine(lines, 'Product type', record.product_type ?? record.productType)\n appendLine(lines, 'Base unit', record.default_unit ?? record.defaultUnit)\n appendLine(lines, 'Default sales unit', record.default_sales_unit ?? record.defaultSalesUnit)\n appendLine(\n lines,\n 'Default sales quantity',\n record.default_sales_unit_quantity ?? record.defaultSalesUnitQuantity,\n )\n appendLine(lines, 'UoM rounding mode', record.uom_rounding_mode ?? record.uomRoundingMode)\n appendLine(lines, 'UoM rounding scale', record.uom_rounding_scale ?? record.uomRoundingScale)\n appendLine(lines, 'Unit price enabled', record.unit_price_enabled ?? record.unitPriceEnabled)\n appendLine(\n lines,\n 'Unit price reference unit',\n record.unit_price_reference_unit ?? record.unitPriceReferenceUnit,\n )\n appendLine(\n lines,\n 'Unit price base quantity',\n record.unit_price_base_quantity ?? record.unitPriceBaseQuantity,\n )\n return buildIndexSource(ctx, buildProductPresenter(translate, record), lines)\n },\n formatResult: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n return buildProductPresenter(translate, ctx.record)\n },\n resolveUrl: async (ctx) => buildProductUrl(readRecordText(ctx.record, 'id')),\n fieldPolicy: {\n searchable: [\n 'title',\n 'subtitle',\n 'description',\n 'sku',\n 'handle',\n 'product_type',\n 'default_unit',\n 'default_sales_unit',\n 'unit_price_reference_unit',\n ],\n excluded: ['metadata', 'dimensions', 'tax_rate_id'],\n },\n },\n {\n entityId: 'catalog:catalog_product_variant',\n aclFeatures: ['catalog.products.view'],\n enabled: true,\n priority: 8,\n buildSource: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Name', record.name)\n appendLine(lines, 'SKU', record.sku)\n appendLine(lines, 'Barcode', record.barcode)\n appendLine(lines, 'Option values', record.option_values ?? record.optionValues)\n return buildIndexSource(ctx, buildVariantPresenter(translate, record), lines)\n },\n formatResult: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n return buildVariantPresenter(translate, ctx.record)\n },\n resolveUrl: async (ctx) => {\n const productId = resolveProductId(ctx.record)\n const variantId = readRecordText(ctx.record, 'id')\n return buildVariantUrl(productId, variantId)\n },\n fieldPolicy: {\n searchable: ['name', 'sku', 'barcode'],\n excluded: ['metadata', 'dimensions', 'option_values', 'tax_rate_id'],\n },\n },\n {\n entityId: 'catalog:catalog_product_category',\n aclFeatures: ['catalog.categories.view'],\n enabled: true,\n priority: 7,\n buildSource: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Name', record.name)\n appendLine(lines, 'Slug', record.slug)\n appendLine(lines, 'Description', record.description)\n appendLine(lines, 'Path', record.tree_path ?? record.treePath)\n return buildIndexSource(ctx, buildCategoryPresenter(translate, record), lines)\n },\n formatResult: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n return buildCategoryPresenter(translate, ctx.record)\n },\n resolveUrl: async (ctx) => buildCategoryUrl(readRecordText(ctx.record, 'id')),\n fieldPolicy: {\n searchable: ['name', 'slug', 'description', 'tree_path'],\n excluded: ['metadata', 'ancestor_ids', 'child_ids', 'descendant_ids'],\n },\n },\n {\n entityId: 'catalog:catalog_offer',\n aclFeatures: ['sales.channels.manage'],\n enabled: true,\n priority: 7,\n buildSource: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Title', record.title)\n appendLine(lines, 'Description', record.description)\n appendLine(lines, 'Channel', record.channel_id ?? record.channelId)\n return buildIndexSource(ctx, buildOfferPresenter(translate, record), lines)\n },\n formatResult: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n return buildOfferPresenter(translate, ctx.record)\n },\n resolveUrl: async (ctx) => {\n const productId = resolveProductId(ctx.record)\n return buildProductUrl(productId)\n },\n fieldPolicy: {\n searchable: ['title', 'description'],\n excluded: ['metadata'],\n },\n },\n {\n entityId: 'catalog:catalog_product_tag',\n aclFeatures: ['catalog.products.view'],\n enabled: true,\n priority: 5,\n buildSource: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Label', record.label)\n appendLine(lines, 'Slug', record.slug)\n return buildIndexSource(ctx, buildTagPresenter(translate, record), lines)\n },\n formatResult: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n return buildTagPresenter(translate, ctx.record)\n },\n resolveUrl: async () => CATALOG_PRODUCTS_URL,\n fieldPolicy: {\n searchable: ['label', 'slug'],\n },\n },\n {\n entityId: 'catalog:catalog_price_kind',\n aclFeatures: ['catalog.settings.manage'],\n enabled: true,\n priority: 4,\n buildSource: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Title', record.title)\n appendLine(lines, 'Code', record.code)\n appendLine(lines, 'Display mode', record.display_mode ?? record.displayMode)\n appendLine(lines, 'Currency', record.currency_code ?? record.currencyCode)\n return buildIndexSource(ctx, buildPriceKindPresenter(translate, record), lines)\n },\n formatResult: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n return buildPriceKindPresenter(translate, ctx.record)\n },\n resolveUrl: async () => CATALOG_CONFIG_URL,\n fieldPolicy: {\n searchable: ['title', 'code'],\n },\n },\n {\n entityId: 'catalog:catalog_product_unit_conversion',\n aclFeatures: ['catalog.products.view'],\n enabled: true,\n priority: 3,\n buildSource: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Unit code', record.unit_code ?? record.unitCode)\n appendLine(lines, 'To base factor', record.to_base_factor ?? record.toBaseFactor)\n appendLine(lines, 'Sort order', record.sort_order ?? record.sortOrder)\n const isActive = record.is_active ?? record.isActive\n appendLine(lines, 'Active', isActive)\n return buildIndexSource(ctx, buildUnitConversionPresenter(translate, record), lines)\n },\n formatResult: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n return buildUnitConversionPresenter(translate, ctx.record)\n },\n resolveUrl: async (ctx) => {\n const productId = resolveProductId(ctx.record)\n return buildProductUrl(productId)\n },\n fieldPolicy: {\n searchable: ['unit_code'],\n excluded: ['metadata'],\n },\n },\n {\n entityId: 'catalog:catalog_option_schema_template',\n aclFeatures: ['catalog.products.view'],\n enabled: true,\n priority: 4,\n buildSource: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Name', record.name)\n appendLine(lines, 'Code', record.code)\n appendLine(lines, 'Description', record.description)\n return buildIndexSource(ctx, buildOptionSchemaPresenter(translate, record), lines)\n },\n formatResult: async (ctx) => {\n const { t: translate } = await resolveTranslations()\n return buildOptionSchemaPresenter(translate, ctx.record)\n },\n resolveUrl: async () => CATALOG_CONFIG_URL,\n fieldPolicy: {\n searchable: ['name', 'code', 'description'],\n excluded: ['schema', 'metadata'],\n },\n },\n ],\n}\n\nexport default searchConfig\nexport const config = searchConfig\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -37,6 +37,7 @@ const searchConfig = {
|
|
|
37
37
|
entities: [
|
|
38
38
|
{
|
|
39
39
|
entityId: "customer_accounts:customer_user",
|
|
40
|
+
aclFeatures: ["customer_accounts.view"],
|
|
40
41
|
enabled: true,
|
|
41
42
|
priority: 6,
|
|
42
43
|
buildSource: async (ctx) => {
|
|
@@ -74,6 +75,7 @@ const searchConfig = {
|
|
|
74
75
|
},
|
|
75
76
|
{
|
|
76
77
|
entityId: "customer_accounts:customer_role",
|
|
78
|
+
aclFeatures: ["customer_accounts.view"],
|
|
77
79
|
enabled: true,
|
|
78
80
|
priority: 6,
|
|
79
81
|
buildSource: async (ctx) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/customer_accounts/search.ts"],
|
|
4
|
-
"sourcesContent": ["import type {\n SearchModuleConfig,\n SearchBuildContext,\n SearchResultPresenter,\n SearchIndexSource,\n} from '@open-mercato/shared/modules/search'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\n\nfunction pickString(...candidates: Array<unknown>): string | null {\n for (const candidate of candidates) {\n if (typeof candidate !== 'string') continue\n const trimmed = candidate.trim()\n if (trimmed.length > 0) return trimmed\n }\n return null\n}\n\nfunction snippet(value: unknown, max = 140): string | undefined {\n if (typeof value !== 'string') return undefined\n const trimmed = value.trim()\n if (!trimmed.length) return undefined\n if (trimmed.length <= max) return trimmed\n return `${trimmed.slice(0, max - 3)}...`\n}\n\nfunction appendLine(lines: string[], label: string, value: unknown) {\n if (value === null || value === undefined) return\n const text = typeof value === 'object' ? JSON.stringify(value) : String(value)\n if (!text.trim()) return\n lines.push(`${label}: ${text}`)\n}\n\nfunction formatSubtitle(...parts: Array<unknown>): string | undefined {\n const text = parts\n .map((part) => (part === null || part === undefined ? '' : String(part)))\n .map((part) => part.trim())\n .filter(Boolean)\n if (text.length === 0) return undefined\n return text.join(' \u00B7 ')\n}\n\nfunction buildIndexSource(\n ctx: SearchBuildContext,\n presenter: SearchResultPresenter,\n lines: string[],\n): SearchIndexSource | null {\n if (!lines.length) return null\n return {\n text: lines,\n presenter,\n checksumSource: { record: ctx.record, customFields: ctx.customFields },\n }\n}\n\nexport const searchConfig: SearchModuleConfig = {\n entities: [\n {\n entityId: 'customer_accounts:customer_user',\n enabled: true,\n priority: 6,\n buildSource: async (ctx) => {\n const { t } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Name', record.display_name ?? record.displayName)\n appendLine(lines, 'Email', record.email)\n return buildIndexSource(\n ctx,\n {\n title: pickString(record.display_name, record.displayName) ?? String(record.id),\n subtitle: formatSubtitle(record.email),\n icon: 'user',\n badge: t('customer_accounts.search.badge.customerUser', 'Customer User'),\n },\n lines,\n )\n },\n formatResult: async (ctx) => {\n const { t } = await resolveTranslations()\n const record = ctx.record\n return {\n title: pickString(record.display_name, record.displayName) ?? String(record.id),\n subtitle: formatSubtitle(record.email),\n icon: 'user',\n badge: t('customer_accounts.search.badge.customerUser', 'Customer User'),\n }\n },\n resolveUrl: async (ctx) => `/backend/customer-accounts/users/${encodeURIComponent(String(ctx.record.id))}`,\n fieldPolicy: {\n searchable: ['display_name', 'email'],\n excluded: ['password_hash', 'email_hash'],\n },\n },\n {\n entityId: 'customer_accounts:customer_role',\n enabled: true,\n priority: 6,\n buildSource: async (ctx) => {\n const { t } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Name', record.name)\n appendLine(lines, 'Description', record.description)\n return buildIndexSource(\n ctx,\n {\n title: pickString(record.name) ?? String(record.id),\n subtitle: formatSubtitle(snippet(record.description)),\n icon: 'shield',\n badge: t('customer_accounts.search.badge.customerRole', 'Customer Role'),\n },\n lines,\n )\n },\n formatResult: async (ctx) => {\n const { t } = await resolveTranslations()\n const record = ctx.record\n return {\n title: pickString(record.name) ?? String(record.id),\n subtitle: formatSubtitle(snippet(record.description)),\n icon: 'shield',\n badge: t('customer_accounts.search.badge.customerRole', 'Customer Role'),\n }\n },\n resolveUrl: async (ctx) => `/backend/customer-accounts/roles/${encodeURIComponent(String(ctx.record.id))}/edit`,\n fieldPolicy: {\n searchable: ['name', 'description'],\n },\n },\n ],\n}\n\nexport default searchConfig\nexport const config = searchConfig\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type {\n SearchModuleConfig,\n SearchBuildContext,\n SearchResultPresenter,\n SearchIndexSource,\n} from '@open-mercato/shared/modules/search'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\n\nfunction pickString(...candidates: Array<unknown>): string | null {\n for (const candidate of candidates) {\n if (typeof candidate !== 'string') continue\n const trimmed = candidate.trim()\n if (trimmed.length > 0) return trimmed\n }\n return null\n}\n\nfunction snippet(value: unknown, max = 140): string | undefined {\n if (typeof value !== 'string') return undefined\n const trimmed = value.trim()\n if (!trimmed.length) return undefined\n if (trimmed.length <= max) return trimmed\n return `${trimmed.slice(0, max - 3)}...`\n}\n\nfunction appendLine(lines: string[], label: string, value: unknown) {\n if (value === null || value === undefined) return\n const text = typeof value === 'object' ? JSON.stringify(value) : String(value)\n if (!text.trim()) return\n lines.push(`${label}: ${text}`)\n}\n\nfunction formatSubtitle(...parts: Array<unknown>): string | undefined {\n const text = parts\n .map((part) => (part === null || part === undefined ? '' : String(part)))\n .map((part) => part.trim())\n .filter(Boolean)\n if (text.length === 0) return undefined\n return text.join(' \u00B7 ')\n}\n\nfunction buildIndexSource(\n ctx: SearchBuildContext,\n presenter: SearchResultPresenter,\n lines: string[],\n): SearchIndexSource | null {\n if (!lines.length) return null\n return {\n text: lines,\n presenter,\n checksumSource: { record: ctx.record, customFields: ctx.customFields },\n }\n}\n\nexport const searchConfig: SearchModuleConfig = {\n entities: [\n {\n entityId: 'customer_accounts:customer_user',\n aclFeatures: ['customer_accounts.view'],\n enabled: true,\n priority: 6,\n buildSource: async (ctx) => {\n const { t } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Name', record.display_name ?? record.displayName)\n appendLine(lines, 'Email', record.email)\n return buildIndexSource(\n ctx,\n {\n title: pickString(record.display_name, record.displayName) ?? String(record.id),\n subtitle: formatSubtitle(record.email),\n icon: 'user',\n badge: t('customer_accounts.search.badge.customerUser', 'Customer User'),\n },\n lines,\n )\n },\n formatResult: async (ctx) => {\n const { t } = await resolveTranslations()\n const record = ctx.record\n return {\n title: pickString(record.display_name, record.displayName) ?? String(record.id),\n subtitle: formatSubtitle(record.email),\n icon: 'user',\n badge: t('customer_accounts.search.badge.customerUser', 'Customer User'),\n }\n },\n resolveUrl: async (ctx) => `/backend/customer-accounts/users/${encodeURIComponent(String(ctx.record.id))}`,\n fieldPolicy: {\n searchable: ['display_name', 'email'],\n excluded: ['password_hash', 'email_hash'],\n },\n },\n {\n entityId: 'customer_accounts:customer_role',\n aclFeatures: ['customer_accounts.view'],\n enabled: true,\n priority: 6,\n buildSource: async (ctx) => {\n const { t } = await resolveTranslations()\n const record = ctx.record\n const lines: string[] = []\n appendLine(lines, 'Name', record.name)\n appendLine(lines, 'Description', record.description)\n return buildIndexSource(\n ctx,\n {\n title: pickString(record.name) ?? String(record.id),\n subtitle: formatSubtitle(snippet(record.description)),\n icon: 'shield',\n badge: t('customer_accounts.search.badge.customerRole', 'Customer Role'),\n },\n lines,\n )\n },\n formatResult: async (ctx) => {\n const { t } = await resolveTranslations()\n const record = ctx.record\n return {\n title: pickString(record.name) ?? String(record.id),\n subtitle: formatSubtitle(snippet(record.description)),\n icon: 'shield',\n badge: t('customer_accounts.search.badge.customerRole', 'Customer Role'),\n }\n },\n resolveUrl: async (ctx) => `/backend/customer-accounts/roles/${encodeURIComponent(String(ctx.record.id))}/edit`,\n fieldPolicy: {\n searchable: ['name', 'description'],\n },\n },\n ],\n}\n\nexport default searchConfig\nexport const config = searchConfig\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -54,6 +54,10 @@ const dealCrudEvents = {
|
|
|
54
54
|
tenantId: ctx.identifiers.tenantId
|
|
55
55
|
})
|
|
56
56
|
};
|
|
57
|
+
const TERMINAL_PIPELINE_STAGE_LABELS = {
|
|
58
|
+
won: /* @__PURE__ */ new Set(["won", "win", "closed won", "closed win"]),
|
|
59
|
+
lost: /* @__PURE__ */ new Set(["lost", "loose", "closed lost", "closed loose"])
|
|
60
|
+
};
|
|
57
61
|
function coerceSnapshotDate(value, fieldName) {
|
|
58
62
|
if (value === void 0 || value === null) return null;
|
|
59
63
|
if (value instanceof Date) return value;
|
|
@@ -80,6 +84,40 @@ async function loadPipelineStageSnapshot(em, pipelineStageId, tenantId, organiza
|
|
|
80
84
|
order: stage.order
|
|
81
85
|
};
|
|
82
86
|
}
|
|
87
|
+
function normalizePipelineStageLabel(value) {
|
|
88
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
89
|
+
}
|
|
90
|
+
function resolveRequestedClosureOutcome(input) {
|
|
91
|
+
if (input.closureOutcome === "won" || input.closureOutcome === "lost") {
|
|
92
|
+
return input.closureOutcome;
|
|
93
|
+
}
|
|
94
|
+
if (input.status === "win") return "won";
|
|
95
|
+
if (input.status === "loose") return "lost";
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
async function loadClosurePipelineStageSnapshot(em, input) {
|
|
99
|
+
if (!input.pipelineId) return null;
|
|
100
|
+
const stages = await findWithDecryption(
|
|
101
|
+
em,
|
|
102
|
+
CustomerPipelineStage,
|
|
103
|
+
{
|
|
104
|
+
pipelineId: input.pipelineId,
|
|
105
|
+
tenantId: input.tenantId,
|
|
106
|
+
organizationId: input.organizationId
|
|
107
|
+
},
|
|
108
|
+
{ orderBy: { order: "ASC" } },
|
|
109
|
+
{ tenantId: input.tenantId, organizationId: input.organizationId }
|
|
110
|
+
);
|
|
111
|
+
const aliases = TERMINAL_PIPELINE_STAGE_LABELS[input.closureOutcome];
|
|
112
|
+
const stage = stages.find((candidate) => aliases.has(normalizePipelineStageLabel(candidate.label)));
|
|
113
|
+
if (!stage) return null;
|
|
114
|
+
return {
|
|
115
|
+
id: stage.id,
|
|
116
|
+
pipelineId: stage.pipelineId,
|
|
117
|
+
label: stage.label,
|
|
118
|
+
order: stage.order
|
|
119
|
+
};
|
|
120
|
+
}
|
|
83
121
|
async function resolvePipelineStageValue(em, pipelineStageId, tenantId, organizationId) {
|
|
84
122
|
const stage = await loadPipelineStageSnapshot(em, pipelineStageId, tenantId, organizationId);
|
|
85
123
|
if (!stage) return null;
|
|
@@ -543,6 +581,7 @@ const updateDealCommand = {
|
|
|
543
581
|
};
|
|
544
582
|
let nextPipelineStageLabel = null;
|
|
545
583
|
let resolvedCurrentPipelineStageLabel = null;
|
|
584
|
+
let pipelineStageAssignmentChanged = false;
|
|
546
585
|
await runCrudCommandWrite({
|
|
547
586
|
ctx,
|
|
548
587
|
em,
|
|
@@ -562,10 +601,18 @@ const updateDealCommand = {
|
|
|
562
601
|
}),
|
|
563
602
|
phases: [
|
|
564
603
|
async () => {
|
|
565
|
-
const
|
|
566
|
-
const requestedPipelineStageId = parsed.pipelineStageId !== void 0 ? parsed.pipelineStageId ?? null : record.pipelineStageId ?? null;
|
|
604
|
+
const requestedClosureOutcome = resolveRequestedClosureOutcome(parsed);
|
|
567
605
|
const requestedPipelineId = parsed.pipelineId !== void 0 ? parsed.pipelineId ?? null : record.pipelineId ?? null;
|
|
568
|
-
|
|
606
|
+
const closureStageSnapshot = parsed.pipelineStageId === void 0 && requestedClosureOutcome ? await loadClosurePipelineStageSnapshot(em, {
|
|
607
|
+
pipelineId: requestedPipelineId,
|
|
608
|
+
closureOutcome: requestedClosureOutcome,
|
|
609
|
+
tenantId: record.tenantId,
|
|
610
|
+
organizationId: record.organizationId
|
|
611
|
+
}) : null;
|
|
612
|
+
pipelineStageAssignmentChanged = parsed.pipelineStageId !== void 0 || closureStageSnapshot !== null;
|
|
613
|
+
const pipelineAssignmentChanged = parsed.pipelineId !== void 0 || pipelineStageAssignmentChanged;
|
|
614
|
+
const requestedPipelineStageId = parsed.pipelineStageId !== void 0 ? parsed.pipelineStageId ?? null : closureStageSnapshot?.id ?? record.pipelineStageId ?? null;
|
|
615
|
+
nextStageSnapshot = closureStageSnapshot ?? (requestedPipelineStageId && (pipelineAssignmentChanged || !record.pipelineStage) ? await loadPipelineStageSnapshot(em, requestedPipelineStageId, record.tenantId, record.organizationId) : null);
|
|
569
616
|
if (pipelineAssignmentChanged) {
|
|
570
617
|
nextPipelineAssignment = resolvePipelineAssignment({
|
|
571
618
|
pipelineId: requestedPipelineId,
|
|
@@ -586,13 +633,13 @@ const updateDealCommand = {
|
|
|
586
633
|
if (parsed.description !== void 0) record.description = parsed.description ?? null;
|
|
587
634
|
if (parsed.status !== void 0) record.status = parsed.status ?? record.status;
|
|
588
635
|
if (parsed.pipelineStage !== void 0) record.pipelineStage = parsed.pipelineStage ?? null;
|
|
589
|
-
if (parsed.pipelineId !== void 0 ||
|
|
636
|
+
if (parsed.pipelineId !== void 0 || pipelineStageAssignmentChanged && nextStageSnapshot) {
|
|
590
637
|
record.pipelineId = nextPipelineAssignment.pipelineId;
|
|
591
638
|
}
|
|
592
|
-
if (
|
|
593
|
-
if (nextPipelineStageLabel && (
|
|
639
|
+
if (pipelineStageAssignmentChanged) record.pipelineStageId = nextPipelineAssignment.pipelineStageId;
|
|
640
|
+
if (nextPipelineStageLabel && (pipelineStageAssignmentChanged || !record.pipelineStage)) {
|
|
594
641
|
record.pipelineStage = nextPipelineStageLabel;
|
|
595
|
-
} else if (resolvedCurrentPipelineStageLabel && (
|
|
642
|
+
} else if (resolvedCurrentPipelineStageLabel && (pipelineStageAssignmentChanged || !record.pipelineStage)) {
|
|
596
643
|
record.pipelineStage = resolvedCurrentPipelineStageLabel;
|
|
597
644
|
}
|
|
598
645
|
if (parsed.valueAmount !== void 0) record.valueAmount = toNumericString(parsed.valueAmount);
|
|
@@ -611,7 +658,7 @@ const updateDealCommand = {
|
|
|
611
658
|
async () => {
|
|
612
659
|
const snapshot = nextStageSnapshot;
|
|
613
660
|
if (!snapshot) return;
|
|
614
|
-
const shouldRecord =
|
|
661
|
+
const shouldRecord = pipelineStageAssignmentChanged && nextPipelineAssignment.pipelineStageId !== null && nextPipelineAssignment.pipelineStageId !== previousPipelineStageId;
|
|
615
662
|
if (!shouldRecord) return;
|
|
616
663
|
await upsertDealStageTransition(em, {
|
|
617
664
|
deal: record,
|