@scalemule/nextjs 0.1.1 → 0.1.2

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.mjs CHANGED
@@ -2098,6 +2098,40 @@ var StorageService = class extends ServiceModule {
2098
2098
  return { data: null, error: { code: "upload_error", message, status: 0 } };
2099
2099
  }
2100
2100
  }
2101
+ /**
2102
+ * Upload a file as a private attachment (e.g. chat / DM attachment).
2103
+ *
2104
+ * Same browser→S3 pipeline as {@link upload}, but enforces:
2105
+ * - `is_public: false` — caller cannot opt out
2106
+ * - no client-side compression — preserve the original bytes
2107
+ * - fail-closed if storage returns `is_public: true` for any reason
2108
+ *
2109
+ * The shared primitive used by `@scalemule/chat`'s chat-attachment uploader
2110
+ * and by `@scalemule/nextjs`'s `useMedia()` when an app's media policy is
2111
+ * `fast_trusted`. Both packages call this method via `@scalemule/sdk` —
2112
+ * `@scalemule/nextjs` does not depend on `@scalemule/chat`.
2113
+ *
2114
+ * See ADR-2026-04-26 (realtime-chat media pipeline) and
2115
+ * docs/MEDIA-UPLOADS.md for the full pattern.
2116
+ */
2117
+ async uploadPrivate(file, options) {
2118
+ const result = await this.upload(file, {
2119
+ ...options,
2120
+ isPublic: false,
2121
+ skipCompression: true
2122
+ });
2123
+ if (result.data && result.data.is_public === true) {
2124
+ return {
2125
+ data: null,
2126
+ error: {
2127
+ code: "visibility_violation",
2128
+ message: "Storage returned is_public=true for an uploadPrivate() call. This usually indicates a server-side bug or an admin override; the caller asked for a private upload.",
2129
+ status: 0
2130
+ }
2131
+ };
2132
+ }
2133
+ return result;
2134
+ }
2101
2135
  // --------------------------------------------------------------------------
2102
2136
  // Direct Upload (3-step with retry + stall)
2103
2137
  // --------------------------------------------------------------------------
@@ -3787,6 +3821,18 @@ var ChatService = class extends ServiceModule {
3787
3821
  return this.post(`/messages/${messageId}/reactions`, data, options);
3788
3822
  }
3789
3823
  // --------------------------------------------------------------------------
3824
+ // Pins
3825
+ // --------------------------------------------------------------------------
3826
+ async pinMessage(messageId, options) {
3827
+ return this.post(`/messages/${messageId}/pin`, void 0, options);
3828
+ }
3829
+ async unpinMessage(messageId, options) {
3830
+ return this.del(`/messages/${messageId}/pin`, options);
3831
+ }
3832
+ async getPinnedMessages(conversationId, options) {
3833
+ return this._get(`/conversations/${conversationId}/pins`, options);
3834
+ }
3835
+ // --------------------------------------------------------------------------
3790
3836
  // Typing & Read Receipts
3791
3837
  // --------------------------------------------------------------------------
3792
3838
  async sendTyping(conversationId, options) {
@@ -3997,15 +4043,80 @@ var BillingService = class extends ServiceModule {
3997
4043
  );
3998
4044
  }
3999
4045
  // --------------------------------------------------------------------------
4000
- // Customers
4046
+ // Customers (legacy user-scoped — deprecated in favor of account-scoped)
4001
4047
  // --------------------------------------------------------------------------
4048
+ /** @deprecated Use `getAccountCustomer({ account_id })`. Targets the user-scoped legacy endpoint. */
4002
4049
  async createCustomer(data, options) {
4003
4050
  return this.post("/customers", data, options);
4004
4051
  }
4052
+ /** @deprecated Use `attachAccountPaymentMethod({ account_id }, ...)`. Targets the user-scoped legacy endpoint. */
4005
4053
  async addPaymentMethod(data, options) {
4006
4054
  return this.post("/payment-methods", data, options);
4007
4055
  }
4008
4056
  // --------------------------------------------------------------------------
