@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,97 @@
1
+ import registry from 'language-subtag-registry/data/json/registry.json'
2
+
3
+ type RegistryEntry = {
4
+ Type: string
5
+ Subtag?: string
6
+ Description?: string[]
7
+ Deprecated?: string
8
+ }
9
+
10
+ export type IsoCountry = {
11
+ code: string
12
+ name: string
13
+ }
14
+
15
+ function isIsoAlpha2(entry: RegistryEntry): entry is RegistryEntry & { Subtag: string; Description: string[] } {
16
+ if (entry.Type !== 'region') return false
17
+ if (!entry.Subtag || !/^[A-Z]{2}$/.test(entry.Subtag)) return false
18
+ if (entry.Deprecated) return false
19
+ if (!entry.Description || !entry.Description.length) return false
20
+ if (entry.Description[0] === 'Private use') return false
21
+ return true
22
+ }
23
+
24
+ const RAW_COUNTRIES: IsoCountry[] = (registry as RegistryEntry[])
25
+ .filter(isIsoAlpha2)
26
+ .map((entry) => ({
27
+ code: entry.Subtag,
28
+ name: entry.Description.join(', '),
29
+ }))
30
+
31
+ export const ISO_COUNTRIES: IsoCountry[] = [...RAW_COUNTRIES].sort((a, b) =>
32
+ a.name.localeCompare(b.name, 'en', { sensitivity: 'base' })
33
+ )
34
+
35
+ export const COUNTRY_PRIORITY: string[] = ['PL', 'DE', 'ES', 'FR', 'IT', 'US', 'GB', 'CA']
36
+
37
+ const displayNameCache = new Map<string, Intl.DisplayNames>()
38
+
39
+ function getDisplayNames(locale: string): Intl.DisplayNames | null {
40
+ if (typeof Intl === 'undefined' || typeof Intl.DisplayNames === 'undefined') return null
41
+ const key = locale || 'en'
42
+ let instance = displayNameCache.get(key)
43
+ if (!instance) {
44
+ try {
45
+ instance = new Intl.DisplayNames([key], { type: 'region' })
46
+ displayNameCache.set(key, instance)
47
+ } catch {
48
+ return null
49
+ }
50
+ }
51
+ return instance
52
+ }
53
+
54
+ export function resolveCountryName(code: string, options: { locale?: string } = {}): string {
55
+ const normalized = code.toUpperCase()
56
+ const fallback = ISO_COUNTRIES.find((entry) => entry.code === normalized)?.name ?? normalized
57
+ const displayNames = getDisplayNames(options.locale ?? 'en')
58
+ if (!displayNames) return fallback
59
+ try {
60
+ const label = displayNames.of(normalized as any)
61
+ return typeof label === 'string' ? label : fallback
62
+ } catch {
63
+ return fallback
64
+ }
65
+ }
66
+
67
+ export function buildCountryOptions(options: {
68
+ locale?: string
69
+ priority?: string[]
70
+ transformLabel?: (code: string, defaultLabel: string) => string
71
+ } = {}): Array<{ code: string; label: string }> {
72
+ const { locale, transformLabel } = options
73
+ const priority = (options.priority ?? COUNTRY_PRIORITY).map((code) => code.toUpperCase())
74
+ const prioritySet = new Set(priority)
75
+ const labelFor = (code: string) => {
76
+ const base = resolveCountryName(code, { locale })
77
+ return transformLabel ? transformLabel(code, base) : base
78
+ }
79
+
80
+ const byCode = new Map(ISO_COUNTRIES.map((entry) => [entry.code, entry]))
81
+
82
+ const prioritized = priority
83
+ .map((code) => {
84
+ const base = byCode.get(code)
85
+ if (!base) return null
86
+ return { code, label: labelFor(code) }
87
+ })
88
+ .filter((entry): entry is { code: string; label: string } => !!entry)
89
+ .sort((a, b) => a.label.localeCompare(b.label, locale ?? 'en', { sensitivity: 'base' }))
90
+
91
+ const remaining = ISO_COUNTRIES
92
+ .filter((entry) => !prioritySet.has(entry.code))
93
+ .map((entry) => ({ code: entry.code, label: labelFor(entry.code) }))
94
+ .sort((a, b) => a.label.localeCompare(b.label, locale ?? 'en', { sensitivity: 'base' }))
95
+
96
+ return [...prioritized, ...remaining]
97
+ }
@@ -0,0 +1 @@
1
+ export { registerModules, getModules } from './registry'
@@ -0,0 +1,18 @@
1
+ import type { Module } from '@open-mercato/shared/modules/registry'
2
+
3
+ // Registration pattern for publishable packages
4
+ let _modules: Module[] | null = null
5
+
6
+ export function registerModules(modules: Module[]) {
7
+ if (_modules !== null && process.env.NODE_ENV === 'development') {
8
+ console.debug('[Bootstrap] Modules re-registered (this may occur during HMR)')
9
+ }
10
+ _modules = modules
11
+ }
12
+
13
+ export function getModules(): Module[] {
14
+ if (!_modules) {
15
+ throw new Error('[Bootstrap] Modules not registered. Call registerModules() at bootstrap.')
16
+ }
17
+ return _modules
18
+ }
@@ -0,0 +1,218 @@
1
+ import { z, type ZodTypeAny } from 'zod'
2
+ import type { OpenApiRouteDoc } from './types'
3
+
4
+ export const defaultCreateResponseSchema = z.object({ id: z.string().uuid().nullable() })
5
+ export const defaultOkResponseSchema = z.object({ ok: z.literal(true) })
6
+
7
+ export type PagedListResponseOptions = {
8
+ paginationMetaOptional?: boolean
9
+ }
10
+
11
+ export function createPagedListResponseSchema(itemSchema: ZodTypeAny, options: PagedListResponseOptions = {}) {
12
+ const paginationMetaOptional = options.paginationMetaOptional ?? false
13
+
14
+ return z.object({
15
+ items: z.array(itemSchema),
16
+ total: z.number(),
17
+ page: paginationMetaOptional ? z.number().optional() : z.number(),
18
+ pageSize: paginationMetaOptional ? z.number().optional() : z.number(),
19
+ totalPages: z.number(),
20
+ })
21
+ }
22
+
23
+ type CrudMethodConfig = {
24
+ schema: ZodTypeAny
25
+ description?: string
26
+ responseSchema?: ZodTypeAny
27
+ }
28
+
29
+ type CrudCreateConfig = CrudMethodConfig & {
30
+ status?: number
31
+ }
32
+
33
+ type CrudDeleteConfig = {
34
+ schema?: ZodTypeAny
35
+ description?: string
36
+ responseSchema?: ZodTypeAny
37
+ }
38
+
39
+ export type CrudOpenApiOptions = {
40
+ tag?: string
41
+ resourceName: string
42
+ pluralName?: string
43
+ description?: string
44
+ querySchema?: ZodTypeAny
45
+ listResponseSchema: ZodTypeAny
46
+ create?: CrudCreateConfig
47
+ update?: CrudMethodConfig
48
+ del?: CrudDeleteConfig
49
+ }
50
+
51
+ export type CrudTextContext = {
52
+ resourceName: string
53
+ resourceLower: string
54
+ pluralName: string
55
+ pluralLower: string
56
+ }
57
+
58
+ export type CrudOpenApiFactoryConfig = {
59
+ defaultTag: string
60
+ defaultCreateResponseSchema?: ZodTypeAny
61
+ defaultOkResponseSchema?: ZodTypeAny
62
+ makeListDescription?: (ctx: CrudTextContext) => string
63
+ makeCreateDescription?: (ctx: CrudTextContext) => string
64
+ makeCreateRequestBodyDescription?: (ctx: CrudTextContext) => string
65
+ makeUpdateDescription?: (ctx: CrudTextContext) => string
66
+ makeUpdateRequestBodyDescription?: (ctx: CrudTextContext) => string
67
+ makeDeleteDescription?: (ctx: CrudTextContext) => string
68
+ makeDeleteRequestBodyDescription?: (ctx: CrudTextContext) => string
69
+ }
70
+
71
+ function resolveDefault(
72
+ factory: ((ctx: CrudTextContext) => string) | undefined,
73
+ ctx: CrudTextContext,
74
+ fallback: string,
75
+ ) {
76
+ if (typeof factory === 'function') return factory(ctx)
77
+ return fallback
78
+ }
79
+
80
+ export function createCrudOpenApiFactory(config: CrudOpenApiFactoryConfig) {
81
+ return function createCrudOpenApi(options: CrudOpenApiOptions): OpenApiRouteDoc {
82
+ const {
83
+ resourceName,
84
+ pluralName,
85
+ tag,
86
+ description,
87
+ querySchema,
88
+ listResponseSchema,
89
+ create,
90
+ update,
91
+ del,
92
+ } = options
93
+
94
+ const plural = pluralName ?? `${resourceName}s`
95
+ const resourceLower = resourceName.toLowerCase()
96
+ const pluralLower = plural.toLowerCase()
97
+ const context: CrudTextContext = {
98
+ resourceName,
99
+ resourceLower,
100
+ pluralName: plural,
101
+ pluralLower,
102
+ }
103
+
104
+ const fallbackCreateResponseSchema = config.defaultCreateResponseSchema ?? defaultCreateResponseSchema
105
+ const fallbackOkResponseSchema = config.defaultOkResponseSchema ?? defaultOkResponseSchema
106
+
107
+ const methods: NonNullable<OpenApiRouteDoc['methods']> = {}
108
+
109
+ methods.GET = {
110
+ summary: `List ${pluralLower}`,
111
+ description:
112
+ description ?? resolveDefault(config.makeListDescription, context, `Returns a paginated collection of ${pluralLower}.`),
113
+ query: querySchema,
114
+ responses: [
115
+ {
116
+ status: 200,
117
+ description: `Paginated ${pluralLower}`,
118
+ schema: listResponseSchema,
119
+ },
120
+ ],
121
+ }
122
+
123
+ if (create) {
124
+ const createDescription =
125
+ create.description ??
126
+ resolveDefault(config.makeCreateDescription, context, `Creates a new ${resourceLower}.`)
127
+
128
+ const createBodyDescription =
129
+ resolveDefault(
130
+ config.makeCreateRequestBodyDescription,
131
+ context,
132
+ create.description ?? `Payload describing the ${resourceLower} to create.`,
133
+ )
134
+
135
+ methods.POST = {
136
+ summary: `Create ${resourceLower}`,
137
+ description: createDescription,
138
+ requestBody: {
139
+ schema: create.schema,
140
+ description: createBodyDescription,
141
+ },
142
+ responses: [
143
+ {
144
+ status: create.status ?? 201,
145
+ description: `${resourceName} created`,
146
+ schema: create.responseSchema ?? fallbackCreateResponseSchema,
147
+ },
148
+ ],
149
+ }
150
+ }
151
+
152
+ if (update) {
153
+ const updateDescription =
154
+ update.description ??
155
+ resolveDefault(config.makeUpdateDescription, context, `Updates an existing ${resourceLower} by id.`)
156
+
157
+ const updateBodyDescription =
158
+ resolveDefault(
159
+ config.makeUpdateRequestBodyDescription,
160
+ context,
161
+ update.description ?? `Fields to update on the ${resourceLower}.`,
162
+ )
163
+
164
+ methods.PUT = {
165
+ summary: `Update ${resourceLower}`,
166
+ description: updateDescription,
167
+ requestBody: {
168
+ schema: update.schema,
169
+ description: updateBodyDescription,
170
+ },
171
+ responses: [
172
+ {
173
+ status: 200,
174
+ description: `${resourceName} updated`,
175
+ schema: update.responseSchema ?? fallbackOkResponseSchema,
176
+ },
177
+ ],
178
+ }
179
+ }
180
+
181
+ if (del) {
182
+ const deleteDescription =
183
+ del.description ??
184
+ resolveDefault(config.makeDeleteDescription, context, `Deletes a ${resourceLower} identified by id.`)
185
+
186
+ const deleteBodyDescription =
187
+ resolveDefault(
188
+ config.makeDeleteRequestBodyDescription,
189
+ context,
190
+ del.description ?? 'Identifier payload.',
191
+ )
192
+
193
+ methods.DELETE = {
194
+ summary: `Delete ${resourceLower}`,
195
+ description: deleteDescription,
196
+ requestBody: del.schema
197
+ ? {
198
+ schema: del.schema,
199
+ description: deleteBodyDescription,
200
+ }
201
+ : undefined,
202
+ responses: [
203
+ {
204
+ status: 200,
205
+ description: `${resourceName} deleted`,
206
+ schema: del.responseSchema ?? fallbackOkResponseSchema,
207
+ },
208
+ ],
209
+ }
210
+ }
211
+
212
+ return {
213
+ tag: tag ?? config.defaultTag,
214
+ summary: `${resourceName} management`,
215
+ methods,
216
+ }
217
+ }
218
+ }