@saasicat/ui-vue 0.24.2 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +32 -8
  2. package/dist/{catalog-DJcfm5N2.d.cts → catalog-Dch1Ryw0.d.cts} +353 -69
  3. package/dist/{catalog-DJcfm5N2.d.ts → catalog-Dch1Ryw0.d.ts} +353 -69
  4. package/dist/{chunk-F7NRM6KI.js → chunk-BPF2BMCQ.js} +748 -24
  5. package/dist/{chunk-D74FFCCP.js → chunk-NEXTZZRQ.js} +95 -177
  6. package/dist/{chunk-TQQMKWN6.js → chunk-O3J3ITF2.js} +119 -7
  7. package/dist/client/index.cjs +758 -83
  8. package/dist/client/index.d.cts +414 -9
  9. package/dist/client/index.d.ts +414 -9
  10. package/dist/client/index.js +84 -12
  11. package/dist/index.cjs +1325 -294
  12. package/dist/index.d.cts +205 -15
  13. package/dist/index.d.ts +205 -15
  14. package/dist/index.js +566 -240
  15. package/dist/quasar/index.cjs +574 -62
  16. package/dist/quasar/index.d.cts +61 -6
  17. package/dist/quasar/index.d.ts +61 -6
  18. package/dist/quasar/index.js +83 -19
  19. package/dist/{use-sa-theme-OUBlaqgl.d.ts → resource-registry-D7Xjs-5f.d.cts} +157 -7
  20. package/dist/{use-sa-theme-B5t7oXph.d.cts → resource-registry-TaR7rtq0.d.ts} +157 -7
  21. package/package.json +4 -3
  22. package/src/client/admin-error.ts +552 -0
  23. package/src/client/admin-resource-client.ts +9 -12
  24. package/src/client/batch-column-fetcher.ts +2 -2
  25. package/src/client/boot-loader.ts +12 -1
  26. package/src/client/http/axios-http-client.ts +383 -0
  27. package/src/client/http/fetch-http-client.ts +89 -0
  28. package/src/client/http/index.ts +6 -0
  29. package/src/client/http-json.ts +82 -16
  30. package/src/client/i18n/messages/discovery.ts +0 -6
  31. package/src/client/i18n/messages/errors.ts +44 -0
  32. package/src/client/i18n/messages/marketing.ts +0 -6
  33. package/src/client/i18n/messages/promos.ts +0 -2
  34. package/src/client/i18n/messages/shell.ts +12 -0
  35. package/src/client/i18n/messages.ts +3 -0
  36. package/src/client/index.ts +6 -0
  37. package/src/client/manifest-loader.ts +65 -15
  38. package/src/client/resources/audit.resource.ts +25 -0
  39. package/src/client/resources/define-resource.ts +166 -0
  40. package/src/client/resources/index.ts +42 -0
  41. package/src/client/resources/list-resource.ts +176 -0
  42. package/src/client/resources/plans.resource.ts +169 -0
  43. package/src/client/resources/resource-request.ts +117 -0
  44. package/src/client/resources/tenants.resource.ts +27 -0
  45. package/src/client/types.ts +10 -3
  46. package/src/components/BundleVersionPublishDialog.vue +1 -1
  47. package/src/components/MarketingPromotionsTab.vue +263 -241
  48. package/src/components/ThemeSwitcher.vue +85 -0
  49. package/src/components/admin-page/AdminAccordion.vue +125 -0
  50. package/src/components/bundle-editor/BundleCreatePanel.vue +1 -1
  51. package/src/components/dialogs/PromoCodeDialogFields.vue +20 -20
  52. package/src/components/plan/PlanCycleToggle.vue +1 -1
  53. package/src/components/plan-detail/PlanVersionsPanel.vue +5 -2
  54. package/src/index.ts +5 -0
  55. package/src/pages-standard/AdminLayout.vue +57 -0
  56. package/src/pages-standard/AdminManifestErrorPage.vue +4 -3
  57. package/src/pages-standard/BundlesPage.vue +5 -35
  58. package/src/pages-standard/DiscoveryPage.vue +8 -1
  59. package/src/pages-standard/MarketingCatalogPage.vue +14 -3
  60. package/src/pages-standard/SuperAdminLoginPage.vue +23 -2
  61. package/src/pages-standard/SuperAdminSetupWizard.vue +37 -3
  62. package/src/pages-standard/bundles-page/BundleAccordionList.vue +30 -32
  63. package/src/pages-standard/discovery-page/DiscoveryFeatureCard.vue +80 -88
  64. package/src/pages-standard/discovery-page/DiscoveryQuotaCard.vue +73 -79
  65. package/src/pages-standard/marketing-catalog/MarketingCatalogAdmin.vue +44 -6
  66. package/src/pages-standard/marketing-catalog/MarketingCatalogPreview.vue +8 -2
  67. package/src/pages-tenant/PackageSnapshotPanel.vue +31 -2
  68. package/src/quasar/confirm.ts +83 -0
  69. package/src/quasar/create-super-admin-app.ts +84 -10
  70. package/src/quasar/dark-bridge.ts +47 -10
  71. package/src/quasar/index.ts +1 -0
  72. package/src/ui/theme/components/accordion.css +118 -0
  73. package/src/ui/theme/index.css +1 -0
  74. package/src/vue/create-admin-routes.ts +9 -1
  75. package/src/vue/platform-loaders.ts +12 -2
  76. package/src/vue/resource-registry.ts +211 -0
  77. package/src/vue/super-admin-context.ts +20 -5
  78. package/src/vue/ui-confirm.ts +76 -0
  79. package/src/vue/use-api-list.ts +50 -45
  80. package/src/vue/use-async-action.ts +170 -0
  81. package/src/vue/use-async-data.ts +83 -0
  82. package/src/vue/use-bundles.ts +34 -7
  83. package/src/vue/use-catalog-entries.ts +37 -12
  84. package/src/vue/use-discovery.ts +27 -6
  85. package/src/vue/use-marketing-projections.ts +29 -7
  86. package/src/vue/use-plans.ts +36 -8
  87. package/src/vue/use-promotions.ts +27 -7
  88. package/src/vue/use-resource-list.ts +272 -0
  89. package/src/vue/use-sa-theme.ts +34 -5
  90. package/src/vue/use-tenant-subscription-bundles.ts +22 -3
