@streamoid/settings 0.7.5 → 0.7.7

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/dist/index.js CHANGED
@@ -67,6 +67,161 @@ __export(index_exports, {
67
67
  });
68
68
  module.exports = __toCommonJS(index_exports);
69
69
 
70
+ // src/settings-errors.ts
71
+ function quietErrorToast(toast) {
72
+ return { ...toast, error(message) {
73
+ if (message.trim()) toast.error(message);
74
+ } };
75
+ }
76
+ function isOnlyWorkspaceAdmin(failure) {
77
+ const seen = /* @__PURE__ */ new Set();
78
+ const visit = (value, depth) => {
79
+ if (depth > 4 || value == null || seen.has(value)) return false;
80
+ if (typeof value === "string") return /single admin|only admin/i.test(value);
81
+ if (typeof value !== "object") return false;
82
+ seen.add(value);
83
+ const error = value;
84
+ return [error.body, error.data, error.status, error.message, error.error, error.cause].some((child) => visit(child, depth + 1));
85
+ };
86
+ return visit(failure, 0);
87
+ }
88
+
89
+ // ../errors/index.js
90
+ var actions = /* @__PURE__ */ new Set(["edit_input", "sign_in", "ask_admin", "wait", "view_status", "retry", "contact_support"]);
91
+ var states = /* @__PURE__ */ new Set(["not_started", "failed", "partial", "in_progress", "unknown"]);
92
+ var isObject = (value) => value !== null && typeof value === "object";
93
+ var object = (value) => isObject(value) ? value : {};
94
+ var text = (value) => typeof value === "string" ? value : "";
95
+ var reference = (value) => /^[a-zA-Z0-9][a-zA-Z0-9_-]{5,127}$/.test(text(value)) ? value : void 0;
96
+ var technical = /https?:\/\/|<[^>]+>|\b(?:TypeError|SyntaxError|ReferenceError|ENOTFOUND|ECONNREFUSED|ECONNRESET|ETIMEDOUT|EAI_AGAIN|Traceback|SQLSTATE|MongoServerError|localhost|undefined|null|NaN|CORS|stack trace|Xvfb|node:sqlite)\b|(?:\/[^\s/]+){3,}|\bat\s+\w+\s*\(/i;
97
+ var publicText = (value) => {
98
+ const s = text(value).trim();
99
+ return s && s.length <= 500 && !technical.test(s) && !/[\r\n{}]/.test(s) ? s : void 0;
100
+ };
101
+ var isCancellation = (error) => object(error).name === "AbortError";
102
+ function parseRetryAfter(value, now = Date.now()) {
103
+ if (!text(value).trim()) return void 0;
104
+ const seconds = /^\d+$/.test(value.trim()) ? Number(value) : Math.ceil((Date.parse(value) - now) / 1e3);
105
+ return Number.isFinite(seconds) && seconds >= 0 && seconds <= 86400 ? seconds : void 0;
106
+ }
107
+ function validationFields(body) {
108
+ if (!Array.isArray(body.detail)) return [];
109
+ return body.detail.slice(0, 20).flatMap((issue) => {
110
+ if (!isObject(issue) || !Array.isArray(issue.loc)) return [];
111
+ const path = issue.loc.filter((part) => part !== "body" && part !== "query").filter((part) => typeof part === "string" || typeof part === "number").join(".");
112
+ if (!/^[a-zA-Z0-9_.-]{1,150}$/.test(path)) return [];
113
+ return [{ path, message: issue.type === "missing" || issue.type === "value_error.missing" ? "This field is required." : "Check this value and try again." }];
114
+ });
115
+ }
116
+ var known = {
117
+ "platform.session_expired": ["Your session has expired. Sign in to continue.", "sign_in"],
118
+ "platform.permission_denied": ["You don't have permission to do this. Ask a workspace admin for access.", "ask_admin"],
119
+ "platform.insufficient_credits": ["There aren't enough credits for this action. Ask your billing admin to add credits.", "ask_admin"],
120
+ "platform.invalid_input": ["Check the information you entered and correct any highlighted fields.", "edit_input"],
121
+ "platform.not_found": ["This item is no longer available or you don't have access to it. Check that you've opened the correct workspace.", "view_status"],
122
+ "platform.conflict": ["This item has changed or already exists. Check its current state before continuing.", "view_status"],
123
+ "platform.file_too_large": ["This file is too large. Choose a smaller file and try again.", "edit_input"],
124
+ "platform.unsupported_file": ["This file type isn't supported. Choose one of the accepted file types.", "edit_input"],
125
+ "platform.content_rejected": ["This request couldn't be completed with the current input. Review the image or description and try again.", "edit_input"],
126
+ "platform.rate_limited": ["This service is busy. Wait a moment before trying again.", "wait"],
127
+ "platform.service_unavailable": ["A service is temporarily unavailable. Try again shortly; contact support if it keeps happening.", "wait"],
128
+ "platform.setup_required": ["There's a setup problem on our side. Contact support so we can help.", "contact_support"],
129
+ "platform.network_error": ["We couldn't reach the service. Check your connection and try again shortly.", "retry"],
130
+ "platform.unexpected": ["We couldn't complete this action. Contact support if it keeps happening.", "contact_support"]
131
+ };
132
+ function inferCode(status, rawCode, diagnostic) {
133
+ if (Object.hasOwn(known, rawCode)) return rawCode;
134
+ const signal = `${rawCode} ${diagnostic}`;
135
+ if (/insufficient.?credits|credits.?unavailable|insufficient.?balance/i.test(signal)) return "platform.insufficient_credits";
136
+ if (/content.?policy|safety.?block|content.?reject/i.test(signal)) return "platform.content_rejected";
137
+ if (/rate.?limit|capacity.?limit/i.test(signal)) return "platform.rate_limited";
138
+ if (status === 401) return "platform.session_expired";
139
+ if (status === 402) return "platform.insufficient_credits";
140
+ if (status === 403) return "platform.permission_denied";
141
+ if (status === 404 || status === 410) return "platform.not_found";
142
+ if (status === 409 || status === 412) return "platform.conflict";
143
+ if (status === 413) return "platform.file_too_large";
144
+ if (status === 415) return "platform.unsupported_file";
145
+ if (status === 429) return "platform.rate_limited";
146
+ if (status === 400 || status === 422) return "platform.invalid_input";
147
+ if ([408, 502, 503, 504].includes(status)) return "platform.service_unavailable";
148
+ if (/ENOTFOUND|ECONNREFUSED|ECONNRESET|ETIMEDOUT|EAI_AGAIN|server misbehaving|upstream unavailable/i.test(signal)) return "platform.service_unavailable";
149
+ if (/failed to fetch|networkerror|network request failed|load failed/i.test(signal)) return "platform.network_error";
150
+ return "platform.unexpected";
151
+ }
152
+ function normalizeError(failure, options = {}) {
153
+ if (failure instanceof UserFacingError && Object.keys(options).length === 0) return failure.publicError;
154
+ const error = object(failure);
155
+ const body = object(options.body ?? error.body ?? error.data ?? failure);
156
+ const envelope = object(body.publicError ?? error.publicError);
157
+ const declared = envelope.version === 1 ? envelope : {};
158
+ const status = Number(options.status ?? error.statusCode ?? (typeof error.status === "number" ? error.status : void 0)) || 0;
159
+ const diagnostic = [text(error.message), text(failure), text(body.detail), text(body.error)].filter(Boolean).join(" ");
160
+ const rawCode = text(declared.code) || text(object(body.error).code) || text(body.code) || text(error.code);
161
+ const category = inferCode(status, rawCode, diagnostic);
162
+ const code = /^[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)+$/.test(text(declared.code)) ? declared.code : category;
163
+ const method = (options.method || text(error.method) || "UNKNOWN").toUpperCase();
164
+ const read = ["GET", "HEAD", "OPTIONS"].includes(method);
165
+ const suppliedState = object(declared.operation).state;
166
+ const state = states.has(suppliedState) ? suppliedState : read ? "failed" : "unknown";
167
+ const retryAfterSeconds = parseRetryAfter(options.retryAfter) ?? (Number.isFinite(declared.recovery?.retryAfterSeconds) && declared.recovery.retryAfterSeconds >= 0 && declared.recovery.retryAfterSeconds <= 86400 ? declared.recovery.retryAfterSeconds : void 0);
168
+ let [message, action] = known[category];
169
+ message = publicText(declared.message) || message;
170
+ if (actions.has(declared.recovery?.action)) action = declared.recovery.action;
171
+ const fieldErrors = Array.isArray(declared.fieldErrors) && declared.fieldErrors.length ? declared.fieldErrors.slice(0, 20).filter((item) => /^[a-zA-Z0-9_.-]{1,150}$/.test(text(item?.path)) && publicText(item?.message)).map((item) => ({ path: item.path, message: item.message })) : validationFields(body);
172
+ if (fieldErrors.length) {
173
+ const fields = fieldErrors.slice(0, 3).map((field) => `${field.path.replace(/_/g, " ")}: ${field.message}`).join(" ");
174
+ message = `${fields}${fieldErrors.length > 3 ? " Check the remaining fields too." : ""}`;
175
+ action = "edit_input";
176
+ }
177
+ if (category === "platform.rate_limited" && retryAfterSeconds !== void 0 && !publicText(declared.message)) {
178
+ const unit = retryAfterSeconds >= 3600 ? "hour" : retryAfterSeconds >= 60 ? "minute" : "second";
179
+ const amount = Math.ceil(retryAfterSeconds / (unit === "hour" ? 3600 : unit === "minute" ? 60 : 1));
180
+ message = `This service is busy. Try again in ${amount} ${unit}${amount === 1 ? "" : "s"}.`;
181
+ }
182
+ const uncertain = !read && state === "unknown" && ["platform.network_error", "platform.service_unavailable", "platform.unexpected"].includes(category);
183
+ if (uncertain) {
184
+ const uncertainty = "We couldn't confirm whether your request completed. Check its status before trying again; contact support if you're unsure.";
185
+ const authored = publicText(declared.message);
186
+ message = authored ? authored.endsWith(uncertainty) ? authored : `${authored} ${uncertainty}` : uncertainty;
187
+ action = "view_status";
188
+ }
189
+ const operationLabel = publicText(options.operation);
190
+ if (operationLabel && !message.startsWith(`We couldn't ${operationLabel}.`)) message = `We couldn't ${operationLabel}. ${message}`;
191
+ const errorRef = reference(options.reference) || reference(declared.reference) || reference(error.errorRef) || reference(object(body.error).ref);
192
+ const safeToRetry = (read || declared.safeToRetry === true && ["not_started", "failed"].includes(state)) && ["wait", "retry"].includes(action);
193
+ return {
194
+ version: 1,
195
+ code,
196
+ message,
197
+ recovery: { action, ...retryAfterSeconds !== void 0 ? { retryAfterSeconds } : {} },
198
+ operation: { state, ...reference(object(declared.operation).id) ? { id: declared.operation.id } : {} },
199
+ safeToRetry,
200
+ fieldErrors,
201
+ ...errorRef ? { reference: errorRef } : {}
202
+ };
203
+ }
204
+ var UserFacingError = class extends Error {
205
+ constructor(failure, options = {}) {
206
+ const normalized = normalizeError(failure, options);
207
+ super(normalized.message, { cause: failure });
208
+ this.name = "UserFacingError";
209
+ this.publicError = normalized;
210
+ this.status = Number(options.status ?? object(failure).status) || 0;
211
+ this.statusCode = this.status;
212
+ this.method = (options.method || text(object(failure).method) || "UNKNOWN").toUpperCase();
213
+ this.code = normalized.code;
214
+ this.body = options.body ?? object(failure).body;
215
+ this.data = this.body;
216
+ this.errorRef = normalized.reference ?? null;
217
+ }
218
+ };
219
+ function errorMessage(failure, options = {}) {
220
+ if (isCancellation(failure)) return "";
221
+ const normalized = normalizeError(failure, options);
222
+ return normalized.reference ? `${normalized.message} Reference: ${normalized.reference}` : normalized.message;
223
+ }
224
+
70
225
  // src/managed-organization-summary.tsx
71
226
  var import_icons = require("@streamoid/icons");
72
227
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -293,7 +448,13 @@ function SettingsAdaptersProvider({
293
448
  adapters,
294
449
  children
295
450
  }) {
296
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SettingsAdaptersContext.Provider, { value: adapters, children });
451
+ const value = (0, import_react2.useMemo)(() => {
452
+ const toast = quietErrorToast(adapters.toast);
453
+ return new Proxy(adapters, { get(target, key) {
454
+ return key === "toast" ? toast : Reflect.get(target, key);
455
+ } });
456
+ }, [adapters]);
457
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SettingsAdaptersContext.Provider, { value, children });
297
458
  }
