@learnorama/mozaik-sdk 0.1.0 → 0.3.2-staging.19.1

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 (242) hide show
  1. package/README.md +186 -5
  2. package/dist/api/account.d.ts +2 -2
  3. package/dist/api/account.js +1 -1
  4. package/dist/api/account.js.map +1 -1
  5. package/dist/api/admin-catalogue.d.ts +2 -2
  6. package/dist/api/admin-catalogue.js +1 -1
  7. package/dist/api/admin-catalogue.js.map +1 -1
  8. package/dist/api/admin-consumption.d.ts +7 -2
  9. package/dist/api/admin-consumption.js +6 -1
  10. package/dist/api/admin-consumption.js.map +1 -1
  11. package/dist/api/admin-licenses.d.ts +2 -2
  12. package/dist/api/admin-licenses.js +1 -1
  13. package/dist/api/admin-licenses.js.map +1 -1
  14. package/dist/api/admin-patrons.d.ts +3 -3
  15. package/dist/api/admin-patrons.js +2 -2
  16. package/dist/api/admin-patrons.js.map +1 -1
  17. package/dist/api/admin-reviews.d.ts +2 -2
  18. package/dist/api/admin-reviews.js +1 -1
  19. package/dist/api/admin-reviews.js.map +1 -1
  20. package/dist/api/admin-roles.d.ts +3 -3
  21. package/dist/api/admin-roles.js +2 -2
  22. package/dist/api/admin-roles.js.map +1 -1
  23. package/dist/api/admin-support.d.ts +2 -2
  24. package/dist/api/admin-support.js +1 -1
  25. package/dist/api/admin-support.js.map +1 -1
  26. package/dist/api/admin-tenants.d.ts +8 -2
  27. package/dist/api/admin-tenants.js +20 -2
  28. package/dist/api/admin-tenants.js.map +1 -1
  29. package/dist/api/admin-tutoring.d.ts +2 -2
  30. package/dist/api/admin-tutoring.js +1 -1
  31. package/dist/api/admin-tutoring.js.map +1 -1
  32. package/dist/api/auth.d.ts +2 -2
  33. package/dist/api/auth.js +1 -1
  34. package/dist/api/auth.js.map +1 -1
  35. package/dist/api/biblio-catalogue.d.ts +2 -2
  36. package/dist/api/biblio-catalogue.js +1 -1
  37. package/dist/api/biblio-catalogue.js.map +1 -1
  38. package/dist/api/biblio-home.d.ts +2 -2
  39. package/dist/api/biblio-home.js +1 -1
  40. package/dist/api/biblio-home.js.map +1 -1
  41. package/dist/api/biblio-library.d.ts +2 -2
  42. package/dist/api/biblio-library.js +1 -1
  43. package/dist/api/biblio-library.js.map +1 -1
  44. package/dist/api/biblio-nextory.d.ts +2 -2
  45. package/dist/api/biblio-nextory.js +1 -1
  46. package/dist/api/biblio-nextory.js.map +1 -1
  47. package/dist/api/biblio-press.d.ts +2 -2
  48. package/dist/api/biblio-press.js +1 -1
  49. package/dist/api/biblio-press.js.map +1 -1
  50. package/dist/api/biblio-search.d.ts +2 -2
  51. package/dist/api/biblio-search.js +1 -1
  52. package/dist/api/biblio-search.js.map +1 -1
  53. package/dist/api/biblio-telemetry.d.ts +2 -2
  54. package/dist/api/biblio-telemetry.js +1 -1
  55. package/dist/api/biblio-telemetry.js.map +1 -1
  56. package/dist/api/consumption.d.ts +2 -2
  57. package/dist/api/consumption.js +1 -1
  58. package/dist/api/consumption.js.map +1 -1
  59. package/dist/api/cvs-app.d.ts +2 -2
  60. package/dist/api/cvs-app.js +1 -1
  61. package/dist/api/cvs-app.js.map +1 -1
  62. package/dist/api/cvs-documents.d.ts +16 -4
  63. package/dist/api/cvs-documents.js +22 -5
  64. package/dist/api/cvs-documents.js.map +1 -1
  65. package/dist/api/cvs-favorites.d.ts +2 -2
  66. package/dist/api/cvs-favorites.js +1 -1
  67. package/dist/api/cvs-favorites.js.map +1 -1
  68. package/dist/api/cvs-ils.d.ts +167 -0
  69. package/dist/api/cvs-ils.js +362 -0
  70. package/dist/api/cvs-ils.js.map +1 -0
  71. package/dist/api/cvs-loans.d.ts +2 -2
  72. package/dist/api/cvs-loans.js +1 -1
  73. package/dist/api/cvs-loans.js.map +1 -1
  74. package/dist/api/cvs-payments.d.ts +49 -0
  75. package/dist/api/cvs-payments.js +84 -0
  76. package/dist/api/cvs-payments.js.map +1 -0
  77. package/dist/api/cvs-streaming.d.ts +2 -2
  78. package/dist/api/cvs-streaming.js +1 -1
  79. package/dist/api/cvs-streaming.js.map +1 -1
  80. package/dist/api/index.d.ts +5 -2
  81. package/dist/api/index.js +7 -4
  82. package/dist/api/partner-catalogue.d.ts +2 -2
  83. package/dist/api/partner-catalogue.js +1 -1
  84. package/dist/api/partner-catalogue.js.map +1 -1
  85. package/dist/api/partner-reader.d.ts +2 -2
  86. package/dist/api/partner-reader.js +1 -1
  87. package/dist/api/partner-reader.js.map +1 -1
  88. package/dist/api/profiles.d.ts +2 -2
  89. package/dist/api/profiles.js +1 -1
  90. package/dist/api/profiles.js.map +1 -1
  91. package/dist/api/search.d.ts +18 -0
  92. package/dist/api/search.js +37 -0
  93. package/dist/api/search.js.map +1 -0
  94. package/dist/api/tutoring-catalogue.d.ts +2 -2
  95. package/dist/api/tutoring-catalogue.js +1 -1
  96. package/dist/api/tutoring-catalogue.js.map +1 -1
  97. package/dist/api/tutoring-chat.d.ts +2 -2
  98. package/dist/api/tutoring-chat.js +1 -1
  99. package/dist/api/tutoring-chat.js.map +1 -1
  100. package/dist/api/tutoring-dispatch.d.ts +2 -2
  101. package/dist/api/tutoring-dispatch.js +1 -1
  102. package/dist/api/tutoring-dispatch.js.map +1 -1
  103. package/dist/api/tutoring-practitioner.d.ts +2 -2
  104. package/dist/api/tutoring-practitioner.js +1 -1
  105. package/dist/api/tutoring-practitioner.js.map +1 -1
  106. package/dist/api/tutoring-requests.d.ts +2 -2
  107. package/dist/api/tutoring-requests.js +1 -1
  108. package/dist/api/tutoring-requests.js.map +1 -1
  109. package/dist/auth.d.ts +10 -0
  110. package/dist/auth.js +451 -0
  111. package/dist/auth.js.map +1 -0
  112. package/dist/chat.d.ts +3 -0
  113. package/dist/chat.js +3 -0
  114. package/dist/{client-BDwBZYB0.d.ts → chunks/client.d.ts} +1 -1
  115. package/dist/{client-D3kMOcIr.js → chunks/client.js} +1 -1
  116. package/dist/chunks/client.js.map +1 -0
  117. package/dist/chunks/client2.d.ts +53 -0
  118. package/dist/chunks/connection.d.ts +28 -0
  119. package/dist/chunks/connection.js +141 -0
  120. package/dist/chunks/connection.js.map +1 -0
  121. package/dist/{index-wL_20vR8.d.ts → chunks/index.d.ts} +901 -128
  122. package/dist/index.d.ts +4 -4
  123. package/dist/index.js +4 -4
  124. package/dist/index.js.map +1 -1
  125. package/dist/models.d.ts +2 -2
  126. package/dist/models.js +44 -1
  127. package/dist/models.js.map +1 -1
  128. package/dist/react/account.d.ts +2 -2
  129. package/dist/react/account.js +2 -2
  130. package/dist/react/account.js.map +1 -1
  131. package/dist/react/admin-catalogue.d.ts +2 -2
  132. package/dist/react/admin-catalogue.js +2 -2
  133. package/dist/react/admin-catalogue.js.map +1 -1
  134. package/dist/react/admin-consumption.d.ts +7 -2
  135. package/dist/react/admin-consumption.js +7 -2
  136. package/dist/react/admin-consumption.js.map +1 -1
  137. package/dist/react/admin-licenses.d.ts +2 -2
  138. package/dist/react/admin-licenses.js +2 -2
  139. package/dist/react/admin-licenses.js.map +1 -1
  140. package/dist/react/admin-patrons.d.ts +3 -3
  141. package/dist/react/admin-patrons.js +3 -3
  142. package/dist/react/admin-patrons.js.map +1 -1
  143. package/dist/react/admin-reviews.d.ts +2 -2
  144. package/dist/react/admin-reviews.js +2 -2
  145. package/dist/react/admin-reviews.js.map +1 -1
  146. package/dist/react/admin-roles.d.ts +3 -3
  147. package/dist/react/admin-roles.js +3 -3
  148. package/dist/react/admin-roles.js.map +1 -1
  149. package/dist/react/admin-support.d.ts +2 -2
  150. package/dist/react/admin-support.js +2 -2
  151. package/dist/react/admin-support.js.map +1 -1
  152. package/dist/react/admin-tenants.d.ts +35 -2
  153. package/dist/react/admin-tenants.js +45 -3
  154. package/dist/react/admin-tenants.js.map +1 -1
  155. package/dist/react/admin-tutoring.d.ts +2 -2
  156. package/dist/react/admin-tutoring.js +2 -2
  157. package/dist/react/admin-tutoring.js.map +1 -1
  158. package/dist/react/auth.d.ts +2 -2
  159. package/dist/react/auth.js +2 -2
  160. package/dist/react/auth.js.map +1 -1
  161. package/dist/react/biblio-catalogue.d.ts +2 -2
  162. package/dist/react/biblio-catalogue.js +2 -2
  163. package/dist/react/biblio-catalogue.js.map +1 -1
  164. package/dist/react/biblio-home.d.ts +2 -2
  165. package/dist/react/biblio-home.js +2 -2
  166. package/dist/react/biblio-home.js.map +1 -1
  167. package/dist/react/biblio-library.d.ts +2 -2
  168. package/dist/react/biblio-library.js +2 -2
  169. package/dist/react/biblio-library.js.map +1 -1
  170. package/dist/react/biblio-nextory.d.ts +2 -2
  171. package/dist/react/biblio-nextory.js +2 -2
  172. package/dist/react/biblio-nextory.js.map +1 -1
  173. package/dist/react/biblio-press.d.ts +2 -2
  174. package/dist/react/biblio-press.js +2 -2
  175. package/dist/react/biblio-press.js.map +1 -1
  176. package/dist/react/biblio-search.d.ts +2 -2
  177. package/dist/react/biblio-search.js +2 -2
  178. package/dist/react/biblio-search.js.map +1 -1
  179. package/dist/react/biblio-telemetry.d.ts +2 -2
  180. package/dist/react/biblio-telemetry.js +2 -2
  181. package/dist/react/biblio-telemetry.js.map +1 -1
  182. package/dist/react/chat.d.ts +36 -0
  183. package/dist/react/chat.js +234 -0
  184. package/dist/react/chat.js.map +1 -0
  185. package/dist/react/consumption.d.ts +2 -2
  186. package/dist/react/consumption.js +2 -2
  187. package/dist/react/consumption.js.map +1 -1
  188. package/dist/react/cvs-app.d.ts +2 -2
  189. package/dist/react/cvs-app.js +2 -2
  190. package/dist/react/cvs-app.js.map +1 -1
  191. package/dist/react/cvs-documents.d.ts +17 -4
  192. package/dist/react/cvs-documents.js +25 -8
  193. package/dist/react/cvs-documents.js.map +1 -1
  194. package/dist/react/cvs-favorites.d.ts +2 -2
  195. package/dist/react/cvs-favorites.js +2 -2
  196. package/dist/react/cvs-favorites.js.map +1 -1
  197. package/dist/react/cvs-ils.d.ts +552 -0
  198. package/dist/react/cvs-ils.js +822 -0
  199. package/dist/react/cvs-ils.js.map +1 -0
  200. package/dist/react/cvs-loans.d.ts +2 -2
  201. package/dist/react/cvs-loans.js +2 -2
  202. package/dist/react/cvs-loans.js.map +1 -1
  203. package/dist/react/cvs-payments.d.ts +123 -0
  204. package/dist/react/cvs-payments.js +181 -0
  205. package/dist/react/cvs-payments.js.map +1 -0
  206. package/dist/react/cvs-streaming.d.ts +2 -2
  207. package/dist/react/cvs-streaming.js +2 -2
  208. package/dist/react/cvs-streaming.js.map +1 -1
  209. package/dist/react/index.d.ts +5 -2
  210. package/dist/react/index.js +5 -2
  211. package/dist/react/partner-catalogue.d.ts +2 -2
  212. package/dist/react/partner-catalogue.js +2 -2
  213. package/dist/react/partner-catalogue.js.map +1 -1
  214. package/dist/react/partner-reader.d.ts +2 -2
  215. package/dist/react/partner-reader.js +2 -2
  216. package/dist/react/partner-reader.js.map +1 -1
  217. package/dist/react/profiles.d.ts +2 -2
  218. package/dist/react/profiles.js +2 -2
  219. package/dist/react/profiles.js.map +1 -1
  220. package/dist/react/search.d.ts +47 -0
  221. package/dist/react/search.js +81 -0
  222. package/dist/react/search.js.map +1 -0
  223. package/dist/react/sso.d.ts +15 -0
  224. package/dist/react/sso.js +48 -0
  225. package/dist/react/sso.js.map +1 -0
  226. package/dist/react/tutoring-catalogue.d.ts +2 -2
  227. package/dist/react/tutoring-catalogue.js +2 -2
  228. package/dist/react/tutoring-catalogue.js.map +1 -1
  229. package/dist/react/tutoring-chat.d.ts +2 -2
  230. package/dist/react/tutoring-chat.js +2 -2
  231. package/dist/react/tutoring-chat.js.map +1 -1
  232. package/dist/react/tutoring-dispatch.d.ts +2 -2
  233. package/dist/react/tutoring-dispatch.js +2 -2
  234. package/dist/react/tutoring-dispatch.js.map +1 -1
  235. package/dist/react/tutoring-practitioner.d.ts +2 -2
  236. package/dist/react/tutoring-practitioner.js +2 -2
  237. package/dist/react/tutoring-practitioner.js.map +1 -1
  238. package/dist/react/tutoring-requests.d.ts +2 -2
  239. package/dist/react/tutoring-requests.js +2 -2
  240. package/dist/react/tutoring-requests.js.map +1 -1
  241. package/package.json +29 -1
  242. package/dist/client-D3kMOcIr.js.map +0 -1
