@open-mercato/shared 0.6.7-develop.6860.1.f796b22e80 → 0.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +6 -57
  3. package/build.mjs +4 -2
  4. package/dist/lib/auth/server.js +2 -15
  5. package/dist/lib/auth/server.js.map +2 -2
  6. package/dist/lib/bootstrap/dynamicLoader.js +43 -117
  7. package/dist/lib/bootstrap/dynamicLoader.js.map +2 -2
  8. package/dist/lib/commands/command-bus.js +1 -5
  9. package/dist/lib/commands/command-bus.js.map +2 -2
  10. package/dist/lib/commands/command-interceptor-runner.js +2 -2
  11. package/dist/lib/commands/command-interceptor-runner.js.map +2 -2
  12. package/dist/lib/crud/enricher-runner.js +11 -2
  13. package/dist/lib/crud/enricher-runner.js.map +2 -2
  14. package/dist/lib/crud/factory.js +4 -63
  15. package/dist/lib/crud/factory.js.map +2 -2
  16. package/dist/lib/crud/interceptor-runner.js +2 -2
  17. package/dist/lib/crud/interceptor-runner.js.map +2 -2
  18. package/dist/lib/crud/mutation-guard-registry.js +2 -2
  19. package/dist/lib/crud/mutation-guard-registry.js.map +2 -2
  20. package/dist/lib/crud/types.js +0 -4
  21. package/dist/lib/crud/types.js.map +3 -3
  22. package/dist/lib/data/engine.js +11 -37
  23. package/dist/lib/data/engine.js.map +2 -2
  24. package/dist/lib/db/mikro.js +12 -3
  25. package/dist/lib/db/mikro.js.map +2 -2
  26. package/dist/lib/db/pg-errors.js +0 -42
  27. package/dist/lib/db/pg-errors.js.map +2 -2
  28. package/dist/lib/di/container.js +6 -48
  29. package/dist/lib/di/container.js.map +2 -2
  30. package/dist/lib/email/send.js +0 -56
  31. package/dist/lib/email/send.js.map +2 -2
  32. package/dist/lib/encryption/subscriber.js +10 -2
  33. package/dist/lib/encryption/subscriber.js.map +2 -2
  34. package/dist/lib/encryption/tenantDataEncryptionService.js +17 -52
  35. package/dist/lib/encryption/tenantDataEncryptionService.js.map +2 -2
  36. package/dist/lib/i18n/config.js +1 -1
  37. package/dist/lib/i18n/config.js.map +2 -2
  38. package/dist/lib/i18n/server.js +4 -4
  39. package/dist/lib/i18n/server.js.map +3 -3
  40. package/dist/lib/logger/index.js +3 -47
  41. package/dist/lib/logger/index.js.map +3 -3
  42. package/dist/lib/modules/registry.js +1 -5
  43. package/dist/lib/modules/registry.js.map +2 -2
  44. package/dist/lib/query/engine.js +40 -54
  45. package/dist/lib/query/engine.js.map +2 -2
  46. package/dist/lib/query/types.js.map +1 -1
  47. package/dist/lib/redis/connection.js +2 -7
  48. package/dist/lib/redis/connection.js.map +2 -2
  49. package/dist/lib/search/config.js +2 -61
  50. package/dist/lib/search/config.js.map +2 -2
  51. package/dist/lib/search/tokenize.js +11 -23
  52. package/dist/lib/search/tokenize.js.map +2 -2
  53. package/dist/lib/version.js +1 -1
  54. package/dist/lib/version.js.map +1 -1
  55. package/dist/lib/webhooks/index.js +1 -13
  56. package/dist/lib/webhooks/index.js.map +2 -2
  57. package/dist/lib/webhooks/verify.js +7 -12
  58. package/dist/lib/webhooks/verify.js.map +2 -2
  59. package/dist/modules/analytics.js.map +2 -2
  60. package/dist/modules/overrides.js +1 -50
  61. package/dist/modules/overrides.js.map +2 -2
  62. package/dist/modules/payment_gateways/types.js +1 -8
  63. package/dist/modules/payment_gateways/types.js.map +2 -2
  64. package/dist/modules/registry.js +0 -1
  65. package/dist/modules/registry.js.map +2 -2
  66. package/dist/security/enabledModulesRegistry.js +11 -56
  67. package/dist/security/enabledModulesRegistry.js.map +2 -2
  68. package/jest.config.cjs +1 -2
  69. package/package.json +9 -19
  70. package/src/lib/ai/llm-provider.ts +0 -33
  71. package/src/lib/auth/__tests__/server.test.ts +0 -23
  72. package/src/lib/auth/server.ts +2 -23
  73. package/src/lib/bootstrap/__tests__/dynamicLoader.commandInterceptors.test.ts +8 -63
  74. package/src/lib/bootstrap/__tests__/dynamicLoader.tsconfig.test.ts +1 -21
  75. package/src/lib/bootstrap/dynamicLoader.ts +65 -213
  76. package/src/lib/commands/__tests__/command-interceptor-runner.test.ts +0 -28
  77. package/src/lib/commands/command-bus.ts +1 -5
  78. package/src/lib/commands/command-interceptor-runner.ts +2 -2
  79. package/src/lib/crud/__tests__/crud-factory.test.ts +0 -298
  80. package/src/lib/crud/__tests__/mutation-guard-registry.test.ts +0 -26
  81. package/src/lib/crud/enricher-runner.ts +11 -2
  82. package/src/lib/crud/factory.ts +1 -69
  83. package/src/lib/crud/interceptor-runner.ts +2 -2
  84. package/src/lib/crud/mutation-guard-registry.ts +2 -2
  85. package/src/lib/crud/types.ts +0 -3
  86. package/src/lib/data/__tests__/engine.bulk-suppress.test.ts +3 -18
  87. package/src/lib/data/engine.ts +16 -50
  88. package/src/lib/db/__tests__/mikro.test.ts +25 -0
  89. package/src/lib/db/mikro.ts +38 -13
  90. package/src/lib/db/pg-errors.ts +0 -58
  91. package/src/lib/di/container.ts +8 -72
  92. package/src/lib/email/__tests__/send.test.ts +1 -37
  93. package/src/lib/email/send.ts +0 -79
  94. package/src/lib/encryption/__tests__/subscriber.test.ts +2 -54
  95. package/src/lib/encryption/__tests__/tenantDataEncryptionService.test.ts +0 -58
  96. package/src/lib/encryption/subscriber.ts +14 -2
  97. package/src/lib/encryption/tenantDataEncryptionService.ts +18 -64
  98. package/src/lib/i18n/config.ts +2 -2
  99. package/src/lib/i18n/server.ts +2 -6
  100. package/src/lib/logger/__tests__/logger.test.ts +0 -77
  101. package/src/lib/logger/index.ts +1 -59
  102. package/src/lib/modules/registry.ts +0 -10
  103. package/src/lib/query/__tests__/engine.test.ts +13 -65
  104. package/src/lib/query/engine.ts +58 -75
  105. package/src/lib/query/types.ts +5 -4
  106. package/src/lib/redis/__tests__/connection.test.ts +0 -16
  107. package/src/lib/redis/connection.ts +7 -10
  108. package/src/lib/search/__tests__/config.test.ts +0 -177
  109. package/src/lib/search/config.ts +8 -106
  110. package/src/lib/search/tokenize.ts +11 -33
  111. package/src/lib/webhooks/__tests__/verify.test.ts +1 -20
  112. package/src/lib/webhooks/index.ts +1 -19
  113. package/src/lib/webhooks/verify.ts +9 -17
  114. package/src/modules/__tests__/registry.test.ts +2 -10
  115. package/src/modules/analytics.ts +0 -7
  116. package/src/modules/customer-auth.ts +0 -1
  117. package/src/modules/encryption.ts +0 -3
  118. package/src/modules/navigation/backendChrome.ts +0 -15
  119. package/src/modules/overrides.ts +0 -103
  120. package/src/modules/payment_gateways/types.ts +0 -41
  121. package/src/modules/registry.ts +0 -3
  122. package/src/security/enabledModulesRegistry.ts +12 -64
  123. package/dist/lib/ai/safety-identifier.js +0 -32
  124. package/dist/lib/ai/safety-identifier.js.map +0 -7
  125. package/dist/lib/auth/organizationScope.js +0 -34
  126. package/dist/lib/auth/organizationScope.js.map +0 -7
  127. package/dist/lib/bootstrap/clientOnlyModules.js +0 -55
  128. package/dist/lib/bootstrap/clientOnlyModules.js.map +0 -7
  129. package/dist/lib/data/consistency.js +0 -19
  130. package/dist/lib/data/consistency.js.map +0 -7
  131. package/dist/lib/db/entityMetadata.js +0 -26
  132. package/dist/lib/db/entityMetadata.js.map +0 -7
  133. package/dist/lib/logger/extension.js +0 -29
  134. package/dist/lib/logger/extension.js.map +0 -7
  135. package/dist/lib/modules/surfaceFingerprint.js +0 -47
  136. package/dist/lib/modules/surfaceFingerprint.js.map +0 -7
  137. package/dist/lib/query/ciphertext-search-warning.js +0 -45
  138. package/dist/lib/query/ciphertext-search-warning.js.map +0 -7
  139. package/dist/lib/search/auto-indexing.js +0 -14
  140. package/dist/lib/search/auto-indexing.js.map +0 -7
  141. package/dist/lib/search/availability.js +0 -107
  142. package/dist/lib/search/availability.js.map +0 -7
  143. package/dist/lib/search/tokenLookup.js +0 -46
  144. package/dist/lib/search/tokenLookup.js.map +0 -7
  145. package/dist/lib/telemetry/runtime.js +0 -35
  146. package/dist/lib/telemetry/runtime.js.map +0 -7
  147. package/dist/lib/webhooks/body.js +0 -57
  148. package/dist/lib/webhooks/body.js.map +0 -7
  149. package/dist/lib/webhooks/inbound-types.js +0 -1
  150. package/dist/lib/webhooks/inbound-types.js.map +0 -7
  151. package/dist/modules/widgets/extension-points.js +0 -96
  152. package/dist/modules/widgets/extension-points.js.map +0 -7
  153. package/dist/security/featurePolicy.js +0 -62
  154. package/dist/security/featurePolicy.js.map +0 -7
  155. package/scripts/versionSource.cjs +0 -65
  156. package/src/lib/__tests__/versionSource.test.ts +0 -46
  157. package/src/lib/ai/__tests__/llm-provider-contract.test.ts +0 -59
  158. package/src/lib/ai/__tests__/safety-identifier.test.ts +0 -71
  159. package/src/lib/ai/safety-identifier.ts +0 -73
  160. package/src/lib/auth/__tests__/organizationScope.test.ts +0 -107
  161. package/src/lib/auth/organizationScope.ts +0 -65
  162. package/src/lib/bootstrap/__tests__/clientOnlyModules.test.ts +0 -189
  163. package/src/lib/bootstrap/__tests__/dynamicLoader.aliasResolver.test.ts +0 -98
  164. package/src/lib/bootstrap/__tests__/dynamicLoader.appDi.test.ts +0 -211
  165. package/src/lib/bootstrap/__tests__/dynamicLoader.generatedCacheRecovery.test.ts +0 -192
  166. package/src/lib/bootstrap/clientOnlyModules.ts +0 -85
  167. package/src/lib/crud/__tests__/crud-factory.cache-user-scope.test.ts +0 -171
  168. package/src/lib/data/__tests__/consistency.test.ts +0 -38
  169. package/src/lib/data/consistency.ts +0 -17
  170. package/src/lib/db/__tests__/entityMetadata.test.ts +0 -38
  171. package/src/lib/db/__tests__/pg-errors.test.ts +0 -48
  172. package/src/lib/db/entityMetadata.ts +0 -43
  173. package/src/lib/di/__tests__/container-app-di-absent.test.ts +0 -155
  174. package/src/lib/di/__tests__/container-app-di.test.ts +0 -173
  175. package/src/lib/di/__tests__/registrar-error-log.test.ts +0 -154
  176. package/src/lib/i18n/__tests__/config.test.ts +0 -8
  177. package/src/lib/i18n/__tests__/server-dictionary-cache.test.ts +0 -30
  178. package/src/lib/i18n/__tests__/server-unbootstrapped.test.ts +0 -29
  179. package/src/lib/logger/extension.ts +0 -59
  180. package/src/lib/modules/__tests__/surfaceFingerprint.test.ts +0 -122
  181. package/src/lib/modules/surfaceFingerprint.ts +0 -87
  182. package/src/lib/query/__tests__/ciphertext-search-warning.test.ts +0 -178
  183. package/src/lib/query/ciphertext-search-warning.ts +0 -95
  184. package/src/lib/search/__tests__/availability.test.ts +0 -211
  185. package/src/lib/search/__tests__/tokenLookup.test.ts +0 -206
  186. package/src/lib/search/__tests__/tokenize.test.ts +0 -49
  187. package/src/lib/search/auto-indexing.ts +0 -22
  188. package/src/lib/search/availability.ts +0 -232
  189. package/src/lib/search/tokenLookup.ts +0 -133
  190. package/src/lib/telemetry/runtime.ts +0 -72
  191. package/src/lib/webhooks/__tests__/body.test.ts +0 -95
  192. package/src/lib/webhooks/body.ts +0 -70
  193. package/src/lib/webhooks/inbound-types.ts +0 -125
  194. package/src/modules/__tests__/nav-group-order-override.test.ts +0 -183
  195. package/src/modules/payment_gateways/__tests__/types.test.ts +0 -37
  196. package/src/modules/widgets/__tests__/extension-points.test.ts +0 -101
  197. package/src/modules/widgets/extension-points.ts +0 -524
  198. package/src/security/__tests__/featurePolicy.test.ts +0 -166
  199. package/src/security/featurePolicy.ts +0 -89
