@dinoconfig/js-sdk 1.0.0

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/index.js ADDED
@@ -0,0 +1,883 @@
1
+ class w {
2
+ /**
3
+ * Creates a new HttpClient instance.
4
+ *
5
+ * @param {string} baseUrl - The base URL of the DinoConfig API
6
+ * @param {number} [timeout=10000] - Default request timeout in milliseconds
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const client = new HttpClient('https://api.dinoconfig.com', 15000);
11
+ * ```
12
+ */
13
+ constructor(e, t = 1e4) {
14
+ this.baseUrl = e.replace(/\/$/, ""), this.defaultTimeout = t;
15
+ }
16
+ /**
17
+ * Configures authorization by exchanging the API key for an access token.
18
+ *
19
+ * This method:
20
+ * 1. Extracts the API key from the provided headers
21
+ * 2. Exchanges it for a JWT access token
22
+ * 3. Configures the Authorization header for subsequent requests
23
+ *
24
+ * @async
25
+ * @method configureAuthorizationHeader
26
+ * @param {Record<string, string>} headers - Headers containing the X-API-Key
27
+ * @returns {Promise<void>}
28
+ * @throws {Error} If token exchange fails
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * await client.configureAuthorizationHeader({
33
+ * 'X-API-Key': 'dino_your-api-key-here'
34
+ * });
35
+ * // Client is now authenticated
36
+ * ```
37
+ */
38
+ async configureAuthorizationHeader(e) {
39
+ const t = e["X-API-Key"], s = await this.exchangeApiKeyForToken(t);
40
+ this.defaultHeaders = {
41
+ "Content-Type": "application/json",
42
+ Authorization: `Bearer ${s}`,
43
+ ...e
44
+ };
45
+ }
46
+ /**
47
+ * Exchanges an API key for a JWT access token.
48
+ *
49
+ * Makes a POST request to the token exchange endpoint with the API key
50
+ * and returns the access token from the response.
51
+ *
52
+ * @async
53
+ * @private
54
+ * @method exchangeApiKeyForToken
55
+ * @param {string} apiKey - The API key to exchange
56
+ * @returns {Promise<string>} The JWT access token
57
+ * @throws {Error} If the exchange fails or the API key is invalid
58
+ *
59
+ * @remarks
60
+ * The API key is sent via HTTPS, which encrypts it in transit.
61
+ * The server validates the key and returns a short-lived access token.
62
+ */
63
+ async exchangeApiKeyForToken(e) {
64
+ try {
65
+ const t = await fetch(`${this.baseUrl}/api/auth/sdk-token/exchange`, {
66
+ method: "POST",
67
+ headers: {
68
+ "Content-Type": "application/json",
69
+ "x-api-key": e
70
+ },
71
+ credentials: "include"
72
+ });
73
+ if (!t.ok) {
74
+ const r = await t.text();
75
+ throw new Error(`Failed to exchange API key for token: ${t.status} ${r}`);
76
+ }
77
+ return (await t.json()).access_token;
78
+ } catch (t) {
79
+ const s = t instanceof Error ? t.message : "Unknown error";
80
+ throw new Error(`Failed to authenticate with API key: ${s}`);
81
+ }
82
+ }
83
+ /**
84
+ * Makes an HTTP request to the API.
85
+ *
86
+ * Handles:
87
+ * - Request formatting and headers
88
+ * - Timeout via AbortController
89
+ * - Response parsing
90
+ * - Error handling
91
+ * - Retry logic with exponential backoff
92
+ *
93
+ * @async
94
+ * @private
95
+ * @method request
96
+ * @typeParam T - The expected response data type
97
+ * @param {string} method - HTTP method (GET, POST, PUT, PATCH, DELETE)
98
+ * @param {string} endpoint - API endpoint path (e.g., '/api/configs')
99
+ * @param {any} [data] - Request body data (for POST, PUT, PATCH)
100
+ * @param {RequestOptions} [options={}] - Request customization options
101
+ * @returns {Promise<ApiResponse<T>>} The API response
102
+ * @throws {ApiError} If the request fails after all retries
103
+ */
104
+ async request(e, t, s, r = {}) {
105
+ const a = `${this.baseUrl}${t}`, i = r.timeout || this.defaultTimeout, c = r.retries || 0;
106
+ let h = null;
107
+ for (let l = 0; l <= c; l++)
108
+ try {
109
+ const n = new AbortController(), o = setTimeout(() => n.abort(), i), d = {
110
+ method: e,
111
+ headers: this.defaultHeaders,
112
+ signal: n.signal
113
+ };
114
+ s && (e === "POST" || e === "PUT" || e === "PATCH") && (d.body = JSON.stringify(s));
115
+ const f = await fetch(a, d);
116
+ clearTimeout(o);
117
+ const p = await f.json();
118
+ if (!f.ok)
119
+ throw {
120
+ message: p.message || f.statusText,
121
+ status: f.status,
122
+ code: p.code
123
+ };
124
+ return {
125
+ data: p,
126
+ success: !0
127
+ };
128
+ } catch (n) {
129
+ if (h = n, n instanceof Error && "status" in n) {
130
+ const o = n;
131
+ if (o.status >= 400 && o.status < 500)
132
+ throw n;
133
+ }
134
+ if (l === c)
135
+ throw n;
136
+ await new Promise((o) => setTimeout(o, Math.pow(2, l) * 1e3));
137
+ }
138
+ throw h || new Error("Request failed after all retries");
139
+ }
140
+ /**
141
+ * Makes a GET request to the specified endpoint.
142
+ *
143
+ * @async
144
+ * @method get
145
+ * @typeParam T - The expected response data type
146
+ * @param {string} endpoint - The API endpoint path
147
+ * @param {RequestOptions} [options] - Optional request configuration
148
+ * @returns {Promise<ApiResponse<T>>} The API response
149
+ *
150
+ * @example
151
+ * ```typescript
152
+ * const response = await client.get<Config>('/api/configs/123');
153
+ * console.log(response.data.name);
154
+ * ```
155
+ */
156
+ async get(e, t) {
157
+ return this.request("GET", e, void 0, t);
158
+ }
159
+ /**
160
+ * Makes a POST request to the specified endpoint.
161
+ *
162
+ * @async
163
+ * @method post
164
+ * @typeParam T - The expected response data type
165
+ * @param {string} endpoint - The API endpoint path
166
+ * @param {any} [data] - The request body data
167
+ * @param {RequestOptions} [options] - Optional request configuration
168
+ * @returns {Promise<ApiResponse<T>>} The API response
169
+ *
170
+ * @example
171
+ * ```typescript
172
+ * const response = await client.post<Config>('/api/configs', {
173
+ * name: 'NewConfig',
174
+ * formData: { key: 'value' }
175
+ * });
176
+ * ```
177
+ */
178
+ async post(e, t, s) {
179
+ return this.request("POST", e, t, s);
180
+ }
181
+ /**
182
+ * Makes a PUT request to the specified endpoint.
183
+ *
184
+ * @async
185
+ * @method put
186
+ * @typeParam T - The expected response data type
187
+ * @param {string} endpoint - The API endpoint path
188
+ * @param {any} [data] - The request body data
189
+ * @param {RequestOptions} [options] - Optional request configuration
190
+ * @returns {Promise<ApiResponse<T>>} The API response
191
+ *
192
+ * @example
193
+ * ```typescript
194
+ * const response = await client.put<Config>('/api/configs/123', {
195
+ * name: 'UpdatedConfig'
196
+ * });
197
+ * ```
198
+ */
199
+ async put(e, t, s) {
200
+ return this.request("PUT", e, t, s);
201
+ }
202
+ /**
203
+ * Makes a PATCH request to the specified endpoint.
204
+ *
205
+ * @async
206
+ * @method patch
207
+ * @typeParam T - The expected response data type
208
+ * @param {string} endpoint - The API endpoint path
209
+ * @param {any} [data] - The request body data (partial update)
210
+ * @param {RequestOptions} [options] - Optional request configuration
211
+ * @returns {Promise<ApiResponse<T>>} The API response
212
+ *
213
+ * @example
214
+ * ```typescript
215
+ * const response = await client.patch<Config>('/api/configs/123', {
216
+ * formData: { updatedKey: 'newValue' }
217
+ * });
218
+ * ```
219
+ */
220
+ async patch(e, t, s) {
221
+ return this.request("PATCH", e, t, s);
222
+ }
223
+ /**
224
+ * Makes a DELETE request to the specified endpoint.
225
+ *
226
+ * @async
227
+ * @method delete
228
+ * @typeParam T - The expected response data type
229
+ * @param {string} endpoint - The API endpoint path
230
+ * @param {RequestOptions} [options] - Optional request configuration
231
+ * @returns {Promise<ApiResponse<T>>} The API response
232
+ *
233
+ * @example
234
+ * ```typescript
235
+ * const response = await client.delete('/api/configs/123');
236
+ * ```
237
+ */
238
+ async delete(e, t) {
239
+ return this.request("DELETE", e, void 0, t);
240
+ }
241
+ /**
242
+ * Updates the authorization token.
243
+ *
244
+ * Use this method if you need to manually update the token
245
+ * (e.g., after refreshing it externally).
246
+ *
247
+ * @method setToken
248
+ * @param {string} token - The new JWT access token
249
+ *
250
+ * @example
251
+ * ```typescript
252
+ * client.setToken('new-jwt-token');
253
+ * ```
254
+ */
255
+ setToken(e) {
256
+ this.defaultHeaders.Authorization = `Bearer ${e}`;
257
+ }
258
+ /**
259
+ * Sets a custom header for all subsequent requests.
260
+ *
261
+ * @method setHeader
262
+ * @param {string} key - The header name
263
+ * @param {string} value - The header value
264
+ *
265
+ * @example
266
+ * ```typescript
267
+ * client.setHeader('X-Custom-Header', 'custom-value');
268
+ * ```
269
+ */
270
+ setHeader(e, t) {
271
+ this.defaultHeaders[e] = t;
272
+ }
273
+ /**
274
+ * Removes a custom header from subsequent requests.
275
+ *
276
+ * @method removeHeader
277
+ * @param {string} key - The header name to remove
278
+ *
279
+ * @example
280
+ * ```typescript
281
+ * client.removeHeader('X-Custom-Header');
282
+ * ```
283
+ */
284
+ removeHeader(e) {
285
+ delete this.defaultHeaders[e];
286
+ }
287
+ }
288
+ const y = "/api/sdk/brands";
289
+ class C {
290
+ constructor(e, t) {
291
+ this.httpClient = e, this.cacheManager = t;
292
+ }
293
+ async get(e, t, s) {
294
+ const { brand: r, config: a, requestOptions: i } = this.parseConfigArgs(
295
+ e,
296
+ t,
297
+ s
298
+ ), c = `config:${r}:${a}`, h = this.cacheManager && i?.cache !== !1 && !i?.forceRefresh;
299
+ if (h) {
300
+ const o = await this.cacheManager.get(c);
301
+ if (o !== null)
302
+ return o;
303
+ }
304
+ const l = await this.httpClient.get(
305
+ this.buildConfigUrl(r, a),
306
+ i
307
+ ), n = {
308
+ ...l,
309
+ data: this.transformConfigResponse(l.data)
310
+ };
311
+ return h && l.success && await this.cacheManager.set(c, n, { ttl: i?.ttl }), n;
312
+ }
313
+ async getValue(e, t, s, r) {
314
+ const { brand: a, config: i, key: c, requestOptions: h } = this.parseValueArgs(
315
+ e,
316
+ t,
317
+ s,
318
+ r
319
+ ), l = `config:${a}:${i}:${c}`, n = this.cacheManager && h?.cache !== !1 && !h?.forceRefresh;
320
+ if (n) {
321
+ const d = await this.cacheManager.get(l);
322
+ if (d !== null)
323
+ return d;
324
+ }
325
+ const o = await this.httpClient.get(
326
+ this.buildValueUrl(a, i, c),
327
+ h
328
+ );
329
+ return n && o.success && await this.cacheManager.set(l, o, { ttl: h?.ttl }), o;
330
+ }
331
+ // ─────────────────────────────────────────────────────────────────────────────
332
+ // Private helpers
333
+ // ─────────────────────────────────────────────────────────────────────────────
334
+ /**
335
+ * Parses arguments for the get() method.
336
+ */
337
+ parseConfigArgs(e, t, s) {
338
+ if (typeof t == "string")
339
+ return {
340
+ brand: e,
341
+ config: t,
342
+ requestOptions: s
343
+ };
344
+ const r = this.parsePath(e, 2);
345
+ return {
346
+ brand: r[0],
347
+ config: r[1],
348
+ requestOptions: t
349
+ };
350
+ }
351
+ /**
352
+ * Parses arguments for the getValue() method.
353
+ */
354
+ parseValueArgs(e, t, s, r) {
355
+ if (typeof t == "string")
356
+ return {
357
+ brand: e,
358
+ config: t,
359
+ key: s,
360
+ requestOptions: r
361
+ };
362
+ const a = this.parsePath(e, 3);
363
+ return {
364
+ brand: a[0],
365
+ config: a[1],
366
+ key: a[2],
367
+ requestOptions: t
368
+ };
369
+ }
370
+ /**
371
+ * Parses a dot-notation path into components.
372
+ */
373
+ parsePath(e, t) {
374
+ const s = e.split(".");
375
+ if (s.length !== t) {
376
+ const r = t === 2 ? "brandName.configName" : "brandName.configName.keyName";
377
+ throw new Error(`Invalid path format "${e}". Expected "${r}"`);
378
+ }
379
+ return s;
380
+ }
381
+ /**
382
+ * Builds the URL for fetching an entire config.
383
+ */
384
+ buildConfigUrl(e, t) {
385
+ return `${y}/${this.encode(e)}/configs/${this.encode(t)}`;
386
+ }
387
+ /**
388
+ * Builds the URL for fetching a single value.
389
+ */
390
+ buildValueUrl(e, t, s) {
391
+ return `${y}/${this.encode(e)}/configs/${this.encode(t)}/${this.encode(s)}`;
392
+ }
393
+ /**
394
+ * URL-encodes a path segment.
395
+ */
396
+ encode(e) {
397
+ return encodeURIComponent(e);
398
+ }
399
+ /**
400
+ * Transforms the backend response to the public ConfigData shape.
401
+ */
402
+ transformConfigResponse(e) {
403
+ return {
404
+ name: e.name,
405
+ description: e.description,
406
+ values: e.formData,
407
+ version: e.version,
408
+ keys: e.keys,
409
+ createdAt: e.createdAt,
410
+ updatedAt: e.updatedAt
411
+ };
412
+ }
413
+ }
414
+ const g = "/api/sdk";
415
+ class x {
416
+ constructor(e) {
417
+ this.httpClient = e;
418
+ }
419
+ /**
420
+ * Lists all brands accessible by the current API key.
421
+ *
422
+ * @example
423
+ * ```typescript
424
+ * const response = await dinoconfig.discovery.listBrands();
425
+ * response.data.forEach(brand => {
426
+ * console.log(`${brand.name}: ${brand.configCount} configs`);
427
+ * });
428
+ * ```
429
+ */
430
+ async listBrands(e) {
431
+ const t = await this.httpClient.get(
432
+ `${g}/brands`,
433
+ e
434
+ );
435
+ return this.extractData(t, t.data.brands);
436
+ }
437
+ /**
438
+ * Lists all configurations for a specific brand.
439
+ *
440
+ * @example
441
+ * ```typescript
442
+ * const response = await dinoconfig.discovery.listConfigs('MyBrand');
443
+ * response.data.forEach(config => {
444
+ * console.log(`${config.name}: ${config.keys.length} keys`);
445
+ * });
446
+ * ```
447
+ */
448
+ async listConfigs(e, t) {
449
+ const s = await this.httpClient.get(
450
+ this.buildBrandUrl(e, "/configs"),
451
+ t
452
+ );
453
+ return this.extractData(s, s.data.configs);
454
+ }
455
+ /**
456
+ * Gets the schema/structure for a specific configuration.
457
+ *
458
+ * @example
459
+ * ```typescript
460
+ * const response = await dinoconfig.discovery.getSchema('MyBrand', 'FeatureFlags');
461
+ * Object.entries(response.data.fields).forEach(([name, field]) => {
462
+ * console.log(`${name}: ${field.type}`);
463
+ * });
464
+ * ```
465
+ */
466
+ async getSchema(e, t, s) {
467
+ return this.httpClient.get(
468
+ this.buildConfigUrl(e, t, "/schema"),
469
+ s
470
+ );
471
+ }
472
+ /**
473
+ * Performs full introspection, returning all brands, configs, and keys.
474
+ *
475
+ * @example
476
+ * ```typescript
477
+ * const response = await dinoconfig.discovery.introspect();
478
+ * response.data.brands.forEach(brand => {
479
+ * console.log(`Brand: ${brand.name}`);
480
+ * brand.configs.forEach(config => {
481
+ * console.log(` Config: ${config.name} (${config.keys.length} keys)`);
482
+ * });
483
+ * });
484
+ * ```
485
+ */
486
+ async introspect(e) {
487
+ return this.httpClient.get(`${g}/introspect`, e);
488
+ }
489
+ // ─────────────────────────────────────────────────────────────────────────────
490
+ // Private helpers
491
+ // ─────────────────────────────────────────────────────────────────────────────
492
+ /**
493
+ * Builds URL path for brand-level endpoints.
494
+ */
495
+ buildBrandUrl(e, t = "") {
496
+ return `${g}/brands/${this.encode(e)}${t}`;
497
+ }
498
+ /**
499
+ * Builds URL path for config-level endpoints.
500
+ */
501
+ buildConfigUrl(e, t, s = "") {
502
+ return `${g}/brands/${this.encode(e)}/configs/${this.encode(t)}${s}`;
503
+ }
504
+ /**
505
+ * URL-encodes a path segment.
506
+ */
507
+ encode(e) {
508
+ return encodeURIComponent(e);
509
+ }
510
+ /**
511
+ * Extracts and transforms response data.
512
+ */
513
+ extractData(e, t) {
514
+ return {
515
+ ...e,
516
+ data: t
517
+ };
518
+ }
519
+ }
520
+ class m {
521
+ constructor(e, t) {
522
+ this.ttl = e, this.maxSize = t, this.cache = /* @__PURE__ */ new Map(), this.hits = 0, this.misses = 0;
523
+ }
524
+ /**
525
+ * Get a value from the cache.
526
+ *
527
+ * @param {string} key - Cache key
528
+ * @returns {T | null} Cached value or null if not found/expired
529
+ */
530
+ get(e) {
531
+ const t = this.cache.get(e);
532
+ return t ? this.isExpired(t) ? (this.cache.delete(e), this.misses++, null) : (this.hits++, t.value) : (this.misses++, null);
533
+ }
534
+ /**
535
+ * Set a value in the cache.
536
+ *
537
+ * @param {string} key - Cache key
538
+ * @param {T} value - Value to cache
539
+ * @param {CacheOptions} options - Optional cache options
540
+ */
541
+ set(e, t, s) {
542
+ const r = s?.ttl ?? this.ttl, a = {
543
+ value: t,
544
+ timestamp: Date.now(),
545
+ expiresAt: Date.now() + r
546
+ };
547
+ this.cache.size >= this.maxSize && !this.cache.has(e) && this.evictOldest(), this.cache.set(e, a);
548
+ }
549
+ /**
550
+ * Delete a value from the cache.
551
+ *
552
+ * @param {string} key - Cache key
553
+ */
554
+ delete(e) {
555
+ this.cache.delete(e);
556
+ }
557
+ /**
558
+ * Clear all entries from the cache.
559
+ */
560
+ clear() {
561
+ this.cache.clear(), this.hits = 0, this.misses = 0;
562
+ }
563
+ /**
564
+ * Invalidate entries matching a pattern.
565
+ *
566
+ * @param {string} pattern - Regex pattern to match keys
567
+ */
568
+ invalidate(e) {
569
+ const t = new RegExp(e);
570
+ for (const s of this.cache.keys())
571
+ t.test(s) && this.cache.delete(s);
572
+ }
573
+ /**
574
+ * Check if an entry exists and is not expired.
575
+ *
576
+ * @param {string} key - Cache key
577
+ * @returns {boolean} True if entry exists and is valid
578
+ */
579
+ has(e) {
580
+ const t = this.cache.get(e);
581
+ return t ? this.isExpired(t) ? (this.cache.delete(e), !1) : !0 : !1;
582
+ }
583
+ /**
584
+ * Get cache statistics.
585
+ *
586
+ * @returns {CacheStats} Cache statistics
587
+ */
588
+ getStats() {
589
+ const e = this.hits + this.misses;
590
+ return {
591
+ hits: this.hits,
592
+ misses: this.misses,
593
+ size: this.cache.size,
594
+ hitRate: e > 0 ? this.hits / e : 0
595
+ };
596
+ }
597
+ /**
598
+ * Check if an entry is expired.
599
+ *
600
+ * @private
601
+ * @param {CacheEntry} entry - Cache entry
602
+ * @returns {boolean} True if expired
603
+ */
604
+ isExpired(e) {
605
+ return Date.now() > e.expiresAt;
606
+ }
607
+ /**
608
+ * Evict the oldest entry from the cache.
609
+ * Uses LRU-like strategy based on expiration time.
610
+ *
611
+ * @private
612
+ */
613
+ evictOldest() {
614
+ let e = null, t = 1 / 0;
615
+ for (const [s, r] of this.cache.entries())
616
+ r.expiresAt < t && (t = r.expiresAt, e = s);
617
+ e && this.cache.delete(e);
618
+ }
619
+ }
620
+ class A {
621
+ constructor() {
622
+ if (typeof window > "u" || !window.localStorage)
623
+ throw new Error("localStorage is not available in this environment");
624
+ this.storage = window.localStorage;
625
+ }
626
+ getItem(e) {
627
+ try {
628
+ return this.storage.getItem(e);
629
+ } catch {
630
+ return null;
631
+ }
632
+ }
633
+ setItem(e, t) {
634
+ try {
635
+ this.storage.setItem(e, t);
636
+ } catch {
637
+ }
638
+ }
639
+ removeItem(e) {
640
+ try {
641
+ this.storage.removeItem(e);
642
+ } catch {
643
+ }
644
+ }
645
+ clear() {
646
+ try {
647
+ const e = Object.keys(this.storage);
648
+ for (const t of e)
649
+ t.startsWith("dinoconfig:") && this.storage.removeItem(t);
650
+ } catch {
651
+ }
652
+ }
653
+ }
654
+ class b {
655
+ constructor(e) {
656
+ if (this.prefix = "dinoconfig:", e === "localStorage")
657
+ this.adapter = new A();
658
+ else
659
+ throw new Error("IndexedDB storage is not yet implemented");
660
+ }
661
+ /**
662
+ * Get a value from storage cache.
663
+ *
664
+ * @param {string} key - Cache key
665
+ * @returns {Promise<T | null>} Cached value or null if not found/expired
666
+ */
667
+ async get(e) {
668
+ try {
669
+ const t = this.adapter.getItem(this.prefix + e);
670
+ if (!t)
671
+ return null;
672
+ const s = JSON.parse(t);
673
+ return this.isExpired(s) ? (this.adapter.removeItem(this.prefix + e), null) : s.value;
674
+ } catch {
675
+ return null;
676
+ }
677
+ }
678
+ /**
679
+ * Set a value in storage cache.
680
+ *
681
+ * @param {string} key - Cache key
682
+ * @param {T} value - Value to cache
683
+ * @param {CacheOptions} options - Optional cache options
684
+ */
685
+ async set(e, t, s) {
686
+ try {
687
+ const r = s?.ttl ?? 3e5, a = {
688
+ value: t,
689
+ timestamp: Date.now(),
690
+ expiresAt: Date.now() + r
691
+ };
692
+ this.adapter.setItem(this.prefix + e, JSON.stringify(a));
693
+ } catch {
694
+ }
695
+ }
696
+ /**
697
+ * Delete a value from storage cache.
698
+ *
699
+ * @param {string} key - Cache key
700
+ */
701
+ async delete(e) {
702
+ try {
703
+ this.adapter.removeItem(this.prefix + e);
704
+ } catch {
705
+ }
706
+ }
707
+ /**
708
+ * Clear all entries from storage cache.
709
+ */
710
+ async clear() {
711
+ try {
712
+ this.adapter.clear();
713
+ } catch {
714
+ }
715
+ }
716
+ /**
717
+ * Invalidate entries matching a pattern.
718
+ *
719
+ * @param {string} pattern - Regex pattern to match keys
720
+ */
721
+ async invalidate(e) {
722
+ try {
723
+ const t = new RegExp(e), r = this.adapter.storage;
724
+ if (!r)
725
+ return;
726
+ const a = [];
727
+ for (let i = 0; i < r.length; i++) {
728
+ const c = r.key(i);
729
+ if (c && c.startsWith(this.prefix)) {
730
+ const h = c.substring(this.prefix.length);
731
+ t.test(h) && a.push(c);
732
+ }
733
+ }
734
+ for (const i of a)
735
+ this.adapter.removeItem(i);
736
+ } catch {
737
+ }
738
+ }
739
+ /**
740
+ * Check if an entry is expired.
741
+ *
742
+ * @private
743
+ * @param {CacheEntry} entry - Cache entry
744
+ * @returns {boolean} True if expired
745
+ */
746
+ isExpired(e) {
747
+ return Date.now() > e.expiresAt;
748
+ }
749
+ }
750
+ class $ {
751
+ constructor(e) {
752
+ if (this.config = e, !e.enabled) {
753
+ this.memoryCache = new m(0, 0);
754
+ return;
755
+ }
756
+ if (this.memoryCache = new m(
757
+ e.ttl,
758
+ e.maxSize
759
+ ), e.storage && e.storage !== "memory")
760
+ try {
761
+ this.storageCache = new b(e.storage);
762
+ } catch {
763
+ this.storageCache = void 0;
764
+ }
765
+ }
766
+ /**
767
+ * Get a value from cache.
768
+ * Checks L1 (memory) first, then L2 (storage) if available.
769
+ *
770
+ * @param {string} key - Cache key
771
+ * @returns {Promise<T | null>} Cached value or null if not found
772
+ */
773
+ async get(e) {
774
+ if (!this.config.enabled)
775
+ return null;
776
+ const t = this.memoryCache.get(e);
777
+ if (t !== null)
778
+ return t;
779
+ if (this.storageCache) {
780
+ const s = await this.storageCache.get(e);
781
+ if (s !== null)
782
+ return this.memoryCache.set(e, s), s;
783
+ }
784
+ return null;
785
+ }
786
+ /**
787
+ * Set a value in cache.
788
+ * Writes to both L1 (memory) and L2 (storage) if available.
789
+ *
790
+ * @param {string} key - Cache key
791
+ * @param {T} value - Value to cache
792
+ * @param {CacheOptions} options - Optional cache options
793
+ */
794
+ async set(e, t, s) {
795
+ this.config.enabled && (this.memoryCache.set(e, t, s), this.storageCache && await this.storageCache.set(e, t, s));
796
+ }
797
+ /**
798
+ * Delete a value from cache.
799
+ * Removes from both L1 and L2.
800
+ *
801
+ * @param {string} key - Cache key
802
+ */
803
+ async delete(e) {
804
+ this.memoryCache.delete(e), this.storageCache && await this.storageCache.delete(e);
805
+ }
806
+ /**
807
+ * Clear all cache entries.
808
+ */
809
+ async clear() {
810
+ this.memoryCache.clear(), this.storageCache && await this.storageCache.clear();
811
+ }
812
+ /**
813
+ * Invalidate entries matching a pattern.
814
+ *
815
+ * @param {string} pattern - Regex pattern to match keys
816
+ */
817
+ async invalidate(e) {
818
+ if (!e) {
819
+ await this.clear();
820
+ return;
821
+ }
822
+ this.memoryCache.invalidate(e), this.storageCache && await this.storageCache.invalidate(e);
823
+ }
824
+ /**
825
+ * Check if a key exists in cache.
826
+ *
827
+ * @param {string} key - Cache key
828
+ * @returns {boolean} True if key exists and is not expired
829
+ */
830
+ has(e) {
831
+ return this.config.enabled ? this.memoryCache.has(e) : !1;
832
+ }
833
+ /**
834
+ * Get cache statistics.
835
+ *
836
+ * @returns {CacheStats} Cache statistics
837
+ */
838
+ getStats() {
839
+ return this.memoryCache.getStats();
840
+ }
841
+ /**
842
+ * Prefetch a value into cache.
843
+ * This is a convenience method for warming the cache.
844
+ *
845
+ * @param {string} key - Cache key
846
+ * @param {() => Promise<T>} fetcher - Function to fetch the value if not cached
847
+ */
848
+ async prefetch(e, t) {
849
+ const s = await this.get(e);
850
+ if (s !== null)
851
+ return s;
852
+ const r = await t();
853
+ return await this.set(e, r), r;
854
+ }
855
+ }
856
+ async function I(u) {
857
+ const {
858
+ apiKey: e,
859
+ baseUrl: t = "http://localhost:3000",
860
+ timeout: s = 1e4,
861
+ cache: r
862
+ } = u, a = new w(t, s);
863
+ await a.configureAuthorizationHeader({
864
+ "X-API-Key": e
865
+ });
866
+ const i = new $({
867
+ enabled: r?.enabled ?? !1,
868
+ ttl: r?.ttl ?? 6e4,
869
+ maxSize: r?.maxSize ?? 1e3,
870
+ storage: r?.storage,
871
+ staleWhileRevalidate: r?.staleWhileRevalidate ?? !1
872
+ });
873
+ return {
874
+ configs: new C(a, i),
875
+ discovery: new x(a),
876
+ cache: i
877
+ };
878
+ }
879
+ export {
880
+ C as ConfigAPI,
881
+ x as DiscoveryAPI,
882
+ I as dinoconfigApi
883
+ };