@toshicloud/sdk 0.0.0-stage → 0.2.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/LICENSE +202 -0
- package/README.md +201 -2
- package/dist/client.d.ts +78 -0
- package/dist/client.js +42 -0
- package/dist/errors.d.ts +20 -0
- package/dist/errors.js +55 -0
- package/dist/execute.d.ts +6 -0
- package/dist/execute.js +380 -0
- package/dist/gen/toshi/workspace/v1/artifacts_pb.d.ts +841 -0
- package/dist/gen/toshi/workspace/v1/artifacts_pb.js +96 -0
- package/dist/gen/toshi/workspace/v1/display_pb.d.ts +1142 -0
- package/dist/gen/toshi/workspace/v1/display_pb.js +214 -0
- package/dist/gen/toshi/workspace/v1/projects_pb.d.ts +1386 -0
- package/dist/gen/toshi/workspace/v1/projects_pb.js +207 -0
- package/dist/gen/toshi/workspace/v1/recordings_pb.d.ts +948 -0
- package/dist/gen/toshi/workspace/v1/recordings_pb.js +201 -0
- package/dist/gen/toshi/workspace/v1/secrets_pb.d.ts +1062 -0
- package/dist/gen/toshi/workspace/v1/secrets_pb.js +182 -0
- package/dist/gen/toshi/workspace/v1/volumes_pb.d.ts +406 -0
- package/dist/gen/toshi/workspace/v1/volumes_pb.js +107 -0
- package/dist/gen/toshi/workspace/v1/webhook_pb.d.ts +974 -0
- package/dist/gen/toshi/workspace/v1/webhook_pb.js +153 -0
- package/dist/gen/toshi/workspace/v1/workspace_pb.d.ts +6133 -0
- package/dist/gen/toshi/workspace/v1/workspace_pb.js +892 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +9 -0
- package/dist/interceptors.d.ts +10 -0
- package/dist/interceptors.js +159 -0
- package/dist/output.d.ts +6 -0
- package/dist/output.js +77 -0
- package/dist/retry.d.ts +42 -0
- package/dist/retry.js +213 -0
- package/dist/stream.d.ts +21 -0
- package/dist/stream.js +84 -0
- package/dist/wait.d.ts +8 -0
- package/dist/wait.js +29 -0
- package/dist/watch.d.ts +8 -0
- package/dist/watch.js +224 -0
- package/package.json +47 -4
package/dist/wait.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { abortReason } from "./retry.js";
|
|
2
|
+
/** Asks again while reached is false, and returns the response when it is (wait.go:11-27). */
|
|
3
|
+
export async function waitWorkload(workspace, req, opts) {
|
|
4
|
+
for (;;) {
|
|
5
|
+
throwIfAborted(opts);
|
|
6
|
+
const resp = await workspace.waitWorkload(req, opts);
|
|
7
|
+
if (resp.reached) {
|
|
8
|
+
return resp;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/** Asks again while the operation is not done, and returns it when it is (wait.go:29-45). */
|
|
13
|
+
export async function waitOperation(workspace, req, opts) {
|
|
14
|
+
for (;;) {
|
|
15
|
+
throwIfAborted(opts);
|
|
16
|
+
const resp = await workspace.waitOperation(req, opts);
|
|
17
|
+
const op = resp.operation;
|
|
18
|
+
if (op !== undefined && op.done) {
|
|
19
|
+
return op;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// wait.go:16-18: the caller's context ends the loop with its own error.
|
|
24
|
+
function throwIfAborted(opts) {
|
|
25
|
+
const signal = opts?.signal;
|
|
26
|
+
if (signal?.aborted === true) {
|
|
27
|
+
throw abortReason(signal);
|
|
28
|
+
}
|
|
29
|
+
}
|
package/dist/watch.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Client } from "@connectrpc/connect";
|
|
2
|
+
import type { CallOptions, EventWatch, WorkloadWatch } from "./client.js";
|
|
3
|
+
import type { WatchEventsRequest, WatchWorkloadsRequest, WorkspaceService } from "./gen/toshi/workspace/v1/workspace_pb.js";
|
|
4
|
+
import type { RetrySetting } from "./retry.js";
|
|
5
|
+
/** Starts a workload watch; with an empty cursor it lists the workloads first (watch.go:40-84). */
|
|
6
|
+
export declare function openWorkloadWatch(workspace: Client<typeof WorkspaceService>, retry: RetrySetting, req: WatchWorkloadsRequest, opts?: CallOptions): Promise<WorkloadWatch>;
|
|
7
|
+
/** Starts an event watch; with an empty cursor it pages ListEvents as the caller iterates (watch.go:261-283). */
|
|
8
|
+
export declare function openEventWatch(workspace: Client<typeof WorkspaceService>, retry: RetrySetting, req: WatchEventsRequest, opts?: CallOptions): Promise<EventWatch>;
|
package/dist/watch.js
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { clone, create } from "@bufbuild/protobuf";
|
|
2
|
+
import { ListEventsRequestSchema, ListWorkloadsRequestSchema, WatchEventsRequestSchema, WatchWorkloadsRequestSchema, } from "./gen/toshi/workspace/v1/workspace_pb.js";
|
|
3
|
+
import { READER_TAKEN, WatchState, createReader, reconnect, streamEnded } from "./stream.js";
|
|
4
|
+
/** Starts a workload watch; with an empty cursor it lists the workloads first (watch.go:40-84). */
|
|
5
|
+
export async function openWorkloadWatch(workspace, retry, req, opts) {
|
|
6
|
+
const state = new WatchState(req.cursor, opts?.signal);
|
|
7
|
+
let snapshot;
|
|
8
|
+
if (req.cursor === "") {
|
|
9
|
+
try {
|
|
10
|
+
const listed = await listWorkloads(workspace, req, state);
|
|
11
|
+
snapshot = listed.workloads;
|
|
12
|
+
state.cursor = listed.cursor;
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
state.close();
|
|
16
|
+
throw err;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const request = clone(WatchWorkloadsRequestSchema, req);
|
|
20
|
+
request.cursor = state.cursor;
|
|
21
|
+
request.keepalive = true;
|
|
22
|
+
let reader;
|
|
23
|
+
return {
|
|
24
|
+
get cursor() {
|
|
25
|
+
return state.cursor;
|
|
26
|
+
},
|
|
27
|
+
get snapshot() {
|
|
28
|
+
return snapshot;
|
|
29
|
+
},
|
|
30
|
+
close() {
|
|
31
|
+
state.close();
|
|
32
|
+
},
|
|
33
|
+
[Symbol.asyncIterator]() {
|
|
34
|
+
if (reader !== undefined) {
|
|
35
|
+
throw new Error(READER_TAKEN);
|
|
36
|
+
}
|
|
37
|
+
reader = createReader(() => runWorkloads(workspace, retry, request, state), () => state.close());
|
|
38
|
+
return reader;
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** Starts an event watch; with an empty cursor it pages ListEvents as the caller iterates (watch.go:261-283). */
|
|
43
|
+
export function openEventWatch(workspace, retry, req, opts) {
|
|
44
|
+
const state = new WatchState(req.cursor, opts?.signal);
|
|
45
|
+
const request = clone(WatchEventsRequestSchema, req);
|
|
46
|
+
request.keepalive = true;
|
|
47
|
+
const list = createListEventsRequest(req);
|
|
48
|
+
const listing = req.cursor === "";
|
|
49
|
+
let reader;
|
|
50
|
+
const watch = {
|
|
51
|
+
get cursor() {
|
|
52
|
+
return state.cursor;
|
|
53
|
+
},
|
|
54
|
+
close() {
|
|
55
|
+
state.close();
|
|
56
|
+
},
|
|
57
|
+
[Symbol.asyncIterator]() {
|
|
58
|
+
if (reader !== undefined) {
|
|
59
|
+
throw new Error(READER_TAKEN);
|
|
60
|
+
}
|
|
61
|
+
reader = createReader(() => runEvents(workspace, retry, request, list, listing, state), () => state.close());
|
|
62
|
+
return reader;
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
return Promise.resolve(watch);
|
|
66
|
+
}
|
|
67
|
+
// watch.go:65-84: every page of the list returns the event log's position.
|
|
68
|
+
async function listWorkloads(workspace, req, state) {
|
|
69
|
+
const list = createListWorkloadsRequest(req);
|
|
70
|
+
const workloads = [];
|
|
71
|
+
for (;;) {
|
|
72
|
+
const page = await workspace.listWorkloads(list, { signal: state.signal });
|
|
73
|
+
workloads.push(...page.workloads);
|
|
74
|
+
if (page.nextPageToken === "") {
|
|
75
|
+
return { workloads, cursor: page.cursor };
|
|
76
|
+
}
|
|
77
|
+
list.pageToken = page.nextPageToken;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function createListWorkloadsRequest(req) {
|
|
81
|
+
return create(ListWorkloadsRequestSchema, {
|
|
82
|
+
project: req.project,
|
|
83
|
+
environment: req.environment,
|
|
84
|
+
kinds: req.kinds,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function createListEventsRequest(req) {
|
|
88
|
+
return create(ListEventsRequestSchema, {
|
|
89
|
+
project: req.project,
|
|
90
|
+
environment: req.environment,
|
|
91
|
+
workloadId: req.workloadId,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async function* runWorkloads(workspace, retry, request, state) {
|
|
95
|
+
let attempt = 0;
|
|
96
|
+
try {
|
|
97
|
+
for (;;) {
|
|
98
|
+
if (state.closed) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
let cause;
|
|
102
|
+
try {
|
|
103
|
+
request.cursor = state.cursor;
|
|
104
|
+
const stream = workspace.watchWorkloads(request, { signal: state.signal });
|
|
105
|
+
for await (const msg of stream) {
|
|
106
|
+
attempt = 0;
|
|
107
|
+
if (msg.keepalive !== undefined) {
|
|
108
|
+
if (msg.keepalive.cursor !== "") {
|
|
109
|
+
state.cursor = msg.keepalive.cursor;
|
|
110
|
+
}
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (msg.cursor !== "") {
|
|
114
|
+
state.cursor = msg.cursor;
|
|
115
|
+
}
|
|
116
|
+
yield msg;
|
|
117
|
+
}
|
|
118
|
+
if (state.closed) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
cause = streamEnded;
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
if (state.closed) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
cause = err;
|
|
128
|
+
}
|
|
129
|
+
attempt = await reconnect(retry, attempt, cause, state);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
if (state.closed) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
throw err;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async function* runEvents(workspace, retry, request, list, listingAtStart, state) {
|
|
140
|
+
let attempt = 0;
|
|
141
|
+
let page = [];
|
|
142
|
+
let pageIndex = 0;
|
|
143
|
+
let listing = listingAtStart;
|
|
144
|
+
// watch.go:305-317: resume is the position the next stream opens at, separate
|
|
145
|
+
// from the cursor the caller sees, which only moves on events handed out and
|
|
146
|
+
// keepalives consumed.
|
|
147
|
+
let resume = state.cursor;
|
|
148
|
+
try {
|
|
149
|
+
for (;;) {
|
|
150
|
+
if (state.closed) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
let cause;
|
|
154
|
+
try {
|
|
155
|
+
if (listing) {
|
|
156
|
+
if (pageIndex < page.length) {
|
|
157
|
+
const event = page[pageIndex];
|
|
158
|
+
pageIndex += 1;
|
|
159
|
+
attempt = 0;
|
|
160
|
+
if (event.cursor !== "") {
|
|
161
|
+
state.cursor = event.cursor;
|
|
162
|
+
resume = event.cursor;
|
|
163
|
+
}
|
|
164
|
+
yield event;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const listed = await workspace.listEvents(list, { signal: state.signal });
|
|
168
|
+
page = listed.events;
|
|
169
|
+
pageIndex = 0;
|
|
170
|
+
if (listed.cursor !== "") {
|
|
171
|
+
list.cursor = listed.cursor;
|
|
172
|
+
}
|
|
173
|
+
if (page.length > 0) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (listed.cursor !== "") {
|
|
177
|
+
resume = listed.cursor;
|
|
178
|
+
}
|
|
179
|
+
listing = false;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
request.cursor = resume;
|
|
183
|
+
const stream = workspace.watchEvents(request, { signal: state.signal });
|
|
184
|
+
for await (const msg of stream) {
|
|
185
|
+
attempt = 0;
|
|
186
|
+
const keepalive = msg.keepalive;
|
|
187
|
+
if (keepalive !== undefined) {
|
|
188
|
+
if (keepalive.cursor !== "") {
|
|
189
|
+
state.cursor = keepalive.cursor;
|
|
190
|
+
resume = keepalive.cursor;
|
|
191
|
+
}
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const event = msg.event;
|
|
195
|
+
if (event === undefined) {
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (event.cursor !== "") {
|
|
199
|
+
state.cursor = event.cursor;
|
|
200
|
+
resume = event.cursor;
|
|
201
|
+
}
|
|
202
|
+
yield event;
|
|
203
|
+
}
|
|
204
|
+
if (state.closed) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
cause = streamEnded;
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
if (state.closed) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
cause = err;
|
|
214
|
+
}
|
|
215
|
+
attempt = await reconnect(retry, attempt, cause, state);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
if (state.closed) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
throw err;
|
|
223
|
+
}
|
|
224
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toshicloud/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "TypeScript client for the Toshi Workspace API",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"packageManager": "pnpm@12.15.0+sha512.b3951d6d6ff4a9f13dc7cf56cd28d2b8eb37dbcc9c68e52e1e1191873ea9f82a017287ab50baadc5ccc494f036c67c0e47eb13608c2fa36f47711b61ea128e61",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=24.22 <25"
|
|
22
|
+
},
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/toshicloud/toshi-ts.git"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
29
|
+
"typecheck": "tsc --noEmit",
|
|
30
|
+
"lint": "eslint .",
|
|
31
|
+
"test": "vitest run --coverage",
|
|
32
|
+
"generate": "scripts/generate.sh",
|
|
33
|
+
"prepack": "pnpm run build"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@bufbuild/protobuf": "^2.16.0",
|
|
37
|
+
"@connectrpc/connect": "^2.2.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@connectrpc/connect-node": "2.2.0",
|
|
41
|
+
"@eslint/js": "10.0.1",
|
|
42
|
+
"@types/node": "26.6.5",
|
|
43
|
+
"@vitest/coverage-v8": "5.0.5",
|
|
44
|
+
"eslint": "10.13.0",
|
|
45
|
+
"typescript": "6.0.3",
|
|
46
|
+
"typescript-eslint": "8.72.0",
|
|
47
|
+
"vitest": "5.0.5"
|
|
48
|
+
}
|
|
49
|
+
}
|