@open-mercato/shared 0.4.2-canary-c02407ff85

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 (324) hide show
  1. package/build.mjs +101 -0
  2. package/dist/index.js +1 -0
  3. package/dist/index.js.map +7 -0
  4. package/dist/lib/api/crud.js +47 -0
  5. package/dist/lib/api/crud.js.map +7 -0
  6. package/dist/lib/api/scoped.js +140 -0
  7. package/dist/lib/api/scoped.js.map +7 -0
  8. package/dist/lib/auth/jwt.js +34 -0
  9. package/dist/lib/auth/jwt.js.map +7 -0
  10. package/dist/lib/auth/server.js +157 -0
  11. package/dist/lib/auth/server.js.map +7 -0
  12. package/dist/lib/boolean.js +22 -0
  13. package/dist/lib/boolean.js.map +7 -0
  14. package/dist/lib/bootstrap/appResolver.js +43 -0
  15. package/dist/lib/bootstrap/appResolver.js.map +7 -0
  16. package/dist/lib/bootstrap/dynamicLoader.js +108 -0
  17. package/dist/lib/bootstrap/dynamicLoader.js.map +7 -0
  18. package/dist/lib/bootstrap/factory.js +59 -0
  19. package/dist/lib/bootstrap/factory.js.map +7 -0
  20. package/dist/lib/bootstrap/index.js +11 -0
  21. package/dist/lib/bootstrap/index.js.map +7 -0
  22. package/dist/lib/bootstrap/types.js +1 -0
  23. package/dist/lib/bootstrap/types.js.map +7 -0
  24. package/dist/lib/cache/segments.js +36 -0
  25. package/dist/lib/cache/segments.js.map +7 -0
  26. package/dist/lib/cli/progress.js +46 -0
  27. package/dist/lib/cli/progress.js.map +7 -0
  28. package/dist/lib/commands/command-bus.js +285 -0
  29. package/dist/lib/commands/command-bus.js.map +7 -0
  30. package/dist/lib/commands/customFieldSnapshots.js +66 -0
  31. package/dist/lib/commands/customFieldSnapshots.js.map +7 -0
  32. package/dist/lib/commands/helpers.js +98 -0
  33. package/dist/lib/commands/helpers.js.map +7 -0
  34. package/dist/lib/commands/index.js +8 -0
  35. package/dist/lib/commands/index.js.map +7 -0
  36. package/dist/lib/commands/operationMetadata.js +32 -0
  37. package/dist/lib/commands/operationMetadata.js.map +7 -0
  38. package/dist/lib/commands/registry.js +43 -0
  39. package/dist/lib/commands/registry.js.map +7 -0
  40. package/dist/lib/commands/scope.js +44 -0
  41. package/dist/lib/commands/scope.js.map +7 -0
  42. package/dist/lib/commands/types.js +8 -0
  43. package/dist/lib/commands/types.js.map +7 -0
  44. package/dist/lib/crud/cache-stats.js +98 -0
  45. package/dist/lib/crud/cache-stats.js.map +7 -0
  46. package/dist/lib/crud/cache.js +175 -0
  47. package/dist/lib/crud/cache.js.map +7 -0
  48. package/dist/lib/crud/custom-fields-client.js +52 -0
  49. package/dist/lib/crud/custom-fields-client.js.map +7 -0
  50. package/dist/lib/crud/custom-fields.js +467 -0
  51. package/dist/lib/crud/custom-fields.js.map +7 -0
  52. package/dist/lib/crud/errors.js +24 -0
  53. package/dist/lib/crud/errors.js.map +7 -0
  54. package/dist/lib/crud/exporters.js +154 -0
  55. package/dist/lib/crud/exporters.js.map +7 -0
  56. package/dist/lib/crud/factory.js +1311 -0
  57. package/dist/lib/crud/factory.js.map +7 -0
  58. package/dist/lib/crud/types.js +1 -0
  59. package/dist/lib/crud/types.js.map +7 -0
  60. package/dist/lib/custom-fields/normalize.js +36 -0
  61. package/dist/lib/custom-fields/normalize.js.map +7 -0
  62. package/dist/lib/data/engine.js +396 -0
  63. package/dist/lib/data/engine.js.map +7 -0
  64. package/dist/lib/db/escapeLikePattern.js +5 -0
  65. package/dist/lib/db/escapeLikePattern.js.map +7 -0
  66. package/dist/lib/db/mikro.js +82 -0
  67. package/dist/lib/db/mikro.js.map +7 -0
  68. package/dist/lib/di/container.js +94 -0
  69. package/dist/lib/di/container.js.map +7 -0
  70. package/dist/lib/email/send.js +12 -0
  71. package/dist/lib/email/send.js.map +7 -0
  72. package/dist/lib/encryption/aes.js +58 -0
  73. package/dist/lib/encryption/aes.js.map +7 -0
  74. package/dist/lib/encryption/customFieldValues.js +49 -0
  75. package/dist/lib/encryption/customFieldValues.js.map +7 -0
  76. package/dist/lib/encryption/entityFields.js +26 -0
  77. package/dist/lib/encryption/entityFields.js.map +7 -0
  78. package/dist/lib/encryption/entityIds.js +80 -0
  79. package/dist/lib/encryption/entityIds.js.map +7 -0
  80. package/dist/lib/encryption/find.js +45 -0
  81. package/dist/lib/encryption/find.js.map +7 -0
  82. package/dist/lib/encryption/indexDoc.js +69 -0
  83. package/dist/lib/encryption/indexDoc.js.map +7 -0
  84. package/dist/lib/encryption/kms.js +282 -0
  85. package/dist/lib/encryption/kms.js.map +7 -0
  86. package/dist/lib/encryption/subscriber.js +330 -0
  87. package/dist/lib/encryption/subscriber.js.map +7 -0
  88. package/dist/lib/encryption/tenantDataEncryptionService.js +252 -0
  89. package/dist/lib/encryption/tenantDataEncryptionService.js.map +7 -0
  90. package/dist/lib/encryption/toggles.js +18 -0
  91. package/dist/lib/encryption/toggles.js.map +7 -0
  92. package/dist/lib/entities/naming.js +9 -0
  93. package/dist/lib/entities/naming.js.map +7 -0
  94. package/dist/lib/entities/system-entities.js +43 -0
  95. package/dist/lib/entities/system-entities.js.map +7 -0
  96. package/dist/lib/frontend/organizationEvents.js +41 -0
  97. package/dist/lib/frontend/organizationEvents.js.map +7 -0
  98. package/dist/lib/frontend/useOrganizationScope.js +32 -0
  99. package/dist/lib/frontend/useOrganizationScope.js.map +7 -0
  100. package/dist/lib/hotkeys/index.js +128 -0
  101. package/dist/lib/hotkeys/index.js.map +7 -0
  102. package/dist/lib/i18n/app-dictionaries.js +17 -0
  103. package/dist/lib/i18n/app-dictionaries.js.map +7 -0
  104. package/dist/lib/i18n/config.js +7 -0
  105. package/dist/lib/i18n/config.js.map +7 -0
  106. package/dist/lib/i18n/context.js +50 -0
  107. package/dist/lib/i18n/context.js.map +7 -0
  108. package/dist/lib/i18n/server.js +68 -0
  109. package/dist/lib/i18n/server.js.map +7 -0
  110. package/dist/lib/i18n/translate.js +45 -0
  111. package/dist/lib/i18n/translate.js.map +7 -0
  112. package/dist/lib/indexers/error-log.js +82 -0
  113. package/dist/lib/indexers/error-log.js.map +7 -0
  114. package/dist/lib/indexers/status-log.js +80 -0
  115. package/dist/lib/indexers/status-log.js.map +7 -0
  116. package/dist/lib/lib/auth/jwt.js +34 -0
  117. package/dist/lib/lib/auth/jwt.js.map +7 -0
  118. package/dist/lib/lib/auth/server.js +77 -0
  119. package/dist/lib/lib/auth/server.js.map +7 -0
  120. package/dist/lib/lib/email/send.js +12 -0
  121. package/dist/lib/lib/email/send.js.map +7 -0
  122. package/dist/lib/lib/i18n/config.js +7 -0
  123. package/dist/lib/lib/i18n/config.js.map +7 -0
  124. package/dist/lib/lib/i18n/context.js +31 -0
  125. package/dist/lib/lib/i18n/context.js.map +7 -0
  126. package/dist/lib/lib/utils.js +9 -0
  127. package/dist/lib/lib/utils.js.map +7 -0
  128. package/dist/lib/location/countries.js +68 -0
  129. package/dist/lib/location/countries.js.map +7 -0
  130. package/dist/lib/modules/index.js +6 -0
  131. package/dist/lib/modules/index.js.map +7 -0
  132. package/dist/lib/modules/registry.js +18 -0
  133. package/dist/lib/modules/registry.js.map +7 -0
  134. package/dist/lib/openapi/crud.js +137 -0
  135. package/dist/lib/openapi/crud.js.map +7 -0
  136. package/dist/lib/openapi/generator.js +1131 -0
  137. package/dist/lib/openapi/generator.js.map +7 -0
  138. package/dist/lib/openapi/index.js +10 -0
  139. package/dist/lib/openapi/index.js.map +7 -0
  140. package/dist/lib/openapi/sanitize.js +110 -0
  141. package/dist/lib/openapi/sanitize.js.map +7 -0
  142. package/dist/lib/openapi/types.js +1 -0
  143. package/dist/lib/openapi/types.js.map +7 -0
  144. package/dist/lib/profiler/index.js +258 -0
  145. package/dist/lib/profiler/index.js.map +7 -0
  146. package/dist/lib/query/engine.js +729 -0
  147. package/dist/lib/query/engine.js.map +7 -0
  148. package/dist/lib/query/join-utils.js +195 -0
  149. package/dist/lib/query/join-utils.js.map +7 -0
  150. package/dist/lib/query/types.js +9 -0
  151. package/dist/lib/query/types.js.map +7 -0
  152. package/dist/lib/search/config.js +32 -0
  153. package/dist/lib/search/config.js.map +7 -0
  154. package/dist/lib/search/tokenize.js +34 -0
  155. package/dist/lib/search/tokenize.js.map +7 -0
  156. package/dist/lib/slugify.js +24 -0
  157. package/dist/lib/slugify.js.map +7 -0
  158. package/dist/lib/testing/bootstrap.js +51 -0
  159. package/dist/lib/testing/bootstrap.js.map +7 -0
  160. package/dist/lib/testing/index.js +17 -0
  161. package/dist/lib/testing/index.js.map +7 -0
  162. package/dist/lib/testing/renderWithProviders.js +15 -0
  163. package/dist/lib/testing/renderWithProviders.js.map +7 -0
  164. package/dist/lib/url.js +12 -0
  165. package/dist/lib/url.js.map +7 -0
  166. package/dist/lib/utils.js +13 -0
  167. package/dist/lib/utils.js.map +7 -0
  168. package/dist/lib/version.js +7 -0
  169. package/dist/lib/version.js.map +7 -0
  170. package/dist/modules/dashboard/widgets.js +1 -0
  171. package/dist/modules/dashboard/widgets.js.map +7 -0
  172. package/dist/modules/dsl.js +30 -0
  173. package/dist/modules/dsl.js.map +7 -0
  174. package/dist/modules/entities/kinds.js +22 -0
  175. package/dist/modules/entities/kinds.js.map +7 -0
  176. package/dist/modules/entities/options.js +26 -0
  177. package/dist/modules/entities/options.js.map +7 -0
  178. package/dist/modules/entities/validation.js +102 -0
  179. package/dist/modules/entities/validation.js.map +7 -0
  180. package/dist/modules/entities/validators.js +88 -0
  181. package/dist/modules/entities/validators.js.map +7 -0
  182. package/dist/modules/entities.js +1 -0
  183. package/dist/modules/entities.js.map +7 -0
  184. package/dist/modules/navigation/sidebarPreferences.js +50 -0
  185. package/dist/modules/navigation/sidebarPreferences.js.map +7 -0
  186. package/dist/modules/perspectives/types.js +1 -0
  187. package/dist/modules/perspectives/types.js.map +7 -0
  188. package/dist/modules/registry.js +96 -0
  189. package/dist/modules/registry.js.map +7 -0
  190. package/dist/modules/search.js +15 -0
  191. package/dist/modules/search.js.map +7 -0
  192. package/dist/modules/vector.js +1 -0
  193. package/dist/modules/vector.js.map +7 -0
  194. package/dist/modules/widgets/injection-loader.js +180 -0
  195. package/dist/modules/widgets/injection-loader.js.map +7 -0
  196. package/dist/modules/widgets/injection.js +1 -0
  197. package/dist/modules/widgets/injection.js.map +7 -0
  198. package/dist/security/features.js +23 -0
  199. package/dist/security/features.js.map +7 -0
  200. package/dist/types/pg.d.js +1 -0
  201. package/dist/types/pg.d.js.map +7 -0
  202. package/dist/types/react-email.d.js +1 -0
  203. package/dist/types/react-email.d.js.map +7 -0
  204. package/dist/types/resend.d.js +1 -0
  205. package/dist/types/resend.d.js.map +7 -0
  206. package/jest.config.cjs +22 -0
  207. package/package.json +88 -0
  208. package/src/index.ts +0 -0
  209. package/src/lib/api/__tests__/scoped.test.ts +38 -0
  210. package/src/lib/api/crud.ts +59 -0
  211. package/src/lib/api/scoped.ts +239 -0
  212. package/src/lib/auth/jwt.ts +39 -0
  213. package/src/lib/auth/server.ts +199 -0
  214. package/src/lib/boolean.ts +17 -0
  215. package/src/lib/bootstrap/appResolver.ts +85 -0
  216. package/src/lib/bootstrap/dynamicLoader.ts +177 -0
  217. package/src/lib/bootstrap/factory.ts +108 -0
  218. package/src/lib/bootstrap/index.ts +23 -0
  219. package/src/lib/bootstrap/types.ts +31 -0
  220. package/src/lib/cache/segments.ts +56 -0
  221. package/src/lib/cli/progress.ts +55 -0
  222. package/src/lib/commands/__tests__/command-bus.test.ts +84 -0
  223. package/src/lib/commands/__tests__/helpers.test.ts +42 -0
  224. package/src/lib/commands/command-bus.ts +349 -0
  225. package/src/lib/commands/customFieldSnapshots.ts +86 -0
  226. package/src/lib/commands/helpers.ts +143 -0
  227. package/src/lib/commands/index.ts +4 -0
  228. package/src/lib/commands/operationMetadata.ts +40 -0
  229. package/src/lib/commands/registry.ts +46 -0
  230. package/src/lib/commands/scope.ts +59 -0
  231. package/src/lib/commands/types.ts +63 -0
  232. package/src/lib/crud/__tests__/crud-factory.test.ts +333 -0
  233. package/src/lib/crud/__tests__/custom-fields.test.ts +150 -0
  234. package/src/lib/crud/cache-stats.ts +127 -0
  235. package/src/lib/crud/cache.ts +205 -0
  236. package/src/lib/crud/custom-fields-client.ts +54 -0
  237. package/src/lib/crud/custom-fields.ts +607 -0
  238. package/src/lib/crud/errors.ts +23 -0
  239. package/src/lib/crud/exporters.ts +188 -0
  240. package/src/lib/crud/factory.ts +1622 -0
  241. package/src/lib/crud/types.ts +29 -0
  242. package/src/lib/custom-fields/normalize.ts +45 -0
  243. package/src/lib/data/engine.ts +562 -0
  244. package/src/lib/db/escapeLikePattern.ts +2 -0
  245. package/src/lib/db/mikro.ts +100 -0
  246. package/src/lib/di/container.ts +105 -0
  247. package/src/lib/email/send.ts +18 -0
  248. package/src/lib/encryption/__tests__/customFieldValues.test.ts +63 -0
  249. package/src/lib/encryption/__tests__/indexDoc.test.ts +115 -0
  250. package/src/lib/encryption/aes.ts +64 -0
  251. package/src/lib/encryption/customFieldValues.ts +67 -0
  252. package/src/lib/encryption/entityFields.ts +39 -0
  253. package/src/lib/encryption/entityIds.ts +107 -0
  254. package/src/lib/encryption/find.ts +81 -0
  255. package/src/lib/encryption/indexDoc.ts +104 -0
  256. package/src/lib/encryption/kms.ts +337 -0
  257. package/src/lib/encryption/subscriber.ts +416 -0
  258. package/src/lib/encryption/tenantDataEncryptionService.ts +313 -0
  259. package/src/lib/encryption/toggles.ts +15 -0
  260. package/src/lib/entities/naming.ts +6 -0
  261. package/src/lib/entities/system-entities.ts +43 -0
  262. package/src/lib/frontend/organizationEvents.ts +55 -0
  263. package/src/lib/frontend/useOrganizationScope.ts +30 -0
  264. package/src/lib/hotkeys/index.ts +168 -0
  265. package/src/lib/i18n/app-dictionaries.ts +18 -0
  266. package/src/lib/i18n/config.ts +4 -0
  267. package/src/lib/i18n/context.tsx +66 -0
  268. package/src/lib/i18n/server.ts +74 -0
  269. package/src/lib/i18n/translate.ts +54 -0
  270. package/src/lib/indexers/error-log.ts +106 -0
  271. package/src/lib/indexers/status-log.ts +119 -0
  272. package/src/lib/lib/auth/jwt.ts +39 -0
  273. package/src/lib/lib/auth/server.ts +94 -0
  274. package/src/lib/lib/email/send.ts +18 -0
  275. package/src/lib/lib/i18n/config.ts +4 -0
  276. package/src/lib/lib/i18n/context.tsx +38 -0
  277. package/src/lib/lib/utils.ts +6 -0
  278. package/src/lib/location/countries.ts +97 -0
  279. package/src/lib/modules/index.ts +1 -0
  280. package/src/lib/modules/registry.ts +18 -0
  281. package/src/lib/openapi/crud.ts +218 -0
  282. package/src/lib/openapi/generator.ts +1311 -0
  283. package/src/lib/openapi/index.ts +4 -0
  284. package/src/lib/openapi/sanitize.ts +137 -0
  285. package/src/lib/openapi/types.ts +79 -0
  286. package/src/lib/profiler/index.ts +371 -0
  287. package/src/lib/query/__tests__/engine.test.ts +274 -0
  288. package/src/lib/query/engine.ts +837 -0
  289. package/src/lib/query/join-utils.ts +238 -0
  290. package/src/lib/query/types.ts +121 -0
  291. package/src/lib/search/config.ts +49 -0
  292. package/src/lib/search/tokenize.ts +45 -0
  293. package/src/lib/slugify.ts +28 -0
  294. package/src/lib/testing/bootstrap.ts +124 -0
  295. package/src/lib/testing/index.ts +15 -0
  296. package/src/lib/testing/renderWithProviders.tsx +31 -0
  297. package/src/lib/url.ts +12 -0
  298. package/src/lib/utils.ts +17 -0
  299. package/src/lib/version.ts +5 -0
  300. package/src/modules/__tests__/dsl.test.ts +35 -0
  301. package/src/modules/__tests__/registry.test.ts +300 -0
  302. package/src/modules/dashboard/widgets.ts +57 -0
  303. package/src/modules/dsl.ts +32 -0
  304. package/src/modules/entities/__tests__/validation.test.ts +52 -0
  305. package/src/modules/entities/kinds.ts +20 -0
  306. package/src/modules/entities/options.ts +36 -0
  307. package/src/modules/entities/validation.ts +118 -0
  308. package/src/modules/entities/validators.ts +93 -0
  309. package/src/modules/entities.ts +102 -0
  310. package/src/modules/navigation/sidebarPreferences.ts +62 -0
  311. package/src/modules/perspectives/types.ts +40 -0
  312. package/src/modules/registry.ts +249 -0
  313. package/src/modules/search.ts +325 -0
  314. package/src/modules/vector.ts +122 -0
  315. package/src/modules/widgets/__tests__/injection.test.ts +48 -0
  316. package/src/modules/widgets/injection-loader.ts +235 -0
  317. package/src/modules/widgets/injection.ts +120 -0
  318. package/src/security/features.ts +22 -0
  319. package/src/types/pg.d.ts +2 -0
  320. package/src/types/react-email.d.ts +2 -0
  321. package/src/types/resend.d.ts +2 -0
  322. package/tsconfig.build.json +11 -0
  323. package/tsconfig.json +9 -0
  324. package/watch.mjs +6 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/encryption/subscriber.ts"],
