@faable/sdk-base 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -0
- package/dist/FaableApi.d.ts +25 -8
- package/dist/FaableApi.d.ts.map +1 -1
- package/dist/FaableApi.js +15 -3
- package/dist/auth/apikey.d.ts +5 -3
- package/dist/auth/apikey.d.ts.map +1 -1
- package/dist/auth/apikey.js +7 -12
- package/dist/auth/client_credentials/client_credentials.d.ts +4 -7
- package/dist/auth/client_credentials/client_credentials.d.ts.map +1 -1
- package/dist/auth/client_credentials/client_credentials.js +22 -35
- package/dist/fetcher/fetcher_axios.d.ts +6 -1
- package/dist/fetcher/fetcher_axios.d.ts.map +1 -1
- package/dist/fetcher/fetcher_axios.js +22 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/types/AuthStrategy.d.ts +9 -0
- package/dist/types/AuthStrategy.d.ts.map +1 -0
- package/dist/types/AuthStrategy.js +13 -0
- package/package.json +2 -1
- package/tsconfig.test.json +7 -0
- package/dist/types/AuthInterface.d.ts +0 -5
- package/dist/types/AuthInterface.d.ts.map +0 -1
- package/dist/types/AuthInterface.js +0 -1
- package/dist/types/StrategyInterface.d.ts +0 -5
- package/dist/types/StrategyInterface.d.ts.map +0 -1
- package/dist/types/StrategyInterface.js +0 -1
package/README.md
CHANGED
|
@@ -16,3 +16,75 @@
|
|
|
16
16
|
```bash
|
|
17
17
|
npm install @faable/sdk-base
|
|
18
18
|
```
|
|
19
|
+
|
|
20
|
+
## Building an API client
|
|
21
|
+
|
|
22
|
+
Extend `FaableApi` and call the static `create` factory:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { FaableApi } from "@faable/sdk-base";
|
|
26
|
+
import type { ApiParams } from "@faable/sdk-base";
|
|
27
|
+
|
|
28
|
+
export class MyApi extends FaableApi {
|
|
29
|
+
constructor(params?: ApiParams) {
|
|
30
|
+
super({ baseURL: "https://api.example.com", ...params });
|
|
31
|
+
}
|
|
32
|
+
getThing(id: string) {
|
|
33
|
+
return this.fetcher.get(`/thing/${id}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Authentication (pluggable, Octokit-style)
|
|
39
|
+
|
|
40
|
+
Auth is **explicit** — strategies never read environment variables. You pick an
|
|
41
|
+
`authStrategy` and pass its config in `auth`; the chosen strategy drives the
|
|
42
|
+
type of `auth`, so the editor autocompletes the right fields and rejects the
|
|
43
|
+
wrong ones.
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { authClientCredentials, authApikey } from "@faable/sdk-base";
|
|
47
|
+
|
|
48
|
+
// client_credentials — the DEFAULT strategy. `auth` requires
|
|
49
|
+
// { client_id, client_secret }.
|
|
50
|
+
const api = MyApi.create({
|
|
51
|
+
baseURL: "https://my-account.auth.faable.link",
|
|
52
|
+
authStrategy: authClientCredentials,
|
|
53
|
+
auth: { client_id: "...", client_secret: "..." },
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// api key — `auth` requires { apikey }.
|
|
57
|
+
const api2 = MyApi.create({
|
|
58
|
+
baseURL: "https://api.example.com",
|
|
59
|
+
authStrategy: authApikey,
|
|
60
|
+
auth: { apikey: "fak_xxx" },
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`authClientCredentials` requests its token from `<baseURL>/oauth/token` by
|
|
65
|
+
default. When the API host is **not** the auth server, point the token request
|
|
66
|
+
at the right host with `auth.domain`:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
auth: { client_id: "...", client_secret: "...", domain: "https://auth.example.com" }
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Writing a custom strategy
|
|
73
|
+
|
|
74
|
+
A strategy is a builder `(config, context) => { headers() }`:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import type { AuthStrategyBuilder } from "@faable/sdk-base";
|
|
78
|
+
|
|
79
|
+
type BearerConfig = { token: string };
|
|
80
|
+
|
|
81
|
+
export const authBearer: AuthStrategyBuilder<BearerConfig> = (config) => {
|
|
82
|
+
if (!config?.token) throw new Error("authBearer: `auth.token` is required");
|
|
83
|
+
return {
|
|
84
|
+
headers: async () => ({ authorization: `Bearer ${config.token}` }),
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`context` carries the api's `domain` (its `baseURL`) and `debug` flag, so a
|
|
90
|
+
token-minting strategy can reach the auth server without extra config.
|
package/dist/FaableApi.d.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import type { AuthInterface } from "./types/AuthInterface.js";
|
|
2
1
|
import type { Fetcher, FetcherCreateParams } from "./fetcher/Fetcher.js";
|
|
3
2
|
import type { Paginator, PaginatorOptions } from "./helpers/paginator.js";
|
|
4
|
-
|
|
3
|
+
import type { AuthStrategyBuilder } from "./types/AuthStrategy.js";
|
|
4
|
+
import { type ClientCredentialsConfig } from "./auth/client_credentials/client_credentials.js";
|
|
5
|
+
export type RequestObserver = (info: {
|
|
6
|
+
method: string;
|
|
7
|
+
url: string;
|
|
8
|
+
status: number;
|
|
9
|
+
durationMs: number;
|
|
10
|
+
}) => void;
|
|
11
|
+
export type ApiParams<TAuth = ClientCredentialsConfig> = {
|
|
5
12
|
baseURL?: string;
|
|
6
13
|
fetcher?: FetcherCreateParams;
|
|
7
|
-
|
|
14
|
+
authStrategy?: AuthStrategyBuilder<TAuth>;
|
|
15
|
+
auth?: TAuth;
|
|
8
16
|
debug?: boolean;
|
|
9
17
|
paginator?: Partial<PaginatorOptions>;
|
|
10
18
|
/**
|
|
@@ -12,21 +20,30 @@ export type ApiParams = {
|
|
|
12
20
|
* remembers each GET's `ETag` and replays it as `If-None-Match`; a `304 Not
|
|
13
21
|
* Modified` reuses the previously parsed body instead of re-downloading it.
|
|
14
22
|
* Correctness relies on the server changing the ETag whenever the resource
|
|
15
|
-
* changes (no client-side invalidation needed
|
|
16
|
-
* ETag, so the next GET gets a fresh `200`). Off by default.
|
|
23
|
+
* changes (no client-side invalidation needed). Off by default.
|
|
17
24
|
*
|
|
18
25
|
* `true` uses the default cap; pass `{ maxEntries }` to size the bounded LRU
|
|
19
|
-
* for the client's key cardinality
|
|
20
|
-
* deployments wants a larger cap than a one-shot script).
|
|
26
|
+
* for the client's key cardinality.
|
|
21
27
|
*/
|
|
22
28
|
etagCache?: boolean | {
|
|
23
29
|
maxEntries?: number;
|
|
24
30
|
};
|
|
31
|
+
/**
|
|
32
|
+
* Pluggable per-response observer. Called once per completed HTTP response
|
|
33
|
+
* with `{ method, url, status, durationMs }`. Errors thrown by the observer
|
|
34
|
+
* are swallowed so instrumentation can never break a request.
|
|
35
|
+
*/
|
|
36
|
+
requestObserver?: RequestObserver;
|
|
37
|
+
};
|
|
38
|
+
type CreateParams<Cls extends abstract new (params?: any) => any, TAuth> = Omit<NonNullable<ConstructorParameters<Cls>[0]>, "auth" | "authStrategy"> & {
|
|
39
|
+
authStrategy?: AuthStrategyBuilder<TAuth>;
|
|
40
|
+
auth?: TAuth;
|
|
25
41
|
};
|
|
26
42
|
export declare abstract class FaableApi<Params extends ApiParams = ApiParams> {
|
|
27
43
|
fetcher: Fetcher;
|
|
28
44
|
protected paginator: Paginator;
|
|
29
45
|
protected constructor(params?: Params);
|
|
30
|
-
static create<
|
|
46
|
+
static create<Cls extends abstract new (params?: any) => any, TAuth = ClientCredentialsConfig>(this: Cls, params?: CreateParams<Cls, TAuth>): InstanceType<Cls>;
|
|
31
47
|
}
|
|
48
|
+
export {};
|
|
32
49
|
//# sourceMappingURL=FaableApi.d.ts.map
|
package/dist/FaableApi.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FaableApi.d.ts","sourceRoot":"","sources":["../src/FaableApi.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"FaableApi.d.ts","sourceRoot":"","sources":["../src/FaableApi.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAGzE,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,KAAK,EAEV,mBAAmB,EACpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,iDAAiD,CAAC;AAQzD,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB,KAAK,IAAI,CAAC;AAMX,MAAM,MAAM,SAAS,CAAC,KAAK,GAAG,uBAAuB,IAAI;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAI9B,YAAY,CAAC,EAAE,mBAAmB,CAAC,KAAK,CAAC,CAAC;IAC1C,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACtC;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C;;;;OAIG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC,CAAC;AAKF,KAAK,YAAY,CACf,GAAG,SAAS,QAAQ,MAAM,MAAM,CAAC,EAAE,GAAG,KAAK,GAAG,EAC9C,KAAK,IACH,IAAI,CAAC,WAAW,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC,GAAG;IAC9E,YAAY,CAAC,EAAE,mBAAmB,CAAC,KAAK,CAAC,CAAC;IAC1C,IAAI,CAAC,EAAE,KAAK,CAAC;CACd,CAAC;AAEF,8BAAsB,SAAS,CAAC,MAAM,SAAS,SAAS,GAAG,SAAS;IAClE,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC;IAE/B,SAAS,aAAa,MAAM,CAAC,EAAE,MAAM;IAmBrC,MAAM,CAAC,MAAM,CACX,GAAG,SAAS,QAAQ,MAAM,MAAM,CAAC,EAAE,GAAG,KAAK,GAAG,EAC9C,KAAK,GAAG,uBAAuB,EAC/B,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,YAAY,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC;CAInE"}
|
package/dist/FaableApi.js
CHANGED
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
import { fetcher_axios } from "./fetcher/fetcher_axios.js";
|
|
2
2
|
import { buildPaginator } from "./helpers/paginator.js";
|
|
3
|
+
import { authClientCredentials, } from "./auth/client_credentials/client_credentials.js";
|
|
3
4
|
export class FaableApi {
|
|
4
5
|
fetcher;
|
|
5
6
|
paginator;
|
|
6
7
|
constructor(params) {
|
|
7
|
-
const options = params
|
|
8
|
-
|
|
8
|
+
const options = (params ?? {});
|
|
9
|
+
// Resolve the auth strategy: explicit `authStrategy`, else default to
|
|
10
|
+
// client_credentials when `auth` is given, else anonymous (no headers).
|
|
11
|
+
const builder = options.authStrategy ?? (options.auth ? authClientCredentials : undefined);
|
|
12
|
+
const strategy = builder
|
|
13
|
+
? builder(options.auth ?? {}, {
|
|
14
|
+
domain: options.baseURL,
|
|
15
|
+
debug: options.debug,
|
|
16
|
+
})
|
|
17
|
+
: undefined;
|
|
18
|
+
this.fetcher = fetcher_axios({ ...options, strategy });
|
|
9
19
|
this.paginator = buildPaginator(this.fetcher, options.paginator);
|
|
10
20
|
}
|
|
21
|
+
// Octokit-style factory. Infers `TAuth` from the passed `authStrategy` so
|
|
22
|
+
// `auth` is typed (and autocompleted) accordingly.
|
|
11
23
|
static create(params) {
|
|
12
|
-
|
|
24
|
+
const Cns = this;
|
|
13
25
|
return new Cns(params);
|
|
14
26
|
}
|
|
15
27
|
}
|
package/dist/auth/apikey.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export type
|
|
3
|
-
|
|
1
|
+
import type { AuthStrategyBuilder } from "../types/AuthStrategy.js";
|
|
2
|
+
export type ApikeyConfig = {
|
|
3
|
+
apikey: string;
|
|
4
|
+
};
|
|
5
|
+
export declare const authApikey: AuthStrategyBuilder<ApikeyConfig>;
|
|
4
6
|
//# sourceMappingURL=apikey.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apikey.d.ts","sourceRoot":"","sources":["../../src/auth/apikey.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"apikey.d.ts","sourceRoot":"","sources":["../../src/auth/apikey.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAIpE,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,mBAAmB,CAAC,YAAY,CAUxD,CAAC"}
|
package/dist/auth/apikey.js
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
// Default to environment variable
|
|
4
|
-
apikey = apikey || process.env[APIKEY_ENV_NAME];
|
|
1
|
+
export const authApikey = (config) => {
|
|
2
|
+
const apikey = config?.apikey;
|
|
5
3
|
if (!apikey) {
|
|
6
|
-
throw new Error(`
|
|
4
|
+
throw new Error("authApikey: `auth.apikey` is required (no env fallback)");
|
|
7
5
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
},
|
|
13
|
-
};
|
|
6
|
+
return {
|
|
7
|
+
headers: async () => ({
|
|
8
|
+
authorization: `basic ${btoa(`${apikey}:${apikey}`)}`,
|
|
9
|
+
}),
|
|
14
10
|
};
|
|
15
|
-
return Object.assign(auth, { hook: auth });
|
|
16
11
|
};
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
type
|
|
1
|
+
import type { AuthStrategyBuilder } from "../../types/AuthStrategy.js";
|
|
2
|
+
export type ClientCredentialsConfig = {
|
|
3
3
|
client_id: string;
|
|
4
4
|
client_secret: string;
|
|
5
|
-
domain
|
|
6
|
-
debug?: boolean;
|
|
5
|
+
domain?: string;
|
|
7
6
|
};
|
|
8
|
-
export
|
|
9
|
-
export declare const createClientCredentials: StrategyInterface<ClientCredentialsAuthParams>;
|
|
10
|
-
export {};
|
|
7
|
+
export declare const authClientCredentials: AuthStrategyBuilder<ClientCredentialsConfig>;
|
|
11
8
|
//# sourceMappingURL=client_credentials.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client_credentials.d.ts","sourceRoot":"","sources":["../../../src/auth/client_credentials/client_credentials.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"client_credentials.d.ts","sourceRoot":"","sources":["../../../src/auth/client_credentials/client_credentials.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AASvE,MAAM,MAAM,uBAAuB,GAAG;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IAMtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAOF,eAAO,MAAM,qBAAqB,EAAE,mBAAmB,CACrD,uBAAuB,CA+ExB,CAAC"}
|
|
@@ -1,57 +1,50 @@
|
|
|
1
1
|
import { fetcher_axios } from "../../fetcher/fetcher_axios.js";
|
|
2
2
|
import { createTokenStore } from "./store.js";
|
|
3
3
|
import PQueue from "p-queue";
|
|
4
|
-
const CLIENT_ID_ENV = "FAABLEAUTH_CLIENT_ID";
|
|
5
|
-
const CLIENT_SECRET_ENV = "FAABLEAUTH_CLIENT_SECRET";
|
|
6
|
-
const AUTH_DOMAIN = "FAABLEAUTH_DOMAIN";
|
|
7
4
|
// Refresh the cached token this many seconds before its real expiry, so a
|
|
8
5
|
// request that starts just before the boundary doesn't ship a token that
|
|
9
6
|
// expires mid-flight.
|
|
10
7
|
const EXPIRY_SKEW_SECONDS = 60;
|
|
11
|
-
export const
|
|
12
|
-
const client_id =
|
|
13
|
-
const client_secret =
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
export const authClientCredentials = (config, context) => {
|
|
9
|
+
const client_id = config?.client_id;
|
|
10
|
+
const client_secret = config?.client_secret;
|
|
11
|
+
// `domain` (the auth host whose /oauth/token we hit) defaults to the api's
|
|
12
|
+
// own `domain` (injected via context), so for auth-sdk the SDK and the token
|
|
13
|
+
// endpoint always agree. An explicit `config.domain` overrides it for apis
|
|
14
|
+
// whose baseURL is not the auth server (e.g. the deploy api).
|
|
15
|
+
const auth_domain = config?.domain ?? context?.domain;
|
|
16
|
+
const debug = context?.debug;
|
|
16
17
|
if (!client_id) {
|
|
17
|
-
throw new Error(`
|
|
18
|
+
throw new Error("authClientCredentials: `auth.client_id` is required (no env fallback)");
|
|
18
19
|
}
|
|
19
20
|
if (!client_secret) {
|
|
20
|
-
throw new Error(`
|
|
21
|
+
throw new Error("authClientCredentials: `auth.client_secret` is required (no env fallback)");
|
|
21
22
|
}
|
|
22
23
|
if (!auth_domain) {
|
|
23
|
-
throw new Error(`
|
|
24
|
+
throw new Error("authClientCredentials: `domain` is required — pass it to the api (FaableAuthApi.create({ domain, ... }))");
|
|
24
25
|
}
|
|
25
26
|
const store = createTokenStore({ debug });
|
|
26
27
|
const queue = new PQueue({ concurrency: 1, timeout: 20 * 1000 });
|
|
27
28
|
const isTokenExpired = (token, issued_at) => {
|
|
28
29
|
const { expires_in } = token;
|
|
29
|
-
// `expires_in` is
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
// 24h (86400s) token was treated as expired after ~86s — causing the
|
|
33
|
-
// SDK to re-request a token ~1000× more often than needed and hammer
|
|
34
|
-
// the /oauth/token endpoint. Convert to ms and refresh a bit early
|
|
35
|
-
// (skew) so an in-flight request never ships an about-to-expire token.
|
|
30
|
+
// `expires_in` is SECONDS (OAuth 2.0 §5.1); `issued_at`/`Date.now()` are ms.
|
|
31
|
+
// Convert and refresh a bit early (skew) so an in-flight request never
|
|
32
|
+
// ships an about-to-expire token.
|
|
36
33
|
const expires_at = issued_at + (expires_in - EXPIRY_SKEW_SECONDS) * 1000;
|
|
37
34
|
return expires_at < Date.now();
|
|
38
35
|
};
|
|
39
|
-
const fetcher = fetcher_axios({
|
|
40
|
-
baseURL: auth_domain,
|
|
41
|
-
});
|
|
36
|
+
const fetcher = fetcher_axios({ baseURL: auth_domain });
|
|
42
37
|
const requestToken = async () => {
|
|
43
38
|
const params = new URLSearchParams();
|
|
44
39
|
params.append("grant_type", "client_credentials");
|
|
45
40
|
params.append("client_id", client_id);
|
|
46
41
|
params.append("client_secret", client_secret);
|
|
47
42
|
const token_response = await fetcher.post("/oauth/token", params);
|
|
48
|
-
// Save token in store
|
|
49
43
|
store.saveTokenResponse(token_response);
|
|
50
44
|
return token_response;
|
|
51
45
|
};
|
|
52
46
|
const getToken = async () => {
|
|
53
|
-
|
|
54
|
-
// We don't have a token or it's expired
|
|
47
|
+
const res = store.getTokenResponse();
|
|
55
48
|
if (!res || isTokenExpired(res.response, res.iat)) {
|
|
56
49
|
return requestToken();
|
|
57
50
|
}
|
|
@@ -64,16 +57,10 @@ export const createClientCredentials = (params) => {
|
|
|
64
57
|
}
|
|
65
58
|
return token;
|
|
66
59
|
};
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return {
|
|
73
|
-
headers: {
|
|
74
|
-
authorization: `Bearer ${token.access_token}`,
|
|
75
|
-
},
|
|
76
|
-
};
|
|
60
|
+
return {
|
|
61
|
+
headers: async () => {
|
|
62
|
+
const token = await getTokenWithLimits();
|
|
63
|
+
return { authorization: `Bearer ${token.access_token}` };
|
|
64
|
+
},
|
|
77
65
|
};
|
|
78
|
-
return Object.assign(auth, { hook: auth });
|
|
79
66
|
};
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { Fetcher } from "./Fetcher.js";
|
|
2
2
|
import type { ApiParams } from "../FaableApi.js";
|
|
3
|
-
|
|
3
|
+
import type { AuthStrategy } from "../types/AuthStrategy.js";
|
|
4
|
+
type FetcherParams = ApiParams<any> & {
|
|
5
|
+
strategy?: AuthStrategy;
|
|
6
|
+
};
|
|
7
|
+
export declare const fetcher_axios: (params?: FetcherParams) => Fetcher;
|
|
8
|
+
export {};
|
|
4
9
|
//# sourceMappingURL=fetcher_axios.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetcher_axios.d.ts","sourceRoot":"","sources":["../../src/fetcher/fetcher_axios.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAiB,MAAM,cAAc,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"fetcher_axios.d.ts","sourceRoot":"","sources":["../../src/fetcher/fetcher_axios.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAiB,MAAM,cAAc,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAI7D,KAAK,aAAa,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG;IAAE,QAAQ,CAAC,EAAE,YAAY,CAAA;CAAE,CAAC;AAiBlE,eAAO,MAAM,aAAa,YAAY,aAAa,KAAQ,OAqJ1D,CAAC"}
|
|
@@ -20,8 +20,13 @@ export const fetcher_axios = (params = {}) => {
|
|
|
20
20
|
...(params.fetcher || {}),
|
|
21
21
|
});
|
|
22
22
|
instance.interceptors.request.use(async (req) => {
|
|
23
|
-
const
|
|
24
|
-
|
|
23
|
+
const headers = params.strategy
|
|
24
|
+
? await params.strategy.headers()
|
|
25
|
+
: undefined;
|
|
26
|
+
req.headers.set(headers || {});
|
|
27
|
+
// Stamp a start time so the response interceptor can report latency to the
|
|
28
|
+
// pluggable observer.
|
|
29
|
+
req.__startTime = Date.now();
|
|
25
30
|
return req;
|
|
26
31
|
});
|
|
27
32
|
// Add base interceptor
|
|
@@ -30,6 +35,21 @@ export const fetcher_axios = (params = {}) => {
|
|
|
30
35
|
const queryparams = new URLSearchParams(res.config.params);
|
|
31
36
|
console.log(`[${res.status}] ${res.config.url}${queryparams.size > 0 ? `?${queryparams.toString()}` : ""}`);
|
|
32
37
|
}
|
|
38
|
+
// Pluggable per-response hook (metrics, etc.). Never let it break a request.
|
|
39
|
+
if (params.requestObserver) {
|
|
40
|
+
try {
|
|
41
|
+
const start = res.config.__startTime;
|
|
42
|
+
params.requestObserver({
|
|
43
|
+
method: (res.config.method ?? "get").toUpperCase(),
|
|
44
|
+
url: res.config.url ?? "",
|
|
45
|
+
status: res.status,
|
|
46
|
+
durationMs: start ? Date.now() - start : 0,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// ignore observer errors
|
|
51
|
+
}
|
|
52
|
+
}
|
|
33
53
|
return res;
|
|
34
54
|
}, handleErrorInterceptor);
|
|
35
55
|
// Opt-in in-memory ETag cache (per fetcher instance). Keyed by url + params
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,iDAAiD,CAAC;AAChE,cAAc,6BAA6B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iDAAiD,CAAC;AAChE,cAAc,6BAA6B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type AuthStrategy = {
|
|
2
|
+
headers: () => Promise<Record<string, string>>;
|
|
3
|
+
};
|
|
4
|
+
export type AuthStrategyContext = {
|
|
5
|
+
domain?: string;
|
|
6
|
+
debug?: boolean;
|
|
7
|
+
};
|
|
8
|
+
export type AuthStrategyBuilder<TConfig = unknown> = (config: TConfig, context?: AuthStrategyContext) => AuthStrategy;
|
|
9
|
+
//# sourceMappingURL=AuthStrategy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthStrategy.d.ts","sourceRoot":"","sources":["../../src/types/AuthStrategy.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,YAAY,GAAG;IAGzB,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAChD,CAAC;AAIF,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,mBAAmB,CAAC,OAAO,GAAG,OAAO,IAAI,CACnD,MAAM,EAAE,OAAO,EACf,OAAO,CAAC,EAAE,mBAAmB,KAC1B,YAAY,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Octokit-style pluggable authentication.
|
|
2
|
+
//
|
|
3
|
+
// An `AuthStrategyBuilder<TConfig>` is a factory the caller passes as
|
|
4
|
+
// `authStrategy`. It receives the explicit `auth` config (TConfig) as its first
|
|
5
|
+
// argument so TypeScript can INFER TConfig from the chosen strategy — i.e.
|
|
6
|
+
// passing `authStrategy: authClientCredentials` makes the editor require/
|
|
7
|
+
// autocomplete `auth: { client_id, client_secret }`. The api `domain`/`debug`
|
|
8
|
+
// are injected separately as `context` (kept out of TConfig so they don't
|
|
9
|
+
// pollute the inferred `auth` shape).
|
|
10
|
+
//
|
|
11
|
+
// Strategies are EXPLICIT: they never read environment variables. The caller
|
|
12
|
+
// sources credentials (env, secret manager, …) and passes them in `auth`.
|
|
13
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@faable/sdk-base",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"author": "Marc Pomar <marc@faable.com>",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"generate-types": "openapi-typescript https://api.faable.com/docs/json -o src/api/types.ts",
|
|
35
35
|
"prebuild": "rimraf dist",
|
|
36
36
|
"build": "tsc",
|
|
37
|
+
"typecheck:tests": "tsc -p tsconfig.test.json",
|
|
37
38
|
"test": "ava",
|
|
38
39
|
"release": "semantic-release"
|
|
39
40
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AuthInterface.d.ts","sourceRoot":"","sources":["../../src/types/AuthInterface.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa,CAC5B,WAAW,SAAS,GAAG,EAAE,EACzB,cAAc,SAAS,GAAG;IAE1B,CAAC,GAAG,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAChD,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;CACzD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { AuthInterface } from "./AuthInterface.js";
|
|
2
|
-
export interface StrategyInterface<StrategyOptions extends any[], AuthOptions extends any[] = any, Authentication extends any = any> {
|
|
3
|
-
(...args: StrategyOptions): AuthInterface<AuthOptions, Authentication>;
|
|
4
|
-
}
|
|
5
|
-
//# sourceMappingURL=StrategyInterface.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"StrategyInterface.d.ts","sourceRoot":"","sources":["../../src/types/StrategyInterface.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,MAAM,WAAW,iBAAiB,CAChC,eAAe,SAAS,GAAG,EAAE,EAC7B,WAAW,SAAS,GAAG,EAAE,GAAG,GAAG,EAC/B,cAAc,SAAS,GAAG,GAAG,GAAG;IAEhC,CAAC,GAAG,IAAI,EAAE,eAAe,GAAG,aAAa,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;CACxE"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|