298
459
  function useSettingsAdapters() {
299
460
  const ctx = (0, import_react2.useContext)(SettingsAdaptersContext);
@@ -2736,7 +2897,7 @@ function ToggleSwitch({
2736
2897
  }
2737
2898
  );
2738
2899
  }
2739
- function FieldLabel2({ text }) {
2900
+ function FieldLabel2({ text: text2 }) {
2740
2901
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex items-center shrink-0 w-full", style: { height: 24 }, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2741
2902
  "div",
2742
2903
  {
@@ -2748,7 +2909,7 @@ function FieldLabel2({ text }) {
2748
2909
  lineHeight: T6.lhSm,
2749
2910
  color: T6.tertiary
2750
2911
  },
2751
- children: text
2912
+ children: text2
2752
2913
  }
2753
2914
  ) });
2754
2915
  }
@@ -4789,7 +4950,7 @@ function TeamsContent({
4789
4950
  toast.success("Invitation resent");
4790
4951
  await fetchData();
4791
4952
  } catch (err) {
4792
- const msg = err instanceof Error ? err.message : "Failed to resend invitation";
4953
+ const msg = errorMessage(err, { method: "POST" });
4793
4954
  toast.error(msg);
4794
4955
  }
4795
4956
  closeModal();
@@ -4824,17 +4985,13 @@ function TeamsContent({
4824
4985
  storeIds: permissions.catalogixStores || []
4825
4986
  });
4826
4987
  toast.success("User permissions updated");
4827
- await fetchData();
4828
4988
  } catch (err) {
4829
- const msg = err instanceof Error ? err.message : "Failed to update user";
4989
+ const msg = errorMessage(err, { method: "POST" });
4830
4990
  toast.error(msg);
4831
- const updateList = (list) => list.map(
4832
- (m) => m.id === modal.target?.id ? { ...m, permissions } : m
4833
- );
4834
- setActiveMembers(updateList);
4835
- setPendingMembers(updateList);
4991
+ return;
4836
4992
  }
