@scalemule/nextjs 0.1.27 → 0.1.29

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
@@ -2093,6 +2093,24 @@ var DEFAULT_STALL_TIMEOUT_MS = 45e3;
2093
2093
  var SLOW_NETWORK_STALL_TIMEOUT_MS = 9e4;
2094
2094
  var MULTIPART_THRESHOLD = 8 * 1024 * 1024;
2095
2095
  var MULTIPART_THRESHOLD_SLOW = 4 * 1024 * 1024;
2096
+ function buildVisibilityWire(options) {
2097
+ if (!options) return {};
2098
+ if (options.visibility) {
2099
+ return {
2100
+ visibility: options.visibility,
2101
+ // Legacy field for old storage instances that haven't deployed
2102
+ // the tristate handler yet — omit `is_public` for the
2103
+ // `anonymous_visible` case so an old server that doesn't know
2104
+ // the new value at least falls through to its own default
2105
+ // rather than mis-treating it as `app_public`.
2106
+ ...options.visibility === "private" ? { is_public: false } : options.visibility === "app_public" ? { is_public: true } : {}
2107
+ };
2108
+ }
2109
+ if (typeof options.isPublic === "boolean") {
2110
+ return { is_public: options.isPublic };
2111
+ }
2112
+ return {};
2113
+ }
2096
2114
  var StorageService = class extends ServiceModule {
2097
2115
  constructor() {
2098
2116
  super(...arguments);
@@ -2174,6 +2192,42 @@ var StorageService = class extends ServiceModule {
2174
2192
  }
2175
2193
  return result;
2176
2194
  }
2195
+ /**
2196
+ * Upload a file as world-readable on the anonymous public CDN.
2197
+ *
2198
+ * Same browser→S3 pipeline as {@link upload}, but pins
2199
+ * `visibility: 'anonymous_visible'` — caller cannot opt out.
2200
+ *
2201
+ * Use this for media meant to render on logged-out marketing
2202
+ * pages, blog posts, embed snippets — anywhere a customer needs
2203
+ * to drop a URL into `<img src>` without an authenticated
2204
+ * session. For everything else (chat attachments, private
2205
+ * uploads, app-internal galleries) prefer {@link upload} or
2206
+ * {@link uploadPrivate}.
2207
+ *
2208
+ * **`cdn_url` may be `null` on the immediate response.** Storage
2209
+ * intentionally withholds the public CDN URL until the AV scan
2210
+ * has flipped to `clean` — exposing the URL pre-scan would let a
2211
+ * customer embed the bytes in a logged-out page before the
2212
+ * platform has verified them. The upload itself succeeds
2213
+ * regardless; consumers should check `result.data.cdn_url` and:
2214
+ * - if non-null → it's safe to publish
2215
+ * - if null → poll {@link getFileStatus} on a small backoff
2216
+ * until `urls.cdn_url` populates (or `scan.status` flips to
2217
+ * `threat` / `quarantined` / `error` — terminal failure)
2218
+ *
2219
+ * Requires the operator to have provisioned the anonymous
2220
+ * delivery bucket + CDN. When they haven't, the storage service
2221
+ * returns 503 `ANONYMOUS_DELIVERY_NOT_CONFIGURED` (an error
2222
+ * propagated through this helper) — it never silently demotes
2223
+ * to `app_public`.
2224
+ */
2225
+ async uploadAnonymous(file, options) {
2226
+ return this.upload(file, {
2227
+ ...options,
2228
+ visibility: "anonymous_visible"
2229
+ });
2230
+ }
2177
2231
  // --------------------------------------------------------------------------
2178
2232
  // Direct Upload (3-step with retry + stall)
2179
2233
  // --------------------------------------------------------------------------
@@ -2187,7 +2241,7 @@ var StorageService = class extends ServiceModule {
2187
2241
  filename,
2188
2242
  content_type: file.type || "application/octet-stream",
2189
2243
  size_bytes: file.size,
2190
- is_public: options?.isPublic ?? true,
2244
+ ...buildVisibilityWire(options),
2191
2245
  metadata: options?.metadata
2192
2246
  },
2193
2247
  requestOpts
@@ -2278,6 +2332,13 @@ var StorageService = class extends ServiceModule {
2278
2332
  content_type: d.content_type,
2279
2333
  size_bytes: d.size_bytes,
2280
2334
  url: d.url,
2335
+ // Surface visibility + cdn_url all the way out so callers
2336
+ // can branch on visibility without a follow-up getInfo()
2337
+ // round-trip. Anonymous-visible callers especially need
2338
+ // `cdn_url` here to render the image immediately.
2339
+ visibility: d.visibility,
2340
+ cdn_url: d.cdn_url,
2341
+ is_public: d.visibility ? d.visibility !== "private" : void 0,
2281
2342
  created_at: (/* @__PURE__ */ new Date()).toISOString()
2282
2343
  },
2283
2344
  error: null
@@ -2359,7 +2420,7 @@ var StorageService = class extends ServiceModule {
2359
2420
  filename,
2360
2421
  content_type: file.type || "application/octet-stream",
2361
2422
  size_bytes: file.size,
2362
- is_public: options?.isPublic ?? true,
2423
+ ...buildVisibilityWire(options),
2363
2424
  metadata: options?.metadata,
2364
2425
  chunk_size: options?.chunkSize,
2365
2426
  ...clientUploadKey ? { client_upload_key: clientUploadKey } : {}
@@ -2637,6 +2698,9 @@ var StorageService = class extends ServiceModule {
2637
2698
  content_type: d.content_type,
2638
2699
  size_bytes: d.size_bytes,
2639
2700
  url: d.url,
2701
+ visibility: d.visibility,
2702
+ cdn_url: d.cdn_url,
2703
+ is_public: d.visibility ? d.visibility !== "private" : void 0,
2640
2704
  created_at: (/* @__PURE__ */ new Date()).toISOString()
2641
2705
  },
2642
2706
  error: null
@@ -2674,7 +2738,7 @@ var StorageService = class extends ServiceModule {
2674
2738
  {
2675
2739
  filename,
2676
2740
  content_type: contentType,
2677
- is_public: options?.isPublic ?? true,
2741
+ ...buildVisibilityWire(options),
2678
2742
  expires_in: options?.expiresIn ?? 3600,
2679
2743
  size_bytes: options?.sizeBytes,
2680
2744
  metadata: options?.metadata
@@ -2721,7 +2785,16 @@ var StorageService = class extends ServiceModule {
2721
2785
  // --------------------------------------------------------------------------
2722
2786
  // Multipart Public API (for advanced/server-side usage)
2723
2787
  // --------------------------------------------------------------------------
2724
- /** Start a multipart upload session. */
2788
+ /**
2789
+ * Start a multipart upload session.
2790
+ *
2791
+ * Accepts both the typed `visibility` field (preferred) and the
2792
+ * legacy `is_public` boolean for back-compat. If both are sent the
2793
+ * storage service uses `visibility`. `'anonymous_visible'` requires
2794
+ * the operator to have provisioned the anonymous bucket — the
2795
+ * service returns 503 `ANONYMOUS_DELIVERY_NOT_CONFIGURED`
2796
+ * otherwise (the SDK never silently demotes).
2797
+ */
2725
2798
  async startMultipartUpload(params, requestOptions) {
2726
2799
  return this.post("/signed-url/multipart/start", params, requestOptions);
2727
2800
  }
@@ -2852,14 +2925,26 @@ var StorageService = class extends ServiceModule {
2852
2925
  return this._get(`/files/${fileId}/view-status`, options);
2853
2926
  }
2854
2927
  /**
2855
- * Update a file's visibility (public/private).
2856
- * Only the file owner can toggle this. Changes URL TTL — does not move the S3 object.
2857
- * Public files get 7-day signed URLs; private files get 1-hour signed URLs.
2928
+ * Update a file's visibility.
2929
+ *
2930
+ * Accepts either:
2931
+ * - a tri-state {@link Visibility} string (preferred), or
2932
+ * - a legacy boolean (`true` → `app_public`, `false` → `private`).
2933
+ *
2934
+ * Only the file owner / app member can toggle this. Same-bucket
2935
+ * transitions (`private` ↔ `app_public`) succeed and the service
2936
+ * keeps the legacy `is_public` column in sync via a DB trigger.
2937
+ * Cross-bucket transitions (any flip into or out of
2938
+ * `'anonymous_visible'`) currently return 409
2939
+ * `VISIBILITY_TRANSITION_UNSUPPORTED` — the bytes would need to
2940
+ * move between S3 buckets and that orchestration is not yet
2941
+ * shipped. Re-upload the file with the desired visibility instead.
2858
2942
  */
2859
- async updateVisibility(fileId, isPublic, options) {
2943
+ async updateVisibility(fileId, visibility, options) {
2944
+ const body = typeof visibility === "boolean" ? { is_public: visibility } : { visibility };
2860
2945
  return this.patch(
2861
2946
  `/files/${fileId}/visibility`,
2862
- { is_public: isPublic },
2947
+ body,
2863
2948
  options
2864
2949
  );
2865
2950
  }
@@ -2957,6 +3042,7 @@ var StorageService = class extends ServiceModule {
2957
3042
  clearTimeout(timer);
2958
3043
  parentSignalCleanup?.();
2959
3044
  if (!response.ok) {
3045
+ const s3ErrorBody = await response.text().catch(() => "");
2960
3046
  return {
2961
3047
  data: null,
2962
3048
  error: {
@@ -2966,7 +3052,9 @@ var StorageService = class extends ServiceModule {
2966
3052
  details: {
2967
3053
  transport: "fetch",
2968
3054
  total_bytes: file.size,
2969
- online: getOnlineStatus()
3055
+ online: getOnlineStatus(),
3056
+ s3_error_body: s3ErrorBody.slice(0, 1024),
3057
+ s3_error_code: extractS3ErrorCode(s3ErrorBody)
2970
3058
  }
2971
3059
  }
2972
3060
  };
@@ -3057,6 +3145,7 @@ var StorageService = class extends ServiceModule {
3057
3145
  onProgress?.(100);
3058
3146
  resolve({ data: null, error: null });
3059
3147
  } else {
3148
+ const body = (xhr.responseText || "").slice(0, 1024);
3060
3149
  resolve({
3061
3150
  data: null,
3062
3151
  error: {
@@ -3068,7 +3157,9 @@ var StorageService = class extends ServiceModule {
3068
3157
  bytes_sent: lastLoaded,
3069
3158
  total_bytes: totalBytes,
3070
3159
  progress_percent: totalBytes > 0 ? Math.round(lastLoaded / totalBytes * 100) : void 0,
3071
- online: getOnlineStatus()
3160
+ online: getOnlineStatus(),
3161
+ s3_error_body: body,
3162
+ s3_error_code: extractS3ErrorCode(body)
3072
3163
  }
3073
3164
  }
3074
3165
  });
@@ -3147,12 +3238,18 @@ var StorageService = class extends ServiceModule {
3147
3238
  }
3148
3239
  return { etag };
3149
3240
  }
3241
+ const body = await response.text().catch(() => "");
3242
+ const s3ErrorCode = extractS3ErrorCode(body);
3150
3243
  if (NON_RETRYABLE_STATUS_CODES.has(response.status)) {
3151
- return { error: `Part upload failed: ${response.status}`, status: response.status, code: "s3_error" };
3244
+ return {
3245
+ error: `Part upload failed: ${response.status}${s3ErrorCode ? ` (${s3ErrorCode})` : ""}`,
3246
+ status: response.status,
3247
+ code: response.status === 403 ? "s3_signature_error" : "s3_error"
3248
+ };
3152
3249
  }
3153
3250
  if (attempt === RETRY_DELAYS.length - 1) {
3154
3251
  return {
3155
- error: `Part upload failed after retries: ${response.status}`,
3252
+ error: `Part upload failed after retries: ${response.status}${s3ErrorCode ? ` (${s3ErrorCode})` : ""}`,
3156
3253
  status: response.status,
3157
3254
  code: "s3_error"
3158
3255
  };
@@ -3278,6 +3375,11 @@ function getUploadEnvironmentDiagnostics() {
3278
3375
  function asNumber2(value) {
3279
3376
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
3280
3377
  }
3378
+ function extractS3ErrorCode(body) {
3379
+ if (!body) return void 0;
3380
+ const m = body.match(/<Code>([^<]+)<\/Code>/);
3381
+ return m ? m[1] : void 0;
3382
+ }
3281
3383
  var DEFAULT_RECONNECT_BASE_MS = 1e3;
3282
3384
  var MAX_RECONNECT_MS = 3e4;
3283
3385
  var HEARTBEAT_INTERVAL_MS = 3e4;
@@ -4191,6 +4293,56 @@ var SocialService = class extends ServiceModule {
4191
4293
  return this._list(`/${targetType}/${targetId}/likes`, params, requestOptions);
4192
4294
  }
4193
4295
  // --------------------------------------------------------------------------
4296
+ // Voting (bipolar — up/down with score)
4297
+ // --------------------------------------------------------------------------
4298
+ /**
4299
+ * Cast, change, or clear the caller's vote on a target.
4300
+ *
4301
+ * `value`: 1 = upvote, -1 = downvote, 0 = clear.
4302
+ *
4303
+ * Idempotent: re-casting the same value is a no-op; clearing when no
4304
+ * vote exists is a no-op. Server validates `target_type` matches
4305
+ * `[a-z0-9_]{1,64}`. Convention: prefix per app (`weekmob_post`,
4306
+ * `gistyo_gist`).
4307
+ *
4308
+ * Requires an authenticated session; anonymous calls return 401.
4309
+ */
4310
+ async vote(targetType, targetId, value, options) {
4311
+ return this.put(`/${targetType}/${targetId}/vote`, { value }, options);
4312
+ }
4313
+ /**
4314
+ * Read the caller's current vote on a target plus the aggregate counts.
4315
+ * `value` is 0 when the caller has no vote.
4316
+ *
4317
+ * Requires an authenticated session.
4318
+ */
4319
+ async getVote(targetType, targetId, options) {
4320
+ return this._get(`/${targetType}/${targetId}/vote`, options);
4321
+ }
4322
+ // --------------------------------------------------------------------------
4323
+ // View counters
4324
+ // --------------------------------------------------------------------------
4325
+ /**
4326
+ * Record a view of a target. Increments the total counter
4327
+ * unconditionally; increments the unique-viewer counter once per
4328
+ * (viewer, UTC day).
4329
+ *
4330
+ * Authenticated callers don't need to pass `viewerFingerprint` — the
4331
+ * user_id is used. Anonymous callers may pass a 64-hex-char
4332
+ * fingerprint hash to participate in unique-viewer dedupe; without
4333
+ * one, only the total counter moves.
4334
+ */
4335
+ async recordView(targetType, targetId, viewerFingerprint, options) {
4336
+ const body = viewerFingerprint ? { viewer_fingerprint: viewerFingerprint } : {};
4337
+ return this.post(`/${targetType}/${targetId}/views`, body, options);
4338
+ }
4339
+ /**
4340
+ * Read view counters for a target.
4341
+ */
4342
+ async getViews(targetType, targetId, options) {
4343
+ return this._get(`/${targetType}/${targetId}/views`, options);
4344
+ }
4345
+ // --------------------------------------------------------------------------
4194
4346
  // Comments
4195
4347
  // --------------------------------------------------------------------------
4196
4348
  async comment(postId, data, options) {
@@ -5551,6 +5703,41 @@ var PhotoService = class extends ServiceModule {
5551
5703
  const qs = params.toString();
5552
5704
  return `${this.client.getBaseUrl()}${this.basePath}/${photoId}/transform${qs ? `?${qs}` : ""}`;
5553
5705
  }
5706
+ /**
5707
+ * Build an absolute URL for the **public** transform endpoint —
5708
+ * the unauthenticated path that serves transformed bytes for
5709
+ * `<img src>` use on logged-out marketing pages, blogs, embeds.
5710
+ *
5711
+ * The photo service refuses any photo that isn't:
5712
+ * - `visibility = 'anonymous_visible'` (the customer explicitly
5713
+ * opted into world-readable for this file at upload time)
5714
+ * - `scan_status = 'clean'` (no exception for pending/scanning)
5715
+ *
5716
+ * Anything else returns 404 — the endpoint refuses to disambiguate
5717
+ * "not found" from "not anonymous" so it can't be probed to
5718
+ * discover existence or visibility of private photos.
5719
+ *
5720
+ * Pair with {@link getTransformUrl} for the authenticated case:
5721
+ * customer apps that already have a session should keep using the
5722
+ * authed transform URL (which also handles `anonymous_visible`
5723
+ * photos transparently) — this URL is for cross-origin embedding.
5724
+ *
5725
+ * @example
5726
+ * ```tsx
5727
+ * // photo was uploaded with visibility: 'anonymous_visible'
5728
+ * <img src={sm.photo.getPublicTransformUrl(photoId, { width: 800 })} />
5729
+ * ```
5730
+ */
5731
+ getPublicTransformUrl(photoId, options) {
5732
+ const params = new URLSearchParams();
5733
+ if (options?.width) params.set("width", String(options.width));
5734
+ if (options?.height) params.set("height", String(options.height));
5735
+ if (options?.fit) params.set("fit", options.fit);
5736
+ if (options?.format) params.set("format", options.format);
5737
+ if (options?.quality) params.set("quality", String(options.quality));
5738
+ const qs = params.toString();
5739
+ return `${this.client.getBaseUrl()}${this.basePath}/public/${photoId}/transform${qs ? `?${qs}` : ""}`;
5740
+ }
5554
5741
  /**
5555
5742
  * Get a transform URL optimized for the given display area.
5556
5743
  *
@@ -5823,6 +6010,7 @@ var TtsService = class extends ServiceModule {
5823
6010
  "/synthesize",
5824
6011
  {
5825
6012
  text: params.text,
6013
+ speech_profile: params.speechProfile,
5826
6014
  voice: params.voice,
5827
6015
  model: params.model,
5828
6016
  provider: params.provider,
@@ -7956,6 +8144,7 @@ function ScaleMuleProvider({
7956
8144
  photo: baseClient.photo,
7957
8145
  video: baseClient.video,
7958
8146
  audio: baseClient.audio,
8147
+ media: baseClient.media,
7959
8148
  tts: baseClient.tts,
7960
8149
  mediaPolicy: effectiveMediaPolicy,
7961
8150
  user,
@@ -9517,148 +9706,43 @@ function useTtsJob(jobId, options) {
9517
9706
  return { job, loading, error, refresh };
9518
9707
  }
9519
9708
  function useMedia() {
9520
- const { storage, photo, video, audio, mediaPolicy: providerDefaultPolicy } = useScaleMule();
9709
+ const { media: rawMedia, mediaPolicy: providerDefaultPolicy } = useScaleMule();
9710
+ const media = rawMedia;
9521
9711
  const [uploading, setUploading] = useState(false);
9522
9712
  const [error, setError] = useState(null);
9523
9713
  const upload = useCallback(
9524
9714
  async (file, options) => {
9525
9715
  setUploading(true);
9526
9716
  setError(null);
9527
- const visibility = options?.visibility ?? (options?.is_public === true ? "app_public" : options?.is_public === false ? "private" : "private");
9528
- const isPublic = visibility !== "private";
9529
- const isAnonymous = visibility === "anonymous_visible";
9530
- const mimeType = file.type || "application/octet-stream";
9531
9717
  const policy = options?.policy ?? providerDefaultPolicy ?? "safe_visible";
9532
- const gateOnPipeline = policy === "safe_public" || policy === "moderated" || policy === "compliance";
9533
- const sharedOpts = {
9534
- filename: options?.filename,
9535
- metadata: options?.metadata,
9536
- onProgress: options?.onProgress,
9537
- signal: options?.signal
9538
- };
9539
- const storageAnon = storage;
9540
9718
  try {
9541
- if (isAnonymous) {
9542
- const r2 = await storageAnon.uploadAnonymous(file, sharedOpts);
9543
- if (r2.error || !r2.data) {
9544
- throw r2.error ?? { code: "upload_error", message: "Upload failed", status: 0 };
9545
- }
9546
- const f2 = r2.data;
9547
- return {
9548
- file_id: f2.id,
9549
- photo_id: null,
9550
- // For anon files, original_view_url is the same unsigned
9551
- // CDN URL — no signed alternative exists. Callers should
9552
- // prefer `cdn_url` (more specific) but `original_view_url`
9553
- // works too for back-compat with code that already reads
9554
- // that field.
9555
- original_view_url: f2.cdn_url ?? null,
9556
- optimized_url_promise: Promise.resolve(null),
9557
- hls_url_promise: Promise.resolve(null),
9558
- mime_type: mimeType,
9559
- is_public: true,
9560
- visibility: "anonymous_visible",
9561
- cdn_url: f2.cdn_url ?? null
9562
- };
9563
- }
9564
- if (mimeType.startsWith("image/") && !options?.skipPhotoRegister && !isPublic) {
9565
- const r2 = await photo.uploadViaStorage(file, sharedOpts);
9566
- if (r2.error || !r2.data) {
9567
- throw r2.error ?? { code: "upload_error", message: "Upload failed", status: 0 };
9568
- }
9569
- if (gateOnPipeline) {
9570
- await r2.data.optimized_url_promise;
9571
- }
9572
- return {
9573
- file_id: r2.data.file_id,
9574
- photo_id: r2.data.photo_id,
9575
- original_view_url: r2.data.original_view_url,
9576
- optimized_url_promise: r2.data.optimized_url_promise,
9577
- hls_url_promise: Promise.resolve(null),
9578
- mime_type: mimeType,
9579
- is_public: false,
9580
- visibility: "private",
9581
- cdn_url: null
9582
- };
9583
- }
9584
- if (mimeType.startsWith("video/") && !isPublic) {
9585
- const r2 = await video.uploadViaStorage(file, sharedOpts);
9586
- if (r2.error || !r2.data) {
9587
- throw r2.error ?? { code: "upload_error", message: "Upload failed", status: 0 };
9588
- }
9589
- if (gateOnPipeline) {
9590
- await r2.data.hls_url_promise;
9591
- }
9592
- return {
9593
- file_id: r2.data.file_id,
9594
- photo_id: null,
9595
- original_view_url: r2.data.original_view_url,
9596
- optimized_url_promise: Promise.resolve(null),
9597
- hls_url_promise: r2.data.hls_url_promise,
9598
- mime_type: mimeType,
9599
- is_public: false,
9600
- visibility: "private",
9601
- cdn_url: null
9602
- };
9603
- }
9604
- if (mimeType.startsWith("audio/") && !isPublic) {
9605
- const r2 = await audio.uploadViaStorage(file, sharedOpts);
9606
- if (r2.error || !r2.data) {
9607
- throw r2.error ?? { code: "upload_error", message: "Upload failed", status: 0 };
9608
- }
9609
- return {
9610
- file_id: r2.data.file_id,
9611
- photo_id: null,
9612
- original_view_url: r2.data.original_view_url,
9613
- optimized_url_promise: Promise.resolve(null),
9614
- hls_url_promise: Promise.resolve(null),
9615
- mime_type: mimeType,
9616
- is_public: false,
9617
- visibility: "private",
9618
- cdn_url: null
9619
- };
9620
- }
9621
- if (mimeType.startsWith("image/") && isPublic) {
9622
- const r2 = await storage.upload(file, {
9623
- ...sharedOpts,
9624
- visibility: "app_public",
9625
- skipCompression: true
9626
- });
9627
- if (r2.error || !r2.data) {
9628
- throw r2.error ?? { code: "upload_error", message: "Upload failed", status: 0 };
9719
+ const result = await media.upload(file, {
9720
+ visibility: options?.visibility,
9721
+ isPublic: options?.is_public,
9722
+ policy,
9723
+ filename: options?.filename,
9724
+ metadata: options?.metadata,
9725
+ signal: options?.signal,
9726
+ skipPhotoRegister: options?.skipPhotoRegister,
9727
+ onProgress: (event) => {
9728
+ if (typeof event.progress === "number") {
9729
+ options?.onProgress?.(event.progress);
9730
+ }
9629
9731
  }
9630
- const f2 = r2.data;
9631
- return {
9632
- file_id: f2.id,
9633
- photo_id: null,
9634
- original_view_url: f2.url ?? null,
9635
- optimized_url_promise: Promise.resolve(null),
9636
- hls_url_promise: Promise.resolve(null),
9637
- mime_type: mimeType,
9638
- is_public: f2.is_public ?? true,
9639
- visibility: f2.visibility ?? "app_public",
9640
- cdn_url: f2.cdn_url ?? null
9641
- };
9642
- }
9643
- const r = isPublic ? await storage.upload(file, {
9644
- ...sharedOpts,
9645
- visibility: "app_public",
9646
- skipCompression: true
9647
- }) : await storage.uploadPrivate(file, sharedOpts);
9648
- if (r.error || !r.data) {
9649
- throw r.error ?? { code: "upload_error", message: "Upload failed", status: 0 };
9732
+ });
9733
+ if (result.error || !result.data) {
9734
+ throw result.error ?? { code: "upload_error", message: "Upload failed", status: 0 };
9650
9735
  }
9651
- const f = r.data;
9652
9736
  return {
9653
- file_id: f.id,
9654
- photo_id: null,
9655
- original_view_url: f.url ?? null,
9656
- optimized_url_promise: Promise.resolve(null),
9657
- hls_url_promise: Promise.resolve(null),
9658
- mime_type: mimeType,
9659
- is_public: f.is_public ?? isPublic,
9660
- visibility: f.visibility ?? (isPublic ? "app_public" : "private"),
9661
- cdn_url: f.cdn_url ?? null
9737
+ file_id: result.data.file_id,
9738
+ photo_id: result.data.photo_id,
9739
+ original_view_url: result.data.original_view_url,
9740
+ optimized_url_promise: result.data.optimized_url_promise,
9741
+ hls_url_promise: result.data.hls_url_promise,
9742
+ mime_type: result.data.mime_type,
9743
+ is_public: result.data.is_public,
9744
+ visibility: result.data.visibility,
9745
+ cdn_url: result.data.cdn_url
9662
9746
  };
9663
9747
  } catch (err) {
9664
9748
  const e = err;
@@ -9668,19 +9752,16 @@ function useMedia() {
9668
9752
  setUploading(false);
9669
9753
  }
9670
9754
  },
9671
- [storage, photo, video, audio, providerDefaultPolicy]
9755
+ [media, providerDefaultPolicy]
9672
9756
  );
9673
9757
  const cancelUpload = useCallback(
9674
9758
  async (fileId) => {
9675
9759
  setError(null);
9676
9760
  try {
9677
- const [, storageResult] = await Promise.all([
9678
- photo.delete(fileId).catch(() => void 0),
9679
- storage.delete(fileId)
9680
- ]);
9681
- if (storageResult.error) {
9682
- if (storageResult.error.status === 404) return;
9683
- throw storageResult.error;
9761
+ const result = await media.delete(fileId);
9762
+ if (result.error) {
9763
+ if (result.error.status === 404) return;
9764
+ throw result.error;
9684
9765
  }
9685
9766
  } catch (err) {
9686
9767
  const e = err;
@@ -9688,7 +9769,7 @@ function useMedia() {
9688
9769
  throw e;
9689
9770
  }
9690
9771
  },
9691
- [storage, photo]
9772
+ [media]
9692
9773
  );
9693
9774
  return { upload, cancelUpload, error, uploading };
9694
9775
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalemule/nextjs",
3
- "version": "0.1.27",
3
+ "version": "0.1.29",
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",
@@ -60,7 +60,7 @@
60
60
  "dependencies": {
61
61
  "@scalemule/ledvery": "^0.0.1",
62
62
  "@scalemule/money": "^0.0.1",
63
- "@scalemule/sdk": "^0.0.55"
63
+ "@scalemule/sdk": "^0.0.58"
64
64
  },
65
65
  "peerDependencies": {
66
66
  "next": ">=14.0.0",