@route-forge/core 0.0.1

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.js ADDED
@@ -0,0 +1,690 @@
1
+ // src/cache.ts
2
+ var KEY_PREFIX = "route-forge:";
3
+ function pickStorage(storage) {
4
+ if (storage === "memory") return null;
5
+ if (typeof globalThis === "undefined") return null;
6
+ if (storage === "sessionStorage") {
7
+ return globalThis.sessionStorage ?? null;
8
+ }
9
+ if (storage === "localStorage") {
10
+ return globalThis.localStorage ?? null;
11
+ }
12
+ return null;
13
+ }
14
+ var RouteCache = class {
15
+ constructor(opts) {
16
+ this.memory = /* @__PURE__ */ new Map();
17
+ this.storage = opts.storage;
18
+ this.fallbackTtl = opts.ttl;
19
+ this.backend = pickStorage(opts.storage);
20
+ }
21
+ key(level) {
22
+ return `${KEY_PREFIX}${level}`;
23
+ }
24
+ get(level) {
25
+ if (this.storage === "memory" || !this.backend) {
26
+ return this.getFromMemory(level);
27
+ }
28
+ const raw = this.backend.getItem(this.key(level));
29
+ if (!raw) return void 0;
30
+ try {
31
+ const entry = JSON.parse(raw);
32
+ if (this.isExpired(entry)) {
33
+ this.del(level);
34
+ return void 0;
35
+ }
36
+ return entry;
37
+ } catch {
38
+ return void 0;
39
+ }
40
+ }
41
+ getFromMemory(level) {
42
+ const entry = this.memory.get(level);
43
+ if (!entry) return void 0;
44
+ if (this.isExpired(entry)) {
45
+ this.memory.delete(level);
46
+ return void 0;
47
+ }
48
+ return entry;
49
+ }
50
+ set(resp) {
51
+ const ttl = resp.cache !== void 0 && resp.cache !== null ? resp.cache : this.fallbackTtl;
52
+ const entry = {
53
+ level: resp.level,
54
+ routes: resp.routes,
55
+ ttl,
56
+ cachedAt: Date.now()
57
+ };
58
+ if (this.storage === "memory" || !this.backend) {
59
+ this.memory.set(resp.level, entry);
60
+ return;
61
+ }
62
+ try {
63
+ this.backend.setItem(this.key(resp.level), JSON.stringify(entry));
64
+ } catch {
65
+ this.memory.set(resp.level, entry);
66
+ }
67
+ }
68
+ del(level) {
69
+ this.memory.delete(level);
70
+ if (this.backend) {
71
+ try {
72
+ this.backend.removeItem(this.key(level));
73
+ } catch {
74
+ }
75
+ }
76
+ }
77
+ clear() {
78
+ this.memory.clear();
79
+ if (this.backend) {
80
+ try {
81
+ const toRemove = [];
82
+ for (let i = 0; i < this.backend.length; i++) {
83
+ const k = this.backend.key(i);
84
+ if (k && k.startsWith(KEY_PREFIX)) toRemove.push(k);
85
+ }
86
+ for (const k of toRemove) this.backend.removeItem(k);
87
+ } catch {
88
+ }
89
+ }
90
+ }
91
+ isExpired(entry) {
92
+ if (entry.ttl === null) return false;
93
+ if (entry.ttl === 0) return false;
94
+ return Date.now() - entry.cachedAt > entry.ttl * 1e3;
95
+ }
96
+ };
97
+
98
+ // src/errors.ts
99
+ var ForgeError = class extends Error {
100
+ constructor(message, opts) {
101
+ super(message);
102
+ this.name = this.constructor.name;
103
+ this.code = opts.code;
104
+ if (opts.route !== void 0) this.route = opts.route;
105
+ if (opts.level !== void 0) this.level = opts.level;
106
+ if (opts.context !== void 0) this.context = opts.context;
107
+ if (opts.cause !== void 0) this.cause = opts.cause;
108
+ }
109
+ };
110
+ var UnknownRouteError = class extends ForgeError {
111
+ constructor(route, level) {
112
+ super(`Route "${route}" not found${level ? ` in level "${level}"` : ""}`, {
113
+ code: "RF_FE_001",
114
+ route,
115
+ level
116
+ });
117
+ }
118
+ };
119
+ var UnknownLevelError = class extends ForgeError {
120
+ constructor(level) {
121
+ super(`Level "${level}" not declared in options.levels`, {
122
+ code: "RF_FE_002",
123
+ level
124
+ });
125
+ }
126
+ };
127
+ var MissingRouteParamError = class extends ForgeError {
128
+ constructor(route, missingParams) {
129
+ super(`Missing path parameter(s) ${missingParams.join(", ")} for route "${route}"`, {
130
+ code: "RF_FE_003",
131
+ route,
132
+ context: { missingParams }
133
+ });
134
+ }
135
+ };
136
+ var InsufficientAuthError = class extends ForgeError {
137
+ constructor(level) {
138
+ super(`Insufficient auth: level "${level}" requires login`, {
139
+ code: "RF_FE_004",
140
+ level
141
+ });
142
+ }
143
+ };
144
+ var AdapterNotFoundError = class extends ForgeError {
145
+ constructor(adapter) {
146
+ super(`Adapter "${adapter}" not available; install axios or use 'builtin'`, {
147
+ code: "RF_FE_005",
148
+ context: { adapter }
149
+ });
150
+ }
151
+ };
152
+ var InvalidInterceptorReturnError = class extends ForgeError {
153
+ constructor(route) {
154
+ super(`Request interceptor must return a RequestConfig object`, {
155
+ code: "RF_FE_006",
156
+ route
157
+ });
158
+ }
159
+ };
160
+ var NetworkError = class extends ForgeError {
161
+ constructor(message, route, level, cause) {
162
+ super(message, { code: "RF_FE_007", route, level, cause });
163
+ }
164
+ };
165
+ var HTTPError = class extends ForgeError {
166
+ constructor(message, opts) {
167
+ super(message, {
168
+ code: "RF_FE_008",
169
+ route: opts.route,
170
+ level: opts.level,
171
+ context: { status: opts.status, url: opts.url, method: opts.method },
172
+ cause: opts.cause
173
+ });
174
+ }
175
+ };
176
+
177
+ // src/interceptors.ts
178
+ var InterceptorManagerImpl = class {
179
+ constructor() {
180
+ this.handlers = [];
181
+ this.nextId = 0;
182
+ }
183
+ use(onFulfilled, onRejected) {
184
+ const id = this.nextId++;
185
+ this.handlers.push({ id, onFulfilled, onRejected });
186
+ return id;
187
+ }
188
+ eject(id) {
189
+ const idx = this.handlers.findIndex((h) => h.id === id);
190
+ if (idx >= 0) this.handlers.splice(idx, 1);
191
+ }
192
+ clear() {
193
+ this.handlers = [];
194
+ }
195
+ forEach(fn) {
196
+ for (const h of this.handlers) fn(h);
197
+ }
198
+ /** 测试用:当前注册数量 */
199
+ get size() {
200
+ return this.handlers.length;
201
+ }
202
+ };
203
+ async function runRequestInterceptors(manager, initial) {
204
+ const handlers = [];
205
+ manager.forEach((h) => handlers.push(h));
206
+ handlers.reverse();
207
+ let p = Promise.resolve(initial);
208
+ for (const h of handlers) {
209
+ const onF = h.onFulfilled;
210
+ const onR = h.onRejected;
211
+ p = p.then(
212
+ async (v) => {
213
+ if (!onF) return v;
214
+ const result = await onF(v);
215
+ if (result === null || typeof result !== "object") {
216
+ throw new InvalidInterceptorReturnError();
217
+ }
218
+ return result;
219
+ },
220
+ (e) => onR ? onR(e) : Promise.reject(e)
221
+ );
222
+ }
223
+ return p;
224
+ }
225
+ async function runResponseInterceptors(manager, source) {
226
+ const handlers = [];
227
+ manager.forEach((h) => handlers.push(h));
228
+ let p = source;
229
+ for (const h of handlers) {
230
+ const onF = h.onFulfilled;
231
+ const onR = h.onRejected;
232
+ p = p.then(
233
+ (v) => onF ? onF(v) : v,
234
+ (e) => onR ? onR(e) : Promise.reject(e)
235
+ );
236
+ }
237
+ return p;
238
+ }
239
+ function createInterceptorManager() {
240
+ return new InterceptorManagerImpl();
241
+ }
242
+
243
+ // src/adapters/builtin-http.ts
244
+ function createBuiltinHttp(forgeInterceptors) {
245
+ const requestMgr = forgeInterceptors?.request ?? createInterceptorManager();
246
+ const responseMgr = forgeInterceptors?.response ?? createInterceptorManager();
247
+ async function request(config) {
248
+ const finalConfig = await runRequestInterceptors(requestMgr, config);
249
+ const signal = finalConfig.timeout && finalConfig.timeout > 0 ? AbortSignal.timeout(finalConfig.timeout) : void 0;
250
+ let url = finalConfig.url;
251
+ if (finalConfig.paramsSerializer && finalConfig.params) {
252
+ const qs = finalConfig.paramsSerializer(finalConfig.params);
253
+ if (qs) {
254
+ url = url.includes("?") ? `${url}&${qs}` : `${url}?${qs}`;
255
+ }
256
+ }
257
+ const fetchInit = {
258
+ method: finalConfig.method,
259
+ headers: new Headers(finalConfig.headers)
260
+ };
261
+ if (signal) fetchInit.signal = signal;
262
+ if (finalConfig.body !== void 0 && !["GET", "HEAD"].includes(finalConfig.method.toUpperCase())) {
263
+ fetchInit.body = typeof finalConfig.body === "string" ? finalConfig.body : JSON.stringify(finalConfig.body);
264
+ }
265
+ const res = await fetch(url, fetchInit);
266
+ const text = await res.text();
267
+ let data = text;
268
+ const contentType = res.headers.get("content-type") ?? "";
269
+ if (contentType.includes("application/json")) {
270
+ try {
271
+ data = JSON.parse(text);
272
+ } catch {
273
+ }
274
+ }
275
+ const responseData = {
276
+ route: finalConfig.route,
277
+ level: finalConfig.level,
278
+ method: finalConfig.method,
279
+ url,
280
+ status: res.status,
281
+ headers: res.headers,
282
+ data,
283
+ config: finalConfig
284
+ };
285
+ return runResponseInterceptors(
286
+ responseMgr,
287
+ Promise.resolve(responseData)
288
+ );
289
+ }
290
+ const get = (url, config) => request({ ...config, url, method: "GET" });
291
+ const post = (url, config) => request({ ...config, url, method: "POST" });
292
+ const put = (url, config) => request({ ...config, url, method: "PUT" });
293
+ const patch = (url, config) => request({ ...config, url, method: "PATCH" });
294
+ const del = (url, config) => request({ ...config, url, method: "DELETE" });
295
+ return {
296
+ request,
297
+ interceptors: {
298
+ request: requestMgr,
299
+ response: responseMgr
300
+ },
301
+ runsInterceptors: true,
302
+ get,
303
+ post,
304
+ put,
305
+ patch,
306
+ delete: del
307
+ };
308
+ }
309
+
310
+ // src/adapters/axios.ts
311
+ async function wrapAxiosAdapter() {
312
+ let axios;
313
+ try {
314
+ const mod = await import(
315
+ /* @vite-ignore */
316
+ 'axios'
317
+ );
318
+ axios = mod.default ?? mod;
319
+ } catch {
320
+ if (typeof globalThis !== "undefined" && globalThis.axios) {
321
+ axios = globalThis.axios;
322
+ }
323
+ }
324
+ if (!axios || typeof axios.request !== "function") return null;
325
+ async function request(config) {
326
+ const headers = { ...config.headers };
327
+ const res = await axios.request({
328
+ url: config.url,
329
+ method: config.method,
330
+ headers,
331
+ data: config.body
332
+ // axios 会处理 baseURL/transformRequest 等 defaults
333
+ });
334
+ return {
335
+ route: config.route,
336
+ level: config.level,
337
+ method: config.method,
338
+ url: config.url,
339
+ status: res.status,
340
+ headers: new Headers(res.headers ?? {}),
341
+ data: res.data,
342
+ config
343
+ };
344
+ }
345
+ return { request, interceptors: void 0 };
346
+ }
347
+
348
+ // src/adapters/index.ts
349
+ async function resolveAdapter(opts) {
350
+ const { adapter } = opts;
351
+ if (adapter && typeof adapter === "object") {
352
+ return {
353
+ request: (config) => adapter.request(config),
354
+ interceptors: adapter.interceptors
355
+ };
356
+ }
357
+ if (adapter === "builtin") {
358
+ return createBuiltinHttp(opts.forgeInterceptors);
359
+ }
360
+ if (adapter === "axios") {
361
+ const wrapped2 = await wrapAxiosAdapter();
362
+ if (!wrapped2) throw new AdapterNotFoundError("axios");
363
+ return wrapped2;
364
+ }
365
+ const wrapped = await wrapAxiosAdapter().catch(() => null);
366
+ if (wrapped) return wrapped;
367
+ return createBuiltinHttp(opts.forgeInterceptors);
368
+ }
369
+
370
+ // src/forge.ts
371
+ var DEFAULT_TIMEOUT = 3e4;
372
+ var DEFAULT_CACHE_TTL = 3600;
373
+ var DEFAULT_NAME_SEPARATOR = ".";
374
+ function createRouteForge(options) {
375
+ if (!options.endpoint) throw new TypeError("options.endpoint is required");
376
+ const {
377
+ adapter = "auto",
378
+ timeout = DEFAULT_TIMEOUT,
379
+ baseURL = "",
380
+ nameSeparator = DEFAULT_NAME_SEPARATOR,
381
+ auth,
382
+ interceptors: declarativeInterceptors,
383
+ cache: cacheOpts = {}
384
+ } = options;
385
+ const explicitLevels = options.levels;
386
+ const explicitEager = options.eager;
387
+ const explicitStrict = options.strict ?? false;
388
+ const explicitEndpoint = options.endpoint;
389
+ let effectiveLevels = explicitLevels ?? [];
390
+ let effectiveEager = explicitEager ?? [];
391
+ let effectiveStrict = explicitStrict;
392
+ let effectiveEndpoint = explicitEndpoint;
393
+ const summaryPromise = (async () => {
394
+ try {
395
+ const summaryUrl = explicitEndpoint;
396
+ const resp = await fetch(summaryUrl, { method: "GET" });
397
+ if (!resp.ok) {
398
+ console.warn(
399
+ `[route-forge] summary endpoint ${summaryUrl} unreachable (HTTP ${resp.status}); falling back to explicit options`
400
+ );
401
+ return null;
402
+ }
403
+ return await resp.json();
404
+ } catch (e) {
405
+ if (explicitLevels && explicitLevels.length > 0) {
406
+ console.warn(
407
+ `[route-forge] summary endpoint unreachable: ${e.message}; using explicit levels`
408
+ );
409
+ return null;
410
+ }
411
+ throw new UnknownLevelError("(auto-discovery)");
412
+ }
413
+ })();
414
+ const autoDiscoveryPromise = summaryPromise.then((summary) => {
415
+ if (summary === null) {
416
+ return;
417
+ }
418
+ if (summary.config.endpoint_prefix && summary.config.endpoint_prefix !== explicitEndpoint) {
419
+ console.warn(
420
+ `[route-forge] backend endpoint_prefix "${summary.config.endpoint_prefix}" overrides frontend endpoint "${explicitEndpoint}"`
421
+ );
422
+ effectiveEndpoint = summary.config.endpoint_prefix;
423
+ }
424
+ if (summary.config.strict_mode && !explicitStrict) {
425
+ console.warn(
426
+ "[route-forge] backend strict_mode=true overrides frontend strict=false; forcing strict=true"
427
+ );
428
+ effectiveStrict = true;
429
+ }
430
+ const backendLevels = Object.keys(summary.levels);
431
+ if (explicitLevels && explicitLevels.length > 0) {
432
+ const intersection = explicitLevels.filter((l) => backendLevels.includes(l));
433
+ const removed = explicitLevels.filter((l) => !backendLevels.includes(l));
434
+ if (removed.length > 0) {
435
+ console.warn(
436
+ `[route-forge] levels not in backend summary and dropped: ${removed.join(", ")}`
437
+ );
438
+ }
439
+ effectiveLevels = intersection;
440
+ } else {
441
+ effectiveLevels = backendLevels;
442
+ }
443
+ if (!explicitEager) {
444
+ effectiveEager = backendLevels.filter((lvl) => summary.levels[lvl]?.load === "eager");
445
+ }
446
+ });
447
+ autoDiscoveryPromise.catch(() => {
448
+ });
449
+ const cacheTtl = cacheOpts.ttl ?? DEFAULT_CACHE_TTL;
450
+ const cacheStorage = cacheOpts.storage ?? "memory";
451
+ const cache = new RouteCache({ storage: cacheStorage, ttl: cacheTtl });
452
+ const requestInterceptors = new InterceptorManagerImpl();
453
+ const responseInterceptors = new InterceptorManagerImpl();
454
+ if (declarativeInterceptors?.request) {
455
+ for (const pair of declarativeInterceptors.request) {
456
+ const [onFulfilled, onRejected] = pair;
457
+ requestInterceptors.use(onFulfilled, onRejected);
458
+ }
459
+ }
460
+ if (declarativeInterceptors?.response) {
461
+ for (const pair of declarativeInterceptors.response) {
462
+ const [onFulfilled, onRejected] = pair;
463
+ responseInterceptors.use(onFulfilled, onRejected);
464
+ }
465
+ }
466
+ const adapterPromise = resolveAdapter({
467
+ adapter,
468
+ forgeInterceptors: { request: requestInterceptors, response: responseInterceptors }
469
+ });
470
+ let adapterResolved = false;
471
+ let adapterObj = null;
472
+ async function ensureAdapter() {
473
+ if (!adapterResolved) {
474
+ adapterObj = await adapterPromise.catch((e) => {
475
+ if (e instanceof AdapterNotFoundError) throw e;
476
+ return resolveAdapter({ adapter: "builtin" });
477
+ });
478
+ adapterResolved = true;
479
+ }
480
+ return adapterObj;
481
+ }
482
+ const inflight = /* @__PURE__ */ new Map();
483
+ function isAuthRequired(level) {
484
+ return Boolean(auth?.levels?.[level]);
485
+ }
486
+ function assertAuth(level) {
487
+ if (isAuthRequired(level) && auth?.state && !auth.state()) {
488
+ throw new InsufficientAuthError(level);
489
+ }
490
+ }
491
+ function assertLevelDeclared(level) {
492
+ if (!effectiveLevels.includes(level)) {
493
+ throw new UnknownLevelError(level);
494
+ }
495
+ }
496
+ function buildUrl(level) {
497
+ const base = baseURL.endsWith("/") ? baseURL.slice(0, -1) : baseURL;
498
+ const ep = effectiveEndpoint.startsWith("/") ? effectiveEndpoint : `/${effectiveEndpoint}`;
499
+ return `${base}${ep}/${encodeURIComponent(level)}`;
500
+ }
501
+ async function fetchLevel(level) {
502
+ const adp = await ensureAdapter();
503
+ const config = {
504
+ route: `__forge__.load.${level}`,
505
+ level,
506
+ method: "GET",
507
+ url: buildUrl(level),
508
+ headers: { Accept: "application/json" },
509
+ params: {},
510
+ timeout,
511
+ meta: {
512
+ name: `__forge__.load.${level}`,
513
+ uri: buildUrl(level),
514
+ methods: ["GET"],
515
+ parameters: [],
516
+ level
517
+ }
518
+ };
519
+ const resp = await adp.request(config);
520
+ if (!resp || resp.status < 200 || resp.status >= 300) {
521
+ throw new Error(`Failed to load level ${level}: HTTP ${resp?.status}`);
522
+ }
523
+ const data = resp.data;
524
+ return data;
525
+ }
526
+ async function loadOne(level) {
527
+ assertLevelDeclared(level);
528
+ assertAuth(level);
529
+ if (cache.get(level)) return;
530
+ const existing = inflight.get(level);
531
+ if (existing) return existing;
532
+ const p = (async () => {
533
+ try {
534
+ const resp = await fetchLevel(level);
535
+ cache.set(resp);
536
+ } finally {
537
+ inflight.delete(level);
538
+ }
539
+ })();
540
+ inflight.set(level, p);
541
+ return p;
542
+ }
543
+ async function load(level) {
544
+ await autoDiscoveryPromise;
545
+ const list = Array.isArray(level) ? level : [level];
546
+ await Promise.all(list.map(loadOne));
547
+ }
548
+ function route(name, params) {
549
+ const meta = findRouteMeta(name);
550
+ if (!meta) {
551
+ if (effectiveStrict) throw new UnknownRouteError(name);
552
+ return "";
553
+ }
554
+ return buildRequestUrl(meta, params ?? {});
555
+ }
556
+ function buildRequestUrl(meta, params) {
557
+ let uri = meta.uri;
558
+ for (const p of meta.parameters) {
559
+ const v = params[p];
560
+ if (v === void 0) {
561
+ if (effectiveStrict) throw new MissingRouteParamError(meta.name, [p]);
562
+ uri = uri.replace(`{${p}}`, "");
563
+ } else {
564
+ uri = uri.replace(`{${p}}`, encodeURIComponent(String(v)));
565
+ }
566
+ }
567
+ const base = baseURL.endsWith("/") ? baseURL.slice(0, -1) : baseURL;
568
+ return uri.startsWith("/") ? `${base}${uri}` : `${base}/${uri}`;
569
+ }
570
+ function findRouteMeta(name) {
571
+ for (const level of effectiveLevels) {
572
+ const entry = cache.get(level);
573
+ const meta = entry?.routes[name];
574
+ if (meta) {
575
+ return { ...meta, level };
576
+ }
577
+ }
578
+ return void 0;
579
+ }
580
+ async function api(name, params = {}) {
581
+ await autoDiscoveryPromise;
582
+ const meta = findRouteMeta(name);
583
+ if (!meta) {
584
+ const firstSeg = name.split(nameSeparator)[0];
585
+ if (firstSeg && effectiveLevels.includes(firstSeg)) {
586
+ await load(firstSeg);
587
+ }
588
+ const reMeta = findRouteMeta(name);
589
+ if (!reMeta) {
590
+ if (effectiveStrict) throw new UnknownRouteError(name);
591
+ return void 0;
592
+ }
593
+ return doApiCall(reMeta, params);
594
+ }
595
+ return doApiCall(meta, params);
596
+ }
597
+ async function doApiCall(meta, params) {
598
+ assertAuth(meta.level ?? "");
599
+ const { query, body, headers, ...pathParams } = params;
600
+ for (const p of meta.parameters) {
601
+ if (pathParams[p] === void 0) {
602
+ if (effectiveStrict) throw new MissingRouteParamError(meta.name, [p]);
603
+ }
604
+ }
605
+ const method = pickMethod(meta);
606
+ const urlWithQuery = appendQuery(buildRequestUrl(meta, pathParams), query);
607
+ const config = {
608
+ route: meta.name,
609
+ level: meta.level ?? "",
610
+ method,
611
+ url: urlWithQuery,
612
+ headers: { Accept: "application/json", ...headers ?? {} },
613
+ body,
614
+ params: pathParams,
615
+ timeout,
616
+ meta
617
+ };
618
+ const adp = await ensureAdapter();
619
+ const finalConfig = adp.runsInterceptors ? config : await runRequestInterceptors(requestInterceptors, config);
620
+ const source = adp.request(finalConfig).then(
621
+ (resp) => {
622
+ if (resp.status < 200 || resp.status >= 300) {
623
+ throw new HTTPError(
624
+ `HTTP ${resp.status} for route "${resp.route}" (${resp.method} ${resp.url})`,
625
+ {
626
+ route: resp.route,
627
+ level: resp.level,
628
+ status: resp.status,
629
+ url: resp.url,
630
+ method: resp.method
631
+ }
632
+ );
633
+ }
634
+ return resp;
635
+ },
636
+ (err) => {
637
+ if (err instanceof ForgeError) throw err;
638
+ throw new NetworkError(
639
+ err instanceof Error ? err.message : String(err),
640
+ meta.name,
641
+ meta.level,
642
+ err
643
+ );
644
+ }
645
+ );
646
+ if (adp.runsInterceptors) return source;
647
+ return runResponseInterceptors(responseInterceptors, source);
648
+ }
649
+ function invalidate(level) {
650
+ if (level) cache.del(level);
651
+ else cache.clear();
652
+ }
653
+ void autoDiscoveryPromise.then(() => {
654
+ if (effectiveEager.length > 0) {
655
+ void Promise.all(effectiveEager.map((lvl) => load(lvl))).catch((e) => {
656
+ console.warn(`[route-forge] eager load failed: ${e.message}`);
657
+ });
658
+ }
659
+ }).catch(() => {
660
+ });
661
+ return {
662
+ api,
663
+ load,
664
+ route,
665
+ invalidate,
666
+ interceptors: {
667
+ request: requestInterceptors,
668
+ response: responseInterceptors
669
+ }
670
+ };
671
+ }
672
+ function pickMethod(meta) {
673
+ const m = meta.methods.find((x) => x.toUpperCase() !== "HEAD");
674
+ return (m ?? meta.methods[0] ?? "GET").toUpperCase();
675
+ }
676
+ function appendQuery(url, query) {
677
+ if (!query) return url;
678
+ const usp = new URLSearchParams();
679
+ for (const [k, v] of Object.entries(query)) {
680
+ if (v === void 0 || v === null) continue;
681
+ usp.append(k, String(v));
682
+ }
683
+ const qs = usp.toString();
684
+ if (!qs) return url;
685
+ return url.includes("?") ? `${url}&${qs}` : `${url}?${qs}`;
686
+ }
687
+
688
+ export { AdapterNotFoundError, ForgeError, HTTPError, InsufficientAuthError, InterceptorManagerImpl, InvalidInterceptorReturnError, MissingRouteParamError, NetworkError, RouteCache, UnknownLevelError, UnknownRouteError, createInterceptorManager, createRouteForge };
689
+ //# sourceMappingURL=index.js.map
690
+ //# sourceMappingURL=index.js.map