@@ -1,19 +1,406 @@
1
- import { H as HttpClient, K as KvStore, S as SaLocale, w as SaNavMessages } from '../catalog-DJcfm5N2.js';
2
- export { f as ActionDefNotInManifestError, A as ActionHandler, e as ActionRegistry, D as DEFAULT_SA_LOCALE, g as HttpResponse, M as MessageTree, h as MissingHandlerError, P as PartialMessages, R as ResolvedAction, i as SA_INTL_LOCALES, j as SA_LOCALES, k as SA_LOCALE_LABELS, l as SA_MESSAGES, m as SaBuiltinLocale, n as SaCatalog, o as SaCatalogOptions, c as SaLocaleDefinition, b as SaLocaleOption, a as SaMessages, d as SaMessagesOverrides, T as TranslationOf, p as createSaCatalog, q as defaultHttpClient, r as defaultKvStore, s as defineMessages, t as isSaBuiltinLocale, u as mergeMessages, v as resolveMessages } from '../catalog-DJcfm5N2.js';
1
+ import { H as HttpClient, a as SaMessages, K as KvStore, S as SaLocale, a9 as SaNavMessages } from '../catalog-Dch1Ryw0.js';
2
+ export { i as ActionDefNotInManifestError, A as ActionHandler, h as ActionRegistry, j as AuditListFilter, B as Bound, D as DEFAULT_SA_LOCALE, k as DefineResourceOptions, l as HttpResponse, L as LIST_FIRST_PAGE, m as LIST_PAGE_SIZE_DEFAULT, n as LIST_PAGE_SIZE_MAX, o as LIST_PAGINATION_PARAMS, q as ListFilterOf, r as ListOp, s as ListQuery, M as MessageTree, t as MissingHandlerError, u as PartialMessages, P as PlatformResources, v as PublishPlanVersionOptions, w as ResolvedAction, f as ResourceContext, R as ResourceDef, g as ResourceListPage, x as ResourceOp, e as ResourceOps, y as SA_INTL_LOCALES, z as SA_LOCALES, C as SA_LOCALE_LABELS, E as SA_MESSAGES, F as SaBuiltinLocale, G as SaCatalog, I as SaCatalogOptions, c as SaLocaleDefinition, b as SaLocaleOption, d as SaMessagesOverrides, T as TenantsListFilter, J as TranslationOf, N as auditResource, O as bindResource, Q as clampListPage, U as clampListPageSize, V as createSaCatalog, W as defaultHttpClient, X as defaultKvStore, Y as defineListOp, Z as defineMessages, _ as defineResource, $ as filterQueryString, a0 as isSaBuiltinLocale, a1 as isSentInQuery, a2 as listUrl, a3 as mergeMessages, a4 as planVersionsResource, a5 as plansResource, p as platformResources, a6 as readListPage, a7 as resolveMessages, a8 as tenantsResource } from '../catalog-Dch1Ryw0.js';
3
3
  import { PublicBootResponse, AdminManifest, StandardPageKey, TenantColumnDef, PromoCodeRecord, AdminTenantDetail, AdminUserListFilter, AdminUserListRow, AdminAuditListFilter, AuditEntry, AdminSubscriptionListRow } from '@saasicat/types';
4
4
 
5
5
  declare const ADMIN_UI_VERSION = "1.2.0";
6
6
 
