@cedvict/http-guardian 0.0.1-next.0 → 0.0.1-next.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/CHANGELOG.md +23 -0
- package/LICENSE +21 -1
- package/README.md +223 -0
- package/dist/aliases.d.ts +35 -0
- package/dist/aliases.d.ts.map +1 -0
- package/dist/auth/authConfig.d.ts +91 -0
- package/dist/auth/authConfig.d.ts.map +1 -0
- package/dist/cache/inFlight.d.ts +10 -0
- package/dist/cache/inFlight.d.ts.map +1 -0
- package/dist/cache/key.d.ts +10 -0
- package/dist/cache/key.d.ts.map +1 -0
- package/dist/cache/memoryCache.d.ts +16 -0
- package/dist/cache/memoryCache.d.ts.map +1 -0
- package/dist/cache/types.d.ts +13 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/client/errors.d.ts +29 -0
- package/dist/client/errors.d.ts.map +1 -0
- package/dist/client/httpClient.d.ts +13 -0
- package/dist/client/httpClient.d.ts.map +1 -0
- package/dist/client/types.d.ts +84 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/guards/bearerRefreshGuard.d.ts +45 -0
- package/dist/guards/bearerRefreshGuard.d.ts.map +1 -0
- package/dist/guards/cookieSafeRefreshGuard.d.ts +40 -0
- package/dist/guards/cookieSafeRefreshGuard.d.ts.map +1 -0
- package/dist/guards/correlationIdGuard.d.ts +18 -0
- package/dist/guards/correlationIdGuard.d.ts.map +1 -0
- package/dist/guards/csrfGuard.d.ts +25 -0
- package/dist/guards/csrfGuard.d.ts.map +1 -0
- package/dist/guards/idempotencyGuard.d.ts +25 -0
- package/dist/guards/idempotencyGuard.d.ts.map +1 -0
- package/dist/guards/instrumentGuard.d.ts +12 -0
- package/dist/guards/instrumentGuard.d.ts.map +1 -0
- package/dist/guards/retryGuard.d.ts +12 -0
- package/dist/guards/retryGuard.d.ts.map +1 -0
- package/dist/guards/types.d.ts +3 -0
- package/dist/guards/types.d.ts.map +1 -0
- package/dist/guards/unauthorizedGuard.d.ts +19 -0
- package/dist/guards/unauthorizedGuard.d.ts.map +1 -0
- package/dist/index.cjs +1734 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +27 -266
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1077 -42
- package/dist/index.js.map +1 -1
- package/dist/internal/authGuard.d.ts +4 -0
- package/dist/internal/authGuard.d.ts.map +1 -0
- package/dist/internal/compose.d.ts +7 -0
- package/dist/internal/compose.d.ts.map +1 -0
- package/dist/internal/context.d.ts +57 -0
- package/dist/internal/context.d.ts.map +1 -0
- package/dist/internal/headersPolyfill.d.ts +25 -0
- package/dist/internal/headersPolyfill.d.ts.map +1 -0
- package/dist/internal/stableStringify.d.ts +2 -0
- package/dist/internal/stableStringify.d.ts.map +1 -0
- package/dist/internal/url.d.ts +3 -0
- package/dist/internal/url.d.ts.map +1 -0
- package/dist/notify/defaults.d.ts +9 -0
- package/dist/notify/defaults.d.ts.map +1 -0
- package/dist/notify/types.d.ts +33 -0
- package/dist/notify/types.d.ts.map +1 -0
- package/dist/parsing/apiParserStructured.d.ts +94 -0
- package/dist/parsing/apiParserStructured.d.ts.map +1 -0
- package/dist/parsing/presets.d.ts +15 -0
- package/dist/parsing/presets.d.ts.map +1 -0
- package/dist/parsing/schemaAdapter.d.ts +18 -0
- package/dist/parsing/schemaAdapter.d.ts.map +1 -0
- package/dist/parsing/shapeParser.d.ts +8 -0
- package/dist/parsing/shapeParser.d.ts.map +1 -0
- package/dist/plugins/types.d.ts +22 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/presets/presetBuilder.d.ts +181 -0
- package/dist/presets/presetBuilder.d.ts.map +1 -0
- package/dist/publicTypes.d.ts +12 -0
- package/dist/publicTypes.d.ts.map +1 -0
- package/package.json +10 -5
package/dist/index.js
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
// src/internal/compose.ts
|
|
2
2
|
function composeGuards(guards, terminal) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return dispatch(0, ctx);
|
|
13
|
-
};
|
|
3
|
+
if (!guards.length) return terminal;
|
|
4
|
+
return (ctx) => dispatch(0, ctx);
|
|
5
|
+
function dispatch(i, c) {
|
|
6
|
+
const guard = guards[i];
|
|
7
|
+
if (!guard) return terminal(c);
|
|
8
|
+
return Promise.resolve(
|
|
9
|
+
guard(c, (nextCtx) => dispatch(i + 1, nextCtx ?? c))
|
|
10
|
+
);
|
|
11
|
+
}
|
|
14
12
|
}
|
|
15
13
|
|
|
16
14
|
// src/internal/url.ts
|
|
@@ -36,6 +34,34 @@ function sanitizeHeaderValue(v) {
|
|
|
36
34
|
if (/[\r\n]/.test(v)) return null;
|
|
37
35
|
return v;
|
|
38
36
|
}
|
|
37
|
+
function base64Encode(input) {
|
|
38
|
+
if (typeof globalThis.btoa === "function") return globalThis.btoa(input);
|
|
39
|
+
const B = globalThis.Buffer;
|
|
40
|
+
if (typeof B?.from === "function") return B.from(input, "utf8").toString("base64");
|
|
41
|
+
throw new Error("No base64 encoder available in this runtime");
|
|
42
|
+
}
|
|
43
|
+
function isUnsafeMethod(m) {
|
|
44
|
+
const u = m.toUpperCase();
|
|
45
|
+
return u !== "GET" && u !== "HEAD" && u !== "OPTIONS";
|
|
46
|
+
}
|
|
47
|
+
function sameOrigin(url, baseOrigin) {
|
|
48
|
+
if (!baseOrigin) return true;
|
|
49
|
+
try {
|
|
50
|
+
return new URL(url).origin === baseOrigin;
|
|
51
|
+
} catch {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function isAllowedOrigin(url, baseOrigin, allowed) {
|
|
56
|
+
if (!allowed) return sameOrigin(url, baseOrigin);
|
|
57
|
+
if (typeof allowed === "function") return !!allowed(url);
|
|
58
|
+
try {
|
|
59
|
+
const origin = new URL(url).origin;
|
|
60
|
+
return allowed.includes(origin);
|
|
61
|
+
} catch {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
39
65
|
function authGuard(auth) {
|
|
40
66
|
return async (ctx, next) => {
|
|
41
67
|
if (ctx.noAuth) return next(ctx);
|
|
@@ -43,6 +69,9 @@ function authGuard(auth) {
|
|
|
43
69
|
if (auth.mode === "bearer") {
|
|
44
70
|
const token = await auth.getToken();
|
|
45
71
|
if (token) {
|
|
72
|
+
if (!isAllowedOrigin(ctx.url, ctx.baseOrigin, auth.allowedOrigins)) {
|
|
73
|
+
return next(ctx);
|
|
74
|
+
}
|
|
46
75
|
const headerName = auth.headerName ?? "Authorization";
|
|
47
76
|
const prefix = auth.prefix ?? "Bearer";
|
|
48
77
|
const safe = sanitizeHeaderValue(`${prefix} ${token}`);
|
|
@@ -50,8 +79,36 @@ function authGuard(auth) {
|
|
|
50
79
|
}
|
|
51
80
|
return next(ctx);
|
|
52
81
|
}
|
|
82
|
+
if (auth.mode === "basic") {
|
|
83
|
+
const cred = await auth.getCredentials();
|
|
84
|
+
if (cred) {
|
|
85
|
+
if (!isAllowedOrigin(ctx.url, ctx.baseOrigin, auth.allowedOrigins)) {
|
|
86
|
+
return next(ctx);
|
|
87
|
+
}
|
|
88
|
+
const headerName = auth.headerName ?? "Authorization";
|
|
89
|
+
const prefix = auth.prefix ?? "Basic";
|
|
90
|
+
const raw = typeof cred === "string" ? cred : `${cred.username}:${cred.password}`;
|
|
91
|
+
const safe = sanitizeHeaderValue(`${prefix} ${base64Encode(raw)}`);
|
|
92
|
+
if (safe) ctx.headers.set(headerName, safe);
|
|
93
|
+
}
|
|
94
|
+
return next(ctx);
|
|
95
|
+
}
|
|
96
|
+
if (auth.mode === "apiKey") {
|
|
97
|
+
const key = await auth.getKey();
|
|
98
|
+
if (key) {
|
|
99
|
+
if (!isAllowedOrigin(ctx.url, ctx.baseOrigin, auth.allowedOrigins)) {
|
|
100
|
+
return next(ctx);
|
|
101
|
+
}
|
|
102
|
+
const headerName = auth.headerName ?? "X-API-Key";
|
|
103
|
+
const prefix = auth.prefix ?? "";
|
|
104
|
+
const value = prefix ? `${prefix} ${key}` : key;
|
|
105
|
+
const safe = sanitizeHeaderValue(value);
|
|
106
|
+
if (safe) ctx.headers.set(headerName, safe);
|
|
107
|
+
}
|
|
108
|
+
return next(ctx);
|
|
109
|
+
}
|
|
53
110
|
ctx.credentials = auth.credentials ?? "include";
|
|
54
|
-
if (auth.csrf) {
|
|
111
|
+
if (auth.csrf && isUnsafeMethod(ctx.method)) {
|
|
55
112
|
const csrf = await auth.csrf.getToken();
|
|
56
113
|
if (csrf) ctx.headers.set(auth.csrf.headerName, csrf);
|
|
57
114
|
}
|
|
@@ -59,6 +116,84 @@ function authGuard(auth) {
|
|
|
59
116
|
};
|
|
60
117
|
}
|
|
61
118
|
|
|
119
|
+
// src/internal/headersPolyfill.ts
|
|
120
|
+
function norm(name) {
|
|
121
|
+
return String(name).toLowerCase();
|
|
122
|
+
}
|
|
123
|
+
var HeadersPolyfill = class {
|
|
124
|
+
map = /* @__PURE__ */ new Map();
|
|
125
|
+
constructor(init) {
|
|
126
|
+
if (!init) return;
|
|
127
|
+
if (typeof init.forEach === "function" && typeof init.get === "function") {
|
|
128
|
+
init.forEach((v, k) => this.set(k, v));
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (Symbol.iterator in Object(init)) {
|
|
132
|
+
for (const [k, v] of init) this.append(k, v);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
for (const [k, v] of Object.entries(init)) {
|
|
136
|
+
this.set(k, String(v));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
get(name) {
|
|
140
|
+
const hit = this.map.get(norm(name));
|
|
141
|
+
return hit ? hit.value : null;
|
|
142
|
+
}
|
|
143
|
+
has(name) {
|
|
144
|
+
return this.map.has(norm(name));
|
|
145
|
+
}
|
|
146
|
+
set(name, value) {
|
|
147
|
+
this.map.set(norm(name), { name, value: String(value) });
|
|
148
|
+
}
|
|
149
|
+
append(name, value) {
|
|
150
|
+
const key = norm(name);
|
|
151
|
+
const prev = this.map.get(key);
|
|
152
|
+
if (!prev) {
|
|
153
|
+
this.map.set(key, { name, value: String(value) });
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
this.map.set(key, { name: prev.name, value: `${prev.value}, ${String(value)}` });
|
|
157
|
+
}
|
|
158
|
+
delete(name) {
|
|
159
|
+
this.map.delete(norm(name));
|
|
160
|
+
}
|
|
161
|
+
forEach(cb) {
|
|
162
|
+
for (const { name, value } of this.map.values()) cb(value, name);
|
|
163
|
+
}
|
|
164
|
+
entries() {
|
|
165
|
+
const it = this.map.values();
|
|
166
|
+
return {
|
|
167
|
+
[Symbol.iterator]() {
|
|
168
|
+
return this;
|
|
169
|
+
},
|
|
170
|
+
next() {
|
|
171
|
+
const n = it.next();
|
|
172
|
+
if (n.done) return { done: true, value: void 0 };
|
|
173
|
+
return { done: false, value: [n.value.name, n.value.value] };
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/** Convert to HeadersInit that any fetch can consume. */
|
|
178
|
+
toObject() {
|
|
179
|
+
const out = {};
|
|
180
|
+
for (const { name, value } of this.map.values()) out[name] = value;
|
|
181
|
+
return out;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
function createHeaders(init) {
|
|
185
|
+
if (typeof globalThis.Headers === "function") {
|
|
186
|
+
return new globalThis.Headers(init);
|
|
187
|
+
}
|
|
188
|
+
return new HeadersPolyfill(init);
|
|
189
|
+
}
|
|
190
|
+
function toFetchHeaders(headers) {
|
|
191
|
+
if (typeof globalThis.Headers === "function" && headers instanceof globalThis.Headers) {
|
|
192
|
+
return headers;
|
|
193
|
+
}
|
|
194
|
+
return headers.toObject();
|
|
195
|
+
}
|
|
196
|
+
|
|
62
197
|
// src/internal/stableStringify.ts
|
|
63
198
|
function stableStringify(value) {
|
|
64
199
|
return JSON.stringify(sortValue(value));
|
|
@@ -164,37 +299,82 @@ var ParseError = class extends Error {
|
|
|
164
299
|
}
|
|
165
300
|
name = "ParseError";
|
|
166
301
|
};
|
|
302
|
+
var ILLEGAL_INVOCATION_HINT = `Detected a browser fetch invocation error ("Illegal invocation"). This usually happens when passing a detached reference to window.fetch. Fix by configuring the client with fetch: (input, init) => window.fetch(input, init) or fetch: window.fetch.bind(window).`;
|
|
167
303
|
|
|
168
304
|
// src/client/httpClient.ts
|
|
305
|
+
function isIllegalInvocation(err) {
|
|
306
|
+
const msg = String(err?.message ?? err);
|
|
307
|
+
return /Illegal invocation/i.test(msg);
|
|
308
|
+
}
|
|
309
|
+
function normalizeFetch(f) {
|
|
310
|
+
const bound = f.bind ? f.bind(globalThis) : f;
|
|
311
|
+
return ((input, init) => bound(input, init));
|
|
312
|
+
}
|
|
313
|
+
function getDefaultFetch() {
|
|
314
|
+
if (typeof globalThis.fetch === "function") return globalThis.fetch;
|
|
315
|
+
throw new Error(
|
|
316
|
+
"No global fetch detected. Provide a fetch implementation via createHttpClient({ fetch }) or run in an environment that supports fetch (Node >= 18 / modern browsers)."
|
|
317
|
+
);
|
|
318
|
+
}
|
|
169
319
|
var REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
|
|
170
|
-
function createHttpClient(
|
|
171
|
-
const
|
|
320
|
+
function createHttpClient(options) {
|
|
321
|
+
const draft = { ...options };
|
|
322
|
+
if (draft.plugins?.length) {
|
|
323
|
+
const seen = /* @__PURE__ */ new Set();
|
|
324
|
+
for (const p of draft.plugins) {
|
|
325
|
+
if (!p || !p.name || typeof p.apply !== "function") continue;
|
|
326
|
+
if (seen.has(p.name)) continue;
|
|
327
|
+
seen.add(p.name);
|
|
328
|
+
p.apply(draft);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
const resolvedFetch = (() => {
|
|
332
|
+
if (draft.fetch) return normalizeFetch(draft.fetch);
|
|
333
|
+
let lastRef = null;
|
|
334
|
+
let lastBound = null;
|
|
335
|
+
return ((input, init) => {
|
|
336
|
+
const cur = getDefaultFetch();
|
|
337
|
+
if (cur !== lastRef || !lastBound) {
|
|
338
|
+
lastRef = cur;
|
|
339
|
+
lastBound = normalizeFetch(cur);
|
|
340
|
+
}
|
|
341
|
+
return lastBound(input, init);
|
|
342
|
+
});
|
|
343
|
+
})();
|
|
344
|
+
const cache = draft.cache;
|
|
172
345
|
const inFlight = new InFlight();
|
|
173
|
-
const defaultNotifier =
|
|
174
|
-
const shouldNotify =
|
|
175
|
-
const baseGuards = [authGuard(
|
|
346
|
+
const defaultNotifier = draft.notifier ?? consoleNotifier;
|
|
347
|
+
const shouldNotify = draft.defaults?.shouldNotify ?? ((r) => !r.ok);
|
|
348
|
+
const baseGuards = [authGuard(draft.auth), ...draft.guards ?? []];
|
|
176
349
|
async function terminal(ctx) {
|
|
177
350
|
const controller = ctx.timeoutMs ? new AbortController() : void 0;
|
|
178
351
|
const timeout = ctx.timeoutMs ? setTimeout(() => controller?.abort(new TimeoutError()), ctx.timeoutMs) : void 0;
|
|
179
352
|
try {
|
|
180
353
|
const init = {
|
|
181
354
|
method: ctx.method,
|
|
182
|
-
headers: ctx.headers,
|
|
355
|
+
headers: toFetchHeaders(ctx.headers),
|
|
183
356
|
...ctx.body !== void 0 ? { body: ctx.body } : {},
|
|
184
357
|
...ctx.signal ? { signal: controller ? anySignal([ctx.signal, controller.signal]) : ctx.signal } : controller ? { signal: controller.signal } : {},
|
|
185
358
|
...ctx.credentials !== void 0 ? { credentials: ctx.credentials } : {},
|
|
186
359
|
...ctx.redirect !== void 0 ? { redirect: ctx.redirect } : {}
|
|
187
360
|
};
|
|
188
|
-
|
|
361
|
+
try {
|
|
362
|
+
return await ctx._fetch(ctx.url, init);
|
|
363
|
+
} catch (err) {
|
|
364
|
+
if (isIllegalInvocation(err) && typeof globalThis.fetch === "function") {
|
|
365
|
+
return await globalThis.fetch(ctx.url, init);
|
|
366
|
+
}
|
|
367
|
+
throw err;
|
|
368
|
+
}
|
|
189
369
|
} finally {
|
|
190
370
|
if (timeout) clearTimeout(timeout);
|
|
191
371
|
}
|
|
192
372
|
}
|
|
193
373
|
const pipeline = composeGuards(baseGuards, terminal);
|
|
194
374
|
async function request(method, path, ro) {
|
|
195
|
-
const requestUrl = withQuery(joinUrl(
|
|
196
|
-
const headers =
|
|
197
|
-
if (
|
|
375
|
+
const requestUrl = withQuery(joinUrl(draft.baseUrl, path), ro?.query);
|
|
376
|
+
const headers = createHeaders();
|
|
377
|
+
if (draft.defaults?.headers) for (const [k, v] of Object.entries(draft.defaults.headers)) headers.set(k, v);
|
|
198
378
|
if (ro?.headers) {
|
|
199
379
|
for (const [k, v] of Object.entries(ro.headers)) if (v !== void 0) headers.set(k, v);
|
|
200
380
|
}
|
|
@@ -212,19 +392,31 @@ function createHttpClient(opts) {
|
|
|
212
392
|
} else {
|
|
213
393
|
if (!headers.has("accept")) headers.set("accept", "application/json");
|
|
214
394
|
}
|
|
215
|
-
const timeoutMs = ro?.timeoutMs ??
|
|
395
|
+
const timeoutMs = ro?.timeoutMs ?? draft.defaults?.timeoutMs;
|
|
216
396
|
const notifier = ro?.notifier ?? defaultNotifier;
|
|
397
|
+
const emit = (event) => {
|
|
398
|
+
const redact = notifier.redact;
|
|
399
|
+
const n = notifier.notify?.bind(notifier) ?? defaultNotifier.notify.bind(defaultNotifier);
|
|
400
|
+
return n(redact ? redact(event) : event);
|
|
401
|
+
};
|
|
217
402
|
const cacheOpts = ro?.cache;
|
|
218
403
|
const wantsCache = method === "GET" && !!cache && !!cacheOpts;
|
|
219
404
|
const dedupe = cacheOpts?.dedupe ?? true;
|
|
220
405
|
const canDedupeWrite = method !== "GET" && !!ro?.idempotencyKey;
|
|
221
|
-
const inflightKey = (wantsCache || canDedupeWrite) && dedupe ? makeCacheKey({
|
|
406
|
+
const inflightKey = (wantsCache || canDedupeWrite) && dedupe ? makeCacheKey({
|
|
407
|
+
method,
|
|
408
|
+
url: requestUrl,
|
|
409
|
+
headers,
|
|
410
|
+
...method === "GET" ? {} : { body: bodyForKey },
|
|
411
|
+
...ro?.idempotencyKey !== void 0 ? { idempotencyKey: ro.idempotencyKey } : {},
|
|
412
|
+
...cacheOpts?.key !== void 0 ? { keyOverride: cacheOpts.key } : {}
|
|
413
|
+
}) : void 0;
|
|
222
414
|
if (inflightKey) {
|
|
223
415
|
const existing = inFlight.get(inflightKey);
|
|
224
416
|
if (existing) {
|
|
225
417
|
try {
|
|
226
418
|
const stored = await existing;
|
|
227
|
-
return asClientResult(stored, requestUrl,
|
|
419
|
+
return asClientResult(stored, requestUrl, draft, ro, notifier, shouldNotify, emit);
|
|
228
420
|
} catch {
|
|
229
421
|
}
|
|
230
422
|
}
|
|
@@ -246,7 +438,8 @@ function createHttpClient(opts) {
|
|
|
246
438
|
}
|
|
247
439
|
}
|
|
248
440
|
const res = await fetchWithRedirects({
|
|
249
|
-
opts,
|
|
441
|
+
opts: draft,
|
|
442
|
+
resolvedFetch,
|
|
250
443
|
pipeline,
|
|
251
444
|
method,
|
|
252
445
|
url: requestUrl,
|
|
@@ -260,13 +453,18 @@ function createHttpClient(opts) {
|
|
|
260
453
|
const envelope = { kind: "network", url: res.url || requestUrl, status: res.status, headers: res.headers, raw };
|
|
261
454
|
if (wantsCache && cacheOpts && res.ok) {
|
|
262
455
|
const key = inflightKey;
|
|
263
|
-
cache.set(key, {
|
|
456
|
+
cache.set(key, {
|
|
457
|
+
expiresAt: Date.now() + cacheOpts.ttlMs,
|
|
458
|
+
value: raw,
|
|
459
|
+
...cacheOpts.tags ? { tags: cacheOpts.tags } : {}
|
|
460
|
+
});
|
|
264
461
|
}
|
|
265
462
|
return envelope;
|
|
266
463
|
async function refreshInBackground(key) {
|
|
267
464
|
try {
|
|
268
465
|
const res2 = await fetchWithRedirects({
|
|
269
|
-
opts,
|
|
466
|
+
opts: draft,
|
|
467
|
+
resolvedFetch,
|
|
270
468
|
pipeline,
|
|
271
469
|
method,
|
|
272
470
|
url: requestUrl,
|
|
@@ -278,7 +476,11 @@ function createHttpClient(opts) {
|
|
|
278
476
|
});
|
|
279
477
|
if (!res2.ok) return;
|
|
280
478
|
const raw2 = await decodeResponse(res2);
|
|
281
|
-
cache.set(key, {
|
|
479
|
+
cache.set(key, {
|
|
480
|
+
expiresAt: Date.now() + cacheOpts.ttlMs,
|
|
481
|
+
value: raw2,
|
|
482
|
+
...cacheOpts.tags ? { tags: cacheOpts.tags } : {}
|
|
483
|
+
});
|
|
282
484
|
} catch {
|
|
283
485
|
}
|
|
284
486
|
}
|
|
@@ -286,10 +488,13 @@ function createHttpClient(opts) {
|
|
|
286
488
|
if (inflightKey) inFlight.set(inflightKey, promise);
|
|
287
489
|
try {
|
|
288
490
|
const stored = await promise;
|
|
289
|
-
return asClientResult(stored, requestUrl,
|
|
491
|
+
return asClientResult(stored, requestUrl, draft, ro, notifier, shouldNotify, emit);
|
|
290
492
|
} catch (e) {
|
|
291
|
-
const err = e instanceof TimeoutError ? e : new NetworkError(
|
|
292
|
-
|
|
493
|
+
const err = e instanceof TimeoutError ? e : new NetworkError(
|
|
494
|
+
isIllegalInvocation(e) ? `Network request failed. ${ILLEGAL_INVOCATION_HINT}` : "Network request failed",
|
|
495
|
+
e
|
|
496
|
+
);
|
|
497
|
+
if (shouldNotify({ ok: false, status: 0 })) emit({ type: "network-error", level: "error", title: "Network error", message: err.message, url: requestUrl });
|
|
293
498
|
return { ok: false, status: 0, headers: new Headers(), errors: [{ message: err.message, details: e }], raw: null, url: requestUrl };
|
|
294
499
|
}
|
|
295
500
|
}
|
|
@@ -321,7 +526,19 @@ async function fetchWithRedirects(args) {
|
|
|
321
526
|
);
|
|
322
527
|
if (!REDIRECT_STATUSES.has(res.status)) return res;
|
|
323
528
|
const loc = res.headers.get("location");
|
|
324
|
-
if (!loc)
|
|
529
|
+
if (!loc) {
|
|
530
|
+
return args.pipeline(
|
|
531
|
+
makeCtx(
|
|
532
|
+
{
|
|
533
|
+
...args,
|
|
534
|
+
url: currentUrl,
|
|
535
|
+
method,
|
|
536
|
+
...body !== void 0 ? { body } : {}
|
|
537
|
+
},
|
|
538
|
+
"follow"
|
|
539
|
+
)
|
|
540
|
+
);
|
|
541
|
+
}
|
|
325
542
|
const nextUrl = new URL(loc, currentUrl).toString();
|
|
326
543
|
const decision = policy.onRedirect?.({ fromUrl: currentUrl, toUrl: nextUrl, status: res.status, hop, method });
|
|
327
544
|
if (decision?.action === "deny") throw new RedirectError(decision.reason ?? "Redirect denied by policy", currentUrl, nextUrl);
|
|
@@ -341,18 +558,38 @@ async function fetchWithRedirects(args) {
|
|
|
341
558
|
}
|
|
342
559
|
function stripAuthOnCrossOriginRedirect(opts, initialUrl, toUrl, headers) {
|
|
343
560
|
try {
|
|
344
|
-
if (opts.auth.mode !== "bearer") return;
|
|
345
561
|
const initialOrigin = new URL(initialUrl).origin;
|
|
346
562
|
const toOrigin = new URL(toUrl).origin;
|
|
347
|
-
if (initialOrigin
|
|
563
|
+
if (initialOrigin === toOrigin) return;
|
|
564
|
+
if (opts.auth.mode === "bearer") {
|
|
565
|
+
const headerName = opts.auth.headerName ?? "Authorization";
|
|
566
|
+
headers.delete(headerName);
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
if (opts.auth.mode === "basic") {
|
|
348
570
|
const headerName = opts.auth.headerName ?? "Authorization";
|
|
349
571
|
headers.delete(headerName);
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
if (opts.auth.mode === "apiKey") {
|
|
575
|
+
const headerName = opts.auth.headerName ?? "X-API-Key";
|
|
576
|
+
headers.delete(headerName);
|
|
577
|
+
return;
|
|
350
578
|
}
|
|
351
579
|
} catch {
|
|
352
580
|
}
|
|
353
581
|
}
|
|
354
582
|
function makeCtx(args, redirect) {
|
|
583
|
+
const baseOrigin = (() => {
|
|
584
|
+
try {
|
|
585
|
+
return new URL(args.opts.baseUrl).origin;
|
|
586
|
+
} catch {
|
|
587
|
+
return "";
|
|
588
|
+
}
|
|
589
|
+
})();
|
|
355
590
|
return {
|
|
591
|
+
baseUrl: args.opts.baseUrl,
|
|
592
|
+
baseOrigin,
|
|
356
593
|
method: args.method,
|
|
357
594
|
url: args.url,
|
|
358
595
|
headers: args.headers,
|
|
@@ -363,10 +600,11 @@ function makeCtx(args, redirect) {
|
|
|
363
600
|
attempt: 0,
|
|
364
601
|
auth: args.opts.auth,
|
|
365
602
|
redirect,
|
|
366
|
-
_fetch: args.
|
|
603
|
+
_fetch: args.resolvedFetch
|
|
367
604
|
};
|
|
368
605
|
}
|
|
369
606
|
async function decodeResponse(res) {
|
|
607
|
+
if (res.status === 204) return null;
|
|
370
608
|
const ct = res.headers.get("content-type") ?? "";
|
|
371
609
|
if (ct.includes("application/json")) {
|
|
372
610
|
try {
|
|
@@ -381,10 +619,10 @@ async function decodeResponse(res) {
|
|
|
381
619
|
throw new ParseError("Failed to read response body", void 0, e);
|
|
382
620
|
}
|
|
383
621
|
}
|
|
384
|
-
function asClientResult(envelope, requestUrl, opts, ro, notifier, shouldNotify) {
|
|
622
|
+
function asClientResult(envelope, requestUrl, opts, ro, notifier, shouldNotify, emit) {
|
|
385
623
|
if (envelope?.kind === "cache-miss") {
|
|
386
624
|
const errors2 = [{ message: "Cache miss" }];
|
|
387
|
-
if (shouldNotify({ ok: false, status: 0 }))
|
|
625
|
+
if (shouldNotify({ ok: false, status: 0 })) emit({ type: "http-error", level: "warning", title: "Cache", message: "Cache miss", status: 0, errors: errors2, url: requestUrl });
|
|
388
626
|
return { ok: false, status: 0, headers: new Headers(), errors: errors2, raw: envelope.raw, url: requestUrl };
|
|
389
627
|
}
|
|
390
628
|
const status = envelope.status ?? 0;
|
|
@@ -399,17 +637,24 @@ function asClientResult(envelope, requestUrl, opts, ro, notifier, shouldNotify)
|
|
|
399
637
|
return { ok: true, status, headers: resHeaders, data, raw, url: finalUrl };
|
|
400
638
|
} catch (e) {
|
|
401
639
|
const msg2 = e instanceof Error ? e.message : "Schema parse failed";
|
|
402
|
-
if (shouldNotify({ ok: false, status }))
|
|
640
|
+
if (shouldNotify({ ok: false, status })) emit({ type: "parse-error", level: "error", title: "Parse error", message: msg2, url: finalUrl });
|
|
403
641
|
return { ok: false, status, headers: resHeaders, errors: [{ message: msg2, details: e }], raw, url: finalUrl };
|
|
404
642
|
}
|
|
405
643
|
}
|
|
406
644
|
const errors = opts.parser.getErrors(raw, status);
|
|
407
645
|
const msg = errors[0]?.message ?? (status ? `HTTP ${status}` : "Request failed");
|
|
408
|
-
if (shouldNotify({ ok: false, status }))
|
|
646
|
+
if (shouldNotify({ ok: false, status })) emit({ type: "http-error", level: status >= 500 ? "error" : "warning", title: "Request failed", message: msg, status, errors, url: finalUrl });
|
|
409
647
|
return { ok: false, status, headers: resHeaders, errors, raw, url: finalUrl };
|
|
410
648
|
}
|
|
411
649
|
function anySignal(signals) {
|
|
412
650
|
const valid = signals.filter(Boolean);
|
|
651
|
+
const any = AbortSignal.any;
|
|
652
|
+
if (any) {
|
|
653
|
+
try {
|
|
654
|
+
return any(valid);
|
|
655
|
+
} catch {
|
|
656
|
+
}
|
|
657
|
+
}
|
|
413
658
|
const controller = new AbortController();
|
|
414
659
|
const onAbort = () => controller.abort();
|
|
415
660
|
for (const s of valid) {
|
|
@@ -425,7 +670,8 @@ function bearerAuth(getToken, opts) {
|
|
|
425
670
|
mode: "bearer",
|
|
426
671
|
getToken,
|
|
427
672
|
...opts?.headerName !== void 0 ? { headerName: opts.headerName } : {},
|
|
428
|
-
...opts?.prefix !== void 0 ? { prefix: opts.prefix } : {}
|
|
673
|
+
...opts?.prefix !== void 0 ? { prefix: opts.prefix } : {},
|
|
674
|
+
...opts?.allowedOrigins !== void 0 ? { allowedOrigins: opts.allowedOrigins } : {}
|
|
429
675
|
};
|
|
430
676
|
}
|
|
431
677
|
function cookieAuth(opts) {
|
|
@@ -435,6 +681,24 @@ function cookieAuth(opts) {
|
|
|
435
681
|
...opts?.csrf !== void 0 ? { csrf: opts.csrf } : {}
|
|
436
682
|
};
|
|
437
683
|
}
|
|
684
|
+
function basicAuth(getCredentials, opts) {
|
|
685
|
+
return {
|
|
686
|
+
mode: "basic",
|
|
687
|
+
getCredentials,
|
|
688
|
+
...opts?.headerName !== void 0 ? { headerName: opts.headerName } : {},
|
|
689
|
+
...opts?.prefix !== void 0 ? { prefix: opts.prefix } : {},
|
|
690
|
+
...opts?.allowedOrigins !== void 0 ? { allowedOrigins: opts.allowedOrigins } : {}
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
function apiKeyAuth(getKey, opts) {
|
|
694
|
+
return {
|
|
695
|
+
mode: "apiKey",
|
|
696
|
+
getKey,
|
|
697
|
+
...opts?.headerName !== void 0 ? { headerName: opts.headerName } : {},
|
|
698
|
+
...opts?.prefix !== void 0 ? { prefix: opts.prefix } : {},
|
|
699
|
+
...opts?.allowedOrigins !== void 0 ? { allowedOrigins: opts.allowedOrigins } : {}
|
|
700
|
+
};
|
|
701
|
+
}
|
|
438
702
|
function noAuth() {
|
|
439
703
|
return { mode: "none" };
|
|
440
704
|
}
|
|
@@ -459,6 +723,515 @@ function retryGuard(opts) {
|
|
|
459
723
|
};
|
|
460
724
|
}
|
|
461
725
|
|
|
726
|
+
// src/guards/cookieSafeRefreshGuard.ts
|
|
727
|
+
var REFRESH_FLAG = "__hg_is_refresh__";
|
|
728
|
+
var RETRY_FLAG = "__hg_refresh_retry__";
|
|
729
|
+
function isReplayableBody(body) {
|
|
730
|
+
if (body == null) return true;
|
|
731
|
+
if (typeof body === "string") return true;
|
|
732
|
+
if (typeof Blob !== "undefined" && body instanceof Blob) return true;
|
|
733
|
+
if (typeof FormData !== "undefined" && body instanceof FormData) return true;
|
|
734
|
+
if (typeof URLSearchParams !== "undefined" && body instanceof URLSearchParams) return true;
|
|
735
|
+
if (typeof ArrayBuffer !== "undefined" && body instanceof ArrayBuffer) return true;
|
|
736
|
+
if (typeof Uint8Array !== "undefined" && body instanceof Uint8Array) return true;
|
|
737
|
+
return false;
|
|
738
|
+
}
|
|
739
|
+
function normalizePath(url) {
|
|
740
|
+
try {
|
|
741
|
+
return new URL(url).pathname;
|
|
742
|
+
} catch {
|
|
743
|
+
const q = url.indexOf("?");
|
|
744
|
+
const h = url.indexOf("#");
|
|
745
|
+
const cut = Math.min(q === -1 ? url.length : q, h === -1 ? url.length : h);
|
|
746
|
+
return url.slice(0, cut);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
function parseRetryAfterMs(headers) {
|
|
750
|
+
const ra = headers.get("retry-after");
|
|
751
|
+
if (!ra) return null;
|
|
752
|
+
const seconds = Number(ra);
|
|
753
|
+
if (!Number.isNaN(seconds) && seconds >= 0) return seconds * 1e3;
|
|
754
|
+
const date = Date.parse(ra);
|
|
755
|
+
if (!Number.isNaN(date)) return Math.max(0, date - Date.now());
|
|
756
|
+
return null;
|
|
757
|
+
}
|
|
758
|
+
function isAbsoluteUrl(u) {
|
|
759
|
+
try {
|
|
760
|
+
void new URL(u);
|
|
761
|
+
return true;
|
|
762
|
+
} catch {
|
|
763
|
+
return false;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
function cookieSafeRefreshGuard(opts) {
|
|
767
|
+
const trigger = new Set(opts.triggerStatuses ?? [401, 419]);
|
|
768
|
+
const maxRetry = opts.maxRetry ?? 1;
|
|
769
|
+
const honorRetryAfter = opts.honorRetryAfter ?? true;
|
|
770
|
+
let refreshPromise = null;
|
|
771
|
+
let refreshCooldownUntil = 0;
|
|
772
|
+
return async (ctx, next) => {
|
|
773
|
+
const anyCtx = ctx;
|
|
774
|
+
if (anyCtx[REFRESH_FLAG]) return next(ctx);
|
|
775
|
+
const path = normalizePath(ctx.url);
|
|
776
|
+
if (opts.excludePaths?.includes(path)) return next(ctx);
|
|
777
|
+
if (ctx.noAuth) return next(ctx);
|
|
778
|
+
const refreshPathNorm = normalizePath(resolveRefreshUrl(ctx, opts.refreshPath));
|
|
779
|
+
if (path === refreshPathNorm) return next(ctx);
|
|
780
|
+
if (honorRetryAfter && refreshCooldownUntil > Date.now()) {
|
|
781
|
+
return next(ctx);
|
|
782
|
+
}
|
|
783
|
+
const res = await next(ctx);
|
|
784
|
+
if (!trigger.has(res.status)) return res;
|
|
785
|
+
if (opts.shouldRefresh && !opts.shouldRefresh(ctx, res.status)) return res;
|
|
786
|
+
const alreadyRetried = Number(anyCtx[RETRY_FLAG] ?? 0);
|
|
787
|
+
if (alreadyRetried >= maxRetry) return res;
|
|
788
|
+
if (!isReplayableBody(ctx.body)) return res;
|
|
789
|
+
const decision = await (refreshPromise ??= doRefresh(ctx, next).finally(() => {
|
|
790
|
+
refreshPromise = null;
|
|
791
|
+
}));
|
|
792
|
+
if (!decision.ok) {
|
|
793
|
+
opts.onRefreshFailed?.({
|
|
794
|
+
url: ctx.url,
|
|
795
|
+
...decision.reason !== void 0 ? { reason: decision.reason } : {},
|
|
796
|
+
...decision.status !== void 0 ? { status: decision.status } : {}
|
|
797
|
+
});
|
|
798
|
+
return res;
|
|
799
|
+
}
|
|
800
|
+
opts.onRefreshSuccess?.({ url: ctx.url });
|
|
801
|
+
const retryCtx = Object.assign({}, ctx, { attempt: ctx.attempt + 1 });
|
|
802
|
+
retryCtx[RETRY_FLAG] = alreadyRetried + 1;
|
|
803
|
+
await applyCookieAuth(retryCtx);
|
|
804
|
+
return next(retryCtx);
|
|
805
|
+
};
|
|
806
|
+
async function doRefresh(originalCtx, next) {
|
|
807
|
+
if (honorRetryAfter && refreshCooldownUntil > Date.now()) {
|
|
808
|
+
return { ok: false, reason: "Refresh cooldown active" };
|
|
809
|
+
}
|
|
810
|
+
const refreshUrl = resolveRefreshUrl(originalCtx, opts.refreshPath);
|
|
811
|
+
const refreshCtx = {
|
|
812
|
+
...originalCtx,
|
|
813
|
+
url: refreshUrl,
|
|
814
|
+
method: opts.refreshMethod ?? "POST",
|
|
815
|
+
// Un refresh n’a en général pas besoin du body original
|
|
816
|
+
...Object.prototype.hasOwnProperty.call(originalCtx, "body") ? { body: void 0 } : {},
|
|
817
|
+
attempt: 0
|
|
818
|
+
};
|
|
819
|
+
refreshCtx[REFRESH_FLAG] = true;
|
|
820
|
+
refreshCtx[RETRY_FLAG] = 0;
|
|
821
|
+
await applyCookieAuth(refreshCtx);
|
|
822
|
+
try {
|
|
823
|
+
const res = await next(refreshCtx);
|
|
824
|
+
if (res.status === 429 && honorRetryAfter) {
|
|
825
|
+
const ms = parseRetryAfterMs(res.headers);
|
|
826
|
+
refreshCooldownUntil = Date.now() + (ms ?? 3e4);
|
|
827
|
+
return { ok: false, reason: "Refresh rate-limited (429)", status: 429 };
|
|
828
|
+
}
|
|
829
|
+
if (!res.ok) {
|
|
830
|
+
return { ok: false, reason: `Refresh failed (HTTP ${res.status})`, status: res.status };
|
|
831
|
+
}
|
|
832
|
+
return { ok: true };
|
|
833
|
+
} catch (e) {
|
|
834
|
+
return { ok: false, reason: e instanceof Error ? e.message : "Refresh exception" };
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
function resolveRefreshUrl(ctx, refreshPath) {
|
|
838
|
+
if (isAbsoluteUrl(refreshPath)) return refreshPath;
|
|
839
|
+
return joinUrl(ctx.baseUrl, refreshPath);
|
|
840
|
+
}
|
|
841
|
+
async function applyCookieAuth(ctx) {
|
|
842
|
+
if (ctx.noAuth) return;
|
|
843
|
+
const auth = ctx.auth;
|
|
844
|
+
if (!auth || auth.mode !== "cookie") return;
|
|
845
|
+
if (ctx.credentials === void 0) {
|
|
846
|
+
ctx.credentials = auth.credentials ?? "include";
|
|
847
|
+
}
|
|
848
|
+
if (auth.csrf) {
|
|
849
|
+
const hn = auth.csrf.headerName;
|
|
850
|
+
if (!ctx.headers.has(hn)) {
|
|
851
|
+
const t = await auth.csrf.getToken();
|
|
852
|
+
if (t != null && String(t) !== "") ctx.headers.set(hn, String(t));
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// src/guards/bearerRefreshGuard.ts
|
|
859
|
+
var REFRESH_FLAG2 = "__hg_bearer_refresh_in_flight__";
|
|
860
|
+
var RETRY_FLAG2 = "__hg_bearer_refresh_retry__";
|
|
861
|
+
function isReplayableBody2(body) {
|
|
862
|
+
if (body == null) return true;
|
|
863
|
+
if (typeof body === "string") return true;
|
|
864
|
+
if (typeof Blob !== "undefined" && body instanceof Blob) return true;
|
|
865
|
+
if (typeof FormData !== "undefined" && body instanceof FormData) return true;
|
|
866
|
+
if (typeof URLSearchParams !== "undefined" && body instanceof URLSearchParams) return true;
|
|
867
|
+
if (typeof ArrayBuffer !== "undefined" && body instanceof ArrayBuffer) return true;
|
|
868
|
+
if (typeof Uint8Array !== "undefined" && body instanceof Uint8Array) return true;
|
|
869
|
+
return false;
|
|
870
|
+
}
|
|
871
|
+
function bearerRefreshGuard(opts) {
|
|
872
|
+
const trigger = new Set(opts.triggerStatuses ?? [401]);
|
|
873
|
+
const queueTimeoutMs = opts.queueTimeoutMs ?? 3e4;
|
|
874
|
+
let refreshPromise = null;
|
|
875
|
+
return async (ctx, next) => {
|
|
876
|
+
const anyCtx = ctx;
|
|
877
|
+
if (ctx.noAuth) return next(ctx);
|
|
878
|
+
if (ctx.auth.mode !== "bearer") return next(ctx);
|
|
879
|
+
if (opts.skipRefreshFor?.(ctx)) return next(ctx);
|
|
880
|
+
if (anyCtx[RETRY_FLAG2]) return next(ctx);
|
|
881
|
+
if (anyCtx[REFRESH_FLAG2]) return next(ctx);
|
|
882
|
+
const res = await next(ctx);
|
|
883
|
+
if (!trigger.has(res.status)) return res;
|
|
884
|
+
if (!isReplayableBody2(ctx.body)) return res;
|
|
885
|
+
try {
|
|
886
|
+
await withTimeout(
|
|
887
|
+
refreshPromise ??= runRefresh(opts).finally(() => {
|
|
888
|
+
refreshPromise = null;
|
|
889
|
+
}),
|
|
890
|
+
queueTimeoutMs
|
|
891
|
+
);
|
|
892
|
+
} catch {
|
|
893
|
+
return res;
|
|
894
|
+
}
|
|
895
|
+
const retryCtx = cloneCtxForReplay(ctx);
|
|
896
|
+
retryCtx[RETRY_FLAG2] = true;
|
|
897
|
+
await applyBearerAuth(retryCtx, ctx.auth);
|
|
898
|
+
return next(retryCtx);
|
|
899
|
+
};
|
|
900
|
+
async function runRefresh(o) {
|
|
901
|
+
try {
|
|
902
|
+
const tokens = await o.refreshFn();
|
|
903
|
+
if (o.onTokenRefreshed) await o.onTokenRefreshed(tokens);
|
|
904
|
+
return tokens;
|
|
905
|
+
} catch (e) {
|
|
906
|
+
if (o.onAuthFailed) {
|
|
907
|
+
try {
|
|
908
|
+
await o.onAuthFailed(e);
|
|
909
|
+
} catch {
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
throw e;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
function cloneCtxForReplay(ctx) {
|
|
917
|
+
return Object.assign({}, ctx, { attempt: ctx.attempt + 1 });
|
|
918
|
+
}
|
|
919
|
+
async function applyBearerAuth(ctx, auth) {
|
|
920
|
+
const headerName = auth.headerName ?? "Authorization";
|
|
921
|
+
const prefix = auth.prefix ?? "Bearer";
|
|
922
|
+
const token = await auth.getToken();
|
|
923
|
+
if (token == null || token === "") {
|
|
924
|
+
ctx.headers.delete(headerName);
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
ctx.headers.set(headerName, prefix ? `${prefix} ${token}` : String(token));
|
|
928
|
+
}
|
|
929
|
+
function withTimeout(p, ms) {
|
|
930
|
+
if (!Number.isFinite(ms) || ms <= 0) return p;
|
|
931
|
+
return new Promise((resolve, reject) => {
|
|
932
|
+
const t = setTimeout(() => reject(new Error(`bearerRefreshGuard: queue timeout after ${ms}ms`)), ms);
|
|
933
|
+
p.then(
|
|
934
|
+
(v) => {
|
|
935
|
+
clearTimeout(t);
|
|
936
|
+
resolve(v);
|
|
937
|
+
},
|
|
938
|
+
(e) => {
|
|
939
|
+
clearTimeout(t);
|
|
940
|
+
reject(e);
|
|
941
|
+
}
|
|
942
|
+
);
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// src/guards/csrfGuard.ts
|
|
947
|
+
var DEFAULT_METHODS = ["POST", "PUT", "PATCH", "DELETE"];
|
|
948
|
+
function readCookie(name) {
|
|
949
|
+
if (typeof document === "undefined") return null;
|
|
950
|
+
const cookieStr = document.cookie;
|
|
951
|
+
if (typeof cookieStr !== "string" || cookieStr.length === 0) return null;
|
|
952
|
+
const target = encodeURIComponent(name);
|
|
953
|
+
const parts = cookieStr.split(";");
|
|
954
|
+
for (const part of parts) {
|
|
955
|
+
const eq = part.indexOf("=");
|
|
956
|
+
if (eq === -1) continue;
|
|
957
|
+
const k = part.slice(0, eq).trim();
|
|
958
|
+
if (k === name || k === target) {
|
|
959
|
+
return decodeURIComponent(part.slice(eq + 1).trim());
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
return null;
|
|
963
|
+
}
|
|
964
|
+
function csrfGuard(opts = {}) {
|
|
965
|
+
const headerName = opts.headerName ?? "X-CSRF-Token";
|
|
966
|
+
const methods = new Set((opts.applyTo ?? DEFAULT_METHODS).map((m) => m.toUpperCase()));
|
|
967
|
+
let cachedHeaderToken = null;
|
|
968
|
+
return async (ctx, next) => {
|
|
969
|
+
if (!methods.has(ctx.method)) return next(ctx);
|
|
970
|
+
if (ctx.headers.has(headerName)) {
|
|
971
|
+
const res2 = await next(ctx);
|
|
972
|
+
maybeReadResponseHeader(res2);
|
|
973
|
+
return res2;
|
|
974
|
+
}
|
|
975
|
+
const token = await resolveToken();
|
|
976
|
+
if (token != null && token !== "") {
|
|
977
|
+
ctx.headers.set(headerName, token);
|
|
978
|
+
}
|
|
979
|
+
const res = await next(ctx);
|
|
980
|
+
maybeReadResponseHeader(res);
|
|
981
|
+
return res;
|
|
982
|
+
};
|
|
983
|
+
async function resolveToken(_ctx) {
|
|
984
|
+
if (opts.tokenFromCookie) {
|
|
985
|
+
const t = readCookie(opts.tokenFromCookie);
|
|
986
|
+
if (t != null && t !== "") return t;
|
|
987
|
+
}
|
|
988
|
+
if (opts.tokenFromHeader && cachedHeaderToken != null) {
|
|
989
|
+
return cachedHeaderToken;
|
|
990
|
+
}
|
|
991
|
+
if (opts.tokenFromStorage) {
|
|
992
|
+
try {
|
|
993
|
+
const t = await opts.tokenFromStorage();
|
|
994
|
+
if (t != null && t !== "") return t;
|
|
995
|
+
} catch {
|
|
996
|
+
return null;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
return null;
|
|
1000
|
+
}
|
|
1001
|
+
function maybeReadResponseHeader(res) {
|
|
1002
|
+
if (!opts.tokenFromHeader) return;
|
|
1003
|
+
try {
|
|
1004
|
+
const v = res.headers?.get?.(opts.tokenFromHeader);
|
|
1005
|
+
if (v != null && v !== "") cachedHeaderToken = v;
|
|
1006
|
+
} catch {
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// src/guards/correlationIdGuard.ts
|
|
1012
|
+
var CORRELATION_FLAG = "__hg_correlation_id__";
|
|
1013
|
+
function defaultGenerator() {
|
|
1014
|
+
const c = globalThis.crypto;
|
|
1015
|
+
if (c && typeof c.randomUUID === "function") {
|
|
1016
|
+
try {
|
|
1017
|
+
return c.randomUUID();
|
|
1018
|
+
} catch {
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
const ts = Date.now().toString(36);
|
|
1022
|
+
const r1 = Math.floor(Math.random() * 4294967295).toString(36);
|
|
1023
|
+
const r2 = Math.floor(Math.random() * 4294967295).toString(36);
|
|
1024
|
+
return `${ts}-${r1}${r2}`;
|
|
1025
|
+
}
|
|
1026
|
+
function correlationIdGuard(opts = {}) {
|
|
1027
|
+
const headerName = opts.headerName ?? "X-Correlation-Id";
|
|
1028
|
+
const generator = opts.generator ?? defaultGenerator;
|
|
1029
|
+
const readResponseHeader = opts.readResponseHeader ?? true;
|
|
1030
|
+
return async (ctx, next) => {
|
|
1031
|
+
const anyCtx = ctx;
|
|
1032
|
+
let id = anyCtx[CORRELATION_FLAG] ?? void 0;
|
|
1033
|
+
if (id == null) {
|
|
1034
|
+
const existing = ctx.headers.get(headerName);
|
|
1035
|
+
id = existing != null && existing !== "" ? existing : generator();
|
|
1036
|
+
anyCtx[CORRELATION_FLAG] = id;
|
|
1037
|
+
}
|
|
1038
|
+
if (!ctx.headers.has(headerName)) {
|
|
1039
|
+
ctx.headers.set(headerName, id);
|
|
1040
|
+
}
|
|
1041
|
+
if (!ctx.meta) ctx.meta = {};
|
|
1042
|
+
if (!ctx.meta.correlationId) ctx.meta.correlationId = id;
|
|
1043
|
+
const res = await next(ctx);
|
|
1044
|
+
if (readResponseHeader) {
|
|
1045
|
+
try {
|
|
1046
|
+
const v = res.headers?.get?.(headerName);
|
|
1047
|
+
if (v != null && v !== "") {
|
|
1048
|
+
if (!ctx.meta) ctx.meta = {};
|
|
1049
|
+
ctx.meta.correlationId = v;
|
|
1050
|
+
}
|
|
1051
|
+
} catch {
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
return res;
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
// src/guards/idempotencyGuard.ts
|
|
1059
|
+
var DEFAULT_METHODS2 = ["POST", "PUT", "PATCH"];
|
|
1060
|
+
var IDEMPOTENCY_FLAG = "__hg_idempotency_key__";
|
|
1061
|
+
function defaultGenerator2() {
|
|
1062
|
+
const c = globalThis.crypto;
|
|
1063
|
+
if (c && typeof c.randomUUID === "function") {
|
|
1064
|
+
try {
|
|
1065
|
+
return c.randomUUID();
|
|
1066
|
+
} catch {
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
const ts = Date.now().toString(36);
|
|
1070
|
+
const r1 = Math.floor(Math.random() * 4294967295).toString(36);
|
|
1071
|
+
const r2 = Math.floor(Math.random() * 4294967295).toString(36);
|
|
1072
|
+
return `${ts}-${r1}${r2}`;
|
|
1073
|
+
}
|
|
1074
|
+
function idempotencyGuard(opts = {}) {
|
|
1075
|
+
const headerName = opts.headerName ?? "X-Idempotency-Key";
|
|
1076
|
+
const methods = new Set((opts.applyTo ?? DEFAULT_METHODS2).map((m) => m.toUpperCase()));
|
|
1077
|
+
const generator = opts.generator ?? defaultGenerator2;
|
|
1078
|
+
const respectExisting = opts.respectExisting ?? true;
|
|
1079
|
+
return async (ctx, next) => {
|
|
1080
|
+
if (!methods.has(ctx.method)) return next(ctx);
|
|
1081
|
+
const anyCtx = ctx;
|
|
1082
|
+
let key = anyCtx[IDEMPOTENCY_FLAG] ?? void 0;
|
|
1083
|
+
if (key == null && respectExisting) {
|
|
1084
|
+
const fromHeader = ctx.headers.get(headerName);
|
|
1085
|
+
if (fromHeader != null && fromHeader !== "") {
|
|
1086
|
+
key = fromHeader;
|
|
1087
|
+
} else if (ctx.idempotencyKey != null && ctx.idempotencyKey !== "") {
|
|
1088
|
+
key = ctx.idempotencyKey;
|
|
1089
|
+
} else if (ctx.meta?.idempotencyKey != null && ctx.meta.idempotencyKey !== "") {
|
|
1090
|
+
key = ctx.meta.idempotencyKey;
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
if (key == null) key = generator();
|
|
1094
|
+
anyCtx[IDEMPOTENCY_FLAG] = key;
|
|
1095
|
+
if (!ctx.headers.has(headerName)) ctx.headers.set(headerName, key);
|
|
1096
|
+
if (!ctx.meta) ctx.meta = {};
|
|
1097
|
+
if (!ctx.meta.idempotencyKey) ctx.meta.idempotencyKey = key;
|
|
1098
|
+
return next(ctx);
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// src/guards/unauthorizedGuard.ts
|
|
1103
|
+
function normalizePath2(url) {
|
|
1104
|
+
try {
|
|
1105
|
+
return new URL(url).pathname;
|
|
1106
|
+
} catch {
|
|
1107
|
+
const q = url.indexOf("?");
|
|
1108
|
+
const h = url.indexOf("#");
|
|
1109
|
+
const cut = Math.min(q === -1 ? url.length : q, h === -1 ? url.length : h);
|
|
1110
|
+
return url.slice(0, cut);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
function unauthorizedGuard(opts) {
|
|
1114
|
+
const statuses = opts.statuses ?? [401, 419];
|
|
1115
|
+
const ignoreNoAuth = opts.ignoreNoAuth ?? true;
|
|
1116
|
+
const once = opts.once ?? true;
|
|
1117
|
+
const cooldownMs = opts.cooldownMs ?? 250;
|
|
1118
|
+
let locked = false;
|
|
1119
|
+
let cooldownUntil = 0;
|
|
1120
|
+
return async (ctx, next) => {
|
|
1121
|
+
let res;
|
|
1122
|
+
try {
|
|
1123
|
+
res = await next(ctx);
|
|
1124
|
+
} catch (e) {
|
|
1125
|
+
throw e;
|
|
1126
|
+
}
|
|
1127
|
+
if (ignoreNoAuth && ctx.noAuth) return res;
|
|
1128
|
+
const path = normalizePath2(ctx.url);
|
|
1129
|
+
if (opts.excludePaths?.includes(path)) return res;
|
|
1130
|
+
if (!statuses.includes(res.status)) return res;
|
|
1131
|
+
if (opts.shouldHandle && !opts.shouldHandle(ctx, res.status)) return res;
|
|
1132
|
+
const now = Date.now();
|
|
1133
|
+
if (now < cooldownUntil) return res;
|
|
1134
|
+
if (once) {
|
|
1135
|
+
if (locked) return res;
|
|
1136
|
+
locked = true;
|
|
1137
|
+
}
|
|
1138
|
+
try {
|
|
1139
|
+
opts.onUnauthorized({ url: ctx.url, status: res.status });
|
|
1140
|
+
} finally {
|
|
1141
|
+
cooldownUntil = Date.now() + cooldownMs;
|
|
1142
|
+
if (once) setTimeout(() => locked = false, cooldownMs);
|
|
1143
|
+
}
|
|
1144
|
+
return res;
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
// src/guards/instrumentGuard.ts
|
|
1149
|
+
var REFRESH_FLAG3 = "__hg_is_refresh__";
|
|
1150
|
+
var RETRY_FLAG3 = "__hg_refresh_retry__";
|
|
1151
|
+
function safeUrl(u) {
|
|
1152
|
+
try {
|
|
1153
|
+
const url = new URL(u);
|
|
1154
|
+
return url.toString();
|
|
1155
|
+
} catch {
|
|
1156
|
+
return u;
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
function nowMs() {
|
|
1160
|
+
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
1161
|
+
}
|
|
1162
|
+
function headersToObject(h) {
|
|
1163
|
+
if (!h) return {};
|
|
1164
|
+
const out = {};
|
|
1165
|
+
try {
|
|
1166
|
+
h.forEach((v, k) => {
|
|
1167
|
+
const key = String(k).toLowerCase();
|
|
1168
|
+
if (key === "authorization" || key === "cookie" || key === "set-cookie" || key === "x-api-key" || key === "proxy-authorization") {
|
|
1169
|
+
out[k] = "[REDACTED]";
|
|
1170
|
+
} else {
|
|
1171
|
+
out[k] = v;
|
|
1172
|
+
}
|
|
1173
|
+
});
|
|
1174
|
+
} catch {
|
|
1175
|
+
}
|
|
1176
|
+
return out;
|
|
1177
|
+
}
|
|
1178
|
+
var __hg_req_seq = 0;
|
|
1179
|
+
function instrumentGuard(opts, guard) {
|
|
1180
|
+
const log = opts.log ?? ((line, data) => data !== void 0 ? console.log(line, data) : console.log(line));
|
|
1181
|
+
return async (ctx, next) => {
|
|
1182
|
+
const id = ++__hg_req_seq;
|
|
1183
|
+
const t0 = nowMs();
|
|
1184
|
+
const url = safeUrl(ctx.url);
|
|
1185
|
+
const method = ctx.method ?? "GET";
|
|
1186
|
+
if (opts.include && !opts.include.test(url)) {
|
|
1187
|
+
return guard(ctx, next);
|
|
1188
|
+
}
|
|
1189
|
+
if (opts.exclude && opts.exclude.test(url)) {
|
|
1190
|
+
return guard(ctx, next);
|
|
1191
|
+
}
|
|
1192
|
+
const anyCtx = ctx;
|
|
1193
|
+
const attempt = ctx.attempt ?? 0;
|
|
1194
|
+
const isRefresh = Boolean(anyCtx[REFRESH_FLAG3]);
|
|
1195
|
+
const retryCount = Number(anyCtx[RETRY_FLAG3] ?? 0);
|
|
1196
|
+
const noAuth2 = Boolean(ctx.noAuth);
|
|
1197
|
+
const baseLine = `[HG][#${id}][${opts.name}]`;
|
|
1198
|
+
log(`${baseLine} \u25B6 start`, {
|
|
1199
|
+
method,
|
|
1200
|
+
url,
|
|
1201
|
+
attempt,
|
|
1202
|
+
retryCount,
|
|
1203
|
+
isRefresh,
|
|
1204
|
+
noAuth: noAuth2,
|
|
1205
|
+
baseUrl: ctx.baseUrl,
|
|
1206
|
+
...opts.logInit ? {
|
|
1207
|
+
credentials: ctx.credentials,
|
|
1208
|
+
redirect: ctx.redirect,
|
|
1209
|
+
headers: headersToObject(ctx.headers)
|
|
1210
|
+
} : {},
|
|
1211
|
+
...opts.stack ? { stack: new Error().stack } : {}
|
|
1212
|
+
});
|
|
1213
|
+
try {
|
|
1214
|
+
const res = await guard(ctx, next);
|
|
1215
|
+
const dt = Math.round(nowMs() - t0);
|
|
1216
|
+
log(`${baseLine} \u25C0 end (${dt}ms)`, {
|
|
1217
|
+
status: res.status,
|
|
1218
|
+
ok: res.ok,
|
|
1219
|
+
url: res.url || url,
|
|
1220
|
+
redirected: res.redirected,
|
|
1221
|
+
location: res.headers?.get?.("location") ?? null,
|
|
1222
|
+
retryAfter: res.headers?.get?.("retry-after") ?? null
|
|
1223
|
+
});
|
|
1224
|
+
return res;
|
|
1225
|
+
} catch (e) {
|
|
1226
|
+
const dt = Math.round(nowMs() - t0);
|
|
1227
|
+
log(`${baseLine} \u2716 throw (${dt}ms)`, {
|
|
1228
|
+
error: e instanceof Error ? { name: e.name, message: e.message, stack: e.stack } : e
|
|
1229
|
+
});
|
|
1230
|
+
throw e;
|
|
1231
|
+
}
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
|
|
462
1235
|
// src/cache/memoryCache.ts
|
|
463
1236
|
function memoryCache(opts) {
|
|
464
1237
|
const max = opts?.maxEntries ?? 1e3;
|
|
@@ -655,6 +1428,268 @@ function createApiParserGraphQL(opts) {
|
|
|
655
1428
|
});
|
|
656
1429
|
}
|
|
657
1430
|
|
|
658
|
-
|
|
1431
|
+
// src/parsing/apiParserStructured.ts
|
|
1432
|
+
function isRecord(v) {
|
|
1433
|
+
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
1434
|
+
}
|
|
1435
|
+
function pickString(o, key) {
|
|
1436
|
+
const v = o[key];
|
|
1437
|
+
return typeof v === "string" ? v : void 0;
|
|
1438
|
+
}
|
|
1439
|
+
function createApiParserStructured(opts = {}) {
|
|
1440
|
+
const exposeMeta = opts.exposeMeta ?? true;
|
|
1441
|
+
const honorRetryableHint = opts.honorRetryableHint ?? true;
|
|
1442
|
+
return createShapeParser({
|
|
1443
|
+
isSuccess: (raw, status) => {
|
|
1444
|
+
if (isRecord(raw) && "success" in raw) return raw["success"] === true;
|
|
1445
|
+
return status >= 200 && status < 400;
|
|
1446
|
+
},
|
|
1447
|
+
getData: (raw) => {
|
|
1448
|
+
if (isRecord(raw) && raw["success"] === true && "data" in raw) {
|
|
1449
|
+
return raw["data"];
|
|
1450
|
+
}
|
|
1451
|
+
return raw;
|
|
1452
|
+
},
|
|
1453
|
+
getErrors: (raw, status) => {
|
|
1454
|
+
if (!isRecord(raw)) {
|
|
1455
|
+
return [{ message: status ? `HTTP ${status}` : "Request failed" }];
|
|
1456
|
+
}
|
|
1457
|
+
const env = raw;
|
|
1458
|
+
const meta = isRecord(env["meta"]) ? env["meta"] : void 0;
|
|
1459
|
+
const traceId = meta?.traceId;
|
|
1460
|
+
const spanId = meta?.spanId;
|
|
1461
|
+
const issues = Array.isArray(env["issues"]) ? env["issues"] : [];
|
|
1462
|
+
const topMessage = pickString(env, "message");
|
|
1463
|
+
const topCode = pickString(env, "code");
|
|
1464
|
+
const topI18n = isRecord(env["i18n"]) ? env["i18n"] : void 0;
|
|
1465
|
+
const baseDetails = (extra = {}) => ({
|
|
1466
|
+
...traceId !== void 0 ? { traceId } : {},
|
|
1467
|
+
...spanId !== void 0 ? { spanId } : {},
|
|
1468
|
+
...exposeMeta ? { envelope: env } : {},
|
|
1469
|
+
...extra
|
|
1470
|
+
});
|
|
1471
|
+
if (issues.length > 0) {
|
|
1472
|
+
return issues.map((iss) => {
|
|
1473
|
+
const message = iss.message ?? topMessage ?? (status ? `HTTP ${status}` : "Request failed");
|
|
1474
|
+
const code = iss.code ?? topCode;
|
|
1475
|
+
const issueDetails = baseDetails({
|
|
1476
|
+
...iss.category !== void 0 ? { category: iss.category } : {},
|
|
1477
|
+
...iss.severity !== void 0 ? { severity: iss.severity } : {},
|
|
1478
|
+
...honorRetryableHint && iss.retryable !== void 0 ? { retryable: iss.retryable } : {},
|
|
1479
|
+
...iss.i18n !== void 0 ? { i18n: iss.i18n } : topI18n !== void 0 ? { i18n: topI18n } : {}
|
|
1480
|
+
});
|
|
1481
|
+
return {
|
|
1482
|
+
message,
|
|
1483
|
+
...code !== void 0 ? { code } : {},
|
|
1484
|
+
...iss.field !== void 0 ? { field: iss.field } : {},
|
|
1485
|
+
details: issueDetails
|
|
1486
|
+
};
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
const details = baseDetails({
|
|
1490
|
+
...topI18n !== void 0 ? { i18n: topI18n } : {}
|
|
1491
|
+
});
|
|
1492
|
+
return [
|
|
1493
|
+
{
|
|
1494
|
+
message: topMessage ?? (status ? `HTTP ${status}` : "Request failed"),
|
|
1495
|
+
...topCode !== void 0 ? { code: topCode } : {},
|
|
1496
|
+
details
|
|
1497
|
+
}
|
|
1498
|
+
];
|
|
1499
|
+
}
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
// src/presets/presetBuilder.ts
|
|
1504
|
+
function createPresetBuilder(init) {
|
|
1505
|
+
const state = {
|
|
1506
|
+
baseUrl: init?.baseUrl ?? "",
|
|
1507
|
+
parser: init?.parser ?? createApiParserRestClassic(),
|
|
1508
|
+
auth: init?.auth ?? noAuth(),
|
|
1509
|
+
...init?.fetch ? { fetch: init.fetch } : {},
|
|
1510
|
+
...init?.notifier ? { notifier: init.notifier } : { notifier: consoleNotifier },
|
|
1511
|
+
guards: []
|
|
1512
|
+
};
|
|
1513
|
+
function ensureBaseUrl() {
|
|
1514
|
+
if (!state.baseUrl) throw new Error("presetBuilder: baseUrl is required");
|
|
1515
|
+
}
|
|
1516
|
+
const api = {
|
|
1517
|
+
/** Set / override baseUrl */
|
|
1518
|
+
baseUrl(baseUrl) {
|
|
1519
|
+
state.baseUrl = baseUrl;
|
|
1520
|
+
return api;
|
|
1521
|
+
},
|
|
1522
|
+
/** Set / override parser */
|
|
1523
|
+
parser(parser) {
|
|
1524
|
+
state.parser = parser;
|
|
1525
|
+
return api;
|
|
1526
|
+
},
|
|
1527
|
+
/** Set / override fetch */
|
|
1528
|
+
fetch(fetchImpl) {
|
|
1529
|
+
state.fetch = fetchImpl;
|
|
1530
|
+
return api;
|
|
1531
|
+
},
|
|
1532
|
+
/** Set / override notifier */
|
|
1533
|
+
notifier(notifier) {
|
|
1534
|
+
state.notifier = notifier;
|
|
1535
|
+
return api;
|
|
1536
|
+
},
|
|
1537
|
+
/** Add guard(s) (preserves order) */
|
|
1538
|
+
guards(...guards) {
|
|
1539
|
+
state.guards = [...state.guards ?? [], ...guards];
|
|
1540
|
+
return api;
|
|
1541
|
+
},
|
|
1542
|
+
/** Enable in-memory cache */
|
|
1543
|
+
memoryCache(opts) {
|
|
1544
|
+
state.cache = memoryCache(opts);
|
|
1545
|
+
return api;
|
|
1546
|
+
},
|
|
1547
|
+
/** Low-level cache assignment */
|
|
1548
|
+
cache(cache) {
|
|
1549
|
+
if (cache === void 0) {
|
|
1550
|
+
delete state.cache;
|
|
1551
|
+
} else {
|
|
1552
|
+
state.cache = cache;
|
|
1553
|
+
}
|
|
1554
|
+
return api;
|
|
1555
|
+
},
|
|
1556
|
+
/** Default headers / timeouts / notify policy */
|
|
1557
|
+
defaults(defaults) {
|
|
1558
|
+
state.defaults = { ...state.defaults ?? {}, ...defaults ?? {} };
|
|
1559
|
+
return api;
|
|
1560
|
+
},
|
|
1561
|
+
/** Redirect policy */
|
|
1562
|
+
redirects(redirects) {
|
|
1563
|
+
if (redirects === void 0) {
|
|
1564
|
+
delete state.redirects;
|
|
1565
|
+
} else {
|
|
1566
|
+
state.redirects = redirects;
|
|
1567
|
+
}
|
|
1568
|
+
return api;
|
|
1569
|
+
},
|
|
1570
|
+
/** Auth presets */
|
|
1571
|
+
auth(auth) {
|
|
1572
|
+
state.auth = auth;
|
|
1573
|
+
return api;
|
|
1574
|
+
},
|
|
1575
|
+
bearer(getToken, opts) {
|
|
1576
|
+
state.auth = bearerAuth(getToken, opts);
|
|
1577
|
+
return api;
|
|
1578
|
+
},
|
|
1579
|
+
cookie(opts) {
|
|
1580
|
+
state.auth = cookieAuth(opts);
|
|
1581
|
+
return api;
|
|
1582
|
+
},
|
|
1583
|
+
basic(getCred, opts) {
|
|
1584
|
+
state.auth = basicAuth(getCred, opts);
|
|
1585
|
+
return api;
|
|
1586
|
+
},
|
|
1587
|
+
apiKey(getKey, opts) {
|
|
1588
|
+
state.auth = apiKeyAuth(getKey, opts);
|
|
1589
|
+
return api;
|
|
1590
|
+
},
|
|
1591
|
+
none() {
|
|
1592
|
+
state.auth = noAuth();
|
|
1593
|
+
return api;
|
|
1594
|
+
},
|
|
1595
|
+
/**
|
|
1596
|
+
* Cookie refresh guard preset (only meaningful for cookie auth).
|
|
1597
|
+
* You can still add it manually via `.guards(...)`.
|
|
1598
|
+
*/
|
|
1599
|
+
cookieRefresh(opts) {
|
|
1600
|
+
state.guards = [...state.guards ?? [], cookieSafeRefreshGuard(opts)];
|
|
1601
|
+
return api;
|
|
1602
|
+
},
|
|
1603
|
+
/** Unauthorized handler preset (redirect/logout). */
|
|
1604
|
+
unauthorized(opts) {
|
|
1605
|
+
state.guards = [...state.guards ?? [], unauthorizedGuard(opts)];
|
|
1606
|
+
return api;
|
|
1607
|
+
},
|
|
1608
|
+
/** Returns the final HttpClientOptions */
|
|
1609
|
+
options() {
|
|
1610
|
+
ensureBaseUrl();
|
|
1611
|
+
return { ...state, guards: [...state.guards ?? []] };
|
|
1612
|
+
},
|
|
1613
|
+
/** Creates the http client */
|
|
1614
|
+
create() {
|
|
1615
|
+
ensureBaseUrl();
|
|
1616
|
+
return createHttpClient(api.options());
|
|
1617
|
+
}
|
|
1618
|
+
};
|
|
1619
|
+
return api;
|
|
1620
|
+
}
|
|
1621
|
+
function createSimpleClient(opts) {
|
|
1622
|
+
return createPresetBuilder(opts).create();
|
|
1623
|
+
}
|
|
1624
|
+
function createBearerClient(opts) {
|
|
1625
|
+
const bearerOpts = {
|
|
1626
|
+
...opts.headerName !== void 0 ? { headerName: opts.headerName } : {},
|
|
1627
|
+
...opts.prefix !== void 0 ? { prefix: opts.prefix } : {}
|
|
1628
|
+
};
|
|
1629
|
+
return createPresetBuilder({
|
|
1630
|
+
baseUrl: opts.baseUrl,
|
|
1631
|
+
...opts.parser ? { parser: opts.parser } : {},
|
|
1632
|
+
...opts.fetch ? { fetch: opts.fetch } : {},
|
|
1633
|
+
...opts.notifier ? { notifier: opts.notifier } : {}
|
|
1634
|
+
}).bearer(opts.getToken, Object.keys(bearerOpts).length ? bearerOpts : void 0).create();
|
|
1635
|
+
}
|
|
1636
|
+
function createCookieClient(opts) {
|
|
1637
|
+
const b = createPresetBuilder({
|
|
1638
|
+
baseUrl: opts.baseUrl,
|
|
1639
|
+
...opts.parser ? { parser: opts.parser } : {},
|
|
1640
|
+
...opts.fetch ? { fetch: opts.fetch } : {},
|
|
1641
|
+
...opts.notifier ? { notifier: opts.notifier } : {}
|
|
1642
|
+
}).cookie({
|
|
1643
|
+
...opts.credentials !== void 0 ? { credentials: opts.credentials } : {},
|
|
1644
|
+
...opts.csrf !== void 0 ? { csrf: opts.csrf } : {}
|
|
1645
|
+
});
|
|
1646
|
+
if (opts.refresh) b.cookieRefresh(opts.refresh);
|
|
1647
|
+
return b.create();
|
|
1648
|
+
}
|
|
1649
|
+
function createBasicClient(opts) {
|
|
1650
|
+
const basicOpts = {
|
|
1651
|
+
...opts.headerName !== void 0 ? { headerName: opts.headerName } : {},
|
|
1652
|
+
...opts.prefix !== void 0 ? { prefix: opts.prefix } : {}
|
|
1653
|
+
};
|
|
1654
|
+
return createPresetBuilder({
|
|
1655
|
+
baseUrl: opts.baseUrl,
|
|
1656
|
+
...opts.parser ? { parser: opts.parser } : {},
|
|
1657
|
+
...opts.fetch ? { fetch: opts.fetch } : {},
|
|
1658
|
+
...opts.notifier ? { notifier: opts.notifier } : {}
|
|
1659
|
+
}).basic(opts.getCredentials, Object.keys(basicOpts).length ? basicOpts : void 0).create();
|
|
1660
|
+
}
|
|
1661
|
+
function createApiKeyClient(opts) {
|
|
1662
|
+
const apiKeyOpts = {
|
|
1663
|
+
...opts.headerName !== void 0 ? { headerName: opts.headerName } : {},
|
|
1664
|
+
...opts.prefix !== void 0 ? { prefix: opts.prefix } : {}
|
|
1665
|
+
};
|
|
1666
|
+
return createPresetBuilder({
|
|
1667
|
+
baseUrl: opts.baseUrl,
|
|
1668
|
+
...opts.parser ? { parser: opts.parser } : {},
|
|
1669
|
+
...opts.fetch ? { fetch: opts.fetch } : {},
|
|
1670
|
+
...opts.notifier ? { notifier: opts.notifier } : {}
|
|
1671
|
+
}).apiKey(opts.getKey, Object.keys(apiKeyOpts).length ? apiKeyOpts : void 0).create();
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
// src/aliases.ts
|
|
1675
|
+
function createClient(opts) {
|
|
1676
|
+
const parser = opts.parser ?? createApiParserRestClassic();
|
|
1677
|
+
const auth = opts.auth ?? noAuth();
|
|
1678
|
+
const notifier = opts.notifier ?? consoleNotifier;
|
|
1679
|
+
const cfg = {
|
|
1680
|
+
baseUrl: opts.baseUrl,
|
|
1681
|
+
parser,
|
|
1682
|
+
auth,
|
|
1683
|
+
notifier,
|
|
1684
|
+
...opts.fetch ? { fetch: opts.fetch } : {},
|
|
1685
|
+
...opts.defaults ? { defaults: opts.defaults } : {},
|
|
1686
|
+
...opts.cache ? { cache: opts.cache } : {},
|
|
1687
|
+
...opts.redirects ? { redirects: opts.redirects } : {},
|
|
1688
|
+
...opts.guards ? { guards: opts.guards } : {}
|
|
1689
|
+
};
|
|
1690
|
+
return createHttpClient(cfg);
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
export { ILLEGAL_INVOCATION_HINT, NetworkError, ParseError, RedirectError, TimeoutError, alertNotifier, apiKeyAuth, basicAuth, bearerAuth, bearerRefreshGuard, consoleNotifier, cookieAuth, cookieSafeRefreshGuard, correlationIdGuard, createApiKeyClient, createApiParserGraphQL, createApiParserLaravel, createApiParserNest, createApiParserRestClassic, createApiParserStructured, createBasicClient, createBearerClient, createClient, createCookieClient, createHttpClient, createPresetBuilder, createShapeParser, createSimpleClient, csrfGuard, fromZod, idempotencyGuard, instrumentGuard, memoryCache, noAuth, retryGuard, schema, tailwindAlertRenderer, unauthorizedGuard };
|
|
659
1694
|
//# sourceMappingURL=index.js.map
|
|
660
1695
|
//# sourceMappingURL=index.js.map
|