@scalemule/nextjs 0.1.13 → 0.1.14

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
@@ -324,6 +324,42 @@ interface UseContentOptions {
324
324
  */
325
325
  declare function useContent(options?: UseContentOptions): UseContentReturn;
326
326
 
327
+ interface AudioFile {
328
+ audio_id: string;
329
+ file_id: string;
330
+ filename: string;
331
+ mime_type: string;
332
+ size_bytes: number;
333
+ status: string;
334
+ codec: string | null;
335
+ bit_rate_kbps: number | null;
336
+ duration_ms: number | null;
337
+ created_at: string;
338
+ original_view_url: string | null;
339
+ transcoded_url: string | null;
340
+ waveform_peaks?: unknown;
341
+ }
342
+ interface UseAudioUploadOptions {
343
+ filename?: string;
344
+ metadata?: Record<string, unknown>;
345
+ onProgress?: (progress: number) => void;
346
+ signal?: AbortSignal;
347
+ }
348
+ interface AudioUploadResult {
349
+ file_id: string;
350
+ audio_id: string | null;
351
+ original_view_url: string | null;
352
+ transcoded_url_promise: Promise<string | null>;
353
+ }
354
+ interface UseAudioReturn {
355
+ upload: (file: File | Blob, opts?: UseAudioUploadOptions) => Promise<AudioUploadResult>;
356
+ list: () => Promise<AudioFile[]>;
357
+ remove: (fileId: string) => Promise<void>;
358
+ error: ApiError | null;
359
+ loading: boolean;
360
+ }
361
+ declare function useAudio(): UseAudioReturn;
362
+
327
363
  /**
328
364
  * Conversation kinds recognized by the chat realtime channel naming scheme.
329
365
  * Matches `broadcast_to_conversation` in `ms/scalemule-chat/src/realtime.rs`.
@@ -996,4 +1032,4 @@ declare function createSafeLogger(prefix: string): {
996
1032
  error: (message: string, data?: unknown) => void;
997
1033
  };
998
1034
 
999
- export { ApiError$1 as ApiError, type ConversationKind, 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 };
1035
+ export { ApiError$1 as ApiError, type AudioFile, type AudioUploadResult, type ConversationKind, 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, type UseAudioReturn, type UseAudioUploadOptions, 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, useAudio, useAuth, useBilling, useContent, useFeatureFlags, useFeedback, useFileStatus, useMedia, useMoney, useMoneyClient, usePushNotifications, useRealtime, useScaleMule, useScaleMuleClient, useShare, useUser, validateForm, validators };
package/dist/index.d.ts CHANGED
@@ -324,6 +324,42 @@ interface UseContentOptions {
324
324
  */
325
325
  declare function useContent(options?: UseContentOptions): UseContentReturn;
326
326
 
327
+ interface AudioFile {
328
+ audio_id: string;
329
+ file_id: string;
330
+ filename: string;
331
+ mime_type: string;
332
+ size_bytes: number;
333
+ status: string;
334
+ codec: string | null;
335
+ bit_rate_kbps: number | null;
336
+ duration_ms: number | null;
337
+ created_at: string;
338
+ original_view_url: string | null;
339
+ transcoded_url: string | null;
340
+ waveform_peaks?: unknown;
341
+ }
342
+ interface UseAudioUploadOptions {
343
+ filename?: string;
344
+ metadata?: Record<string, unknown>;
345
+ onProgress?: (progress: number) => void;
346
+ signal?: AbortSignal;
347
+ }
348
+ interface AudioUploadResult {
349
+ file_id: string;
350
+ audio_id: string | null;
351
+ original_view_url: string | null;
352
+ transcoded_url_promise: Promise<string | null>;
353
+ }
354
+ interface UseAudioReturn {
355
+ upload: (file: File | Blob, opts?: UseAudioUploadOptions) => Promise<AudioUploadResult>;
356
+ list: () => Promise<AudioFile[]>;
357
+ remove: (fileId: string) => Promise<void>;
358
+ error: ApiError | null;
359
+ loading: boolean;
360
+ }
361
+ declare function useAudio(): UseAudioReturn;
362
+
327
363
  /**
328
364
  * Conversation kinds recognized by the chat realtime channel naming scheme.
329
365
  * Matches `broadcast_to_conversation` in `ms/scalemule-chat/src/realtime.rs`.
@@ -996,4 +1032,4 @@ declare function createSafeLogger(prefix: string): {
996
1032
  error: (message: string, data?: unknown) => void;
997
1033
  };
998
1034
 
999
- export { ApiError$1 as ApiError, type ConversationKind, 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 };
1035
+ export { ApiError$1 as ApiError, type AudioFile, type AudioUploadResult, type ConversationKind, 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, type UseAudioReturn, type UseAudioUploadOptions, 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, useAudio, useAuth, useBilling, useContent, useFeatureFlags, useFeedback, useFileStatus, useMedia, useMoney, useMoneyClient, usePushNotifications, useRealtime, useScaleMule, useScaleMuleClient, useShare, useUser, validateForm, validators };
package/dist/index.js CHANGED
@@ -9036,6 +9036,193 @@ function useContent(options = {}) {
9036
9036
  ]
9037
9037
  );
9038
9038
  }
9039
+ var AUDIO_POLL_INTERVAL_MS = 1e3;
9040
+ var AUDIO_POLL_MAX_ATTEMPTS = 30;
9041
+ function isAudioFile(file) {
9042
+ return file.content_type.startsWith("audio/");
9043
+ }
9044
+ function toStorageBackedAudioFile(file) {
9045
+ return {
9046
+ audio_id: file.id,
9047
+ file_id: file.id,
9048
+ filename: file.filename,
9049
+ mime_type: file.content_type,
9050
+ size_bytes: file.size_bytes,
9051
+ status: "pending_transcode",
9052
+ codec: null,
9053
+ bit_rate_kbps: null,
9054
+ duration_ms: null,
9055
+ created_at: file.created_at,
9056
+ original_view_url: file.url ?? null,
9057
+ transcoded_url: null
9058
+ };
9059
+ }
9060
+ function isNotFoundError(error) {
9061
+ if (!error || typeof error !== "object") {
9062
+ return false;
9063
+ }
9064
+ const candidate = error;
9065
+ return candidate.status === 404 || candidate.statusCode === 404 || candidate.response?.status === 404 || candidate.code === "not_found" || candidate.message?.toLowerCase().includes("not found") === true;
9066
+ }
9067
+ function isAbortError(error) {
9068
+ return error instanceof DOMException && error.name === "AbortError" || !!error && typeof error === "object" && "name" in error && error.name === "AbortError";
9069
+ }
9070
+ async function waitForPollInterval(signal) {
9071
+ if (signal?.aborted) {
9072
+ return false;
9073
+ }
9074
+ return new Promise((resolve) => {
9075
+ const timeoutId = window.setTimeout(() => {
9076
+ cleanup();
9077
+ resolve(true);
9078
+ }, AUDIO_POLL_INTERVAL_MS);
9079
+ const onAbort = () => {
9080
+ cleanup();
9081
+ resolve(false);
9082
+ };
9083
+ const cleanup = () => {
9084
+ window.clearTimeout(timeoutId);
9085
+ signal?.removeEventListener("abort", onAbort);
9086
+ };
9087
+ signal?.addEventListener("abort", onAbort, { once: true });
9088
+ });
9089
+ }
9090
+ function useAudio() {
9091
+ const { client, audio } = useScaleMule();
9092
+ const [loading, setLoading] = React.useState(false);
9093
+ const [error, setError] = React.useState(null);
9094
+ const pollTranscodedUrl = React.useCallback(
9095
+ async (audioId, signal) => {
9096
+ for (let attempt = 0; attempt < AUDIO_POLL_MAX_ATTEMPTS; attempt++) {
9097
+ if (signal?.aborted) {
9098
+ return null;
9099
+ }
9100
+ try {
9101
+ const details = await client.get(`/v1/audios/${audioId}`);
9102
+ if (details.status === "ready" && details.url) {
9103
+ return details.url;
9104
+ }
9105
+ if (details.status === "failed") {
9106
+ return null;
9107
+ }
9108
+ } catch (err) {
9109
+ if (isAbortError(err) || signal?.aborted) {
9110
+ return null;
9111
+ }
9112
+ if (!isNotFoundError(err)) {
9113
+ throw err;
9114
+ }
9115
+ }
9116
+ if (!await waitForPollInterval(signal)) {
9117
+ return null;
9118
+ }
9119
+ }
9120
+ return null;
9121
+ },
9122
+ [client]
9123
+ );
9124
+ const upload = React.useCallback(
9125
+ async (file, opts) => {
9126
+ setLoading(true);
9127
+ setError(null);
9128
+ try {
9129
+ const result = await audio.uploadViaStorage(file, {
9130
+ filename: opts?.filename,
9131
+ metadata: opts?.metadata,
9132
+ onProgress: opts?.onProgress,
9133
+ signal: opts?.signal
9134
+ });
9135
+ if (result.error || !result.data) {
9136
+ throw result.error ?? { code: "upload_error", message: "Audio upload failed" };
9137
+ }
9138
+ return {
9139
+ file_id: result.data.file_id,
9140
+ audio_id: result.data.audio_id,
9141
+ original_view_url: result.data.original_view_url,
9142
+ transcoded_url_promise: result.data.audio_id ? pollTranscodedUrl(result.data.audio_id, opts?.signal) : Promise.resolve(null)
9143
+ };
9144
+ } catch (err) {
9145
+ const apiError = err;
9146
+ setError(apiError);
9147
+ throw err;
9148
+ } finally {
9149
+ setLoading(false);
9150
+ }
9151
+ },
9152
+ [audio, pollTranscodedUrl]
9153
+ );
9154
+ const list = React.useCallback(async () => {
9155
+ setLoading(true);
9156
+ setError(null);
9157
+ try {
9158
+ const response = await client.get("/v1/storage/my-files");
9159
+ const audioFiles = response.files.filter(isAudioFile);
9160
+ const enriched = await Promise.all(
9161
+ audioFiles.map(async (file) => {
9162
+ try {
9163
+ const details = await client.get(`/v1/audios/${file.id}`);
9164
+ return {
9165
+ audio_id: details.id,
9166
+ file_id: file.id,
9167
+ filename: file.filename,
9168
+ mime_type: file.content_type,
9169
+ size_bytes: details.size_bytes ?? file.size_bytes,
9170
+ status: details.status,
9171
+ codec: details.codec,
9172
+ bit_rate_kbps: details.bit_rate_kbps ?? null,
9173
+ duration_ms: details.duration_ms ?? null,
9174
+ created_at: details.created_at,
9175
+ original_view_url: file.url ?? null,
9176
+ transcoded_url: details.url ?? null,
9177
+ waveform_peaks: details.waveform_peaks
9178
+ };
9179
+ } catch {
9180
+ return toStorageBackedAudioFile(file);
9181
+ }
9182
+ })
9183
+ );
9184
+ return enriched;
9185
+ } catch (err) {
9186
+ const apiError = err;
9187
+ setError(apiError);
9188
+ throw err;
9189
+ } finally {
9190
+ setLoading(false);
9191
+ }
9192
+ }, [client]);
9193
+ const remove = React.useCallback(
9194
+ async (fileId) => {
9195
+ setLoading(true);
9196
+ setError(null);
9197
+ try {
9198
+ const [audioDeleteResult, storageDeleteResult] = await Promise.allSettled([
9199
+ client.delete(`/v1/audios/${fileId}`),
9200
+ client.delete(`/v1/storage/files/${fileId}`)
9201
+ ]);
9202
+ if (storageDeleteResult.status === "rejected" && !isNotFoundError(storageDeleteResult.reason)) {
9203
+ throw storageDeleteResult.reason ?? new Error("Storage delete failed");
9204
+ }
9205
+ if (audioDeleteResult.status === "rejected" && !isNotFoundError(audioDeleteResult.reason)) {
9206
+ console.warn("Audio record delete failed after storage delete", audioDeleteResult.reason);
9207
+ }
9208
+ } catch (err) {
9209
+ const apiError = err;
9210
+ setError(apiError);
9211
+ throw err;
9212
+ } finally {
9213
+ setLoading(false);
9214
+ }
9215
+ },
9216
+ [client]
9217
+ );
9218
+ return {
9219
+ upload,
9220
+ list,
9221
+ remove,
9222
+ error,
9223
+ loading
9224
+ };
9225
+ }
9039
9226
  function useMedia() {
9040
9227
  const { storage, photo, video, audio, mediaPolicy: providerDefaultPolicy } = useScaleMule();
9041
9228
  const [uploading, setUploading] = React.useState(false);
@@ -11261,6 +11448,7 @@ exports.normalizePhone = normalizePhone;
11261
11448
  exports.phoneCountries = phoneCountries;
11262
11449
  exports.sanitizeForLog = sanitizeForLog;
11263
11450
  exports.useAnalytics = useAnalytics;
11451
+ exports.useAudio = useAudio;
11264
11452
  exports.useAuth = useAuth;
11265
11453
  exports.useBilling = useBilling;
11266
11454
  exports.useContent = useContent;
package/dist/index.mjs CHANGED
@@ -9016,6 +9016,193 @@ function useContent(options = {}) {
9016
9016
  ]
9017
9017
  );
9018
9018
  }
9019
+ var AUDIO_POLL_INTERVAL_MS = 1e3;
9020
+ var AUDIO_POLL_MAX_ATTEMPTS = 30;
9021
+ function isAudioFile(file) {
9022
+ return file.content_type.startsWith("audio/");
9023
+ }
9024
+ function toStorageBackedAudioFile(file) {
9025
+ return {
9026
+ audio_id: file.id,
9027
+ file_id: file.id,
9028
+ filename: file.filename,
9029
+ mime_type: file.content_type,
9030
+ size_bytes: file.size_bytes,
9031
+ status: "pending_transcode",
9032
+ codec: null,
9033
+ bit_rate_kbps: null,
9034
+ duration_ms: null,
9035
+ created_at: file.created_at,
9036
+ original_view_url: file.url ?? null,
9037
+ transcoded_url: null
9038
+ };
9039
+ }
9040
+ function isNotFoundError(error) {
9041
+ if (!error || typeof error !== "object") {
9042
+ return false;
9043
+ }
9044
+ const candidate = error;
9045
+ return candidate.status === 404 || candidate.statusCode === 404 || candidate.response?.status === 404 || candidate.code === "not_found" || candidate.message?.toLowerCase().includes("not found") === true;
9046
+ }
9047
+ function isAbortError(error) {
9048
+ return error instanceof DOMException && error.name === "AbortError" || !!error && typeof error === "object" && "name" in error && error.name === "AbortError";
9049
+ }
9050
+ async function waitForPollInterval(signal) {
9051
+ if (signal?.aborted) {
9052
+ return false;
9053
+ }
9054
+ return new Promise((resolve) => {
9055
+ const timeoutId = window.setTimeout(() => {
9056
+ cleanup();
9057
+ resolve(true);
9058
+ }, AUDIO_POLL_INTERVAL_MS);
9059
+ const onAbort = () => {
9060
+ cleanup();
9061
+ resolve(false);
9062
+ };
9063
+ const cleanup = () => {
9064
+ window.clearTimeout(timeoutId);
9065
+ signal?.removeEventListener("abort", onAbort);
9066
+ };
9067
+ signal?.addEventListener("abort", onAbort, { once: true });
9068
+ });
9069
+ }
9070
+ function useAudio() {
9071
+ const { client, audio } = useScaleMule();
9072
+ const [loading, setLoading] = useState(false);
9073
+ const [error, setError] = useState(null);
9074
+ const pollTranscodedUrl = useCallback(
9075
+ async (audioId, signal) => {
9076
+ for (let attempt = 0; attempt < AUDIO_POLL_MAX_ATTEMPTS; attempt++) {
9077
+ if (signal?.aborted) {
9078
+ return null;
9079
+ }
9080
+ try {
9081
+ const details = await client.get(`/v1/audios/${audioId}`);
9082
+ if (details.status === "ready" && details.url) {
9083
+ return details.url;
9084
+ }
9085
+ if (details.status === "failed") {
9086
+ return null;
9087
+ }
9088
+ } catch (err) {
9089
+ if (isAbortError(err) || signal?.aborted) {
9090
+ return null;
9091
+ }
9092
+ if (!isNotFoundError(err)) {
9093
+ throw err;
9094
+ }
9095
+ }
9096
+ if (!await waitForPollInterval(signal)) {
9097
+ return null;
9098
+ }
9099
+ }
9100
+ return null;
9101
+ },
9102
+ [client]
9103
+ );
9104
+ const upload = useCallback(
9105
+ async (file, opts) => {
9106
+ setLoading(true);
9107
+ setError(null);
9108
+ try {
9109
+ const result = await audio.uploadViaStorage(file, {
9110
+ filename: opts?.filename,
9111
+ metadata: opts?.metadata,
9112
+ onProgress: opts?.onProgress,
9113
+ signal: opts?.signal
9114
+ });
9115
+ if (result.error || !result.data) {
9116
+ throw result.error ?? { code: "upload_error", message: "Audio upload failed" };
9117
+ }
9118
+ return {
9119
+ file_id: result.data.file_id,
9120
+ audio_id: result.data.audio_id,
9121
+ original_view_url: result.data.original_view_url,
9122
+ transcoded_url_promise: result.data.audio_id ? pollTranscodedUrl(result.data.audio_id, opts?.signal) : Promise.resolve(null)
9123
+ };
9124
+ } catch (err) {
9125
+ const apiError = err;
9126
+ setError(apiError);
9127
+ throw err;
9128
+ } finally {
9129
+ setLoading(false);
9130
+ }
9131
+ },
9132
+ [audio, pollTranscodedUrl]
9133
+ );
9134
+ const list = useCallback(async () => {
9135
+ setLoading(true);
9136
+ setError(null);
9137
+ try {
9138
+ const response = await client.get("/v1/storage/my-files");
9139
+ const audioFiles = response.files.filter(isAudioFile);
9140
+ const enriched = await Promise.all(
9141
+ audioFiles.map(async (file) => {
9142
+ try {
9143
+ const details = await client.get(`/v1/audios/${file.id}`);
9144
+ return {
9145
+ audio_id: details.id,
9146
+ file_id: file.id,
9147
+ filename: file.filename,
9148
+ mime_type: file.content_type,
9149
+ size_bytes: details.size_bytes ?? file.size_bytes,
9150
+ status: details.status,
9151
+ codec: details.codec,
9152
+ bit_rate_kbps: details.bit_rate_kbps ?? null,
9153
+ duration_ms: details.duration_ms ?? null,
9154
+ created_at: details.created_at,
9155
+ original_view_url: file.url ?? null,
9156
+ transcoded_url: details.url ?? null,
9157
+ waveform_peaks: details.waveform_peaks
9158
+ };
9159
+ } catch {
9160
+ return toStorageBackedAudioFile(file);
9161
+ }
9162
+ })
9163
+ );
9164
+ return enriched;
9165
+ } catch (err) {
9166
+ const apiError = err;
9167
+ setError(apiError);
9168
+ throw err;
9169
+ } finally {
9170
+ setLoading(false);
9171
+ }
9172
+ }, [client]);
9173
+ const remove = useCallback(
9174
+ async (fileId) => {
9175
+ setLoading(true);
9176
+ setError(null);
9177
+ try {
9178
+ const [audioDeleteResult, storageDeleteResult] = await Promise.allSettled([
9179
+ client.delete(`/v1/audios/${fileId}`),
9180
+ client.delete(`/v1/storage/files/${fileId}`)
9181
+ ]);
9182
+ if (storageDeleteResult.status === "rejected" && !isNotFoundError(storageDeleteResult.reason)) {
9183
+ throw storageDeleteResult.reason ?? new Error("Storage delete failed");
9184
+ }
9185
+ if (audioDeleteResult.status === "rejected" && !isNotFoundError(audioDeleteResult.reason)) {
9186
+ console.warn("Audio record delete failed after storage delete", audioDeleteResult.reason);
9187
+ }
9188
+ } catch (err) {
9189
+ const apiError = err;
9190
+ setError(apiError);
9191
+ throw err;
9192
+ } finally {
9193
+ setLoading(false);
9194
+ }
9195
+ },
9196
+ [client]
9197
+ );
9198
+ return {
9199
+ upload,
9200
+ list,
9201
+ remove,
9202
+ error,
9203
+ loading
9204
+ };
9205
+ }
9019
9206
  function useMedia() {
9020
9207
  const { storage, photo, video, audio, mediaPolicy: providerDefaultPolicy } = useScaleMule();
9021
9208
  const [uploading, setUploading] = useState(false);
@@ -11221,4 +11408,4 @@ function createSafeLogger(prefix) {
11221
11408
  };
11222
11409
  }
11223
11410
 
11224
- export { FeedbackWidget, ScaleMuleApiError, ScaleMuleClient2 as ScaleMuleClient, ScaleMuleMedia, ScaleMuleProvider, composePhone, createClient, createSafeLogger, normalizePhone, phoneCountries, sanitizeForLog, useAnalytics, useAuth, useBilling, useContent, useFeatureFlags, useFeedback, useFileStatus, useMedia, useMoney, useMoneyClient, usePushNotifications, useRealtime, useScaleMule, useScaleMuleClient, useShare, useUser, validateForm, validators };
11411
+ export { FeedbackWidget, ScaleMuleApiError, ScaleMuleClient2 as ScaleMuleClient, ScaleMuleMedia, ScaleMuleProvider, composePhone, createClient, createSafeLogger, normalizePhone, phoneCountries, sanitizeForLog, useAnalytics, useAudio, useAuth, useBilling, useContent, useFeatureFlags, useFeedback, useFileStatus, useMedia, useMoney, useMoneyClient, usePushNotifications, useRealtime, useScaleMule, useScaleMuleClient, useShare, useUser, validateForm, validators };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalemule/nextjs",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
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",