@leaflow/sdk 0.3.0 → 0.4.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/dist/http.d.ts +43 -32
- package/dist/http.js +28 -42
- package/dist/index.d.ts +55 -2
- package/dist/index.js +55 -1
- package/package.json +3 -3
package/dist/http.d.ts
CHANGED
|
@@ -7,9 +7,9 @@ export interface ClientOptions {
|
|
|
7
7
|
/**
|
|
8
8
|
* getToken is called before every request.
|
|
9
9
|
*
|
|
10
|
-
* Called each time rather than read once at
|
|
11
|
-
* it is replaced when the active project changes, and in a server it
|
|
12
|
-
* to whoever is making the current request rather than to the process.
|
|
10
|
+
* Called each time rather than read once at construction: the project token
|
|
11
|
+
* expires, it is replaced when the active project changes, and in a server it
|
|
12
|
+
* belongs to whoever is making the current request rather than to the process.
|
|
13
13
|
*/
|
|
14
14
|
getToken?: TokenProvider;
|
|
15
15
|
/** timeout in milliseconds. */
|
|
@@ -20,10 +20,11 @@ export interface Client {
|
|
|
20
20
|
request<T>(config: AxiosRequestConfig): Promise<T>;
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
* The
|
|
23
|
+
* The last argument of every generated operation: which client to go through.
|
|
24
24
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
25
|
+
* Callers of this package do not construct it. `createIamClient` and its siblings
|
|
26
|
+
* hand back operations with this parameter already bound and gone from the
|
|
27
|
+
* signature — see `bindClient`.
|
|
27
28
|
*/
|
|
28
29
|
export interface RequestOptions {
|
|
29
30
|
client?: Client;
|
|
@@ -31,16 +32,18 @@ export interface RequestOptions {
|
|
|
31
32
|
/**
|
|
32
33
|
* A connection to one service.
|
|
33
34
|
*
|
|
34
|
-
* #
|
|
35
|
+
* # There is no process-wide client, deliberately
|
|
35
36
|
*
|
|
36
|
-
*
|
|
37
|
-
* is
|
|
37
|
+
* An earlier version of this package had `configure()`, which set a module-level
|
|
38
|
+
* axios instance and a module-level token provider. That is state an SDK has no
|
|
39
|
+
* business owning, and it ruled out two things this package is otherwise well
|
|
40
|
+
* suited to:
|
|
38
41
|
*
|
|
39
42
|
* - **More than one service.** The documents bundled here are separate
|
|
40
43
|
* deployments on separate addresses — one host per service through the
|
|
41
44
|
* gateway (`iam.leaflow.cloud`, `compute.leaflow.cloud`, …) — and their paths
|
|
42
45
|
* are bare `/api/v1/...` with no service prefix: `iam` and `monitoring` both
|
|
43
|
-
* own `/api/v1/projects/{id}/...`. One `baseURL` per process therefore
|
|
46
|
+
* own `/api/v1/projects/{id}/...`. One `baseURL` per process therefore meant
|
|
44
47
|
* one service per process.
|
|
45
48
|
* - **More than one caller.** A server handles requests concurrently, and a
|
|
46
49
|
* single module-level `getToken` is read at await points interleaved across
|
|
@@ -48,19 +51,13 @@ export interface RequestOptions {
|
|
|
48
51
|
* the other's project token — intermittently, under load, which is the worst
|
|
49
52
|
* possible way to find out.
|
|
50
53
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* somewhere different per call — which is to say, to work around being a
|
|
57
|
-
* singleton. A client per service answers the same question by construction, and
|
|
58
|
-
* a resolver that runs before every request is one more place for the address to
|
|
59
|
-
* be wrong.
|
|
54
|
+
* So a client is an ordinary value: constructed per service, and — where the
|
|
55
|
+
* token belongs to a request rather than to the process — per request. This is
|
|
56
|
+
* the shape the major cloud SDKs converged on: `new InstancesClient({...})` in
|
|
57
|
+
* Google Cloud, `new EC2Client({...})` in AWS v3, whose predecessor's global
|
|
58
|
+
* `AWS.config` was removed for these same two reasons.
|
|
60
59
|
*/
|
|
61
60
|
export declare function createClient(options: ClientOptions): Client;
|
|
62
|
-
/** configure initialises the process-wide fallback client. Call it once. */
|
|
63
|
-
export declare function configure(options: ClientOptions): void;
|
|
64
61
|
/**
|
|
65
62
|
* request is the entry point used by the generated code.
|
|
66
63
|
*
|
|
@@ -68,22 +65,36 @@ export declare function configure(options: ClientOptions): void;
|
|
|
68
65
|
* it as its last parameter and passes it straight through, which is what lets a
|
|
69
66
|
* caller say which client a call belongs to without this module holding any
|
|
70
67
|
* per-call state.
|
|
68
|
+
*
|
|
69
|
+
* A call with no client throws rather than falling back to something. There used
|
|
70
|
+
* to be a fallback, and a fallback is worse than an error exactly when it works:
|
|
71
|
+
* in a process that talks to four services, the call that forgot its client
|
|
72
|
+
* *succeeds*, against whichever service happened to be configured last.
|
|
71
73
|
*/
|
|
72
74
|
export declare function request<T>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T>;
|
|
73
75
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* `compute.listInstances(params, { client })` at every call site is correct and
|
|
77
|
-
* tiring, and the tiring part is what makes somebody eventually forget one — at
|
|
78
|
-
* which point the call silently falls through to the process-wide fallback,
|
|
79
|
-
* which in a multi-service process points at the wrong service.
|
|
76
|
+
* One service's operations, with the client bound and no longer in the signature.
|
|
80
77
|
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
78
|
+
* The mapped type is the point: `getAccount()` on a bound namespace has no
|
|
79
|
+
* `options` parameter at all, so there is no way to reach the transport by hand,
|
|
80
|
+
* and no way for one service's client to be handed to another service's
|
|
81
|
+
* operation. What is left is what the caller of an SDK should see — the operation
|
|
82
|
+
* and its arguments.
|
|
83
|
+
*/
|
|
84
|
+
export type Bound<T> = {
|
|
85
|
+
[K in keyof T]: T[K] extends (...args: [...infer Head, (RequestOptions | undefined)?]) => infer Result ? (...args: Head) => Result : T[K];
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Attach a client to a whole generated namespace.
|
|
83
89
|
*
|
|
84
90
|
* The binding is by position: orval emits `options` as the last declared
|
|
85
91
|
* parameter of every operation, so `fn.length - 1` is the slot. Arguments the
|
|
86
|
-
* caller omitted are filled with `undefined` to reach it
|
|
87
|
-
*
|
|
92
|
+
* caller omitted are filled with `undefined` to reach it.
|
|
93
|
+
*
|
|
94
|
+
* `fn.length` stops counting at the first parameter that has a default, so that
|
|
95
|
+
* assumption holds only while the generated code has none. It has none today, and
|
|
96
|
+
* `scripts/check-generated-arity.mjs` fails the build if that changes: a slot
|
|
97
|
+
* that silently shifts by one would pass the client as somebody's request body,
|
|
98
|
+
* and no amount of type checking on generated code would catch it.
|
|
88
99
|
*/
|
|
89
|
-
export declare function
|
|
100
|
+
export declare function bindClient<T extends object>(api: T, client: Client): Bound<T>;
|
package/dist/http.js
CHANGED
|
@@ -7,16 +7,18 @@ import axios from 'axios';
|
|
|
7
7
|
/**
|
|
8
8
|
* A connection to one service.
|
|
9
9
|
*
|
|
10
|
-
* #
|
|
10
|
+
* # There is no process-wide client, deliberately
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
* is
|
|
12
|
+
* An earlier version of this package had `configure()`, which set a module-level
|
|
13
|
+
* axios instance and a module-level token provider. That is state an SDK has no
|
|
14
|
+
* business owning, and it ruled out two things this package is otherwise well
|
|
15
|
+
* suited to:
|
|
14
16
|
*
|
|
15
17
|
* - **More than one service.** The documents bundled here are separate
|
|
16
18
|
* deployments on separate addresses — one host per service through the
|
|
17
19
|
* gateway (`iam.leaflow.cloud`, `compute.leaflow.cloud`, …) — and their paths
|
|
18
20
|
* are bare `/api/v1/...` with no service prefix: `iam` and `monitoring` both
|
|
19
|
-
* own `/api/v1/projects/{id}/...`. One `baseURL` per process therefore
|
|
21
|
+
* own `/api/v1/projects/{id}/...`. One `baseURL` per process therefore meant
|
|
20
22
|
* one service per process.
|
|
21
23
|
* - **More than one caller.** A server handles requests concurrently, and a
|
|
22
24
|
* single module-level `getToken` is read at await points interleaved across
|
|
@@ -24,15 +26,11 @@ import axios from 'axios';
|
|
|
24
26
|
* the other's project token — intermittently, under load, which is the worst
|
|
25
27
|
* possible way to find out.
|
|
26
28
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* somewhere different per call — which is to say, to work around being a
|
|
33
|
-
* singleton. A client per service answers the same question by construction, and
|
|
34
|
-
* a resolver that runs before every request is one more place for the address to
|
|
35
|
-
* be wrong.
|
|
29
|
+
* So a client is an ordinary value: constructed per service, and — where the
|
|
30
|
+
* token belongs to a request rather than to the process — per request. This is
|
|
31
|
+
* the shape the major cloud SDKs converged on: `new InstancesClient({...})` in
|
|
32
|
+
* Google Cloud, `new EC2Client({...})` in AWS v3, whose predecessor's global
|
|
33
|
+
* `AWS.config` was removed for these same two reasons.
|
|
36
34
|
*/
|
|
37
35
|
export function createClient(options) {
|
|
38
36
|
const instance = axios.create({
|
|
@@ -53,19 +51,6 @@ export function createClient(options) {
|
|
|
53
51
|
},
|
|
54
52
|
};
|
|
55
53
|
}
|
|
56
|
-
/**
|
|
57
|
-
* The client used when a call does not name one.
|
|
58
|
-
*
|
|
59
|
-
* Kept for single-service consumers — a browser talking to one service, a script
|
|
60
|
-
* — for whom threading a client through every call is ceremony with no payoff. It
|
|
61
|
-
* is a fallback and not the default anyone should reach for in a server: see
|
|
62
|
-
* `createClient` for why.
|
|
63
|
-
*/
|
|
64
|
-
let fallback;
|
|
65
|
-
/** configure initialises the process-wide fallback client. Call it once. */
|
|
66
|
-
export function configure(options) {
|
|
67
|
-
fallback = createClient(options);
|
|
68
|
-
}
|
|
69
54
|
/**
|
|
70
55
|
* request is the entry point used by the generated code.
|
|
71
56
|
*
|
|
@@ -73,31 +58,33 @@ export function configure(options) {
|
|
|
73
58
|
* it as its last parameter and passes it straight through, which is what lets a
|
|
74
59
|
* caller say which client a call belongs to without this module holding any
|
|
75
60
|
* per-call state.
|
|
61
|
+
*
|
|
62
|
+
* A call with no client throws rather than falling back to something. There used
|
|
63
|
+
* to be a fallback, and a fallback is worse than an error exactly when it works:
|
|
64
|
+
* in a process that talks to four services, the call that forgot its client
|
|
65
|
+
* *succeeds*, against whichever service happened to be configured last.
|
|
76
66
|
*/
|
|
77
67
|
export async function request(config, options) {
|
|
78
|
-
const client = options?.client
|
|
68
|
+
const client = options?.client;
|
|
79
69
|
if (!client) {
|
|
80
|
-
throw new Error('
|
|
70
|
+
throw new Error('this operation was called without a client: get one from createIamClient / createComputeClient / createMonitoringClient / createAssistantClient');
|
|
81
71
|
}
|
|
82
72
|
return client.request(config);
|
|
83
73
|
}
|
|
84
74
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* `compute.listInstances(params, { client })` at every call site is correct and
|
|
88
|
-
* tiring, and the tiring part is what makes somebody eventually forget one — at
|
|
89
|
-
* which point the call silently falls through to the process-wide fallback,
|
|
90
|
-
* which in a multi-service process points at the wrong service.
|
|
91
|
-
*
|
|
92
|
-
* const compute = withClient(sdk.compute, createClient({ baseURL, getToken }));
|
|
93
|
-
* await compute.listInstances({});
|
|
75
|
+
* Attach a client to a whole generated namespace.
|
|
94
76
|
*
|
|
95
77
|
* The binding is by position: orval emits `options` as the last declared
|
|
96
78
|
* parameter of every operation, so `fn.length - 1` is the slot. Arguments the
|
|
97
|
-
* caller omitted are filled with `undefined` to reach it
|
|
98
|
-
*
|
|
79
|
+
* caller omitted are filled with `undefined` to reach it.
|
|
80
|
+
*
|
|
81
|
+
* `fn.length` stops counting at the first parameter that has a default, so that
|
|
82
|
+
* assumption holds only while the generated code has none. It has none today, and
|
|
83
|
+
* `scripts/check-generated-arity.mjs` fails the build if that changes: a slot
|
|
84
|
+
* that silently shifts by one would pass the client as somebody's request body,
|
|
85
|
+
* and no amount of type checking on generated code would catch it.
|
|
99
86
|
*/
|
|
100
|
-
export function
|
|
87
|
+
export function bindClient(api, client) {
|
|
101
88
|
return new Proxy(api, {
|
|
102
89
|
get(target, property, receiver) {
|
|
103
90
|
const value = Reflect.get(target, property, receiver);
|
|
@@ -111,8 +98,7 @@ export function withClient(api, client) {
|
|
|
111
98
|
while (head.length < slot) {
|
|
112
99
|
head.push(undefined);
|
|
113
100
|
}
|
|
114
|
-
|
|
115
|
-
return operation(...head, { ...passed, client });
|
|
101
|
+
return operation(...head, { client });
|
|
116
102
|
};
|
|
117
103
|
},
|
|
118
104
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,61 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { Bound, ClientOptions } from './http.js';
|
|
2
|
+
import * as assistantOperations from './generated/assistant/index.js';
|
|
3
|
+
import * as computeOperations from './generated/compute/index.js';
|
|
4
|
+
import * as iamOperations from './generated/iam/index.js';
|
|
5
|
+
import * as monitoringOperations from './generated/monitoring/index.js';
|
|
6
|
+
export { createClient } from './http.js';
|
|
7
|
+
export type { Client, ClientOptions, TokenProvider } from './http.js';
|
|
3
8
|
export * as assistant from './generated/assistant/index.js';
|
|
4
9
|
export * as compute from './generated/compute/index.js';
|
|
5
10
|
export * as iam from './generated/iam/index.js';
|
|
6
11
|
export * as monitoring from './generated/monitoring/index.js';
|
|
12
|
+
/** Every assistant operation, bound to one address and one token. */
|
|
13
|
+
export type AssistantClient = Bound<typeof assistantOperations>;
|
|
14
|
+
/**
|
|
15
|
+
* A client for assistant.
|
|
16
|
+
*
|
|
17
|
+
* const assistant = createAssistantClient({ baseURL, getToken });
|
|
18
|
+
*
|
|
19
|
+
* Hold one per address, and — where the token belongs to the request
|
|
20
|
+
* rather than to the process — one per request. There is no
|
|
21
|
+
* process-wide alternative; see `createClient`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function createAssistantClient(options: ClientOptions): AssistantClient;
|
|
24
|
+
/** Every compute operation, bound to one address and one token. */
|
|
25
|
+
export type ComputeClient = Bound<typeof computeOperations>;
|
|
26
|
+
/**
|
|
27
|
+
* A client for compute.
|
|
28
|
+
*
|
|
29
|
+
* const compute = createComputeClient({ baseURL, getToken });
|
|
30
|
+
*
|
|
31
|
+
* Hold one per address, and — where the token belongs to the request
|
|
32
|
+
* rather than to the process — one per request. There is no
|
|
33
|
+
* process-wide alternative; see `createClient`.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createComputeClient(options: ClientOptions): ComputeClient;
|
|
36
|
+
/** Every iam operation, bound to one address and one token. */
|
|
37
|
+
export type IamClient = Bound<typeof iamOperations>;
|
|
38
|
+
/**
|
|
39
|
+
* A client for iam.
|
|
40
|
+
*
|
|
41
|
+
* const iam = createIamClient({ baseURL, getToken });
|
|
42
|
+
*
|
|
43
|
+
* Hold one per address, and — where the token belongs to the request
|
|
44
|
+
* rather than to the process — one per request. There is no
|
|
45
|
+
* process-wide alternative; see `createClient`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function createIamClient(options: ClientOptions): IamClient;
|
|
48
|
+
/** Every monitoring operation, bound to one address and one token. */
|
|
49
|
+
export type MonitoringClient = Bound<typeof monitoringOperations>;
|
|
50
|
+
/**
|
|
51
|
+
* A client for monitoring.
|
|
52
|
+
*
|
|
53
|
+
* const monitoring = createMonitoringClient({ baseURL, getToken });
|
|
54
|
+
*
|
|
55
|
+
* Hold one per address, and — where the token belongs to the request
|
|
56
|
+
* rather than to the process — one per request. There is no
|
|
57
|
+
* process-wide alternative; see `createClient`.
|
|
58
|
+
*/
|
|
59
|
+
export declare function createMonitoringClient(options: ClientOptions): MonitoringClient;
|
|
7
60
|
/** Service versions the bundled specs were generated from. */
|
|
8
61
|
export declare const SERVICE_VERSIONS: Record<string, string>;
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,63 @@
|
|
|
1
1
|
// Generated by scripts/build-index.mjs. Do not edit.
|
|
2
|
-
|
|
2
|
+
import { bindClient, createClient } from './http.js';
|
|
3
|
+
import * as assistantOperations from './generated/assistant/index.js';
|
|
4
|
+
import * as computeOperations from './generated/compute/index.js';
|
|
5
|
+
import * as iamOperations from './generated/iam/index.js';
|
|
6
|
+
import * as monitoringOperations from './generated/monitoring/index.js';
|
|
7
|
+
export { createClient } from './http.js';
|
|
8
|
+
/* The models, and the operations as free functions for anyone binding a client themselves. */
|
|
3
9
|
export * as assistant from './generated/assistant/index.js';
|
|
4
10
|
export * as compute from './generated/compute/index.js';
|
|
5
11
|
export * as iam from './generated/iam/index.js';
|
|
6
12
|
export * as monitoring from './generated/monitoring/index.js';
|
|
13
|
+
/**
|
|
14
|
+
* A client for assistant.
|
|
15
|
+
*
|
|
16
|
+
* const assistant = createAssistantClient({ baseURL, getToken });
|
|
17
|
+
*
|
|
18
|
+
* Hold one per address, and — where the token belongs to the request
|
|
19
|
+
* rather than to the process — one per request. There is no
|
|
20
|
+
* process-wide alternative; see `createClient`.
|
|
21
|
+
*/
|
|
22
|
+
export function createAssistantClient(options) {
|
|
23
|
+
return bindClient(assistantOperations, createClient(options));
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A client for compute.
|
|
27
|
+
*
|
|
28
|
+
* const compute = createComputeClient({ baseURL, getToken });
|
|
29
|
+
*
|
|
30
|
+
* Hold one per address, and — where the token belongs to the request
|
|
31
|
+
* rather than to the process — one per request. There is no
|
|
32
|
+
* process-wide alternative; see `createClient`.
|
|
33
|
+
*/
|
|
34
|
+
export function createComputeClient(options) {
|
|
35
|
+
return bindClient(computeOperations, createClient(options));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A client for iam.
|
|
39
|
+
*
|
|
40
|
+
* const iam = createIamClient({ baseURL, getToken });
|
|
41
|
+
*
|
|
42
|
+
* Hold one per address, and — where the token belongs to the request
|
|
43
|
+
* rather than to the process — one per request. There is no
|
|
44
|
+
* process-wide alternative; see `createClient`.
|
|
45
|
+
*/
|
|
46
|
+
export function createIamClient(options) {
|
|
47
|
+
return bindClient(iamOperations, createClient(options));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* A client for monitoring.
|
|
51
|
+
*
|
|
52
|
+
* const monitoring = createMonitoringClient({ baseURL, getToken });
|
|
53
|
+
*
|
|
54
|
+
* Hold one per address, and — where the token belongs to the request
|
|
55
|
+
* rather than to the process — one per request. There is no
|
|
56
|
+
* process-wide alternative; see `createClient`.
|
|
57
|
+
*/
|
|
58
|
+
export function createMonitoringClient(options) {
|
|
59
|
+
return bindClient(monitoringOperations, createClient(options));
|
|
60
|
+
}
|
|
7
61
|
/** Service versions the bundled specs were generated from. */
|
|
8
62
|
export const SERVICE_VERSIONS = {
|
|
9
63
|
assistant: "v2.1.1",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leaflow/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "TypeScript SDK for the Leaflow platform APIs",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
},
|
|
23
23
|
"scripts": {
|
|
24
24
|
"generate": "node scripts/clean-generated.mjs && orval --config orval.config.ts",
|
|
25
|
-
"build": "pnpm generate && node scripts/fix-generated-imports.mjs && node scripts/build-index.mjs && tsc -p tsconfig.build.json",
|
|
26
|
-
"typecheck": "pnpm generate && node scripts/fix-generated-imports.mjs && node scripts/build-index.mjs && tsc -p tsconfig.json --noEmit"
|
|
25
|
+
"build": "pnpm generate && node scripts/fix-generated-imports.mjs && node scripts/check-generated-arity.mjs && node scripts/build-index.mjs && tsc -p tsconfig.build.json",
|
|
26
|
+
"typecheck": "pnpm generate && node scripts/fix-generated-imports.mjs && node scripts/check-generated-arity.mjs && node scripts/build-index.mjs && tsc -p tsconfig.json --noEmit"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"axios": "^1.7.9"
|