4837
4993
  closeModal();
4994
+ await fetchData();
4838
4995
  };
4839
4996
  const handleRemove = async () => {
4840
4997
  if (!modal.target) {
@@ -4869,16 +5026,13 @@ function TeamsContent({
4869
5026
  } else {
4870
5027
  throw new Error("Unable to identify target to remove");
4871
5028
  }
4872
- await fetchData();
4873
5029
  } catch (err) {
4874
- const msg = err instanceof Error ? err.message : "Failed to remove user";
5030
+ const msg = errorMessage(err, { method: "POST" });
4875
5031
  toast.error(msg);
4876
- setActiveMembers((prev) => prev.filter((m) => m.id !== modal.target?.id));
4877
- setPendingMembers(
4878
- (prev) => prev.filter((m) => m.id !== modal.target?.id)
4879
- );
5032
+ return;
4880
5033
  }
4881
5034
  closeModal();
5035
+ await fetchData();
4882
5036
  };
4883
5037
  const handleCancelPendingInvite = async (member) => {
4884
5038
  if (member.kind !== "invite" || !member.invitationId) {
@@ -4904,7 +5058,7 @@ function TeamsContent({
4904
5058
  toast.success("Invitation removed");
4905
5059
  await fetchData();
4906
5060
  } catch (err) {
4907
- const msg = err instanceof Error ? err.message : "Failed to remove user";
5061
+ const msg = errorMessage(err, { method: "POST" });
4908
5062
  toast.error(msg);
4909
5063
  }
4910
5064
  };
@@ -4921,7 +5075,7 @@ function TeamsContent({
4921
5075
  toast.success("Invitation resent");
4922
5076
  await fetchData();
4923
5077
  } catch (err) {
4924
- const msg = err instanceof Error ? err.message : "Failed to resend invitation";
5078
+ const msg = errorMessage(err, { method: "POST" });
4925
5079
  toast.error(msg);
4926
5080
  }
4927
5081
  };
@@ -4950,7 +5104,7 @@ function TeamsContent({
4950
5104
  await fetchData();
4951
5105
  closeModal();
4952
5106
  } catch (err) {
4953
- const msg = err instanceof Error ? err.message : "Failed to send invite";
5107
+ const msg = errorMessage(err, { method: "POST" });
4954
5108
  toast.error(msg);
4955
5109
  }
4956
5110
  };
@@ -5166,7 +5320,7 @@ var T9 = {
5166
5320
  r4xl: "var(--radius-4xl)",
5167
5321
  neutral300: "var(--primitives-neutral-300)"
5168
5322
  };
5169
- function FieldLabel3({ text }) {
5323
+ function FieldLabel3({ text: text2 }) {
5170
5324
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex items-center shrink-0 w-full", style: { height: 24 }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5171
5325
  "div",
5172
5326
  {
@@ -5178,7 +5332,7 @@ function FieldLabel3({ text }) {
5178
5332
  lineHeight: T9.lhSm,
5179
5333
  color: T9.tertiary
5180
5334
  },
5181
- children: text
5335
+ children: text2
5182
5336
  }
5183
5337
  ) });
5184
5338
  }
@@ -6133,7 +6287,11 @@ function MemberRow({
6133
6287
  }
6134
6288
  );
6135
6289
  }
