@scalemule/nextjs 0.1.26 → 0.1.27

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.d.mts CHANGED
@@ -9,6 +9,19 @@ export { ClientConfig, RequestOptions, createClient } from './client.mjs';
9
9
  import { S as ScaleMuleConfig, U as User, L as LoginResponse, A as ApiError$1, a as UseAuthReturn, b as UseBillingReturn, c as ListFilesParams, d as UseContentReturn, e as UseUserReturn, f as UseAnalyticsOptions, g as UseAnalyticsReturn } from './index-zQloSkpW.mjs';
10
10
  export { h as AccountBalance, i as AnalyticsEvent, j as ApiResponse, B as BatchTrackRequest, k as BillingPayment, l as BillingPayout, m as BillingRefund, n as BillingTransaction, C as ChangeEmailRequest, o as ChangePasswordRequest, p as ClientContext, q as ConnectedAccount, D as DeviceFingerprint, r as DeviceInfo, E as EnhancedAnalyticsEvent, F as ForgotPasswordRequest, K as KnownAccountInfo, s as LinkedAccount, t as ListFilesResponse, u as LoginDeviceInfo, v as LoginRequest, w as LoginResponseWithMFA, x as LoginRiskInfo, M as MFAChallengeResponse, y as MFAMethod, z as MFASMSSetupResponse, G as MFASetupRequest, H as MFAStatus, I as MFATOTPSetupResponse, J as MFAVerifyRequest, O as OAuthCallbackRequest, N as OAuthCallbackResponse, P as OAuthConfig, Q as OAuthProvider, R as OAuthStartResponse, T as PageViewData, V as PayoutSchedule, W as PhoneLoginRequest, X as PhoneSendCodeRequest, Y as PhoneVerifyRequest, Z as Profile, _ as RefreshResponse, $ as RegisterRequest, a0 as ResetPasswordRequest, a1 as ScaleMuleApiError, a2 as ScaleMuleEnvironment, a3 as Session, a4 as SignedUploadCompleteRequest, a5 as SignedUploadRequest, a6 as SignedUploadResponse, a7 as SignedUploadUrl, a8 as StorageAdapter, a9 as StorageFile, aa as TrackEventResponse, ab as TransactionSummary, ac as UTMParams, ad as UpdateProfileRequest, ae as UploadOptions, af as UploadResponse, ag as VerifyEmailRequest } from './index-zQloSkpW.mjs';
11
11
 
12
+ /**
13
+ * Tri-state file visibility — `'private' | 'app_public' | 'anonymous_visible'`.
14
+ *
15
+ * Locally redeclared here rather than imported from `@scalemule/sdk`
16
+ * so this package can ship before the sdk's `Visibility` export is
17
+ * published. Once `@scalemule/sdk` ≥ the version that exports
18
+ * `Visibility` is the minimum dep version, swap this for an `import
19
+ * type { Visibility } from '@scalemule/sdk'`.
20
+ *
21
+ * Keep the union members in lockstep with the storage migration's
22
+ * ENUM and the SDK's typed export.
23
+ */
24
+ type Visibility = 'private' | 'app_public' | 'anonymous_visible';
12
25
  /**
13
26
  * Result of a single {@link useMedia} upload call.
14
27
  *
@@ -33,8 +46,24 @@ interface MediaUploadResult {
33
46
  hls_url_promise: Promise<string | null>;
34
47
  /** The file's MIME type — preserved from the input File / Blob. */
35
48
  mime_type: string;
36
- /** Whether the resulting storage object is public-readable. */
49
+ /** Whether the resulting storage object is public-readable.
50
+ * Derived from `visibility !== 'private'` for back-compat with
51
+ * callers that still branch on the boolean. */
37
52
  is_public: boolean;
