@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.
Files changed (186) hide show
  1. package/LICENSE +8 -0
  2. package/dist/index.d.ts +4 -3
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/adapters/fetch-helpers.d.ts +2 -1
  7. package/dist/lib/adapters/fetch-helpers.d.ts.map +1 -0
  8. package/dist/lib/adapters/fetch.d.ts +1 -0
  9. package/dist/lib/adapters/fetch.d.ts.map +1 -0
  10. package/dist/lib/adapters/fetch.js +86 -26
  11. package/dist/lib/adapters/fetch.js.map +1 -1
  12. package/dist/lib/cancel/CanceledError.d.ts +2 -1
  13. package/dist/lib/cancel/CanceledError.d.ts.map +1 -0
  14. package/dist/lib/cancel/isCancel.d.ts +1 -0
  15. package/dist/lib/cancel/isCancel.d.ts.map +1 -0
  16. package/dist/lib/core/Faxios.d.ts +4 -3
  17. package/dist/lib/core/Faxios.d.ts.map +1 -0
  18. package/dist/lib/core/Faxios.js +8 -4
  19. package/dist/lib/core/Faxios.js.map +1 -1
  20. package/dist/lib/core/FaxiosError.d.ts +3 -2
  21. package/dist/lib/core/FaxiosError.d.ts.map +1 -0
  22. package/dist/lib/core/FaxiosHeaders.d.ts +9 -8
  23. package/dist/lib/core/FaxiosHeaders.d.ts.map +1 -0
  24. package/dist/lib/core/buildFullPath.d.ts +1 -0
  25. package/dist/lib/core/buildFullPath.d.ts.map +1 -0
  26. package/dist/lib/core/define.d.ts +20 -3
  27. package/dist/lib/core/define.d.ts.map +1 -0
  28. package/dist/lib/core/define.js.map +1 -1
  29. package/dist/lib/core/dispatchRequest.d.ts +2 -1
  30. package/dist/lib/core/dispatchRequest.d.ts.map +1 -0
  31. package/dist/lib/core/mergeConfig.d.ts +3 -2
  32. package/dist/lib/core/mergeConfig.d.ts.map +1 -0
  33. package/dist/lib/core/mergeConfig.js +40 -2
  34. package/dist/lib/core/mergeConfig.js.map +1 -1
  35. package/dist/lib/core/prepareRequest.d.ts +1 -0
  36. package/dist/lib/core/prepareRequest.d.ts.map +1 -0
  37. package/dist/lib/core/route.d.ts +10 -1
  38. package/dist/lib/core/route.d.ts.map +1 -0
  39. package/dist/lib/core/route.js +2 -5
  40. package/dist/lib/core/route.js.map +1 -1
  41. package/dist/lib/core/settle.d.ts +1 -0
  42. package/dist/lib/core/settle.d.ts.map +1 -0
  43. package/dist/lib/core/transformData.d.ts +3 -2
  44. package/dist/lib/core/transformData.d.ts.map +1 -0
  45. package/dist/lib/core/validateSchema.d.ts +3 -2
  46. package/dist/lib/core/validateSchema.d.ts.map +1 -0
  47. package/dist/lib/defaults/index.d.ts +1 -0
  48. package/dist/lib/defaults/index.d.ts.map +1 -0
  49. package/dist/lib/defaults/transitional.d.ts +1 -0
  50. package/dist/lib/defaults/transitional.d.ts.map +1 -0
  51. package/dist/lib/env/data.d.ts +2 -1
  52. package/dist/lib/env/data.d.ts.map +1 -0
  53. package/dist/lib/env/data.js +1 -1
  54. package/dist/lib/faxios.d.ts +59 -38
  55. package/dist/lib/faxios.d.ts.map +1 -0
  56. package/dist/lib/faxios.js.map +1 -1
  57. package/dist/lib/helpers/FaxiosURLSearchParams.d.ts +1 -0
  58. package/dist/lib/helpers/FaxiosURLSearchParams.d.ts.map +1 -0
  59. package/dist/lib/helpers/HttpStatusCode.d.ts +1 -0
  60. package/dist/lib/helpers/HttpStatusCode.d.ts.map +1 -0
  61. package/dist/lib/helpers/buildURL.d.ts +1 -0
  62. package/dist/lib/helpers/buildURL.d.ts.map +1 -0
  63. package/dist/lib/helpers/cloneConfig.d.ts +1 -1
  64. package/dist/lib/helpers/cloneConfig.d.ts.map +1 -0
  65. package/dist/lib/helpers/cloneConfig.js +2 -2
  66. package/dist/lib/helpers/cloneConfig.js.map +1 -1
  67. package/dist/lib/helpers/combineURLs.d.ts +1 -0
  68. package/dist/lib/helpers/combineURLs.d.ts.map +1 -0
  69. package/dist/lib/helpers/composeSignals.d.ts +1 -0
  70. package/dist/lib/helpers/composeSignals.d.ts.map +1 -0
  71. package/dist/lib/helpers/cookies.d.ts +2 -1
  72. package/dist/lib/helpers/cookies.d.ts.map +1 -0
  73. package/dist/lib/helpers/estimateDataURLDecodedBytes.d.ts +1 -0
  74. package/dist/lib/helpers/estimateDataURLDecodedBytes.d.ts.map +1 -0
  75. package/dist/lib/helpers/formDataToJSON.d.ts +2 -1
  76. package/dist/lib/helpers/formDataToJSON.d.ts.map +1 -0
  77. package/dist/lib/helpers/formDataToJSON.js +1 -1
  78. package/dist/lib/helpers/formDataToJSON.js.map +1 -1
  79. package/dist/lib/helpers/isAbsoluteURL.d.ts +1 -0
  80. package/dist/lib/helpers/isAbsoluteURL.d.ts.map +1 -0
  81. package/dist/lib/helpers/isFaxiosError.d.ts +1 -0
  82. package/dist/lib/helpers/isFaxiosError.d.ts.map +1 -0
  83. package/dist/lib/helpers/isURLSameOrigin.d.ts +1 -0
  84. package/dist/lib/helpers/isURLSameOrigin.d.ts.map +1 -0
  85. package/dist/lib/helpers/parseHeaders.d.ts +2 -1
  86. package/dist/lib/helpers/parseHeaders.d.ts.map +1 -0
  87. package/dist/lib/helpers/progressEventReducer.d.ts +1 -0
  88. package/dist/lib/helpers/progressEventReducer.d.ts.map +1 -0
  89. package/dist/lib/helpers/sanitizeHeaderValue.d.ts +1 -0
  90. package/dist/lib/helpers/sanitizeHeaderValue.d.ts.map +1 -0
  91. package/dist/lib/helpers/speedometer.d.ts +1 -0
  92. package/dist/lib/helpers/speedometer.d.ts.map +1 -0
  93. package/dist/lib/helpers/substitutePathParams.d.ts +1 -0
  94. package/dist/lib/helpers/substitutePathParams.d.ts.map +1 -0
  95. package/dist/lib/helpers/substitutePathParams.js +11 -3
  96. package/dist/lib/helpers/substitutePathParams.js.map +1 -1
  97. package/dist/lib/helpers/throttle.d.ts +1 -0
  98. package/dist/lib/helpers/throttle.d.ts.map +1 -0
  99. package/dist/lib/helpers/toFormData.d.ts +1 -0
  100. package/dist/lib/helpers/toFormData.d.ts.map +1 -0
  101. package/dist/lib/helpers/toFormData.js +4 -2
  102. package/dist/lib/helpers/toFormData.js.map +1 -1
  103. package/dist/lib/helpers/toURLEncodedForm.d.ts +1 -0
  104. package/dist/lib/helpers/toURLEncodedForm.d.ts.map +1 -0
  105. package/dist/lib/helpers/trackStream.d.ts +1 -0
  106. package/dist/lib/helpers/trackStream.d.ts.map +1 -0
  107. package/dist/lib/helpers/validator.d.ts +2 -1
  108. package/dist/lib/helpers/validator.d.ts.map +1 -0
  109. package/dist/lib/platform.d.ts +1 -0
  110. package/dist/lib/platform.d.ts.map +1 -0
  111. package/dist/lib/plugins/attempt.d.ts +1 -0
  112. package/dist/lib/plugins/attempt.d.ts.map +1 -0
  113. package/dist/lib/plugins/authBearer.d.ts +10 -5
  114. package/dist/lib/plugins/authBearer.d.ts.map +1 -0
  115. package/dist/lib/plugins/authBearer.js +6 -0
  116. package/dist/lib/plugins/authBearer.js.map +1 -1
  117. package/dist/lib/plugins/definePlugin.d.ts +4 -3
  118. package/dist/lib/plugins/definePlugin.d.ts.map +1 -0
  119. package/dist/lib/plugins/retry.d.ts +24 -23
  120. package/dist/lib/plugins/retry.d.ts.map +1 -0
  121. package/dist/lib/plugins/timing.d.ts +9 -8
  122. package/dist/lib/plugins/timing.d.ts.map +1 -0
  123. package/dist/lib/types/standard-schema.d.ts +7 -6
  124. package/dist/lib/types/standard-schema.d.ts.map +1 -0
  125. package/dist/lib/types.d.ts +104 -92
  126. package/dist/lib/types.d.ts.map +1 -0
  127. package/dist/lib/utils.d.ts +5 -3
  128. package/dist/lib/utils.d.ts.map +1 -0
  129. package/dist/lib/utils.js +9 -2
  130. package/dist/lib/utils.js.map +1 -1
  131. package/package.json +8 -4
  132. package/skills/configure-requests/SKILL.md +53 -8
  133. package/skills/configure-requests/references/plugins.md +54 -6
  134. package/src/index.ts +57 -0
  135. package/src/lib/adapters/fetch-helpers.ts +167 -0
  136. package/src/lib/adapters/fetch.ts +871 -0
  137. package/src/lib/cancel/CanceledError.ts +23 -0
  138. package/src/lib/cancel/isCancel.ts +10 -0
  139. package/src/lib/core/Faxios.ts +517 -0
  140. package/src/lib/core/FaxiosError.ts +283 -0
  141. package/src/lib/core/FaxiosHeaders.ts +529 -0
  142. package/src/lib/core/buildFullPath.ts +51 -0
  143. package/src/lib/core/define.ts +127 -0
  144. package/src/lib/core/dispatchRequest.ts +171 -0
  145. package/src/lib/core/mergeConfig.ts +213 -0
  146. package/src/lib/core/prepareRequest.ts +155 -0
  147. package/src/lib/core/route.ts +65 -0
  148. package/src/lib/core/settle.ts +20 -0
  149. package/src/lib/core/transformData.ts +23 -0
  150. package/src/lib/core/validateSchema.ts +53 -0
  151. package/src/lib/defaults/index.ts +221 -0
  152. package/src/lib/defaults/transitional.ts +9 -0
  153. package/src/lib/env/data.ts +1 -0
  154. package/src/lib/faxios.ts +306 -0
  155. package/src/lib/helpers/FaxiosURLSearchParams.ts +52 -0
  156. package/src/lib/helpers/HttpStatusCode.ts +77 -0
  157. package/src/lib/helpers/buildURL.ts +77 -0
  158. package/src/lib/helpers/cloneConfig.ts +22 -0
  159. package/src/lib/helpers/combineURLs.ts +11 -0
  160. package/src/lib/helpers/composeSignals.ts +74 -0
  161. package/src/lib/helpers/cookies.ts +68 -0
  162. package/src/lib/helpers/deprecatedMethod.ts +32 -0
  163. package/src/lib/helpers/estimateDataURLDecodedBytes.ts +95 -0
  164. package/src/lib/helpers/formDataToJSON.ts +122 -0
  165. package/src/lib/helpers/isAbsoluteURL.ts +13 -0
  166. package/src/lib/helpers/isFaxiosError.ts +19 -0
  167. package/src/lib/helpers/isURLSameOrigin.ts +16 -0
  168. package/src/lib/helpers/parseHeaders.ts +76 -0
  169. package/src/lib/helpers/progressEventReducer.ts +57 -0
  170. package/src/lib/helpers/sanitizeHeaderValue.ts +61 -0
  171. package/src/lib/helpers/speedometer.ts +55 -0
  172. package/src/lib/helpers/substitutePathParams.ts +23 -0
  173. package/src/lib/helpers/throttle.ts +45 -0
  174. package/src/lib/helpers/toFormData.ts +298 -0
  175. package/src/lib/helpers/toURLEncodedForm.ts +14 -0
  176. package/src/lib/helpers/trackStream.ts +123 -0
  177. package/src/lib/helpers/validator.ts +114 -0
  178. package/src/lib/platform.ts +44 -0
  179. package/src/lib/plugins/attempt.ts +3 -0
  180. package/src/lib/plugins/authBearer.ts +129 -0
  181. package/src/lib/plugins/definePlugin.ts +34 -0
  182. package/src/lib/plugins/retry.ts +245 -0
  183. package/src/lib/plugins/timing.ts +76 -0
  184. package/src/lib/types/standard-schema.ts +58 -0
  185. package/src/lib/types.ts +496 -0
  186. 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,3 @@
1
+ // Shared only by the built-in plugins; not exported from any entry point. retry writes the number
2
+ // of the try it is about to make under this key of ctx.state, and timing reads it.
3
+ export const RETRY_ATTEMPT: unique symbol = Symbol("faxios.retry.attempt");
@@ -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
+ }