package/README.md CHANGED
@@ -35,9 +35,123 @@ The token getter runs for every request, so refreshing a token does not require
35
35
  recreating the client. Clients can also supply default headers and a custom fetch
36
36
  implementation. Request options accept headers and an AbortSignal.
37
37
 
38
- There is no global token storage, automatic sign-in or automatic replay of failed
39
- writes. The application owns its PKCE flow and session refresh. Backend-owned
40
- Keycloak client secrets must never be supplied to this SDK.
38
+ The basic `createClient` transport uses the token getter you provide. For managed
39
+ PKCE and session refresh, use the SSO client below. Neither transport automatically
40
+ replays failed application writes. Backend-owned Keycloak client secrets must
41
+ never be supplied to this SDK.
42
+
43
+ ## Google Workspace and partner SSO
44
+
45
+ ```ts
46
+ // auth.ts — one instance for the browser application
47
+ import { createMozaikAuth } from '@learnorama/mozaik-sdk/auth';
48
+
49
+ export const auth = createMozaikAuth({
50
+ baseUrl: 'https://api.stg.mozaik.co',
51
+ redirectUri: 'https://your-app.example/auth/callback',
52
+ });
53
+
54
+ // A click handler: prepare returns a URL; navigation stays under your control.
55
+ const { url } = await auth.prepareInternalSso({ returnTo: '/requests' });
56
+ window.location.assign(url);
57
+
58
+ // On /auth/callback, pass your SPA router's replace-navigation function:
59
+ async function finishSso(navigate: (path: string) => void) {
60
+ const { returnTo } = await auth.completeSso();
61
+ navigate(returnTo);
62
+ }
63
+ ```
64
+
65
+ Register the application's callback URL in Keycloak and allow its origin in the
66
+ backend's `AUTH_ALLOWED_REDIRECT_ORIGINS`. Replace `your-app.example` with your
67
+ frontend origin. Start login on that origin so the callback can read the same
68
+ tab's transaction storage.
69
+
70
+ | Method | Behavior |
71
+ | --- | --- |
72
+ | `prepareInternalSso({ returnTo? })` | Selects the existing `google` Workspace staff provider. |
73
+ | `prepareProviderSso(alias, { returnTo? })` | Selects a registered, brokered partner; the backend validates the alias. |
74
+ | `prepareSso({ returnTo? })` | Lets the backend resolve the provider from the configured API hostname. |
75
+ | `completeSso(callbackUrl?)` | Validates and consumes the transaction, exchanges the code through the backend, and returns account, principal and return path. Defaults to the current browser URL. |
76
+ | `refresh()` | Refreshes the session; concurrent callers share one exchange. |
77
+ | `logout()` | Clears local authentication immediately and revokes the refresh token. |
78
+ | `dispose()` | Local teardown for the application owner; does not revoke the server session. |
79
+
80
+ Preparation also accepts `prompt: 'login' | 'none' | 'consent'`. Return paths must
81
+ be local absolute paths such as `/requests`, never an external URL. PKCE uses
82
+ S256; state, callback address, transaction expiry, expected provider and any
83
+ returned issuer are checked. The code exchange stays on our backend so first
84
+ login provisions the local account. Google Workspace membership and permissions
85
+ remain enforced by Keycloak and the backend.
86
+
87
+ Only short-lived PKCE transactions are saved to per-tab `sessionStorage` (ten
88
+ minutes). Access and refresh tokens stay in memory and are absent from the auth
89
+ snapshot. **Reloading the page starts without a local session**: initiate SSO again;
90
+ an existing Keycloak/Workspace session may complete it without another credential
91
+ prompt. This implementation uses full-page redirects, not popup messaging or
92
+ silent iframe authentication. After completing the callback, use your SPA router
93
+ to navigate locally: `location.replace()` would reload and lose the new session.
94
+
95
+ The client refreshes before expiry and on demand, coordinating token rotation.
96
+ Transient refresh failures may use an access token only until its expiry; rejected
97
+ or expired sessions clear local authentication. Remote logout errors reject the
98
+ promise while leaving the application signed out. Tokens are attached through
99
+ `auth.client`; its identity changes on login/logout and stays stable across
100
+ refreshes. Old client instances reject requests after a session change.
101
+
102
+ ## React SSO shortcut
103
+
104
+ `MozaikAuthProvider` supplies both authentication and its own TanStack Query
105
+ provider. Put SDK consumers inside it; an additional QueryClientProvider is not
106
+ needed for that subtree. `/react/auth` still contains the generated HTTP hooks;
107
+ the handwritten SSO integration is `/react/sso`.
108
+
109
+ ```tsx
110
+ import { useEffect } from 'react';
111
+ import { MozaikAuthProvider, useMozaikAuth } from '@learnorama/mozaik-sdk/react/sso';
112
+ import { useReadMe } from '@learnorama/mozaik-sdk/react/auth';
113
+ import { auth } from './auth';
114
+
115
+ export function Root() {
116
+ return <MozaikAuthProvider auth={auth}><Account /></MozaikAuthProvider>;
117
+ }
118
+
119
+ function Account() {
120
+ const { client, isAuthenticated, isLoading, error, prepareInternalSso, logout } = useMozaikAuth();
121
+ const me = useReadMe({ request: { client }, query: { enabled: isAuthenticated } });
122
+ async function signIn() {
123
+ try {
124
+ const { url } = await prepareInternalSso({ returnTo: '/requests' });
125
+ window.location.assign(url);
126
+ } catch { /* error is exposed by useMozaikAuth */ }
127
+ }
128
+ if (isAuthenticated) return <button onClick={() => { void logout().catch(() => {}); }}>Sign out {me.data?.account.email}</button>;
129
+ return <><button disabled={isLoading} onClick={() => { void signIn(); }}>Sign in with Google Workspace</button>{error && <p role="alert">{error.message}</p>}</>;
130
+ }
131
+
132
+ // Render this at /auth/callback, also inside MozaikAuthProvider.
133
+ export function SsoCallback({ navigate }: { navigate: (path: string) => void }) {
134
+ const { completeSso, error } = useMozaikAuth();
135
+ useEffect(() => {
136
+ void completeSso().then(({ returnTo }) => navigate(returnTo)).catch(() => {});
137
+ }, [completeSso, navigate]);
138
+ return <p>{error ? error.message : 'Completing sign-in…'}</p>;
139
+ }
140
+ ```
141
+
142
+ Concurrent callback completions share one exchange. The callback parameters are
143
+ removed from browser history; repeated default completion calls on that cleaned
144
+ URL are safe during React remounts. Replaying an explicit consumed callback is
145
+ rejected. Login/logout replace the query cache and remount the session subtree,
146
+ preventing previous-account results from appearing. Refresh retains the cache.
147
+ For chat, use the same auth `client` and `enabled: isAuthenticated` with
148
+ `useTutoringChat`; session changes close the previous connection automatically.
149
+
150
+ Create the auth instance outside component renders, once per browser application
151
+ or per server request. The provider does not call `dispose()` on unmount: this
152
+ would invalidate the shared controller during Strict Mode's cleanup/re-setup.
153
+ The application owner may dispose it on permanent teardown. Browser operations
154
+ require Web Crypto and sessionStorage in a secure context.
41
155
 
42
156
  ## React
43
157
 
@@ -64,6 +178,73 @@ Create a separate client and QueryClient for each server-side request. Clear the
64
178
  query cache on logout, account change, or a change of acting-as/tenant context;
65
179
  generated query keys do not contain credentials or identify the current user.
66
180
 
181
+ ## ProfExpress student/teacher chat
182
+
183
+ The handwritten chat hook is published alongside the generated HTTP hooks:
184
+
185
+ ```tsx
186
+ import type { Client } from '@learnorama/mozaik-sdk';
187
+ import { useTutoringChat } from '@learnorama/mozaik-sdk/react/chat';
188
+
189
+ export function TutoringRoom({ client, requestId }: { client: Client; requestId: number }) {
190
+ const chat = useTutoringChat({ client, requestId });
191
+
192
+ async function send(body: string) {
193
+ try {
194
+ await chat.sendMessage({ body });
195
+ } catch {
196
+ // chat.sendError exposes the error; keep the draft for an explicit retry.
197
+ }
198
+ }
199
+
200
+ return (
201
+ <section>
202
+ <p>Connection: {chat.status}</p>
203
+ {chat.error && <p role="alert">Chat connection interrupted.</p>}
204
+ {chat.hasMore && (
205
+ <button disabled={chat.isLoadingOlder} onClick={() => { void chat.loadOlder().catch(() => {}); }}>
206
+ Older messages
207
+ </button>
208
+ )}
209
+ {chat.messages.map(message => <p key={message.id}>{message.body ?? 'Message removed'}</p>)}
210
+ <button disabled={chat.isSending} onClick={() => { void send('Bonjour !'); }}>Send</button>
211
+ {chat.sendError && <p role="alert">Message not confirmed. Please retry.</p>}
212
+ </section>
213
+ );
214
+ }
215
+ ```
216
+
217
+ Use this inside `QueryClientProvider`. Keep `client` stable; create a new client
218
+ when switching signed-in accounts. Pass `requestId: null` or `enabled: false`
219
+ on logout/when no room is selected. The hook closes its connection, cancels
220
+ requests and clears its private history on cleanup, including React Strict Mode.
221
+ Messages are ordered oldest first and deduplicated by ID.
222
+
223
+ The hook obtains a fresh single-use ticket for every connection; the OAuth access
224
+ token never enters the WebSocket URL. The server acknowledges the Redis
225
+ subscription before accepting the socket. The hook then loads history while
226
+ buffering live messages and retrieves missed pages after reconnecting. Network
227
+ failures reconnect with backoff; HTTP 401/403/404 and socket policy refusal stop
228
+ retries. Refresh authentication/access before enabling the hook again.
229
+
230
+ `sendMessage` uses the HTTP endpoint so success confirms persistence. Each send
231
+ gets a `clientKey`; writes are not automatically retried. For a failed send, pass
232
+ `chat.lastSend` back to `chat.sendMessage` to retain that key and avoid duplication.
233
+ For multiple simultaneous drafts, supply and retain a separate `clientKey` for
234
+ each draft yourself. `loadOlderError`, `sendError` and the connection `error` are
235
+ separate; `isSending`, `isLoading`, and `isLoadingOlder` support the corresponding UI.
236
+
237
+ Without React, import `connectTutoringChat` from `@learnorama/mozaik-sdk/chat`.
238
+ Provide `client`, `requestId`, and `onMessage`; call the returned `close()` on
239
+ cleanup. Its optional async `onConnect(signal)` callback can retrieve history;
240
+ incoming messages are buffered until it resolves. Sending and history are the
241
+ generated functions under `/api/tutoring-chat`.
242
+
243
+ Live chat messages are validated at runtime against the generated message kind
244
+ and the expected room/message shape. The chat module uses native WebSocket,
245
+ AbortSignal and secure-context `crypto.randomUUID()` APIs. It introduces no
246
+ additional production dependencies.
247
+
67
248
  ## Models and errors
68
249
 
69
250
  ```ts
@@ -89,6 +270,6 @@ are required; the backend commit SHA is metadata, not the package version.
89
270
  import { apiVersion, backendRevision, schemaSha256 } from '@learnorama/mozaik-sdk';
90
271
  ```
91
272
 
92
- TypeScript types describe the API contract; they do not validate response bodies
93
- at runtime. The package contains the client and type declarations, not application
273
+ Generated HTTP TypeScript types describe the API contract; they do not validate
274
+ response bodies at runtime. The package contains the client and type declarations, not application
94
275
  credentials or backend implementation code.
@@ -1,6 +1,6 @@
1
1
 
2
- import { $r as AccountDetailRead, B as ServiceListRead, Zr as AccountUpdateWrite } from "../index-wL_20vR8.js";
3
- import { c as request } from "../client-BDwBZYB0.js";
2
+ import { H as ServiceListRead, Ii as AccountUpdateWrite, Ri as AccountDetailRead } from "../chunks/index.js";
3
+ import { c as request } from "../chunks/client.js";
4
4
  //#region src/api/account/account.d.ts
5
5
  export declare const getDeleteAccountUrl: () => string;
