@open-mercato/core 0.4.2-canary-19353c5970 → 0.4.2-canary-ad4e7882e9
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/modules/auth/api/login.js +25 -6
- package/dist/modules/auth/api/login.js.map +2 -2
- package/dist/modules/auth/data/validators.js +2 -1
- package/dist/modules/auth/data/validators.js.map +2 -2
- package/dist/modules/auth/frontend/login.js +85 -1
- package/dist/modules/auth/frontend/login.js.map +2 -2
- package/dist/modules/auth/lib/setup-app.js +25 -12
- package/dist/modules/auth/lib/setup-app.js.map +2 -2
- package/dist/modules/auth/services/authService.js +21 -0
- package/dist/modules/auth/services/authService.js.map +2 -2
- package/dist/modules/business_rules/cli.js +2 -1
- package/dist/modules/business_rules/cli.js.map +2 -2
- package/dist/modules/directory/api/get/tenants/lookup.js +70 -0
- package/dist/modules/directory/api/get/tenants/lookup.js.map +7 -0
- package/dist/modules/notifications/migrations/Migration20260129082610.js +13 -0
- package/dist/modules/notifications/migrations/Migration20260129082610.js.map +7 -0
- package/dist/modules/query_index/cli.js +63 -7
- package/dist/modules/query_index/cli.js.map +2 -2
- package/dist/modules/workflows/cli.js +12 -12
- package/dist/modules/workflows/cli.js.map +2 -2
- package/package.json +2 -2
- package/src/modules/auth/api/__tests__/login.test.ts +2 -0
- package/src/modules/auth/api/login.ts +26 -7
- package/src/modules/auth/data/validators.ts +1 -0
- package/src/modules/auth/frontend/login.tsx +106 -2
- package/src/modules/auth/i18n/de.json +5 -0
- package/src/modules/auth/i18n/en.json +5 -0
- package/src/modules/auth/i18n/es.json +5 -0
- package/src/modules/auth/i18n/pl.json +5 -0
- package/src/modules/auth/lib/setup-app.ts +37 -15
- package/src/modules/auth/services/authService.ts +23 -0
- package/src/modules/business_rules/cli.ts +2 -1
- package/src/modules/directory/api/get/tenants/lookup.ts +75 -0
- package/src/modules/notifications/migrations/.snapshot-open-mercato.json +36 -0
- package/src/modules/notifications/migrations/Migration20260129082610.ts +13 -0
- package/src/modules/query_index/cli.ts +82 -13
- package/src/modules/workflows/cli.ts +12 -12
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/query_index/cli.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { Knex } from 'knex'\nimport { createProgressBar } from '@open-mercato/shared/lib/cli/progress'\nimport { resolveTenantEncryptionService } from '@open-mercato/shared/lib/encryption/customFieldValues'\nimport { decryptIndexDocForSearch, encryptIndexDocForStorage } from '@open-mercato/shared/lib/encryption/indexDoc'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\n\ntype ProgressBarHandle = {\n update(completed: number): void\n complete(): void\n}\nimport { resolveEntityTableName } from '@open-mercato/shared/lib/query/engine'\nimport { recordIndexerError } from '@open-mercato/shared/lib/indexers/error-log'\nimport { recordIndexerLog } from '@open-mercato/shared/lib/indexers/status-log'\nimport { upsertIndexBatch, type AnyRow } from './lib/batch'\nimport { reindexEntity, DEFAULT_REINDEX_PARTITIONS } from './lib/reindexer'\nimport { purgeIndexScope } from './lib/purge'\nimport { flattenSystemEntityIds } from '@open-mercato/shared/lib/entities/system-entities'\nimport type { VectorIndexService } from '@open-mercato/search/vector'\n\ntype ParsedArgs = Record<string, string | boolean>\n\nfunction parseArgs(rest: string[]): ParsedArgs {\n const args: ParsedArgs = {}\n for (let i = 0; i < rest.length; i += 1) {\n const part = rest[i]\n if (!part?.startsWith('--')) continue\n const [rawKey, rawValue] = part.slice(2).split('=')\n if (!rawKey) continue\n if (rawValue !== undefined) {\n args[rawKey] = rawValue\n } else if (i + 1 < rest.length && !rest[i + 1]!.startsWith('--')) {\n args[rawKey] = rest[i + 1]!\n i += 1\n } else {\n args[rawKey] = true\n }\n }\n return args\n}\n\nfunction stringOption(args: ParsedArgs, ...keys: string[]): string | undefined {\n for (const key of keys) {\n const raw = args[key]\n if (typeof raw !== 'string') continue\n const trimmed = raw.trim()\n if (trimmed.length > 0) return trimmed\n }\n return undefined\n}\n\nfunction numberOption(args: ParsedArgs, ...keys: string[]): number | undefined {\n for (const key of keys) {\n const raw = args[key]\n if (typeof raw === 'number') return raw\n if (typeof raw === 'string') {\n const parsed = Number(raw)\n if (Number.isFinite(parsed)) return parsed\n }\n }\n return undefined\n}\n\nfunction flagEnabled(args: ParsedArgs, ...keys: string[]): boolean {\n for (const key of keys) {\n const raw = args[key]\n if (raw === undefined) continue\n if (raw === true) return true\n if (raw === false) continue\n if (typeof raw === 'string') {\n const trimmed = raw.trim()\n if (!trimmed) return true\n const parsed = parseBooleanToken(trimmed)\n return parsed === null ? true : parsed\n }\n }\n return false\n}\n\nfunction toPositiveInt(value: number | undefined): number | undefined {\n if (value === undefined) return undefined\n const n = Math.floor(value)\n if (!Number.isFinite(n) || n <= 0) return undefined\n return n\n}\n\nfunction toNonNegativeInt(value: number | undefined, fallback = 0): number {\n if (value === undefined) return fallback\n const n = Math.floor(value)\n if (!Number.isFinite(n) || n < 0) return fallback\n return n\n}\n\nconst DEFAULT_BATCH_SIZE = 200\n\ntype RebuildExecutionOptions = {\n em: EntityManager\n knex: Knex\n entityType: string\n tableName: string\n orgOverride?: string\n tenantOverride?: string\n global: boolean\n includeDeleted: boolean\n limit?: number\n offset: number\n recordId?: string\n batchSize: number\n progressLabel?: string\n supportsOrgFilter: boolean\n supportsTenantFilter: boolean\n supportsDeletedFilter: boolean\n}\n\ntype RebuildResult = {\n processed: number\n matched: number\n}\nasync function rebuildEntityIndexes(options: RebuildExecutionOptions): Promise<RebuildResult> {\n const {\n em,\n knex,\n entityType,\n tableName,\n orgOverride,\n tenantOverride,\n global,\n includeDeleted,\n limit,\n offset,\n recordId,\n batchSize,\n progressLabel,\n supportsOrgFilter,\n supportsTenantFilter,\n supportsDeletedFilter,\n } = options\n\n const encryption = resolveTenantEncryptionService(em as any)\n const dekKeyCache = new Map<string | null, string | null>()\n\n const encryptDoc = async (\n targetEntity: string,\n doc: Record<string, unknown>,\n scope: { organizationId: string | null; tenantId: string | null },\n ) => {\n try {\n return await encryptIndexDocForStorage(\n targetEntity,\n doc,\n { tenantId: scope.tenantId ?? null, organizationId: scope.organizationId ?? null },\n encryption,\n )\n } catch {\n return doc\n }\n }\n\n const decryptDoc = async (\n targetEntity: string,\n doc: Record<string, unknown>,\n scope: { organizationId: string | null; tenantId: string | null },\n ) => {\n try {\n return await decryptIndexDocForSearch(\n targetEntity,\n doc,\n { tenantId: scope.tenantId ?? null, organizationId: scope.organizationId ?? null },\n encryption,\n dekKeyCache,\n )\n } catch {\n return doc\n }\n }\n\n const filters: Record<string, unknown> = {}\n if (!global) {\n if (orgOverride !== undefined && supportsOrgFilter) filters.organization_id = orgOverride\n if (tenantOverride !== undefined && supportsTenantFilter) filters.tenant_id = tenantOverride\n }\n if (!includeDeleted && supportsDeletedFilter) filters.deleted_at = null\n\n const baseQuery = knex(tableName).where(filters)\n\n if (recordId) {\n const row = await baseQuery.clone().where({ id: recordId }).first<AnyRow>()\n if (!row) return { processed: 0, matched: 0 }\n const bar = createProgressBar(progressLabel ?? `Rebuilding ${entityType}`, 1)\n await upsertIndexBatch(knex, entityType, [row], { orgId: orgOverride, tenantId: tenantOverride }, { encryptDoc, decryptDoc })\n bar.update(1)\n bar.complete()\n return { processed: 1, matched: 1 }\n }\n\n const countRow = await baseQuery.clone().count<{ count: string }>({ count: '*' }).first()\n const totalRaw = countRow?.count ?? (countRow as any)?.['count(*)']\n const total = totalRaw ? Number(totalRaw) : 0\n const effectiveOffset = Math.max(0, offset)\n const matchedWithoutLimit = Math.max(0, total - effectiveOffset)\n const limitValue = toPositiveInt(limit)\n const intended = limitValue !== undefined ? Math.min(matchedWithoutLimit, limitValue) : matchedWithoutLimit\n if (!Number.isFinite(intended) || intended <= 0) {\n return { processed: 0, matched: 0 }\n }\n\n const bar = createProgressBar(progressLabel ?? `Rebuilding ${entityType}`, intended)\n let processed = 0\n let cursorOffset = effectiveOffset\n let remaining = limitValue\n\n while (processed < intended) {\n const chunkLimit = remaining !== undefined ? Math.min(batchSize, remaining) : batchSize\n const chunk = await baseQuery\n .clone()\n .select('*')\n .orderBy('id')\n .limit(chunkLimit)\n .offset(cursorOffset)\n if (!chunk.length) break\n\n await upsertIndexBatch(knex, entityType, chunk as AnyRow[], {\n orgId: orgOverride,\n tenantId: tenantOverride,\n }, { encryptDoc, decryptDoc })\n\n processed += chunk.length\n cursorOffset += chunk.length\n if (remaining !== undefined) remaining -= chunk.length\n bar.update(processed)\n if (remaining !== undefined && remaining <= 0) break\n }\n\n if (processed < intended) {\n bar.update(processed)\n }\n bar.complete()\n return { processed, matched: intended }\n}\n\nasync function getColumnSet(knex: Knex, tableName: string): Promise<Set<string>> {\n try {\n const info = await knex(tableName).columnInfo()\n return new Set(Object.keys(info).map((key) => key.toLowerCase()))\n } catch {\n return new Set<string>()\n }\n}\n\ntype ScopeDescriptor = {\n global: boolean\n orgId?: string\n tenantId?: string\n includeDeleted: boolean\n supportsOrg: boolean\n supportsTenant: boolean\n supportsDeleted: boolean\n}\n\nfunction describeScope(scope: ScopeDescriptor): string {\n const parts: string[] = []\n if (scope.global) parts.push('global')\n if (!scope.global && scope.orgId && scope.supportsOrg) parts.push(`org=${scope.orgId}`)\n if (!scope.global && scope.tenantId && scope.supportsTenant) parts.push(`tenant=${scope.tenantId}`)\n if (!scope.includeDeleted && scope.supportsDeleted) parts.push('active-only')\n return parts.length ? ` (${parts.join(' ')})` : ''\n}\n\nconst rebuild: ModuleCli = {\n command: 'rebuild',\n async run(rest) {\n const args = parseArgs(rest)\n const entity = stringOption(args, 'entity', 'e')\n if (!entity) {\n console.error(\n 'Usage: mercato query_index rebuild --entity <module:entity> [--record <id>] [--org <id>] [--tenant <id>] [--global] [--withDeleted] [--limit <n>] [--offset <n>]',\n )\n return\n }\n\n const globalFlag = flagEnabled(args, 'global')\n const includeDeleted = flagEnabled(args, 'withDeleted')\n const orgId = stringOption(args, 'org', 'organizationId')\n const tenantId = stringOption(args, 'tenant', 'tenantId')\n const recordId = stringOption(args, 'record', 'recordId', 'id')\n const limit = toPositiveInt(numberOption(args, 'limit'))\n const offset = toNonNegativeInt(numberOption(args, 'offset'))\n const batchSize = toPositiveInt(numberOption(args, 'batch', 'chunk', 'size')) ?? DEFAULT_BATCH_SIZE\n\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager)\n try {\n const knex = em.getConnection().getKnex()\n const tableName = resolveEntityTableName(em, entity)\n const columns = await getColumnSet(knex, tableName)\n const supportsOrg = columns.has('organization_id')\n const supportsTenant = columns.has('tenant_id')\n const supportsDeleted = columns.has('deleted_at')\n\n if (!globalFlag && orgId && !supportsOrg) {\n console.warn(`[query_index] ${entity} does not expose organization_id, ignoring --org filter`)\n }\n if (!globalFlag && tenantId && !supportsTenant) {\n console.warn(`[query_index] ${entity} does not expose tenant_id, ignoring --tenant filter`)\n }\n if (!includeDeleted && !supportsDeleted) {\n console.warn(`[query_index] ${entity} does not expose deleted_at, cannot skip deleted rows`)\n }\n\n const result = await rebuildEntityIndexes({\n em,\n knex,\n entityType: entity,\n tableName,\n orgOverride: orgId,\n tenantOverride: tenantId,\n global: globalFlag,\n includeDeleted,\n limit,\n offset,\n recordId,\n batchSize,\n progressLabel: recordId ? `Rebuilding ${entity} record ${recordId}` : `Rebuilding ${entity}`,\n supportsOrgFilter: supportsOrg,\n supportsTenantFilter: supportsTenant,\n supportsDeletedFilter: supportsDeleted,\n })\n\n if (recordId) {\n if (result.processed === 0) {\n console.log(`No matching row found for ${entity} with id ${recordId}`)\n } else {\n console.log(`Rebuilt index for ${entity} record ${recordId}`)\n }\n return\n }\n\n const scopeLabel = describeScope({\n global: globalFlag,\n orgId,\n tenantId,\n includeDeleted,\n supportsOrg,\n supportsTenant,\n supportsDeleted,\n })\n\n if (result.matched === 0) {\n console.log(`No rows matched filters for ${entity}${scopeLabel}`)\n return\n }\n\n console.log(`Rebuilt ${result.processed} row(s) for ${entity}${scopeLabel}`)\n } catch (error) {\n await recordIndexerError(\n { em },\n {\n source: 'query_index',\n handler: 'cli:query_index.rebuild',\n error,\n entityType: entity,\n recordId,\n tenantId,\n organizationId: orgId,\n payload: { args },\n },\n )\n throw error\n } finally {\n if (typeof (container as any)?.dispose === 'function') {\n await (container as any).dispose()\n }\n }\n },\n}\n\nconst rebuildAll: ModuleCli = {\n command: 'rebuild-all',\n async run(rest) {\n const args = parseArgs(rest)\n const globalFlag = flagEnabled(args, 'global')\n const includeDeleted = flagEnabled(args, 'withDeleted')\n const orgId = stringOption(args, 'org', 'organizationId')\n const tenantId = stringOption(args, 'tenant', 'tenantId')\n const limit = toPositiveInt(numberOption(args, 'limit'))\n const offset = toNonNegativeInt(numberOption(args, 'offset'))\n const batchSize = toPositiveInt(numberOption(args, 'batch', 'chunk', 'size')) ?? DEFAULT_BATCH_SIZE\n const recordId = stringOption(args, 'record', 'recordId', 'id')\n if (recordId) {\n console.error('`rebuild-all` does not support --record. Use `mercato query_index rebuild --record <id>` instead.')\n return\n }\n\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager)\n try {\n const knex = em.getConnection().getKnex()\n\n const { getEntityIds } = await import('@open-mercato/shared/lib/encryption/entityIds')\n const entityIds = flattenSystemEntityIds(getEntityIds() as Record<string, Record<string, string>>)\n if (!entityIds.length) {\n console.log('No entity definitions registered for query indexing.')\n return\n }\n\n let totalProcessed = 0\n for (let idx = 0; idx < entityIds.length; idx += 1) {\n const entity = entityIds[idx]!\n const tableName = resolveEntityTableName(em, entity)\n const columns = await getColumnSet(knex, tableName)\n const supportsOrg = columns.has('organization_id')\n const supportsTenant = columns.has('tenant_id')\n const supportsDeleted = columns.has('deleted_at')\n\n if (!globalFlag && orgId && !supportsOrg) {\n console.warn(`[query_index] ${entity} does not expose organization_id, ignoring --org filter`)\n }\n if (!globalFlag && tenantId && !supportsTenant) {\n console.warn(`[query_index] ${entity} does not expose tenant_id, ignoring --tenant filter`)\n }\n if (!includeDeleted && !supportsDeleted) {\n console.warn(`[query_index] ${entity} does not expose deleted_at, cannot skip deleted rows`)\n }\n\n const scopeLabel = describeScope({\n global: globalFlag,\n orgId,\n tenantId,\n includeDeleted,\n supportsOrg,\n supportsTenant,\n supportsDeleted,\n })\n\n console.log(`[${idx + 1}/${entityIds.length}] Rebuilding ${entity}${scopeLabel}`)\n const result = await rebuildEntityIndexes({\n em,\n knex,\n entityType: entity,\n tableName,\n orgOverride: orgId,\n tenantOverride: tenantId,\n global: globalFlag,\n includeDeleted,\n limit,\n offset,\n batchSize,\n supportsOrgFilter: supportsOrg,\n supportsTenantFilter: supportsTenant,\n supportsDeletedFilter: supportsDeleted,\n })\n totalProcessed += result.processed\n if (result.matched === 0) {\n console.log(' -> no rows matched filters')\n } else {\n console.log(` -> processed ${result.processed} row(s)`)\n }\n }\n\n console.log(`Finished rebuilding all query indexes (processed ${totalProcessed} row(s))`)\n } catch (error) {\n await recordIndexerError(\n { em },\n {\n source: 'query_index',\n handler: 'cli:query_index.rebuild-all',\n error,\n tenantId,\n organizationId: orgId,\n payload: { args },\n },\n )\n throw error\n } finally {\n if (typeof (container as any)?.dispose === 'function') {\n await (container as any).dispose()\n }\n }\n },\n}\n\nconst reindex: ModuleCli = {\n command: 'reindex',\n async run(rest) {\n const args = parseArgs(rest)\n const entity = stringOption(args, 'entity', 'e')\n const orgId = stringOption(args, 'org', 'organizationId')\n const tenantId = stringOption(args, 'tenant', 'tenantId')\n const force = flagEnabled(args, 'force', 'full')\n const batchSize = toPositiveInt(numberOption(args, 'batch', 'chunk', 'size'))\n const partitionsOption = toPositiveInt(numberOption(args, 'partitions', 'partitionCount', 'parallel'))\n const partitionIndexOptionRaw = numberOption(args, 'partition', 'partitionIndex')\n const resetCoverageFlag = flagEnabled(args, 'resetCoverage')\n const skipResetCoverageFlag = flagEnabled(args, 'skipResetCoverage', 'noResetCoverage')\n const skipPurge = flagEnabled(args, 'skipPurge', 'noPurge')\n\n const container = await createRequestContainer()\n const baseEm = (container.resolve('em') as EntityManager)\n const partitionIndexOption =\n partitionIndexOptionRaw === undefined ? undefined : toNonNegativeInt(partitionIndexOptionRaw, 0)\n const partitionCount = Math.max(\n 1,\n partitionsOption ?? DEFAULT_REINDEX_PARTITIONS,\n )\n\n if (partitionIndexOption !== undefined && partitionIndexOption >= partitionCount) {\n console.error(`partitionIndex (${partitionIndexOption}) must be < partitionCount (${partitionCount})`)\n if (typeof (container as any)?.dispose === 'function') {\n await (container as any).dispose()\n }\n return\n }\n\n const partitionTargets =\n partitionIndexOption !== undefined\n ? [partitionIndexOption]\n : Array.from({ length: partitionCount }, (_, idx) => idx)\n\n const shouldResetCoverage = (partition: number): boolean => {\n if (resetCoverageFlag) return true\n if (skipResetCoverageFlag) return false\n if (partitionIndexOption !== undefined) return partitionIndexOption === 0\n return partition === partitionTargets[0]\n }\n\n try {\n if (entity) {\n await recordIndexerLog(\n { em: baseEm },\n {\n source: 'query_index',\n handler: 'cli:query_index.reindex',\n message: `Reindex started for ${entity}`,\n entityType: entity,\n tenantId: tenantId ?? null,\n organizationId: orgId ?? null,\n details: {\n force,\n partitions: partitionTargets.length,\n partitionCount,\n partitionIndex: partitionIndexOption ?? null,\n skipPurge,\n },\n },\n ).catch(() => undefined)\n if (!skipPurge) {\n console.log(`Purging existing index rows for ${entity}...`)\n await purgeIndexScope(baseEm, { entityType: entity, organizationId: orgId, tenantId })\n }\n console.log(`Reindexing ${entity}${force ? ' (forced)' : ''} in ${partitionTargets.length} partition(s)...`)\n const progressState = new Map<number, { last: number }>()\n const renderProgress = (part: number, entityId: string, info: { processed: number; total: number }) => {\n const state = progressState.get(part) ?? { last: 0 }\n const now = Date.now()\n if (now - state.last < 1000 && info.processed < info.total) return\n state.last = now\n progressState.set(part, state)\n const percent = info.total > 0 ? ((info.processed / info.total) * 100).toFixed(2) : '0.00'\n console.log(\n ` [${entityId}] partition ${part + 1}/${partitionCount}: ${info.processed.toLocaleString()} / ${info.total.toLocaleString()} (${percent}%)`,\n )\n }\n\n const stats = await Promise.all(\n partitionTargets.map(async (part, idx) => {\n const label = partitionTargets.length > 1 ? ` [partition ${part + 1}/${partitionCount}]` : ''\n if (partitionTargets.length === 1) {\n console.log(` -> processing${label}`)\n } else if (idx === 0) {\n console.log(` -> processing partitions in parallel (count=${partitionTargets.length})`)\n }\n const partitionContainer = await createRequestContainer()\n const partitionEm = partitionContainer.resolve<EntityManager>('em')\n let partitionVectorService: VectorIndexService | null = null\n try {\n partitionVectorService = partitionContainer.resolve<VectorIndexService>('vectorIndexService')\n } catch {\n partitionVectorService = null\n }\n try {\n let progressBar: ProgressBarHandle | null = null\n const useBar = partitionTargets.length === 1\n const partitionStats = await reindexEntity(partitionEm, {\n entityType: entity,\n tenantId,\n organizationId: orgId,\n force,\n batchSize,\n emitVectorizeEvents: false,\n partitionCount,\n partitionIndex: part,\n resetCoverage: shouldResetCoverage(part),\n vectorService: partitionVectorService,\n onProgress(info) {\n if (useBar) {\n if (info.total > 0 && !progressBar) {\n progressBar = createProgressBar(`Reindexing ${entity}${label}`, info.total) as ProgressBarHandle\n }\n progressBar?.update(info.processed)\n } else {\n renderProgress(part, entity, info)\n }\n },\n })\n if (progressBar) {\n (progressBar as ProgressBarHandle).complete()\n }\n if (!useBar) {\n renderProgress(part, entity, { processed: partitionStats.processed, total: partitionStats.total })\n } else {\n console.log(\n ` processed ${partitionStats.processed} row(s)${partitionStats.total ? ` (base ${partitionStats.total})` : ''}`,\n )\n }\n return partitionStats.processed\n } finally {\n if (typeof (partitionContainer as any)?.dispose === 'function') {\n await (partitionContainer as any).dispose()\n }\n }\n }),\n )\n const totalProcessed = stats.reduce((acc, value) => acc + value, 0)\n console.log(`Finished ${entity}: processed ${totalProcessed} row(s) across ${partitionTargets.length} partition(s)`)\n await recordIndexerLog(\n { em: baseEm },\n {\n source: 'query_index',\n handler: 'cli:query_index.reindex',\n message: `Reindex completed for ${entity}`,\n entityType: entity,\n tenantId: tenantId ?? null,\n organizationId: orgId ?? null,\n details: {\n processed: totalProcessed,\n partitions: partitionTargets.length,\n partitionCount,\n partitionIndex: partitionIndexOption ?? null,\n },\n },\n ).catch(() => undefined)\n return\n }\n\n const { getEntityIds } = await import('@open-mercato/shared/lib/encryption/entityIds')\n const entityIds = flattenSystemEntityIds(getEntityIds() as Record<string, Record<string, string>>)\n if (!entityIds.length) {\n console.log('No entity definitions registered for query indexing.')\n return\n }\n for (let idx = 0; idx < entityIds.length; idx += 1) {\n const id = entityIds[idx]!\n await recordIndexerLog(\n { em: baseEm },\n {\n source: 'query_index',\n handler: 'cli:query_index.reindex',\n message: `Reindex started for ${id}`,\n entityType: id,\n tenantId: tenantId ?? null,\n organizationId: orgId ?? null,\n details: {\n force,\n partitions: partitionTargets.length,\n partitionCount,\n partitionIndex: partitionIndexOption ?? null,\n skipPurge,\n },\n },\n ).catch(() => undefined)\n if (!skipPurge) {\n console.log(`[${idx + 1}/${entityIds.length}] Purging existing index rows for ${id}...`)\n await purgeIndexScope(baseEm, { entityType: id, organizationId: orgId, tenantId })\n }\n console.log(\n `[${idx + 1}/${entityIds.length}] Reindexing ${id}${force ? ' (forced)' : ''} in ${partitionTargets.length} partition(s)...`,\n )\n const progressState = new Map<number, { last: number }>()\n const renderProgress = (part: number, entityId: string, info: { processed: number; total: number }) => {\n const state = progressState.get(part) ?? { last: 0 }\n const now = Date.now()\n if (now - state.last < 1000 && info.processed < info.total) return\n state.last = now\n progressState.set(part, state)\n const percent = info.total > 0 ? ((info.processed / info.total) * 100).toFixed(2) : '0.00'\n console.log(\n ` [${entityId}] partition ${part + 1}/${partitionCount}: ${info.processed.toLocaleString()} / ${info.total.toLocaleString()} (${percent}%)`,\n )\n }\n\n const partitionResults = await Promise.all(\n partitionTargets.map(async (part, partitionIdx) => {\n const label = partitionTargets.length > 1 ? ` [partition ${part + 1}/${partitionCount}]` : ''\n if (partitionTargets.length === 1) {\n console.log(` -> processing${label}`)\n } else if (partitionIdx === 0) {\n console.log(` -> processing partitions in parallel (count=${partitionTargets.length})`)\n }\n const partitionContainer = await createRequestContainer()\n const partitionEm = partitionContainer.resolve<EntityManager>('em')\n let partitionVectorService: VectorIndexService | null = null\n try {\n partitionVectorService = partitionContainer.resolve<VectorIndexService>('vectorIndexService')\n } catch {\n partitionVectorService = null\n }\n try {\n let progressBar: ProgressBarHandle | null = null\n const useBar = partitionTargets.length === 1\n const result = await reindexEntity(partitionEm, {\n entityType: id,\n tenantId,\n organizationId: orgId,\n force,\n batchSize,\n emitVectorizeEvents: false,\n partitionCount,\n partitionIndex: part,\n resetCoverage: shouldResetCoverage(part),\n vectorService: partitionVectorService,\n onProgress(info) {\n if (useBar) {\n if (info.total > 0 && !progressBar) {\n progressBar = createProgressBar(`Reindexing ${id}${label}`, info.total) as ProgressBarHandle\n }\n progressBar?.update(info.processed)\n } else {\n renderProgress(part, id, info)\n }\n },\n })\n if (progressBar) {\n (progressBar as ProgressBarHandle).complete()\n }\n if (!useBar) {\n renderProgress(part, id, { processed: result.processed, total: result.total })\n } else {\n console.log(\n ` processed ${result.processed} row(s)${result.total ? ` (base ${result.total})` : ''}`,\n )\n }\n return result.processed\n } finally {\n if (typeof (partitionContainer as any)?.dispose === 'function') {\n await (partitionContainer as any).dispose()\n }\n }\n }),\n )\n const totalProcessed = partitionResults.reduce((acc, value) => acc + value, 0)\n console.log(` -> ${id} complete: processed ${totalProcessed} row(s) across ${partitionTargets.length} partition(s)`)\n await recordIndexerLog(\n { em: baseEm },\n {\n source: 'query_index',\n handler: 'cli:query_index.reindex',\n message: `Reindex completed for ${id}`,\n entityType: id,\n tenantId: tenantId ?? null,\n organizationId: orgId ?? null,\n details: {\n processed: totalProcessed,\n partitions: partitionTargets.length,\n partitionCount,\n partitionIndex: partitionIndexOption ?? null,\n },\n },\n ).catch(() => undefined)\n }\n console.log(`Finished reindexing ${entityIds.length} entities`)\n } catch (error) {\n const targetLabel = entity ?? 'multiple entities'\n await recordIndexerLog(\n { em: baseEm },\n {\n source: 'query_index',\n handler: 'cli:query_index.reindex',\n level: 'warn',\n message: `Reindex failed for ${targetLabel}`,\n entityType: entity ?? null,\n tenantId: tenantId ?? null,\n organizationId: orgId ?? null,\n details: {\n error: error instanceof Error ? error.message : String(error),\n },\n },\n ).catch(() => undefined)\n await recordIndexerError(\n { em: baseEm },\n {\n source: 'query_index',\n handler: 'cli:query_index.reindex',\n error,\n entityType: entity ?? null,\n tenantId,\n organizationId: orgId ?? null,\n payload: {\n args,\n partitionTargets,\n partitionCount,\n partitionIndex: partitionIndexOption,\n force,\n skipPurge,\n },\n },\n )\n throw error\n } finally {\n if (typeof (container as any)?.dispose === 'function') {\n await (container as any).dispose()\n }\n }\n },\n}\n\nconst purge: ModuleCli = {\n command: 'purge',\n async run(rest) {\n const args = parseArgs(rest)\n const entity = stringOption(args, 'entity', 'e')\n const orgId = stringOption(args, 'org', 'organizationId')\n const tenantId = stringOption(args, 'tenant', 'tenantId')\n\n const container = await createRequestContainer()\n let em: EntityManager | null = null\n try {\n em = (container.resolve('em') as EntityManager)\n } catch {\n em = null\n }\n\n try {\n const bus = container.resolve('eventBus') as {\n emitEvent(event: string, payload: any, options?: any): Promise<void>\n }\n if (entity) {\n await bus.emitEvent(\n 'query_index.purge',\n { entityType: entity, organizationId: orgId, tenantId },\n { persistent: true },\n )\n await recordIndexerLog(\n { em: em ?? undefined },\n {\n source: 'query_index',\n handler: 'cli:query_index.purge',\n message: `Purge requested for ${entity}`,\n entityType: entity,\n tenantId: tenantId ?? null,\n organizationId: orgId ?? null,\n },\n ).catch(() => undefined)\n console.log(`Scheduled purge for ${entity}`)\n return\n }\n\n const { getEntityIds } = await import('@open-mercato/shared/lib/encryption/entityIds')\n const entityIds = flattenSystemEntityIds(getEntityIds() as Record<string, Record<string, string>>)\n for (const id of entityIds) {\n await bus.emitEvent(\n 'query_index.purge',\n { entityType: id, organizationId: orgId, tenantId },\n { persistent: true },\n )\n await recordIndexerLog(\n { em: em ?? undefined },\n {\n source: 'query_index',\n handler: 'cli:query_index.purge',\n message: `Purge requested for ${id}`,\n entityType: id,\n tenantId: tenantId ?? null,\n organizationId: orgId ?? null,\n details: { mode: 'bulk' },\n },\n ).catch(() => undefined)\n }\n console.log(`Scheduled purge for ${entityIds.length} entities`)\n } catch (error) {\n await recordIndexerLog(\n { em: em ?? undefined },\n {\n source: 'query_index',\n handler: 'cli:query_index.purge',\n level: 'warn',\n message: `Purge scheduling failed${entity ? ` for ${entity}` : ''}`,\n entityType: entity ?? null,\n tenantId: tenantId ?? null,\n organizationId: orgId ?? null,\n details: { error: error instanceof Error ? error.message : String(error) },\n },\n ).catch(() => undefined)\n await recordIndexerError(\n { em: em ?? undefined },\n {\n source: 'query_index',\n handler: 'cli:query_index.purge',\n error,\n entityType: entity ?? null,\n tenantId,\n organizationId: orgId,\n payload: { args },\n },\n )\n throw error\n } finally {\n if (typeof (container as any)?.dispose === 'function') {\n await (container as any).dispose()\n }\n }\n },\n}\n\nexport default [rebuild, rebuildAll, reindex, purge]\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { Knex } from 'knex'\nimport { createProgressBar } from '@open-mercato/shared/lib/cli/progress'\nimport { resolveTenantEncryptionService } from '@open-mercato/shared/lib/encryption/customFieldValues'\nimport { decryptIndexDocForSearch, encryptIndexDocForStorage } from '@open-mercato/shared/lib/encryption/indexDoc'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\n\ntype ProgressBarHandle = {\n update(completed: number): void\n complete(): void\n}\nimport { resolveEntityTableName } from '@open-mercato/shared/lib/query/engine'\nimport { recordIndexerError } from '@open-mercato/shared/lib/indexers/error-log'\nimport { recordIndexerLog } from '@open-mercato/shared/lib/indexers/status-log'\nimport { upsertIndexBatch, type AnyRow } from './lib/batch'\nimport { reindexEntity, DEFAULT_REINDEX_PARTITIONS } from './lib/reindexer'\nimport { purgeIndexScope } from './lib/purge'\nimport { flattenSystemEntityIds } from '@open-mercato/shared/lib/entities/system-entities'\nimport type { VectorIndexService } from '@open-mercato/search/vector'\n\ntype ParsedArgs = Record<string, string | boolean>\n\ntype PartitionProgressInfo = { processed: number; total: number }\n\nfunction isIndexerVerbose(): boolean {\n const parsed = parseBooleanToken(process.env.OM_INDEXER_VERBOSE ?? '')\n return parsed === true\n}\n\nfunction createGroupedProgress(label: string, partitionTargets: number[]) {\n const totals = new Map<number, number>()\n const processed = new Map<number, number>()\n let bar: ProgressBarHandle | null = null\n\n const getTotals = () => {\n let total = 0\n let done = 0\n for (const value of totals.values()) total += value\n for (const value of processed.values()) done += value\n return { total, done }\n }\n\n const tryInitBar = () => {\n if (bar) return\n if (totals.size < partitionTargets.length) return\n const { total } = getTotals()\n if (total <= 0) return\n bar = createProgressBar(label, total) as ProgressBarHandle\n }\n\n return {\n onProgress(partition: number, info: PartitionProgressInfo) {\n processed.set(partition, info.processed)\n if (!totals.has(partition)) totals.set(partition, info.total)\n tryInitBar()\n if (!bar) return\n const { done } = getTotals()\n bar.update(done)\n },\n complete() {\n if (bar) bar.complete()\n },\n getTotals,\n }\n}\n\nfunction parseArgs(rest: string[]): ParsedArgs {\n const args: ParsedArgs = {}\n for (let i = 0; i < rest.length; i += 1) {\n const part = rest[i]\n if (!part?.startsWith('--')) continue\n const [rawKey, rawValue] = part.slice(2).split('=')\n if (!rawKey) continue\n if (rawValue !== undefined) {\n args[rawKey] = rawValue\n } else if (i + 1 < rest.length && !rest[i + 1]!.startsWith('--')) {\n args[rawKey] = rest[i + 1]!\n i += 1\n } else {\n args[rawKey] = true\n }\n }\n return args\n}\n\nfunction stringOption(args: ParsedArgs, ...keys: string[]): string | undefined {\n for (const key of keys) {\n const raw = args[key]\n if (typeof raw !== 'string') continue\n const trimmed = raw.trim()\n if (trimmed.length > 0) return trimmed\n }\n return undefined\n}\n\nfunction numberOption(args: ParsedArgs, ...keys: string[]): number | undefined {\n for (const key of keys) {\n const raw = args[key]\n if (typeof raw === 'number') return raw\n if (typeof raw === 'string') {\n const parsed = Number(raw)\n if (Number.isFinite(parsed)) return parsed\n }\n }\n return undefined\n}\n\nfunction flagEnabled(args: ParsedArgs, ...keys: string[]): boolean {\n for (const key of keys) {\n const raw = args[key]\n if (raw === undefined) continue\n if (raw === true) return true\n if (raw === false) continue\n if (typeof raw === 'string') {\n const trimmed = raw.trim()\n if (!trimmed) return true\n const parsed = parseBooleanToken(trimmed)\n return parsed === null ? true : parsed\n }\n }\n return false\n}\n\nfunction toPositiveInt(value: number | undefined): number | undefined {\n if (value === undefined) return undefined\n const n = Math.floor(value)\n if (!Number.isFinite(n) || n <= 0) return undefined\n return n\n}\n\nfunction toNonNegativeInt(value: number | undefined, fallback = 0): number {\n if (value === undefined) return fallback\n const n = Math.floor(value)\n if (!Number.isFinite(n) || n < 0) return fallback\n return n\n}\n\nconst DEFAULT_BATCH_SIZE = 200\n\ntype RebuildExecutionOptions = {\n em: EntityManager\n knex: Knex\n entityType: string\n tableName: string\n orgOverride?: string\n tenantOverride?: string\n global: boolean\n includeDeleted: boolean\n limit?: number\n offset: number\n recordId?: string\n batchSize: number\n progressLabel?: string\n supportsOrgFilter: boolean\n supportsTenantFilter: boolean\n supportsDeletedFilter: boolean\n}\n\ntype RebuildResult = {\n processed: number\n matched: number\n}\nasync function rebuildEntityIndexes(options: RebuildExecutionOptions): Promise<RebuildResult> {\n const {\n em,\n knex,\n entityType,\n tableName,\n orgOverride,\n tenantOverride,\n global,\n includeDeleted,\n limit,\n offset,\n recordId,\n batchSize,\n progressLabel,\n supportsOrgFilter,\n supportsTenantFilter,\n supportsDeletedFilter,\n } = options\n\n const encryption = resolveTenantEncryptionService(em as any)\n const dekKeyCache = new Map<string | null, string | null>()\n\n const encryptDoc = async (\n targetEntity: string,\n doc: Record<string, unknown>,\n scope: { organizationId: string | null; tenantId: string | null },\n ) => {\n try {\n return await encryptIndexDocForStorage(\n targetEntity,\n doc,\n { tenantId: scope.tenantId ?? null, organizationId: scope.organizationId ?? null },\n encryption,\n )\n } catch {\n return doc\n }\n }\n\n const decryptDoc = async (\n targetEntity: string,\n doc: Record<string, unknown>,\n scope: { organizationId: string | null; tenantId: string | null },\n ) => {\n try {\n return await decryptIndexDocForSearch(\n targetEntity,\n doc,\n { tenantId: scope.tenantId ?? null, organizationId: scope.organizationId ?? null },\n encryption,\n dekKeyCache,\n )\n } catch {\n return doc\n }\n }\n\n const filters: Record<string, unknown> = {}\n if (!global) {\n if (orgOverride !== undefined && supportsOrgFilter) filters.organization_id = orgOverride\n if (tenantOverride !== undefined && supportsTenantFilter) filters.tenant_id = tenantOverride\n }\n if (!includeDeleted && supportsDeletedFilter) filters.deleted_at = null\n\n const baseQuery = knex(tableName).where(filters)\n\n if (recordId) {\n const row = await baseQuery.clone().where({ id: recordId }).first<AnyRow>()\n if (!row) return { processed: 0, matched: 0 }\n const bar = createProgressBar(progressLabel ?? `Rebuilding ${entityType}`, 1)\n await upsertIndexBatch(knex, entityType, [row], { orgId: orgOverride, tenantId: tenantOverride }, { encryptDoc, decryptDoc })\n bar.update(1)\n bar.complete()\n return { processed: 1, matched: 1 }\n }\n\n const countRow = await baseQuery.clone().count<{ count: string }>({ count: '*' }).first()\n const totalRaw = countRow?.count ?? (countRow as any)?.['count(*)']\n const total = totalRaw ? Number(totalRaw) : 0\n const effectiveOffset = Math.max(0, offset)\n const matchedWithoutLimit = Math.max(0, total - effectiveOffset)\n const limitValue = toPositiveInt(limit)\n const intended = limitValue !== undefined ? Math.min(matchedWithoutLimit, limitValue) : matchedWithoutLimit\n if (!Number.isFinite(intended) || intended <= 0) {\n return { processed: 0, matched: 0 }\n }\n\n const bar = createProgressBar(progressLabel ?? `Rebuilding ${entityType}`, intended)\n let processed = 0\n let cursorOffset = effectiveOffset\n let remaining = limitValue\n\n while (processed < intended) {\n const chunkLimit = remaining !== undefined ? Math.min(batchSize, remaining) : batchSize\n const chunk = await baseQuery\n .clone()\n .select('*')\n .orderBy('id')\n .limit(chunkLimit)\n .offset(cursorOffset)\n if (!chunk.length) break\n\n await upsertIndexBatch(knex, entityType, chunk as AnyRow[], {\n orgId: orgOverride,\n tenantId: tenantOverride,\n }, { encryptDoc, decryptDoc })\n\n processed += chunk.length\n cursorOffset += chunk.length\n if (remaining !== undefined) remaining -= chunk.length\n bar.update(processed)\n if (remaining !== undefined && remaining <= 0) break\n }\n\n if (processed < intended) {\n bar.update(processed)\n }\n bar.complete()\n return { processed, matched: intended }\n}\n\nasync function getColumnSet(knex: Knex, tableName: string): Promise<Set<string>> {\n try {\n const info = await knex(tableName).columnInfo()\n return new Set(Object.keys(info).map((key) => key.toLowerCase()))\n } catch {\n return new Set<string>()\n }\n}\n\ntype ScopeDescriptor = {\n global: boolean\n orgId?: string\n tenantId?: string\n includeDeleted: boolean\n supportsOrg: boolean\n supportsTenant: boolean\n supportsDeleted: boolean\n}\n\nfunction describeScope(scope: ScopeDescriptor): string {\n const parts: string[] = []\n if (scope.global) parts.push('global')\n if (!scope.global && scope.orgId && scope.supportsOrg) parts.push(`org=${scope.orgId}`)\n if (!scope.global && scope.tenantId && scope.supportsTenant) parts.push(`tenant=${scope.tenantId}`)\n if (!scope.includeDeleted && scope.supportsDeleted) parts.push('active-only')\n return parts.length ? ` (${parts.join(' ')})` : ''\n}\n\nconst rebuild: ModuleCli = {\n command: 'rebuild',\n async run(rest) {\n const args = parseArgs(rest)\n const entity = stringOption(args, 'entity', 'e')\n if (!entity) {\n console.error(\n 'Usage: mercato query_index rebuild --entity <module:entity> [--record <id>] [--org <id>] [--tenant <id>] [--global] [--withDeleted] [--limit <n>] [--offset <n>]',\n )\n return\n }\n\n const globalFlag = flagEnabled(args, 'global')\n const includeDeleted = flagEnabled(args, 'withDeleted')\n const orgId = stringOption(args, 'org', 'organizationId')\n const tenantId = stringOption(args, 'tenant', 'tenantId')\n const recordId = stringOption(args, 'record', 'recordId', 'id')\n const limit = toPositiveInt(numberOption(args, 'limit'))\n const offset = toNonNegativeInt(numberOption(args, 'offset'))\n const batchSize = toPositiveInt(numberOption(args, 'batch', 'chunk', 'size')) ?? DEFAULT_BATCH_SIZE\n\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager)\n try {\n const knex = em.getConnection().getKnex()\n const tableName = resolveEntityTableName(em, entity)\n const columns = await getColumnSet(knex, tableName)\n const supportsOrg = columns.has('organization_id')\n const supportsTenant = columns.has('tenant_id')\n const supportsDeleted = columns.has('deleted_at')\n\n if (!globalFlag && orgId && !supportsOrg) {\n console.warn(`[query_index] ${entity} does not expose organization_id, ignoring --org filter`)\n }\n if (!globalFlag && tenantId && !supportsTenant) {\n console.warn(`[query_index] ${entity} does not expose tenant_id, ignoring --tenant filter`)\n }\n if (!includeDeleted && !supportsDeleted) {\n console.warn(`[query_index] ${entity} does not expose deleted_at, cannot skip deleted rows`)\n }\n\n const result = await rebuildEntityIndexes({\n em,\n knex,\n entityType: entity,\n tableName,\n orgOverride: orgId,\n tenantOverride: tenantId,\n global: globalFlag,\n includeDeleted,\n limit,\n offset,\n recordId,\n batchSize,\n progressLabel: recordId ? `Rebuilding ${entity} record ${recordId}` : `Rebuilding ${entity}`,\n supportsOrgFilter: supportsOrg,\n supportsTenantFilter: supportsTenant,\n supportsDeletedFilter: supportsDeleted,\n })\n\n if (recordId) {\n if (result.processed === 0) {\n console.log(`No matching row found for ${entity} with id ${recordId}`)\n } else {\n console.log(`Rebuilt index for ${entity} record ${recordId}`)\n }\n return\n }\n\n const scopeLabel = describeScope({\n global: globalFlag,\n orgId,\n tenantId,\n includeDeleted,\n supportsOrg,\n supportsTenant,\n supportsDeleted,\n })\n\n if (result.matched === 0) {\n console.log(`No rows matched filters for ${entity}${scopeLabel}`)\n return\n }\n\n console.log(`Rebuilt ${result.processed} row(s) for ${entity}${scopeLabel}`)\n } catch (error) {\n await recordIndexerError(\n { em },\n {\n source: 'query_index',\n handler: 'cli:query_index.rebuild',\n error,\n entityType: entity,\n recordId,\n tenantId,\n organizationId: orgId,\n payload: { args },\n },\n )\n throw error\n } finally {\n if (typeof (container as any)?.dispose === 'function') {\n await (container as any).dispose()\n }\n }\n },\n}\n\nconst rebuildAll: ModuleCli = {\n command: 'rebuild-all',\n async run(rest) {\n const args = parseArgs(rest)\n const globalFlag = flagEnabled(args, 'global')\n const includeDeleted = flagEnabled(args, 'withDeleted')\n const orgId = stringOption(args, 'org', 'organizationId')\n const tenantId = stringOption(args, 'tenant', 'tenantId')\n const limit = toPositiveInt(numberOption(args, 'limit'))\n const offset = toNonNegativeInt(numberOption(args, 'offset'))\n const batchSize = toPositiveInt(numberOption(args, 'batch', 'chunk', 'size')) ?? DEFAULT_BATCH_SIZE\n const recordId = stringOption(args, 'record', 'recordId', 'id')\n if (recordId) {\n console.error('`rebuild-all` does not support --record. Use `mercato query_index rebuild --record <id>` instead.')\n return\n }\n\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager)\n try {\n const knex = em.getConnection().getKnex()\n\n const { getEntityIds } = await import('@open-mercato/shared/lib/encryption/entityIds')\n const entityIds = flattenSystemEntityIds(getEntityIds() as Record<string, Record<string, string>>)\n if (!entityIds.length) {\n console.log('No entity definitions registered for query indexing.')\n return\n }\n\n let totalProcessed = 0\n for (let idx = 0; idx < entityIds.length; idx += 1) {\n const entity = entityIds[idx]!\n const tableName = resolveEntityTableName(em, entity)\n const columns = await getColumnSet(knex, tableName)\n const supportsOrg = columns.has('organization_id')\n const supportsTenant = columns.has('tenant_id')\n const supportsDeleted = columns.has('deleted_at')\n\n if (!globalFlag && orgId && !supportsOrg) {\n console.warn(`[query_index] ${entity} does not expose organization_id, ignoring --org filter`)\n }\n if (!globalFlag && tenantId && !supportsTenant) {\n console.warn(`[query_index] ${entity} does not expose tenant_id, ignoring --tenant filter`)\n }\n if (!includeDeleted && !supportsDeleted) {\n console.warn(`[query_index] ${entity} does not expose deleted_at, cannot skip deleted rows`)\n }\n\n const scopeLabel = describeScope({\n global: globalFlag,\n orgId,\n tenantId,\n includeDeleted,\n supportsOrg,\n supportsTenant,\n supportsDeleted,\n })\n\n console.log(`[${idx + 1}/${entityIds.length}] Rebuilding ${entity}${scopeLabel}`)\n const result = await rebuildEntityIndexes({\n em,\n knex,\n entityType: entity,\n tableName,\n orgOverride: orgId,\n tenantOverride: tenantId,\n global: globalFlag,\n includeDeleted,\n limit,\n offset,\n batchSize,\n supportsOrgFilter: supportsOrg,\n supportsTenantFilter: supportsTenant,\n supportsDeletedFilter: supportsDeleted,\n })\n totalProcessed += result.processed\n if (result.matched === 0) {\n console.log(' -> no rows matched filters')\n } else {\n console.log(` -> processed ${result.processed} row(s)`)\n }\n }\n\n console.log(`Finished rebuilding all query indexes (processed ${totalProcessed} row(s))`)\n } catch (error) {\n await recordIndexerError(\n { em },\n {\n source: 'query_index',\n handler: 'cli:query_index.rebuild-all',\n error,\n tenantId,\n organizationId: orgId,\n payload: { args },\n },\n )\n throw error\n } finally {\n if (typeof (container as any)?.dispose === 'function') {\n await (container as any).dispose()\n }\n }\n },\n}\n\nconst reindex: ModuleCli = {\n command: 'reindex',\n async run(rest) {\n const args = parseArgs(rest)\n const entity = stringOption(args, 'entity', 'e')\n const orgId = stringOption(args, 'org', 'organizationId')\n const tenantId = stringOption(args, 'tenant', 'tenantId')\n const force = flagEnabled(args, 'force', 'full')\n const batchSize = toPositiveInt(numberOption(args, 'batch', 'chunk', 'size'))\n const partitionsOption = toPositiveInt(numberOption(args, 'partitions', 'partitionCount', 'parallel'))\n const partitionIndexOptionRaw = numberOption(args, 'partition', 'partitionIndex')\n const resetCoverageFlag = flagEnabled(args, 'resetCoverage')\n const skipResetCoverageFlag = flagEnabled(args, 'skipResetCoverage', 'noResetCoverage')\n const skipPurge = flagEnabled(args, 'skipPurge', 'noPurge')\n\n const container = await createRequestContainer()\n const baseEm = (container.resolve('em') as EntityManager)\n const partitionIndexOption =\n partitionIndexOptionRaw === undefined ? undefined : toNonNegativeInt(partitionIndexOptionRaw, 0)\n const partitionCount = Math.max(\n 1,\n partitionsOption ?? DEFAULT_REINDEX_PARTITIONS,\n )\n\n if (partitionIndexOption !== undefined && partitionIndexOption >= partitionCount) {\n console.error(`partitionIndex (${partitionIndexOption}) must be < partitionCount (${partitionCount})`)\n if (typeof (container as any)?.dispose === 'function') {\n await (container as any).dispose()\n }\n return\n }\n\n const partitionTargets =\n partitionIndexOption !== undefined\n ? [partitionIndexOption]\n : Array.from({ length: partitionCount }, (_, idx) => idx)\n\n const shouldResetCoverage = (partition: number): boolean => {\n if (resetCoverageFlag) return true\n if (skipResetCoverageFlag) return false\n if (partitionIndexOption !== undefined) return partitionIndexOption === 0\n return partition === partitionTargets[0]\n }\n\n try {\n if (entity) {\n await recordIndexerLog(\n { em: baseEm },\n {\n source: 'query_index',\n handler: 'cli:query_index.reindex',\n message: `Reindex started for ${entity}`,\n entityType: entity,\n tenantId: tenantId ?? null,\n organizationId: orgId ?? null,\n details: {\n force,\n partitions: partitionTargets.length,\n partitionCount,\n partitionIndex: partitionIndexOption ?? null,\n skipPurge,\n },\n },\n ).catch(() => undefined)\n if (!skipPurge) {\n console.log(`Purging existing index rows for ${entity}...`)\n await purgeIndexScope(baseEm, { entityType: entity, organizationId: orgId, tenantId })\n }\n console.log(`Reindexing ${entity}${force ? ' (forced)' : ''} in ${partitionTargets.length} partition(s)...`)\n const verbose = isIndexerVerbose()\n const progressState = verbose ? new Map<number, { last: number }>() : null\n const groupedProgress =\n !verbose && partitionTargets.length > 1\n ? createGroupedProgress(`Reindexing ${entity}`, partitionTargets)\n : null\n const renderProgress = (part: number, entityId: string, info: PartitionProgressInfo) => {\n if (!progressState) return\n const state = progressState.get(part) ?? { last: 0 }\n const now = Date.now()\n if (now - state.last < 1000 && info.processed < info.total) return\n state.last = now\n progressState.set(part, state)\n const percent = info.total > 0 ? ((info.processed / info.total) * 100).toFixed(2) : '0.00'\n console.log(\n ` [${entityId}] partition ${part + 1}/${partitionCount}: ${info.processed.toLocaleString()} / ${info.total.toLocaleString()} (${percent}%)`,\n )\n }\n\n const stats = await Promise.all(\n partitionTargets.map(async (part, idx) => {\n const label = partitionTargets.length > 1 ? ` [partition ${part + 1}/${partitionCount}]` : ''\n if (partitionTargets.length === 1) {\n console.log(` -> processing${label}`)\n } else if (verbose && idx === 0) {\n console.log(` -> processing partitions in parallel (count=${partitionTargets.length})`)\n }\n const partitionContainer = await createRequestContainer()\n const partitionEm = partitionContainer.resolve<EntityManager>('em')\n let partitionVectorService: VectorIndexService | null = null\n try {\n partitionVectorService = partitionContainer.resolve<VectorIndexService>('vectorIndexService')\n } catch {\n partitionVectorService = null\n }\n try {\n let progressBar: ProgressBarHandle | null = null\n const useBar = partitionTargets.length === 1\n const partitionStats = await reindexEntity(partitionEm, {\n entityType: entity,\n tenantId,\n organizationId: orgId,\n force,\n batchSize,\n emitVectorizeEvents: false,\n partitionCount,\n partitionIndex: part,\n resetCoverage: shouldResetCoverage(part),\n vectorService: partitionVectorService,\n onProgress(info) {\n if (useBar) {\n if (info.total > 0 && !progressBar) {\n progressBar = createProgressBar(\n `Reindexing ${entity}${label}`,\n info.total,\n ) as ProgressBarHandle\n }\n progressBar?.update(info.processed)\n } else if (groupedProgress) {\n groupedProgress.onProgress(part, info)\n } else {\n renderProgress(part, entity, info)\n }\n },\n })\n if (progressBar) {\n (progressBar as ProgressBarHandle).complete()\n }\n if (!useBar && groupedProgress) {\n groupedProgress.onProgress(part, { processed: partitionStats.processed, total: partitionStats.total })\n } else if (!useBar) {\n renderProgress(part, entity, { processed: partitionStats.processed, total: partitionStats.total })\n } else {\n console.log(\n ` processed ${partitionStats.processed} row(s)${partitionStats.total ? ` (base ${partitionStats.total})` : ''}`,\n )\n }\n return partitionStats.processed\n } finally {\n if (typeof (partitionContainer as any)?.dispose === 'function') {\n await (partitionContainer as any).dispose()\n }\n }\n }),\n )\n groupedProgress?.complete()\n const totalProcessed = stats.reduce((acc, value) => acc + value, 0)\n console.log(`Finished ${entity}: processed ${totalProcessed} row(s) across ${partitionTargets.length} partition(s)`)\n await recordIndexerLog(\n { em: baseEm },\n {\n source: 'query_index',\n handler: 'cli:query_index.reindex',\n message: `Reindex completed for ${entity}`,\n entityType: entity,\n tenantId: tenantId ?? null,\n organizationId: orgId ?? null,\n details: {\n processed: totalProcessed,\n partitions: partitionTargets.length,\n partitionCount,\n partitionIndex: partitionIndexOption ?? null,\n },\n },\n ).catch(() => undefined)\n return\n }\n\n const { getEntityIds } = await import('@open-mercato/shared/lib/encryption/entityIds')\n const entityIds = flattenSystemEntityIds(getEntityIds() as Record<string, Record<string, string>>)\n if (!entityIds.length) {\n console.log('No entity definitions registered for query indexing.')\n return\n }\n for (let idx = 0; idx < entityIds.length; idx += 1) {\n const id = entityIds[idx]!\n await recordIndexerLog(\n { em: baseEm },\n {\n source: 'query_index',\n handler: 'cli:query_index.reindex',\n message: `Reindex started for ${id}`,\n entityType: id,\n tenantId: tenantId ?? null,\n organizationId: orgId ?? null,\n details: {\n force,\n partitions: partitionTargets.length,\n partitionCount,\n partitionIndex: partitionIndexOption ?? null,\n skipPurge,\n },\n },\n ).catch(() => undefined)\n if (!skipPurge) {\n console.log(`[${idx + 1}/${entityIds.length}] Purging existing index rows for ${id}...`)\n await purgeIndexScope(baseEm, { entityType: id, organizationId: orgId, tenantId })\n }\n console.log(\n `[${idx + 1}/${entityIds.length}] Reindexing ${id}${force ? ' (forced)' : ''} in ${partitionTargets.length} partition(s)...`,\n )\n const verbose = isIndexerVerbose()\n const progressState = verbose ? new Map<number, { last: number }>() : null\n const groupedProgress =\n !verbose && partitionTargets.length > 1\n ? createGroupedProgress(`Reindexing ${id}`, partitionTargets)\n : null\n const renderProgress = (part: number, entityId: string, info: PartitionProgressInfo) => {\n if (!progressState) return\n const state = progressState.get(part) ?? { last: 0 }\n const now = Date.now()\n if (now - state.last < 1000 && info.processed < info.total) return\n state.last = now\n progressState.set(part, state)\n const percent = info.total > 0 ? ((info.processed / info.total) * 100).toFixed(2) : '0.00'\n console.log(\n ` [${entityId}] partition ${part + 1}/${partitionCount}: ${info.processed.toLocaleString()} / ${info.total.toLocaleString()} (${percent}%)`,\n )\n }\n\n const partitionResults = await Promise.all(\n partitionTargets.map(async (part, partitionIdx) => {\n const label = partitionTargets.length > 1 ? ` [partition ${part + 1}/${partitionCount}]` : ''\n if (partitionTargets.length === 1) {\n console.log(` -> processing${label}`)\n } else if (verbose && partitionIdx === 0) {\n console.log(` -> processing partitions in parallel (count=${partitionTargets.length})`)\n }\n const partitionContainer = await createRequestContainer()\n const partitionEm = partitionContainer.resolve<EntityManager>('em')\n let partitionVectorService: VectorIndexService | null = null\n try {\n partitionVectorService = partitionContainer.resolve<VectorIndexService>('vectorIndexService')\n } catch {\n partitionVectorService = null\n }\n try {\n let progressBar: ProgressBarHandle | null = null\n const useBar = partitionTargets.length === 1\n const result = await reindexEntity(partitionEm, {\n entityType: id,\n tenantId,\n organizationId: orgId,\n force,\n batchSize,\n emitVectorizeEvents: false,\n partitionCount,\n partitionIndex: part,\n resetCoverage: shouldResetCoverage(part),\n vectorService: partitionVectorService,\n onProgress(info) {\n if (useBar) {\n if (info.total > 0 && !progressBar) {\n progressBar = createProgressBar(`Reindexing ${id}${label}`, info.total) as ProgressBarHandle\n }\n progressBar?.update(info.processed)\n } else if (groupedProgress) {\n groupedProgress.onProgress(part, info)\n } else {\n renderProgress(part, id, info)\n }\n },\n })\n if (progressBar) {\n (progressBar as ProgressBarHandle).complete()\n }\n if (!useBar && groupedProgress) {\n groupedProgress.onProgress(part, { processed: result.processed, total: result.total })\n } else if (!useBar) {\n renderProgress(part, id, { processed: result.processed, total: result.total })\n } else {\n console.log(\n ` processed ${result.processed} row(s)${result.total ? ` (base ${result.total})` : ''}`,\n )\n }\n return result.processed\n } finally {\n if (typeof (partitionContainer as any)?.dispose === 'function') {\n await (partitionContainer as any).dispose()\n }\n }\n }),\n )\n groupedProgress?.complete()\n const totalProcessed = partitionResults.reduce((acc, value) => acc + value, 0)\n console.log(` -> ${id} complete: processed ${totalProcessed} row(s) across ${partitionTargets.length} partition(s)`)\n await recordIndexerLog(\n { em: baseEm },\n {\n source: 'query_index',\n handler: 'cli:query_index.reindex',\n message: `Reindex completed for ${id}`,\n entityType: id,\n tenantId: tenantId ?? null,\n organizationId: orgId ?? null,\n details: {\n processed: totalProcessed,\n partitions: partitionTargets.length,\n partitionCount,\n partitionIndex: partitionIndexOption ?? null,\n },\n },\n ).catch(() => undefined)\n }\n console.log(`Finished reindexing ${entityIds.length} entities`)\n } catch (error) {\n const targetLabel = entity ?? 'multiple entities'\n await recordIndexerLog(\n { em: baseEm },\n {\n source: 'query_index',\n handler: 'cli:query_index.reindex',\n level: 'warn',\n message: `Reindex failed for ${targetLabel}`,\n entityType: entity ?? null,\n tenantId: tenantId ?? null,\n organizationId: orgId ?? null,\n details: {\n error: error instanceof Error ? error.message : String(error),\n },\n },\n ).catch(() => undefined)\n await recordIndexerError(\n { em: baseEm },\n {\n source: 'query_index',\n handler: 'cli:query_index.reindex',\n error,\n entityType: entity ?? null,\n tenantId,\n organizationId: orgId ?? null,\n payload: {\n args,\n partitionTargets,\n partitionCount,\n partitionIndex: partitionIndexOption,\n force,\n skipPurge,\n },\n },\n )\n throw error\n } finally {\n if (typeof (container as any)?.dispose === 'function') {\n await (container as any).dispose()\n }\n }\n },\n}\n\nconst purge: ModuleCli = {\n command: 'purge',\n async run(rest) {\n const args = parseArgs(rest)\n const entity = stringOption(args, 'entity', 'e')\n const orgId = stringOption(args, 'org', 'organizationId')\n const tenantId = stringOption(args, 'tenant', 'tenantId')\n\n const container = await createRequestContainer()\n let em: EntityManager | null = null\n try {\n em = (container.resolve('em') as EntityManager)\n } catch {\n em = null\n }\n\n try {\n const bus = container.resolve('eventBus') as {\n emitEvent(event: string, payload: any, options?: any): Promise<void>\n }\n if (entity) {\n await bus.emitEvent(\n 'query_index.purge',\n { entityType: entity, organizationId: orgId, tenantId },\n { persistent: true },\n )\n await recordIndexerLog(\n { em: em ?? undefined },\n {\n source: 'query_index',\n handler: 'cli:query_index.purge',\n message: `Purge requested for ${entity}`,\n entityType: entity,\n tenantId: tenantId ?? null,\n organizationId: orgId ?? null,\n },\n ).catch(() => undefined)\n console.log(`Scheduled purge for ${entity}`)\n return\n }\n\n const { getEntityIds } = await import('@open-mercato/shared/lib/encryption/entityIds')\n const entityIds = flattenSystemEntityIds(getEntityIds() as Record<string, Record<string, string>>)\n for (const id of entityIds) {\n await bus.emitEvent(\n 'query_index.purge',\n { entityType: id, organizationId: orgId, tenantId },\n { persistent: true },\n )\n await recordIndexerLog(\n { em: em ?? undefined },\n {\n source: 'query_index',\n handler: 'cli:query_index.purge',\n message: `Purge requested for ${id}`,\n entityType: id,\n tenantId: tenantId ?? null,\n organizationId: orgId ?? null,\n details: { mode: 'bulk' },\n },\n ).catch(() => undefined)\n }\n console.log(`Scheduled purge for ${entityIds.length} entities`)\n } catch (error) {\n await recordIndexerLog(\n { em: em ?? undefined },\n {\n source: 'query_index',\n handler: 'cli:query_index.purge',\n level: 'warn',\n message: `Purge scheduling failed${entity ? ` for ${entity}` : ''}`,\n entityType: entity ?? null,\n tenantId: tenantId ?? null,\n organizationId: orgId ?? null,\n details: { error: error instanceof Error ? error.message : String(error) },\n },\n ).catch(() => undefined)\n await recordIndexerError(\n { em: em ?? undefined },\n {\n source: 'query_index',\n handler: 'cli:query_index.purge',\n error,\n entityType: entity ?? null,\n tenantId,\n organizationId: orgId,\n payload: { args },\n },\n )\n throw error\n } finally {\n if (typeof (container as any)?.dispose === 'function') {\n await (container as any).dispose()\n }\n }\n },\n}\n\nexport default [rebuild, rebuildAll, reindex, purge]\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["bar"]
|
|
7
7
|
}
|
|
@@ -38,7 +38,7 @@ const seedDemo = {
|
|
|
38
38
|
organizationId
|
|
39
39
|
});
|
|
40
40
|
if (existing) {
|
|
41
|
-
console.log(`\
|
|
41
|
+
console.log(`\u2139\uFE0F Demo workflow '${demoData.workflowId}' already exists (ID: ${existing.id})`);
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
44
|
const workflow = em.create(WorkflowDefinition, {
|
|
@@ -47,7 +47,7 @@ const seedDemo = {
|
|
|
47
47
|
organizationId
|
|
48
48
|
});
|
|
49
49
|
await em.persistAndFlush(workflow);
|
|
50
|
-
console.log(`\
|
|
50
|
+
console.log(`\u2705 Seeded demo workflow: ${workflow.workflowName}`);
|
|
51
51
|
console.log(` - ID: ${workflow.id}`);
|
|
52
52
|
console.log(` - Workflow ID: ${workflow.workflowId}`);
|
|
53
53
|
console.log(` - Version: ${workflow.version}`);
|
|
@@ -79,11 +79,11 @@ const seedDemoWithRules = {
|
|
|
79
79
|
console.error(" or: mercato workflows seed-demo-with-rules -t <tenantId> -o <organizationId>");
|
|
80
80
|
return;
|
|
81
81
|
}
|
|
82
|
-
console.log("Seeding demo workflow with guard rules...\n");
|
|
82
|
+
console.log("\u{1F9E9} Seeding demo workflow with guard rules...\n");
|
|
83
83
|
try {
|
|
84
|
-
console.log("1. Seeding demo workflow...");
|
|
84
|
+
console.log("1. \u{1F9E9} Seeding demo workflow...");
|
|
85
85
|
await seedDemo.run(rest);
|
|
86
|
-
console.log("\n2. Seeding guard rules...");
|
|
86
|
+
console.log("\n2. \u{1F9E0} Seeding guard rules...");
|
|
87
87
|
const { resolve } = await createRequestContainer();
|
|
88
88
|
const em = resolve("em");
|
|
89
89
|
const { BusinessRule } = await import("../business_rules/data/entities.js");
|
|
@@ -112,7 +112,7 @@ const seedDemoWithRules = {
|
|
|
112
112
|
seededCount++;
|
|
113
113
|
}
|
|
114
114
|
console.log(`
|
|
115
|
-
\
|
|
115
|
+
\u2705 Demo workflow with guard rules seeded successfully!`);
|
|
116
116
|
console.log(` - Workflow: checkout_simple_v1`);
|
|
117
117
|
console.log(` - Guard rules seeded: ${seededCount}`);
|
|
118
118
|
console.log(` - Guard rules skipped: ${skippedCount}`);
|
|
@@ -143,7 +143,7 @@ const seedSalesPipeline = {
|
|
|
143
143
|
organizationId
|
|
144
144
|
});
|
|
145
145
|
if (existing) {
|
|
146
|
-
console.log(`\
|
|
146
|
+
console.log(`\u2139\uFE0F Sales pipeline workflow '${pipelineData.workflowId}' already exists (ID: ${existing.id})`);
|
|
147
147
|
return;
|
|
148
148
|
}
|
|
149
149
|
const workflow = em.create(WorkflowDefinition, {
|
|
@@ -152,7 +152,7 @@ const seedSalesPipeline = {
|
|
|
152
152
|
organizationId
|
|
153
153
|
});
|
|
154
154
|
await em.persistAndFlush(workflow);
|
|
155
|
-
console.log(`\
|
|
155
|
+
console.log(`\u2705 Seeded sales pipeline workflow: ${workflow.workflowName}`);
|
|
156
156
|
console.log(` - ID: ${workflow.id}`);
|
|
157
157
|
console.log(` - Workflow ID: ${workflow.workflowId}`);
|
|
158
158
|
console.log(` - Version: ${workflow.version}`);
|
|
@@ -188,7 +188,7 @@ const seedSimpleApproval = {
|
|
|
188
188
|
organizationId
|
|
189
189
|
});
|
|
190
190
|
if (existing) {
|
|
191
|
-
console.log(`\
|
|
191
|
+
console.log(`\u2139\uFE0F Simple approval workflow '${approvalData.workflowId}' already exists (ID: ${existing.id})`);
|
|
192
192
|
return;
|
|
193
193
|
}
|
|
194
194
|
const workflow = em.create(WorkflowDefinition, {
|
|
@@ -197,7 +197,7 @@ const seedSimpleApproval = {
|
|
|
197
197
|
organizationId
|
|
198
198
|
});
|
|
199
199
|
await em.persistAndFlush(workflow);
|
|
200
|
-
console.log(`\
|
|
200
|
+
console.log(`\u2705 Seeded simple approval workflow: ${workflow.workflowName}`);
|
|
201
201
|
console.log(` - ID: ${workflow.id}`);
|
|
202
202
|
console.log(` - Workflow ID: ${workflow.workflowId}`);
|
|
203
203
|
console.log(` - Version: ${workflow.version}`);
|
|
@@ -260,7 +260,7 @@ const seedAll = {
|
|
|
260
260
|
console.error("Usage: mercato workflows seed-all --tenant <tenantId> --org <organizationId>");
|
|
261
261
|
return;
|
|
262
262
|
}
|
|
263
|
-
console.log("Seeding all example workflows...\n");
|
|
263
|
+
console.log("\u{1F9E9} Seeding all example workflows...\n");
|
|
264
264
|
try {
|
|
265
265
|
await seedDemoWithRules.run(rest);
|
|
266
266
|
console.log("");
|
|
@@ -268,7 +268,7 @@ const seedAll = {
|
|
|
268
268
|
console.log("");
|
|
269
269
|
await seedSimpleApproval.run(rest);
|
|
270
270
|
console.log("");
|
|
271
|
-
console.log("\
|
|
271
|
+
console.log("\u2705 All example workflows seeded successfully!");
|
|
272
272
|
} catch (error) {
|
|
273
273
|
console.error("Error seeding workflows:", error);
|
|
274
274
|
throw error;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/workflows/cli.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { WorkflowDefinition } from './data/entities'\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { fileURLToPath } from 'url'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = path.dirname(__filename)\n\n/**\n * Parse CLI arguments\n */\nfunction parseArgs(args: string[]) {\n const result: Record<string, string> = {}\n for (let i = 0; i < args.length; i += 2) {\n const key = args[i]?.replace(/^-+/, '') // Remove one or more dashes\n const value = args[i + 1]\n if (key && value) {\n result[key] = value\n }\n }\n return result\n}\n\n/**\n * Seed demo checkout workflow\n */\nconst seedDemo: ModuleCli = {\n command: 'seed-demo',\n async run(rest: string[]) {\n const args = parseArgs(rest)\n const tenantId = String(args.tenantId ?? args.tenant ?? args.t ?? '')\n const organizationId = String(args.organizationId ?? args.orgId ?? args.org ?? args.o ?? '')\n\n if (!tenantId || !organizationId) {\n console.error('Usage: mercato workflows seed-demo --tenant <tenantId> --org <organizationId>')\n console.error(' or: mercato workflows seed-demo -t <tenantId> -o <organizationId>')\n return\n }\n\n try {\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n\n // Read the demo workflow definition\n const demoPath = path.join(__dirname, 'examples', 'checkout-demo-definition.json')\n const demoData = JSON.parse(fs.readFileSync(demoPath, 'utf8'))\n\n // Check if it already exists\n const existing = await em.findOne(WorkflowDefinition, {\n workflowId: demoData.workflowId,\n tenantId,\n organizationId,\n })\n\n if (existing) {\n console.log(`\u2713 Demo workflow '${demoData.workflowId}' already exists (ID: ${existing.id})`)\n return\n }\n\n // Create the workflow definition\n const workflow = em.create(WorkflowDefinition, {\n ...demoData,\n tenantId,\n organizationId,\n })\n\n await em.persistAndFlush(workflow)\n\n console.log(`\u2713 Seeded demo workflow: ${workflow.workflowName}`)\n console.log(` - ID: ${workflow.id}`)\n console.log(` - Workflow ID: ${workflow.workflowId}`)\n console.log(` - Version: ${workflow.version}`)\n console.log(` - Steps: ${workflow.definition.steps.length}`)\n console.log(` - Transitions: ${workflow.definition.transitions.length}`)\n console.log('')\n console.log('Demo workflow is ready! You can now:')\n console.log(' 1. View it in admin: /backend/definitions')\n console.log(' 2. Try the demo page: /checkout-demo')\n console.log(' 3. Start an instance via API: POST /api/workflows/instances')\n console.log('')\n console.log('Note: This workflow includes a USER_TASK step for customer information.')\n console.log('When the workflow reaches this step, it will pause and require user input.')\n console.log('Complete pending tasks at: /backend/tasks')\n } catch (error) {\n console.error('Error seeding demo workflow:', error)\n throw error\n }\n },\n}\n\n/**\n * Seed demo checkout workflow with guard rules\n */\nconst seedDemoWithRules: ModuleCli = {\n command: 'seed-demo-with-rules',\n async run(rest: string[]) {\n const args = parseArgs(rest)\n const tenantId = String(args.tenantId ?? args.tenant ?? args.t ?? '')\n const organizationId = String(args.organizationId ?? args.orgId ?? args.org ?? args.o ?? '')\n\n if (!tenantId || !organizationId) {\n console.error('Usage: mercato workflows seed-demo-with-rules --tenant <tenantId> --org <organizationId>')\n console.error(' or: mercato workflows seed-demo-with-rules -t <tenantId> -o <organizationId>')\n return\n }\n\n console.log('Seeding demo workflow with guard rules...\\n')\n\n try {\n // Seed the workflow definition\n console.log('1. Seeding demo workflow...')\n await seedDemo.run(rest)\n\n // Seed the guard rules\n console.log('\\n2. Seeding guard rules...')\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n\n // Import BusinessRule entity\n const { BusinessRule } = await import('../business_rules/data/entities')\n\n // Read guard rules\n const rulesPath = path.join(__dirname, 'examples', 'guard-rules-example.json')\n const rulesData = JSON.parse(fs.readFileSync(rulesPath, 'utf8'))\n\n let seededCount = 0\n let skippedCount = 0\n\n for (const ruleData of rulesData) {\n const existing = await em.findOne(BusinessRule, {\n ruleId: ruleData.ruleId,\n tenantId,\n organizationId,\n })\n\n if (existing) {\n console.log(` \u2298 Guard rule '${ruleData.ruleId}' already exists`)\n skippedCount++\n continue\n }\n\n const rule = em.create(BusinessRule, {\n ...ruleData,\n tenantId,\n organizationId,\n })\n\n await em.persistAndFlush(rule)\n console.log(` \u2713 Seeded guard rule: ${rule.ruleName}`)\n seededCount++\n }\n\n console.log(`\\n\u2713 Demo workflow with guard rules seeded successfully!`)\n console.log(` - Workflow: checkout_simple_v1`)\n console.log(` - Guard rules seeded: ${seededCount}`)\n console.log(` - Guard rules skipped: ${skippedCount}`)\n } catch (error) {\n console.error('Error seeding demo with rules:', error)\n throw error\n }\n },\n}\n\n/**\n * Seed sales pipeline example\n */\nconst seedSalesPipeline: ModuleCli = {\n command: 'seed-sales-pipeline',\n async run(rest: string[]) {\n const args = parseArgs(rest)\n const tenantId = String(args.tenantId ?? args.tenant ?? args.t ?? '')\n const organizationId = String(args.organizationId ?? args.orgId ?? args.org ?? args.o ?? '')\n\n if (!tenantId || !organizationId) {\n console.error('Usage: mercato workflows seed-sales-pipeline --tenant <tenantId> --org <organizationId>')\n return\n }\n\n try {\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n\n // Read the sales pipeline workflow definition\n const pipelinePath = path.join(__dirname, 'examples', 'sales-pipeline-definition.json')\n const pipelineData = JSON.parse(fs.readFileSync(pipelinePath, 'utf8'))\n\n // Check if it already exists\n const existing = await em.findOne(WorkflowDefinition, {\n workflowId: pipelineData.workflowId,\n tenantId,\n organizationId,\n })\n\n if (existing) {\n console.log(`\u2713 Sales pipeline workflow '${pipelineData.workflowId}' already exists (ID: ${existing.id})`)\n return\n }\n\n // Create the workflow definition\n const workflow = em.create(WorkflowDefinition, {\n ...pipelineData,\n tenantId,\n organizationId,\n })\n\n await em.persistAndFlush(workflow)\n\n console.log(`\u2713 Seeded sales pipeline workflow: ${workflow.workflowName}`)\n console.log(` - ID: ${workflow.id}`)\n console.log(` - Workflow ID: ${workflow.workflowId}`)\n console.log(` - Version: ${workflow.version}`)\n console.log(` - Steps: ${workflow.definition.steps.length}`)\n console.log(` - Transitions: ${workflow.definition.transitions.length}`)\n console.log(` - Activities: ${workflow.definition.transitions.reduce((sum, t) => sum + (t.activities?.length || 0), 0)}`)\n console.log('')\n console.log('Sales pipeline workflow is ready!')\n } catch (error) {\n console.error('Error seeding sales pipeline workflow:', error)\n throw error\n }\n },\n}\n\n/**\n * Seed simple approval example\n */\nconst seedSimpleApproval: ModuleCli = {\n command: 'seed-simple-approval',\n async run(rest: string[]) {\n const args = parseArgs(rest)\n const tenantId = String(args.tenantId ?? args.tenant ?? args.t ?? '')\n const organizationId = String(args.organizationId ?? args.orgId ?? args.org ?? args.o ?? '')\n\n if (!tenantId || !organizationId) {\n console.error('Usage: mercato workflows seed-simple-approval --tenant <tenantId> --org <organizationId>')\n return\n }\n\n try {\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n\n // Read the simple approval workflow definition\n const approvalPath = path.join(__dirname, 'examples', 'simple-approval-definition.json')\n const approvalData = JSON.parse(fs.readFileSync(approvalPath, 'utf8'))\n\n // Check if it already exists\n const existing = await em.findOne(WorkflowDefinition, {\n workflowId: approvalData.workflowId,\n tenantId,\n organizationId,\n })\n\n if (existing) {\n console.log(`\u2713 Simple approval workflow '${approvalData.workflowId}' already exists (ID: ${existing.id})`)\n return\n }\n\n // Create the workflow definition\n const workflow = em.create(WorkflowDefinition, {\n ...approvalData,\n tenantId,\n organizationId,\n })\n\n await em.persistAndFlush(workflow)\n\n console.log(`\u2713 Seeded simple approval workflow: ${workflow.workflowName}`)\n console.log(` - ID: ${workflow.id}`)\n console.log(` - Workflow ID: ${workflow.workflowId}`)\n console.log(` - Version: ${workflow.version}`)\n console.log(` - Steps: ${workflow.definition.steps.length}`)\n console.log(` - Transitions: ${workflow.definition.transitions.length}`)\n console.log('')\n console.log('Simple approval workflow is ready!')\n } catch (error) {\n console.error('Error seeding simple approval workflow:', error)\n throw error\n }\n },\n}\n\n/**\n * Start workflow activity worker\n */\nconst startWorker: ModuleCli = {\n command: 'start-worker',\n async run(rest: string[]) {\n const args = parseArgs(rest)\n const concurrency = parseInt(args.concurrency ?? args.c ?? '5')\n\n const strategy = process.env.QUEUE_STRATEGY === 'async' ? 'async' : 'local'\n\n console.log('[Workflow Worker] Starting activity worker...')\n console.log(`[Workflow Worker] Strategy: ${strategy}`)\n\n if (strategy === 'local') {\n const pollMs = process.env.LOCAL_QUEUE_POLL_MS || '5000'\n console.log(`[Workflow Worker] Polling interval: ${pollMs}ms`)\n console.log('[Workflow Worker] NOTE: Local strategy is for development only.')\n console.log('[Workflow Worker] Use QUEUE_STRATEGY=async with Redis for production.')\n } else {\n console.log(`[Workflow Worker] Concurrency: ${concurrency}`)\n console.log(`[Workflow Worker] Redis: ${process.env.REDIS_URL || process.env.QUEUE_REDIS_URL}`)\n }\n\n try {\n const container = await createRequestContainer()\n const em = container.resolve<EntityManager>('em')\n\n // Import queue and handler\n const { runWorker } = await import('@open-mercato/queue/worker')\n const { createActivityWorkerHandler } = await import('./lib/activity-worker-handler')\n const { WORKFLOW_ACTIVITIES_QUEUE_NAME } = await import('./lib/activity-queue-types')\n\n // Create handler\n const handler = createActivityWorkerHandler(em, container)\n\n // Run worker\n await runWorker({\n queueName: WORKFLOW_ACTIVITIES_QUEUE_NAME,\n handler,\n connection: strategy === 'async' ? {\n url: process.env.REDIS_URL || process.env.QUEUE_REDIS_URL,\n } : undefined,\n concurrency,\n gracefulShutdown: true,\n })\n } catch (error) {\n console.error('[Workflow Worker] Failed to start worker:', error)\n throw error\n }\n },\n}\n\n/**\n * Seed all example workflows\n */\nconst seedAll: ModuleCli = {\n command: 'seed-all',\n async run(rest: string[]) {\n const args = parseArgs(rest)\n const tenantId = String(args.tenantId ?? args.tenant ?? args.t ?? '')\n const organizationId = String(args.organizationId ?? args.orgId ?? args.org ?? args.o ?? '')\n\n if (!tenantId || !organizationId) {\n console.error('Usage: mercato workflows seed-all --tenant <tenantId> --org <organizationId>')\n return\n }\n\n console.log('Seeding all example workflows...\\n')\n\n try {\n // Seed demo checkout with rules\n await seedDemoWithRules.run(rest)\n console.log('')\n\n // Seed sales pipeline\n await seedSalesPipeline.run(rest)\n console.log('')\n\n // Seed simple approval\n await seedSimpleApproval.run(rest)\n console.log('')\n\n console.log('\u2713 All example workflows seeded successfully!')\n } catch (error) {\n console.error('Error seeding workflows:', error)\n throw error\n }\n },\n}\n\n/**\n * Manually process pending workflow activities\n */\nconst processActivities: ModuleCli = {\n command: 'process-activities',\n async run(rest: string[]) {\n const args = parseArgs(rest)\n const limit = parseInt(args.limit ?? args.l ?? '0')\n\n console.log('[Workflow Activities] Processing pending activities...')\n if (limit > 0) {\n console.log(`[Workflow Activities] Limit: ${limit} jobs`)\n }\n\n try {\n const container = await createRequestContainer()\n const em = container.resolve<EntityManager>('em')\n\n // Import queue and handler\n const { createQueue } = await import('@open-mercato/queue')\n const { createActivityWorkerHandler } = await import('./lib/activity-worker-handler')\n const { WORKFLOW_ACTIVITIES_QUEUE_NAME } = await import('./lib/activity-queue-types')\n\n // Create queue instance\n const queue = createQueue(WORKFLOW_ACTIVITIES_QUEUE_NAME, 'local', {\n baseDir: process.env.QUEUE_BASE_DIR || '.queue',\n })\n\n // Create handler\n const handler = createActivityWorkerHandler(em, container)\n\n // Get initial counts\n const initialCounts = await queue.getJobCounts()\n console.log(`[Workflow Activities] Pending jobs: ${initialCounts.waiting}`)\n\n if (initialCounts.waiting === 0) {\n console.log('[Workflow Activities] No jobs to process')\n await queue.close()\n return\n }\n\n // Process jobs\n const result = await queue.process(handler as any, limit > 0 ? { limit } : undefined)\n\n console.log(`\\n[Workflow Activities] \u2713 Processed ${result.processed} activities`)\n if (result.failed > 0) {\n console.log(`[Workflow Activities] \u2717 Failed: ${result.failed} activities`)\n }\n\n // Show remaining\n const finalCounts = await queue.getJobCounts()\n if (finalCounts.waiting > 0) {\n console.log(`[Workflow Activities] Remaining: ${finalCounts.waiting} jobs`)\n }\n\n await queue.close()\n } catch (error) {\n console.error('[Workflow Activities] Error processing activities:', error)\n throw error\n }\n },\n}\n\nconst workflowsCliCommands = [\n startWorker,\n processActivities,\n seedDemo,\n seedDemoWithRules,\n seedSalesPipeline,\n seedSimpleApproval,\n seedAll,\n]\n\nexport default workflowsCliCommands\n"],
|
|
5
|
-
"mappings": "AACA,SAAS,8BAA8B;AAEvC,SAAS,0BAA0B;AACnC,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,SAAS,qBAAqB;AAE9B,MAAM,aAAa,cAAc,YAAY,GAAG;AAChD,MAAM,YAAY,KAAK,QAAQ,UAAU;AAKzC,SAAS,UAAU,MAAgB;AACjC,QAAM,SAAiC,CAAC;AACxC,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,UAAM,MAAM,KAAK,CAAC,GAAG,QAAQ,OAAO,EAAE;AACtC,UAAM,QAAQ,KAAK,IAAI,CAAC;AACxB,QAAI,OAAO,OAAO;AAChB,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AACA,SAAO;AACT;AAKA,MAAM,WAAsB;AAAA,EAC1B,SAAS;AAAA,EACT,MAAM,IAAI,MAAgB;AACxB,UAAM,OAAO,UAAU,IAAI;AAC3B,UAAM,WAAW,OAAO,KAAK,YAAY,KAAK,UAAU,KAAK,KAAK,EAAE;AACpE,UAAM,iBAAiB,OAAO,KAAK,kBAAkB,KAAK,SAAS,KAAK,OAAO,KAAK,KAAK,EAAE;AAE3F,QAAI,CAAC,YAAY,CAAC,gBAAgB;AAChC,cAAQ,MAAM,+EAA+E;AAC7F,cAAQ,MAAM,sEAAsE;AACpF;AAAA,IACF;AAEA,QAAI;AACF,YAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,YAAM,KAAK,QAAuB,IAAI;AAGtC,YAAM,WAAW,KAAK,KAAK,WAAW,YAAY,+BAA+B;AACjF,YAAM,WAAW,KAAK,MAAM,GAAG,aAAa,UAAU,MAAM,CAAC;AAG7D,YAAM,WAAW,MAAM,GAAG,QAAQ,oBAAoB;AAAA,QACpD,YAAY,SAAS;AAAA,QACrB;AAAA,QACA;AAAA,MACF,CAAC;AAED,UAAI,UAAU;AACZ,gBAAQ,IAAI,
|
|
4
|
+
"sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { WorkflowDefinition } from './data/entities'\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { fileURLToPath } from 'url'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = path.dirname(__filename)\n\n/**\n * Parse CLI arguments\n */\nfunction parseArgs(args: string[]) {\n const result: Record<string, string> = {}\n for (let i = 0; i < args.length; i += 2) {\n const key = args[i]?.replace(/^-+/, '') // Remove one or more dashes\n const value = args[i + 1]\n if (key && value) {\n result[key] = value\n }\n }\n return result\n}\n\n/**\n * Seed demo checkout workflow\n */\nconst seedDemo: ModuleCli = {\n command: 'seed-demo',\n async run(rest: string[]) {\n const args = parseArgs(rest)\n const tenantId = String(args.tenantId ?? args.tenant ?? args.t ?? '')\n const organizationId = String(args.organizationId ?? args.orgId ?? args.org ?? args.o ?? '')\n\n if (!tenantId || !organizationId) {\n console.error('Usage: mercato workflows seed-demo --tenant <tenantId> --org <organizationId>')\n console.error(' or: mercato workflows seed-demo -t <tenantId> -o <organizationId>')\n return\n }\n\n try {\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n\n // Read the demo workflow definition\n const demoPath = path.join(__dirname, 'examples', 'checkout-demo-definition.json')\n const demoData = JSON.parse(fs.readFileSync(demoPath, 'utf8'))\n\n // Check if it already exists\n const existing = await em.findOne(WorkflowDefinition, {\n workflowId: demoData.workflowId,\n tenantId,\n organizationId,\n })\n\n if (existing) {\n console.log(`\u2139\uFE0F Demo workflow '${demoData.workflowId}' already exists (ID: ${existing.id})`)\n return\n }\n\n // Create the workflow definition\n const workflow = em.create(WorkflowDefinition, {\n ...demoData,\n tenantId,\n organizationId,\n })\n\n await em.persistAndFlush(workflow)\n\n console.log(`\u2705 Seeded demo workflow: ${workflow.workflowName}`)\n console.log(` - ID: ${workflow.id}`)\n console.log(` - Workflow ID: ${workflow.workflowId}`)\n console.log(` - Version: ${workflow.version}`)\n console.log(` - Steps: ${workflow.definition.steps.length}`)\n console.log(` - Transitions: ${workflow.definition.transitions.length}`)\n console.log('')\n console.log('Demo workflow is ready! You can now:')\n console.log(' 1. View it in admin: /backend/definitions')\n console.log(' 2. Try the demo page: /checkout-demo')\n console.log(' 3. Start an instance via API: POST /api/workflows/instances')\n console.log('')\n console.log('Note: This workflow includes a USER_TASK step for customer information.')\n console.log('When the workflow reaches this step, it will pause and require user input.')\n console.log('Complete pending tasks at: /backend/tasks')\n } catch (error) {\n console.error('Error seeding demo workflow:', error)\n throw error\n }\n },\n}\n\n/**\n * Seed demo checkout workflow with guard rules\n */\nconst seedDemoWithRules: ModuleCli = {\n command: 'seed-demo-with-rules',\n async run(rest: string[]) {\n const args = parseArgs(rest)\n const tenantId = String(args.tenantId ?? args.tenant ?? args.t ?? '')\n const organizationId = String(args.organizationId ?? args.orgId ?? args.org ?? args.o ?? '')\n\n if (!tenantId || !organizationId) {\n console.error('Usage: mercato workflows seed-demo-with-rules --tenant <tenantId> --org <organizationId>')\n console.error(' or: mercato workflows seed-demo-with-rules -t <tenantId> -o <organizationId>')\n return\n }\n\n console.log('\uD83E\uDDE9 Seeding demo workflow with guard rules...\\n')\n\n try {\n // Seed the workflow definition\n console.log('1. \uD83E\uDDE9 Seeding demo workflow...')\n await seedDemo.run(rest)\n\n // Seed the guard rules\n console.log('\\n2. \uD83E\uDDE0 Seeding guard rules...')\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n\n // Import BusinessRule entity\n const { BusinessRule } = await import('../business_rules/data/entities')\n\n // Read guard rules\n const rulesPath = path.join(__dirname, 'examples', 'guard-rules-example.json')\n const rulesData = JSON.parse(fs.readFileSync(rulesPath, 'utf8'))\n\n let seededCount = 0\n let skippedCount = 0\n\n for (const ruleData of rulesData) {\n const existing = await em.findOne(BusinessRule, {\n ruleId: ruleData.ruleId,\n tenantId,\n organizationId,\n })\n\n if (existing) {\n console.log(` \u2298 Guard rule '${ruleData.ruleId}' already exists`)\n skippedCount++\n continue\n }\n\n const rule = em.create(BusinessRule, {\n ...ruleData,\n tenantId,\n organizationId,\n })\n\n await em.persistAndFlush(rule)\n console.log(` \u2713 Seeded guard rule: ${rule.ruleName}`)\n seededCount++\n }\n\n console.log(`\\n\u2705 Demo workflow with guard rules seeded successfully!`)\n console.log(` - Workflow: checkout_simple_v1`)\n console.log(` - Guard rules seeded: ${seededCount}`)\n console.log(` - Guard rules skipped: ${skippedCount}`)\n } catch (error) {\n console.error('Error seeding demo with rules:', error)\n throw error\n }\n },\n}\n\n/**\n * Seed sales pipeline example\n */\nconst seedSalesPipeline: ModuleCli = {\n command: 'seed-sales-pipeline',\n async run(rest: string[]) {\n const args = parseArgs(rest)\n const tenantId = String(args.tenantId ?? args.tenant ?? args.t ?? '')\n const organizationId = String(args.organizationId ?? args.orgId ?? args.org ?? args.o ?? '')\n\n if (!tenantId || !organizationId) {\n console.error('Usage: mercato workflows seed-sales-pipeline --tenant <tenantId> --org <organizationId>')\n return\n }\n\n try {\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n\n // Read the sales pipeline workflow definition\n const pipelinePath = path.join(__dirname, 'examples', 'sales-pipeline-definition.json')\n const pipelineData = JSON.parse(fs.readFileSync(pipelinePath, 'utf8'))\n\n // Check if it already exists\n const existing = await em.findOne(WorkflowDefinition, {\n workflowId: pipelineData.workflowId,\n tenantId,\n organizationId,\n })\n\n if (existing) {\n console.log(`\u2139\uFE0F Sales pipeline workflow '${pipelineData.workflowId}' already exists (ID: ${existing.id})`)\n return\n }\n\n // Create the workflow definition\n const workflow = em.create(WorkflowDefinition, {\n ...pipelineData,\n tenantId,\n organizationId,\n })\n\n await em.persistAndFlush(workflow)\n\n console.log(`\u2705 Seeded sales pipeline workflow: ${workflow.workflowName}`)\n console.log(` - ID: ${workflow.id}`)\n console.log(` - Workflow ID: ${workflow.workflowId}`)\n console.log(` - Version: ${workflow.version}`)\n console.log(` - Steps: ${workflow.definition.steps.length}`)\n console.log(` - Transitions: ${workflow.definition.transitions.length}`)\n console.log(` - Activities: ${workflow.definition.transitions.reduce((sum, t) => sum + (t.activities?.length || 0), 0)}`)\n console.log('')\n console.log('Sales pipeline workflow is ready!')\n } catch (error) {\n console.error('Error seeding sales pipeline workflow:', error)\n throw error\n }\n },\n}\n\n/**\n * Seed simple approval example\n */\nconst seedSimpleApproval: ModuleCli = {\n command: 'seed-simple-approval',\n async run(rest: string[]) {\n const args = parseArgs(rest)\n const tenantId = String(args.tenantId ?? args.tenant ?? args.t ?? '')\n const organizationId = String(args.organizationId ?? args.orgId ?? args.org ?? args.o ?? '')\n\n if (!tenantId || !organizationId) {\n console.error('Usage: mercato workflows seed-simple-approval --tenant <tenantId> --org <organizationId>')\n return\n }\n\n try {\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n\n // Read the simple approval workflow definition\n const approvalPath = path.join(__dirname, 'examples', 'simple-approval-definition.json')\n const approvalData = JSON.parse(fs.readFileSync(approvalPath, 'utf8'))\n\n // Check if it already exists\n const existing = await em.findOne(WorkflowDefinition, {\n workflowId: approvalData.workflowId,\n tenantId,\n organizationId,\n })\n\n if (existing) {\n console.log(`\u2139\uFE0F Simple approval workflow '${approvalData.workflowId}' already exists (ID: ${existing.id})`)\n return\n }\n\n // Create the workflow definition\n const workflow = em.create(WorkflowDefinition, {\n ...approvalData,\n tenantId,\n organizationId,\n })\n\n await em.persistAndFlush(workflow)\n\n console.log(`\u2705 Seeded simple approval workflow: ${workflow.workflowName}`)\n console.log(` - ID: ${workflow.id}`)\n console.log(` - Workflow ID: ${workflow.workflowId}`)\n console.log(` - Version: ${workflow.version}`)\n console.log(` - Steps: ${workflow.definition.steps.length}`)\n console.log(` - Transitions: ${workflow.definition.transitions.length}`)\n console.log('')\n console.log('Simple approval workflow is ready!')\n } catch (error) {\n console.error('Error seeding simple approval workflow:', error)\n throw error\n }\n },\n}\n\n/**\n * Start workflow activity worker\n */\nconst startWorker: ModuleCli = {\n command: 'start-worker',\n async run(rest: string[]) {\n const args = parseArgs(rest)\n const concurrency = parseInt(args.concurrency ?? args.c ?? '5')\n\n const strategy = process.env.QUEUE_STRATEGY === 'async' ? 'async' : 'local'\n\n console.log('[Workflow Worker] Starting activity worker...')\n console.log(`[Workflow Worker] Strategy: ${strategy}`)\n\n if (strategy === 'local') {\n const pollMs = process.env.LOCAL_QUEUE_POLL_MS || '5000'\n console.log(`[Workflow Worker] Polling interval: ${pollMs}ms`)\n console.log('[Workflow Worker] NOTE: Local strategy is for development only.')\n console.log('[Workflow Worker] Use QUEUE_STRATEGY=async with Redis for production.')\n } else {\n console.log(`[Workflow Worker] Concurrency: ${concurrency}`)\n console.log(`[Workflow Worker] Redis: ${process.env.REDIS_URL || process.env.QUEUE_REDIS_URL}`)\n }\n\n try {\n const container = await createRequestContainer()\n const em = container.resolve<EntityManager>('em')\n\n // Import queue and handler\n const { runWorker } = await import('@open-mercato/queue/worker')\n const { createActivityWorkerHandler } = await import('./lib/activity-worker-handler')\n const { WORKFLOW_ACTIVITIES_QUEUE_NAME } = await import('./lib/activity-queue-types')\n\n // Create handler\n const handler = createActivityWorkerHandler(em, container)\n\n // Run worker\n await runWorker({\n queueName: WORKFLOW_ACTIVITIES_QUEUE_NAME,\n handler,\n connection: strategy === 'async' ? {\n url: process.env.REDIS_URL || process.env.QUEUE_REDIS_URL,\n } : undefined,\n concurrency,\n gracefulShutdown: true,\n })\n } catch (error) {\n console.error('[Workflow Worker] Failed to start worker:', error)\n throw error\n }\n },\n}\n\n/**\n * Seed all example workflows\n */\nconst seedAll: ModuleCli = {\n command: 'seed-all',\n async run(rest: string[]) {\n const args = parseArgs(rest)\n const tenantId = String(args.tenantId ?? args.tenant ?? args.t ?? '')\n const organizationId = String(args.organizationId ?? args.orgId ?? args.org ?? args.o ?? '')\n\n if (!tenantId || !organizationId) {\n console.error('Usage: mercato workflows seed-all --tenant <tenantId> --org <organizationId>')\n return\n }\n\n console.log('\uD83E\uDDE9 Seeding all example workflows...\\n')\n\n try {\n // Seed demo checkout with rules\n await seedDemoWithRules.run(rest)\n console.log('')\n\n // Seed sales pipeline\n await seedSalesPipeline.run(rest)\n console.log('')\n\n // Seed simple approval\n await seedSimpleApproval.run(rest)\n console.log('')\n\n console.log('\u2705 All example workflows seeded successfully!')\n } catch (error) {\n console.error('Error seeding workflows:', error)\n throw error\n }\n },\n}\n\n/**\n * Manually process pending workflow activities\n */\nconst processActivities: ModuleCli = {\n command: 'process-activities',\n async run(rest: string[]) {\n const args = parseArgs(rest)\n const limit = parseInt(args.limit ?? args.l ?? '0')\n\n console.log('[Workflow Activities] Processing pending activities...')\n if (limit > 0) {\n console.log(`[Workflow Activities] Limit: ${limit} jobs`)\n }\n\n try {\n const container = await createRequestContainer()\n const em = container.resolve<EntityManager>('em')\n\n // Import queue and handler\n const { createQueue } = await import('@open-mercato/queue')\n const { createActivityWorkerHandler } = await import('./lib/activity-worker-handler')\n const { WORKFLOW_ACTIVITIES_QUEUE_NAME } = await import('./lib/activity-queue-types')\n\n // Create queue instance\n const queue = createQueue(WORKFLOW_ACTIVITIES_QUEUE_NAME, 'local', {\n baseDir: process.env.QUEUE_BASE_DIR || '.queue',\n })\n\n // Create handler\n const handler = createActivityWorkerHandler(em, container)\n\n // Get initial counts\n const initialCounts = await queue.getJobCounts()\n console.log(`[Workflow Activities] Pending jobs: ${initialCounts.waiting}`)\n\n if (initialCounts.waiting === 0) {\n console.log('[Workflow Activities] No jobs to process')\n await queue.close()\n return\n }\n\n // Process jobs\n const result = await queue.process(handler as any, limit > 0 ? { limit } : undefined)\n\n console.log(`\\n[Workflow Activities] \u2713 Processed ${result.processed} activities`)\n if (result.failed > 0) {\n console.log(`[Workflow Activities] \u2717 Failed: ${result.failed} activities`)\n }\n\n // Show remaining\n const finalCounts = await queue.getJobCounts()\n if (finalCounts.waiting > 0) {\n console.log(`[Workflow Activities] Remaining: ${finalCounts.waiting} jobs`)\n }\n\n await queue.close()\n } catch (error) {\n console.error('[Workflow Activities] Error processing activities:', error)\n throw error\n }\n },\n}\n\nconst workflowsCliCommands = [\n startWorker,\n processActivities,\n seedDemo,\n seedDemoWithRules,\n seedSalesPipeline,\n seedSimpleApproval,\n seedAll,\n]\n\nexport default workflowsCliCommands\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.4.2-canary-
|
|
3
|
+
"version": "0.4.2-canary-ad4e7882e9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -207,7 +207,7 @@
|
|
|
207
207
|
}
|
|
208
208
|
},
|
|
209
209
|
"dependencies": {
|
|
210
|
-
"@open-mercato/shared": "0.4.2-canary-
|
|
210
|
+
"@open-mercato/shared": "0.4.2-canary-ad4e7882e9",
|
|
211
211
|
"@xyflow/react": "^12.6.0",
|
|
212
212
|
"date-fns": "^4.1.0",
|
|
213
213
|
"date-fns-tz": "^3.2.0"
|
|
@@ -15,6 +15,8 @@ jest.mock('@open-mercato/shared/lib/di/container', () => ({
|
|
|
15
15
|
createRequestContainer: async () => ({
|
|
16
16
|
resolve: (_: string) => ({
|
|
17
17
|
findUserByEmail: async (email: string) => ({ id: 1, email, passwordHash: 'hash', tenantId: tenantId, organizationId: orgId }),
|
|
18
|
+
findUsersByEmail: async (email: string) => ([{ id: 1, email, passwordHash: 'hash', tenantId: tenantId, organizationId: orgId }]),
|
|
19
|
+
findUserByEmailAndTenant: async (email: string) => ({ id: 1, email, passwordHash: 'hash', tenantId: tenantId, organizationId: orgId }),
|
|
18
20
|
verifyPassword: async () => true,
|
|
19
21
|
getUserRoles: async (_user: any, _tenant: string | null | undefined) => ['admin'],
|
|
20
22
|
updateLastLoginAt: async () => undefined,
|
|
@@ -17,15 +17,33 @@ export async function POST(req: Request) {
|
|
|
17
17
|
const email = String(form.get('email') ?? '')
|
|
18
18
|
const password = String(form.get('password') ?? '')
|
|
19
19
|
const remember = parseBooleanToken(form.get('remember')?.toString()) === true
|
|
20
|
+
const tenantIdRaw = String(form.get('tenantId') ?? form.get('tenant') ?? '').trim()
|
|
20
21
|
const requireRoleRaw = (String(form.get('requireRole') ?? form.get('role') ?? '')).trim()
|
|
21
22
|
const requiredRoles = requireRoleRaw ? requireRoleRaw.split(',').map((s) => s.trim()).filter(Boolean) : []
|
|
22
|
-
const parsed = userLoginSchema.pick({ email: true, password: true }).safeParse({
|
|
23
|
+
const parsed = userLoginSchema.pick({ email: true, password: true, tenantId: true }).safeParse({
|
|
24
|
+
email,
|
|
25
|
+
password,
|
|
26
|
+
tenantId: tenantIdRaw || undefined,
|
|
27
|
+
})
|
|
23
28
|
if (!parsed.success) {
|
|
24
29
|
return NextResponse.json({ ok: false, error: translate('auth.login.errors.invalidCredentials', 'Invalid credentials') }, { status: 400 })
|
|
25
30
|
}
|
|
26
31
|
const container = await createRequestContainer()
|
|
27
32
|
const auth = (container.resolve('authService') as AuthService)
|
|
28
|
-
const
|
|
33
|
+
const tenantId = parsed.data.tenantId ?? null
|
|
34
|
+
let user = null
|
|
35
|
+
if (tenantId) {
|
|
36
|
+
user = await auth.findUserByEmailAndTenant(parsed.data.email, tenantId)
|
|
37
|
+
} else {
|
|
38
|
+
const users = await auth.findUsersByEmail(parsed.data.email)
|
|
39
|
+
if (users.length > 1) {
|
|
40
|
+
return NextResponse.json({
|
|
41
|
+
ok: false,
|
|
42
|
+
error: translate('auth.login.errors.tenantRequired', 'Use the login link provided with your tenant activation to continue.'),
|
|
43
|
+
}, { status: 400 })
|
|
44
|
+
}
|
|
45
|
+
user = users[0] ?? null
|
|
46
|
+
}
|
|
29
47
|
if (!user || !user.passwordHash) {
|
|
30
48
|
return NextResponse.json({ ok: false, error: translate('auth.login.errors.invalidCredentials', 'Invalid email or password') }, { status: 401 })
|
|
31
49
|
}
|
|
@@ -35,26 +53,27 @@ export async function POST(req: Request) {
|
|
|
35
53
|
}
|
|
36
54
|
// Optional role requirement
|
|
37
55
|
if (requiredRoles.length) {
|
|
38
|
-
const userRoleNames = await auth.getUserRoles(user, user.tenantId ? String(user.tenantId) : null)
|
|
56
|
+
const userRoleNames = await auth.getUserRoles(user, tenantId ?? (user.tenantId ? String(user.tenantId) : null))
|
|
39
57
|
const authorized = requiredRoles.some(r => userRoleNames.includes(r))
|
|
40
58
|
if (!authorized) {
|
|
41
59
|
return NextResponse.json({ ok: false, error: translate('auth.login.errors.permissionDenied', 'Not authorized for this area') }, { status: 403 })
|
|
42
60
|
}
|
|
43
61
|
}
|
|
44
62
|
await auth.updateLastLoginAt(user)
|
|
45
|
-
const
|
|
63
|
+
const resolvedTenantId = tenantId ?? (user.tenantId ? String(user.tenantId) : null)
|
|
64
|
+
const userRoleNames = await auth.getUserRoles(user, resolvedTenantId)
|
|
46
65
|
try {
|
|
47
66
|
const eventBus = (container.resolve('eventBus') as EventBus)
|
|
48
67
|
void eventBus.emitEvent('query_index.coverage.warmup', {
|
|
49
|
-
tenantId:
|
|
68
|
+
tenantId: resolvedTenantId,
|
|
50
69
|
}).catch(() => undefined)
|
|
51
70
|
} catch {
|
|
52
71
|
// optional warmup
|
|
53
72
|
}
|
|
54
73
|
const token = signJwt({
|
|
55
74
|
sub: String(user.id),
|
|
56
|
-
tenantId:
|
|
57
|
-
orgId: user.organizationId ? String(user.organizationId) : null,
|
|
75
|
+
tenantId: resolvedTenantId,
|
|
76
|
+
orgId: user.organizationId ? String(user.organizationId) : null,
|
|
58
77
|
email: user.email,
|
|
59
78
|
roles: userRoleNames
|
|
60
79
|
})
|