@open-mercato/shared 0.6.7 → 0.6.8-develop.6874.1.982d6097d8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +57 -6
- package/build.mjs +2 -4
- package/dist/lib/ai/safety-identifier.js +32 -0
- package/dist/lib/ai/safety-identifier.js.map +7 -0
- package/dist/lib/auth/organizationScope.js +34 -0
- package/dist/lib/auth/organizationScope.js.map +7 -0
- package/dist/lib/auth/server.js +15 -2
- package/dist/lib/auth/server.js.map +2 -2
- package/dist/lib/bootstrap/clientOnlyModules.js +55 -0
- package/dist/lib/bootstrap/clientOnlyModules.js.map +7 -0
- package/dist/lib/bootstrap/dynamicLoader.js +117 -43
- package/dist/lib/bootstrap/dynamicLoader.js.map +2 -2
- package/dist/lib/commands/command-bus.js +5 -1
- package/dist/lib/commands/command-bus.js.map +2 -2
- package/dist/lib/commands/command-interceptor-runner.js +2 -2
- package/dist/lib/commands/command-interceptor-runner.js.map +2 -2
- package/dist/lib/crud/enricher-runner.js +2 -11
- package/dist/lib/crud/enricher-runner.js.map +2 -2
- package/dist/lib/crud/factory.js +72 -9
- package/dist/lib/crud/factory.js.map +2 -2
- package/dist/lib/crud/interceptor-runner.js +2 -2
- package/dist/lib/crud/interceptor-runner.js.map +2 -2
- package/dist/lib/crud/mutation-guard-registry.js +2 -2
- package/dist/lib/crud/mutation-guard-registry.js.map +2 -2
- package/dist/lib/crud/types.js +4 -0
- package/dist/lib/crud/types.js.map +3 -3
- package/dist/lib/data/consistency.js +19 -0
- package/dist/lib/data/consistency.js.map +7 -0
- package/dist/lib/data/engine.js +37 -11
- package/dist/lib/data/engine.js.map +2 -2
- package/dist/lib/db/entityMetadata.js +26 -0
- package/dist/lib/db/entityMetadata.js.map +7 -0
- package/dist/lib/db/pg-errors.js +42 -0
- package/dist/lib/db/pg-errors.js.map +2 -2
- package/dist/lib/di/container.js +48 -6
- package/dist/lib/di/container.js.map +2 -2
- package/dist/lib/email/send.js +56 -0
- package/dist/lib/email/send.js.map +2 -2
- package/dist/lib/encryption/subscriber.js +2 -10
- package/dist/lib/encryption/subscriber.js.map +2 -2
- package/dist/lib/encryption/tenantDataEncryptionService.js +52 -17
- package/dist/lib/encryption/tenantDataEncryptionService.js.map +2 -2
- package/dist/lib/i18n/config.js +1 -1
- package/dist/lib/i18n/config.js.map +2 -2
- package/dist/lib/i18n/server.js +4 -4
- package/dist/lib/i18n/server.js.map +3 -3
- package/dist/lib/logger/extension.js +29 -0
- package/dist/lib/logger/extension.js.map +7 -0
- package/dist/lib/logger/index.js +47 -3
- package/dist/lib/logger/index.js.map +3 -3
- package/dist/lib/modules/registry.js +5 -1
- package/dist/lib/modules/registry.js.map +2 -2
- package/dist/lib/modules/surfaceFingerprint.js +47 -0
- package/dist/lib/modules/surfaceFingerprint.js.map +7 -0
- package/dist/lib/query/ciphertext-search-warning.js +45 -0
- package/dist/lib/query/ciphertext-search-warning.js.map +7 -0
- package/dist/lib/query/engine.js +54 -40
- package/dist/lib/query/engine.js.map +2 -2
- package/dist/lib/query/types.js.map +1 -1
- package/dist/lib/redis/connection.js +7 -2
- package/dist/lib/redis/connection.js.map +2 -2
- package/dist/lib/search/auto-indexing.js +14 -0
- package/dist/lib/search/auto-indexing.js.map +7 -0
- package/dist/lib/search/availability.js +107 -0
- package/dist/lib/search/availability.js.map +7 -0
- package/dist/lib/search/config.js +61 -2
- package/dist/lib/search/config.js.map +2 -2
- package/dist/lib/search/tokenLookup.js +46 -0
- package/dist/lib/search/tokenLookup.js.map +7 -0
- package/dist/lib/search/tokenize.js +23 -11
- package/dist/lib/search/tokenize.js.map +2 -2
- package/dist/lib/telemetry/runtime.js +35 -0
- package/dist/lib/telemetry/runtime.js.map +7 -0
- package/dist/lib/version.js +1 -1
- package/dist/lib/version.js.map +1 -1
- package/dist/lib/webhooks/body.js +57 -0
- package/dist/lib/webhooks/body.js.map +7 -0
- package/dist/lib/webhooks/inbound-types.js +1 -0
- package/dist/lib/webhooks/inbound-types.js.map +7 -0
- package/dist/lib/webhooks/index.js +13 -1
- package/dist/lib/webhooks/index.js.map +2 -2
- package/dist/lib/webhooks/verify.js +12 -7
- package/dist/lib/webhooks/verify.js.map +2 -2
- package/dist/modules/analytics.js.map +2 -2
- package/dist/modules/overrides.js +50 -1
- package/dist/modules/overrides.js.map +2 -2
- package/dist/modules/payment_gateways/types.js +8 -1
- package/dist/modules/payment_gateways/types.js.map +2 -2
- package/dist/modules/registry.js +1 -0
- package/dist/modules/registry.js.map +2 -2
- package/dist/modules/widgets/extension-points.js +96 -0
- package/dist/modules/widgets/extension-points.js.map +7 -0
- package/dist/security/enabledModulesRegistry.js +56 -11
- package/dist/security/enabledModulesRegistry.js.map +2 -2
- package/dist/security/featurePolicy.js +62 -0
- package/dist/security/featurePolicy.js.map +7 -0
- package/jest.config.cjs +2 -1
- package/package.json +19 -9
- package/scripts/versionSource.cjs +65 -0
- package/src/lib/__tests__/versionSource.test.ts +46 -0
- package/src/lib/ai/__tests__/llm-provider-contract.test.ts +59 -0
- package/src/lib/ai/__tests__/safety-identifier.test.ts +71 -0
- package/src/lib/ai/llm-provider.ts +33 -0
- package/src/lib/ai/safety-identifier.ts +73 -0
- package/src/lib/auth/__tests__/organizationScope.test.ts +107 -0
- package/src/lib/auth/__tests__/server.test.ts +23 -0
- package/src/lib/auth/organizationScope.ts +65 -0
- package/src/lib/auth/server.ts +23 -2
- package/src/lib/bootstrap/__tests__/clientOnlyModules.test.ts +189 -0
- package/src/lib/bootstrap/__tests__/dynamicLoader.aliasResolver.test.ts +98 -0
- package/src/lib/bootstrap/__tests__/dynamicLoader.appDi.test.ts +211 -0
- package/src/lib/bootstrap/__tests__/dynamicLoader.commandInterceptors.test.ts +63 -8
- package/src/lib/bootstrap/__tests__/dynamicLoader.generatedCacheRecovery.test.ts +256 -0
- package/src/lib/bootstrap/__tests__/dynamicLoader.tsconfig.test.ts +21 -1
- package/src/lib/bootstrap/clientOnlyModules.ts +85 -0
- package/src/lib/bootstrap/dynamicLoader.ts +213 -65
- package/src/lib/commands/__tests__/command-interceptor-runner.test.ts +28 -0
- package/src/lib/commands/command-bus.ts +5 -1
- package/src/lib/commands/command-interceptor-runner.ts +2 -2
- package/src/lib/crud/__tests__/crud-factory.cache-user-scope.test.ts +171 -0
- package/src/lib/crud/__tests__/crud-factory.test.ts +351 -0
- package/src/lib/crud/__tests__/mutation-guard-registry.test.ts +26 -0
- package/src/lib/crud/enricher-runner.ts +2 -11
- package/src/lib/crud/factory.ts +82 -6
- package/src/lib/crud/interceptor-runner.ts +2 -2
- package/src/lib/crud/mutation-guard-registry.ts +2 -2
- package/src/lib/crud/types.ts +3 -0
- package/src/lib/data/__tests__/consistency.test.ts +38 -0
- package/src/lib/data/__tests__/engine.bulk-suppress.test.ts +18 -3
- package/src/lib/data/consistency.ts +17 -0
- package/src/lib/data/engine.ts +50 -16
- package/src/lib/db/__tests__/entityMetadata.test.ts +38 -0
- package/src/lib/db/__tests__/pg-errors.test.ts +48 -0
- package/src/lib/db/entityMetadata.ts +43 -0
- package/src/lib/db/pg-errors.ts +58 -0
- package/src/lib/di/__tests__/container-app-di-absent.test.ts +155 -0
- package/src/lib/di/__tests__/container-app-di.test.ts +173 -0
- package/src/lib/di/__tests__/registrar-error-log.test.ts +154 -0
- package/src/lib/di/container.ts +72 -8
- package/src/lib/email/__tests__/send.test.ts +37 -1
- package/src/lib/email/send.ts +79 -0
- package/src/lib/encryption/__tests__/subscriber.test.ts +54 -2
- package/src/lib/encryption/__tests__/tenantDataEncryptionService.test.ts +58 -0
- package/src/lib/encryption/subscriber.ts +2 -14
- package/src/lib/encryption/tenantDataEncryptionService.ts +64 -18
- package/src/lib/i18n/__tests__/config.test.ts +8 -0
- package/src/lib/i18n/__tests__/server-dictionary-cache.test.ts +30 -0
- package/src/lib/i18n/__tests__/server-unbootstrapped.test.ts +29 -0
- package/src/lib/i18n/config.ts +2 -2
- package/src/lib/i18n/server.ts +6 -2
- package/src/lib/logger/__tests__/logger.test.ts +77 -0
- package/src/lib/logger/extension.ts +59 -0
- package/src/lib/logger/index.ts +59 -1
- package/src/lib/modules/__tests__/surfaceFingerprint.test.ts +122 -0
- package/src/lib/modules/registry.ts +10 -0
- package/src/lib/modules/surfaceFingerprint.ts +87 -0
- package/src/lib/query/__tests__/ciphertext-search-warning.test.ts +178 -0
- package/src/lib/query/__tests__/engine.test.ts +65 -13
- package/src/lib/query/ciphertext-search-warning.ts +95 -0
- package/src/lib/query/engine.ts +75 -58
- package/src/lib/query/types.ts +4 -5
- package/src/lib/redis/__tests__/connection.test.ts +16 -0
- package/src/lib/redis/connection.ts +10 -7
- package/src/lib/search/__tests__/availability.test.ts +211 -0
- package/src/lib/search/__tests__/config.test.ts +177 -0
- package/src/lib/search/__tests__/tokenLookup.test.ts +206 -0
- package/src/lib/search/__tests__/tokenize.test.ts +49 -0
- package/src/lib/search/auto-indexing.ts +22 -0
- package/src/lib/search/availability.ts +232 -0
- package/src/lib/search/config.ts +106 -8
- package/src/lib/search/tokenLookup.ts +133 -0
- package/src/lib/search/tokenize.ts +33 -11
- package/src/lib/telemetry/runtime.ts +72 -0
- package/src/lib/webhooks/__tests__/body.test.ts +95 -0
- package/src/lib/webhooks/__tests__/verify.test.ts +20 -1
- package/src/lib/webhooks/body.ts +70 -0
- package/src/lib/webhooks/inbound-types.ts +125 -0
- package/src/lib/webhooks/index.ts +19 -1
- package/src/lib/webhooks/verify.ts +17 -9
- package/src/modules/__tests__/nav-group-order-override.test.ts +183 -0
- package/src/modules/__tests__/registry.test.ts +10 -2
- package/src/modules/analytics.ts +7 -0
- package/src/modules/customer-auth.ts +1 -0
- package/src/modules/encryption.ts +3 -0
- package/src/modules/navigation/backendChrome.ts +15 -0
- package/src/modules/overrides.ts +103 -0
- package/src/modules/payment_gateways/__tests__/types.test.ts +37 -0
- package/src/modules/payment_gateways/types.ts +41 -0
- package/src/modules/registry.ts +3 -0
- package/src/modules/widgets/__tests__/extension-points.test.ts +101 -0
- package/src/modules/widgets/extension-points.ts +524 -0
- package/src/security/__tests__/featurePolicy.test.ts +166 -0
- package/src/security/enabledModulesRegistry.ts +64 -12
- package/src/security/featurePolicy.ts +89 -0
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { timingSafeEqual, createHmac } from "node:crypto";
|
|
2
2
|
import { parseWebhookSecret } from "./secrets.js";
|
|
3
|
-
const
|
|
4
|
-
function
|
|
5
|
-
const
|
|
6
|
-
if (
|
|
7
|
-
return
|
|
3
|
+
const WEBHOOK_SIGNATURE_TOLERANCE_SECONDS = 5 * 60;
|
|
4
|
+
function isWebhookTimestampWithinTolerance(timestamp, toleranceSeconds = WEBHOOK_SIGNATURE_TOLERANCE_SECONDS, nowSeconds = Math.floor(Date.now() / 1e3)) {
|
|
5
|
+
const normalizedTimestamp = timestamp.trim();
|
|
6
|
+
if (!/^\d+$/.test(normalizedTimestamp)) {
|
|
7
|
+
return false;
|
|
8
8
|
}
|
|
9
|
-
const
|
|
10
|
-
|
|
9
|
+
const timestampNum = Number.parseInt(normalizedTimestamp, 10);
|
|
10
|
+
return Math.abs(nowSeconds - timestampNum) <= toleranceSeconds;
|
|
11
|
+
}
|
|
12
|
+
function verifyWebhookSignature(msgId, timestamp, body, signatureHeader, secrets, toleranceSeconds = WEBHOOK_SIGNATURE_TOLERANCE_SECONDS) {
|
|
13
|
+
if (!isWebhookTimestampWithinTolerance(timestamp, toleranceSeconds)) {
|
|
11
14
|
return { valid: false };
|
|
12
15
|
}
|
|
13
16
|
const signedContent = `${msgId}.${timestamp}.${body}`;
|
|
@@ -29,6 +32,8 @@ function verifyWebhookSignature(msgId, timestamp, body, signatureHeader, secrets
|
|
|
29
32
|
return { valid: false };
|
|
30
33
|
}
|
|
31
34
|
export {
|
|
35
|
+
WEBHOOK_SIGNATURE_TOLERANCE_SECONDS,
|
|
36
|
+
isWebhookTimestampWithinTolerance,
|
|
32
37
|
verifyWebhookSignature
|
|
33
38
|
};
|
|
34
39
|
//# sourceMappingURL=verify.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/webhooks/verify.ts"],
|
|
4
|
-
"sourcesContent": ["import { timingSafeEqual, createHmac } from 'node:crypto'\nimport { parseWebhookSecret } from './secrets'\nimport type { WebhookVerificationResult } from './types'\n\
|
|
5
|
-
"mappings": "AAAA,SAAS,iBAAiB,kBAAkB;AAC5C,SAAS,0BAA0B;
|
|
4
|
+
"sourcesContent": ["import { timingSafeEqual, createHmac } from 'node:crypto'\nimport { parseWebhookSecret } from './secrets'\nimport type { WebhookVerificationResult } from './types'\n\nexport const WEBHOOK_SIGNATURE_TOLERANCE_SECONDS = 5 * 60 // 5 minutes\n\nexport function isWebhookTimestampWithinTolerance(\n timestamp: string,\n toleranceSeconds: number = WEBHOOK_SIGNATURE_TOLERANCE_SECONDS,\n nowSeconds: number = Math.floor(Date.now() / 1000),\n): boolean {\n const normalizedTimestamp = timestamp.trim()\n if (!/^\\d+$/.test(normalizedTimestamp)) {\n return false\n }\n\n const timestampNum = Number.parseInt(normalizedTimestamp, 10)\n return Math.abs(nowSeconds - timestampNum) <= toleranceSeconds\n}\n\n/**\n * Verify a Standard Webhooks signature against one or more secrets.\n * Returns verification result with which key matched.\n */\nexport function verifyWebhookSignature(\n msgId: string,\n timestamp: string,\n body: string,\n signatureHeader: string,\n secrets: string[],\n toleranceSeconds: number = WEBHOOK_SIGNATURE_TOLERANCE_SECONDS,\n): WebhookVerificationResult {\n if (!isWebhookTimestampWithinTolerance(timestamp, toleranceSeconds)) {\n return { valid: false }\n }\n\n const signedContent = `${msgId}.${timestamp}.${body}`\n const providedSignatures = signatureHeader.split(' ')\n\n for (let keyIndex = 0; keyIndex < secrets.length; keyIndex++) {\n const key = parseWebhookSecret(secrets[keyIndex])\n const expectedSig = createHmac('sha256', key)\n .update(signedContent)\n .digest('base64')\n\n for (const sig of providedSignatures) {\n const parts = sig.split(',')\n if (parts.length !== 2 || parts[0] !== 'v1') continue\n\n const sigBytes = Buffer.from(parts[1], 'base64')\n const expectedBytes = Buffer.from(expectedSig, 'base64')\n\n if (sigBytes.length !== expectedBytes.length) continue\n\n if (timingSafeEqual(sigBytes, expectedBytes)) {\n return { valid: true, matchedKeyIndex: keyIndex }\n }\n }\n }\n\n return { valid: false }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,iBAAiB,kBAAkB;AAC5C,SAAS,0BAA0B;AAG5B,MAAM,sCAAsC,IAAI;AAEhD,SAAS,kCACd,WACA,mBAA2B,qCAC3B,aAAqB,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,GACxC;AACT,QAAM,sBAAsB,UAAU,KAAK;AAC3C,MAAI,CAAC,QAAQ,KAAK,mBAAmB,GAAG;AACtC,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,OAAO,SAAS,qBAAqB,EAAE;AAC5D,SAAO,KAAK,IAAI,aAAa,YAAY,KAAK;AAChD;AAMO,SAAS,uBACd,OACA,WACA,MACA,iBACA,SACA,mBAA2B,qCACA;AAC3B,MAAI,CAAC,kCAAkC,WAAW,gBAAgB,GAAG;AACnE,WAAO,EAAE,OAAO,MAAM;AAAA,EACxB;AAEA,QAAM,gBAAgB,GAAG,KAAK,IAAI,SAAS,IAAI,IAAI;AACnD,QAAM,qBAAqB,gBAAgB,MAAM,GAAG;AAEpD,WAAS,WAAW,GAAG,WAAW,QAAQ,QAAQ,YAAY;AAC5D,UAAM,MAAM,mBAAmB,QAAQ,QAAQ,CAAC;AAChD,UAAM,cAAc,WAAW,UAAU,GAAG,EACzC,OAAO,aAAa,EACpB,OAAO,QAAQ;AAElB,eAAW,OAAO,oBAAoB;AACpC,YAAM,QAAQ,IAAI,MAAM,GAAG;AAC3B,UAAI,MAAM,WAAW,KAAK,MAAM,CAAC,MAAM,KAAM;AAE7C,YAAM,WAAW,OAAO,KAAK,MAAM,CAAC,GAAG,QAAQ;AAC/C,YAAM,gBAAgB,OAAO,KAAK,aAAa,QAAQ;AAEvD,UAAI,SAAS,WAAW,cAAc,OAAQ;AAE9C,UAAI,gBAAgB,UAAU,aAAa,GAAG;AAC5C,eAAO,EAAE,OAAO,MAAM,iBAAiB,SAAS;AAAA,MAClD;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,MAAM;AACxB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/modules/analytics.ts"],
|
|
4
|
-
"sourcesContent": ["import { createLogger } from '../lib/logger'\n\nconst logger = createLogger('shared').child({ component: 'analytics-registry' })\n\nexport type AggregateFunction = 'count' | 'sum' | 'avg' | 'min' | 'max'\n\nexport type DateGranularity = 'day' | 'week' | 'month' | 'quarter' | 'year'\n\nexport type AnalyticsFieldType = 'numeric' | 'text' | 'uuid' | 'timestamp' | 'jsonb'\n\nexport type AnalyticsEntityTypeConfig = {\n tableName: string\n schema?: string\n dateField: string\n defaultScopeFields: string[]\n}\n\nexport type AnalyticsFieldMapping = {\n dbColumn: string\n type: AnalyticsFieldType\n}\n\nexport type AnalyticsLabelResolverConfig = {\n table: string\n idColumn: string\n labelColumn: string\n}\n\nexport type AnalyticsEntityConfig = {\n entityId: string\n requiredFeatures: string[]\n entityConfig: AnalyticsEntityTypeConfig\n fieldMappings: Record<string, AnalyticsFieldMapping>\n labelResolvers?: Record<string, AnalyticsLabelResolverConfig>\n}\n\nexport type AnalyticsModuleConfig = {\n entities: AnalyticsEntityConfig[]\n}\n\nlet _analyticsModuleConfigs: AnalyticsModuleConfig[] | null = null\n\nexport function registerAnalyticsModuleConfigs(configs: AnalyticsModuleConfig[]): void {\n if (_analyticsModuleConfigs !== null && process.env.NODE_ENV === 'development') {\n logger.debug('Analytics module configs re-registered (this may occur during HMR)')\n }\n _analyticsModuleConfigs = configs\n}\n\nexport function getAnalyticsModuleConfigs(): AnalyticsModuleConfig[] {\n return _analyticsModuleConfigs ?? []\n}\n\nexport function getAllAnalyticsEntityConfigs(): AnalyticsEntityConfig[] {\n const configs = getAnalyticsModuleConfigs()\n return configs.flatMap((moduleConfig) => moduleConfig.entities)\n}\n\nexport function getAnalyticsEntityConfig(entityId: string): AnalyticsEntityConfig | null {\n const entities = getAllAnalyticsEntityConfigs()\n return entities.find((e) => e.entityId === entityId) ?? null\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,QAAQ,EAAE,MAAM,EAAE,WAAW,qBAAqB,CAAC;
|
|
4
|
+
"sourcesContent": ["import { createLogger } from '../lib/logger'\n\nconst logger = createLogger('shared').child({ component: 'analytics-registry' })\n\nexport type AggregateFunction = 'count' | 'sum' | 'avg' | 'min' | 'max'\n\nexport type DateGranularity = 'day' | 'week' | 'month' | 'quarter' | 'year'\n\nexport type AnalyticsFieldType = 'numeric' | 'text' | 'uuid' | 'timestamp' | 'jsonb'\n\nexport type AnalyticsEntityTypeConfig = {\n tableName: string\n schema?: string\n dateField: string\n defaultScopeFields: string[]\n /**\n * Field name (a key of `fieldMappings`) holding the per-row currency of monetary\n * columns. Declaring it lets analytics consumers verify that the rows they aggregate\n * all carry one currency before labelling the result with it (#4676). Omit it for\n * entities whose amounts are not per-row denominated.\n */\n currencyField?: string\n}\n\nexport type AnalyticsFieldMapping = {\n dbColumn: string\n type: AnalyticsFieldType\n}\n\nexport type AnalyticsLabelResolverConfig = {\n table: string\n idColumn: string\n labelColumn: string\n}\n\nexport type AnalyticsEntityConfig = {\n entityId: string\n requiredFeatures: string[]\n entityConfig: AnalyticsEntityTypeConfig\n fieldMappings: Record<string, AnalyticsFieldMapping>\n labelResolvers?: Record<string, AnalyticsLabelResolverConfig>\n}\n\nexport type AnalyticsModuleConfig = {\n entities: AnalyticsEntityConfig[]\n}\n\nlet _analyticsModuleConfigs: AnalyticsModuleConfig[] | null = null\n\nexport function registerAnalyticsModuleConfigs(configs: AnalyticsModuleConfig[]): void {\n if (_analyticsModuleConfigs !== null && process.env.NODE_ENV === 'development') {\n logger.debug('Analytics module configs re-registered (this may occur during HMR)')\n }\n _analyticsModuleConfigs = configs\n}\n\nexport function getAnalyticsModuleConfigs(): AnalyticsModuleConfig[] {\n return _analyticsModuleConfigs ?? []\n}\n\nexport function getAllAnalyticsEntityConfigs(): AnalyticsEntityConfig[] {\n const configs = getAnalyticsModuleConfigs()\n return configs.flatMap((moduleConfig) => moduleConfig.entities)\n}\n\nexport function getAnalyticsEntityConfig(entityId: string): AnalyticsEntityConfig | null {\n const entities = getAllAnalyticsEntityConfigs()\n return entities.find((e) => e.entityId === entityId) ?? null\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,QAAQ,EAAE,MAAM,EAAE,WAAW,qBAAqB,CAAC;AA6C/E,IAAI,0BAA0D;AAEvD,SAAS,+BAA+B,SAAwC;AACrF,MAAI,4BAA4B,QAAQ,QAAQ,IAAI,aAAa,eAAe;AAC9E,WAAO,MAAM,oEAAoE;AAAA,EACnF;AACA,4BAA0B;AAC5B;AAEO,SAAS,4BAAqD;AACnE,SAAO,2BAA2B,CAAC;AACrC;AAEO,SAAS,+BAAwD;AACtE,QAAM,UAAU,0BAA0B;AAC1C,SAAO,QAAQ,QAAQ,CAAC,iBAAiB,aAAa,QAAQ;AAChE;AAEO,SAAS,yBAAyB,UAAgD;AACvF,QAAM,WAAW,6BAA6B;AAC9C,SAAO,SAAS,KAAK,CAAC,MAAM,EAAE,aAAa,QAAQ,KAAK;AAC1D;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -23,7 +23,8 @@ const DOMAIN_KEYS = [
|
|
|
23
23
|
"setup",
|
|
24
24
|
"acl",
|
|
25
25
|
"di",
|
|
26
|
-
"encryption"
|
|
26
|
+
"encryption",
|
|
27
|
+
"nav"
|
|
27
28
|
];
|
|
28
29
|
const TRACKING_ISSUE_HINT = "See `.ai/specs/implemented/2026-05-04-modules-ts-unified-overrides.md` and tracking issue https://github.com/open-mercato/open-mercato/issues/1787.";
|
|
29
30
|
function applyModuleOverridesFromEnabledModules(modules) {
|
|
@@ -74,6 +75,33 @@ const aclFeatureOverrideStore = { modules: {}, programmatic: {} };
|
|
|
74
75
|
const encryptionMapOverrideStore = { modules: {}, programmatic: {} };
|
|
75
76
|
const diOverrideStore = { modules: {}, programmatic: {} };
|
|
76
77
|
const setupOverridesByModule = {};
|
|
78
|
+
const GLOBAL_NAV_OVERRIDE_STATE_KEY = "__openMercatoNavOverrideState__";
|
|
79
|
+
function getNavOverrideState() {
|
|
80
|
+
const existing = globalThis[GLOBAL_NAV_OVERRIDE_STATE_KEY];
|
|
81
|
+
if (existing && typeof existing === "object") {
|
|
82
|
+
const typed = existing;
|
|
83
|
+
if ("modules" in typed && "programmatic" in typed) return typed;
|
|
84
|
+
}
|
|
85
|
+
const initial = { modules: null, programmatic: null };
|
|
86
|
+
globalThis[GLOBAL_NAV_OVERRIDE_STATE_KEY] = initial;
|
|
87
|
+
return initial;
|
|
88
|
+
}
|
|
89
|
+
function normalizeNavGroupOrder(value) {
|
|
90
|
+
if (!Array.isArray(value)) return null;
|
|
91
|
+
const ids = Array.from(
|
|
92
|
+
new Set(
|
|
93
|
+
value.filter((id) => typeof id === "string" && id.trim().length > 0).map((id) => id.trim())
|
|
94
|
+
)
|
|
95
|
+
);
|
|
96
|
+
return ids.length > 0 ? ids : null;
|
|
97
|
+
}
|
|
98
|
+
function applyNavGroupOrderOverrides(groupOrder) {
|
|
99
|
+
getNavOverrideState().programmatic = groupOrder === null ? null : normalizeNavGroupOrder(groupOrder);
|
|
100
|
+
}
|
|
101
|
+
function getNavGroupOrderOverride() {
|
|
102
|
+
const state = getNavOverrideState();
|
|
103
|
+
return state.programmatic ?? state.modules?.groupOrder ?? null;
|
|
104
|
+
}
|
|
77
105
|
function normalizeIdOverrideKey(key, label) {
|
|
78
106
|
if (typeof key !== "string") return null;
|
|
79
107
|
const trimmed = key.trim();
|
|
@@ -261,6 +289,9 @@ function resetModuleContractOverridesForTests() {
|
|
|
261
289
|
clearStore(encryptionMapOverrideStore);
|
|
262
290
|
clearStore(diOverrideStore);
|
|
263
291
|
for (const key of Object.keys(setupOverridesByModule)) delete setupOverridesByModule[key];
|
|
292
|
+
const navState = getNavOverrideState();
|
|
293
|
+
navState.modules = null;
|
|
294
|
+
navState.programmatic = null;
|
|
264
295
|
}
|
|
265
296
|
function composeApiRouteOverrides() {
|
|
266
297
|
const modulesKeys = Object.keys(modulesConfigApiRouteOverrides);
|
|
@@ -827,7 +858,23 @@ function encryptionOverridesApplier(entries) {
|
|
|
827
858
|
applyStoreOverrides(encryptionMapOverrideStore, "modules", entry.overrides?.maps, { label: "encryption.maps" });
|
|
828
859
|
}
|
|
829
860
|
}
|
|
861
|
+
function navOverridesApplier(entries) {
|
|
862
|
+
const state = getNavOverrideState();
|
|
863
|
+
for (const entry of entries) {
|
|
864
|
+
const groupOrder = normalizeNavGroupOrder(entry.overrides?.groupOrder);
|
|
865
|
+
if (!groupOrder) continue;
|
|
866
|
+
if (state.modules && state.modules.moduleId !== entry.moduleId) {
|
|
867
|
+
logger.warn("nav.groupOrder declared by more than one module \u2014 the later one wins", {
|
|
868
|
+
previousModuleId: state.modules.moduleId,
|
|
869
|
+
moduleId: entry.moduleId,
|
|
870
|
+
hint: "Sidebar group ordering is a single app-wide decision; declare it on one module entry."
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
state.modules = { moduleId: entry.moduleId, groupOrder };
|
|
874
|
+
}
|
|
875
|
+
}
|
|
830
876
|
function registerBuiltInModuleOverrideAppliers() {
|
|
877
|
+
registerModuleOverrideApplier("nav", navOverridesApplier);
|
|
831
878
|
registerModuleOverrideApplier("routes", routesOverridesApplier);
|
|
832
879
|
registerModuleOverrideApplier("events", eventsOverridesApplier);
|
|
833
880
|
registerModuleOverrideApplier("workers", workersOverridesApplier);
|
|
@@ -865,6 +912,7 @@ export {
|
|
|
865
912
|
applyInjectionWidgetOverridesToTables,
|
|
866
913
|
applyModuleOverridesFromEnabledModules,
|
|
867
914
|
applyModuleOverridesToModules,
|
|
915
|
+
applyNavGroupOrderOverrides,
|
|
868
916
|
applyNotificationHandlerOverrides,
|
|
869
917
|
applyNotificationHandlerOverridesToEntries,
|
|
870
918
|
applyNotificationTypeOverrides,
|
|
@@ -895,6 +943,7 @@ export {
|
|
|
895
943
|
composeResponseEnricherOverrides,
|
|
896
944
|
composeSubscriberOverrides,
|
|
897
945
|
composeWorkerOverrides,
|
|
946
|
+
getNavGroupOrderOverride,
|
|
898
947
|
registerModuleOverrideApplier,
|
|
899
948
|
resetApiRouteOverridesForTests,
|
|
900
949
|
resetModuleContractOverridesForTests,
|