6136
- function WorkspaceRow({ workspace }) {
6290
+ function WorkspaceRow({
6291
+ workspace,
6292
+ joining = false,
6293
+ onJoin
6294
+ }) {
6137
6295
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
6138
6296
  "div",
6139
6297
  {
@@ -6169,80 +6327,19 @@ function WorkspaceRow({ workspace }) {
6169
6327
  }
6170
6328
  )
6171
6329
  ] }),
6172
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VisibilityBadge, { visibility: workspace.orgVisibility })
6173
- ]
6174
- }
6175
- );
6176
- }
6177
- function JoinableWorkspaceRow({
6178
- workspace,
6179
- joining,
6180
- onJoin
6181
- }) {
6182
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
6183
- "div",
6184
- {
6185
- className: "flex items-center self-stretch shrink-0",
6186
- style: {
6187
- borderBottom: `1px solid ${T11.borderSubtle}`,
6188
- padding: `${T11.spXl} ${T11.sp3xl}`,
6189
- gap: T11.sp6xl
6190
- },
6191
- children: [
6192
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center min-w-0", style: { flex: 1, gap: T11.spMd }, children: [
6193
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6194
- ProfileAvatar,
6330
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center shrink-0", style: { gap: T11.spMd }, children: [
6331
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(VisibilityBadge, { visibility: workspace.orgVisibility }),
6332
+ onJoin && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6333
+ import_ui8.ScButton,
6195
6334
  {
6196
- name: workspace.name,
6197
- profileImage: workspace.imageUrl,
6198
- size: 28,
6199
- borderRadius: "var(--radius-lg)"
6335
+ text: joining ? "Joining\u2026" : "Join",
6336
+ variant: "secondary",
6337
+ size: "sm",
6338
+ state: joining ? "disabled" : "default",
6339
+ onClick: joining ? void 0 : onJoin
6200
6340
  }
6201
- ),
6202
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex flex-col min-w-0", children: [
6203
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6204
- "span",
6205
- {
6206
- className: "truncate",
6207
- style: {
6208
- fontFamily: T11.font,
6209
- fontSize: T11.fsSm,
6210
- fontWeight: 500,
6211
- lineHeight: T11.lhSm,
6212
- color: T11.primary
6213
- },
6214
- children: workspace.name
6215
- }
6216
- ),
6217
- workspace.ownerName && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
6218
- "span",
6219
- {
6220
- className: "truncate",
6221
- style: {
6222
- fontFamily: T11.font,
6223
- fontSize: T11.fsXs,
6224
- fontWeight: 400,
6225
- lineHeight: T11.lhXs,
6226
- color: T11.muted
6227
- },
6228
- children: [
6229
- "Created by ",
6230
- workspace.ownerName
6231
- ]
6232
- }
6233
- )
6234
- ] })
6235
- ] }),
6236
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6237
- import_ui8.ScButton,
6238
- {
6239
- text: joining ? "Joining\u2026" : "Join",
6240
- variant: "secondary",
6241
- size: "sm",
6242
- state: joining ? "disabled" : "default",
6243
- onClick: joining ? void 0 : onJoin
6244
- }
6245
- )
6341
+ )
6342
+ ] })
6246
6343
  ]