7
+ interface FetchHttpClientOptions {
8
+ /**
9
+ * Prefix for relative URLs, e.g. `'https://api.example.com'`. Absolute
10
+ * URLs are passed through untouched.
11
+ *
12
+ * Note that an app configures its API prefix twice if it also passes a
13
+ * fully-qualified `apiBase` to the shell — the two would concatenate. Pick
14
+ * one; a doubled prefix 404s on the first request rather than degrading
15
+ * quietly, which is the reason this does not try to detect it.
16
+ */
17
+ baseUrl?: string;
18
+ /**
19
+ * Headers to add to every request, read per request so a token that
20
+ * changes between calls is picked up without rebuilding the client. May
21
+ * return a promise for a token that has to be refreshed first.
22
+ *
23
+ * Headers passed by the caller win, so a request that sets its own
24
+ * `Content-Type` keeps it.
25
+ */
26
+ headers?: () => Record<string, string> | Promise<Record<string, string>>;
27
+ }
28
+ /**
29
+ * Builds an `HttpClient` over `fetch`.
30
+ *
31
+ * With no options it is a bare passthrough plus an `Accept: application/json`
32
+ * header — the platform speaks JSON everywhere, and saying so is what stops a
33
+ * content-negotiating gateway from answering with HTML.
34
+ */
35
+ declare function createFetchHttpClient(options?: FetchHttpClientOptions): HttpClient;
36
+
37
+ /**
38
+ * The part of the merged request config axios echoes back on every response —
39
+ * on the one it resolves with and on the one it attaches to a rejection. These
40
+ * three fields say whether `data` is a decoded value or the body as it arrived,
41
+ * for as long as axios's own response transform is the one that produced it;
42
+ * see `bodyIsRaw`.
43
+ *
44
+ * They are `unknown` rather than their axios types so that a real
45
+ * `AxiosResponse` stays structurally assignable without this package taking a
46
+ * dependency on axios to name them.
47
+ */
48
+ interface AxiosLikeResponseConfig {
49
+ transformResponse?: unknown;
50
+ responseType?: unknown;
51
+ transitional?: unknown;
52
+ }
53
+ interface AxiosLikeResponse {
54
+ status: number;
55
+ data: unknown;
56
+ headers: unknown;
57
+ config?: AxiosLikeResponseConfig;
58
+ }
59
+ interface AxiosLikeConfig {
60
+ url: string;
61
+ method?: string;
62
+ headers?: Record<string, string>;
63
+ data?: unknown;
64
+ }
65
+ /** The part of an axios instance this adapter uses. */
66
+ interface AxiosLike {
67
+ request(config: AxiosLikeConfig): Promise<AxiosLikeResponse>;
68
+ }
69
+ /**
70
+ * What an instance leaves in `response.data`, for the one case the response
71
+ * cannot describe: an instance that replaced `transformResponse` with a
72
+ * non-empty pipeline of its own.
73
+ *
74
+ * - `'auto'` — read it off the config axios echoes on the response. Correct for
75
+ * an instance that still runs axios's own transform, and for every way of
76
+ * switching that transform's parsing off: `responseType: 'text'`,
77
+ * `transitional: { forcedJSONParsing: false }`, and a `transformResponse`
78
+ * that runs nothing (`[]` or `null`).
79
+ * - `'raw'` — `data` is the body as it arrived, and `json()` parses it.
80
+ * - `'decoded'` — `data` is a value the pipeline already produced, and `json()`
81
+ * hands it over untouched.
82
+ */
83
+ type AxiosResponseBody = 'auto' | 'raw' | 'decoded';
84
+ interface AxiosHttpClientOptions {
85
+ /**
86
+ * Prefix(es) to remove from the start of a URL before handing it to the
87
+ * instance, for the usual case where the instance already carries them as
88
+ * its `baseURL`. Tried in order, first match wins — so list the longest
89
+ * first (`['/api/v1', '/api']`), or `/api/v1/admin/x` loses only `/api`.
90
+ */
91
+ stripPrefix?: string | readonly string[];
92
+ /**
93
+ * How this instance hands the body over. Defaults to `'auto'`, which needs
94
+ * no configuration and is right for every instance that leaves axios's own
95
+ * `transformResponse` in place.
96
+ *
97
+ * Set it only if you replaced `transformResponse` with a non-empty pipeline
98
+ * of your own: `'raw'` if that pipeline hands the body over as it arrived
99
+ * (`[(data) => data]`), `'decoded'` if it parses. The response cannot be
100
+ * read for the answer — both echo one opaque function — so `'auto'` would
101
+ * take your pipeline for axios's; see `bodyIsRaw`.
102
+ *
103
+ * The declaration is read before anything else, an empty `data` included:
104
+ * under `'decoded'` an empty `data` is the empty string the pipeline
105
+ * produced, and `json()` hands it over; under `'raw'` it is an empty body,
106
+ * and `json()` throws the way `Response.json()` does.
107
+ */
108
+ responseBody?: AxiosResponseBody;
109
+ }
110
+ /**
111
+ * Adapts an axios instance to `HttpClient`.
112
+ *
113
+ * Two decisions are worth knowing about, because five of the six shims this
114
+ * replaces made them differently:
115
+ *
116
+ * **No status throws — but the instance still decides.** A 402, a 404 and a
117
+ * 500 all arrive as responses, because the platform reads statuses itself (a
118
+ * 304 is a cache hit, a 402 carries a limit payload) and can only do that for
119
+ * statuses it is handed. That is achieved by adapting the rejection rather
120
+ * than by overriding `validateStatus`: an override would make axios resolve
121
+ * everything, and a config that never rejects makes the **rejection half of
122
+ * the instance's own response interceptors unreachable** — the conventional
123
+ * place a consumer puts token refresh and retry. Their session would expire
124
+ * silently instead of refreshing.
125
+ *
126
+ * **`json()` decodes only what axios left undecoded.** Which of the two it is
127
+ * cannot be guessed from the value. The response says it for every instance
128
+ * that still runs axios's own `transformResponse`; an instance that replaced it
129
+ * says it with the `responseBody` option, because at that point the response no
130
+ * longer can. See `bodyIsRaw`.
131
+ *
132
+ * Nothing here overrides the instance's own configuration — not
133
+ * `validateStatus`, and not `transformResponse` either. Forcing the transform
134
+ * off would make the answer knowable, at the price of handing the consumer's
135
+ * own response interceptors a string where they had an object.
136
+ */
137
+ declare function createAxiosHttpClient(instance: AxiosLike, options?: AxiosHttpClientOptions): HttpClient;
138
+
139
+ interface ResourceRequestInit {
140
+ /** Default `GET`. */
141
+ method?: string;
142
+ /** Serialised here — the `HttpClient` contract only carries strings. */
143
+ body?: unknown;
144
+ /** Merged over the defaults, so a caller's header wins. */
145
+ headers?: Record<string, string>;
146
+ }
147
+ /**
148
+ * Sends a request and returns the parsed body, or `null` when there was none.
149
+ *
150
+ * `null` covers both a `204` and a 2xx whose body would not parse. The two are
151
+ * deliberately not distinguished: every caller treats them the same way — a
152
+ * list turns `null` into `[]`, a mutation turns it into a failure — and no
153
+ * endpoint in this family answers a mutation with an empty 200 on purpose.
154
+ *
155
+ * A non-2xx throws an `AdminError` carrying the parsed body, so a caller can
156
+ * still branch on `status` and `body.code` the way the pages do today.
157
+ *
158
+ * A client that resolved without an HTTP status throws before any of that: no
159
+ * status means the request never reached the server, which is a transport
160
+ * failure and not an answer this function may read.
161
+ */
162
+ declare function requestJson<T>(http: HttpClient, url: string, init?: ResourceRequestInit): Promise<T | null>;
163
+ /**
164
+ * Reads the body of a request that must answer with one.
165
+ *
166
+ * The empty case is a failure rather than a `null` the caller has to re-check:
167
+ * a create that returns nothing has not told us what it created. Raised with
168
+ * `status: 0`, because no HTTP status went wrong — the response was a 2xx.
169
+ */
170
+ declare function requestJsonBody<T>(http: HttpClient, url: string, what: string, init?: ResourceRequestInit): Promise<T>;
171
+
172
+ /**
173
+ * Marks an error as one this package raised. Called by the package's own error
174
+ * classes in their constructors; nothing else should call it.
175
+ */
176
+ declare function markPlatformError(error: Error): void;
177
+ /** Whether an error came from this package, across bundle copies. */
178
+ declare function isPlatformError(value: unknown): boolean;
179
+ /**
180
+ * Marks an error as the empty-body sentinel: the call reached the server, the
181
+ * server answered, and the body the caller needed was not in it. Returns the
182
+ * error so a throw site stays one expression.
183
+ *
184
+ * Called at those throw sites only — never in a constructor, because the class
185
+ * is also raised for calls that failed outright.
186
+ */
187
+ declare function markEmptyResponse<E extends Error>(error: E): E;
188
+ /** Whether an error is that sentinel, across bundle copies. */
189
+ declare function isEmptyResponse(value: unknown): boolean;
7
190
  /**
8
- * Error for non-2xx responses. `code` is the machine-readable error code
9
- * from the JSON body (`{ code }`), if present callers map it to
10
- * a message (e.g. via `SETUP_ERROR_CODES`).
191
+ * Marks an error as one the request did not survive: no connection, DNS
192
+ * failure, CORS rejection, abort, or a client that reported the same by
193
+ * resolving without an HTTP status. Returns the error so a throw site stays one
194
+ * expression.
11
195
  */
