@akterjs/client 0.0.0-stage → 0.1.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 +201 -0
- package/README.md +17 -2
- package/dist/actor-fields.pure.d.mts +77 -0
- package/dist/actor-fields.pure.mjs +85 -0
- package/dist/actor-operations.pure.d.mts +49 -0
- package/dist/actor-operations.pure.mjs +512 -0
- package/dist/actor-protocol.layer.d.mts +18 -0
- package/dist/actor-protocol.layer.mjs +172 -0
- package/dist/actor-rpc.pure.d.mts +28 -0
- package/dist/actor-rpc.pure.mjs +154 -0
- package/dist/akter-client.layer.d.mts +13 -0
- package/dist/akter-client.layer.mjs +27 -0
- package/dist/promise-client.adapter.d.mts +55 -0
- package/dist/promise-client.adapter.mjs +114 -0
- package/dist/promise-files.pure.d.mts +48 -0
- package/dist/promise-files.pure.mjs +35 -0
- package/dist/read-floors.pure.d.mts +7 -0
- package/dist/read-floors.pure.mjs +49 -0
- package/dist/remote-interrupt.pure.d.mts +13 -0
- package/dist/remote-interrupt.pure.mjs +45 -0
- package/dist/upload-source.errors.d.mts +9 -0
- package/dist/upload-source.errors.mjs +5 -0
- package/package.json +68 -3
- package/src/actor-fields.pure.ts +60 -0
- package/src/actor-operations.pure.ts +484 -0
- package/src/actor-protocol.layer.ts +331 -0
- package/src/actor-rpc.pure.ts +179 -0
- package/src/akter-client.layer.ts +67 -0
- package/src/promise-client.adapter.ts +354 -0
- package/src/promise-files.pure.ts +106 -0
- package/src/read-floors.pure.ts +82 -0
- package/src/remote-interrupt.pure.ts +79 -0
- package/src/upload-source.errors.ts +9 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @akterjs/client
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Clients for calling actors from anywhere: an Effect client (`Room.client(key)`) and a Promise client (`createClient<App>`) with live query subscriptions. Runs in Bun, Node, Workers and browsers, so it imports only contracts.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm install @akterjs/client
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Docs: https://docs.akter.dev. Pricing: https://docs.akter.dev/pricing.
|
|
10
|
+
|
|
11
|
+
Status: both clients work. The package root exports `createClient({ url, actors, headers?, fetch? })`, the Promise client: `akter.Room("lobby").post({ text: "hi" })` returns a Promise that rejects with the contract's error classes, so `instanceof` works, and its public types hold no Effect types. `@akterjs/client/effect` exports `akterClient({ url, headers?, fetch?, webSocket? })`, the layer behind `Room.client(key)`: it provides `RpcClient.Protocol` and `ActorClients` (`@akterjs/effect/contract/actor-client`). Both clients speak the Effect RPC that `akter dev` and Akter Cloud serve (`docs/adr/0016-clients-speak-effect-rpc-everywhere.md`): calls, queries, `create`, `ensure` and `warm` are NDJSON posts to `<url>/rpc` over `fetch`, and each live subscription is its own WebSocket to `<url>/rpc?request=<the RPC request as JSON>` with RPC JSON frames (`src/actor-protocol.layer.ts`). `headers()` runs for every request and every socket, so a refreshed token takes effect on the next one. A socket can't carry headers from a browser, so the client offers the subprotocols `akter-rpc` and `akter-auth.<token>` for an `Authorization: Bearer <token>` header, and sends the other headers in the RPC request frame; the token never goes in the URL. `webSocket` replaces the global `WebSocket` constructor. A subscription whose socket drops resubscribes with backoff (50 ms doubling, at most 5 s apart) and skips any update at or below the version it already delivered, so a reconnect never repeats an update. Both clients read their own writes: when a server answers a command or query `Exit` with the actor's commit `version`, as Akter Cloud does, the client keeps the highest version it has seen for that actor and key and sends it as each later request's `akter-floor` RPC header, and the server never answers it from a cache older than that (`src/read-floors.pure.ts`). `akter dev` answers no versions and gets no floor. Both clients mint a fresh command id for each command and retry a lost reply (a transport failure) with the same id, so a retried command takes effect once. The contract's errors and the hosted ones arrive as their own classes, and both `@akterjs/client` and `@akterjs/client/effect` re-export every hosted one a call can fail with (`ActorAlreadyExistsError`, `ActorNotCreatedError`, `ActorUnavailableError`, `CallerNotAllowedError`, `CommandIdExpiredError`, `HandlerFailedError`, `InvalidRequestError`, `UnauthenticatedError`, and for files `BlobTransferError`, `FileAccessDeniedError`, `FileNotFoundError`, `FilesUnavailableError` and `InvalidFileRequestError`), the same classes `@akterjs/effect` and `@akterjs/effect/contract/errors` export. A request the server ends with only an interrupt, which says it stopped the request without answering, fails with `ActorUnavailableError` ("…stopped this request before it answered. Retry shortly.") rather than interrupting the caller, so a CLI prints why instead of exiting 130 with nothing (`src/remote-interrupt.pure.ts`). A bundle test builds both clients and a contract for browsers and Workers and checks that only contract files and `effect` go in. `pnpm test` runs every client test on Node. `pnpm test:browser` and `pnpm test:workerd` run only the tests `vitest.config.ts` lists for headless Chromium and workerd, which is `tests/promise-client.adapter.portable.test.ts`: it drives the Promise client against a stubbed `fetch` that answers in the wire format, with no server. The other tests need Node, because the bundle test runs esbuild and the rest serve a room with `Akter.serve`. `@akterjs/conformance/client` holds the public-contract fixture both clients pass against `akter dev` here and against Akter Cloud in akter-cloud. Each query also has `subscribe`, and each actor has `state`: `room.messages.subscribe({ limit: 50 })` and `room.state` are streams on the Effect client, and on the Promise client `akter.Room("lobby").messages.subscribe({ limit: 50 }, render, onError?)` (a query declared without a payload subscribes as `subscribe(render, onError?)`) and `akter.Room("lobby").state.subscribe(render, onError?)` return a function that stops the subscription. Each update is `{ value, version }`, where `version` is the actor's journal position of the commit it reflects. An actor declared with `create` also has `create(input)` and `ensure(input)` on both clients: `create` fails with `ActorAlreadyExistsError` (exported by `@akterjs/effect`) when the actor exists, and `ensure` creates it only when it doesn't. Each mints a command id and retries a lost reply with it, like a command, so a retried create replays its first result instead of failing. Each actor also has `warm`: `room.warm` on the Effect client and `akter.Room("lobby").warm()` on the Promise client ask the server to open the actor before its first command, so a page can start the open on load and hide it behind the user's think time. A warm runs no command, journals nothing and leaves the actor's version as it was, and a warm on a key that was never created does nothing. Each actor also has `fork(into)`: `room.fork("copy")` on the Effect client and `akter.Room("lobby").fork("copy")` on the Promise client copy the actor at the moment the fork runs into the new key and return a client or handle on the copy. The copy starts quiet (no timers, outbox or running jobs, `docs/adr/0017-forks-start-quiet.md`), and later commands to either side don't reach the other. Each fork mints a command id and retries a lost reply with it, so a retried fork makes one copy; a fork into a key that exists fails with `ActorAlreadyExistsError`. `akter dev` forks. Akter Cloud refuses the fork RPC for every caller by design (`InvalidRequestError`): a fork there is an operator action on the Cloud API (decision 5), made with `akter actors fork`, the `actors_fork` MCP tool or the Cloud API's `actorForks.create`, `POST /v1/organizations/{organization}/apps/{app}/environments/{environment}/actors/{actor}/{key}/forks`.
|
|
12
|
+
|
|
13
|
+
An actor whose contract declares `files: Akter.fileAccess({ callers })` has files on both clients; one without has no file calls, and its Promise handle has no `.files`. On the Promise client, `akter.Board("b1").files` has `upload(bytes)`, which takes a `Uint8Array` or a `ReadableStream<Uint8Array>` and resolves with the receipt, `list({ prefix?, limit?, cursor? })` and `usage(...)`, which resolve with `{ entries, cursor? }` (and `bytes` for usage), and `download(path, { offset, length }?)`, which resolves with a `ReadableStream<Uint8Array>`. The download is lazy: a missing file errors the stream with `FileNotFoundError` on its first read, not the Promise. A source stream that fails rejects the upload with `UploadSourceError`. On the Effect client, `akterClient` provides `ActorFileClients`, so `Room.files(key)` works over the network as it does in process: `upload(stream)` returns the receipt a command attaches with `Akter.files.attach(path, receipt)`, and `list`, `fileUsage` and `download(path, range?)` read what the actor holds. They are the `<Actor>/files/<operation>` RPCs ADR 0016 reserves, all NDJSON posts. Effect RPC has no client streams, so an upload is a session: `upload/open` returns an id bound to the actor, key and caller, `upload/append` sends chunks of at most 1 MiB in order and is idempotent by offset, so a lost reply is retried with the same offset, and `upload/close` checks the length and returns the receipt. A download streams chunks of at most 256 KiB. The server admits a file call only from a caller kind the declaration lists, and then runs the actor's `files.authorize` rule, which reads the state and the verified principal and refuses with `FileAccessDeniedError` unless it allows the call; a download's path is checked before the rule sees it. A fork admits only an app's operators, the `user` and `apiKey` kinds, because it runs no handler. The key must decode with the actor's key schema. A session another actor, key or caller names is refused. The server keeps at most 64 open sessions, 8 per caller and 8 per actor, and drops one idle for 60 seconds. A file past 1 GiB fails with `FileTooLargeError`. An actor holds at most 16 receipts no command has attached, and a receipt expires an hour after its upload closes.
|
|
14
|
+
|
|
15
|
+
`@akterjs/client/operations` is the actor half of the operation registry that drives the `akter` CLI and the hosted MCP server (the Cloud half is `@akterjs/cloud-api/operations`): `actorOperations` lists each exposed kind (`call`, `query`, `state`, `watch`, `create`, `ensure`, `fork`, and the file kinds `files/list`, `files/usage`, `files/download` and `files/upload`, whose three session RPCs are one kind) with its command, tool name, input schema and annotations, `kindsOf` gives every RPC of an actor's group its kind or none, so a new kind of RPC fails the coverage test until it has an operation, and `actorRequestOf` builds the RPC tag and payload a caller sends, minting a UUIDv7 command id when the kind takes one and none was given. `@akterjs/client/actor-rpc` holds the one-RPC unary and streaming clients those requests go through, with schema-free JSON payloads, and the file transfers both callers run: `uploadSession({ request, content, wait })` opens an upload, appends the bytes in chunks of at most 1 MiB at their offsets and closes it for the receipt, and `downloadChunks({ request, wait })` collects a download's base64 chunks, failing whole rather than returning part of a file.
|
|
16
|
+
|
|
17
|
+
- Depends on: `@akterjs/effect` (peer, contracts only) and `effect` (peer). `@akterjs/effect` is a peer so the app and the client share one copy: the client decodes errors into its classes, and a second copy would break `instanceof` on them.
|
|
18
|
+
- Must not depend on: any private package.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
type JsonObject = Schema.JsonObject;
|
|
3
|
+
declare const fieldSchemas: {
|
|
4
|
+
actor: {
|
|
5
|
+
description: string;
|
|
6
|
+
type: string;
|
|
7
|
+
};
|
|
8
|
+
after: {
|
|
9
|
+
description: string;
|
|
10
|
+
type: string;
|
|
11
|
+
};
|
|
12
|
+
commandId: {
|
|
13
|
+
description: string;
|
|
14
|
+
type: string;
|
|
15
|
+
};
|
|
16
|
+
computer: {
|
|
17
|
+
description: string;
|
|
18
|
+
type: string;
|
|
19
|
+
};
|
|
20
|
+
computerPath: {
|
|
21
|
+
description: string;
|
|
22
|
+
type: string;
|
|
23
|
+
};
|
|
24
|
+
content: {
|
|
25
|
+
description: string;
|
|
26
|
+
type: string;
|
|
27
|
+
};
|
|
28
|
+
cursor: {
|
|
29
|
+
description: string;
|
|
30
|
+
type: string;
|
|
31
|
+
};
|
|
32
|
+
direction: {
|
|
33
|
+
description: string;
|
|
34
|
+
enum: string[];
|
|
35
|
+
type: string;
|
|
36
|
+
};
|
|
37
|
+
into: {
|
|
38
|
+
description: string;
|
|
39
|
+
};
|
|
40
|
+
key: {
|
|
41
|
+
description: string;
|
|
42
|
+
};
|
|
43
|
+
length: {
|
|
44
|
+
description: string;
|
|
45
|
+
type: string;
|
|
46
|
+
};
|
|
47
|
+
limit: {
|
|
48
|
+
description: string;
|
|
49
|
+
type: string;
|
|
50
|
+
};
|
|
51
|
+
member: {
|
|
52
|
+
description: string;
|
|
53
|
+
type: string;
|
|
54
|
+
};
|
|
55
|
+
offset: {
|
|
56
|
+
description: string;
|
|
57
|
+
type: string;
|
|
58
|
+
};
|
|
59
|
+
path: {
|
|
60
|
+
description: string;
|
|
61
|
+
type: string;
|
|
62
|
+
};
|
|
63
|
+
payload: {
|
|
64
|
+
description: string;
|
|
65
|
+
};
|
|
66
|
+
prefix: {
|
|
67
|
+
description: string;
|
|
68
|
+
type: string;
|
|
69
|
+
};
|
|
70
|
+
step: {
|
|
71
|
+
description: string;
|
|
72
|
+
type: string;
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
type FieldName = keyof typeof fieldSchemas;
|
|
76
|
+
declare const inputOf: (fields: readonly FieldName[]) => JsonObject;
|
|
77
|
+
export { type FieldName, inputOf };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Array, Record } from "effect";
|
|
2
|
+
// src/actor-fields.pure.ts
|
|
3
|
+
const fieldSchemas = {
|
|
4
|
+
actor: {
|
|
5
|
+
description: "The actor's name in the app, such as Sale.",
|
|
6
|
+
type: "string"
|
|
7
|
+
},
|
|
8
|
+
after: {
|
|
9
|
+
description: "Only log lines after this seq, the last one a page before printed, to read on from there.",
|
|
10
|
+
type: "integer"
|
|
11
|
+
},
|
|
12
|
+
commandId: {
|
|
13
|
+
description: "An id for this call, so a retry with the same id runs it once. A new one is made when it is missing.",
|
|
14
|
+
type: "string"
|
|
15
|
+
},
|
|
16
|
+
computer: {
|
|
17
|
+
description: "The computer's name, as the actor named it.",
|
|
18
|
+
type: "string"
|
|
19
|
+
},
|
|
20
|
+
computerPath: {
|
|
21
|
+
description: "Relative path in the computer's files directory.",
|
|
22
|
+
type: "string"
|
|
23
|
+
},
|
|
24
|
+
content: {
|
|
25
|
+
description: "The file's bytes, as base64.",
|
|
26
|
+
type: "string"
|
|
27
|
+
},
|
|
28
|
+
cursor: {
|
|
29
|
+
description: "Where the page to read starts, from the cursor of the page before.",
|
|
30
|
+
type: "string"
|
|
31
|
+
},
|
|
32
|
+
direction: {
|
|
33
|
+
description: "toComputer or fromComputer; copy-back returns an upload receipt.",
|
|
34
|
+
enum: ["toComputer", "fromComputer"],
|
|
35
|
+
type: "string"
|
|
36
|
+
},
|
|
37
|
+
into: { description: "The new actor's key, as JSON. A bare word is taken as a string key." },
|
|
38
|
+
key: { description: "The actor's key, as JSON. A bare word is taken as a string key." },
|
|
39
|
+
length: {
|
|
40
|
+
description: "How many bytes to read, from offset; give both or neither.",
|
|
41
|
+
type: "integer"
|
|
42
|
+
},
|
|
43
|
+
limit: {
|
|
44
|
+
description: "The most entries to return.",
|
|
45
|
+
type: "integer"
|
|
46
|
+
},
|
|
47
|
+
member: {
|
|
48
|
+
description: "The command or query to run.",
|
|
49
|
+
type: "string"
|
|
50
|
+
},
|
|
51
|
+
offset: {
|
|
52
|
+
description: "The byte to start reading at, with length; give both or neither.",
|
|
53
|
+
type: "integer"
|
|
54
|
+
},
|
|
55
|
+
path: {
|
|
56
|
+
description: "The file's path in the actor, such as photos/cat.png.",
|
|
57
|
+
type: "string"
|
|
58
|
+
},
|
|
59
|
+
payload: { description: "The command's, query's or create input's payload, as JSON." },
|
|
60
|
+
prefix: {
|
|
61
|
+
description: "Only paths that start with this.",
|
|
62
|
+
type: "string"
|
|
63
|
+
},
|
|
64
|
+
step: {
|
|
65
|
+
description: "The step's id, as steps list prints it.",
|
|
66
|
+
type: "string"
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const optional = [
|
|
70
|
+
"after",
|
|
71
|
+
"commandId",
|
|
72
|
+
"cursor",
|
|
73
|
+
"length",
|
|
74
|
+
"limit",
|
|
75
|
+
"offset",
|
|
76
|
+
"payload",
|
|
77
|
+
"prefix"
|
|
78
|
+
];
|
|
79
|
+
const inputOf = (fields) => ({
|
|
80
|
+
additionalProperties: false,
|
|
81
|
+
properties: Record.fromEntries(Array.map(fields, (name) => [name, fieldSchemas[name]])),
|
|
82
|
+
required: Array.filter(fields, (name) => !Array.contains(optional, name)),
|
|
83
|
+
type: "object"
|
|
84
|
+
});
|
|
85
|
+
export { inputOf };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { FieldName } from "./actor-fields.pure.mjs";
|
|
2
|
+
import { Effect, Option, Schema } from "effect";
|
|
3
|
+
import { SecureRandom } from "effect/crypto";
|
|
4
|
+
import { Rpc } from "effect/rpc";
|
|
5
|
+
type JsonObject = Schema.JsonObject;
|
|
6
|
+
interface Annotations {
|
|
7
|
+
readonly confirm: boolean;
|
|
8
|
+
readonly destructive: boolean;
|
|
9
|
+
readonly idempotent: boolean;
|
|
10
|
+
readonly openWorld: boolean;
|
|
11
|
+
readonly readOnly: boolean;
|
|
12
|
+
}
|
|
13
|
+
type ActorKind = "call" | "computers/cp" | "computers/delete" | "computers/get" | "computers/list" | "computers/logs" | "computers/pause" | "computers/resume" | "computers/stop" | "create" | "ensure" | "files/download" | "files/list" | "files/upload" | "files/usage" | "fork" | "query" | "state" | "steps/get" | "steps/list" | "warm" | "watch";
|
|
14
|
+
interface ActorOperation {
|
|
15
|
+
readonly annotations: Annotations;
|
|
16
|
+
readonly flag: Option.Option<string>;
|
|
17
|
+
readonly command: readonly [string, string];
|
|
18
|
+
readonly description: string;
|
|
19
|
+
readonly fields: readonly FieldName[];
|
|
20
|
+
readonly id: string;
|
|
21
|
+
readonly input: JsonObject;
|
|
22
|
+
readonly kind: ActorKind;
|
|
23
|
+
readonly stream: boolean;
|
|
24
|
+
readonly tool: string;
|
|
25
|
+
}
|
|
26
|
+
declare const unexposedKinds: readonly ActorKind[];
|
|
27
|
+
declare const actorOperations: readonly ActorOperation[];
|
|
28
|
+
interface Addressed {
|
|
29
|
+
readonly actor: string;
|
|
30
|
+
readonly kind: ActorKind;
|
|
31
|
+
readonly member: string;
|
|
32
|
+
}
|
|
33
|
+
declare const tagOf: ({ actor, kind, member }: Addressed) => string;
|
|
34
|
+
declare const kindsOf: (requests: ReadonlyMap<string, Rpc.AnyWithProps>) => readonly (readonly [string, Option.Option<ActorKind>])[];
|
|
35
|
+
interface ActorInput {
|
|
36
|
+
readonly actor: string;
|
|
37
|
+
readonly commandId: Option.Option<string>;
|
|
38
|
+
readonly fileFields: Readonly<Record<string, Schema.Json>>;
|
|
39
|
+
readonly into: Option.Option<Schema.Json>;
|
|
40
|
+
readonly key: Schema.Json;
|
|
41
|
+
readonly member: string;
|
|
42
|
+
readonly payload: Option.Option<Schema.Json>;
|
|
43
|
+
}
|
|
44
|
+
interface ActorRequest {
|
|
45
|
+
readonly payload: Readonly<Record<string, Schema.Json>>;
|
|
46
|
+
readonly tag: string;
|
|
47
|
+
}
|
|
48
|
+
declare const actorRequestOf: (operation: ActorOperation) => ((input: ActorInput) => Effect.Effect<ActorRequest, never, SecureRandom.SecureRandom>);
|
|
49
|
+
export { type ActorInput, type ActorKind, type ActorOperation, type ActorRequest, type Annotations, type FieldName, actorOperations, actorRequestOf, kindsOf, tagOf, unexposedKinds };
|