@scalemule/nextjs 0.1.6 → 0.1.8

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
@@ -1,14 +1,156 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ApiError, RealtimeService, StorageService, PhotoService, VideoService, AudioService, FileStatus } from '@scalemule/sdk';
2
3
  import * as React from 'react';
3
4
  import { ReactNode, ReactElement } from 'react';
4
5
  import { MoneyClient } from '@scalemule/money';
5
6
  export { MoneyClient, MoneyClientConfig, createMoneyClient } from '@scalemule/money';
6
- import { RealtimeService, StorageService, PhotoService, VideoService, ApiError as ApiError$1, FileStatus } from '@scalemule/sdk';
7
7
  import { ScaleMuleClient } from './client.mjs';
8
8
  export { ClientConfig, RequestOptions, createClient } from './client.mjs';
9
- import { S as ScaleMuleConfig, U as User, L as LoginResponse, A as ApiError, a as UseAuthReturn, b as UseBillingReturn, c as ListFilesParams, d as UseContentReturn, e as UseUserReturn, f as UseAnalyticsOptions, g as UseAnalyticsReturn } from './index-BIIUrnPr.mjs';
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-BIIUrnPr.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-BIIUrnPr.mjs';
11
11
 
12
+ /**
13
+ * Result of a single {@link useMedia} upload call.
14
+ *
15
+ * The shape is normalized regardless of MIME type — `optimized_url_promise`
16
+ * resolves on image uploads after the photo optimizer finishes; for non-image
17
+ * uploads it resolves to `null` immediately. (Video / audio branches will
18
+ * populate `hls_url_promise` in later phases — today they fall through to
19
+ * generic storage and that field stays `null`.)
20
+ */
21
+ interface MediaUploadResult {
22
+ /** Storage file_id — store this in chat-attachment metadata. */
23
+ file_id: string;
24
+ /** Photo service id — null for non-image uploads or when register() failed. */
25
+ photo_id: string | null;
26
+ /** Short-lived signed URL to the original bytes (private uploads) or
27
+ * a public CDN URL (when caller passed `is_public: true`). */
28
+ original_view_url: string | null;
29
+ /** Resolves once the photo optimizer finishes. `null` for non-image
30
+ * MIME types or when register() failed. */
31
+ optimized_url_promise: Promise<string | null>;
32
+ /** Resolves once the video transcoder finishes (Phase 2 / S5b). `null` today. */
33
+ hls_url_promise: Promise<string | null>;
34
+ /** The file's MIME type — preserved from the input File / Blob. */
35
+ mime_type: string;
36
+ /** Whether the resulting storage object is public-readable. */
37
+ is_public: boolean;
38
+ }
39
+ /**
40
+ * Per-app media-pipeline policy. Drives release-gating + processing
41
+ * behavior. **Orthogonal to `is_public`.** See
42
+ * `docs/MEDIA-UPLOADS.md` and ADR-2026-04-26 for the full taxonomy.
43
+ *
44
+ * Today's behavior (Phase 4 v1):
45
+ * - `fast_trusted` / `safe_visible` (default): upload promise resolves
46
+ * as soon as the file is uploaded + registered. The post-processing
47
+ * (scan, optimize, transcode) runs async; callers can observe via
48
+ * `useFileStatus`.
49
+ * - `safe_public` / `moderated`: upload promise *waits* for the
50
+ * optimized image variant (image MIME) or HLS playlist (video MIME)
51
+ * to become ready before resolving. Acts as the release-gate for
52
+ * UGC-style apps that should not publish raw bytes.
53
+ * - `compliance`: today behaves as `safe_public`; Phase 4+ adds the
54
+ * audit-log + signed-with-purpose URL semantics.
55
+ */
56
+ type MediaPolicy = 'fast_trusted' | 'safe_visible' | 'safe_public' | 'moderated' | 'compliance';
57
+ interface UseMediaUploadOptions {
58
+ /** Whether the resulting storage object should be public-readable.
59
+ * Default: `false` (private). Public is opt-in for surfaces that
60
+ * genuinely need it (avatars, public listings). Chat / DM uploads
61
+ * should always be private. */
62
+ is_public?: boolean;
63
+ /**
64
+ * Per-call media-policy override. Defaults to `safe_visible` (visible
65
+ * immediately at original fidelity; optimized variants swap in async).
66
+ * Set to `safe_public` to make the upload promise *await* the optimized
67
+ * variant (image) or HLS playlist (video) before resolving — useful for
68
+ * broadcast / UGC apps that gate publication on processing complete.
69
+ *
70
+ * The per-app default lives in `application_storage_settings.media_policy`
71
+ * (Phase 4 / P3, live in prod 2026-04-26). Reading the per-app default
72
+ * into `useMedia` defaults lands in a follow-up; today the option is
73
+ * caller-provided per call.
74
+ */
75
+ policy?: MediaPolicy;
76
+ /** Display filename (sanitized server-side). */
77
+ filename?: string;
78
+ /** Custom metadata attached to the file. */
79
+ metadata?: Record<string, unknown>;
80
+ /** Upload progress callback (0-100). */
81
+ onProgress?: (percent: number) => void;
82
+ /** AbortSignal for cancellation. */
83
+ signal?: AbortSignal;
84
+ /** Force a non-photo path even for image MIME types — useful for
85
+ * generic file uploads where you don't want the photo optimizer
86
+ * to register the file. Default: `false`. */
87
+ skipPhotoRegister?: boolean;
88
+ }
89
+ interface UseMediaReturn {
90
+ /** Upload a file. MIME-aware: images go through photo register +
91
+ * optimization; everything else goes through generic storage. */
92
+ upload: (file: File | Blob, options?: UseMediaUploadOptions) => Promise<MediaUploadResult>;
93
+ /** Cancel an upload by its `file_id`. Deletes the storage object so
94
+ * it doesn't orphan in S3 — useful when a chat composer accepts a
95
+ * file but the user removes it before sending. Idempotent. */
96
+ cancelUpload: (fileId: string) => Promise<void>;
97
+ /** Last error from `upload` or `cancelUpload`. */
98
+ error: ApiError | null;
99
+ /** True while an upload is in progress. */
100
+ uploading: boolean;
101
+ }
102
+ /**
103
+ * Opinionated, MIME-aware media upload hook.
104
+ *
105
+ * `useMedia()` is the canonical upload primitive for chat / progressive
106
+ * media use. It branches by MIME type:
107
+ * - `image/*` → `client.photo.uploadViaStorage()` — upload to storage,
108
+ * then register with the photo service so the on-demand transform
109
+ * endpoint resolves to optimized variants. The returned
110
+ * `optimized_url_promise` resolves once the optimizer finishes.
111
+ * - everything else → `client.storage.uploadPrivate()` — a private,
112
+ * uncompressed, fail-closed upload to generic storage.
113
+ *
114
+ * **Default visibility is `is_public: false`.** Public is opt-in per call.
115
+ * `useMedia()` does not expose `is_public` via app-level config — visibility
116
+ * is always an explicit per-call surface choice.
117
+ *
118
+ * Compared to `useContent()`:
119
+ * - `useContent()` is a thin wrapper over generic storage and does not
120
+ * register photos / videos with their typed services. Use it for plain
121
+ * file uploads where you don't need optimization or transcoding.
122
+ * - `useMedia()` defaults to private + no compression, integrates with the
123
+ * typed media services automatically, and is the right primitive for
124
+ * anything chat- or media-shaped.
125
+ *
126
+ * @example
127
+ * ```tsx
128
+ * 'use client';
129
+ *
130
+ * import { useMedia, ScaleMuleMedia } from '@scalemule/nextjs';
131
+ *
132
+ * function ChatComposer({ onAttach }) {
133
+ * const { upload, uploading } = useMedia();
134
+ *
135
+ * async function handlePick(file: File) {
136
+ * const result = await upload(file);
137
+ * onAttach({
138
+ * file_id: result.file_id,
139
+ * mime_type: result.mime_type,
140
+ * optimized_url_promise: result.optimized_url_promise,
141
+ * });
142
+ * }
143
+ *
144
+ * return <input type="file" disabled={uploading}
145
+ * onChange={(e) => e.target.files?.[0] && handlePick(e.target.files[0])} />;
146
+ * }
147
+ * ```
148
+ *
149
+ * See `docs/MEDIA-UPLOADS.md` in the platform repo for the decision
150
+ * tree and the full anti-patterns list.
151
+ */
152
+ declare function useMedia(): UseMediaReturn;
153
+
12
154
  interface ScaleMuleContextValue {
13
155
  /** The API client instance */
14
156
  client: ScaleMuleClient;
@@ -22,6 +164,14 @@ interface ScaleMuleContextValue {
22
164
  photo: PhotoService;
23
165
  /** Base SDK video service — exposed for `useMedia()` and `video.uploadViaStorage()` */
24
166
  video: VideoService;
167
+ /** Base SDK audio service — exposed for `useMedia()` and `audio.uploadViaStorage()` */
168
+ audio: AudioService;
169
+ /**
170
+ * Default media policy for `useMedia()` calls. Set via
171
+ * `<ScaleMuleProvider mediaPolicy="…">`; per-call overrides win.
172
+ * Undefined falls back to `useMedia()`'s built-in `safe_visible` default.
173
+ */
174
+ mediaPolicy?: MediaPolicy;
25
175
  /** Current authenticated user */
26
176
  user: User | null;
27
177
  /** Set the current user */
@@ -29,9 +179,9 @@ interface ScaleMuleContextValue {
29
179
  /** Whether the SDK is initializing */
30
180
  initializing: boolean;
31
181
  /** Last error */
32
- error: ApiError | null;
182
+ error: ApiError$1 | null;
33
183
  /** Set error */
34
- setError: (error: ApiError | null) => void;
184
+ setError: (error: ApiError$1 | null) => void;
35
185
  /** Analytics proxy URL (when set, SDK sends events here instead of ScaleMule) */
36
186
  analyticsProxyUrl?: string;
37
187
  /** Auth proxy URL (when set, auth operations route through this proxy) */
@@ -56,11 +206,27 @@ interface ScaleMuleProviderProps extends ScaleMuleConfig {
56
206
  /** Called when user logs out */
57
207
  onLogout?: () => void;
58
208
  /** Called on authentication error */
59
- onAuthError?: (error: ApiError) => void;
209
+ onAuthError?: (error: ApiError$1) => void;
60
210
  /** Server-evaluated flag values to bootstrap the client (eliminates loading flash) */
61
211
  bootstrapFlags?: Record<string, unknown>;
212
+ /**
213
+ * Default media-pipeline policy applied to `useMedia()` calls when no
214
+ * per-call `policy` override is given. Five modes — see
215
+ * `docs/MEDIA-UPLOADS.md` and the {@link import('./hooks/useMedia').MediaPolicy} type.
216
+ *
217
+ * The platform stores the per-app policy in
218
+ * `application_storage_settings.media_policy` (Phase 4 / P3, live in
219
+ * prod). That endpoint is `MemberOnly` admin-auth, so end-user-context
220
+ * provider can't fetch it directly — apps that want policy-driven
221
+ * defaults should declare the value here, mirroring whatever the
222
+ * platform admin set.
223
+ *
224
+ * Default: undefined → `useMedia()` falls back to its built-in
225
+ * `safe_visible` default.
226
+ */
227
+ mediaPolicy?: MediaPolicy;
62
228
  }
63
- declare function ScaleMuleProvider({ apiKey, applicationId, environment, gatewayUrl, debug, storage, analyticsProxyUrl, authProxyUrl, publishableKey, enableAccountSwitcher, accountSwitcherPrivacy, children, onLogin, onLogout, onAuthError, bootstrapFlags, }: ScaleMuleProviderProps): react_jsx_runtime.JSX.Element;
229
+ declare function ScaleMuleProvider({ apiKey, applicationId, environment, gatewayUrl, debug, storage, analyticsProxyUrl, authProxyUrl, publishableKey, enableAccountSwitcher, accountSwitcherPrivacy, children, onLogin, onLogout, onAuthError, bootstrapFlags, mediaPolicy, }: ScaleMuleProviderProps): react_jsx_runtime.JSX.Element;
64
230
  declare function useScaleMule(): ScaleMuleContextValue;
65
231
  declare function useScaleMuleClient(): ScaleMuleClient;
66
232
  declare function useMoneyClient(): MoneyClient;
@@ -158,148 +324,6 @@ interface UseContentOptions {
158
324
  */
159
325
  declare function useContent(options?: UseContentOptions): UseContentReturn;
160
326
 
161
- /**
162
- * Result of a single {@link useMedia} upload call.
163
- *
164
- * The shape is normalized regardless of MIME type — `optimized_url_promise`
165
- * resolves on image uploads after the photo optimizer finishes; for non-image
166
- * uploads it resolves to `null` immediately. (Video / audio branches will
167
- * populate `hls_url_promise` in later phases — today they fall through to
168
- * generic storage and that field stays `null`.)
169
- */
170
- interface MediaUploadResult {
171
- /** Storage file_id — store this in chat-attachment metadata. */
172
- file_id: string;
173
- /** Photo service id — null for non-image uploads or when register() failed. */
174
- photo_id: string | null;
175
- /** Short-lived signed URL to the original bytes (private uploads) or
176
- * a public CDN URL (when caller passed `is_public: true`). */
177
- original_view_url: string | null;
178
- /** Resolves once the photo optimizer finishes. `null` for non-image
179
- * MIME types or when register() failed. */
180
- optimized_url_promise: Promise<string | null>;
181
- /** Resolves once the video transcoder finishes (Phase 2 / S5b). `null` today. */
182
- hls_url_promise: Promise<string | null>;
183
- /** The file's MIME type — preserved from the input File / Blob. */
184
- mime_type: string;
185
- /** Whether the resulting storage object is public-readable. */
186
- is_public: boolean;
187
- }
188
- /**
189
- * Per-app media-pipeline policy. Drives release-gating + processing
190
- * behavior. **Orthogonal to `is_public`.** See
191
- * `docs/MEDIA-UPLOADS.md` and ADR-2026-04-26 for the full taxonomy.
192
- *
193
- * Today's behavior (Phase 4 v1):
194
- * - `fast_trusted` / `safe_visible` (default): upload promise resolves
195
- * as soon as the file is uploaded + registered. The post-processing
196
- * (scan, optimize, transcode) runs async; callers can observe via
197
- * `useFileStatus`.
198
- * - `safe_public` / `moderated`: upload promise *waits* for the
199
- * optimized image variant (image MIME) or HLS playlist (video MIME)
200
- * to become ready before resolving. Acts as the release-gate for
201
- * UGC-style apps that should not publish raw bytes.
202
- * - `compliance`: today behaves as `safe_public`; Phase 4+ adds the
203
- * audit-log + signed-with-purpose URL semantics.
204
- */
205
- type MediaPolicy = 'fast_trusted' | 'safe_visible' | 'safe_public' | 'moderated' | 'compliance';
206
- interface UseMediaUploadOptions {
207
- /** Whether the resulting storage object should be public-readable.
208
- * Default: `false` (private). Public is opt-in for surfaces that
209
- * genuinely need it (avatars, public listings). Chat / DM uploads
210
- * should always be private. */
211
- is_public?: boolean;
212
- /**
213
- * Per-call media-policy override. Defaults to `safe_visible` (visible
214
- * immediately at original fidelity; optimized variants swap in async).
215
- * Set to `safe_public` to make the upload promise *await* the optimized
216
- * variant (image) or HLS playlist (video) before resolving — useful for
217
- * broadcast / UGC apps that gate publication on processing complete.
218
- *
219
- * The per-app default lives in `application_storage_settings.media_policy`
220
- * (Phase 4 / P3, live in prod 2026-04-26). Reading the per-app default
221
- * into `useMedia` defaults lands in a follow-up; today the option is
222
- * caller-provided per call.
223
- */
224
- policy?: MediaPolicy;
225
- /** Display filename (sanitized server-side). */
226
- filename?: string;
227
- /** Custom metadata attached to the file. */
228
- metadata?: Record<string, unknown>;
229
- /** Upload progress callback (0-100). */
230
- onProgress?: (percent: number) => void;
231
- /** AbortSignal for cancellation. */
232
- signal?: AbortSignal;
233
- /** Force a non-photo path even for image MIME types — useful for
234
- * generic file uploads where you don't want the photo optimizer
235
- * to register the file. Default: `false`. */
236
- skipPhotoRegister?: boolean;
237
- }
238
- interface UseMediaReturn {
239
- /** Upload a file. MIME-aware: images go through photo register +
240
- * optimization; everything else goes through generic storage. */
241
- upload: (file: File | Blob, options?: UseMediaUploadOptions) => Promise<MediaUploadResult>;
242
- /** Cancel an upload by its `file_id`. Deletes the storage object so
243
- * it doesn't orphan in S3 — useful when a chat composer accepts a
244
- * file but the user removes it before sending. Idempotent. */
245
- cancelUpload: (fileId: string) => Promise<void>;
246
- /** Last error from `upload` or `cancelUpload`. */
247
- error: ApiError$1 | null;
248
- /** True while an upload is in progress. */
249
- uploading: boolean;
250
- }
251
- /**
252
- * Opinionated, MIME-aware media upload hook.
253
- *
254
- * `useMedia()` is the canonical upload primitive for chat / progressive
255
- * media use. It branches by MIME type:
256
- * - `image/*` → `client.photo.uploadViaStorage()` — upload to storage,
257
- * then register with the photo service so the on-demand transform
258
- * endpoint resolves to optimized variants. The returned
259
- * `optimized_url_promise` resolves once the optimizer finishes.
260
- * - everything else → `client.storage.uploadPrivate()` — a private,
261
- * uncompressed, fail-closed upload to generic storage.
262
- *
263
- * **Default visibility is `is_public: false`.** Public is opt-in per call.
264
- * `useMedia()` does not expose `is_public` via app-level config — visibility
265
- * is always an explicit per-call surface choice.
266
- *
267
- * Compared to `useContent()`:
268
- * - `useContent()` is a thin wrapper over generic storage and does not
269
- * register photos / videos with their typed services. Use it for plain
270
- * file uploads where you don't need optimization or transcoding.
271
- * - `useMedia()` defaults to private + no compression, integrates with the
272
- * typed media services automatically, and is the right primitive for
273
- * anything chat- or media-shaped.
274
- *
275
- * @example
276
- * ```tsx
277
- * 'use client';
278
- *
279
- * import { useMedia, ScaleMuleMedia } from '@scalemule/nextjs';
280
- *
281
- * function ChatComposer({ onAttach }) {
282
- * const { upload, uploading } = useMedia();
283
- *
284
- * async function handlePick(file: File) {
285
- * const result = await upload(file);
286
- * onAttach({
287
- * file_id: result.file_id,
288
- * mime_type: result.mime_type,
289
- * optimized_url_promise: result.optimized_url_promise,
290
- * });
291
- * }
292
- *
293
- * return <input type="file" disabled={uploading}
294
- * onChange={(e) => e.target.files?.[0] && handlePick(e.target.files[0])} />;
295
- * }
296
- * ```
297
- *
298
- * See `docs/MEDIA-UPLOADS.md` in the platform repo for the decision
299
- * tree and the full anti-patterns list.
300
- */
301
- declare function useMedia(): UseMediaReturn;
302
-
303
327
  interface UseFileStatusOptions {
304
328
  /** Storage file_id to read status for. */
305
329
  fileId: string | null | undefined;
@@ -326,7 +350,7 @@ interface UseFileStatusReturn {
326
350
  /** True while a fetch is in flight. */
327
351
  loading: boolean;
328
352
  /** Last error, or null. */
329
- error: ApiError$1 | null;
353
+ error: ApiError | null;
330
354
  /**
331
355
  * Convenience: scan is clean. For images/videos, this means the file
332
356
  * is *safe to render*; the optimized / HLS variants may still be
@@ -586,7 +610,7 @@ interface UseFeatureFlagsOptions {
586
610
  interface UseFeatureFlagsReturn {
587
611
  flags: Record<string, FeatureFlagEvaluation>;
588
612
  loading: boolean;
589
- error: ApiError | null;
613
+ error: ApiError$1 | null;
590
614
  refresh: () => Promise<void>;
591
615
  isEnabled: (flagKey: string, fallback?: boolean) => boolean;
592
616
  getFlag: <T = unknown>(flagKey: string, fallback?: T) => T;
@@ -613,7 +637,7 @@ interface UsePushNotificationsReturn {
613
637
  /** Whether an operation is in progress */
614
638
  isLoading: boolean;
615
639
  /** Last error */
616
- error: ApiError | null;
640
+ error: ApiError$1 | null;
617
641
  /** Request permission and subscribe to push notifications */
618
642
  subscribe: () => Promise<void>;
619
643
  /** Unsubscribe from push notifications */
@@ -714,7 +738,7 @@ interface UseFeedbackResult {
714
738
  /** End-user's own feedback items for the current tenant. Empty when not signed in. */
715
739
  items: FeedbackItem[];
716
740
  loading: boolean;
717
- error: ApiError | null;
741
+ error: ApiError$1 | null;
718
742
  /** Submit a new feedback item. Returns the persisted item on success. */
719
743
  submit: (input: FeedbackItemInput) => Promise<FeedbackItem>;
720
744
  /** Re-fetch the list. */
@@ -926,4 +950,4 @@ declare function createSafeLogger(prefix: string): {
926
950
  error: (message: string, data?: unknown) => void;
927
951
  };
928
952
 
929
- export { ApiError, type FeatureFlagEvaluation, type FeatureFlagEvaluation as FeatureFlagResult, type FeedbackItem, type FeedbackItemInput, type FeedbackPriority, type FeedbackStatus, type FeedbackType, FeedbackWidget, type FeedbackWidgetConfig, type FeedbackWidgetProps, ListFilesParams, LoginResponse, type MediaPolicy, type MediaUploadResult, type PasswordValidationResult, type PhoneCountry, type PhoneValidationResult, type RealtimeEvent, type RealtimeMessage, type RealtimeStatus, ScaleMuleClient, ScaleMuleConfig, ScaleMuleMedia, type ScaleMuleMediaProps, ScaleMuleProvider, type ScaleMuleProviderProps, UseAnalyticsOptions, UseAnalyticsReturn, UseAuthReturn, UseBillingReturn, UseContentReturn, type UseFeatureFlagsOptions, type UseFeatureFlagsReturn, type UseFeedbackOptions, type UseFeedbackResult, type UseFileStatusOptions, type UseFileStatusReturn, type UseFeatureFlagsOptions as UseFlagsOptions, type UseFeatureFlagsReturn as UseFlagsReturn, type UseMediaReturn, type UseMediaUploadOptions, type UsePushNotificationsOptions, type UsePushNotificationsReturn, type UseRealtimeOptions, type UseRealtimeReturn, type UseShareOptions, type UseShareReturn, UseUserReturn, User, type UsernameValidationResult, composePhone, createSafeLogger, normalizePhone, phoneCountries, sanitizeForLog, useAnalytics, useAuth, useBilling, useContent, useFeatureFlags, useFeedback, useFileStatus, useMedia, useMoney, useMoneyClient, usePushNotifications, useRealtime, useScaleMule, useScaleMuleClient, useShare, useUser, validateForm, validators };
953
+ export { ApiError$1 as ApiError, type FeatureFlagEvaluation, type FeatureFlagEvaluation as FeatureFlagResult, type FeedbackItem, type FeedbackItemInput, type FeedbackPriority, type FeedbackStatus, type FeedbackType, FeedbackWidget, type FeedbackWidgetConfig, type FeedbackWidgetProps, ListFilesParams, LoginResponse, type MediaPolicy, type MediaUploadResult, type PasswordValidationResult, type PhoneCountry, type PhoneValidationResult, type RealtimeEvent, type RealtimeMessage, type RealtimeStatus, ScaleMuleClient, ScaleMuleConfig, ScaleMuleMedia, type ScaleMuleMediaProps, ScaleMuleProvider, type ScaleMuleProviderProps, UseAnalyticsOptions, UseAnalyticsReturn, UseAuthReturn, UseBillingReturn, UseContentReturn, type UseFeatureFlagsOptions, type UseFeatureFlagsReturn, type UseFeedbackOptions, type UseFeedbackResult, type UseFileStatusOptions, type UseFileStatusReturn, type UseFeatureFlagsOptions as UseFlagsOptions, type UseFeatureFlagsReturn as UseFlagsReturn, type UseMediaReturn, type UseMediaUploadOptions, type UsePushNotificationsOptions, type UsePushNotificationsReturn, type UseRealtimeOptions, type UseRealtimeReturn, type UseShareOptions, type UseShareReturn, UseUserReturn, User, type UsernameValidationResult, composePhone, createSafeLogger, normalizePhone, phoneCountries, sanitizeForLog, useAnalytics, useAuth, useBilling, useContent, useFeatureFlags, useFeedback, useFileStatus, useMedia, useMoney, useMoneyClient, usePushNotifications, useRealtime, useScaleMule, useScaleMuleClient, useShare, useUser, validateForm, validators };
package/dist/index.d.ts CHANGED
@@ -1,14 +1,156 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ApiError, RealtimeService, StorageService, PhotoService, VideoService, AudioService, FileStatus } from '@scalemule/sdk';
2
3
  import * as React from 'react';
3
4
  import { ReactNode, ReactElement } from 'react';
4
5
  import { MoneyClient } from '@scalemule/money';
5
6
  export { MoneyClient, MoneyClientConfig, createMoneyClient } from '@scalemule/money';
6
- import { RealtimeService, StorageService, PhotoService, VideoService, ApiError as ApiError$1, FileStatus } from '@scalemule/sdk';
7
7
  import { ScaleMuleClient } from './client.js';
8
8
  export { ClientConfig, RequestOptions, createClient } from './client.js';
9
- import { S as ScaleMuleConfig, U as User, L as LoginResponse, A as ApiError, a as UseAuthReturn, b as UseBillingReturn, c as ListFilesParams, d as UseContentReturn, e as UseUserReturn, f as UseAnalyticsOptions, g as UseAnalyticsReturn } from './index-BIIUrnPr.js';
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-BIIUrnPr.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-BIIUrnPr.js';
11
11
 
12
+ /**
13
+ * Result of a single {@link useMedia} upload call.
14
+ *
15
+ * The shape is normalized regardless of MIME type — `optimized_url_promise`
16
+ * resolves on image uploads after the photo optimizer finishes; for non-image
17
+ * uploads it resolves to `null` immediately. (Video / audio branches will
18
+ * populate `hls_url_promise` in later phases — today they fall through to
19
+ * generic storage and that field stays `null`.)
20
+ */
21
+ interface MediaUploadResult {
22
+ /** Storage file_id — store this in chat-attachment metadata. */
23
+ file_id: string;
24
+ /** Photo service id — null for non-image uploads or when register() failed. */
25
+ photo_id: string | null;
26
+ /** Short-lived signed URL to the original bytes (private uploads) or
27
+ * a public CDN URL (when caller passed `is_public: true`). */
28
+ original_view_url: string | null;
29
+ /** Resolves once the photo optimizer finishes. `null` for non-image
30
+ * MIME types or when register() failed. */
31
+ optimized_url_promise: Promise<string | null>;
32
+ /** Resolves once the video transcoder finishes (Phase 2 / S5b). `null` today. */
33
+ hls_url_promise: Promise<string | null>;
34
+ /** The file's MIME type — preserved from the input File / Blob. */
35
+ mime_type: string;
36
+ /** Whether the resulting storage object is public-readable. */
37
+ is_public: boolean;
38
+ }
39
+ /**
40
+ * Per-app media-pipeline policy. Drives release-gating + processing
41
+ * behavior. **Orthogonal to `is_public`.** See
42
+ * `docs/MEDIA-UPLOADS.md` and ADR-2026-04-26 for the full taxonomy.
43
+ *
44
+ * Today's behavior (Phase 4 v1):
45
+ * - `fast_trusted` / `safe_visible` (default): upload promise resolves
46
+ * as soon as the file is uploaded + registered. The post-processing
47
+ * (scan, optimize, transcode) runs async; callers can observe via
48
+ * `useFileStatus`.
49
+ * - `safe_public` / `moderated`: upload promise *waits* for the
50
+ * optimized image variant (image MIME) or HLS playlist (video MIME)
51
+ * to become ready before resolving. Acts as the release-gate for
52
+ * UGC-style apps that should not publish raw bytes.
53
+ * - `compliance`: today behaves as `safe_public`; Phase 4+ adds the
54
+ * audit-log + signed-with-purpose URL semantics.
55
+ */
56
+ type MediaPolicy = 'fast_trusted' | 'safe_visible' | 'safe_public' | 'moderated' | 'compliance';
57
+ interface UseMediaUploadOptions {
58
+ /** Whether the resulting storage object should be public-readable.
59
+ * Default: `false` (private). Public is opt-in for surfaces that
60
+ * genuinely need it (avatars, public listings). Chat / DM uploads
61
+ * should always be private. */
62
+ is_public?: boolean;
63
+ /**
64
+ * Per-call media-policy override. Defaults to `safe_visible` (visible
65
+ * immediately at original fidelity; optimized variants swap in async).
66
+ * Set to `safe_public` to make the upload promise *await* the optimized
67
+ * variant (image) or HLS playlist (video) before resolving — useful for
68
+ * broadcast / UGC apps that gate publication on processing complete.
69
+ *
70
+ * The per-app default lives in `application_storage_settings.media_policy`
71
+ * (Phase 4 / P3, live in prod 2026-04-26). Reading the per-app default
72
+ * into `useMedia` defaults lands in a follow-up; today the option is
73
+ * caller-provided per call.
74
+ */
75
+ policy?: MediaPolicy;
76
+ /** Display filename (sanitized server-side). */
77
+ filename?: string;
78
+ /** Custom metadata attached to the file. */
79
+ metadata?: Record<string, unknown>;
80
+ /** Upload progress callback (0-100). */
81
+ onProgress?: (percent: number) => void;
82
+ /** AbortSignal for cancellation. */
83
+ signal?: AbortSignal;
84
+ /** Force a non-photo path even for image MIME types — useful for
85
+ * generic file uploads where you don't want the photo optimizer
86
+ * to register the file. Default: `false`. */
87
+ skipPhotoRegister?: boolean;
88
+ }
89
+ interface UseMediaReturn {
90
+ /** Upload a file. MIME-aware: images go through photo register +
91
+ * optimization; everything else goes through generic storage. */
92
+ upload: (file: File | Blob, options?: UseMediaUploadOptions) => Promise<MediaUploadResult>;
93
+ /** Cancel an upload by its `file_id`. Deletes the storage object so
94
+ * it doesn't orphan in S3 — useful when a chat composer accepts a
95
+ * file but the user removes it before sending. Idempotent. */
96
+ cancelUpload: (fileId: string) => Promise<void>;
97
+ /** Last error from `upload` or `cancelUpload`. */
98
+ error: ApiError | null;
99
+ /** True while an upload is in progress. */
100
+ uploading: boolean;
101
+ }
102
+ /**
103
+ * Opinionated, MIME-aware media upload hook.
104
+ *
105
+ * `useMedia()` is the canonical upload primitive for chat / progressive
106
+ * media use. It branches by MIME type:
107
+ * - `image/*` → `client.photo.uploadViaStorage()` — upload to storage,
108
+ * then register with the photo service so the on-demand transform
109
+ * endpoint resolves to optimized variants. The returned
110
+ * `optimized_url_promise` resolves once the optimizer finishes.
111
+ * - everything else → `client.storage.uploadPrivate()` — a private,
112
+ * uncompressed, fail-closed upload to generic storage.
113
+ *
114
+ * **Default visibility is `is_public: false`.** Public is opt-in per call.
115
+ * `useMedia()` does not expose `is_public` via app-level config — visibility
116
+ * is always an explicit per-call surface choice.
117
+ *
118
+ * Compared to `useContent()`:
119
+ * - `useContent()` is a thin wrapper over generic storage and does not
120
+ * register photos / videos with their typed services. Use it for plain
121
+ * file uploads where you don't need optimization or transcoding.
122
+ * - `useMedia()` defaults to private + no compression, integrates with the
123
+ * typed media services automatically, and is the right primitive for
124
+ * anything chat- or media-shaped.
125
+ *
126
+ * @example
127
+ * ```tsx
128
+ * 'use client';
129
+ *
130
+ * import { useMedia, ScaleMuleMedia } from '@scalemule/nextjs';
131
+ *
132
+ * function ChatComposer({ onAttach }) {
133
+ * const { upload, uploading } = useMedia();
134
+ *
135
+ * async function handlePick(file: File) {
136
+ * const result = await upload(file);
137
+ * onAttach({
138
+ * file_id: result.file_id,
139
+ * mime_type: result.mime_type,
140
+ * optimized_url_promise: result.optimized_url_promise,
141
+ * });
142
+ * }
143
+ *
144
+ * return <input type="file" disabled={uploading}
145
+ * onChange={(e) => e.target.files?.[0] && handlePick(e.target.files[0])} />;
146
+ * }
147
+ * ```
148
+ *
149
+ * See `docs/MEDIA-UPLOADS.md` in the platform repo for the decision
150
+ * tree and the full anti-patterns list.
151
+ */
152
+ declare function useMedia(): UseMediaReturn;
153
+
12
154
  interface ScaleMuleContextValue {
13
155
  /** The API client instance */
14
156
  client: ScaleMuleClient;
@@ -22,6 +164,14 @@ interface ScaleMuleContextValue {
22
164
  photo: PhotoService;
23
165
  /** Base SDK video service — exposed for `useMedia()` and `video.uploadViaStorage()` */
24
166
  video: VideoService;
167
+ /** Base SDK audio service — exposed for `useMedia()` and `audio.uploadViaStorage()` */
168
+ audio: AudioService;
169
+ /**
170
+ * Default media policy for `useMedia()` calls. Set via
171
+ * `<ScaleMuleProvider mediaPolicy="…">`; per-call overrides win.
172
+ * Undefined falls back to `useMedia()`'s built-in `safe_visible` default.
173
+ */
174
+ mediaPolicy?: MediaPolicy;
25
175
  /** Current authenticated user */
26
176
  user: User | null;
27
177
  /** Set the current user */
@@ -29,9 +179,9 @@ interface ScaleMuleContextValue {
29
179
  /** Whether the SDK is initializing */
30
180
  initializing: boolean;
31
181
  /** Last error */
32
- error: ApiError | null;
182
+ error: ApiError$1 | null;
33
183
  /** Set error */
34
- setError: (error: ApiError | null) => void;
184
+ setError: (error: ApiError$1 | null) => void;
35
185
  /** Analytics proxy URL (when set, SDK sends events here instead of ScaleMule) */
36
186
  analyticsProxyUrl?: string;
37
187
  /** Auth proxy URL (when set, auth operations route through this proxy) */
@@ -56,11 +206,27 @@ interface ScaleMuleProviderProps extends ScaleMuleConfig {
56
206
  /** Called when user logs out */
57
207
  onLogout?: () => void;
58
208
  /** Called on authentication error */
59
- onAuthError?: (error: ApiError) => void;
209
+ onAuthError?: (error: ApiError$1) => void;
60
210
  /** Server-evaluated flag values to bootstrap the client (eliminates loading flash) */
61
211
  bootstrapFlags?: Record<string, unknown>;
212
+ /**
213
+ * Default media-pipeline policy applied to `useMedia()` calls when no
214
+ * per-call `policy` override is given. Five modes — see
215
+ * `docs/MEDIA-UPLOADS.md` and the {@link import('./hooks/useMedia').MediaPolicy} type.
216
+ *
217
+ * The platform stores the per-app policy in
218
+ * `application_storage_settings.media_policy` (Phase 4 / P3, live in
219
+ * prod). That endpoint is `MemberOnly` admin-auth, so end-user-context
220
+ * provider can't fetch it directly — apps that want policy-driven
221
+ * defaults should declare the value here, mirroring whatever the
222
+ * platform admin set.
223
+ *
224
+ * Default: undefined → `useMedia()` falls back to its built-in
225
+ * `safe_visible` default.
226
+ */
227
+ mediaPolicy?: MediaPolicy;
62
228
  }
63
- declare function ScaleMuleProvider({ apiKey, applicationId, environment, gatewayUrl, debug, storage, analyticsProxyUrl, authProxyUrl, publishableKey, enableAccountSwitcher, accountSwitcherPrivacy, children, onLogin, onLogout, onAuthError, bootstrapFlags, }: ScaleMuleProviderProps): react_jsx_runtime.JSX.Element;
229
+ declare function ScaleMuleProvider({ apiKey, applicationId, environment, gatewayUrl, debug, storage, analyticsProxyUrl, authProxyUrl, publishableKey, enableAccountSwitcher, accountSwitcherPrivacy, children, onLogin, onLogout, onAuthError, bootstrapFlags, mediaPolicy, }: ScaleMuleProviderProps): react_jsx_runtime.JSX.Element;
64
230
  declare function useScaleMule(): ScaleMuleContextValue;
65
231
  declare function useScaleMuleClient(): ScaleMuleClient;
66
232
  declare function useMoneyClient(): MoneyClient;
@@ -158,148 +324,6 @@ interface UseContentOptions {
158
324
  */
159
325
  declare function useContent(options?: UseContentOptions): UseContentReturn;
160
326
 
161
- /**
162
- * Result of a single {@link useMedia} upload call.
163
- *
164
- * The shape is normalized regardless of MIME type — `optimized_url_promise`
165
- * resolves on image uploads after the photo optimizer finishes; for non-image
166
- * uploads it resolves to `null` immediately. (Video / audio branches will
167
- * populate `hls_url_promise` in later phases — today they fall through to
168
- * generic storage and that field stays `null`.)
169
- */
170
- interface MediaUploadResult {
171
- /** Storage file_id — store this in chat-attachment metadata. */
172
- file_id: string;
173
- /** Photo service id — null for non-image uploads or when register() failed. */
174
- photo_id: string | null;
175
- /** Short-lived signed URL to the original bytes (private uploads) or
176
- * a public CDN URL (when caller passed `is_public: true`). */
177
- original_view_url: string | null;
178
- /** Resolves once the photo optimizer finishes. `null` for non-image
179
- * MIME types or when register() failed. */
180
- optimized_url_promise: Promise<string | null>;
181
- /** Resolves once the video transcoder finishes (Phase 2 / S5b). `null` today. */
182
- hls_url_promise: Promise<string | null>;
183
- /** The file's MIME type — preserved from the input File / Blob. */
184
- mime_type: string;
185
- /** Whether the resulting storage object is public-readable. */
186
- is_public: boolean;
187
- }
188
- /**
189
- * Per-app media-pipeline policy. Drives release-gating + processing
190
- * behavior. **Orthogonal to `is_public`.** See
191
- * `docs/MEDIA-UPLOADS.md` and ADR-2026-04-26 for the full taxonomy.
192
- *
193
- * Today's behavior (Phase 4 v1):
194
- * - `fast_trusted` / `safe_visible` (default): upload promise resolves
195
- * as soon as the file is uploaded + registered. The post-processing
196
- * (scan, optimize, transcode) runs async; callers can observe via
197
- * `useFileStatus`.
198
- * - `safe_public` / `moderated`: upload promise *waits* for the
199
- * optimized image variant (image MIME) or HLS playlist (video MIME)
200
- * to become ready before resolving. Acts as the release-gate for
201
- * UGC-style apps that should not publish raw bytes.
202
- * - `compliance`: today behaves as `safe_public`; Phase 4+ adds the
203
- * audit-log + signed-with-purpose URL semantics.
204
- */
205
- type MediaPolicy = 'fast_trusted' | 'safe_visible' | 'safe_public' | 'moderated' | 'compliance';
206
- interface UseMediaUploadOptions {
207
- /** Whether the resulting storage object should be public-readable.
208
- * Default: `false` (private). Public is opt-in for surfaces that
209
- * genuinely need it (avatars, public listings). Chat / DM uploads
210
- * should always be private. */
211
- is_public?: boolean;
212
- /**
213
- * Per-call media-policy override. Defaults to `safe_visible` (visible
214
- * immediately at original fidelity; optimized variants swap in async).
215
- * Set to `safe_public` to make the upload promise *await* the optimized
216
- * variant (image) or HLS playlist (video) before resolving — useful for
217
- * broadcast / UGC apps that gate publication on processing complete.
218
- *
219
- * The per-app default lives in `application_storage_settings.media_policy`
220
- * (Phase 4 / P3, live in prod 2026-04-26). Reading the per-app default
221
- * into `useMedia` defaults lands in a follow-up; today the option is
222
- * caller-provided per call.
223
- */
224
- policy?: MediaPolicy;
225
- /** Display filename (sanitized server-side). */
226
- filename?: string;
227
- /** Custom metadata attached to the file. */
228
- metadata?: Record<string, unknown>;
229
- /** Upload progress callback (0-100). */
230
- onProgress?: (percent: number) => void;
231
- /** AbortSignal for cancellation. */
232
- signal?: AbortSignal;
233
- /** Force a non-photo path even for image MIME types — useful for
234
- * generic file uploads where you don't want the photo optimizer
235
- * to register the file. Default: `false`. */
236
- skipPhotoRegister?: boolean;
237
- }
238
- interface UseMediaReturn {
239
- /** Upload a file. MIME-aware: images go through photo register +
240
- * optimization; everything else goes through generic storage. */
241
- upload: (file: File | Blob, options?: UseMediaUploadOptions) => Promise<MediaUploadResult>;
242
- /** Cancel an upload by its `file_id`. Deletes the storage object so
243
- * it doesn't orphan in S3 — useful when a chat composer accepts a
244
- * file but the user removes it before sending. Idempotent. */
245
- cancelUpload: (fileId: string) => Promise<void>;
246
- /** Last error from `upload` or `cancelUpload`. */
247
- error: ApiError$1 | null;
248
- /** True while an upload is in progress. */
249
- uploading: boolean;
250
- }
251
- /**
252
- * Opinionated, MIME-aware media upload hook.
253
- *
254
- * `useMedia()` is the canonical upload primitive for chat / progressive
255
- * media use. It branches by MIME type:
256
- * - `image/*` → `client.photo.uploadViaStorage()` — upload to storage,
257
- * then register with the photo service so the on-demand transform
258
- * endpoint resolves to optimized variants. The returned
259
- * `optimized_url_promise` resolves once the optimizer finishes.
260
- * - everything else → `client.storage.uploadPrivate()` — a private,
261
- * uncompressed, fail-closed upload to generic storage.
262
- *
263
- * **Default visibility is `is_public: false`.** Public is opt-in per call.
264
- * `useMedia()` does not expose `is_public` via app-level config — visibility
265
- * is always an explicit per-call surface choice.
266
- *
267
- * Compared to `useContent()`:
268
- * - `useContent()` is a thin wrapper over generic storage and does not
269
- * register photos / videos with their typed services. Use it for plain
270
- * file uploads where you don't need optimization or transcoding.
271
- * - `useMedia()` defaults to private + no compression, integrates with the
272
- * typed media services automatically, and is the right primitive for
273
- * anything chat- or media-shaped.
274
- *
275
- * @example
276
- * ```tsx
277
- * 'use client';
278
- *
279
- * import { useMedia, ScaleMuleMedia } from '@scalemule/nextjs';
280
- *
281
- * function ChatComposer({ onAttach }) {
282
- * const { upload, uploading } = useMedia();
283
- *
284
- * async function handlePick(file: File) {
285
- * const result = await upload(file);
286
- * onAttach({
287
- * file_id: result.file_id,
288
- * mime_type: result.mime_type,
289
- * optimized_url_promise: result.optimized_url_promise,
290
- * });
291
- * }
292
- *
293
- * return <input type="file" disabled={uploading}
294
- * onChange={(e) => e.target.files?.[0] && handlePick(e.target.files[0])} />;
295
- * }
296
- * ```
297
- *
298
- * See `docs/MEDIA-UPLOADS.md` in the platform repo for the decision
299
- * tree and the full anti-patterns list.
300
- */
301
- declare function useMedia(): UseMediaReturn;
302
-
303
327
  interface UseFileStatusOptions {
304
328
  /** Storage file_id to read status for. */
305
329
  fileId: string | null | undefined;
@@ -326,7 +350,7 @@ interface UseFileStatusReturn {
326
350
  /** True while a fetch is in flight. */
327
351
  loading: boolean;
328
352
  /** Last error, or null. */
329
- error: ApiError$1 | null;
353
+ error: ApiError | null;
330
354
  /**
331
355
  * Convenience: scan is clean. For images/videos, this means the file
332
356
  * is *safe to render*; the optimized / HLS variants may still be
@@ -586,7 +610,7 @@ interface UseFeatureFlagsOptions {
586
610
  interface UseFeatureFlagsReturn {
587
611
  flags: Record<string, FeatureFlagEvaluation>;
588
612
  loading: boolean;
589
- error: ApiError | null;
613
+ error: ApiError$1 | null;
590
614
  refresh: () => Promise<void>;
591
615
  isEnabled: (flagKey: string, fallback?: boolean) => boolean;
592
616
  getFlag: <T = unknown>(flagKey: string, fallback?: T) => T;
@@ -613,7 +637,7 @@ interface UsePushNotificationsReturn {
613
637
  /** Whether an operation is in progress */
614
638
  isLoading: boolean;
615
639
  /** Last error */
616
- error: ApiError | null;
640
+ error: ApiError$1 | null;
617
641
  /** Request permission and subscribe to push notifications */
618
642
  subscribe: () => Promise<void>;
619
643
  /** Unsubscribe from push notifications */
@@ -714,7 +738,7 @@ interface UseFeedbackResult {
714
738
  /** End-user's own feedback items for the current tenant. Empty when not signed in. */
715
739
  items: FeedbackItem[];
716
740
  loading: boolean;
717
- error: ApiError | null;
741
+ error: ApiError$1 | null;
718
742
  /** Submit a new feedback item. Returns the persisted item on success. */
719
743
  submit: (input: FeedbackItemInput) => Promise<FeedbackItem>;
720
744
  /** Re-fetch the list. */
@@ -926,4 +950,4 @@ declare function createSafeLogger(prefix: string): {
926
950
  error: (message: string, data?: unknown) => void;
927
951
  };
928
952
 
929
- export { ApiError, type FeatureFlagEvaluation, type FeatureFlagEvaluation as FeatureFlagResult, type FeedbackItem, type FeedbackItemInput, type FeedbackPriority, type FeedbackStatus, type FeedbackType, FeedbackWidget, type FeedbackWidgetConfig, type FeedbackWidgetProps, ListFilesParams, LoginResponse, type MediaPolicy, type MediaUploadResult, type PasswordValidationResult, type PhoneCountry, type PhoneValidationResult, type RealtimeEvent, type RealtimeMessage, type RealtimeStatus, ScaleMuleClient, ScaleMuleConfig, ScaleMuleMedia, type ScaleMuleMediaProps, ScaleMuleProvider, type ScaleMuleProviderProps, UseAnalyticsOptions, UseAnalyticsReturn, UseAuthReturn, UseBillingReturn, UseContentReturn, type UseFeatureFlagsOptions, type UseFeatureFlagsReturn, type UseFeedbackOptions, type UseFeedbackResult, type UseFileStatusOptions, type UseFileStatusReturn, type UseFeatureFlagsOptions as UseFlagsOptions, type UseFeatureFlagsReturn as UseFlagsReturn, type UseMediaReturn, type UseMediaUploadOptions, type UsePushNotificationsOptions, type UsePushNotificationsReturn, type UseRealtimeOptions, type UseRealtimeReturn, type UseShareOptions, type UseShareReturn, UseUserReturn, User, type UsernameValidationResult, composePhone, createSafeLogger, normalizePhone, phoneCountries, sanitizeForLog, useAnalytics, useAuth, useBilling, useContent, useFeatureFlags, useFeedback, useFileStatus, useMedia, useMoney, useMoneyClient, usePushNotifications, useRealtime, useScaleMule, useScaleMuleClient, useShare, useUser, validateForm, validators };
953
+ export { ApiError$1 as ApiError, type FeatureFlagEvaluation, type FeatureFlagEvaluation as FeatureFlagResult, type FeedbackItem, type FeedbackItemInput, type FeedbackPriority, type FeedbackStatus, type FeedbackType, FeedbackWidget, type FeedbackWidgetConfig, type FeedbackWidgetProps, ListFilesParams, LoginResponse, type MediaPolicy, type MediaUploadResult, type PasswordValidationResult, type PhoneCountry, type PhoneValidationResult, type RealtimeEvent, type RealtimeMessage, type RealtimeStatus, ScaleMuleClient, ScaleMuleConfig, ScaleMuleMedia, type ScaleMuleMediaProps, ScaleMuleProvider, type ScaleMuleProviderProps, UseAnalyticsOptions, UseAnalyticsReturn, UseAuthReturn, UseBillingReturn, UseContentReturn, type UseFeatureFlagsOptions, type UseFeatureFlagsReturn, type UseFeedbackOptions, type UseFeedbackResult, type UseFileStatusOptions, type UseFileStatusReturn, type UseFeatureFlagsOptions as UseFlagsOptions, type UseFeatureFlagsReturn as UseFlagsReturn, type UseMediaReturn, type UseMediaUploadOptions, type UsePushNotificationsOptions, type UsePushNotificationsReturn, type UseRealtimeOptions, type UseRealtimeReturn, type UseShareOptions, type UseShareReturn, UseUserReturn, User, type UsernameValidationResult, composePhone, createSafeLogger, normalizePhone, phoneCountries, sanitizeForLog, useAnalytics, useAuth, useBilling, useContent, useFeatureFlags, useFeedback, useFileStatus, useMedia, useMoney, useMoneyClient, usePushNotifications, useRealtime, useScaleMule, useScaleMuleClient, useShare, useUser, validateForm, validators };
package/dist/index.js CHANGED
@@ -2727,6 +2727,25 @@ var StorageService = class extends ServiceModule {
2727
2727
  async getInfo(fileId, options) {
2728
2728
  return this._get(`/files/${fileId}/info`, options);
2729
2729
  }
2730
+ /**
2731
+ * Get the calling app's storage + media settings — content policy,
2732
+ * retention, max upload size, and the per-app `media_policy`.
2733
+ *
2734
+ * `media_policy` drives release-gating in the SDK upload helpers:
2735
+ * `fast_trusted` / `safe_visible` resolve immediately on upload; the
2736
+ * `safe_public` / `moderated` / `compliance` modes await pipeline
2737
+ * completion before resolving the upload promise.
2738
+ */
2739
+ async getSettings(options) {
2740
+ return this._get("/settings", options);
2741
+ }
2742
+ /**
2743
+ * Update the calling app's storage + media settings. Admin-only on the
2744
+ * platform side (callers without the right role get 403).
2745
+ */
2746
+ async updateSettings(settings, options) {
2747
+ return this.put("/settings", settings, options);
2748
+ }
2730
2749
  /**
2731
2750
  * Aggregate status for a file — single call returns scan + reserved
2732
2751
  * optimize/transcode slots + the canonical view URL paths for image
@@ -5678,6 +5697,72 @@ var PhotoService = class extends ServiceModule {
5678
5697
  return this.get(id);
5679
5698
  }
5680
5699
  };
5700
+ var AudioService = class extends ServiceModule {
5701
+ /**
5702
+ * @param storage Required for {@link uploadViaStorage}. Wired up by the
5703
+ * top-level {@link ScaleMule} constructor — most call sites should not
5704
+ * instantiate `AudioService` directly.
5705
+ */
5706
+ constructor(client, storage) {
5707
+ super(client);
5708
+ this.storage = storage;
5709
+ this.basePath = "/v1/audios";
5710
+ }
5711
+ /**
5712
+ * Register a storage-uploaded audio asset with the audio service.
5713
+ * Idempotent — re-calling with the same `file_id` returns the existing
5714
+ * record.
5715
+ */
5716
+ async register(args, options) {
5717
+ return this.post("/register", { file_id: args.fileId, sm_user_id: args.userId }, options);
5718
+ }
5719
+ /**
5720
+ * Upload a file to storage (browser → S3 direct, private, uncompressed)
5721
+ * and register it with the audio service.
5722
+ *
5723
+ * Mirrors `photo.uploadViaStorage()` / `video.uploadViaStorage()`. If
5724
+ * `register()` fails after a successful storage upload, the file is *not*
5725
+ * lost — the returned `file_id` is still valid as a generic private storage
5726
+ * file. The SDK logs a warning and returns `audio_id: null`.
5727
+ */
5728
+ async uploadViaStorage(file, uploadOptions, requestOptions) {
5729
+ const uploadResult = await this.storage.uploadPrivate(file, {
5730
+ filename: uploadOptions?.filename,
5731
+ metadata: uploadOptions?.metadata,
5732
+ onProgress: uploadOptions?.onProgress,
5733
+ signal: uploadOptions?.signal
5734
+ });
5735
+ if (uploadResult.error || !uploadResult.data) {
5736
+ return { data: null, error: uploadResult.error };
5737
+ }
5738
+ const fileInfo = uploadResult.data;
5739
+ const fileId = fileInfo.id;
5740
+ const originalViewUrl = fileInfo.url ?? null;
5741
+ const registerResult = await this.register({ fileId, userId: uploadOptions?.userId }, requestOptions);
5742
+ if (registerResult.error || !registerResult.data) {
5743
+ console.warn(
5744
+ "[scalemule-sdk] audio.register() failed after storage upload; typed audio metadata unavailable.",
5745
+ registerResult.error
5746
+ );
5747
+ return {
5748
+ data: {
5749
+ file_id: fileId,
5750
+ audio_id: null,
5751
+ original_view_url: originalViewUrl
5752
+ },
5753
+ error: null
5754
+ };
5755
+ }
5756
+ return {
5757
+ data: {
5758
+ file_id: fileId,
5759
+ audio_id: registerResult.data.audio_id,
5760
+ original_view_url: originalViewUrl
5761
+ },
5762
+ error: null
5763
+ };
5764
+ }
5765
+ };
5681
5766
  var DeadLetterApi = class extends ServiceModule {
5682
5767
  constructor() {
5683
5768
  super(...arguments);
@@ -6497,6 +6582,7 @@ var ScaleMule = class {
6497
6582
  this.graph = new GraphService(this._client);
6498
6583
  this.functions = new FunctionsService(this._client);
6499
6584
  this.photo = new PhotoService(this._client, this.storage);
6585
+ this.audio = new AudioService(this._client, this.storage);
6500
6586
  this.flagContent = new FlagContentService(this._client);
6501
6587
  this.creatorMaker = new CreatorMakerService(this._client);
6502
6588
  this.compliance = new ComplianceService(this._client);
@@ -7445,7 +7531,8 @@ function ScaleMuleProvider({
7445
7531
  onLogin,
7446
7532
  onLogout,
7447
7533
  onAuthError,
7448
- bootstrapFlags
7534
+ bootstrapFlags,
7535
+ mediaPolicy
7449
7536
  }) {
7450
7537
  const [user, setUser] = React.useState(null);
7451
7538
  const [initializing, setInitializing] = React.useState(true);
@@ -7584,6 +7671,8 @@ function ScaleMuleProvider({
7584
7671
  storage: baseClient.storage,
7585
7672
  photo: baseClient.photo,
7586
7673
  video: baseClient.video,
7674
+ audio: baseClient.audio,
7675
+ mediaPolicy,
7587
7676
  user,
7588
7677
  setUser: handleSetUser,
7589
7678
  initializing,
@@ -7598,7 +7687,7 @@ function ScaleMuleProvider({
7598
7687
  accountSwitcherPrivacy,
7599
7688
  bootstrapFlags
7600
7689
  }),
7601
- [client, money$1, baseClient, user, handleSetUser, initializing, error, analyticsProxyUrl, authProxyUrl, publishableKey, resolvedGatewayUrl, environment, enableAccountSwitcher, accountSwitcherPrivacy, bootstrapFlags]
7690
+ [client, money$1, baseClient, user, handleSetUser, initializing, error, analyticsProxyUrl, authProxyUrl, publishableKey, resolvedGatewayUrl, environment, enableAccountSwitcher, accountSwitcherPrivacy, bootstrapFlags, mediaPolicy]
7602
7691
  );
7603
7692
  return /* @__PURE__ */ jsxRuntime.jsx(ScaleMuleContext.Provider, { value, children });
7604
7693
  }
@@ -8897,7 +8986,7 @@ function useContent(options = {}) {
8897
8986
  );
8898
8987
  }
8899
8988
  function useMedia() {
8900
- const { storage, photo, video } = useScaleMule();
8989
+ const { storage, photo, video, audio, mediaPolicy: providerDefaultPolicy } = useScaleMule();
8901
8990
  const [uploading, setUploading] = React.useState(false);
8902
8991
  const [error, setError] = React.useState(null);
8903
8992
  const upload = React.useCallback(
@@ -8906,7 +8995,7 @@ function useMedia() {
8906
8995
  setError(null);
8907
8996
  const isPublic = options?.is_public ?? false;
8908
8997
  const mimeType = file.type || "application/octet-stream";
8909
- const policy = options?.policy ?? "safe_visible";
8998
+ const policy = options?.policy ?? providerDefaultPolicy ?? "safe_visible";
8910
8999
  const gateOnPipeline = policy === "safe_public" || policy === "moderated" || policy === "compliance";
8911
9000
  const sharedOpts = {
8912
9001
  filename: options?.filename,
@@ -8951,6 +9040,21 @@ function useMedia() {
8951
9040
  is_public: false
8952
9041
  };
8953
9042
  }
9043
+ if (mimeType.startsWith("audio/") && !isPublic) {
9044
+ const r2 = await audio.uploadViaStorage(file, sharedOpts);
9045
+ if (r2.error || !r2.data) {
9046
+ throw r2.error ?? { code: "upload_error", message: "Upload failed", status: 0 };
9047
+ }
9048
+ return {
9049
+ file_id: r2.data.file_id,
9050
+ photo_id: null,
9051
+ original_view_url: r2.data.original_view_url,
9052
+ optimized_url_promise: Promise.resolve(null),
9053
+ hls_url_promise: Promise.resolve(null),
9054
+ mime_type: mimeType,
9055
+ is_public: false
9056
+ };
9057
+ }
8954
9058
  if (mimeType.startsWith("image/") && isPublic) {
8955
9059
  const r2 = await storage.upload(file, {
8956
9060
  ...sharedOpts,
@@ -8993,7 +9097,7 @@ function useMedia() {
8993
9097
  setUploading(false);
8994
9098
  }
8995
9099
  },
8996
- [storage, photo]
9100
+ [storage, photo, video, audio, providerDefaultPolicy]
8997
9101
  );
8998
9102
  const cancelUpload = React.useCallback(
8999
9103
  async (fileId) => {
package/dist/index.mjs CHANGED
@@ -2707,6 +2707,25 @@ var StorageService = class extends ServiceModule {
2707
2707
  async getInfo(fileId, options) {
2708
2708
  return this._get(`/files/${fileId}/info`, options);
2709
2709
  }
2710
+ /**
2711
+ * Get the calling app's storage + media settings — content policy,
2712
+ * retention, max upload size, and the per-app `media_policy`.
2713
+ *
2714
+ * `media_policy` drives release-gating in the SDK upload helpers:
2715
+ * `fast_trusted` / `safe_visible` resolve immediately on upload; the
2716
+ * `safe_public` / `moderated` / `compliance` modes await pipeline
2717
+ * completion before resolving the upload promise.
2718
+ */
2719
+ async getSettings(options) {
2720
+ return this._get("/settings", options);
2721
+ }
2722
+ /**
2723
+ * Update the calling app's storage + media settings. Admin-only on the
2724
+ * platform side (callers without the right role get 403).
2725
+ */
2726
+ async updateSettings(settings, options) {
2727
+ return this.put("/settings", settings, options);
2728
+ }
2710
2729
  /**
2711
2730
  * Aggregate status for a file — single call returns scan + reserved
2712
2731
  * optimize/transcode slots + the canonical view URL paths for image
@@ -5658,6 +5677,72 @@ var PhotoService = class extends ServiceModule {
5658
5677
  return this.get(id);
5659
5678
  }
5660
5679
  };
5680
+ var AudioService = class extends ServiceModule {
5681
+ /**
5682
+ * @param storage Required for {@link uploadViaStorage}. Wired up by the
5683
+ * top-level {@link ScaleMule} constructor — most call sites should not
5684
+ * instantiate `AudioService` directly.
5685
+ */
5686
+ constructor(client, storage) {
5687
+ super(client);
5688
+ this.storage = storage;
5689
+ this.basePath = "/v1/audios";
5690
+ }
5691
+ /**
5692
+ * Register a storage-uploaded audio asset with the audio service.
5693
+ * Idempotent — re-calling with the same `file_id` returns the existing
5694
+ * record.
5695
+ */
5696
+ async register(args, options) {
5697
+ return this.post("/register", { file_id: args.fileId, sm_user_id: args.userId }, options);
5698
+ }
5699
+ /**
5700
+ * Upload a file to storage (browser → S3 direct, private, uncompressed)
5701
+ * and register it with the audio service.
5702
+ *
5703
+ * Mirrors `photo.uploadViaStorage()` / `video.uploadViaStorage()`. If
5704
+ * `register()` fails after a successful storage upload, the file is *not*
5705
+ * lost — the returned `file_id` is still valid as a generic private storage
5706
+ * file. The SDK logs a warning and returns `audio_id: null`.
5707
+ */
5708
+ async uploadViaStorage(file, uploadOptions, requestOptions) {
5709
+ const uploadResult = await this.storage.uploadPrivate(file, {
5710
+ filename: uploadOptions?.filename,
5711
+ metadata: uploadOptions?.metadata,
5712
+ onProgress: uploadOptions?.onProgress,
5713
+ signal: uploadOptions?.signal
5714
+ });
5715
+ if (uploadResult.error || !uploadResult.data) {
5716
+ return { data: null, error: uploadResult.error };
5717
+ }
5718
+ const fileInfo = uploadResult.data;
5719
+ const fileId = fileInfo.id;
5720
+ const originalViewUrl = fileInfo.url ?? null;
5721
+ const registerResult = await this.register({ fileId, userId: uploadOptions?.userId }, requestOptions);
5722
+ if (registerResult.error || !registerResult.data) {
5723
+ console.warn(
5724
+ "[scalemule-sdk] audio.register() failed after storage upload; typed audio metadata unavailable.",
5725
+ registerResult.error
5726
+ );
5727
+ return {
5728
+ data: {
5729
+ file_id: fileId,
5730
+ audio_id: null,
5731
+ original_view_url: originalViewUrl
5732
+ },
5733
+ error: null
5734
+ };
5735
+ }
5736
+ return {
5737
+ data: {
5738
+ file_id: fileId,
5739
+ audio_id: registerResult.data.audio_id,
5740
+ original_view_url: originalViewUrl
5741
+ },
5742
+ error: null
5743
+ };
5744
+ }
5745
+ };
5661
5746
  var DeadLetterApi = class extends ServiceModule {
5662
5747
  constructor() {
5663
5748
  super(...arguments);
@@ -6477,6 +6562,7 @@ var ScaleMule = class {
6477
6562
  this.graph = new GraphService(this._client);
6478
6563
  this.functions = new FunctionsService(this._client);
6479
6564
  this.photo = new PhotoService(this._client, this.storage);
6565
+ this.audio = new AudioService(this._client, this.storage);
6480
6566
  this.flagContent = new FlagContentService(this._client);
6481
6567
  this.creatorMaker = new CreatorMakerService(this._client);
6482
6568
  this.compliance = new ComplianceService(this._client);
@@ -7425,7 +7511,8 @@ function ScaleMuleProvider({
7425
7511
  onLogin,
7426
7512
  onLogout,
7427
7513
  onAuthError,
7428
- bootstrapFlags
7514
+ bootstrapFlags,
7515
+ mediaPolicy
7429
7516
  }) {
7430
7517
  const [user, setUser] = useState(null);
7431
7518
  const [initializing, setInitializing] = useState(true);
@@ -7564,6 +7651,8 @@ function ScaleMuleProvider({
7564
7651
  storage: baseClient.storage,
7565
7652
  photo: baseClient.photo,
7566
7653
  video: baseClient.video,
7654
+ audio: baseClient.audio,
7655
+ mediaPolicy,
7567
7656
  user,
7568
7657
  setUser: handleSetUser,
7569
7658
  initializing,
@@ -7578,7 +7667,7 @@ function ScaleMuleProvider({
7578
7667
  accountSwitcherPrivacy,
7579
7668
  bootstrapFlags
7580
7669
  }),
7581
- [client, money, baseClient, user, handleSetUser, initializing, error, analyticsProxyUrl, authProxyUrl, publishableKey, resolvedGatewayUrl, environment, enableAccountSwitcher, accountSwitcherPrivacy, bootstrapFlags]
7670
+ [client, money, baseClient, user, handleSetUser, initializing, error, analyticsProxyUrl, authProxyUrl, publishableKey, resolvedGatewayUrl, environment, enableAccountSwitcher, accountSwitcherPrivacy, bootstrapFlags, mediaPolicy]
7582
7671
  );
7583
7672
  return /* @__PURE__ */ jsx(ScaleMuleContext.Provider, { value, children });
7584
7673
  }
@@ -8877,7 +8966,7 @@ function useContent(options = {}) {
8877
8966
  );
8878
8967
  }
8879
8968
  function useMedia() {
8880
- const { storage, photo, video } = useScaleMule();
8969
+ const { storage, photo, video, audio, mediaPolicy: providerDefaultPolicy } = useScaleMule();
8881
8970
  const [uploading, setUploading] = useState(false);
8882
8971
  const [error, setError] = useState(null);
8883
8972
  const upload = useCallback(
@@ -8886,7 +8975,7 @@ function useMedia() {
8886
8975
  setError(null);
8887
8976
  const isPublic = options?.is_public ?? false;
8888
8977
  const mimeType = file.type || "application/octet-stream";
8889
- const policy = options?.policy ?? "safe_visible";
8978
+ const policy = options?.policy ?? providerDefaultPolicy ?? "safe_visible";
8890
8979
  const gateOnPipeline = policy === "safe_public" || policy === "moderated" || policy === "compliance";
8891
8980
  const sharedOpts = {
8892
8981
  filename: options?.filename,
@@ -8931,6 +9020,21 @@ function useMedia() {
8931
9020
  is_public: false
8932
9021
  };
8933
9022
  }
9023
+ if (mimeType.startsWith("audio/") && !isPublic) {
9024
+ const r2 = await audio.uploadViaStorage(file, sharedOpts);
9025
+ if (r2.error || !r2.data) {
9026
+ throw r2.error ?? { code: "upload_error", message: "Upload failed", status: 0 };
9027
+ }
9028
+ return {
9029
+ file_id: r2.data.file_id,
9030
+ photo_id: null,
9031
+ original_view_url: r2.data.original_view_url,
9032
+ optimized_url_promise: Promise.resolve(null),
9033
+ hls_url_promise: Promise.resolve(null),
9034
+ mime_type: mimeType,
9035
+ is_public: false
9036
+ };
9037
+ }
8934
9038
  if (mimeType.startsWith("image/") && isPublic) {
8935
9039
  const r2 = await storage.upload(file, {
8936
9040
  ...sharedOpts,
@@ -8973,7 +9077,7 @@ function useMedia() {
8973
9077
  setUploading(false);
8974
9078
  }
8975
9079
  },
8976
- [storage, photo]
9080
+ [storage, photo, video, audio, providerDefaultPolicy]
8977
9081
  );
8978
9082
  const cancelUpload = useCallback(
8979
9083
  async (fileId) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalemule/nextjs",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
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",
@@ -49,11 +49,12 @@
49
49
  "test:watch": "vitest",
50
50
  "type-check": "tsc --noEmit",
51
51
  "lint": "tsc --noEmit",
52
- "clean": "rm -rf dist"
52
+ "clean": "rm -rf dist",
53
+ "prepublishOnly": "node -e \"const v=require('./package.json').version; if(!/^0\\.(0|1)\\.\\d+$/.test(v)){console.error('ERR: SDK '+v+' violates patch-only policy (0.0.x or 0.1.x only). See CONTRIBUTING.md SDK versioning section.');process.exit(1)}\""
53
54
  },
54
55
  "dependencies": {
55
56
  "@scalemule/money": "^0.0.1",
56
- "@scalemule/sdk": "^0.0.41"
57
+ "@scalemule/sdk": "^0.0.43"
57
58
  },
58
59
  "peerDependencies": {
59
60
  "next": ">=14.0.0",