@entifix/next-shell 0.1.0-0

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 (271) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +7 -0
  3. package/dist/index.d.ts +21 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +19 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/lib/back-office/account-menu.d.ts +29 -0
  8. package/dist/lib/back-office/account-menu.d.ts.map +1 -0
  9. package/dist/lib/back-office/account-menu.js +70 -0
  10. package/dist/lib/back-office/account-menu.js.map +1 -0
  11. package/dist/lib/back-office/back-office-shell.d.ts +50 -0
  12. package/dist/lib/back-office/back-office-shell.d.ts.map +1 -0
  13. package/dist/lib/back-office/back-office-shell.js +128 -0
  14. package/dist/lib/back-office/back-office-shell.js.map +1 -0
  15. package/dist/lib/back-office/breadcrumbs.d.ts +16 -0
  16. package/dist/lib/back-office/breadcrumbs.d.ts.map +1 -0
  17. package/dist/lib/back-office/breadcrumbs.js +53 -0
  18. package/dist/lib/back-office/breadcrumbs.js.map +1 -0
  19. package/dist/lib/back-office/group-by-screen-type.d.ts +21 -0
  20. package/dist/lib/back-office/group-by-screen-type.d.ts.map +1 -0
  21. package/dist/lib/back-office/group-by-screen-type.js +39 -0
  22. package/dist/lib/back-office/group-by-screen-type.js.map +1 -0
  23. package/dist/lib/back-office/index.d.ts +6 -0
  24. package/dist/lib/back-office/index.d.ts.map +1 -0
  25. package/dist/lib/back-office/index.js +7 -0
  26. package/dist/lib/back-office/index.js.map +1 -0
  27. package/dist/lib/back-office/nav.d.ts +39 -0
  28. package/dist/lib/back-office/nav.d.ts.map +1 -0
  29. package/dist/lib/back-office/nav.js +3 -0
  30. package/dist/lib/back-office/nav.js.map +1 -0
  31. package/dist/lib/back-office/sidebar-nav.d.ts +26 -0
  32. package/dist/lib/back-office/sidebar-nav.d.ts.map +1 -0
  33. package/dist/lib/back-office/sidebar-nav.js +181 -0
  34. package/dist/lib/back-office/sidebar-nav.js.map +1 -0
  35. package/dist/lib/command-palette/command-palette-host.d.ts +41 -0
  36. package/dist/lib/command-palette/command-palette-host.d.ts.map +1 -0
  37. package/dist/lib/command-palette/command-palette-host.js +196 -0
  38. package/dist/lib/command-palette/command-palette-host.js.map +1 -0
  39. package/dist/lib/command-palette/index.d.ts +9 -0
  40. package/dist/lib/command-palette/index.d.ts.map +1 -0
  41. package/dist/lib/command-palette/index.js +10 -0
  42. package/dist/lib/command-palette/index.js.map +1 -0
  43. package/dist/lib/command-palette/palette-command.d.ts +32 -0
  44. package/dist/lib/command-palette/palette-command.d.ts.map +1 -0
  45. package/dist/lib/command-palette/palette-command.js +19 -0
  46. package/dist/lib/command-palette/palette-command.js.map +1 -0
  47. package/dist/lib/command-palette/use-command-recency.d.ts +28 -0
  48. package/dist/lib/command-palette/use-command-recency.d.ts.map +1 -0
  49. package/dist/lib/command-palette/use-command-recency.js +67 -0
  50. package/dist/lib/command-palette/use-command-recency.js.map +1 -0
  51. package/dist/lib/command-palette/use-command-source.d.ts +17 -0
  52. package/dist/lib/command-palette/use-command-source.d.ts.map +1 -0
  53. package/dist/lib/command-palette/use-command-source.js +70 -0
  54. package/dist/lib/command-palette/use-command-source.js.map +1 -0
  55. package/dist/lib/command-palette/use-nav-source.d.ts +21 -0
  56. package/dist/lib/command-palette/use-nav-source.d.ts.map +1 -0
  57. package/dist/lib/command-palette/use-nav-source.js +78 -0
  58. package/dist/lib/command-palette/use-nav-source.js.map +1 -0
  59. package/dist/lib/command-palette/use-records-source.d.ts +23 -0
  60. package/dist/lib/command-palette/use-records-source.d.ts.map +1 -0
  61. package/dist/lib/command-palette/use-records-source.js +199 -0
  62. package/dist/lib/command-palette/use-records-source.js.map +1 -0
  63. package/dist/lib/command-palette/use-tabs-source.d.ts +20 -0
  64. package/dist/lib/command-palette/use-tabs-source.d.ts.map +1 -0
  65. package/dist/lib/command-palette/use-tabs-source.js +52 -0
  66. package/dist/lib/command-palette/use-tabs-source.js.map +1 -0
  67. package/dist/lib/command-palette/use-use-cases-source.d.ts +22 -0
  68. package/dist/lib/command-palette/use-use-cases-source.d.ts.map +1 -0
  69. package/dist/lib/command-palette/use-use-cases-source.js +93 -0
  70. package/dist/lib/command-palette/use-use-cases-source.js.map +1 -0
  71. package/dist/lib/config/config-route.d.ts +51 -0
  72. package/dist/lib/config/config-route.d.ts.map +1 -0
  73. package/dist/lib/config/config-route.js +65 -0
  74. package/dist/lib/config/config-route.js.map +1 -0
  75. package/dist/lib/config/service-proxy-route.d.ts +28 -0
  76. package/dist/lib/config/service-proxy-route.d.ts.map +1 -0
  77. package/dist/lib/config/service-proxy-route.js +125 -0
  78. package/dist/lib/config/service-proxy-route.js.map +1 -0
  79. package/dist/lib/config/service-token.d.ts +5 -0
  80. package/dist/lib/config/service-token.d.ts.map +1 -0
  81. package/dist/lib/config/service-token.js +18 -0
  82. package/dist/lib/config/service-token.js.map +1 -0
  83. package/dist/lib/crud/entity-crud-form.d.ts +49 -0
  84. package/dist/lib/crud/entity-crud-form.d.ts.map +1 -0
  85. package/dist/lib/crud/entity-crud-form.js +133 -0
  86. package/dist/lib/crud/entity-crud-form.js.map +1 -0
  87. package/dist/lib/crud/hand-off-write.d.ts +35 -0
  88. package/dist/lib/crud/hand-off-write.d.ts.map +1 -0
  89. package/dist/lib/crud/hand-off-write.js +27 -0
  90. package/dist/lib/crud/hand-off-write.js.map +1 -0
  91. package/dist/lib/crud/index.d.ts +9 -0
  92. package/dist/lib/crud/index.d.ts.map +1 -0
  93. package/dist/lib/crud/index.js +10 -0
  94. package/dist/lib/crud/index.js.map +1 -0
  95. package/dist/lib/crud/make-entity-crud.d.ts +28 -0
  96. package/dist/lib/crud/make-entity-crud.d.ts.map +1 -0
  97. package/dist/lib/crud/make-entity-crud.js +318 -0
  98. package/dist/lib/crud/make-entity-crud.js.map +1 -0
  99. package/dist/lib/crud/make-entity-crud.types.d.ts +237 -0
  100. package/dist/lib/crud/make-entity-crud.types.d.ts.map +1 -0
  101. package/dist/lib/crud/make-entity-crud.types.js +6 -0
  102. package/dist/lib/crud/make-entity-crud.types.js.map +1 -0
  103. package/dist/lib/crud/pending-notice.d.ts +31 -0
  104. package/dist/lib/crud/pending-notice.d.ts.map +1 -0
  105. package/dist/lib/crud/pending-notice.js +72 -0
  106. package/dist/lib/crud/pending-notice.js.map +1 -0
  107. package/dist/lib/crud/slug.d.ts +13 -0
  108. package/dist/lib/crud/slug.d.ts.map +1 -0
  109. package/dist/lib/crud/slug.js +13 -0
  110. package/dist/lib/crud/slug.js.map +1 -0
  111. package/dist/lib/crud/use-entity-affordances.d.ts +20 -0
  112. package/dist/lib/crud/use-entity-affordances.d.ts.map +1 -0
  113. package/dist/lib/crud/use-entity-affordances.js +22 -0
  114. package/dist/lib/crud/use-entity-affordances.js.map +1 -0
  115. package/dist/lib/crud/use-entity-bulk.d.ts +43 -0
  116. package/dist/lib/crud/use-entity-bulk.d.ts.map +1 -0
  117. package/dist/lib/crud/use-entity-bulk.js +106 -0
  118. package/dist/lib/crud/use-entity-bulk.js.map +1 -0
  119. package/dist/lib/crud/use-entity-link-sources.d.ts +25 -0
  120. package/dist/lib/crud/use-entity-link-sources.d.ts.map +1 -0
  121. package/dist/lib/crud/use-entity-link-sources.js +37 -0
  122. package/dist/lib/crud/use-entity-link-sources.js.map +1 -0
  123. package/dist/lib/health/health-routes.d.ts +37 -0
  124. package/dist/lib/health/health-routes.d.ts.map +1 -0
  125. package/dist/lib/health/health-routes.js +90 -0
  126. package/dist/lib/health/health-routes.js.map +1 -0
  127. package/dist/lib/i18n/catalog/en.d.ts +4 -0
  128. package/dist/lib/i18n/catalog/en.d.ts.map +1 -0
  129. package/dist/lib/i18n/catalog/en.js +347 -0
  130. package/dist/lib/i18n/catalog/en.js.map +1 -0
  131. package/dist/lib/i18n/catalog/es.d.ts +397 -0
  132. package/dist/lib/i18n/catalog/es.d.ts.map +1 -0
  133. package/dist/lib/i18n/catalog/es.js +407 -0
  134. package/dist/lib/i18n/catalog/es.js.map +1 -0
  135. package/dist/lib/i18n/catalog/index.d.ts +671 -0
  136. package/dist/lib/i18n/catalog/index.d.ts.map +1 -0
  137. package/dist/lib/i18n/catalog/index.js +28 -0
  138. package/dist/lib/i18n/catalog/index.js.map +1 -0
  139. package/dist/lib/i18n/index.d.ts +2 -0
  140. package/dist/lib/i18n/index.d.ts.map +1 -0
  141. package/dist/lib/i18n/index.js +3 -0
  142. package/dist/lib/i18n/index.js.map +1 -0
  143. package/dist/lib/i18n/locale-link.d.ts +23 -0
  144. package/dist/lib/i18n/locale-link.d.ts.map +1 -0
  145. package/dist/lib/i18n/locale-link.js +43 -0
  146. package/dist/lib/i18n/locale-link.js.map +1 -0
  147. package/dist/lib/i18n/spec-catalog.d.ts +2 -0
  148. package/dist/lib/i18n/spec-catalog.d.ts.map +1 -0
  149. package/dist/lib/i18n/spec-catalog.js +84 -0
  150. package/dist/lib/i18n/spec-catalog.js.map +1 -0
  151. package/dist/lib/search/record-search-route.d.ts +44 -0
  152. package/dist/lib/search/record-search-route.d.ts.map +1 -0
  153. package/dist/lib/search/record-search-route.js +158 -0
  154. package/dist/lib/search/record-search-route.js.map +1 -0
  155. package/dist/lib/search/record-search-source.d.ts +59 -0
  156. package/dist/lib/search/record-search-source.d.ts.map +1 -0
  157. package/dist/lib/search/record-search-source.js +118 -0
  158. package/dist/lib/search/record-search-source.js.map +1 -0
  159. package/dist/lib/search/record-search.types.d.ts +85 -0
  160. package/dist/lib/search/record-search.types.d.ts.map +1 -0
  161. package/dist/lib/search/record-search.types.js +29 -0
  162. package/dist/lib/search/record-search.types.js.map +1 -0
  163. package/dist/lib/search/search-records.d.ts +25 -0
  164. package/dist/lib/search/search-records.d.ts.map +1 -0
  165. package/dist/lib/search/search-records.js +36 -0
  166. package/dist/lib/search/search-records.js.map +1 -0
  167. package/dist/lib/session/account-links.d.ts +46 -0
  168. package/dist/lib/session/account-links.d.ts.map +1 -0
  169. package/dist/lib/session/account-links.js +41 -0
  170. package/dist/lib/session/account-links.js.map +1 -0
  171. package/dist/lib/session/bearer.d.ts +25 -0
  172. package/dist/lib/session/bearer.d.ts.map +1 -0
  173. package/dist/lib/session/bearer.js +31 -0
  174. package/dist/lib/session/bearer.js.map +1 -0
  175. package/dist/lib/session/cookies.d.ts +14 -0
  176. package/dist/lib/session/cookies.d.ts.map +1 -0
  177. package/dist/lib/session/cookies.js +33 -0
  178. package/dist/lib/session/cookies.js.map +1 -0
  179. package/dist/lib/session/device.d.ts +31 -0
  180. package/dist/lib/session/device.d.ts.map +1 -0
  181. package/dist/lib/session/device.js +80 -0
  182. package/dist/lib/session/device.js.map +1 -0
  183. package/dist/lib/session/index.d.ts +3 -0
  184. package/dist/lib/session/index.d.ts.map +1 -0
  185. package/dist/lib/session/index.js +12 -0
  186. package/dist/lib/session/index.js.map +1 -0
  187. package/dist/lib/session/refresh-route.d.ts +19 -0
  188. package/dist/lib/session/refresh-route.d.ts.map +1 -0
  189. package/dist/lib/session/refresh-route.js +85 -0
  190. package/dist/lib/session/refresh-route.js.map +1 -0
  191. package/dist/lib/session/session-keepalive.d.ts +52 -0
  192. package/dist/lib/session/session-keepalive.d.ts.map +1 -0
  193. package/dist/lib/session/session-keepalive.js +72 -0
  194. package/dist/lib/session/session-keepalive.js.map +1 -0
  195. package/dist/lib/session/use-session-refresh.d.ts +43 -0
  196. package/dist/lib/session/use-session-refresh.d.ts.map +1 -0
  197. package/dist/lib/session/use-session-refresh.js +120 -0
  198. package/dist/lib/session/use-session-refresh.js.map +1 -0
  199. package/dist/lib/wizard/index.d.ts +4 -0
  200. package/dist/lib/wizard/index.d.ts.map +1 -0
  201. package/dist/lib/wizard/index.js +5 -0
  202. package/dist/lib/wizard/index.js.map +1 -0
  203. package/dist/lib/wizard/use-wizard-draft.d.ts +22 -0
  204. package/dist/lib/wizard/use-wizard-draft.d.ts.map +1 -0
  205. package/dist/lib/wizard/use-wizard-draft.js +39 -0
  206. package/dist/lib/wizard/use-wizard-draft.js.map +1 -0
  207. package/dist/lib/wizard/use-wizard-step-param.d.ts +60 -0
  208. package/dist/lib/wizard/use-wizard-step-param.d.ts.map +1 -0
  209. package/dist/lib/wizard/use-wizard-step-param.js +94 -0
  210. package/dist/lib/wizard/use-wizard-step-param.js.map +1 -0
  211. package/dist/lib/wizard/wizard-tab-kind.d.ts +31 -0
  212. package/dist/lib/wizard/wizard-tab-kind.d.ts.map +1 -0
  213. package/dist/lib/wizard/wizard-tab-kind.js +33 -0
  214. package/dist/lib/wizard/wizard-tab-kind.js.map +1 -0
  215. package/dist/lib/workspace/drafts-state.d.ts +88 -0
  216. package/dist/lib/workspace/drafts-state.d.ts.map +1 -0
  217. package/dist/lib/workspace/drafts-state.js +128 -0
  218. package/dist/lib/workspace/drafts-state.js.map +1 -0
  219. package/dist/lib/workspace/entity-nav.d.ts +26 -0
  220. package/dist/lib/workspace/entity-nav.d.ts.map +1 -0
  221. package/dist/lib/workspace/entity-nav.js +56 -0
  222. package/dist/lib/workspace/entity-nav.js.map +1 -0
  223. package/dist/lib/workspace/entity-tab-kind.d.ts +51 -0
  224. package/dist/lib/workspace/entity-tab-kind.d.ts.map +1 -0
  225. package/dist/lib/workspace/entity-tab-kind.js +47 -0
  226. package/dist/lib/workspace/entity-tab-kind.js.map +1 -0
  227. package/dist/lib/workspace/idb-state-storage.d.ts +9 -0
  228. package/dist/lib/workspace/idb-state-storage.d.ts.map +1 -0
  229. package/dist/lib/workspace/idb-state-storage.js +32 -0
  230. package/dist/lib/workspace/idb-state-storage.js.map +1 -0
  231. package/dist/lib/workspace/index.d.ts +13 -0
  232. package/dist/lib/workspace/index.d.ts.map +1 -0
  233. package/dist/lib/workspace/index.js +14 -0
  234. package/dist/lib/workspace/index.js.map +1 -0
  235. package/dist/lib/workspace/pending-state.d.ts +84 -0
  236. package/dist/lib/workspace/pending-state.d.ts.map +1 -0
  237. package/dist/lib/workspace/pending-state.js +155 -0
  238. package/dist/lib/workspace/pending-state.js.map +1 -0
  239. package/dist/lib/workspace/pending-transactions.d.ts +44 -0
  240. package/dist/lib/workspace/pending-transactions.d.ts.map +1 -0
  241. package/dist/lib/workspace/pending-transactions.js +114 -0
  242. package/dist/lib/workspace/pending-transactions.js.map +1 -0
  243. package/dist/lib/workspace/tab-kind.d.ts +48 -0
  244. package/dist/lib/workspace/tab-kind.d.ts.map +1 -0
  245. package/dist/lib/workspace/tab-kind.js +47 -0
  246. package/dist/lib/workspace/tab-kind.js.map +1 -0
  247. package/dist/lib/workspace/tab-state.d.ts +36 -0
  248. package/dist/lib/workspace/tab-state.d.ts.map +1 -0
  249. package/dist/lib/workspace/tab-state.js +81 -0
  250. package/dist/lib/workspace/tab-state.js.map +1 -0
  251. package/dist/lib/workspace/tabs-state.d.ts +46 -0
  252. package/dist/lib/workspace/tabs-state.d.ts.map +1 -0
  253. package/dist/lib/workspace/tabs-state.js +56 -0
  254. package/dist/lib/workspace/tabs-state.js.map +1 -0
  255. package/dist/lib/workspace/use-entity-draft.d.ts +25 -0
  256. package/dist/lib/workspace/use-entity-draft.d.ts.map +1 -0
  257. package/dist/lib/workspace/use-entity-draft.js +38 -0
  258. package/dist/lib/workspace/use-entity-draft.js.map +1 -0
  259. package/dist/lib/workspace/workspace-scope.d.ts +51 -0
  260. package/dist/lib/workspace/workspace-scope.d.ts.map +1 -0
  261. package/dist/lib/workspace/workspace-scope.js +42 -0
  262. package/dist/lib/workspace/workspace-scope.js.map +1 -0
  263. package/dist/lib/workspace/workspace-shell.d.ts +33 -0
  264. package/dist/lib/workspace/workspace-shell.d.ts.map +1 -0
  265. package/dist/lib/workspace/workspace-shell.js +206 -0
  266. package/dist/lib/workspace/workspace-shell.js.map +1 -0
  267. package/dist/server.d.ts +13 -0
  268. package/dist/server.d.ts.map +1 -0
  269. package/dist/server.js +41 -0
  270. package/dist/server.js.map +1 -0
  271. package/package.json +95 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/i18n/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './locale-link.js';
