@saasicat/ui-vue 0.25.0 → 0.26.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.
- package/README.md +55 -8
- package/dist/{catalog-DJcfm5N2.d.cts → catalog-Dch1Ryw0.d.cts} +353 -69
- package/dist/{catalog-DJcfm5N2.d.ts → catalog-Dch1Ryw0.d.ts} +353 -69
- package/dist/{chunk-F7NRM6KI.js → chunk-COVQLDBZ.js} +759 -24
- package/dist/{chunk-TQQMKWN6.js → chunk-DT25QAU2.js} +119 -7
- package/dist/{chunk-D74FFCCP.js → chunk-O3Y3BYYS.js} +96 -178
- package/dist/client/index.cjs +770 -84
- package/dist/client/index.d.cts +429 -9
- package/dist/client/index.d.ts +429 -9
- package/dist/client/index.js +84 -12
- package/dist/index.cjs +1337 -295
- package/dist/index.d.cts +205 -15
- package/dist/index.d.ts +205 -15
- package/dist/index.js +566 -240
- package/dist/quasar/index.cjs +585 -62
- package/dist/quasar/index.d.cts +61 -6
- package/dist/quasar/index.d.ts +61 -6
- package/dist/quasar/index.js +83 -19
- package/dist/{use-sa-theme-OUBlaqgl.d.ts → resource-registry-D7Xjs-5f.d.cts} +157 -7
- package/dist/{use-sa-theme-B5t7oXph.d.cts → resource-registry-TaR7rtq0.d.ts} +157 -7
- package/package.json +6 -4
- package/src/client/admin-error.ts +569 -0
- package/src/client/admin-resource-client.ts +9 -12
- package/src/client/attach-cause.ts +21 -0
- package/src/client/batch-column-fetcher.ts +2 -2
- package/src/client/boot-loader.ts +12 -1
- package/src/client/http/axios-http-client.ts +383 -0
- package/src/client/http/fetch-http-client.ts +89 -0
- package/src/client/http/index.ts +6 -0
- package/src/client/http-json.ts +82 -16
- package/src/client/i18n/messages/discovery.ts +0 -6
- package/src/client/i18n/messages/errors.ts +44 -0
- package/src/client/i18n/messages/marketing.ts +0 -6
- package/src/client/i18n/messages/promos.ts +0 -2
- package/src/client/i18n/messages/shell.ts +12 -0
- package/src/client/i18n/messages.ts +3 -0
- package/src/client/index.ts +6 -0
- package/src/client/manifest-loader.ts +65 -15
- package/src/client/nav-builder.ts +3 -1
- package/src/client/resources/audit.resource.ts +25 -0
- package/src/client/resources/define-resource.ts +166 -0
- package/src/client/resources/index.ts +42 -0
- package/src/client/resources/list-resource.ts +176 -0
- package/src/client/resources/plans.resource.ts +169 -0
- package/src/client/resources/resource-request.ts +117 -0
- package/src/client/resources/tenants.resource.ts +27 -0
- package/src/client/types.ts +10 -3
- package/src/components/BundleVersionPublishDialog.vue +1 -1
- package/src/components/MarketingPromotionsTab.vue +263 -241
- package/src/components/ThemeSwitcher.vue +85 -0
- package/src/components/bundle-editor/BundleCreatePanel.vue +1 -1
- package/src/components/dialogs/PromoCodeDialogFields.vue +20 -20
- package/src/components/plan/PlanCycleToggle.vue +1 -1
- package/src/components/plan-detail/PlanVersionsPanel.vue +5 -2
- package/src/index.ts +5 -0
- package/src/pages-standard/AdminLayout.vue +57 -0
- package/src/pages-standard/AdminManifestErrorPage.vue +4 -3
- package/src/pages-standard/DiscoveryPage.vue +8 -1
- package/src/pages-standard/MarketingCatalogPage.vue +14 -3
- package/src/pages-standard/SuperAdminLoginPage.vue +23 -2
- package/src/pages-standard/SuperAdminSetupWizard.vue +44 -7
- package/src/pages-standard/discovery-page/DiscoveryFeatureCard.vue +7 -2
- package/src/pages-standard/marketing-catalog/MarketingCatalogAdmin.vue +44 -6
- package/src/pages-standard/marketing-catalog/MarketingCatalogPreview.vue +8 -2
- package/src/pages-tenant/PackageSnapshotPanel.vue +31 -2
- package/src/quasar/confirm.ts +83 -0
- package/src/quasar/create-super-admin-app.ts +84 -10
- package/src/quasar/dark-bridge.ts +47 -10
- package/src/quasar/index.ts +1 -0
- package/src/vue/create-admin-routes.ts +9 -1
- package/src/vue/platform-loaders.ts +12 -2
- package/src/vue/resource-registry.ts +211 -0
- package/src/vue/super-admin-context.ts +20 -5
- package/src/vue/ui-confirm.ts +76 -0
- package/src/vue/use-api-list.ts +50 -45
- package/src/vue/use-async-action.ts +170 -0
- package/src/vue/use-async-data.ts +83 -0
- package/src/vue/use-bundles.ts +34 -7
- package/src/vue/use-catalog-entries.ts +37 -12
- package/src/vue/use-discovery.ts +27 -6
- package/src/vue/use-marketing-projections.ts +29 -7
- package/src/vue/use-plans.ts +36 -8
- package/src/vue/use-promotions.ts +27 -7
- package/src/vue/use-resource-list.ts +272 -0
- package/src/vue/use-sa-theme.ts +34 -5
- package/src/vue/use-tenant-subscription-bundles.ts +22 -3
package/dist/index.cjs
CHANGED
|
@@ -24,6 +24,7 @@ __export(src_exports, {
|
|
|
24
24
|
ALL_ROWS: () => ALL_ROWS,
|
|
25
25
|
ActionDefNotInManifestError: () => ActionDefNotInManifestError,
|
|
26
26
|
ActionRegistry: () => ActionRegistry,
|
|
27
|
+
AdminError: () => AdminError,
|
|
27
28
|
BatchColumnDriftError: () => BatchColumnDriftError,
|
|
28
29
|
BatchColumnFetcher: () => BatchColumnFetcher,
|
|
29
30
|
BootLoadError: () => BootLoadError,
|
|
@@ -44,6 +45,10 @@ __export(src_exports, {
|
|
|
44
45
|
IDENTITY_ACCENT_VALUES: () => IDENTITY_ACCENT_VALUES,
|
|
45
46
|
IDENTITY_NEUTRAL: () => IDENTITY_NEUTRAL,
|
|
46
47
|
IDENTITY_NEUTRAL_VALUE: () => IDENTITY_NEUTRAL_VALUE,
|
|
48
|
+
LIST_FIRST_PAGE: () => LIST_FIRST_PAGE,
|
|
49
|
+
LIST_PAGE_SIZE_DEFAULT: () => LIST_PAGE_SIZE_DEFAULT,
|
|
50
|
+
LIST_PAGE_SIZE_MAX: () => LIST_PAGE_SIZE_MAX,
|
|
51
|
+
LIST_PAGINATION_PARAMS: () => LIST_PAGINATION_PARAMS,
|
|
47
52
|
ManifestLoadError: () => ManifestLoadError,
|
|
48
53
|
ManifestLoader: () => ManifestLoader,
|
|
49
54
|
MarketingProjectionsApiError: () => MarketingProjectionsApiError,
|
|
@@ -53,15 +58,18 @@ __export(src_exports, {
|
|
|
53
58
|
PlansApiError: () => PlansApiError,
|
|
54
59
|
ProjectPageHost: () => ProjectPageHost,
|
|
55
60
|
PromotionsApiError: () => PromotionsApiError,
|
|
61
|
+
SA_COLOR_SCHEMES: () => SA_COLOR_SCHEMES,
|
|
56
62
|
SA_INTL_LOCALES: () => SA_INTL_LOCALES,
|
|
57
63
|
SA_LOCALES: () => SA_LOCALES,
|
|
58
64
|
SA_LOCALE_LABELS: () => SA_LOCALE_LABELS,
|
|
59
65
|
SA_LOCALE_STORAGE_KEY: () => SA_LOCALE_STORAGE_KEY,
|
|
60
66
|
SA_MESSAGES: () => SA_MESSAGES,
|
|
61
67
|
SA_THEME_KEY: () => SA_THEME_KEY,
|
|
68
|
+
SA_THEME_STORAGE_KEY: () => SA_THEME_STORAGE_KEY,
|
|
62
69
|
SERVER_PAGE_SIZE_OPTIONS: () => SERVER_PAGE_SIZE_OPTIONS,
|
|
63
70
|
SUPER_ADMIN_ACTIONS_KEY: () => SUPER_ADMIN_ACTIONS_KEY,
|
|
64
71
|
SUPER_ADMIN_BRAND_KEY: () => SUPER_ADMIN_BRAND_KEY,
|
|
72
|
+
SUPER_ADMIN_CONFIRM_KEY: () => SUPER_ADMIN_CONFIRM_KEY,
|
|
65
73
|
SUPER_ADMIN_ENDPOINTS_KEY: () => SUPER_ADMIN_ENDPOINTS_KEY,
|
|
66
74
|
SUPER_ADMIN_EXTENSIONS_KEY: () => SUPER_ADMIN_EXTENSIONS_KEY,
|
|
67
75
|
SUPER_ADMIN_HTTP_KEY: () => SUPER_ADMIN_HTTP_KEY,
|
|
@@ -70,19 +78,28 @@ __export(src_exports, {
|
|
|
70
78
|
SUPER_ADMIN_MANIFEST_CLEAR_CACHE_KEY: () => SUPER_ADMIN_MANIFEST_CLEAR_CACHE_KEY,
|
|
71
79
|
SUPER_ADMIN_MANIFEST_KEY: () => SUPER_ADMIN_MANIFEST_KEY,
|
|
72
80
|
SUPER_ADMIN_NOTIFY_KEY: () => SUPER_ADMIN_NOTIFY_KEY,
|
|
81
|
+
SUPER_ADMIN_RESOURCES_KEY: () => SUPER_ADMIN_RESOURCES_KEY,
|
|
73
82
|
TenantSubscriptionBundlesApiError: () => TenantSubscriptionBundlesApiError,
|
|
83
|
+
adminErrorMessage: () => adminErrorMessage,
|
|
84
|
+
auditResource: () => auditResource,
|
|
85
|
+
bindResource: () => bindResource,
|
|
74
86
|
buildFeatureRegistry: () => buildFeatureRegistry,
|
|
75
87
|
buildFeatureRouterGuard: () => buildFeatureRouterGuard,
|
|
76
88
|
buildNavigationGuard: () => buildNavigationGuard,
|
|
77
89
|
buildQuotaRegistry: () => buildQuotaRegistry,
|
|
78
90
|
buildRoutes: () => buildRoutes,
|
|
79
91
|
buildSidebar: () => buildSidebar,
|
|
92
|
+
clampListPage: () => clampListPage,
|
|
93
|
+
clampListPageSize: () => clampListPageSize,
|
|
80
94
|
countPlans: () => countPlans,
|
|
81
95
|
createAdminResourceClient: () => createAdminResourceClient,
|
|
82
96
|
createAdminRoutes: () => createAdminRoutes,
|
|
97
|
+
createAxiosHttpClient: () => createAxiosHttpClient,
|
|
98
|
+
createFetchHttpClient: () => createFetchHttpClient,
|
|
83
99
|
createManifestStore: () => createManifestStore,
|
|
84
100
|
createPlatformLoaders: () => createPlatformLoaders,
|
|
85
101
|
createProjectPageHostRoute: () => createProjectPageHostRoute,
|
|
102
|
+
createResourceRegistry: () => createResourceRegistry,
|
|
86
103
|
createSaCatalog: () => createSaCatalog,
|
|
87
104
|
createSaTheme: () => createSaTheme,
|
|
88
105
|
createSuperAdminI18n: () => createSuperAdminI18n,
|
|
@@ -92,30 +109,57 @@ __export(src_exports, {
|
|
|
92
109
|
defaultSectionOrder: () => defaultSectionOrder,
|
|
93
110
|
defaultTenantPlanSectionI18n: () => defaultTenantPlanSectionI18n,
|
|
94
111
|
defaultToneForActionKey: () => defaultToneForActionKey,
|
|
112
|
+
defineListOp: () => defineListOp,
|
|
95
113
|
defineMessages: () => defineMessages,
|
|
114
|
+
defineResource: () => defineResource,
|
|
115
|
+
filterQueryString: () => filterQueryString,
|
|
96
116
|
formatCurrency: () => formatCurrency,
|
|
97
117
|
formatMessage: () => formatMessage,
|
|
98
118
|
getJson: () => getJson,
|
|
99
119
|
identityAccentAt: () => identityAccentAt,
|
|
100
120
|
identityAccentFor: () => identityAccentFor,
|
|
101
121
|
identityChipStyle: () => identityChipStyle,
|
|
122
|
+
isAdminError: () => isAdminError,
|
|
123
|
+
isAxiosNoResponseError: () => isAxiosNoResponseError,
|
|
102
124
|
isCurrentlyValid: () => isCurrentlyValid,
|
|
125
|
+
isEmptyResponse: () => isEmptyResponse,
|
|
103
126
|
isExpired: () => isExpired,
|
|
104
127
|
isFutureScheduled: () => isFutureScheduled,
|
|
128
|
+
isPlatformError: () => isPlatformError,
|
|
105
129
|
isProductionBoot: () => isProductionBoot,
|
|
106
130
|
isSaBuiltinLocale: () => isSaBuiltinLocale,
|
|
131
|
+
isSentInQuery: () => isSentInQuery,
|
|
132
|
+
isTransportFailure: () => isTransportFailure,
|
|
133
|
+
listUrl: () => listUrl,
|
|
134
|
+
markEmptyResponse: () => markEmptyResponse,
|
|
135
|
+
markPlatformError: () => markPlatformError,
|
|
136
|
+
markTransportFailure: () => markTransportFailure,
|
|
107
137
|
mergeMessages: () => mergeMessages,
|
|
108
138
|
planChangeWizardI18n: () => planChangeWizardI18n,
|
|
139
|
+
planVersionsResource: () => planVersionsResource,
|
|
140
|
+
plansResource: () => plansResource,
|
|
141
|
+
platformResources: () => platformResources,
|
|
109
142
|
postJson: () => postJson,
|
|
110
143
|
provideEntitlement: () => provideEntitlement,
|
|
144
|
+
readErrorCode: () => readErrorCode,
|
|
145
|
+
readErrorDetail: () => readErrorDetail,
|
|
146
|
+
readListPage: () => readListPage,
|
|
147
|
+
requestJson: () => requestJson,
|
|
148
|
+
requestJsonBody: () => requestJsonBody,
|
|
149
|
+
requireServerAnswer: () => requireServerAnswer,
|
|
111
150
|
resolveExtension: () => resolveExtension,
|
|
112
151
|
resolveLoginBranding: () => resolveLoginBranding,
|
|
113
152
|
resolveMessages: () => resolveMessages,
|
|
114
153
|
resolvePlans: () => resolvePlans,
|
|
154
|
+
resolveSuperAdminEndpoints: () => resolveSuperAdminEndpoints,
|
|
155
|
+
tenantsResource: () => tenantsResource,
|
|
156
|
+
toAdminError: () => toAdminError,
|
|
115
157
|
todayIsoDate: () => todayIsoDate,
|
|
116
158
|
trimTrailingSlashes: () => trimTrailingSlashes,
|
|
117
159
|
useActions: () => useActions,
|
|
118
160
|
useApiList: () => useApiList,
|
|
161
|
+
useAsyncAction: () => useAsyncAction,
|
|
162
|
+
useAsyncData: () => useAsyncData,
|
|
119
163
|
useAuditEntries: () => useAuditEntries,
|
|
120
164
|
useBatchColumns: () => useBatchColumns,
|
|
121
165
|
useBulkPublish: () => useBulkPublish,
|
|
@@ -137,6 +181,8 @@ __export(src_exports, {
|
|
|
137
181
|
usePlatformTenantActions: () => usePlatformTenantActions,
|
|
138
182
|
usePromotions: () => usePromotions,
|
|
139
183
|
usePublicBoot: () => usePublicBoot,
|
|
184
|
+
useResource: () => useResource,
|
|
185
|
+
useResourceList: () => useResourceList,
|
|
140
186
|
useSaMessages: () => useSaMessages,
|
|
141
187
|
useSaTheme: () => useSaTheme,
|
|
142
188
|
useSubscriptionDraft: () => useSubscriptionDraft,
|
|
@@ -160,6 +206,298 @@ module.exports = __toCommonJS(src_exports);
|
|
|
160
206
|
// src/client/version.ts
|
|
161
207
|
var ADMIN_UI_VERSION = "1.2.0";
|
|
162
208
|
|
|
209
|
+
// src/client/attach-cause.ts
|
|
210
|
+
function attachCause(error, cause) {
|
|
211
|
+
Object.defineProperty(error, "cause", {
|
|
212
|
+
value: cause,
|
|
213
|
+
configurable: true,
|
|
214
|
+
writable: true
|
|
215
|
+
});
|
|
216
|
+
return error;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// src/client/i18n/format.ts
|
|
220
|
+
var import_types = require("@saasicat/types");
|
|
221
|
+
function formatMessage(template, params) {
|
|
222
|
+
return (0, import_types.formatErrorMessage)(template, params);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// src/client/admin-error.ts
|
|
226
|
+
var ADMIN_ERROR = /* @__PURE__ */ Symbol.for("@saasicat/ui-vue/AdminError");
|
|
227
|
+
var PLATFORM_ERROR = /* @__PURE__ */ Symbol.for("@saasicat/ui-vue/PlatformError");
|
|
228
|
+
function markPlatformError(error) {
|
|
229
|
+
Object.defineProperty(error, PLATFORM_ERROR, { value: true });
|
|
230
|
+
}
|
|
231
|
+
function isPlatformError(value) {
|
|
232
|
+
return typeof value === "object" && value !== null && PLATFORM_ERROR in value;
|
|
233
|
+
}
|
|
234
|
+
var EMPTY_RESPONSE = /* @__PURE__ */ Symbol.for("@saasicat/ui-vue/EmptyResponse");
|
|
235
|
+
function markEmptyResponse(error) {
|
|
236
|
+
Object.defineProperty(error, EMPTY_RESPONSE, { value: true });
|
|
237
|
+
return error;
|
|
238
|
+
}
|
|
239
|
+
function isEmptyResponse(value) {
|
|
240
|
+
return typeof value === "object" && value !== null && EMPTY_RESPONSE in value;
|
|
241
|
+
}
|
|
242
|
+
var TRANSPORT_FAILURE = /* @__PURE__ */ Symbol.for("@saasicat/ui-vue/TransportFailure");
|
|
243
|
+
function markTransportFailure(error) {
|
|
244
|
+
if (typeof error === "object" && error !== null) {
|
|
245
|
+
try {
|
|
246
|
+
Object.defineProperty(error, TRANSPORT_FAILURE, { value: true });
|
|
247
|
+
} catch {
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return error;
|
|
251
|
+
}
|
|
252
|
+
function isTransportFailure(value) {
|
|
253
|
+
return typeof value === "object" && value !== null && TRANSPORT_FAILURE in value;
|
|
254
|
+
}
|
|
255
|
+
function describe(init) {
|
|
256
|
+
const status = init.status ?? 0;
|
|
257
|
+
const where = [init.method, init.url].filter(Boolean).join(" ");
|
|
258
|
+
const what = status > 0 ? `HTTP ${status}` : "request failed";
|
|
259
|
+
const head = where ? `${where} \u2192 ${what}` : what;
|
|
260
|
+
const code = init.code ? ` (${init.code})` : "";
|
|
261
|
+
return init.detail ? `${head}${code}: ${init.detail}` : `${head}${code}`;
|
|
262
|
+
}
|
|
263
|
+
var AdminError = class extends Error {
|
|
264
|
+
/** HTTP status. `0` means the request never produced one. */
|
|
265
|
+
status;
|
|
266
|
+
/** Machine-readable code from the response body, when it carried one. */
|
|
267
|
+
code;
|
|
268
|
+
/** Parsed response body, when there was one. */
|
|
269
|
+
body;
|
|
270
|
+
/** Requested URL, when known. */
|
|
271
|
+
url;
|
|
272
|
+
/** Request method, when known. */
|
|
273
|
+
method;
|
|
274
|
+
/**
|
|
275
|
+
* Text the failing side supplied — the only candidate for user-facing
|
|
276
|
+
* output. `undefined` when nothing was supplied.
|
|
277
|
+
*/
|
|
278
|
+
detail;
|
|
279
|
+
/** The request completed but the answer was unusable. See `AdminErrorInit`. */
|
|
280
|
+
emptyResponse;
|
|
281
|
+
/** The request never reached the server. See `AdminErrorInit`. */
|
|
282
|
+
transportFailure;
|
|
283
|
+
constructor(init = {}) {
|
|
284
|
+
super(init.message ?? describe(init));
|
|
285
|
+
if (init.cause !== void 0) attachCause(this, init.cause);
|
|
286
|
+
this.name = "AdminError";
|
|
287
|
+
this.status = init.status ?? 0;
|
|
288
|
+
this.code = init.code;
|
|
289
|
+
this.body = init.body;
|
|
290
|
+
this.url = init.url;
|
|
291
|
+
this.method = init.method;
|
|
292
|
+
this.detail = init.detail;
|
|
293
|
+
this.emptyResponse = init.emptyResponse ?? false;
|
|
294
|
+
this.transportFailure = init.transportFailure ?? false;
|
|
295
|
+
Object.defineProperty(this, ADMIN_ERROR, { value: true });
|
|
296
|
+
markPlatformError(this);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
function isAdminError(err) {
|
|
300
|
+
return typeof err === "object" && err !== null && ADMIN_ERROR in err;
|
|
301
|
+
}
|
|
302
|
+
function asRecord(value) {
|
|
303
|
+
return typeof value === "object" && value !== null ? value : void 0;
|
|
304
|
+
}
|
|
305
|
+
function asString(value) {
|
|
306
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
307
|
+
}
|
|
308
|
+
function isAxiosNoResponseError(value) {
|
|
309
|
+
const record = asRecord(value);
|
|
310
|
+
if (!record) return false;
|
|
311
|
+
return record.isAxiosError === true && record.request != null && record.response == null;
|
|
312
|
+
}
|
|
313
|
+
function readErrorDetail(body) {
|
|
314
|
+
const record = asRecord(body);
|
|
315
|
+
if (!record) return asString(body);
|
|
316
|
+
const message = record.message;
|
|
317
|
+
if (Array.isArray(message)) {
|
|
318
|
+
const parts = message.filter((part) => typeof part === "string");
|
|
319
|
+
return parts.length > 0 ? parts.join(", ") : void 0;
|
|
320
|
+
}
|
|
321
|
+
return asString(message) ?? asString(record.error) ?? asString(record.reason);
|
|
322
|
+
}
|
|
323
|
+
function readErrorCode(body) {
|
|
324
|
+
return asString(asRecord(body)?.code);
|
|
325
|
+
}
|
|
326
|
+
function toAdminError(err) {
|
|
327
|
+
if (isAdminError(err)) return err;
|
|
328
|
+
const record = asRecord(err);
|
|
329
|
+
const response = asRecord(record?.response);
|
|
330
|
+
if (response && typeof response.status === "number") {
|
|
331
|
+
const config = asRecord(record?.config);
|
|
332
|
+
const body = response.data;
|
|
333
|
+
return new AdminError({
|
|
334
|
+
status: response.status,
|
|
335
|
+
code: readErrorCode(body),
|
|
336
|
+
body,
|
|
337
|
+
url: asString(config?.url),
|
|
338
|
+
method: asString(config?.method)?.toUpperCase(),
|
|
339
|
+
// Only what the response actually said. `err.message` here is
|
|
340
|
+
// axios's own generated line ("Request failed with status code
|
|
341
|
+
// 401") — English, untranslated, and about the transport rather
|
|
342
|
+
// than about what went wrong. Letting it through as `detail` would
|
|
343
|
+
// make `adminErrorMessage` return it in preference to the
|
|
344
|
+
// localized wording, which is the whole point of that wording.
|
|
345
|
+
detail: readErrorDetail(body),
|
|
346
|
+
cause: err
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
if (record && typeof record.status === "number") {
|
|
350
|
+
const ours = isPlatformError(err);
|
|
351
|
+
return new AdminError({
|
|
352
|
+
status: record.status,
|
|
353
|
+
code: readErrorCode(record.body) ?? asString(record.code),
|
|
354
|
+
body: record.body,
|
|
355
|
+
// Same reasoning as above: `BundlesApiError`'s message is
|
|
356
|
+
// "Bundles API responded with HTTP 403", a diagnostic. It stays on
|
|
357
|
+
// `message`, where logs read it.
|
|
358
|
+
detail: readErrorDetail(record.body) ?? (ours ? void 0 : asString(record.message)),
|
|
359
|
+
// Asked of the throw site, not of the class: only the site that
|
|
360
|
+
// read the response knows whether one arrived. `status === 0 &&
|
|
361
|
+
// ours` was the same question put to the wrong witness — the
|
|
362
|
+
// loaders raise branded errors from a status the client chose, so
|
|
363
|
+
// a client that resolves a transport failure as `0` turned a
|
|
364
|
+
// failed GET into "the change may have been applied".
|
|
365
|
+
emptyResponse: isEmptyResponse(err),
|
|
366
|
+
transportFailure: isTransportFailure(err),
|
|
367
|
+
message: asString(record.message),
|
|
368
|
+
cause: err
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
if (err instanceof Error) {
|
|
372
|
+
const transport = isTransportFailure(err) || isAxiosNoResponseError(err);
|
|
373
|
+
const saidSomethingReadable = !transport && !(err instanceof TypeError);
|
|
374
|
+
return new AdminError({
|
|
375
|
+
detail: saidSomethingReadable ? asString(err.message) : void 0,
|
|
376
|
+
transportFailure: transport,
|
|
377
|
+
message: err.message,
|
|
378
|
+
cause: err
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
const text = asString(err) ?? asString(record?.message);
|
|
382
|
+
return new AdminError({
|
|
383
|
+
code: asString(record?.code),
|
|
384
|
+
detail: text,
|
|
385
|
+
message: text,
|
|
386
|
+
cause: err
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
function statusKey(status) {
|
|
390
|
+
if (status === 400 || status === 422) return "validation";
|
|
391
|
+
if (status === 401) return "unauthorized";
|
|
392
|
+
if (status === 403) return "forbidden";
|
|
393
|
+
if (status === 404) return "notFound";
|
|
394
|
+
if (status === 409) return "conflict";
|
|
395
|
+
if (status === 429) return "rateLimited";
|
|
396
|
+
if (status >= 500) return "server";
|
|
397
|
+
return void 0;
|
|
398
|
+
}
|
|
399
|
+
function adminErrorMessage(err, msgs) {
|
|
400
|
+
const error = toAdminError(err);
|
|
401
|
+
if (error.detail) return error.detail;
|
|
402
|
+
if (error.emptyResponse) return msgs.emptyResponse;
|
|
403
|
+
if (error.transportFailure) return msgs.network;
|
|
404
|
+
if (error.status > 0) {
|
|
405
|
+
const key = statusKey(error.status);
|
|
406
|
+
return key ? msgs[key] : formatMessage(msgs.httpStatus, { status: error.status });
|
|
407
|
+
}
|
|
408
|
+
return msgs.unexpected;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// src/client/http-json.ts
|
|
412
|
+
function serverAnswered(status) {
|
|
413
|
+
return status > 0;
|
|
414
|
+
}
|
|
415
|
+
function requireServerAnswer(status, method, url, raise) {
|
|
416
|
+
if (serverAnswered(status)) return;
|
|
417
|
+
throw markTransportFailure(
|
|
418
|
+
raise(`${method} ${url} produced no HTTP status \u2014 the request did not reach the server`)
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
var HttpJsonError = AdminError;
|
|
422
|
+
async function readErrorBody(res) {
|
|
423
|
+
try {
|
|
424
|
+
return await res.json();
|
|
425
|
+
} catch {
|
|
426
|
+
return void 0;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
async function failed(res, method, url) {
|
|
430
|
+
const body = await readErrorBody(res);
|
|
431
|
+
return new AdminError({
|
|
432
|
+
status: res.status,
|
|
433
|
+
// Read here rather than left to the status: `status: 0` reaches
|
|
434
|
+
// `adminErrorMessage` as "nothing is known about this failure", and
|
|
435
|
+
// this seam knows better — it held the response.
|
|
436
|
+
transportFailure: !serverAnswered(res.status),
|
|
437
|
+
code: readErrorCode(body),
|
|
438
|
+
body,
|
|
439
|
+
url,
|
|
440
|
+
method,
|
|
441
|
+
// Shared with `toAdminError` on purpose: a NestJS `ValidationPipe`
|
|
442
|
+
// rejection carries `message` as an array, and reading it here in a
|
|
443
|
+
// second, narrower way is how those constraints went missing.
|
|
444
|
+
detail: readErrorDetail(body)
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
function trimTrailingSlashes(url) {
|
|
448
|
+
let end = url.length;
|
|
449
|
+
while (end > 0 && url[end - 1] === "/") end--;
|
|
450
|
+
return url.slice(0, end);
|
|
451
|
+
}
|
|
452
|
+
async function getJson(http, url) {
|
|
453
|
+
const res = await http(url);
|
|
454
|
+
if (res.status < 200 || res.status >= 300) {
|
|
455
|
+
throw await failed(res, "GET", url);
|
|
456
|
+
}
|
|
457
|
+
return await res.json();
|
|
458
|
+
}
|
|
459
|
+
async function postJson(http, url, body) {
|
|
460
|
+
const res = await http(url, {
|
|
461
|
+
method: "POST",
|
|
462
|
+
headers: { "Content-Type": "application/json" },
|
|
463
|
+
body: JSON.stringify(body)
|
|
464
|
+
});
|
|
465
|
+
if (res.status < 200 || res.status >= 300) {
|
|
466
|
+
throw await failed(res, "POST", url);
|
|
467
|
+
}
|
|
468
|
+
return await res.json();
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// src/client/http/fetch-http-client.ts
|
|
472
|
+
var ABSOLUTE_URL = /^[a-z][a-z0-9+.-]*:|^\/\//i;
|
|
473
|
+
function resolveUrl(url, baseUrl) {
|
|
474
|
+
if (!baseUrl || ABSOLUTE_URL.test(url)) return url;
|
|
475
|
+
return `${trimTrailingSlashes(baseUrl)}${url.startsWith("/") ? "" : "/"}${url}`;
|
|
476
|
+
}
|
|
477
|
+
function createFetchHttpClient(options = {}) {
|
|
478
|
+
return async (url, init) => {
|
|
479
|
+
const headers = new Headers(await options.headers?.());
|
|
480
|
+
if (!headers.has("Accept")) headers.set("Accept", "application/json");
|
|
481
|
+
for (const [name, value] of Object.entries(init?.headers ?? {})) {
|
|
482
|
+
headers.set(name, value);
|
|
483
|
+
}
|
|
484
|
+
if (init?.body !== void 0 && !headers.has("Content-Type")) {
|
|
485
|
+
headers.set("Content-Type", "application/json");
|
|
486
|
+
}
|
|
487
|
+
let response;
|
|
488
|
+
try {
|
|
489
|
+
response = await fetch(resolveUrl(url, options.baseUrl), {
|
|
490
|
+
method: init?.method ?? "GET",
|
|
491
|
+
headers,
|
|
492
|
+
body: init?.body
|
|
493
|
+
});
|
|
494
|
+
} catch (err) {
|
|
495
|
+
throw markTransportFailure(err);
|
|
496
|
+
}
|
|
497
|
+
return response;
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
|
|
163
501
|
// src/client/types.ts
|
|
164
502
|
var NOOP_KV_STORE = {
|
|
165
503
|
get: () => null,
|
|
@@ -201,51 +539,326 @@ function resolveLocalStorage() {
|
|
|
201
539
|
}
|
|
202
540
|
}
|
|
203
541
|
function defaultHttpClient() {
|
|
204
|
-
return (
|
|
542
|
+
return createFetchHttpClient();
|
|
205
543
|
}
|
|
206
544
|
|
|
207
|
-
// src/client/http-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
545
|
+
// src/client/http/axios-http-client.ts
|
|
546
|
+
function stripped(url, prefixes) {
|
|
547
|
+
for (const prefix of prefixes) {
|
|
548
|
+
const trimmed = trimTrailingSlashes(prefix);
|
|
549
|
+
if (!trimmed || !url.startsWith(trimmed)) continue;
|
|
550
|
+
const rest = url.slice(trimmed.length);
|
|
551
|
+
if (rest === "") return "/";
|
|
552
|
+
if (rest.startsWith("/")) return rest;
|
|
553
|
+
if (rest.startsWith("?") || rest.startsWith("#")) return `/${rest}`;
|
|
554
|
+
}
|
|
555
|
+
return url;
|
|
556
|
+
}
|
|
557
|
+
function headerReader(headers) {
|
|
558
|
+
const record = typeof headers === "object" && headers !== null ? headers : {};
|
|
559
|
+
const lowered = /* @__PURE__ */ new Map();
|
|
560
|
+
for (const [key, value] of Object.entries(record)) lowered.set(key.toLowerCase(), value);
|
|
561
|
+
return (name) => {
|
|
562
|
+
const value = lowered.get(name.toLowerCase());
|
|
563
|
+
return value == null ? null : String(value);
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
function bodyIsRaw(response, declared) {
|
|
567
|
+
if (declared !== "auto") return declared === "raw";
|
|
568
|
+
if (response.data === "") return true;
|
|
569
|
+
const config = response.config;
|
|
570
|
+
if (config?.transformResponse === null) return true;
|
|
571
|
+
if (Array.isArray(config?.transformResponse) && config.transformResponse.length === 0) {
|
|
572
|
+
return true;
|
|
214
573
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
574
|
+
const responseType = config?.responseType;
|
|
575
|
+
if (responseType === "json") return false;
|
|
576
|
+
if (typeof responseType === "string" && responseType !== "") return true;
|
|
577
|
+
const transitional = config?.transitional;
|
|
578
|
+
return typeof transitional === "object" && transitional !== null && transitional.forcedJSONParsing === false;
|
|
579
|
+
}
|
|
580
|
+
async function readBody(data) {
|
|
581
|
+
if (typeof data === "string" || data === null || data === void 0) return data;
|
|
582
|
+
if (data instanceof ArrayBuffer) return new TextDecoder().decode(data);
|
|
583
|
+
if (ArrayBuffer.isView(data)) return new TextDecoder().decode(data);
|
|
584
|
+
const maybe = data;
|
|
585
|
+
if (typeof maybe.pipe === "function" || typeof maybe.getReader === "function") {
|
|
586
|
+
throw new TypeError(
|
|
587
|
+
"createAxiosHttpClient cannot read a streamed body: responseType 'stream' can be consumed only once, and never synchronously. Use 'arraybuffer' or the default for the instance the platform receives."
|
|
588
|
+
);
|
|
224
589
|
}
|
|
590
|
+
if (typeof maybe.text === "function") return maybe.text.call(data);
|
|
591
|
+
return data;
|
|
225
592
|
}
|
|
226
|
-
function
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
593
|
+
function adapt(response, declared) {
|
|
594
|
+
return {
|
|
595
|
+
status: response.status,
|
|
596
|
+
headers: { get: headerReader(response.headers) },
|
|
597
|
+
json: async () => {
|
|
598
|
+
const data = await readBody(response.data);
|
|
599
|
+
if (typeof data !== "string") return data;
|
|
600
|
+
return bodyIsRaw({ ...response, data }, declared) ? JSON.parse(data) : data;
|
|
601
|
+
},
|
|
602
|
+
text: async () => {
|
|
603
|
+
const data = await readBody(response.data);
|
|
604
|
+
return typeof data === "string" ? data : JSON.stringify(data);
|
|
605
|
+
}
|
|
606
|
+
};
|
|
230
607
|
}
|
|
231
|
-
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
608
|
+
function createAxiosHttpClient(instance, options = {}) {
|
|
609
|
+
const prefixes = typeof options.stripPrefix === "string" ? [options.stripPrefix] : options.stripPrefix ?? [];
|
|
610
|
+
const responseBody = options.responseBody ?? "auto";
|
|
611
|
+
return async (url, init) => {
|
|
612
|
+
try {
|
|
613
|
+
return adapt(
|
|
614
|
+
await instance.request({
|
|
615
|
+
url: stripped(url, prefixes),
|
|
616
|
+
method: (init?.method ?? "GET").toUpperCase(),
|
|
617
|
+
headers: init?.headers,
|
|
618
|
+
data: init?.body
|
|
619
|
+
}),
|
|
620
|
+
responseBody
|
|
621
|
+
);
|
|
622
|
+
} catch (err) {
|
|
623
|
+
const response = err?.response;
|
|
624
|
+
if (response && typeof response.status === "number") {
|
|
625
|
+
return adapt(response, responseBody);
|
|
626
|
+
}
|
|
627
|
+
throw isAxiosNoResponseError(err) ? markTransportFailure(err) : err;
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// src/client/resources/define-resource.ts
|
|
633
|
+
function defineResource(key, ops, options = {}) {
|
|
634
|
+
return { key, ops, projectScoped: options.projectScoped ?? false };
|
|
635
|
+
}
|
|
636
|
+
function requireProjectKey(def, ctx) {
|
|
637
|
+
if (!def.projectScoped) return;
|
|
638
|
+
if (typeof ctx.projectKey === "string" && ctx.projectKey.trim() !== "") return;
|
|
639
|
+
throw new Error(
|
|
640
|
+
`Resource "${def.key}" is project-scoped, but the context it is bound to names no project. Its requests would go out as \`?projectKey=\`, which the admin API answers for no project at all: the catalogue reads as empty and the first create fails validation instead. Name the project this admin administers \u2014 \`endpoints.projectKey\` for createSuperAdminApp(), \`context.projectKey\` for createResourceRegistry().`
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
function bindResource(def, http, ctx) {
|
|
644
|
+
const readContext = typeof ctx === "function" ? ctx : () => ctx;
|
|
645
|
+
if (def.projectScoped) requireProjectKey(def, readContext());
|
|
646
|
+
const bound = {};
|
|
647
|
+
for (const [name, op] of Object.entries(def.ops)) {
|
|
648
|
+
bound[name] = async (...args) => {
|
|
649
|
+
const context = readContext();
|
|
650
|
+
requireProjectKey(def, context);
|
|
651
|
+
return op(http, context, ...args);
|
|
652
|
+
};
|
|
235
653
|
}
|
|
236
|
-
return
|
|
654
|
+
return bound;
|
|
237
655
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
656
|
+
|
|
657
|
+
// src/client/resources/resource-request.ts
|
|
658
|
+
async function requestJson(http, url, init = {}) {
|
|
659
|
+
const method = init.method ?? "GET";
|
|
660
|
+
const response = await http(url, {
|
|
661
|
+
method,
|
|
662
|
+
headers: { "content-type": "application/json", ...init.headers },
|
|
663
|
+
body: init.body === void 0 ? void 0 : JSON.stringify(init.body)
|
|
243
664
|
});
|
|
244
|
-
|
|
245
|
-
|
|
665
|
+
requireServerAnswer(
|
|
666
|
+
response.status,
|
|
667
|
+
method,
|
|
668
|
+
url,
|
|
669
|
+
(diagnostic) => new AdminError({ status: response.status, url, method, message: diagnostic })
|
|
670
|
+
);
|
|
671
|
+
if (response.status === 204) return null;
|
|
672
|
+
const body = await response.json().catch(() => null);
|
|
673
|
+
if (response.status >= 400) {
|
|
674
|
+
throw new AdminError({
|
|
675
|
+
status: response.status,
|
|
676
|
+
code: readErrorCode(body),
|
|
677
|
+
body,
|
|
678
|
+
// The third place this had to be read, and the second time it was
|
|
679
|
+
// read a narrower way: without `detail` a page migrating onto these
|
|
680
|
+
// resources loses "Plan already exists" and shows the generic
|
|
681
|
+
// status sentence instead. One decision, one function.
|
|
682
|
+
detail: readErrorDetail(body),
|
|
683
|
+
url,
|
|
684
|
+
method
|
|
685
|
+
});
|
|
246
686
|
}
|
|
247
|
-
return
|
|
687
|
+
return body;
|
|
688
|
+
}
|
|
689
|
+
async function requestJsonBody(http, url, what, init = {}) {
|
|
690
|
+
const body = await requestJson(http, url, init);
|
|
691
|
+
if (body === null || body === void 0) {
|
|
692
|
+
throw new AdminError({
|
|
693
|
+
status: 0,
|
|
694
|
+
url,
|
|
695
|
+
method: init.method ?? "GET",
|
|
696
|
+
// `what` is a diagnostic — "Create returned no body" — so it goes
|
|
697
|
+
// on `message`, not `detail`. As `detail` it out-ranked the
|
|
698
|
+
// localized wording and the operator read an English internal
|
|
699
|
+
// string instead of being told the change may already have landed.
|
|
700
|
+
message: what,
|
|
701
|
+
emptyResponse: true
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
return body;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// src/client/resources/list-resource.ts
|
|
708
|
+
var LIST_FIRST_PAGE = 1;
|
|
709
|
+
var LIST_PAGE_SIZE_DEFAULT = 50;
|
|
710
|
+
var LIST_PAGE_SIZE_MAX = 200;
|
|
711
|
+
var LIST_PAGINATION_PARAMS = ["page", "pageSize"];
|
|
712
|
+
function isSentInQuery(value) {
|
|
713
|
+
return value !== void 0 && value !== null && value !== "";
|
|
714
|
+
}
|
|
715
|
+
function appendFilter(params, filter) {
|
|
716
|
+
for (const [key, value] of Object.entries(filter ?? {})) {
|
|
717
|
+
if (!isSentInQuery(value)) continue;
|
|
718
|
+
params.set(key, String(value));
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
function filterQueryString(filter) {
|
|
722
|
+
const params = new URLSearchParams();
|
|
723
|
+
appendFilter(params, filter);
|
|
724
|
+
const query = params.toString();
|
|
725
|
+
return query ? `?${query}` : "";
|
|
726
|
+
}
|
|
727
|
+
function listUrl(endpoint, query = {}) {
|
|
728
|
+
const params = new URLSearchParams();
|
|
729
|
+
params.set("page", String(query.page ?? LIST_FIRST_PAGE));
|
|
730
|
+
params.set("pageSize", String(query.pageSize ?? LIST_PAGE_SIZE_DEFAULT));
|
|
731
|
+
appendFilter(params, query.filter);
|
|
732
|
+
return `${endpoint}${endpoint.includes("?") ? "&" : "?"}${params.toString()}`;
|
|
733
|
+
}
|
|
734
|
+
function readListPage(raw) {
|
|
735
|
+
if (Array.isArray(raw)) return { items: raw, total: raw.length };
|
|
736
|
+
if (raw === null || typeof raw !== "object") return { items: [], total: 0 };
|
|
737
|
+
const body = raw;
|
|
738
|
+
const page = {
|
|
739
|
+
items: Array.isArray(body.items) ? body.items : []
|
|
740
|
+
};
|
|
741
|
+
if (typeof body.total === "number") page.total = body.total;
|
|
742
|
+
if (typeof body.page === "number") page.page = body.page;
|
|
743
|
+
if (typeof body.pageSize === "number") page.pageSize = body.pageSize;
|
|
744
|
+
return page;
|
|
248
745
|
}
|
|
746
|
+
function clampListPage(page) {
|
|
747
|
+
return Math.max(LIST_FIRST_PAGE, Math.floor(page));
|
|
748
|
+
}
|
|
749
|
+
function clampListPageSize(size) {
|
|
750
|
+
return Math.min(LIST_PAGE_SIZE_MAX, Math.max(1, Math.floor(size)));
|
|
751
|
+
}
|
|
752
|
+
function defineListOp(endpoint) {
|
|
753
|
+
return async (http, ctx, query) => readListPage(await requestJson(http, listUrl(endpoint(ctx), query)));
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// src/client/resources/plans.resource.ts
|
|
757
|
+
function plansUrl(ctx) {
|
|
758
|
+
return `${ctx.apiBase}/catalog/plans`;
|
|
759
|
+
}
|
|
760
|
+
function versionsUrl(ctx) {
|
|
761
|
+
return `${ctx.apiBase}/catalog/plan-versions`;
|
|
762
|
+
}
|
|
763
|
+
function scoped(url, ctx) {
|
|
764
|
+
return `${url}?projectKey=${encodeURIComponent(ctx.projectKey)}`;
|
|
765
|
+
}
|
|
766
|
+
var plansResource = defineResource(
|
|
767
|
+
"plans",
|
|
768
|
+
{
|
|
769
|
+
list: async (http, ctx) => await requestJson(http, scoped(plansUrl(ctx), ctx)) ?? [],
|
|
770
|
+
tenantCounts: async (http, ctx) => await requestJson(
|
|
771
|
+
http,
|
|
772
|
+
scoped(`${plansUrl(ctx)}/tenant-counts`, ctx)
|
|
773
|
+
) ?? {},
|
|
774
|
+
/**
|
|
775
|
+
* The project comes from the context, not from the caller.
|
|
776
|
+
*
|
|
777
|
+
* The endpoint takes it in the body, and the composable this replaces
|
|
778
|
+
* made that the caller's job — so a page had to keep hold of the
|
|
779
|
+
* project it was administering just to create in it, and `list()` and
|
|
780
|
+
* `create()` read two different sources for one value. A stale one
|
|
781
|
+
* creates a plan outside the project the list is showing.
|
|
782
|
+
*/
|
|
783
|
+
create: (http, ctx, data) => requestJsonBody(http, plansUrl(ctx), "Create returned no body", {
|
|
784
|
+
method: "POST",
|
|
785
|
+
body: { ...data, projectKey: ctx.projectKey }
|
|
786
|
+
}),
|
|
787
|
+
update: (http, ctx, planId, data) => requestJsonBody(
|
|
788
|
+
http,
|
|
789
|
+
`${plansUrl(ctx)}/${planId}`,
|
|
790
|
+
"Update returned no body",
|
|
791
|
+
{
|
|
792
|
+
method: "PATCH",
|
|
793
|
+
body: data
|
|
794
|
+
}
|
|
795
|
+
),
|
|
796
|
+
/** Marks the plan deleted. Fails while it still has versions. */
|
|
797
|
+
softDelete: async (http, ctx, planId) => {
|
|
798
|
+
await requestJson(http, `${plansUrl(ctx)}/${planId}`, { method: "DELETE" });
|
|
799
|
+
},
|
|
800
|
+
/** Removes the plan for good. 422 `PLAN_HAS_VERSIONS` when it cannot. */
|
|
801
|
+
hardDelete: async (http, ctx, planId) => {
|
|
802
|
+
await requestJson(http, `${plansUrl(ctx)}/${planId}/purge`, { method: "DELETE" });
|
|
803
|
+
}
|
|
804
|
+
},
|
|
805
|
+
{ projectScoped: true }
|
|
806
|
+
);
|
|
807
|
+
var planVersionsResource = defineResource("planVersions", {
|
|
808
|
+
listForPlan: async (http, ctx, planId) => await requestJson(http, `${plansUrl(ctx)}/${planId}/versions`) ?? [],
|
|
809
|
+
createDraft: (http, ctx, planId, data) => requestJsonBody(
|
|
810
|
+
http,
|
|
811
|
+
`${plansUrl(ctx)}/${planId}/versions`,
|
|
812
|
+
"CreateDraft returned no body",
|
|
813
|
+
{ method: "POST", body: data }
|
|
814
|
+
),
|
|
815
|
+
updateDraft: (http, ctx, versionId, data) => requestJsonBody(
|
|
816
|
+
http,
|
|
817
|
+
`${versionsUrl(ctx)}/${versionId}`,
|
|
818
|
+
"UpdateDraft returned no body",
|
|
819
|
+
{ method: "PATCH", body: data }
|
|
820
|
+
),
|
|
821
|
+
publish: (http, ctx, versionId, options = {}) => requestJsonBody(
|
|
822
|
+
http,
|
|
823
|
+
`${versionsUrl(ctx)}/${versionId}/publish`,
|
|
824
|
+
"Publish returned no body",
|
|
825
|
+
{ method: "POST", body: options }
|
|
826
|
+
),
|
|
827
|
+
discardDraft: async (http, ctx, versionId) => {
|
|
828
|
+
await requestJson(http, `${versionsUrl(ctx)}/${versionId}`, { method: "DELETE" });
|
|
829
|
+
},
|
|
830
|
+
/**
|
|
831
|
+
* Ends a live version on `endsAt`, with no successor. Idempotent — a
|
|
832
|
+
* second call with another date overwrites the first.
|
|
833
|
+
*
|
|
834
|
+
* `endsAt` is the date itself, not a payload: the endpoint's body is
|
|
835
|
+
* `{ endsAt }` and wrapping it is this operation's job, not the caller's.
|
|
836
|
+
*/
|
|
837
|
+
terminate: (http, ctx, versionId, endsAt) => requestJsonBody(
|
|
838
|
+
http,
|
|
839
|
+
`${versionsUrl(ctx)}/${versionId}/terminate`,
|
|
840
|
+
"Terminate returned no body",
|
|
841
|
+
{ method: "POST", body: { endsAt } }
|
|
842
|
+
)
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
// src/client/resources/tenants.resource.ts
|
|
846
|
+
var tenantsResource = defineResource("tenants", {
|
|
847
|
+
list: defineListOp((ctx) => `${ctx.apiBase}/tenants`)
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
// src/client/resources/audit.resource.ts
|
|
851
|
+
var auditResource = defineResource("audit", {
|
|
852
|
+
list: defineListOp((ctx) => `${ctx.apiBase}/audit`)
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
// src/client/resources/index.ts
|
|
856
|
+
var platformResources = {
|
|
857
|
+
plans: plansResource,
|
|
858
|
+
planVersions: planVersionsResource,
|
|
859
|
+
tenants: tenantsResource,
|
|
860
|
+
audit: auditResource
|
|
861
|
+
};
|
|
249
862
|
|
|
250
863
|
// src/client/boot-loader.ts
|
|
251
864
|
var BootLoadError = class extends Error {
|
|
@@ -253,6 +866,7 @@ var BootLoadError = class extends Error {
|
|
|
253
866
|
super(message);
|
|
254
867
|
this.status = status;
|
|
255
868
|
this.name = "BootLoadError";
|
|
869
|
+
markPlatformError(this);
|
|
256
870
|
}
|
|
257
871
|
status;
|
|
258
872
|
};
|
|
@@ -270,19 +884,27 @@ var BootLoader = class {
|
|
|
270
884
|
}
|
|
271
885
|
async load() {
|
|
272
886
|
const res = await this.http(this.endpoint);
|
|
887
|
+
requireServerAnswer(
|
|
888
|
+
res.status,
|
|
889
|
+
"GET",
|
|
890
|
+
this.endpoint,
|
|
891
|
+
(diagnostic) => new BootLoadError(res.status, diagnostic)
|
|
892
|
+
);
|
|
273
893
|
if (res.status !== 200) {
|
|
274
|
-
throw new BootLoadError(res.status, `Boot
|
|
894
|
+
throw new BootLoadError(res.status, `Boot endpoint responded with HTTP ${res.status}`);
|
|
275
895
|
}
|
|
276
896
|
return await res.json();
|
|
277
897
|
}
|
|
278
898
|
};
|
|
279
899
|
|
|
280
900
|
// src/client/manifest-loader.ts
|
|
901
|
+
var NOT_MODIFIED = 304;
|
|
281
902
|
var ManifestLoadError = class extends Error {
|
|
282
903
|
constructor(status, message) {
|
|
283
904
|
super(message);
|
|
284
905
|
this.status = status;
|
|
285
906
|
this.name = "ManifestLoadError";
|
|
907
|
+
markPlatformError(this);
|
|
286
908
|
}
|
|
287
909
|
status;
|
|
288
910
|
};
|
|
@@ -310,24 +932,48 @@ var ManifestLoader = class {
|
|
|
310
932
|
/**
|
|
311
933
|
* Loads the current manifest. On a cache hit (304) the cached
|
|
312
934
|
* body is returned — otherwise the fresh server body.
|
|
935
|
+
*
|
|
936
|
+
* A 304 with no usable cached body is not a failure to report but one to
|
|
937
|
+
* undo: the request only carried `If-None-Match` because this loader put
|
|
938
|
+
* the ETag there, and the cache it validated against is this loader's own.
|
|
939
|
+
* So it drops the pair and asks once more without the header, which is the
|
|
940
|
+
* same recovery a person would have had to trigger by hand — except that
|
|
941
|
+
* the person reading an admin screen has neither this loader nor a
|
|
942
|
+
* console. Exactly one retry: a 304 to a request that asked nothing
|
|
943
|
+
* conditional is a server fault, and no repetition fixes it.
|
|
313
944
|
*/
|
|
314
945
|
async load() {
|
|
315
946
|
const cachedEtag = this.storage.get(this.etagKey);
|
|
947
|
+
const res = await this.request(cachedEtag);
|
|
948
|
+
if (res.status !== NOT_MODIFIED) return this.acceptFresh(res);
|
|
949
|
+
const cached = this.readCachedBody();
|
|
950
|
+
if (cached) return cached.body;
|
|
951
|
+
if (cachedEtag) {
|
|
952
|
+
this.clearCache();
|
|
953
|
+
const fresh = await this.request(null);
|
|
954
|
+
if (fresh.status !== NOT_MODIFIED) return this.acceptFresh(fresh);
|
|
955
|
+
}
|
|
956
|
+
throw new ManifestLoadError(
|
|
957
|
+
NOT_MODIFIED,
|
|
958
|
+
"Manifest endpoint answered HTTP 304 to a request without If-None-Match"
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
/** Sends the manifest GET, conditional when an ETag is supplied. */
|
|
962
|
+
request(etag) {
|
|
316
963
|
const headers = {};
|
|
317
964
|
const token = this.getAuthToken?.();
|
|
318
965
|
if (token) headers.Authorization = `Bearer ${token}`;
|
|
319
|
-
if (
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}
|
|
966
|
+
if (etag) headers["If-None-Match"] = etag;
|
|
967
|
+
return this.http(this.endpoint, { method: "GET", headers });
|
|
968
|
+
}
|
|
969
|
+
/** Takes a non-304 response as the current manifest and caches it. */
|
|
970
|
+
async acceptFresh(res) {
|
|
971
|
+
requireServerAnswer(
|
|
972
|
+
res.status,
|
|
973
|
+
"GET",
|
|
974
|
+
this.endpoint,
|
|
975
|
+
(diagnostic) => new ManifestLoadError(res.status, diagnostic)
|
|
976
|
+
);
|
|
331
977
|
if (res.status !== 200) {
|
|
332
978
|
throw new ManifestLoadError(
|
|
333
979
|
res.status,
|
|
@@ -463,7 +1109,7 @@ var DEFAULT_STANDARD_PAGE_ROUTES = {
|
|
|
463
1109
|
platformEmailHistory: "/admin/platform-email-history"
|
|
464
1110
|
};
|
|
465
1111
|
function isSupportedStandardPageKey(key) {
|
|
466
|
-
return Object.
|
|
1112
|
+
return Object.prototype.hasOwnProperty.call(DEFAULT_STANDARD_PAGE_ROUTES, key);
|
|
467
1113
|
}
|
|
468
1114
|
var DEFAULT_ICONS = {
|
|
469
1115
|
dashboard: "dashboard",
|
|
@@ -662,7 +1308,7 @@ var ActionRegistry = class {
|
|
|
662
1308
|
// src/client/batch-column-fetcher.ts
|
|
663
1309
|
var BatchColumnDriftError = class extends Error {
|
|
664
1310
|
constructor(column, reason) {
|
|
665
|
-
super(`
|
|
1311
|
+
super(`Column "${column.key}": ${reason}`);
|
|
666
1312
|
this.column = column;
|
|
667
1313
|
this.name = "BatchColumnDriftError";
|
|
668
1314
|
}
|
|
@@ -705,7 +1351,7 @@ var BatchColumnFetcher = class {
|
|
|
705
1351
|
const res = await this.http(url, { method: "GET", headers });
|
|
706
1352
|
if (res.status !== 200) {
|
|
707
1353
|
throw new Error(
|
|
708
|
-
`
|
|
1354
|
+
`Column "${column.key}" \u2014 endpoint ${column.endpoint} responded with HTTP ${res.status}`
|
|
709
1355
|
);
|
|
710
1356
|
}
|
|
711
1357
|
const body = await res.json();
|
|
@@ -781,10 +1427,10 @@ function createAdminResourceClient(options) {
|
|
|
781
1427
|
tenantsEndpoint,
|
|
782
1428
|
loadTenantDetail: (slug) => getJson2(`${tenantsEndpoint}/${encodeURIComponent(slug)}`),
|
|
783
1429
|
loadUsers: (filter) => getJson2(
|
|
784
|
-
`${base}/users${
|
|
1430
|
+
`${base}/users${filterQueryString({ q: filter.q, tenant: filter.tenant })}`
|
|
785
1431
|
),
|
|
786
1432
|
loadAudit: (filter) => getJson2(
|
|
787
|
-
`${base}/audit${
|
|
1433
|
+
`${base}/audit${filterQueryString({
|
|
788
1434
|
actor: filter.actor,
|
|
789
1435
|
action: filter.action,
|
|
790
1436
|
entity: filter.entity,
|
|
@@ -794,7 +1440,7 @@ function createAdminResourceClient(options) {
|
|
|
794
1440
|
),
|
|
795
1441
|
loadSubscriptions: () => getJson2(`${base}/subscriptions`),
|
|
796
1442
|
loadPromos: (filter) => getJson2(
|
|
797
|
-
`${base}/promo-codes${
|
|
1443
|
+
`${base}/promo-codes${filterQueryString({
|
|
798
1444
|
search: filter.search,
|
|
799
1445
|
status: filter.status
|
|
800
1446
|
})}`
|
|
@@ -819,21 +1465,6 @@ function assertSuccess(status, method, url) {
|
|
|
819
1465
|
throw new Error(`${method} ${url} \u2192 HTTP ${status}`);
|
|
820
1466
|
}
|
|
821
1467
|
}
|
|
822
|
-
function queryString(params) {
|
|
823
|
-
const search = new URLSearchParams();
|
|
824
|
-
for (const [key, value2] of Object.entries(params)) {
|
|
825
|
-
if (value2 === void 0 || value2 === null || value2 === "") continue;
|
|
826
|
-
search.set(key, String(value2));
|
|
827
|
-
}
|
|
828
|
-
const value = search.toString();
|
|
829
|
-
return value ? `?${value}` : "";
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
// src/client/i18n/format.ts
|
|
833
|
-
var import_types4 = require("@saasicat/types");
|
|
834
|
-
function formatMessage(template, params) {
|
|
835
|
-
return (0, import_types4.formatErrorMessage)(template, params);
|
|
836
|
-
}
|
|
837
1468
|
|
|
838
1469
|
// src/client/i18n/currency.ts
|
|
839
1470
|
var DEFAULT_CURRENCY = "EUR";
|
|
@@ -1532,9 +2163,6 @@ var discoveryMessages = defineMessages(
|
|
|
1532
2163
|
replacedBy: "ersetzt durch {key}",
|
|
1533
2164
|
replaces: "ersetzt: {keys}",
|
|
1534
2165
|
reapproveEmphasis: "erneut freigeben",
|
|
1535
|
-
errorDiscoveryHttp: "Discovery-Endpoint antwortete mit HTTP {status}",
|
|
1536
|
-
errorRescanHttp: "Discovery-Rescan antwortete mit HTTP {status}",
|
|
1537
|
-
errorCatalogHttp: "Catalog-Entries-API antwortete mit HTTP {status}",
|
|
1538
2166
|
capList: {
|
|
1539
2167
|
orphanFeature: "Feature im Katalog ohne implementierende Capability \u2014 blockiert im blocking-Strict-Mode das Plan-Publish.",
|
|
1540
2168
|
removedFromCode: "aus Code entfernt"
|
|
@@ -1636,9 +2264,6 @@ var discoveryMessages = defineMessages(
|
|
|
1636
2264
|
replacedBy: "replaced by {key}",
|
|
1637
2265
|
replaces: "replaces: {keys}",
|
|
1638
2266
|
reapproveEmphasis: "approve again",
|
|
1639
|
-
errorDiscoveryHttp: "Discovery endpoint responded with HTTP {status}",
|
|
1640
|
-
errorRescanHttp: "Discovery rescan responded with HTTP {status}",
|
|
1641
|
-
errorCatalogHttp: "Catalog entries API responded with HTTP {status}",
|
|
1642
2267
|
capList: {
|
|
1643
2268
|
orphanFeature: "Feature in the catalog without an implementing capability \u2014 blocks plan publishing in blocking strict mode.",
|
|
1644
2269
|
removedFromCode: "removed from code"
|
|
@@ -1843,6 +2468,36 @@ var emailMessages = defineMessages(
|
|
|
1843
2468
|
}
|
|
1844
2469
|
);
|
|
1845
2470
|
|
|
2471
|
+
// src/client/i18n/messages/errors.ts
|
|
2472
|
+
var errorsMessages = defineMessages(
|
|
2473
|
+
{
|
|
2474
|
+
network: "Die Anfrage konnte nicht abgeschlossen werden. Bitte Verbindung pr\xFCfen.",
|
|
2475
|
+
emptyResponse: "Der Server hat nichts zur\xFCckgegeben. Bitte pr\xFCfen, ob die \xC4nderung angekommen ist.",
|
|
2476
|
+
unauthorized: "Die Sitzung ist abgelaufen. Bitte erneut anmelden.",
|
|
2477
|
+
forbidden: "F\xFCr diese Aktion fehlt die Berechtigung.",
|
|
2478
|
+
notFound: "Der angeforderte Eintrag existiert nicht mehr.",
|
|
2479
|
+
conflict: "Der Eintrag wurde zwischenzeitlich ge\xE4ndert. Bitte neu laden.",
|
|
2480
|
+
validation: "Die Eingaben wurden nicht akzeptiert.",
|
|
2481
|
+
rateLimited: "Zu viele Anfragen. Bitte kurz warten und erneut versuchen.",
|
|
2482
|
+
server: "Der Server konnte die Anfrage nicht verarbeiten.",
|
|
2483
|
+
httpStatus: "Die Anfrage ist fehlgeschlagen (HTTP {status}).",
|
|
2484
|
+
unexpected: "Es ist ein unerwarteter Fehler aufgetreten. Bitte erneut versuchen."
|
|
2485
|
+
},
|
|
2486
|
+
{
|
|
2487
|
+
network: "The request could not be completed. Please check your connection.",
|
|
2488
|
+
emptyResponse: "The server returned nothing. Please check whether the change was applied.",
|
|
2489
|
+
unauthorized: "Your session has expired. Please sign in again.",
|
|
2490
|
+
forbidden: "You do not have permission for this action.",
|
|
2491
|
+
notFound: "The requested entry no longer exists.",
|
|
2492
|
+
conflict: "The entry changed in the meantime. Please reload.",
|
|
2493
|
+
validation: "The input was not accepted.",
|
|
2494
|
+
rateLimited: "Too many requests. Please wait a moment and try again.",
|
|
2495
|
+
server: "The server could not process the request.",
|
|
2496
|
+
httpStatus: "The request failed (HTTP {status}).",
|
|
2497
|
+
unexpected: "Something went wrong. Please try again."
|
|
2498
|
+
}
|
|
2499
|
+
);
|
|
2500
|
+
|
|
1846
2501
|
// src/client/i18n/messages/marketing.ts
|
|
1847
2502
|
var marketingMessages = defineMessages(
|
|
1848
2503
|
{
|
|
@@ -1967,9 +2622,6 @@ var marketingMessages = defineMessages(
|
|
|
1967
2622
|
colorLabel: "Farbe",
|
|
1968
2623
|
delete: "Aktion l\xF6schen",
|
|
1969
2624
|
deleteConfirm: "Aktion \u201E{label}\u201C wirklich l\xF6schen?"
|
|
1970
|
-
},
|
|
1971
|
-
errors: {
|
|
1972
|
-
projectionsApi: "MarketingProjections-API antwortete mit HTTP {status}"
|
|
1973
2625
|
}
|
|
1974
2626
|
},
|
|
1975
2627
|
{
|
|
@@ -2094,9 +2746,6 @@ var marketingMessages = defineMessages(
|
|
|
2094
2746
|
colorLabel: "Color",
|
|
2095
2747
|
delete: "Delete promotion",
|
|
2096
2748
|
deleteConfirm: 'Really delete promotion "{label}"?'
|
|
2097
|
-
},
|
|
2098
|
-
errors: {
|
|
2099
|
-
projectionsApi: "MarketingProjections API responded with HTTP {status}"
|
|
2100
2749
|
}
|
|
2101
2750
|
}
|
|
2102
2751
|
);
|
|
@@ -3290,7 +3939,6 @@ var promosMessages = defineMessages(
|
|
|
3290
3939
|
{
|
|
3291
3940
|
title: "Promo-Codes",
|
|
3292
3941
|
createAction: "Promo-Code anlegen",
|
|
3293
|
-
apiErrorHttpStatus: "Promotions-API antwortete mit HTTP {status}",
|
|
3294
3942
|
list: {
|
|
3295
3943
|
searchLabel: "Suche (Code)",
|
|
3296
3944
|
tileScheduled: "Geplant",
|
|
@@ -3388,7 +4036,6 @@ var promosMessages = defineMessages(
|
|
|
3388
4036
|
{
|
|
3389
4037
|
title: "Promo codes",
|
|
3390
4038
|
createAction: "Create promo code",
|
|
3391
|
-
apiErrorHttpStatus: "Promotions API responded with HTTP {status}",
|
|
3392
4039
|
list: {
|
|
3393
4040
|
searchLabel: "Search (code)",
|
|
3394
4041
|
tileScheduled: "Scheduled",
|
|
@@ -3496,7 +4143,13 @@ var shellMessages = defineMessages(
|
|
|
3496
4143
|
subtitle: "SuperAdmin \xB7 Plattform-Verwaltung",
|
|
3497
4144
|
roleBadge: "SUPER ADMIN",
|
|
3498
4145
|
logout: "Abmelden",
|
|
3499
|
-
language: "Sprache"
|
|
4146
|
+
language: "Sprache",
|
|
4147
|
+
theme: {
|
|
4148
|
+
label: "Darstellung",
|
|
4149
|
+
light: "Hell",
|
|
4150
|
+
dark: "Dunkel",
|
|
4151
|
+
system: "System"
|
|
4152
|
+
}
|
|
3500
4153
|
},
|
|
3501
4154
|
drawer: {
|
|
3502
4155
|
docs: "Doku \xF6ffnen"
|
|
@@ -3565,7 +4218,13 @@ var shellMessages = defineMessages(
|
|
|
3565
4218
|
subtitle: "SuperAdmin \xB7 Platform administration",
|
|
3566
4219
|
roleBadge: "SUPER ADMIN",
|
|
3567
4220
|
logout: "Sign out",
|
|
3568
|
-
language: "Language"
|
|
4221
|
+
language: "Language",
|
|
4222
|
+
theme: {
|
|
4223
|
+
label: "Appearance",
|
|
4224
|
+
light: "Light",
|
|
4225
|
+
dark: "Dark",
|
|
4226
|
+
system: "System"
|
|
4227
|
+
}
|
|
3569
4228
|
},
|
|
3570
4229
|
drawer: {
|
|
3571
4230
|
docs: "Open documentation"
|
|
@@ -3809,6 +4468,7 @@ var usersMessages = defineMessages(
|
|
|
3809
4468
|
function catalogFor(locale) {
|
|
3810
4469
|
return {
|
|
3811
4470
|
common: commonMessages[locale],
|
|
4471
|
+
errors: errorsMessages[locale],
|
|
3812
4472
|
nav: navMessages[locale],
|
|
3813
4473
|
shell: shellMessages[locale],
|
|
3814
4474
|
dashboard: dashboardMessages[locale],
|
|
@@ -4063,6 +4723,11 @@ var SUPER_ADMIN_NOTIFY_KEY = /* @__PURE__ */ Symbol.for(
|
|
|
4063
4723
|
"@saasicat/ui-vue/SUPER_ADMIN_NOTIFY"
|
|
4064
4724
|
);
|
|
4065
4725
|
|
|
4726
|
+
// src/vue/ui-confirm.ts
|
|
4727
|
+
var SUPER_ADMIN_CONFIRM_KEY = /* @__PURE__ */ Symbol.for(
|
|
4728
|
+
"@saasicat/ui-vue/SUPER_ADMIN_CONFIRM"
|
|
4729
|
+
);
|
|
4730
|
+
|
|
4066
4731
|
// src/vue/use-super-admin-context.ts
|
|
4067
4732
|
var import_vue = require("vue");
|
|
4068
4733
|
function useSuperAdminExtensions() {
|
|
@@ -4153,10 +4818,10 @@ function useSaMessages(namespace) {
|
|
|
4153
4818
|
|
|
4154
4819
|
// src/vue/use-sa-theme.ts
|
|
4155
4820
|
var import_vue3 = require("vue");
|
|
4156
|
-
var
|
|
4157
|
-
var
|
|
4821
|
+
var SA_COLOR_SCHEMES = ["light", "dark", "system"];
|
|
4822
|
+
var SA_THEME_STORAGE_KEY = "saasicat.theme.scheme";
|
|
4158
4823
|
function isScheme(value) {
|
|
4159
|
-
return typeof value === "string" &&
|
|
4824
|
+
return typeof value === "string" && SA_COLOR_SCHEMES.includes(value);
|
|
4160
4825
|
}
|
|
4161
4826
|
function systemScheme() {
|
|
4162
4827
|
const value = (0, import_vue3.ref)("light");
|
|
@@ -4177,13 +4842,19 @@ function createSaTheme(options = {}) {
|
|
|
4177
4842
|
const persist = options.persist !== false;
|
|
4178
4843
|
const provided = options.scheme;
|
|
4179
4844
|
const appOwnsIt = (0, import_vue3.isRef)(provided);
|
|
4180
|
-
const
|
|
4845
|
+
const key = `${options.storageKeyPrefix ?? ""}${SA_THEME_STORAGE_KEY}`;
|
|
4846
|
+
const stored = persist && !appOwnsIt ? storage.get(key) : null;
|
|
4181
4847
|
const scheme = (0, import_vue3.isRef)(provided) ? provided : (0, import_vue3.ref)(isScheme(stored) ? stored : provided ?? "system");
|
|
4182
4848
|
const { value: system, unsubscribe } = systemScheme();
|
|
4183
|
-
const stopPersisting = persist && !appOwnsIt ? (0, import_vue3.watch)(scheme, (next) => storage.set(
|
|
4849
|
+
const stopPersisting = persist && !appOwnsIt ? (0, import_vue3.watch)(scheme, (next) => storage.set(key, next)) : null;
|
|
4184
4850
|
return {
|
|
4185
4851
|
scheme,
|
|
4186
4852
|
resolved: (0, import_vue3.computed)(() => scheme.value === "system" ? system.value : scheme.value),
|
|
4853
|
+
// A readonly ref (typically a `computed` derived from a profile
|
|
4854
|
+
// setting) silently swallows writes, so the switcher would be a dead
|
|
4855
|
+
// control. TypeScript ignores `readonly` when checking assignability,
|
|
4856
|
+
// so the guard has to be a runtime one — same as the locale next door.
|
|
4857
|
+
switcherEnabled: (options.switcher ?? true) && !(0, import_vue3.isReadonly)(scheme),
|
|
4187
4858
|
dispose: () => {
|
|
4188
4859
|
unsubscribe();
|
|
4189
4860
|
stopPersisting?.();
|
|
@@ -4295,22 +4966,17 @@ var import_vue5 = require("vue");
|
|
|
4295
4966
|
function useApiList(options) {
|
|
4296
4967
|
const http = options.http ?? defaultHttpClient();
|
|
4297
4968
|
const items = (0, import_vue5.ref)([]);
|
|
4298
|
-
const page = (0, import_vue5.ref)(
|
|
4299
|
-
const pageSize = (0, import_vue5.ref)(
|
|
4969
|
+
const page = (0, import_vue5.ref)(LIST_FIRST_PAGE);
|
|
4970
|
+
const pageSize = (0, import_vue5.ref)(LIST_PAGE_SIZE_DEFAULT);
|
|
4300
4971
|
const total = (0, import_vue5.ref)(0);
|
|
4301
4972
|
const loading = (0, import_vue5.ref)(false);
|
|
4302
4973
|
const error = (0, import_vue5.ref)(null);
|
|
4303
4974
|
function buildUrl() {
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
if (v === void 0 || v === null || v === "") continue;
|
|
4310
|
-
params.set(k, String(v));
|
|
4311
|
-
}
|
|
4312
|
-
const sep = options.endpoint.includes("?") ? "&" : "?";
|
|
4313
|
-
return `${options.endpoint}${sep}${params.toString()}`;
|
|
4975
|
+
return listUrl(options.endpoint, {
|
|
4976
|
+
page: page.value,
|
|
4977
|
+
pageSize: pageSize.value,
|
|
4978
|
+
filter: options.filter?.value
|
|
4979
|
+
});
|
|
4314
4980
|
}
|
|
4315
4981
|
async function load() {
|
|
4316
4982
|
loading.value = true;
|
|
@@ -4323,20 +4989,11 @@ function useApiList(options) {
|
|
|
4323
4989
|
if (res.status !== 200) {
|
|
4324
4990
|
throw new Error(`Endpoint ${options.endpoint} \u2192 HTTP ${res.status}`);
|
|
4325
4991
|
}
|
|
4326
|
-
const
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
const body = raw;
|
|
4332
|
-
items.value = body.items ?? [];
|
|
4333
|
-
if (typeof body.page === "number") page.value = body.page;
|
|
4334
|
-
if (typeof body.pageSize === "number") pageSize.value = body.pageSize;
|
|
4335
|
-
if (typeof body.total === "number") total.value = body.total;
|
|
4336
|
-
} else {
|
|
4337
|
-
items.value = [];
|
|
4338
|
-
total.value = 0;
|
|
4339
|
-
}
|
|
4992
|
+
const body = readListPage(await res.json());
|
|
4993
|
+
items.value = body.items;
|
|
4994
|
+
if (typeof body.page === "number") page.value = body.page;
|
|
4995
|
+
if (typeof body.pageSize === "number") pageSize.value = body.pageSize;
|
|
4996
|
+
if (typeof body.total === "number") total.value = body.total;
|
|
4340
4997
|
} catch (err) {
|
|
4341
4998
|
error.value = err instanceof Error ? err : new Error(String(err));
|
|
4342
4999
|
items.value = [];
|
|
@@ -4346,19 +5003,19 @@ function useApiList(options) {
|
|
|
4346
5003
|
}
|
|
4347
5004
|
}
|
|
4348
5005
|
async function goToPage(p) {
|
|
4349
|
-
page.value =
|
|
5006
|
+
page.value = clampListPage(p);
|
|
4350
5007
|
await load();
|
|
4351
5008
|
}
|
|
4352
5009
|
async function setPageSize(size) {
|
|
4353
|
-
pageSize.value =
|
|
4354
|
-
page.value =
|
|
5010
|
+
pageSize.value = clampListPageSize(size);
|
|
5011
|
+
page.value = LIST_FIRST_PAGE;
|
|
4355
5012
|
await load();
|
|
4356
5013
|
}
|
|
4357
5014
|
if (options.filter) {
|
|
4358
5015
|
(0, import_vue5.watch)(
|
|
4359
5016
|
options.filter,
|
|
4360
5017
|
() => {
|
|
4361
|
-
page.value =
|
|
5018
|
+
page.value = LIST_FIRST_PAGE;
|
|
4362
5019
|
void load();
|
|
4363
5020
|
},
|
|
4364
5021
|
{ deep: true }
|
|
@@ -4404,15 +5061,253 @@ function usePagination(rows, initialRowsPerPage = DEFAULT_ROWS_PER_PAGE) {
|
|
|
4404
5061
|
return { page, rowsPerPage, total, pagedRows };
|
|
4405
5062
|
}
|
|
4406
5063
|
|
|
4407
|
-
// src/vue/use-
|
|
5064
|
+
// src/vue/use-async-action.ts
|
|
4408
5065
|
var import_vue7 = require("vue");
|
|
5066
|
+
function report(announce) {
|
|
5067
|
+
try {
|
|
5068
|
+
announce();
|
|
5069
|
+
} catch (err) {
|
|
5070
|
+
queueMicrotask(() => {
|
|
5071
|
+
throw err;
|
|
5072
|
+
});
|
|
5073
|
+
}
|
|
5074
|
+
}
|
|
5075
|
+
function useAsyncAction(fn, options = {}) {
|
|
5076
|
+
const pending = (0, import_vue7.ref)(false);
|
|
5077
|
+
const error = (0, import_vue7.ref)(null);
|
|
5078
|
+
const notify = options.notify ?? (0, import_vue7.inject)(SUPER_ADMIN_NOTIFY_KEY, void 0);
|
|
5079
|
+
const messages = useSaMessages("errors");
|
|
5080
|
+
const notifyOn = options.notifyOn ?? "error";
|
|
5081
|
+
let inFlight = 0;
|
|
5082
|
+
let generation = 0;
|
|
5083
|
+
async function run(...args) {
|
|
5084
|
+
const mine = ++generation;
|
|
5085
|
+
inFlight++;
|
|
5086
|
+
pending.value = true;
|
|
5087
|
+
error.value = null;
|
|
5088
|
+
try {
|
|
5089
|
+
const result = await fn(...args);
|
|
5090
|
+
await options.onSuccess?.(result);
|
|
5091
|
+
if (notifyOn === "both") {
|
|
5092
|
+
report(() => {
|
|
5093
|
+
const message = typeof options.successMessage === "function" ? options.successMessage() : options.successMessage;
|
|
5094
|
+
if (message) notify?.("positive", message);
|
|
5095
|
+
});
|
|
5096
|
+
}
|
|
5097
|
+
return { ok: true, value: result };
|
|
5098
|
+
} catch (err) {
|
|
5099
|
+
const adminError = toAdminError(err);
|
|
5100
|
+
if (mine === generation) error.value = adminError;
|
|
5101
|
+
if (notifyOn !== "none") {
|
|
5102
|
+
report(
|
|
5103
|
+
() => notify?.(
|
|
5104
|
+
"negative",
|
|
5105
|
+
options.errorMessage?.(adminError) ?? adminErrorMessage(adminError, messages.value)
|
|
5106
|
+
)
|
|
5107
|
+
);
|
|
5108
|
+
}
|
|
5109
|
+
return { ok: false, error: adminError };
|
|
5110
|
+
} finally {
|
|
5111
|
+
if (--inFlight === 0) pending.value = false;
|
|
5112
|
+
}
|
|
5113
|
+
}
|
|
5114
|
+
return {
|
|
5115
|
+
run,
|
|
5116
|
+
pending,
|
|
5117
|
+
error,
|
|
5118
|
+
reset: () => {
|
|
5119
|
+
error.value = null;
|
|
5120
|
+
}
|
|
5121
|
+
};
|
|
5122
|
+
}
|
|
5123
|
+
|
|
5124
|
+
// src/vue/use-async-data.ts
|
|
5125
|
+
var import_vue8 = require("vue");
|
|
5126
|
+
function useAsyncData(fn, options) {
|
|
5127
|
+
const data = (0, import_vue8.ref)(options.initial);
|
|
5128
|
+
const pending = (0, import_vue8.ref)(false);
|
|
5129
|
+
const error = (0, import_vue8.ref)(null);
|
|
5130
|
+
let generation = 0;
|
|
5131
|
+
async function reload() {
|
|
5132
|
+
const mine = ++generation;
|
|
5133
|
+
pending.value = true;
|
|
5134
|
+
error.value = null;
|
|
5135
|
+
try {
|
|
5136
|
+
const result = await fn();
|
|
5137
|
+
if (mine !== generation) return;
|
|
5138
|
+
data.value = result;
|
|
5139
|
+
} catch (err) {
|
|
5140
|
+
if (mine !== generation) return;
|
|
5141
|
+
error.value = toAdminError(err);
|
|
5142
|
+
data.value = options.initial;
|
|
5143
|
+
} finally {
|
|
5144
|
+
if (mine === generation) pending.value = false;
|
|
5145
|
+
}
|
|
5146
|
+
}
|
|
5147
|
+
if (options.watch?.length) {
|
|
5148
|
+
(0, import_vue8.watch)(options.watch, () => void reload());
|
|
5149
|
+
}
|
|
5150
|
+
if (options.immediate !== false) {
|
|
5151
|
+
void Promise.resolve().then(() => reload());
|
|
5152
|
+
}
|
|
5153
|
+
return { data, pending, error, reload };
|
|
5154
|
+
}
|
|
5155
|
+
|
|
5156
|
+
// src/vue/resource-registry.ts
|
|
5157
|
+
var import_vue9 = require("vue");
|
|
5158
|
+
function boundWithOverride(def, http, readContext, override) {
|
|
5159
|
+
const context = override?.context ? () => ({ ...readContext(), ...override.context }) : readContext;
|
|
5160
|
+
const platform = bindResource(def, override?.http ?? http, context);
|
|
5161
|
+
if (!override?.ops) return platform;
|
|
5162
|
+
const result = { ...platform };
|
|
5163
|
+
for (const [name, wrap] of Object.entries(override.ops)) {
|
|
5164
|
+
if (typeof wrap !== "function") continue;
|
|
5165
|
+
if (!Object.hasOwn(platform, name)) {
|
|
5166
|
+
throw new Error(
|
|
5167
|
+
`createResourceRegistry: resource "${def.key}" has no operation "${name}" to override. It offers: ${Object.keys(platform).join(", ")}.`
|
|
5168
|
+
);
|
|
5169
|
+
}
|
|
5170
|
+
const next = platform[name];
|
|
5171
|
+
result[name] = (...args) => wrap(next, ...args);
|
|
5172
|
+
}
|
|
5173
|
+
return result;
|
|
5174
|
+
}
|
|
5175
|
+
function createResourceRegistry(options) {
|
|
5176
|
+
if (!options?.http) {
|
|
5177
|
+
throw new Error(
|
|
5178
|
+
"createResourceRegistry: `http` is required. Pass the HttpClient your app already uses \u2014 createAxiosHttpClient(instance) or createFetchHttpClient({ headers }) \u2014 so every platform request carries its auth."
|
|
5179
|
+
);
|
|
5180
|
+
}
|
|
5181
|
+
const readContext = typeof options.context === "function" ? options.context : () => options.context;
|
|
5182
|
+
const unknown = Object.keys(options.overrides ?? {}).filter(
|
|
5183
|
+
(key) => !Object.hasOwn(options.resources, key)
|
|
5184
|
+
);
|
|
5185
|
+
if (unknown.length > 0) {
|
|
5186
|
+
throw new Error(
|
|
5187
|
+
`createResourceRegistry: no resource named ${unknown.map((k) => `"${k}"`).join(", ")} to override. The registry holds: ${Object.keys(options.resources).join(", ")}.`
|
|
5188
|
+
);
|
|
5189
|
+
}
|
|
5190
|
+
const bound = /* @__PURE__ */ new Map();
|
|
5191
|
+
for (const [key, def] of Object.entries(options.resources)) {
|
|
5192
|
+
bound.set(key, boundWithOverride(def, options.http, readContext, options.overrides?.[key]));
|
|
5193
|
+
}
|
|
5194
|
+
return {
|
|
5195
|
+
get(key) {
|
|
5196
|
+
const ops = bound.get(key);
|
|
5197
|
+
if (!ops) {
|
|
5198
|
+
throw new Error(
|
|
5199
|
+
`useResource("${String(key)}"): no such resource. The registry offers: ${[...bound.keys()].join(", ")}.`
|
|
5200
|
+
);
|
|
5201
|
+
}
|
|
5202
|
+
return ops;
|
|
5203
|
+
},
|
|
5204
|
+
keys: () => [...bound.keys()]
|
|
5205
|
+
};
|
|
5206
|
+
}
|
|
5207
|
+
var SUPER_ADMIN_RESOURCES_KEY = /* @__PURE__ */ Symbol.for(
|
|
5208
|
+
"@saasicat/ui-vue/SUPER_ADMIN_RESOURCES"
|
|
5209
|
+
);
|
|
5210
|
+
function useResource(key) {
|
|
5211
|
+
const registry = (0, import_vue9.inject)(SUPER_ADMIN_RESOURCES_KEY, null);
|
|
5212
|
+
if (!registry) {
|
|
5213
|
+
throw new Error(
|
|
5214
|
+
`useResource("${key}"): no resource registry in scope. Either the component was not mounted inside createSuperAdminApp(), or the app did not pass an \`http\` client \u2014 the registry is not installed without one, because a bare fetch would send every request without your Authorization header and fail silently.`
|
|
5215
|
+
);
|
|
5216
|
+
}
|
|
5217
|
+
return registry.get(key);
|
|
5218
|
+
}
|
|
5219
|
+
|
|
5220
|
+
// src/vue/use-resource-list.ts
|
|
5221
|
+
var import_vue10 = require("vue");
|
|
5222
|
+
var DEFAULT_LIST_OP = "list";
|
|
5223
|
+
var EMPTY_PAGE = Object.freeze({
|
|
5224
|
+
items: Object.freeze([]),
|
|
5225
|
+
total: 0
|
|
5226
|
+
});
|
|
5227
|
+
function paginationCollision(key, op, claimed) {
|
|
5228
|
+
return new AdminError({
|
|
5229
|
+
message: `useResourceList("${key}", { op: "${op}" }): the filter carries ${claimed.join(" and ")}, which the list already sends. The filter is serialised after the pagination, so its value wins on the wire while page.value keeps the one goToPage() set \u2014 the list would report a page it is not showing. Drive the page with goToPage()/setPageSize() and leave those keys out of the filter.`
|
|
5230
|
+
});
|
|
5231
|
+
}
|
|
5232
|
+
function paginationKeysIn(filter) {
|
|
5233
|
+
return LIST_PAGINATION_PARAMS.filter((param) => isSentInQuery(filter[param]));
|
|
5234
|
+
}
|
|
5235
|
+
function readListOp(key, ops, name) {
|
|
5236
|
+
const op = Object.hasOwn(ops, name) ? ops[name] : void 0;
|
|
5237
|
+
if (typeof op !== "function") {
|
|
5238
|
+
throw new Error(
|
|
5239
|
+
`useResourceList("${key}", { op: "${name}" }): that resource has no such operation. It offers: ${Object.keys(ops).join(", ")}.`
|
|
5240
|
+
);
|
|
5241
|
+
}
|
|
5242
|
+
return op;
|
|
5243
|
+
}
|
|
5244
|
+
function useResourceList(key, opts) {
|
|
5245
|
+
const ops = useResource(key);
|
|
5246
|
+
const opName = opts?.op ?? DEFAULT_LIST_OP;
|
|
5247
|
+
const load = readListOp(key, ops, opName);
|
|
5248
|
+
if (opts?.filter) {
|
|
5249
|
+
const claimed = paginationKeysIn(opts.filter.value);
|
|
5250
|
+
if (claimed.length > 0) throw paginationCollision(key, opName, claimed);
|
|
5251
|
+
}
|
|
5252
|
+
const page = (0, import_vue10.ref)(LIST_FIRST_PAGE);
|
|
5253
|
+
const pageSize = (0, import_vue10.ref)(clampListPageSize(opts?.pageSize ?? LIST_PAGE_SIZE_DEFAULT));
|
|
5254
|
+
function query() {
|
|
5255
|
+
const filter = opts?.filter?.value;
|
|
5256
|
+
if (filter) {
|
|
5257
|
+
const claimed = paginationKeysIn(filter);
|
|
5258
|
+
if (claimed.length > 0) throw paginationCollision(key, opName, claimed);
|
|
5259
|
+
}
|
|
5260
|
+
return { page: page.value, pageSize: pageSize.value, filter };
|
|
5261
|
+
}
|
|
5262
|
+
const state = useAsyncData(() => load(query()), {
|
|
5263
|
+
initial: EMPTY_PAGE,
|
|
5264
|
+
immediate: opts?.immediate
|
|
5265
|
+
});
|
|
5266
|
+
(0, import_vue10.watch)(state.data, (answer) => {
|
|
5267
|
+
if (typeof answer.page === "number") page.value = answer.page;
|
|
5268
|
+
if (typeof answer.pageSize === "number") pageSize.value = answer.pageSize;
|
|
5269
|
+
});
|
|
5270
|
+
async function goToPage(next) {
|
|
5271
|
+
page.value = clampListPage(next);
|
|
5272
|
+
await state.reload();
|
|
5273
|
+
}
|
|
5274
|
+
async function setPageSize(size) {
|
|
5275
|
+
pageSize.value = clampListPageSize(size);
|
|
5276
|
+
page.value = LIST_FIRST_PAGE;
|
|
5277
|
+
await state.reload();
|
|
5278
|
+
}
|
|
5279
|
+
if (opts?.filter) {
|
|
5280
|
+
(0, import_vue10.watch)(
|
|
5281
|
+
opts.filter,
|
|
5282
|
+
() => {
|
|
5283
|
+
page.value = LIST_FIRST_PAGE;
|
|
5284
|
+
void state.reload();
|
|
5285
|
+
},
|
|
5286
|
+
{ deep: true }
|
|
5287
|
+
);
|
|
5288
|
+
}
|
|
5289
|
+
return {
|
|
5290
|
+
items: (0, import_vue10.computed)(() => state.data.value.items),
|
|
5291
|
+
total: (0, import_vue10.computed)(() => state.data.value.total ?? state.data.value.items.length),
|
|
5292
|
+
page,
|
|
5293
|
+
pageSize,
|
|
5294
|
+
pending: state.pending,
|
|
5295
|
+
error: state.error,
|
|
5296
|
+
reload: state.reload,
|
|
5297
|
+
goToPage,
|
|
5298
|
+
setPageSize
|
|
5299
|
+
};
|
|
5300
|
+
}
|
|
5301
|
+
|
|
5302
|
+
// src/vue/use-tenants.ts
|
|
5303
|
+
var import_vue11 = require("vue");
|
|
4409
5304
|
function useTenants(options) {
|
|
4410
5305
|
if (!options?.endpoint) {
|
|
4411
5306
|
throw new Error(
|
|
4412
5307
|
'useTenants: `endpoint` is required (e.g. "/api/v1/admin/tenants" or "/api/admin/tenants"). The platform has no default because apps use different globalPrefix conventions.'
|
|
4413
5308
|
);
|
|
4414
5309
|
}
|
|
4415
|
-
const filter = options.filter ?? (0,
|
|
5310
|
+
const filter = options.filter ?? (0, import_vue11.ref)({});
|
|
4416
5311
|
const list = useApiList({
|
|
4417
5312
|
endpoint: options.endpoint,
|
|
4418
5313
|
filter,
|
|
@@ -4424,14 +5319,14 @@ function useTenants(options) {
|
|
|
4424
5319
|
}
|
|
4425
5320
|
|
|
4426
5321
|
// src/vue/use-audit-entries.ts
|
|
4427
|
-
var
|
|
5322
|
+
var import_vue12 = require("vue");
|
|
4428
5323
|
function useAuditEntries(options) {
|
|
4429
5324
|
if (!options?.endpoint) {
|
|
4430
5325
|
throw new Error(
|
|
4431
5326
|
'useAuditEntries: `endpoint` is required (e.g. "/api/admin/audit" or "/api/v1/admin/audit"). The platform has no default because apps use different globalPrefix conventions.'
|
|
4432
5327
|
);
|
|
4433
5328
|
}
|
|
4434
|
-
const filter = options.filter ?? (0,
|
|
5329
|
+
const filter = options.filter ?? (0, import_vue12.ref)({});
|
|
4435
5330
|
const list = useApiList({
|
|
4436
5331
|
endpoint: options.endpoint,
|
|
4437
5332
|
filter,
|
|
@@ -4443,7 +5338,7 @@ function useAuditEntries(options) {
|
|
|
4443
5338
|
}
|
|
4444
5339
|
|
|
4445
5340
|
// src/vue/use-entitlement.ts
|
|
4446
|
-
var
|
|
5341
|
+
var import_vue13 = require("vue");
|
|
4447
5342
|
function useEntitlement(options) {
|
|
4448
5343
|
if (!options?.endpoint) {
|
|
4449
5344
|
throw new Error(
|
|
@@ -4452,9 +5347,9 @@ function useEntitlement(options) {
|
|
|
4452
5347
|
}
|
|
4453
5348
|
const endpoint = options.endpoint;
|
|
4454
5349
|
const http = options.http ?? defaultHttpClient();
|
|
4455
|
-
const entitlement = (0,
|
|
4456
|
-
const loading = (0,
|
|
4457
|
-
const error = (0,
|
|
5350
|
+
const entitlement = (0, import_vue13.ref)(null);
|
|
5351
|
+
const loading = (0, import_vue13.ref)(false);
|
|
5352
|
+
const error = (0, import_vue13.ref)(null);
|
|
4458
5353
|
async function load() {
|
|
4459
5354
|
loading.value = true;
|
|
4460
5355
|
error.value = null;
|
|
@@ -4484,15 +5379,15 @@ function useEntitlement(options) {
|
|
|
4484
5379
|
}
|
|
4485
5380
|
|
|
4486
5381
|
// src/vue/use-tenant-manifest.ts
|
|
4487
|
-
var
|
|
5382
|
+
var import_vue14 = require("vue");
|
|
4488
5383
|
function useTenantManifest(options) {
|
|
4489
5384
|
if (!options?.endpoint) {
|
|
4490
5385
|
throw new Error('useTenantManifest: `endpoint` is required (e.g. "/api/tenant/manifest").');
|
|
4491
5386
|
}
|
|
4492
5387
|
const http = options.http ?? defaultHttpClient();
|
|
4493
|
-
const manifest = (0,
|
|
4494
|
-
const loading = (0,
|
|
4495
|
-
const error = (0,
|
|
5388
|
+
const manifest = (0, import_vue14.ref)(null);
|
|
5389
|
+
const loading = (0, import_vue14.ref)(false);
|
|
5390
|
+
const error = (0, import_vue14.ref)(null);
|
|
4496
5391
|
async function load() {
|
|
4497
5392
|
loading.value = true;
|
|
4498
5393
|
error.value = null;
|
|
@@ -4526,7 +5421,7 @@ function useTenantManifest(options) {
|
|
|
4526
5421
|
}
|
|
4527
5422
|
|
|
4528
5423
|
// src/vue/entitlement-provider.ts
|
|
4529
|
-
var
|
|
5424
|
+
var import_vue15 = require("vue");
|
|
4530
5425
|
var ENTITLEMENT_INJECTION_KEY = /* @__PURE__ */ Symbol.for(
|
|
4531
5426
|
"@saasicat/ui-vue/ENTITLEMENT"
|
|
4532
5427
|
);
|
|
@@ -4534,7 +5429,7 @@ function provideEntitlement(app, entitlement) {
|
|
|
4534
5429
|
app.provide(ENTITLEMENT_INJECTION_KEY, entitlement);
|
|
4535
5430
|
}
|
|
4536
5431
|
function useInjectedEntitlement() {
|
|
4537
|
-
return (0,
|
|
5432
|
+
return (0, import_vue15.inject)(ENTITLEMENT_INJECTION_KEY, null);
|
|
4538
5433
|
}
|
|
4539
5434
|
|
|
4540
5435
|
// src/vue/feature-router-guard.ts
|
|
@@ -4563,15 +5458,15 @@ function buildFeatureRouterGuard(options) {
|
|
|
4563
5458
|
}
|
|
4564
5459
|
|
|
4565
5460
|
// src/vue/use-tenant-billing-catalog.ts
|
|
4566
|
-
var
|
|
5461
|
+
var import_vue16 = require("vue");
|
|
4567
5462
|
function useTenantBillingCatalog(options = {}) {
|
|
4568
5463
|
const apiPrefix = trimTrailingSlashes(options.apiPrefix ?? "/billing");
|
|
4569
5464
|
const http = options.http ?? defaultHttpClient();
|
|
4570
|
-
const plans = (0,
|
|
4571
|
-
const featureRegistry = (0,
|
|
4572
|
-
const bundles = (0,
|
|
4573
|
-
const loading = (0,
|
|
4574
|
-
const error = (0,
|
|
5465
|
+
const plans = (0, import_vue16.ref)(null);
|
|
5466
|
+
const featureRegistry = (0, import_vue16.ref)(null);
|
|
5467
|
+
const bundles = (0, import_vue16.ref)(null);
|
|
5468
|
+
const loading = (0, import_vue16.ref)(false);
|
|
5469
|
+
const error = (0, import_vue16.ref)(null);
|
|
4575
5470
|
async function fetchJson(path) {
|
|
4576
5471
|
const res = await http(`${apiPrefix}${path}`, { method: "GET" });
|
|
4577
5472
|
if (res.status !== 200) {
|
|
@@ -4621,14 +5516,14 @@ function useTenantBillingCatalog(options = {}) {
|
|
|
4621
5516
|
}
|
|
4622
5517
|
|
|
4623
5518
|
// src/vue/use-tenant-billing.ts
|
|
4624
|
-
var
|
|
5519
|
+
var import_vue17 = require("vue");
|
|
4625
5520
|
function useTenantBilling(options = {}) {
|
|
4626
5521
|
const apiPrefix = trimTrailingSlashes(options.apiPrefix ?? "/billing");
|
|
4627
5522
|
const http = options.http ?? defaultHttpClient();
|
|
4628
|
-
const usage = (0,
|
|
4629
|
-
const loading = (0,
|
|
4630
|
-
const error = (0,
|
|
4631
|
-
const subscriptionBundles = (0,
|
|
5523
|
+
const usage = (0, import_vue17.ref)(null);
|
|
5524
|
+
const loading = (0, import_vue17.ref)(false);
|
|
5525
|
+
const error = (0, import_vue17.ref)(null);
|
|
5526
|
+
const subscriptionBundles = (0, import_vue17.ref)([]);
|
|
4632
5527
|
function authHeaders() {
|
|
4633
5528
|
const headers = {};
|
|
4634
5529
|
const token = options.getAuthToken?.();
|
|
@@ -4762,7 +5657,7 @@ function useTenantBilling(options = {}) {
|
|
|
4762
5657
|
}
|
|
4763
5658
|
|
|
4764
5659
|
// src/vue/use-subscription-draft.ts
|
|
4765
|
-
var
|
|
5660
|
+
var import_vue18 = require("vue");
|
|
4766
5661
|
var import_types13 = require("@saasicat/types");
|
|
4767
5662
|
var DEFAULT_YEARLY_FACTOR = 10;
|
|
4768
5663
|
function unwrap(source) {
|
|
@@ -4779,27 +5674,27 @@ function priceForCycle(monthlyNet, yearlyNet, cycle, yearlyFactor) {
|
|
|
4779
5674
|
}
|
|
4780
5675
|
function useSubscriptionDraft(options) {
|
|
4781
5676
|
const yearlyFactor = options.yearlyFactor ?? DEFAULT_YEARLY_FACTOR;
|
|
4782
|
-
const plansRef = (0,
|
|
4783
|
-
const subscriptionBundlesRef = (0,
|
|
5677
|
+
const plansRef = (0, import_vue18.computed)(() => unwrap(options.plans) ?? []);
|
|
5678
|
+
const subscriptionBundlesRef = (0, import_vue18.computed)(
|
|
4784
5679
|
() => unwrap(options.subscriptionBundles ?? null) ?? []
|
|
4785
5680
|
);
|
|
4786
|
-
const plan = (0,
|
|
4787
|
-
const cycle = (0,
|
|
4788
|
-
const selectedBundleVersionIds = (0,
|
|
5681
|
+
const plan = (0, import_vue18.ref)(options.initialPlan ?? null);
|
|
5682
|
+
const cycle = (0, import_vue18.ref)(options.initialCycle ?? "MONTHLY");
|
|
5683
|
+
const selectedBundleVersionIds = (0, import_vue18.ref)(
|
|
4789
5684
|
new Set(options.initialBundleVersionIds ?? [])
|
|
4790
5685
|
);
|
|
4791
|
-
const promoCode = (0,
|
|
4792
|
-
const promoState = (0,
|
|
4793
|
-
const selectedPlan = (0,
|
|
5686
|
+
const promoCode = (0, import_vue18.ref)("");
|
|
5687
|
+
const promoState = (0, import_vue18.ref)({ status: "idle", preview: null, message: "" });
|
|
5688
|
+
const selectedPlan = (0, import_vue18.computed)(() => {
|
|
4794
5689
|
if (!plan.value) return null;
|
|
4795
5690
|
return plansRef.value.find((p) => p.id === plan.value) ?? null;
|
|
4796
5691
|
});
|
|
4797
|
-
const selectedBundles = (0,
|
|
5692
|
+
const selectedBundles = (0, import_vue18.computed)(
|
|
4798
5693
|
() => subscriptionBundlesRef.value.filter(
|
|
4799
5694
|
(b) => selectedBundleVersionIds.value.has(b.bundleVersionId)
|
|
4800
5695
|
)
|
|
4801
5696
|
);
|
|
4802
|
-
const chargeableBundles = (0,
|
|
5697
|
+
const chargeableBundles = (0, import_vue18.computed)(() => {
|
|
4803
5698
|
const planFeatures = selectedPlan.value?.features ?? [];
|
|
4804
5699
|
const selected = selectedBundles.value;
|
|
4805
5700
|
const keptIds = new Set(
|
|
@@ -4807,7 +5702,7 @@ function useSubscriptionDraft(options) {
|
|
|
4807
5702
|
);
|
|
4808
5703
|
return selected.filter((b) => keptIds.has(b.bundleVersionId));
|
|
4809
5704
|
});
|
|
4810
|
-
const activeFeatures = (0,
|
|
5705
|
+
const activeFeatures = (0, import_vue18.computed)(() => {
|
|
4811
5706
|
const result = /* @__PURE__ */ new Set();
|
|
4812
5707
|
const planObj = selectedPlan.value;
|
|
4813
5708
|
if (planObj) {
|
|
@@ -4818,7 +5713,7 @@ function useSubscriptionDraft(options) {
|
|
|
4818
5713
|
}
|
|
4819
5714
|
return result;
|
|
4820
5715
|
});
|
|
4821
|
-
const pricing = (0,
|
|
5716
|
+
const pricing = (0, import_vue18.computed)(() => {
|
|
4822
5717
|
const cyc = cycle.value;
|
|
4823
5718
|
const planObj = selectedPlan.value;
|
|
4824
5719
|
const planNet = planObj ? priceForCycle(planObj.monthlyNet ?? 0, planObj.yearlyNet, cyc, yearlyFactor) : 0;
|
|
@@ -4878,7 +5773,7 @@ function useSubscriptionDraft(options) {
|
|
|
4878
5773
|
}
|
|
4879
5774
|
};
|
|
4880
5775
|
});
|
|
4881
|
-
const isDirty = (0,
|
|
5776
|
+
const isDirty = (0, import_vue18.computed)(() => {
|
|
4882
5777
|
return selectedBundleVersionIds.value.size > 0 || promoState.value.status === "valid";
|
|
4883
5778
|
});
|
|
4884
5779
|
function setPlan(planId) {
|
|
@@ -4954,7 +5849,7 @@ function useSubscriptionDraft(options) {
|
|
|
4954
5849
|
}
|
|
4955
5850
|
|
|
4956
5851
|
// src/vue/use-bulk-publish.ts
|
|
4957
|
-
var
|
|
5852
|
+
var import_vue19 = require("vue");
|
|
4958
5853
|
function useBulkPublish(options) {
|
|
4959
5854
|
const msg = useSaMessages("planVersions");
|
|
4960
5855
|
if (!options?.endpoints) {
|
|
@@ -4964,15 +5859,15 @@ function useBulkPublish(options) {
|
|
|
4964
5859
|
}
|
|
4965
5860
|
const http = options.http ?? defaultHttpClient();
|
|
4966
5861
|
const endpoints = options.endpoints;
|
|
4967
|
-
const items = (0,
|
|
4968
|
-
const successCount = (0,
|
|
4969
|
-
const failureCount = (0,
|
|
4970
|
-
const progress = (0,
|
|
5862
|
+
const items = (0, import_vue19.ref)([]);
|
|
5863
|
+
const successCount = (0, import_vue19.computed)(() => items.value.filter((i) => i.status === "published").length);
|
|
5864
|
+
const failureCount = (0, import_vue19.computed)(() => items.value.filter((i) => i.status === "failed").length);
|
|
5865
|
+
const progress = (0, import_vue19.computed)(() => {
|
|
4971
5866
|
if (items.value.length === 0) return 0;
|
|
4972
5867
|
const done2 = successCount.value + failureCount.value;
|
|
4973
5868
|
return done2 / items.value.length;
|
|
4974
5869
|
});
|
|
4975
|
-
const done = (0,
|
|
5870
|
+
const done = (0, import_vue19.computed)(
|
|
4976
5871
|
() => items.value.length > 0 && items.value.every((i) => i.status === "published" || i.status === "failed")
|
|
4977
5872
|
);
|
|
4978
5873
|
function setItems(next) {
|
|
@@ -5030,7 +5925,7 @@ function useBulkPublish(options) {
|
|
|
5030
5925
|
}
|
|
5031
5926
|
|
|
5032
5927
|
// src/vue/use-plan-editor.ts
|
|
5033
|
-
var
|
|
5928
|
+
var import_vue20 = require("vue");
|
|
5034
5929
|
var PlannedOnlyFeatureError = class extends Error {
|
|
5035
5930
|
constructor(violations) {
|
|
5036
5931
|
super(
|
|
@@ -5050,10 +5945,10 @@ function tierWeight(tier) {
|
|
|
5050
5945
|
function usePlanEditor(manifest, options = {}) {
|
|
5051
5946
|
const baseSet = new Set(options.baseFeatures ?? []);
|
|
5052
5947
|
const nonRegressive = options.nonRegressive ?? true;
|
|
5053
|
-
const selectedFeatures = (0,
|
|
5948
|
+
const selectedFeatures = (0, import_vue20.ref)(new Set(options.initialFeatures ?? []));
|
|
5054
5949
|
const catalogFeatures = manifest.planCatalogSnapshot.features ?? [];
|
|
5055
5950
|
const plannedOnlySet = new Set(catalogFeatures.filter((f) => f.plannedOnly).map((f) => f.key));
|
|
5056
|
-
const availableFeatures = (0,
|
|
5951
|
+
const availableFeatures = (0, import_vue20.computed)(() => {
|
|
5057
5952
|
return catalogFeatures.map((def) => {
|
|
5058
5953
|
const isSelected = selectedFeatures.value.has(def.key);
|
|
5059
5954
|
const isPlannedOnly = !!def.plannedOnly;
|
|
@@ -5062,7 +5957,7 @@ function usePlanEditor(manifest, options = {}) {
|
|
|
5062
5957
|
return { def, isSelected, isPlannedOnly, isInherited, canToggle };
|
|
5063
5958
|
});
|
|
5064
5959
|
});
|
|
5065
|
-
const featuresByTier = (0,
|
|
5960
|
+
const featuresByTier = (0, import_vue20.computed)(() => {
|
|
5066
5961
|
const groups = /* @__PURE__ */ new Map();
|
|
5067
5962
|
for (const row of availableFeatures.value) {
|
|
5068
5963
|
const tier = row.def.tier ?? "OTHER";
|
|
@@ -5107,12 +6002,12 @@ function usePlanEditor(manifest, options = {}) {
|
|
|
5107
6002
|
}
|
|
5108
6003
|
|
|
5109
6004
|
// src/vue/use-public-boot.ts
|
|
5110
|
-
var
|
|
6005
|
+
var import_vue21 = require("vue");
|
|
5111
6006
|
function usePublicBoot(options) {
|
|
5112
6007
|
const loader = new BootLoader(options);
|
|
5113
|
-
const boot = (0,
|
|
5114
|
-
const loading = (0,
|
|
5115
|
-
const error = (0,
|
|
6008
|
+
const boot = (0, import_vue21.ref)(null);
|
|
6009
|
+
const loading = (0, import_vue21.ref)(false);
|
|
6010
|
+
const error = (0, import_vue21.ref)(null);
|
|
5116
6011
|
async function load() {
|
|
5117
6012
|
loading.value = true;
|
|
5118
6013
|
error.value = null;
|
|
@@ -5129,27 +6024,27 @@ function usePublicBoot(options) {
|
|
|
5129
6024
|
}
|
|
5130
6025
|
|
|
5131
6026
|
// src/vue/use-discovery.ts
|
|
5132
|
-
var
|
|
6027
|
+
var import_vue22 = require("vue");
|
|
5133
6028
|
var DiscoveryLoadError = class extends Error {
|
|
5134
6029
|
constructor(status, message) {
|
|
5135
6030
|
super(message);
|
|
5136
6031
|
this.status = status;
|
|
5137
6032
|
this.name = "DiscoveryLoadError";
|
|
6033
|
+
markPlatformError(this);
|
|
5138
6034
|
}
|
|
5139
6035
|
status;
|
|
5140
6036
|
};
|
|
5141
6037
|
function useDiscovery(options) {
|
|
5142
|
-
const msg = useSaMessages("discovery");
|
|
5143
6038
|
if (!options?.endpoint) {
|
|
5144
6039
|
throw new Error(
|
|
5145
6040
|
'useDiscovery: `endpoint` is required (e.g. "/api/admin/discovery" or "/api/v1/admin/discovery"). The platform has no default because apps use different globalPrefix conventions.'
|
|
5146
6041
|
);
|
|
5147
6042
|
}
|
|
5148
6043
|
const http = options.http ?? defaultHttpClient();
|
|
5149
|
-
const snapshot = (0,
|
|
5150
|
-
const etag = (0,
|
|
5151
|
-
const loading = (0,
|
|
5152
|
-
const error = (0,
|
|
6044
|
+
const snapshot = (0, import_vue22.ref)(null);
|
|
6045
|
+
const etag = (0, import_vue22.ref)(null);
|
|
6046
|
+
const loading = (0, import_vue22.ref)(false);
|
|
6047
|
+
const error = (0, import_vue22.ref)(null);
|
|
5153
6048
|
async function fetchSnapshot(includeEtag) {
|
|
5154
6049
|
loading.value = true;
|
|
5155
6050
|
error.value = null;
|
|
@@ -5161,13 +6056,19 @@ function useDiscovery(options) {
|
|
|
5161
6056
|
headers["If-None-Match"] = etag.value;
|
|
5162
6057
|
}
|
|
5163
6058
|
const res = await http(options.endpoint, { method: "GET", headers });
|
|
6059
|
+
requireServerAnswer(
|
|
6060
|
+
res.status,
|
|
6061
|
+
"GET",
|
|
6062
|
+
options.endpoint,
|
|
6063
|
+
(diagnostic) => new DiscoveryLoadError(res.status, diagnostic)
|
|
6064
|
+
);
|
|
5164
6065
|
if (res.status === 304) {
|
|
5165
6066
|
return;
|
|
5166
6067
|
}
|
|
5167
6068
|
if (res.status !== 200) {
|
|
5168
6069
|
throw new DiscoveryLoadError(
|
|
5169
6070
|
res.status,
|
|
5170
|
-
|
|
6071
|
+
`Discovery endpoint responded with HTTP ${res.status}`
|
|
5171
6072
|
);
|
|
5172
6073
|
}
|
|
5173
6074
|
const body = await res.json();
|
|
@@ -5193,11 +6094,18 @@ function useDiscovery(options) {
|
|
|
5193
6094
|
const headers = { "content-type": "application/json" };
|
|
5194
6095
|
const token = options.getAuthToken?.();
|
|
5195
6096
|
if (token) headers.Authorization = `Bearer ${token}`;
|
|
5196
|
-
const
|
|
6097
|
+
const rescanUrl = `${options.endpoint}/rescan`;
|
|
6098
|
+
const res = await http(rescanUrl, { method: "POST", headers });
|
|
6099
|
+
requireServerAnswer(
|
|
6100
|
+
res.status,
|
|
6101
|
+
"POST",
|
|
6102
|
+
rescanUrl,
|
|
6103
|
+
(diagnostic) => new DiscoveryLoadError(res.status, diagnostic)
|
|
6104
|
+
);
|
|
5197
6105
|
if (res.status !== 200 && res.status !== 201) {
|
|
5198
6106
|
throw new DiscoveryLoadError(
|
|
5199
6107
|
res.status,
|
|
5200
|
-
|
|
6108
|
+
`Discovery rescan responded with HTTP ${res.status}`
|
|
5201
6109
|
);
|
|
5202
6110
|
}
|
|
5203
6111
|
const body = await res.json();
|
|
@@ -5216,13 +6124,14 @@ function useDiscovery(options) {
|
|
|
5216
6124
|
}
|
|
5217
6125
|
|
|
5218
6126
|
// src/vue/use-catalog-entries.ts
|
|
5219
|
-
var
|
|
6127
|
+
var import_vue23 = require("vue");
|
|
5220
6128
|
var CatalogEntriesApiError = class extends Error {
|
|
5221
6129
|
constructor(status, body, message) {
|
|
5222
6130
|
super(message);
|
|
5223
6131
|
this.status = status;
|
|
5224
6132
|
this.body = body;
|
|
5225
6133
|
this.name = "CatalogEntriesApiError";
|
|
6134
|
+
markPlatformError(this);
|
|
5226
6135
|
}
|
|
5227
6136
|
status;
|
|
5228
6137
|
body;
|
|
@@ -5234,13 +6143,12 @@ function useCatalogEntries(options) {
|
|
|
5234
6143
|
if (!options?.projectKey) {
|
|
5235
6144
|
throw new Error("useCatalogEntries: `projectKey` is required.");
|
|
5236
6145
|
}
|
|
5237
|
-
const msg = useSaMessages("discovery");
|
|
5238
6146
|
const http = options.http ?? defaultHttpClient();
|
|
5239
|
-
const capabilities = (0,
|
|
5240
|
-
const features = (0,
|
|
5241
|
-
const quotas = (0,
|
|
5242
|
-
const loading = (0,
|
|
5243
|
-
const error = (0,
|
|
6147
|
+
const capabilities = (0, import_vue23.ref)([]);
|
|
6148
|
+
const features = (0, import_vue23.ref)([]);
|
|
6149
|
+
const quotas = (0, import_vue23.ref)([]);
|
|
6150
|
+
const loading = (0, import_vue23.ref)(false);
|
|
6151
|
+
const error = (0, import_vue23.ref)(null);
|
|
5244
6152
|
const base = `${options.adminEndpoint}/catalog`;
|
|
5245
6153
|
const pk = encodeURIComponent(options.projectKey);
|
|
5246
6154
|
function authHeaders() {
|
|
@@ -5248,18 +6156,25 @@ function useCatalogEntries(options) {
|
|
|
5248
6156
|
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
5249
6157
|
}
|
|
5250
6158
|
async function fetchJson(url, init) {
|
|
6159
|
+
const method = init?.method ?? "GET";
|
|
5251
6160
|
const res = await http(url, {
|
|
5252
|
-
method
|
|
6161
|
+
method,
|
|
5253
6162
|
headers: { "content-type": "application/json", ...authHeaders(), ...init?.headers },
|
|
5254
6163
|
body: init?.body
|
|
5255
6164
|
});
|
|
6165
|
+
requireServerAnswer(
|
|
6166
|
+
res.status,
|
|
6167
|
+
method,
|
|
6168
|
+
url,
|
|
6169
|
+
(diagnostic) => new CatalogEntriesApiError(res.status, null, diagnostic)
|
|
6170
|
+
);
|
|
5256
6171
|
if (res.status === 204) return null;
|
|
5257
6172
|
const body = await res.json().catch(() => null);
|
|
5258
6173
|
if (res.status >= 400) {
|
|
5259
6174
|
throw new CatalogEntriesApiError(
|
|
5260
6175
|
res.status,
|
|
5261
6176
|
body,
|
|
5262
|
-
|
|
6177
|
+
`Catalog entries API responded with HTTP ${res.status}`
|
|
5263
6178
|
);
|
|
5264
6179
|
}
|
|
5265
6180
|
return body;
|
|
@@ -5287,7 +6202,8 @@ function useCatalogEntries(options) {
|
|
|
5287
6202
|
`${base}/features/${encodeURIComponent(featureKey)}/review?projectKey=${pk}`,
|
|
5288
6203
|
{ method: "PATCH", body: JSON.stringify(data) }
|
|
5289
6204
|
);
|
|
5290
|
-
if (!updated)
|
|
6205
|
+
if (!updated)
|
|
6206
|
+
throw markEmptyResponse(new CatalogEntriesApiError(0, null, "Review returned no body"));
|
|
5291
6207
|
features.value = features.value.map((f) => f.featureKey === featureKey ? updated : f);
|
|
5292
6208
|
return updated;
|
|
5293
6209
|
}
|
|
@@ -5296,7 +6212,8 @@ function useCatalogEntries(options) {
|
|
|
5296
6212
|
`${base}/quotas/${encodeURIComponent(quotaKey)}/review?projectKey=${pk}`,
|
|
5297
6213
|
{ method: "PATCH", body: JSON.stringify(data) }
|
|
5298
6214
|
);
|
|
5299
|
-
if (!updated)
|
|
6215
|
+
if (!updated)
|
|
6216
|
+
throw markEmptyResponse(new CatalogEntriesApiError(0, null, "Review returned no body"));
|
|
5300
6217
|
quotas.value = quotas.value.map((q) => q.quotaKey === quotaKey ? updated : q);
|
|
5301
6218
|
return updated;
|
|
5302
6219
|
}
|
|
@@ -5305,7 +6222,8 @@ function useCatalogEntries(options) {
|
|
|
5305
6222
|
`${base}/features/${encodeURIComponent(featureKey)}/i18n?projectKey=${pk}`,
|
|
5306
6223
|
{ method: "PATCH", body: JSON.stringify({ i18n }) }
|
|
5307
6224
|
);
|
|
5308
|
-
if (!updated)
|
|
6225
|
+
if (!updated)
|
|
6226
|
+
throw markEmptyResponse(new CatalogEntriesApiError(0, null, "i18n returned no body"));
|
|
5309
6227
|
features.value = features.value.map((f) => f.featureKey === featureKey ? updated : f);
|
|
5310
6228
|
return updated;
|
|
5311
6229
|
}
|
|
@@ -5314,7 +6232,8 @@ function useCatalogEntries(options) {
|
|
|
5314
6232
|
`${base}/quotas/${encodeURIComponent(quotaKey)}/i18n?projectKey=${pk}`,
|
|
5315
6233
|
{ method: "PATCH", body: JSON.stringify({ i18n }) }
|
|
5316
6234
|
);
|
|
5317
|
-
if (!updated)
|
|
6235
|
+
if (!updated)
|
|
6236
|
+
throw markEmptyResponse(new CatalogEntriesApiError(0, null, "i18n returned no body"));
|
|
5318
6237
|
quotas.value = quotas.value.map((q) => q.quotaKey === quotaKey ? updated : q);
|
|
5319
6238
|
return updated;
|
|
5320
6239
|
}
|
|
@@ -5323,7 +6242,8 @@ function useCatalogEntries(options) {
|
|
|
5323
6242
|
`${base}/features/${encodeURIComponent(featureKey)}?projectKey=${pk}`,
|
|
5324
6243
|
{ method: "PATCH", body: JSON.stringify(data) }
|
|
5325
6244
|
);
|
|
5326
|
-
if (!updated)
|
|
6245
|
+
if (!updated)
|
|
6246
|
+
throw markEmptyResponse(new CatalogEntriesApiError(0, null, "Base returned no body"));
|
|
5327
6247
|
features.value = features.value.map((f) => f.featureKey === featureKey ? updated : f);
|
|
5328
6248
|
return updated;
|
|
5329
6249
|
}
|
|
@@ -5332,7 +6252,8 @@ function useCatalogEntries(options) {
|
|
|
5332
6252
|
`${base}/quotas/${encodeURIComponent(quotaKey)}?projectKey=${pk}`,
|
|
5333
6253
|
{ method: "PATCH", body: JSON.stringify(data) }
|
|
5334
6254
|
);
|
|
5335
|
-
if (!updated)
|
|
6255
|
+
if (!updated)
|
|
6256
|
+
throw markEmptyResponse(new CatalogEntriesApiError(0, null, "Base returned no body"));
|
|
5336
6257
|
quotas.value = quotas.value.map((q) => q.quotaKey === quotaKey ? updated : q);
|
|
5337
6258
|
return updated;
|
|
5338
6259
|
}
|
|
@@ -5341,7 +6262,8 @@ function useCatalogEntries(options) {
|
|
|
5341
6262
|
method: "POST",
|
|
5342
6263
|
body: JSON.stringify({ snapshot })
|
|
5343
6264
|
});
|
|
5344
|
-
if (!result)
|
|
6265
|
+
if (!result)
|
|
6266
|
+
throw markEmptyResponse(new CatalogEntriesApiError(0, null, "Sync returned no body"));
|
|
5345
6267
|
await load();
|
|
5346
6268
|
return result;
|
|
5347
6269
|
}
|
|
@@ -5366,13 +6288,14 @@ function useCatalogEntries(options) {
|
|
|
5366
6288
|
}
|
|
5367
6289
|
|
|
5368
6290
|
// src/vue/use-bundles.ts
|
|
5369
|
-
var
|
|
6291
|
+
var import_vue24 = require("vue");
|
|
5370
6292
|
var BundlesApiError = class extends Error {
|
|
5371
6293
|
constructor(status, body, message) {
|
|
5372
6294
|
super(message);
|
|
5373
6295
|
this.status = status;
|
|
5374
6296
|
this.body = body;
|
|
5375
6297
|
this.name = "BundlesApiError";
|
|
6298
|
+
markPlatformError(this);
|
|
5376
6299
|
}
|
|
5377
6300
|
status;
|
|
5378
6301
|
body;
|
|
@@ -5387,20 +6310,27 @@ function useBundles(options) {
|
|
|
5387
6310
|
throw new Error("useBundles: `projectKey` is required.");
|
|
5388
6311
|
}
|
|
5389
6312
|
const http = options.http ?? defaultHttpClient();
|
|
5390
|
-
const bundles = (0,
|
|
5391
|
-
const loading = (0,
|
|
5392
|
-
const error = (0,
|
|
6313
|
+
const bundles = (0, import_vue24.ref)([]);
|
|
6314
|
+
const loading = (0, import_vue24.ref)(false);
|
|
6315
|
+
const error = (0, import_vue24.ref)(null);
|
|
5393
6316
|
const baseUrl = `${options.adminEndpoint}/catalog/bundles`;
|
|
5394
6317
|
function authHeaders() {
|
|
5395
6318
|
const token = options.getAuthToken?.();
|
|
5396
6319
|
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
5397
6320
|
}
|
|
5398
6321
|
async function fetchJson(url, init) {
|
|
6322
|
+
const method = init?.method ?? "GET";
|
|
5399
6323
|
const res = await http(url, {
|
|
5400
|
-
method
|
|
6324
|
+
method,
|
|
5401
6325
|
headers: { "content-type": "application/json", ...authHeaders(), ...init?.headers },
|
|
5402
6326
|
body: init?.body
|
|
5403
6327
|
});
|
|
6328
|
+
requireServerAnswer(
|
|
6329
|
+
res.status,
|
|
6330
|
+
method,
|
|
6331
|
+
url,
|
|
6332
|
+
(diagnostic) => new BundlesApiError(res.status, null, diagnostic)
|
|
6333
|
+
);
|
|
5404
6334
|
if (res.status === 204) return null;
|
|
5405
6335
|
const body = await res.json().catch(() => null);
|
|
5406
6336
|
if (res.status >= 400) {
|
|
@@ -5431,7 +6361,8 @@ function useBundles(options) {
|
|
|
5431
6361
|
method: "POST",
|
|
5432
6362
|
body: JSON.stringify(data)
|
|
5433
6363
|
});
|
|
5434
|
-
if (!created)
|
|
6364
|
+
if (!created)
|
|
6365
|
+
throw markEmptyResponse(new BundlesApiError(0, null, "Create returned no body"));
|
|
5435
6366
|
bundles.value = [...bundles.value, created];
|
|
5436
6367
|
return created;
|
|
5437
6368
|
}
|
|
@@ -5440,7 +6371,8 @@ function useBundles(options) {
|
|
|
5440
6371
|
method: "PATCH",
|
|
5441
6372
|
body: JSON.stringify(data)
|
|
5442
6373
|
});
|
|
5443
|
-
if (!updated)
|
|
6374
|
+
if (!updated)
|
|
6375
|
+
throw markEmptyResponse(new BundlesApiError(0, null, "Update returned no body"));
|
|
5444
6376
|
bundles.value = bundles.value.map((b) => b.id === bundleId ? updated : b);
|
|
5445
6377
|
return updated;
|
|
5446
6378
|
}
|
|
@@ -5461,9 +6393,9 @@ function useBundleVersions(options) {
|
|
|
5461
6393
|
throw new Error("useBundleVersions: `bundleId` is required.");
|
|
5462
6394
|
}
|
|
5463
6395
|
const http = options.http ?? defaultHttpClient();
|
|
5464
|
-
const versions = (0,
|
|
5465
|
-
const loading = (0,
|
|
5466
|
-
const error = (0,
|
|
6396
|
+
const versions = (0, import_vue24.ref)([]);
|
|
6397
|
+
const loading = (0, import_vue24.ref)(false);
|
|
6398
|
+
const error = (0, import_vue24.ref)(null);
|
|
5467
6399
|
const bundleVersionsUrl = `${options.adminEndpoint}/catalog/bundles/${options.bundleId}/versions`;
|
|
5468
6400
|
const versionUrlBase = `${options.adminEndpoint}/catalog/bundle-versions`;
|
|
5469
6401
|
function authHeaders() {
|
|
@@ -5471,11 +6403,18 @@ function useBundleVersions(options) {
|
|
|
5471
6403
|
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
5472
6404
|
}
|
|
5473
6405
|
async function fetchJson(url, init) {
|
|
6406
|
+
const method = init?.method ?? "GET";
|
|
5474
6407
|
const res = await http(url, {
|
|
5475
|
-
method
|
|
6408
|
+
method,
|
|
5476
6409
|
headers: { "content-type": "application/json", ...authHeaders(), ...init?.headers },
|
|
5477
6410
|
body: init?.body
|
|
5478
6411
|
});
|
|
6412
|
+
requireServerAnswer(
|
|
6413
|
+
res.status,
|
|
6414
|
+
method,
|
|
6415
|
+
url,
|
|
6416
|
+
(diagnostic) => new BundlesApiError(res.status, null, diagnostic)
|
|
6417
|
+
);
|
|
5479
6418
|
if (res.status === 204) return null;
|
|
5480
6419
|
const body = await res.json().catch(() => null);
|
|
5481
6420
|
if (res.status >= 400) {
|
|
@@ -5504,7 +6443,8 @@ function useBundleVersions(options) {
|
|
|
5504
6443
|
method: "POST",
|
|
5505
6444
|
body: JSON.stringify(data)
|
|
5506
6445
|
});
|
|
5507
|
-
if (!result)
|
|
6446
|
+
if (!result)
|
|
6447
|
+
throw markEmptyResponse(new BundlesApiError(0, null, "CreateDraft returned no body"));
|
|
5508
6448
|
versions.value = [...versions.value, result.bundleVersion];
|
|
5509
6449
|
return result;
|
|
5510
6450
|
}
|
|
@@ -5513,7 +6453,8 @@ function useBundleVersions(options) {
|
|
|
5513
6453
|
`${versionUrlBase}/${versionId}`,
|
|
5514
6454
|
{ method: "PATCH", body: JSON.stringify(data) }
|
|
5515
6455
|
);
|
|
5516
|
-
if (!result)
|
|
6456
|
+
if (!result)
|
|
6457
|
+
throw markEmptyResponse(new BundlesApiError(0, null, "UpdateDraft returned no body"));
|
|
5517
6458
|
versions.value = versions.value.map((v) => v.id === versionId ? result.bundleVersion : v);
|
|
5518
6459
|
return result;
|
|
5519
6460
|
}
|
|
@@ -5522,7 +6463,8 @@ function useBundleVersions(options) {
|
|
|
5522
6463
|
`${versionUrlBase}/${versionId}/publish`,
|
|
5523
6464
|
{ method: "POST", body: JSON.stringify(opts) }
|
|
5524
6465
|
);
|
|
5525
|
-
if (!result)
|
|
6466
|
+
if (!result)
|
|
6467
|
+
throw markEmptyResponse(new BundlesApiError(0, null, "Publish returned no body"));
|
|
5526
6468
|
await load();
|
|
5527
6469
|
return result;
|
|
5528
6470
|
}
|
|
@@ -5537,7 +6479,7 @@ function useBundleVersions(options) {
|
|
|
5537
6479
|
}
|
|
5538
6480
|
|
|
5539
6481
|
// src/vue/use-bundle-versions-map.ts
|
|
5540
|
-
var
|
|
6482
|
+
var import_vue25 = require("vue");
|
|
5541
6483
|
function useBundleVersionsMap(options) {
|
|
5542
6484
|
if (!options?.adminEndpoint) {
|
|
5543
6485
|
throw new Error("useBundleVersionsMap: `adminEndpoint` is required.");
|
|
@@ -5546,9 +6488,9 @@ function useBundleVersionsMap(options) {
|
|
|
5546
6488
|
throw new Error("useBundleVersionsMap: `bundles` is required.");
|
|
5547
6489
|
}
|
|
5548
6490
|
const http = options.http ?? defaultHttpClient();
|
|
5549
|
-
const versionsByBundle = (0,
|
|
5550
|
-
const loading = (0,
|
|
5551
|
-
const error = (0,
|
|
6491
|
+
const versionsByBundle = (0, import_vue25.ref)({});
|
|
6492
|
+
const loading = (0, import_vue25.ref)(false);
|
|
6493
|
+
const error = (0, import_vue25.ref)(null);
|
|
5552
6494
|
function authHeaders() {
|
|
5553
6495
|
const token = options.getAuthToken?.();
|
|
5554
6496
|
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
@@ -5598,7 +6540,7 @@ function useBundleVersionsMap(options) {
|
|
|
5598
6540
|
console.warn(`useBundleVersionsMap: refreshOne('${bundleId}') failed`, err);
|
|
5599
6541
|
}
|
|
5600
6542
|
}
|
|
5601
|
-
(0,
|
|
6543
|
+
(0, import_vue25.watch)(
|
|
5602
6544
|
() => options.bundles.value.map((b) => b.id).join(","),
|
|
5603
6545
|
() => {
|
|
5604
6546
|
if (options.bundles.value.length > 0) void refresh();
|
|
@@ -5610,13 +6552,14 @@ function useBundleVersionsMap(options) {
|
|
|
5610
6552
|
}
|
|
5611
6553
|
|
|
5612
6554
|
// src/vue/use-tenant-subscription-bundles.ts
|
|
5613
|
-
var
|
|
6555
|
+
var import_vue26 = require("vue");
|
|
5614
6556
|
var TenantSubscriptionBundlesApiError = class extends Error {
|
|
5615
6557
|
constructor(status, body, message) {
|
|
5616
6558
|
super(message);
|
|
5617
6559
|
this.status = status;
|
|
5618
6560
|
this.body = body;
|
|
5619
6561
|
this.name = "TenantSubscriptionBundlesApiError";
|
|
6562
|
+
markPlatformError(this);
|
|
5620
6563
|
}
|
|
5621
6564
|
status;
|
|
5622
6565
|
body;
|
|
@@ -5629,19 +6572,26 @@ function useTenantSubscriptionBundles(options) {
|
|
|
5629
6572
|
}
|
|
5630
6573
|
const http = options.http ?? defaultHttpClient();
|
|
5631
6574
|
const baseUrl = `${options.billingEndpoint}/billing/subscription-bundles`;
|
|
5632
|
-
const bundles = (0,
|
|
5633
|
-
const loading = (0,
|
|
5634
|
-
const error = (0,
|
|
6575
|
+
const bundles = (0, import_vue26.ref)([]);
|
|
6576
|
+
const loading = (0, import_vue26.ref)(false);
|
|
6577
|
+
const error = (0, import_vue26.ref)(null);
|
|
5635
6578
|
function authHeaders() {
|
|
5636
6579
|
const token = options.getAuthToken?.();
|
|
5637
6580
|
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
5638
6581
|
}
|
|
5639
6582
|
async function fetchJson(url, init) {
|
|
6583
|
+
const method = init?.method ?? "GET";
|
|
5640
6584
|
const res = await http(url, {
|
|
5641
|
-
method
|
|
6585
|
+
method,
|
|
5642
6586
|
headers: { "content-type": "application/json", ...authHeaders(), ...init?.headers },
|
|
5643
6587
|
body: init?.body
|
|
5644
6588
|
});
|
|
6589
|
+
requireServerAnswer(
|
|
6590
|
+
res.status,
|
|
6591
|
+
method,
|
|
6592
|
+
url,
|
|
6593
|
+
(diagnostic) => new TenantSubscriptionBundlesApiError(res.status, null, diagnostic)
|
|
6594
|
+
);
|
|
5645
6595
|
if (res.status === 204) return null;
|
|
5646
6596
|
const body = await res.json().catch(() => null);
|
|
5647
6597
|
if (res.status >= 400) {
|
|
@@ -5671,7 +6621,9 @@ function useTenantSubscriptionBundles(options) {
|
|
|
5671
6621
|
body: JSON.stringify(data)
|
|
5672
6622
|
});
|
|
5673
6623
|
if (!result) {
|
|
5674
|
-
throw
|
|
6624
|
+
throw markEmptyResponse(
|
|
6625
|
+
new TenantSubscriptionBundlesApiError(0, null, "add returned no body")
|
|
6626
|
+
);
|
|
5675
6627
|
}
|
|
5676
6628
|
const hydrated = rehydrateDates(result);
|
|
5677
6629
|
bundles.value = [hydrated, ...bundles.value];
|
|
@@ -5683,7 +6635,9 @@ function useTenantSubscriptionBundles(options) {
|
|
|
5683
6635
|
{ method: "DELETE", body: JSON.stringify(opts) }
|
|
5684
6636
|
);
|
|
5685
6637
|
if (!result) {
|
|
5686
|
-
throw
|
|
6638
|
+
throw markEmptyResponse(
|
|
6639
|
+
new TenantSubscriptionBundlesApiError(0, null, "cancel returned no body")
|
|
6640
|
+
);
|
|
5687
6641
|
}
|
|
5688
6642
|
const hydrated = rehydrateDates(result);
|
|
5689
6643
|
bundles.value = bundles.value.map((b) => b.id === subscriptionBundleId ? hydrated : b);
|
|
@@ -5705,13 +6659,14 @@ function rehydrateDates(raw) {
|
|
|
5705
6659
|
}
|
|
5706
6660
|
|
|
5707
6661
|
// src/vue/use-marketing-projections.ts
|
|
5708
|
-
var
|
|
6662
|
+
var import_vue27 = require("vue");
|
|
5709
6663
|
var MarketingProjectionsApiError = class extends Error {
|
|
5710
6664
|
constructor(status, body, message) {
|
|
5711
6665
|
super(message);
|
|
5712
6666
|
this.status = status;
|
|
5713
6667
|
this.body = body;
|
|
5714
6668
|
this.name = "MarketingProjectionsApiError";
|
|
6669
|
+
markPlatformError(this);
|
|
5715
6670
|
}
|
|
5716
6671
|
status;
|
|
5717
6672
|
body;
|
|
@@ -5723,12 +6678,11 @@ function useMarketingProjections(options) {
|
|
|
5723
6678
|
if (!options?.filter?.projectKey) {
|
|
5724
6679
|
throw new Error("useMarketingProjections: `filter.projectKey` is required.");
|
|
5725
6680
|
}
|
|
5726
|
-
const msg = useSaMessages("marketing");
|
|
5727
6681
|
const http = options.http ?? defaultHttpClient();
|
|
5728
|
-
const projections = (0,
|
|
5729
|
-
const filter = (0,
|
|
5730
|
-
const loading = (0,
|
|
5731
|
-
const error = (0,
|
|
6682
|
+
const projections = (0, import_vue27.ref)([]);
|
|
6683
|
+
const filter = (0, import_vue27.ref)({ ...options.filter });
|
|
6684
|
+
const loading = (0, import_vue27.ref)(false);
|
|
6685
|
+
const error = (0, import_vue27.ref)(null);
|
|
5732
6686
|
const baseUrl = `${options.adminEndpoint}/catalog/marketing-projections`;
|
|
5733
6687
|
function authHeaders() {
|
|
5734
6688
|
const token = options.getAuthToken?.();
|
|
@@ -5744,18 +6698,25 @@ function useMarketingProjections(options) {
|
|
|
5744
6698
|
return `${baseUrl}?${params.toString()}`;
|
|
5745
6699
|
}
|
|
5746
6700
|
async function fetchJson(url, init) {
|
|
6701
|
+
const method = init?.method ?? "GET";
|
|
5747
6702
|
const res = await http(url, {
|
|
5748
|
-
method
|
|
6703
|
+
method,
|
|
5749
6704
|
headers: { "content-type": "application/json", ...authHeaders(), ...init?.headers },
|
|
5750
6705
|
body: init?.body
|
|
5751
6706
|
});
|
|
6707
|
+
requireServerAnswer(
|
|
6708
|
+
res.status,
|
|
6709
|
+
method,
|
|
6710
|
+
url,
|
|
6711
|
+
(diagnostic) => new MarketingProjectionsApiError(res.status, null, diagnostic)
|
|
6712
|
+
);
|
|
5752
6713
|
if (res.status === 204) return null;
|
|
5753
6714
|
const body = await res.json().catch(() => null);
|
|
5754
6715
|
if (res.status >= 400) {
|
|
5755
6716
|
throw new MarketingProjectionsApiError(
|
|
5756
6717
|
res.status,
|
|
5757
6718
|
body,
|
|
5758
|
-
|
|
6719
|
+
`Marketing projections API responded with HTTP ${res.status}`
|
|
5759
6720
|
);
|
|
5760
6721
|
}
|
|
5761
6722
|
return body;
|
|
@@ -5782,7 +6743,9 @@ function useMarketingProjections(options) {
|
|
|
5782
6743
|
body: JSON.stringify(data)
|
|
5783
6744
|
});
|
|
5784
6745
|
if (!created) {
|
|
5785
|
-
throw
|
|
6746
|
+
throw markEmptyResponse(
|
|
6747
|
+
new MarketingProjectionsApiError(0, null, "Create returned no body")
|
|
6748
|
+
);
|
|
5786
6749
|
}
|
|
5787
6750
|
await load();
|
|
5788
6751
|
return created;
|
|
@@ -5793,7 +6756,9 @@ function useMarketingProjections(options) {
|
|
|
5793
6756
|
body: JSON.stringify(data)
|
|
5794
6757
|
});
|
|
5795
6758
|
if (!updated) {
|
|
5796
|
-
throw
|
|
6759
|
+
throw markEmptyResponse(
|
|
6760
|
+
new MarketingProjectionsApiError(0, null, "Update returned no body")
|
|
6761
|
+
);
|
|
5797
6762
|
}
|
|
5798
6763
|
projections.value = projections.value.map((p) => p.id === id ? updated : p);
|
|
5799
6764
|
return updated;
|
|
@@ -5809,13 +6774,14 @@ function useMarketingProjections(options) {
|
|
|
5809
6774
|
}
|
|
5810
6775
|
|
|
5811
6776
|
// src/vue/use-promotions.ts
|
|
5812
|
-
var
|
|
6777
|
+
var import_vue28 = require("vue");
|
|
5813
6778
|
var PromotionsApiError = class extends Error {
|
|
5814
6779
|
constructor(status, body, message) {
|
|
5815
6780
|
super(message);
|
|
5816
6781
|
this.status = status;
|
|
5817
6782
|
this.body = body;
|
|
5818
6783
|
this.name = "PromotionsApiError";
|
|
6784
|
+
markPlatformError(this);
|
|
5819
6785
|
}
|
|
5820
6786
|
status;
|
|
5821
6787
|
body;
|
|
@@ -5827,11 +6793,10 @@ function usePromotions(options) {
|
|
|
5827
6793
|
if (!options?.projectKey) {
|
|
5828
6794
|
throw new Error("usePromotions: `projectKey` is required.");
|
|
5829
6795
|
}
|
|
5830
|
-
const msg = useSaMessages("promos");
|
|
5831
6796
|
const http = options.http ?? defaultHttpClient();
|
|
5832
|
-
const promotions = (0,
|
|
5833
|
-
const loading = (0,
|
|
5834
|
-
const error = (0,
|
|
6797
|
+
const promotions = (0, import_vue28.ref)([]);
|
|
6798
|
+
const loading = (0, import_vue28.ref)(false);
|
|
6799
|
+
const error = (0, import_vue28.ref)(null);
|
|
5835
6800
|
const baseUrl = `${options.adminEndpoint}/catalog/promotions`;
|
|
5836
6801
|
const pk = encodeURIComponent(options.projectKey);
|
|
5837
6802
|
function authHeaders() {
|
|
@@ -5839,18 +6804,25 @@ function usePromotions(options) {
|
|
|
5839
6804
|
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
5840
6805
|
}
|
|
5841
6806
|
async function fetchJson(url, init) {
|
|
6807
|
+
const method = init?.method ?? "GET";
|
|
5842
6808
|
const res = await http(url, {
|
|
5843
|
-
method
|
|
6809
|
+
method,
|
|
5844
6810
|
headers: { "content-type": "application/json", ...authHeaders(), ...init?.headers },
|
|
5845
6811
|
body: init?.body
|
|
5846
6812
|
});
|
|
6813
|
+
requireServerAnswer(
|
|
6814
|
+
res.status,
|
|
6815
|
+
method,
|
|
6816
|
+
url,
|
|
6817
|
+
(diagnostic) => new PromotionsApiError(res.status, null, diagnostic)
|
|
6818
|
+
);
|
|
5847
6819
|
if (res.status === 204) return null;
|
|
5848
6820
|
const body = await res.json().catch(() => null);
|
|
5849
6821
|
if (res.status >= 400) {
|
|
5850
6822
|
throw new PromotionsApiError(
|
|
5851
6823
|
res.status,
|
|
5852
6824
|
body,
|
|
5853
|
-
|
|
6825
|
+
`Promotions API responded with HTTP ${res.status}`
|
|
5854
6826
|
);
|
|
5855
6827
|
}
|
|
5856
6828
|
return body;
|
|
@@ -5872,7 +6844,8 @@ function usePromotions(options) {
|
|
|
5872
6844
|
method: "POST",
|
|
5873
6845
|
body: JSON.stringify(data)
|
|
5874
6846
|
});
|
|
5875
|
-
if (!created)
|
|
6847
|
+
if (!created)
|
|
6848
|
+
throw markEmptyResponse(new PromotionsApiError(0, null, "Create returned no body"));
|
|
5876
6849
|
promotions.value = [...promotions.value, created];
|
|
5877
6850
|
return created;
|
|
5878
6851
|
}
|
|
@@ -5881,7 +6854,8 @@ function usePromotions(options) {
|
|
|
5881
6854
|
method: "PATCH",
|
|
5882
6855
|
body: JSON.stringify(data)
|
|
5883
6856
|
});
|
|
5884
|
-
if (!updated)
|
|
6857
|
+
if (!updated)
|
|
6858
|
+
throw markEmptyResponse(new PromotionsApiError(0, null, "Update returned no body"));
|
|
5885
6859
|
promotions.value = promotions.value.map((p) => p.id === id ? updated : p);
|
|
5886
6860
|
return updated;
|
|
5887
6861
|
}
|
|
@@ -5896,13 +6870,14 @@ function usePromotions(options) {
|
|
|
5896
6870
|
}
|
|
5897
6871
|
|
|
5898
6872
|
// src/vue/use-plans.ts
|
|
5899
|
-
var
|
|
6873
|
+
var import_vue29 = require("vue");
|
|
5900
6874
|
var PlansApiError = class extends Error {
|
|
5901
6875
|
constructor(status, body, message) {
|
|
5902
6876
|
super(message);
|
|
5903
6877
|
this.status = status;
|
|
5904
6878
|
this.body = body;
|
|
5905
6879
|
this.name = "PlansApiError";
|
|
6880
|
+
markPlatformError(this);
|
|
5906
6881
|
}
|
|
5907
6882
|
status;
|
|
5908
6883
|
body;
|
|
@@ -5917,18 +6892,19 @@ function usePlans(options) {
|
|
|
5917
6892
|
throw new Error("usePlans: `projectKey` is required.");
|
|
5918
6893
|
}
|
|
5919
6894
|
const http = options.http ?? defaultHttpClient();
|
|
5920
|
-
const plans = (0,
|
|
5921
|
-
const loading = (0,
|
|
5922
|
-
const error = (0,
|
|
5923
|
-
const tenantCountsByPlanKey = (0,
|
|
6895
|
+
const plans = (0, import_vue29.ref)([]);
|
|
6896
|
+
const loading = (0, import_vue29.ref)(false);
|
|
6897
|
+
const error = (0, import_vue29.ref)(null);
|
|
6898
|
+
const tenantCountsByPlanKey = (0, import_vue29.ref)({});
|
|
5924
6899
|
const baseUrl = `${options.adminEndpoint}/catalog/plans`;
|
|
5925
6900
|
function authHeaders() {
|
|
5926
6901
|
const token = options.getAuthToken?.();
|
|
5927
6902
|
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
5928
6903
|
}
|
|
5929
6904
|
async function fetchJson(url, init) {
|
|
6905
|
+
const method = init?.method ?? "GET";
|
|
5930
6906
|
const res = await http(url, {
|
|
5931
|
-
method
|
|
6907
|
+
method,
|
|
5932
6908
|
headers: {
|
|
5933
6909
|
"content-type": "application/json",
|
|
5934
6910
|
...authHeaders(),
|
|
@@ -5936,6 +6912,12 @@ function usePlans(options) {
|
|
|
5936
6912
|
},
|
|
5937
6913
|
body: init?.body
|
|
5938
6914
|
});
|
|
6915
|
+
requireServerAnswer(
|
|
6916
|
+
res.status,
|
|
6917
|
+
method,
|
|
6918
|
+
url,
|
|
6919
|
+
(diagnostic) => new PlansApiError(res.status, null, diagnostic)
|
|
6920
|
+
);
|
|
5939
6921
|
if (res.status === 204) return null;
|
|
5940
6922
|
const body = await res.json().catch(() => null);
|
|
5941
6923
|
if (res.status >= 400) {
|
|
@@ -5976,7 +6958,8 @@ function usePlans(options) {
|
|
|
5976
6958
|
method: "POST",
|
|
5977
6959
|
body: JSON.stringify(data)
|
|
5978
6960
|
});
|
|
5979
|
-
if (!created)
|
|
6961
|
+
if (!created)
|
|
6962
|
+
throw markEmptyResponse(new PlansApiError(0, null, "Create returned no body"));
|
|
5980
6963
|
plans.value = [...plans.value, created];
|
|
5981
6964
|
return created;
|
|
5982
6965
|
}
|
|
@@ -5985,7 +6968,8 @@ function usePlans(options) {
|
|
|
5985
6968
|
method: "PATCH",
|
|
5986
6969
|
body: JSON.stringify(data)
|
|
5987
6970
|
});
|
|
5988
|
-
if (!updated)
|
|
6971
|
+
if (!updated)
|
|
6972
|
+
throw markEmptyResponse(new PlansApiError(0, null, "Update returned no body"));
|
|
5989
6973
|
plans.value = plans.value.map((p) => p.id === planId ? updated : p);
|
|
5990
6974
|
return updated;
|
|
5991
6975
|
}
|
|
@@ -6021,9 +7005,9 @@ function usePlanVersions(options) {
|
|
|
6021
7005
|
throw new Error("usePlanVersions: `planId` is required.");
|
|
6022
7006
|
}
|
|
6023
7007
|
const http = options.http ?? defaultHttpClient();
|
|
6024
|
-
const versions = (0,
|
|
6025
|
-
const loading = (0,
|
|
6026
|
-
const error = (0,
|
|
7008
|
+
const versions = (0, import_vue29.ref)([]);
|
|
7009
|
+
const loading = (0, import_vue29.ref)(false);
|
|
7010
|
+
const error = (0, import_vue29.ref)(null);
|
|
6027
7011
|
const planVersionsUrl = `${options.adminEndpoint}/catalog/plans/${options.planId}/versions`;
|
|
6028
7012
|
const versionUrlBase = `${options.adminEndpoint}/catalog/plan-versions`;
|
|
6029
7013
|
function authHeaders() {
|
|
@@ -6031,8 +7015,9 @@ function usePlanVersions(options) {
|
|
|
6031
7015
|
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
6032
7016
|
}
|
|
6033
7017
|
async function fetchJson(url, init) {
|
|
7018
|
+
const method = init?.method ?? "GET";
|
|
6034
7019
|
const res = await http(url, {
|
|
6035
|
-
method
|
|
7020
|
+
method,
|
|
6036
7021
|
headers: {
|
|
6037
7022
|
"content-type": "application/json",
|
|
6038
7023
|
...authHeaders(),
|
|
@@ -6040,6 +7025,12 @@ function usePlanVersions(options) {
|
|
|
6040
7025
|
},
|
|
6041
7026
|
body: init?.body
|
|
6042
7027
|
});
|
|
7028
|
+
requireServerAnswer(
|
|
7029
|
+
res.status,
|
|
7030
|
+
method,
|
|
7031
|
+
url,
|
|
7032
|
+
(diagnostic) => new PlansApiError(res.status, null, diagnostic)
|
|
7033
|
+
);
|
|
6043
7034
|
if (res.status === 204) return null;
|
|
6044
7035
|
const body = await res.json().catch(() => null);
|
|
6045
7036
|
if (res.status >= 400) {
|
|
@@ -6068,7 +7059,8 @@ function usePlanVersions(options) {
|
|
|
6068
7059
|
method: "POST",
|
|
6069
7060
|
body: JSON.stringify(data)
|
|
6070
7061
|
});
|
|
6071
|
-
if (!result)
|
|
7062
|
+
if (!result)
|
|
7063
|
+
throw markEmptyResponse(new PlansApiError(0, null, "CreateDraft returned no body"));
|
|
6072
7064
|
versions.value = [...versions.value, result.planVersion];
|
|
6073
7065
|
return result;
|
|
6074
7066
|
}
|
|
@@ -6077,7 +7069,8 @@ function usePlanVersions(options) {
|
|
|
6077
7069
|
`${versionUrlBase}/${versionId}`,
|
|
6078
7070
|
{ method: "PATCH", body: JSON.stringify(data) }
|
|
6079
7071
|
);
|
|
6080
|
-
if (!result)
|
|
7072
|
+
if (!result)
|
|
7073
|
+
throw markEmptyResponse(new PlansApiError(0, null, "UpdateDraft returned no body"));
|
|
6081
7074
|
versions.value = versions.value.map((v) => v.id === versionId ? result.planVersion : v);
|
|
6082
7075
|
return result;
|
|
6083
7076
|
}
|
|
@@ -6086,7 +7079,8 @@ function usePlanVersions(options) {
|
|
|
6086
7079
|
`${versionUrlBase}/${versionId}/publish`,
|
|
6087
7080
|
{ method: "POST", body: JSON.stringify(opts) }
|
|
6088
7081
|
);
|
|
6089
|
-
if (!result)
|
|
7082
|
+
if (!result)
|
|
7083
|
+
throw markEmptyResponse(new PlansApiError(0, null, "Publish returned no body"));
|
|
6090
7084
|
versions.value = versions.value.map((v) => v.id === versionId ? result.planVersion : v);
|
|
6091
7085
|
return result;
|
|
6092
7086
|
}
|
|
@@ -6099,7 +7093,8 @@ function usePlanVersions(options) {
|
|
|
6099
7093
|
`${versionUrlBase}/${versionId}/terminate`,
|
|
6100
7094
|
{ method: "POST", body: JSON.stringify({ endsAt }) }
|
|
6101
7095
|
);
|
|
6102
|
-
if (!updated)
|
|
7096
|
+
if (!updated)
|
|
7097
|
+
throw markEmptyResponse(new PlansApiError(0, null, "Terminate returned no body"));
|
|
6103
7098
|
versions.value = versions.value.map((v) => v.id === versionId ? updated : v);
|
|
6104
7099
|
return updated;
|
|
6105
7100
|
}
|
|
@@ -6120,7 +7115,7 @@ function usePlanVersions(options) {
|
|
|
6120
7115
|
}
|
|
6121
7116
|
|
|
6122
7117
|
// src/vue/use-live-plan-versions.ts
|
|
6123
|
-
var
|
|
7118
|
+
var import_vue30 = require("vue");
|
|
6124
7119
|
function useLivePlanVersions(options) {
|
|
6125
7120
|
if (!options?.adminEndpoint) {
|
|
6126
7121
|
throw new Error("useLivePlanVersions: `adminEndpoint` is required.");
|
|
@@ -6129,9 +7124,9 @@ function useLivePlanVersions(options) {
|
|
|
6129
7124
|
throw new Error("useLivePlanVersions: `plans` is required.");
|
|
6130
7125
|
}
|
|
6131
7126
|
const http = options.http ?? defaultHttpClient();
|
|
6132
|
-
const livePlanVersions = (0,
|
|
6133
|
-
const loading = (0,
|
|
6134
|
-
const error = (0,
|
|
7127
|
+
const livePlanVersions = (0, import_vue30.ref)({});
|
|
7128
|
+
const loading = (0, import_vue30.ref)(false);
|
|
7129
|
+
const error = (0, import_vue30.ref)(null);
|
|
6135
7130
|
function authHeaders() {
|
|
6136
7131
|
const token = options.getAuthToken?.();
|
|
6137
7132
|
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
@@ -6173,7 +7168,7 @@ function useLivePlanVersions(options) {
|
|
|
6173
7168
|
loading.value = false;
|
|
6174
7169
|
}
|
|
6175
7170
|
}
|
|
6176
|
-
(0,
|
|
7171
|
+
(0, import_vue30.watch)(
|
|
6177
7172
|
() => options.plans.value.map((p) => p.id).join(","),
|
|
6178
7173
|
() => {
|
|
6179
7174
|
if (options.plans.value.length > 0) void refresh();
|
|
@@ -6195,12 +7190,12 @@ function findLatestLive(versions) {
|
|
|
6195
7190
|
}
|
|
6196
7191
|
|
|
6197
7192
|
// src/vue/use-manifest.ts
|
|
6198
|
-
var
|
|
7193
|
+
var import_vue31 = require("vue");
|
|
6199
7194
|
function useManifest(options) {
|
|
6200
7195
|
const loader = new ManifestLoader(options);
|
|
6201
|
-
const manifest = (0,
|
|
6202
|
-
const loading = (0,
|
|
6203
|
-
const error = (0,
|
|
7196
|
+
const manifest = (0, import_vue31.ref)(null);
|
|
7197
|
+
const loading = (0, import_vue31.ref)(false);
|
|
7198
|
+
const error = (0, import_vue31.ref)(null);
|
|
6204
7199
|
async function load() {
|
|
6205
7200
|
loading.value = true;
|
|
6206
7201
|
error.value = null;
|
|
@@ -6226,24 +7221,24 @@ function useManifest(options) {
|
|
|
6226
7221
|
}
|
|
6227
7222
|
|
|
6228
7223
|
// src/vue/use-nav.ts
|
|
6229
|
-
var
|
|
7224
|
+
var import_vue32 = require("vue");
|
|
6230
7225
|
function useNav(manifest, options = {}) {
|
|
6231
7226
|
const { locale } = useSuperAdminI18n();
|
|
6232
|
-
const activeLocale = (0,
|
|
6233
|
-
const routes = (0,
|
|
7227
|
+
const activeLocale = (0, import_vue32.computed)(() => options.locale ?? locale.value);
|
|
7228
|
+
const routes = (0, import_vue32.computed)(() => {
|
|
6234
7229
|
if (!manifest.value) return [];
|
|
6235
7230
|
return buildRoutes(manifest.value, { ...options, locale: activeLocale.value });
|
|
6236
7231
|
});
|
|
6237
|
-
const sidebar = (0,
|
|
7232
|
+
const sidebar = (0, import_vue32.computed)(
|
|
6238
7233
|
() => buildSidebar(routes.value, defaultSectionOrder(activeLocale.value))
|
|
6239
7234
|
);
|
|
6240
7235
|
return { routes, sidebar };
|
|
6241
7236
|
}
|
|
6242
7237
|
|
|
6243
7238
|
// src/vue/use-actions.ts
|
|
6244
|
-
var
|
|
7239
|
+
var import_vue33 = require("vue");
|
|
6245
7240
|
function useActions(manifest, actions) {
|
|
6246
|
-
const registry = (0,
|
|
7241
|
+
const registry = (0, import_vue33.computed)(() => {
|
|
6247
7242
|
if (!manifest.value) return null;
|
|
6248
7243
|
return new ActionRegistry(manifest.value, actions);
|
|
6249
7244
|
});
|
|
@@ -6251,18 +7246,18 @@ function useActions(manifest, actions) {
|
|
|
6251
7246
|
}
|
|
6252
7247
|
|
|
6253
7248
|
// src/vue/use-tenant-action-flow.ts
|
|
6254
|
-
var
|
|
7249
|
+
var import_vue34 = require("vue");
|
|
6255
7250
|
function useTenantActionFlow(manifest, providers = {}) {
|
|
6256
7251
|
const handlers = useSuperAdminActions();
|
|
6257
7252
|
const msg = useSaMessages("tenants");
|
|
6258
|
-
const registry = (0,
|
|
7253
|
+
const registry = (0, import_vue34.computed)(() => {
|
|
6259
7254
|
if (!manifest.value) return null;
|
|
6260
7255
|
return new ActionRegistry(manifest.value, handlers);
|
|
6261
7256
|
});
|
|
6262
|
-
const orphanedDefs = (0,
|
|
7257
|
+
const orphanedDefs = (0, import_vue34.computed)(
|
|
6263
7258
|
() => registry.value ? registry.value.listOrphanedDefs() : []
|
|
6264
7259
|
);
|
|
6265
|
-
const availableActions = (0,
|
|
7260
|
+
const availableActions = (0, import_vue34.computed)(() => {
|
|
6266
7261
|
const m = manifest.value;
|
|
6267
7262
|
const reg = registry.value;
|
|
6268
7263
|
if (!m || !reg) return [];
|
|
@@ -6335,7 +7330,7 @@ async function runFlow(def, row, registry, providers, successTemplate) {
|
|
|
6335
7330
|
}
|
|
6336
7331
|
|
|
6337
7332
|
// src/vue/use-platform-tenant-actions.ts
|
|
6338
|
-
var
|
|
7333
|
+
var import_vue35 = require("vue");
|
|
6339
7334
|
var DEFAULT_VISIBLE_FOR_ROW = (def, row) => {
|
|
6340
7335
|
if (def.actionKey === "tenants.suspend") return row.isActive;
|
|
6341
7336
|
if (def.actionKey === "tenants.reactivate") return !row.isActive;
|
|
@@ -6371,7 +7366,7 @@ function usePlatformTenantActions(options) {
|
|
|
6371
7366
|
const iconForActionKey = options.iconForActionKey ?? DEFAULT_ICON_FOR_ACTION_KEY;
|
|
6372
7367
|
const toneForActionKey = options.toneForActionKey ?? DEFAULT_TONE_FOR_ACTION_KEY;
|
|
6373
7368
|
const visibleForRow = options.visibleForRow ?? DEFAULT_VISIBLE_FOR_ROW;
|
|
6374
|
-
const mfa = (0,
|
|
7369
|
+
const mfa = (0, import_vue35.ref)({ show: false, description: "", error: "" });
|
|
6375
7370
|
let pendingMfaResolve = null;
|
|
6376
7371
|
function showMfaDialog(def, ctx) {
|
|
6377
7372
|
return new Promise((resolve) => {
|
|
@@ -6396,7 +7391,7 @@ function usePlatformTenantActions(options) {
|
|
|
6396
7391
|
pendingMfaResolve(null);
|
|
6397
7392
|
}
|
|
6398
7393
|
}
|
|
6399
|
-
const confirmDialog = (0,
|
|
7394
|
+
const confirmDialog = (0, import_vue35.ref)({
|
|
6400
7395
|
show: false,
|
|
6401
7396
|
def: null,
|
|
6402
7397
|
row: null
|
|
@@ -6438,7 +7433,7 @@ function usePlatformTenantActions(options) {
|
|
|
6438
7433
|
},
|
|
6439
7434
|
visibleForRow
|
|
6440
7435
|
});
|
|
6441
|
-
const manifestActions = (0,
|
|
7436
|
+
const manifestActions = (0, import_vue35.computed)(
|
|
6442
7437
|
() => flow.availableActions.value.map((def) => ({
|
|
6443
7438
|
id: def.id,
|
|
6444
7439
|
label: def.label,
|
|
@@ -6452,7 +7447,7 @@ function usePlatformTenantActions(options) {
|
|
|
6452
7447
|
}
|
|
6453
7448
|
}))
|
|
6454
7449
|
);
|
|
6455
|
-
const realOrphans = (0,
|
|
7450
|
+
const realOrphans = (0, import_vue35.computed)(() => {
|
|
6456
7451
|
const caps = options.manifest.value?.capabilities ?? {};
|
|
6457
7452
|
const defs = options.manifest.value?.tenants?.actions ?? [];
|
|
6458
7453
|
const defByActionKey = new Map(defs.map((d) => [d.actionKey, d]));
|
|
@@ -6477,12 +7472,12 @@ function usePlatformTenantActions(options) {
|
|
|
6477
7472
|
}
|
|
6478
7473
|
|
|
6479
7474
|
// src/vue/use-batch-columns.ts
|
|
6480
|
-
var
|
|
7475
|
+
var import_vue36 = require("vue");
|
|
6481
7476
|
function useBatchColumns(manifest, tenantIds, options = {}) {
|
|
6482
7477
|
const fetcher = new BatchColumnFetcher(options);
|
|
6483
|
-
const data = (0,
|
|
6484
|
-
const loading = (0,
|
|
6485
|
-
const error = (0,
|
|
7478
|
+
const data = (0, import_vue36.ref)({});
|
|
7479
|
+
const loading = (0, import_vue36.ref)(false);
|
|
7480
|
+
const error = (0, import_vue36.ref)(null);
|
|
6486
7481
|
async function load() {
|
|
6487
7482
|
if (!manifest.value || tenantIds.value.length === 0) {
|
|
6488
7483
|
data.value = {};
|
|
@@ -6499,22 +7494,23 @@ function useBatchColumns(manifest, tenantIds, options = {}) {
|
|
|
6499
7494
|
loading.value = false;
|
|
6500
7495
|
}
|
|
6501
7496
|
}
|
|
6502
|
-
(0,
|
|
7497
|
+
(0, import_vue36.watch)([manifest, tenantIds], () => {
|
|
6503
7498
|
void load();
|
|
6504
7499
|
});
|
|
6505
7500
|
return { data, loading, error, reload: load };
|
|
6506
7501
|
}
|
|
6507
7502
|
|
|
6508
7503
|
// src/vue/platform-loaders.ts
|
|
6509
|
-
function
|
|
7504
|
+
function resolveSuperAdminEndpoints(endpoints) {
|
|
6510
7505
|
return {
|
|
6511
7506
|
apiBase: endpoints.apiBase,
|
|
6512
7507
|
publicBootEndpoint: endpoints.publicBootEndpoint ?? `${endpoints.apiBase}/boot`,
|
|
6513
|
-
manifestEndpoint: endpoints.manifestEndpoint ?? `${endpoints.apiBase}/manifest
|
|
7508
|
+
manifestEndpoint: endpoints.manifestEndpoint ?? `${endpoints.apiBase}/manifest`,
|
|
7509
|
+
projectKey: endpoints.projectKey ?? ""
|
|
6514
7510
|
};
|
|
6515
7511
|
}
|
|
6516
7512
|
function createPlatformLoaders(options) {
|
|
6517
|
-
const resolved =
|
|
7513
|
+
const resolved = resolveSuperAdminEndpoints(options.endpoints);
|
|
6518
7514
|
const bootLoader = new BootLoader({
|
|
6519
7515
|
endpoint: resolved.publicBootEndpoint,
|
|
6520
7516
|
http: options.http
|
|
@@ -6531,13 +7527,13 @@ function createPlatformLoaders(options) {
|
|
|
6531
7527
|
|
|
6532
7528
|
// src/vue/manifest-store-factory.ts
|
|
6533
7529
|
var import_pinia = require("pinia");
|
|
6534
|
-
var
|
|
7530
|
+
var import_vue37 = require("vue");
|
|
6535
7531
|
function createManifestStore(options) {
|
|
6536
7532
|
return (0, import_pinia.defineStore)(options.id ?? "admin-manifest", () => {
|
|
6537
|
-
const manifest = (0,
|
|
6538
|
-
const loading = (0,
|
|
6539
|
-
const error = (0,
|
|
6540
|
-
const loaded = (0,
|
|
7533
|
+
const manifest = (0, import_vue37.ref)(null);
|
|
7534
|
+
const loading = (0, import_vue37.ref)(false);
|
|
7535
|
+
const error = (0, import_vue37.ref)(null);
|
|
7536
|
+
const loaded = (0, import_vue37.ref)(false);
|
|
6541
7537
|
let inflight = null;
|
|
6542
7538
|
async function load() {
|
|
6543
7539
|
loading.value = true;
|
|
@@ -6970,6 +7966,7 @@ function planChangeWizardI18n(i18n) {
|
|
|
6970
7966
|
ALL_ROWS,
|
|
6971
7967
|
ActionDefNotInManifestError,
|
|
6972
7968
|
ActionRegistry,
|
|
7969
|
+
AdminError,
|
|
6973
7970
|
BatchColumnDriftError,
|
|
6974
7971
|
BatchColumnFetcher,
|
|
6975
7972
|
BootLoadError,
|
|
@@ -6990,6 +7987,10 @@ function planChangeWizardI18n(i18n) {
|
|
|
6990
7987
|
IDENTITY_ACCENT_VALUES,
|
|
6991
7988
|
IDENTITY_NEUTRAL,
|
|
6992
7989
|
IDENTITY_NEUTRAL_VALUE,
|
|
7990
|
+
LIST_FIRST_PAGE,
|
|
7991
|
+
LIST_PAGE_SIZE_DEFAULT,
|
|
7992
|
+
LIST_PAGE_SIZE_MAX,
|
|
7993
|
+
LIST_PAGINATION_PARAMS,
|
|
6993
7994
|
ManifestLoadError,
|
|
6994
7995
|
ManifestLoader,
|
|
6995
7996
|
MarketingProjectionsApiError,
|
|
@@ -6999,15 +8000,18 @@ function planChangeWizardI18n(i18n) {
|
|
|
6999
8000
|
PlansApiError,
|
|
7000
8001
|
ProjectPageHost,
|
|
7001
8002
|
PromotionsApiError,
|
|
8003
|
+
SA_COLOR_SCHEMES,
|
|
7002
8004
|
SA_INTL_LOCALES,
|
|
7003
8005
|
SA_LOCALES,
|
|
7004
8006
|
SA_LOCALE_LABELS,
|
|
7005
8007
|
SA_LOCALE_STORAGE_KEY,
|
|
7006
8008
|
SA_MESSAGES,
|
|
7007
8009
|
SA_THEME_KEY,
|
|
8010
|
+
SA_THEME_STORAGE_KEY,
|
|
7008
8011
|
SERVER_PAGE_SIZE_OPTIONS,
|
|
7009
8012
|
SUPER_ADMIN_ACTIONS_KEY,
|
|
7010
8013
|
SUPER_ADMIN_BRAND_KEY,
|
|
8014
|
+
SUPER_ADMIN_CONFIRM_KEY,
|
|
7011
8015
|
SUPER_ADMIN_ENDPOINTS_KEY,
|
|
7012
8016
|
SUPER_ADMIN_EXTENSIONS_KEY,
|
|
7013
8017
|
SUPER_ADMIN_HTTP_KEY,
|
|
@@ -7016,19 +8020,28 @@ function planChangeWizardI18n(i18n) {
|
|
|
7016
8020
|
SUPER_ADMIN_MANIFEST_CLEAR_CACHE_KEY,
|
|
7017
8021
|
SUPER_ADMIN_MANIFEST_KEY,
|
|
7018
8022
|
SUPER_ADMIN_NOTIFY_KEY,
|
|
8023
|
+
SUPER_ADMIN_RESOURCES_KEY,
|
|
7019
8024
|
TenantSubscriptionBundlesApiError,
|
|
8025
|
+
adminErrorMessage,
|
|
8026
|
+
auditResource,
|
|
8027
|
+
bindResource,
|
|
7020
8028
|
buildFeatureRegistry,
|
|
7021
8029
|
buildFeatureRouterGuard,
|
|
7022
8030
|
buildNavigationGuard,
|
|
7023
8031
|
buildQuotaRegistry,
|
|
7024
8032
|
buildRoutes,
|
|
7025
8033
|
buildSidebar,
|
|
8034
|
+
clampListPage,
|
|
8035
|
+
clampListPageSize,
|
|
7026
8036
|
countPlans,
|
|
7027
8037
|
createAdminResourceClient,
|
|
7028
8038
|
createAdminRoutes,
|
|
8039
|
+
createAxiosHttpClient,
|
|
8040
|
+
createFetchHttpClient,
|
|
7029
8041
|
createManifestStore,
|
|
7030
8042
|
createPlatformLoaders,
|
|
7031
8043
|
createProjectPageHostRoute,
|
|
8044
|
+
createResourceRegistry,
|
|
7032
8045
|
createSaCatalog,
|
|
7033
8046
|
createSaTheme,
|
|
7034
8047
|
createSuperAdminI18n,
|
|
@@ -7038,30 +8051,57 @@ function planChangeWizardI18n(i18n) {
|
|
|
7038
8051
|
defaultSectionOrder,
|
|
7039
8052
|
defaultTenantPlanSectionI18n,
|
|
7040
8053
|
defaultToneForActionKey,
|
|
8054
|
+
defineListOp,
|
|
7041
8055
|
defineMessages,
|
|
8056
|
+
defineResource,
|
|
8057
|
+
filterQueryString,
|
|
7042
8058
|
formatCurrency,
|
|
7043
8059
|
formatMessage,
|
|
7044
8060
|
getJson,
|
|
7045
8061
|
identityAccentAt,
|
|
7046
8062
|
identityAccentFor,
|
|
7047
8063
|
identityChipStyle,
|
|
8064
|
+
isAdminError,
|
|
8065
|
+
isAxiosNoResponseError,
|
|
7048
8066
|
isCurrentlyValid,
|
|
8067
|
+
isEmptyResponse,
|
|
7049
8068
|
isExpired,
|
|
7050
8069
|
isFutureScheduled,
|
|
8070
|
+
isPlatformError,
|
|
7051
8071
|
isProductionBoot,
|
|
7052
8072
|
isSaBuiltinLocale,
|
|
8073
|
+
isSentInQuery,
|
|
8074
|
+
isTransportFailure,
|
|
8075
|
+
listUrl,
|
|
8076
|
+
markEmptyResponse,
|
|
8077
|
+
markPlatformError,
|
|
8078
|
+
markTransportFailure,
|
|
7053
8079
|
mergeMessages,
|
|
7054
8080
|
planChangeWizardI18n,
|
|
8081
|
+
planVersionsResource,
|
|
8082
|
+
plansResource,
|
|
8083
|
+
platformResources,
|
|
7055
8084
|
postJson,
|
|
7056
8085
|
provideEntitlement,
|
|
8086
|
+
readErrorCode,
|
|
8087
|
+
readErrorDetail,
|
|
8088
|
+
readListPage,
|
|
8089
|
+
requestJson,
|
|
8090
|
+
requestJsonBody,
|
|
8091
|
+
requireServerAnswer,
|
|
7057
8092
|
resolveExtension,
|
|
7058
8093
|
resolveLoginBranding,
|
|
7059
8094
|
resolveMessages,
|
|
7060
8095
|
resolvePlans,
|
|
8096
|
+
resolveSuperAdminEndpoints,
|
|
8097
|
+
tenantsResource,
|
|
8098
|
+
toAdminError,
|
|
7061
8099
|
todayIsoDate,
|
|
7062
8100
|
trimTrailingSlashes,
|
|
7063
8101
|
useActions,
|
|
7064
8102
|
useApiList,
|
|
8103
|
+
useAsyncAction,
|
|
8104
|
+
useAsyncData,
|
|
7065
8105
|
useAuditEntries,
|
|
7066
8106
|
useBatchColumns,
|
|
7067
8107
|
useBulkPublish,
|
|
@@ -7083,6 +8123,8 @@ function planChangeWizardI18n(i18n) {
|
|
|
7083
8123
|
usePlatformTenantActions,
|
|
7084
8124
|
usePromotions,
|
|
7085
8125
|
usePublicBoot,
|
|
8126
|
+
useResource,
|
|
8127
|
+
useResourceList,
|
|
7086
8128
|
useSaMessages,
|
|
7087
8129
|
useSaTheme,
|
|
7088
8130
|
useSubscriptionDraft,
|