@jini-ai/admin 0.1.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 (235) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +138 -0
  3. package/dist/browser/index.d.ts +8 -0
  4. package/dist/browser/index.d.ts.map +1 -0
  5. package/dist/browser/index.js +8 -0
  6. package/dist/browser/index.js.map +1 -0
  7. package/dist/browser/navigation.d.ts +39 -0
  8. package/dist/browser/navigation.d.ts.map +1 -0
  9. package/dist/browser/navigation.js +113 -0
  10. package/dist/browser/navigation.js.map +1 -0
  11. package/dist/core/gated/types.d.ts +87 -0
  12. package/dist/core/gated/types.d.ts.map +1 -0
  13. package/dist/core/gated/types.js +37 -0
  14. package/dist/core/gated/types.js.map +1 -0
  15. package/dist/core/index.d.ts +20 -0
  16. package/dist/core/index.d.ts.map +1 -0
  17. package/dist/core/index.js +15 -0
  18. package/dist/core/index.js.map +1 -0
  19. package/dist/core/manifest/rules.d.ts +84 -0
  20. package/dist/core/manifest/rules.d.ts.map +1 -0
  21. package/dist/core/manifest/rules.js +130 -0
  22. package/dist/core/manifest/rules.js.map +1 -0
  23. package/dist/core/manifest/types.d.ts +154 -0
  24. package/dist/core/manifest/types.d.ts.map +1 -0
  25. package/dist/core/manifest/types.js +31 -0
  26. package/dist/core/manifest/types.js.map +1 -0
  27. package/dist/core/permissions/rules.d.ts +32 -0
  28. package/dist/core/permissions/rules.d.ts.map +1 -0
  29. package/dist/core/permissions/rules.js +34 -0
  30. package/dist/core/permissions/rules.js.map +1 -0
  31. package/dist/core/ports/analytics.d.ts +35 -0
  32. package/dist/core/ports/analytics.d.ts.map +1 -0
  33. package/dist/core/ports/analytics.js +14 -0
  34. package/dist/core/ports/analytics.js.map +1 -0
  35. package/dist/core/ports/auth.d.ts +49 -0
  36. package/dist/core/ports/auth.d.ts.map +1 -0
  37. package/dist/core/ports/auth.js +19 -0
  38. package/dist/core/ports/auth.js.map +1 -0
  39. package/dist/core/ports/comments.d.ts +90 -0
  40. package/dist/core/ports/comments.d.ts.map +1 -0
  41. package/dist/core/ports/comments.js +24 -0
  42. package/dist/core/ports/comments.js.map +1 -0
  43. package/dist/core/ports/database.d.ts +101 -0
  44. package/dist/core/ports/database.d.ts.map +1 -0
  45. package/dist/core/ports/database.js +2 -0
  46. package/dist/core/ports/database.js.map +1 -0
  47. package/dist/core/ports/forms.d.ts +130 -0
  48. package/dist/core/ports/forms.d.ts.map +1 -0
  49. package/dist/core/ports/forms.js +62 -0
  50. package/dist/core/ports/forms.js.map +1 -0
  51. package/dist/core/ports/identity.d.ts +114 -0
  52. package/dist/core/ports/identity.d.ts.map +1 -0
  53. package/dist/core/ports/identity.js +28 -0
  54. package/dist/core/ports/identity.js.map +1 -0
  55. package/dist/core/ports/index.d.ts +21 -0
  56. package/dist/core/ports/index.d.ts.map +1 -0
  57. package/dist/core/ports/index.js +6 -0
  58. package/dist/core/ports/index.js.map +1 -0
  59. package/dist/core/ports/integrations.d.ts +81 -0
  60. package/dist/core/ports/integrations.d.ts.map +1 -0
  61. package/dist/core/ports/integrations.js +21 -0
  62. package/dist/core/ports/integrations.js.map +1 -0
  63. package/dist/core/ports/media.d.ts +74 -0
  64. package/dist/core/ports/media.d.ts.map +1 -0
  65. package/dist/core/ports/media.js +28 -0
  66. package/dist/core/ports/media.js.map +1 -0
  67. package/dist/core/ports/members.d.ts +50 -0
  68. package/dist/core/ports/members.d.ts.map +1 -0
  69. package/dist/core/ports/members.js +17 -0
  70. package/dist/core/ports/members.js.map +1 -0
  71. package/dist/core/ports/menus.d.ts +179 -0
  72. package/dist/core/ports/menus.d.ts.map +1 -0
  73. package/dist/core/ports/menus.js +70 -0
  74. package/dist/core/ports/menus.js.map +1 -0
  75. package/dist/core/ports/plugins.d.ts +68 -0
  76. package/dist/core/ports/plugins.d.ts.map +1 -0
  77. package/dist/core/ports/plugins.js +27 -0
  78. package/dist/core/ports/plugins.js.map +1 -0
  79. package/dist/core/ports/recovery.d.ts +171 -0
  80. package/dist/core/ports/recovery.d.ts.map +1 -0
  81. package/dist/core/ports/recovery.js +2 -0
  82. package/dist/core/ports/recovery.js.map +1 -0
  83. package/dist/core/ports/redirects.d.ts +139 -0
  84. package/dist/core/ports/redirects.d.ts.map +1 -0
  85. package/dist/core/ports/redirects.js +55 -0
  86. package/dist/core/ports/redirects.js.map +1 -0
  87. package/dist/core/ports/seo.d.ts +147 -0
  88. package/dist/core/ports/seo.d.ts.map +1 -0
  89. package/dist/core/ports/seo.js +39 -0
  90. package/dist/core/ports/seo.js.map +1 -0
  91. package/dist/core/ports/settings.d.ts +87 -0
  92. package/dist/core/ports/settings.d.ts.map +1 -0
  93. package/dist/core/ports/settings.js +23 -0
  94. package/dist/core/ports/settings.js.map +1 -0
  95. package/dist/core/ports/workspace.d.ts +44 -0
  96. package/dist/core/ports/workspace.d.ts.map +1 -0
  97. package/dist/core/ports/workspace.js +29 -0
  98. package/dist/core/ports/workspace.js.map +1 -0
  99. package/dist/core/routing/rules.d.ts +54 -0
  100. package/dist/core/routing/rules.d.ts.map +1 -0
  101. package/dist/core/routing/rules.js +113 -0
  102. package/dist/core/routing/rules.js.map +1 -0
  103. package/dist/core/routing/types.d.ts +36 -0
  104. package/dist/core/routing/types.d.ts.map +1 -0
  105. package/dist/core/routing/types.js +17 -0
  106. package/dist/core/routing/types.js.map +1 -0
  107. package/dist/core/transport/errors.d.ts +53 -0
  108. package/dist/core/transport/errors.d.ts.map +1 -0
  109. package/dist/core/transport/errors.js +63 -0
  110. package/dist/core/transport/errors.js.map +1 -0
  111. package/dist/core/transport/http.d.ts +63 -0
  112. package/dist/core/transport/http.d.ts.map +1 -0
  113. package/dist/core/transport/http.js +63 -0
  114. package/dist/core/transport/http.js.map +1 -0
  115. package/dist/core/transport/index.d.ts +5 -0
  116. package/dist/core/transport/index.d.ts.map +1 -0
  117. package/dist/core/transport/index.js +3 -0
  118. package/dist/core/transport/index.js.map +1 -0
  119. package/dist/core/transport/types.d.ts +81 -0
  120. package/dist/core/transport/types.d.ts.map +1 -0
  121. package/dist/core/transport/types.js +43 -0
  122. package/dist/core/transport/types.js.map +1 -0
  123. package/dist/react/components/ConfirmButton/ConfirmButton.d.ts +40 -0
  124. package/dist/react/components/ConfirmButton/ConfirmButton.d.ts.map +1 -0
  125. package/dist/react/components/ConfirmButton/ConfirmButton.hooks.d.ts +22 -0
  126. package/dist/react/components/ConfirmButton/ConfirmButton.hooks.d.ts.map +1 -0
  127. package/dist/react/components/ConfirmButton/ConfirmButton.hooks.js +59 -0
  128. package/dist/react/components/ConfirmButton/ConfirmButton.hooks.js.map +1 -0
  129. package/dist/react/components/ConfirmButton/ConfirmButton.js +40 -0
  130. package/dist/react/components/ConfirmButton/ConfirmButton.js.map +1 -0
  131. package/dist/react/components/ConfirmButton.d.ts +39 -0
  132. package/dist/react/components/ConfirmButton.d.ts.map +1 -0
  133. package/dist/react/components/ConfirmButton.js +79 -0
  134. package/dist/react/components/ConfirmButton.js.map +1 -0
  135. package/dist/react/components/ConfirmDialog/ConfirmDialog.d.ts +90 -0
  136. package/dist/react/components/ConfirmDialog/ConfirmDialog.d.ts.map +1 -0
  137. package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.d.ts +49 -0
  138. package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.d.ts.map +1 -0
  139. package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.js +81 -0
  140. package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.js.map +1 -0
  141. package/dist/react/components/ConfirmDialog/ConfirmDialog.js +92 -0
  142. package/dist/react/components/ConfirmDialog/ConfirmDialog.js.map +1 -0
  143. package/dist/react/components/ConfirmDialog.d.ts +57 -0
  144. package/dist/react/components/ConfirmDialog.d.ts.map +1 -0
  145. package/dist/react/components/ConfirmDialog.js +85 -0
  146. package/dist/react/components/ConfirmDialog.js.map +1 -0
  147. package/dist/react/components/DataTable.d.ts +97 -0
  148. package/dist/react/components/DataTable.d.ts.map +1 -0
  149. package/dist/react/components/DataTable.js +20 -0
  150. package/dist/react/components/DataTable.js.map +1 -0
  151. package/dist/react/components/InteractiveHtmlEditor/InteractiveHtmlEditor.d.ts +25 -0
  152. package/dist/react/components/InteractiveHtmlEditor/InteractiveHtmlEditor.d.ts.map +1 -0
  153. package/dist/react/components/InteractiveHtmlEditor/InteractiveHtmlEditor.js +148 -0
  154. package/dist/react/components/InteractiveHtmlEditor/InteractiveHtmlEditor.js.map +1 -0
  155. package/dist/react/components/RowMenu/RowMenu.d.ts +77 -0
  156. package/dist/react/components/RowMenu/RowMenu.d.ts.map +1 -0
  157. package/dist/react/components/RowMenu/RowMenu.hooks.d.ts +30 -0
  158. package/dist/react/components/RowMenu/RowMenu.hooks.d.ts.map +1 -0
  159. package/dist/react/components/RowMenu/RowMenu.hooks.js +154 -0
  160. package/dist/react/components/RowMenu/RowMenu.hooks.js.map +1 -0
  161. package/dist/react/components/RowMenu/RowMenu.js +46 -0
  162. package/dist/react/components/RowMenu/RowMenu.js.map +1 -0
  163. package/dist/react/components/RowMenu.d.ts +44 -0
  164. package/dist/react/components/RowMenu.d.ts.map +1 -0
  165. package/dist/react/components/RowMenu.js +149 -0
  166. package/dist/react/components/RowMenu.js.map +1 -0
  167. package/dist/react/components/Sidebar.d.ts +128 -0
  168. package/dist/react/components/Sidebar.d.ts.map +1 -0
  169. package/dist/react/components/Sidebar.js +168 -0
  170. package/dist/react/components/Sidebar.js.map +1 -0
  171. package/dist/react/hooks/use-nav-sections.d.ts +50 -0
  172. package/dist/react/hooks/use-nav-sections.d.ts.map +1 -0
  173. package/dist/react/hooks/use-nav-sections.js +112 -0
  174. package/dist/react/hooks/use-nav-sections.js.map +1 -0
  175. package/dist/react/hooks/use-sidebar-rail.d.ts +39 -0
  176. package/dist/react/hooks/use-sidebar-rail.d.ts.map +1 -0
  177. package/dist/react/hooks/use-sidebar-rail.js +93 -0
  178. package/dist/react/hooks/use-sidebar-rail.js.map +1 -0
  179. package/dist/react/index.d.ts +53 -0
  180. package/dist/react/index.d.ts.map +1 -0
  181. package/dist/react/index.js +53 -0
  182. package/dist/react/index.js.map +1 -0
  183. package/dist/react/types.d.ts +28 -0
  184. package/dist/react/types.d.ts.map +1 -0
  185. package/dist/react/types.js +23 -0
  186. package/dist/react/types.js.map +1 -0
  187. package/dist/server/composio/bounded-data.d.ts +36 -0
  188. package/dist/server/composio/bounded-data.d.ts.map +1 -0
  189. package/dist/server/composio/bounded-data.js +181 -0
  190. package/dist/server/composio/bounded-data.js.map +1 -0
  191. package/dist/server/composio/catalog.d.ts +147 -0
  192. package/dist/server/composio/catalog.d.ts.map +1 -0
  193. package/dist/server/composio/catalog.js +126 -0
  194. package/dist/server/composio/catalog.js.map +1 -0
  195. package/dist/server/composio/composio-config.d.ts +42 -0
  196. package/dist/server/composio/composio-config.d.ts.map +1 -0
  197. package/dist/server/composio/composio-config.js +145 -0
  198. package/dist/server/composio/composio-config.js.map +1 -0
  199. package/dist/server/composio/composio-descriptions.d.ts +16 -0
  200. package/dist/server/composio/composio-descriptions.d.ts.map +1 -0
  201. package/dist/server/composio/composio-descriptions.js +766 -0
  202. package/dist/server/composio/composio-descriptions.js.map +1 -0
  203. package/dist/server/composio/composio.d.ts +364 -0
  204. package/dist/server/composio/composio.d.ts.map +1 -0
  205. package/dist/server/composio/composio.js +1902 -0
  206. package/dist/server/composio/composio.js.map +1 -0
  207. package/dist/server/composio/errors.d.ts +15 -0
  208. package/dist/server/composio/errors.d.ts.map +1 -0
  209. package/dist/server/composio/errors.js +16 -0
  210. package/dist/server/composio/errors.js.map +1 -0
  211. package/dist/server/composio/file-lock.d.ts +13 -0
  212. package/dist/server/composio/file-lock.d.ts.map +1 -0
  213. package/dist/server/composio/file-lock.js +92 -0
  214. package/dist/server/composio/file-lock.js.map +1 -0
  215. package/dist/server/composio/index.d.ts +18 -0
  216. package/dist/server/composio/index.d.ts.map +1 -0
  217. package/dist/server/composio/index.js +7 -0
  218. package/dist/server/composio/index.js.map +1 -0
  219. package/dist/server/composio/json-schema.d.ts +93 -0
  220. package/dist/server/composio/json-schema.d.ts.map +1 -0
  221. package/dist/server/composio/json-schema.js +498 -0
  222. package/dist/server/composio/json-schema.js.map +1 -0
  223. package/dist/server/composio/output-protection.d.ts +32 -0
  224. package/dist/server/composio/output-protection.d.ts.map +1 -0
  225. package/dist/server/composio/output-protection.js +105 -0
  226. package/dist/server/composio/output-protection.js.map +1 -0
  227. package/dist/server/composio/service.d.ts +181 -0
  228. package/dist/server/composio/service.d.ts.map +1 -0
  229. package/dist/server/composio/service.js +758 -0
  230. package/dist/server/composio/service.js.map +1 -0
  231. package/dist/server/index.d.ts +15 -0
  232. package/dist/server/index.d.ts.map +1 -0
  233. package/dist/server/index.js +15 -0
  234. package/dist/server/index.js.map +1 -0
  235. package/package.json +92 -0
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @file Pure route matching, driven by the panel registry rather than a hard-coded branch list.
3
+ *
4
+ * ## What changed from the ported original
5
+ *
6
+ * The reference implementation's `parseRoute` is a ~30-line if-chain with one branch per
7
+ * detail route — `/posts/:id`, `/menus/new`, `/widgets/regions/:key`,
8
+ * `/collections/:key/:entryId`, and so on. It works, and its fall-through-to-dashboard behaviour
9
+ * is deliberate. But it means a panel cannot own a URL without an edit to the router, which is
10
+ * fatal for a package whose whole point is that hosts and AI-generated code add panels.
11
+ *
12
+ * Here the panel declares its patterns and the matcher is generic. Behaviour that is preserved
13
+ * exactly, because each was a considered decision in the original:
14
+ *
15
+ * - **Unrecognized paths fall through** (`panelId: null`) rather than rendering a placeholder,
16
+ * so a typo behaves like a bad URL rather than an empty screen.
17
+ * - **A bare single segment only matches a registered panel.** Testing against the registry
18
+ * rather than accepting any single segment is what keeps `/typo` from rendering an empty shell.
19
+ * - **Trailing slashes are insignificant** — segments are split and empties dropped, so
20
+ * `/settings/` and `/settings` are the same screen.
21
+ * - **Query is parsed but never part of matching.** The reference implementation's widget-editor
22
+ * route reads `?type=` for its initial state; that is panel state, not routing.
23
+ */
24
+ import type { AdminPanel } from '../manifest/types.js';
25
+ import type { AdminRoute } from './types.js';
26
+ /** Default mount point. Overridable per call — a product that mounts its admin elsewhere passes
27
+ * its own base rather than patching this package. */
28
+ export declare const DEFAULT_ADMIN_BASE = "/admin";
29
+ /** `"/admin/settings/"` -> `"/admin/settings"`; leaves a lone `"/"` alone. */
30
+ export declare function stripTrailingSlash(pathname: string): string;
31
+ /** Route path -> URL. `/settings` -> `/admin/settings`; `/` -> `/admin/`. */
32
+ export declare function adminHref(routePath: string, base?: string): string;
33
+ /**
34
+ * URL pathname -> route path, base stripped. Always starts with `/`.
35
+ *
36
+ * A bare `/admin` (no trailing slash) and `/admin/` both mean the dashboard, so the base is
37
+ * removed by prefix rather than by assuming a trailing separator.
38
+ *
39
+ * Unlike the reference implementation's version this takes `pathname` as a required argument instead of defaulting to
40
+ * `window.location.pathname` — that default is what made the original untestable without a DOM,
41
+ * and it belongs in the browser layer, not here.
42
+ */
43
+ export declare function currentRoutePath(pathname: string, base?: string): string;
44
+ /**
45
+ * Matches a route path against the registered panels.
46
+ *
47
+ * Callers should pass the **resolved** panel set (see `resolvePanels`), not the raw one — routing
48
+ * to a panel whose capability is not wired would render a section the host deliberately withheld.
49
+ *
50
+ * @complexity O(p * r * s) — p panels, r patterns per panel, s segments per pattern. Bounded by
51
+ * the manifest, and the common case exits on the first segment comparison.
52
+ */
53
+ export declare function matchRoute<T>(routePath: string, panels: readonly AdminPanel<T>[]): AdminRoute;
54
+ //# sourceMappingURL=rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../../src/core/routing/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;sDACsD;AACtD,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAE3C,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,6EAA6E;AAC7E,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,GAAE,MAA2B,GAAG,MAAM,CAGtF;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,MAA2B,GAAG,MAAM,CAM5F;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,UAAU,CA0B7F"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * @file Pure route matching, driven by the panel registry rather than a hard-coded branch list.
3
+ *
4
+ * ## What changed from the ported original
5
+ *
6
+ * The reference implementation's `parseRoute` is a ~30-line if-chain with one branch per
7
+ * detail route — `/posts/:id`, `/menus/new`, `/widgets/regions/:key`,
8
+ * `/collections/:key/:entryId`, and so on. It works, and its fall-through-to-dashboard behaviour
9
+ * is deliberate. But it means a panel cannot own a URL without an edit to the router, which is
10
+ * fatal for a package whose whole point is that hosts and AI-generated code add panels.
11
+ *
12
+ * Here the panel declares its patterns and the matcher is generic. Behaviour that is preserved
13
+ * exactly, because each was a considered decision in the original:
14
+ *
15
+ * - **Unrecognized paths fall through** (`panelId: null`) rather than rendering a placeholder,
16
+ * so a typo behaves like a bad URL rather than an empty screen.
17
+ * - **A bare single segment only matches a registered panel.** Testing against the registry
18
+ * rather than accepting any single segment is what keeps `/typo` from rendering an empty shell.
19
+ * - **Trailing slashes are insignificant** — segments are split and empties dropped, so
20
+ * `/settings/` and `/settings` are the same screen.
21
+ * - **Query is parsed but never part of matching.** The reference implementation's widget-editor
22
+ * route reads `?type=` for its initial state; that is panel state, not routing.
23
+ */
24
+ /** Default mount point. Overridable per call — a product that mounts its admin elsewhere passes
25
+ * its own base rather than patching this package. */
26
+ export const DEFAULT_ADMIN_BASE = '/admin';
27
+ /** `"/admin/settings/"` -> `"/admin/settings"`; leaves a lone `"/"` alone. */
28
+ export function stripTrailingSlash(pathname) {
29
+ return pathname.length > 1 && pathname.endsWith('/') ? pathname.slice(0, -1) : pathname;
30
+ }
31
+ /** Route path -> URL. `/settings` -> `/admin/settings`; `/` -> `/admin/`. */
32
+ export function adminHref(routePath, base = DEFAULT_ADMIN_BASE) {
33
+ const normalized = routePath.startsWith('/') ? routePath : `/${routePath}`;
34
+ return `${base}${normalized}`;
35
+ }
36
+ /**
37
+ * URL pathname -> route path, base stripped. Always starts with `/`.
38
+ *
39
+ * A bare `/admin` (no trailing slash) and `/admin/` both mean the dashboard, so the base is
40
+ * removed by prefix rather than by assuming a trailing separator.
41
+ *
42
+ * Unlike the reference implementation's version this takes `pathname` as a required argument instead of defaulting to
43
+ * `window.location.pathname` — that default is what made the original untestable without a DOM,
44
+ * and it belongs in the browser layer, not here.
45
+ */
46
+ export function currentRoutePath(pathname, base = DEFAULT_ADMIN_BASE) {
47
+ if (pathname === base)
48
+ return '/';
49
+ if (pathname.startsWith(`${base}/`))
50
+ return pathname.slice(base.length) || '/';
51
+ // Reached only if the app is mounted somewhere unexpected; treat the whole path as the route
52
+ // rather than silently returning the dashboard, so the mismatch is visible in the URL.
53
+ return pathname || '/';
54
+ }
55
+ /**
56
+ * Matches a route path against the registered panels.
57
+ *
58
+ * Callers should pass the **resolved** panel set (see `resolvePanels`), not the raw one — routing
59
+ * to a panel whose capability is not wired would render a section the host deliberately withheld.
60
+ *
61
+ * @complexity O(p * r * s) — p panels, r patterns per panel, s segments per pattern. Bounded by
62
+ * the manifest, and the common case exits on the first segment comparison.
63
+ */
64
+ export function matchRoute(routePath, panels) {
65
+ const [rawPath, rawQuery] = routePath.split('?');
66
+ const segments = (rawPath ?? '').split('/').filter(Boolean);
67
+ const query = new URLSearchParams(rawQuery ?? '');
68
+ const miss = (panelId) => ({ panelId, view: null, params: {}, query });
69
+ // Root is the dashboard by convention, and `dashboard` need not be registered for `/` to
70
+ // resolve — the shell's fallback handles an empty registry.
71
+ if (segments.length === 0)
72
+ return miss('dashboard');
73
+ const panel = panels.find((p) => p.id === segments[0]);
74
+ if (!panel)
75
+ return miss(null);
76
+ const rest = segments.slice(1);
77
+ // Bare panel segment: the index view.
78
+ if (rest.length === 0)
79
+ return miss(panel.id);
80
+ for (const route of panel.routes ?? []) {
81
+ const params = matchPattern(route.pattern, rest);
82
+ if (params)
83
+ return { panelId: panel.id, view: route.view, params, query };
84
+ }
85
+ // A registered panel with unmatched trailing segments. Falls through rather than rendering the
86
+ // panel's index view, so `/posts/abc/typo` is a bad URL rather than silently showing the list.
87
+ return miss(null);
88
+ }
89
+ /**
90
+ * Matches one pattern against already-split segments.
91
+ *
92
+ * @returns captured params, or `null` when the pattern does not match. An empty object is a
93
+ * successful match with no captures — which is why this returns `null` rather than `{}` for a
94
+ * miss, and why callers must check for `null` explicitly rather than truthiness of the size.
95
+ */
96
+ function matchPattern(pattern, segments) {
97
+ const patternSegments = pattern.split('/').filter(Boolean);
98
+ if (patternSegments.length !== segments.length)
99
+ return null;
100
+ const params = {};
101
+ for (let i = 0; i < patternSegments.length; i += 1) {
102
+ const patternSegment = patternSegments[i] ?? '';
103
+ const segment = segments[i] ?? '';
104
+ if (patternSegment.startsWith(':')) {
105
+ params[patternSegment.slice(1)] = segment;
106
+ continue;
107
+ }
108
+ if (patternSegment !== segment)
109
+ return null;
110
+ }
111
+ return params;
112
+ }
113
+ //# sourceMappingURL=rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.js","sourceRoot":"","sources":["../../../src/core/routing/rules.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @file Route model types.
3
+ *
4
+ * ## Route paths vs URLs — the distinction this module exists to keep straight
5
+ *
6
+ * Carried over from the reference implementation's `lib/router.ts`, where collapsing these two is documented as the exact
7
+ * mistake that produced `/admin/#/section/settings` URLs:
8
+ *
9
+ * - a **route path** is base-agnostic and is what the matcher consumes: `/`, `/settings`,
10
+ * `/posts/abc`
11
+ * - a **URL** is what goes in an `href` or the address bar: `/admin/`, `/admin/settings`
12
+ *
13
+ * `adminHref` converts the first into the second; `currentRoutePath` recovers the first from a
14
+ * pathname. Nothing else should be doing string surgery on a location.
15
+ */
16
+ /** A resolved route. */
17
+ export interface AdminRoute {
18
+ /**
19
+ * The matched panel's id, or `null` when nothing matched.
20
+ *
21
+ * `null` is not an error state the shell must handle specially — the documented behaviour, kept
22
+ * from the reference implementation, is that an unrecognized path falls through to the dashboard rather than rendering
23
+ * an empty placeholder for a typo.
24
+ */
25
+ readonly panelId: string | null;
26
+ /**
27
+ * Which of the panel's views to render. `null` means the panel's own index view (`/users`), as
28
+ * opposed to one of its detail routes (`/users/abc`).
29
+ */
30
+ readonly view: string | null;
31
+ /** Captured `:name` segments from the matched pattern. Empty for an index route. */
32
+ readonly params: Readonly<Record<string, string>>;
33
+ /** Parsed query string. Always present, possibly empty. */
34
+ readonly query: URLSearchParams;
35
+ }
36
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/routing/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,wBAAwB;AACxB,MAAM,WAAW,UAAU;IACzB;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhC;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAE7B,oFAAoF;IACpF,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAElD,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;CACjC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @file Route model types.
3
+ *
4
+ * ## Route paths vs URLs — the distinction this module exists to keep straight
5
+ *
6
+ * Carried over from the reference implementation's `lib/router.ts`, where collapsing these two is documented as the exact
7
+ * mistake that produced `/admin/#/section/settings` URLs:
8
+ *
9
+ * - a **route path** is base-agnostic and is what the matcher consumes: `/`, `/settings`,
10
+ * `/posts/abc`
11
+ * - a **URL** is what goes in an `href` or the address bar: `/admin/`, `/admin/settings`
12
+ *
13
+ * `adminHref` converts the first into the second; `currentRoutePath` recovers the first from a
14
+ * pathname. Nothing else should be doing string surgery on a location.
15
+ */
16
+ export {};
17
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/routing/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @file The error contract every admin route group throws, host-supplied or Jini-shipped.
3
+ *
4
+ * Ported from the reference implementation's `lib/api.ts` (`ApiError`, `describeApiError`), which is the
5
+ * only implementation of this shape that has been through real operator use. Two decisions from
6
+ * that file are carried across deliberately, because both were arrived at by an audit and both
7
+ * look like oversights if you only read the code:
8
+ *
9
+ * 1. **`body` is kept raw.** A caller sometimes needs a route-specific field beyond
10
+ * `code`/`message` (a 409's `currentVersion`, a validation route's field list). Exposing the
11
+ * parsed body means that never requires an `AdminApiError` subclass per route.
12
+ *
13
+ * 2. **There is deliberately NO shared `code` -> message table.** The same `code` means
14
+ * genuinely different things in different domains — in the reference implementation,
15
+ * `RESOURCE_CONFLICT` is "still
16
+ * referenced elsewhere" on Roles, "that slug is already taken" on Workspace, and "that
17
+ * username is already in use" on Users. Three correct operator-facing sentences for one code.
18
+ * A shared table could only pick one, which is a regression dressed as consolidation. So
19
+ * `describeApiError` is only the *base case*: it decides what to show when the server sent no
20
+ * message, or when the thrown value is not an `AdminApiError` at all (network failure, a
21
+ * thrown non-Error). Panels layer their own per-code copy on top and fall through to this.
22
+ */
23
+ /** A failed admin API call that reached the server and came back non-2xx. */
24
+ export declare class AdminApiError extends Error {
25
+ readonly status: number;
26
+ /**
27
+ * Canonical error `code` from the response body when the server sent one. Not enumerated here
28
+ * on purpose — the vocabulary belongs to the host's API, and a union type in this package would
29
+ * have to be widened by a Jini release every time a product added a code.
30
+ */
31
+ readonly code?: string | undefined;
32
+ /** Raw parsed JSON error body, when present. See decision 1 in the file header. */
33
+ readonly body?: Record<string, unknown> | undefined;
34
+ constructor(message: string, status: number, code?: string, body?: Record<string, unknown>);
35
+ }
36
+ /**
37
+ * Base-case translation from a thrown request error to operator-facing copy.
38
+ *
39
+ * This is the "opt-out by default" fallback, not a complete message catalog — see decision 2 in
40
+ * the file header. A panel with real per-code handling checks its own codes first and falls
41
+ * through here for the rest:
42
+ *
43
+ * ```ts
44
+ * function describe(e: unknown, fallback: string): string {
45
+ * if (e instanceof AdminApiError && e.code === 'PANEL_SPECIFIC_CODE') return '...';
46
+ * return describeApiError(e, fallback);
47
+ * }
48
+ * ```
49
+ *
50
+ * @complexity O(1) — two `instanceof` checks, no iteration.
51
+ */
52
+ export declare function describeApiError(e: unknown, fallback: string): string;
53
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/core/transport/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,6EAA6E;AAC7E,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAEnC,mFAAmF;IACnF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;gBAExC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAO3F;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGrE"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @file The error contract every admin route group throws, host-supplied or Jini-shipped.
3
+ *
4
+ * Ported from the reference implementation's `lib/api.ts` (`ApiError`, `describeApiError`), which is the
5
+ * only implementation of this shape that has been through real operator use. Two decisions from
6
+ * that file are carried across deliberately, because both were arrived at by an audit and both
7
+ * look like oversights if you only read the code:
8
+ *
9
+ * 1. **`body` is kept raw.** A caller sometimes needs a route-specific field beyond
10
+ * `code`/`message` (a 409's `currentVersion`, a validation route's field list). Exposing the
11
+ * parsed body means that never requires an `AdminApiError` subclass per route.
12
+ *
13
+ * 2. **There is deliberately NO shared `code` -> message table.** The same `code` means
14
+ * genuinely different things in different domains — in the reference implementation,
15
+ * `RESOURCE_CONFLICT` is "still
16
+ * referenced elsewhere" on Roles, "that slug is already taken" on Workspace, and "that
17
+ * username is already in use" on Users. Three correct operator-facing sentences for one code.
18
+ * A shared table could only pick one, which is a regression dressed as consolidation. So
19
+ * `describeApiError` is only the *base case*: it decides what to show when the server sent no
20
+ * message, or when the thrown value is not an `AdminApiError` at all (network failure, a
21
+ * thrown non-Error). Panels layer their own per-code copy on top and fall through to this.
22
+ */
23
+ /** A failed admin API call that reached the server and came back non-2xx. */
24
+ export class AdminApiError extends Error {
25
+ status;
26
+ /**
27
+ * Canonical error `code` from the response body when the server sent one. Not enumerated here
28
+ * on purpose — the vocabulary belongs to the host's API, and a union type in this package would
29
+ * have to be widened by a Jini release every time a product added a code.
30
+ */
31
+ code;
32
+ /** Raw parsed JSON error body, when present. See decision 1 in the file header. */
33
+ body;
34
+ constructor(message, status, code, body) {
35
+ super(message);
36
+ this.name = 'AdminApiError';
37
+ this.status = status;
38
+ this.code = code;
39
+ this.body = body;
40
+ }
41
+ }
42
+ /**
43
+ * Base-case translation from a thrown request error to operator-facing copy.
44
+ *
45
+ * This is the "opt-out by default" fallback, not a complete message catalog — see decision 2 in
46
+ * the file header. A panel with real per-code handling checks its own codes first and falls
47
+ * through here for the rest:
48
+ *
49
+ * ```ts
50
+ * function describe(e: unknown, fallback: string): string {
51
+ * if (e instanceof AdminApiError && e.code === 'PANEL_SPECIFIC_CODE') return '...';
52
+ * return describeApiError(e, fallback);
53
+ * }
54
+ * ```
55
+ *
56
+ * @complexity O(1) — two `instanceof` checks, no iteration.
57
+ */
58
+ export function describeApiError(e, fallback) {
59
+ if (e instanceof AdminApiError)
60
+ return e.message || fallback;
61
+ return e instanceof Error ? e.message : fallback;
62
+ }
63
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/core/transport/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,6EAA6E;AAC7E,MAAM,OAAO,aAAc,SAAQ,KAAK;IAC7B,MAAM,CAAS;IAExB;;;;OAIG;IACM,IAAI,CAAsB;IAEnC,mFAAmF;IAC1E,IAAI,CAAuC;IAEpD,YAAY,OAAe,EAAE,MAAc,EAAE,IAAa,EAAE,IAA8B;QACxF,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,gBAAgB,CAAC,CAAU,EAAE,QAAgB;IAC3D,IAAI,CAAC,YAAY,aAAa;QAAE,OAAO,CAAC,CAAC,OAAO,IAAI,QAAQ,CAAC;IAC7D,OAAO,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;AACnD,CAAC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @file The default HTTP `AdminTransport`, and the client assembler.
3
+ *
4
+ * Ported from the reference implementation's `lib/api.ts` `request()`, with the two
5
+ * hard-coded values it closed over — the base URL and the same-origin cookie policy — lifted into
6
+ * options. Nothing else about the request/response handling changed; the error-body parsing in
7
+ * particular is deliberately identical, including the `.catch(() => ({}))` on a body that is not
8
+ * JSON.
9
+ */
10
+ import type { AdminClient, AdminRouteGroupFactory, AdminTransport } from './types.js';
11
+ /**
12
+ * The subset of `fetch` this transport actually uses.
13
+ *
14
+ * Deliberately narrower than `typeof globalThis.fetch`: the transport only ever passes a string
15
+ * URL, never a `URL` or a `Request`. Declaring that narrows what a test double has to implement
16
+ * (the global signature forces a stub to handle three input shapes it will never receive), and
17
+ * the real `globalThis.fetch` is still assignable here because a function accepting a wider input
18
+ * satisfies one accepting a narrower one.
19
+ */
20
+ export type AdminFetch = (input: string, init?: RequestInit) => Promise<Response>;
21
+ export interface HttpTransportOptions {
22
+ /**
23
+ * Prefix for every path (`/api/admin/v1`). No trailing slash — paths always start with `/`.
24
+ *
25
+ * Unlike the reference implementation's module-level `const BASE`, this is per-transport so one
26
+ * process can talk to more than one admin API. A fleet orchestrator managing many site instances
27
+ * in one process is exactly the case where a single shared base is the thing that would have to
28
+ * be unpicked first.
29
+ */
30
+ readonly baseUrl: string;
31
+ /**
32
+ * Injected `fetch`, defaulting to the global. Present so a test can supply a stub without
33
+ * `vi.stubGlobal`, which is what forced the reference implementation's own hook tests to assert
34
+ * URLs and HTTP methods when what they meant to describe was behaviour.
35
+ */
36
+ readonly fetch?: AdminFetch;
37
+ /**
38
+ * Merged into every request. `Content-Type: application/json` is applied unless overridden;
39
+ * a per-call `headers` still wins over both.
40
+ */
41
+ readonly headers?: Readonly<Record<string, string>>;
42
+ /**
43
+ * Defaults to `same-origin`, matching the cookie-session admin the reference implementation
44
+ * ships. A token-authenticated
45
+ * host sets `omit` and supplies an `Authorization` header instead.
46
+ */
47
+ readonly credentials?: RequestCredentials;
48
+ }
49
+ /**
50
+ * The standard `fetch`-backed transport.
51
+ *
52
+ * Success bodies are parsed as JSON. A non-2xx response throws `AdminApiError` carrying the
53
+ * server's `error` string, HTTP status, canonical `code` when present, and the raw parsed body.
54
+ */
55
+ export declare function createHttpTransport(options: HttpTransportOptions): AdminTransport;
56
+ /**
57
+ * Assembles route groups into one client.
58
+ *
59
+ * Jini-shipped and host-owned groups are indistinguishable here on purpose — both are just
60
+ * `(transport) => port`. See `types.ts`'s header for the worked example.
61
+ */
62
+ export declare function createAdminClient<TGroups extends Record<string, AdminRouteGroupFactory<unknown>>>(transport: AdminTransport, groups: TGroups): AdminClient<TGroups>;
63
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/core/transport/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEtF;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAElF,MAAM,WAAW,oBAAoB;IACnC;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAE5B;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAEpD;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAC3C;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,cAAc,CAkCjF;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,OAAO,CAAC,CAAC,EAC/F,SAAS,EAAE,cAAc,EACzB,MAAM,EAAE,OAAO,GACd,WAAW,CAAC,OAAO,CAAC,CAYtB"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @file The default HTTP `AdminTransport`, and the client assembler.
3
+ *
4
+ * Ported from the reference implementation's `lib/api.ts` `request()`, with the two
5
+ * hard-coded values it closed over — the base URL and the same-origin cookie policy — lifted into
6
+ * options. Nothing else about the request/response handling changed; the error-body parsing in
7
+ * particular is deliberately identical, including the `.catch(() => ({}))` on a body that is not
8
+ * JSON.
9
+ */
10
+ import { AdminApiError } from './errors.js';
11
+ /**
12
+ * The standard `fetch`-backed transport.
13
+ *
14
+ * Success bodies are parsed as JSON. A non-2xx response throws `AdminApiError` carrying the
15
+ * server's `error` string, HTTP status, canonical `code` when present, and the raw parsed body.
16
+ */
17
+ export function createHttpTransport(options) {
18
+ const { baseUrl, headers: baseHeaders, credentials = 'same-origin' } = options;
19
+ const doFetch = options.fetch ?? globalThis.fetch;
20
+ return {
21
+ async request(path, init = {}) {
22
+ const res = await doFetch(`${baseUrl}${path}`, {
23
+ credentials,
24
+ ...init,
25
+ // Spread last so a per-call header overrides both the JSON default and the transport's
26
+ // own. The reference implementation's original spread `...init` last, which meant a call passing `headers` lost
27
+ // the `Content-Type` default rather than merging with it.
28
+ headers: {
29
+ 'Content-Type': 'application/json',
30
+ ...baseHeaders,
31
+ ...init.headers,
32
+ },
33
+ });
34
+ // A 204 or an empty body is not a JSON parse failure worth surfacing — it is a successful
35
+ // mutation with nothing to return. Same `.catch(() => ({}))` as the original.
36
+ const body = (await res.json().catch(() => ({})));
37
+ if (!res.ok) {
38
+ throw new AdminApiError(String(body?.error ?? `request failed (${res.status})`), res.status, typeof body?.code === 'string' ? body.code : undefined, body);
39
+ }
40
+ return body;
41
+ },
42
+ };
43
+ }
44
+ /**
45
+ * Assembles route groups into one client.
46
+ *
47
+ * Jini-shipped and host-owned groups are indistinguishable here on purpose — both are just
48
+ * `(transport) => port`. See `types.ts`'s header for the worked example.
49
+ */
50
+ export function createAdminClient(transport, groups) {
51
+ const built = { transport };
52
+ for (const [name, factory] of Object.entries(groups)) {
53
+ // `transport` is a reserved key: shadowing it would silently replace the escape hatch every
54
+ // host relies on for un-wrapped routes. Loud failure beats a client whose `.transport` is a
55
+ // route group.
56
+ if (name === 'transport') {
57
+ throw new Error('createAdminClient: "transport" is a reserved route-group name');
58
+ }
59
+ built[name] = factory(transport);
60
+ }
61
+ return built;
62
+ }
63
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/core/transport/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AA8C5C;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAA6B;IAC/D,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,GAAG,aAAa,EAAE,GAAG,OAAO,CAAC;IAC/E,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC;IAElD,OAAO;QACL,KAAK,CAAC,OAAO,CAAI,IAAY,EAAE,OAAoB,EAAE;YACnD,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,OAAO,GAAG,IAAI,EAAE,EAAE;gBAC7C,WAAW;gBACX,GAAG,IAAI;gBACP,uFAAuF;gBACvF,gHAAgH;gBAChH,0DAA0D;gBAC1D,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,GAAG,WAAW;oBACd,GAAI,IAAI,CAAC,OAA8C;iBACxD;aACF,CAAC,CAAC;YAEH,0FAA0F;YAC1F,8EAA8E;YAC9E,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAA4B,CAAC;YAE7E,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,IAAI,aAAa,CACrB,MAAM,CAAC,IAAI,EAAE,KAAK,IAAI,mBAAmB,GAAG,CAAC,MAAM,GAAG,CAAC,EACvD,GAAG,CAAC,MAAM,EACV,OAAO,IAAI,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,EACtD,IAAI,CACL,CAAC;YACJ,CAAC;YACD,OAAO,IAAS,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,SAAyB,EACzB,MAAe;IAEf,MAAM,KAAK,GAA4B,EAAE,SAAS,EAAE,CAAC;IACrD,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACrD,4FAA4F;QAC5F,4FAA4F;QAC5F,eAAe;QACf,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;QACnF,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,KAA6B,CAAC;AACvC,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { AdminApiError, describeApiError } from './errors.js';
2
+ export { createAdminClient, createHttpTransport } from './http.js';
3
+ export type { AdminFetch, HttpTransportOptions } from './http.js';
4
+ export type { AdminClient, AdminRouteGroupFactory, AdminTransport } from './types.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/transport/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACnE,YAAY,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAClE,YAAY,EAAE,WAAW,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { AdminApiError, describeApiError } from './errors.js';
2
+ export { createAdminClient, createHttpTransport } from './http.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/transport/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @file The transport seam — the single primitive every admin route group is built on.
3
+ *
4
+ * ## Why this exists
5
+ *
6
+ * The reference implementation's `lib/api.ts` is 1,548 lines and 134 methods in one flat object literal,
7
+ * every one of them closing over a module-private `request()` and a module-private
8
+ * `BASE = "/api/admin/v1"`. That shape has two costs: the whole object is one unit (you cannot
9
+ * take the 67 generic methods without the 67 CMS-domain ones), and there is no way for a host to
10
+ * add a route group without editing the file.
11
+ *
12
+ * Inverting it — `request` becomes an injected `AdminTransport`, and each route group becomes a
13
+ * factory over it — makes both work:
14
+ *
15
+ * ```ts
16
+ * const transport = createHttpTransport({ baseUrl: '/api/admin/v1' });
17
+ *
18
+ * const client = createAdminClient(transport, {
19
+ * identity: createIdentityRoutes, // shipped by @jini-ai/admin
20
+ * media: createMediaRoutes, // shipped by @jini-ai/admin
21
+ * posts: createHostPostRoutes, // the host's own, same signature
22
+ * widgets: createHostWidgetRoutes, // the host's own
23
+ * });
24
+ *
25
+ * await client.identity.listUsers(); // Jini's
26
+ * await client.posts.list(); // the host's
27
+ * ```
28
+ *
29
+ * Both halves share one auth policy, one error class, one place to add retries or tracing. A host
30
+ * adding a route is a factory plus one line in the map — it never edits a Jini file, and it never
31
+ * grows a second `fetch` wrapper with its own subtly different error handling, which is exactly
32
+ * how `api.ts` reached 1,548 lines.
33
+ *
34
+ * ## Why `AdminTransport` is an interface and not just a function
35
+ *
36
+ * A bare `request<T>(path, init)` function would have been enough for HTTP. It is an interface so
37
+ * a host can supply a non-HTTP implementation without any route group knowing: a fleet orchestrator
38
+ * drives many site instances in-process and can hand over a direct-dispatch transport that never opens a
39
+ * socket, and tests can supply a fake that returns fixtures. Both are the same seam, which is the
40
+ * point.
41
+ */
42
+ /**
43
+ * How an admin route group talks to its backend.
44
+ *
45
+ * Implementations must throw `AdminApiError` on a non-success response so panels can rely on one
46
+ * error shape regardless of which transport is wired. A transport that throws its own error type
47
+ * silently breaks every panel's `instanceof` check.
48
+ */
49
+ export interface AdminTransport {
50
+ /**
51
+ * @param path Route-group-relative path, always starting with `/` (`/users`, `/media/abc`).
52
+ * The base is the transport's concern — a route group that embeds a base is not portable to
53
+ * a host that mounts the admin API somewhere else.
54
+ * @param init Standard `RequestInit`. A transport that is not HTTP-backed may ignore fields it
55
+ * cannot honour, but must respect `method` and `body`.
56
+ * @throws {AdminApiError} on any non-success response.
57
+ */
58
+ request<T>(path: string, init?: RequestInit): Promise<T>;
59
+ }
60
+ /**
61
+ * A route group: turns a transport into a typed set of operations.
62
+ *
63
+ * Every port implementation in this package is one of these, and a host's own route groups use
64
+ * the identical signature — that symmetry is what lets Jini-shipped and host-owned groups sit in
65
+ * the same client with no adapter between them.
66
+ */
67
+ export type AdminRouteGroupFactory<TPort> = (transport: AdminTransport) => TPort;
68
+ /**
69
+ * The assembled client: one object carrying every wired route group.
70
+ *
71
+ * `TGroups` is inferred from the factory map handed to `createAdminClient`, so a host's own
72
+ * groups are as type-safe as the shipped ones with no declaration merging or module augmentation.
73
+ */
74
+ export type AdminClient<TGroups extends Record<string, AdminRouteGroupFactory<unknown>>> = {
75
+ readonly [K in keyof TGroups]: ReturnType<TGroups[K]>;
76
+ } & {
77
+ /** The transport every group was built over — exposed so a host can make one-off calls to a
78
+ * route it has not (or will never) wrap in a group, without reaching for a bare `fetch`. */
79
+ readonly transport: AdminTransport;
80
+ };
81
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/transport/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;;;OAOG;IACH,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC1D;AAED;;;;;;GAMG;AACH,MAAM,MAAM,sBAAsB,CAAC,KAAK,IAAI,CAAC,SAAS,EAAE,cAAc,KAAK,KAAK,CAAC;AAEjF;;;;;GAKG;AACH,MAAM,MAAM,WAAW,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,OAAO,CAAC,CAAC,IAAI;IACzF,QAAQ,EAAE,CAAC,IAAI,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;CACtD,GAAG;IACF;iGAC6F;IAC7F,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;CACpC,CAAC"}