@jewel998/config 0.2.2 → 0.2.6

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/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @jewel998/config
2
2
 
3
+ > See also: [Getting Started](/guide/getting-started) · [SDK Reference](/api/) · [Self-Hosting](/guide/self-hosting) · [Cloud Functions](/api/cloud-functions)
4
+
3
5
  [![npm version](https://img.shields.io/npm/v/@jewel998/config)](https://www.npmjs.com/package/@jewel998/config)
4
6
  [![npm downloads](https://img.shields.io/npm/dm/@jewel998/config)](https://www.npmjs.com/package/@jewel998/config)
5
7
  [![license](https://img.shields.io/npm/l/@jewel998/config)](./LICENSE)
@@ -12,12 +14,16 @@ A free, self-hostable feature flag and remote configuration platform. Deploy to
12
14
  ## Features
13
15
 
14
16
  - **Feature flags** — Boolean, string, number, JSON, array value types
15
- - **Targeting rules** — Serve different values based on user attributes (plan, country, etc.)
16
- - **Segments** — Reusable audience groups with one-click targeting
17
- - **Percentage rollouts** — Gradually roll out features with deterministic bucketing
18
- - **Prerequisites** — Flag dependencies with operator support (equals, greater_than, etc.)
19
- - **Scheduling** — Schedule config changes for a future date/time
20
- - **Environments** — Separate dev/staging/production configs
17
+ - **A/B testing & multivariate testing** — Targeting rules with variant arms; sticky bucketing keeps users in a consistent group
18
+ - **Canary releases & progressive delivery** — Percentage rollouts with deterministic bucketing (MurmurHash3); ramp 5% → 100% safely
19
+ - **Kill switch** — Instantly revert any feature to its safe state with zero code deploy
20
+ - **Targeting rules** — User segmentation: serve different values based on attributes (plan, country, role, etc.)
21
+ - **Segments** — Reusable audience cohorts with one-click targeting across all flags
22
+ - **Ring deployments** — Combine segments + rollouts to release through internal → beta → general rings
23
+ - **Dark launch** — Deploy code behind a flag, activate on schedule without a new deploy
24
+ - **Timed release** — Schedule flag changes for a precise date/time
25
+ - **Prerequisites / guard flags** — Dependency-based feature gating; prevent inconsistent feature states
26
+ - **Environments** — Separate dev/staging/production configs, keys, and targeting rules
21
27
  - **Audit log** — Track every change with actor, timestamp, and diff viewer
22
28
  - **Webhooks** — Notify Slack, Discord, Google Chat, MS Teams, or custom endpoints
23
29
  - **RBAC** — Viewer, Editor, Admin roles per project
@@ -52,21 +58,30 @@ import { initConfig, autoContext } from "@jewel998/config";
52
58
 
53
59
  const flags = initConfig({
54
60
  clientId: "cid_xxx", // From your Portal → API Keys
55
- baseUrl: "https://your-project.web.app/api", // Your Firebase URL
61
+ baseUrl: "https://your-project.web.app/api",
62
+ prefetch: ["app.maintenance_mode"], // Fetch these immediately, block ready()
56
63
  defaults: {
57
64
  "feature.dark_mode": false,
58
65
  "app.upload_limit": 50,
59
66
  },
60
67
  context: autoContext({ userId: "user_123", plan: "pro" }),
68
+ onError: (err) => console.error(err.type, err.key),
61
69
  });
62
70
 
63
- // Instant — returns default value, no loading state
64
- flags.get("feature.dark_mode"); // → false
71
+ // Wait for prefetched keys before rendering
72
+ await flags.ready();
65
73
 
66
- // After API responds, returns resolved value
67
- flags.on("updated", () => {
68
- flags.get("feature.dark_mode"); // → true (matched targeting rule)
69
- });
74
+ // Instant — in memory or has a default
75
+ const darkMode = await flags.get<boolean>("feature.dark_mode"); // → true
76
+
77
+ // Declare page-level keys (Tier 2, fire-and-forget)
78
+ flags.prefetch(["feature.new_checkout", "app.upload_limit"]);
79
+
80
+ // React to any key changing
81
+ flags.on("updated", ({ keys }) => rerender());
82
+
83
+ // React to a specific key changing
84
+ flags.on("updated:feature.dark_mode", (value) => applyTheme(value));
70
85
  ```
71
86
 
72
87
  ### API Key Types
@@ -103,7 +118,7 @@ config.getFlag("feature.checkout_v2"); // Evaluated locally — no round-trip
103
118
  │ │
104
119
  │ import { initConfig } from "@jewel998/config" │
105
120
  │ const flags = initConfig({ clientId, baseUrl }) │
106
- │ flags.get("feature.dark_mode") → true │
121
+ │ await flags.get("feature.dark_mode") → true │
107
122
  └──────────────────────┬──────────────────────────────┘
108
123
  │ POST /api/v1/config
109
124
  ▼
@@ -192,6 +207,7 @@ apps/
192
207
  landing/ — Marketing site (Astro + React)
193
208
  docs/ — Documentation (VitePress)
194
209
  packages/
210
+ api/ — Internal micro-framework for Cloud Functions (@jewel998/api)
195
211
  config/ — SDK (@jewel998/config)
196
212
  tour/ — Tour framework (@jewel998/tour)
197
213
  functions/ — Firebase Cloud Functions (API + webhooks)
@@ -1,2 +1,2 @@
1
- export type { CacheEntry, CacheStorage, BrowserStorageOptions, } from "../types.js";
1
+ export type { CacheEntry, CacheStorage, BrowserStorageOptions } from "../types.js";
2
2
  export { DEFAULT_CACHE_TTL } from "../types.js";
@@ -15,9 +15,14 @@ export function createConfig(options) {
15
15
  if (!options.clientId) {
16
16
  throw new ConfigError("clientId is required", "MISSING_CLIENT_ID");
17
17
  }
18
- // 2. Check browser environment
19
- if (typeof window === "undefined") {
20
- throw new ConfigError("@jewel998/config is browser-only. Server-side usage is not supported.", "INITIALIZATION_FAILED");
18
+ // 2. Check browser environment (for client keys only)
19
+ // Server keys (svr_) can run in any environment.
20
+ // Client keys (cid_) require a browser because they rely on server-side evaluation
21
+ // with origin/referer headers for domain validation.
22
+ if (typeof window === "undefined" && !options.clientId.startsWith("svr_")) {
23
+ throw new ConfigError("Client keys (cid_) require a browser environment. " +
24
+ "For server-side usage, use `initServerConfig` from '@jewel998/config/server' " +
25
+ "with a server key (svr_).", "INITIALIZATION_FAILED");
21
26
  }
22
27
  // 3. Resolve defaults
23
28
  const strategy = options.loadingStrategy ?? "optimistic";
@@ -42,9 +47,7 @@ export function createConfig(options) {
42
47
  getContext: () => currentContext,
43
48
  });
44
49
  const events = new TypedEventEmitter();
45
- const fetcher = granularity === "batch"
46
- ? createBatchFetcher(transport)
47
- : createProjectedFetcher(transport);
50
+ const fetcher = granularity === "batch" ? createBatchFetcher(transport) : createProjectedFetcher(transport);
48
51
  // 6. Create loading context
49
52
  const context = {
50
53
  clientId: options.clientId,
@@ -19,3 +19,21 @@ export declare class TimeoutError extends ConfigError {
19
19
  export declare class AuthenticationError extends ConfigError {
20
20
  constructor(message: string);
21
21
  }
22
+ /**
23
+ * Error type passed to onError and thrown by get() on failure.
24
+ *
25
+ * type:
26
+ * TIMEOUT — get() waited longer than global timeout, no default provided
27
+ * FETCH_FAILED — network error or non-2xx response during any tier fetch
28
+ * KEY_NOT_FOUND — key does not exist in the project
29
+ * AUTH — 401/403 from the API (circuit breaker opens)
30
+ * RATE_LIMITED — 429 from the API
31
+ */
32
+ export type SdkErrorType = "TIMEOUT" | "FETCH_FAILED" | "KEY_NOT_FOUND" | "AUTH" | "RATE_LIMITED";
33
+ export declare class SdkError extends Error {
34
+ readonly type: SdkErrorType;
35
+ readonly key?: string | undefined;
36
+ readonly cause?: Error | undefined;
37
+ name: string;
38
+ constructor(type: SdkErrorType, message: string, key?: string | undefined, cause?: Error | undefined);
39
+ }
@@ -35,4 +35,16 @@ export class AuthenticationError extends ConfigError {
35
35
  this.name = "AuthenticationError";
36
36
  }
37
37
  }
38
+ export class SdkError extends Error {
39
+ type;
40
+ key;
41
+ cause;
42
+ name = "SdkError";
43
+ constructor(type, message, key, cause) {
44
+ super(message);
45
+ this.type = type;
46
+ this.key = key;
47
+ this.cause = cause;
48
+ }
49
+ }
38
50
  //# sourceMappingURL=index.js.map
@@ -1,8 +1,29 @@
1
1
  import type { ConfigEventCallback, ConfigEventPayloads, ConfigEventType, EventEmitterInterface } from "../types.js";
2
+ /**
3
+ * TypedEventEmitter supports two event patterns:
4
+ *
5
+ * 1. Typed batch events (ConfigEventType):
6
+ * emitter.on("updated", ({ keys }) => ...)
7
+ * emitter.on("fetchError", ({ error }) => ...)
8
+ *
9
+ * 2. Key-specific events (template literal `updated:${key}`):
10
+ * emitter.on("updated:feature.dark_mode", (value) => ...)
11
+ * Fired individually per key alongside the batch "updated" event.
12
+ */
2
13
  export declare class TypedEventEmitter implements EventEmitterInterface {
3
14
  private listeners;
15
+ private keyListeners;
4
16
  on<E extends ConfigEventType>(event: E, callback: ConfigEventCallback<E>): void;
5
17
  off<E extends ConfigEventType>(event: E, callback: ConfigEventCallback<E>): void;
18
+ /**
19
+ * Subscribe to a key-specific update event.
20
+ * Fires when that exact key's value is updated.
21
+ *
22
+ * @example
23
+ * emitter.onKey("feature.dark_mode", (value) => applyTheme(value));
24
+ */
25
+ onKey(key: string, callback: (value: unknown) => void): void;
26
+ offKey(key: string, callback: (value: unknown) => void): void;
6
27
  emit<E extends ConfigEventType>(event: E, payload: ConfigEventPayloads[E]): void;
7
28
  removeAllListeners(): void;
8
29
  }
@@ -1,5 +1,17 @@
1
+ /**
2
+ * TypedEventEmitter supports two event patterns:
3
+ *
4
+ * 1. Typed batch events (ConfigEventType):
5
+ * emitter.on("updated", ({ keys }) => ...)
6
+ * emitter.on("fetchError", ({ error }) => ...)
7
+ *
8
+ * 2. Key-specific events (template literal `updated:${key}`):
9
+ * emitter.on("updated:feature.dark_mode", (value) => ...)
10
+ * Fired individually per key alongside the batch "updated" event.
11
+ */
1
12
  export class TypedEventEmitter {
2
13
  listeners = new Map();
14
+ keyListeners = new Map();
3
15
  on(event, callback) {
4
16
  if (!this.listeners.has(event)) {
5
17
  this.listeners.set(event, new Set());
@@ -9,22 +21,56 @@ export class TypedEventEmitter {
9
21
  off(event, callback) {
10
22
  this.listeners.get(event)?.delete(callback);
11
23
  }
24
+ /**
25
+ * Subscribe to a key-specific update event.
26
+ * Fires when that exact key's value is updated.
27
+ *
28
+ * @example
29
+ * emitter.onKey("feature.dark_mode", (value) => applyTheme(value));
30
+ */
31
+ onKey(key, callback) {
32
+ if (!this.keyListeners.has(key)) {
33
+ this.keyListeners.set(key, new Set());
34
+ }
35
+ this.keyListeners.get(key).add(callback);
36
+ }
37
+ offKey(key, callback) {
38
+ this.keyListeners.get(key)?.delete(callback);
39
+ }
12
40
  emit(event, payload) {
41
+ // Fire batch listeners
13
42
  const callbacks = this.listeners.get(event);
14
- if (!callbacks) {
15
- return;
16
- }
17
- for (const cb of callbacks) {
18
- try {
19
- cb(payload);
43
+ if (callbacks) {
44
+ for (const cb of callbacks) {
45
+ try {
46
+ cb(payload);
47
+ }
48
+ catch (err) {
49
+ console.error(`[config] Event handler error for "${event}":`, err);
50
+ }
20
51
  }
21
- catch (err) {
22
- console.error(`[Alpha] Event handler error for "${event}":`, err);
52
+ }
53
+ // For "updated" events, also fire per-key listeners
54
+ if (event === "updated") {
55
+ const updatedPayload = payload;
56
+ for (const key of updatedPayload.keys) {
57
+ const keyCallbacks = this.keyListeners.get(key);
58
+ if (keyCallbacks) {
59
+ for (const cb of keyCallbacks) {
60
+ try {
61
+ cb(key); // value is read from data store by the subscriber
62
+ }
63
+ catch (err) {
64
+ console.error(`[config] Key listener error for "updated:${key}":`, err);
65
+ }
66
+ }
67
+ }
23
68
  }
24
69
  }
25
70
  }
26
71
  removeAllListeners() {
27
72
  this.listeners.clear();
73
+ this.keyListeners.clear();
28
74
  }
29
75
  }
30
76
  //# sourceMappingURL=EventEmitter.js.map
@@ -0,0 +1,77 @@
1
+ import type { ConfigFetcher } from "../types.js";
2
+ export type FetchTier = "prefetch" | "page" | "idle";
3
+ /**
4
+ * TierFetcher — wraps a ConfigFetcher with three responsibilities:
5
+ *
6
+ * 1. Tier tracking: records which keys were fetched under which tier
7
+ * so context re-fetches are scoped to already-fetched keys only.
8
+ *
9
+ * 2. Cross-tier deduplication: a key registered in a higher-priority tier
10
+ * is never re-fetched by a lower-priority tier call.
11
+ * Priority order: prefetch > page > idle
12
+ *
13
+ * 3. Pending waiters: get() calls that arrive for an unfetched key
14
+ * register a waiter here. When the key eventually arrives (via idle
15
+ * fetch or refresh), all waiters for that key are resolved.
16
+ */
17
+ export declare class TierFetcher {
18
+ private readonly fetcher;
19
+ private readonly fetchedKeys;
20
+ private readonly allFetched;
21
+ private readonly waiters;
22
+ constructor(fetcher: ConfigFetcher);
23
+ /**
24
+ * Fetch a specific set of keys under a given tier.
25
+ * Keys already tracked under any tier are skipped (deduplication).
26
+ */
27
+ fetchTier(keys: string[], tier: FetchTier): Promise<Record<string, unknown>>;
28
+ /**
29
+ * Fetch all remaining keys (idle tier).
30
+ * Only keys not already tracked are added to the idle set.
31
+ */
32
+ fetchAll(): Promise<Record<string, unknown>>;
33
+ /** Check the remote version (passthrough). */
34
+ checkVersion(): ReturnType<ConfigFetcher["checkVersion"]>;
35
+ /**
36
+ * Re-fetch specific keys without deduplication check.
37
+ * Used by doRefresh() — we're intentionally re-fetching already-fetched keys
38
+ * with a new context, so the allFetched deduplication should be bypassed.
39
+ */
40
+ refetchKeys(keys: string[]): Promise<Record<string, unknown>>;
41
+ /**
42
+ * Record keys as fetched under a tier WITHOUT performing a network fetch.
43
+ * Used when values are hydrated from persistent storage (the boot gate),
44
+ * so later context re-fetches correctly scope to these keys.
45
+ */
46
+ markFetched(keys: string[], tier: FetchTier): void;
47
+ /**
48
+ * Register a waiter for a key that hasn't been fetched yet.
49
+ * Returns a Promise that resolves when the key arrives, or rejects on error.
50
+ */
51
+ waitForKey(key: string): Promise<unknown>;
52
+ /**
53
+ * Notify all waiters for the given keys with their resolved values.
54
+ * Called by initConfig after any successful fetch.
55
+ */
56
+ notifyWaiters(result: Record<string, unknown>): void;
57
+ /**
58
+ * Reject all waiters for all pending keys.
59
+ * Called when a fetch fails and we need to unblock all suspended get() calls.
60
+ */
61
+ rejectAllWaiters(error: Error): void;
62
+ /**
63
+ * Reject waiters for specific keys only.
64
+ * Used when a targeted fetch fails for a subset of keys.
65
+ */
66
+ rejectWaiters(keys: string[], error: Error): void;
67
+ /** Whether there are any pending waiters for a given key. */
68
+ hasWaiters(key: string): boolean;
69
+ getFetchedKeys(): {
70
+ prefetch: string[];
71
+ page: string[];
72
+ idle: string[];
73
+ };
74
+ isKeyFetched(key: string): boolean;
75
+ hasFetchedTier(tier: FetchTier): boolean;
76
+ private recordKeys;
77
+ }
@@ -0,0 +1,162 @@
1
+ /**
2
+ * TierFetcher — wraps a ConfigFetcher with three responsibilities:
3
+ *
4
+ * 1. Tier tracking: records which keys were fetched under which tier
5
+ * so context re-fetches are scoped to already-fetched keys only.
6
+ *
7
+ * 2. Cross-tier deduplication: a key registered in a higher-priority tier
8
+ * is never re-fetched by a lower-priority tier call.
9
+ * Priority order: prefetch > page > idle
10
+ *
11
+ * 3. Pending waiters: get() calls that arrive for an unfetched key
12
+ * register a waiter here. When the key eventually arrives (via idle
13
+ * fetch or refresh), all waiters for that key are resolved.
14
+ */
15
+ export class TierFetcher {
16
+ fetcher;
17
+ // Keys fetched per tier — used to scope context re-fetches
18
+ fetchedKeys = new Map([
19
+ ["prefetch", new Set()],
20
+ ["page", new Set()],
21
+ ["idle", new Set()],
22
+ ]);
23
+ // All fetched keys across all tiers — fast membership check for deduplication
24
+ allFetched = new Set();
25
+ // Pending waiters for suspended get() calls
26
+ // key → array of { resolve, reject } pairs
27
+ waiters = new Map();
28
+ constructor(fetcher) {
29
+ this.fetcher = fetcher;
30
+ }
31
+ // ─── Fetch methods ───────────────────────────────────────────
32
+ /**
33
+ * Fetch a specific set of keys under a given tier.
34
+ * Keys already tracked under any tier are skipped (deduplication).
35
+ */
36
+ async fetchTier(keys, tier) {
37
+ const toFetch = keys.filter((k) => !this.allFetched.has(k));
38
+ if (toFetch.length === 0)
39
+ return {};
40
+ const result = await this.fetcher.fetchKeys(toFetch);
41
+ this.recordKeys(Object.keys(result), tier);
42
+ return result;
43
+ }
44
+ /**
45
+ * Fetch all remaining keys (idle tier).
46
+ * Only keys not already tracked are added to the idle set.
47
+ */
48
+ async fetchAll() {
49
+ const result = await this.fetcher.fetchAll();
50
+ for (const key of Object.keys(result)) {
51
+ if (!this.allFetched.has(key)) {
52
+ this.fetchedKeys.get("idle").add(key);
53
+ this.allFetched.add(key);
54
+ }
55
+ }
56
+ return result;
57
+ }
58
+ /** Check the remote version (passthrough). */
59
+ checkVersion() {
60
+ return this.fetcher.checkVersion();
61
+ }
62
+ /**
63
+ * Re-fetch specific keys without deduplication check.
64
+ * Used by doRefresh() — we're intentionally re-fetching already-fetched keys
65
+ * with a new context, so the allFetched deduplication should be bypassed.
66
+ */
67
+ async refetchKeys(keys) {
68
+ return this.fetcher.fetchKeys(keys);
69
+ }
70
+ /**
71
+ * Record keys as fetched under a tier WITHOUT performing a network fetch.
72
+ * Used when values are hydrated from persistent storage (the boot gate),
73
+ * so later context re-fetches correctly scope to these keys.
74
+ */
75
+ markFetched(keys, tier) {
76
+ this.recordKeys(keys, tier);
77
+ }
78
+ // ─── Waiter management ───────────────────────────────────────
79
+ /**
80
+ * Register a waiter for a key that hasn't been fetched yet.
81
+ * Returns a Promise that resolves when the key arrives, or rejects on error.
82
+ */
83
+ waitForKey(key) {
84
+ return new Promise((resolve, reject) => {
85
+ if (!this.waiters.has(key)) {
86
+ this.waiters.set(key, []);
87
+ }
88
+ this.waiters.get(key).push({ resolve, reject });
89
+ });
90
+ }
91
+ /**
92
+ * Notify all waiters for the given keys with their resolved values.
93
+ * Called by initConfig after any successful fetch.
94
+ */
95
+ notifyWaiters(result) {
96
+ for (const [key, value] of Object.entries(result)) {
97
+ const keyWaiters = this.waiters.get(key);
98
+ if (keyWaiters && keyWaiters.length > 0) {
99
+ for (const { resolve } of keyWaiters) {
100
+ resolve(value);
101
+ }
102
+ this.waiters.delete(key);
103
+ }
104
+ }
105
+ }
106
+ /**
107
+ * Reject all waiters for all pending keys.
108
+ * Called when a fetch fails and we need to unblock all suspended get() calls.
109
+ */
110
+ rejectAllWaiters(error) {
111
+ for (const [key, keyWaiters] of this.waiters) {
112
+ for (const { reject } of keyWaiters) {
113
+ reject(error);
114
+ }
115
+ this.waiters.delete(key);
116
+ }
117
+ }
118
+ /**
119
+ * Reject waiters for specific keys only.
120
+ * Used when a targeted fetch fails for a subset of keys.
121
+ */
122
+ rejectWaiters(keys, error) {
123
+ for (const key of keys) {
124
+ const keyWaiters = this.waiters.get(key);
125
+ if (keyWaiters) {
126
+ for (const { reject } of keyWaiters) {
127
+ reject(error);
128
+ }
129
+ this.waiters.delete(key);
130
+ }
131
+ }
132
+ }
133
+ /** Whether there are any pending waiters for a given key. */
134
+ hasWaiters(key) {
135
+ return (this.waiters.get(key)?.length ?? 0) > 0;
136
+ }
137
+ // ─── Tier introspection ──────────────────────────────────────
138
+ getFetchedKeys() {
139
+ return {
140
+ prefetch: Array.from(this.fetchedKeys.get("prefetch")),
141
+ page: Array.from(this.fetchedKeys.get("page")),
142
+ idle: Array.from(this.fetchedKeys.get("idle")),
143
+ };
144
+ }
145
+ isKeyFetched(key) {
146
+ return this.allFetched.has(key);
147
+ }
148
+ hasFetchedTier(tier) {
149
+ return this.fetchedKeys.get(tier).size > 0;
150
+ }
151
+ // ─── Private helpers ─────────────────────────────────────────
152
+ recordKeys(keys, tier) {
153
+ const tierSet = this.fetchedKeys.get(tier);
154
+ for (const key of keys) {
155
+ if (!this.allFetched.has(key)) {
156
+ tierSet.add(key);
157
+ this.allFetched.add(key);
158
+ }
159
+ }
160
+ }
161
+ }
162
+ //# sourceMappingURL=TierFetcher.js.map
package/dist/index.d.ts CHANGED
@@ -9,5 +9,5 @@ export { WEBHOOK_FORMATS, WEBHOOK_FORMAT_INFO, TEMPLATE_VARIABLES, WEBHOOK_EVENT
9
9
  export type { WebhookFormat, WebhookEventType, WebhookResourceCategory, } from "./webhook-formats.js";
10
10
  export { memoryStorage } from "./cache/memoryStorage.js";
11
11
  export { browserStorage } from "./cache/browserStorage.js";
12
- export { AuthenticationError, ConfigError, InitializationError, RateLimitError, TimeoutError, } from "./errors/index.js";
13
- export type { ConfigErrorCode } from "./errors/index.js";
12
+ export { AuthenticationError, ConfigError, InitializationError, RateLimitError, SdkError, TimeoutError, } from "./errors/index.js";
13
+ export type { ConfigErrorCode, SdkErrorType } from "./errors/index.js";
package/dist/index.js CHANGED
@@ -14,5 +14,5 @@ export { WEBHOOK_FORMATS, WEBHOOK_FORMAT_INFO, TEMPLATE_VARIABLES, WEBHOOK_EVENT
14
14
  export { memoryStorage } from "./cache/memoryStorage.js";
15
15
  export { browserStorage } from "./cache/browserStorage.js";
16
16
  // Errors
17
- export { AuthenticationError, ConfigError, InitializationError, RateLimitError, TimeoutError, } from "./errors/index.js";
17
+ export { AuthenticationError, ConfigError, InitializationError, RateLimitError, SdkError, TimeoutError, } from "./errors/index.js";
18
18
  //# sourceMappingURL=index.js.map