12
- declare class HttpJsonError extends Error {
196
+ declare function markTransportFailure<E>(error: E): E;
197
+ /** Whether an error is a declared transport failure, across bundle copies. */
198
+ declare function isTransportFailure(value: unknown): boolean;
199
+ interface AdminErrorInit {
200
+ /** HTTP status. `0` means the request never produced one. */
201
+ status?: number;
202
+ /** Machine-readable code from the response body (`{ code }`). */
203
+ code?: string;
204
+ /** Parsed response body, when there was one. */
205
+ body?: unknown;
206
+ /** Requested URL, when known. */
207
+ url?: string;
208
+ /** Request method, when known. */
209
+ method?: string;
210
+ /**
211
+ * Text the failing side supplied. Never invent one — its absence is the
212
+ * signal that only a translated fallback can be shown.
213
+ */
214
+ detail?: string;
215
+ /**
216
+ * The request completed and the answer was unusable — a 2xx that carried
217
+ * no body where one was required.
218
+ *
219
+ * Its own state because the model could not otherwise tell it from a
220
+ * transport failure: both have no HTTP status to report, and both were
221
+ * therefore `status: 0`. They need opposite words. A request that never
222
+ * left says "check your connection"; one the server accepted and answered
223
+ * with nothing says "check whether the change was applied", because it may
224
+ * well have been.
225
+ */
226
+ emptyResponse?: boolean;
227
+ /**
228
+ * The request never reached the server.
229
+ *
230
+ * Its own state for the same reason as `emptyResponse`: neither has an HTTP
231
+ * status to reason from, so a status of `0` cannot tell them apart — and
232
+ * neither can it tell either of them from an error that simply carries no
233
+ * status, such as a bug in page code. Only the seam that made the request
234
+ * knows, and it says so with `markTransportFailure`.
235
+ */
236
+ transportFailure?: boolean;
237
+ /** Diagnostic message. Derived from the fields above when omitted. */
238
+ message?: string;
239
+ /** The error this one was built from. */
240
+ cause?: unknown;
241
+ }
242
+ /** A failed admin API call, in the one shape the package works with. */
243
+ declare class AdminError extends Error {
244
+ /** HTTP status. `0` means the request never produced one. */
13
245
  readonly status: number;
14
- readonly code?: string | undefined;
15
- constructor(status: number, code?: string | undefined);
246
+ /** Machine-readable code from the response body, when it carried one. */
247
+ readonly code?: string;
248
+ /** Parsed response body, when there was one. */
249
+ readonly body?: unknown;
250
+ /** Requested URL, when known. */
251
+ readonly url?: string;
252
+ /** Request method, when known. */
253
+ readonly method?: string;
254
+ /**
255
+ * Text the failing side supplied — the only candidate for user-facing
256
+ * output. `undefined` when nothing was supplied.
257
+ */
258
+ readonly detail?: string;
259
+ /** The request completed but the answer was unusable. See `AdminErrorInit`. */
260
+ readonly emptyResponse: boolean;
261
+ /** The request never reached the server. See `AdminErrorInit`. */
262
+ readonly transportFailure: boolean;
263
+ constructor(init?: AdminErrorInit);
16
264
  }
265
+ /** Whether `err` is an `AdminError`, including one from another bundle copy. */
266
+ declare function isAdminError(err: unknown): err is AdminError;
267
+ /**
268
+ * Whether an error is axios saying, in its own words, that the request was made
269
+ * and no response ever arrived.
270
+ *
271
+ * This is the one transport failure read rather than branded, because a library
272
+ * cannot be asked to mark itself. What it reads is axios's own three-way split,
273
+ * documented under "Handling errors" in its README and visible in the
274
+ * `AxiosError` constructor: `response` is set only when the server answered,
275
+ * `request` only once one was actually made, and an error with neither happened
276
+ * while the request was still being set up.
277
+ *
278
+ * Measured against axios 1.18.1 driven at a real `node:http` server, and the
279
+ * groups came out disjoint:
280
+ *
281
+ * | rejection | `isAxiosError` | `request` | `response` |
282
+ * | ----------------------------------------------- | -------------- | --------- | ---------- |
283
+ * | connection refused / reset, DNS, timeout, abort | `true` | yes | no |
284
+ * | the same, rethrown by a rejection interceptor | `true` | yes | no |
285
+ * | a 401 or 500 the server really sent | `true` | yes | yes |
286
+ * | interceptor rejects with `new Error(…)` | absent | no | no |
287
+ * | …the same, but copying `error.config` across | absent | no | no |
288
+ * | …the same, copying `config` and `request` | absent | yes | no |
289
+ * | unsupported protocol, signal aborted before call | `true` | no | no |
290
+ * | request interceptor throws, malformed URL | absent | no | no |
291
+ *
292
+ * Three readings follow from that table, and only the first is free of choice:
293
+ *
294
+ * - `response` present means the server answered, whatever else is set.
295
+ * - `request` present without it is axios's "made, nothing came back". The
296
+ * browser and `fetch` adapters attach it too — an `XMLHttpRequest` and a
297
+ * `Request` respectively — so the reading is not Node-only.
298
+ * - Requiring `isAxiosError` on top of that is the decision. It removes the
299
+ * ordinary interceptor that copies `config` and `request` onto a hand-built
300
+ * `new Error`, which is shape-wise axios's no-response form after the server
301
+ * had answered.
302
+ *
303
+ * The previous reading was `config` without `response`, which is not axios's
304
+ * statement about anything: `config` is echoed on every axios error including
305
+ * the ones carrying a 401, and it is the field an interceptor is most likely to
306
+ * carry over when it replaces the rejection. It matched three rows above that
307
+ * mean the opposite of a transport failure.
308
+ *
309
+ * ## What this cannot decide, and which way it errs
310
+ *
311
+ * This is read *after* the consumer's interceptors have run — after the one
312
+ * place that can set or delete any field on the rejection. Both readings are
313
+ * therefore forgeable, in both directions, and each was reproduced:
314
+ *
315
+ * - An interceptor that **drops** `request` — `AxiosError.from(err, code,
316
+ * config)` without its fourth argument, `err.toJSON()`, or a `delete
317
+ * err.request` before logging — leaves a genuine `ECONNREFUSED` unbranded.
318
+ * The operator then reads `connect ECONNREFUSED 127.0.0.1:44947` instead of
319
+ * the localized sentence.
320
+ * - An interceptor that **builds** one — `AxiosError.from(msg, code,
321
+ * error.config, error.request)` after an answered 401 — is branded. Nothing
322
+ * distinguishes it: `AxiosError.from` is public API, so `isAxiosError` is
323
+ * available to user code as well.
324
+ *
325
+ * Neither is closable by a better predicate; the fields simply do not carry the
326
+ * fact by then. So the reading errs deliberately toward *not* branding, because
327
+ * the two mistakes do not cost the same: an unbranded transport failure shows a
328
+ * true sentence in the wrong register, while a branded application failure tells
329
+ * the operator to check a connection that is fine. Saying less beats saying
330
+ * something false.
331
+ *
332
+ * An interceptor that rewrites rejections should say what it means with
333
+ * `markTransportFailure`, which is exported for exactly this. It is checked
334
+ * first and settles the case.
335
+ */
336
+ declare function isAxiosNoResponseError(value: unknown): boolean;
337
+ /**
338
+ * Reads the human-readable message out of a response body.
339
+ *
340
+ * Covers the two shapes the platform and its host produce: a coded platform
341
+ * error (`{ code, message }`) carries a string, while a NestJS
342
+ * `ValidationPipe` rejection carries `message: string[]` — one entry per
343
+ * failed constraint. Joining them is what keeps a validation error readable
344
+ * instead of rendering as `[object Object]`.
345
+ *
346
+ * Exported because every place that builds an `AdminError` from a response
347
+ * needs the same answer. It had two implementations for one release and they
348
+ * disagreed: the one in `http-json.ts` accepted only a string, so a validation
349
+ * rejection arriving through `getJson`/`postJson` lost its constraints
350
+ * entirely and the operator was shown the generic fallback.
351
+ */
352
+ declare function readErrorDetail(body: unknown): string | undefined;
353
+ /** Reads the machine-readable code out of a response body, if it carries one. */
354
+ declare function readErrorCode(body: unknown): string | undefined;
355
+ /**
356
+ * Turns anything that was caught into an `AdminError`.
357
+ *
358
+ * Recognises, in order: an `AdminError` (from any bundle copy), an axios-style
359
+ * rejection, an error carrying a numeric `status` — which is every one of the
360
+ * package's own `*ApiError` classes — and finally any `Error`, string or
361
+ * unknown value.
362
+ */
363
+ declare function toAdminError(err: unknown): AdminError;
364
+ /**
365
+ * Turns anything that was caught into text for a user.
366
+ *
367
+ * What the failing side said outranks anything this package could guess, so a
368
+ * `detail` wins whenever there is one. After that come the two facts a seam
369
+ * declared about a request with no HTTP status — they need opposite sentences
370
+ * and no number can tell them apart — and only then the status.
371
+ *
372
+ * The last line is the honest one. An error with no status, no text and no
373
+ * declaration is an error nothing knows anything about; "check your connection"
374
+ * used to be the answer, and it sent an operator after their router for a null
375
+ * dereference in a page. Whoever knows better says so with
376
+ * `markTransportFailure` or `markEmptyResponse`.
377
+ *
378
+ * Pages do not call this — `useAsyncAction` and the error banner do.
379
+ */
380
+ declare function adminErrorMessage(err: unknown, msgs: SaMessages['errors']): string;
381
+
382
+ /**
383
+ * Fails, at the seam that can still tell, when the client resolved without an
384
+ * HTTP status: the request never reached the server.
385
+ *
386
+ * Every caller below then knows that an absent body means the server answered
387
+ * without one, which is the precondition the empty-response sentinel needs and
388
+ * did not have — `if (!data)` was true for both facts, and the mutation
389
+ * sentinel told the operator of a request that never left the machine to go
390
+ * check whether their change had been applied.
391
+ *
392
+ * `raise` builds the error class of the calling seam from the diagnostic, so
393
+ * the caught error keeps saying which API it came from.
394
+ */
395
+ declare function requireServerAnswer(status: number, method: string, url: string, raise: (diagnostic: string) => Error): void;
396
+ /**
397
+ * @deprecated Renamed to {@link AdminError}, which is the same class: an
398
+ * `instanceof HttpJsonError` check keeps working and now also matches errors
399
+ * raised elsewhere in the package. Construction changed — `AdminError` takes
400
+ * one options object instead of `(status, code)`.
401
+ */
402
+ declare const HttpJsonError: typeof AdminError;
403
+ type HttpJsonError = AdminError;
17
404
  /**
18
405
  * Removes trailing slashes so an API prefix can be concatenated with paths
19
406
  * that start with `/`. Deliberately index-based instead of
@@ -65,6 +452,11 @@ interface ManifestLoaderOptions {
65
452
  */
66
453
  getAuthToken?: () => string | null;
67
454
  }