4057
+ // Account-Scoped Billing (v2 — ADR-2026-04-25)
4058
+ //
4059
+ // The customer/payment-method/invoice record set belongs to an `app_account`
4060
+ // (typically a `customer_org`), not to a single user. These methods target
4061
+ // the `/v1/billing/accounts/{account_id}/*` endpoints described in
4062
+ // openapi/scalemule-money-billing-v1.yaml.
4063
+ // --------------------------------------------------------------------------
4064
+ accountBase(accountId) {
4065
+ return `/v1/billing/accounts/${encodeURIComponent(accountId)}`;
4066
+ }
4067
+ async getAccountCustomer(req, options) {
4068
+ const path = this.withQuery(`${this.accountBase(req.account_id)}/customer`, {
4069
+ billing_mode: req.billing_mode
4070
+ });
4071
+ return this.client.get(path, options);
4072
+ }
4073
+ async listAccountPaymentMethods(req, options) {
4074
+ const path = this.withQuery(`${this.accountBase(req.account_id)}/payment-methods`, {
4075
+ billing_mode: req.billing_mode
4076
+ });
4077
+ return this.client.get(path, options);
4078
+ }
4079
+ async attachAccountPaymentMethod(req, body, options) {
4080
+ const path = this.withQuery(`${this.accountBase(req.account_id)}/payment-methods`, {
4081
+ billing_mode: req.billing_mode
4082
+ });
4083
+ return this.client.post(path, body, options);
4084
+ }
4085
+ async detachAccountPaymentMethod(req, paymentMethodId, options) {
4086
+ const path = this.withQuery(
4087
+ `${this.accountBase(req.account_id)}/payment-methods/${encodeURIComponent(paymentMethodId)}`,
4088
+ { billing_mode: req.billing_mode }
4089
+ );
4090
+ return this.client.del(path, options);
4091
+ }
4092
+ async setDefaultAccountPaymentMethod(req, paymentMethodId, options) {
4093
+ const path = this.withQuery(
4094
+ `${this.accountBase(req.account_id)}/payment-methods/${encodeURIComponent(paymentMethodId)}/default`,
4095
+ { billing_mode: req.billing_mode }
4096
+ );
4097
+ return this.client.put(path, void 0, options);
4098
+ }
4099
+ async listAccountInvoices(req, params, options) {
4100
+ const path = this.withQuery(`${this.accountBase(req.account_id)}/invoices`, {
4101
+ billing_mode: req.billing_mode,
4102
+ limit: params?.limit,
4103
+ cursor: params?.cursor
4104
+ });
4105
+ return this.client.get(path, options);
4106
+ }
4107
+ async getAccountInvoice(req, invoiceId, options) {
4108
+ const path = this.withQuery(`${this.accountBase(req.account_id)}/invoices/${encodeURIComponent(invoiceId)}`, {
4109
+ billing_mode: req.billing_mode
4110
+ });
4111
+ return this.client.get(path, options);
4112
+ }
4113
+ async createAccountPortalSession(req, body, options) {
4114
+ const path = this.withQuery(`${this.accountBase(req.account_id)}/portal-session`, {
4115
+ billing_mode: req.billing_mode
4116
+ });
4117
+ return this.client.post(path, body ?? {}, options);
4118
+ }
4119
+ // --------------------------------------------------------------------------
4009
4120
  // Subscriptions
4010
4121
  // --------------------------------------------------------------------------