6
6
  /**
@@ -1,5 +1,5 @@
1
1
 
2
- import { r as request } from "../client-D3kMOcIr.js";
2
+ import { r as request } from "../chunks/client.js";
3
3
  //#region src/api/account/account.ts
4
4
  const getDeleteAccountUrl = () => {
5
5
  return `/me`;
@@ -1 +1 @@
1
- {"version":3,"file":"account.js","names":[],"sources":["../../src/api/account/account.ts"],"sourcesContent":["/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Mozaik backend\n * Four products behind one API — Biblio, Médiathèques, the partner catalogues (Nextory and YouScribe) and Prof Express. Authentication is Keycloak SSO only, once, for all of them: see the **auth** section below for the browser flow and the three failure modes.\n * OpenAPI spec version: 0.1.0\n */\nimport type {\n AccountDetailRead,\n AccountUpdateWrite,\n ServiceListRead\n} from '../../models';\n\nimport { request } from '../../client';\n\nexport const getDeleteAccountUrl = () => {\n\n\n\n\n return `/me`\n}\n\n/**\n * Close the account (E17). Anonymises; does not erase.\n *\n * The spec asked for the password to be re-submitted. We do not hold one, and\n * assurance is the better guard anyway: a password check would have passed for a\n * session that arrived through a partner handing over a static key in a URL,\n * and ``RequireHighAssurance`` refuses exactly that. The legacy required no\n * confirmation of any kind and accepted the call from a sub-profile (defect D8).\n *\n * **What survives, and why.** The row stays, with identity cleared and\n * ``is_anonymous`` set. Consumption history, loans and billing records point at\n * this user, and deleting the row would either cascade them away — destroying\n * the library's statutory reporting — or orphan them. Clearing the identifying\n * columns satisfies erasure of personal data while leaving the countable facts\n * countable. The retained set is: which tenant, when they registered, and what\n * they consumed, with no name, login, email, birth date or member number.\n *\n * Sub-profiles go with the holder: they are people on the same membership and\n * cannot outlive it. Their rows are anonymised too, not deleted, for the same\n * reason.\n *\n * **The broker is disabled first, and a failure aborts the whole thing.** The\n * credential lives in Keycloak, so anonymising locally while leaving it enabled\n * would let the same person sign in tomorrow and be provisioned a fresh account\n * — erasure that erases nothing. Disabling before any local write means the\n * failure mode is \"nothing happened\", which is recoverable, rather than \"half\n * happened\", which is not.\n *\n * Still missing, and not safe to ship without: no audit record is written.\n * @summary Delete Account\n */\nexport const deleteAccount = async ( options?: Parameters<typeof request>[1]): Promise<void> => {\n\n return request<void>(getDeleteAccountUrl(),\n {\n ...options,\n method: 'DELETE'\n\n\n }\n);}\n\n\nexport const getReadAccountUrl = () => {\n\n\n\n\n return `/me`\n}\n\n/**\n * The authenticated account (E15).\n *\n * No query, no join: ``CurrentUserDep`` has already resolved the row behind the\n * token. The endpoint the legacy should have had here has been returning ``500``\n * on every request for as long as anyone has looked (defect D1).\n * @summary Read Account\n */\nexport const readAccount = async ( options?: Parameters<typeof request>[1]): Promise<AccountDetailRead> => {\n\n return request<AccountDetailRead>(getReadAccountUrl(),\n {\n ...options,\n method: 'GET'\n\n\n }\n);}\n\n\nexport const getUpdateAccountUrl = () => {\n\n\n\n\n return `/me`\n}\n\n/**\n * Partially update the account (E16).\n *\n * ``exclude_unset`` is what separates PATCH from PUT here: a field the client\n * omitted is left alone, which is not the same as a field it sent as null. The\n * response is built from the persisted row rather than the submitted body.\n * @summary Update Account\n */\nexport const updateAccount = async (accountUpdateWrite: AccountUpdateWrite, options?: Parameters<typeof request>[1]): Promise<AccountDetailRead> => {\n\n const getHeaders = (h?: NonNullable<RequestInit['headers']>): Record<string, string | readonly string[]> => {\n if (!h) return {};\n if (h instanceof Headers) return Object.fromEntries(h.entries());\n if (Symbol.iterator in h) {\n return Object.fromEntries(\n Array.from(h as Iterable<Iterable<string>>, (entry) => Array.from(entry) as [string, string]),\n );\n }\n const headers: Record<string, string | readonly string[]> = {};\n for (const [name, value] of Object.entries<string | readonly string[] | undefined>(h)) {\n if (value !== undefined) headers[name] = value;\n }\n return headers;\n };\nreturn request<AccountDetailRead>(getUpdateAccountUrl(),\n {\n ...options,\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) },\n body: JSON.stringify(accountUpdateWrite)\n }\n);}\n\n\nexport const getListServicesUrl = () => {\n\n\n\n\n return `/me/services`\n}\n\n/**\n * Third-party services this account's establishment offers (E27).\n *\n * Under ``/me`` rather than ``/biblio/me`` because it is not a catalogue: these\n * are the establishment's own service tiles, and they belong to the tenant\n * whichever catalogue that tenant reads from. It is also the one place the\n * biblio spec describes something the *CVS* schema models, which is why it sat\n * in the wrong module until the prefixes made the mismatch visible.\n *\n * Date-windowed. A service outside its window is absent rather than disabled:\n * the client should not have to know the difference, and a visible dead link is\n * worse than no link.\n * @summary List Services\n */\nexport const listServices = async ( options?: Parameters<typeof request>[1]): Promise<ServiceListRead> => {\n\n return request<ServiceListRead>(getListServicesUrl(),\n {\n ...options,\n method: 'GET'\n\n\n }\n);}\n\n\n"],"mappings":";;;AAeA,MAAa,4BAA4B;CAKvC,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,MAAa,gBAAgB,OAAQ,YAA2D;CAE9F,OAAO,QAAc,oBAAoB,GACzC;EACE,GAAG;EACH,QAAQ;CAGV,CACF;AAAE;AAGF,MAAa,0BAA0B;CAKrC,OAAO;AACT;;;;;;;;;AAUA,MAAa,cAAc,OAAQ,YAAwE;CAEzG,OAAO,QAA2B,kBAAkB,GACpD;EACE,GAAG;EACH,QAAQ;CAGV,CACF;AAAE;AAGF,MAAa,4BAA4B;CAKvC,OAAO;AACT;;;;;;;;;AAUA,MAAa,gBAAgB,OAAO,oBAAwC,YAAwE;CAEhJ,MAAM,cAAc,MAAwF;EAC5G,IAAI,CAAC,GAAG,OAAO,CAAC;EAChB,IAAI,aAAa,SAAS,OAAO,OAAO,YAAY,EAAE,QAAQ,CAAC;EAC/D,IAAI,OAAO,YAAY,GACrB,OAAO,OAAO,YACZ,MAAM,KAAK,IAAkC,UAAU,MAAM,KAAK,KAAK,CAAqB,CAC9F;EAEF,MAAM,UAAsD,CAAC;EAC7D,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAgD,CAAC,GAClF,IAAI,UAAU,KAAA,GAAW,QAAQ,QAAQ;EAE3C,OAAO;CACT;CACF,OAAO,QAA2B,oBAAoB,GACpD;EACE,GAAG;EACH,QAAQ;EACR,SAAS;GAAE,gBAAgB;GAAoB,GAAG,WAAW,SAAS,OAAO;EAAE;EAC/E,MAAM,KAAK,UAAU,kBAAkB;CACzC,CACF;AAAE;AAGF,MAAa,2BAA2B;CAKtC,OAAO;AACT;;;;;;;;;;;;;;;AAgBA,MAAa,eAAe,OAAQ,YAAsE;CAExG,OAAO,QAAyB,mBAAmB,GACnD;EACE,GAAG;EACH,QAAQ;CAGV,CACF;AAAE"}
1
+ {"version":3,"file":"account.js","names":[],"sources":["../../src/api/account/account.ts"],"sourcesContent":["/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Mozaik backend\n * Four products behind one API — Biblio, Médiathèques, the partner catalogues (Nextory and YouScribe) and Prof Express. Authentication is Keycloak SSO only, once, for all of them: see the **auth** section below for the browser flow and the three failure modes.\n * OpenAPI spec version: 0.3.2\n */\nimport type {\n AccountDetailRead,\n AccountUpdateWrite,\n ServiceListRead\n} from '../../models';\n\nimport { request } from '../../client';\n\nexport const getDeleteAccountUrl = () => {\n\n\n\n\n return `/me`\n}\n\n/**\n * Close the account (E17). Anonymises; does not erase.\n *\n * The spec asked for the password to be re-submitted. We do not hold one, and\n * assurance is the better guard anyway: a password check would have passed for a\n * session that arrived through a partner handing over a static key in a URL,\n * and ``RequireHighAssurance`` refuses exactly that. The legacy required no\n * confirmation of any kind and accepted the call from a sub-profile (defect D8).\n *\n * **What survives, and why.** The row stays, with identity cleared and\n * ``is_anonymous`` set. Consumption history, loans and billing records point at\n * this user, and deleting the row would either cascade them away — destroying\n * the library's statutory reporting — or orphan them. Clearing the identifying\n * columns satisfies erasure of personal data while leaving the countable facts\n * countable. The retained set is: which tenant, when they registered, and what\n * they consumed, with no name, login, email, birth date or member number.\n *\n * Sub-profiles go with the holder: they are people on the same membership and\n * cannot outlive it. Their rows are anonymised too, not deleted, for the same\n * reason.\n *\n * **The broker is disabled first, and a failure aborts the whole thing.** The\n * credential lives in Keycloak, so anonymising locally while leaving it enabled\n * would let the same person sign in tomorrow and be provisioned a fresh account\n * — erasure that erases nothing. Disabling before any local write means the\n * failure mode is \"nothing happened\", which is recoverable, rather than \"half\n * happened\", which is not.\n *\n * Still missing, and not safe to ship without: no audit record is written.\n * @summary Delete Account\n */\nexport const deleteAccount = async ( options?: Parameters<typeof request>[1]): Promise<void> => {\n\n return request<void>(getDeleteAccountUrl(),\n {\n ...options,\n method: 'DELETE'\n\n\n }\n);}\n\n\nexport const getReadAccountUrl = () => {\n\n\n\n\n return `/me`\n}\n\n/**\n * The authenticated account (E15).\n *\n * No query, no join: ``CurrentUserDep`` has already resolved the row behind the\n * token. The endpoint the legacy should have had here has been returning ``500``\n * on every request for as long as anyone has looked (defect D1).\n * @summary Read Account\n */\nexport const readAccount = async ( options?: Parameters<typeof request>[1]): Promise<AccountDetailRead> => {\n\n return request<AccountDetailRead>(getReadAccountUrl(),\n {\n ...options,\n method: 'GET'\n\n\n }\n);}\n\n\nexport const getUpdateAccountUrl = () => {\n\n\n\n\n return `/me`\n}\n\n/**\n * Partially update the account (E16).\n *\n * ``exclude_unset`` is what separates PATCH from PUT here: a field the client\n * omitted is left alone, which is not the same as a field it sent as null. The\n * response is built from the persisted row rather than the submitted body.\n * @summary Update Account\n */\nexport const updateAccount = async (accountUpdateWrite: AccountUpdateWrite, options?: Parameters<typeof request>[1]): Promise<AccountDetailRead> => {\n\n const getHeaders = (h?: NonNullable<RequestInit['headers']>): Record<string, string | readonly string[]> => {\n if (!h) return {};\n if (h instanceof Headers) return Object.fromEntries(h.entries());\n if (Symbol.iterator in h) {\n return Object.fromEntries(\n Array.from(h as Iterable<Iterable<string>>, (entry) => Array.from(entry) as [string, string]),\n );\n }\n const headers: Record<string, string | readonly string[]> = {};\n for (const [name, value] of Object.entries<string | readonly string[] | undefined>(h)) {\n if (value !== undefined) headers[name] = value;\n }\n return headers;\n };\nreturn request<AccountDetailRead>(getUpdateAccountUrl(),\n {\n ...options,\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) },\n body: JSON.stringify(accountUpdateWrite)\n }\n);}\n\n\nexport const getListServicesUrl = () => {\n\n\n\n\n return `/me/services`\n}\n\n/**\n * Third-party services this account's establishment offers (E27).\n *\n * Under ``/me`` rather than ``/biblio/me`` because it is not a catalogue: these\n * are the establishment's own service tiles, and they belong to the tenant\n * whichever catalogue that tenant reads from. It is also the one place the\n * biblio spec describes something the *CVS* schema models, which is why it sat\n * in the wrong module until the prefixes made the mismatch visible.\n *\n * Date-windowed. A service outside its window is absent rather than disabled:\n * the client should not have to know the difference, and a visible dead link is\n * worse than no link.\n * @summary List Services\n */\nexport const listServices = async ( options?: Parameters<typeof request>[1]): Promise<ServiceListRead> => {\n\n return request<ServiceListRead>(getListServicesUrl(),\n {\n ...options,\n method: 'GET'\n\n\n }\n);}\n\n\n"],"mappings":";;;AAeA,MAAa,4BAA4B;CAKvC,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,MAAa,gBAAgB,OAAQ,YAA2D;CAE9F,OAAO,QAAc,oBAAoB,GACzC;EACE,GAAG;EACH,QAAQ;CAGV,CACF;AAAE;AAGF,MAAa,0BAA0B;CAKrC,OAAO;AACT;;;;;;;;;AAUA,MAAa,cAAc,OAAQ,YAAwE;CAEzG,OAAO,QAA2B,kBAAkB,GACpD;EACE,GAAG;EACH,QAAQ;CAGV,CACF;AAAE;AAGF,MAAa,4BAA4B;CAKvC,OAAO;AACT;;;;;;;;;AAUA,MAAa,gBAAgB,OAAO,oBAAwC,YAAwE;CAEhJ,MAAM,cAAc,MAAwF;EAC5G,IAAI,CAAC,GAAG,OAAO,CAAC;EAChB,IAAI,aAAa,SAAS,OAAO,OAAO,YAAY,EAAE,QAAQ,CAAC;EAC/D,IAAI,OAAO,YAAY,GACrB,OAAO,OAAO,YACZ,MAAM,KAAK,IAAkC,UAAU,MAAM,KAAK,KAAK,CAAqB,CAC9F;EAEF,MAAM,UAAsD,CAAC;EAC7D,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAgD,CAAC,GAClF,IAAI,UAAU,KAAA,GAAW,QAAQ,QAAQ;EAE3C,OAAO;CACT;CACF,OAAO,QAA2B,oBAAoB,GACpD;EACE,GAAG;EACH,QAAQ;EACR,SAAS;GAAE,gBAAgB;GAAoB,GAAG,WAAW,SAAS,OAAO;EAAE;EAC/E,MAAM,KAAK,UAAU,kBAAkB;CACzC,CACF;AAAE;AAGF,MAAa,2BAA2B;CAKtC,OAAO;AACT;;;;;;;;;;;;;;;AAgBA,MAAa,eAAe,OAAQ,YAAsE;CAExG,OAAO,QAAyB,mBAAmB,GACnD;EACE,GAAG;EACH,QAAQ;CAGV,CACF;AAAE"}
@@ -1,6 +1,6 @@
1
1
 
2
- import { C as ThemeWrite, E as ThemeRead, T as ThemeListRead, sn as ListThemesParams, w as ThemeOrderWrite } from "../index-wL_20vR8.js";
3
- import { c as request } from "../client-BDwBZYB0.js";
2
+ import { C as ThemeWrite, E as ThemeRead, T as ThemeListRead, bn as ListThemesParams, w as ThemeOrderWrite } from "../chunks/index.js";
3
+ import { c as request } from "../chunks/client.js";
4
4
  //#region src/api/admin-catalogue/admin-catalogue.d.ts
