@scalemule/nextjs 0.1.1 → 0.1.3

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
@@ -2,7 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, ReactElement } from 'react';
3
3
  import { MoneyClient } from '@scalemule/money';
4
4
  export { MoneyClient, MoneyClientConfig, createMoneyClient } from '@scalemule/money';
5
- import { RealtimeService } from '@scalemule/sdk';
5
+ import { RealtimeService, StorageService, PhotoService, VideoService, ApiError as ApiError$1 } from '@scalemule/sdk';
6
6
  import { ScaleMuleClient } from './client.mjs';
7
7
  export { ClientConfig, RequestOptions, createClient } from './client.mjs';
8
8
  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';
@@ -15,6 +15,12 @@ interface ScaleMuleContextValue {
15
15
  money: MoneyClient;
16
16
  /** Base SDK realtime service — shared singleton for WebSocket connections */
17
17
  realtime: RealtimeService;
18
+ /** Base SDK storage service — exposed for `useMedia()` and direct chat-attachment uploads */
19
+ storage: StorageService;
20
+ /** Base SDK photo service — exposed for `useMedia()` and `photo.uploadViaStorage()` */
21
+ photo: PhotoService;
22
+ /** Base SDK video service — exposed for `useMedia()` and `video.uploadViaStorage()` */
23
+ video: VideoService;
18
24
  /** Current authenticated user */
19
25
  user: User | null;
20
26
  /** Set the current user */
@@ -102,6 +108,17 @@ interface UseContentOptions {
102
108
  * Provides file upload, listing, and deletion functionality.
103
109
  * Automatically includes user ID for proper multi-tenancy.
104
110
  *
111
+ * **For chat / progressive media use, prefer {@link useMedia} instead.**
112
+ * `useContent()` is a thin wrapper over generic storage and does NOT register
113
+ * uploaded images with the photo service or videos with the video service —
114
+ * so optimized thumbnails and HLS streaming don't light up automatically.
115
+ * It also defaults to `is_public: true` and compresses images by default;
116
+ * `useMedia()` defaults to private + uncompressed (the right choice for chat).
117
+ *
118
+ * Use `useContent()` for plain file gallery / browser surfaces where you
119
+ * want flat storage and no media pipeline integration. See
120
+ * `docs/MEDIA-UPLOADS.md` in the platform repo for the decision tree.
121
+ *
105
122
  * @example
106
123
  * ```tsx
107
124
  * function Gallery() {
@@ -140,6 +157,117 @@ interface UseContentOptions {
140
157
  */
141
158
  declare function useContent(options?: UseContentOptions): UseContentReturn;
142
159
 
160
+ /**
161
+ * Result of a single {@link useMedia} upload call.
162
+ *
163
+ * The shape is normalized regardless of MIME type — `optimized_url_promise`
164
+ * resolves on image uploads after the photo optimizer finishes; for non-image
165
+ * uploads it resolves to `null` immediately. (Video / audio branches will
166
+ * populate `hls_url_promise` in later phases — today they fall through to
167
+ * generic storage and that field stays `null`.)
168
+ */
169
+ interface MediaUploadResult {
170
+ /** Storage file_id — store this in chat-attachment metadata. */
171
+ file_id: string;
172
+ /** Photo service id — null for non-image uploads or when register() failed. */
173
+ photo_id: string | null;
174
+ /** Short-lived signed URL to the original bytes (private uploads) or
175
+ * a public CDN URL (when caller passed `is_public: true`). */
176
+ original_view_url: string | null;
177
+ /** Resolves once the photo optimizer finishes. `null` for non-image
178
+ * MIME types or when register() failed. */
179
+ optimized_url_promise: Promise<string | null>;
180
+ /** Resolves once the video transcoder finishes (Phase 2 / S5b). `null` today. */
181
+ hls_url_promise: Promise<string | null>;
182
+ /** The file's MIME type — preserved from the input File / Blob. */
183
+ mime_type: string;
184
+ /** Whether the resulting storage object is public-readable. */
185
+ is_public: boolean;
186
+ }
187
+ interface UseMediaUploadOptions {
188
+ /** Whether the resulting storage object should be public-readable.
189
+ * Default: `false` (private). Public is opt-in for surfaces that
190
+ * genuinely need it (avatars, public listings). Chat / DM uploads
191
+ * should always be private. */
192
+ is_public?: boolean;
193
+ /** Display filename (sanitized server-side). */
194
+ filename?: string;
195
+ /** Custom metadata attached to the file. */
196
+ metadata?: Record<string, unknown>;
197
+ /** Upload progress callback (0-100). */
198
+ onProgress?: (percent: number) => void;
199
+ /** AbortSignal for cancellation. */
200
+ signal?: AbortSignal;
201
+ /** Force a non-photo path even for image MIME types — useful for
202
+ * generic file uploads where you don't want the photo optimizer
203
+ * to register the file. Default: `false`. */
204
+ skipPhotoRegister?: boolean;
205
+ }
206
+ interface UseMediaReturn {
207
+ /** Upload a file. MIME-aware: images go through photo register +
208
+ * optimization; everything else goes through generic storage. */
209
+ upload: (file: File | Blob, options?: UseMediaUploadOptions) => Promise<MediaUploadResult>;
210
+ /** Cancel an upload by its `file_id`. Deletes the storage object so
211
+ * it doesn't orphan in S3 — useful when a chat composer accepts a
212
+ * file but the user removes it before sending. Idempotent. */
213
+ cancelUpload: (fileId: string) => Promise<void>;
214
+ /** Last error from `upload` or `cancelUpload`. */
215
+ error: ApiError$1 | null;
216
+ /** True while an upload is in progress. */
217
+ uploading: boolean;
218
+ }
219
+ /**
220
+ * Opinionated, MIME-aware media upload hook.
221
+ *
222
+ * `useMedia()` is the canonical upload primitive for chat / progressive
223
+ * media use. It branches by MIME type:
224
+ * - `image/*` → `client.photo.uploadViaStorage()` — upload to storage,
225
+ * then register with the photo service so the on-demand transform
226
+ * endpoint resolves to optimized variants. The returned
227
+ * `optimized_url_promise` resolves once the optimizer finishes.
228
+ * - everything else → `client.storage.uploadPrivate()` — a private,
229
+ * uncompressed, fail-closed upload to generic storage.
230
+ *
231
+ * **Default visibility is `is_public: false`.** Public is opt-in per call.
232
+ * `useMedia()` does not expose `is_public` via app-level config — visibility
233
+ * is always an explicit per-call surface choice.
234
+ *
235
+ * Compared to `useContent()`:
236
+ * - `useContent()` is a thin wrapper over generic storage and does not
237
+ * register photos / videos with their typed services. Use it for plain
238
+ * file uploads where you don't need optimization or transcoding.
239
+ * - `useMedia()` defaults to private + no compression, integrates with the
240
+ * typed media services automatically, and is the right primitive for
241
+ * anything chat- or media-shaped.
242
+ *
243
+ * @example
244
+ * ```tsx
245
+ * 'use client';
246
+ *
247
+ * import { useMedia, ScaleMuleMedia } from '@scalemule/nextjs';
248
+ *
249
+ * function ChatComposer({ onAttach }) {
250
+ * const { upload, uploading } = useMedia();
251
+ *
252
+ * async function handlePick(file: File) {
253
+ * const result = await upload(file);
254
+ * onAttach({
255
+ * file_id: result.file_id,
256
+ * mime_type: result.mime_type,
257
+ * optimized_url_promise: result.optimized_url_promise,
258
+ * });
259
+ * }
260
+ *
261
+ * return <input type="file" disabled={uploading}
262
+ * onChange={(e) => e.target.files?.[0] && handlePick(e.target.files[0])} />;
263
+ * }
264
+ * ```
265
+ *
266
+ * See `docs/MEDIA-UPLOADS.md` in the platform repo for the decision
267
+ * tree and the full anti-patterns list.
268
+ */
269
+ declare function useMedia(): UseMediaReturn;
270
+
143
271
  declare const useMoney: typeof useMoneyClient;
144
272
 
145
273
  /**
@@ -373,6 +501,17 @@ interface FeedbackItem {
373
501
  created_at: string;
374
502
  updated_at: string;
375
503
  }
504
+ /**
505
+ * Public widget configuration returned by `GET /v1/feedback/widget-config`.
506
+ * The SDK uses this to drive runtime widget behavior per application.
507
+ */
508
+ interface FeedbackWidgetConfig {
509
+ enabled: boolean;
510
+ allow_anonymous: boolean;
511
+ widget_theme: 'light' | 'dark' | 'auto';
512
+ widget_position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
513
+ allowed_types: FeedbackType[] | null;
514
+ }
376
515
  /**
377
516
  * Body for `POST /v1/feedback/submit`.
378
517
  *
@@ -612,4 +751,4 @@ declare function createSafeLogger(prefix: string): {
612
751
  error: (message: string, data?: unknown) => void;
613
752
  };
614
753
 
615
- export { ApiError, type FeatureFlagEvaluation, type FeatureFlagEvaluation as FeatureFlagResult, type FeedbackItem, type FeedbackItemInput, type FeedbackPriority, type FeedbackStatus, type FeedbackType, FeedbackWidget, type FeedbackWidgetProps, ListFilesParams, LoginResponse, type PasswordValidationResult, type PhoneCountry, type PhoneValidationResult, type RealtimeEvent, type RealtimeMessage, type RealtimeStatus, ScaleMuleClient, ScaleMuleConfig, ScaleMuleProvider, type ScaleMuleProviderProps, UseAnalyticsOptions, UseAnalyticsReturn, UseAuthReturn, UseBillingReturn, UseContentReturn, type UseFeatureFlagsOptions, type UseFeatureFlagsReturn, type UseFeedbackOptions, type UseFeedbackResult, type UseFeatureFlagsOptions as UseFlagsOptions, type UseFeatureFlagsReturn as UseFlagsReturn, 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, useMoney, useMoneyClient, usePushNotifications, useRealtime, useScaleMule, useScaleMuleClient, useShare, useUser, validateForm, validators };
754
+ 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 MediaUploadResult, type PasswordValidationResult, type PhoneCountry, type PhoneValidationResult, type RealtimeEvent, type RealtimeMessage, type RealtimeStatus, ScaleMuleClient, ScaleMuleConfig, ScaleMuleProvider, type ScaleMuleProviderProps, UseAnalyticsOptions, UseAnalyticsReturn, UseAuthReturn, UseBillingReturn, UseContentReturn, type UseFeatureFlagsOptions, type UseFeatureFlagsReturn, type UseFeedbackOptions, type UseFeedbackResult, 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, useMedia, useMoney, useMoneyClient, usePushNotifications, useRealtime, useScaleMule, useScaleMuleClient, useShare, useUser, validateForm, validators };
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, ReactElement } from 'react';
3
3
  import { MoneyClient } from '@scalemule/money';
4
4
  export { MoneyClient, MoneyClientConfig, createMoneyClient } from '@scalemule/money';
5
- import { RealtimeService } from '@scalemule/sdk';
5
+ import { RealtimeService, StorageService, PhotoService, VideoService, ApiError as ApiError$1 } from '@scalemule/sdk';
6
6
  import { ScaleMuleClient } from './client.js';
7
7
  export { ClientConfig, RequestOptions, createClient } from './client.js';
8
8
  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';
@@ -15,6 +15,12 @@ interface ScaleMuleContextValue {
15
15
  money: MoneyClient;
16
16
  /** Base SDK realtime service — shared singleton for WebSocket connections */
17
17
  realtime: RealtimeService;
18
+ /** Base SDK storage service — exposed for `useMedia()` and direct chat-attachment uploads */
19
+ storage: StorageService;
20
+ /** Base SDK photo service — exposed for `useMedia()` and `photo.uploadViaStorage()` */
21
+ photo: PhotoService;
22
+ /** Base SDK video service — exposed for `useMedia()` and `video.uploadViaStorage()` */
23
+ video: VideoService;
18
24
  /** Current authenticated user */
19
25
  user: User | null;
20
26
  /** Set the current user */
@@ -102,6 +108,17 @@ interface UseContentOptions {
102
108
  * Provides file upload, listing, and deletion functionality.
103
109
  * Automatically includes user ID for proper multi-tenancy.
104
110
  *
111
+ * **For chat / progressive media use, prefer {@link useMedia} instead.**
112
+ * `useContent()` is a thin wrapper over generic storage and does NOT register
113
+ * uploaded images with the photo service or videos with the video service —
114
+ * so optimized thumbnails and HLS streaming don't light up automatically.
115
+ * It also defaults to `is_public: true` and compresses images by default;
116
+ * `useMedia()` defaults to private + uncompressed (the right choice for chat).
117
+ *
118
+ * Use `useContent()` for plain file gallery / browser surfaces where you
119
+ * want flat storage and no media pipeline integration. See
120
+ * `docs/MEDIA-UPLOADS.md` in the platform repo for the decision tree.
121
+ *
105
122
  * @example
106
123
  * ```tsx
107
124
  * function Gallery() {
@@ -140,6 +157,117 @@ interface UseContentOptions {
140
157
  */
141
158
  declare function useContent(options?: UseContentOptions): UseContentReturn;
142
159
 
160
+ /**
161
+ * Result of a single {@link useMedia} upload call.
162
+ *
163
+ * The shape is normalized regardless of MIME type — `optimized_url_promise`
164
+ * resolves on image uploads after the photo optimizer finishes; for non-image
165
+ * uploads it resolves to `null` immediately. (Video / audio branches will
166
+ * populate `hls_url_promise` in later phases — today they fall through to
167
+ * generic storage and that field stays `null`.)
168
+ */
169
+ interface MediaUploadResult {
170
+ /** Storage file_id — store this in chat-attachment metadata. */
171
+ file_id: string;
172
+ /** Photo service id — null for non-image uploads or when register() failed. */
173
+ photo_id: string | null;
174
+ /** Short-lived signed URL to the original bytes (private uploads) or
175
+ * a public CDN URL (when caller passed `is_public: true`). */
176
+ original_view_url: string | null;
177
+ /** Resolves once the photo optimizer finishes. `null` for non-image
178
+ * MIME types or when register() failed. */
179
+ optimized_url_promise: Promise<string | null>;
180
+ /** Resolves once the video transcoder finishes (Phase 2 / S5b). `null` today. */
181
+ hls_url_promise: Promise<string | null>;
182
+ /** The file's MIME type — preserved from the input File / Blob. */
183
+ mime_type: string;
184
+ /** Whether the resulting storage object is public-readable. */
185
+ is_public: boolean;
186
+ }
187
+ interface UseMediaUploadOptions {
188
+ /** Whether the resulting storage object should be public-readable.
189
+ * Default: `false` (private). Public is opt-in for surfaces that
190
+ * genuinely need it (avatars, public listings). Chat / DM uploads
191
+ * should always be private. */
192
+ is_public?: boolean;
193
+ /** Display filename (sanitized server-side). */
194
+ filename?: string;
195
+ /** Custom metadata attached to the file. */
196
+ metadata?: Record<string, unknown>;
197
+ /** Upload progress callback (0-100). */
198
+ onProgress?: (percent: number) => void;
199
+ /** AbortSignal for cancellation. */
200
+ signal?: AbortSignal;
201
+ /** Force a non-photo path even for image MIME types — useful for
202
+ * generic file uploads where you don't want the photo optimizer
203
+ * to register the file. Default: `false`. */
204
+ skipPhotoRegister?: boolean;
205
+ }
206
+ interface UseMediaReturn {
207
+ /** Upload a file. MIME-aware: images go through photo register +
208
+ * optimization; everything else goes through generic storage. */
209
+ upload: (file: File | Blob, options?: UseMediaUploadOptions) => Promise<MediaUploadResult>;
210
+ /** Cancel an upload by its `file_id`. Deletes the storage object so
211
+ * it doesn't orphan in S3 — useful when a chat composer accepts a
212
+ * file but the user removes it before sending. Idempotent. */
213
+ cancelUpload: (fileId: string) => Promise<void>;
214
+ /** Last error from `upload` or `cancelUpload`. */
215
+ error: ApiError$1 | null;
216
+ /** True while an upload is in progress. */
217
+ uploading: boolean;
218
+ }
219
+ /**
220
+ * Opinionated, MIME-aware media upload hook.
221
+ *
222
+ * `useMedia()` is the canonical upload primitive for chat / progressive
223
+ * media use. It branches by MIME type:
224
+ * - `image/*` → `client.photo.uploadViaStorage()` — upload to storage,
225
+ * then register with the photo service so the on-demand transform
226
+ * endpoint resolves to optimized variants. The returned
227
+ * `optimized_url_promise` resolves once the optimizer finishes.
228
+ * - everything else → `client.storage.uploadPrivate()` — a private,
229
+ * uncompressed, fail-closed upload to generic storage.
230
+ *
231
+ * **Default visibility is `is_public: false`.** Public is opt-in per call.
232
+ * `useMedia()` does not expose `is_public` via app-level config — visibility
233
+ * is always an explicit per-call surface choice.
234
+ *
235
+ * Compared to `useContent()`:
236
+ * - `useContent()` is a thin wrapper over generic storage and does not
237
+ * register photos / videos with their typed services. Use it for plain
238
+ * file uploads where you don't need optimization or transcoding.
239
+ * - `useMedia()` defaults to private + no compression, integrates with the
240
+ * typed media services automatically, and is the right primitive for
241
+ * anything chat- or media-shaped.
242
+ *
243
+ * @example
244
+ * ```tsx
245
+ * 'use client';
246
+ *
247
+ * import { useMedia, ScaleMuleMedia } from '@scalemule/nextjs';
248
+ *
249
+ * function ChatComposer({ onAttach }) {
250
+ * const { upload, uploading } = useMedia();
251
+ *
252
+ * async function handlePick(file: File) {
253
+ * const result = await upload(file);
254
+ * onAttach({
255
+ * file_id: result.file_id,
256
+ * mime_type: result.mime_type,
257
+ * optimized_url_promise: result.optimized_url_promise,
258
+ * });
259
+ * }
260
+ *
261
+ * return <input type="file" disabled={uploading}
262
+ * onChange={(e) => e.target.files?.[0] && handlePick(e.target.files[0])} />;
263
+ * }
264
+ * ```
265
+ *
266
+ * See `docs/MEDIA-UPLOADS.md` in the platform repo for the decision
267
+ * tree and the full anti-patterns list.
268
+ */
269
+ declare function useMedia(): UseMediaReturn;
270
+
143
271
  declare const useMoney: typeof useMoneyClient;
144
272
 
145
273
  /**
@@ -373,6 +501,17 @@ interface FeedbackItem {
373
501
  created_at: string;
374
502
  updated_at: string;
375
503
  }
504
+ /**
505
+ * Public widget configuration returned by `GET /v1/feedback/widget-config`.
506
+ * The SDK uses this to drive runtime widget behavior per application.
507
+ */
508
+ interface FeedbackWidgetConfig {
509
+ enabled: boolean;
510
+ allow_anonymous: boolean;
511
+ widget_theme: 'light' | 'dark' | 'auto';
512
+ widget_position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
513
+ allowed_types: FeedbackType[] | null;
514
+ }
376
515
  /**
377
516
  * Body for `POST /v1/feedback/submit`.
378
517
  *
@@ -612,4 +751,4 @@ declare function createSafeLogger(prefix: string): {
612
751
  error: (message: string, data?: unknown) => void;
613
752
  };
614
753
 
615
- export { ApiError, type FeatureFlagEvaluation, type FeatureFlagEvaluation as FeatureFlagResult, type FeedbackItem, type FeedbackItemInput, type FeedbackPriority, type FeedbackStatus, type FeedbackType, FeedbackWidget, type FeedbackWidgetProps, ListFilesParams, LoginResponse, type PasswordValidationResult, type PhoneCountry, type PhoneValidationResult, type RealtimeEvent, type RealtimeMessage, type RealtimeStatus, ScaleMuleClient, ScaleMuleConfig, ScaleMuleProvider, type ScaleMuleProviderProps, UseAnalyticsOptions, UseAnalyticsReturn, UseAuthReturn, UseBillingReturn, UseContentReturn, type UseFeatureFlagsOptions, type UseFeatureFlagsReturn, type UseFeedbackOptions, type UseFeedbackResult, type UseFeatureFlagsOptions as UseFlagsOptions, type UseFeatureFlagsReturn as UseFlagsReturn, 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, useMoney, useMoneyClient, usePushNotifications, useRealtime, useScaleMule, useScaleMuleClient, useShare, useUser, validateForm, validators };
754
+ 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 MediaUploadResult, type PasswordValidationResult, type PhoneCountry, type PhoneValidationResult, type RealtimeEvent, type RealtimeMessage, type RealtimeStatus, ScaleMuleClient, ScaleMuleConfig, ScaleMuleProvider, type ScaleMuleProviderProps, UseAnalyticsOptions, UseAnalyticsReturn, UseAuthReturn, UseBillingReturn, UseContentReturn, type UseFeatureFlagsOptions, type UseFeatureFlagsReturn, type UseFeedbackOptions, type UseFeedbackResult, 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, useMedia, useMoney, useMoneyClient, usePushNotifications, useRealtime, useScaleMule, useScaleMuleClient, useShare, useUser, validateForm, validators };