@saasicat/ui-vue 0.26.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/LICENSE +90 -201
  2. package/README.md +23 -0
  3. package/dist/{catalog-Dch1Ryw0.d.cts → catalog-Dv2twuNi.d.cts} +205 -4
  4. package/dist/{catalog-Dch1Ryw0.d.ts → catalog-Dv2twuNi.d.ts} +205 -4
  5. package/dist/{chunk-BPF2BMCQ.js → chunk-4IZWT4UY.js} +414 -2
  6. package/dist/{chunk-O3J3ITF2.js → chunk-LK6YJDXC.js} +1 -1
  7. package/dist/{chunk-NEXTZZRQ.js → chunk-NRVF3JJJ.js} +2 -2
  8. package/dist/client/index.cjs +426 -5
  9. package/dist/client/index.d.cts +70 -4
  10. package/dist/client/index.d.ts +70 -4
  11. package/dist/client/index.js +22 -4
  12. package/dist/index.cjs +429 -8
  13. package/dist/index.d.cts +5 -5
  14. package/dist/index.d.ts +5 -5
  15. package/dist/index.js +23 -5
  16. package/dist/quasar/index.cjs +411 -2
  17. package/dist/quasar/index.d.cts +2 -2
  18. package/dist/quasar/index.d.ts +2 -2
  19. package/dist/quasar/index.js +2 -2
  20. package/dist/{resource-registry-D7Xjs-5f.d.cts → resource-registry-BxFpnzHY.d.cts} +1 -1
  21. package/dist/{resource-registry-TaR7rtq0.d.ts → resource-registry-Cn1Gy2LE.d.ts} +1 -1
  22. package/package.json +5 -4
  23. package/src/client/admin-error.ts +18 -1
  24. package/src/client/attach-cause.ts +21 -0
  25. package/src/client/nav-builder.ts +3 -1
  26. package/src/client/resources/bundles.resource.ts +145 -0
  27. package/src/client/resources/catalog.resource.ts +160 -0
  28. package/src/client/resources/discovery.resource.ts +108 -0
  29. package/src/client/resources/index.ts +52 -14
  30. package/src/client/resources/marketing.resource.ts +121 -0
  31. package/src/client/resources/promo-codes.resource.ts +63 -0
  32. package/src/client/resources/promotions.resource.ts +44 -0
  33. package/src/client/resources/subscriptions.resource.ts +19 -0
  34. package/src/client/resources/tenants.resource.ts +35 -9
  35. package/src/client/resources/users.resource.ts +31 -0
  36. package/src/pages-standard/SuperAdminSetupWizard.vue +7 -4
package/dist/index.js CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  resolveLoginBranding,
28
28
  resolvePlans,
29
29
  todayIsoDate
