@kottera/tera-web 0.1.0-rc.6 → 0.1.0-rc.7
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 +7 -4
- package/dist/client.d.ts +18 -0
- package/dist/client.js +19 -1
- package/dist/generated/kit.d.ts +69 -2
- package/dist/generated/kit.js +86 -0
- package/dist/generated/workorder-events.d.ts +1 -1
- package/dist/generated/workorder.d.ts +50 -0
- package/dist/generated/wsstream.d.ts +34 -0
- package/dist/generated/wsstream.js +28 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +8 -0
- package/dist/stream/connection.d.ts +145 -0
- package/dist/stream/connection.js +377 -0
- package/dist/terminal/view.d.ts +168 -0
- package/dist/terminal/view.js +540 -0
- package/dist/terminalwire/codec.d.ts +176 -0
- package/dist/terminalwire/codec.js +547 -0
- package/dist/unreached.d.ts +26 -0
- package/dist/unreached.js +30 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,12 +11,15 @@ author's — and has no runtime dependencies. It is not yet published.
|
|
|
11
11
|
|------|---------------|
|
|
12
12
|
| `src/index.ts` | The package's one entry point. Everything public is re-exported here. |
|
|
13
13
|
| `src/routes.ts` | The route prefix, `routePath`, and the app's base (`appBase`, `underBase`) every route and the sign-out are reached under. |
|
|
14
|
-
| `src/client.ts` | `TeraClient`, the typed client over D3's routes (D4 items 1, 3 and 4): `me()`, `invoke(capability, params)`, `feed(options)` (a `FeedConnection` read with the client's fetch), `upload(file, options)` with progress, and `signOut()`. A kit refusal throws `KitRequestError`; a verb's refusal answers as its verdict. |
|
|
14
|
+
| `src/client.ts` | `TeraClient`, the typed client over D3's routes (D4 items 1, 3 and 4): `me()`, `invoke(capability, params)`, `feed(options)` (a `FeedConnection` read with the client's fetch), `stream(options)` (a `StreamConnection` probed with the client's fetch), `terminal(options)` (a `TerminalView` opened the same way), `upload(file, options)` with progress, and `signOut()`. A kit refusal throws `KitRequestError`; a verb's refusal answers as its verdict. |
|
|
15
15
|
| `src/phone.ts` | `phoneURL(me, location, base?)`, the "Open on your phone" link (ADR-177 D4): `me().phone_path` resolved against the app's base (`/` by default, `/plugins/<name>/` on a path-based installation), with `next` set to `location.href`, fragment included. tera's proxy sends it on to tera's phone page, which draws the page's URL as a QR code, so the page needs no knowledge of tera's URL. A `phone_path` that is not relative to the base throws. |
|
|
16
|
-
| `src/signin.ts` | Signing in again after tera forgets the page's session, as a daemon restart does: `isSignInRequired(response)` recognises the UI proxy's 401 `sign_in_required`, and `reSignIn()` loads the page again, which signs the person in with no prompt while their tera session lives. `TeraClient` and `
|
|
16
|
+
| `src/signin.ts` | Signing in again after tera forgets the page's session, as a daemon restart does: `isSignInRequired(response)` recognises the UI proxy's 401 `sign_in_required`, and `reSignIn()` loads the page again, which signs the person in with no prompt while their tera session lives. `TeraClient`, `FeedConnection` and `StreamConnection` do it on their own, once; a page reading its own routes with its own `fetch` applies `isSignInRequired`. |
|
|
17
|
+
| `src/unreached.ts` | The codes tera's UI proxy answers the page's script with in the kit's envelope when the plugin did not answer: `PLUGIN_BUSY` and `PLUGIN_STARTING` (503, with `Retry-After`), `PLUGIN_RUNNER_UNREACHABLE` and `PLUGIN_NOT_ANSWERING` (502), all retryable, listed as `PLUGIN_UNREACHED_CODES`. |
|
|
17
18
|
| `src/stepup.ts` | A person's step-up from the page's side (ADR-173 D11): the step-up header, the `return_to` the navigation fallback carries, and `StepUpError`. Every `TeraClient` request — `me()`, `invoke()`, `upload()` and `fetch(path)` to the plugin's own routes — has the person confirm a `step_up_required` and sends the request once more; `stepUp(id)` reads `GET /tera/v1/step-ups/{id}`. |
|
|
18
|
-
| `src/generated/` | The contract, generated from the committed schemas under `sdk/plugin/webui/schema/`, `sdk/workorder/schema/`, `sdk/feed/schema/` and `sdk/
|
|
19
|
-
| `src/feed/` | The
|
|
19
|
+
| `src/generated/` | The contract, generated from the committed schemas under `sdk/plugin/webui/schema/`, `sdk/workorder/schema/`, `sdk/feed/schema/`, `sdk/shares/schema/` and `sdk/wsstream/schema/` (D4 item 1; the share types, ADR-174 D14; the close codes, ADR-181 D15): one type per schema, the kit's route table as `KIT_ROUTE_TABLE`, a closed set of named constants as a map by name (`STREAM_CLOSE_CODES`), and the maps from a route or a feed event to the type it carries. Never hand-edited, and never hand-write a type it declares: `make web-types` regenerates it, and `TestWebTypes_MatchCommitted` (`cmd/internal/genwebtypes`) fails on a hand edit or a stale generation. |
|
|
20
|
+
| `src/feed/` | The feed connection (D4 item 2). |
|
|
21
|
+
| `src/stream/` | `StreamConnection`, one WebSocket to a kit stream route (ADR-181 D9, D11 item 1), reopened by the close code the far side chose: after backoff on `going_away`, `retry_later` and any code it does not know, at once on `reconnect` (three in 30 seconds, then backoff), never on `refused` or `too_big`, and on a normal closure as the protocol above said with `onNormalClose`. A failed handshake is settled by one probe of `GET /tera/v1/me`; more than 1 MiB unsent is a defect, closed and reconnected; `reconnect(reason)` gives up on a socket the protocol above finds dead; `close()` is the page's own end. |
|
|
22
|
+
| `src/terminal/` | `TerminalView`, one view of an attended session's terminal (ADR-181 D11 item 2): `terminalwire` over a `StreamConnection` to the kit's terminal route, holding ADR-180's client duties itself — the resume offset in bytes handed to the renderer, an `ACK` only for bytes the renderer's `parsed` callback reported, at most 64 KiB of `INPUT` beyond `INPUT_ACK` (a paste queued in the view), a one-byte keystroke the window cannot hold sent as `SIGNAL` (P-R2), input dropped while history is drawn, `CONTROL {take}` before a viewer's first keystroke, `RESIZE` only while the view sets the size, and the step after each `CLOSE` reason. The renderer is named by its shape (`TerminalRenderer`, which xterm.js's `Terminal` meets), so xterm.js is the page's dependency, not the package's. Its tests drive it with a scripted socket and a recording renderer; no real browser runs here. |
|
|
20
23
|
| `src/**/*.test.ts` | The suite, beside what it tests. |
|
|
21
24
|
|
|
22
25
|
## Tests
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { FeedConnection, type FeedConnectionOptions } from "./feed/connection.ts";
|
|
2
|
+
import { StreamConnection, type StreamConnectionOptions } from "./stream/connection.ts";
|
|
3
|
+
import { TerminalView, type TerminalViewOptions } from "./terminal/view.ts";
|
|
2
4
|
import type { KitError, KitInvokeResponse, KitMeResponse, KitStepUpResponse, KitUploadResponse } from "./generated/kit.ts";
|
|
3
5
|
import { type StepUpChallenge, type StepUpOptions } from "./stepup.ts";
|
|
4
6
|
/** The body of a kit failure: the envelope's `error` member. */
|
|
@@ -107,6 +109,10 @@ export interface TeraClientOptions {
|
|
|
107
109
|
}
|
|
108
110
|
/** The options of {@link TeraClient.feed}: a {@link FeedConnection}'s, with the opener the client's own. */
|
|
109
111
|
export type FeedOptions = Omit<FeedConnectionOptions, "open" | "base">;
|
|
112
|
+
/** The options of {@link TeraClient.stream}: a {@link StreamConnection}'s, with the probe's fetch the client's own. */
|
|
113
|
+
export type StreamOptions = Omit<StreamConnectionOptions, "fetch" | "base" | "reSignIn">;
|
|
114
|
+
/** What {@link TeraClient.terminal} takes: a view's options but what the client supplies. */
|
|
115
|
+
export type TerminalOptions = Omit<TerminalViewOptions, "fetch" | "base" | "reSignIn">;
|
|
110
116
|
export declare class TeraClient {
|
|
111
117
|
private readonly base;
|
|
112
118
|
private readonly fetchFn;
|
|
@@ -140,6 +146,18 @@ export declare class TeraClient {
|
|
|
140
146
|
* the page no longer wants it.
|
|
141
147
|
*/
|
|
142
148
|
feed(options: FeedOptions): FeedConnection;
|
|
149
|
+
/**
|
|
150
|
+
* A {@link StreamConnection} to one of the kit's stream routes, its failed
|
|
151
|
+
* handshakes probed with this client's fetch. It opens at once: call
|
|
152
|
+
* `close()` on it when the page no longer wants it.
|
|
153
|
+
*/
|
|
154
|
+
stream(options: StreamOptions): StreamConnection;
|
|
155
|
+
/**
|
|
156
|
+
* A {@link TerminalView} of an attended session's terminal at the kit's
|
|
157
|
+
* terminal route, its failed handshakes probed with this client's fetch. It
|
|
158
|
+
* opens at once: call `detach()` on it when the pane closes.
|
|
159
|
+
*/
|
|
160
|
+
terminal(options: TerminalOptions): TerminalView;
|
|
143
161
|
/**
|
|
144
162
|
* `POST /tera/v1/uploads`: sends one file as the signed-in person and
|
|
145
163
|
* answers the staged artifact, whose `handle` a Work Order's attachments
|
package/dist/client.js
CHANGED
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
* page again ({@link TeraClient.reSignIn}), which signs the person in, and
|
|
51
51
|
* still rejects (or, from {@link TeraClient.fetch}, answers) as it arrived.
|
|
52
52
|
* The page is loaded again once per client, however many requests were
|
|
53
|
-
* refused together. A feed the client opens does the same.
|
|
53
|
+
* refused together. A feed or stream the client opens does the same.
|
|
54
54
|
*
|
|
55
55
|
* It is a plain class with an injected fetch, request factory, navigation,
|
|
56
56
|
* window opener and timer, so its tests drive it against a fake and no
|
|
@@ -59,6 +59,8 @@
|
|
|
59
59
|
import { appBase, isBaseRelative, routePath, underBase } from "./routes.js";
|
|
60
60
|
import { FeedConnection } from "./feed/connection.js";
|
|
61
61
|
import { fetchOpener } from "./feed/sse.js";
|
|
62
|
+
import { StreamConnection } from "./stream/connection.js";
|
|
63
|
+
import { TerminalView } from "./terminal/view.js";
|
|
62
64
|
import { SIGN_IN_REQUIRED, isSignInRequired } from "./signin.js";
|
|
63
65
|
import { STEP_UP_HEADER, STEP_UP_POLL_MS, StepUpError, defaultOpenWindow, defaultSleep, resumedStepUp, stepUpChallengeOf, stepUpPageURL, } from "./stepup.js";
|
|
64
66
|
/**
|
|
@@ -171,6 +173,22 @@ export class TeraClient {
|
|
|
171
173
|
feed(options) {
|
|
172
174
|
return new FeedConnection({ ...options, base: this.base, open: fetchOpener(this.fetchFn), reSignIn: () => this.reSignIn() });
|
|
173
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* A {@link StreamConnection} to one of the kit's stream routes, its failed
|
|
178
|
+
* handshakes probed with this client's fetch. It opens at once: call
|
|
179
|
+
* `close()` on it when the page no longer wants it.
|
|
180
|
+
*/
|
|
181
|
+
stream(options) {
|
|
182
|
+
return new StreamConnection({ ...options, base: this.base, fetch: this.fetchFn, reSignIn: () => this.reSignIn() });
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* A {@link TerminalView} of an attended session's terminal at the kit's
|
|
186
|
+
* terminal route, its failed handshakes probed with this client's fetch. It
|
|
187
|
+
* opens at once: call `detach()` on it when the pane closes.
|
|
188
|
+
*/
|
|
189
|
+
terminal(options) {
|
|
190
|
+
return new TerminalView({ ...options, base: this.base, fetch: this.fetchFn, reSignIn: () => this.reSignIn() });
|
|
191
|
+
}
|
|
174
192
|
/**
|
|
175
193
|
* `POST /tera/v1/uploads`: sends one file as the signed-in person and
|
|
176
194
|
* answers the staged artifact, whose `handle` a Work Order's attachments
|
package/dist/generated/kit.d.ts
CHANGED
|
@@ -2,11 +2,11 @@ import type { FeedFrameRecord, FeedFrameStatus } from "./feed.ts";
|
|
|
2
2
|
/**
|
|
3
3
|
* The vocabulary of the kit's own error codes (sdk/plugin/webui.KitCodes).
|
|
4
4
|
*
|
|
5
|
-
* Every code the kit answers with when no verb gave a verdict, and sign_in_required, which tera's UI proxy answers in front of the kit when a request that is not a page load reaches the plugin's origin with no live session: load the page again to sign in. Only tera_unavailable
|
|
5
|
+
* Every code the kit answers with when no verb gave a verdict, and sign_in_required, which tera's UI proxy answers in front of the kit when a request that is not a page load reaches the plugin's origin with no live session: load the page again to sign in; and the codes the proxy answers such a request with when it did not reach the plugin: plugin_busy (503, the plugin is at its runner link's stream cap, with Retry-After), plugin_starting (503, tera has not yet settled where the plugin runs, with Retry-After), plugin_runner_unreachable (502, the plugin's runner has no live link) and plugin_not_answering (502, the plugin's UI did not answer on its socket). Only tera_unavailable and those four are retryable. A failure envelope may also carry a verb's own code, or tera's own code for a call it refused, verbatim, which is not a member: a reader keeps a code it does not know as it arrived and branches on retryable.
|
|
6
6
|
*
|
|
7
7
|
* Schema: sdk/plugin/webui/schema/v1/error-code.json
|
|
8
8
|
*/
|
|
9
|
-
export type KitErrorCode = "unauthenticated" | "cross_origin" | "route_not_found" | "method_not_allowed" | "route_not_served" | "invalid_request" | "exchange_refused" | "tera_unavailable" | "step_up_required" | "sign_in_required";
|
|
9
|
+
export type KitErrorCode = "unauthenticated" | "cross_origin" | "route_not_found" | "method_not_allowed" | "route_not_served" | "invalid_request" | "exchange_refused" | "tera_unavailable" | "step_up_required" | "sign_in_required" | "plugin_busy" | "plugin_starting" | "plugin_runner_unreachable" | "plugin_not_answering";
|
|
10
10
|
/**
|
|
11
11
|
* Kit error envelope.
|
|
12
12
|
*
|
|
@@ -283,6 +283,73 @@ export declare const KIT_ROUTE_TABLE: {
|
|
|
283
283
|
readonly schema: "https://kottera.dev/schemas/plugin/webui/step-up.response/v1";
|
|
284
284
|
readonly description: "The step-up's state.";
|
|
285
285
|
};
|
|
286
|
+
}, {
|
|
287
|
+
readonly name: "terminal";
|
|
288
|
+
readonly method: "GET";
|
|
289
|
+
readonly path: "/tera/v1/workorders/{id}/terminal";
|
|
290
|
+
readonly summary: "Open one view of an attended session's terminal as the person (ADR-181 D8): a WebSocket the kit splices to tera's terminal door, opened with the person's token. Which Work Order it is, and whether the person may see it, is tera's to decide; the kit keeps no list of sessions. Every refusal after the upgrade is a close: a query of the wrong shape is refused invalid_request; tera's 401 is reconnect, reason reauthenticate, and a second within the minute refused; its 400, 403 and 404 are refused with tera's code (not_attendee, attend_view_not_granted, not_found); its 410 is refused, reason ended, a session with no process and none coming without the person's act; its 409 (not_running, a session whose process is coming), 408, 429 and 5xx, and no answer, are retry_later with tera's code or tera_unavailable. A text message is refused binary_only.";
|
|
291
|
+
readonly same_origin: true;
|
|
292
|
+
readonly path_params: readonly [{
|
|
293
|
+
readonly name: "id";
|
|
294
|
+
readonly description: "The attended Work Order's id.";
|
|
295
|
+
readonly required: true;
|
|
296
|
+
}];
|
|
297
|
+
readonly query: readonly [{
|
|
298
|
+
readonly name: "view";
|
|
299
|
+
readonly description: "The view's id: a canonical, lower-case UUIDv7 the page mints per pane and keeps across reconnects.";
|
|
300
|
+
readonly required: true;
|
|
301
|
+
}, {
|
|
302
|
+
readonly name: "want";
|
|
303
|
+
readonly description: "Whether the view asks to type (control) or only to watch (view).";
|
|
304
|
+
readonly required: true;
|
|
305
|
+
readonly values: readonly ["control", "view"];
|
|
306
|
+
}, {
|
|
307
|
+
readonly name: "resume";
|
|
308
|
+
readonly description: "The output offset the page has handed to its renderer so far, a decimal integer from 0 to 9007199254740991; absent on a fresh view, which replays.";
|
|
309
|
+
}, {
|
|
310
|
+
readonly name: "cols";
|
|
311
|
+
readonly description: "The view's width in cells, a decimal integer from 1 to 65535.";
|
|
312
|
+
readonly required: true;
|
|
313
|
+
}, {
|
|
314
|
+
readonly name: "rows";
|
|
315
|
+
readonly description: "The view's height in cells, a decimal integer from 1 to 65535.";
|
|
316
|
+
readonly required: true;
|
|
317
|
+
}];
|
|
318
|
+
readonly response: {
|
|
319
|
+
readonly media_type: "application/octet-stream";
|
|
320
|
+
readonly description: "A WebSocket upgrade (101), then messages both ways until either side closes, with the close vocabulary.";
|
|
321
|
+
};
|
|
322
|
+
readonly upgrade: "websocket";
|
|
323
|
+
readonly subprotocol: "tera.terminalwire.v1";
|
|
324
|
+
readonly messages: {
|
|
325
|
+
readonly media_type: "application/octet-stream";
|
|
326
|
+
readonly description: "One sdk/terminalwire frame per binary message, each way, at most 16388 bytes. The kit relays each as it arrived, reading no payload and redacting nothing.";
|
|
327
|
+
};
|
|
328
|
+
}];
|
|
329
|
+
readonly close_codes: readonly [{
|
|
330
|
+
readonly code: 1000;
|
|
331
|
+
readonly name: "ended";
|
|
332
|
+
readonly does: "what the protocol above said last; otherwise nothing";
|
|
333
|
+
}, {
|
|
334
|
+
readonly code: 1001;
|
|
335
|
+
readonly name: "going_away";
|
|
336
|
+
readonly does: "reconnects after backoff: the peer is stopping or stopped answering pings";
|
|
337
|
+
}, {
|
|
338
|
+
readonly code: 1009;
|
|
339
|
+
readonly name: "too_big";
|
|
340
|
+
readonly does: "nothing: a message broke the route's bound, a client defect";
|
|
341
|
+
}, {
|
|
342
|
+
readonly code: 4000;
|
|
343
|
+
readonly name: "reconnect";
|
|
344
|
+
readonly does: "reconnects at once with the same identity: a token renewal";
|
|
345
|
+
}, {
|
|
346
|
+
readonly code: 4001;
|
|
347
|
+
readonly name: "retry_later";
|
|
348
|
+
readonly does: "reconnects after backoff: the far side is unreachable or not ready";
|
|
349
|
+
}, {
|
|
350
|
+
readonly code: 4003;
|
|
351
|
+
readonly name: "refused";
|
|
352
|
+
readonly does: "nothing until something changes; the reason is the error code";
|
|
286
353
|
}];
|
|
287
354
|
};
|
|
288
355
|
/** The name of a route in KIT_ROUTE_TABLE. */
|
package/dist/generated/kit.js
CHANGED
|
@@ -167,5 +167,91 @@ export const KIT_ROUTE_TABLE = {
|
|
|
167
167
|
description: "The step-up's state.",
|
|
168
168
|
},
|
|
169
169
|
},
|
|
170
|
+
{
|
|
171
|
+
name: "terminal",
|
|
172
|
+
method: "GET",
|
|
173
|
+
path: "/tera/v1/workorders/{id}/terminal",
|
|
174
|
+
summary: "Open one view of an attended session's terminal as the person (ADR-181 D8): a WebSocket the kit splices to tera's terminal door, opened with the person's token. Which Work Order it is, and whether the person may see it, is tera's to decide; the kit keeps no list of sessions. Every refusal after the upgrade is a close: a query of the wrong shape is refused invalid_request; tera's 401 is reconnect, reason reauthenticate, and a second within the minute refused; its 400, 403 and 404 are refused with tera's code (not_attendee, attend_view_not_granted, not_found); its 410 is refused, reason ended, a session with no process and none coming without the person's act; its 409 (not_running, a session whose process is coming), 408, 429 and 5xx, and no answer, are retry_later with tera's code or tera_unavailable. A text message is refused binary_only.",
|
|
175
|
+
same_origin: true,
|
|
176
|
+
path_params: [
|
|
177
|
+
{
|
|
178
|
+
name: "id",
|
|
179
|
+
description: "The attended Work Order's id.",
|
|
180
|
+
required: true,
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
query: [
|
|
184
|
+
{
|
|
185
|
+
name: "view",
|
|
186
|
+
description: "The view's id: a canonical, lower-case UUIDv7 the page mints per pane and keeps across reconnects.",
|
|
187
|
+
required: true,
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "want",
|
|
191
|
+
description: "Whether the view asks to type (control) or only to watch (view).",
|
|
192
|
+
required: true,
|
|
193
|
+
values: [
|
|
194
|
+
"control",
|
|
195
|
+
"view",
|
|
196
|
+
],
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
name: "resume",
|
|
200
|
+
description: "The output offset the page has handed to its renderer so far, a decimal integer from 0 to 9007199254740991; absent on a fresh view, which replays.",
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
name: "cols",
|
|
204
|
+
description: "The view's width in cells, a decimal integer from 1 to 65535.",
|
|
205
|
+
required: true,
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: "rows",
|
|
209
|
+
description: "The view's height in cells, a decimal integer from 1 to 65535.",
|
|
210
|
+
required: true,
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
response: {
|
|
214
|
+
media_type: "application/octet-stream",
|
|
215
|
+
description: "A WebSocket upgrade (101), then messages both ways until either side closes, with the close vocabulary.",
|
|
216
|
+
},
|
|
217
|
+
upgrade: "websocket",
|
|
218
|
+
subprotocol: "tera.terminalwire.v1",
|
|
219
|
+
messages: {
|
|
220
|
+
media_type: "application/octet-stream",
|
|
221
|
+
description: "One sdk/terminalwire frame per binary message, each way, at most 16388 bytes. The kit relays each as it arrived, reading no payload and redacting nothing.",
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
close_codes: [
|
|
226
|
+
{
|
|
227
|
+
code: 1000,
|
|
228
|
+
name: "ended",
|
|
229
|
+
does: "what the protocol above said last; otherwise nothing",
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
code: 1001,
|
|
233
|
+
name: "going_away",
|
|
234
|
+
does: "reconnects after backoff: the peer is stopping or stopped answering pings",
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
code: 1009,
|
|
238
|
+
name: "too_big",
|
|
239
|
+
does: "nothing: a message broke the route's bound, a client defect",
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
code: 4000,
|
|
243
|
+
name: "reconnect",
|
|
244
|
+
does: "reconnects at once with the same identity: a token renewal",
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
code: 4001,
|
|
248
|
+
name: "retry_later",
|
|
249
|
+
does: "reconnects after backoff: the far side is unreachable or not ready",
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
code: 4003,
|
|
253
|
+
name: "refused",
|
|
254
|
+
does: "nothing until something changes; the reason is the error code",
|
|
255
|
+
},
|
|
170
256
|
],
|
|
171
257
|
};
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Schema: sdk/workorder/schema/events/v1/event-kind.workorder.json
|
|
7
7
|
*/
|
|
8
|
-
export type WorkOrderEventKind = "cohort.set" | "off_tera.wake" | "relation.set" | "relation.removed" | "off_tera.taken" | "off_tera.progress" | "off_tera.reposted" | "off_tera.late_report" | "off_tera.checks_started" | "created" | "transitioned" | "acknowledged" | "retention_expired" | "edited" | "seat.reclaimed" | "claim.overdue" | "claim.unreadable" | "subject_claim.overridden" | "seat.cooling" | "seat.credential_held" | "member_credential.requested" | "member_credential.settled" | "seat.acquired" | "seat.runner_released" | "seat.meters_voided" | "step.binding" | "session.terminated" | "postcondition.verdict" | "parked_run.unreadable" | "parked_run.reclaimed" | "dispatch.settled_unobserved" | "parked_run.parked" | "parked_run.resumed" | "resume.refused" | "dispatch.salvaged" | "workspace.retired" | "dispatch.routed" | "session.lost" | "activity_partition.pinned" | "session.continuity" | "session.context_compacted" | "upstream.answer_forwarded" | "invocation.checkpoint" | "invocation.guidance" | "invocation.guidance.ack" | "invocation.reply" | "invocation.checkpoint.unreadable" | "invocation.guidance.unreadable" | "config_pin.unreadable" | "config_pin.repinned" | "type.followed" | "observation.scrubbed" | "observation.secret-shape-redacted" | "observation.promoted" | "observation.refuted" | "observation.purged" | "conversation.input_barrier" | "walk_resume.fell_back" | "store.ahead_of_binary" | "artifact.published" | "history.dropped" | "history.archived" | "seat.work_order_released" | "step.settled_late" | "step.reattached" | "relay.unrebuilt" | "references.recorded";
|
|
8
|
+
export type WorkOrderEventKind = "cohort.set" | "off_tera.wake" | "relation.set" | "relation.removed" | "off_tera.taken" | "off_tera.progress" | "off_tera.reposted" | "off_tera.late_report" | "off_tera.checks_started" | "created" | "transitioned" | "acknowledged" | "retention_expired" | "edited" | "seat.reclaimed" | "claim.overdue" | "claim.unreadable" | "subject_claim.overridden" | "seat.cooling" | "seat.credential_held" | "member_credential.requested" | "member_credential.settled" | "seat.acquired" | "seat.runner_released" | "seat.meters_voided" | "step.binding" | "session.terminated" | "postcondition.verdict" | "parked_run.unreadable" | "parked_run.reclaimed" | "dispatch.settled_unobserved" | "parked_run.parked" | "parked_run.resumed" | "resume.refused" | "dispatch.salvaged" | "workspace.retired" | "dispatch.routed" | "session.lost" | "activity_partition.pinned" | "session.continuity" | "session.context_compacted" | "upstream.answer_forwarded" | "invocation.checkpoint" | "invocation.guidance" | "invocation.guidance.ack" | "invocation.reply" | "invocation.checkpoint.unreadable" | "invocation.guidance.unreadable" | "config_pin.unreadable" | "config_pin.repinned" | "type.followed" | "observation.scrubbed" | "observation.secret-shape-redacted" | "observation.promoted" | "observation.refuted" | "observation.purged" | "conversation.input_barrier" | "walk_resume.fell_back" | "store.ahead_of_binary" | "artifact.published" | "history.dropped" | "history.archived" | "seat.work_order_released" | "step.settled_late" | "step.reattached" | "relay.unrebuilt" | "references.recorded" | "attended.wall_reported" | "attended.signal";
|
|
9
9
|
/**
|
|
10
10
|
* `workorder` event payload (event/workorder).
|
|
11
11
|
*
|
|
@@ -1004,6 +1004,9 @@ export type WorkOrderGetResult = {
|
|
|
1004
1004
|
at: string;
|
|
1005
1005
|
};
|
|
1006
1006
|
display_name?: string;
|
|
1007
|
+
work_order?: string;
|
|
1008
|
+
attempt?: number;
|
|
1009
|
+
runner?: string;
|
|
1007
1010
|
};
|
|
1008
1011
|
};
|
|
1009
1012
|
question?: {
|
|
@@ -1024,6 +1027,7 @@ export type WorkOrderGetResult = {
|
|
|
1024
1027
|
member?: string;
|
|
1025
1028
|
pool?: string;
|
|
1026
1029
|
pool_cause?: string;
|
|
1030
|
+
pool_note?: string;
|
|
1027
1031
|
verb?: string;
|
|
1028
1032
|
repairable: boolean;
|
|
1029
1033
|
refused_at?: string;
|
|
@@ -1442,6 +1446,27 @@ export type WorkOrderGetResult = {
|
|
|
1442
1446
|
created_at: string;
|
|
1443
1447
|
updated_at: string;
|
|
1444
1448
|
off_tera: boolean;
|
|
1449
|
+
attended?: {
|
|
1450
|
+
host?: string;
|
|
1451
|
+
personal: boolean;
|
|
1452
|
+
candidate?: string;
|
|
1453
|
+
unsandboxed: boolean;
|
|
1454
|
+
unsandboxed_notice?: string;
|
|
1455
|
+
attendee: string;
|
|
1456
|
+
attached: {
|
|
1457
|
+
views: number;
|
|
1458
|
+
controller?: string;
|
|
1459
|
+
size_setter?: string;
|
|
1460
|
+
since?: string;
|
|
1461
|
+
last_detached_at?: string;
|
|
1462
|
+
} | null;
|
|
1463
|
+
signal: {
|
|
1464
|
+
signal: string;
|
|
1465
|
+
message?: string;
|
|
1466
|
+
at: string;
|
|
1467
|
+
dropped?: number;
|
|
1468
|
+
} | null;
|
|
1469
|
+
};
|
|
1445
1470
|
has_outputs?: boolean;
|
|
1446
1471
|
archived: boolean;
|
|
1447
1472
|
history?: {
|
|
@@ -1771,6 +1796,8 @@ export type WorkOrderInterruptResult = {
|
|
|
1771
1796
|
* Schema: sdk/workorder/schema/v1/list.params.json
|
|
1772
1797
|
*/
|
|
1773
1798
|
export type WorkOrderListParams = {
|
|
1799
|
+
/** Select only attended Work Orders — sessions a person attends in a terminal — and carry on each row the `attended` projection work_order.get answers (host, personal, candidate, unsandboxed, attendee, attached, signal), so a list of sessions needs no get per row. Absent or false is no constraint, and rows then carry no projection. Unindexed on both store backends; pair it with `states` or `parent_id`. */
|
|
1800
|
+
attended?: boolean;
|
|
1774
1801
|
/** Select the Work Orders in this cohort — the undertaking a Work Order belongs to, read off any row's `cohort_id` (a Work Order filed alone is a cohort of one, whose id is its own). A UUID; anything else is refused rather than matched. Unlike `tag` this reads an indexed column on both store backends, so it is the filter to pass alone for 'everything in this undertaking', in every state, terminal included. Composes with the other filters as an AND. */
|
|
1775
1802
|
cohort?: string;
|
|
1776
1803
|
/** Select Work Orders filed with exactly this resolved capacity key. Matched verbatim against the key on the row, which is already materialized against that Work Order's own inputs — pass `prefilter/<subject id>`, not the type's `${inputs...}` template. This is how a caller asks 'is another Work Order of this tier already working my subject?' without reading inputs back out of every row. */
|
|
@@ -1848,6 +1875,27 @@ export type WorkOrderListResult = {
|
|
|
1848
1875
|
at: string;
|
|
1849
1876
|
};
|
|
1850
1877
|
};
|
|
1878
|
+
attended?: {
|
|
1879
|
+
host?: string;
|
|
1880
|
+
personal: boolean;
|
|
1881
|
+
candidate?: string;
|
|
1882
|
+
unsandboxed: boolean;
|
|
1883
|
+
unsandboxed_notice?: string;
|
|
1884
|
+
attendee: string;
|
|
1885
|
+
attached: {
|
|
1886
|
+
views: number;
|
|
1887
|
+
controller?: string;
|
|
1888
|
+
size_setter?: string;
|
|
1889
|
+
since?: string;
|
|
1890
|
+
last_detached_at?: string;
|
|
1891
|
+
} | null;
|
|
1892
|
+
signal: {
|
|
1893
|
+
signal: string;
|
|
1894
|
+
message?: string;
|
|
1895
|
+
at: string;
|
|
1896
|
+
dropped?: number;
|
|
1897
|
+
} | null;
|
|
1898
|
+
};
|
|
1851
1899
|
}[] | null;
|
|
1852
1900
|
};
|
|
1853
1901
|
/**
|
|
@@ -3147,6 +3195,7 @@ export type WorkOrderTreeResult = {
|
|
|
3147
3195
|
member?: string;
|
|
3148
3196
|
pool?: string;
|
|
3149
3197
|
pool_cause?: string;
|
|
3198
|
+
pool_note?: string;
|
|
3150
3199
|
verb?: string;
|
|
3151
3200
|
repairable: boolean;
|
|
3152
3201
|
refused_at?: string;
|
|
@@ -3377,6 +3426,7 @@ export type WorkOrderTreeResult = {
|
|
|
3377
3426
|
member?: string;
|
|
3378
3427
|
pool?: string;
|
|
3379
3428
|
pool_cause?: string;
|
|
3429
|
+
pool_note?: string;
|
|
3380
3430
|
verb?: string;
|
|
3381
3431
|
repairable: boolean;
|
|
3382
3432
|
refused_at?: string;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The close vocabulary of a tera stream (sdk/wsstream.CloseCodes).
|
|
3
|
+
*
|
|
4
|
+
* The WebSocket close codes a tera stream is closed with. Each states only what the hop that closed decided, and a relay passes a close on as it arrived. The reason beside a code is a code of at most 64 bytes of [a-z0-9_.:-], or "closed". A code is never renamed or given a new meaning; a client that meets a code it does not know treats it as retry_later (4001).
|
|
5
|
+
*
|
|
6
|
+
* Schema: sdk/wsstream/schema/v1/close-code.json
|
|
7
|
+
*/
|
|
8
|
+
export type StreamCloseCode = 1000 | 1001 | 1009 | 4000 | 4001 | 4003;
|
|
9
|
+
/**
|
|
10
|
+
* Each member of StreamCloseCode, by name.
|
|
11
|
+
*
|
|
12
|
+
* Schema: sdk/wsstream/schema/v1/close-code.json
|
|
13
|
+
*/
|
|
14
|
+
export declare const STREAM_CLOSE_CODES: {
|
|
15
|
+
/** What the client does: what the protocol above said last; otherwise nothing. */
|
|
16
|
+
readonly ended: 1000;
|
|
17
|
+
/** What the client does: reconnects after backoff: the peer is stopping or stopped answering pings. */
|
|
18
|
+
readonly going_away: 1001;
|
|
19
|
+
/** What the client does: nothing: a message broke the route's bound, a client defect. */
|
|
20
|
+
readonly too_big: 1009;
|
|
21
|
+
/** What the client does: reconnects at once with the same identity: a token renewal. */
|
|
22
|
+
readonly reconnect: 4000;
|
|
23
|
+
/** What the client does: reconnects after backoff: the far side is unreachable or not ready. */
|
|
24
|
+
readonly retry_later: 4001;
|
|
25
|
+
/** What the client does: nothing until something changes; the reason is the error code. */
|
|
26
|
+
readonly refused: 4003;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Every type this module declares, mapped to the repository path of the schema
|
|
30
|
+
* it was generated from.
|
|
31
|
+
*/
|
|
32
|
+
export declare const STREAM_SCHEMAS: {
|
|
33
|
+
readonly StreamCloseCode: "sdk/wsstream/schema/v1/close-code.json";
|
|
34
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Code generated by cmd/internal/genwebtypes from sdk/wsstream/schema/v1/; DO NOT EDIT.
|
|
2
|
+
// Regenerate with `make web-types` (ADR-162 D4 item 1).
|
|
3
|
+
/**
|
|
4
|
+
* Each member of StreamCloseCode, by name.
|
|
5
|
+
*
|
|
6
|
+
* Schema: sdk/wsstream/schema/v1/close-code.json
|
|
7
|
+
*/
|
|
8
|
+
export const STREAM_CLOSE_CODES = {
|
|
9
|
+
/** What the client does: what the protocol above said last; otherwise nothing. */
|
|
10
|
+
ended: 1000,
|
|
11
|
+
/** What the client does: reconnects after backoff: the peer is stopping or stopped answering pings. */
|
|
12
|
+
going_away: 1001,
|
|
13
|
+
/** What the client does: nothing: a message broke the route's bound, a client defect. */
|
|
14
|
+
too_big: 1009,
|
|
15
|
+
/** What the client does: reconnects at once with the same identity: a token renewal. */
|
|
16
|
+
reconnect: 4000,
|
|
17
|
+
/** What the client does: reconnects after backoff: the far side is unreachable or not ready. */
|
|
18
|
+
retry_later: 4001,
|
|
19
|
+
/** What the client does: nothing until something changes; the reason is the error code. */
|
|
20
|
+
refused: 4003,
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Every type this module declares, mapped to the repository path of the schema
|
|
24
|
+
* it was generated from.
|
|
25
|
+
*/
|
|
26
|
+
export const STREAM_SCHEMAS = {
|
|
27
|
+
StreamCloseCode: "sdk/wsstream/schema/v1/close-code.json",
|
|
28
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
export { ROOT_BASE, ROUTE_PREFIX, appBase, routePath, underBase } from "./routes.ts";
|
|
2
2
|
export { phoneURL } from "./phone.ts";
|
|
3
3
|
export { SIGN_IN_REQUIRED, isSignInRequired, isSignInRequiredAnswer, reSignIn, type ReSignInOptions } from "./signin.ts";
|
|
4
|
-
export {
|
|
4
|
+
export { PLUGIN_BUSY, PLUGIN_STARTING, PLUGIN_RUNNER_UNREACHABLE, PLUGIN_NOT_ANSWERING, PLUGIN_UNREACHED_CODES, } from "./unreached.ts";
|
|
5
|
+
export { TeraClient, KitRequestError, UNRECOGNIZED_RESPONSE, type FeedOptions, type StreamOptions, type TerminalOptions, type InvokeVerdict, type KitErrorBody, type TeraClientOptions, type UploadOptions, type UploadProgress, type UploadRequest, type VerbRefusal, } from "./client.ts";
|
|
5
6
|
export { STEP_UP_HEADER, STEP_UP_POLL_MS, STEP_UP_REQUIRED, STEP_UP_RESUME_PARAM, StepUpError, resumedStepUp, stepUpChallengeOf, stepUpPageURL, type StepUpChallenge, type StepUpEnd, type StepUpOptions, type StepUpWindow, } from "./stepup.ts";
|
|
6
7
|
export { BASE_DELAY_MS, MAX_DELAY_MS, REPLAY_LIMIT, REPLAY_WINDOW_MS, HEARTBEAT_GRACE_MS, REAUTHENTICATE_EVENT, FeedConnection, browserSignals, defaultOrderKey, type Clock, type FeedConnectionOptions, type FeedConnectionPhase, type FeedConnectionState, type FeedFrame, type FeedLoss, type FeedSignal, type FeedSignals, } from "./feed/connection.ts";
|
|
8
|
+
export { IMMEDIATE_LIMIT, IMMEDIATE_WINDOW_MS, MAX_BUFFERED_BYTES, SEND_BUFFER_EXCEEDED, STABLE_OPEN_MS, StreamConnection, webSocketURL, type NormalCloseNext, type StreamConnectionOptions, type StreamConnectionState, type WebSocketLike, } from "./stream/connection.ts";
|
|
9
|
+
export { ACK_EVERY, INPUT_GRANT_TIMEOUT, INPUT_GRANT_TIMEOUT_MS, INPUT_WINDOW, MAX_QUEUED_INPUT, MAX_SIGNALS_PER_GRANT, RESIZE_DEBOUNCE_MS, TerminalView, newViewId, nextAfterClose, type TerminalRenderer, type TerminalStatus, type TerminalViewOptions, } from "./terminal/view.ts";
|
|
7
10
|
export { FeedOpenError, SSEParser, fetchOpener, isRefusal, type FeedOpener, type SSEEvent } from "./feed/sse.ts";
|
|
11
|
+
export * as terminalwire from "./terminalwire/codec.ts";
|
|
8
12
|
export type * from "./generated/feed.ts";
|
|
9
13
|
export type * from "./generated/kit.ts";
|
|
10
14
|
export type * from "./generated/shares.ts";
|
|
11
15
|
export type * from "./generated/workorder.ts";
|
|
12
16
|
export type * from "./generated/workorder-events.ts";
|
|
17
|
+
export type * from "./generated/wsstream.ts";
|
|
13
18
|
export { FEED_SCHEMAS } from "./generated/feed.ts";
|
|
14
19
|
export { KIT_ROUTE_PREFIX, KIT_ROUTE_TABLE, KIT_PHONE_PATH, KIT_SCHEMAS, KIT_SIGNOUT_PATH } from "./generated/kit.ts";
|
|
15
20
|
export { SHARES_SCHEMAS } from "./generated/shares.ts";
|
|
16
21
|
export { WORK_ORDER_SCHEMAS } from "./generated/workorder.ts";
|
|
17
22
|
export { WORK_ORDER_EVENT_SCHEMAS } from "./generated/workorder-events.ts";
|
|
23
|
+
export { STREAM_CLOSE_CODES, STREAM_SCHEMAS } from "./generated/wsstream.ts";
|
package/dist/index.js
CHANGED
|
@@ -5,12 +5,20 @@
|
|
|
5
5
|
export { ROOT_BASE, ROUTE_PREFIX, appBase, routePath, underBase } from "./routes.js";
|
|
6
6
|
export { phoneURL } from "./phone.js";
|
|
7
7
|
export { SIGN_IN_REQUIRED, isSignInRequired, isSignInRequiredAnswer, reSignIn } from "./signin.js";
|
|
8
|
+
export { PLUGIN_BUSY, PLUGIN_STARTING, PLUGIN_RUNNER_UNREACHABLE, PLUGIN_NOT_ANSWERING, PLUGIN_UNREACHED_CODES, } from "./unreached.js";
|
|
8
9
|
export { TeraClient, KitRequestError, UNRECOGNIZED_RESPONSE, } from "./client.js";
|
|
9
10
|
export { STEP_UP_HEADER, STEP_UP_POLL_MS, STEP_UP_REQUIRED, STEP_UP_RESUME_PARAM, StepUpError, resumedStepUp, stepUpChallengeOf, stepUpPageURL, } from "./stepup.js";
|
|
10
11
|
export { BASE_DELAY_MS, MAX_DELAY_MS, REPLAY_LIMIT, REPLAY_WINDOW_MS, HEARTBEAT_GRACE_MS, REAUTHENTICATE_EVENT, FeedConnection, browserSignals, defaultOrderKey, } from "./feed/connection.js";
|
|
12
|
+
export { IMMEDIATE_LIMIT, IMMEDIATE_WINDOW_MS, MAX_BUFFERED_BYTES, SEND_BUFFER_EXCEEDED, STABLE_OPEN_MS, StreamConnection, webSocketURL, } from "./stream/connection.js";
|
|
13
|
+
export { ACK_EVERY, INPUT_GRANT_TIMEOUT, INPUT_GRANT_TIMEOUT_MS, INPUT_WINDOW, MAX_QUEUED_INPUT, MAX_SIGNALS_PER_GRANT, RESIZE_DEBOUNCE_MS, TerminalView, newViewId, nextAfterClose, } from "./terminal/view.js";
|
|
11
14
|
export { FeedOpenError, SSEParser, fetchOpener, isRefusal } from "./feed/sse.js";
|
|
15
|
+
// The frames a view of an attended session's terminal speaks (ADR-180 D4):
|
|
16
|
+
// github.com/kottera/sdk/terminalwire's codec in the browser, under one name,
|
|
17
|
+
// so its frame and vocabulary names claim nothing at the package's root.
|
|
18
|
+
export * as terminalwire from "./terminalwire/codec.js";
|
|
12
19
|
export { FEED_SCHEMAS } from "./generated/feed.js";
|
|
13
20
|
export { KIT_ROUTE_PREFIX, KIT_ROUTE_TABLE, KIT_PHONE_PATH, KIT_SCHEMAS, KIT_SIGNOUT_PATH } from "./generated/kit.js";
|
|
14
21
|
export { SHARES_SCHEMAS } from "./generated/shares.js";
|
|
15
22
|
export { WORK_ORDER_SCHEMAS } from "./generated/workorder.js";
|
|
16
23
|
export { WORK_ORDER_EVENT_SCHEMAS } from "./generated/workorder-events.js";
|
|
24
|
+
export { STREAM_CLOSE_CODES, STREAM_SCHEMAS } from "./generated/wsstream.js";
|