@gcmdev/faxios 0.2.0 → 0.2.2
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/LICENSE +8 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/adapters/fetch-helpers.d.ts +2 -1
- package/dist/lib/adapters/fetch-helpers.d.ts.map +1 -0
- package/dist/lib/adapters/fetch.d.ts +1 -0
- package/dist/lib/adapters/fetch.d.ts.map +1 -0
- package/dist/lib/adapters/fetch.js +86 -26
- package/dist/lib/adapters/fetch.js.map +1 -1
- package/dist/lib/cancel/CanceledError.d.ts +2 -1
- package/dist/lib/cancel/CanceledError.d.ts.map +1 -0
- package/dist/lib/cancel/isCancel.d.ts +1 -0
- package/dist/lib/cancel/isCancel.d.ts.map +1 -0
- package/dist/lib/core/Faxios.d.ts +4 -3
- package/dist/lib/core/Faxios.d.ts.map +1 -0
- package/dist/lib/core/Faxios.js +8 -4
- package/dist/lib/core/Faxios.js.map +1 -1
- package/dist/lib/core/FaxiosError.d.ts +3 -2
- package/dist/lib/core/FaxiosError.d.ts.map +1 -0
- package/dist/lib/core/FaxiosHeaders.d.ts +9 -8
- package/dist/lib/core/FaxiosHeaders.d.ts.map +1 -0
- package/dist/lib/core/buildFullPath.d.ts +1 -0
- package/dist/lib/core/buildFullPath.d.ts.map +1 -0
- package/dist/lib/core/define.d.ts +20 -3
- package/dist/lib/core/define.d.ts.map +1 -0
- package/dist/lib/core/define.js.map +1 -1
- package/dist/lib/core/dispatchRequest.d.ts +2 -1
- package/dist/lib/core/dispatchRequest.d.ts.map +1 -0
- package/dist/lib/core/mergeConfig.d.ts +3 -2
- package/dist/lib/core/mergeConfig.d.ts.map +1 -0
- package/dist/lib/core/mergeConfig.js +40 -2
- package/dist/lib/core/mergeConfig.js.map +1 -1
- package/dist/lib/core/prepareRequest.d.ts +1 -0
- package/dist/lib/core/prepareRequest.d.ts.map +1 -0
- package/dist/lib/core/route.d.ts +10 -1
- package/dist/lib/core/route.d.ts.map +1 -0
- package/dist/lib/core/route.js +2 -5
- package/dist/lib/core/route.js.map +1 -1
- package/dist/lib/core/settle.d.ts +1 -0
- package/dist/lib/core/settle.d.ts.map +1 -0
- package/dist/lib/core/transformData.d.ts +3 -2
- package/dist/lib/core/transformData.d.ts.map +1 -0
- package/dist/lib/core/validateSchema.d.ts +3 -2
- package/dist/lib/core/validateSchema.d.ts.map +1 -0
- package/dist/lib/defaults/index.d.ts +1 -0
- package/dist/lib/defaults/index.d.ts.map +1 -0
- package/dist/lib/defaults/transitional.d.ts +1 -0
- package/dist/lib/defaults/transitional.d.ts.map +1 -0
- package/dist/lib/env/data.d.ts +2 -1
- package/dist/lib/env/data.d.ts.map +1 -0
- package/dist/lib/env/data.js +1 -1
- package/dist/lib/faxios.d.ts +59 -38
- package/dist/lib/faxios.d.ts.map +1 -0
- package/dist/lib/faxios.js.map +1 -1
- package/dist/lib/helpers/FaxiosURLSearchParams.d.ts +1 -0
- package/dist/lib/helpers/FaxiosURLSearchParams.d.ts.map +1 -0
- package/dist/lib/helpers/HttpStatusCode.d.ts +1 -0
- package/dist/lib/helpers/HttpStatusCode.d.ts.map +1 -0
- package/dist/lib/helpers/buildURL.d.ts +1 -0
- package/dist/lib/helpers/buildURL.d.ts.map +1 -0
- package/dist/lib/helpers/cloneConfig.d.ts +1 -1
- package/dist/lib/helpers/cloneConfig.d.ts.map +1 -0
- package/dist/lib/helpers/cloneConfig.js +2 -2
- package/dist/lib/helpers/cloneConfig.js.map +1 -1
- package/dist/lib/helpers/combineURLs.d.ts +1 -0
- package/dist/lib/helpers/combineURLs.d.ts.map +1 -0
- package/dist/lib/helpers/composeSignals.d.ts +1 -0
- package/dist/lib/helpers/composeSignals.d.ts.map +1 -0
- package/dist/lib/helpers/cookies.d.ts +2 -1
- package/dist/lib/helpers/cookies.d.ts.map +1 -0
- package/dist/lib/helpers/estimateDataURLDecodedBytes.d.ts +1 -0
- package/dist/lib/helpers/estimateDataURLDecodedBytes.d.ts.map +1 -0
- package/dist/lib/helpers/formDataToJSON.d.ts +2 -1
- package/dist/lib/helpers/formDataToJSON.d.ts.map +1 -0
- package/dist/lib/helpers/formDataToJSON.js +1 -1
- package/dist/lib/helpers/formDataToJSON.js.map +1 -1
- package/dist/lib/helpers/isAbsoluteURL.d.ts +1 -0
- package/dist/lib/helpers/isAbsoluteURL.d.ts.map +1 -0
- package/dist/lib/helpers/isFaxiosError.d.ts +1 -0
- package/dist/lib/helpers/isFaxiosError.d.ts.map +1 -0
- package/dist/lib/helpers/isURLSameOrigin.d.ts +1 -0
- package/dist/lib/helpers/isURLSameOrigin.d.ts.map +1 -0
- package/dist/lib/helpers/parseHeaders.d.ts +2 -1
- package/dist/lib/helpers/parseHeaders.d.ts.map +1 -0
- package/dist/lib/helpers/progressEventReducer.d.ts +1 -0
- package/dist/lib/helpers/progressEventReducer.d.ts.map +1 -0
- package/dist/lib/helpers/sanitizeHeaderValue.d.ts +1 -0
- package/dist/lib/helpers/sanitizeHeaderValue.d.ts.map +1 -0
- package/dist/lib/helpers/speedometer.d.ts +1 -0
- package/dist/lib/helpers/speedometer.d.ts.map +1 -0
- package/dist/lib/helpers/substitutePathParams.d.ts +1 -0
- package/dist/lib/helpers/substitutePathParams.d.ts.map +1 -0
- package/dist/lib/helpers/substitutePathParams.js +11 -3
- package/dist/lib/helpers/substitutePathParams.js.map +1 -1
- package/dist/lib/helpers/throttle.d.ts +1 -0
- package/dist/lib/helpers/throttle.d.ts.map +1 -0
- package/dist/lib/helpers/toFormData.d.ts +1 -0
- package/dist/lib/helpers/toFormData.d.ts.map +1 -0
- package/dist/lib/helpers/toFormData.js +4 -2
- package/dist/lib/helpers/toFormData.js.map +1 -1
- package/dist/lib/helpers/toURLEncodedForm.d.ts +1 -0
- package/dist/lib/helpers/toURLEncodedForm.d.ts.map +1 -0
- package/dist/lib/helpers/trackStream.d.ts +1 -0
- package/dist/lib/helpers/trackStream.d.ts.map +1 -0
- package/dist/lib/helpers/validator.d.ts +2 -1
- package/dist/lib/helpers/validator.d.ts.map +1 -0
- package/dist/lib/platform.d.ts +1 -0
- package/dist/lib/platform.d.ts.map +1 -0
- package/dist/lib/plugins/attempt.d.ts +1 -0
- package/dist/lib/plugins/attempt.d.ts.map +1 -0
- package/dist/lib/plugins/authBearer.d.ts +10 -5
- package/dist/lib/plugins/authBearer.d.ts.map +1 -0
- package/dist/lib/plugins/authBearer.js +6 -0
- package/dist/lib/plugins/authBearer.js.map +1 -1
- package/dist/lib/plugins/definePlugin.d.ts +4 -3
- package/dist/lib/plugins/definePlugin.d.ts.map +1 -0
- package/dist/lib/plugins/retry.d.ts +24 -23
- package/dist/lib/plugins/retry.d.ts.map +1 -0
- package/dist/lib/plugins/timing.d.ts +9 -8
- package/dist/lib/plugins/timing.d.ts.map +1 -0
- package/dist/lib/types/standard-schema.d.ts +7 -6
- package/dist/lib/types/standard-schema.d.ts.map +1 -0
- package/dist/lib/types.d.ts +104 -92
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/utils.d.ts +5 -3
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +9 -2
- package/dist/lib/utils.js.map +1 -1
- package/package.json +8 -4
- package/skills/configure-requests/SKILL.md +53 -8
- package/skills/configure-requests/references/plugins.md +54 -6
- package/src/index.ts +57 -0
- package/src/lib/adapters/fetch-helpers.ts +167 -0
- package/src/lib/adapters/fetch.ts +871 -0
- package/src/lib/cancel/CanceledError.ts +23 -0
- package/src/lib/cancel/isCancel.ts +10 -0
- package/src/lib/core/Faxios.ts +517 -0
- package/src/lib/core/FaxiosError.ts +283 -0
- package/src/lib/core/FaxiosHeaders.ts +529 -0
- package/src/lib/core/buildFullPath.ts +51 -0
- package/src/lib/core/define.ts +127 -0
- package/src/lib/core/dispatchRequest.ts +171 -0
- package/src/lib/core/mergeConfig.ts +213 -0
- package/src/lib/core/prepareRequest.ts +155 -0
- package/src/lib/core/route.ts +65 -0
- package/src/lib/core/settle.ts +20 -0
- package/src/lib/core/transformData.ts +23 -0
- package/src/lib/core/validateSchema.ts +53 -0
- package/src/lib/defaults/index.ts +221 -0
- package/src/lib/defaults/transitional.ts +9 -0
- package/src/lib/env/data.ts +1 -0
- package/src/lib/faxios.ts +306 -0
- package/src/lib/helpers/FaxiosURLSearchParams.ts +52 -0
- package/src/lib/helpers/HttpStatusCode.ts +77 -0
- package/src/lib/helpers/buildURL.ts +77 -0
- package/src/lib/helpers/cloneConfig.ts +22 -0
- package/src/lib/helpers/combineURLs.ts +11 -0
- package/src/lib/helpers/composeSignals.ts +74 -0
- package/src/lib/helpers/cookies.ts +68 -0
- package/src/lib/helpers/deprecatedMethod.ts +32 -0
- package/src/lib/helpers/estimateDataURLDecodedBytes.ts +95 -0
- package/src/lib/helpers/formDataToJSON.ts +122 -0
- package/src/lib/helpers/isAbsoluteURL.ts +13 -0
- package/src/lib/helpers/isFaxiosError.ts +19 -0
- package/src/lib/helpers/isURLSameOrigin.ts +16 -0
- package/src/lib/helpers/parseHeaders.ts +76 -0
- package/src/lib/helpers/progressEventReducer.ts +57 -0
- package/src/lib/helpers/sanitizeHeaderValue.ts +61 -0
- package/src/lib/helpers/speedometer.ts +55 -0
- package/src/lib/helpers/substitutePathParams.ts +23 -0
- package/src/lib/helpers/throttle.ts +45 -0
- package/src/lib/helpers/toFormData.ts +298 -0
- package/src/lib/helpers/toURLEncodedForm.ts +14 -0
- package/src/lib/helpers/trackStream.ts +123 -0
- package/src/lib/helpers/validator.ts +114 -0
- package/src/lib/platform.ts +44 -0
- package/src/lib/plugins/attempt.ts +3 -0
- package/src/lib/plugins/authBearer.ts +129 -0
- package/src/lib/plugins/definePlugin.ts +34 -0
- package/src/lib/plugins/retry.ts +245 -0
- package/src/lib/plugins/timing.ts +76 -0
- package/src/lib/types/standard-schema.ts +58 -0
- package/src/lib/types.ts +496 -0
- package/src/lib/utils.ts +941 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
type ReadableStreamController = {
|
|
2
|
+
close: () => void;
|
|
3
|
+
enqueue: (chunk: Uint8Array) => void;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
type ReadableStreamSource = {
|
|
7
|
+
cancel?: (reason?: unknown) => Promise<unknown>;
|
|
8
|
+
pull?: (controller: ReadableStreamController) => Promise<void>;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type ReadableStreamInit = {
|
|
12
|
+
highWaterMark?: number;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type ReadableStreamCtor = new (source: ReadableStreamSource, init?: ReadableStreamInit) => unknown;
|
|
16
|
+
|
|
17
|
+
const ReadableStreamGlobal = (globalThis as Record<string, unknown>)["ReadableStream"] as ReadableStreamCtor | undefined;
|
|
18
|
+
|
|
19
|
+
type ByteChunk = { byteLength: number; slice: (start: number, end?: number) => ByteChunk; };
|
|
20
|
+
|
|
21
|
+
const streamChunk = function* (chunk: ByteChunk, chunkSize?: number): Generator<ByteChunk> {
|
|
22
|
+
const len = chunk.byteLength;
|
|
23
|
+
|
|
24
|
+
if (!chunkSize || len < chunkSize) {
|
|
25
|
+
yield chunk;
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let pos = 0;
|
|
30
|
+
let end: number;
|
|
31
|
+
|
|
32
|
+
while (pos < len) {
|
|
33
|
+
end = pos + chunkSize;
|
|
34
|
+
yield chunk.slice(pos, end);
|
|
35
|
+
pos = end;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const readBytes = async function* (iterable: AsyncIterable<ByteChunk> | unknown, chunkSize?: number): AsyncGenerator<ByteChunk> {
|
|
40
|
+
for await (const chunk of readStream(iterable)) {
|
|
41
|
+
yield* streamChunk(chunk, chunkSize);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
type ReaderLike = {
|
|
46
|
+
cancel: () => Promise<void>;
|
|
47
|
+
read: () => Promise<{ done: boolean; value: ByteChunk; }>;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
type StreamLike = {
|
|
51
|
+
[Symbol.asyncIterator]?: () => AsyncIterator<ByteChunk>;
|
|
52
|
+
getReader?: () => ReaderLike;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const readStream = async function* (stream: unknown): AsyncGenerator<ByteChunk> {
|
|
56
|
+
const s = stream as StreamLike;
|
|
57
|
+
if (s[Symbol.asyncIterator]) {
|
|
58
|
+
yield* s as AsyncIterable<ByteChunk>;
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const reader = s.getReader!();
|
|
63
|
+
try {
|
|
64
|
+
for (;;) {
|
|
65
|
+
// eslint-disable-next-line no-await-in-loop
|
|
66
|
+
const { done, value } = await reader.read();
|
|
67
|
+
if (done) {
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
yield value;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
await reader.cancel();
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const trackStream = (stream: unknown, chunkSize: number | undefined, onProgress?: ((bytes: number) => void) | null, onFinish?: ((err?: unknown) => void) | null): unknown => {
|
|
79
|
+
const iterator = readBytes(stream, chunkSize);
|
|
80
|
+
|
|
81
|
+
let bytes = 0;
|
|
82
|
+
let done = false;
|
|
83
|
+
const _onFinish = (e?: unknown): void => {
|
|
84
|
+
if (!done) {
|
|
85
|
+
done = true;
|
|
86
|
+
onFinish && onFinish(e);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return new ReadableStreamGlobal!(
|
|
91
|
+
{
|
|
92
|
+
async pull(controller: ReadableStreamController) {
|
|
93
|
+
try {
|
|
94
|
+
const { done: iterDone, value } = await iterator.next();
|
|
95
|
+
|
|
96
|
+
if (iterDone) {
|
|
97
|
+
_onFinish();
|
|
98
|
+
controller.close();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const len = value.byteLength;
|
|
103
|
+
if (onProgress) {
|
|
104
|
+
const loadedBytes = (bytes += len);
|
|
105
|
+
onProgress(loadedBytes);
|
|
106
|
+
}
|
|
107
|
+
controller.enqueue(new Uint8Array(value as unknown as ArrayBuffer));
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
_onFinish(err);
|
|
111
|
+
throw err;
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
async cancel(reason?: unknown) {
|
|
115
|
+
_onFinish(reason);
|
|
116
|
+
return iterator.return(undefined);
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
highWaterMark: 2,
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import FaxiosError from "../core/FaxiosError.js";
|
|
4
|
+
import { VERSION } from "../env/data.js";
|
|
5
|
+
|
|
6
|
+
export type ValidatorFn = (value: unknown, opt?: string, opts?: unknown) => boolean | string;
|
|
7
|
+
|
|
8
|
+
const validators: Record<string, undefined | ValidatorFn> = {};
|
|
9
|
+
|
|
10
|
+
[ "object", "boolean", "number", "function", "string", "symbol" ].forEach((type, i) => {
|
|
11
|
+
validators[type] = function validator(thing: unknown): boolean | string {
|
|
12
|
+
return typeof thing === type || "a" + (i < 1 ? "n " : " ") + type;
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const deprecatedWarnings: Record<string, boolean | undefined> = {};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Transitional option validator
|
|
20
|
+
*
|
|
21
|
+
* @param {function|boolean?} validator - set to false if the transitional option has been removed
|
|
22
|
+
* @param {string?} version - deprecated version / removed since version
|
|
23
|
+
* @param {string?} message - some message with additional info
|
|
24
|
+
*
|
|
25
|
+
* @returns {function}
|
|
26
|
+
*/
|
|
27
|
+
(validators as Record<string, unknown>)["transitional"] = function transitional(validator: false | undefined | ValidatorFn, version?: string, message?: string): ValidatorFn {
|
|
28
|
+
function formatMessage(opt: string, desc: string): string {
|
|
29
|
+
return (
|
|
30
|
+
"[Faxios v" +
|
|
31
|
+
VERSION +
|
|
32
|
+
"] Transitional option '" +
|
|
33
|
+
opt +
|
|
34
|
+
"'" +
|
|
35
|
+
desc +
|
|
36
|
+
(message ? ". " + message : "")
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return (value: unknown, opt?: string, opts?: unknown): boolean | string => {
|
|
41
|
+
if (validator === false) {
|
|
42
|
+
throw new FaxiosError(
|
|
43
|
+
formatMessage(opt ?? "", " has been removed" + (version ? " in " + version : "")),
|
|
44
|
+
FaxiosError.ERR_DEPRECATED
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (version && opt && !deprecatedWarnings[opt]) {
|
|
49
|
+
deprecatedWarnings[opt] = true;
|
|
50
|
+
|
|
51
|
+
// eslint-disable-next-line no-console
|
|
52
|
+
console.warn(
|
|
53
|
+
formatMessage(
|
|
54
|
+
opt,
|
|
55
|
+
" has been deprecated since v" + version + " and will be removed in the near future"
|
|
56
|
+
)
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return validator ? validator(value, opt, opts) : true;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
(validators as Record<string, unknown>)["spelling"] = function spelling(correctSpelling: string): ValidatorFn {
|
|
65
|
+
return (_value: unknown, opt?: string): boolean | string => {
|
|
66
|
+
|
|
67
|
+
// eslint-disable-next-line no-console
|
|
68
|
+
console.warn(`${opt ?? ""} is likely a misspelling of ${correctSpelling}`);
|
|
69
|
+
return true;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Assert object's properties type
|
|
75
|
+
*
|
|
76
|
+
* @param {object} options
|
|
77
|
+
* @param {object} schema
|
|
78
|
+
* @param {boolean?} allowUnknown
|
|
79
|
+
*
|
|
80
|
+
* @returns {object}
|
|
81
|
+
*/
|
|
82
|
+
|
|
83
|
+
function assertOptions(options: unknown, schema: Record<string, undefined | ValidatorFn>, allowUnknown: boolean): void {
|
|
84
|
+
if (typeof options !== "object") {
|
|
85
|
+
throw new FaxiosError("options must be an object", FaxiosError.ERR_BAD_OPTION_VALUE);
|
|
86
|
+
}
|
|
87
|
+
const keys = Object.keys(options as object);
|
|
88
|
+
let i = keys.length;
|
|
89
|
+
while (i-- > 0) {
|
|
90
|
+
const opt = keys[i]!;
|
|
91
|
+
// Use hasOwnProperty so a polluted Object.prototype.<opt> cannot supply
|
|
92
|
+
// a non-function validator and cause a TypeError.
|
|
93
|
+
const validator = Object.prototype.hasOwnProperty.call(schema, opt) ? schema[opt] : undefined;
|
|
94
|
+
if (validator) {
|
|
95
|
+
const value = (options as Record<string, unknown>)[opt];
|
|
96
|
+
const result = value === undefined || validator(value, opt, options);
|
|
97
|
+
if (result !== true) {
|
|
98
|
+
throw new FaxiosError(
|
|
99
|
+
"option " + opt + " must be " + result,
|
|
100
|
+
FaxiosError.ERR_BAD_OPTION_VALUE
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (allowUnknown !== true) {
|
|
106
|
+
throw new FaxiosError("Unknown option " + opt, FaxiosError.ERR_BAD_OPTION);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export default {
|
|
112
|
+
assertOptions,
|
|
113
|
+
validators,
|
|
114
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import FaxiosURLSearchParams from "./helpers/FaxiosURLSearchParams.js";
|
|
4
|
+
|
|
5
|
+
const _globalThis = globalThis as {
|
|
6
|
+
Blob?: new (...args: Array<unknown>) => object;
|
|
7
|
+
document?: unknown;
|
|
8
|
+
FormData?: new () => object;
|
|
9
|
+
navigator?: { product?: string; userAgent?: string; };
|
|
10
|
+
self?: { importScripts?: unknown; };
|
|
11
|
+
window?: { location?: { href?: string; }; };
|
|
12
|
+
WorkerGlobalScope?: unknown;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const hasBrowserEnv = typeof _globalThis.window !== "undefined" && typeof _globalThis.document !== "undefined";
|
|
16
|
+
|
|
17
|
+
const _navigator = typeof _globalThis.navigator === "object" ? _globalThis.navigator : undefined;
|
|
18
|
+
|
|
19
|
+
const hasStandardBrowserEnv =
|
|
20
|
+
hasBrowserEnv &&
|
|
21
|
+
(!_navigator || [ "ReactNative", "NativeScript", "NS" ].indexOf(_navigator.product ?? "") < 0);
|
|
22
|
+
|
|
23
|
+
const hasStandardBrowserWebWorkerEnv = (() => (
|
|
24
|
+
typeof _globalThis.WorkerGlobalScope !== "undefined" &&
|
|
25
|
+
_globalThis.self instanceof (_globalThis.WorkerGlobalScope as new (...args: Array<unknown>) => unknown) &&
|
|
26
|
+
typeof _globalThis.self?.importScripts === "function"
|
|
27
|
+
))();
|
|
28
|
+
|
|
29
|
+
const origin = (hasBrowserEnv && _globalThis.window?.location?.href) || "http://localhost";
|
|
30
|
+
|
|
31
|
+
export default {
|
|
32
|
+
isBrowser: true,
|
|
33
|
+
classes: {
|
|
34
|
+
URLSearchParams: typeof URLSearchParams !== "undefined" ? URLSearchParams : FaxiosURLSearchParams,
|
|
35
|
+
FormData: _globalThis.FormData !== undefined ? _globalThis.FormData : null,
|
|
36
|
+
Blob: _globalThis.Blob !== undefined ? _globalThis.Blob : null,
|
|
37
|
+
},
|
|
38
|
+
protocols: [ "http", "https", "file", "blob", "url", "data" ],
|
|
39
|
+
hasBrowserEnv,
|
|
40
|
+
hasStandardBrowserEnv,
|
|
41
|
+
hasStandardBrowserWebWorkerEnv,
|
|
42
|
+
navigator: _navigator,
|
|
43
|
+
origin,
|
|
44
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// @ts-self-types="./authBearer.d.ts" — required for Deno: maps built .js to adjacent .d.ts in dist/
|
|
2
|
+
|
|
3
|
+
import buildFullPath from "../core/buildFullPath.js";
|
|
4
|
+
import FaxiosError from "../core/FaxiosError.js";
|
|
5
|
+
import FaxiosHeaders from "../core/FaxiosHeaders.js";
|
|
6
|
+
import isAbsoluteURL from "../helpers/isAbsoluteURL.js";
|
|
7
|
+
import validator from "../helpers/validator.js";
|
|
8
|
+
import type { FaxiosPlugin, InternalFaxiosRequestConfig } from "../types.js";
|
|
9
|
+
import utils from "../utils.js";
|
|
10
|
+
import { definePlugin } from "./definePlugin.js";
|
|
11
|
+
|
|
12
|
+
/** What `authBearer` provides to plugins installed after it. */
|
|
13
|
+
export type AuthBearerCapability = { auth: { getToken: () => Promise<string>; }; };
|
|
14
|
+
|
|
15
|
+
export type AuthBearerOptions = {
|
|
16
|
+
/** The header to set. Default `"Authorization"`. */
|
|
17
|
+
header?: string;
|
|
18
|
+
/**
|
|
19
|
+
* The origins (`"https://api.example.com"`) that get the token, replacing the default rule:
|
|
20
|
+
* with an absolute `baseURL`, only its origin; otherwise only relative request URLs.
|
|
21
|
+
*/
|
|
22
|
+
origins?: Array<string>;
|
|
23
|
+
/** Replace a header the request already has. Default `false`: an explicit header is kept. */
|
|
24
|
+
overwrite?: boolean;
|
|
25
|
+
/** Put before the token, separated by a space. Default `"Bearer"`; `""` sends the bare token. */
|
|
26
|
+
scheme?: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// A header name is a token (RFC 9110 §5.6.2).
|
|
30
|
+
const TOKEN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
31
|
+
|
|
32
|
+
// A bare origin, optionally with a trailing slash. A path would look like a narrower limit than
|
|
33
|
+
// the origin the token is really sent to.
|
|
34
|
+
function originOf(value: unknown): null | string {
|
|
35
|
+
if (typeof value !== "string") return null;
|
|
36
|
+
try {
|
|
37
|
+
const { origin } = new URL(value);
|
|
38
|
+
return origin !== "null" && (value === origin || value === `${origin}/`) ? origin : null;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const optionsSchema = {
|
|
46
|
+
overwrite: validator.validators["boolean"],
|
|
47
|
+
scheme: (value: unknown) => (typeof value === "string" && !/[\r\n]/.test(value)) || "a string without line breaks",
|
|
48
|
+
header: (value: unknown) => (typeof value === "string" && TOKEN.test(value)) || "a header name",
|
|
49
|
+
origins: (value: unknown) => (Array.isArray(value) && value.every(o => originOf(o) !== null)) || "an array of origins such as \"https://api.example.com\"",
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const own = <T extends object, K extends keyof T>(source: T, key: K): T[K] | undefined =>
|
|
53
|
+
(utils.hasOwnProp(source, key) ? source[key] : undefined);
|
|
54
|
+
|
|
55
|
+
// Resolves a relative URL the way a page would, without ever matching a real origin, so a URL
|
|
56
|
+
// that leaves the page (`//host`, `/\host`) is told apart from one that stays on it.
|
|
57
|
+
const SAME_ORIGIN = "https://same-origin.invalid";
|
|
58
|
+
|
|
59
|
+
function resolvedOrigin(url: string, base: string | undefined): null | string {
|
|
60
|
+
try {
|
|
61
|
+
return new URL(url, base).origin;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// A relative URL goes to the page's origin, which only a browser has.
|
|
69
|
+
function pageHref(): string | undefined {
|
|
70
|
+
const location: unknown = "location" in globalThis ? globalThis.location : undefined;
|
|
71
|
+
if (location && typeof location === "object" && "href" in location && typeof location.href === "string") return location.href;
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function sendsTokenTo(config: InternalFaxiosRequestConfig, origins: null | ReadonlyArray<string>): boolean {
|
|
76
|
+
const baseURL = own(config, "baseURL");
|
|
77
|
+
const url = buildFullPath(baseURL, own(config, "url"), own(config, "allowAbsoluteUrls"), config);
|
|
78
|
+
if (origins) {
|
|
79
|
+
const origin = resolvedOrigin(url, isAbsoluteURL(url) ? undefined : pageHref());
|
|
80
|
+
return origin !== null && origins.includes(origin);
|
|
81
|
+
}
|
|
82
|
+
if (typeof baseURL === "string" && isAbsoluteURL(baseURL)) {
|
|
83
|
+
return resolvedOrigin(url, baseURL) === resolvedOrigin(baseURL, undefined);
|
|
84
|
+
}
|
|
85
|
+
return !isAbsoluteURL(url) && resolvedOrigin(url, SAME_ORIGIN) === SAME_ORIGIN;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Sets `Authorization: Bearer <token>` on requests to the API it guards, asking `getToken` each
|
|
90
|
+
* time, and provides the `auth` capability so later plugins (for example a token-refresh plugin)
|
|
91
|
+
* can ask for a token too. A header the request already has is kept unless `overwrite` is set.
|
|
92
|
+
* The token goes only to the `baseURL` origin when `baseURL` is absolute, otherwise only to
|
|
93
|
+
* relative request URLs; `origins` replaces that rule.
|
|
94
|
+
*
|
|
95
|
+
* When `getToken` gives anything other than a non-empty string (`null`, `undefined`, `""`, a
|
|
96
|
+
* number, an object), the request is sent without the header, so a signed-out state never sends
|
|
97
|
+
* `Bearer null`. The parameter type stays `string`: the guard is for JavaScript callers and casts.
|
|
98
|
+
*/
|
|
99
|
+
export function authBearer(getToken: () => Promise<string> | string, options: AuthBearerOptions = {}): FaxiosPlugin<{ provides: AuthBearerCapability; }> {
|
|
100
|
+
if (typeof getToken !== "function") {
|
|
101
|
+
throw new FaxiosError("authBearer: getToken must be a function", FaxiosError.ERR_BAD_OPTION_VALUE);
|
|
102
|
+
}
|
|
103
|
+
// Callers without TypeScript can pass anything.
|
|
104
|
+
const given: unknown = options;
|
|
105
|
+
if (given === null || typeof given !== "object") {
|
|
106
|
+
throw new FaxiosError("authBearer options must be an object", FaxiosError.ERR_BAD_OPTION_VALUE);
|
|
107
|
+
}
|
|
108
|
+
validator.assertOptions(options, optionsSchema, false);
|
|
109
|
+
const overwrite = own(options, "overwrite") ?? false;
|
|
110
|
+
const scheme = own(options, "scheme") ?? "Bearer";
|
|
111
|
+
const header = own(options, "header") ?? "Authorization";
|
|
112
|
+
const origins = own(options, "origins")?.map(o => originOf(o)!) ?? null;
|
|
113
|
+
const auth = { getToken: async () => getToken() };
|
|
114
|
+
return definePlugin({
|
|
115
|
+
name: "authBearer",
|
|
116
|
+
provides: { auth },
|
|
117
|
+
middleware: async (ctx, next) => {
|
|
118
|
+
if ((!overwrite && ctx.config.headers.has(header)) || !sendsTokenTo(ctx.config, origins)) return next(ctx);
|
|
119
|
+
const token: unknown = await auth.getToken();
|
|
120
|
+
if (typeof token !== "string" || token === "") return next(ctx);
|
|
121
|
+
// On a copy: middleware that calls next() again (retry installed before this plugin) comes
|
|
122
|
+
// back through here without the header, so every try asks for a fresh token.
|
|
123
|
+
const headers = new FaxiosHeaders(ctx.config.headers).set(header, scheme ? `${scheme} ${token}` : token, true);
|
|
124
|
+
const config = { ...ctx.config };
|
|
125
|
+
config.headers = headers;
|
|
126
|
+
return next({ ...ctx, config });
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// @ts-self-types="./definePlugin.d.ts" — required for Deno: maps built .js to adjacent .d.ts in dist/
|
|
2
|
+
// The `@gcmdev/faxios/plugins` entry point. The built-in plugins have their own subpaths.
|
|
3
|
+
|
|
4
|
+
import type { FaxiosMiddleware, FaxiosPlugin } from "../types.js";
|
|
5
|
+
|
|
6
|
+
// An empty capability or option map adds nothing, so it's the `unknown` an absent slot is.
|
|
7
|
+
type Slot<T> = [keyof T] extends [never] ? unknown : T;
|
|
8
|
+
|
|
9
|
+
// The middleware's capabilities are what it requires plus what it provides itself.
|
|
10
|
+
type RequiredCapabilities<TCapabilities, TProvides> = [keyof TProvides] extends [never]
|
|
11
|
+
? Slot<TCapabilities>
|
|
12
|
+
: Slot<{ [K in keyof TCapabilities as K extends keyof TProvides ? never : K]: TCapabilities[K]; }>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Builds a plugin for `use()` and infers its `FaxiosPlugin` slots from the object: `provides` from
|
|
16
|
+
* the `provides` value, `options` and `requires` from the context type the middleware annotates
|
|
17
|
+
* (`ctx: FaxiosContext<Options, Capabilities>`). Capabilities the plugin provides itself don't
|
|
18
|
+
* count as required. Returns the same object.
|
|
19
|
+
*/
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type -- `{}`: no capabilities or options, as in FaxiosContext
|
|
21
|
+
export function definePlugin<TProvides = {}, TOptions = {}, TCapabilities = TProvides>(plugin: {
|
|
22
|
+
middleware: FaxiosMiddleware<TOptions, TCapabilities>;
|
|
23
|
+
name: string;
|
|
24
|
+
// The middleware reads the capabilities it provides from ctx too, so they must have the types
|
|
25
|
+
// its context declares. NoInfer: TProvides is inferred from the value alone.
|
|
26
|
+
provides?: TProvides & NoInfer<Pick<TCapabilities, keyof TProvides & keyof TCapabilities>>;
|
|
27
|
+
}): FaxiosPlugin<{
|
|
28
|
+
options: Slot<TOptions>;
|
|
29
|
+
provides: Slot<TProvides>;
|
|
30
|
+
requires: RequiredCapabilities<TCapabilities, TProvides>;
|
|
31
|
+
}> {
|
|
32
|
+
// Only the types change: use() reads the fields at install time, as it does for any plugin.
|
|
33
|
+
return plugin as never;
|
|
34
|
+
}
|