53
+ /**
54
+ * Resolved tri-state visibility from the storage service. Use this
55
+ * over `is_public` for new code — `is_public: true` covers both
56
+ * `'app_public'` (auth-gated CDN) and `'anonymous_visible'`
57
+ * (unsigned public CDN), which have different delivery contracts.
58
+ */
59
+ visibility: Visibility;
60
+ /**
61
+ * Stable unsigned public-CDN URL — populated only when
62
+ * `visibility === 'anonymous_visible'`. Drop directly into
63
+ * `<img src>` on logged-out pages. For other visibilities use
64
+ * `original_view_url` (signed) or the photo transform URL.
65
+ */
66
+ cdn_url: string | null;
38
67
  }
39
68
  /**
40
69
  * Per-app media-pipeline policy. Drives release-gating + processing
@@ -55,10 +84,34 @@ interface MediaUploadResult {
55
84
  */
56
85
  type MediaPolicy = 'fast_trusted' | 'safe_visible' | 'safe_public' | 'moderated' | 'compliance';
57
86
  interface UseMediaUploadOptions {
58
- /** Whether the resulting storage object should be public-readable.
87
+ /**
88
+ * Tri-state visibility (preferred — see {@link Visibility} in
89
+ * `@scalemule/sdk`). Three modes:
90
+ * - `'private'` — owner + app members only (DEFAULT)
91
+ * - `'app_public'` — readable by any authenticated end-user
92
+ * in the same application; same semantics
93
+ * the legacy `is_public: true` flag has
94
+ * always had
95
+ * - `'anonymous_visible'` — world-readable on the unsigned public
96
+ * CDN. Returned `cdn_url` is safe to drop
97
+ * into `<img src>` on a logged-out page.
98
+ *
99
+ * If both `visibility` and `is_public` are passed, `visibility`
100
+ * wins. `'anonymous_visible'` requires the operator to have
101
+ * provisioned the anonymous-delivery bucket — the storage service
102
+ * surfaces 503 `ANONYMOUS_DELIVERY_NOT_CONFIGURED` otherwise (the
103
+ * SDK never silently demotes).
104
+ */
105
+ visibility?: Visibility;
106
+ /**
107
+ * Legacy two-state visibility flag. `true` → `app_public`; `false`
108
+ * → `private`. Prefer the typed `visibility` field for new code —
109
+ * `is_public: true` cannot express `anonymous_visible`.
110
+ *
59
111
  * Default: `false` (private). Public is opt-in for surfaces that
60
112
  * genuinely need it (avatars, public listings). Chat / DM uploads
61
- * should always be private. */
113
+ * should always be private.
114
+ */
62
115
  is_public?: boolean;
63
116
  /**
64
117
  * Per-call media-policy override. Defaults to `safe_visible` (visible
package/dist/index.d.ts CHANGED
@@ -9,6 +9,19 @@ export { ClientConfig, RequestOptions, createClient } from './client.js';
9
9
  import { S as ScaleMuleConfig, U as User, L as LoginResponse, A as ApiError$1, a as UseAuthReturn, b as UseBillingReturn, c as ListFilesParams, d as UseContentReturn, e as UseUserReturn, f as UseAnalyticsOptions, g as UseAnalyticsReturn } from './index-zQloSkpW.js';
10
10
  export { h as AccountBalance, i as AnalyticsEvent, j as ApiResponse, B as BatchTrackRequest, k as BillingPayment, l as BillingPayout, m as BillingRefund, n as BillingTransaction, C as ChangeEmailRequest, o as ChangePasswordRequest, p as ClientContext, q as ConnectedAccount, D as DeviceFingerprint, r as DeviceInfo, E as EnhancedAnalyticsEvent, F as ForgotPasswordRequest, K as KnownAccountInfo, s as LinkedAccount, t as ListFilesResponse, u as LoginDeviceInfo, v as LoginRequest, w as LoginResponseWithMFA, x as LoginRiskInfo, M as MFAChallengeResponse, y as MFAMethod, z as MFASMSSetupResponse, G as MFASetupRequest, H as MFAStatus, I as MFATOTPSetupResponse, J as MFAVerifyRequest, O as OAuthCallbackRequest, N as OAuthCallbackResponse, P as OAuthConfig, Q as OAuthProvider, R as OAuthStartResponse, T as PageViewData, V as PayoutSchedule, W as PhoneLoginRequest, X as PhoneSendCodeRequest, Y as PhoneVerifyRequest, Z as Profile, _ as RefreshResponse, $ as RegisterRequest, a0 as ResetPasswordRequest, a1 as ScaleMuleApiError, a2 as ScaleMuleEnvironment, a3 as Session, a4 as SignedUploadCompleteRequest, a5 as SignedUploadRequest, a6 as SignedUploadResponse, a7 as SignedUploadUrl, a8 as StorageAdapter, a9 as StorageFile, aa as TrackEventResponse, ab as TransactionSummary, ac as UTMParams, ad as UpdateProfileRequest, ae as UploadOptions, af as UploadResponse, ag as VerifyEmailRequest } from './index-zQloSkpW.js';
11
11
 
12
+ /**
13
+ * Tri-state file visibility — `'private' | 'app_public' | 'anonymous_visible'`.
14
+ *
15
+ * Locally redeclared here rather than imported from `@scalemule/sdk`
16
+ * so this package can ship before the sdk's `Visibility` export is
17
+ * published. Once `@scalemule/sdk` ≥ the version that exports
18
+ * `Visibility` is the minimum dep version, swap this for an `import
19
+ * type { Visibility } from '@scalemule/sdk'`.
20
+ *
21
+ * Keep the union members in lockstep with the storage migration's
22
+ * ENUM and the SDK's typed export.
23
+ */
24
+ type Visibility = 'private' | 'app_public' | 'anonymous_visible';
12
25
  /**
13
26
  * Result of a single {@link useMedia} upload call.
14
27
  *
@@ -33,8 +46,24 @@ interface MediaUploadResult {
33
46
  hls_url_promise: Promise<string | null>;
34
47
  /** The file's MIME type — preserved from the input File / Blob. */
35
48
  mime_type: string;
36
- /** Whether the resulting storage object is public-readable. */
49
+ /** Whether the resulting storage object is public-readable.
50
+ * Derived from `visibility !== 'private'` for back-compat with
51
+ * callers that still branch on the boolean. */
37
52
  is_public: boolean;
53
+ /**
54
+ * Resolved tri-state visibility from the storage service. Use this
55
+ * over `is_public` for new code — `is_public: true` covers both
56
+ * `'app_public'` (auth-gated CDN) and `'anonymous_visible'`
57
+ * (unsigned public CDN), which have different delivery contracts.
58
+ */
59
+ visibility: Visibility;
60
+ /**
61
+ * Stable unsigned public-CDN URL — populated only when
62
+ * `visibility === 'anonymous_visible'`. Drop directly into
63
+ * `<img src>` on logged-out pages. For other visibilities use
64
+ * `original_view_url` (signed) or the photo transform URL.
65
+ */
66
+ cdn_url: string | null;
38
67
  }