5
5
  export declare const getListThemesUrl: (params?: ListThemesParams) => string;
6
6
  /**
@@ -1,5 +1,5 @@
1
1
 
2
- import { r as request } from "../client-D3kMOcIr.js";
2
+ import { r as request } from "../chunks/client.js";
3
3
  //#region src/api/admin-catalogue/admin-catalogue.ts
4
4
  const getListThemesUrl = (params) => {
5
5
  const normalizedParams = new URLSearchParams();
@@ -1 +1 @@
1
- {"version":3,"file":"admin-catalogue.js","names":[],"sources":["../../src/api/admin-catalogue/admin-catalogue.ts"],"sourcesContent":["/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Mozaik backend\n * Four products behind one API — Biblio, Médiathèques, the partner catalogues (Nextory and YouScribe) and Prof Express. Authentication is Keycloak SSO only, once, for all of them: see the **auth** section below for the browser flow and the three failure modes.\n * OpenAPI spec version: 0.1.0\n */\nimport type {\n ListThemesParams,\n ThemeListRead,\n ThemeOrderWrite,\n ThemeRead,\n ThemeWrite\n} from '../../models';\n\nimport { request } from '../../client';\n\nexport const getListThemesUrl = (params?: ListThemesParams,) => {\n const normalizedParams = new URLSearchParams();\n\n Object.entries(params || {}).forEach(([key, value]) => {\n\n if (value !== undefined) {\n normalizedParams.append(key, value === null ? 'null' : String(value))\n }\n });\n\n const stringifiedParams = normalizedParams.toString();\n\n return stringifiedParams.length > 0 ? `/admin/themes?${stringifiedParams}` : `/admin/themes`\n}\n\n/**\n * Every theme, or one pillar's, in display order.\n *\n * Not paginated. There are 27 of them, and a client that has to page through\n * a list it is about to reorder cannot reorder it.\n * @summary List Themes\n */\nexport const listThemes = async (params?: ListThemesParams, options?: Parameters<typeof request>[1]): Promise<ThemeListRead> => {\n\n return request<ThemeListRead>(getListThemesUrl(params),\n {\n ...options,\n method: 'GET'\n\n\n }\n);}\n\n\nexport const getCreateThemeUrl = () => {\n\n\n\n\n return `/admin/themes`\n}\n\n/**\n * Add a theme.\n *\n * ``409`` on a duplicate ``(pillar, title)``: that pair is unique in the\n * schema, and two themes with the same name in the same pillar are\n * indistinguishable to everyone downstream.\n *\n * With no ``sortOrder`` it goes last, which is what the legacy's form does by\n * defaulting the field to the current maximum plus one.\n * @summary Create Theme\n */\nexport const createTheme = async (themeWrite: ThemeWrite, options?: Parameters<typeof request>[1]): Promise<ThemeRead> => {\n\n const getHeaders = (h?: NonNullable<RequestInit['headers']>): Record<string, string | readonly string[]> => {\n if (!h) return {};\n if (h instanceof Headers) return Object.fromEntries(h.entries());\n if (Symbol.iterator in h) {\n return Object.fromEntries(\n Array.from(h as Iterable<Iterable<string>>, (entry) => Array.from(entry) as [string, string]),\n );\n }\n const headers: Record<string, string | readonly string[]> = {};\n for (const [name, value] of Object.entries<string | readonly string[] | undefined>(h)) {\n if (value !== undefined) headers[name] = value;\n }\n return headers;\n };\nreturn request<ThemeRead>(getCreateThemeUrl(),\n {\n ...options,\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) },\n body: JSON.stringify(themeWrite)\n }\n);}\n\n\nexport const getReorderThemesUrl = () => {\n\n\n\n\n return `/admin/themes/order`\n}\n\n/**\n * Set the display order for a set of themes, in one transaction.\n *\n * The legacy renumbers one theme per request, so dragging an item in a list of\n * thirty was thirty requests — and closing the tab halfway left the order\n * half-applied, with two themes claiming the same position and the listing\n * silently choosing between them.\n *\n * Ids not named keep their relative order after the ones that are, so a client\n * may send only what moved. An unknown id is ``404`` and nothing is written:\n * a partially applied reorder is the failure this endpoint exists to prevent.\n * @summary Reorder Themes\n */\nexport const reorderThemes = async (themeOrderWrite: ThemeOrderWrite, options?: Parameters<typeof request>[1]): Promise<void> => {\n\n const getHeaders = (h?: NonNullable<RequestInit['headers']>): Record<string, string | readonly string[]> => {\n if (!h) return {};\n if (h instanceof Headers) return Object.fromEntries(h.entries());\n if (Symbol.iterator in h) {\n return Object.fromEntries(\n Array.from(h as Iterable<Iterable<string>>, (entry) => Array.from(entry) as [string, string]),\n );\n }\n const headers: Record<string, string | readonly string[]> = {};\n for (const [name, value] of Object.entries<string | readonly string[] | undefined>(h)) {\n if (value !== undefined) headers[name] = value;\n }\n return headers;\n };\nreturn request<void>(getReorderThemesUrl(),\n {\n ...options,\n method: 'PUT',\n headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) },\n body: JSON.stringify(themeOrderWrite)\n }\n);}\n\n\nexport const getDeleteThemeUrl = (themeId: number,) => {\n\n\n\n\n return `/admin/themes/${themeId}`\n}\n\n/**\n * Remove a theme.\n *\n * ``409`` when works still carry it. The legacy deletes and leaves the\n * references dangling, which is how a catalogue ends up with items filed under\n * a theme that no longer exists and no screen that can find them.\n *\n * Idempotent otherwise: deleting something already gone is ``204``, because\n * the caller wanted it gone and it is gone.\n * @summary Delete Theme\n */\nexport const deleteTheme = async (themeId: number, options?: Parameters<typeof request>[1]): Promise<void> => {\n\n return request<void>(getDeleteThemeUrl(themeId),\n {\n ...options,\n method: 'DELETE'\n\n\n }\n);}\n\n\nexport const getReadThemeUrl = (themeId: number,) => {\n\n\n\n\n return `/admin/themes/${themeId}`\n}\n\n/**\n * One theme.\n * @summary Read Theme\n */\nexport const readTheme = async (themeId: number, options?: Parameters<typeof request>[1]): Promise<ThemeRead> => {\n\n return request<ThemeRead>(getReadThemeUrl(themeId),\n {\n ...options,\n method: 'GET'\n\n\n }\n);}\n\n\nexport const getAmendThemeUrl = (themeId: number,) => {\n\n\n\n\n return `/admin/themes/${themeId}`\n}\n\n/**\n * Amend a theme. Same uniqueness rule as creating one.\n * @summary Amend Theme\n */\nexport const amendTheme = async (themeId: number,\n themeWrite: ThemeWrite, options?: Parameters<typeof request>[1]): Promise<ThemeRead> => {\n\n const getHeaders = (h?: NonNullable<RequestInit['headers']>): Record<string, string | readonly string[]> => {\n if (!h) return {};\n if (h instanceof Headers) return Object.fromEntries(h.entries());\n if (Symbol.iterator in h) {\n return Object.fromEntries(\n Array.from(h as Iterable<Iterable<string>>, (entry) => Array.from(entry) as [string, string]),\n );\n }\n const headers: Record<string, string | readonly string[]> = {};\n for (const [name, value] of Object.entries<string | readonly string[] | undefined>(h)) {\n if (value !== undefined) headers[name] = value;\n }\n return headers;\n };\nreturn request<ThemeRead>(getAmendThemeUrl(themeId),\n {\n ...options,\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) },\n body: JSON.stringify(themeWrite)\n }\n);}\n\n\n"],"mappings":"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"}
1
+ {"version":3,"file":"admin-catalogue.js","names":[],"sources":["../../src/api/admin-catalogue/admin-catalogue.ts"],"sourcesContent":["/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Mozaik backend\n * Four products behind one API — Biblio, Médiathèques, the partner catalogues (Nextory and YouScribe) and Prof Express. Authentication is Keycloak SSO only, once, for all of them: see the **auth** section below for the browser flow and the three failure modes.\n * OpenAPI spec version: 0.3.2\n */\nimport type {\n ListThemesParams,\n ThemeListRead,\n ThemeOrderWrite,\n ThemeRead,\n ThemeWrite\n} from '../../models';\n\nimport { request } from '../../client';\n\nexport const getListThemesUrl = (params?: ListThemesParams,) => {\n const normalizedParams = new URLSearchParams();\n\n Object.entries(params || {}).forEach(([key, value]) => {\n\n if (value !== undefined) {\n normalizedParams.append(key, value === null ? 'null' : String(value))\n }\n });\n\n const stringifiedParams = normalizedParams.toString();\n\n return stringifiedParams.length > 0 ? `/admin/themes?${stringifiedParams}` : `/admin/themes`\n}\n\n/**\n * Every theme, or one pillar's, in display order.\n *\n * Not paginated. There are 27 of them, and a client that has to page through\n * a list it is about to reorder cannot reorder it.\n * @summary List Themes\n */\nexport const listThemes = async (params?: ListThemesParams, options?: Parameters<typeof request>[1]): Promise<ThemeListRead> => {\n\n return request<ThemeListRead>(getListThemesUrl(params),\n {\n ...options,\n method: 'GET'\n\n\n }\n);}\n\n\nexport const getCreateThemeUrl = () => {\n\n\n\n\n return `/admin/themes`\n}\n\n/**\n * Add a theme.\n *\n * ``409`` on a duplicate ``(pillar, title)``: that pair is unique in the\n * schema, and two themes with the same name in the same pillar are\n * indistinguishable to everyone downstream.\n *\n * With no ``sortOrder`` it goes last, which is what the legacy's form does by\n * defaulting the field to the current maximum plus one.\n * @summary Create Theme\n */\nexport const createTheme = async (themeWrite: ThemeWrite, options?: Parameters<typeof request>[1]): Promise<ThemeRead> => {\n\n const getHeaders = (h?: NonNullable<RequestInit['headers']>): Record<string, string | readonly string[]> => {\n if (!h) return {};\n if (h instanceof Headers) return Object.fromEntries(h.entries());\n if (Symbol.iterator in h) {\n return Object.fromEntries(\n Array.from(h as Iterable<Iterable<string>>, (entry) => Array.from(entry) as [string, string]),\n );\n }\n const headers: Record<string, string | readonly string[]> = {};\n for (const [name, value] of Object.entries<string | readonly string[] | undefined>(h)) {\n if (value !== undefined) headers[name] = value;\n }\n return headers;\n };\nreturn request<ThemeRead>(getCreateThemeUrl(),\n {\n ...options,\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) },\n body: JSON.stringify(themeWrite)\n }\n);}\n\n\nexport const getReorderThemesUrl = () => {\n\n\n\n\n return `/admin/themes/order`\n}\n\n/**\n * Set the display order for a set of themes, in one transaction.\n *\n * The legacy renumbers one theme per request, so dragging an item in a list of\n * thirty was thirty requests — and closing the tab halfway left the order\n * half-applied, with two themes claiming the same position and the listing\n * silently choosing between them.\n *\n * Ids not named keep their relative order after the ones that are, so a client\n * may send only what moved. An unknown id is ``404`` and nothing is written:\n * a partially applied reorder is the failure this endpoint exists to prevent.\n * @summary Reorder Themes\n */\nexport const reorderThemes = async (themeOrderWrite: ThemeOrderWrite, options?: Parameters<typeof request>[1]): Promise<void> => {\n\n const getHeaders = (h?: NonNullable<RequestInit['headers']>): Record<string, string | readonly string[]> => {\n if (!h) return {};\n if (h instanceof Headers) return Object.fromEntries(h.entries());\n if (Symbol.iterator in h) {\n return Object.fromEntries(\n Array.from(h as Iterable<Iterable<string>>, (entry) => Array.from(entry) as [string, string]),\n );\n }\n const headers: Record<string, string | readonly string[]> = {};\n for (const [name, value] of Object.entries<string | readonly string[] | undefined>(h)) {\n if (value !== undefined) headers[name] = value;\n }\n return headers;\n };\nreturn request<void>(getReorderThemesUrl(),\n {\n ...options,\n method: 'PUT',\n headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) },\n body: JSON.stringify(themeOrderWrite)\n }\n);}\n\n\nexport const getDeleteThemeUrl = (themeId: number,) => {\n\n\n\n\n return `/admin/themes/${themeId}`\n}\n\n/**\n * Remove a theme.\n *\n * ``409`` when works still carry it. The legacy deletes and leaves the\n * references dangling, which is how a catalogue ends up with items filed under\n * a theme that no longer exists and no screen that can find them.\n *\n * Idempotent otherwise: deleting something already gone is ``204``, because\n * the caller wanted it gone and it is gone.\n * @summary Delete Theme\n */\nexport const deleteTheme = async (themeId: number, options?: Parameters<typeof request>[1]): Promise<void> => {\n\n return request<void>(getDeleteThemeUrl(themeId),\n {\n ...options,\n method: 'DELETE'\n\n\n }\n);}\n\n\nexport const getReadThemeUrl = (themeId: number,) => {\n\n\n\n\n return `/admin/themes/${themeId}`\n}\n\n/**\n * One theme.\n * @summary Read Theme\n */\nexport const readTheme = async (themeId: number, options?: Parameters<typeof request>[1]): Promise<ThemeRead> => {\n\n return request<ThemeRead>(getReadThemeUrl(themeId),\n {\n ...options,\n method: 'GET'\n\n\n }\n);}\n\n\nexport const getAmendThemeUrl = (themeId: number,) => {\n\n\n\n\n return `/admin/themes/${themeId}`\n}\n\n/**\n * Amend a theme. Same uniqueness rule as creating one.\n * @summary Amend Theme\n */\nexport const amendTheme = async (themeId: number,\n themeWrite: ThemeWrite, options?: Parameters<typeof request>[1]): Promise<ThemeRead> => {\n\n const getHeaders = (h?: NonNullable<RequestInit['headers']>): Record<string, string | readonly string[]> => {\n if (!h) return {};\n if (h instanceof Headers) return Object.fromEntries(h.entries());\n if (Symbol.iterator in h) {\n return Object.fromEntries(\n Array.from(h as Iterable<Iterable<string>>, (entry) => Array.from(entry) as [string, string]),\n );\n }\n const headers: Record<string, string | readonly string[]> = {};\n for (const [name, value] of Object.entries<string | readonly string[] | undefined>(h)) {\n if (value !== undefined) headers[name] = value;\n }\n return headers;\n };\nreturn request<ThemeRead>(getAmendThemeUrl(themeId),\n {\n ...options,\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) },\n body: JSON.stringify(themeWrite)\n }\n);}\n\n\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
 
2
- import { in as LiveLeaseListRead, zn as LeaseCloseWrite } from "../index-wL_20vR8.js";
3
- import { c as request } from "../client-BDwBZYB0.js";
2
+ import { $n as LeaseCloseWrite, _n as LiveLeaseListRead } from "../chunks/index.js";
3
+ import { c as request } from "../chunks/client.js";
4
4
  //#region src/api/admin-consumption/admin-consumption.d.ts
5
5
  export declare const getListLiveLeasesUrl: () => string;
6
6
  /**
@@ -9,6 +9,11 @@ export declare const getListLiveLeasesUrl: () => string;
9
9
  * Read from the ledger rather than the Redis live set: this is a diagnostic and
10
10
  * must show the truth even when the cache is cold, and it runs once when
11
11
  * somebody opens a screen rather than on every read.
12
+ *
13
+ * Shares :func:`~app.services.entitlement.is_live` with the two seat counts.
14
+ * An earlier version of this route filtered on ``returned_at`` alone and so
15
+ * listed every lapsed-but-unreaped lease as live — which made the screen look
16
+ * exactly like a stuck reaper even when the reaper was fine.
12
17
  * @summary List Live Leases
13
18
  */
14
19
  export declare const listLiveLeases: (options?: Parameters<typeof request>[1]) => Promise<LiveLeaseListRead>;
@@ -1,5 +1,5 @@
1
1
 
2
- import { r as request } from "../client-D3kMOcIr.js";
2
+ import { r as request } from "../chunks/client.js";
3
3
  //#region src/api/admin-consumption/admin-consumption.ts
4
4
  const getListLiveLeasesUrl = () => {
5
5
  return `/admin/consumption/live`;
@@ -10,6 +10,11 @@ const getListLiveLeasesUrl = () => {
10
10
  * Read from the ledger rather than the Redis live set: this is a diagnostic and
11
11
  * must show the truth even when the cache is cold, and it runs once when
12
12
  * somebody opens a screen rather than on every read.
13
+ *
14
+ * Shares :func:`~app.services.entitlement.is_live` with the two seat counts.
15
+ * An earlier version of this route filtered on ``returned_at`` alone and so
16
+ * listed every lapsed-but-unreaped lease as live — which made the screen look
17
+ * exactly like a stuck reaper even when the reaper was fine.
13
18
  * @summary List Live Leases
14
19
  */
15
20
  const listLiveLeases = async (options) => {
@@ -1 +1 @@
1
- {"version":3,"file":"admin-consumption.js","names":[],"sources":["../../src/api/admin-consumption/admin-consumption.ts"],"sourcesContent":["/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Mozaik backend\n * Four products behind one API — Biblio, Médiathèques, the partner catalogues (Nextory and YouScribe) and Prof Express. Authentication is Keycloak SSO only, once, for all of them: see the **auth** section below for the browser flow and the three failure modes.\n * OpenAPI spec version: 0.1.0\n */\nimport type {\n LeaseCloseWrite,\n LiveLeaseListRead\n} from '../../models';\n\nimport { request } from '../../client';\n\nexport const getListLiveLeasesUrl = () => {\n\n\n\n\n return `/admin/consumption/live`\n}\n\n/**\n * What is open right now, within the caller's tenant scope.\n *\n * Read from the ledger rather than the Redis live set: this is a diagnostic and\n * must show the truth even when the cache is cold, and it runs once when\n * somebody opens a screen rather than on every read.\n * @summary List Live Leases\n */\nexport const listLiveLeases = async ( options?: Parameters<typeof request>[1]): Promise<LiveLeaseListRead> => {\n\n return request<LiveLeaseListRead>(getListLiveLeasesUrl(),\n {\n ...options,\n method: 'GET'\n\n\n }\n);}\n\n\nexport const getForceCloseLeaseUrl = (eventId: number,) => {\n\n\n\n\n return `/admin/consumption/${eventId}`\n}\n\n/**\n * Force-close somebody else's lease. The legacy's ``FermeSessionAdherent``.\n *\n * Audited, which the legacy's was not: a button that ends a stranger's reading\n * session and leaves no trace is indistinguishable from abuse of the same\n * button. Tenant-scoped like every staff action, so a lease outside the\n * caller's scope is ``404`` rather than ``403``.\n * @summary Force Close Lease\n */\nexport const forceCloseLease = async (eventId: number,\n leaseCloseWrite: LeaseCloseWrite, options?: Parameters<typeof request>[1]): Promise<void> => {\n\n const getHeaders = (h?: NonNullable<RequestInit['headers']>): Record<string, string | readonly string[]> => {\n if (!h) return {};\n if (h instanceof Headers) return Object.fromEntries(h.entries());\n if (Symbol.iterator in h) {\n return Object.fromEntries(\n Array.from(h as Iterable<Iterable<string>>, (entry) => Array.from(entry) as [string, string]),\n );\n }\n const headers: Record<string, string | readonly string[]> = {};\n for (const [name, value] of Object.entries<string | readonly string[] | undefined>(h)) {\n if (value !== undefined) headers[name] = value;\n }\n return headers;\n };\nreturn request<void>(getForceCloseLeaseUrl(eventId),\n {\n ...options,\n method: 'DELETE',\n headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) },\n body: JSON.stringify(leaseCloseWrite)\n }\n);}\n\n\n"],"mappings":";;;AAcA,MAAa,6BAA6B;CAKxC,OAAO;AACT;;;;;;;;;AAUA,MAAa,iBAAiB,OAAQ,YAAwE;CAE5G,OAAO,QAA2B,qBAAqB,GACvD;EACE,GAAG;EACH,QAAQ;CAGV,CACF;AAAE;AAGF,MAAa,yBAAyB,YAAqB;CAKzD,OAAO,sBAAsB;AAC/B;;;;;;;;;;AAWA,MAAa,kBAAkB,OAAO,SAClC,iBAAkC,YAA2D;CAE7F,MAAM,cAAc,MAAwF;EAC5G,IAAI,CAAC,GAAG,OAAO,CAAC;EAChB,IAAI,aAAa,SAAS,OAAO,OAAO,YAAY,EAAE,QAAQ,CAAC;EAC/D,IAAI,OAAO,YAAY,GACrB,OAAO,OAAO,YACZ,MAAM,KAAK,IAAkC,UAAU,MAAM,KAAK,KAAK,CAAqB,CAC9F;EAEF,MAAM,UAAsD,CAAC;EAC7D,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAgD,CAAC,GAClF,IAAI,UAAU,KAAA,GAAW,QAAQ,QAAQ;EAE3C,OAAO;CACT;CACF,OAAO,QAAc,sBAAsB,OAAO,GAChD;EACE,GAAG;EACH,QAAQ;EACR,SAAS;GAAE,gBAAgB;GAAoB,GAAG,WAAW,SAAS,OAAO;EAAE;EAC/E,MAAM,KAAK,UAAU,eAAe;CACtC,CACF;AAAE"}
1
+ {"version":3,"file":"admin-consumption.js","names":[],"sources":["../../src/api/admin-consumption/admin-consumption.ts"],"sourcesContent":["/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Mozaik backend\n * Four products behind one API — Biblio, Médiathèques, the partner catalogues (Nextory and YouScribe) and Prof Express. Authentication is Keycloak SSO only, once, for all of them: see the **auth** section below for the browser flow and the three failure modes.\n * OpenAPI spec version: 0.3.2\n */\nimport type {\n LeaseCloseWrite,\n LiveLeaseListRead\n} from '../../models';\n\nimport { request } from '../../client';\n\nexport const getListLiveLeasesUrl = () => {\n\n\n\n\n return `/admin/consumption/live`\n}\n\n/**\n * What is open right now, within the caller's tenant scope.\n *\n * Read from the ledger rather than the Redis live set: this is a diagnostic and\n * must show the truth even when the cache is cold, and it runs once when\n * somebody opens a screen rather than on every read.\n *\n * Shares :func:`~app.services.entitlement.is_live` with the two seat counts.\n * An earlier version of this route filtered on ``returned_at`` alone and so\n * listed every lapsed-but-unreaped lease as live — which made the screen look\n * exactly like a stuck reaper even when the reaper was fine.\n * @summary List Live Leases\n */\nexport const listLiveLeases = async ( options?: Parameters<typeof request>[1]): Promise<LiveLeaseListRead> => {\n\n return request<LiveLeaseListRead>(getListLiveLeasesUrl(),\n {\n ...options,\n method: 'GET'\n\n\n }\n);}\n\n\nexport const getForceCloseLeaseUrl = (eventId: number,) => {\n\n\n\n\n return `/admin/consumption/${eventId}`\n}\n\n/**\n * Force-close somebody else's lease. The legacy's ``FermeSessionAdherent``.\n *\n * Audited, which the legacy's was not: a button that ends a stranger's reading\n * session and leaves no trace is indistinguishable from abuse of the same\n * button. Tenant-scoped like every staff action, so a lease outside the\n * caller's scope is ``404`` rather than ``403``.\n * @summary Force Close Lease\n */\nexport const forceCloseLease = async (eventId: number,\n leaseCloseWrite: LeaseCloseWrite, options?: Parameters<typeof request>[1]): Promise<void> => {\n\n const getHeaders = (h?: NonNullable<RequestInit['headers']>): Record<string, string | readonly string[]> => {\n if (!h) return {};\n if (h instanceof Headers) return Object.fromEntries(h.entries());\n if (Symbol.iterator in h) {\n return Object.fromEntries(\n Array.from(h as Iterable<Iterable<string>>, (entry) => Array.from(entry) as [string, string]),\n );\n }\n const headers: Record<string, string | readonly string[]> = {};\n for (const [name, value] of Object.entries<string | readonly string[] | undefined>(h)) {\n if (value !== undefined) headers[name] = value;\n }\n return headers;\n };\nreturn request<void>(getForceCloseLeaseUrl(eventId),\n {\n ...options,\n method: 'DELETE',\n headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) },\n body: JSON.stringify(leaseCloseWrite)\n }\n);}\n\n\n"],"mappings":";;;AAcA,MAAa,6BAA6B;CAKxC,OAAO;AACT;;;;;;;;;;;;;;AAeA,MAAa,iBAAiB,OAAQ,YAAwE;CAE5G,OAAO,QAA2B,qBAAqB,GACvD;EACE,GAAG;EACH,QAAQ;CAGV,CACF;AAAE;AAGF,MAAa,yBAAyB,YAAqB;CAKzD,OAAO,sBAAsB;AAC/B;;;;;;;;;;AAWA,MAAa,kBAAkB,OAAO,SAClC,iBAAkC,YAA2D;CAE7F,MAAM,cAAc,MAAwF;EAC5G,IAAI,CAAC,GAAG,OAAO,CAAC;EAChB,IAAI,aAAa,SAAS,OAAO,OAAO,YAAY,EAAE,QAAQ,CAAC;EAC/D,IAAI,OAAO,YAAY,GACrB,OAAO,OAAO,YACZ,MAAM,KAAK,IAAkC,UAAU,MAAM,KAAK,KAAK,CAAqB,CAC9F;EAEF,MAAM,UAAsD,CAAC;EAC7D,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAgD,CAAC,GAClF,IAAI,UAAU,KAAA,GAAW,QAAQ,QAAQ;EAE3C,OAAO;CACT;CACF,OAAO,QAAc,sBAAsB,OAAO,GAChD;EACE,GAAG;EACH,QAAQ;EACR,SAAS;GAAE,gBAAgB;GAAoB,GAAG,WAAW,SAAS,OAAO;EAAE;EAC/E,MAAM,KAAK,UAAU,eAAe;CACtC,CACF;AAAE"}
@@ -1,6 +1,6 @@
1
1
 
2
- import { $ as ReadPatronLicenseParams, R as StaffLicenseRead } from "../index-wL_20vR8.js";
3
- import { c as request } from "../client-BDwBZYB0.js";
2
+ import { B as StaffLicenseRead, rt as ReadPatronLicenseParams } from "../chunks/index.js";
3
+ import { c as request } from "../chunks/client.js";
4
4
  //#region src/api/admin-licenses/admin-licenses.d.ts
5
5
  export declare const getReadPatronLicenseUrl: (params: ReadPatronLicenseParams) => string;
6
6
  /**
@@ -1,5 +1,5 @@
1
1
 
2
- import { r as request } from "../client-D3kMOcIr.js";
2
+ import { r as request } from "../chunks/client.js";
3
3
  //#region src/api/admin-licenses/admin-licenses.ts
4
4
  const getReadPatronLicenseUrl = (params) => {
5
5
  const normalizedParams = new URLSearchParams();
@@ -1 +1 @@
1
- {"version":3,"file":"admin-licenses.js","names":[],"sources":["../../src/api/admin-licenses/admin-licenses.ts"],"sourcesContent":["/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Mozaik backend\n * Four products behind one API — Biblio, Médiathèques, the partner catalogues (Nextory and YouScribe) and Prof Express. Authentication is Keycloak SSO only, once, for all of them: see the **auth** section below for the browser flow and the three failure modes.\n * OpenAPI spec version: 0.1.0\n */\nimport type {\n ReadPatronLicenseParams,\n StaffLicenseRead\n} from '../../models';\n\nimport { request } from '../../client';\n\nexport const getReadPatronLicenseUrl = (params: ReadPatronLicenseParams,) => {\n const normalizedParams = new URLSearchParams();\n\n Object.entries(params || {}).forEach(([key, value]) => {\n\n if (value !== undefined) {\n normalizedParams.append(key, value === null ? 'null' : String(value))\n }\n });\n\n const stringifiedParams = normalizedParams.toString();\n\n return stringifiedParams.length > 0 ? `/admin/nextory-licenses?${stringifiedParams}` : `/admin/nextory-licenses`\n}\n\n/**\n * Report one account's licence state. Never allocates, never repairs.\n *\n * Scoped like every other staff read: an agent who cannot see the account gets\n * ``404``, the same answer as for an address nobody holds, so the endpoint\n * cannot be used to test whether a member exists elsewhere.\n *\n * ``partner_active`` is ``None`` when the partner could not be reached. That is\n * deliberately distinct from ``False`` — \"we could not ask\" and \"they said the\n * subscription is closed\" lead a support agent to different actions, and\n * collapsing them is how a working account gets re-provisioned.\n * @summary Read Patron License\n */\nexport const readPatronLicense = async (params: ReadPatronLicenseParams, options?: Parameters<typeof request>[1]): Promise<StaffLicenseRead> => {\n\n return request<StaffLicenseRead>(getReadPatronLicenseUrl(params),\n {\n ...options,\n method: 'GET'\n\n\n }\n);}\n\n\n"],"mappings":";;;AAcA,MAAa,2BAA2B,WAAqC;CAC3E,MAAM,mBAAmB,IAAI,gBAAgB;CAE7C,OAAO,QAAQ,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,WAAW;EAErD,IAAI,UAAU,KAAA,GACZ,iBAAiB,OAAO,KAAK,UAAU,OAAO,SAAS,OAAO,KAAK,CAAC;CAExE,CAAC;CAED,MAAM,oBAAoB,iBAAiB,SAAS;CAEpD,OAAO,kBAAkB,SAAS,IAAI,2BAA2B,sBAAsB;AACzF;;;;;;;;;;;;;;AAeA,MAAa,oBAAoB,OAAO,QAAiC,YAAuE;CAE9I,OAAO,QAA0B,wBAAwB,MAAM,GAC/D;EACE,GAAG;EACH,QAAQ;CAGV,CACF;AAAE"}
1
+ {"version":3,"file":"admin-licenses.js","names":[],"sources":["../../src/api/admin-licenses/admin-licenses.ts"],"sourcesContent":["/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Mozaik backend\n * Four products behind one API — Biblio, Médiathèques, the partner catalogues (Nextory and YouScribe) and Prof Express. Authentication is Keycloak SSO only, once, for all of them: see the **auth** section below for the browser flow and the three failure modes.\n * OpenAPI spec version: 0.3.2\n */\nimport type {\n ReadPatronLicenseParams,\n StaffLicenseRead\n} from '../../models';\n\nimport { request } from '../../client';\n\nexport const getReadPatronLicenseUrl = (params: ReadPatronLicenseParams,) => {\n const normalizedParams = new URLSearchParams();\n\n Object.entries(params || {}).forEach(([key, value]) => {\n\n if (value !== undefined) {\n normalizedParams.append(key, value === null ? 'null' : String(value))\n }\n });\n\n const stringifiedParams = normalizedParams.toString();\n\n return stringifiedParams.length > 0 ? `/admin/nextory-licenses?${stringifiedParams}` : `/admin/nextory-licenses`\n}\n\n/**\n * Report one account's licence state. Never allocates, never repairs.\n *\n * Scoped like every other staff read: an agent who cannot see the account gets\n * ``404``, the same answer as for an address nobody holds, so the endpoint\n * cannot be used to test whether a member exists elsewhere.\n *\n * ``partner_active`` is ``None`` when the partner could not be reached. That is\n * deliberately distinct from ``False`` — \"we could not ask\" and \"they said the\n * subscription is closed\" lead a support agent to different actions, and\n * collapsing them is how a working account gets re-provisioned.\n * @summary Read Patron License\n */\nexport const readPatronLicense = async (params: ReadPatronLicenseParams, options?: Parameters<typeof request>[1]): Promise<StaffLicenseRead> => {\n\n return request<StaffLicenseRead>(getReadPatronLicenseUrl(params),\n {\n ...options,\n method: 'GET'\n\n\n }\n);}\n\n\n"],"mappings":";;;AAcA,MAAa,2BAA2B,WAAqC;CAC3E,MAAM,mBAAmB,IAAI,gBAAgB;CAE7C,OAAO,QAAQ,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,WAAW;EAErD,IAAI,UAAU,KAAA,GACZ,iBAAiB,OAAO,KAAK,UAAU,OAAO,SAAS,OAAO,KAAK,CAAC;CAExE,CAAC;CAED,MAAM,oBAAoB,iBAAiB,SAAS;CAEpD,OAAO,kBAAkB,SAAS,IAAI,2BAA2B,sBAAsB;AACzF;;;;;;;;;;;;;;AAeA,MAAa,oBAAoB,OAAO,QAAiC,YAAuE;CAE9I,OAAO,QAA0B,wBAAwB,MAAM,GAC/D;EACE,GAAG;EACH,QAAQ;CAGV,CACF;AAAE"}
@@ -1,12 +1,12 @@
1
1
 
2
- import { Ft as PatronDetailRead, Mt as PatronUpdateWrite, Nt as PatronPage, pn as ListPatronsParams } from "../index-wL_20vR8.js";
3
- import { c as request } from "../client-BDwBZYB0.js";
2
+ import { En as ListPatronsParams, Gt as PatronPage, Wt as PatronUpdateWrite, qt as PatronDetailRead } from "../chunks/index.js";
3
+ import { c as request } from "../chunks/client.js";
4
4
  //#region src/api/admin-patrons/admin-patrons.d.ts
5
5
  export declare const getListPatronsUrl: (params?: ListPatronsParams) => string;
6
6
  /**
7
7
  * Patrons of the libraries this caller administers.
8
8
  *
9
- * ``q`` matches login, name and email with a prefix-friendly ``ILIKE``.
9
+ * ``q`` matches login, name and email as a literal case-insensitive substring.
10
10
  * Deliberately not a full-text search: an operator looking somebody up has a
11
11
  * surname or a card number, and ranking would only make the exact match harder
12
12
  * to find.
@@ -1,5 +1,5 @@
1
1
 
2
- import { r as request } from "../client-D3kMOcIr.js";
2
+ import { r as request } from "../chunks/client.js";
3
3
  //#region src/api/admin-patrons/admin-patrons.ts
4
4
  const getListPatronsUrl = (params) => {
5
5
  const normalizedParams = new URLSearchParams();
@@ -12,7 +12,7 @@ const getListPatronsUrl = (params) => {
12
12
  /**
13
13
  * Patrons of the libraries this caller administers.
14
14
  *
15
- * ``q`` matches login, name and email with a prefix-friendly ``ILIKE``.
15
+ * ``q`` matches login, name and email as a literal case-insensitive substring.
16
16
  * Deliberately not a full-text search: an operator looking somebody up has a
17
17
  * surname or a card number, and ranking would only make the exact match harder
18
18
  * to find.
@@ -1 +1 @@
1
- {"version":3,"file":"admin-patrons.js","names":[],"sources":["../../src/api/admin-patrons/admin-patrons.ts"],"sourcesContent":["/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Mozaik backend\n * Four products behind one API — Biblio, Médiathèques, the partner catalogues (Nextory and YouScribe) and Prof Express. Authentication is Keycloak SSO only, once, for all of them: see the **auth** section below for the browser flow and the three failure modes.\n * OpenAPI spec version: 0.1.0\n */\nimport type {\n ListPatronsParams,\n PatronDetailRead,\n PatronPage,\n PatronUpdateWrite\n} from '../../models';\n\nimport { request } from '../../client';\n\nexport const getListPatronsUrl = (params?: ListPatronsParams,) => {\n const normalizedParams = new URLSearchParams();\n\n Object.entries(params || {}).forEach(([key, value]) => {\n\n if (value !== undefined) {\n normalizedParams.append(key, value === null ? 'null' : String(value))\n }\n });\n\n const stringifiedParams = normalizedParams.toString();\n\n return stringifiedParams.length > 0 ? `/admin/patrons?${stringifiedParams}` : `/admin/patrons`\n}\n\n/**\n * Patrons of the libraries this caller administers.\n *\n * ``q`` matches login, name and email with a prefix-friendly ``ILIKE``.\n * Deliberately not a full-text search: an operator looking somebody up has a\n * surname or a card number, and ranking would only make the exact match harder\n * to find.\n * @summary List Patrons\n */\nexport const listPatrons = async (params?: ListPatronsParams, options?: Parameters<typeof request>[1]): Promise<PatronPage> => {\n\n return request<PatronPage>(getListPatronsUrl(params),\n {\n ...options,\n method: 'GET'\n\n\n }\n);}\n\n\nexport const getReadPatronUrl = (patronId: number,) => {\n\n\n\n\n return `/admin/patrons/${patronId}`\n}\n\n/**\n * One patron, including the email the listing withholds.\n * @summary Read Patron\n */\nexport const readPatron = async (patronId: number, options?: Parameters<typeof request>[1]): Promise<PatronDetailRead> => {\n\n return request<PatronDetailRead>(getReadPatronUrl(patronId),\n {\n ...options,\n method: 'GET'\n\n\n }\n);}\n\n\nexport const getUpdatePatronUrl = (patronId: number,) => {\n\n\n\n\n return `/admin/patrons/${patronId}`\n}\n\n/**\n * Change what staff may change about a patron.\n *\n * Audited, because these are the changes somebody may have to account for:\n * suspending an account, raising an access level, extending a subscription past\n * what was paid for. The legacy records none of them.\n *\n * Nothing here can move a patron between tenants — see\n * :class:`~app.datamodels.admin.patrons.PatronUpdateWrite`.\n * @summary Update Patron\n */\nexport const updatePatron = async (patronId: number,\n patronUpdateWrite: PatronUpdateWrite, options?: Parameters<typeof request>[1]): Promise<PatronDetailRead> => {\n\n const getHeaders = (h?: NonNullable<RequestInit['headers']>): Record<string, string | readonly string[]> => {\n if (!h) return {};\n if (h instanceof Headers) return Object.fromEntries(h.entries());\n if (Symbol.iterator in h) {\n return Object.fromEntries(\n Array.from(h as Iterable<Iterable<string>>, (entry) => Array.from(entry) as [string, string]),\n );\n }\n const headers: Record<string, string | readonly string[]> = {};\n for (const [name, value] of Object.entries<string | readonly string[] | undefined>(h)) {\n if (value !== undefined) headers[name] = value;\n }\n return headers;\n };\nreturn request<PatronDetailRead>(getUpdatePatronUrl(patronId),\n {\n ...options,\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) },\n body: JSON.stringify(patronUpdateWrite)\n }\n);}\n\n\n"],"mappings":";;;AAgBA,MAAa,qBAAqB,WAAgC;CAChE,MAAM,mBAAmB,IAAI,gBAAgB;CAE7C,OAAO,QAAQ,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,WAAW;EAErD,IAAI,UAAU,KAAA,GACZ,iBAAiB,OAAO,KAAK,UAAU,OAAO,SAAS,OAAO,KAAK,CAAC;CAExE,CAAC;CAED,MAAM,oBAAoB,iBAAiB,SAAS;CAEpD,OAAO,kBAAkB,SAAS,IAAI,kBAAkB,sBAAsB;AAChF;;;;;;;;;;AAWA,MAAa,cAAc,OAAO,QAA4B,YAAiE;CAE7H,OAAO,QAAoB,kBAAkB,MAAM,GACnD;EACE,GAAG;EACH,QAAQ;CAGV,CACF;AAAE;AAGF,MAAa,oBAAoB,aAAsB;CAKrD,OAAO,kBAAkB;AAC3B;;;;;AAMA,MAAa,aAAa,OAAO,UAAkB,YAAuE;CAExH,OAAO,QAA0B,iBAAiB,QAAQ,GAC1D;EACE,GAAG;EACH,QAAQ;CAGV,CACF;AAAE;AAGF,MAAa,sBAAsB,aAAsB;CAKvD,OAAO,kBAAkB;AAC3B;;;;;;;;;;;;AAaA,MAAa,eAAe,OAAO,UAC/B,mBAAsC,YAAuE;CAE7G,MAAM,cAAc,MAAwF;EAC5G,IAAI,CAAC,GAAG,OAAO,CAAC;EAChB,IAAI,aAAa,SAAS,OAAO,OAAO,YAAY,EAAE,QAAQ,CAAC;EAC/D,IAAI,OAAO,YAAY,GACrB,OAAO,OAAO,YACZ,MAAM,KAAK,IAAkC,UAAU,MAAM,KAAK,KAAK,CAAqB,CAC9F;EAEF,MAAM,UAAsD,CAAC;EAC7D,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAgD,CAAC,GAClF,IAAI,UAAU,KAAA,GAAW,QAAQ,QAAQ;EAE3C,OAAO;CACT;CACF,OAAO,QAA0B,mBAAmB,QAAQ,GAC1D;EACE,GAAG;EACH,QAAQ;EACR,SAAS;GAAE,gBAAgB;GAAoB,GAAG,WAAW,SAAS,OAAO;EAAE;EAC/E,MAAM,KAAK,UAAU,iBAAiB;CACxC,CACF;AAAE"}
1
+ {"version":3,"file":"admin-patrons.js","names":[],"sources":["../../src/api/admin-patrons/admin-patrons.ts"],"sourcesContent":["/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Mozaik backend\n * Four products behind one API — Biblio, Médiathèques, the partner catalogues (Nextory and YouScribe) and Prof Express. Authentication is Keycloak SSO only, once, for all of them: see the **auth** section below for the browser flow and the three failure modes.\n * OpenAPI spec version: 0.3.2\n */\nimport type {\n ListPatronsParams,\n PatronDetailRead,\n PatronPage,\n PatronUpdateWrite\n} from '../../models';\n\nimport { request } from '../../client';\n\nexport const getListPatronsUrl = (params?: ListPatronsParams,) => {\n const normalizedParams = new URLSearchParams();\n\n Object.entries(params || {}).forEach(([key, value]) => {\n\n if (value !== undefined) {\n normalizedParams.append(key, value === null ? 'null' : String(value))\n }\n });\n\n const stringifiedParams = normalizedParams.toString();\n\n return stringifiedParams.length > 0 ? `/admin/patrons?${stringifiedParams}` : `/admin/patrons`\n}\n\n/**\n * Patrons of the libraries this caller administers.\n *\n * ``q`` matches login, name and email as a literal case-insensitive substring.\n * Deliberately not a full-text search: an operator looking somebody up has a\n * surname or a card number, and ranking would only make the exact match harder\n * to find.\n * @summary List Patrons\n */\nexport const listPatrons = async (params?: ListPatronsParams, options?: Parameters<typeof request>[1]): Promise<PatronPage> => {\n\n return request<PatronPage>(getListPatronsUrl(params),\n {\n ...options,\n method: 'GET'\n\n\n }\n);}\n\n\nexport const getReadPatronUrl = (patronId: number,) => {\n\n\n\n\n return `/admin/patrons/${patronId}`\n}\n\n/**\n * One patron, including the email the listing withholds.\n * @summary Read Patron\n */\nexport const readPatron = async (patronId: number, options?: Parameters<typeof request>[1]): Promise<PatronDetailRead> => {\n\n return request<PatronDetailRead>(getReadPatronUrl(patronId),\n {\n ...options,\n method: 'GET'\n\n\n }\n);}\n\n\nexport const getUpdatePatronUrl = (patronId: number,) => {\n\n\n\n\n return `/admin/patrons/${patronId}`\n}\n\n/**\n * Change what staff may change about a patron.\n *\n * Audited, because these are the changes somebody may have to account for:\n * suspending an account, raising an access level, extending a subscription past\n * what was paid for. The legacy records none of them.\n *\n * Nothing here can move a patron between tenants — see\n * :class:`~app.datamodels.admin.patrons.PatronUpdateWrite`.\n * @summary Update Patron\n */\nexport const updatePatron = async (patronId: number,\n patronUpdateWrite: PatronUpdateWrite, options?: Parameters<typeof request>[1]): Promise<PatronDetailRead> => {\n\n const getHeaders = (h?: NonNullable<RequestInit['headers']>): Record<string, string | readonly string[]> => {\n if (!h) return {};\n if (h instanceof Headers) return Object.fromEntries(h.entries());\n if (Symbol.iterator in h) {\n return Object.fromEntries(\n Array.from(h as Iterable<Iterable<string>>, (entry) => Array.from(entry) as [string, string]),\n );\n }\n const headers: Record<string, string | readonly string[]> = {};\n for (const [name, value] of Object.entries<string | readonly string[] | undefined>(h)) {\n if (value !== undefined) headers[name] = value;\n }\n return headers;\n };\nreturn request<PatronDetailRead>(getUpdatePatronUrl(patronId),\n {\n ...options,\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) },\n body: JSON.stringify(patronUpdateWrite)\n }\n);}\n\n\n"],"mappings":";;;AAgBA,MAAa,qBAAqB,WAAgC;CAChE,MAAM,mBAAmB,IAAI,gBAAgB;CAE7C,OAAO,QAAQ,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,WAAW;EAErD,IAAI,UAAU,KAAA,GACZ,iBAAiB,OAAO,KAAK,UAAU,OAAO,SAAS,OAAO,KAAK,CAAC;CAExE,CAAC;CAED,MAAM,oBAAoB,iBAAiB,SAAS;CAEpD,OAAO,kBAAkB,SAAS,IAAI,kBAAkB,sBAAsB;AAChF;;;;;;;;;;AAWA,MAAa,cAAc,OAAO,QAA4B,YAAiE;CAE7H,OAAO,QAAoB,kBAAkB,MAAM,GACnD;EACE,GAAG;EACH,QAAQ;CAGV,CACF;AAAE;AAGF,MAAa,oBAAoB,aAAsB;CAKrD,OAAO,kBAAkB;AAC3B;;;;;AAMA,MAAa,aAAa,OAAO,UAAkB,YAAuE;CAExH,OAAO,QAA0B,iBAAiB,QAAQ,GAC1D;EACE,GAAG;EACH,QAAQ;CAGV,CACF;AAAE;AAGF,MAAa,sBAAsB,aAAsB;CAKvD,OAAO,kBAAkB;AAC3B;;;;;;;;;;;;AAaA,MAAa,eAAe,OAAO,UAC/B,mBAAsC,YAAuE;CAE7G,MAAM,cAAc,MAAwF;EAC5G,IAAI,CAAC,GAAG,OAAO,CAAC;EAChB,IAAI,aAAa,SAAS,OAAO,OAAO,YAAY,EAAE,QAAQ,CAAC;EAC/D,IAAI,OAAO,YAAY,GACrB,OAAO,OAAO,YACZ,MAAM,KAAK,IAAkC,UAAU,MAAM,KAAK,KAAK,CAAqB,CAC9F;EAEF,MAAM,UAAsD,CAAC;EAC7D,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAgD,CAAC,GAClF,IAAI,UAAU,KAAA,GAAW,QAAQ,QAAQ;EAE3C,OAAO;CACT;CACF,OAAO,QAA0B,mBAAmB,QAAQ,GAC1D;EACE,GAAG;EACH,QAAQ;EACR,SAAS;GAAE,gBAAgB;GAAoB,GAAG,WAAW,SAAS,OAAO;EAAE;EAC/E,MAAM,KAAK,UAAU,iBAAiB;CACxC,CACF;AAAE"}
@@ -1,6 +1,6 @@
1
1
 
2
- import { J as ReviewModerationRead, Jt as ModerationWrite, cn as ListReviewsParams, q as ReviewPage } from "../index-wL_20vR8.js";
3
- import { c as request } from "../client-BDwBZYB0.js";
2
+ import { Q as ReviewModerationRead, Z as ReviewPage, sn as ModerationWrite, xn as ListReviewsParams } from "../chunks/index.js";
3
+ import { c as request } from "../chunks/client.js";
4
4
  //#region src/api/admin-reviews/admin-reviews.d.ts
5
5
  export declare const getListReviewsUrl: (params?: ListReviewsParams) => string;
6
6
  /**
@@ -1,5 +1,5 @@
1
1
 
2
- import { r as request } from "../client-D3kMOcIr.js";
2
+ import { r as request } from "../chunks/client.js";
3
3
  //#region src/api/admin-reviews/admin-reviews.ts
4
4
  const getListReviewsUrl = (params) => {
5
5
  const normalizedParams = new URLSearchParams();
@@ -1 +1 @@
1
- {"version":3,"file":"admin-reviews.js","names":[],"sources":["../../src/api/admin-reviews/admin-reviews.ts"],"sourcesContent":["/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Mozaik backend\n * Four products behind one API — Biblio, Médiathèques, the partner catalogues (Nextory and YouScribe) and Prof Express. Authentication is Keycloak SSO only, once, for all of them: see the **auth** section below for the browser flow and the three failure modes.\n * OpenAPI spec version: 0.1.0\n */\nimport type {\n ListReviewsParams,\n ModerationWrite,\n ReviewModerationRead,\n ReviewPage\n} from '../../models';\n\nimport { request } from '../../client';\n\nexport const getListReviewsUrl = (params?: ListReviewsParams,) => {\n const normalizedParams = new URLSearchParams();\n\n Object.entries(params || {}).forEach(([key, value]) => {\n\n if (value !== undefined) {\n normalizedParams.append(key, value === null ? 'null' : String(value))\n }\n });\n\n const stringifiedParams = normalizedParams.toString();\n\n return stringifiedParams.length > 0 ? `/admin/reviews?${stringifiedParams}` : `/admin/reviews`\n}\n\n/**\n * Reviews awaiting a decision, oldest first.\n *\n * Oldest first on purpose: a moderation queue sorted newest-first starves its\n * tail, and the review that has waited longest is the one whose author is\n * wondering what happened.\n * @summary List Reviews\n */\nexport const listReviews = async (params?: ListReviewsParams, options?: Parameters<typeof request>[1]): Promise<ReviewPage> => {\n\n return request<ReviewPage>(getListReviewsUrl(params),\n {\n ...options,\n method: 'GET'\n\n\n }\n);}\n\n\nexport const getModerateReviewUrl = (reviewId: number,) => {\n\n\n\n\n return `/admin/reviews/${reviewId}/moderation`\n}\n\n/**\n * Publish or reject one review.\n *\n * ``PUT`` on a moderation sub-resource rather than ``POST /approve`` and\n * ``POST /reject``: the outcome is a state the review is *in*, not two different\n * events, and a repeated decision must be harmless.\n *\n * Rejecting requires a reason. The author is owed one, and the moderator is the\n * only person who has it — the audit row is where it survives after the queue\n * has moved on.\n * @summary Moderate Review\n */\nexport const moderateReview = async (reviewId: number,\n moderationWrite: ModerationWrite, options?: Parameters<typeof request>[1]): Promise<ReviewModerationRead> => {\n\n const getHeaders = (h?: NonNullable<RequestInit['headers']>): Record<string, string | readonly string[]> => {\n if (!h) return {};\n if (h instanceof Headers) return Object.fromEntries(h.entries());\n if (Symbol.iterator in h) {\n return Object.fromEntries(\n Array.from(h as Iterable<Iterable<string>>, (entry) => Array.from(entry) as [string, string]),\n );\n }\n const headers: Record<string, string | readonly string[]> = {};\n for (const [name, value] of Object.entries<string | readonly string[] | undefined>(h)) {\n if (value !== undefined) headers[name] = value;\n }\n return headers;\n };\nreturn request<ReviewModerationRead>(getModerateReviewUrl(reviewId),\n {\n ...options,\n method: 'PUT',\n headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) },\n body: JSON.stringify(moderationWrite)\n }\n);}\n\n\n"],"mappings":";;;AAgBA,MAAa,qBAAqB,WAAgC;CAChE,MAAM,mBAAmB,IAAI,gBAAgB;CAE7C,OAAO,QAAQ,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,WAAW;EAErD,IAAI,UAAU,KAAA,GACZ,iBAAiB,OAAO,KAAK,UAAU,OAAO,SAAS,OAAO,KAAK,CAAC;CAExE,CAAC;CAED,MAAM,oBAAoB,iBAAiB,SAAS;CAEpD,OAAO,kBAAkB,SAAS,IAAI,kBAAkB,sBAAsB;AAChF;;;;;;;;;AAUA,MAAa,cAAc,OAAO,QAA4B,YAAiE;CAE7H,OAAO,QAAoB,kBAAkB,MAAM,GACnD;EACE,GAAG;EACH,QAAQ;CAGV,CACF;AAAE;AAGF,MAAa,wBAAwB,aAAsB;CAKzD,OAAO,kBAAkB,SAAS;AACpC;;;;;;;;;;;;;AAcA,MAAa,iBAAiB,OAAO,UACjC,iBAAkC,YAA2E;CAE7G,MAAM,cAAc,MAAwF;EAC5G,IAAI,CAAC,GAAG,OAAO,CAAC;EAChB,IAAI,aAAa,SAAS,OAAO,OAAO,YAAY,EAAE,QAAQ,CAAC;EAC/D,IAAI,OAAO,YAAY,GACrB,OAAO,OAAO,YACZ,MAAM,KAAK,IAAkC,UAAU,MAAM,KAAK,KAAK,CAAqB,CAC9F;EAEF,MAAM,UAAsD,CAAC;EAC7D,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAgD,CAAC,GAClF,IAAI,UAAU,KAAA,GAAW,QAAQ,QAAQ;EAE3C,OAAO;CACT;CACF,OAAO,QAA8B,qBAAqB,QAAQ,GAChE;EACE,GAAG;EACH,QAAQ;EACR,SAAS;GAAE,gBAAgB;GAAoB,GAAG,WAAW,SAAS,OAAO;EAAE;EAC/E,MAAM,KAAK,UAAU,eAAe;CACtC,CACF;AAAE"}
1
+ {"version":3,"file":"admin-reviews.js","names":[],"sources":["../../src/api/admin-reviews/admin-reviews.ts"],"sourcesContent":["/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Mozaik backend\n * Four products behind one API — Biblio, Médiathèques, the partner catalogues (Nextory and YouScribe) and Prof Express. Authentication is Keycloak SSO only, once, for all of them: see the **auth** section below for the browser flow and the three failure modes.\n * OpenAPI spec version: 0.3.2\n */\nimport type {\n ListReviewsParams,\n ModerationWrite,\n ReviewModerationRead,\n ReviewPage\n} from '../../models';\n\nimport { request } from '../../client';\n\nexport const getListReviewsUrl = (params?: ListReviewsParams,) => {\n const normalizedParams = new URLSearchParams();\n\n Object.entries(params || {}).forEach(([key, value]) => {\n\n if (value !== undefined) {\n normalizedParams.append(key, value === null ? 'null' : String(value))\n }\n });\n\n const stringifiedParams = normalizedParams.toString();\n\n return stringifiedParams.length > 0 ? `/admin/reviews?${stringifiedParams}` : `/admin/reviews`\n}\n\n/**\n * Reviews awaiting a decision, oldest first.\n *\n * Oldest first on purpose: a moderation queue sorted newest-first starves its\n * tail, and the review that has waited longest is the one whose author is\n * wondering what happened.\n * @summary List Reviews\n */\nexport const listReviews = async (params?: ListReviewsParams, options?: Parameters<typeof request>[1]): Promise<ReviewPage> => {\n\n return request<ReviewPage>(getListReviewsUrl(params),\n {\n ...options,\n method: 'GET'\n\n\n }\n);}\n\n\nexport const getModerateReviewUrl = (reviewId: number,) => {\n\n\n\n\n return `/admin/reviews/${reviewId}/moderation`\n}\n\n/**\n * Publish or reject one review.\n *\n * ``PUT`` on a moderation sub-resource rather than ``POST /approve`` and\n * ``POST /reject``: the outcome is a state the review is *in*, not two different\n * events, and a repeated decision must be harmless.\n *\n * Rejecting requires a reason. The author is owed one, and the moderator is the\n * only person who has it — the audit row is where it survives after the queue\n * has moved on.\n * @summary Moderate Review\n */\nexport const moderateReview = async (reviewId: number,\n moderationWrite: ModerationWrite, options?: Parameters<typeof request>[1]): Promise<ReviewModerationRead> => {\n\n const getHeaders = (h?: NonNullable<RequestInit['headers']>): Record<string, string | readonly string[]> => {\n if (!h) return {};\n if (h instanceof Headers) return Object.fromEntries(h.entries());\n if (Symbol.iterator in h) {\n return Object.fromEntries(\n Array.from(h as Iterable<Iterable<string>>, (entry) => Array.from(entry) as [string, string]),\n );\n }\n const headers: Record<string, string | readonly string[]> = {};\n for (const [name, value] of Object.entries<string | readonly string[] | undefined>(h)) {\n if (value !== undefined) headers[name] = value;\n }\n return headers;\n };\nreturn request<ReviewModerationRead>(getModerateReviewUrl(reviewId),\n {\n ...options,\n method: 'PUT',\n headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) },\n body: JSON.stringify(moderationWrite)\n }\n);}\n\n\n"],"mappings":";;;AAgBA,MAAa,qBAAqB,WAAgC;CAChE,MAAM,mBAAmB,IAAI,gBAAgB;CAE7C,OAAO,QAAQ,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,WAAW;EAErD,IAAI,UAAU,KAAA,GACZ,iBAAiB,OAAO,KAAK,UAAU,OAAO,SAAS,OAAO,KAAK,CAAC;CAExE,CAAC;CAED,MAAM,oBAAoB,iBAAiB,SAAS;CAEpD,OAAO,kBAAkB,SAAS,IAAI,kBAAkB,sBAAsB;AAChF;;;;;;;;;AAUA,MAAa,cAAc,OAAO,QAA4B,YAAiE;CAE7H,OAAO,QAAoB,kBAAkB,MAAM,GACnD;EACE,GAAG;EACH,QAAQ;CAGV,CACF;AAAE;AAGF,MAAa,wBAAwB,aAAsB;CAKzD,OAAO,kBAAkB,SAAS;AACpC;;;;;;;;;;;;;AAcA,MAAa,iBAAiB,OAAO,UACjC,iBAAkC,YAA2E;CAE7G,MAAM,cAAc,MAAwF;EAC5G,IAAI,CAAC,GAAG,OAAO,CAAC;EAChB,IAAI,aAAa,SAAS,OAAO,OAAO,YAAY,EAAE,QAAQ,CAAC;EAC/D,IAAI,OAAO,YAAY,GACrB,OAAO,OAAO,YACZ,MAAM,KAAK,IAAkC,UAAU,MAAM,KAAK,KAAK,CAAqB,CAC9F;EAEF,MAAM,UAAsD,CAAC;EAC7D,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAgD,CAAC,GAClF,IAAI,UAAU,KAAA,GAAW,QAAQ,QAAQ;EAE3C,OAAO;CACT;CACF,OAAO,QAA8B,qBAAqB,QAAQ,GAChE;EACE,GAAG;EACH,QAAQ;EACR,SAAS;GAAE,gBAAgB;GAAoB,GAAG,WAAW,SAAS,OAAO;EAAE;EAC/E,MAAM,KAAK,UAAU,eAAe;CACtC,CACF;AAAE"}
@@ -1,10 +1,10 @@
1
1
 
2
- import { _t as ProductGrantWrite, vt as ProductGrantListRead } from "../index-wL_20vR8.js";
3
- import { c as request } from "../client-BDwBZYB0.js";
2
+ import { Ct as ProductGrantListRead, St as ProductGrantWrite } from "../chunks/index.js";
3
+ import { c as request } from "../chunks/client.js";
4
4
  //#region src/api/admin-roles/admin-roles.d.ts
5
5
  export declare const getListGrantsUrl: (userId: number) => string;
6
6
  /**
7
- * Everything this account is, across products and organisations.
7
+ * This account's grants within the caller's organisation scope.
8
8
  *
9
9
  * Readable by SUPPORT as well as ADMIN: knowing what somebody is, is part of
10
10
  * helping them. Changing it is not.
@@ -1,11 +1,11 @@
1
1
 
2
- import { r as request } from "../client-D3kMOcIr.js";
2
+ import { r as request } from "../chunks/client.js";
3
3
  //#region src/api/admin-roles/admin-roles.ts
4
4
  const getListGrantsUrl = (userId) => {
5
5
  return `/admin/users/${userId}/product-roles`;
6
6
  };
7
7
  /**
8
- * Everything this account is, across products and organisations.
8
+ * This account's grants within the caller's organisation scope.
9
9
  *
10
10
  * Readable by SUPPORT as well as ADMIN: knowing what somebody is, is part of
11
11
  * helping them. Changing it is not.
@@ -1 +1 @@
1
- {"version":3,"file":"admin-roles.js","names":[],"sources":["../../src/api/admin-roles/admin-roles.ts"],"sourcesContent":["/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Mozaik backend\n * Four products behind one API — Biblio, Médiathèques, the partner catalogues (Nextory and YouScribe) and Prof Express. Authentication is Keycloak SSO only, once, for all of them: see the **auth** section below for the browser flow and the three failure modes.\n * OpenAPI spec version: 0.1.0\n */\nimport type {\n ProductGrantListRead,\n ProductGrantWrite\n} from '../../models';\n\nimport { request } from '../../client';\n\nexport const getListGrantsUrl = (userId: number,) => {\n\n\n\n\n return `/admin/users/${userId}/product-roles`\n}\n\n/**\n * Everything this account is, across products and organisations.\n *\n * Readable by SUPPORT as well as ADMIN: knowing what somebody is, is part of\n * helping them. Changing it is not.\n * @summary List Grants\n */\nexport const listGrants = async (userId: number, options?: Parameters<typeof request>[1]): Promise<ProductGrantListRead> => {\n\n return request<ProductGrantListRead>(getListGrantsUrl(userId),\n {\n ...options,\n method: 'GET'\n\n\n }\n);}\n\n\nexport const getReplaceGrantsUrl = (userId: number,) => {\n\n\n\n\n return `/admin/users/${userId}/product-roles`\n}\n\n/**\n * Replace this account's roles for one product at one organisation.\n *\n * ``PUT`` because it is idempotent and the body is the whole desired state for\n * that ``(tenant, product)`` slice — sending it twice leaves the same grants.\n *\n * Two scope checks, and both are needed. The *subject* must be an account the\n * operator reaches, and the ``tenantId`` being granted at must be one they\n * reach too: without the second, an operator could grant a remit at an\n * organisation they have no authority over, which is how a scoped\n * administrator becomes an unscoped one.\n *\n * ``422`` for a role the product does not recognise —\n * :data:`~app.models.enums.PRODUCT_ROLES` decides, so ``student`` for biblio is\n * refused rather than stored and puzzled over later.\n * @summary Replace Grants\n */\nexport const replaceGrants = async (userId: number,\n productGrantWrite: ProductGrantWrite, options?: Parameters<typeof request>[1]): Promise<ProductGrantListRead> => {\n\n const getHeaders = (h?: NonNullable<RequestInit['headers']>): Record<string, string | readonly string[]> => {\n if (!h) return {};\n if (h instanceof Headers) return Object.fromEntries(h.entries());\n if (Symbol.iterator in h) {\n return Object.fromEntries(\n Array.from(h as Iterable<Iterable<string>>, (entry) => Array.from(entry) as [string, string]),\n );\n }\n const headers: Record<string, string | readonly string[]> = {};\n for (const [name, value] of Object.entries<string | readonly string[] | undefined>(h)) {\n if (value !== undefined) headers[name] = value;\n }\n return headers;\n };\nreturn request<ProductGrantListRead>(getReplaceGrantsUrl(userId),\n {\n ...options,\n method: 'PUT',\n headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) },\n body: JSON.stringify(productGrantWrite)\n }\n);}\n\n\n"],"mappings":";;;AAcA,MAAa,oBAAoB,WAAoB;CAKnD,OAAO,gBAAgB,OAAO;AAChC;;;;;;;;AASA,MAAa,aAAa,OAAO,QAAgB,YAA2E;CAE1H,OAAO,QAA8B,iBAAiB,MAAM,GAC5D;EACE,GAAG;EACH,QAAQ;CAGV,CACF;AAAE;AAGF,MAAa,uBAAuB,WAAoB;CAKtD,OAAO,gBAAgB,OAAO;AAChC;;;;;;;;;;;;;;;;;;AAmBA,MAAa,gBAAgB,OAAO,QAChC,mBAAsC,YAA2E;CAEjH,MAAM,cAAc,MAAwF;EAC5G,IAAI,CAAC,GAAG,OAAO,CAAC;EAChB,IAAI,aAAa,SAAS,OAAO,OAAO,YAAY,EAAE,QAAQ,CAAC;EAC/D,IAAI,OAAO,YAAY,GACrB,OAAO,OAAO,YACZ,MAAM,KAAK,IAAkC,UAAU,MAAM,KAAK,KAAK,CAAqB,CAC9F;EAEF,MAAM,UAAsD,CAAC;EAC7D,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAgD,CAAC,GAClF,IAAI,UAAU,KAAA,GAAW,QAAQ,QAAQ;EAE3C,OAAO;CACT;CACF,OAAO,QAA8B,oBAAoB,MAAM,GAC7D;EACE,GAAG;EACH,QAAQ;EACR,SAAS;GAAE,gBAAgB;GAAoB,GAAG,WAAW,SAAS,OAAO;EAAE;EAC/E,MAAM,KAAK,UAAU,iBAAiB;CACxC,CACF;AAAE"}
1
+ {"version":3,"file":"admin-roles.js","names":[],"sources":["../../src/api/admin-roles/admin-roles.ts"],"sourcesContent":["/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Mozaik backend\n * Four products behind one API — Biblio, Médiathèques, the partner catalogues (Nextory and YouScribe) and Prof Express. Authentication is Keycloak SSO only, once, for all of them: see the **auth** section below for the browser flow and the three failure modes.\n * OpenAPI spec version: 0.3.2\n */\nimport type {\n ProductGrantListRead,\n ProductGrantWrite\n} from '../../models';\n\nimport { request } from '../../client';\n\nexport const getListGrantsUrl = (userId: number,) => {\n\n\n\n\n return `/admin/users/${userId}/product-roles`\n}\n\n/**\n * This account's grants within the caller's organisation scope.\n *\n * Readable by SUPPORT as well as ADMIN: knowing what somebody is, is part of\n * helping them. Changing it is not.\n * @summary List Grants\n */\nexport const listGrants = async (userId: number, options?: Parameters<typeof request>[1]): Promise<ProductGrantListRead> => {\n\n return request<ProductGrantListRead>(getListGrantsUrl(userId),\n {\n ...options,\n method: 'GET'\n\n\n }\n);}\n\n\nexport const getReplaceGrantsUrl = (userId: number,) => {\n\n\n\n\n return `/admin/users/${userId}/product-roles`\n}\n\n/**\n * Replace this account's roles for one product at one organisation.\n *\n * ``PUT`` because it is idempotent and the body is the whole desired state for\n * that ``(tenant, product)`` slice — sending it twice leaves the same grants.\n *\n * Two scope checks, and both are needed. The *subject* must be an account the\n * operator reaches, and the ``tenantId`` being granted at must be one they\n * reach too: without the second, an operator could grant a remit at an\n * organisation they have no authority over, which is how a scoped\n * administrator becomes an unscoped one.\n *\n * ``422`` for a role the product does not recognise —\n * :data:`~app.models.enums.PRODUCT_ROLES` decides, so ``student`` for biblio is\n * refused rather than stored and puzzled over later.\n * @summary Replace Grants\n */\nexport const replaceGrants = async (userId: number,\n productGrantWrite: ProductGrantWrite, options?: Parameters<typeof request>[1]): Promise<ProductGrantListRead> => {\n\n const getHeaders = (h?: NonNullable<RequestInit['headers']>): Record<string, string | readonly string[]> => {\n if (!h) return {};\n if (h instanceof Headers) return Object.fromEntries(h.entries());\n if (Symbol.iterator in h) {\n return Object.fromEntries(\n Array.from(h as Iterable<Iterable<string>>, (entry) => Array.from(entry) as [string, string]),\n );\n }\n const headers: Record<string, string | readonly string[]> = {};\n for (const [name, value] of Object.entries<string | readonly string[] | undefined>(h)) {\n if (value !== undefined) headers[name] = value;\n }\n return headers;\n };\nreturn request<ProductGrantListRead>(getReplaceGrantsUrl(userId),\n {\n ...options,\n method: 'PUT',\n headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) },\n body: JSON.stringify(productGrantWrite)\n }\n);}\n\n\n"],"mappings":";;;AAcA,MAAa,oBAAoB,WAAoB;CAKnD,OAAO,gBAAgB,OAAO;AAChC;;;;;;;;AASA,MAAa,aAAa,OAAO,QAAgB,YAA2E;CAE1H,OAAO,QAA8B,iBAAiB,MAAM,GAC5D;EACE,GAAG;EACH,QAAQ;CAGV,CACF;AAAE;AAGF,MAAa,uBAAuB,WAAoB;CAKtD,OAAO,gBAAgB,OAAO;AAChC;;;;;;;;;;;;;;;;;;AAmBA,MAAa,gBAAgB,OAAO,QAChC,mBAAsC,YAA2E;CAEjH,MAAM,cAAc,MAAwF;EAC5G,IAAI,CAAC,GAAG,OAAO,CAAC;EAChB,IAAI,aAAa,SAAS,OAAO,OAAO,YAAY,EAAE,QAAQ,CAAC;EAC/D,IAAI,OAAO,YAAY,GACrB,OAAO,OAAO,YACZ,MAAM,KAAK,IAAkC,UAAU,MAAM,KAAK,KAAK,CAAqB,CAC9F;EAEF,MAAM,UAAsD,CAAC;EAC7D,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAgD,CAAC,GAClF,IAAI,UAAU,KAAA,GAAW,QAAQ,QAAQ;EAE3C,OAAO;CACT;CACF,OAAO,QAA8B,oBAAoB,MAAM,GAC7D;EACE,GAAG;EACH,QAAQ;EACR,SAAS;GAAE,gBAAgB;GAAoB,GAAG,WAAW,SAAS,OAAO;EAAE;EAC/E,MAAM,KAAK,UAAU,iBAAiB;CACxC,CACF;AAAE"}
@@ -1,6 +1,6 @@
1
1
 
2
- import { Cr as DemoUserWrite, Wn as ImpersonationWrite, j as SupportSessionRead, wr as DemoUserRead } from "../index-wL_20vR8.js";
3
- import { c as request } from "../client-BDwBZYB0.js";
2
+ import { N as SupportSessionRead, ir as ImpersonationWrite, ni as DemoUserWrite, ri as DemoUserRead } from "../chunks/index.js";
3
+ import { c as request } from "../chunks/client.js";
4
4
  //#region src/api/admin-support/admin-support.d.ts
5
5
  export declare const getCreateDemoUserUrl: () => string;
6
6
  /**
@@ -1,5 +1,5 @@
1
1
 
2
- import { r as request } from "../client-D3kMOcIr.js";
2
+ import { r as request } from "../chunks/client.js";
3
3
  //#region src/api/admin-support/admin-support.ts
4
4
  const getCreateDemoUserUrl = () => {
5
5
  return `/admin/demo-users`;