@@ -1,5 +1,5 @@
1
1
  import crypto from 'crypto'
2
- import { resolveSearchConfig, resolveSearchTokenLimits, type SearchConfig } from './config'
2
+ import { resolveSearchConfig, type SearchConfig } from './config'
3
3
 
4
4
  export type TokenizationResult = {
5
5
  tokens: string[]
@@ -20,28 +20,13 @@ function splitTokens(text: string, minLength: number): string[] {
20
20
  .filter((token) => token.length >= minLength)
21
21
  }
22
22
 
23
- function appendExpandedToken(
24
- token: string,
25
- config: SearchConfig,
26
- seen: Set<string>,
27
- tokens: string[],
28
- limit: number,
29
- ): void {
30
- const append = (candidate: string): boolean => {
31
- if (seen.has(candidate)) return tokens.length < limit
32
- seen.add(candidate)
33
- tokens.push(candidate)
34
- return tokens.length < limit
35
- }
36
-
37
- if (!config.enablePartials) {
38
- append(token)
39
- return
40
- }
41
-
42
- for (let length = config.minTokenLength; length <= token.length; length += 1) {
43
- if (!append(token.slice(0, length))) return
23
+ function expandToken(token: string, config: SearchConfig): string[] {
24
+ if (!config.enablePartials) return [token]
25
+ const results: string[] = []
26
+ for (let i = config.minTokenLength; i <= token.length; i += 1) {
27
+ results.push(token.slice(0, i))
44
28
  }
29
+ return results
45
30
  }
46
31
 
47
32
  export function hashToken(token: string, config?: SearchConfig): string {
@@ -51,17 +36,10 @@ export function hashToken(token: string, config?: SearchConfig): string {
51
36
 
52
37
  export function tokenizeText(text: string, config?: SearchConfig): TokenizationResult {
53
38
  const cfg = config ?? resolveSearchConfig()
54
- const limits = resolveSearchTokenLimits(cfg)
55
- const boundedText = limits.maxFieldChars > 0 ? text.slice(0, limits.maxFieldChars) : text
56
- const tokenLimit = limits.maxTokensPerField > 0 ? limits.maxTokensPerField : Number.POSITIVE_INFINITY
57
- const seen = new Set<string>()
58
- const tokens: string[] = []
59
-
60
- for (const token of splitTokens(boundedText, cfg.minTokenLength)) {
61
- if (tokens.length >= tokenLimit) break
62
- appendExpandedToken(token, cfg, seen, tokens, tokenLimit)
63
- }
64
-
39
+ const baseTokens = splitTokens(text, cfg.minTokenLength)
40
+ const expanded = baseTokens.flatMap((token) => expandToken(token, cfg))
41
+ const unique = Array.from(new Set(expanded))
42
+ const tokens = unique.filter((token) => token.length >= cfg.minTokenLength)
65
43
  const hashes = tokens.map((token) => hashToken(token, cfg))
66
44
  return { tokens, hashes }
67
45
  }
@@ -1,26 +1,7 @@
1
1
  import { signWebhookPayload } from '../sign'
2
- import { isWebhookTimestampWithinTolerance, verifyWebhookSignature } from '../verify'
2
+ import { verifyWebhookSignature } from '../verify'
3
3
  import { generateWebhookSecret } from '../secrets'
4
4
 
5
- describe('isWebhookTimestampWithinTolerance', () => {
6
- it('accepts timestamps inside the configured tolerance', () => {
7
- expect(isWebhookTimestampWithinTolerance('1700000000', 300, 1700000299)).toBe(true)
8
- })
9
-
10
- it('rejects stale timestamps outside the configured tolerance', () => {
11
- expect(isWebhookTimestampWithinTolerance('1700000000', 300, 1700000301)).toBe(false)
12
- })
13
-
14
- it('rejects timestamps too far in the future', () => {
15
- expect(isWebhookTimestampWithinTolerance('1700000601', 300, 1700000000)).toBe(false)
16
- })
17
-
18
- it('rejects malformed timestamps', () => {
19
- expect(isWebhookTimestampWithinTolerance('not-a-timestamp', 300, 1700000000)).toBe(false)
20
- expect(isWebhookTimestampWithinTolerance('1700000000abc', 300, 1700000000)).toBe(false)
21
- })
22
- })
23
-
24
5
  describe('verifyWebhookSignature', () => {
25
6
  const msgId = 'msg_test123'
26
7
  const body = '{"type":"test.event","data":{}}'
@@ -1,22 +1,4 @@
1
1
  export { signWebhookPayload, buildWebhookHeaders, generateMessageId } from './sign'
2
- export { WEBHOOK_SIGNATURE_TOLERANCE_SECONDS, isWebhookTimestampWithinTolerance, verifyWebhookSignature } from './verify'
2
+ export { verifyWebhookSignature } from './verify'
3
3
  export { generateWebhookSecret, parseWebhookSecret, isValidWebhookSecret } from './secrets'
4
- export {
5
- DEFAULT_WEBHOOK_BODY_LIMIT_BYTES,
6
- readBoundedRequestBody,
7
- resolveWebhookBodyLimitBytes,
8
- WebhookBodyTooLargeError,
9
- } from './body'
10
4
  export type { StandardWebhookHeaders, WebhookSigningKey, WebhookVerificationResult, StandardWebhookPayload } from './types'
11
- export type {
12
- InboundWebhookRequest,
13
- WebhookSourceCredentialField,
14
- WebhookSourceConfig,
15
- WebhookHandlerMeta,
16
- WebhookHandlerPayload,
17
- WebhookHandlerContext,
18
- WebhookHandler,
19
- WebhookHandlerRegistryEntry,
20
- WebhookHandlerResult,
21
- WebhookIngestionStatus,
22
- } from './inbound-types'
@@ -2,21 +2,7 @@ import { timingSafeEqual, createHmac } from 'node:crypto'
2
2
  import { parseWebhookSecret } from './secrets'
3
3
  import type { WebhookVerificationResult } from './types'
4
4
 
5
- export const WEBHOOK_SIGNATURE_TOLERANCE_SECONDS = 5 * 60 // 5 minutes
6
-
7
- export function isWebhookTimestampWithinTolerance(
8
- timestamp: string,
9
- toleranceSeconds: number = WEBHOOK_SIGNATURE_TOLERANCE_SECONDS,
10
- nowSeconds: number = Math.floor(Date.now() / 1000),
11
- ): boolean {
12
- const normalizedTimestamp = timestamp.trim()
13
- if (!/^\d+$/.test(normalizedTimestamp)) {
14
- return false
15
- }
16
-
17
- const timestampNum = Number.parseInt(normalizedTimestamp, 10)
18
- return Math.abs(nowSeconds - timestampNum) <= toleranceSeconds
19
- }
5
+ const TOLERANCE_SECONDS = 5 * 60 // 5 minutes
20
6
 
21
7
  /**
22
8
  * Verify a Standard Webhooks signature against one or more secrets.
@@ -28,9 +14,15 @@ export function verifyWebhookSignature(
28
14
  body: string,
29
15
  signatureHeader: string,
30
16
  secrets: string[],
31
- toleranceSeconds: number = WEBHOOK_SIGNATURE_TOLERANCE_SECONDS,
17
+ toleranceSeconds: number = TOLERANCE_SECONDS,
32
18
  ): WebhookVerificationResult {
33
- if (!isWebhookTimestampWithinTolerance(timestamp, toleranceSeconds)) {
19
+ const timestampNum = parseInt(timestamp, 10)
20
+ if (isNaN(timestampNum)) {
21
+ return { valid: false }
22
+ }
23
+
24
+ const now = Math.floor(Date.now() / 1000)
25
+ if (Math.abs(now - timestampNum) > toleranceSeconds) {
34
26
  return { valid: false }
35
27
  }
36
28
 
@@ -94,11 +94,7 @@ describe('CLI Modules Registry', () => {
94
94
  {
95
95
  id: 'auth',
96
96
  defaultEncryptionMaps: [
97
- {
98
- entityId: 'auth:user',
99
- keyScope: 'system',
100
- fields: [{ field: 'email', hashField: 'email_hash' }],
101
- },
97
+ { entityId: 'auth:user', fields: [{ field: 'email', hashField: 'email_hash' }] },
102
98
  ],
103
99
  },
104
100
  {
@@ -110,11 +106,7 @@ describe('CLI Modules Registry', () => {
110
106
  ])
111
107
 
112
108
  expect(maps).toEqual([
113
- {
114
- entityId: 'auth:user',
115
- keyScope: 'system',
116
- fields: [{ field: 'email', hashField: 'email_hash' }],
117
- },
109
+ { entityId: 'auth:user', fields: [{ field: 'email', hashField: 'email_hash' }] },
118
110
  { entityId: 'customers:customer_comment', fields: [{ field: 'body', hashField: null }] },
119
111
  ])
120
112
  })
@@ -13,13 +13,6 @@ export type AnalyticsEntityTypeConfig = {
13
13
  schema?: string
14
14
  dateField: string
15
15
  defaultScopeFields: string[]
16
- /**
17
- * Field name (a key of `fieldMappings`) holding the per-row currency of monetary
18
- * columns. Declaring it lets analytics consumers verify that the rows they aggregate
19
- * all carry one currency before labelling the result with it (#4676). Omit it for
20
- * entities whose amounts are not per-row denominated.
21
- */
22
- currencyField?: string
23
16
  }
24
17
 
25
18
  export type AnalyticsFieldMapping = {
@@ -20,7 +20,6 @@ export interface CustomerAuthContext {
20
20
  customerEntityId?: string | null
21
21
  personEntityId?: string | null
22
22
  resolvedFeatures: string[]
23
- isPortalAdmin?: boolean
24
23
  }
25
24
 
26
25
  export type CustomerUser = {
@@ -3,10 +3,7 @@ export type ModuleEncryptionFieldRule = {
3
3
  hashField?: string | null
4
4
  }
5
5
 
6
- export type EncryptionKeyScope = 'tenant' | 'system'
7
-
8
6
  export type ModuleEncryptionMap = {
9
7
  entityId: string
10
- keyScope?: EncryptionKeyScope
11
8
  fields: ModuleEncryptionFieldRule[]
12
9
  }
@@ -44,23 +44,9 @@ export type BackendChromeBrand = {
44
44
  logo?: {
45
45
  src: string
46
46
  alt?: string
47
- preserveAspectRatio?: boolean
48
47
  } | null
49
48
  }
50
49
 
51
- /**
52
- * The organization the current request is scoped to, resolved server-side.
53
- *
54
- * Distinct from `brand`, which is a *branding* channel and only populates when the organization has a
55
- * logo configured. This is always present when a single organization is in scope, so UI can label
56
- * "you are viewing: <name>" without a second round trip. `null` under an all-organizations selection,
57
- * when no organization is in scope, or when the lookup fails.
58
- */
59
- export type BackendChromeCurrentOrganization = {
60
- id: string
61
- name: string
62
- }
63
-
64
50
  export type BackendChromePayload = {
65
51
  groups: BackendChromeNavGroup[]
66
52
  settingsSections: BackendChromeSectionGroup[]
@@ -70,5 +56,4 @@ export type BackendChromePayload = {
70
56
  grantedFeatures: string[]
71
57
  roles: string[]
72
58
  brand?: BackendChromeBrand | null
73
- currentOrganization?: BackendChromeCurrentOrganization | null
74
59
  }
@@ -116,21 +116,6 @@ export interface EncryptionOverridesShape {
116
116
  maps?: EncryptionMapOverridesMap | LooseOverrideMap
117
117
  }
118
118
 
119
- /**
120
- * Backend navigation ordering.
121
- *
122
- * `groupOrder` **prepends** sidebar nav group ids: the ids listed here rank ahead of every other
123
- * group, in the order given, and any group not named keeps the ordering it has today. Prepending
124
- * rather than replacing means an app that only cares about its own group lists that one id, instead of
125
- * having to enumerate every shipped group and accidentally demoting the ones it forgot.
126
- *
127
- * This is a *default*, applied beneath both role and user sidebar preferences — an operator's own
128
- * arrangement still wins.
129
- */
130
- export interface NavOverridesShape {
131
- groupOrder?: string[]
132
- }
133
-
134
119
  /**
135
120
  * Umbrella shape for `entry.overrides`. Every key is optional; a
136
121
  * downstream app sets only the domains it cares about.
@@ -151,7 +136,6 @@ export interface ModuleOverrides {
151
136
  acl?: AclOverridesShape
152
137
  di?: DiOverridesMap | LooseOverrideMap
153
138
  encryption?: EncryptionOverridesShape
154
- nav?: NavOverridesShape
155
139
  }
156
140
 
157
141
  /**
@@ -191,7 +175,6 @@ export type ModuleOverrideDomain =
191
175
  | 'acl'
192
176
  | 'di'
193
177
  | 'encryption'
194
- | 'nav'
195
178
 
196
179
  export interface ModuleOverrideEntry<TShape> {
197
180
  moduleId: string
@@ -244,7 +227,6 @@ const DOMAIN_KEYS: ModuleOverrideDomain[] = [
244
227
  'acl',
245
228
  'di',
246
229
  'encryption',
247
- 'nav',
248
230
  ]
249
231
 
250
232
  const TRACKING_ISSUE_HINT =
@@ -459,71 +441,6 @@ const encryptionMapOverrideStore: OverrideStore<ModuleEncryptionMap> = { modules
459
441
  const diOverrideStore: OverrideStore<Exclude<DiBindingOverride, null>> = { modules: {}, programmatic: {} }
460
442
  const setupOverridesByModule: Record<string, SetupOverridesShape> = {}
461
443
 
462
- /**
463
- * Sidebar nav ordering state.
464
- *
465
- * Persisted on `globalThis` rather than in a module-local variable. This is the one override domain
466
- * whose consumer lives in a *different package* (`@open-mercato/core`'s backend chrome reads what the
467
- * app's bootstrap wrote), and standalone builds can evaluate `@open-mercato/shared` through more than
468
- * one server chunk — bootstrap would store the value in one instance while the reader saw `null` from
469
- * another. See `.ai/lessons.md`, "Global registries in publishable packages must use `globalThis`".
470
- *
471
- * Two tiers, matching every other override domain and the documented resolution order: programmatic
472
- * calls win over `modules.ts` inline declarations.
473
- */
474
- const GLOBAL_NAV_OVERRIDE_STATE_KEY = '__openMercatoNavOverrideState__'
475
-
476
- type NavOverrideState = {
477
- /** From `modules.ts` inline `overrides.nav`, with the module entry that supplied it. */
478
- modules: { moduleId: string; groupOrder: string[] } | null
479
- /** From `applyNavGroupOrderOverrides`. Takes precedence over `modules`. */
480
- programmatic: string[] | null
481
- }
482
-
483
- function getNavOverrideState(): NavOverrideState {
484
- const existing = (globalThis as Record<string, unknown>)[GLOBAL_NAV_OVERRIDE_STATE_KEY]
485
- if (existing && typeof existing === 'object') {
486
- const typed = existing as NavOverrideState
487
- if ('modules' in typed && 'programmatic' in typed) return typed
488
- }
489
- const initial: NavOverrideState = { modules: null, programmatic: null }
490
- ;(globalThis as Record<string, unknown>)[GLOBAL_NAV_OVERRIDE_STATE_KEY] = initial
491
- return initial
492
- }
493
-
494
- /** Drops blank/duplicate ids and returns `null` when nothing usable remains. */
495
- function normalizeNavGroupOrder(value: unknown): string[] | null {
496
- if (!Array.isArray(value)) return null
497
- const ids = Array.from(
498
- new Set(
499
- value
500
- .filter((id): id is string => typeof id === 'string' && id.trim().length > 0)
501
- .map((id) => id.trim()),
502
- ),
503
- )
504
- return ids.length > 0 ? ids : null
505
- }
506
-
507
- /**
508
- * Programmatic nav ordering, for env-driven boot decisions and tests. Takes precedence over
509
- * `modules.ts` inline `overrides.nav`, consistent with the other domains' programmatic tier. Pass
510
- * `null` to clear it and fall back to the inline declaration.
511
- */
512
- export function applyNavGroupOrderOverrides(groupOrder: string[] | null): void {
513
- getNavOverrideState().programmatic = groupOrder === null ? null : normalizeNavGroupOrder(groupOrder)
514
- }
515
-
516
- /**
517
- * Nav group ids an app wants ranked ahead of the built-in order, or `null` when none is configured.
518
- *
519
- * Consumers MUST treat `null` as "use the shipped ordering unchanged" — this is a default, applied
520
- * beneath role and user sidebar preferences.
521
- */
522
- export function getNavGroupOrderOverride(): readonly string[] | null {
523
- const state = getNavOverrideState()
524
- return state.programmatic ?? state.modules?.groupOrder ?? null
525
- }
526
-
527
444
  function normalizeIdOverrideKey(key: string, label: string): string | null {
528
445
  if (typeof key !== 'string') return null
529
446
  const trimmed = key.trim()
@@ -804,9 +721,6 @@ export function resetModuleContractOverridesForTests(): void {
804
721
  clearStore(encryptionMapOverrideStore)
805
722
  clearStore(diOverrideStore)
806
723
  for (const key of Object.keys(setupOverridesByModule)) delete setupOverridesByModule[key]
807
- const navState = getNavOverrideState()
808
- navState.modules = null
809
- navState.programmatic = null
810
724
  }
811
725
 
812
726
  /**
@@ -1586,24 +1500,7 @@ function encryptionOverridesApplier(entries: ReadonlyArray<ModuleOverrideEntry<E
1586
1500
  }
1587
1501
  }
1588
1502
 
1589
- function navOverridesApplier(entries: ReadonlyArray<ModuleOverrideEntry<NavOverridesShape>>): void {
1590
- const state = getNavOverrideState()
1591
- for (const entry of entries) {
1592
- const groupOrder = normalizeNavGroupOrder(entry.overrides?.groupOrder)
1593
- if (!groupOrder) continue
1594
- if (state.modules && state.modules.moduleId !== entry.moduleId) {
1595
- logger.warn('nav.groupOrder declared by more than one module — the later one wins', {
1596
- previousModuleId: state.modules.moduleId,
1597
- moduleId: entry.moduleId,
1598
- hint: 'Sidebar group ordering is a single app-wide decision; declare it on one module entry.',
1599
- })
1600
- }
1601
- state.modules = { moduleId: entry.moduleId, groupOrder }
1602
- }
1603
- }
1604
-
1605
1503
  function registerBuiltInModuleOverrideAppliers(): void {
1606
- registerModuleOverrideApplier<NavOverridesShape>('nav', navOverridesApplier)
1607
1504
  registerModuleOverrideApplier<RoutesOverridesShape>('routes', routesOverridesApplier)
1608
1505
  registerModuleOverrideApplier<EventsOverridesShape>('events', eventsOverridesApplier)
1609
1506
  registerModuleOverrideApplier<WorkerOverridesMap>('workers', workersOverridesApplier)
@@ -1,5 +1,3 @@
1
- import { DEFAULT_WEBHOOK_BODY_LIMIT_BYTES } from '../../lib/webhooks/body'
2
-
3
1
  // ── Unified Payment Status ──────────────────────────────────────────────────
4
2
 
5
3
  export type UnifiedPaymentStatus =
@@ -114,32 +112,6 @@ export type PaymentGatewayClientSession =
114
112
  | EmbeddedPaymentGatewayClientSession
115
113
  | RedirectPaymentGatewayClientSession
116
114
 
117
- // ── Order Amount Reconciliation ─────────────────────────────────────────────
118
-
119
- export interface PaymentGatewayScope {
120
- organizationId: string
121
- tenantId: string
122
- }
123
-
124
- export interface PaymentOrderTotal {
125
- orderId: string
126
- currencyCode: string
127
- amountDue: number
128
- }
129
-
130
- /**
131
- * Contract a module that owns orders implements so `payment_gateways` can
132
- * reconcile a caller-supplied session amount against the authoritative amount
133
- * due, without depending on that module. Implementations MUST scope the lookup
134
- * by tenant and organization and MUST return `null` for anything outside the
135
- * caller's scope, so an out-of-scope order is indistinguishable from a missing
136
- * one. The `sales` module registers the default implementation under the
137
- * `paymentOrderTotalResolver` DI name.
138
- */
139
- export interface PaymentOrderTotalResolver {
140
- resolveOrderTotal(orderId: string, scope: PaymentGatewayScope): Promise<PaymentOrderTotal | null>
141
- }
142
-
143
115
  // ── Input / Output Types ────────────────────────────────────────────────────
144
116
 
145
117
  export interface CreateSessionInput {
@@ -253,7 +225,6 @@ export interface WebhookHandlerRegistration {
253
225
  handler: (input: VerifyWebhookInput) => Promise<WebhookEvent>
254
226
  queue?: string
255
227
  readSessionIdHint?: (payload: Record<string, unknown> | null) => string | null
256
- maxBodyBytes?: number
257
228
  }
258
229
 
259
230
  // ── Adapter Registry Options ────────────────────────────────────────────────
@@ -348,25 +319,13 @@ export function registerWebhookHandler(
348
319
  options?: {
349
320
  queue?: string
350
321
  readSessionIdHint?: (payload: Record<string, unknown> | null) => string | null
351
- maxBodyBytes?: number
352
322
  },
353
323
  ): () => void {
354
- if (
355
- options?.maxBodyBytes !== undefined
356
- && (!Number.isSafeInteger(options.maxBodyBytes)
357
- || options.maxBodyBytes <= 0
358
- || options.maxBodyBytes > DEFAULT_WEBHOOK_BODY_LIMIT_BYTES)
359
- ) {
360
- throw new Error(
361
- `[internal] Payment gateway webhook maxBodyBytes must be a positive safe integer no greater than ${DEFAULT_WEBHOOK_BODY_LIMIT_BYTES}`,
362
- )
363
- }
364
324
  const webhookHandlerRegistry = getWebhookHandlerRegistry()
365
325
  webhookHandlerRegistry.set(providerKey, {
366
326
  handler,
367
327
  queue: options?.queue,
368
328
  readSessionIdHint: options?.readSessionIdHint,
369
- maxBodyBytes: options?.maxBodyBytes,
370
329
  })
371
330
  return () => {
372
331
  webhookHandlerRegistry.delete(providerKey)
@@ -194,8 +194,6 @@ export type ModuleWorker = {
194
194
  moduleId?: string
195
195
  queue: string
196
196
  concurrency: number
197
- lockDuration?: number
198
- maxStalledCount?: number
199
197
  handler: ModuleWorkerHandler
200
198
  }
201
199
 
@@ -515,7 +513,6 @@ export function getDefaultEncryptionMaps(modules: Module[]): import('./encryptio
515
513
  moduleId: mod.id,
516
514
  map: {
517
515
  entityId: entry.entityId,
518
- ...(entry.keyScope ? { keyScope: entry.keyScope } : {}),
519
516
  fields: entry.fields.map((field) => ({
520
517
  field: field.field,
521
518
  hashField: field.hashField ?? null,
@@ -31,7 +31,6 @@ type FeatureRegistry = {
31
31
  enabledModuleSet: Set<string>
32
32
  featureToModule: Map<string, string>
33
33
  prefixToModule: Map<string, string>
34
- concreteFeatureIds: string[]
35
34
  }
36
35
 
37
36
  let cachedRegistry: FeatureRegistry | null = null
@@ -42,63 +41,23 @@ function buildRegistry(modules: readonly Module[]): FeatureRegistry {
42
41
  const enabledModuleSet = new Set(enabledModuleIds)
43
42
  const featureToModule = new Map<string, string>()
44
43
  const prefixToModule = new Map<string, string>()
45
- const concreteFeatureIds: string[] = []
46
- const concreteFeatureSet = new Set<string>()
47
-
48
- const addConcreteFeature = (featureId: string, owningModule: string, authoritative: boolean) => {
49
- if (!featureId || featureId === '*' || featureId.endsWith('.*')) return
50
- if (!concreteFeatureSet.has(featureId)) {
51
- concreteFeatureSet.add(featureId)
52
- concreteFeatureIds.push(featureId)
53
- }
54
- if (authoritative || !featureToModule.has(featureId)) {
55
- featureToModule.set(featureId, owningModule)
56
- }
57
- const dot = featureId.indexOf('.')
58
- if (dot > 0) {
59
- const prefix = featureId.slice(0, dot)
60
- if (!prefixToModule.has(prefix)) prefixToModule.set(prefix, owningModule)
61
- }
62
- }
63
-
64
44
  for (const mod of modules) {
65
45
  const features = mod.features
66
- if (Array.isArray(features)) {
67
- for (const feature of features) {
68
- if (!feature || typeof feature.id !== 'string' || !feature.id) continue
69
- const declared = typeof feature.module === 'string' && feature.module.length > 0
70
- ? feature.module
71
- : mod.id
72
- addConcreteFeature(feature.id, declared, true)
73
- }
74
- }
75
-
76
- const customerDefaults = mod.setup?.defaultCustomerRoleFeatures
77
- if (customerDefaults) {
78
- for (const roleFeatures of Object.values(customerDefaults)) {
79
- if (!Array.isArray(roleFeatures)) continue
80
- for (const featureId of roleFeatures) {
81
- if (typeof featureId === 'string') addConcreteFeature(featureId, mod.id, false)
82
- }
46
+ if (!Array.isArray(features)) continue
47
+ for (const feature of features) {
48
+ if (!feature || typeof feature.id !== 'string' || !feature.id) continue
49
+ const declared = typeof feature.module === 'string' && feature.module.length > 0
50
+ ? feature.module
51
+ : mod.id
52
+ featureToModule.set(feature.id, declared)
53
+ const dot = feature.id.indexOf('.')
54
+ if (dot > 0) {
55
+ const prefix = feature.id.slice(0, dot)
56
+ if (!prefixToModule.has(prefix)) prefixToModule.set(prefix, declared)
83
57
  }
84
58
  }
85
-
86
- if (Array.isArray(mod.frontendRoutes)) {
87
- for (const route of mod.frontendRoutes) {
88
- if (!Array.isArray(route.requireCustomerFeatures)) continue
89
- for (const featureId of route.requireCustomerFeatures) {
90
- if (typeof featureId === 'string') addConcreteFeature(featureId, mod.id, false)
91
- }
92
- }
93
- }
94
- }
95
- return {
96
- enabledModuleIds,
97
- enabledModuleSet,
98
- featureToModule,
99
- prefixToModule,
100
- concreteFeatureIds,
101
59
  }
60
+ return { enabledModuleIds, enabledModuleSet, featureToModule, prefixToModule }
102
61
  }
103
62
 
104
63
  function getRegistry(): FeatureRegistry | null {
@@ -134,17 +93,6 @@ export function getEnabledModuleIds(): string[] {
134
93
  return registry ? [...registry.enabledModuleIds] : []
135
94
  }
136
95
 
137
- /** @internal Infrastructure input for concrete feature-policy projection. */
138
- export function getConcreteFeatureIds(): string[] {
139
- const registry = getRegistry()
140
- return registry ? [...registry.concreteFeatureIds] : []
141
- }
142
-
143
- /** @internal Distinguishes an empty registry from an unavailable bootstrap registry. */
144
- export function hasEnabledModulesRegistry(): boolean {
145
- return getRegistry() !== null
146
- }
147
-
148
96
  /**
149
97
  * Filters a raw granted-features list down to the grants whose owning
150
98
  * module is currently enabled. Expands `*` (superadmin) into one wildcard
@@ -1,32 +0,0 @@
1
- import crypto from "node:crypto";
2
- const SAFETY_IDENTIFIER_SECRET_LABEL = "open-mercato:ai-safety-identifier:v1";
3
- const derivedSecretCache = /* @__PURE__ */ new Map();
4
- function readBaseSecret(explicit) {
5
- const secret = explicit ?? process.env.JWT_SECRET;
6
- if (!secret) {
7
- throw new Error("[internal] JWT_SECRET is not set; cannot derive AI safety-identifier secret");
8
- }
9
- return secret;
10
- }
11
- function deriveAiSafetyIdentifierSecret(baseSecret) {
12
- const base = readBaseSecret(baseSecret);
13
- const cached = derivedSecretCache.get(base);
14
- if (cached !== void 0) return cached;
15
- const derived = crypto.createHmac("sha256", base).update(SAFETY_IDENTIFIER_SECRET_LABEL).digest("hex");
16
- derivedSecretCache.set(base, derived);
17
- return derived;
18
- }
19
- function computeEndUserIdentifier(tenantId, userId, options) {
20
- const normalizedUser = (userId ?? "").trim();
21
- if (!normalizedUser) {
22
- throw new Error("[internal] computeEndUserIdentifier requires a non-empty userId");
23
- }
24
- const key = deriveAiSafetyIdentifierSecret(options?.baseSecret);
25
- const salt = (tenantId ?? "").trim();
26
- return crypto.createHmac("sha256", key).update(`${salt}:${normalizedUser}`).digest("hex");
27
- }
28
- export {
29
- computeEndUserIdentifier,
30
- deriveAiSafetyIdentifierSecret
31
- };
32
- //# sourceMappingURL=safety-identifier.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/lib/ai/safety-identifier.ts"],
4
- "sourcesContent": ["/**\n * End-user safety identifiers for AI provider calls.\n *\n * Providers such as OpenAI let developers attach an opaque per-end-user\n * identifier to each request so abuse enforcement can target one user instead\n * of suspending the whole API-key organization. We never send a reversible id:\n * the value is a tenant-salted HMAC computed from the platform's existing auth\n * secret, so no PII or internal id leaves the platform and the same end user in\n * two tenants produces two unrelated hashes.\n *\n * The per-process secret derivation mirrors `deriveJwtAudienceSecret`\n * (`@open-mercato/shared/lib/auth/jwt`): one HMAC from the base `JWT_SECRET`\n * under a versioned purpose label, memoized for the process lifetime. No new\n * secret to provision.\n *\n * @see .ai/specs/2026-06-04-ai-input-moderation-and-safety-identifiers.md\n */\n\nimport crypto from 'node:crypto'\n\nconst SAFETY_IDENTIFIER_SECRET_LABEL = 'open-mercato:ai-safety-identifier:v1'\n\nconst derivedSecretCache = new Map<string, string>()\n\nfunction readBaseSecret(explicit?: string): string {\n const secret = explicit ?? process.env.JWT_SECRET\n if (!secret) {\n throw new Error('[internal] JWT_SECRET is not set; cannot derive AI safety-identifier secret')\n }\n return secret\n}\n\n/**\n * Derive the per-process safety-identifier HMAC key from the base auth secret.\n *\n * Deterministic HMAC-SHA256 of a versioned purpose label keyed by the base\n * secret, memoized per base secret. Rotating the base secret rotates every\n * derived identifier \u2014 documented as accepted (identifiers are advisory\n * provider-side metadata, not an in-platform security control).\n */\nexport function deriveAiSafetyIdentifierSecret(baseSecret?: string): string {\n const base = readBaseSecret(baseSecret)\n const cached = derivedSecretCache.get(base)\n if (cached !== undefined) return cached\n const derived = crypto\n .createHmac('sha256', base)\n .update(SAFETY_IDENTIFIER_SECRET_LABEL)\n .digest('hex')\n derivedSecretCache.set(base, derived)\n return derived\n}\n\n/**\n * Compute the opaque end-user safety identifier for a (tenant, user) pair.\n *\n * Returns a 64-char lowercase hex HMAC-SHA256 of `${tenantId}:${userId}` keyed\n * by the derived secret. Throws (with an `[internal]` message) when the base\n * secret is missing or `userId` is empty \u2014 callers in the runtime wrap this in\n * a best-effort try/catch so identifier-derivation failures never break chat.\n */\nexport function computeEndUserIdentifier(\n tenantId: string | null | undefined,\n userId: string,\n options?: { baseSecret?: string },\n): string {\n const normalizedUser = (userId ?? '').trim()\n if (!normalizedUser) {\n throw new Error('[internal] computeEndUserIdentifier requires a non-empty userId')\n }\n const key = deriveAiSafetyIdentifierSecret(options?.baseSecret)\n const salt = (tenantId ?? '').trim()\n return crypto.createHmac('sha256', key).update(`${salt}:${normalizedUser}`).digest('hex')\n}\n"],
5
- "mappings": "AAkBA,OAAO,YAAY;AAEnB,MAAM,iCAAiC;AAEvC,MAAM,qBAAqB,oBAAI,IAAoB;AAEnD,SAAS,eAAe,UAA2B;AACjD,QAAM,SAAS,YAAY,QAAQ,IAAI;AACvC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,6EAA6E;AAAA,EAC/F;AACA,SAAO;AACT;AAUO,SAAS,+BAA+B,YAA6B;AAC1E,QAAM,OAAO,eAAe,UAAU;AACtC,QAAM,SAAS,mBAAmB,IAAI,IAAI;AAC1C,MAAI,WAAW,OAAW,QAAO;AACjC,QAAM,UAAU,OACb,WAAW,UAAU,IAAI,EACzB,OAAO,8BAA8B,EACrC,OAAO,KAAK;AACf,qBAAmB,IAAI,MAAM,OAAO;AACpC,SAAO;AACT;AAUO,SAAS,yBACd,UACA,QACA,SACQ;AACR,QAAM,kBAAkB,UAAU,IAAI,KAAK;AAC3C,MAAI,CAAC,gBAAgB;AACnB,UAAM,IAAI,MAAM,iEAAiE;AAAA,EACnF;AACA,QAAM,MAAM,+BAA+B,SAAS,UAAU;AAC9D,QAAM,QAAQ,YAAY,IAAI,KAAK;AACnC,SAAO,OAAO,WAAW,UAAU,GAAG,EAAE,OAAO,GAAG,IAAI,IAAI,cAAc,EAAE,EAAE,OAAO,KAAK;AAC1F;",
6
- "names": []
7
- }