4011
4122
  async subscribe(data, options) {
@@ -5166,8 +5277,14 @@ var SearchService = class extends ServiceModule {
5166
5277
  };
5167
5278
  var PHOTO_BREAKPOINTS = [36, 150, 320, 640, 1080];
5168
5279
  var PhotoService = class extends ServiceModule {
5169
- constructor() {
5170
- super(...arguments);
5280
+ /**
5281
+ * @param storage Required for {@link uploadViaStorage}. Wired up by the
5282
+ * top-level {@link ScaleMule} constructor — most call sites should not
5283
+ * instantiate `PhotoService` directly.
5284
+ */
5285
+ constructor(client, storage) {
5286
+ super(client);
5287
+ this.storage = storage;
5171
5288
  this.basePath = "/v1/photos";
5172
5289
  }
5173
5290
  async upload(file, uploadOptions, requestOptions) {
@@ -5307,6 +5424,87 @@ var PhotoService = class extends ServiceModule {
5307
5424
  }
5308
5425
  };
5309
5426
  }
5427
+ /**
5428
+ * Upload a file to storage (browser → S3 direct, private, uncompressed) and
5429
+ * register it with the photo service so optimization + transform URLs work.
5430
+ *
5431
+ * The canonical chat-attachment / progressive-image upload primitive. Same
5432
+ * presigned-direct-to-S3 path as `storage.uploadPrivate()`, plus a follow-up
5433
+ * `photo.register()` call so `getTransformUrl()` / `getOptimalUrl()` return
5434
+ * usable URLs.
5435
+ *
5436
+ * The returned `optimized_url_promise` resolves once the photo's
5437
+ * `optimization_status` flips to `completed` (or once a 10s timeout fires;
5438
+ * the caller can still use `getTransformUrl()` directly — the on-demand
5439
+ * transform path produces a variant even before pre-rendered breakpoints
5440
+ * are cached). Phase 3 of ADR-2026-04-26 replaces the poll with a realtime
5441
+ * subscription.
5442
+ *
5443
+ * If `register()` fails after a successful storage upload (e.g. scan times
5444
+ * out), the file is *not* lost: the returned `file_id` is still valid as a
5445
+ * generic storage file. The SDK logs a warning and resolves
5446
+ * `optimized_url_promise` to `null`.
5447
+ */
5448
+ async uploadViaStorage(file, uploadOptions, requestOptions) {
5449
+ const uploadResult = await this.storage.uploadPrivate(file, {
5450
+ filename: uploadOptions?.filename,
5451
+ metadata: uploadOptions?.metadata,
5452
+ onProgress: uploadOptions?.onProgress,
5453
+ signal: uploadOptions?.signal
5454
+ });
5455
+ if (uploadResult.error || !uploadResult.data) {
5456
+ return { data: null, error: uploadResult.error };
5457
+ }
5458
+ const fileInfo = uploadResult.data;
5459
+ const fileId = fileInfo.id;
5460
+ const originalViewUrl = fileInfo.url ?? null;
5461
+ const registerResult = await this.register({ fileId, userId: uploadOptions?.userId }, requestOptions);
5462
+ if (registerResult.error || !registerResult.data) {
5463
+ console.warn(
5464
+ "[scalemule-sdk] photo.register() failed after storage upload; optimized variants unavailable.",
5465
+ registerResult.error
5466
+ );
5467
+ return {
5468
+ data: {
5469
+ file_id: fileId,
5470
+ photo_id: null,
5471
+ original_view_url: originalViewUrl,
5472
+ optimized_url_promise: Promise.resolve(null)
5473
+ },
5474
+ error: null
5475
+ };
5476
+ }
5477
+ const photoId = registerResult.data.id;
5478
+ const optimizedUrlPromise = this.pollOptimizationComplete(photoId, requestOptions);
5479
+ return {
5480
+ data: {
5481
+ file_id: fileId,
5482
+ photo_id: photoId,
5483
+ original_view_url: originalViewUrl,
5484
+ optimized_url_promise: optimizedUrlPromise
5485
+ },
5486
+ error: null
5487
+ };
5488
+ }
5489
+ /**
5490
+ * Poll {@link get} until the photo's `optimization_status` is `completed`
5491
+ * or the 10s timeout fires. Resolves to a usable transform URL on success;
5492
+ * `null` on timeout (caller should fall back to {@link getTransformUrl}
5493
+ * which uses the on-demand transform path).
5494
+ */
5495
+ async pollOptimizationComplete(photoId, requestOptions) {
5496
+ const intervalMs = 250;
5497
+ const maxAttempts = 40;
5498
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
5499
+ const result = await this.get(photoId, requestOptions);
5500
+ const status = result.data?.optimization_status;
5501
+ if (status === "completed") {
5502
+ return this.getOptimalUrl(photoId, 1080);
5503
+ }
5504
+ await new Promise((r) => setTimeout(r, intervalMs));
5505
+ }
5506
+ return null;
5507
+ }
5310
5508
  /** @deprecated Use upload() instead */
5311
5509
  async uploadPhoto(file, options) {
5312
5510
  return this.upload(file, options);
@@ -6138,7 +6336,7 @@ var ScaleMule = class {
6138
6336
  this.events = new EventsService(this._client);
6139
6337
  this.graph = new GraphService(this._client);
6140
6338
  this.functions = new FunctionsService(this._client);
6141
- this.photo = new PhotoService(this._client);
6339
+ this.photo = new PhotoService(this._client, this.storage);
6142
6340
  this.flagContent = new FlagContentService(this._client);
6143
6341
  this.creatorMaker = new CreatorMakerService(this._client);
6144
6342
  this.compliance = new ComplianceService(this._client);
@@ -7223,6 +7421,8 @@ function ScaleMuleProvider({
7223
7421
  client,
7224
7422
  money,
7225
7423
  realtime: baseClient.realtime,
7424
+ storage: baseClient.storage,
7425
+ photo: baseClient.photo,
7226
7426
  user,
7227
7427
  setUser: handleSetUser,
7228
7428
  initializing,
@@ -8535,6 +8735,101 @@ function useContent(options = {}) {
8535
8735
  ]
8536
8736
  );
8537
8737
  }
8738
+ function useMedia() {
8739
+ const { storage, photo } = useScaleMule();
8740
+ const [uploading, setUploading] = useState(false);
8741
+ const [error, setError] = useState(null);
8742
+ const upload = useCallback(
8743
+ async (file, options) => {
8744
+ setUploading(true);
8745
+ setError(null);
8746
+ const isPublic = options?.is_public ?? false;
8747
+ const mimeType = file.type || "application/octet-stream";
8748
+ const sharedOpts = {
8749
+ filename: options?.filename,
8750
+ metadata: options?.metadata,
8751
+ onProgress: options?.onProgress,
8752
+ signal: options?.signal
8753
+ };
8754
+ try {
8755
+ if (mimeType.startsWith("image/") && !options?.skipPhotoRegister && !isPublic) {
8756
+ const r2 = await photo.uploadViaStorage(file, sharedOpts);
8757
+ if (r2.error || !r2.data) {
8758
+ throw r2.error ?? { code: "upload_error", message: "Upload failed", status: 0 };
8759
+ }
8760
+ return {
8761
+ file_id: r2.data.file_id,
8762
+ photo_id: r2.data.photo_id,
8763
+ original_view_url: r2.data.original_view_url,
8764
+ optimized_url_promise: r2.data.optimized_url_promise,
8765
+ hls_url_promise: Promise.resolve(null),
8766
+ mime_type: mimeType,
8767
+ is_public: false
8768
+ };
8769
+ }
8770
+ if (mimeType.startsWith("image/") && isPublic) {
8771
+ const r2 = await storage.upload(file, {
8772
+ ...sharedOpts,
8773
+ isPublic: true,
8774
+ skipCompression: true
8775
+ });
8776
+ if (r2.error || !r2.data) {
8777
+ throw r2.error ?? { code: "upload_error", message: "Upload failed", status: 0 };
8778
+ }
8779
+ const f2 = r2.data;
8780
+ return {
8781
+ file_id: f2.id,
8782
+ photo_id: null,
8783
+ original_view_url: f2.url ?? null,
8784
+ optimized_url_promise: Promise.resolve(null),
8785
+ hls_url_promise: Promise.resolve(null),
8786
+ mime_type: mimeType,
8787
+ is_public: f2.is_public ?? true
8788
+ };
8789
+ }
8790
+ const r = isPublic ? await storage.upload(file, { ...sharedOpts, isPublic: true, skipCompression: true }) : await storage.uploadPrivate(file, sharedOpts);
8791
+ if (r.error || !r.data) {
8792
+ throw r.error ?? { code: "upload_error", message: "Upload failed", status: 0 };
8793
+ }
8794
+ const f = r.data;
8795
+ return {
8796
+ file_id: f.id,
8797
+ photo_id: null,
8798
+ original_view_url: f.url ?? null,
8799
+ optimized_url_promise: Promise.resolve(null),
8800
+ hls_url_promise: Promise.resolve(null),
8801
+ mime_type: mimeType,
8802
+ is_public: f.is_public ?? isPublic
8803
+ };
8804
+ } catch (err) {
8805
+ const e = err;
8806
+ setError(e);
8807
+ throw e;
8808
+ } finally {
8809
+ setUploading(false);
8810
+ }
8811
+ },
8812
+ [storage, photo]
8813
+ );
8814
+ const cancelUpload = useCallback(
8815
+ async (fileId) => {
8816
+ setError(null);
8817
+ try {
8818
+ const r = await storage.delete(fileId);
8819
+ if (r.error) {
8820
+ if (r.error.status === 404) return;
8821
+ throw r.error;
8822
+ }
8823
+ } catch (err) {
8824
+ const e = err;
8825
+ setError(e);
8826
+ throw e;
8827
+ }
8828
+ },
8829
+ [storage]
8830
+ );
8831
+ return { upload, cancelUpload, error, uploading };
8832
+ }
8538
8833
 
8539
8834
  // src/hooks/useMoney.ts
8540
8835
  var useMoney = useMoneyClient;
@@ -9761,6 +10056,10 @@ var STYLES = {
9761
10056
  fontSize: 13,
9762
10057
  color: "#dc2626"
9763
10058
  },
10059
+ note: {
10060
+ fontSize: 13,
10061
+ color: "#475569"
10062
+ },
9764
10063
  success: {
9765
10064
  fontSize: 14,
9766
10065
  fontWeight: 500,
@@ -9770,21 +10069,13 @@ var STYLES = {
9770
10069
  };
9771
10070
  var ALL_TYPES = ["bug_report", "feature_request", "improvement", "other"];
9772
10071
  function FeedbackWidget(props) {
9773
- const {
9774
- position = "bottom-right",
9775
- defaultType = "feature_request",
9776
- allowedTypes = ALL_TYPES,
9777
- triggerLabel = "Feedback",
9778
- theme = "auto",
9779
- className,
9780
- enableRating = true,
9781
- ratingLabel = "How would you rate your experience?",
9782
- onSubmitted
9783
- } = props;
9784
10072
  const sm = useScaleMule();
10073
+ const { client } = sm;
9785
10074
  const { submit } = useFeedback({ enabled: false });
10075
+ const [serverConfig, setServerConfig] = useState(null);
10076
+ const [configLoaded, setConfigLoaded] = useState(false);
9786
10077
  const [open, setOpen] = useState(false);
9787
- const [type, setType] = useState(defaultType);
10078
+ const [type, setType] = useState("feature_request");
9788
10079
  const [title, setTitle] = useState("");
9789
10080
  const [description, setDescription] = useState("");
9790
10081
  const [email, setEmail] = useState("");
@@ -9793,10 +10084,39 @@ function FeedbackWidget(props) {
9793
10084
  const [submitting, setSubmitting] = useState(false);
9794
10085
  const [errMsg, setErrMsg] = useState(null);
9795
10086
  const [done, setDone] = useState(false);
9796
- Boolean(sm.user || sm.user === void 0 ? false : false);
9797
10087
  const signedIn = Boolean(sm.user);
10088
+ const resolvedAllowedTypes = props.allowedTypes ?? serverConfig?.allowed_types ?? ALL_TYPES;
10089
+ const resolvedDefaultType = (props.defaultType && resolvedAllowedTypes.includes(props.defaultType) ? props.defaultType : resolvedAllowedTypes[0]) ?? "feature_request";
10090
+ const resolvedPosition = props.position ?? serverConfig?.widget_position ?? "bottom-right";
10091
+ const resolvedTheme = props.theme ?? serverConfig?.widget_theme ?? "auto";
10092
+ const allowAnonymous = serverConfig?.allow_anonymous ?? true;
10093
+ const anonymousBlocked = !signedIn && !allowAnonymous;
10094
+ useEffect(() => {
10095
+ let active = true;
10096
+ client.get("/v1/feedback/widget-config").then((config) => {
10097
+ if (active) setServerConfig(config);
10098
+ }).catch(() => {
10099
+ if (active) setServerConfig(null);
10100
+ }).finally(() => {
10101
+ if (active) setConfigLoaded(true);
10102
+ });
10103
+ return () => {
10104
+ active = false;
10105
+ };
10106
+ }, [client]);
10107
+ useEffect(() => {
10108
+ setType(
10109
+ (current) => resolvedAllowedTypes.includes(current) ? current : resolvedDefaultType
10110
+ );
10111
+ }, [resolvedAllowedTypes, resolvedDefaultType]);
10112
+ if (!configLoaded) {
10113
+ return null;
10114
+ }
10115
+ if (serverConfig && !serverConfig.enabled) {
10116
+ return null;
10117
+ }
9798
10118
  function reset() {
9799
- setType(defaultType);
10119
+ setType(resolvedDefaultType);
9800
10120
  setTitle("");
9801
10121
  setDescription("");
9802
10122
  setEmail("");
@@ -9808,6 +10128,10 @@ function FeedbackWidget(props) {
9808
10128
  async function handleSubmit(e) {
9809
10129
  e.preventDefault();
9810
10130
  if (!title.trim() || !description.trim()) return;
10131
+ if (anonymousBlocked) {
10132
+ setErrMsg("Please sign in to send feedback for this app");
10133
+ return;
10134
+ }
9811
10135
  if (!signedIn && !email.trim()) {
9812
10136
  setErrMsg("email is required when not signed in");
9813
10137
  return;
@@ -9824,7 +10148,7 @@ function FeedbackWidget(props) {
9824
10148
  tags
9825
10149
  });
9826
10150
  setDone(true);
9827
- onSubmitted?.(item);
10151
+ props.onSubmitted?.(item);
9828
10152
  } catch (err) {
9829
10153
  const message = err && typeof err === "object" && "message" in err ? String(err.message) : "Submit failed";
9830
10154
  setErrMsg(message);
@@ -9836,16 +10160,16 @@ function FeedbackWidget(props) {
9836
10160
  setOpen(false);
9837
10161
  setTimeout(reset, 200);
9838
10162
  }
9839
- const dark = theme === "dark" || theme === "auto" && typeof window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches;
10163
+ const dark = resolvedTheme === "dark" || resolvedTheme === "auto" && typeof window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches;
9840
10164
  return /* @__PURE__ */ jsxs(Fragment, { children: [
9841
10165
  /* @__PURE__ */ jsx(
9842
10166
  "button",
9843
10167
  {
9844
10168
  type: "button",
9845
10169
  onClick: () => setOpen(true),
9846
- className,
9847
- style: { ...STYLES.trigger, ...POSITION_STYLES[position] },
9848
- children: triggerLabel
10170
+ className: props.className,
10171
+ style: { ...STYLES.trigger, ...POSITION_STYLES[resolvedPosition] },
10172
+ children: props.triggerLabel ?? "Feedback"
9849
10173
  }
9850
10174
  ),
9851
10175
  open && /* @__PURE__ */ jsx("div", { role: "dialog", "aria-modal": "true", style: STYLES.overlay, onClick: handleClose, children: /* @__PURE__ */ jsx(
@@ -9880,8 +10204,8 @@ function FeedbackWidget(props) {
9880
10204
  }
9881
10205
  )
9882
10206
  ] }),
9883
- enableRating && /* @__PURE__ */ jsxs("div", { children: [
9884
- /* @__PURE__ */ jsx("div", { style: STYLES.label, children: ratingLabel }),
10207
+ props.enableRating !== false && /* @__PURE__ */ jsxs("div", { children: [
10208
+ /* @__PURE__ */ jsx("div", { style: STYLES.label, children: props.ratingLabel ?? "How would you rate your experience?" }),
9885
10209
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 4, marginTop: 4 }, children: [
9886
10210
  [1, 2, 3, 4, 5].map((n) => {
9887
10211
  const active = (hoverRating ?? rating ?? 0) >= n;
@@ -9935,7 +10259,7 @@ function FeedbackWidget(props) {
9935
10259
  style: STYLES.field,
9936
10260
  value: type,
9937
10261
  onChange: (e) => setType(e.target.value),
9938
- children: allowedTypes.map((t) => /* @__PURE__ */ jsx("option", { value: t, children: TYPE_LABELS[t] }, t))
10262
+ children: resolvedAllowedTypes.map((t) => /* @__PURE__ */ jsx("option", { value: t, children: TYPE_LABELS[t] }, t))
9939
10263
  }
9940
10264
  )
9941
10265
  ] }),
@@ -9967,7 +10291,7 @@ function FeedbackWidget(props) {
9967
10291
  }
9968
10292
  )
9969
10293
  ] }),
9970
- !signedIn && /* @__PURE__ */ jsxs("label", { children: [
10294
+ !signedIn && allowAnonymous && /* @__PURE__ */ jsxs("label", { children: [
9971
10295
  /* @__PURE__ */ jsx("div", { style: STYLES.label, children: "Email" }),
9972
10296
  /* @__PURE__ */ jsx(
9973
10297
  "input",
@@ -9981,10 +10305,19 @@ function FeedbackWidget(props) {
9981
10305
  }
9982
10306
  )
9983
10307
  ] }),
10308
+ anonymousBlocked && /* @__PURE__ */ jsx("div", { style: STYLES.note, children: "This app only accepts feedback from signed-in users." }),
9984
10309
  errMsg && /* @__PURE__ */ jsx("div", { style: STYLES.error, children: errMsg }),
9985
10310
  /* @__PURE__ */ jsxs("div", { style: STYLES.rowEnd, children: [
9986
10311
  /* @__PURE__ */ jsx("button", { type: "button", style: STYLES.secondaryBtn, onClick: handleClose, children: "Cancel" }),
9987
- /* @__PURE__ */ jsx("button", { type: "submit", style: STYLES.primaryBtn, disabled: submitting, children: submitting ? "Sending\u2026" : "Send" })
10312
+ /* @__PURE__ */ jsx(
10313
+ "button",
10314
+ {
10315
+ type: "submit",
10316
+ style: STYLES.primaryBtn,
10317
+ disabled: submitting || anonymousBlocked,
10318
+ children: submitting ? "Sending\u2026" : "Send"
10319
+ }
10320
+ )
9988
10321
  ] })
9989
10322
  ] })
9990
10323
  }
@@ -10283,4 +10616,4 @@ function createSafeLogger(prefix) {
10283
10616
  };
10284
10617
  }
10285
10618
 
10286
- export { FeedbackWidget, ScaleMuleApiError, ScaleMuleClient2 as ScaleMuleClient, ScaleMuleProvider, composePhone, createClient, createSafeLogger, normalizePhone, phoneCountries, sanitizeForLog, useAnalytics, useAuth, useBilling, useContent, useFeatureFlags, useFeedback, useMoney, useMoneyClient, usePushNotifications, useRealtime, useScaleMule, useScaleMuleClient, useShare, useUser, validateForm, validators };
10619
+ export { FeedbackWidget, ScaleMuleApiError, ScaleMuleClient2 as ScaleMuleClient, ScaleMuleProvider, composePhone, createClient, createSafeLogger, normalizePhone, phoneCountries, sanitizeForLog, useAnalytics, useAuth, useBilling, useContent, useFeatureFlags, useFeedback, useMedia, useMoney, useMoneyClient, usePushNotifications, useRealtime, useScaleMule, useScaleMuleClient, useShare, useUser, validateForm, validators };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalemule/nextjs",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "ScaleMule SDK for Next.js applications - authentication, storage, and user management",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -53,7 +53,7 @@
53
53
  },
54
54
  "dependencies": {
55
55
  "@scalemule/money": "^0.0.1",
56
- "@scalemule/sdk": "^0.0.36"
56
+ "@scalemule/sdk": "^0.0.38"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "next": ">=14.0.0",