4
+ "sourcesContent": ["import type { EntityMetadata, EventArgs, EventSubscriber } from '@mikro-orm/core'\nimport { ReferenceKind } from '@mikro-orm/core'\nimport { resolveEntityIdFromMetadata } from './entityIds'\nimport { TenantDataEncryptionService } from './tenantDataEncryptionService'\nimport { isTenantDataEncryptionEnabled } from './toggles'\nimport { isEncryptionDebugEnabled } from './toggles'\nimport { resolveTenantEncryptionService } from './customFieldValues'\n\ntype Scoped = {\n tenantId?: string | null\n tenant_id?: string | null\n tenant?: { id?: string | null } | null\n organizationId?: string | null\n organization_id?: string | null\n organization?: { id?: string | null } | null\n}\n\ntype Scope = { tenantId: string | null; organizationId: string | null }\n\nfunction resolveScope(entity: Scoped): Scope {\n const tenantId = entity.tenantId ?? entity.tenant_id ?? entity.tenant?.id ?? null\n const organizationId = entity.organizationId ?? entity.organization_id ?? entity.organization?.id ?? null\n return {\n tenantId: tenantId ? String(tenantId) : null,\n organizationId: organizationId ? String(organizationId) : null,\n }\n}\n\nfunction debug(event: string, payload: Record<string, unknown>) {\n if (!isEncryptionDebugEnabled()) return\n try {\n // eslint-disable-next-line no-console\n console.debug(event, payload)\n } catch {\n // ignore\n }\n}\n\nconst registeredEventManagers = new WeakSet<object>()\n\nconst toSnakeCase = (value: string): string =>\n value.replace(/([A-Z])/g, '_$1').replace(/__/g, '_').toLowerCase()\n\nexport class TenantEncryptionSubscriber implements EventSubscriber<any> {\n constructor(private readonly service: TenantDataEncryptionService) {}\n\n getSubscribedEntities() {\n return [] // listen to all entities\n }\n\n private resolveMeta(\n meta: EntityMetadata<any> | undefined,\n entity: Record<string, unknown>,\n em?: { getMetadata?: () => any },\n ): EntityMetadata<any> | undefined {\n if (meta) return meta\n const ctor = (entity as any)?.constructor\n const name = ctor?.name\n const registry = em?.getMetadata?.()\n if (!registry || !name) return meta\n try { return registry.find?.(name) } catch {}\n try { return registry.find?.(ctor) } catch {}\n try { return registry.get?.(name) } catch {}\n try { return registry.get?.(ctor) } catch {}\n const all =\n (typeof registry.getAll === 'function' && registry.getAll()) ||\n (Array.isArray((registry as any).metadata) ? (registry as any).metadata : undefined) ||\n (registry as any).metadata ||\n {}\n try {\n const entries = Array.isArray(all) ? all : Object.values<any>(all)\n const match = entries.find(\n (m: any) =>\n m?.className === name ||\n m?.name === name ||\n m?.entityName === name ||\n m?.collection === ctor?.prototype?.__meta?.tableName ||\n m?.tableName === ctor?.prototype?.__meta?.tableName,\n )\n if (match) return match as EntityMetadata<any>\n } catch {\n // best-effort\n }\n return meta\n }\n\n private resolveEntityId(meta: EntityMetadata<any> | undefined): string | null {\n try {\n return resolveEntityIdFromMetadata(meta)\n } catch {\n return null\n }\n }\n\n private syncOriginalEntityData(\n target: Record<string, unknown>,\n meta: EntityMetadata<any> | undefined,\n em?: { getComparator?: () => any },\n ) {\n const helper = (target as any)?.__helper\n if (!helper || typeof helper !== 'object') return\n\n // Prefer MikroORM comparator snapshot so change detection uses the expected shape.\n try {\n const comparator = em?.getComparator?.()\n if (comparator?.prepareEntity) {\n helper.__originalEntityData = comparator.prepareEntity(target)\n helper.__touched = false\n return\n }\n } catch (err) {\n debug('\u26AA\uFE0F subscriber.sync_original.comparator_failed', {\n entity: meta?.className || meta?.name,\n message: (err as Error)?.message ?? String(err),\n })\n }\n\n // Fallback: shallow snapshot of scalar/owner props to keep entities clean without comparator.\n const properties = meta?.properties ? Object.values(meta.properties) : []\n if (properties.length === 0) return\n const snapshot: Record<string, unknown> = { ...(helper.__originalEntityData ?? {}) }\n for (const prop of properties) {\n if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes((prop as any).kind)) continue\n const name = (prop as any).name\n if (typeof name !== 'string' || !name.length) continue\n snapshot[name] = (target as Record<string, unknown>)[name]\n }\n helper.__originalEntityData = snapshot\n helper.__touched = false\n }\n\n private async encrypt(\n target: Record<string, unknown>,\n meta: EntityMetadata<any> | undefined,\n em?: { getMetadata?: () => any; getComparator?: () => any },\n changeSet?: { payload?: Record<string, unknown> },\n ) {\n if (!isTenantDataEncryptionEnabled() || !this.service.isEnabled()) {\n debug('\u26AA\uFE0F subscriber.skip', { reason: 'disabled', entity: meta?.className || meta?.name })\n return\n }\n const resolvedMeta = this.resolveMeta(meta, target, em)\n const entityId = this.resolveEntityId(resolvedMeta)\n if (!entityId) {\n debug('\u26A0\uFE0F subscriber.decrypt.skip.entity_id_missing', {\n metaName: resolvedMeta?.className || resolvedMeta?.name,\n table: (resolvedMeta as any)?.tableName,\n })\n return\n }\n const { tenantId, organizationId } = resolveScope(target)\n if (!tenantId) {\n debug('\u26AA\uFE0F subscriber.skip', { reason: 'no-tenant', entityId })\n return\n }\n const encrypted = await this.service.encryptEntityPayload(entityId, target, tenantId, organizationId)\n const metaProps: Record<string, unknown> = resolvedMeta?.properties && typeof resolvedMeta.properties === 'object'\n ? resolvedMeta.properties\n : {}\n const payloadObj: Record<string, unknown> | null =\n changeSet && typeof changeSet === 'object'\n ? (typeof changeSet.payload === 'object' && changeSet.payload\n ? (changeSet.payload as Record<string, unknown>)\n : ((changeSet.payload = {}) as Record<string, unknown>))\n : null\n const updates: Record<string, unknown> = {}\n const columnNameFor = (propKey: string, prop: Record<string, unknown> | undefined): string => {\n try {\n if (prop && typeof prop === 'object') {\n const explicit = (prop as any)?.fieldName\n if (typeof explicit === 'string' && explicit.length) return explicit\n const name = (prop as any)?.name\n if (typeof name === 'string' && name.length) return name\n }\n } catch (err) {\n debug('\u26A0\uFE0F subscriber.column_name.resolve', {\n entityId,\n propKey,\n message: (err as Error)?.message ?? String(err),\n })\n }\n return toSnakeCase(propKey)\n }\n\n for (const [key, value] of Object.entries(encrypted)) {\n const prop = (metaProps as Record<string, any>)[key]\n if (!prop || typeof prop !== 'object') continue\n if ((target as Record<string, unknown>)[key] === value) continue\n updates[key] = value\n }\n if (Object.keys(updates).length === 0) return\n Object.assign(target, updates)\n if (payloadObj) {\n try {\n const ensureColumnKey = (propKey: string, value: unknown) => {\n const columnName = columnNameFor(propKey, (metaProps as Record<string, any>)[propKey])\n const canonicalKey = columnName || toSnakeCase(propKey)\n const aliases = new Set(\n [propKey, toSnakeCase(propKey), columnName, columnName ? toSnakeCase(columnName) : undefined].filter(\n (v): v is string => typeof v === 'string' && v.length > 0,\n ),\n )\n for (const alias of aliases) {\n if (Object.prototype.hasOwnProperty.call(payloadObj, alias)) delete payloadObj[alias]\n }\n const finalKey = columnName || toSnakeCase(propKey)\n payloadObj[finalKey] = value\n }\n for (const key of Object.keys(updates)) {\n ensureColumnKey(key, updates[key])\n }\n } catch (err) {\n debug('\u26A0\uFE0F subscriber.payload.normalize.error', {\n entityId,\n message: (err as Error)?.message ?? String(err),\n })\n }\n }\n }\n\n async decryptEntityGraph(\n target: Record<string, unknown>,\n meta: EntityMetadata<any> | undefined,\n em?: { getMetadata?: () => any; getComparator?: () => any },\n opts: { syncOriginal?: boolean; seen?: WeakSet<object>; fallbackScope?: Scope } = {},\n ) {\n await this.decrypt(target, meta, em, opts)\n }\n\n private async decrypt(\n target: Record<string, unknown>,\n meta: EntityMetadata<any> | undefined,\n em?: { getMetadata?: () => any; getComparator?: () => any },\n {\n syncOriginal = false,\n seen,\n fallbackScope,\n }: { syncOriginal?: boolean; seen?: WeakSet<object>; fallbackScope?: Scope } = {},\n ) {\n const visited = seen ?? new WeakSet<object>()\n if (visited.has(target as object)) return\n visited.add(target as object)\n if (!isTenantDataEncryptionEnabled() || !this.service.isEnabled()) {\n debug('\u26AA\uFE0F subscriber.skip', { reason: 'disabled', entity: meta?.className || meta?.name })\n return\n }\n const resolvedMeta = this.resolveMeta(meta, target, em)\n const entityId = this.resolveEntityId(resolvedMeta)\n if (!entityId) return\n const { tenantId, organizationId } = resolveScope(target)\n const scopedTenantId = tenantId ?? fallbackScope?.tenantId ?? null\n const scopedOrgId = organizationId ?? fallbackScope?.organizationId ?? null\n if (!scopedTenantId) {\n debug('\u26AA\uFE0F subscriber.skip', { reason: 'no-tenant', entityId })\n return\n }\n const decrypted = await this.service.decryptEntityPayload(entityId, target, scopedTenantId, scopedOrgId)\n Object.assign(target, decrypted)\n if (syncOriginal) {\n this.syncOriginalEntityData(target, resolvedMeta, em as any)\n }\n const nextFallback =\n fallbackScope ??\n (tenantId || organizationId\n ? { tenantId: tenantId ?? null, organizationId: organizationId ?? null }\n : { tenantId: scopedTenantId, organizationId: scopedOrgId })\n // Best-effort deep decrypt for loaded relations so populated graphs get cleaned too.\n try {\n const extractEntities = (value: any): any[] => {\n if (!value) return []\n // MikroORM Reference wrapper\n if (typeof value === 'object' && typeof (value as any).isInitialized === 'function') {\n try {\n if ((value as any).isInitialized()) {\n const unwrapped = typeof (value as any).unwrap === 'function' ? (value as any).unwrap() : (value as any).__entity ?? (value as any)\n if (unwrapped && typeof unwrapped === 'object') return [unwrapped]\n }\n } catch {\n // ignore\n }\n return []\n }\n // Collection wrapper\n if (typeof value === 'object' && typeof (value as any).isInitialized === 'function' && typeof (value as any).getItems === 'function') {\n try {\n return (value as any).isInitialized() ? (value as any).getItems() ?? [] : []\n } catch {\n return []\n }\n }\n if (Array.isArray(value)) return value\n if (typeof value === 'object') return [value]\n return []\n }\n const props = resolvedMeta?.properties ? Object.values(resolvedMeta.properties) : []\n for (const prop of props) {\n const kind = (prop as any)?.kind\n const name = (prop as any)?.name\n if (typeof name !== 'string' || !name.length) continue\n const value = (target as any)[name]\n if (!value) continue\n // Single-valued relation\n if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(kind)) {\n const nestedEntities = extractEntities(value)\n for (const nested of nestedEntities) {\n const nestedMeta = this.resolveMeta((nested as any).__meta ?? (nested as any).__helper?.__meta, nested, em)\n await this.decrypt(nested as Record<string, unknown>, nestedMeta, em, {\n syncOriginal: true,\n seen: visited,\n fallbackScope: nextFallback,\n })\n }\n continue\n }\n // Collections\n if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(kind)) {\n const items = extractEntities(value)\n for (const item of items) {\n if (!item || typeof item !== 'object') continue\n const nestedMeta = this.resolveMeta((item as any).__meta ?? (item as any).__helper?.__meta, item, em)\n await this.decrypt(item as Record<string, unknown>, nestedMeta, em, {\n syncOriginal: true,\n seen: visited,\n fallbackScope: nextFallback,\n })\n }\n }\n }\n } catch (err) {\n debug('\u26A0\uFE0F subscriber.deep_decrypt.error', {\n entityId,\n message: (err as Error)?.message ?? String(err),\n })\n }\n }\n\n async beforeCreate(args: EventArgs<any>) {\n await this.encrypt(args.entity as Record<string, unknown>, args.meta, args.em, args.changeSet as any)\n }\n\n async beforeUpdate(args: EventArgs<any>) {\n await this.decrypt(args.entity as Record<string, unknown>, args.meta, args.em)\n await this.encrypt(args.entity as Record<string, unknown>, args.meta, args.em, args.changeSet as any)\n }\n\n async afterCreate(args: EventArgs<any>) {\n await this.decrypt(args.entity as Record<string, unknown>, args.meta, args.em, { syncOriginal: true })\n }\n\n async afterUpdate(args: EventArgs<any>) {\n await this.decrypt(args.entity as Record<string, unknown>, args.meta, args.em, { syncOriginal: true })\n }\n\n async afterUpsert(args: EventArgs<any>) {\n await this.decrypt(args.entity as Record<string, unknown>, args.meta, args.em, { syncOriginal: true })\n }\n\n async onLoad(args: EventArgs<any>) {\n await this.decrypt(args.entity as Record<string, unknown>, args.meta, args.em, { syncOriginal: true })\n }\n\n async afterFind(args: EventArgs<any> & { entities?: unknown[] }) {\n const entities = Array.isArray(args.entities) ? args.entities : []\n for (const entity of entities) {\n await this.decrypt(entity as Record<string, unknown>, args.meta, args.em, { syncOriginal: true })\n }\n }\n}\n\nexport function registerTenantEncryptionSubscriber(\n em: { getEventManager?: () => { registerSubscriber?: (subscriber: EventSubscriber<any>) => void } } | null | undefined,\n service: TenantDataEncryptionService,\n): void {\n const eventManager = em?.getEventManager?.()\n if (!eventManager || typeof eventManager.registerSubscriber !== 'function') return\n if (registeredEventManagers.has(eventManager)) return\n eventManager.registerSubscriber(new TenantEncryptionSubscriber(service))\n registeredEventManagers.add(eventManager)\n}\n\nexport async function decryptEntitiesWithFallbackScope(\n targets: unknown | unknown[],\n {\n em,\n tenantId,\n organizationId,\n encryptionService,\n }: {\n em: { getMetadata?: () => any; getComparator?: () => any }\n tenantId?: string | null\n organizationId?: string | null\n encryptionService?: TenantDataEncryptionService | null\n },\n): Promise<void> {\n if (!isTenantDataEncryptionEnabled()) return\n const list = Array.isArray(targets) ? targets : [targets]\n if (!list.length) return\n const service = encryptionService ?? resolveTenantEncryptionService(em as any)\n if (!service || !service.isEnabled()) return\n const subscriber = new TenantEncryptionSubscriber(service)\n const fallback: Scope | undefined =\n tenantId || organizationId\n ? {\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n }\n : undefined\n for (const entity of list) {\n if (!entity || typeof entity !== 'object') continue\n const meta = (entity as any).__meta ?? (entity as any).__helper?.__meta\n await subscriber.decryptEntityGraph(entity as Record<string, unknown>, meta, em as any, {\n syncOriginal: true,\n fallbackScope: fallback,\n })\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,252 @@
1
+ import { decryptWithAesGcm, encryptWithAesGcm, hashForLookup } from "./aes.js";
2
+ import { createKmsService } from "./kms.js";
3
+ import { isTenantDataEncryptionEnabled, isEncryptionDebugEnabled } from "./toggles.js";
4
+ const MAP_MISS_TTL_MS = 5 * 60 * 1e3;
5
+ function cacheKey(key) {
6
+ return [
7
+ "encmap",
8
+ key.entityId.toLowerCase(),
9
+ key.tenantId ?? "null",
10
+ key.organizationId ?? "null"
11
+ ].join(":");
12
+ }
13
+ function debug(event, payload) {
14
+ if (!isEncryptionDebugEnabled()) return;
15
+ try {
16
+ console.debug(`${event} [tenant-encryption]`, payload);
17
+ } catch {
18
+ }
19
+ }
20
+ const toSnakeCase = (value) => value.replace(/([A-Z])/g, "_$1").replace(/__/g, "_").toLowerCase();
21
+ const toCamelCase = (value) => value.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
22
+ function findKey(obj, key) {
23
+ const candidates = [key, toSnakeCase(key), toCamelCase(key)];
24
+ for (const candidate of candidates) {
25
+ if (Object.prototype.hasOwnProperty.call(obj, candidate)) return candidate;
26
+ }
27
+ return null;
28
+ }
29
+ function isEncryptedPayload(value) {
30
+ if (typeof value !== "string") return false;
31
+ const parts = value.split(":");
32
+ return parts.length === 4 && parts[3] === "v1";
33
+ }
34
+ class TenantDataEncryptionService {
35
+ constructor(em, opts) {
36
+ this.em = em;
37
+ this.memoryCache = TenantDataEncryptionService.globalMemoryCache;
38
+ this.dekCache = TenantDataEncryptionService.globalDekCache;
39
+ this.inflightMaps = TenantDataEncryptionService.globalInflightMaps;
40
+ this.missCache = TenantDataEncryptionService.globalMissCache;
41
+ this.cache = opts?.cache;
42
+ this.kms = opts?.kms ?? createKmsService();
43
+ }
44
+ static {
45
+ this.globalMemoryCache = /* @__PURE__ */ new Map();
46
+ }
47
+ static {
48
+ this.globalInflightMaps = /* @__PURE__ */ new Map();
49
+ }
50
+ static {
51
+ this.globalDekCache = /* @__PURE__ */ new Map();
52
+ }
53
+ static {
54
+ this.globalMissCache = /* @__PURE__ */ new Map();
55
+ }
56
+ isEnabled() {
57
+ return isTenantDataEncryptionEnabled() && this.kms.isHealthy();
58
+ }
59
+ async getDek(tenantId) {
60
+ if (!tenantId) return null;
61
+ const cached = this.dekCache.get(tenantId);
62
+ if (cached) return cached;
63
+ const dek = await this.kms.getTenantDek(tenantId);
64
+ if (!dek) {
65
+ debug("\u{1F50E} dek.miss", { tenantId });
66
+ } else {
67
+ debug("\u2705 dek.hit", { tenantId });
68
+ }
69
+ if (dek) this.dekCache.set(tenantId, dek);
70
+ return dek;
71
+ }
72
+ async resolveDekForEncrypt(tenantId) {
73
+ const existing = await this.getDek(tenantId);
74
+ if (existing || !tenantId) return existing ?? null;
75
+ if (typeof this.kms.createTenantDek !== "function") return existing ?? null;
76
+ const created = await this.kms.createTenantDek(tenantId);
77
+ if (created) this.dekCache.set(tenantId, created);
78
+ return created ?? null;
79
+ }
80
+ async createDek(tenantId) {
81
+ const dek = await this.kms.createTenantDek(tenantId);
82
+ if (dek) this.dekCache.set(tenantId, dek);
83
+ return dek;
84
+ }
85
+ async fetchMap(key) {
86
+ const conn = this.em?.getConnection?.();
87
+ if (!conn || typeof conn.execute !== "function") return null;
88
+ const sql = `
89
+ select entity_id, fields_json
90
+ from encryption_maps
91
+ where entity_id = ?
92
+ and tenant_id is not distinct from ?
93
+ and organization_id is not distinct from ?
94
+ and is_active = true
95
+ and deleted_at is null
96
+ limit 1
97
+ `;
98
+ const rows = await conn.execute(sql, [key.entityId, key.tenantId ?? null, key.organizationId ?? null]);
99
+ const row = Array.isArray(rows) && rows.length ? rows[0] : null;
100
+ if (!row) return null;
101
+ return {
102
+ entityId: row.entity_id || row.entityId || key.entityId,
103
+ fields: Array.isArray(row.fields_json) ? row.fields_json : Array.isArray(row.fieldsJson) ? row.fieldsJson : []
104
+ };
105
+ }
106
+ async getMap(key) {
107
+ const shouldSkipLookup = (tag) => {
108
+ const expiresAt = this.missCache.get(tag);
109
+ if (!expiresAt) return false;
110
+ if (expiresAt > Date.now()) return true;
111
+ this.missCache.delete(tag);
112
+ return false;
113
+ };
114
+ const recordMiss = (tag) => {
115
+ this.missCache.set(tag, Date.now() + MAP_MISS_TTL_MS);
116
+ };
117
+ const candidates = [
118
+ key,
119
+ { entityId: key.entityId, tenantId: key.tenantId ?? null, organizationId: null },
120
+ { entityId: key.entityId, tenantId: null, organizationId: null }
121
+ ];
122
+ for (const candidate of candidates) {
123
+ const tag = cacheKey(candidate);
124
+ if (shouldSkipLookup(tag)) continue;
125
+ if (this.inflightMaps.has(tag)) {
126
+ const pending2 = this.inflightMaps.get(tag);
127
+ const resolved = await pending2;
128
+ if (resolved) return resolved;
129
+ }
130
+ const mem = this.memoryCache.get(tag);
131
+ if (mem) return mem;
132
+ if (this.cache && typeof this.cache.get === "function") {
133
+ const cached = await this.cache.get(tag);
134
+ if (cached) return cached;
135
+ }
136
+ const pending = this.fetchMap(candidate);
137
+ this.inflightMaps.set(tag, pending);
138
+ const loaded = await pending;
139
+ this.inflightMaps.delete(tag);
140
+ if (!loaded) {
141
+ recordMiss(tag);
142
+ debug("\u{1F50D} encmap.miss", {
143
+ entityId: candidate.entityId,
144
+ tenantId: candidate.tenantId,
145
+ organizationId: candidate.organizationId
146
+ });
147
+ continue;
148
+ }
149
+ this.missCache.delete(tag);
150
+ this.memoryCache.set(tag, loaded);
151
+ if (this.cache && typeof this.cache.set === "function") {
152
+ await this.cache.set(tag, loaded, { ttl: 300 });
153
+ }
154
+ return loaded;
155
+ }
156
+ return null;
157
+ }
158
+ async invalidateMap(entityId, tenantId, organizationId) {
159
+ const tag = cacheKey({ entityId, tenantId, organizationId });
160
+ this.memoryCache.delete(tag);
161
+ this.inflightMaps.delete(tag);
162
+ this.missCache.delete(tag);
163
+ if (this.cache && typeof this.cache.delete === "function") {
164
+ await this.cache.delete(tag);
165
+ }
166
+ }
167
+ encryptFields(obj, fields, dek) {
168
+ const clone = { ...obj };
169
+ for (const rule of fields) {
170
+ const key = findKey(clone, rule.field);
171
+ if (!key) continue;
172
+ const value = clone[key];
173
+ if (value === null || value === void 0) continue;
174
+ if (isEncryptedPayload(value)) continue;
175
+ const serialized = typeof value === "string" ? value : JSON.stringify(value);
176
+ const payload = encryptWithAesGcm(serialized, dek.key);
177
+ clone[key] = payload.value;
178
+ if (rule.hashField) {
179
+ const hashKey = findKey(clone, rule.hashField) ?? rule.hashField;
180
+ clone[hashKey] = hashForLookup(serialized);
181
+ }
182
+ }
183
+ return clone;
184
+ }
185
+ decryptFields(obj, fields, dek) {
186
+ const clone = { ...obj };
187
+ const maybeDecrypt = (payload) => {
188
+ const first = decryptWithAesGcm(payload, dek.key);
189
+ if (first === null) return null;
190
+ const parts = first.split(":");
191
+ if (parts.length === 4 && parts[3] === "v1") {
192
+ const second = decryptWithAesGcm(first, dek.key);
193
+ return second ?? first;
194
+ }
195
+ return first;
196
+ };
197
+ for (const rule of fields) {
198
+ const key = findKey(clone, rule.field);
199
+ if (!key) continue;
200
+ const value = clone[key];
201
+ if (typeof value !== "string") continue;
202
+ const decrypted = maybeDecrypt(value);
203
+ if (decrypted === null) continue;
204
+ try {
205
+ clone[key] = JSON.parse(decrypted);
206
+ } catch {
207
+ clone[key] = decrypted;
208
+ }
209
+ }
210
+ return clone;
211
+ }
212
+ async encryptEntityPayload(entityId, payload, tenantId, organizationId) {
213
+ if (!this.isEnabled()) {
214
+ debug("\u26AA\uFE0F encrypt.skip.disabled", { entityId, tenantId });
215
+ return payload;
216
+ }
217
+ const dek = await this.resolveDekForEncrypt(tenantId ?? null);
218
+ if (!dek) {
219
+ debug("\u26A0\uFE0F encrypt.skip.no-dek", { entityId, tenantId });
220
+ return payload;
221
+ }
222
+ const map = await this.getMap({ entityId, tenantId: tenantId ?? null, organizationId: organizationId ?? null });
223
+ if (!map || !map.fields?.length) {
224
+ debug("\u26AA\uFE0F encrypt.skip.no-map", { entityId, tenantId });
225
+ return payload;
226
+ }
227
+ debug("\u{1F512} encrypt_entity", { entityId, tenantId, organizationId, fields: map.fields.length });
228
+ return this.encryptFields(payload, map.fields, dek);
229
+ }
230
+ async decryptEntityPayload(entityId, payload, tenantId, organizationId) {
231
+ if (!isTenantDataEncryptionEnabled()) {
232
+ debug("\u26AA\uFE0F decrypt.skip.disabled", { entityId, tenantId });
233
+ return payload;
234
+ }
235
+ const dek = await this.getDek(tenantId ?? null);
236
+ if (!dek) {
237
+ debug("\u26A0\uFE0F decrypt.skip.no-dek", { entityId, tenantId });
238
+ return payload;
239
+ }
240
+ const map = await this.getMap({ entityId, tenantId: tenantId ?? null, organizationId: organizationId ?? null });
241
+ if (!map || !map.fields?.length) {
242
+ debug("\u26AA\uFE0F decrypt.skip.no-map", { entityId, tenantId });
243
+ return payload;
244
+ }
245
+ debug("\u{1F513} decrypt_entity", { entityId, tenantId, organizationId, fields: map.fields.length });
246
+ return this.decryptFields(payload, map.fields, dek);
247
+ }
248
+ }
249
+ export {
250
+ TenantDataEncryptionService
251
+ };
252
+ //# sourceMappingURL=tenantDataEncryptionService.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/encryption/tenantDataEncryptionService.ts"],
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CacheStrategy } from '@open-mercato/cache'\nimport { decryptWithAesGcm, encryptWithAesGcm, hashForLookup } from './aes'\nimport { createKmsService, type KmsService, type TenantDek } from './kms'\nimport { isTenantDataEncryptionEnabled, isEncryptionDebugEnabled } from './toggles'\nimport { EncryptionMap } from '@open-mercato/core/modules/entities/data/entities'\n\nexport type EncryptedFieldRule = {\n field: string\n hashField?: string | null\n}\n\nexport type EncryptionMapRecord = {\n entityId: string\n fields: EncryptedFieldRule[]\n}\n\ntype MapCacheKey = {\n entityId: string\n tenantId: string | null\n organizationId: string | null\n}\n\nconst MAP_MISS_TTL_MS = 5 * 60 * 1000\n\nfunction cacheKey(key: MapCacheKey): string {\n return [\n 'encmap',\n key.entityId.toLowerCase(),\n key.tenantId ?? 'null',\n key.organizationId ?? 'null',\n ].join(':')\n}\n\nfunction debug(event: string, payload: Record<string, unknown>) {\n if (!isEncryptionDebugEnabled()) return\n try {\n // eslint-disable-next-line no-console\n console.debug(`${event} [tenant-encryption]`, payload)\n } catch {\n // ignore\n }\n}\n\nconst toSnakeCase = (value: string): string =>\n value.replace(/([A-Z])/g, '_$1').replace(/__/g, '_').toLowerCase()\n\nconst toCamelCase = (value: string): string =>\n value.replace(/_([a-z])/g, (_, c) => c.toUpperCase())\n\nfunction findKey(obj: Record<string, unknown>, key: string): string | null {\n const candidates = [key, toSnakeCase(key), toCamelCase(key)]\n for (const candidate of candidates) {\n if (Object.prototype.hasOwnProperty.call(obj, candidate)) return candidate\n }\n return null\n}\n\nfunction isEncryptedPayload(value: unknown): boolean {\n if (typeof value !== 'string') return false\n const parts = value.split(':')\n return parts.length === 4 && parts[3] === 'v1'\n}\n\nexport class TenantDataEncryptionService {\n private static globalMemoryCache = new Map<string, EncryptionMapRecord>()\n private static globalInflightMaps = new Map<string, Promise<EncryptionMapRecord | null>>()\n private static globalDekCache = new Map<string, TenantDek>()\n private static globalMissCache = new Map<string, number>()\n private readonly kms: KmsService\n private readonly cache?: CacheStrategy\n private readonly memoryCache = TenantDataEncryptionService.globalMemoryCache\n private readonly dekCache = TenantDataEncryptionService.globalDekCache\n private readonly inflightMaps = TenantDataEncryptionService.globalInflightMaps\n private readonly missCache = TenantDataEncryptionService.globalMissCache\n\n constructor(\n private em: EntityManager,\n opts?: { cache?: CacheStrategy; kms?: KmsService }\n ) {\n this.cache = opts?.cache\n this.kms = opts?.kms ?? createKmsService()\n }\n\n isEnabled(): boolean {\n return isTenantDataEncryptionEnabled() && this.kms.isHealthy()\n }\n\n async getDek(tenantId: string | null | undefined): Promise<TenantDek | null> {\n if (!tenantId) return null\n const cached = this.dekCache.get(tenantId)\n if (cached) return cached\n const dek = await this.kms.getTenantDek(tenantId)\n if (!dek) {\n debug('\uD83D\uDD0E dek.miss', { tenantId })\n } else {\n debug('\u2705 dek.hit', { tenantId })\n }\n if (dek) this.dekCache.set(tenantId, dek)\n return dek\n }\n\n private async resolveDekForEncrypt(tenantId: string | null): Promise<TenantDek | null> {\n const existing = await this.getDek(tenantId)\n if (existing || !tenantId) return existing ?? null\n if (typeof this.kms.createTenantDek !== 'function') return existing ?? null\n const created = await this.kms.createTenantDek(tenantId)\n if (created) this.dekCache.set(tenantId, created)\n return created ?? null\n }\n\n async createDek(tenantId: string): Promise<TenantDek | null> {\n const dek = await this.kms.createTenantDek(tenantId)\n if (dek) this.dekCache.set(tenantId, dek)\n return dek\n }\n\n private async fetchMap(key: MapCacheKey): Promise<EncryptionMapRecord | null> {\n // Bypass ORM lifecycle hooks to avoid recursive decrypt loops by querying directly.\n const conn: any = (this.em as any)?.getConnection?.()\n if (!conn || typeof conn.execute !== 'function') return null\n const sql = `\n select entity_id, fields_json\n from encryption_maps\n where entity_id = ?\n and tenant_id is not distinct from ?\n and organization_id is not distinct from ?\n and is_active = true\n and deleted_at is null\n limit 1\n `\n const rows = await conn.execute(sql, [key.entityId, key.tenantId ?? null, key.organizationId ?? null])\n const row = Array.isArray(rows) && rows.length ? rows[0] : null\n if (!row) return null\n return {\n entityId: row.entity_id || row.entityId || key.entityId,\n fields: Array.isArray(row.fields_json)\n ? (row.fields_json as EncryptedFieldRule[])\n : Array.isArray(row.fieldsJson)\n ? (row.fieldsJson as EncryptedFieldRule[])\n : [],\n }\n }\n\n private async getMap(key: MapCacheKey): Promise<EncryptionMapRecord | null> {\n const shouldSkipLookup = (tag: string) => {\n const expiresAt = this.missCache.get(tag)\n if (!expiresAt) return false\n if (expiresAt > Date.now()) return true\n this.missCache.delete(tag)\n return false\n }\n const recordMiss = (tag: string) => {\n this.missCache.set(tag, Date.now() + MAP_MISS_TTL_MS)\n }\n\n const candidates: MapCacheKey[] = [\n key,\n { entityId: key.entityId, tenantId: key.tenantId ?? null, organizationId: null },\n { entityId: key.entityId, tenantId: null, organizationId: null },\n ]\n for (const candidate of candidates) {\n const tag = cacheKey(candidate)\n if (shouldSkipLookup(tag)) continue\n if (this.inflightMaps.has(tag)) {\n const pending = this.inflightMaps.get(tag)!\n const resolved = await pending\n if (resolved) return resolved\n }\n const mem = this.memoryCache.get(tag)\n if (mem) return mem\n if (this.cache && typeof this.cache.get === 'function') {\n const cached = await this.cache.get(tag)\n if (cached) return cached as EncryptionMapRecord\n }\n const pending = this.fetchMap(candidate)\n this.inflightMaps.set(tag, pending)\n const loaded = await pending\n this.inflightMaps.delete(tag)\n if (!loaded) {\n recordMiss(tag)\n debug('\uD83D\uDD0D encmap.miss', {\n entityId: candidate.entityId,\n tenantId: candidate.tenantId,\n organizationId: candidate.organizationId,\n })\n continue\n }\n this.missCache.delete(tag)\n this.memoryCache.set(tag, loaded)\n if (this.cache && typeof this.cache.set === 'function') {\n await this.cache.set(tag, loaded, { ttl: 300 })\n }\n return loaded\n }\n return null\n }\n\n async invalidateMap(entityId: string, tenantId: string | null, organizationId: string | null): Promise<void> {\n const tag = cacheKey({ entityId, tenantId, organizationId })\n this.memoryCache.delete(tag)\n this.inflightMaps.delete(tag)\n this.missCache.delete(tag)\n if (this.cache && typeof (this.cache as any).delete === 'function') {\n await (this.cache as any).delete(tag)\n }\n }\n\n private encryptFields(\n obj: Record<string, unknown>,\n fields: EncryptedFieldRule[],\n dek: TenantDek\n ): Record<string, unknown> {\n const clone: Record<string, unknown> = { ...obj }\n for (const rule of fields) {\n const key = findKey(clone, rule.field)\n if (!key) continue\n const value = clone[key]\n if (value === null || value === undefined) continue\n // Avoid double-encrypting already encrypted payloads\n if (isEncryptedPayload(value)) continue\n const serialized = typeof value === 'string' ? value : JSON.stringify(value)\n const payload = encryptWithAesGcm(serialized, dek.key)\n clone[key] = payload.value\n if (rule.hashField) {\n const hashKey = findKey(clone, rule.hashField) ?? rule.hashField\n clone[hashKey] = hashForLookup(serialized)\n }\n }\n return clone\n }\n\n private decryptFields(\n obj: Record<string, unknown>,\n fields: EncryptedFieldRule[],\n dek: TenantDek\n ): Record<string, unknown> {\n const clone: Record<string, unknown> = { ...obj }\n const maybeDecrypt = (payload: string): string | null => {\n const first = decryptWithAesGcm(payload, dek.key)\n if (first === null) return null\n // Handle accidental double-encryption: if the first pass still looks like a v1 payload, try once more.\n const parts = first.split(':')\n if (parts.length === 4 && parts[3] === 'v1') {\n const second = decryptWithAesGcm(first, dek.key)\n return second ?? first\n }\n return first\n }\n for (const rule of fields) {\n const key = findKey(clone, rule.field)\n if (!key) continue\n const value = clone[key]\n if (typeof value !== 'string') continue\n const decrypted = maybeDecrypt(value)\n if (decrypted === null) continue\n try {\n clone[key] = JSON.parse(decrypted)\n } catch {\n clone[key] = decrypted\n }\n }\n return clone\n }\n\n async encryptEntityPayload(\n entityId: string,\n payload: Record<string, unknown>,\n tenantId: string | null | undefined,\n organizationId?: string | null\n ): Promise<Record<string, unknown>> {\n if (!this.isEnabled()) {\n debug('\u26AA\uFE0F encrypt.skip.disabled', { entityId, tenantId })\n return payload\n }\n const dek = await this.resolveDekForEncrypt(tenantId ?? null)\n if (!dek) {\n debug('\u26A0\uFE0F encrypt.skip.no-dek', { entityId, tenantId })\n return payload\n }\n const map = await this.getMap({ entityId, tenantId: tenantId ?? null, organizationId: organizationId ?? null })\n if (!map || !map.fields?.length) {\n debug('\u26AA\uFE0F encrypt.skip.no-map', { entityId, tenantId })\n return payload\n }\n debug('\uD83D\uDD12 encrypt_entity', { entityId, tenantId, organizationId, fields: map.fields.length })\n return this.encryptFields(payload, map.fields, dek)\n }\n\n async decryptEntityPayload(\n entityId: string,\n payload: Record<string, unknown>,\n tenantId: string | null | undefined,\n organizationId?: string | null\n ): Promise<Record<string, unknown>> {\n if (!isTenantDataEncryptionEnabled()) {\n debug('\u26AA\uFE0F decrypt.skip.disabled', { entityId, tenantId })\n return payload\n }\n const dek = await this.getDek(tenantId ?? null)\n if (!dek) {\n debug('\u26A0\uFE0F decrypt.skip.no-dek', { entityId, tenantId })\n return payload\n }\n const map = await this.getMap({ entityId, tenantId: tenantId ?? null, organizationId: organizationId ?? null })\n if (!map || !map.fields?.length) {\n debug('\u26AA\uFE0F decrypt.skip.no-map', { entityId, tenantId })\n return payload\n }\n debug('\uD83D\uDD13 decrypt_entity', { entityId, tenantId, organizationId, fields: map.fields.length })\n return this.decryptFields(payload, map.fields, dek)\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["pending"]
7
+ }
@@ -0,0 +1,18 @@
1
+ import { parseBooleanToken } from "../boolean.js";
2
+ function isTenantDataEncryptionEnabled() {
3
+ const rawEnv = process.env.TENANT_DATA_ENCRYPTION;
4
+ if (rawEnv === void 0) return true;
5
+ const trimmed = rawEnv.trim();
6
+ if (!trimmed) return true;
7
+ const parsed = parseBooleanToken(trimmed);
8
+ return parsed === null ? true : parsed;
9
+ }
10
+ function isEncryptionDebugEnabled() {
11
+ const parsed = parseBooleanToken(process.env.TENANT_DATA_ENCRYPTION_DEBUG ?? "");
12
+ return parsed === true;
13
+ }
14
+ export {
15
+ isEncryptionDebugEnabled,
16
+ isTenantDataEncryptionEnabled
17
+ };
18
+ //# sourceMappingURL=toggles.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/encryption/toggles.ts"],
4
+ "sourcesContent": ["import { parseBooleanToken } from '../boolean'\n\nexport function isTenantDataEncryptionEnabled(): boolean {\n const rawEnv = process.env.TENANT_DATA_ENCRYPTION\n if (rawEnv === undefined) return true\n const trimmed = rawEnv.trim()\n if (!trimmed) return true\n const parsed = parseBooleanToken(trimmed)\n return parsed === null ? true : parsed\n}\n\nexport function isEncryptionDebugEnabled(): boolean {\n const parsed = parseBooleanToken(process.env.TENANT_DATA_ENCRYPTION_DEBUG ?? '')\n return parsed === true\n}\n"],
5
+ "mappings": "AAAA,SAAS,yBAAyB;AAE3B,SAAS,gCAAyC;AACvD,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,WAAW,OAAW,QAAO;AACjC,QAAM,UAAU,OAAO,KAAK;AAC5B,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,SAAS,kBAAkB,OAAO;AACxC,SAAO,WAAW,OAAO,OAAO;AAClC;AAEO,SAAS,2BAAoC;AAClD,QAAM,SAAS,kBAAkB,QAAQ,IAAI,gCAAgC,EAAE;AAC/E,SAAO,WAAW;AACpB;",
6
+ "names": []
7
+ }
@@ -0,0 +1,9 @@
1
+ function tableNameFromEntityId(entityId) {
2
+ const [, name] = entityId.split(":");
3
+ if (!name) return "";
4
+ return name.endsWith("s") ? name : `${name}s`;
5
+ }
6
+ export {
7
+ tableNameFromEntityId
8
+ };
9
+ //# sourceMappingURL=naming.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/entities/naming.ts"],
4
+ "sourcesContent": ["export function tableNameFromEntityId(entityId: string): string {\n const [, name] = entityId.split(':')\n if (!name) return ''\n return name.endsWith('s') ? name : `${name}s`\n}\n\n"],
5
+ "mappings": "AAAO,SAAS,sBAAsB,UAA0B;AAC9D,QAAM,CAAC,EAAE,IAAI,IAAI,SAAS,MAAM,GAAG;AACnC,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,KAAK,SAAS,GAAG,IAAI,OAAO,GAAG,IAAI;AAC5C;",
6
+ "names": []
7
+ }
@@ -0,0 +1,43 @@
1
+ const RESERVED_SYSTEM_ENTITY_TYPES = /* @__PURE__ */ new Set([
2
+ "entities:custom_entity",
3
+ "entities:custom_entity_storage",
4
+ "entities:custom_field_def",
5
+ "entities:custom_field_value",
6
+ "query_index:entity_index_row",
7
+ "query_index:entity_index_coverage",
8
+ "query_index:search_token"
9
+ ]);
10
+ function isSystemEntitySelectable(entityId) {
11
+ if (!entityId) return false;
12
+ return !RESERVED_SYSTEM_ENTITY_TYPES.has(entityId);
13
+ }
14
+ function flattenSystemEntityIds(allEntities, options) {
15
+ if (!allEntities) return [];
16
+ const predicate = options?.predicate || isSystemEntitySelectable;
17
+ const seen = /* @__PURE__ */ new Set();
18
+ for (const bucket of Object.values(allEntities)) {
19
+ for (const id of Object.values(bucket ?? {})) {
20
+ if (typeof id !== "string" || id.length === 0) continue;
21
+ if (!predicate(id)) continue;
22
+ seen.add(id);
23
+ }
24
+ }
25
+ return Array.from(seen).sort();
26
+ }
27
+ function filterSelectableSystemEntityIds(entityIds) {
28
+ const selected = [];
29
+ for (const id of entityIds) {
30
+ if (isSystemEntitySelectable(id)) selected.push(id);
31
+ }
32
+ return selected;
33
+ }
34
+ function isReservedSystemEntityType(entityId) {
35
+ return RESERVED_SYSTEM_ENTITY_TYPES.has(entityId);
36
+ }
37
+ export {
38
+ filterSelectableSystemEntityIds,
39
+ flattenSystemEntityIds,
40
+ isReservedSystemEntityType,
41
+ isSystemEntitySelectable
42
+ };
43
+ //# sourceMappingURL=system-entities.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/entities/system-entities.ts"],
4
+ "sourcesContent": ["const RESERVED_SYSTEM_ENTITY_TYPES = new Set<string>([\n 'entities:custom_entity',\n 'entities:custom_entity_storage',\n 'entities:custom_field_def',\n 'entities:custom_field_value',\n 'query_index:entity_index_row',\n 'query_index:entity_index_coverage',\n 'query_index:search_token',\n])\n\nexport function isSystemEntitySelectable(entityId: string): boolean {\n if (!entityId) return false\n return !RESERVED_SYSTEM_ENTITY_TYPES.has(entityId)\n}\n\nexport function flattenSystemEntityIds(\n allEntities: Record<string, Record<string, string>>,\n options?: { predicate?: (entityType: string) => boolean },\n): string[] {\n if (!allEntities) return []\n const predicate = options?.predicate || isSystemEntitySelectable\n const seen = new Set<string>()\n for (const bucket of Object.values(allEntities)) {\n for (const id of Object.values(bucket ?? {})) {\n if (typeof id !== 'string' || id.length === 0) continue\n if (!predicate(id)) continue\n seen.add(id)\n }\n }\n return Array.from(seen).sort()\n}\n\nexport function filterSelectableSystemEntityIds(entityIds: Iterable<string>): string[] {\n const selected: string[] = []\n for (const id of entityIds) {\n if (isSystemEntitySelectable(id)) selected.push(id)\n }\n return selected\n}\n\nexport function isReservedSystemEntityType(entityId: string): boolean {\n return RESERVED_SYSTEM_ENTITY_TYPES.has(entityId)\n}\n"],
5
+ "mappings": "AAAA,MAAM,+BAA+B,oBAAI,IAAY;AAAA,EACnD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,SAAS,yBAAyB,UAA2B;AAClE,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,CAAC,6BAA6B,IAAI,QAAQ;AACnD;AAEO,SAAS,uBACd,aACA,SACU;AACV,MAAI,CAAC,YAAa,QAAO,CAAC;AAC1B,QAAM,YAAY,SAAS,aAAa;AACxC,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,UAAU,OAAO,OAAO,WAAW,GAAG;AAC/C,eAAW,MAAM,OAAO,OAAO,UAAU,CAAC,CAAC,GAAG;AAC5C,UAAI,OAAO,OAAO,YAAY,GAAG,WAAW,EAAG;AAC/C,UAAI,CAAC,UAAU,EAAE,EAAG;AACpB,WAAK,IAAI,EAAE;AAAA,IACb;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI,EAAE,KAAK;AAC/B;AAEO,SAAS,gCAAgC,WAAuC;AACrF,QAAM,WAAqB,CAAC;AAC5B,aAAW,MAAM,WAAW;AAC1B,QAAI,yBAAyB,EAAE,EAAG,UAAS,KAAK,EAAE;AAAA,EACpD;AACA,SAAO;AACT;AAEO,SAAS,2BAA2B,UAA2B;AACpE,SAAO,6BAA6B,IAAI,QAAQ;AAClD;",
6
+ "names": []
7
+ }
@@ -0,0 +1,41 @@
1
+ const ORGANIZATION_SCOPE_CHANGED_EVENT = "om:organization-scope-changed";
2
+ let currentScope = {
3
+ organizationId: null,
4
+ tenantId: null
5
+ };
6
+ let currentVersion = 0;
7
+ function getCurrentOrganizationScope() {
8
+ return { ...currentScope };
9
+ }
10
+ function getCurrentOrganizationScopeVersion() {
11
+ return currentVersion;
12
+ }
13
+ function emitOrganizationScopeChanged(detail) {
14
+ if (typeof window === "undefined" || typeof CustomEvent === "undefined") return;
15
+ const hasChanged = currentScope.organizationId !== detail.organizationId || currentScope.tenantId !== detail.tenantId;
16
+ currentScope = { ...detail };
17
+ if (hasChanged) {
18
+ currentVersion++;
19
+ }
20
+ window.dispatchEvent(new CustomEvent(ORGANIZATION_SCOPE_CHANGED_EVENT, { detail }));
21
+ }
22
+ function subscribeOrganizationScopeChanged(handler) {
23
+ if (typeof window === "undefined") return () => {
24
+ };
25
+ const listener = (event) => {
26
+ const detail = event.detail ?? { organizationId: null, tenantId: null };
27
+ handler(detail);
28
+ };
29
+ window.addEventListener(ORGANIZATION_SCOPE_CHANGED_EVENT, listener);
30
+ return () => {
31
+ window.removeEventListener(ORGANIZATION_SCOPE_CHANGED_EVENT, listener);
32
+ };
33
+ }
34
+ export {
35
+ ORGANIZATION_SCOPE_CHANGED_EVENT,
36
+ emitOrganizationScopeChanged,
37
+ getCurrentOrganizationScope,
38
+ getCurrentOrganizationScopeVersion,
39
+ subscribeOrganizationScopeChanged
40
+ };
41
+ //# sourceMappingURL=organizationEvents.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/frontend/organizationEvents.ts"],
4
+ "sourcesContent": ["export const ORGANIZATION_SCOPE_CHANGED_EVENT = 'om:organization-scope-changed'\n\nexport type OrganizationScopeChangedDetail = {\n organizationId: string | null\n tenantId: string | null\n}\n\n// Module-level state to track current scope and version\nlet currentScope: OrganizationScopeChangedDetail = { \n organizationId: null, \n tenantId: null \n}\nlet currentVersion = 0\n\nexport function getCurrentOrganizationScope(): OrganizationScopeChangedDetail {\n return { ...currentScope }\n}\n\nexport function getCurrentOrganizationScopeVersion(): number {\n return currentVersion\n}\n\nexport function emitOrganizationScopeChanged(detail: OrganizationScopeChangedDetail): void {\n if (typeof window === 'undefined' || typeof CustomEvent === 'undefined') return\n \n // Detect actual changes\n const hasChanged = \n currentScope.organizationId !== detail.organizationId ||\n currentScope.tenantId !== detail.tenantId\n \n // Update module-level state\n currentScope = { ...detail }\n \n // Increment version only if actual change detected\n if (hasChanged) {\n currentVersion++\n }\n \n // Emit event\n window.dispatchEvent(new CustomEvent<OrganizationScopeChangedDetail>(ORGANIZATION_SCOPE_CHANGED_EVENT, { detail }))\n}\n\nexport function subscribeOrganizationScopeChanged(\n handler: (detail: OrganizationScopeChangedDetail) => void\n): () => void {\n if (typeof window === 'undefined') return () => {}\n const listener = (event: Event) => {\n const detail = (event as CustomEvent<OrganizationScopeChangedDetail>).detail ?? { organizationId: null, tenantId: null }\n handler(detail)\n }\n window.addEventListener(ORGANIZATION_SCOPE_CHANGED_EVENT, listener as EventListener)\n return () => {\n window.removeEventListener(ORGANIZATION_SCOPE_CHANGED_EVENT, listener as EventListener)\n }\n}\n"],
5
+ "mappings": "AAAO,MAAM,mCAAmC;AAQhD,IAAI,eAA+C;AAAA,EACjD,gBAAgB;AAAA,EAChB,UAAU;AACZ;AACA,IAAI,iBAAiB;AAEd,SAAS,8BAA8D;AAC5E,SAAO,EAAE,GAAG,aAAa;AAC3B;AAEO,SAAS,qCAA6C;AAC3D,SAAO;AACT;AAEO,SAAS,6BAA6B,QAA8C;AACzF,MAAI,OAAO,WAAW,eAAe,OAAO,gBAAgB,YAAa;AAGzE,QAAM,aACJ,aAAa,mBAAmB,OAAO,kBACvC,aAAa,aAAa,OAAO;AAGnC,iBAAe,EAAE,GAAG,OAAO;AAG3B,MAAI,YAAY;AACd;AAAA,EACF;AAGA,SAAO,cAAc,IAAI,YAA4C,kCAAkC,EAAE,OAAO,CAAC,CAAC;AACpH;AAEO,SAAS,kCACd,SACY;AACZ,MAAI,OAAO,WAAW,YAAa,QAAO,MAAM;AAAA,EAAC;AACjD,QAAM,WAAW,CAAC,UAAiB;AACjC,UAAM,SAAU,MAAsD,UAAU,EAAE,gBAAgB,MAAM,UAAU,KAAK;AACvH,YAAQ,MAAM;AAAA,EAChB;AACA,SAAO,iBAAiB,kCAAkC,QAAyB;AACnF,SAAO,MAAM;AACX,WAAO,oBAAoB,kCAAkC,QAAyB;AAAA,EACxF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,32 @@
1
+ "use client";
2
+ import * as React from "react";
3
+ import {
4
+ subscribeOrganizationScopeChanged,
5
+ getCurrentOrganizationScope,
6
+ getCurrentOrganizationScopeVersion
7
+ } from "./organizationEvents.js";
8
+ function useOrganizationScopeVersion() {
9
+ const [version, setVersion] = React.useState(getCurrentOrganizationScopeVersion);
10
+ React.useEffect(() => {
11
+ return subscribeOrganizationScopeChanged(() => {
12
+ setVersion(getCurrentOrganizationScopeVersion());
13
+ });
14
+ }, []);
15
+ return version;
16
+ }
17
+ function useOrganizationScopeDetail() {
18
+ const [detail, setDetail] = React.useState(
19
+ getCurrentOrganizationScope
20
+ );
21
+ React.useEffect(() => {
22
+ return subscribeOrganizationScopeChanged((next) => {
23
+ setDetail(next);
24
+ });
25
+ }, []);
26
+ return detail;
27
+ }
28
+ export {
29
+ useOrganizationScopeDetail,
30
+ useOrganizationScopeVersion
31
+ };
32
+ //# sourceMappingURL=useOrganizationScope.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/lib/frontend/useOrganizationScope.ts"],
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { \n subscribeOrganizationScopeChanged, \n getCurrentOrganizationScope,\n getCurrentOrganizationScopeVersion,\n type OrganizationScopeChangedDetail \n} from './organizationEvents'\n\nexport function useOrganizationScopeVersion(): number {\n const [version, setVersion] = React.useState(getCurrentOrganizationScopeVersion)\n React.useEffect(() => {\n return subscribeOrganizationScopeChanged(() => {\n setVersion(getCurrentOrganizationScopeVersion())\n })\n }, [])\n return version\n}\n\nexport function useOrganizationScopeDetail(): OrganizationScopeChangedDetail {\n const [detail, setDetail] = React.useState<OrganizationScopeChangedDetail>(\n getCurrentOrganizationScope\n )\n React.useEffect(() => {\n return subscribeOrganizationScopeChanged((next) => {\n setDetail(next)\n })\n }, [])\n return detail\n}\n"],
5
+ "mappings": ";AACA,YAAY,WAAW;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAEA,SAAS,8BAAsC;AACpD,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,kCAAkC;AAC/E,QAAM,UAAU,MAAM;AACpB,WAAO,kCAAkC,MAAM;AAC7C,iBAAW,mCAAmC,CAAC;AAAA,IACjD,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AACL,SAAO;AACT;AAEO,SAAS,6BAA6D;AAC3E,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM;AAAA,IAChC;AAAA,EACF;AACA,QAAM,UAAU,MAAM;AACpB,WAAO,kCAAkC,CAAC,SAAS;AACjD,gBAAU,IAAI;AAAA,IAChB,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AACL,SAAO;AACT;",
6
+ "names": []
7
+ }