@faable/deploy-sdk 1.0.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.
@@ -0,0 +1,11 @@
1
+ import { FetcherCreateParams } from "../fetcher/Fetcher.js";
2
+
3
+ export const createApikeyAuth = (
4
+ apikey: string = process.env.FAABLE_APIKEY || ""
5
+ ): FetcherCreateParams => {
6
+ return {
7
+ headers: {
8
+ authorization: `basic ${btoa(`${apikey}:${apikey}`)}`,
9
+ },
10
+ };
11
+ };
@@ -0,0 +1,42 @@
1
+ type Method =
2
+ | "get"
3
+ | "GET"
4
+ | "delete"
5
+ | "DELETE"
6
+ | "head"
7
+ | "HEAD"
8
+ | "options"
9
+ | "OPTIONS"
10
+ | "post"
11
+ | "POST"
12
+ | "put"
13
+ | "PUT"
14
+ | "patch"
15
+ | "PATCH"
16
+ | "purge"
17
+ | "PURGE"
18
+ | "link"
19
+ | "LINK"
20
+ | "unlink"
21
+ | "UNLINK";
22
+ export type FetcherRequestParams = {
23
+ url: string;
24
+ method?: Method;
25
+ params?: any;
26
+ };
27
+
28
+ export type Headers = {
29
+ authorization?: string;
30
+ };
31
+
32
+ export type FetcherCreateParams = {
33
+ baseURL?: string;
34
+ headers?: Headers;
35
+ };
36
+
37
+ export type Fetcher = {
38
+ get: <T>(url: string) => Promise<T>;
39
+ post: <T>(url: string, data: any) => Promise<T>;
40
+ request: <T>(params: FetcherRequestParams) => Promise<T>;
41
+ check: <T>(url: string, status?: number) => Promise<boolean>;
42
+ };
@@ -0,0 +1,50 @@
1
+ import axios, { AxiosError } from "axios";
2
+ import { AxiosRequestConfig, AxiosResponse } from "axios";
3
+ import { FaableApiError } from "../helpers/error_handler.js";
4
+ import { Fetcher, FetcherCreateParams } from "./Fetcher.js";
5
+
6
+ const handleErrorInterceptor = async (e: AxiosError) => {
7
+ const data: any = e.response?.data;
8
+ throw new FaableApiError(
9
+ `FaableApiError: ${data?.message} (status=${e.response?.status}, url=${e.response?.config.url})`
10
+ );
11
+ };
12
+
13
+ export const fetcher_axios = (params: FetcherCreateParams = {}): Fetcher => {
14
+ const instance = axios.create(params);
15
+ // Add base interceptor
16
+ instance.interceptors.response.use((res: AxiosResponse) => {
17
+ //console.log(`Request ${res.config.url}`);
18
+ return res;
19
+ }, handleErrorInterceptor);
20
+
21
+ return {
22
+ get: async <T>(url: string) => {
23
+ const res = await instance.request<T>({
24
+ method: "GET",
25
+ url,
26
+ });
27
+ return res.data;
28
+ },
29
+ post: async <T>(url: string, data: any) => {
30
+ const res = await instance.request<T>({
31
+ method: "GET",
32
+ url,
33
+ data,
34
+ });
35
+ return res.data;
36
+ },
37
+ request: async <T>(params: AxiosRequestConfig) => {
38
+ const res = await instance.request<T>(params);
39
+ return res.data;
40
+ },
41
+ check: async <T>(url: string) => {
42
+ const res = await instance.request<T>({
43
+ method: "GET",
44
+ url,
45
+ validateStatus: () => true,
46
+ });
47
+ return res.status == 200;
48
+ },
49
+ };
50
+ };
@@ -0,0 +1,54 @@
1
+ import axios, { AxiosInstance } from "axios";
2
+ import { jwtDecode } from "jwt-decode";
3
+
4
+ export type ClientCredentialsConfig = {
5
+ domain: string;
6
+ clientId: string;
7
+ clientSecret: string;
8
+ };
9
+
10
+ export interface ClientCredentialsResponse {
11
+ access_token: string;
12
+ }
13
+
14
+ export class ClientCredentialsFlow {
15
+ constructor(private config: ClientCredentialsConfig) {}
16
+
17
+ private response?: ClientCredentialsResponse;
18
+
19
+ private isTokenExpired() {
20
+ if (!this.response) throw new Error("No token");
21
+ let { exp } = jwtDecode(this.response?.access_token);
22
+ if (!exp) {
23
+ throw new Error("Missing expire date");
24
+ }
25
+ const currentSeconds = new Date().getTime() / 1000;
26
+ return currentSeconds > exp;
27
+ }
28
+
29
+ private async _getToken() {
30
+ const params = new URLSearchParams();
31
+ params.append("grant_type", "client_credentials");
32
+ params.append("client_id", this.config.clientId);
33
+ params.append("client_secret", this.config.clientSecret);
34
+ const res = await axios.post<ClientCredentialsResponse>(
35
+ this.config.domain + "/oauth/token",
36
+ params
37
+ );
38
+ this.response = res.data;
39
+ console.log(
40
+ `[${this.config.domain}] ✅ Client Credentials flow completed `
41
+ );
42
+ return this.response;
43
+ }
44
+
45
+ async getToken() {
46
+ if (!this.response || this.isTokenExpired()) {
47
+ await this._getToken();
48
+ }
49
+ if (!this.response?.access_token) {
50
+ throw new Error("Cannot get token");
51
+ }
52
+ return this.response?.access_token;
53
+ }
54
+ }
@@ -0,0 +1,58 @@
1
+ import axios, { AxiosInstance } from "axios";
2
+
3
+ import {
4
+ ClientCredentialsFlow,
5
+ ClientCredentialsConfig,
6
+ } from "./authorize-client-credentials.js";
7
+
8
+ interface AuthConfig {
9
+ client?: AxiosInstance;
10
+ client_credentials?: ClientCredentialsConfig;
11
+ }
12
+
13
+ export const authorize = (_config?: AuthConfig) => {
14
+ const authDomain =
15
+ process.env.FAABLEAUTH_DOMAIN || _config?.client_credentials?.domain || "";
16
+ if (!authDomain) {
17
+ throw new Error("Setup Faable Auth failed, missing env FAABLEAUTH_DOMAIN");
18
+ }
19
+ const client_credentials_config: ClientCredentialsConfig = {
20
+ domain: authDomain,
21
+ clientId:
22
+ process.env.FAABLEAUTH_CLIENT_ID ||
23
+ _config?.client_credentials?.clientId ||
24
+ "",
25
+ clientSecret:
26
+ process.env.FAABLEAUTH_CLIENT_SECRET ||
27
+ _config?.client_credentials?.clientSecret ||
28
+ "",
29
+ };
30
+
31
+ // Reuse client or setup a new one
32
+ const client = _config?.client || axios.create({ baseURL: authDomain });
33
+
34
+ // Config is OK
35
+ let enable_client_credentials = false;
36
+ if (
37
+ client_credentials_config.domain &&
38
+ client_credentials_config.clientId &&
39
+ client_credentials_config.clientSecret
40
+ ) {
41
+ enable_client_credentials = true;
42
+ console.log(
43
+ `[${client_credentials_config.domain}] Using client credentials`
44
+ );
45
+ } else {
46
+ console.warn(`Client credentials not configured`);
47
+ }
48
+
49
+ const flow = new ClientCredentialsFlow(client_credentials_config);
50
+ client.interceptors.request.use(async function (config) {
51
+ if (enable_client_credentials) {
52
+ const token = await flow.getToken();
53
+ config.headers.set("Authorization", `Bearer ${token}`);
54
+ }
55
+ return config;
56
+ });
57
+ return client;
58
+ };
@@ -0,0 +1,7 @@
1
+ export class FaableApiError extends Error {
2
+ constructor(msg: string) {
3
+ super(msg);
4
+ this.name = "FaableApiError";
5
+ Object.setPrototypeOf(this, FaableApiError.prototype);
6
+ }
7
+ }
@@ -0,0 +1,37 @@
1
+ import { Fetcher, FetcherRequestParams } from "../fetcher/Fetcher.js";
2
+
3
+ export interface Page<T> {
4
+ next: string | null;
5
+ results: T[];
6
+ }
7
+ export type Paginator = ReturnType<typeof buildPaginator>;
8
+ export const buildPaginator =
9
+ (fetcher: Fetcher) =>
10
+ <T>(req: FetcherRequestParams) => {
11
+ return {
12
+ all: async () => {
13
+ const pages: Page<T>[] = [];
14
+ let data: Page<T> | undefined;
15
+ do {
16
+ data = await fetcher.request<Page<T>>({
17
+ ...req,
18
+ params: { ...req.params, next: data && data?.next },
19
+ });
20
+ pages.push(data);
21
+ } while (data.next);
22
+ return pages.map((page) => page.results).flat();
23
+ },
24
+ first: async () => {
25
+ return fetcher.request<Page<T>>(req);
26
+ },
27
+ pass: async (params: { cursor?: string; pageSize?: string } = {}) => {
28
+ return fetcher.request<Page<T>>({
29
+ ...req,
30
+ params: {
31
+ ...req.params,
32
+ ...params,
33
+ },
34
+ });
35
+ },
36
+ };
37
+ };
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./FaableDeployApi.js";
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "esModuleInterop": true,
7
+ "forceConsistentCasingInFileNames": true,
8
+ "strict": true,
9
+ "rootDir": "./src",
10
+ "outDir": "dist",
11
+ "declaration": true,
12
+ "declarationMap": true
13
+ },
14
+ "include": ["src/**/*"],
15
+ "exclude": ["src/**/*.test.ts"]
16
+ }