@toshicloud/sdk 0.2.0 → 0.3.1
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 +74 -18
- package/dist/client.d.ts +111 -18
- package/dist/client.js +82 -16
- package/dist/errors.d.ts +9 -1
- package/dist/errors.js +22 -0
- package/dist/execute.d.ts +3 -2
- package/dist/execute.js +3 -3
- package/dist/gen/toshi/workspace/v1/artifacts_pb.d.ts +25 -23
- package/dist/gen/toshi/workspace/v1/artifacts_pb.js +7 -7
- package/dist/gen/toshi/workspace/v1/audit_events_pb.d.ts +297 -0
- package/dist/gen/toshi/workspace/v1/audit_events_pb.js +37 -0
- package/dist/gen/toshi/workspace/v1/channels_pb.d.ts +2713 -0
- package/dist/gen/toshi/workspace/v1/channels_pb.js +336 -0
- package/dist/gen/toshi/workspace/v1/display_pb.d.ts +35 -11
- package/dist/gen/toshi/workspace/v1/display_pb.js +2 -2
- package/dist/gen/toshi/workspace/v1/fillers_pb.d.ts +920 -0
- package/dist/gen/toshi/workspace/v1/fillers_pb.js +94 -0
- package/dist/gen/toshi/workspace/v1/hostjobs_pb.d.ts +2048 -0
- package/dist/gen/toshi/workspace/v1/hostjobs_pb.js +226 -0
- package/dist/gen/toshi/workspace/v1/hosts_pb.d.ts +2135 -0
- package/dist/gen/toshi/workspace/v1/hosts_pb.js +301 -0
- package/dist/gen/toshi/workspace/v1/projects_pb.d.ts +157 -3
- package/dist/gen/toshi/workspace/v1/projects_pb.js +2 -2
- package/dist/gen/toshi/workspace/v1/recordings_pb.d.ts +76 -0
- package/dist/gen/toshi/workspace/v1/recordings_pb.js +1 -1
- package/dist/gen/toshi/workspace/v1/secrets_pb.d.ts +175 -23
- package/dist/gen/toshi/workspace/v1/secrets_pb.js +9 -9
- package/dist/gen/toshi/workspace/v1/storage_providers_pb.d.ts +717 -0
- package/dist/gen/toshi/workspace/v1/storage_providers_pb.js +74 -0
- package/dist/gen/toshi/workspace/v1/tokens_pb.d.ts +518 -0
- package/dist/gen/toshi/workspace/v1/tokens_pb.js +88 -0
- package/dist/gen/toshi/workspace/v1/volumes_pb.d.ts +39 -1
- package/dist/gen/toshi/workspace/v1/volumes_pb.js +2 -2
- package/dist/gen/toshi/workspace/v1/webhook_pb.d.ts +62 -18
- package/dist/gen/toshi/workspace/v1/webhook_pb.js +3 -3
- package/dist/gen/toshi/workspace/v1/workloads_pb.d.ts +2858 -0
- package/dist/gen/toshi/workspace/v1/workloads_pb.js +261 -0
- package/dist/gen/toshi/workspace/v1/workspace_pb.d.ts +1377 -2130
- package/dist/gen/toshi/workspace/v1/workspace_pb.js +235 -272
- package/dist/gen/toshi/workspace/v1/workspace_service_pb.d.ts +3719 -0
- package/dist/gen/toshi/workspace/v1/workspace_service_pb.js +270 -0
- package/dist/index.d.ts +14 -2
- package/dist/index.js +13 -1
- package/dist/output.d.ts +3 -2
- package/dist/output.js +2 -2
- package/dist/retry.js +116 -30
- package/dist/run.d.ts +10 -0
- package/dist/run.js +175 -0
- package/dist/wait.d.ts +10 -5
- package/dist/wait.js +23 -11
- package/dist/watch.d.ts +14 -6
- package/dist/watch.js +138 -85
- package/package.json +1 -1
package/dist/run.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { create } from "@bufbuild/protobuf";
|
|
2
|
+
import { JobExitError } from "./errors.js";
|
|
3
|
+
import { OutputEndSchema, WatchOutputRequestSchema } from "./gen/toshi/workspace/v1/workspace_pb.js";
|
|
4
|
+
import { EndReason } from "./gen/toshi/workspace/v1/workspace_pb.js";
|
|
5
|
+
import { openOutputWatch } from "./output.js";
|
|
6
|
+
/**
|
|
7
|
+
* Starts a job and follows its output, as `toshi run` does: CreateJob, then
|
|
8
|
+
* WatchOutput from the first byte. A job whose create answered its output
|
|
9
|
+
* (wait_for_end was set) is returned finished.
|
|
10
|
+
*/
|
|
11
|
+
export async function runJob(service, retry, req, opts) {
|
|
12
|
+
const created = await service.createJob(req, opts);
|
|
13
|
+
const job = created.job;
|
|
14
|
+
if (job === undefined) {
|
|
15
|
+
throw new Error("toshi: CreateJob answered no job");
|
|
16
|
+
}
|
|
17
|
+
if (created.output !== undefined) {
|
|
18
|
+
return new FinishedRun(job, created.output);
|
|
19
|
+
}
|
|
20
|
+
const watch = await openOutputWatch(service, retry, create(WatchOutputRequestSchema, {
|
|
21
|
+
project: req.project,
|
|
22
|
+
environment: req.environment,
|
|
23
|
+
id: job.id,
|
|
24
|
+
}), opts);
|
|
25
|
+
return new LiveRun(job, watch);
|
|
26
|
+
}
|
|
27
|
+
/** A run whose create answered the job's output instead of a live stream. */
|
|
28
|
+
class FinishedRun {
|
|
29
|
+
job;
|
|
30
|
+
output;
|
|
31
|
+
queue;
|
|
32
|
+
constructor(job, output) {
|
|
33
|
+
this.job = job;
|
|
34
|
+
this.output = output;
|
|
35
|
+
}
|
|
36
|
+
[Symbol.asyncIterator]() {
|
|
37
|
+
this.queue ??= chunksOf(this.output);
|
|
38
|
+
const queue = this.queue;
|
|
39
|
+
return {
|
|
40
|
+
next: () => {
|
|
41
|
+
const chunk = queue.shift();
|
|
42
|
+
return Promise.resolve(chunk === undefined ? { done: true, value: undefined } : { done: false, value: chunk });
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
wait() {
|
|
47
|
+
const end = create(OutputEndSchema, {
|
|
48
|
+
exitCode: this.job.status?.exitCode,
|
|
49
|
+
cancelled: this.job.status?.endReason === EndReason.CANCELLED,
|
|
50
|
+
truncated: this.output.truncated,
|
|
51
|
+
});
|
|
52
|
+
if (end.exitCode === 0) {
|
|
53
|
+
return Promise.resolve();
|
|
54
|
+
}
|
|
55
|
+
return Promise.reject(new JobExitError(this.job.id, end));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** A run whose output the server streams while the job runs. */
|
|
59
|
+
class LiveRun {
|
|
60
|
+
job;
|
|
61
|
+
watch;
|
|
62
|
+
responses;
|
|
63
|
+
queue = [];
|
|
64
|
+
end;
|
|
65
|
+
failure;
|
|
66
|
+
readerOwned = false;
|
|
67
|
+
waitPromise;
|
|
68
|
+
constructor(job, watch) {
|
|
69
|
+
this.job = job;
|
|
70
|
+
this.watch = watch;
|
|
71
|
+
this.responses = watch[Symbol.asyncIterator]();
|
|
72
|
+
}
|
|
73
|
+
[Symbol.asyncIterator]() {
|
|
74
|
+
if (this.readerOwned) {
|
|
75
|
+
throw new Error("toshi: this run already has an output reader");
|
|
76
|
+
}
|
|
77
|
+
this.readerOwned = true;
|
|
78
|
+
return {
|
|
79
|
+
next: () => this.pull(),
|
|
80
|
+
// A break ends only this reader: the job keeps running.
|
|
81
|
+
return: () => {
|
|
82
|
+
this.watch.close();
|
|
83
|
+
return Promise.resolve({ done: true, value: undefined });
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
wait() {
|
|
88
|
+
this.waitPromise ??= this.awaitOutcome();
|
|
89
|
+
return this.waitPromise;
|
|
90
|
+
}
|
|
91
|
+
async awaitOutcome() {
|
|
92
|
+
if (!this.readerOwned) {
|
|
93
|
+
// No reader owns the output: take it and drain it, so wait() never stalls.
|
|
94
|
+
this.readerOwned = true;
|
|
95
|
+
try {
|
|
96
|
+
for (;;) {
|
|
97
|
+
const result = await this.pull();
|
|
98
|
+
if (result.done === true) {
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// pull() recorded the outcome; it is reported below.
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
for (;;) {
|
|
108
|
+
if (this.failure !== undefined) {
|
|
109
|
+
throw this.failure;
|
|
110
|
+
}
|
|
111
|
+
if (this.end !== undefined) {
|
|
112
|
+
if (this.end.exitCode === 0 && !this.end.cancelled) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
throw new JobExitError(this.job.id, this.end);
|
|
116
|
+
}
|
|
117
|
+
await this.readOne();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async pull() {
|
|
121
|
+
for (;;) {
|
|
122
|
+
const chunk = this.queue.shift();
|
|
123
|
+
if (chunk !== undefined) {
|
|
124
|
+
return { done: false, value: chunk };
|
|
125
|
+
}
|
|
126
|
+
if (this.failure !== undefined) {
|
|
127
|
+
throw this.failure;
|
|
128
|
+
}
|
|
129
|
+
if (this.end !== undefined) {
|
|
130
|
+
return { done: true, value: undefined };
|
|
131
|
+
}
|
|
132
|
+
await this.readOne();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async readOne() {
|
|
136
|
+
let result;
|
|
137
|
+
try {
|
|
138
|
+
result = await this.responses.next();
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
this.failure = err instanceof Error ? err : new Error(String(err));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (result.done === true) {
|
|
145
|
+
this.failure = new Error("toshi: the output stream ended without its end message");
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const message = result.value.message;
|
|
149
|
+
switch (message.case) {
|
|
150
|
+
case "stdout":
|
|
151
|
+
this.queue.push({ stream: "stdout", data: message.value });
|
|
152
|
+
return;
|
|
153
|
+
case "stderr":
|
|
154
|
+
this.queue.push({ stream: "stderr", data: message.value });
|
|
155
|
+
return;
|
|
156
|
+
case "end":
|
|
157
|
+
this.end = message.value;
|
|
158
|
+
// The watch has nothing left to send; release the RPC.
|
|
159
|
+
this.watch.close();
|
|
160
|
+
return;
|
|
161
|
+
default:
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function chunksOf(output) {
|
|
167
|
+
const chunks = [];
|
|
168
|
+
if (output.stdout.length > 0) {
|
|
169
|
+
chunks.push({ stream: "stdout", data: output.stdout });
|
|
170
|
+
}
|
|
171
|
+
if (output.stderr.length > 0) {
|
|
172
|
+
chunks.push({ stream: "stderr", data: output.stderr });
|
|
173
|
+
}
|
|
174
|
+
return chunks;
|
|
175
|
+
}
|
package/dist/wait.d.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import type { Client } from "@connectrpc/connect";
|
|
2
2
|
import type { CallOptions } from "./client.js";
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
import type { WorkloadService } from "./gen/toshi/workspace/v1/workloads_pb.js";
|
|
4
|
+
import type { WaitJobRequest, WaitJobResponse, WaitSandboxRequest, WaitSandboxResponse, WaitServiceRequest, WaitServiceResponse } from "./gen/toshi/workspace/v1/workloads_pb.js";
|
|
5
|
+
import type { Operation, WaitOperationRequest } from "./gen/toshi/workspace/v1/workspace_pb.js";
|
|
6
|
+
/** Asks again while the job is not in a state it waits for, and returns the answer when it is. */
|
|
7
|
+
export declare function waitJob(service: Client<typeof WorkloadService>, req: WaitJobRequest, opts?: CallOptions): Promise<WaitJobResponse>;
|
|
8
|
+
/** Asks again while the sandbox is not in a state it waits for. */
|
|
9
|
+
export declare function waitSandbox(service: Client<typeof WorkloadService>, req: WaitSandboxRequest, opts?: CallOptions): Promise<WaitSandboxResponse>;
|
|
10
|
+
/** Asks again while the service is not in a state it waits for. */
|
|
11
|
+
export declare function waitService(service: Client<typeof WorkloadService>, req: WaitServiceRequest, opts?: CallOptions): Promise<WaitServiceResponse>;
|
|
7
12
|
/** Asks again while the operation is not done, and returns it when it is (wait.go:29-45). */
|
|
8
|
-
export declare function waitOperation(
|
|
13
|
+
export declare function waitOperation(service: Client<typeof WorkloadService>, req: WaitOperationRequest, opts?: CallOptions): Promise<Operation>;
|
package/dist/wait.js
CHANGED
|
@@ -1,25 +1,37 @@
|
|
|
1
1
|
import { abortReason } from "./retry.js";
|
|
2
|
-
/** Asks again while
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
/** Asks again while the job is not in a state it waits for, and returns the answer when it is. */
|
|
3
|
+
export function waitJob(service, req, opts) {
|
|
4
|
+
return waitReached(() => service.waitJob(req, opts), opts);
|
|
5
|
+
}
|
|
6
|
+
/** Asks again while the sandbox is not in a state it waits for. */
|
|
7
|
+
export function waitSandbox(service, req, opts) {
|
|
8
|
+
return waitReached(() => service.waitSandbox(req, opts), opts);
|
|
9
|
+
}
|
|
10
|
+
/** Asks again while the service is not in a state it waits for. */
|
|
11
|
+
export function waitService(service, req, opts) {
|
|
12
|
+
return waitReached(() => service.waitService(req, opts), opts);
|
|
11
13
|
}
|
|
12
14
|
/** Asks again while the operation is not done, and returns it when it is (wait.go:29-45). */
|
|
13
|
-
export async function waitOperation(
|
|
15
|
+
export async function waitOperation(service, req, opts) {
|
|
14
16
|
for (;;) {
|
|
15
17
|
throwIfAborted(opts);
|
|
16
|
-
const resp = await
|
|
18
|
+
const resp = await service.waitOperation(req, opts);
|
|
17
19
|
const op = resp.operation;
|
|
18
20
|
if (op !== undefined && op.done) {
|
|
19
21
|
return op;
|
|
20
22
|
}
|
|
21
23
|
}
|
|
22
24
|
}
|
|
25
|
+
// The three waits share one loop: each answers reached when the state arrived.
|
|
26
|
+
async function waitReached(call, opts) {
|
|
27
|
+
for (;;) {
|
|
28
|
+
throwIfAborted(opts);
|
|
29
|
+
const resp = await call();
|
|
30
|
+
if (resp.reached) {
|
|
31
|
+
return resp;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
23
35
|
// wait.go:16-18: the caller's context ends the loop with its own error.
|
|
24
36
|
function throwIfAborted(opts) {
|
|
25
37
|
const signal = opts?.signal;
|
package/dist/watch.d.ts
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import type { Client } from "@connectrpc/connect";
|
|
2
|
-
import type { CallOptions, EventWatch,
|
|
3
|
-
import type {
|
|
2
|
+
import type { CallOptions, EventWatch, JobWatch, SandboxWatch, ServiceWatch } from "./client.js";
|
|
3
|
+
import type { WatchJobsRequest, WatchSandboxesRequest, WatchServicesRequest, WorkloadService } from "./gen/toshi/workspace/v1/workloads_pb.js";
|
|
4
|
+
import type { WatchEventsRequest } from "./gen/toshi/workspace/v1/workspace_pb.js";
|
|
4
5
|
import type { RetrySetting } from "./retry.js";
|
|
5
|
-
/** Starts a
|
|
6
|
-
export declare function
|
|
7
|
-
/** Starts
|
|
8
|
-
export declare function
|
|
6
|
+
/** Starts a job watch; with an empty cursor it lists the jobs first. */
|
|
7
|
+
export declare function openJobWatch(service: Client<typeof WorkloadService>, retry: RetrySetting, req: WatchJobsRequest, opts?: CallOptions): Promise<JobWatch>;
|
|
8
|
+
/** Starts a sandbox watch; with an empty cursor it lists the sandboxes first. */
|
|
9
|
+
export declare function openSandboxWatch(service: Client<typeof WorkloadService>, retry: RetrySetting, req: WatchSandboxesRequest, opts?: CallOptions): Promise<SandboxWatch>;
|
|
10
|
+
/** Starts a service watch; with an empty cursor it lists the services first. */
|
|
11
|
+
export declare function openServiceWatch(service: Client<typeof WorkloadService>, retry: RetrySetting, req: WatchServicesRequest, opts?: CallOptions): Promise<ServiceWatch>;
|
|
12
|
+
/**
|
|
13
|
+
* Starts an event watch; with an empty cursor it pages the event log as the
|
|
14
|
+
* caller iterates. Every message it yields is the stream's response.
|
|
15
|
+
*/
|
|
16
|
+
export declare function openEventWatch(service: Client<typeof WorkloadService>, retry: RetrySetting, req: WatchEventsRequest, opts?: CallOptions): Promise<EventWatch>;
|
package/dist/watch.js
CHANGED
|
@@ -1,14 +1,89 @@
|
|
|
1
1
|
import { clone, create } from "@bufbuild/protobuf";
|
|
2
|
-
import {
|
|
2
|
+
import { ListJobsRequestSchema, ListSandboxesRequestSchema, ListServicesRequestSchema, WatchJobsRequestSchema, WatchSandboxesRequestSchema, WatchServicesRequestSchema, } from "./gen/toshi/workspace/v1/workloads_pb.js";
|
|
3
|
+
import { ListEventsRequestSchema, WatchEventsRequestSchema, WatchEventsResponseSchema, } from "./gen/toshi/workspace/v1/workspace_pb.js";
|
|
3
4
|
import { READER_TAKEN, WatchState, createReader, reconnect, streamEnded } from "./stream.js";
|
|
4
|
-
/** Starts a
|
|
5
|
-
export
|
|
6
|
-
const
|
|
5
|
+
/** Starts a job watch; with an empty cursor it lists the jobs first. */
|
|
6
|
+
export function openJobWatch(service, retry, req, opts) {
|
|
7
|
+
const request = clone(WatchJobsRequestSchema, req);
|
|
8
|
+
return openWatch({
|
|
9
|
+
request,
|
|
10
|
+
list: async (signal) => {
|
|
11
|
+
const list = create(ListJobsRequestSchema, {
|
|
12
|
+
project: request.project,
|
|
13
|
+
environment: request.environment,
|
|
14
|
+
});
|
|
15
|
+
const items = [];
|
|
16
|
+
for (;;) {
|
|
17
|
+
const page = await service.listJobs(list, { signal });
|
|
18
|
+
items.push(...page.jobs);
|
|
19
|
+
if (page.nextPageToken === "") {
|
|
20
|
+
return { items, cursor: page.cursor };
|
|
21
|
+
}
|
|
22
|
+
list.pageToken = page.nextPageToken;
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
watch: (signal) => service.watchJobs(request, { signal }),
|
|
26
|
+
cursor: (message) => message.cursor,
|
|
27
|
+
keepalive: (message) => message.keepalive,
|
|
28
|
+
}, retry, opts);
|
|
29
|
+
}
|
|
30
|
+
/** Starts a sandbox watch; with an empty cursor it lists the sandboxes first. */
|
|
31
|
+
export function openSandboxWatch(service, retry, req, opts) {
|
|
32
|
+
const request = clone(WatchSandboxesRequestSchema, req);
|
|
33
|
+
return openWatch({
|
|
34
|
+
request,
|
|
35
|
+
list: async (signal) => {
|
|
36
|
+
const list = create(ListSandboxesRequestSchema, {
|
|
37
|
+
project: request.project,
|
|
38
|
+
environment: request.environment,
|
|
39
|
+
});
|
|
40
|
+
const items = [];
|
|
41
|
+
for (;;) {
|
|
42
|
+
const page = await service.listSandboxes(list, { signal });
|
|
43
|
+
items.push(...page.sandboxes);
|
|
44
|
+
if (page.nextPageToken === "") {
|
|
45
|
+
return { items, cursor: page.cursor };
|
|
46
|
+
}
|
|
47
|
+
list.pageToken = page.nextPageToken;
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
watch: (signal) => service.watchSandboxes(request, { signal }),
|
|
51
|
+
cursor: (message) => message.cursor,
|
|
52
|
+
keepalive: (message) => message.keepalive,
|
|
53
|
+
}, retry, opts);
|
|
54
|
+
}
|
|
55
|
+
/** Starts a service watch; with an empty cursor it lists the services first. */
|
|
56
|
+
export function openServiceWatch(service, retry, req, opts) {
|
|
57
|
+
const request = clone(WatchServicesRequestSchema, req);
|
|
58
|
+
return openWatch({
|
|
59
|
+
request,
|
|
60
|
+
list: async (signal) => {
|
|
61
|
+
const list = create(ListServicesRequestSchema, {
|
|
62
|
+
project: request.project,
|
|
63
|
+
environment: request.environment,
|
|
64
|
+
});
|
|
65
|
+
const items = [];
|
|
66
|
+
for (;;) {
|
|
67
|
+
const page = await service.listServices(list, { signal });
|
|
68
|
+
items.push(...page.services);
|
|
69
|
+
if (page.nextPageToken === "") {
|
|
70
|
+
return { items, cursor: page.cursor };
|
|
71
|
+
}
|
|
72
|
+
list.pageToken = page.nextPageToken;
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
watch: (signal) => service.watchServices(request, { signal }),
|
|
76
|
+
cursor: (message) => message.cursor,
|
|
77
|
+
keepalive: (message) => message.keepalive,
|
|
78
|
+
}, retry, opts);
|
|
79
|
+
}
|
|
80
|
+
async function openWatch(wire, retry, opts) {
|
|
81
|
+
const state = new WatchState(wire.request.cursor, opts?.signal);
|
|
7
82
|
let snapshot;
|
|
8
|
-
if (
|
|
83
|
+
if (wire.request.cursor === "") {
|
|
9
84
|
try {
|
|
10
|
-
const listed = await
|
|
11
|
-
snapshot = listed.
|
|
85
|
+
const listed = await wire.list(state.signal);
|
|
86
|
+
snapshot = listed.items;
|
|
12
87
|
state.cursor = listed.cursor;
|
|
13
88
|
}
|
|
14
89
|
catch (err) {
|
|
@@ -16,9 +91,8 @@ export async function openWorkloadWatch(workspace, retry, req, opts) {
|
|
|
16
91
|
throw err;
|
|
17
92
|
}
|
|
18
93
|
}
|
|
19
|
-
|
|
20
|
-
request.
|
|
21
|
-
request.keepalive = true;
|
|
94
|
+
wire.request.cursor = state.cursor;
|
|
95
|
+
wire.request.keepalive = true;
|
|
22
96
|
let reader;
|
|
23
97
|
return {
|
|
24
98
|
get cursor() {
|
|
@@ -34,64 +108,12 @@ export async function openWorkloadWatch(workspace, retry, req, opts) {
|
|
|
34
108
|
if (reader !== undefined) {
|
|
35
109
|
throw new Error(READER_TAKEN);
|
|
36
110
|
}
|
|
37
|
-
reader = createReader(() =>
|
|
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());
|
|
111
|
+
reader = createReader(() => runWatch(wire, retry, state), () => state.close());
|
|
62
112
|
return reader;
|
|
63
113
|
},
|
|
64
114
|
};
|
|
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
115
|
}
|
|
80
|
-
function
|
|
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) {
|
|
116
|
+
async function* runWatch(wire, retry, state) {
|
|
95
117
|
let attempt = 0;
|
|
96
118
|
try {
|
|
97
119
|
for (;;) {
|
|
@@ -100,20 +122,21 @@ async function* runWorkloads(workspace, retry, request, state) {
|
|
|
100
122
|
}
|
|
101
123
|
let cause;
|
|
102
124
|
try {
|
|
103
|
-
request.cursor = state.cursor;
|
|
104
|
-
const
|
|
105
|
-
for await (const msg of stream) {
|
|
125
|
+
wire.request.cursor = state.cursor;
|
|
126
|
+
for await (const message of wire.watch(state.signal)) {
|
|
106
127
|
attempt = 0;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
128
|
+
const keepalive = wire.keepalive(message);
|
|
129
|
+
if (keepalive !== undefined) {
|
|
130
|
+
if (keepalive.cursor !== "") {
|
|
131
|
+
state.cursor = keepalive.cursor;
|
|
110
132
|
}
|
|
111
133
|
continue;
|
|
112
134
|
}
|
|
113
|
-
|
|
114
|
-
|
|
135
|
+
const cursor = wire.cursor(message);
|
|
136
|
+
if (cursor !== "") {
|
|
137
|
+
state.cursor = cursor;
|
|
115
138
|
}
|
|
116
|
-
yield
|
|
139
|
+
yield message;
|
|
117
140
|
}
|
|
118
141
|
if (state.closed) {
|
|
119
142
|
return;
|
|
@@ -136,14 +159,45 @@ async function* runWorkloads(workspace, retry, request, state) {
|
|
|
136
159
|
throw err;
|
|
137
160
|
}
|
|
138
161
|
}
|
|
139
|
-
|
|
162
|
+
/**
|
|
163
|
+
* Starts an event watch; with an empty cursor it pages the event log as the
|
|
164
|
+
* caller iterates. Every message it yields is the stream's response.
|
|
165
|
+
*/
|
|
166
|
+
export function openEventWatch(service, retry, req, opts) {
|
|
167
|
+
const state = new WatchState(req.cursor, opts?.signal);
|
|
168
|
+
const request = clone(WatchEventsRequestSchema, req);
|
|
169
|
+
request.keepalive = true;
|
|
170
|
+
const list = create(ListEventsRequestSchema, {
|
|
171
|
+
project: req.project,
|
|
172
|
+
environment: req.environment,
|
|
173
|
+
workloadId: req.workloadId,
|
|
174
|
+
});
|
|
175
|
+
const listing = req.cursor === "";
|
|
176
|
+
let reader;
|
|
177
|
+
const watch = {
|
|
178
|
+
get cursor() {
|
|
179
|
+
return state.cursor;
|
|
180
|
+
},
|
|
181
|
+
close() {
|
|
182
|
+
state.close();
|
|
183
|
+
},
|
|
184
|
+
[Symbol.asyncIterator]() {
|
|
185
|
+
if (reader !== undefined) {
|
|
186
|
+
throw new Error(READER_TAKEN);
|
|
187
|
+
}
|
|
188
|
+
reader = createReader(() => runEvents(service, retry, request, list, listing, state), () => state.close());
|
|
189
|
+
return reader;
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
return Promise.resolve(watch);
|
|
193
|
+
}
|
|
194
|
+
async function* runEvents(service, retry, request, list, listingAtStart, state) {
|
|
140
195
|
let attempt = 0;
|
|
141
196
|
let page = [];
|
|
142
197
|
let pageIndex = 0;
|
|
143
198
|
let listing = listingAtStart;
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
// keepalives consumed.
|
|
199
|
+
// Resume is the position the next stream opens at, separate from the cursor
|
|
200
|
+
// the caller sees, which only moves on events handed out and keepalives read.
|
|
147
201
|
let resume = state.cursor;
|
|
148
202
|
try {
|
|
149
203
|
for (;;) {
|
|
@@ -161,14 +215,14 @@ async function* runEvents(workspace, retry, request, list, listingAtStart, state
|
|
|
161
215
|
state.cursor = event.cursor;
|
|
162
216
|
resume = event.cursor;
|
|
163
217
|
}
|
|
164
|
-
yield event;
|
|
218
|
+
yield create(WatchEventsResponseSchema, { event });
|
|
165
219
|
continue;
|
|
166
220
|
}
|
|
167
|
-
const listed = await
|
|
221
|
+
const listed = await service.listEvents(list, { signal: state.signal });
|
|
168
222
|
page = listed.events;
|
|
169
223
|
pageIndex = 0;
|
|
170
224
|
if (listed.cursor !== "") {
|
|
171
|
-
list.
|
|
225
|
+
list.pageToken = listed.cursor;
|
|
172
226
|
}
|
|
173
227
|
if (page.length > 0) {
|
|
174
228
|
continue;
|
|
@@ -180,10 +234,9 @@ async function* runEvents(workspace, retry, request, list, listingAtStart, state
|
|
|
180
234
|
continue;
|
|
181
235
|
}
|
|
182
236
|
request.cursor = resume;
|
|
183
|
-
const
|
|
184
|
-
for await (const msg of stream) {
|
|
237
|
+
for await (const message of service.watchEvents(request, { signal: state.signal })) {
|
|
185
238
|
attempt = 0;
|
|
186
|
-
const keepalive =
|
|
239
|
+
const keepalive = message.keepalive;
|
|
187
240
|
if (keepalive !== undefined) {
|
|
188
241
|
if (keepalive.cursor !== "") {
|
|
189
242
|
state.cursor = keepalive.cursor;
|
|
@@ -191,7 +244,7 @@ async function* runEvents(workspace, retry, request, list, listingAtStart, state
|
|
|
191
244
|
}
|
|
192
245
|
continue;
|
|
193
246
|
}
|
|
194
|
-
const event =
|
|
247
|
+
const event = message.event;
|
|
195
248
|
if (event === undefined) {
|
|
196
249
|
continue;
|
|
197
250
|
}
|
|
@@ -199,7 +252,7 @@ async function* runEvents(workspace, retry, request, list, listingAtStart, state
|
|
|
199
252
|
state.cursor = event.cursor;
|
|
200
253
|
resume = event.cursor;
|
|
201
254
|
}
|
|
202
|
-
yield
|
|
255
|
+
yield message;
|
|
203
256
|
}
|
|
204
257
|
if (state.closed) {
|
|
205
258
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toshicloud/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "TypeScript client for the Toshi Workspace API",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"packageManager": "pnpm@12.15.0+sha512.b3951d6d6ff4a9f13dc7cf56cd28d2b8eb37dbcc9c68e52e1e1191873ea9f82a017287ab50baadc5ccc494f036c67c0e47eb13608c2fa36f47711b61ea128e61",
|