@scalemule/nextjs 0.0.8 → 0.0.10

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/client.d.mts CHANGED
@@ -64,6 +64,7 @@ declare class ScaleMuleClient {
64
64
  private enableOfflineQueue;
65
65
  private sessionGate;
66
66
  private resolveSessionGate;
67
+ private workspaceId;
67
68
  constructor(config: ClientConfig);
68
69
  /**
69
70
  * Sync offline queue when coming back online
@@ -85,6 +86,15 @@ declare class ScaleMuleClient {
85
86
  * Check if currently rate limited
86
87
  */
87
88
  isRateLimited(): boolean;
89
+ /**
90
+ * Set the active workspace context. All subsequent requests will include
91
+ * x-sm-workspace-id header. Pass null to clear.
92
+ */
93
+ setWorkspaceContext(id: string | null): void;
94
+ /**
95
+ * Get the current workspace ID
96
+ */
97
+ getWorkspaceId(): string | null;
88
98
  /**
89
99
  * Get the gateway URL
90
100
  */
package/dist/client.d.ts CHANGED
@@ -64,6 +64,7 @@ declare class ScaleMuleClient {
64
64
  private enableOfflineQueue;
65
65
  private sessionGate;
66
66
  private resolveSessionGate;
67
+ private workspaceId;
67
68
  constructor(config: ClientConfig);
68
69
  /**
69
70
  * Sync offline queue when coming back online
@@ -85,6 +86,15 @@ declare class ScaleMuleClient {
85
86
  * Check if currently rate limited
86
87
  */
87
88
  isRateLimited(): boolean;
89
+ /**
90
+ * Set the active workspace context. All subsequent requests will include
91
+ * x-sm-workspace-id header. Pass null to clear.
92
+ */
93
+ setWorkspaceContext(id: string | null): void;
94
+ /**
95
+ * Get the current workspace ID
96
+ */
97
+ getWorkspaceId(): string | null;
88
98
  /**
89
99
  * Get the gateway URL
90
100
  */
package/dist/client.js CHANGED
@@ -17,6 +17,7 @@ var GATEWAY_URLS = {
17
17
  };
18
18
  var SESSION_STORAGE_KEY = "scalemule_session";
19
19
  var USER_ID_STORAGE_KEY = "scalemule_user_id";
20
+ var WORKSPACE_STORAGE_KEY = "scalemule_workspace_id";
20
21
  var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
21
22
  function sleep(ms) {
22
23
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -275,6 +276,7 @@ var ScaleMuleClient = class {
275
276
  this.offlineQueue = null;
276
277
  this.sessionGate = null;
277
278
  this.resolveSessionGate = null;
279
+ this.workspaceId = null;
278
280
  this.apiKey = config.apiKey;
279
281
  this.applicationId = config.applicationId || null;
280
282
  this.gatewayUrl = resolveGatewayUrl(config);
@@ -345,6 +347,24 @@ var ScaleMuleClient = class {
345
347
  isRateLimited() {
346
348
  return this.rateLimitQueue?.isRateLimited || false;
347
349
  }
350
+ /**
351
+ * Set the active workspace context. All subsequent requests will include
352
+ * x-sm-workspace-id header. Pass null to clear.
353
+ */
354
+ setWorkspaceContext(id) {
355
+ this.workspaceId = id;
356
+ if (id) {
357
+ this.storage.setItem(WORKSPACE_STORAGE_KEY, id);
358
+ } else {
359
+ this.storage.removeItem(WORKSPACE_STORAGE_KEY);
360
+ }
361
+ }
362
+ /**
363
+ * Get the current workspace ID
364
+ */
365
+ getWorkspaceId() {
366
+ return this.workspaceId;
367
+ }
348
368
  /**
349
369
  * Get the gateway URL
350
370
  */
@@ -387,6 +407,8 @@ var ScaleMuleClient = class {
387
407
  const userId = await this.storage.getItem(USER_ID_STORAGE_KEY);
388
408
  if (token) this.sessionToken = token;
389
409
  if (userId) this.userId = userId;
410
+ const wsId = await this.storage.getItem(WORKSPACE_STORAGE_KEY);
411
+ if (wsId) this.workspaceId = wsId;
390
412
  if (token) {
391
413
  this.resolveSessionPending();
392
414
  }
@@ -412,8 +434,10 @@ var ScaleMuleClient = class {
412
434
  async clearSession() {
413
435
  this.sessionToken = null;
414
436
  this.userId = null;
437
+ this.workspaceId = null;
415
438
  await this.storage.removeItem(SESSION_STORAGE_KEY);
416
439
  await this.storage.removeItem(USER_ID_STORAGE_KEY);
440
+ await this.storage.removeItem(WORKSPACE_STORAGE_KEY);
417
441
  if (this.debug) {
418
442
  console.log("[ScaleMule] Session cleared");
419
443
  }
@@ -445,6 +469,9 @@ var ScaleMuleClient = class {
445
469
  if (!options?.skipAuth && this.sessionToken) {
446
470
  headers.set("Authorization", `Bearer ${this.sessionToken}`);
447
471
  }
472
+ if (this.workspaceId) {
473
+ headers.set("x-sm-workspace-id", this.workspaceId);
474
+ }
448
475
  if (!headers.has("Content-Type") && options?.body && typeof options.body === "string") {
449
476
  headers.set("Content-Type", "application/json");
450
477
  }
@@ -600,6 +627,9 @@ var ScaleMuleClient = class {
600
627
  if (this.sessionToken) {
601
628
  headers.set("Authorization", `Bearer ${this.sessionToken}`);
602
629
  }
630
+ if (this.workspaceId) {
631
+ headers.set("x-sm-workspace-id", this.workspaceId);
632
+ }
603
633
  let lastError = null;
604
634
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
605
635
  try {
@@ -737,6 +767,9 @@ var ScaleMuleClient = class {
737
767
  if (this.sessionToken) {
738
768
  xhr.setRequestHeader("Authorization", `Bearer ${this.sessionToken}`);
739
769
  }
770
+ if (this.workspaceId) {
771
+ xhr.setRequestHeader("x-sm-workspace-id", this.workspaceId);
772
+ }
740
773
  xhr.send(formData);
741
774
  });
742
775
  }
package/dist/client.mjs CHANGED
@@ -15,6 +15,7 @@ var GATEWAY_URLS = {
15
15
  };
16
16
  var SESSION_STORAGE_KEY = "scalemule_session";
17
17
  var USER_ID_STORAGE_KEY = "scalemule_user_id";
18
+ var WORKSPACE_STORAGE_KEY = "scalemule_workspace_id";
18
19
  var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
19
20
  function sleep(ms) {
20
21
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -273,6 +274,7 @@ var ScaleMuleClient = class {
273
274
  this.offlineQueue = null;
274
275
  this.sessionGate = null;
275
276
  this.resolveSessionGate = null;
277
+ this.workspaceId = null;
276
278
  this.apiKey = config.apiKey;
277
279
  this.applicationId = config.applicationId || null;
278
280
  this.gatewayUrl = resolveGatewayUrl(config);
@@ -343,6 +345,24 @@ var ScaleMuleClient = class {
343
345
  isRateLimited() {
344
346
  return this.rateLimitQueue?.isRateLimited || false;
345
347
  }
348
+ /**
349
+ * Set the active workspace context. All subsequent requests will include
350
+ * x-sm-workspace-id header. Pass null to clear.
351
+ */
352
+ setWorkspaceContext(id) {
353
+ this.workspaceId = id;
354
+ if (id) {
355
+ this.storage.setItem(WORKSPACE_STORAGE_KEY, id);
356
+ } else {
357
+ this.storage.removeItem(WORKSPACE_STORAGE_KEY);
358
+ }
359
+ }
360
+ /**
361
+ * Get the current workspace ID
362
+ */
363
+ getWorkspaceId() {
364
+ return this.workspaceId;
365
+ }
346
366
  /**
347
367
  * Get the gateway URL
348
368
  */
@@ -385,6 +405,8 @@ var ScaleMuleClient = class {
385
405
  const userId = await this.storage.getItem(USER_ID_STORAGE_KEY);
386
406
  if (token) this.sessionToken = token;
387
407
  if (userId) this.userId = userId;
408
+ const wsId = await this.storage.getItem(WORKSPACE_STORAGE_KEY);
409
+ if (wsId) this.workspaceId = wsId;
388
410
  if (token) {
389
411
  this.resolveSessionPending();
390
412
  }
@@ -410,8 +432,10 @@ var ScaleMuleClient = class {
410
432
  async clearSession() {
411
433
  this.sessionToken = null;
412
434
  this.userId = null;
435
+ this.workspaceId = null;
413
436
  await this.storage.removeItem(SESSION_STORAGE_KEY);
414
437
  await this.storage.removeItem(USER_ID_STORAGE_KEY);
438
+ await this.storage.removeItem(WORKSPACE_STORAGE_KEY);
415
439
  if (this.debug) {
416
440
  console.log("[ScaleMule] Session cleared");
417
441
  }
@@ -443,6 +467,9 @@ var ScaleMuleClient = class {
443
467
  if (!options?.skipAuth && this.sessionToken) {
444
468
  headers.set("Authorization", `Bearer ${this.sessionToken}`);
445
469
  }
470
+ if (this.workspaceId) {
471
+ headers.set("x-sm-workspace-id", this.workspaceId);
472
+ }
446
473
  if (!headers.has("Content-Type") && options?.body && typeof options.body === "string") {
447
474
  headers.set("Content-Type", "application/json");
448
475
  }
@@ -598,6 +625,9 @@ var ScaleMuleClient = class {
598
625
  if (this.sessionToken) {
599
626
  headers.set("Authorization", `Bearer ${this.sessionToken}`);
600
627
  }
628
+ if (this.workspaceId) {
629
+ headers.set("x-sm-workspace-id", this.workspaceId);
630
+ }
601
631
  let lastError = null;
602
632
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
603
633
  try {
@@ -735,6 +765,9 @@ var ScaleMuleClient = class {
735
765
  if (this.sessionToken) {
736
766
  xhr.setRequestHeader("Authorization", `Bearer ${this.sessionToken}`);
737
767
  }
768
+ if (this.workspaceId) {
769
+ xhr.setRequestHeader("x-sm-workspace-id", this.workspaceId);
770
+ }
738
771
  xhr.send(formData);
739
772
  });
740
773
  }
package/dist/index.d.mts CHANGED
@@ -257,6 +257,32 @@ declare function useRealtime(options?: UseRealtimeOptions): UseRealtimeReturn;
257
257
  */
258
258
  declare function useAnalytics(options?: UseAnalyticsOptions): UseAnalyticsReturn;
259
259
 
260
+ interface FeatureFlagEvaluation<T = unknown> {
261
+ flag_id: string;
262
+ flag_key: string;
263
+ environment: string;
264
+ value: T;
265
+ reason: string;
266
+ matched_rule_id?: string | null;
267
+ variant_key?: string | null;
268
+ bucket?: number | null;
269
+ }
270
+ interface UseFeatureFlagsOptions {
271
+ environment?: string;
272
+ context?: Record<string, unknown>;
273
+ keys?: string[];
274
+ enabled?: boolean;
275
+ }
276
+ interface UseFeatureFlagsReturn {
277
+ flags: Record<string, FeatureFlagEvaluation>;
278
+ loading: boolean;
279
+ error: ApiError | null;
280
+ refresh: () => Promise<void>;
281
+ isEnabled: (flagKey: string, fallback?: boolean) => boolean;
282
+ getFlag: <T = unknown>(flagKey: string, fallback?: T) => T;
283
+ }
284
+ declare function useFeatureFlags(options?: UseFeatureFlagsOptions): UseFeatureFlagsReturn;
285
+
260
286
  /**
261
287
  * Client-side validation helpers
262
288
  *
@@ -415,4 +441,4 @@ declare function createSafeLogger(prefix: string): {
415
441
  error: (message: string, data?: unknown) => void;
416
442
  };
417
443
 
418
- export { ApiError, 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 UseRealtimeOptions, type UseRealtimeReturn, UseUserReturn, User, type UsernameValidationResult, composePhone, createSafeLogger, normalizePhone, phoneCountries, sanitizeForLog, useAnalytics, useAuth, useBilling, useContent, useRealtime, useScaleMule, useScaleMuleClient, useUser, validateForm, validators };
444
+ export { ApiError, type FeatureFlagEvaluation, type FeatureFlagEvaluation as FeatureFlagResult, 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 UseFeatureFlagsOptions as UseFlagsOptions, type UseFeatureFlagsReturn as UseFlagsReturn, type UseRealtimeOptions, type UseRealtimeReturn, UseUserReturn, User, type UsernameValidationResult, composePhone, createSafeLogger, normalizePhone, phoneCountries, sanitizeForLog, useAnalytics, useAuth, useBilling, useContent, useFeatureFlags, useRealtime, useScaleMule, useScaleMuleClient, useUser, validateForm, validators };
package/dist/index.d.ts CHANGED
@@ -257,6 +257,32 @@ declare function useRealtime(options?: UseRealtimeOptions): UseRealtimeReturn;
257
257
  */
258
258
  declare function useAnalytics(options?: UseAnalyticsOptions): UseAnalyticsReturn;
259
259
 
260
+ interface FeatureFlagEvaluation<T = unknown> {
261
+ flag_id: string;
262
+ flag_key: string;
263
+ environment: string;
264
+ value: T;
265
+ reason: string;
266
+ matched_rule_id?: string | null;
267
+ variant_key?: string | null;
268
+ bucket?: number | null;
269
+ }
270
+ interface UseFeatureFlagsOptions {
271
+ environment?: string;
272
+ context?: Record<string, unknown>;
273
+ keys?: string[];
274
+ enabled?: boolean;
275
+ }
276
+ interface UseFeatureFlagsReturn {
277
+ flags: Record<string, FeatureFlagEvaluation>;
278
+ loading: boolean;
279
+ error: ApiError | null;
280
+ refresh: () => Promise<void>;
281
+ isEnabled: (flagKey: string, fallback?: boolean) => boolean;
282
+ getFlag: <T = unknown>(flagKey: string, fallback?: T) => T;
283
+ }
284
+ declare function useFeatureFlags(options?: UseFeatureFlagsOptions): UseFeatureFlagsReturn;
285
+
260
286
  /**
261
287
  * Client-side validation helpers
262
288
  *
@@ -415,4 +441,4 @@ declare function createSafeLogger(prefix: string): {
415
441
  error: (message: string, data?: unknown) => void;
416
442
  };
417
443
 
418
- export { ApiError, 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 UseRealtimeOptions, type UseRealtimeReturn, UseUserReturn, User, type UsernameValidationResult, composePhone, createSafeLogger, normalizePhone, phoneCountries, sanitizeForLog, useAnalytics, useAuth, useBilling, useContent, useRealtime, useScaleMule, useScaleMuleClient, useUser, validateForm, validators };
444
+ export { ApiError, type FeatureFlagEvaluation, type FeatureFlagEvaluation as FeatureFlagResult, 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 UseFeatureFlagsOptions as UseFlagsOptions, type UseFeatureFlagsReturn as UseFlagsReturn, type UseRealtimeOptions, type UseRealtimeReturn, UseUserReturn, User, type UsernameValidationResult, composePhone, createSafeLogger, normalizePhone, phoneCountries, sanitizeForLog, useAnalytics, useAuth, useBilling, useContent, useFeatureFlags, useRealtime, useScaleMule, useScaleMuleClient, useUser, validateForm, validators };
package/dist/index.js CHANGED
@@ -22,6 +22,7 @@ var GATEWAY_URLS = {
22
22
  };
23
23
  var SESSION_STORAGE_KEY = "scalemule_session";
24
24
  var USER_ID_STORAGE_KEY = "scalemule_user_id";
25
+ var WORKSPACE_STORAGE_KEY = "scalemule_workspace_id";
25
26
  var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
26
27
  function sleep(ms) {
27
28
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -280,6 +281,7 @@ var ScaleMuleClient = class {
280
281
  this.offlineQueue = null;
281
282
  this.sessionGate = null;
282
283
  this.resolveSessionGate = null;
284
+ this.workspaceId = null;
283
285
  this.apiKey = config.apiKey;
284
286
  this.applicationId = config.applicationId || null;
285
287
  this.gatewayUrl = resolveGatewayUrl(config);
@@ -350,6 +352,24 @@ var ScaleMuleClient = class {
350
352
  isRateLimited() {
351
353
  return this.rateLimitQueue?.isRateLimited || false;
352
354
  }
355
+ /**
356
+ * Set the active workspace context. All subsequent requests will include
357
+ * x-sm-workspace-id header. Pass null to clear.
358
+ */
359
+ setWorkspaceContext(id) {
360
+ this.workspaceId = id;
361
+ if (id) {
362
+ this.storage.setItem(WORKSPACE_STORAGE_KEY, id);
363
+ } else {
364
+ this.storage.removeItem(WORKSPACE_STORAGE_KEY);
365
+ }
366
+ }
367
+ /**
368
+ * Get the current workspace ID
369
+ */
370
+ getWorkspaceId() {
371
+ return this.workspaceId;
372
+ }
353
373
  /**
354
374
  * Get the gateway URL
355
375
  */
@@ -392,6 +412,8 @@ var ScaleMuleClient = class {
392
412
  const userId = await this.storage.getItem(USER_ID_STORAGE_KEY);
393
413
  if (token) this.sessionToken = token;
394
414
  if (userId) this.userId = userId;
415
+ const wsId = await this.storage.getItem(WORKSPACE_STORAGE_KEY);
416
+ if (wsId) this.workspaceId = wsId;
395
417
  if (token) {
396
418
  this.resolveSessionPending();
397
419
  }
@@ -417,8 +439,10 @@ var ScaleMuleClient = class {
417
439
  async clearSession() {
418
440
  this.sessionToken = null;
419
441
  this.userId = null;
442
+ this.workspaceId = null;
420
443
  await this.storage.removeItem(SESSION_STORAGE_KEY);
421
444
  await this.storage.removeItem(USER_ID_STORAGE_KEY);
445
+ await this.storage.removeItem(WORKSPACE_STORAGE_KEY);
422
446
  if (this.debug) {
423
447
  console.log("[ScaleMule] Session cleared");
424
448
  }
@@ -450,6 +474,9 @@ var ScaleMuleClient = class {
450
474
  if (!options?.skipAuth && this.sessionToken) {
451
475
  headers.set("Authorization", `Bearer ${this.sessionToken}`);
452
476
  }
477
+ if (this.workspaceId) {
478
+ headers.set("x-sm-workspace-id", this.workspaceId);
479
+ }
453
480
  if (!headers.has("Content-Type") && options?.body && typeof options.body === "string") {
454
481
  headers.set("Content-Type", "application/json");
455
482
  }
@@ -605,6 +632,9 @@ var ScaleMuleClient = class {
605
632
  if (this.sessionToken) {
606
633
  headers.set("Authorization", `Bearer ${this.sessionToken}`);
607
634
  }
635
+ if (this.workspaceId) {
636
+ headers.set("x-sm-workspace-id", this.workspaceId);
637
+ }
608
638
  let lastError = null;
609
639
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
610
640
  try {
@@ -742,6 +772,9 @@ var ScaleMuleClient = class {
742
772
  if (this.sessionToken) {
743
773
  xhr.setRequestHeader("Authorization", `Bearer ${this.sessionToken}`);
744
774
  }
775
+ if (this.workspaceId) {
776
+ xhr.setRequestHeader("x-sm-workspace-id", this.workspaceId);
777
+ }
745
778
  xhr.send(formData);
746
779
  });
747
780
  }
@@ -2798,6 +2831,80 @@ function useAnalytics(options = {}) {
2798
2831
  ]
2799
2832
  );
2800
2833
  }
2834
+ function toApiError(error) {
2835
+ if (error instanceof ScaleMuleApiError) {
2836
+ return {
2837
+ code: error.code,
2838
+ message: error.message,
2839
+ field: error.field
2840
+ };
2841
+ }
2842
+ return {
2843
+ code: "UNKNOWN",
2844
+ message: error instanceof Error ? error.message : "Failed to load feature flags"
2845
+ };
2846
+ }
2847
+ function useFeatureFlags(options = {}) {
2848
+ const {
2849
+ environment = "prod",
2850
+ context = {},
2851
+ keys,
2852
+ enabled = true
2853
+ } = options;
2854
+ const { client } = useScaleMule();
2855
+ const [flags, setFlags] = react.useState({});
2856
+ const [loading, setLoading] = react.useState(enabled);
2857
+ const [error, setError] = react.useState(null);
2858
+ const contextRef = react.useRef(context);
2859
+ const keysKey = react.useMemo(() => keys && keys.length > 0 ? [...keys].sort().join("|") : "", [keys]);
2860
+ react.useEffect(() => {
2861
+ contextRef.current = context;
2862
+ }, [context]);
2863
+ const refresh = react.useCallback(async () => {
2864
+ if (!enabled) {
2865
+ setLoading(false);
2866
+ return;
2867
+ }
2868
+ setLoading(true);
2869
+ try {
2870
+ const payload = keys && keys.length > 0 ? { flag_keys: keys, environment, context: contextRef.current } : { environment, context: contextRef.current };
2871
+ const result = keys && keys.length > 0 ? await client.post("/v1/flags/evaluate/batch", payload) : await client.post("/v1/flags/evaluate/all", payload);
2872
+ setFlags(result || {});
2873
+ setError(null);
2874
+ } catch (err) {
2875
+ setError(toApiError(err));
2876
+ } finally {
2877
+ setLoading(false);
2878
+ }
2879
+ }, [client, enabled, environment, keys, keysKey]);
2880
+ react.useEffect(() => {
2881
+ void refresh();
2882
+ }, [refresh]);
2883
+ const isEnabled = react.useCallback(
2884
+ (flagKey, fallback = false) => {
2885
+ const evaluation = flags[flagKey];
2886
+ if (!evaluation) return fallback;
2887
+ return typeof evaluation.value === "boolean" ? evaluation.value : fallback;
2888
+ },
2889
+ [flags]
2890
+ );
2891
+ const getFlag = react.useCallback(
2892
+ (flagKey, fallback) => {
2893
+ const evaluation = flags[flagKey];
2894
+ if (!evaluation) return fallback;
2895
+ return evaluation.value ?? fallback;
2896
+ },
2897
+ [flags]
2898
+ );
2899
+ return {
2900
+ flags,
2901
+ loading,
2902
+ error,
2903
+ refresh,
2904
+ isEnabled,
2905
+ getFlag
2906
+ };
2907
+ }
2801
2908
 
2802
2909
  // src/validation.ts
2803
2910
  var phoneCountries = [
@@ -3103,6 +3210,7 @@ exports.useAnalytics = useAnalytics;
3103
3210
  exports.useAuth = useAuth;
3104
3211
  exports.useBilling = useBilling;
3105
3212
  exports.useContent = useContent;
3213
+ exports.useFeatureFlags = useFeatureFlags;
3106
3214
  exports.useRealtime = useRealtime;
3107
3215
  exports.useScaleMule = useScaleMule;
3108
3216
  exports.useScaleMuleClient = useScaleMuleClient;
package/dist/index.mjs CHANGED
@@ -20,6 +20,7 @@ var GATEWAY_URLS = {
20
20
  };
21
21
  var SESSION_STORAGE_KEY = "scalemule_session";
22
22
  var USER_ID_STORAGE_KEY = "scalemule_user_id";
23
+ var WORKSPACE_STORAGE_KEY = "scalemule_workspace_id";
23
24
  var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
24
25
  function sleep(ms) {
25
26
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -278,6 +279,7 @@ var ScaleMuleClient = class {
278
279
  this.offlineQueue = null;
279
280
  this.sessionGate = null;
280
281
  this.resolveSessionGate = null;
282
+ this.workspaceId = null;
281
283
  this.apiKey = config.apiKey;
282
284
  this.applicationId = config.applicationId || null;
283
285
  this.gatewayUrl = resolveGatewayUrl(config);
@@ -348,6 +350,24 @@ var ScaleMuleClient = class {
348
350
  isRateLimited() {
349
351
  return this.rateLimitQueue?.isRateLimited || false;
350
352
  }
353
+ /**
354
+ * Set the active workspace context. All subsequent requests will include
355
+ * x-sm-workspace-id header. Pass null to clear.
356
+ */
357
+ setWorkspaceContext(id) {
358
+ this.workspaceId = id;
359
+ if (id) {
360
+ this.storage.setItem(WORKSPACE_STORAGE_KEY, id);
361
+ } else {
362
+ this.storage.removeItem(WORKSPACE_STORAGE_KEY);
363
+ }
364
+ }
365
+ /**
366
+ * Get the current workspace ID
367
+ */
368
+ getWorkspaceId() {
369
+ return this.workspaceId;
370
+ }
351
371
  /**
352
372
  * Get the gateway URL
353
373
  */
@@ -390,6 +410,8 @@ var ScaleMuleClient = class {
390
410
  const userId = await this.storage.getItem(USER_ID_STORAGE_KEY);
391
411
  if (token) this.sessionToken = token;
392
412
  if (userId) this.userId = userId;
413
+ const wsId = await this.storage.getItem(WORKSPACE_STORAGE_KEY);
414
+ if (wsId) this.workspaceId = wsId;
393
415
  if (token) {
394
416
  this.resolveSessionPending();
395
417
  }
@@ -415,8 +437,10 @@ var ScaleMuleClient = class {
415
437
  async clearSession() {
416
438
  this.sessionToken = null;
417
439
  this.userId = null;
440
+ this.workspaceId = null;
418
441
  await this.storage.removeItem(SESSION_STORAGE_KEY);
419
442
  await this.storage.removeItem(USER_ID_STORAGE_KEY);
443
+ await this.storage.removeItem(WORKSPACE_STORAGE_KEY);
420
444
  if (this.debug) {
421
445
  console.log("[ScaleMule] Session cleared");
422
446
  }
@@ -448,6 +472,9 @@ var ScaleMuleClient = class {
448
472
  if (!options?.skipAuth && this.sessionToken) {
449
473
  headers.set("Authorization", `Bearer ${this.sessionToken}`);
450
474
  }
475
+ if (this.workspaceId) {
476
+ headers.set("x-sm-workspace-id", this.workspaceId);
477
+ }
451
478
  if (!headers.has("Content-Type") && options?.body && typeof options.body === "string") {
452
479
  headers.set("Content-Type", "application/json");
453
480
  }
@@ -603,6 +630,9 @@ var ScaleMuleClient = class {
603
630
  if (this.sessionToken) {
604
631
  headers.set("Authorization", `Bearer ${this.sessionToken}`);
605
632
  }
633
+ if (this.workspaceId) {
634
+ headers.set("x-sm-workspace-id", this.workspaceId);
635
+ }
606
636
  let lastError = null;
607
637
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
608
638
  try {
@@ -740,6 +770,9 @@ var ScaleMuleClient = class {
740
770
  if (this.sessionToken) {
741
771
  xhr.setRequestHeader("Authorization", `Bearer ${this.sessionToken}`);
742
772
  }
773
+ if (this.workspaceId) {
774
+ xhr.setRequestHeader("x-sm-workspace-id", this.workspaceId);
775
+ }
743
776
  xhr.send(formData);
744
777
  });
745
778
  }
@@ -2796,6 +2829,80 @@ function useAnalytics(options = {}) {
2796
2829
  ]
2797
2830
  );
2798
2831
  }
2832
+ function toApiError(error) {
2833
+ if (error instanceof ScaleMuleApiError) {
2834
+ return {
2835
+ code: error.code,
2836
+ message: error.message,
2837
+ field: error.field
2838
+ };
2839
+ }
2840
+ return {
2841
+ code: "UNKNOWN",
2842
+ message: error instanceof Error ? error.message : "Failed to load feature flags"
2843
+ };
2844
+ }
2845
+ function useFeatureFlags(options = {}) {
2846
+ const {
2847
+ environment = "prod",
2848
+ context = {},
2849
+ keys,
2850
+ enabled = true
2851
+ } = options;
2852
+ const { client } = useScaleMule();
2853
+ const [flags, setFlags] = useState({});
2854
+ const [loading, setLoading] = useState(enabled);
2855
+ const [error, setError] = useState(null);
2856
+ const contextRef = useRef(context);
2857
+ const keysKey = useMemo(() => keys && keys.length > 0 ? [...keys].sort().join("|") : "", [keys]);
2858
+ useEffect(() => {
2859
+ contextRef.current = context;
2860
+ }, [context]);
2861
+ const refresh = useCallback(async () => {
2862
+ if (!enabled) {
2863
+ setLoading(false);
2864
+ return;
2865
+ }
2866
+ setLoading(true);
2867
+ try {
2868
+ const payload = keys && keys.length > 0 ? { flag_keys: keys, environment, context: contextRef.current } : { environment, context: contextRef.current };
2869
+ const result = keys && keys.length > 0 ? await client.post("/v1/flags/evaluate/batch", payload) : await client.post("/v1/flags/evaluate/all", payload);
2870
+ setFlags(result || {});
2871
+ setError(null);
2872
+ } catch (err) {
2873
+ setError(toApiError(err));
2874
+ } finally {
2875
+ setLoading(false);
2876
+ }
2877
+ }, [client, enabled, environment, keys, keysKey]);
2878
+ useEffect(() => {
2879
+ void refresh();
2880
+ }, [refresh]);
2881
+ const isEnabled = useCallback(
2882
+ (flagKey, fallback = false) => {
2883
+ const evaluation = flags[flagKey];
2884
+ if (!evaluation) return fallback;
2885
+ return typeof evaluation.value === "boolean" ? evaluation.value : fallback;
2886
+ },
2887
+ [flags]
2888
+ );
2889
+ const getFlag = useCallback(
2890
+ (flagKey, fallback) => {
2891
+ const evaluation = flags[flagKey];
2892
+ if (!evaluation) return fallback;
2893
+ return evaluation.value ?? fallback;
2894
+ },
2895
+ [flags]
2896
+ );
2897
+ return {
2898
+ flags,
2899
+ loading,
2900
+ error,
2901
+ refresh,
2902
+ isEnabled,
2903
+ getFlag
2904
+ };
2905
+ }
2799
2906
 
2800
2907
  // src/validation.ts
2801
2908
  var phoneCountries = [
@@ -3088,4 +3195,4 @@ function createSafeLogger(prefix) {
3088
3195
  };
3089
3196
  }
3090
3197
 
3091
- export { ScaleMuleApiError, ScaleMuleClient, ScaleMuleProvider, composePhone, createClient, createSafeLogger, normalizePhone, phoneCountries, sanitizeForLog, useAnalytics, useAuth, useBilling, useContent, useRealtime, useScaleMule, useScaleMuleClient, useUser, validateForm, validators };
3198
+ export { ScaleMuleApiError, ScaleMuleClient, ScaleMuleProvider, composePhone, createClient, createSafeLogger, normalizePhone, phoneCountries, sanitizeForLog, useAnalytics, useAuth, useBilling, useContent, useFeatureFlags, useRealtime, useScaleMule, useScaleMuleClient, useUser, validateForm, validators };
@@ -655,6 +655,37 @@ var ScaleMuleServer = class {
655
655
  });
656
656
  }
657
657
  };
658
+ this.flags = {
659
+ evaluate: async (flagKey, context = {}, environment = "prod", options) => {
660
+ return this.request("POST", "/v1/flags/evaluate", {
661
+ body: {
662
+ flag_key: flagKey,
663
+ environment,
664
+ context
665
+ },
666
+ clientContext: options?.clientContext
667
+ });
668
+ },
669
+ evaluateBatch: async (flagKeys, context = {}, environment = "prod", options) => {
670
+ return this.request("POST", "/v1/flags/evaluate/batch", {
671
+ body: {
672
+ flag_keys: flagKeys,
673
+ environment,
674
+ context
675
+ },
676
+ clientContext: options?.clientContext
677
+ });
678
+ },
679
+ evaluateAll: async (context = {}, environment = "prod", options) => {
680
+ return this.request("POST", "/v1/flags/evaluate/all", {
681
+ body: {
682
+ environment,
683
+ context
684
+ },
685
+ clientContext: options?.clientContext
686
+ });
687
+ }
688
+ };
658
689
  this.apiKey = config.apiKey;
659
690
  this.gatewayUrl = resolveGatewayUrl(config);
660
691
  this.debug = config.debug || false;
@@ -653,6 +653,37 @@ var ScaleMuleServer = class {
653
653
  });
654
654
  }
655
655
  };
656
+ this.flags = {
657
+ evaluate: async (flagKey, context = {}, environment = "prod", options) => {
658
+ return this.request("POST", "/v1/flags/evaluate", {
659
+ body: {
660
+ flag_key: flagKey,
661
+ environment,
662
+ context
663
+ },
664
+ clientContext: options?.clientContext
665
+ });
666
+ },
667
+ evaluateBatch: async (flagKeys, context = {}, environment = "prod", options) => {
668
+ return this.request("POST", "/v1/flags/evaluate/batch", {
669
+ body: {
670
+ flag_keys: flagKeys,
671
+ environment,
672
+ context
673
+ },
674
+ clientContext: options?.clientContext
675
+ });
676
+ },
677
+ evaluateAll: async (context = {}, environment = "prod", options) => {
678
+ return this.request("POST", "/v1/flags/evaluate/all", {
679
+ body: {
680
+ environment,
681
+ context
682
+ },
683
+ clientContext: options?.clientContext
684
+ });
685
+ }
686
+ };
656
687
  this.apiKey = config.apiKey;
657
688
  this.gatewayUrl = resolveGatewayUrl(config);
658
689
  this.debug = config.debug || false;
@@ -1,5 +1,5 @@
1
- import { S as ServerConfig } from '../webhook-handler-BzBvYbZU.mjs';
2
- export { a as ScaleMuleServer, d as VideoFailedEvent, V as VideoReadyEvent, f as VideoTranscodedEvent, e as VideoUploadedEvent, W as WebhookEvent, g as WebhookRoutesConfig, c as createServerClient, h as createWebhookHandler, b as createWebhookRoutes, p as parseWebhookEvent, r as registerVideoWebhook, v as verifyWebhookSignature } from '../webhook-handler-BzBvYbZU.mjs';
1
+ import { S as ServerConfig } from '../webhook-handler-CweF_nT3.mjs';
2
+ export { a as ScaleMuleServer, d as VideoFailedEvent, V as VideoReadyEvent, f as VideoTranscodedEvent, e as VideoUploadedEvent, W as WebhookEvent, g as WebhookRoutesConfig, c as createServerClient, h as createWebhookHandler, b as createWebhookRoutes, p as parseWebhookEvent, r as registerVideoWebhook, v as verifyWebhookSignature } from '../webhook-handler-CweF_nT3.mjs';
3
3
  import { $ as ClientContext, L as LoginResponse, A as ApiError } from '../index-jomBa89d.mjs';
4
4
  import { NextRequest, NextResponse } from 'next/server';
5
5
 
@@ -1,5 +1,5 @@
1
- import { S as ServerConfig } from '../webhook-handler-tcJtRpkE.js';
2
- export { a as ScaleMuleServer, d as VideoFailedEvent, V as VideoReadyEvent, f as VideoTranscodedEvent, e as VideoUploadedEvent, W as WebhookEvent, g as WebhookRoutesConfig, c as createServerClient, h as createWebhookHandler, b as createWebhookRoutes, p as parseWebhookEvent, r as registerVideoWebhook, v as verifyWebhookSignature } from '../webhook-handler-tcJtRpkE.js';
1
+ import { S as ServerConfig } from '../webhook-handler-Bi3bMNty.js';
2
+ export { a as ScaleMuleServer, d as VideoFailedEvent, V as VideoReadyEvent, f as VideoTranscodedEvent, e as VideoUploadedEvent, W as WebhookEvent, g as WebhookRoutesConfig, c as createServerClient, h as createWebhookHandler, b as createWebhookRoutes, p as parseWebhookEvent, r as registerVideoWebhook, v as verifyWebhookSignature } from '../webhook-handler-Bi3bMNty.js';
3
3
  import { $ as ClientContext, L as LoginResponse, A as ApiError } from '../index-jomBa89d.js';
4
4
  import { NextRequest, NextResponse } from 'next/server';
5
5
 
@@ -715,6 +715,37 @@ var ScaleMuleServer = class {
715
715
  });
716
716
  }
717
717
  };
718
+ this.flags = {
719
+ evaluate: async (flagKey, context = {}, environment = "prod", options) => {
720
+ return this.request("POST", "/v1/flags/evaluate", {
721
+ body: {
722
+ flag_key: flagKey,
723
+ environment,
724
+ context
725
+ },
726
+ clientContext: options?.clientContext
727
+ });
728
+ },
729
+ evaluateBatch: async (flagKeys, context = {}, environment = "prod", options) => {
730
+ return this.request("POST", "/v1/flags/evaluate/batch", {
731
+ body: {
732
+ flag_keys: flagKeys,
733
+ environment,
734
+ context
735
+ },
736
+ clientContext: options?.clientContext
737
+ });
738
+ },
739
+ evaluateAll: async (context = {}, environment = "prod", options) => {
740
+ return this.request("POST", "/v1/flags/evaluate/all", {
741
+ body: {
742
+ environment,
743
+ context
744
+ },
745
+ clientContext: options?.clientContext
746
+ });
747
+ }
748
+ };
718
749
  this.apiKey = config.apiKey;
719
750
  this.gatewayUrl = resolveGatewayUrl(config);
720
751
  this.debug = config.debug || false;
@@ -713,6 +713,37 @@ var ScaleMuleServer = class {
713
713
  });
714
714
  }
715
715
  };
716
+ this.flags = {
717
+ evaluate: async (flagKey, context = {}, environment = "prod", options) => {
718
+ return this.request("POST", "/v1/flags/evaluate", {
719
+ body: {
720
+ flag_key: flagKey,
721
+ environment,
722
+ context
723
+ },
724
+ clientContext: options?.clientContext
725
+ });
726
+ },
727
+ evaluateBatch: async (flagKeys, context = {}, environment = "prod", options) => {
728
+ return this.request("POST", "/v1/flags/evaluate/batch", {
729
+ body: {
730
+ flag_keys: flagKeys,
731
+ environment,
732
+ context
733
+ },
734
+ clientContext: options?.clientContext
735
+ });
736
+ },
737
+ evaluateAll: async (context = {}, environment = "prod", options) => {
738
+ return this.request("POST", "/v1/flags/evaluate/all", {
739
+ body: {
740
+ environment,
741
+ context
742
+ },
743
+ clientContext: options?.clientContext
744
+ });
745
+ }
746
+ };
716
747
  this.apiKey = config.apiKey;
717
748
  this.gatewayUrl = resolveGatewayUrl(config);
718
749
  this.debug = config.debug || false;
@@ -1,2 +1,2 @@
1
- export { h as createWebhookHandler } from '../webhook-handler-BzBvYbZU.mjs';
1
+ export { h as createWebhookHandler } from '../webhook-handler-CweF_nT3.mjs';
2
2
  import '../index-jomBa89d.mjs';
@@ -1,2 +1,2 @@
1
- export { h as createWebhookHandler } from '../webhook-handler-tcJtRpkE.js';
1
+ export { h as createWebhookHandler } from '../webhook-handler-Bi3bMNty.js';
2
2
  import '../index-jomBa89d.js';
@@ -523,6 +523,44 @@ declare class ScaleMuleServer {
523
523
  tracked: number;
524
524
  }>;
525
525
  };
526
+ flags: {
527
+ evaluate: <T = unknown>(flagKey: string, context?: Record<string, unknown>, environment?: string, options?: {
528
+ clientContext?: ClientContext;
529
+ }) => Promise<{
530
+ flag_id: string;
531
+ flag_key: string;
532
+ environment: string;
533
+ value: T;
534
+ reason: string;
535
+ matched_rule_id?: string | null;
536
+ variant_key?: string | null;
537
+ bucket?: number | null;
538
+ }>;
539
+ evaluateBatch: (flagKeys: string[], context?: Record<string, unknown>, environment?: string, options?: {
540
+ clientContext?: ClientContext;
541
+ }) => Promise<Record<string, {
542
+ flag_id: string;
543
+ flag_key: string;
544
+ environment: string;
545
+ value: unknown;
546
+ reason: string;
547
+ matched_rule_id?: string | null;
548
+ variant_key?: string | null;
549
+ bucket?: number | null;
550
+ }>>;
551
+ evaluateAll: (context?: Record<string, unknown>, environment?: string, options?: {
552
+ clientContext?: ClientContext;
553
+ }) => Promise<Record<string, {
554
+ flag_id: string;
555
+ flag_key: string;
556
+ environment: string;
557
+ value: unknown;
558
+ reason: string;
559
+ matched_rule_id?: string | null;
560
+ variant_key?: string | null;
561
+ bucket?: number | null;
562
+ }>>;
563
+ };
526
564
  }
527
565
  /**
528
566
  * Create a server client with environment-based defaults
@@ -523,6 +523,44 @@ declare class ScaleMuleServer {
523
523
  tracked: number;
524
524
  }>;
525
525
  };
526
+ flags: {
527
+ evaluate: <T = unknown>(flagKey: string, context?: Record<string, unknown>, environment?: string, options?: {
528
+ clientContext?: ClientContext;
529
+ }) => Promise<{
530
+ flag_id: string;
531
+ flag_key: string;
532
+ environment: string;
533
+ value: T;
534
+ reason: string;
535
+ matched_rule_id?: string | null;
536
+ variant_key?: string | null;
537
+ bucket?: number | null;
538
+ }>;
539
+ evaluateBatch: (flagKeys: string[], context?: Record<string, unknown>, environment?: string, options?: {
540
+ clientContext?: ClientContext;
541
+ }) => Promise<Record<string, {
542
+ flag_id: string;
543
+ flag_key: string;
544
+ environment: string;
545
+ value: unknown;
546
+ reason: string;
547
+ matched_rule_id?: string | null;
548
+ variant_key?: string | null;
549
+ bucket?: number | null;
550
+ }>>;
551
+ evaluateAll: (context?: Record<string, unknown>, environment?: string, options?: {
552
+ clientContext?: ClientContext;
553
+ }) => Promise<Record<string, {
554
+ flag_id: string;
555
+ flag_key: string;
556
+ environment: string;
557
+ value: unknown;
558
+ reason: string;
559
+ matched_rule_id?: string | null;
560
+ variant_key?: string | null;
561
+ bucket?: number | null;
562
+ }>>;
563
+ };
526
564
  }
527
565
  /**
528
566
  * Create a server client with environment-based defaults
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalemule/nextjs",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
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",