6247
6344
  }
6248
6345
  );
@@ -6370,8 +6467,7 @@ function OrganizationContent({
6370
6467
  const current = organization.current;
6371
6468
  const [members, setMembers] = (0, import_react12.useState)([]);
6372
6469
  const [workspaces, setWorkspaces] = (0, import_react12.useState)([]);
6373
- const canJoinShared = current?.role === "member" && !!organization.fetchJoinableWorkspaces && !!organization.joinWorkspaces;
6374
- const [joinable, setJoinable] = (0, import_react12.useState)([]);
6470
+ const canJoinShared = current?.role === "member" && !!organization.joinWorkspaces;
6375
6471
  const [joiningId, setJoiningId] = (0, import_react12.useState)(null);
6376
6472
  const [joinRequests, setJoinRequests] = (0, import_react12.useState)([]);
6377
6473
  const [highestRole, setHighestRole] = (0, import_react12.useState)("guest");
@@ -6382,20 +6478,12 @@ function OrganizationContent({
6382
6478
  if (!organization.current) return;
6383
6479
  setLoadingData(true);
6384
6480
  try {
6385
- const [
6386
- membersRes,
6387
- workspacesRes,
6388
- joinRequestsRes,
6389
- highestRoleRes,
6390
- joinableRes
6391
- ] = await Promise.all([
6481
+ const [membersRes, workspacesRes, joinRequestsRes, highestRoleRes] = await Promise.all([
6392
6482
  organization.fetchMembers().catch(() => null),
6393
6483
  organization.fetchWorkspaces().catch(() => null),
6394
6484
  organization.fetchJoinRequests().catch(() => null),
6395
- organization.getHighestRole?.().catch(() => null) ?? Promise.resolve(null),
6396
- canJoinShared ? organization.fetchJoinableWorkspaces().catch(() => null) : Promise.resolve(null)
6485
+ organization.getHighestRole?.().catch(() => null) ?? Promise.resolve(null)
6397
6486
  ]);
6398
- if (joinableRes) setJoinable(joinableRes);
6399
6487
  if (highestRoleRes?.role) setHighestRole(highestRoleRes.role);
6400
6488
  if (membersRes) setMembers(membersRes.filter((m) => m?.user));
6401
6489
  if (workspacesRes) setWorkspaces(workspacesRes);
@@ -6404,7 +6492,7 @@ function OrganizationContent({
6404
6492
  } finally {
6405
6493
  setLoadingData(false);
6406
6494
  }
6407
- }, [organization, canJoinShared]);
6495
+ }, [organization]);
6408
6496
  (0, import_react12.useEffect)(() => {
6409
6497
  fetchData();
6410
6498
  }, [fetchData]);
@@ -6421,9 +6509,7 @@ function OrganizationContent({
6421
6509
  await workspaceAdapter.refresh?.().catch(() => void 0);
6422
6510
  await fetchData();
6423
6511
  } catch (err) {
6424
- toast.error(
6425
- err instanceof Error ? err.message : `Couldn't join ${target.name}`
6426
- );
6512
+ toast.error(errorMessage(err, { method: "POST" }));
6427
6513
  } finally {
6428
6514
  setJoiningId(null);
6429
6515
  }
@@ -6436,7 +6522,7 @@ function OrganizationContent({
6436
6522
  toast.success("Organization updated");
6437
6523
  return true;
6438
6524
  } catch (err) {
6439
- toast.error(err instanceof Error ? err.message : "Failed to update organization");
6525
+ toast.error(errorMessage(err, { method: "POST" }));
6440
6526
  return false;
6441
6527
  }
6442
6528
  };
@@ -6447,7 +6533,7 @@ function OrganizationContent({
6447
6533
  toast.success("Organization deleted");
6448
6534
  return true;
6449
6535
  } catch (err) {
6450
- toast.error(err instanceof Error ? err.message : "Failed to delete organization");
6536
+ toast.error(errorMessage(err, { method: "POST" }));
6451
6537
  return false;
6452
6538
  }
6453
6539
  };
@@ -6479,7 +6565,7 @@ function OrganizationContent({
6479
6565
  toast.success("Member updated");
6480
6566
  await fetchData();
6481
6567
  } catch (err) {
6482
- toast.error(err instanceof Error ? err.message : "Failed to update member");
6568
+ toast.error(errorMessage(err, { method: "POST" }));
6483
6569
  }
6484
6570
  closeModal();
6485
6571
  };
@@ -6509,7 +6595,7 @@ function OrganizationContent({
6509
6595
  toast.success("Member removed");
6510
6596
  await fetchData();
6511
6597
  } catch (err) {
6512
- toast.error(err instanceof Error ? err.message : "Failed to remove member");
6598
+ toast.error(errorMessage(err, { method: "POST" }));
6513
6599
  }
6514
6600
  closeModal();
6515
6601
  };
@@ -6521,7 +6607,7 @@ function OrganizationContent({
6521
6607
  );
6522
6608
  await fetchData();
6523
6609
  } catch (err) {
6524
- toast.error(err instanceof Error ? err.message : "Failed to approve request");
6610
+ toast.error(errorMessage(err, { method: "POST" }));
6525
6611
  }
6526
6612
  };
6527
6613
  const handleRejectJoinRequest = async (request) => {
@@ -6530,7 +6616,7 @@ function OrganizationContent({
6530
6616
  toast.success("Request rejected");
6531
6617
  await fetchData();
6532
6618
  } catch (err) {
6533
- toast.error(err instanceof Error ? err.message : "Failed to reject request");
6619
+ toast.error(errorMessage(err, { method: "POST" }));
6534
6620
  }
6535
6621
  };
6536
6622
  if (!current) {
@@ -6653,21 +6739,12 @@ function OrganizationContent({
6653
6739
  {
6654
6740
  title: "Workspaces in this organization",
6655
6741
  empty: workspaces.length === 0,
6656
- children: workspaces.map((ws) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(WorkspaceRow, { workspace: ws }, ws._id))
6657
- }
6658
- ),
6659
- canJoinShared && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6660
- Section,
6661
- {
6662
- title: "Shared workspaces you can join",
6663
- empty: joinable.length === 0,
6664
- emptyText: "You're in every shared workspace in this organization",
6665
- children: joinable.map((ws) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6666
- JoinableWorkspaceRow,
6742
+ children: workspaces.map((ws) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
6743
+ WorkspaceRow,
6667
6744
  {
6668
6745
  workspace: ws,
6669
6746
  joining: joiningId === ws._id,
6670
- onJoin: () => void handleJoinWorkspace(ws)
6747
+ onJoin: canJoinShared && ws.joined === false && ws.orgVisibility === "shared" ? () => void handleJoinWorkspace(ws) : void 0
6671
6748
  },
6672
6749
  ws._id
6673
6750
  ))
@@ -6769,6 +6846,21 @@ function OrganizationContent({
6769
6846
  ) });
6770
6847
  }
6771
6848
 
6849
+ // src/billing-errors.ts
6850
+ function isBillingWorkspaceMissing(failure) {
6851
+ const seen = /* @__PURE__ */ new Set();
6852
+ const visit = (value, depth) => {
6853
+ if (depth > 4 || value == null || seen.has(value)) return false;
6854
+ if (typeof value === "string") return /workspace.*not found/i.test(value);
6855
+ if (typeof value !== "object") return false;
6856
+ seen.add(value);
6857
+ const error = value;
6858
+ if (error.code === "billing.workspace_not_found") return true;
6859
+ return [error.message, error.error, error.data, error.body, error.cause, error.publicError].some((child) => visit(child, depth + 1));
6860
+ };
6861
+ return visit(failure, 0);
6862
+ }
6863
+
6772
6864
  // src/billing-content.tsx
6773
6865
  var import_react15 = require("react");
6774
6866
  var import_ui9 = require("@streamoid/ui");
@@ -8911,8 +9003,8 @@ function BillingContent({
8911
9003
  toast.success("Plan update submitted");
8912
9004
  await fetchBillingOverview();
8913
9005
  } catch (err) {
8914
- const message = err instanceof Error ? err.message : "Plan update failed";
8915
- const shouldCreateWorkspace = /workspace.*not found/i.test(message);
9006
+ const message = errorMessage(err, { method: "POST" });
9007
+ const shouldCreateWorkspace = isBillingWorkspaceMissing(err);
8916
9008
  if (shouldCreateWorkspace) {
8917
9009
  const created = await ensureWorkspaceOnBilling();
8918
9010
  if (created && api.updateSubscription) {
@@ -8965,7 +9057,7 @@ function BillingContent({
8965
9057
  toast.error("Unable to cancel downgrade");
8966
9058
  }
8967
9059
  } catch (err) {
8968
- const msg = err instanceof Error ? err.message : "Failed to cancel downgrade";
9060
+ const msg = errorMessage(err, { method: "POST" });
8969
9061
  toast.error(msg);
8970
9062
  } finally {
8971
9063
  setPlanActionInFlight(null);
@@ -9156,7 +9248,7 @@ function BillingContent({
9156
9248
  setShowBuyCreditsModal(false);
9157
9249
  await fetchBillingOverview();
9158
9250
  } catch (err) {
9159
- const message = err instanceof Error ? err.message : "Failed to create top-up session";
9251
+ const message = errorMessage(err, { method: "POST" });
9160
9252
  toast.error(message);
9161
9253
  } finally {
9162
9254
  setBuyCreditsInFlight(false);
@@ -9487,7 +9579,7 @@ function CreateTokenModal({ onClose, onCreated }) {
9487
9579
  const data = await api.createToken({ name: name.trim(), expiry });
9488
9580
  onCreated(data);
9489
9581
  } catch (err) {
9490
- toast.error(err instanceof Error ? err.message : "Failed to create token.");
9582
+ toast.error(errorMessage(err, { method: "POST" }));
9491
9583
  } finally {
9492
9584
  setLoading(false);
9493
9585
  }
@@ -9842,7 +9934,7 @@ function TokensContent({
9842
9934
  setTokens((prev) => prev.filter((t) => t._id !== tokenId));
9843
9935
  }
9844
9936
  } catch (err) {
9845
- toast.error(err instanceof Error ? err.message : "Failed to revoke token.");
9937
+ toast.error(errorMessage(err, { method: "POST" }));
9846
9938
  } finally {
9847
9939
  setDeletingId(null);
9848
9940
  }
@@ -9854,7 +9946,7 @@ function TokensContent({
9854
9946
  toast.success("Token permanently deleted.");
9855
9947
  setTokens((prev) => prev.filter((t) => t._id !== tokenId));
9856
9948
  } catch (err) {
9857
- toast.error(err instanceof Error ? err.message : "Failed to delete token.");
9949
+ toast.error(errorMessage(err, { method: "POST" }));
9858
9950
  } finally {
9859
9951
  setDeletingId(null);
9860
9952
  }
@@ -10692,7 +10784,7 @@ function ProfileTab() {
10692
10784
  await auth.refreshProfile();
10693
10785
  toast.success("Profile updated");
10694
10786
  } catch (err) {
10695
- const msg = err instanceof Error ? err.message : "Failed to update profile";
10787
+ const msg = errorMessage(err, { method: "POST" });
10696
10788
  toast.error(msg);
10697
10789
  } finally {
10698
10790
  setSaving(false);
@@ -10725,7 +10817,7 @@ function ProfileTab() {
10725
10817
  toast.success("Profile image updated");
10726
10818
  } catch (err) {
10727
10819
  setImagePreview(void 0);
10728
- const msg = err instanceof Error ? err.message : "Failed to update profile image";
10820
+ const msg = errorMessage(err, { method: "POST" });
10729
10821
  toast.error(msg);
10730
10822
  } finally {
10731
10823
  URL.revokeObjectURL(localPreviewUrl);
@@ -10745,7 +10837,7 @@ function ProfileTab() {
10745
10837
  await auth.refreshProfile();
10746
10838
  toast.success("Profile image removed");
10747
10839
  } catch (err) {
10748
- const msg = err instanceof Error ? err.message : "Failed to remove profile image";
10840
+ const msg = errorMessage(err, { method: "POST" });
10749
10841
  toast.error(msg);
10750
10842
  } finally {
10751
10843
  setImageSaving(false);
@@ -11036,9 +11128,9 @@ function groupWorkspaceList(workspaces, organizations = []) {
11036
11128
  });
11037
11129
  }
11038
11130
  }
11039
- const known = new Set(organizations.map((org) => org._id));
11131
+ const known2 = new Set(organizations.map((org) => org._id));
11040
11132
  const ungrouped = workspaces.filter(
11041
- (ws) => !ws.organizationId || !known.has(ws.organizationId)
11133
+ (ws) => !ws.organizationId || !known2.has(ws.organizationId)
11042
11134
  );
11043
11135
  if (ungrouped.length) {
11044
11136
  groups.push({
@@ -11589,17 +11681,6 @@ function SettingsContentInner({
11589
11681
  imageRemoved
11590
11682
  });
11591
11683
  };
11592
- const getErrorMessage = (err, fallback) => {
11593
- if (err instanceof Error && err.message.trim()) return err.message;
11594
- if (err && typeof err === "object" && "body" in err && err.body && typeof err.body === "object" && "status" in err.body && err.body.status && typeof err.body.status === "object" && "message" in err.body.status && typeof err.body.status.message === "string") {
11595
- return err.body.status.message;
11596
- }
11597
- return fallback;
11598
- };
11599
- const isSingleAdminError = (message) => {
11600
- const normalized = message.toLowerCase();
11601
- return normalized.includes("single admin") || normalized.includes("only admin");
11602
- };
11603
11684
  const getSettingsPath = (nextWorkspaceId) => `/dashboard/${nextWorkspaceId}/settings`;
11604
11685
  const getNextWorkspaceId = async (excludedWorkspaceId) => {
11605
11686
  try {
@@ -11683,7 +11764,7 @@ function SettingsContentInner({
11683
11764
  toast.success("Workspace updated");
11684
11765
  return true;
11685
11766
  } catch (err) {
11686
- toast.error(getErrorMessage(err, "Failed to update workspace"));
11767
+ toast.error(errorMessage(err, { method: "PATCH" }));
11687
11768
  return false;
11688
11769
  } finally {
11689
11770
  setUpdatingWorkspace(false);
@@ -11751,7 +11832,7 @@ function SettingsContentInner({
11751
11832
  toast.success("Workspace created");
11752
11833
  return true;
11753
11834
  } catch (err) {
11754
- const msg = err instanceof Error ? err.message : "Failed to create workspace";
11835
+ const msg = errorMessage(err, { method: "POST" });
11755
11836
  toast.error(msg);
11756
11837
  return false;
11757
11838
  } finally {
@@ -11798,7 +11879,7 @@ function SettingsContentInner({
11798
11879
  toast.success("Workspace updated");
11799
11880
  return true;
11800
11881
  } catch (err) {
11801
- const msg = err instanceof Error ? err.message : "Failed to update workspace";
11882
+ const msg = errorMessage(err, { method: "POST" });
11802
11883
  toast.error(msg);
11803
11884
  return false;
11804
11885
  } finally {
@@ -12274,11 +12355,10 @@ function SettingsContentInner({
12274
12355
  setLeavingWorkspace(null);
12275
12356
  await completeWorkspaceRemoval(wsId, `You left ${wsName}`);
12276
12357
  } catch (err) {
12277
- const message = getErrorMessage(err, "Failed to leave workspace");
12278
- if (isSingleAdminError(message)) {
12358
+ if (isOnlyWorkspaceAdmin(err)) {
12279
12359
  toast.error("You are the only admin. Promote another admin before leaving.");
12280
12360
  } else {
12281
- toast.error(message);
12361
+ toast.error(errorMessage(err, { method: "POST" }));
12282
12362
  }
12283
12363
  }
12284
12364
  },
@@ -12308,7 +12388,7 @@ function SettingsContentInner({
12308
12388
  setDeletingWorkspace(null);
12309
12389
  await completeWorkspaceRemoval(wsId, `Workspace ${wsName} deleted`);
12310
12390
  } catch (err) {
12311
- toast.error(getErrorMessage(err, "Failed to delete workspace"));
12391
+ toast.error(errorMessage(err, { method: "DELETE" }));
12312
12392
  }
12313
12393
  }
12314
12394
  }