2
+
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/i18n/index.ts"],"sourcesContent":["export * from './locale-link.js';\n"],"names":[],"mappings":"AAAA,cAAc,mBAAmB"}
@@ -0,0 +1,23 @@
1
+ import { type Locale } from '@entifix/core';
2
+ import Link from 'next/link';
3
+ import type { ComponentPropsWithoutRef } from 'react';
4
+ /**
5
+ * Prefixes an in-app href with the active locale.
6
+ *
7
+ * Every internal link has to go through this. An unprefixed `/catalog` still
8
+ * works — the middleware negotiates and redirects — but the visitor pays a
9
+ * round trip and, worse, a visitor reading `/en/...` who follows a bare link
10
+ * lands back on whatever their cookie says. The helper is idempotent and leaves
11
+ * absolute URLs alone, so it is safe to apply at every call site.
12
+ */
13
+ export declare function useLocaleHref(): (href: string) => string;
14
+ /**
15
+ * The locale the URL is actually on. Reads the browser path rather than the
16
+ * provider, so it is correct inside `usePathname`-driven code (breadcrumbs)
17
+ * even before the provider re-renders.
18
+ */
19
+ export declare function usePathLocale(): Locale | undefined;
20
+ export type LocaleLinkProps = ComponentPropsWithoutRef<typeof Link>;
21
+ /** `next/link` with the active locale already on the href. */
22
+ export declare function LocaleLink({ href, ...props }: LocaleLinkProps): import("react").JSX.Element;
23
+ //# sourceMappingURL=locale-link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locale-link.d.ts","sourceRoot":"","sources":["../../../src/lib/i18n/locale-link.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,MAAM,EAA+B,MAAM,eAAe,CAAC;AAEzE,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,OAAO,CAAC;AAGtD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,IAAI,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAGxD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAElD;AAED,MAAM,MAAM,eAAe,GAAG,wBAAwB,CAAC,OAAO,IAAI,CAAC,CAAC;AAEpE,8DAA8D;AAC9D,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,+BAK7D"}
@@ -0,0 +1,43 @@
1
+ 'use client';
2
+ import { _ as _extends } from "@swc/helpers/_/_extends";
3
+ import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
4
+ import { jsx as _jsx } from "react/jsx-runtime";
5
+ import { localeHref, splitLocalePath } from '@entifix/core';
6
+ import { useLocale } from '@entifix/react-controls';
7
+ import Link from 'next/link';
8
+ import { usePathname } from 'next/navigation';
9
+ import { useCallback } from 'react';
10
+ /**
11
+ * Prefixes an in-app href with the active locale.
12
+ *
13
+ * Every internal link has to go through this. An unprefixed `/catalog` still
14
+ * works — the middleware negotiates and redirects — but the visitor pays a
15
+ * round trip and, worse, a visitor reading `/en/...` who follows a bare link
16
+ * lands back on whatever their cookie says. The helper is idempotent and leaves
17
+ * absolute URLs alone, so it is safe to apply at every call site.
18
+ */ export function useLocaleHref() {
19
+ const locale = useLocale();
20
+ return useCallback((href)=>localeHref(locale, href), [
21
+ locale
22
+ ]);
23
+ }
24
+ /**
25
+ * The locale the URL is actually on. Reads the browser path rather than the
26
+ * provider, so it is correct inside `usePathname`-driven code (breadcrumbs)
27
+ * even before the provider re-renders.
28
+ */ export function usePathLocale() {
29
+ var _usePathname;
30
+ return splitLocalePath((_usePathname = usePathname()) != null ? _usePathname : '/').locale;
31
+ }
32
+ /** `next/link` with the active locale already on the href. */ export function LocaleLink(_0) {
33
+ let { href } = _0, props = _object_without_properties_loose(_0, [
34
+ "href"
35
+ ]);
36
+ const withLocale = useLocaleHref();
37
+ const resolved = typeof href === 'string' ? withLocale(href) : href;
38
+ return /*#__PURE__*/ _jsx(Link, _extends({
39
+ href: resolved
40
+ }, props));
41
+ }
42
+
43
+ //# sourceMappingURL=locale-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/i18n/locale-link.tsx"],"sourcesContent":["'use client';\n\nimport { type Locale, localeHref, splitLocalePath } from '@entifix/core';\nimport { useLocale } from '@entifix/react-controls';\nimport Link from 'next/link';\nimport { usePathname } from 'next/navigation';\nimport type { ComponentPropsWithoutRef } from 'react';\nimport { useCallback } from 'react';\n\n/**\n * Prefixes an in-app href with the active locale.\n *\n * Every internal link has to go through this. An unprefixed `/catalog` still\n * works — the middleware negotiates and redirects — but the visitor pays a\n * round trip and, worse, a visitor reading `/en/...` who follows a bare link\n * lands back on whatever their cookie says. The helper is idempotent and leaves\n * absolute URLs alone, so it is safe to apply at every call site.\n */\nexport function useLocaleHref(): (href: string) => string {\n const locale = useLocale();\n return useCallback((href: string) => localeHref(locale, href), [locale]);\n}\n\n/**\n * The locale the URL is actually on. Reads the browser path rather than the\n * provider, so it is correct inside `usePathname`-driven code (breadcrumbs)\n * even before the provider re-renders.\n */\nexport function usePathLocale(): Locale | undefined {\n return splitLocalePath(usePathname() ?? '/').locale;\n}\n\nexport type LocaleLinkProps = ComponentPropsWithoutRef<typeof Link>;\n\n/** `next/link` with the active locale already on the href. */\nexport function LocaleLink({ href, ...props }: LocaleLinkProps) {\n const withLocale = useLocaleHref();\n const resolved = typeof href === 'string' ? withLocale(href) : href;\n\n return <Link href={resolved} {...props} />;\n}\n"],"names":["localeHref","splitLocalePath","useLocale","Link","usePathname","useCallback","useLocaleHref","locale","href","usePathLocale","LocaleLink","props","withLocale","resolved"],"mappings":"AAAA;;;;AAEA,SAAsBA,UAAU,EAAEC,eAAe,QAAQ,gBAAgB;AACzE,SAASC,SAAS,QAAQ,0BAA0B;AACpD,OAAOC,UAAU,YAAY;AAC7B,SAASC,WAAW,QAAQ,kBAAkB;AAE9C,SAASC,WAAW,QAAQ,QAAQ;AAEpC;;;;;;;;CAQC,GACD,OAAO,SAASC;IACd,MAAMC,SAASL;IACf,OAAOG,YAAY,CAACG,OAAiBR,WAAWO,QAAQC,OAAO;QAACD;KAAO;AACzE;AAEA;;;;CAIC,GACD,OAAO,SAASE;QACSL;IAAvB,OAAOH,iBAAgBG,eAAAA,yBAAAA,eAAiB,KAAKG,MAAM;AACrD;AAIA,4DAA4D,GAC5D,OAAO,SAASG,WAAW,EAAmC;QAAnC,EAAEF,IAAI,EAA6B,OAAxBG;;;IACpC,MAAMC,aAAaN;IACnB,MAAMO,WAAW,OAAOL,SAAS,WAAWI,WAAWJ,QAAQA;IAE/D,qBAAO,KAACL;QAAKK,MAAMK;OAAcF;AACnC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=spec-catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-catalog.d.ts","sourceRoot":"","sources":["../../../src/lib/i18n/spec-catalog.ts"],"names":[],"mappings":""}
@@ -0,0 +1,84 @@
1
+ import { registerFallbackCatalog } from '@entifix/react-controls';
2
+ /**
3
+ * An `entity` catalog for this package's own specs.
4
+ *
5
+ * ⚠️ **A fixture, not a shipped catalog.** `entity` names the *host's* entities,
6
+ * so a framework shipping one would be shipping product copy — which is what
7
+ * [ADR 0059](../../../../../../docs/adr/0059-entifix-leaves-the-repo.md)
8
+ * removes. These specs assert that a nav entry, a palette command and a
9
+ * workspace tab name a record in the catalog's own words; a fixture proves that
10
+ * as well as r10c's copy did, and keeps the assertions readable as sentences.
11
+ */ const es = {
12
+ product: {
13
+ label: 'Producto',
14
+ plural: 'Productos'
15
+ },
16
+ thing: {
17
+ label: 'Cosa',
18
+ plural: 'Cosas'
19
+ },
20
+ widget: {
21
+ label: 'Artilugio',
22
+ plural: 'Artilugios'
23
+ },
24
+ 'product-category': {
25
+ label: 'Categoría',
26
+ plural: 'Categorías'
27
+ },
28
+ 'product-brand': {
29
+ label: 'Marca',
30
+ plural: 'Marcas',
31
+ form: {
32
+ newTitle: 'Nueva marca',
33
+ editTitle: 'Editar marca'
34
+ },
35
+ fields: {
36
+ id: 'Id',
37
+ code: 'Código',
38
+ name: 'Nombre',
39
+ website: 'Sitio web'
40
+ },
41
+ // No `archive` on purpose — a CRUD spec asserts that a verb the
42
+ // catalog does not name still renders, as its key.
43
+ useCases: {
44
+ retire: 'Retirar'
45
+ }
46
+ },
47
+ 'product-specification': {
48
+ label: 'Producto',
49
+ plural: 'Productos',
50
+ fields: {
51
+ id: 'Id',
52
+ name: 'Nombre',
53
+ brand: 'Marca'
54
+ }
55
+ },
56
+ 'user-identity': {
57
+ label: 'Usuario',
58
+ plural: 'Usuarios',
59
+ useCases: {
60
+ signOutOthers: 'Cerrar mis otras sesiones',
61
+ signOutOthersConfirm: 'Se cerrarán todas tus sesiones en otros dispositivos y navegadores. ' + 'Ésta seguirá abierta.',
62
+ signOutOthersKeywords: 'cerrar sesión, dispositivos, logout'
63
+ }
64
+ }
65
+ };
66
+ registerFallbackCatalog('entity', {
67
+ es,
68
+ en: es
69
+ });
70
+ /**
71
+ * The same for `errors` — the code vocabulary a host's services answer with.
72
+ * The palette renders a scope refusal through it, so it needs the two codes
73
+ * these specs actually raise.
74
+ */ const errors = {
75
+ noActiveOrganization: 'Selecciona una organización para ver estos registros.',
76
+ alreadyRetired: 'El registro ya estaba retirado.',
77
+ unexpected: 'Ocurrió un error inesperado.'
78
+ };
79
+ registerFallbackCatalog('errors', {
80
+ es: errors,
81
+ en: errors
82
+ });
83
+
84
+ //# sourceMappingURL=spec-catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/i18n/spec-catalog.ts"],"sourcesContent":["import { registerFallbackCatalog } from '@entifix/react-controls';\n\n/**\n * An `entity` catalog for this package's own specs.\n *\n * ⚠️ **A fixture, not a shipped catalog.** `entity` names the *host's* entities,\n * so a framework shipping one would be shipping product copy — which is what\n * [ADR 0059](../../../../../../docs/adr/0059-entifix-leaves-the-repo.md)\n * removes. These specs assert that a nav entry, a palette command and a\n * workspace tab name a record in the catalog's own words; a fixture proves that\n * as well as r10c's copy did, and keeps the assertions readable as sentences.\n */\nconst es = {\n product: { label: 'Producto', plural: 'Productos' },\n thing: { label: 'Cosa', plural: 'Cosas' },\n widget: { label: 'Artilugio', plural: 'Artilugios' },\n 'product-category': { label: 'Categoría', plural: 'Categorías' },\n 'product-brand': {\n label: 'Marca',\n plural: 'Marcas',\n form: { newTitle: 'Nueva marca', editTitle: 'Editar marca' },\n fields: {\n id: 'Id',\n code: 'Código',\n name: 'Nombre',\n website: 'Sitio web',\n },\n // No `archive` on purpose — a CRUD spec asserts that a verb the\n // catalog does not name still renders, as its key.\n useCases: { retire: 'Retirar' },\n },\n 'product-specification': {\n label: 'Producto',\n plural: 'Productos',\n fields: { id: 'Id', name: 'Nombre', brand: 'Marca' },\n },\n 'user-identity': {\n label: 'Usuario',\n plural: 'Usuarios',\n useCases: {\n signOutOthers: 'Cerrar mis otras sesiones',\n signOutOthersConfirm:\n 'Se cerrarán todas tus sesiones en otros dispositivos y navegadores. ' +\n 'Ésta seguirá abierta.',\n signOutOthersKeywords: 'cerrar sesión, dispositivos, logout',\n },\n },\n} as const;\n\nregisterFallbackCatalog('entity', { es, en: es });\n\n/**\n * The same for `errors` — the code vocabulary a host's services answer with.\n * The palette renders a scope refusal through it, so it needs the two codes\n * these specs actually raise.\n */\nconst errors = {\n noActiveOrganization: 'Selecciona una organización para ver estos registros.',\n alreadyRetired: 'El registro ya estaba retirado.',\n unexpected: 'Ocurrió un error inesperado.',\n} as const;\n\nregisterFallbackCatalog('errors', { es: errors, en: errors });\n"],"names":["registerFallbackCatalog","es","product","label","plural","thing","widget","form","newTitle","editTitle","fields","id","code","name","website","useCases","retire","brand","signOutOthers","signOutOthersConfirm","signOutOthersKeywords","en","errors","noActiveOrganization","alreadyRetired","unexpected"],"mappings":"AAAA,SAASA,uBAAuB,QAAQ,0BAA0B;AAElE;;;;;;;;;CASC,GACD,MAAMC,KAAK;IACTC,SAAS;QAAEC,OAAO;QAAYC,QAAQ;IAAY;IAClDC,OAAO;QAAEF,OAAO;QAAQC,QAAQ;IAAQ;IACxCE,QAAQ;QAAEH,OAAO;QAAaC,QAAQ;IAAa;IACnD,oBAAoB;QAAED,OAAO;QAAaC,QAAQ;IAAa;IAC/D,iBAAiB;QACfD,OAAO;QACPC,QAAQ;QACRG,MAAM;YAAEC,UAAU;YAAeC,WAAW;QAAe;QAC3DC,QAAQ;YACNC,IAAI;YACJC,MAAM;YACNC,MAAM;YACNC,SAAS;QACX;QACA,gEAAgE;QAChE,mDAAmD;QACnDC,UAAU;YAAEC,QAAQ;QAAU;IAChC;IACA,yBAAyB;QACvBb,OAAO;QACPC,QAAQ;QACRM,QAAQ;YAAEC,IAAI;YAAME,MAAM;YAAUI,OAAO;QAAQ;IACrD;IACA,iBAAiB;QACfd,OAAO;QACPC,QAAQ;QACRW,UAAU;YACRG,eAAe;YACfC,sBACE,yEACA;YACFC,uBAAuB;QACzB;IACF;AACF;AAEApB,wBAAwB,UAAU;IAAEC;IAAIoB,IAAIpB;AAAG;AAE/C;;;;CAIC,GACD,MAAMqB,SAAS;IACbC,sBAAsB;IACtBC,gBAAgB;IAChBC,YAAY;AACd;AAEAzB,wBAAwB,UAAU;IAAEC,IAAIqB;IAAQD,IAAIC;AAAO"}
@@ -0,0 +1,44 @@
1
+ import type { RecordSearchSource } from './record-search-source.js';
2
+ export declare const SEARCH_TERM_PARAM = "q";
3
+ export declare const SEARCH_SOURCES_PARAM = "sources";
4
+ export declare const SEARCH_LIMIT_PARAM = "limit";
5
+ /**
6
+ * Below this, nothing is queried at all.
7
+ *
8
+ * A `like` becomes an unanchored, case-insensitive `$regex`, which no index can
9
+ * serve — so this endpoint is a collection scan by construction. One character
10
+ * matches most of every collection and is worth nothing to the person typing, so
11
+ * the floor costs them nothing and saves four scans per keystroke.
12
+ */
13
+ export declare const MIN_TERM_LENGTH = 2;
14
+ export declare const DEFAULT_GROUP_LIMIT = 5;
15
+ export declare const MAX_GROUP_LIMIT = 20;
16
+ export declare const DEFAULT_SOURCE_TIMEOUT_MS = 1500;
17
+ export interface RecordSearchRouteOptions {
18
+ /**
19
+ * The sources to fan out over, **in the order results are presented**. That
20
+ * order is the ranking: an operator learns where a kind of record lands, which
21
+ * a relevance score across four unrelated entities cannot offer.
22
+ */
23
+ readonly sources: readonly RecordSearchSource[];
24
+ /** Per-source budget. Concurrent, so this also bounds the whole request. */
25
+ readonly timeoutMs?: number;
26
+ /** Records per group. */
27
+ readonly limit?: number;
28
+ }
29
+ /**
30
+ * `GET /api/search` — records matching a term, across every source this host
31
+ * mounts (ADR 0040).
32
+ *
33
+ * The caller's own session is the only credential that crosses: each source is
34
+ * asked over the same guarded route its screens use, so what comes back is
35
+ * already authorized for this principal and this organization. There is no
36
+ * index, and there must never be one — a prefetched index is a tenant leak, and
37
+ * it is the reason this is a fan-out rather than a search service.
38
+ *
39
+ * A source that fails is **named** in `unavailable` rather than dropped. An
40
+ * absent group renders as "no matches", which is a confident claim the server is
41
+ * in no position to make about a service it could not reach.
42
+ */
43
+ export declare const createRecordSearchRoute: ({ sources, timeoutMs, limit: defaultLimit, }: RecordSearchRouteOptions) => (request: Request) => Promise<Response>;
44
+ //# sourceMappingURL=record-search-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record-search-route.d.ts","sourceRoot":"","sources":["../../../src/lib/search/record-search-route.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEpE,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,oBAAoB,YAAY,CAAC;AAC9C,eAAO,MAAM,kBAAkB,UAAU,CAAC;AAE1C;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,IAAI,CAAC;AACjC,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,yBAAyB,OAAQ,CAAC;AAa/C,MAAM,WAAW,wBAAwB;IACvC;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAChD,4EAA4E;IAC5E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,yBAAyB;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAmFD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,uBAAuB,GAAI,8CAIrC,wBAAwB,MAGC,SAAS,OAAO,KAAG,OAAO,CAAC,QAAQ,CA+D9D,CAAC"}
@@ -0,0 +1,158 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ import { NextResponse } from 'next/server';
3
+ import { bearerHeader, sessionToken } from '../session/bearer.js';
4
+ export const SEARCH_TERM_PARAM = 'q';
5
+ export const SEARCH_SOURCES_PARAM = 'sources';
6
+ export const SEARCH_LIMIT_PARAM = 'limit';
7
+ /**
8
+ * Below this, nothing is queried at all.
9
+ *
10
+ * A `like` becomes an unanchored, case-insensitive `$regex`, which no index can
11
+ * serve — so this endpoint is a collection scan by construction. One character
12
+ * matches most of every collection and is worth nothing to the person typing, so
13
+ * the floor costs them nothing and saves four scans per keystroke.
14
+ */ export const MIN_TERM_LENGTH = 2;
15
+ export const DEFAULT_GROUP_LIMIT = 5;
16
+ export const MAX_GROUP_LIMIT = 20;
17
+ export const DEFAULT_SOURCE_TIMEOUT_MS = 1500;
18
+ /** Upstream statuses that mean something other than "we could not reach it". */ const REASON_FOR_STATUS = {
19
+ 400: 'invalidQuery',
20
+ 401: 'unauthenticated',
21
+ 403: 'forbidden',
22
+ 404: 'notFound',
23
+ 409: 'noActiveOrganization'
24
+ };
25
+ const unavailable = (source, reason, status)=>({
26
+ ok: false,
27
+ unavailable: _extends({
28
+ source: source.key,
29
+ entity: source.entity,
30
+ reason
31
+ }, status === undefined ? {} : {
32
+ status
33
+ })
34
+ });
35
+ /**
36
+ * Ask one source, and **never reject**.
37
+ *
38
+ * Returning a typed outcome rather than throwing is what makes the fan-out a
39
+ * plain `Promise.all`: isolation comes from this catch, not from
40
+ * `allSettled` — whose rejected arm would then be unreachable and untestable.
41
+ */ const runSource = async (source, term, limit, token, timeoutMs)=>{
42
+ let upstream;
43
+ try {
44
+ upstream = await fetch(source.url(term, limit), {
45
+ headers: bearerHeader(token),
46
+ cache: 'no-store',
47
+ signal: AbortSignal.timeout(timeoutMs)
48
+ });
49
+ } catch (error) {
50
+ // `AbortSignal.timeout` rejects with a `DOMException` named `TimeoutError`;
51
+ // branching on the name rather than the class keeps this reachable from a
52
+ // test without fabricating a platform error.
53
+ return unavailable(source, error.name === 'TimeoutError' ? 'timeout' : 'network');
54
+ }
55
+ if (!upstream.ok) {
56
+ var _REASON_FOR_STATUS_upstream_status;
57
+ return unavailable(source, (_REASON_FOR_STATUS_upstream_status = REASON_FOR_STATUS[upstream.status]) != null ? _REASON_FOR_STATUS_upstream_status : 'unexpected', upstream.status);
58
+ }
59
+ const body = await upstream.json().catch(()=>undefined);
60
+ const page = source.read(body);
61
+ return page === undefined ? unavailable(source, 'unexpected', upstream.status) : {
62
+ ok: true,
63
+ group: {
64
+ source: source.key,
65
+ entity: source.entity,
66
+ labelKey: source.labelKey,
67
+ items: page.items,
68
+ total: page.total
69
+ }
70
+ };
71
+ };
72
+ const clampLimit = (raw, fallback)=>{
73
+ const asked = Number(raw);
74
+ if (!Number.isInteger(asked) || asked < 1) return fallback;
75
+ return Math.min(asked, MAX_GROUP_LIMIT);
76
+ };
77
+ /**
78
+ * `GET /api/search` — records matching a term, across every source this host
79
+ * mounts (ADR 0040).
80
+ *
81
+ * The caller's own session is the only credential that crosses: each source is
82
+ * asked over the same guarded route its screens use, so what comes back is
83
+ * already authorized for this principal and this organization. There is no
84
+ * index, and there must never be one — a prefetched index is a tenant leak, and
85
+ * it is the reason this is a fan-out rather than a search service.
86
+ *
87
+ * A source that fails is **named** in `unavailable` rather than dropped. An
88
+ * absent group renders as "no matches", which is a confident claim the server is
89
+ * in no position to make about a service it could not reach.
90
+ */ export const createRecordSearchRoute = ({ sources, timeoutMs = DEFAULT_SOURCE_TIMEOUT_MS, limit: defaultLimit = DEFAULT_GROUP_LIMIT })=>{
91
+ const byKey = new Map(sources.map((source)=>[
92
+ source.key,
93
+ source
94
+ ]));
95
+ return async function GET(request) {
96
+ var _params_get, _params_get1;
97
+ // The services decide what this caller may read; this only carries the
98
+ // token. But the gate is not redundant: `catalog-reference` reads are
99
+ // unauthenticated at the service by design, so without it a signed-out
100
+ // caller gets brand matches back and the answer reads as a real search —
101
+ // and the endpoint becomes an anonymous amplifier against that service.
102
+ const token = await sessionToken();
103
+ if (token === undefined) {
104
+ return NextResponse.json({
105
+ error: 'unauthenticated',
106
+ code: 'unauthenticated'
107
+ }, {
108
+ status: 401
109
+ });
110
+ }
111
+ const params = new URL(request.url).searchParams;
112
+ const asked = ((_params_get = params.get(SEARCH_SOURCES_PARAM)) != null ? _params_get : '').split(',').filter((name)=>name !== '');
113
+ const unknown = asked.find((name)=>!byKey.has(name));
114
+ if (unknown !== undefined) {
115
+ // Loud rather than ignored: no keystroke can produce this, only a caller
116
+ // naming a source that does not exist — and silently searching everything
117
+ // instead would answer a question nobody asked.
118
+ return NextResponse.json({
119
+ error: 'invalid query',
120
+ code: 'invalidQuery',
121
+ detail: `unknown search source "${unknown}"`
122
+ }, {
123
+ status: 400
124
+ });
125
+ }
126
+ const selected = asked.length === 0 ? sources : sources.filter((source)=>asked.includes(source.key));
127
+ const term = ((_params_get1 = params.get(SEARCH_TERM_PARAM)) != null ? _params_get1 : '').trim();
128
+ const answer = (body)=>// The body is scoped to one principal and one organization. `no-store` is
129
+ // correctness here, not tuning.
130
+ NextResponse.json(body, {
131
+ headers: {
132
+ 'cache-control': 'no-store'
133
+ }
134
+ });
135
+ if (term.length < MIN_TERM_LENGTH) {
136
+ return answer({
137
+ term,
138
+ groups: [],
139
+ unavailable: []
140
+ });
141
+ }
142
+ const limit = clampLimit(params.get(SEARCH_LIMIT_PARAM), defaultLimit);
143
+ const outcomes = await Promise.all(selected.map((source)=>runSource(source, term, limit, token, timeoutMs)));
144
+ return answer({
145
+ term,
146
+ // Declared order, never completion order — otherwise the list reshuffles
147
+ // with the network and nobody can learn where anything lands.
148
+ groups: outcomes.flatMap((outcome)=>outcome.ok ? [
149
+ outcome.group
150
+ ] : []),
151
+ unavailable: outcomes.flatMap((outcome)=>outcome.ok ? [] : [
152
+ outcome.unavailable
153
+ ])
154
+ });
155
+ };
156
+ };
157
+
158
+ //# sourceMappingURL=record-search-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/search/record-search-route.ts"],"sourcesContent":["import { NextResponse } from 'next/server';\n\nimport { bearerHeader, sessionToken } from '../session/bearer.js';\nimport type {\n RecordSearchGroup,\n RecordSearchResponse,\n RecordSearchUnavailable,\n RecordSearchUnavailableReason,\n} from './record-search.types.js';\nimport type { RecordSearchSource } from './record-search-source.js';\n\nexport const SEARCH_TERM_PARAM = 'q';\nexport const SEARCH_SOURCES_PARAM = 'sources';\nexport const SEARCH_LIMIT_PARAM = 'limit';\n\n/**\n * Below this, nothing is queried at all.\n *\n * A `like` becomes an unanchored, case-insensitive `$regex`, which no index can\n * serve — so this endpoint is a collection scan by construction. One character\n * matches most of every collection and is worth nothing to the person typing, so\n * the floor costs them nothing and saves four scans per keystroke.\n */\nexport const MIN_TERM_LENGTH = 2;\nexport const DEFAULT_GROUP_LIMIT = 5;\nexport const MAX_GROUP_LIMIT = 20;\nexport const DEFAULT_SOURCE_TIMEOUT_MS = 1_500;\n\n/** Upstream statuses that mean something other than \"we could not reach it\". */\nconst REASON_FOR_STATUS: Readonly<\n Record<number, RecordSearchUnavailableReason>\n> = {\n 400: 'invalidQuery',\n 401: 'unauthenticated',\n 403: 'forbidden',\n 404: 'notFound',\n 409: 'noActiveOrganization',\n};\n\nexport interface RecordSearchRouteOptions {\n /**\n * The sources to fan out over, **in the order results are presented**. That\n * order is the ranking: an operator learns where a kind of record lands, which\n * a relevance score across four unrelated entities cannot offer.\n */\n readonly sources: readonly RecordSearchSource[];\n /** Per-source budget. Concurrent, so this also bounds the whole request. */\n readonly timeoutMs?: number;\n /** Records per group. */\n readonly limit?: number;\n}\n\ntype SourceOutcome =\n | { readonly ok: true; readonly group: RecordSearchGroup }\n | { readonly ok: false; readonly unavailable: RecordSearchUnavailable };\n\nconst unavailable = (\n source: RecordSearchSource,\n reason: RecordSearchUnavailableReason,\n status?: number,\n): SourceOutcome => ({\n ok: false,\n unavailable: {\n source: source.key,\n entity: source.entity,\n reason,\n ...(status === undefined ? {} : { status }),\n },\n});\n\n/**\n * Ask one source, and **never reject**.\n *\n * Returning a typed outcome rather than throwing is what makes the fan-out a\n * plain `Promise.all`: isolation comes from this catch, not from\n * `allSettled` — whose rejected arm would then be unreachable and untestable.\n */\nconst runSource = async (\n source: RecordSearchSource,\n term: string,\n limit: number,\n token: string | undefined,\n timeoutMs: number,\n): Promise<SourceOutcome> => {\n let upstream: Response;\n try {\n upstream = await fetch(source.url(term, limit), {\n headers: bearerHeader(token),\n cache: 'no-store',\n signal: AbortSignal.timeout(timeoutMs),\n });\n } catch (error) {\n // `AbortSignal.timeout` rejects with a `DOMException` named `TimeoutError`;\n // branching on the name rather than the class keeps this reachable from a\n // test without fabricating a platform error.\n return unavailable(\n source,\n (error as { name?: string }).name === 'TimeoutError'\n ? 'timeout'\n : 'network',\n );\n }\n\n if (!upstream.ok) {\n return unavailable(\n source,\n REASON_FOR_STATUS[upstream.status] ?? 'unexpected',\n upstream.status,\n );\n }\n\n const body = await upstream.json().catch(() => undefined);\n const page = source.read(body);\n return page === undefined\n ? unavailable(source, 'unexpected', upstream.status)\n : {\n ok: true,\n group: {\n source: source.key,\n entity: source.entity,\n labelKey: source.labelKey,\n items: page.items,\n total: page.total,\n },\n };\n};\n\nconst clampLimit = (raw: string | null, fallback: number): number => {\n const asked = Number(raw);\n if (!Number.isInteger(asked) || asked < 1) return fallback;\n return Math.min(asked, MAX_GROUP_LIMIT);\n};\n\n/**\n * `GET /api/search` — records matching a term, across every source this host\n * mounts (ADR 0040).\n *\n * The caller's own session is the only credential that crosses: each source is\n * asked over the same guarded route its screens use, so what comes back is\n * already authorized for this principal and this organization. There is no\n * index, and there must never be one — a prefetched index is a tenant leak, and\n * it is the reason this is a fan-out rather than a search service.\n *\n * A source that fails is **named** in `unavailable` rather than dropped. An\n * absent group renders as \"no matches\", which is a confident claim the server is\n * in no position to make about a service it could not reach.\n */\nexport const createRecordSearchRoute = ({\n sources,\n timeoutMs = DEFAULT_SOURCE_TIMEOUT_MS,\n limit: defaultLimit = DEFAULT_GROUP_LIMIT,\n}: RecordSearchRouteOptions) => {\n const byKey = new Map(sources.map(source => [source.key, source]));\n\n return async function GET(request: Request): Promise<Response> {\n // The services decide what this caller may read; this only carries the\n // token. But the gate is not redundant: `catalog-reference` reads are\n // unauthenticated at the service by design, so without it a signed-out\n // caller gets brand matches back and the answer reads as a real search —\n // and the endpoint becomes an anonymous amplifier against that service.\n const token = await sessionToken();\n if (token === undefined) {\n return NextResponse.json(\n { error: 'unauthenticated', code: 'unauthenticated' },\n { status: 401 },\n );\n }\n\n const params = new URL(request.url).searchParams;\n\n const asked = (params.get(SEARCH_SOURCES_PARAM) ?? '')\n .split(',')\n .filter(name => name !== '');\n const unknown = asked.find(name => !byKey.has(name));\n if (unknown !== undefined) {\n // Loud rather than ignored: no keystroke can produce this, only a caller\n // naming a source that does not exist — and silently searching everything\n // instead would answer a question nobody asked.\n return NextResponse.json(\n {\n error: 'invalid query',\n code: 'invalidQuery',\n detail: `unknown search source \"${unknown}\"`,\n },\n { status: 400 },\n );\n }\n const selected =\n asked.length === 0\n ? sources\n : sources.filter(source => asked.includes(source.key));\n\n const term = (params.get(SEARCH_TERM_PARAM) ?? '').trim();\n const answer = (body: RecordSearchResponse) =>\n // The body is scoped to one principal and one organization. `no-store` is\n // correctness here, not tuning.\n NextResponse.json(body, { headers: { 'cache-control': 'no-store' } });\n\n if (term.length < MIN_TERM_LENGTH) {\n return answer({ term, groups: [], unavailable: [] });\n }\n\n const limit = clampLimit(params.get(SEARCH_LIMIT_PARAM), defaultLimit);\n const outcomes = await Promise.all(\n selected.map(source => runSource(source, term, limit, token, timeoutMs)),\n );\n\n return answer({\n term,\n // Declared order, never completion order — otherwise the list reshuffles\n // with the network and nobody can learn where anything lands.\n groups: outcomes.flatMap(outcome => (outcome.ok ? [outcome.group] : [])),\n unavailable: outcomes.flatMap(outcome =>\n outcome.ok ? [] : [outcome.unavailable],\n ),\n });\n };\n};\n"],"names":["NextResponse","bearerHeader","sessionToken","SEARCH_TERM_PARAM","SEARCH_SOURCES_PARAM","SEARCH_LIMIT_PARAM","MIN_TERM_LENGTH","DEFAULT_GROUP_LIMIT","MAX_GROUP_LIMIT","DEFAULT_SOURCE_TIMEOUT_MS","REASON_FOR_STATUS","unavailable","source","reason","status","ok","key","entity","undefined","runSource","term","limit","token","timeoutMs","upstream","fetch","url","headers","cache","signal","AbortSignal","timeout","error","name","body","json","catch","page","read","group","labelKey","items","total","clampLimit","raw","fallback","asked","Number","isInteger","Math","min","createRecordSearchRoute","sources","defaultLimit","byKey","Map","map","GET","request","params","code","URL","searchParams","get","split","filter","unknown","find","has","detail","selected","length","includes","trim","answer","groups","outcomes","Promise","all","flatMap","outcome"],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import { type Entity, type EntityConstructor } from '@entifix/core';
2
+ import type { RecordSearchOption } from './record-search.types.js';
3
+ /**
4
+ * What a host declares in order to make one entity searchable from the palette
5
+ * (ADR 0040).
6
+ *
7
+ * The search and label members are **declared**, not derived, and that is not an
8
+ * oversight in the metadata: `linkSearchProperty`/`linkLabelProperty` are
9
+ * properties of a *referring* accessor — they say how the owner of a relation
10
+ * looks up its target — and an entity has no member that says "this is what I am
11
+ * called". So the host that mounts the screens says it, next to the route those
12
+ * screens live at.
13
+ */
14
+ export interface RecordSearchSourceConfig<TEntity extends Entity> {
15
+ /** Stable id, unique per host. Defaults to the entity's wire name. */
16
+ readonly key?: string;
17
+ readonly entityConstructor: EntityConstructor<TEntity>;
18
+ /** Where the owning service listens, **without** the `/api` suffix. */
19
+ readonly baseUrl: string;
20
+ /** The `filterable` string member a term is matched against with `like`. */
21
+ readonly searchProperty: string;
22
+ /** The `sortable` member read as a record's label. */
23
+ readonly labelProperty: string;
24
+ /** An optional secondary line — a code, a role. */
25
+ readonly sublabelProperty?: string;
26
+ /** The group heading, as the entity's own `pluralKey`. */
27
+ readonly labelKey: string;
28
+ /** Where selecting a record lands. Locale-free; the caller prefixes it. */
29
+ readonly href: (id: string) => string;
30
+ }
31
+ /** A declared source, reduced to what the fan-out actually calls. */
32
+ export interface RecordSearchSource {
33
+ readonly key: string;
34
+ readonly entity: string;
35
+ readonly labelKey: string;
36
+ /** The upstream URL for one term. */
37
+ readonly url: (term: string, limit: number) => string;
38
+ /** `undefined` when the body is not a readable `entityPage` envelope. */
39
+ readonly read: (body: unknown) => {
40
+ items: RecordSearchOption[];
41
+ total: number;
42
+ } | undefined;
43
+ }
44
+ /**
45
+ * Declare a record search source, validating it against the entity's own
46
+ * metadata **now** rather than on the first keystroke.
47
+ *
48
+ * Every check here guards a failure that is otherwise silent at both ends: the
49
+ * service answers `400`, and a caller renders that as a group it could not
50
+ * reach — which reads as "there are no products". `assertSearchable` took the
51
+ * same posture for the relation picker, for the same reason.
52
+ *
53
+ * ⚠️ This throws at **module load**, so a bad declaration fails the whole app at
54
+ * boot rather than just search. That is deliberate — a source silently missing
55
+ * from a palette is indistinguishable from a permission the caller lacks — but
56
+ * the blast radius is wider than the picker's, which fails only its own render.
57
+ */
58
+ export declare const defineRecordSearchSource: <TEntity extends Entity>({ key, entityConstructor, baseUrl, searchProperty, labelProperty, sublabelProperty, labelKey, href, }: RecordSearchSourceConfig<TEntity>) => RecordSearchSource;
59
+ //# sourceMappingURL=record-search-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record-search-source.d.ts","sourceRoot":"","sources":["../../../src/lib/search/record-search-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,MAAM,EACX,KAAK,iBAAiB,EAMvB,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEnE;;;;;;;;;;GAUG;AACH,MAAM,WAAW,wBAAwB,CAAC,OAAO,SAAS,MAAM;IAC9D,sEAAsE;IACtE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACvD,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,4EAA4E;IAC5E,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,sDAAsD;IACtD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,mDAAmD;IACnD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;CACvC;AAED,qEAAqE;AACrE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,qCAAqC;IACrC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACtD,yEAAyE;IACzE,QAAQ,CAAC,IAAI,EAAE,CACb,IAAI,EAAE,OAAO,KACV;QAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;CACjE;AAoBD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,GAAI,OAAO,SAAS,MAAM,EAAE,uGAS9D,wBAAwB,CAAC,OAAO,CAAC,KAAG,kBAiItC,CAAC"}
@@ -0,0 +1,118 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ import { describeEntityColumns, EntifixLogicError, envelopeEntityName, readEntityPageEnvelope, serializeLoadRequestParams } from '@entifix/core';
3
+ import { Effect, Exit } from 'effect';
4
+ const descriptorOf = (columns, name)=>columns.find((column)=>column.name === name);
5
+ const rejection = (entityName, member, because, fix)=>new EntifixLogicError(`Cannot search ${entityName} by "${member}": ${because} ${fix}`, undefined, {
6
+ entity: entityName,
7
+ member
8
+ });
9
+ /**
10
+ * Declare a record search source, validating it against the entity's own
11
+ * metadata **now** rather than on the first keystroke.
12
+ *
13
+ * Every check here guards a failure that is otherwise silent at both ends: the
14
+ * service answers `400`, and a caller renders that as a group it could not
15
+ * reach — which reads as "there are no products". `assertSearchable` took the
16
+ * same posture for the relation picker, for the same reason.
17
+ *
18
+ * ⚠️ This throws at **module load**, so a bad declaration fails the whole app at
19
+ * boot rather than just search. That is deliberate — a source silently missing
20
+ * from a palette is indistinguishable from a permission the caller lacks — but
21
+ * the blast radius is wider than the picker's, which fails only its own render.
22
+ */ export const defineRecordSearchSource = ({ key, entityConstructor, baseUrl, searchProperty, labelProperty, sublabelProperty, labelKey, href })=>{
23
+ const entity = envelopeEntityName(entityConstructor);
24
+ const entityName = entityConstructor.name;
25
+ const columns = describeEntityColumns(entityConstructor);
26
+ const search = descriptorOf(columns, searchProperty);
27
+ if (search === undefined) {
28
+ throw rejection(entityName, searchProperty, 'the entity declares no such member.', 'Name a member the entity actually has.');
29
+ }
30
+ // `filterable` is simultaneously the server-side RSQL allowlist.
31
+ if (!search.filterable) {
32
+ throw rejection(entityName, searchProperty, 'the member is not filterable, so the service would reject the query.', 'Declare it `filterable` on the entity, or search another member.');
33
+ }
34
+ // A `like` against anything else reaches `coerceValue`, which rejects a
35
+ // partial term — so an enum member reads as declarable and is permanently
36
+ // broken: every keystroke short of a whole value answers `400`.
37
+ if (search.type !== 'string') {
38
+ throw rejection(entityName, searchProperty, `the member is a ${search.type}, and a partial term is not a valid value for it.`, 'Search a string member instead.');
39
+ }
40
+ const label = descriptorOf(columns, labelProperty);
41
+ if (label === undefined) {
42
+ throw rejection(entityName, labelProperty, 'the entity declares no such label member.', 'Name a member the entity actually has.');
43
+ }
44
+ // The group is sorted by the label, so the flag is load-bearing: without it
45
+ // the service rejects the sort and the group degrades whole. Unsorted was the
46
+ // alternative and is worse — the services apply no default order, so results
47
+ // would arrive in storage order and shuffle as records are rewritten.
48
+ if (!label.sortable) {
49
+ throw rejection(entityName, labelProperty, 'the label member is not sortable, so the service would reject the sort.', 'Declare it `sortable` on the entity, or label records by another member.');
50
+ }
51
+ let sublabel;
52
+ if (sublabelProperty !== undefined) {
53
+ sublabel = descriptorOf(columns, sublabelProperty);
54
+ if (sublabel === undefined) {
55
+ throw rejection(entityName, sublabelProperty, 'the entity declares no such sublabel member.', 'Name a member the entity actually has, or drop `sublabelProperty`.');
56
+ }
57
+ }
58
+ const read = (body)=>{
59
+ const page = Effect.runSyncExit(readEntityPageEnvelope(entityConstructor, body));
60
+ if (Exit.isFailure(page)) return undefined;
61
+ const items = page.value.items.filter((record)=>record.id != null).map((record)=>{
62
+ const row = record;
63
+ const raw = row[labelProperty];
64
+ const id = String(record.id);
65
+ return _extends({
66
+ id,
67
+ // A blank label is as useless as a missing one, so both fall back to
68
+ // the id — an unnamed record must stay distinguishable from the one
69
+ // above it. Same rule the relation picker's `labelOf` applies.
70
+ label: raw == null || raw === '' ? id : String(raw)
71
+ }, sublabel !== undefined && row[sublabel.name] != null ? {
72
+ sublabel: String(row[sublabel.name])
73
+ } : {}, {
74
+ entity,
75
+ href: href(id)
76
+ });
77
+ });
78
+ return {
79
+ items,
80
+ total: page.value.total
81
+ };
82
+ };
83
+ return {
84
+ key: key != null ? key : entity,
85
+ entity,
86
+ labelKey,
87
+ /**
88
+ * Built through `serializeLoadRequestParams`, never by hand. That is
89
+ * correctness rather than tidiness: `encodeRsqlValue` quotes a term carrying
90
+ * any of `;,()'"=!<>\` or whitespace, so `Acme, Inc.` stays one value
91
+ * instead of splitting the expression into two comparisons.
92
+ */ url: (term, limit)=>{
93
+ const request = {
94
+ filtering: [
95
+ {
96
+ property: search.key,
97
+ operator: 'like',
98
+ value: term
99
+ }
100
+ ],
101
+ sorting: [
102
+ {
103
+ 0: {
104
+ property: label.key,
105
+ type: 'asc'
106
+ }
107
+ }
108
+ ],
109
+ page: 1,
110
+ pageSize: limit
111
+ };
112
+ return `${baseUrl}/api/${entity}?${serializeLoadRequestParams(request).toString()}`;
113
+ },
114
+ read
115
+ };
116
+ };
117
+
118
+ //# sourceMappingURL=record-search-source.js.map