30
- } from "./chunk-NEXTZZRQ.js";
30
+ } from "./chunk-NRVF3JJJ.js";
31
31
  import {
32
32
  SA_COLOR_SCHEMES,
33
33
  SA_LOCALE_STORAGE_KEY,
@@ -55,7 +55,7 @@ import {
55
55
  useSaMessages,
56
56
  useSaTheme,
57
57
  useSuperAdminI18n
58
- } from "./chunk-O3J3ITF2.js";
58
+ } from "./chunk-LK6YJDXC.js";
59
59
  import {
60
60
  AdminError,
61
61
  BootLoadError,
@@ -75,6 +75,9 @@ import {
75
75
  adminErrorMessage,
76
76
  auditResource,
77
77
  bindResource,
78
+ bundleVersionsResource,
79
+ bundlesResource,
80
+ catalogResource,
78
81
  clampListPage,
79
82
  clampListPageSize,
80
83
  createFetchHttpClient,
@@ -84,6 +87,7 @@ import {
84
87
  defineListOp,
85
88
  defineMessages,
86
89
  defineResource,
90
+ discoveryResource,
87
91
  filterQueryString,
88
92
  formatCurrency,
89
93
  formatMessage,
@@ -99,11 +103,14 @@ import {
99
103
  markEmptyResponse,
100
104
  markPlatformError,
101
105
  markTransportFailure,
106
+ marketingResource,
102
107
  mergeMessages,
103
108
  planVersionsResource,
104
109
  plansResource,
105
110
  platformResources,
106
111
  postJson,
112
+ promoCodesResource,
113
+ promotionsResource,
107
114
  readErrorCode,
108
115
  readErrorDetail,
109
116
  readListPage,
@@ -111,10 +118,12 @@ import {
111
118
  requestJsonBody,
112
119
  requireServerAnswer,
113
120
  resolveMessages,
121
+ subscriptionsResource,
114
122
  tenantsResource,
115
123
  toAdminError,
116
- trimTrailingSlashes
117
- } from "./chunk-BPF2BMCQ.js";
124
+ trimTrailingSlashes,
125
+ usersResource
126
+ } from "./chunk-4IZWT4UY.js";
118
127
 
119
128
  // src/vue/use-super-admin-context.ts
120
129
  import { inject } from "vue";
@@ -3232,6 +3241,9 @@ export {
3232
3241
  buildQuotaRegistry,
3233
3242
  buildRoutes,
3234
3243
  buildSidebar,
3244
+ bundleVersionsResource,
3245
+ bundlesResource,
3246
+ catalogResource,
3235
3247
  clampListPage,
3236
3248
  clampListPageSize,
3237
3249
  countPlans,
@@ -3255,6 +3267,7 @@ export {
3255
3267
  defineListOp,
3256
3268
  defineMessages,
3257
3269
  defineResource,
3270
+ discoveryResource,
3258
3271
  filterQueryString,
3259
3272
  formatCurrency,
3260
3273
  formatMessage,
@@ -3277,12 +3290,15 @@ export {
3277
3290
  markEmptyResponse,
3278
3291
  markPlatformError,
3279
3292
  markTransportFailure,
3293
+ marketingResource,
3280
3294
  mergeMessages,
3281
3295
  planChangeWizardI18n,
3282
3296
  planVersionsResource,
3283
3297
  plansResource,
3284
3298
  platformResources,
3285
3299
  postJson,
3300
+ promoCodesResource,
3301
+ promotionsResource,
3286
3302
  provideEntitlement,
3287
3303
  readErrorCode,
3288
3304
  readErrorDetail,
@@ -3295,6 +3311,7 @@ export {
3295
3311
  resolveMessages,
3296
3312
  resolvePlans,
3297
3313
  resolveSuperAdminEndpoints,
3314
+ subscriptionsResource,
3298
3315
  tenantsResource,
3299
3316
  toAdminError,
3300
3317
  todayIsoDate,
@@ -3342,5 +3359,6 @@ export {
3342
3359
  useTenantBillingCatalog,
3343
3360
  useTenantManifest,
3344
3361
  useTenantSubscriptionBundles,
3345
- useTenants
3362
+ useTenants,
3363
+ usersResource
3346
3364
  };
@@ -38,6 +38,16 @@ var import_quasar4 = require("quasar");
38
38
  var import_pinia = require("pinia");
39
39
  var import_vue_router = require("vue-router");
40
40
 
41
+ // src/client/attach-cause.ts
42
+ function attachCause(error, cause) {
43
+ Object.defineProperty(error, "cause", {
44
+ value: cause,
45
+ configurable: true,
46
+ writable: true
47
+ });
48
+ return error;
49
+ }
50
+
41
51
  // src/client/i18n/format.ts
42
52
  var import_types = require("@saasicat/types");
43
53
 
@@ -86,7 +96,8 @@ var AdminError = class extends Error {
86
96
  /** The request never reached the server. See `AdminErrorInit`. */
87
97
  transportFailure;
88
98
  constructor(init = {}) {
89
- super(init.message ?? describe(init), { cause: init.cause });
99
+ super(init.message ?? describe(init));
100
+ if (init.cause !== void 0) attachCause(this, init.cause);
90
101
  this.name = "AdminError";
91
102
  this.status = init.status ?? 0;
92
103
  this.code = init.code;
@@ -375,6 +386,12 @@ function appendFilter(params, filter) {
375
386
  params.set(key, String(value));
376
387
  }
377
388
  }
389
+ function filterQueryString(filter) {
390
+ const params = new URLSearchParams();
391
+ appendFilter(params, filter);
392
+ const query = params.toString();
393
+ return query ? `?${query}` : "";
394
+ }
378
395
  function listUrl(endpoint, query = {}) {
379
396
  const params = new URLSearchParams();
380
397
  params.set("page", String(query.page ?? LIST_FIRST_PAGE));
@@ -487,9 +504,392 @@ var planVersionsResource = defineResource("planVersions", {
487
504
  )
488
505
  });
489
506
 
507
+ // src/client/resources/bundles.resource.ts
508
+ function bundlesUrl(ctx) {
509
+ return `${ctx.apiBase}/catalog/bundles`;
510
+ }
511
+ function bundleVersionsUrl(ctx) {
512
+ return `${ctx.apiBase}/catalog/bundle-versions`;
513
+ }
514
+ function scoped2(url, ctx) {
515
+ return `${url}?projectKey=${encodeURIComponent(ctx.projectKey)}`;
516
+ }
517
+ var bundlesResource = defineResource(
518
+ "bundles",
519
+ {
520
+ list: async (http, ctx) => await requestJson(http, scoped2(bundlesUrl(ctx), ctx)) ?? [],
521
+ create: (http, ctx, data) => requestJsonBody(http, bundlesUrl(ctx), "Create returned no body", {
522
+ method: "POST",
523
+ body: { ...data, projectKey: ctx.projectKey }
524
+ }),
525
+ update: (http, ctx, bundleId, data) => requestJsonBody(
526
+ http,
527
+ `${bundlesUrl(ctx)}/${bundleId}`,
528
+ "Update returned no body",
529
+ { method: "PATCH", body: data }
530
+ ),
531
+ /** Marks the bundle deleted. */
532
+ softDelete: async (http, ctx, bundleId) => {
533
+ await requestJson(http, `${bundlesUrl(ctx)}/${bundleId}`, { method: "DELETE" });
534
+ }
535
+ },
536
+ { projectScoped: true }
537
+ );
538
+ var bundleVersionsResource = defineResource("bundleVersions", {
539
+ listForBundle: async (http, ctx, bundleId) => await requestJson(http, `${bundlesUrl(ctx)}/${bundleId}/versions`) ?? [],
540
+ createDraft: (http, ctx, bundleId, data) => requestJsonBody(
541
+ http,
542
+ `${bundlesUrl(ctx)}/${bundleId}/versions`,
543
+ "CreateDraft returned no body",
544
+ { method: "POST", body: data }
545
+ ),
546
+ updateDraft: (http, ctx, versionId, data) => requestJsonBody(
547
+ http,
548
+ `${bundleVersionsUrl(ctx)}/${versionId}`,
549
+ "UpdateDraft returned no body",
550
+ { method: "PATCH", body: data }
551
+ ),
552
+ publish: (http, ctx, versionId, options = {}) => requestJsonBody(
553
+ http,
554
+ `${bundleVersionsUrl(ctx)}/${versionId}/publish`,
555
+ "Publish returned no body",
556
+ { method: "POST", body: options }
557
+ ),
558
+ /**
559
+ * Discards a draft. A published version cannot be discarded — the API
560
+ * answers 422 `BUNDLE_VERSION_ALREADY_PUBLISHED`.
561
+ */
562
+ discardDraft: async (http, ctx, versionId) => {
563
+ await requestJson(http, `${bundleVersionsUrl(ctx)}/${versionId}`, { method: "DELETE" });
564
+ }
565
+ });
566
+
567
+ // src/client/resources/catalog.resource.ts
568
+ function catalogUrl(ctx) {
569
+ return `${ctx.apiBase}/catalog`;
570
+ }
571
+ function scoped3(url, ctx) {
572
+ return `${url}?projectKey=${encodeURIComponent(ctx.projectKey)}`;
573
+ }
574
+ var catalogResource = defineResource(
575
+ "catalog",
576
+ {
577
+ capabilities: async (http, ctx) => await requestJson(
578
+ http,
579
+ scoped3(`${catalogUrl(ctx)}/capabilities`, ctx)
580
+ ) ?? [],
581
+ features: async (http, ctx) => await requestJson(
582
+ http,
583
+ scoped3(`${catalogUrl(ctx)}/features`, ctx)
584
+ ) ?? [],
585
+ quotas: async (http, ctx) => await requestJson(
586
+ http,
587
+ scoped3(`${catalogUrl(ctx)}/quotas`, ctx)
588
+ ) ?? [],
589
+ reviewFeature: (http, ctx, featureKey, data) => requestJsonBody(
590
+ http,
591
+ scoped3(`${catalogUrl(ctx)}/features/${encodeURIComponent(featureKey)}/review`, ctx),
592
+ "Review returned no body",
593
+ { method: "PATCH", body: data }
594
+ ),
595
+ reviewQuota: (http, ctx, quotaKey, data) => requestJsonBody(
596
+ http,
597
+ scoped3(`${catalogUrl(ctx)}/quotas/${encodeURIComponent(quotaKey)}/review`, ctx),
598
+ "Review returned no body",
599
+ { method: "PATCH", body: data }
600
+ ),
601
+ /** The translations are wrapped in `{ i18n }` here, not by the caller. */
602
+ setFeatureI18n: (http, ctx, featureKey, i18n) => requestJsonBody(
603
+ http,
604
+ scoped3(`${catalogUrl(ctx)}/features/${encodeURIComponent(featureKey)}/i18n`, ctx),
605
+ "i18n returned no body",
606
+ { method: "PATCH", body: { i18n } }
607
+ ),
608
+ setQuotaI18n: (http, ctx, quotaKey, i18n) => requestJsonBody(
609
+ http,
610
+ scoped3(`${catalogUrl(ctx)}/quotas/${encodeURIComponent(quotaKey)}/i18n`, ctx),
611
+ "i18n returned no body",
612
+ { method: "PATCH", body: { i18n } }
613
+ ),
614
+ setFeatureBase: (http, ctx, featureKey, data) => requestJsonBody(
615
+ http,
616
+ scoped3(`${catalogUrl(ctx)}/features/${encodeURIComponent(featureKey)}`, ctx),
617
+ "Base returned no body",
618
+ { method: "PATCH", body: data }
619
+ ),
620
+ setQuotaBase: (http, ctx, quotaKey, data) => requestJsonBody(
621
+ http,
622
+ scoped3(`${catalogUrl(ctx)}/quotas/${encodeURIComponent(quotaKey)}`, ctx),
623
+ "Base returned no body",
624
+ { method: "PATCH", body: data }
625
+ ),
626
+ /**
627
+ * Writes a discovery snapshot into the catalogue.
628
+ *
629
+ * The request only. `useCatalogEntries.syncDiscovery` reloads the three
630
+ * lists afterwards, which is that composable keeping its refs true —
631
+ * not part of the call, and not something an override should inherit
632
+ * invisibly.
633
+ */
634
+ syncDiscovery: (http, ctx, snapshot) => requestJsonBody(
635
+ http,
636
+ `${catalogUrl(ctx)}/discovery/sync`,
637
+ "Sync returned no body",
638
+ { method: "POST", body: { snapshot } }
639
+ )
640
+ },
641
+ { projectScoped: true }
642
+ );
643
+
644
+ // src/client/resources/discovery.resource.ts
645
+ function discoveryUrl(ctx) {
646
+ return `${ctx.apiBase}/discovery`;
647
+ }
648
+ function fail(status, method, url, message) {
649
+ return new AdminError({ status, url, method, message });
650
+ }
651
+ var discoveryResource = defineResource("discovery", {
652
+ /**
653
+ * Reads the snapshot, revalidating against `etag` when one is passed.
654
+ *
655
+ * Pass `null` (or nothing) to force a full read — that is what
656
+ * `useDiscovery.reload()` does when the operator asks for fresh data.
657
+ */
658
+ read: async (http, ctx, etag = null) => {
659
+ const url = discoveryUrl(ctx);
660
+ const headers = {};
661
+ if (etag) headers["If-None-Match"] = etag;
662
+ const response = await http(url, { method: "GET", headers });
663
+ requireServerAnswer(
664
+ response.status,
665
+ "GET",
666
+ url,
667
+ (diagnostic) => fail(response.status, "GET", url, diagnostic)
668
+ );
669
+ if (response.status === 304) return { status: "unchanged" };
670
+ if (response.status !== 200) {
671
+ throw fail(
672
+ response.status,
673
+ "GET",
674
+ url,
675
+ `Discovery endpoint responded with HTTP ${response.status}`
676
+ );
677
+ }
678
+ return {
679
+ status: "loaded",
680
+ snapshot: await response.json(),
681
+ etag: response.headers.get("ETag")
682
+ };
683
+ },
684
+ /**
685
+ * Re-runs the scan and returns what it found.
686
+ *
687
+ * Unconditional by construction: a rescan asks the server to look again,
688
+ * so revalidating against a tag the previous scan produced would answer
689
+ * with the snapshot the rescan was meant to replace.
690
+ */
691
+ rescan: async (http, ctx) => {
692
+ const url = `${discoveryUrl(ctx)}/rescan`;
693
+ const response = await http(url, {
694
+ method: "POST",
695
+ headers: { "content-type": "application/json" }
696
+ });
697
+ requireServerAnswer(
698
+ response.status,
699
+ "POST",
700
+ url,
701
+ (diagnostic) => fail(response.status, "POST", url, diagnostic)
702
+ );
703
+ if (response.status !== 200 && response.status !== 201) {
704
+ throw fail(
705
+ response.status,
706
+ "POST",
707
+ url,
708
+ `Discovery rescan responded with HTTP ${response.status}`
709
+ );
710
+ }
711
+ return {
712
+ snapshot: await response.json(),
713
+ etag: response.headers.get("ETag")
714
+ };
715
+ }
716
+ });
717
+
718
+ // src/client/resources/marketing.resource.ts
719
+ function projectionsUrl(ctx) {
720
+ return `${ctx.apiBase}/catalog/marketing-projections`;
721
+ }
722
+ function settingsUrl(ctx) {
723
+ return `${ctx.apiBase}/catalog/marketing-settings`;
724
+ }
725
+ var marketingResource = defineResource(
726
+ "marketing",
727
+ {
728
+ listProjections: async (http, ctx, filter = {}) => {
729
+ const params = new URLSearchParams();
730
+ params.set("projectKey", ctx.projectKey);
731
+ if (filter.targetType) params.set("targetType", filter.targetType);
732
+ if (filter.targetVersionId) params.set("targetVersionId", filter.targetVersionId);
733
+ if (filter.locale) params.set("locale", filter.locale);
734
+ return await requestJson(
735
+ http,
736
+ `${projectionsUrl(ctx)}?${params.toString()}`
737
+ ) ?? [];
738
+ },
739
+ /**
740
+ * The request only. `useMarketingProjections.create` reloads the list
741
+ * afterwards because a unique-tuple insert can change what the filter
742
+ * matches — that is the composable keeping its refs true, not part of
743
+ * the call.
744
+ */
745
+ createProjection: (http, ctx, data) => requestJsonBody(
746
+ http,
747
+ projectionsUrl(ctx),
748
+ "Create returned no body",
749
+ { method: "POST", body: data }
750
+ ),
751
+ updateProjection: (http, ctx, id, data) => requestJsonBody(
752
+ http,
753
+ `${projectionsUrl(ctx)}/${id}`,
754
+ "Update returned no body",
755
+ { method: "PATCH", body: data }
756
+ ),
757
+ deleteProjection: async (http, ctx, id) => {
758
+ await requestJson(http, `${projectionsUrl(ctx)}/${id}`, { method: "DELETE" });
759
+ },
760
+ /**
761
+ * The activated locale subset, or `null` when none was ever stored.
762
+ *
763
+ * `null` means the full pool is active — the server's answer for "no
764
+ * row", and the state the page falls back to.
765
+ */
766
+ settings: (http, ctx) => requestJson(
767
+ http,
768
+ `${settingsUrl(ctx)}?projectKey=${encodeURIComponent(ctx.projectKey)}`
769
+ ),
770
+ /** `PUT`, not `PATCH`: the endpoint replaces the set it is given. */
771
+ saveSettings: async (http, ctx, activeLocales) => {
772
+ await requestJson(http, settingsUrl(ctx), {
773
+ method: "PUT",
774
+ body: { projectKey: ctx.projectKey, activeLocales }
775
+ });
776
+ }
777
+ },
778
+ { projectScoped: true }
779
+ );
780
+
781
+ // src/client/resources/promo-codes.resource.ts
782
+ function promoCodesUrl(ctx) {
783
+ return `${ctx.apiBase}/promo-codes`;
784
+ }
785
+ var promoCodesResource = defineResource("promoCodes", {
786
+ list: async (http, ctx, filter = {}) => await requestJson(
787
+ http,
788
+ `${promoCodesUrl(ctx)}${filterQueryString({
789
+ search: filter.search,
790
+ status: filter.status
791
+ })}`
792
+ ) ?? [],
793
+ /**
794
+ * Returns nothing, matching `createAdminResourceClient.createPromo`.
795
+ *
796
+ * The endpoint does answer with the created record, but the page it feeds
797
+ * reloads the list rather than reading it, and promising a body a caller
798
+ * would then have to trust is a wider contract than anything holds today.
799
+ */
800
+ create: async (http, ctx, payload) => {
801
+ await requestJson(http, promoCodesUrl(ctx), { method: "POST", body: payload });
802
+ },
803
+ update: async (http, ctx, id, payload) => {
804
+ await requestJson(http, `${promoCodesUrl(ctx)}/${encodeURIComponent(id)}`, {
805
+ method: "PATCH",
806
+ body: payload
807
+ });
808
+ },
809
+ remove: async (http, ctx, id) => {
810
+ await requestJson(http, `${promoCodesUrl(ctx)}/${encodeURIComponent(id)}`, {
811
+ method: "DELETE"
812
+ });
813
+ }
814
+ });
815
+
816
+ // src/client/resources/promotions.resource.ts
817
+ function promotionsUrl(ctx) {
818
+ return `${ctx.apiBase}/catalog/promotions`;
819
+ }
820
+ var promotionsResource = defineResource(
821
+ "promotions",
822
+ {
823
+ list: async (http, ctx) => await requestJson(
824
+ http,
825
+ `${promotionsUrl(ctx)}?projectKey=${encodeURIComponent(ctx.projectKey)}`
826
+ ) ?? [],
827
+ create: (http, ctx, data) => requestJsonBody(http, promotionsUrl(ctx), "Create returned no body", {
828
+ method: "POST",
829
+ body: data
830
+ }),
831
+ update: (http, ctx, id, data) => requestJsonBody(
832
+ http,
833
+ `${promotionsUrl(ctx)}/${id}`,
834
+ "Update returned no body",
835
+ { method: "PATCH", body: data }
836
+ ),
837
+ remove: async (http, ctx, id) => {
838
+ await requestJson(http, `${promotionsUrl(ctx)}/${id}`, { method: "DELETE" });
839
+ }
840
+ },
841
+ { projectScoped: true }
842
+ );
843
+
490
844
  // src/client/resources/tenants.resource.ts
845
+ function tenantsUrl(ctx) {
846
+ return `${ctx.apiBase}/tenants`;
847
+ }
848
+ function tenantUrl(ctx, slug) {
849
+ return `${tenantsUrl(ctx)}/${encodeURIComponent(slug)}`;
850
+ }
491
851
  var tenantsResource = defineResource("tenants", {
492
- list: defineListOp((ctx) => `${ctx.apiBase}/tenants`)
852
+ list: defineListOp((ctx) => tenantsUrl(ctx)),
853
+ detail: async (http, ctx, slug) => requestJson(http, tenantUrl(ctx, slug)),
854
+ /**
855
+ * Suspends a tenant. The reason is recorded on the audit trail, so it is a
856
+ * required argument rather than an option with a default.
857
+ */
858
+ suspend: async (http, ctx, slug, reason) => {
859
+ await requestJson(http, `${tenantUrl(ctx, slug)}/suspend`, {
860
+ method: "POST",
861
+ body: { reason }
862
+ });
863
+ },
864
+ reactivate: async (http, ctx, slug) => {
865
+ await requestJson(http, `${tenantUrl(ctx, slug)}/reactivate`, { method: "POST" });
866
+ }
867
+ });
868
+
869
+ // src/client/resources/users.resource.ts
870
+ function usersUrl(ctx) {
871
+ return `${ctx.apiBase}/users`;
872
+ }
873
+ var usersResource = defineResource("users", {
874
+ /**
875
+ * `q` searches name and mail, `tenant` narrows to one tenant's members.
876
+ *
877
+ * Both are dropped when empty rather than sent as `q=` — see
878
+ * `isSentInQuery`; an empty search that reached the server would filter
879
+ * every row away on an endpoint that treats the parameter as a substring.
880
+ */
881
+ list: async (http, ctx, filter = {}) => await requestJson(
882
+ http,
883
+ `${usersUrl(ctx)}${filterQueryString({ q: filter.q, tenant: filter.tenant })}`
884
+ ) ?? []
885
+ });
886
+
887
+ // src/client/resources/subscriptions.resource.ts
888
+ function subscriptionsUrl(ctx) {
889
+ return `${ctx.apiBase}/subscriptions`;
890
+ }
891
+ var subscriptionsResource = defineResource("subscriptions", {
892
+ list: async (http, ctx) => await requestJson(http, subscriptionsUrl(ctx)) ?? []
493
893
  });
494
894
 
495
895
  // src/client/resources/audit.resource.ts
@@ -501,7 +901,16 @@ var auditResource = defineResource("audit", {
501
901
  var platformResources = {
502
902
  plans: plansResource,
503
903
  planVersions: planVersionsResource,
904
+ bundles: bundlesResource,
905
+ bundleVersions: bundleVersionsResource,
906
+ catalog: catalogResource,
907
+ discovery: discoveryResource,
908
+ marketing: marketingResource,
909
+ promoCodes: promoCodesResource,
910
+ promotions: promotionsResource,
504
911
  tenants: tenantsResource,
912
+ users: usersResource,
913
+ subscriptions: subscriptionsResource,
505
914
  audit: auditResource
506
915
  };
507
916
 
@@ -2,8 +2,8 @@ import { Component, App, WatchStopHandle } from 'vue';
2
2
  import { QuasarPluginOptions, QDialogOptions } from 'quasar';
3
3
  import { Pinia } from 'pinia';
4
4
  import { RouteRecordRaw, RouterHistory, Router } from 'vue-router';
5
- import { p as platformResources, H as HttpClient } from '../catalog-Dch1Ryw0.cjs';
6
- import { S as SuperAdminGuardOptions, a as SuperAdminBrand, b as SuperAdminEndpoints, E as ExtensionsMap, A as ActionsMap, c as SuperAdminLoginAdapter, U as UiNotify, d as UiConfirm, R as ResourceOverrides, I as InstallPlugin, e as SuperAdminI18nOptions, f as SaThemeOptions, g as SuperAdminI18n, h as SaTheme, i as UiConfirmRequest } from '../resource-registry-D7Xjs-5f.cjs';
5
+ import { p as platformResources, H as HttpClient } from '../catalog-Dv2twuNi.cjs';
6
+ import { S as SuperAdminGuardOptions, a as SuperAdminBrand, b as SuperAdminEndpoints, E as ExtensionsMap, A as ActionsMap, c as SuperAdminLoginAdapter, U as UiNotify, d as UiConfirm, R as ResourceOverrides, I as InstallPlugin, e as SuperAdminI18nOptions, f as SaThemeOptions, g as SuperAdminI18n, h as SaTheme, i as UiConfirmRequest } from '../resource-registry-BxFpnzHY.cjs';
7
7
  import '@saasicat/types';
8
8
 
9
9
  interface CreateSuperAdminAppOptions extends SuperAdminGuardOptions {
@@ -2,8 +2,8 @@ import { Component, App, WatchStopHandle } from 'vue';
2
2
  import { QuasarPluginOptions, QDialogOptions } from 'quasar';
3
3
  import { Pinia } from 'pinia';
4
4
  import { RouteRecordRaw, RouterHistory, Router } from 'vue-router';
5
- import { p as platformResources, H as HttpClient } from '../catalog-Dch1Ryw0.js';
6
- import { S as SuperAdminGuardOptions, a as SuperAdminBrand, b as SuperAdminEndpoints, E as ExtensionsMap, A as ActionsMap, c as SuperAdminLoginAdapter, U as UiNotify, d as UiConfirm, R as ResourceOverrides, I as InstallPlugin, e as SuperAdminI18nOptions, f as SaThemeOptions, g as SuperAdminI18n, h as SaTheme, i as UiConfirmRequest } from '../resource-registry-TaR7rtq0.js';
5
+ import { p as platformResources, H as HttpClient } from '../catalog-Dv2twuNi.js';
6
+ import { S as SuperAdminGuardOptions, a as SuperAdminBrand, b as SuperAdminEndpoints, E as ExtensionsMap, A as ActionsMap, c as SuperAdminLoginAdapter, U as UiNotify, d as UiConfirm, R as ResourceOverrides, I as InstallPlugin, e as SuperAdminI18nOptions, f as SaThemeOptions, g as SuperAdminI18n, h as SaTheme, i as UiConfirmRequest } from '../resource-registry-Cn1Gy2LE.js';
7
7
  import '@saasicat/types';
8
8
 
9
9
  interface CreateSuperAdminAppOptions extends SuperAdminGuardOptions {
@@ -17,11 +17,11 @@ import {
17
17
  createSaTheme,
18
18
  createSuperAdminI18n,
19
19
  resolveSuperAdminEndpoints
20
- } from "../chunk-O3J3ITF2.js";
20
+ } from "../chunk-LK6YJDXC.js";
21
21
  import {
22
22
  defaultHttpClient,
23
23
  platformResources
24
- } from "../chunk-BPF2BMCQ.js";
24
+ } from "../chunk-4IZWT4UY.js";
25
25
 
26
26
  // src/quasar/create-super-admin-app.ts
27
27
  import { createApp } from "vue";
@@ -1,7 +1,7 @@
1
1
  import { Component, App, InjectionKey, Ref, ComputedRef } from 'vue';
2
2
  import { NavigationGuardWithThis } from 'vue-router';
3
3
  import { ActionKey, ComponentKey, AdminManifest } from '@saasicat/types';
4
- import { A as ActionHandler, H as HttpClient, S as SaLocale, a as SaMessages, b as SaLocaleOption, c as SaLocaleDefinition, d as SaMessagesOverrides, K as KvStore, R as ResourceDef, e as ResourceOps, f as ResourceContext, B as Bound, P as PlatformResources } from './catalog-Dch1Ryw0.cjs';
4
+ import { A as ActionHandler, H as HttpClient, S as SaLocale, a as SaMessages, b as SaLocaleOption, c as SaLocaleDefinition, d as SaMessagesOverrides, K as KvStore, R as ResourceDef, e as ResourceOps, f as ResourceContext, B as Bound, P as PlatformResources } from './catalog-Dv2twuNi.cjs';
5
5
 
6
6
  /**
7
7
  * App-specific branding data that the platform `AdminLayout` and other
@@ -1,7 +1,7 @@
1
1
  import { Component, App, InjectionKey, Ref, ComputedRef } from 'vue';
2
2
  import { NavigationGuardWithThis } from 'vue-router';
3
3
  import { ActionKey, ComponentKey, AdminManifest } from '@saasicat/types';
4
- import { A as ActionHandler, H as HttpClient, S as SaLocale, a as SaMessages, b as SaLocaleOption, c as SaLocaleDefinition, d as SaMessagesOverrides, K as KvStore, R as ResourceDef, e as ResourceOps, f as ResourceContext, B as Bound, P as PlatformResources } from './catalog-Dch1Ryw0.js';
4
+ import { A as ActionHandler, H as HttpClient, S as SaLocale, a as SaMessages, b as SaLocaleOption, c as SaLocaleDefinition, d as SaMessagesOverrides, K as KvStore, R as ResourceDef, e as ResourceOps, f as ResourceContext, B as Bound, P as PlatformResources } from './catalog-Dv2twuNi.js';
5
5
 
6
6
  /**
7
7
  * App-specific branding data that the platform `AdminLayout` and other
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saasicat/ui-vue",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
4
4
  "description": "Vue 3 components, resource clients and composables for the SuperAdmin UI shell. Provides boot and manifest loaders, navigation, actions and standard pages.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -61,7 +61,7 @@
61
61
  "src"
62
62
  ],
63
63
  "dependencies": {
64
- "@saasicat/types": "^0.26.0"
64
+ "@saasicat/types": "^0.27.0"
65
65
  },
66
66
  "peerDependencies": {
67
67
  "pinia": "^2.0.0 || ^3.0.0",
@@ -99,7 +99,7 @@
99
99
  "vue-router": "^5.0.0",
100
100
  "vue-tsc": "^3.3.3"
101
101
  },
102
- "license": "Apache-2.0",
102
+ "license": "PolyForm-Shield-1.0.0",
103
103
  "author": "Taci Uelker",
104
104
  "homepage": "https://github.com/uelker70/saasicat",
105
105
  "repository": {
@@ -121,6 +121,7 @@
121
121
  "test:component": "vitest run --config vitest.config.ts",
122
122
  "test:e2e": "playwright test",
123
123
  "test:visual": "playwright test --project=visual",
124
- "test:visual:update": "playwright test --project=visual --update-snapshots"
124
+ "test:visual:update": "playwright test --project=visual --update-snapshots",
125
+ "test:shipped-source": "node scripts/check-shipped-source.mjs"
125
126
  }
126
127
  }
@@ -19,6 +19,7 @@
19
19
  // `adminErrorMessage()` is what turns an error into user-facing text; it is
20
20
  // the only place that decides between `detail` and a translated fallback.
21
21
 
22
+ import { attachCause } from './attach-cause.js';
22
23
  import { formatMessage } from './i18n/format.js';
23
24
  import type { SaMessages } from './i18n/messages.js';
24
25
 
@@ -234,9 +235,25 @@ export class AdminError extends Error {
234
235
  readonly emptyResponse: boolean;
235
236
  /** The request never reached the server. See `AdminErrorInit`. */
236
237
  readonly transportFailure: boolean;
238
+ /**
239
+ * The error this one was raised from, when `AdminErrorInit` carried one.
240
+ *
241
+ * `declare` because the property is real but not emitted here: `attachCause`
242
+ * defines it, exactly as the ES2022 `Error` constructor would. The
243
+ * declaration is what makes it *readable* — `Error.cause` lives in
244
+ * `lib.es2022.error.d.ts`, and this package supports consumers on ES2021,
245
+ * where taking a cause in and never being able to read it out would be a
246
+ * one-way public API.
247
+ *
248
+ * Not `readonly`, unlike its siblings: the inherited `Error.cause` is
249
+ * writable, and narrowing that in a patch release would reject assignments
250
+ * a consumer can make today on ES2022.
251
+ */
252
+ declare cause?: unknown;
237
253
 
238
254
  constructor(init: AdminErrorInit = {}) {
239
- super(init.message ?? describe(init), { cause: init.cause });
255
+ super(init.message ?? describe(init));
256
+ if (init.cause !== undefined) attachCause(this, init.cause);
240
257
  this.name = 'AdminError';
241
258
  this.status = init.status ?? 0;
242
259
  this.code = init.code;