39
68
  /**
40
69
  * Per-app media-pipeline policy. Drives release-gating + processing
@@ -55,10 +84,34 @@ interface MediaUploadResult {
55
84
  */
56
85
  type MediaPolicy = 'fast_trusted' | 'safe_visible' | 'safe_public' | 'moderated' | 'compliance';
57
86
  interface UseMediaUploadOptions {
58
- /** Whether the resulting storage object should be public-readable.
87
+ /**
88
+ * Tri-state visibility (preferred — see {@link Visibility} in
89
+ * `@scalemule/sdk`). Three modes:
90
+ * - `'private'` — owner + app members only (DEFAULT)
91
+ * - `'app_public'` — readable by any authenticated end-user
92
+ * in the same application; same semantics
93
+ * the legacy `is_public: true` flag has
94
+ * always had
95
+ * - `'anonymous_visible'` — world-readable on the unsigned public
96
+ * CDN. Returned `cdn_url` is safe to drop
97
+ * into `<img src>` on a logged-out page.
98
+ *
99
+ * If both `visibility` and `is_public` are passed, `visibility`
100
+ * wins. `'anonymous_visible'` requires the operator to have
101
+ * provisioned the anonymous-delivery bucket — the storage service
102
+ * surfaces 503 `ANONYMOUS_DELIVERY_NOT_CONFIGURED` otherwise (the
103
+ * SDK never silently demotes).
104
+ */
105
+ visibility?: Visibility;
106
+ /**
107
+ * Legacy two-state visibility flag. `true` → `app_public`; `false`
108
+ * → `private`. Prefer the typed `visibility` field for new code —
109
+ * `is_public: true` cannot express `anonymous_visible`.
110
+ *
59
111
  * Default: `false` (private). Public is opt-in for surfaces that
60
112
  * genuinely need it (avatars, public listings). Chat / DM uploads
61
- * should always be private. */
113
+ * should always be private.
114
+ */
62
115
  is_public?: boolean;
63
116
  /**
64
117
  * Per-call media-policy override. Defaults to `safe_visible` (visible
package/dist/index.js CHANGED
@@ -9544,7 +9544,9 @@ function useMedia() {
9544
9544
  async (file, options) => {
9545
9545
  setUploading(true);
9546
9546
  setError(null);
9547
- const isPublic = options?.is_public ?? false;
9547
+ const visibility = options?.visibility ?? (options?.is_public === true ? "app_public" : options?.is_public === false ? "private" : "private");
9548
+ const isPublic = visibility !== "private";
9549
+ const isAnonymous = visibility === "anonymous_visible";
9548
9550
  const mimeType = file.type || "application/octet-stream";
9549
9551
  const policy = options?.policy ?? providerDefaultPolicy ?? "safe_visible";
9550
9552
  const gateOnPipeline = policy === "safe_public" || policy === "moderated" || policy === "compliance";
@@ -9554,7 +9556,31 @@ function useMedia() {
9554
9556
  onProgress: options?.onProgress,
9555
9557
  signal: options?.signal
9556
9558
  };
9559
+ const storageAnon = storage;
9557
9560
  try {
9561
+ if (isAnonymous) {
9562
+ const r2 = await storageAnon.uploadAnonymous(file, sharedOpts);
9563
+ if (r2.error || !r2.data) {
9564
+ throw r2.error ?? { code: "upload_error", message: "Upload failed", status: 0 };
9565
+ }
9566
+ const f2 = r2.data;
9567
+ return {
9568
+ file_id: f2.id,
9569
+ photo_id: null,
9570
+ // For anon files, original_view_url is the same unsigned
9571
+ // CDN URL — no signed alternative exists. Callers should
9572
+ // prefer `cdn_url` (more specific) but `original_view_url`
9573
+ // works too for back-compat with code that already reads
9574
+ // that field.
9575
+ original_view_url: f2.cdn_url ?? null,
9576
+ optimized_url_promise: Promise.resolve(null),
9577
+ hls_url_promise: Promise.resolve(null),
9578
+ mime_type: mimeType,
9579
+ is_public: true,
9580
+ visibility: "anonymous_visible",
9581
+ cdn_url: f2.cdn_url ?? null
9582
+ };
9583
+ }
9558
9584
  if (mimeType.startsWith("image/") && !options?.skipPhotoRegister && !isPublic) {
9559
9585
  const r2 = await photo.uploadViaStorage(file, sharedOpts);
9560
9586
  if (r2.error || !r2.data) {
@@ -9570,7 +9596,9 @@ function useMedia() {
9570
9596
  optimized_url_promise: r2.data.optimized_url_promise,
9571
9597
  hls_url_promise: Promise.resolve(null),
9572
9598
  mime_type: mimeType,
9573
- is_public: false
9599
+ is_public: false,
9600
+ visibility: "private",
9601
+ cdn_url: null
9574
9602
  };
9575
9603
  }
9576
9604
  if (mimeType.startsWith("video/") && !isPublic) {
@@ -9588,7 +9616,9 @@ function useMedia() {
9588
9616
  optimized_url_promise: Promise.resolve(null),
9589
9617
  hls_url_promise: r2.data.hls_url_promise,
9590
9618
  mime_type: mimeType,
9591
- is_public: false
9619
+ is_public: false,
9620
+ visibility: "private",
9621
+ cdn_url: null
9592
9622
  };
9593
9623
  }
9594
9624
  if (mimeType.startsWith("audio/") && !isPublic) {
@@ -9603,13 +9633,15 @@ function useMedia() {
9603
9633
  optimized_url_promise: Promise.resolve(null),
9604
9634
  hls_url_promise: Promise.resolve(null),
9605
9635
  mime_type: mimeType,
9606
- is_public: false
9636
+ is_public: false,
9637
+ visibility: "private",
9638
+ cdn_url: null
9607
9639
  };
9608
9640
  }
9609
9641
  if (mimeType.startsWith("image/") && isPublic) {
9610
9642
  const r2 = await storage.upload(file, {
9611
9643
  ...sharedOpts,
9612
- isPublic: true,
9644
+ visibility: "app_public",
9613
9645
  skipCompression: true
9614
9646
  });
9615
9647
  if (r2.error || !r2.data) {
@@ -9623,10 +9655,16 @@ function useMedia() {
9623
9655
  optimized_url_promise: Promise.resolve(null),
9624
9656
  hls_url_promise: Promise.resolve(null),
9625
9657
  mime_type: mimeType,
9626
- is_public: f2.is_public ?? true
9658
+ is_public: f2.is_public ?? true,
9659
+ visibility: f2.visibility ?? "app_public",
9660
+ cdn_url: f2.cdn_url ?? null
9627
9661
  };
9628
9662
  }
9629
- const r = isPublic ? await storage.upload(file, { ...sharedOpts, isPublic: true, skipCompression: true }) : await storage.uploadPrivate(file, sharedOpts);
9663
+ const r = isPublic ? await storage.upload(file, {
9664
+ ...sharedOpts,
9665
+ visibility: "app_public",
9666
+ skipCompression: true
9667
+ }) : await storage.uploadPrivate(file, sharedOpts);
9630
9668
  if (r.error || !r.data) {
9631
9669
  throw r.error ?? { code: "upload_error", message: "Upload failed", status: 0 };
9632
9670
  }
@@ -9638,7 +9676,9 @@ function useMedia() {
9638
9676
  optimized_url_promise: Promise.resolve(null),
9639
9677
  hls_url_promise: Promise.resolve(null),
9640
9678
  mime_type: mimeType,
9641
- is_public: f.is_public ?? isPublic
9679
+ is_public: f.is_public ?? isPublic,
9680
+ visibility: f.visibility ?? (isPublic ? "app_public" : "private"),
9681
+ cdn_url: f.cdn_url ?? null
9642
9682
  };
9643
9683
  } catch (err) {
9644
9684
  const e = err;
package/dist/index.mjs CHANGED
@@ -9524,7 +9524,9 @@ function useMedia() {
9524
9524
  async (file, options) => {
9525
9525
  setUploading(true);
9526
9526
  setError(null);
9527
- const isPublic = options?.is_public ?? false;
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";
9528
9530
  const mimeType = file.type || "application/octet-stream";
9529
9531
  const policy = options?.policy ?? providerDefaultPolicy ?? "safe_visible";
9530
9532
  const gateOnPipeline = policy === "safe_public" || policy === "moderated" || policy === "compliance";
@@ -9534,7 +9536,31 @@ function useMedia() {
9534
9536
  onProgress: options?.onProgress,
9535
9537
  signal: options?.signal
9536
9538
  };
9539
+ const storageAnon = storage;
9537
9540
  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
+ }
9538
9564
  if (mimeType.startsWith("image/") && !options?.skipPhotoRegister && !isPublic) {
9539
9565
  const r2 = await photo.uploadViaStorage(file, sharedOpts);
9540
9566
  if (r2.error || !r2.data) {
@@ -9550,7 +9576,9 @@ function useMedia() {
9550
9576
  optimized_url_promise: r2.data.optimized_url_promise,
9551
9577
  hls_url_promise: Promise.resolve(null),
9552
9578
  mime_type: mimeType,
9553
- is_public: false
9579
+ is_public: false,
9580
+ visibility: "private",
9581
+ cdn_url: null
9554
9582
  };
9555
9583
  }
9556
9584
  if (mimeType.startsWith("video/") && !isPublic) {
@@ -9568,7 +9596,9 @@ function useMedia() {
9568
9596
  optimized_url_promise: Promise.resolve(null),
9569
9597
  hls_url_promise: r2.data.hls_url_promise,
9570
9598
  mime_type: mimeType,
9571
- is_public: false
9599
+ is_public: false,
9600
+ visibility: "private",
9601
+ cdn_url: null
9572
9602
  };
9573
9603
  }
9574
9604
  if (mimeType.startsWith("audio/") && !isPublic) {
@@ -9583,13 +9613,15 @@ function useMedia() {
9583
9613
  optimized_url_promise: Promise.resolve(null),
9584
9614
  hls_url_promise: Promise.resolve(null),
9585
9615
  mime_type: mimeType,
9586
- is_public: false
9616
+ is_public: false,
9617
+ visibility: "private",
9618
+ cdn_url: null
9587
9619
  };
9588
9620
  }
9589
9621
  if (mimeType.startsWith("image/") && isPublic) {
9590
9622
  const r2 = await storage.upload(file, {
9591
9623
  ...sharedOpts,
9592
- isPublic: true,
9624
+ visibility: "app_public",
9593
9625
  skipCompression: true
9594
9626
  });
9595
9627
  if (r2.error || !r2.data) {
@@ -9603,10 +9635,16 @@ function useMedia() {
9603
9635
  optimized_url_promise: Promise.resolve(null),
9604
9636
  hls_url_promise: Promise.resolve(null),
9605
9637
  mime_type: mimeType,
9606
- is_public: f2.is_public ?? true
9638
+ is_public: f2.is_public ?? true,
9639
+ visibility: f2.visibility ?? "app_public",
9640
+ cdn_url: f2.cdn_url ?? null
9607
9641
  };
9608
9642
  }
9609
- const r = isPublic ? await storage.upload(file, { ...sharedOpts, isPublic: true, skipCompression: true }) : await storage.uploadPrivate(file, sharedOpts);
9643
+ const r = isPublic ? await storage.upload(file, {
9644
+ ...sharedOpts,
9645
+ visibility: "app_public",
9646
+ skipCompression: true
9647
+ }) : await storage.uploadPrivate(file, sharedOpts);
9610
9648
  if (r.error || !r.data) {
9611
9649
  throw r.error ?? { code: "upload_error", message: "Upload failed", status: 0 };
9612
9650
  }
@@ -9618,7 +9656,9 @@ function useMedia() {
9618
9656
  optimized_url_promise: Promise.resolve(null),
9619
9657
  hls_url_promise: Promise.resolve(null),
9620
9658
  mime_type: mimeType,
9621
- is_public: f.is_public ?? isPublic
9659
+ is_public: f.is_public ?? isPublic,
9660
+ visibility: f.visibility ?? (isPublic ? "app_public" : "private"),
9661
+ cdn_url: f.cdn_url ?? null
9622
9662
  };
9623
9663
  } catch (err) {
9624
9664
  const e = err;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalemule/nextjs",
3
- "version": "0.1.26",
3
+ "version": "0.1.27",
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.49"
63
+ "@scalemule/sdk": "^0.0.55"
64
64
  },
65
65
  "peerDependencies": {
66
66
  "next": ">=14.0.0",