455
+ /**
456
+ * The manifest could not be loaded. Its `message` is a diagnostic for the log,
457
+ * never text for a screen — `markPlatformError` is what tells `toAdminError`
458
+ * so, and it is a promise this class has to keep at every throw site.
459
+ */
68
460
  declare class ManifestLoadError extends Error {
69
461
  readonly status: number;
70
462
  constructor(status: number, message: string);
@@ -84,8 +476,21 @@ declare class ManifestLoader {
84
476
  /**
85
477
  * Loads the current manifest. On a cache hit (304) the cached
86
478
  * body is returned — otherwise the fresh server body.
479
+ *
480
+ * A 304 with no usable cached body is not a failure to report but one to
481
+ * undo: the request only carried `If-None-Match` because this loader put
482
+ * the ETag there, and the cache it validated against is this loader's own.
483
+ * So it drops the pair and asks once more without the header, which is the
484
+ * same recovery a person would have had to trigger by hand — except that
485
+ * the person reading an admin screen has neither this loader nor a
486
+ * console. Exactly one retry: a 304 to a request that asked nothing
487
+ * conditional is a server fault, and no repetition fixes it.
87
488
  */
88
489
  load(): Promise<AdminManifest>;
490
+ /** Sends the manifest GET, conditional when an ETag is supplied. */
491
+ private request;
492
+ /** Takes a non-304 response as the current manifest and caches it. */
493
+ private acceptFresh;
89
494
  /** Reads the cached manifest body from storage; null if absent. */
90
495
  readCachedBody(): CachedManifestEntry | null;
91
496
  /** Clears the cache — e.g. on logout or after `manifest reload`. */
@@ -458,4 +863,4 @@ declare function identityAccentFor(key: string, overrides?: Readonly<Record<stri
458
863
  */
459
864
  declare function identityChipStyle(accent: string): Record<string, string>;
460
865
 
461
- export { ADMIN_UI_VERSION, type AdminPromoListFilter, type AdminPromoListRow, type AdminResourceClientOptions, type BatchColumnData, BatchColumnDriftError, BatchColumnFetcher, type BatchColumnFetcherOptions, type BatchColumnRow, type BatchColumnValue, BootLoadError, BootLoader, type BootLoaderOptions, type BuildRouteEntry, type CachedManifestEntry, DEFAULT_STANDARD_PAGE_ROUTES, HttpClient, HttpJsonError, IDENTITY_ACCENTS, IDENTITY_ACCENT_VALUES, IDENTITY_NEUTRAL, IDENTITY_NEUTRAL_VALUE, KvStore, type LoginBootProject, type LoginBrandFallback, type LoginBranding, ManifestLoadError, ManifestLoader, type ManifestLoaderOptions, type MessageParams, type NavBuilderOptions, type ParamStyle, type PlanCounts, type PlanRowLike, type PlanVersionLike, type ResolvePlansInput, type ResolvedPlan, SaLocale, type SidebarItem, type SidebarSection, buildRoutes, buildSidebar, countPlans, createAdminResourceClient, defaultSectionOrder, formatCurrency, formatMessage, getJson, identityAccentAt, identityAccentFor, identityChipStyle, isCurrentlyValid, isExpired, isFutureScheduled, isProductionBoot, postJson, resolveExtension, resolveLoginBranding, resolvePlans, todayIsoDate, trimTrailingSlashes };
866
+ export { ADMIN_UI_VERSION, AdminError, type AdminErrorInit, type AdminPromoListFilter, type AdminPromoListRow, type AdminResourceClientOptions, type AxiosHttpClientOptions, type AxiosLike, type AxiosLikeConfig, type AxiosLikeResponse, type AxiosLikeResponseConfig, type AxiosResponseBody, type BatchColumnData, BatchColumnDriftError, BatchColumnFetcher, type BatchColumnFetcherOptions, type BatchColumnRow, type BatchColumnValue, BootLoadError, BootLoader, type BootLoaderOptions, type BuildRouteEntry, type CachedManifestEntry, DEFAULT_STANDARD_PAGE_ROUTES, type FetchHttpClientOptions, HttpClient, HttpJsonError, IDENTITY_ACCENTS, IDENTITY_ACCENT_VALUES, IDENTITY_NEUTRAL, IDENTITY_NEUTRAL_VALUE, KvStore, type LoginBootProject, type LoginBrandFallback, type LoginBranding, ManifestLoadError, ManifestLoader, type ManifestLoaderOptions, type MessageParams, type NavBuilderOptions, type ParamStyle, type PlanCounts, type PlanRowLike, type PlanVersionLike, type ResolvePlansInput, type ResolvedPlan, type ResourceRequestInit, SaLocale, SaMessages, type SidebarItem, type SidebarSection, adminErrorMessage, buildRoutes, buildSidebar, countPlans, createAdminResourceClient, createAxiosHttpClient, createFetchHttpClient, defaultSectionOrder, formatCurrency, formatMessage, getJson, identityAccentAt, identityAccentFor, identityChipStyle, isAdminError, isAxiosNoResponseError, isCurrentlyValid, isEmptyResponse, isExpired, isFutureScheduled, isPlatformError, isProductionBoot, isTransportFailure, markEmptyResponse, markPlatformError, markTransportFailure, postJson, readErrorCode, readErrorDetail, requestJson, requestJsonBody, requireServerAnswer, resolveExtension, resolveLoginBranding, resolvePlans, toAdminError, todayIsoDate, trimTrailingSlashes };
@@ -4,23 +4,18 @@ import {
4
4
  ActionRegistry,
5
5
  BatchColumnDriftError,
6
6
  BatchColumnFetcher,
7
- BootLoadError,
8
- BootLoader,
9
7
  DEFAULT_STANDARD_PAGE_ROUTES,
10
- HttpJsonError,
11
8
  IDENTITY_ACCENTS,
12
9
  IDENTITY_ACCENT_VALUES,
13
10
  IDENTITY_NEUTRAL,
14
11
  IDENTITY_NEUTRAL_VALUE,
15
- ManifestLoadError,
16
- ManifestLoader,
17
12
  MissingHandlerError,
18
13
  buildRoutes,
19
14
  buildSidebar,
20
15
  countPlans,
21
16
  createAdminResourceClient,
17
+ createAxiosHttpClient,
22
18
  defaultSectionOrder,
23
- getJson,
24
19
  identityAccentAt,
25
20
  identityAccentFor,
26
21
  identityChipStyle,
@@ -28,33 +23,75 @@ import {
28
23
  isExpired,
29
24
  isFutureScheduled,
30
25
  isProductionBoot,
31
- postJson,
32
26
  resolveExtension,
33
27
  resolveLoginBranding,
34
28
  resolvePlans,
35
- todayIsoDate,
36
- trimTrailingSlashes
37
- } from "../chunk-D74FFCCP.js";
29
+ todayIsoDate
30
+ } from "../chunk-NEXTZZRQ.js";
38
31
  import {
32
+ AdminError,
33
+ BootLoadError,
34
+ BootLoader,
39
35
  DEFAULT_SA_LOCALE,
36
+ HttpJsonError,
37
+ LIST_FIRST_PAGE,
38
+ LIST_PAGE_SIZE_DEFAULT,
39
+ LIST_PAGE_SIZE_MAX,
40
+ LIST_PAGINATION_PARAMS,
41
+ ManifestLoadError,
42
+ ManifestLoader,
40
43
  SA_INTL_LOCALES,
41
44
  SA_LOCALES,
42
45
  SA_LOCALE_LABELS,
43
46
  SA_MESSAGES,
47
+ adminErrorMessage,
48
+ auditResource,
49
+ bindResource,
50
+ clampListPage,
51
+ clampListPageSize,
52
+ createFetchHttpClient,
44
53
  createSaCatalog,
45
54
  defaultHttpClient,
46
55
  defaultKvStore,
56
+ defineListOp,
47
57
  defineMessages,
58
+ defineResource,
59
+ filterQueryString,
48
60
  formatCurrency,
49
61
  formatMessage,
62
+ getJson,
63
+ isAdminError,
64
+ isAxiosNoResponseError,
65
+ isEmptyResponse,
66
+ isPlatformError,
50
67
  isSaBuiltinLocale,
68
+ isSentInQuery,
69
+ isTransportFailure,
70
+ listUrl,
71
+ markEmptyResponse,
72
+ markPlatformError,
73
+ markTransportFailure,
51
74
  mergeMessages,
52
- resolveMessages
53
- } from "../chunk-F7NRM6KI.js";
75
+ planVersionsResource,
76
+ plansResource,
77
+ platformResources,
78
+ postJson,
79
+ readErrorCode,
80
+ readErrorDetail,
81
+ readListPage,
82
+ requestJson,
83
+ requestJsonBody,
84
+ requireServerAnswer,
85
+ resolveMessages,
86
+ tenantsResource,
87
+ toAdminError,
88
+ trimTrailingSlashes
89
+ } from "../chunk-BPF2BMCQ.js";
54
90
  export {
55
91
  ADMIN_UI_VERSION,
56
92
  ActionDefNotInManifestError,
57
93
  ActionRegistry,
94
+ AdminError,
58
95
  BatchColumnDriftError,
59
96
  BatchColumnFetcher,
60
97
  BootLoadError,
@@ -66,6 +103,10 @@ export {
66
103
  IDENTITY_ACCENT_VALUES,
67
104
  IDENTITY_NEUTRAL,
68
105
  IDENTITY_NEUTRAL_VALUE,
106
+ LIST_FIRST_PAGE,
107
+ LIST_PAGE_SIZE_DEFAULT,
108
+ LIST_PAGE_SIZE_MAX,
109
+ LIST_PAGINATION_PARAMS,
69
110
  ManifestLoadError,
70
111
  ManifestLoader,
71
112
  MissingHandlerError,
@@ -73,32 +114,63 @@ export {
73
114
  SA_LOCALES,
74
115
  SA_LOCALE_LABELS,
75
116
  SA_MESSAGES,
117
+ adminErrorMessage,
118
+ auditResource,
119
+ bindResource,
76
120
  buildRoutes,
77
121
  buildSidebar,
122
+ clampListPage,
123
+ clampListPageSize,
78
124
  countPlans,
79
125
  createAdminResourceClient,
126
+ createAxiosHttpClient,
127
+ createFetchHttpClient,
80
128
  createSaCatalog,
81
129
  defaultHttpClient,
82
130
  defaultKvStore,
83
131
  defaultSectionOrder,
132
+ defineListOp,
84
133
  defineMessages,
134
+ defineResource,
135
+ filterQueryString,
85
136
  formatCurrency,
86
137
  formatMessage,
87
138
  getJson,
88
139
  identityAccentAt,
89
140
  identityAccentFor,
90
141
  identityChipStyle,
142
+ isAdminError,
143
+ isAxiosNoResponseError,
91
144
  isCurrentlyValid,
145
+ isEmptyResponse,
92
146
  isExpired,
93
147
  isFutureScheduled,
148
+ isPlatformError,
94
149
  isProductionBoot,
95
150
  isSaBuiltinLocale,
151
+ isSentInQuery,
152
+ isTransportFailure,
153
+ listUrl,
154
+ markEmptyResponse,
155
+ markPlatformError,
156
+ markTransportFailure,
96
157
  mergeMessages,
158
+ planVersionsResource,
159
+ plansResource,
160
+ platformResources,
97
161
  postJson,
162
+ readErrorCode,
163
+ readErrorDetail,
164
+ readListPage,
165
+ requestJson,
166
+ requestJsonBody,
167
+ requireServerAnswer,
98
168
  resolveExtension,
99
169
  resolveLoginBranding,
100
170
  resolveMessages,
101
171
  resolvePlans,
172
+ tenantsResource,
173
+ toAdminError,
102
174
  todayIsoDate,
103
175
  trimTrailingSlashes
104
176
  };