@akterjs/cli 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 +47 -2
- package/dist/Argument-D81dqizB.mjs +57 -0
- package/dist/Array-B4kuTQeW.mjs +2323 -0
- package/dist/Boolean-kILIvmhE.mjs +26 -0
- package/dist/ChildProcess-BCIwxPGc.mjs +577 -0
- package/dist/CliError-Cf5xqkkj.mjs +633 -0
- package/dist/Command-Dzq9KM-h.mjs +5135 -0
- package/dist/Config-B6OND9WR.mjs +854 -0
- package/dist/Console-BKHUkH6a.mjs +141 -0
- package/dist/Data-CeCULr0-.mjs +251 -0
- package/dist/Digest-CBd3uv5B.mjs +348 -0
- package/dist/Duration-EH9Vy4U6.mjs +791 -0
- package/dist/Effect-CKFpqbmw.mjs +12911 -0
- package/dist/FileSystem-4pPcEb_i.mjs +1366 -0
- package/dist/HttpClientError-BUeMWx70.mjs +303 -0
- package/dist/HttpClientResponse-D5ZKICVV.mjs +855 -0
- package/dist/Match-CGPLZYrV.mjs +434 -0
- package/dist/MutableHashMap-f8nW8AJR.mjs +376 -0
- package/dist/MutableHashSet-_HI572Ya.mjs +155 -0
- package/dist/MutableRef-D4Qv592C.mjs +107 -0
- package/dist/Number-BvkRkfc-.mjs +137 -0
- package/dist/Option-DkxSnBhp.mjs +3967 -0
- package/dist/Order-C1JC991I.mjs +481 -0
- package/dist/Path-Djdv_eF5.mjs +130 -0
- package/dist/Queue-CO5UQ6os.mjs +1394 -0
- package/dist/Redacted-RkJrG5Me.mjs +160 -0
- package/dist/Ref-BIPOxJNH.mjs +267 -0
- package/dist/Runtime-BInIAwK3.mjs +297 -0
- package/dist/Schema-MiKt5K1v.mjs +10999 -0
- package/dist/SecureRandom-B_U4ysO3.mjs +279 -0
- package/dist/Semaphore-DOVJ4ZGS.mjs +177 -0
- package/dist/Stdio-KylmOBX_.mjs +74 -0
- package/dist/Stream-bK9QIETN.mjs +4012 -0
- package/dist/String-q5dcsVe4.mjs +351 -0
- package/dist/TerminalCapabilities-w_44MAca.mjs +1769 -0
- package/dist/WebCrypto-ZI_GNVlw.mjs +33 -0
- package/dist/actor-call.service-CGM_vD9u.mjs +1269 -0
- package/dist/actor-client.adapter-Coem7gF1.mjs +3691 -0
- package/dist/akter-program.config-C4cXx4Fr.mjs +7034 -0
- package/dist/akter.cli.mjs +5 -0
- package/dist/app-cli.config.mjs +49 -0
- package/dist/app-entry.pure.mjs +31 -0
- package/dist/archive/akter-archive.schema.mjs +65 -0
- package/dist/archive/akter-archive.service.mjs +309 -0
- package/dist/archive/archive-cli.config.mjs +45 -0
- package/dist/archive/archive-invalid.errors.mjs +5 -0
- package/dist/archive/archived-lineage.pure.mjs +13 -0
- package/dist/archive/tar.pure.mjs +291 -0
- package/dist/archive/zstd.adapter.mjs +46 -0
- package/dist/bench/bench-report.pure.mjs +120 -0
- package/dist/bench/bench-runner.pure.mjs +89 -0
- package/dist/bench/bench.service.mjs +301 -0
- package/dist/cli-runtime.layer-BqK2mmVL.mjs +1191 -0
- package/dist/cli-runtime.layer.mjs +1 -0
- package/dist/cloud-client.adapter-Cy1BjDbG.mjs +3186 -0
- package/dist/cloud-client.adapter-DOVMoYAj.mjs +304 -0
- package/dist/command/akter-commands.config.mjs +32 -0
- package/dist/command/akter-program.config.mjs +1 -0
- package/dist/credentials/credentials.service.mjs +226 -0
- package/dist/credentials/keychain.adapter.mjs +92 -0
- package/dist/credentials/login.config.mjs +251 -0
- package/dist/deploy/auth/api-attempt.service.mjs +30 -0
- package/dist/deploy/auth/token-refresh.errors.mjs +4 -0
- package/dist/deploy/build-bundle.adapter.mjs +133 -0
- package/dist/deploy/build-manifest.pure.mjs +44 -0
- package/dist/deploy/cloud-client.adapter.mjs +1 -0
- package/dist/deploy/cloud-url.pure.mjs +17 -0
- package/dist/deploy/deploy-failed.errors.mjs +17 -0
- package/dist/deploy/deploy.service.mjs +156 -0
- package/dist/deploy/request-failure.pure.mjs +87 -0
- package/dist/dev-runner.pure.mjs +30 -0
- package/dist/docs/doc-topics.pure.mjs +21 -0
- package/dist/docs/docs.config.mjs +1 -0
- package/dist/docs.config-_21Xi6HU.mjs +190 -0
- package/dist/export/actor-export.service.mjs +267 -0
- package/dist/export/export-failed.errors.mjs +4 -0
- package/dist/export/export-failure.pure.mjs +18 -0
- package/dist/export/ordered-lines.pure.mjs +43 -0
- package/dist/export/ranged-download.pure.mjs +66 -0
- package/dist/export/snapshot-download.pure.mjs +86 -0
- package/dist/history-runner.pure.mjs +186 -0
- package/dist/history-since.pure.mjs +17 -0
- package/dist/hosted-runtime.layer-Cx3IPsEd.mjs +16358 -0
- package/dist/local/environment-cli.config.mjs +90 -0
- package/dist/local/environment-runner.pure.mjs +75 -0
- package/dist/local/local-cli.config.mjs +159 -0
- package/dist/logs/log-follow.config.mjs +21 -0
- package/dist/logs/log-follow.service.mjs +87 -0
- package/dist/logs/log-lines.pure.mjs +44 -0
- package/dist/migrate-runner.pure.mjs +28 -0
- package/dist/operation-input.pure-CS9iUhqK.mjs +1383 -0
- package/dist/operation-registry.pure-DVC8EDgj.mjs +283 -0
- package/dist/registry/actor-call.service.mjs +1 -0
- package/dist/registry/actor-commands.config.mjs +127 -0
- package/dist/registry/actor-refusal.pure.mjs +34 -0
- package/dist/registry/approval.service.mjs +80 -0
- package/dist/registry/cloud-commands.config.mjs +151 -0
- package/dist/registry/cloud-served.pure.mjs +31 -0
- package/dist/registry/operation-call.service.mjs +262 -0
- package/dist/registry/operation-input.pure.mjs +1 -0
- package/dist/registry/operation-output.pure.mjs +91 -0
- package/dist/registry/served-command.config.mjs +57 -0
- package/dist/runner-exited.errors.mjs +10 -0
- package/dist/terminal/cli-exit.errors.mjs +38 -0
- package/dist/terminal/cli-failure.pure.mjs +31 -0
- package/dist/terminal/confirm.adapter.mjs +27 -0
- package/package.json +61 -3
|
@@ -0,0 +1,1269 @@
|
|
|
1
|
+
import { R as some, T as join, j as map, k as makeBy, l as filter, r as contains, s as ensure, y as head } from "./Array-B4kuTQeW.mjs";
|
|
2
|
+
import { t as match } from "./Boolean-kILIvmhE.mjs";
|
|
3
|
+
import { a as option, i as String$1 } from "./Config-B6OND9WR.mjs";
|
|
4
|
+
import { t as error } from "./Console-BKHUkH6a.mjs";
|
|
5
|
+
import { $t as suspend, A as ensuring, An as spaced, At as provideContext, B as flatMap, Bn as build, Dt as orElseSucceed, En as exponential, Et as orDie, Ft as repeat, Gt as scoped, Jn as succeed, K as forEach, Kn as provide, Lt as retry, M as fail, Nt as raceFirst, Q as fromOption, T as context, Vn as effect, W as fnUntraced, Wn as merge, X as forkScoped, Xt as succeed$1, Yt as sleep, _n as when, a as all, aa as Service, d as asVoid, dt as map$1, en as sync, et as gen, ft as mapBoth, g as catchDefect, gn as void_, ht as mapError, in as timeout, l as as, ln as try_, n as acquireRelease, o as andThen, q as forever, qn as provideMerge, sn as transposeOption, tt as ignore, x as catchTag, yt as matchEffect } from "./Effect-CKFpqbmw.mjs";
|
|
6
|
+
import { t as FileSystem } from "./FileSystem-4pPcEb_i.mjs";
|
|
7
|
+
import { C as orElse, S as none, T as toArray, _ as isSome, b as map$2, c as flatten, cn as isString, d as fromUndefinedOr, f as getOrElse, h as isNone, i as filter$1, o as flatMap$1, p as getOrNull, t as all$1, u as fromNullishOr, un as isTagged, v as liftPredicate, w as some$1, x as match$1 } from "./Option-DkxSnBhp.mjs";
|
|
8
|
+
import { B as TaggedStruct, F as String$2, I as Struct, J as UndefinedOr, T as NonEmptyArray, U as Tuple, X as Unknown, Y as Union, an as optionalKey, ar as filter$2, at as decodeUnknownEffect, cr as getSomes, ct as encodeOption, ft as encodeUnknownOption, g as Finite, hr as toEntries, mt as is, n as ArraySchema, nt as decodeOption, or as fromEntries, ot as decodeUnknownOption, pt as fromJsonString, q as Uint8ArrayFromBase64, sr as get, st as encodeEffect, tt as decodeEffect, un as toEncoded, v as Int, y as Json } from "./Schema-MiKt5K1v.mjs";
|
|
9
|
+
import { i as value } from "./Redacted-RkJrG5Me.mjs";
|
|
10
|
+
import { n as getUnsafe, o as set, r as make, t as get$1 } from "./Ref-BIPOxJNH.mjs";
|
|
11
|
+
import { t as Stdio } from "./Stdio-KylmOBX_.mjs";
|
|
12
|
+
import { H as runFold, I as provideContext$1, T as make$2, U as runFoldEffect, W as runForEach, Y as take, dt as make$1, k as mapError$1, m as flatMap$2, nt as unwrap, p as filter$3, pt as open, y as fromIterable, z as run } from "./Stream-bK9QIETN.mjs";
|
|
13
|
+
import { d as replace, g as toLowerCase, h as startsWith, m as split, p as slice, r as endsWith } from "./String-q5dcsVe4.mjs";
|
|
14
|
+
import { D as uploadChunkBytes$1, E as fileTag, It as make$6, Lt as remove, Ot as fromWebSocket, Rt as run$1, T as fileRpcsOf, X as ActorUnavailableError, et as Protocol, ft as isTerminalResponse, mt as RpcClientError, n as ActorFileClients, nt as makeProtocolHttp, o as Authentication, ot as layerNdjson, pt as RpcClientDefect, t as ActorClients, tt as make$4, ut as constPing, wt as make$3, yt as make$5 } from "./actor-client.adapter-Coem7gF1.mjs";
|
|
15
|
+
import { n as layerWebCrypto, t as SecureRandom } from "./SecureRandom-B_U4ysO3.mjs";
|
|
16
|
+
import { n as Fetch, r as layer, t as CloudClient } from "./cloud-client.adapter-DOVMoYAj.mjs";
|
|
17
|
+
import { f as HttpClient, g as mapRequest } from "./cloud-client.adapter-Cy1BjDbG.mjs";
|
|
18
|
+
import { P as currentTimeMillis, S as prependUrl, w as setHeaders } from "./HttpClientResponse-D5ZKICVV.mjs";
|
|
19
|
+
import { l as millis } from "./Duration-EH9Vy4U6.mjs";
|
|
20
|
+
import { a as tags, r as orElse$1, s as value$1 } from "./Match-CGPLZYrV.mjs";
|
|
21
|
+
import { Credentials } from "./credentials/credentials.service.mjs";
|
|
22
|
+
import { refusalCode, rejectedRequest, unansweredOf } from "./registry/actor-refusal.pure.mjs";
|
|
23
|
+
import { compactJson, printJson, reported, signInFailure } from "./registry/operation-output.pure.mjs";
|
|
24
|
+
import { RunnerExitedError } from "./runner-exited.errors.mjs";
|
|
25
|
+
import { CliExitError } from "./terminal/cli-exit.errors.mjs";
|
|
26
|
+
// ../client/src/actor-rpc.pure.ts
|
|
27
|
+
const commandFields = {
|
|
28
|
+
commandId: String$2,
|
|
29
|
+
key: Json,
|
|
30
|
+
payload: Json
|
|
31
|
+
};
|
|
32
|
+
const queryFields = {
|
|
33
|
+
key: Json,
|
|
34
|
+
payload: Json
|
|
35
|
+
};
|
|
36
|
+
const keyFields = { key: Json };
|
|
37
|
+
const computerFields = {
|
|
38
|
+
computer: Json,
|
|
39
|
+
key: Json
|
|
40
|
+
};
|
|
41
|
+
const listFields = {
|
|
42
|
+
cursor: optionalKey(Json),
|
|
43
|
+
key: Json,
|
|
44
|
+
limit: optionalKey(Json),
|
|
45
|
+
prefix: optionalKey(Json)
|
|
46
|
+
};
|
|
47
|
+
const fieldsOf = {
|
|
48
|
+
call: commandFields,
|
|
49
|
+
"computers/cp": {
|
|
50
|
+
...computerFields,
|
|
51
|
+
computerPath: Json,
|
|
52
|
+
direction: Json,
|
|
53
|
+
path: Json
|
|
54
|
+
},
|
|
55
|
+
"computers/delete": computerFields,
|
|
56
|
+
"computers/get": computerFields,
|
|
57
|
+
"computers/list": keyFields,
|
|
58
|
+
"computers/logs": {
|
|
59
|
+
...computerFields,
|
|
60
|
+
after: optionalKey(Json),
|
|
61
|
+
limit: optionalKey(Json)
|
|
62
|
+
},
|
|
63
|
+
"computers/pause": computerFields,
|
|
64
|
+
"computers/resume": computerFields,
|
|
65
|
+
"computers/stop": computerFields,
|
|
66
|
+
create: commandFields,
|
|
67
|
+
ensure: commandFields,
|
|
68
|
+
"files/download": {
|
|
69
|
+
key: Json,
|
|
70
|
+
length: optionalKey(Json),
|
|
71
|
+
offset: optionalKey(Json),
|
|
72
|
+
path: Json
|
|
73
|
+
},
|
|
74
|
+
"files/list": listFields,
|
|
75
|
+
"files/upload": keyFields,
|
|
76
|
+
"files/upload/append": {
|
|
77
|
+
bytes: Json,
|
|
78
|
+
key: Json,
|
|
79
|
+
offset: Json,
|
|
80
|
+
upload: Json
|
|
81
|
+
},
|
|
82
|
+
"files/upload/close": {
|
|
83
|
+
bytes: Json,
|
|
84
|
+
key: Json,
|
|
85
|
+
upload: Json
|
|
86
|
+
},
|
|
87
|
+
"files/upload/open": keyFields,
|
|
88
|
+
"files/usage": listFields,
|
|
89
|
+
fork: {
|
|
90
|
+
commandId: String$2,
|
|
91
|
+
into: Json,
|
|
92
|
+
key: Json
|
|
93
|
+
},
|
|
94
|
+
query: queryFields,
|
|
95
|
+
state: keyFields,
|
|
96
|
+
"steps/get": {
|
|
97
|
+
key: Json,
|
|
98
|
+
step: Json
|
|
99
|
+
},
|
|
100
|
+
"steps/list": {
|
|
101
|
+
key: Json,
|
|
102
|
+
limit: optionalKey(Json)
|
|
103
|
+
},
|
|
104
|
+
warm: keyFields,
|
|
105
|
+
watch: queryFields
|
|
106
|
+
};
|
|
107
|
+
function unaryClient({ kind, tag }) {
|
|
108
|
+
const rpc = make$3(tag, {
|
|
109
|
+
error: Json,
|
|
110
|
+
payload: Struct(fieldsOf[kind]),
|
|
111
|
+
success: UndefinedOr(Json)
|
|
112
|
+
});
|
|
113
|
+
return make$4(make$5(rpc), { flatten: true });
|
|
114
|
+
}
|
|
115
|
+
function streamClient({ kind, tag }) {
|
|
116
|
+
const rpc = make$3(tag, {
|
|
117
|
+
error: Json,
|
|
118
|
+
payload: Struct(fieldsOf[kind]),
|
|
119
|
+
stream: true,
|
|
120
|
+
success: Json
|
|
121
|
+
});
|
|
122
|
+
return make$4(make$5(rpc), { flatten: true });
|
|
123
|
+
}
|
|
124
|
+
const uploadChunkBytes = 1048576;
|
|
125
|
+
const Base64$1 = Uint8ArrayFromBase64;
|
|
126
|
+
const Opened = Struct({ upload: String$2 });
|
|
127
|
+
const appended = fnUntraced(function* appended({ content, request, upload, wait }) {
|
|
128
|
+
const tag = `${request.tag}/append`;
|
|
129
|
+
const append = yield* unaryClient({
|
|
130
|
+
kind: "files/upload/append",
|
|
131
|
+
tag
|
|
132
|
+
});
|
|
133
|
+
const count = Math.ceil(content.length / uploadChunkBytes);
|
|
134
|
+
const chunks = makeBy(count, (index) => ({
|
|
135
|
+
bytes: content.subarray(index * uploadChunkBytes, index * uploadChunkBytes + uploadChunkBytes),
|
|
136
|
+
offset: index * uploadChunkBytes
|
|
137
|
+
}));
|
|
138
|
+
yield* forEach(chunks, ({ bytes, offset }) => flatMap(orDie(encodeEffect(Base64$1)(bytes)), (encoded) => timeout(append(tag, {
|
|
139
|
+
...request.payload,
|
|
140
|
+
bytes: encoded,
|
|
141
|
+
offset,
|
|
142
|
+
upload
|
|
143
|
+
}), wait)));
|
|
144
|
+
});
|
|
145
|
+
const uploadSession = (upload) => gen(function* runUploadSession() {
|
|
146
|
+
const { content, request, wait } = upload;
|
|
147
|
+
const open = yield* unaryClient({
|
|
148
|
+
kind: "files/upload/open",
|
|
149
|
+
tag: `${request.tag}/open`
|
|
150
|
+
});
|
|
151
|
+
const close = yield* unaryClient({
|
|
152
|
+
kind: "files/upload/close",
|
|
153
|
+
tag: `${request.tag}/close`
|
|
154
|
+
});
|
|
155
|
+
const opened = yield* timeout(open(`${request.tag}/open`, request.payload), wait);
|
|
156
|
+
const session = yield* orDie(decodeUnknownEffect(Opened)(opened));
|
|
157
|
+
yield* appended({
|
|
158
|
+
content,
|
|
159
|
+
request,
|
|
160
|
+
upload: session.upload,
|
|
161
|
+
wait
|
|
162
|
+
});
|
|
163
|
+
const closing = {
|
|
164
|
+
...request.payload,
|
|
165
|
+
bytes: content.length,
|
|
166
|
+
upload: session.upload
|
|
167
|
+
};
|
|
168
|
+
return yield* timeout(close(`${request.tag}/close`, closing), wait);
|
|
169
|
+
});
|
|
170
|
+
// ../effect/src/contract/file/rpc/rpc-file-clients.layer.ts
|
|
171
|
+
const retryMillis = 50;
|
|
172
|
+
const retryLimit = 5;
|
|
173
|
+
const firstByte = 0;
|
|
174
|
+
const one = 1;
|
|
175
|
+
function wireOf(actor) {
|
|
176
|
+
const group = make$5(...fileRpcsOf(actor)).middleware(Authentication);
|
|
177
|
+
return map$1(make$4(group, { flatten: true }), (client) => ({
|
|
178
|
+
send: client,
|
|
179
|
+
watch: client
|
|
180
|
+
}));
|
|
181
|
+
}
|
|
182
|
+
const unavailable = (actor, message) => ActorUnavailableError.make({
|
|
183
|
+
actor,
|
|
184
|
+
message
|
|
185
|
+
});
|
|
186
|
+
/**
|
|
187
|
+
* What a network file client answers for a failure: a lost reply, after its retries, means the actor is unavailable,
|
|
188
|
+
* and the actor RPC failures a file request can't cause fold into the same retryable answer.
|
|
189
|
+
*/
|
|
190
|
+
const clientFailure = (actor) => (failure) => value$1(failure).pipe(tags({
|
|
191
|
+
ActorNotCreatedError: () => unavailable(actor, "the actor's files are unavailable"),
|
|
192
|
+
CommandIdExpiredError: () => unavailable(actor, "the actor's files are unavailable"),
|
|
193
|
+
HandlerFailedError: () => unavailable(actor, "the actor's files are unavailable"),
|
|
194
|
+
RpcClientError: () => unavailable(actor, "the server didn't answer. Retry shortly.")
|
|
195
|
+
}), orElse$1((other) => other));
|
|
196
|
+
const tagOf$1 = (address, operation) => fileTag({
|
|
197
|
+
actor: address.actor,
|
|
198
|
+
operation
|
|
199
|
+
});
|
|
200
|
+
/** One unary file RPC. A lost reply is retried, which every file RPC allows: reads, and appends by their offset. */
|
|
201
|
+
const call = ({ address, protocol }, operation, payload) => {
|
|
202
|
+
const sent = flatMap(wireOf(address.actor), (wire) => wire.send(tagOf$1(address, operation), {
|
|
203
|
+
...payload,
|
|
204
|
+
key: address.key
|
|
205
|
+
}));
|
|
206
|
+
const retried = retry(scoped(sent), {
|
|
207
|
+
schedule: exponential(millis(retryMillis)),
|
|
208
|
+
times: retryLimit,
|
|
209
|
+
while: (failure) => isTagged(failure, "RpcClientError")
|
|
210
|
+
});
|
|
211
|
+
return provideContext(mapError(retried, clientFailure(address.actor)), protocol);
|
|
212
|
+
};
|
|
213
|
+
const listingOf = ({ cursor, limit, prefix }) => ({
|
|
214
|
+
...getSomes({
|
|
215
|
+
cursor: fromNullishOr(cursor),
|
|
216
|
+
prefix: fromNullishOr(prefix)
|
|
217
|
+
}),
|
|
218
|
+
...getSomes({ limit: fromNullishOr(limit) })
|
|
219
|
+
});
|
|
220
|
+
const slices = (bytes) => makeBy(Math.max(one, Math.ceil(bytes.length / uploadChunkBytes$1)), (index) => bytes.subarray(index * uploadChunkBytes$1, (index + one) * uploadChunkBytes$1));
|
|
221
|
+
const appendAll = (bound, upload, source) => source.pipe(flatMap$2((chunk) => fromIterable(slices(chunk))), filter$3((chunk) => chunk.length > firstByte), runFoldEffect(() => firstByte, (offset, bytes) => as(call(bound, "upload/append", {
|
|
222
|
+
bytes,
|
|
223
|
+
offset,
|
|
224
|
+
upload
|
|
225
|
+
}), offset + bytes.length)));
|
|
226
|
+
/** An upload is a session: `upload/open`, the source's bytes as ordered `upload/append` chunks, then `upload/close`. */
|
|
227
|
+
const uploadWith = (bound) => (source) => gen(function* upload() {
|
|
228
|
+
const opened = yield* call(bound, "upload/open", {});
|
|
229
|
+
const bytes = yield* appendAll(bound, opened.upload, source);
|
|
230
|
+
return (yield* call(bound, "upload/close", {
|
|
231
|
+
bytes,
|
|
232
|
+
upload: opened.upload
|
|
233
|
+
})).receipt;
|
|
234
|
+
});
|
|
235
|
+
const downloadWith = ({ address, protocol }) => (path, range) => {
|
|
236
|
+
const bytes = unwrap(map$1(wireOf(address.actor), (wire) => wire.watch(tagOf$1(address, "download"), {
|
|
237
|
+
key: address.key,
|
|
238
|
+
path,
|
|
239
|
+
...range
|
|
240
|
+
})));
|
|
241
|
+
return provideContext$1(mapError$1(bytes, clientFailure(address.actor)), protocol);
|
|
242
|
+
};
|
|
243
|
+
const clientWith = (bound) => ({
|
|
244
|
+
download: downloadWith(bound),
|
|
245
|
+
fileUsage: (listing = {}) => call(bound, "usage", listingOf(listing)),
|
|
246
|
+
list: (listing = {}) => call(bound, "list", listingOf(listing)),
|
|
247
|
+
upload: uploadWith(bound)
|
|
248
|
+
});
|
|
249
|
+
/**
|
|
250
|
+
* File clients that reach an actor's files over its `<Actor>/files/<operation>` RPCs, for `@akterjs/client` and the
|
|
251
|
+
* in-process client. They give the same `FileClient` an actor's own host gives, so customer code is the same either
|
|
252
|
+
* way.
|
|
253
|
+
*/
|
|
254
|
+
const rpcFileClients = effect(ActorFileClients, map$1(context(), (protocol) => ({ connect: (address) => clientWith({
|
|
255
|
+
address,
|
|
256
|
+
protocol
|
|
257
|
+
}) })));
|
|
258
|
+
// ../client/src/read-floors.pure.ts
|
|
259
|
+
const floorHeader = "akter-floor";
|
|
260
|
+
const Keyed = Struct({ key: Unknown });
|
|
261
|
+
const Versioned = Struct({
|
|
262
|
+
requestId: Union([String$2, Finite]),
|
|
263
|
+
version: Int
|
|
264
|
+
});
|
|
265
|
+
const KeyText = fromJsonString(Unknown);
|
|
266
|
+
const keyedOf = decodeUnknownOption(Keyed);
|
|
267
|
+
const versionedOf = decodeUnknownOption(Versioned);
|
|
268
|
+
const keyTextOf = encodeUnknownOption(KeyText);
|
|
269
|
+
const actorKeyOf = ({ payload, tag }) => flatMap$1(keyedOf(payload), ({ key }) => map$2(keyTextOf(key), (keyText) => `${getOrElse(head(split(tag, "/")), () => tag)}\n${keyText}`));
|
|
270
|
+
const pendingOf = (clientId, requestId) => `${clientId}/${requestId}`;
|
|
271
|
+
const trackedFloors = () => {
|
|
272
|
+
const floors = /* @__PURE__ */ new Map();
|
|
273
|
+
const pending = /* @__PURE__ */ new Map();
|
|
274
|
+
const floored = (clientId, request) => match$1(actorKeyOf(request), {
|
|
275
|
+
onNone: () => request,
|
|
276
|
+
onSome: (actorKey) => {
|
|
277
|
+
pending.set(pendingOf(clientId, request.id), actorKey);
|
|
278
|
+
return match$1(fromNullishOr(floors.get(actorKey)), {
|
|
279
|
+
onNone: () => request,
|
|
280
|
+
onSome: (floor) => ({
|
|
281
|
+
...request,
|
|
282
|
+
headers: [...request.headers, [floorHeader, String(floor)]]
|
|
283
|
+
})
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
const sent = (clientId, message) => {
|
|
288
|
+
if (isTagged(message, "Request")) return floored(clientId, message);
|
|
289
|
+
return message;
|
|
290
|
+
};
|
|
291
|
+
const received = (clientId, response) => {
|
|
292
|
+
if (!isTagged(response, "Exit")) return;
|
|
293
|
+
const id = pendingOf(clientId, response.requestId);
|
|
294
|
+
const actorKey = pending.get(id);
|
|
295
|
+
pending.delete(id);
|
|
296
|
+
map$2(all$1([fromNullishOr(actorKey), versionedOf(response)]), ([key, { version }]) => {
|
|
297
|
+
floors.set(key, Math.max(version, floors.get(key) ?? version));
|
|
298
|
+
});
|
|
299
|
+
};
|
|
300
|
+
return {
|
|
301
|
+
received,
|
|
302
|
+
sent
|
|
303
|
+
};
|
|
304
|
+
};
|
|
305
|
+
// ../client/src/remote-interrupt.pure.ts
|
|
306
|
+
const InterruptReasons = NonEmptyArray(TaggedStruct("Interrupt", {}));
|
|
307
|
+
const InterruptedOnly = Struct({ exit: TaggedStruct("Failure", { cause: InterruptReasons }) });
|
|
308
|
+
const UnavailableWire = toEncoded(ActorUnavailableError);
|
|
309
|
+
const FailReason = TaggedStruct("Fail", { error: UnavailableWire });
|
|
310
|
+
/** An encoded exit that failed with one error, as Effect RPC writes it. */
|
|
311
|
+
const FailedExit = TaggedStruct("Failure", { cause: Tuple([FailReason]) });
|
|
312
|
+
const isInterruptedOnly = is(InterruptedOnly);
|
|
313
|
+
const encodeUnavailable = encodeOption(ActorUnavailableError);
|
|
314
|
+
const message = "the server stopped this request before it answered. Retry shortly.";
|
|
315
|
+
const requestOf$1 = (clientId, requestId) => `${clientId}/${requestId}`;
|
|
316
|
+
/** `Room/files/list` and `Room/post` are both Room's. */
|
|
317
|
+
const actorOf = (tag) => getOrElse(head(split(tag, "/")), () => tag);
|
|
318
|
+
const failedWith = (error) => FailedExit.make({ cause: [FailReason.make({ error })] });
|
|
319
|
+
const answered$1 = (actor, response) => match$1(encodeUnavailable(ActorUnavailableError.make({
|
|
320
|
+
actor,
|
|
321
|
+
message
|
|
322
|
+
})), {
|
|
323
|
+
onNone: () => response,
|
|
324
|
+
onSome: (error) => ({
|
|
325
|
+
...response,
|
|
326
|
+
exit: failedWith(error)
|
|
327
|
+
})
|
|
328
|
+
});
|
|
329
|
+
const remoteInterrupts = () => {
|
|
330
|
+
const actors = /* @__PURE__ */ new Map();
|
|
331
|
+
const sent = (clientId, outgoing) => {
|
|
332
|
+
if (isTagged(outgoing, "Request")) actors.set(requestOf$1(clientId, outgoing.id), actorOf(outgoing.tag));
|
|
333
|
+
};
|
|
334
|
+
const received = (clientId, response) => {
|
|
335
|
+
if (!isTagged(response, "Exit")) return response;
|
|
336
|
+
const id = requestOf$1(clientId, response.requestId);
|
|
337
|
+
const actor = fromNullishOr(actors.get(id));
|
|
338
|
+
actors.delete(id);
|
|
339
|
+
if (isNone(actor) || !isInterruptedOnly(response)) return response;
|
|
340
|
+
return answered$1(actor.value, response);
|
|
341
|
+
};
|
|
342
|
+
return {
|
|
343
|
+
received,
|
|
344
|
+
sent
|
|
345
|
+
};
|
|
346
|
+
};
|
|
347
|
+
// ../client/src/actor-protocol.layer.ts
|
|
348
|
+
const streamSuffix = "/subscribe";
|
|
349
|
+
const liveProtocol = "akter-rpc";
|
|
350
|
+
const authProtocol = "akter-auth.";
|
|
351
|
+
const authorizationHeader = "authorization";
|
|
352
|
+
const bearerPrefix = "Bearer ";
|
|
353
|
+
const pingEvery = "10 seconds";
|
|
354
|
+
const silentMillis = 25e3;
|
|
355
|
+
const openTimeout = "10 seconds";
|
|
356
|
+
const normalClose = 1e3;
|
|
357
|
+
const ClientProtocolError = TaggedStruct("ClientProtocolError", {
|
|
358
|
+
error: RpcClientError,
|
|
359
|
+
requestIds: ArraySchema(Union([String$2, Finite]))
|
|
360
|
+
});
|
|
361
|
+
const JsonText$1 = fromJsonString(Unknown);
|
|
362
|
+
const encodeJson = encodeEffect(JsonText$1);
|
|
363
|
+
const decodeJson = decodeEffect(JsonText$1);
|
|
364
|
+
const utf8 = new globalThis.TextDecoder();
|
|
365
|
+
function serverMessages(values) {
|
|
366
|
+
return values;
|
|
367
|
+
}
|
|
368
|
+
const textOf = (message) => orDie(encodeJson(message));
|
|
369
|
+
const tokenOf = (headers) => map$2(filter$1(get(headers, authorizationHeader), startsWith(bearerPrefix)), slice(7));
|
|
370
|
+
const withoutAuthorization = (headers) => filter(toEntries(headers), ([name]) => toLowerCase(name) !== authorizationHeader);
|
|
371
|
+
const socketUrl = (url, request) => map$1(textOf({
|
|
372
|
+
...request,
|
|
373
|
+
headers: []
|
|
374
|
+
}), (text) => {
|
|
375
|
+
const target = new globalThis.URL(url);
|
|
376
|
+
target.protocol = replace(/^http/u, "ws")(target.protocol);
|
|
377
|
+
target.searchParams.set("request", text);
|
|
378
|
+
return target.href;
|
|
379
|
+
});
|
|
380
|
+
const lost = (message, cause) => RpcClientError.make({ reason: RpcClientDefect.make({
|
|
381
|
+
cause,
|
|
382
|
+
message
|
|
383
|
+
}) });
|
|
384
|
+
const textFrom = (frame) => {
|
|
385
|
+
if (isString(frame)) return frame;
|
|
386
|
+
return utf8.decode(frame);
|
|
387
|
+
};
|
|
388
|
+
const decodeFrame = (frame) => mapBoth(decodeJson(textFrom(frame)), {
|
|
389
|
+
onFailure: (cause) => lost("the live socket sent a frame that isn't RPC JSON", cause),
|
|
390
|
+
onSuccess: (decoded) => serverMessages(ensure(decoded))
|
|
391
|
+
});
|
|
392
|
+
const opened = ({ headers, url, webSocket }, request) => gen(function* openLiveSocket() {
|
|
393
|
+
const sent = headers();
|
|
394
|
+
const credentials = map$2(tokenOf(sent), (token) => `${authProtocol}${token}`);
|
|
395
|
+
const protocols = [liveProtocol, ...toArray(credentials)];
|
|
396
|
+
const target = yield* socketUrl(url, request);
|
|
397
|
+
const socket = yield* fromWebSocket(acquireRelease(sync(() => new webSocket(target, protocols)), (native) => sync(() => {
|
|
398
|
+
native.close(normalClose);
|
|
399
|
+
})), { openTimeout });
|
|
400
|
+
const reader = yield* socket.reader;
|
|
401
|
+
const writer = yield* socket.writer;
|
|
402
|
+
const opening = yield* textOf({
|
|
403
|
+
...request,
|
|
404
|
+
headers: [...request.headers, ...withoutAuthorization(sent)]
|
|
405
|
+
});
|
|
406
|
+
yield* writer.write(opening);
|
|
407
|
+
return {
|
|
408
|
+
reader,
|
|
409
|
+
writer
|
|
410
|
+
};
|
|
411
|
+
});
|
|
412
|
+
const received = ({ deliver, ended }) => (response) => {
|
|
413
|
+
if (isTagged(response, "Pong")) return void_;
|
|
414
|
+
if (isTerminalResponse(response)) return andThen(deliver(response), open(ended));
|
|
415
|
+
return deliver(response);
|
|
416
|
+
};
|
|
417
|
+
const receiving = (live) => {
|
|
418
|
+
const onFrame = (frame) => flatMap(decodeFrame(frame), (responses) => andThen(flatMap(currentTimeMillis, (now) => set(live.seen, now)), forEach(responses, received(live), { discard: true })));
|
|
419
|
+
return forever(flatMap(live.reader.pull, (frames) => forEach(frames, onFrame)));
|
|
420
|
+
};
|
|
421
|
+
const pinging = ({ seen, writer }) => forever(gen(function* pingLiveSocket() {
|
|
422
|
+
yield* sleep(pingEvery);
|
|
423
|
+
const now = yield* currentTimeMillis;
|
|
424
|
+
const last = yield* get$1(seen);
|
|
425
|
+
if (now - last > silentMillis) return yield* lost("the live socket went silent", now - last);
|
|
426
|
+
const frame = yield* textOf(constPing);
|
|
427
|
+
return yield* writer.write(frame);
|
|
428
|
+
}));
|
|
429
|
+
const transportFailure = (failure) => {
|
|
430
|
+
if (isTagged(failure, "RpcClientError")) return failure;
|
|
431
|
+
return RpcClientError.make({ reason: failure.reason });
|
|
432
|
+
};
|
|
433
|
+
const liveStream = (options, writers) => ({ deliver, request }) => {
|
|
434
|
+
const served = gen(function* serveLiveRequest() {
|
|
435
|
+
const { reader, writer } = yield* opened(options, request);
|
|
436
|
+
const ended = yield* make$1();
|
|
437
|
+
const now = yield* currentTimeMillis;
|
|
438
|
+
const live = {
|
|
439
|
+
deliver,
|
|
440
|
+
ended,
|
|
441
|
+
reader,
|
|
442
|
+
seen: yield* make(now),
|
|
443
|
+
writer
|
|
444
|
+
};
|
|
445
|
+
yield* acquireRelease(sync(() => writers.set(request.id, writer)), () => sync(() => writers.delete(request.id)));
|
|
446
|
+
yield* raceFirst(raceFirst(receiving(live), pinging(live)), ended.await);
|
|
447
|
+
});
|
|
448
|
+
return matchEffect(scoped(served), {
|
|
449
|
+
onFailure: (failure) => deliver(ClientProtocolError.make({
|
|
450
|
+
error: transportFailure(failure),
|
|
451
|
+
requestIds: [request.id]
|
|
452
|
+
})),
|
|
453
|
+
onSuccess: () => void_
|
|
454
|
+
});
|
|
455
|
+
};
|
|
456
|
+
const liveRequestOf = (message) => {
|
|
457
|
+
if (isTagged(message, "Request") && endsWith(streamSuffix)(message.tag)) return some$1(message);
|
|
458
|
+
return none();
|
|
459
|
+
};
|
|
460
|
+
const deliveries = (http) => {
|
|
461
|
+
const delivers = /* @__PURE__ */ new Map();
|
|
462
|
+
const floors = trackedFloors();
|
|
463
|
+
const interrupts = remoteInterrupts();
|
|
464
|
+
const deliverTo = (clientId) => (response) => match$1(fromNullishOr(delivers.get(clientId)), {
|
|
465
|
+
onNone: () => void_,
|
|
466
|
+
onSome: (deliver) => deliver(interrupts.received(clientId, response))
|
|
467
|
+
});
|
|
468
|
+
const run = (clientId, deliver) => ensuring(suspend(() => {
|
|
469
|
+
delivers.set(clientId, deliver);
|
|
470
|
+
return http.run(clientId, (response) => andThen(sync(() => {
|
|
471
|
+
floors.received(clientId, response);
|
|
472
|
+
}), suspend(() => deliver(interrupts.received(clientId, response)))));
|
|
473
|
+
}), sync(() => delivers.delete(clientId)));
|
|
474
|
+
return {
|
|
475
|
+
deliverTo,
|
|
476
|
+
floors,
|
|
477
|
+
interrupts,
|
|
478
|
+
run
|
|
479
|
+
};
|
|
480
|
+
};
|
|
481
|
+
const actorProtocol = fnUntraced(function* actorProtocol(options) {
|
|
482
|
+
const http = yield* makeProtocolHttp(options.http);
|
|
483
|
+
const lives = yield* make$6();
|
|
484
|
+
const writers = /* @__PURE__ */ new Map();
|
|
485
|
+
const { deliverTo, floors, interrupts, run } = deliveries(http);
|
|
486
|
+
const serve = liveStream(options, writers);
|
|
487
|
+
const writeTo = (requestId, message) => match$1(fromNullishOr(writers.get(requestId)), {
|
|
488
|
+
onNone: () => void_,
|
|
489
|
+
onSome: (writer) => ignore(flatMap(textOf(message), (frame) => writer.write(frame)))
|
|
490
|
+
});
|
|
491
|
+
const send = (clientId, message, transferables) => {
|
|
492
|
+
const live = liveRequestOf(message);
|
|
493
|
+
interrupts.sent(clientId, message);
|
|
494
|
+
if (isSome(live)) {
|
|
495
|
+
const streamed = serve({
|
|
496
|
+
deliver: deliverTo(clientId),
|
|
497
|
+
request: live.value
|
|
498
|
+
});
|
|
499
|
+
return asVoid(run$1(lives, live.value.id, streamed));
|
|
500
|
+
}
|
|
501
|
+
if (isTagged(message, "Ack")) return writeTo(message.requestId, message);
|
|
502
|
+
if (isTagged(message, "Interrupt")) return andThen(writeTo(message.requestId, message), remove(lives, message.requestId));
|
|
503
|
+
return http.send(clientId, floors.sent(clientId, message), transferables);
|
|
504
|
+
};
|
|
505
|
+
return Protocol.of({
|
|
506
|
+
codecFor: http.codecFor,
|
|
507
|
+
run,
|
|
508
|
+
send,
|
|
509
|
+
supportsAck: true,
|
|
510
|
+
supportsTransferables: false
|
|
511
|
+
});
|
|
512
|
+
});
|
|
513
|
+
// ../client/src/akter-client.layer.ts
|
|
514
|
+
const noHeaders$1 = () => ({});
|
|
515
|
+
const rpcPath = "/rpc";
|
|
516
|
+
const akterClient = ({ fetch = globalThis.fetch, headers = noHeaders$1, url, webSocket = globalThis.WebSocket }) => {
|
|
517
|
+
const rpcUrl = new globalThis.URL(rpcPath, url).href;
|
|
518
|
+
const httpClient = effect(HttpClient, gen(function* withHeaders() {
|
|
519
|
+
const client = yield* HttpClient;
|
|
520
|
+
return mapRequest(client, (request) => setHeaders(prependUrl(request, rpcUrl), headers()));
|
|
521
|
+
})).pipe(provide(layer), provide(succeed(Fetch)(fetch)));
|
|
522
|
+
const protocol = effect(Protocol, flatMap(HttpClient, (http) => actorProtocol({
|
|
523
|
+
headers,
|
|
524
|
+
http,
|
|
525
|
+
url: rpcUrl,
|
|
526
|
+
webSocket
|
|
527
|
+
}))).pipe(provide([httpClient, layerNdjson]));
|
|
528
|
+
const clients = merge(ActorClients.layer.pipe(provide(layerWebCrypto)), rpcFileClients);
|
|
529
|
+
return provideMerge(clients, protocol);
|
|
530
|
+
};
|
|
531
|
+
// ../client/src/actor-fields.pure.ts
|
|
532
|
+
const fieldSchemas = {
|
|
533
|
+
actor: {
|
|
534
|
+
description: "The actor's name in the app, such as Sale.",
|
|
535
|
+
type: "string"
|
|
536
|
+
},
|
|
537
|
+
after: {
|
|
538
|
+
description: "Only log lines after this seq, the last one a page before printed, to read on from there.",
|
|
539
|
+
type: "integer"
|
|
540
|
+
},
|
|
541
|
+
commandId: {
|
|
542
|
+
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.",
|
|
543
|
+
type: "string"
|
|
544
|
+
},
|
|
545
|
+
computer: {
|
|
546
|
+
description: "The computer's name, as the actor named it.",
|
|
547
|
+
type: "string"
|
|
548
|
+
},
|
|
549
|
+
computerPath: {
|
|
550
|
+
description: "Relative path in the computer's files directory.",
|
|
551
|
+
type: "string"
|
|
552
|
+
},
|
|
553
|
+
content: {
|
|
554
|
+
description: "The file's bytes, as base64.",
|
|
555
|
+
type: "string"
|
|
556
|
+
},
|
|
557
|
+
cursor: {
|
|
558
|
+
description: "Where the page to read starts, from the cursor of the page before.",
|
|
559
|
+
type: "string"
|
|
560
|
+
},
|
|
561
|
+
direction: {
|
|
562
|
+
description: "toComputer or fromComputer; copy-back returns an upload receipt.",
|
|
563
|
+
enum: ["toComputer", "fromComputer"],
|
|
564
|
+
type: "string"
|
|
565
|
+
},
|
|
566
|
+
into: { description: "The new actor's key, as JSON. A bare word is taken as a string key." },
|
|
567
|
+
key: { description: "The actor's key, as JSON. A bare word is taken as a string key." },
|
|
568
|
+
length: {
|
|
569
|
+
description: "How many bytes to read, from offset; give both or neither.",
|
|
570
|
+
type: "integer"
|
|
571
|
+
},
|
|
572
|
+
limit: {
|
|
573
|
+
description: "The most entries to return.",
|
|
574
|
+
type: "integer"
|
|
575
|
+
},
|
|
576
|
+
member: {
|
|
577
|
+
description: "The command or query to run.",
|
|
578
|
+
type: "string"
|
|
579
|
+
},
|
|
580
|
+
offset: {
|
|
581
|
+
description: "The byte to start reading at, with length; give both or neither.",
|
|
582
|
+
type: "integer"
|
|
583
|
+
},
|
|
584
|
+
path: {
|
|
585
|
+
description: "The file's path in the actor, such as photos/cat.png.",
|
|
586
|
+
type: "string"
|
|
587
|
+
},
|
|
588
|
+
payload: { description: "The command's, query's or create input's payload, as JSON." },
|
|
589
|
+
prefix: {
|
|
590
|
+
description: "Only paths that start with this.",
|
|
591
|
+
type: "string"
|
|
592
|
+
},
|
|
593
|
+
step: {
|
|
594
|
+
description: "The step's id, as steps list prints it.",
|
|
595
|
+
type: "string"
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
const optional = [
|
|
599
|
+
"after",
|
|
600
|
+
"commandId",
|
|
601
|
+
"cursor",
|
|
602
|
+
"length",
|
|
603
|
+
"limit",
|
|
604
|
+
"offset",
|
|
605
|
+
"payload",
|
|
606
|
+
"prefix"
|
|
607
|
+
];
|
|
608
|
+
const inputOf = (fields) => ({
|
|
609
|
+
additionalProperties: false,
|
|
610
|
+
properties: fromEntries(map(fields, (name) => [name, fieldSchemas[name]])),
|
|
611
|
+
required: filter(fields, (name) => !contains(optional, name)),
|
|
612
|
+
type: "object"
|
|
613
|
+
});
|
|
614
|
+
// ../client/src/actor-operations.pure.ts
|
|
615
|
+
const group = "actors";
|
|
616
|
+
const commandOfKind = (kind) => {
|
|
617
|
+
const [owner, name] = split("/")(kind);
|
|
618
|
+
return match$1(fromUndefinedOr(name), {
|
|
619
|
+
onNone: () => [group, kind],
|
|
620
|
+
onSome: (found) => [owner, found]
|
|
621
|
+
});
|
|
622
|
+
};
|
|
623
|
+
const subscribe = "/subscribe";
|
|
624
|
+
const declared = [
|
|
625
|
+
{
|
|
626
|
+
description: "Run one of an actor's commands and print its reply.",
|
|
627
|
+
fields: [
|
|
628
|
+
"actor",
|
|
629
|
+
"key",
|
|
630
|
+
"member",
|
|
631
|
+
"payload",
|
|
632
|
+
"commandId"
|
|
633
|
+
],
|
|
634
|
+
idempotent: false,
|
|
635
|
+
kind: "call",
|
|
636
|
+
readOnly: false
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
description: "Run one of an actor's queries and print its reply.",
|
|
640
|
+
fields: [
|
|
641
|
+
"actor",
|
|
642
|
+
"key",
|
|
643
|
+
"member",
|
|
644
|
+
"payload"
|
|
645
|
+
],
|
|
646
|
+
idempotent: true,
|
|
647
|
+
kind: "query",
|
|
648
|
+
readOnly: true
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
description: "Print an actor's state once.",
|
|
652
|
+
fields: ["actor", "key"],
|
|
653
|
+
idempotent: true,
|
|
654
|
+
kind: "state",
|
|
655
|
+
readOnly: true
|
|
656
|
+
},
|
|
657
|
+
{
|
|
658
|
+
description: "Print a query's reply, then each new reply as the actor changes, one JSON line each.",
|
|
659
|
+
fields: [
|
|
660
|
+
"actor",
|
|
661
|
+
"key",
|
|
662
|
+
"member",
|
|
663
|
+
"payload"
|
|
664
|
+
],
|
|
665
|
+
idempotent: true,
|
|
666
|
+
kind: "watch",
|
|
667
|
+
readOnly: true
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
description: "Create an actor from its create input; fails when it already exists.",
|
|
671
|
+
fields: [
|
|
672
|
+
"actor",
|
|
673
|
+
"key",
|
|
674
|
+
"payload",
|
|
675
|
+
"commandId"
|
|
676
|
+
],
|
|
677
|
+
idempotent: false,
|
|
678
|
+
kind: "create",
|
|
679
|
+
readOnly: false
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
description: "Create an actor from its create input unless it already exists.",
|
|
683
|
+
fields: [
|
|
684
|
+
"actor",
|
|
685
|
+
"key",
|
|
686
|
+
"payload",
|
|
687
|
+
"commandId"
|
|
688
|
+
],
|
|
689
|
+
idempotent: true,
|
|
690
|
+
kind: "ensure",
|
|
691
|
+
readOnly: false
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
description: "List an actor's files a page at a time, with each path, size, digest and write time.",
|
|
695
|
+
fields: [
|
|
696
|
+
"actor",
|
|
697
|
+
"key",
|
|
698
|
+
"prefix",
|
|
699
|
+
"cursor",
|
|
700
|
+
"limit"
|
|
701
|
+
],
|
|
702
|
+
idempotent: true,
|
|
703
|
+
kind: "files/list",
|
|
704
|
+
readOnly: true
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
description: "List an actor's files a page at a time, with the total bytes they hold.",
|
|
708
|
+
fields: [
|
|
709
|
+
"actor",
|
|
710
|
+
"key",
|
|
711
|
+
"prefix",
|
|
712
|
+
"cursor",
|
|
713
|
+
"limit"
|
|
714
|
+
],
|
|
715
|
+
idempotent: true,
|
|
716
|
+
kind: "files/usage",
|
|
717
|
+
readOnly: true
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
description: "Download one of an actor's files, whole or the byte range offset and length give.",
|
|
721
|
+
fields: [
|
|
722
|
+
"actor",
|
|
723
|
+
"key",
|
|
724
|
+
"path",
|
|
725
|
+
"offset",
|
|
726
|
+
"length"
|
|
727
|
+
],
|
|
728
|
+
idempotent: true,
|
|
729
|
+
kind: "files/download",
|
|
730
|
+
readOnly: true
|
|
731
|
+
},
|
|
732
|
+
{
|
|
733
|
+
description: "Upload a file for an actor and print its receipt, which a command attaches at a path.",
|
|
734
|
+
fields: [
|
|
735
|
+
"actor",
|
|
736
|
+
"key",
|
|
737
|
+
"content"
|
|
738
|
+
],
|
|
739
|
+
idempotent: false,
|
|
740
|
+
kind: "files/upload",
|
|
741
|
+
readOnly: false
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
description: "Fork an actor into a new actor at another key: a copy of its state and files at one moment, with none of its work in flight (its jobs, timers and undelivered messages stay with the original). A retry with the same commandId makes one copy.",
|
|
745
|
+
fields: [
|
|
746
|
+
"actor",
|
|
747
|
+
"key",
|
|
748
|
+
"into",
|
|
749
|
+
"commandId"
|
|
750
|
+
],
|
|
751
|
+
idempotent: false,
|
|
752
|
+
kind: "fork",
|
|
753
|
+
readOnly: false
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
description: "List an actor's computers, each with its image, size, egress and state.",
|
|
757
|
+
fields: ["actor", "key"],
|
|
758
|
+
idempotent: true,
|
|
759
|
+
kind: "computers/list",
|
|
760
|
+
readOnly: true
|
|
761
|
+
},
|
|
762
|
+
{
|
|
763
|
+
description: "Copy one file (at most 1 MiB) between an actor and its computer. fromComputer returns a receipt; attach it with an actor command.",
|
|
764
|
+
fields: [
|
|
765
|
+
"actor",
|
|
766
|
+
"key",
|
|
767
|
+
"computer",
|
|
768
|
+
"direction",
|
|
769
|
+
"path",
|
|
770
|
+
"computerPath"
|
|
771
|
+
],
|
|
772
|
+
flag: "computerFiles",
|
|
773
|
+
idempotent: false,
|
|
774
|
+
kind: "computers/cp",
|
|
775
|
+
readOnly: false
|
|
776
|
+
},
|
|
777
|
+
{
|
|
778
|
+
description: "Show one of an actor's computers: its image, size, egress and state.",
|
|
779
|
+
fields: [
|
|
780
|
+
"actor",
|
|
781
|
+
"key",
|
|
782
|
+
"computer"
|
|
783
|
+
],
|
|
784
|
+
idempotent: true,
|
|
785
|
+
kind: "computers/get",
|
|
786
|
+
readOnly: true
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
description: "Read a page of a computer's log, oldest first; pass the last seq printed as after to read on.",
|
|
790
|
+
fields: [
|
|
791
|
+
"actor",
|
|
792
|
+
"key",
|
|
793
|
+
"computer",
|
|
794
|
+
"after",
|
|
795
|
+
"limit"
|
|
796
|
+
],
|
|
797
|
+
idempotent: true,
|
|
798
|
+
kind: "computers/logs",
|
|
799
|
+
readOnly: true
|
|
800
|
+
},
|
|
801
|
+
{
|
|
802
|
+
description: "Pause a computer, keeping its memory and disk, and print it.",
|
|
803
|
+
fields: [
|
|
804
|
+
"actor",
|
|
805
|
+
"key",
|
|
806
|
+
"computer"
|
|
807
|
+
],
|
|
808
|
+
idempotent: true,
|
|
809
|
+
kind: "computers/pause",
|
|
810
|
+
readOnly: false
|
|
811
|
+
},
|
|
812
|
+
{
|
|
813
|
+
description: "Resume a paused computer and print it; fails while a fork holds it.",
|
|
814
|
+
fields: [
|
|
815
|
+
"actor",
|
|
816
|
+
"key",
|
|
817
|
+
"computer"
|
|
818
|
+
],
|
|
819
|
+
idempotent: true,
|
|
820
|
+
kind: "computers/resume",
|
|
821
|
+
readOnly: false
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
description: "Stop a computer, keeping its disk, and print it.",
|
|
825
|
+
fields: [
|
|
826
|
+
"actor",
|
|
827
|
+
"key",
|
|
828
|
+
"computer"
|
|
829
|
+
],
|
|
830
|
+
idempotent: true,
|
|
831
|
+
kind: "computers/stop",
|
|
832
|
+
readOnly: false
|
|
833
|
+
},
|
|
834
|
+
{
|
|
835
|
+
description: "Delete a computer and its disk. The actor's own jobs make computers; this only removes one.",
|
|
836
|
+
fields: [
|
|
837
|
+
"actor",
|
|
838
|
+
"key",
|
|
839
|
+
"computer"
|
|
840
|
+
],
|
|
841
|
+
idempotent: true,
|
|
842
|
+
kind: "computers/delete",
|
|
843
|
+
readOnly: false
|
|
844
|
+
},
|
|
845
|
+
{
|
|
846
|
+
description: "List an actor's durable computer steps, newest first: each one's job, name, computer, command and state, and its exit code once it ran.",
|
|
847
|
+
fields: [
|
|
848
|
+
"actor",
|
|
849
|
+
"key",
|
|
850
|
+
"limit"
|
|
851
|
+
],
|
|
852
|
+
flag: "computerSteps",
|
|
853
|
+
idempotent: true,
|
|
854
|
+
kind: "steps/list",
|
|
855
|
+
readOnly: true
|
|
856
|
+
},
|
|
857
|
+
{
|
|
858
|
+
description: "Show one of an actor's durable computer steps with its output. A step in state unknown may have done its work; reconcile it by its id.",
|
|
859
|
+
fields: [
|
|
860
|
+
"actor",
|
|
861
|
+
"key",
|
|
862
|
+
"step"
|
|
863
|
+
],
|
|
864
|
+
flag: "computerSteps",
|
|
865
|
+
idempotent: true,
|
|
866
|
+
kind: "steps/get",
|
|
867
|
+
readOnly: true
|
|
868
|
+
},
|
|
869
|
+
{
|
|
870
|
+
description: "Load an actor ahead of its first call, so that call answers sooner.",
|
|
871
|
+
fields: ["actor", "key"],
|
|
872
|
+
idempotent: true,
|
|
873
|
+
kind: "warm",
|
|
874
|
+
readOnly: true
|
|
875
|
+
}
|
|
876
|
+
];
|
|
877
|
+
const streams = [
|
|
878
|
+
"files/download",
|
|
879
|
+
"state",
|
|
880
|
+
"watch"
|
|
881
|
+
];
|
|
882
|
+
const dangerous = ["computers/delete"];
|
|
883
|
+
const operationOf = ({ description, fields, flag, idempotent, kind, readOnly }) => {
|
|
884
|
+
const [owner, name] = commandOfKind(kind);
|
|
885
|
+
const confirm = contains(dangerous, kind);
|
|
886
|
+
const operation = {
|
|
887
|
+
annotations: {
|
|
888
|
+
confirm,
|
|
889
|
+
destructive: confirm,
|
|
890
|
+
idempotent,
|
|
891
|
+
openWorld: false,
|
|
892
|
+
readOnly
|
|
893
|
+
},
|
|
894
|
+
command: [owner, name],
|
|
895
|
+
description,
|
|
896
|
+
fields,
|
|
897
|
+
flag: fromUndefinedOr(flag),
|
|
898
|
+
id: `${owner}.${name}`,
|
|
899
|
+
input: inputOf(fields),
|
|
900
|
+
kind,
|
|
901
|
+
stream: contains(streams, kind),
|
|
902
|
+
tool: `${owner}_${name}`
|
|
903
|
+
};
|
|
904
|
+
if (kind === "computers/cp") return {
|
|
905
|
+
...operation,
|
|
906
|
+
tool: "computers_files"
|
|
907
|
+
};
|
|
908
|
+
return operation;
|
|
909
|
+
};
|
|
910
|
+
const unexposedKinds = ["warm"];
|
|
911
|
+
const actorOperations = map(filter(declared, ({ kind }) => !contains(unexposedKinds, kind)), operationOf);
|
|
912
|
+
const tagOf = ({ actor, kind, member }) => {
|
|
913
|
+
return {
|
|
914
|
+
call: `${actor}/${member}`,
|
|
915
|
+
"computers/cp": `${actor}/computer/files`,
|
|
916
|
+
"computers/delete": `${actor}/computer/delete`,
|
|
917
|
+
"computers/get": `${actor}/computer/get`,
|
|
918
|
+
"computers/list": `${actor}/computer/list`,
|
|
919
|
+
"computers/logs": `${actor}/computer/logs`,
|
|
920
|
+
"computers/pause": `${actor}/computer/pause`,
|
|
921
|
+
"computers/resume": `${actor}/computer/resume`,
|
|
922
|
+
"computers/stop": `${actor}/computer/stop`,
|
|
923
|
+
create: `${actor}/create`,
|
|
924
|
+
ensure: `${actor}/ensure`,
|
|
925
|
+
"files/download": `${actor}/files/download`,
|
|
926
|
+
"files/list": `${actor}/files/list`,
|
|
927
|
+
"files/upload": `${actor}/files/upload`,
|
|
928
|
+
"files/usage": `${actor}/files/usage`,
|
|
929
|
+
fork: `${actor}/fork`,
|
|
930
|
+
query: `${actor}/${member}`,
|
|
931
|
+
state: `${actor}/state${subscribe}`,
|
|
932
|
+
"steps/get": `${actor}/computer/step`,
|
|
933
|
+
"steps/list": `${actor}/computer/steps`,
|
|
934
|
+
warm: `${actor}/warm`,
|
|
935
|
+
watch: `${actor}/${member}${subscribe}`
|
|
936
|
+
}[kind];
|
|
937
|
+
};
|
|
938
|
+
const fileRpcs = {
|
|
939
|
+
"files/download": {
|
|
940
|
+
fields: "key,length,offset,path",
|
|
941
|
+
kind: "files/download",
|
|
942
|
+
stream: true
|
|
943
|
+
},
|
|
944
|
+
"files/list": {
|
|
945
|
+
fields: "cursor,key,limit,prefix",
|
|
946
|
+
kind: "files/list",
|
|
947
|
+
stream: false
|
|
948
|
+
},
|
|
949
|
+
"files/upload/append": {
|
|
950
|
+
fields: "bytes,key,offset,upload",
|
|
951
|
+
kind: "files/upload",
|
|
952
|
+
stream: false
|
|
953
|
+
},
|
|
954
|
+
"files/upload/close": {
|
|
955
|
+
fields: "bytes,key,upload",
|
|
956
|
+
kind: "files/upload",
|
|
957
|
+
stream: false
|
|
958
|
+
},
|
|
959
|
+
"files/upload/open": {
|
|
960
|
+
fields: "key",
|
|
961
|
+
kind: "files/upload",
|
|
962
|
+
stream: false
|
|
963
|
+
},
|
|
964
|
+
"files/usage": {
|
|
965
|
+
fields: "cursor,key,limit,prefix",
|
|
966
|
+
kind: "files/usage",
|
|
967
|
+
stream: false
|
|
968
|
+
}
|
|
969
|
+
};
|
|
970
|
+
const computerRpcs = {
|
|
971
|
+
"computer/delete": {
|
|
972
|
+
fields: "computer,key",
|
|
973
|
+
kind: "computers/delete",
|
|
974
|
+
stream: false
|
|
975
|
+
},
|
|
976
|
+
"computer/files": {
|
|
977
|
+
fields: "computer,computerPath,direction,key,path",
|
|
978
|
+
kind: "computers/cp",
|
|
979
|
+
stream: false
|
|
980
|
+
},
|
|
981
|
+
"computer/get": {
|
|
982
|
+
fields: "computer,key",
|
|
983
|
+
kind: "computers/get",
|
|
984
|
+
stream: false
|
|
985
|
+
},
|
|
986
|
+
"computer/list": {
|
|
987
|
+
fields: "key",
|
|
988
|
+
kind: "computers/list",
|
|
989
|
+
stream: false
|
|
990
|
+
},
|
|
991
|
+
"computer/logs": {
|
|
992
|
+
fields: "after,computer,key,limit",
|
|
993
|
+
kind: "computers/logs",
|
|
994
|
+
stream: false
|
|
995
|
+
},
|
|
996
|
+
"computer/pause": {
|
|
997
|
+
fields: "computer,key",
|
|
998
|
+
kind: "computers/pause",
|
|
999
|
+
stream: false
|
|
1000
|
+
},
|
|
1001
|
+
"computer/resume": {
|
|
1002
|
+
fields: "computer,key",
|
|
1003
|
+
kind: "computers/resume",
|
|
1004
|
+
stream: false
|
|
1005
|
+
},
|
|
1006
|
+
"computer/step": {
|
|
1007
|
+
fields: "key,step",
|
|
1008
|
+
kind: "steps/get",
|
|
1009
|
+
stream: false
|
|
1010
|
+
},
|
|
1011
|
+
"computer/steps": {
|
|
1012
|
+
fields: "key,limit",
|
|
1013
|
+
kind: "steps/list",
|
|
1014
|
+
stream: false
|
|
1015
|
+
},
|
|
1016
|
+
"computer/stop": {
|
|
1017
|
+
fields: "computer,key",
|
|
1018
|
+
kind: "computers/stop",
|
|
1019
|
+
stream: false
|
|
1020
|
+
}
|
|
1021
|
+
};
|
|
1022
|
+
({
|
|
1023
|
+
...fileRpcs,
|
|
1024
|
+
...computerRpcs
|
|
1025
|
+
});
|
|
1026
|
+
const someField = (name, value) => match$1(value, {
|
|
1027
|
+
onNone: () => ({}),
|
|
1028
|
+
onSome: (found) => ({ [name]: found })
|
|
1029
|
+
});
|
|
1030
|
+
const actorRequestOf = (operation) => (input) => gen(function* buildRequest() {
|
|
1031
|
+
const takes = (name) => contains(operation.fields, name);
|
|
1032
|
+
const minted = SecureRandom.use((random) => random.uuidV7());
|
|
1033
|
+
const fresh = yield* when(minted, succeed$1(takes("commandId") && isNone(input.commandId)));
|
|
1034
|
+
const commandId = filter$1(orElse(input.commandId, () => fresh), () => takes("commandId"));
|
|
1035
|
+
const payload = liftPredicate(getOrNull(input.payload), () => takes("payload"));
|
|
1036
|
+
return {
|
|
1037
|
+
payload: {
|
|
1038
|
+
...filter$2(input.fileFields, (_value, name) => some(operation.fields, (field) => field === name)),
|
|
1039
|
+
...someField("commandId", commandId),
|
|
1040
|
+
...someField("into", filter$1(input.into, () => takes("into"))),
|
|
1041
|
+
...someField("payload", payload),
|
|
1042
|
+
key: input.key
|
|
1043
|
+
},
|
|
1044
|
+
tag: tagOf({
|
|
1045
|
+
actor: input.actor,
|
|
1046
|
+
kind: operation.kind,
|
|
1047
|
+
member: input.member
|
|
1048
|
+
})
|
|
1049
|
+
};
|
|
1050
|
+
});
|
|
1051
|
+
// src/registry/actor-call.service.ts
|
|
1052
|
+
const callWait = "2 minutes";
|
|
1053
|
+
const firstOnly = 1;
|
|
1054
|
+
const fromFile = "@";
|
|
1055
|
+
const fromStdin = "-";
|
|
1056
|
+
const Base64 = Uint8ArrayFromBase64;
|
|
1057
|
+
const textFields = [
|
|
1058
|
+
"computer",
|
|
1059
|
+
"computerPath",
|
|
1060
|
+
"direction",
|
|
1061
|
+
"cursor",
|
|
1062
|
+
"path",
|
|
1063
|
+
"prefix",
|
|
1064
|
+
"step"
|
|
1065
|
+
];
|
|
1066
|
+
const numberFields = [
|
|
1067
|
+
"after",
|
|
1068
|
+
"length",
|
|
1069
|
+
"limit",
|
|
1070
|
+
"offset"
|
|
1071
|
+
];
|
|
1072
|
+
const loopbackHosts = /* @__PURE__ */ new Set([
|
|
1073
|
+
"localhost",
|
|
1074
|
+
"127.0.0.1",
|
|
1075
|
+
"[::1]"
|
|
1076
|
+
]);
|
|
1077
|
+
const JsonText = fromJsonString(Json);
|
|
1078
|
+
const usage = (message) => CliExitError.make({
|
|
1079
|
+
code: CliExitError.codes.usage,
|
|
1080
|
+
message,
|
|
1081
|
+
reason: "InvalidInput"
|
|
1082
|
+
});
|
|
1083
|
+
const appUrl = fnUntraced(function* appUrl(flag) {
|
|
1084
|
+
const configured = yield* option(String$1("AKTER_APP_URL")).pipe(orElseSucceed(() => none()));
|
|
1085
|
+
const given = yield* fromOption(orElse(flag, () => configured)).pipe(mapError(() => usage("Pass --url with the app's URL, such as the one akter dev prints, or set AKTER_APP_URL.")));
|
|
1086
|
+
return yield* try_({
|
|
1087
|
+
catch: () => usage(`${given} is not a URL.`),
|
|
1088
|
+
try: () => new globalThis.URL(given)
|
|
1089
|
+
});
|
|
1090
|
+
});
|
|
1091
|
+
const noHeaders = { current: succeed$1({}) };
|
|
1092
|
+
const headersFor = fnUntraced(function* headersFor(url) {
|
|
1093
|
+
if (loopbackHosts.has(url.hostname)) return noHeaders;
|
|
1094
|
+
const credentials = yield* Credentials;
|
|
1095
|
+
const cloudUrl = yield* credentials.url(none());
|
|
1096
|
+
const credential = yield* credentials.credentialFor(cloudUrl);
|
|
1097
|
+
const cloud = yield* CloudClient.use((factory) => factory.connect({
|
|
1098
|
+
apiKey: credential.secret,
|
|
1099
|
+
url: cloudUrl
|
|
1100
|
+
}));
|
|
1101
|
+
return { current: mapBoth(cloud.bearer, {
|
|
1102
|
+
onFailure: signInFailure,
|
|
1103
|
+
onSuccess: (token) => ({ authorization: `Bearer ${value(token)}` })
|
|
1104
|
+
}) };
|
|
1105
|
+
});
|
|
1106
|
+
/** How often a call that outlives its token, such as a watch, reads the current headers. */
|
|
1107
|
+
const headerCheck = "10 seconds";
|
|
1108
|
+
const keyOf = (text) => getOrElse(decodeOption(JsonText)(text), () => text);
|
|
1109
|
+
const payloadOf = (text) => transposeOption(map$2(text, (raw) => mapError(decodeEffect(JsonText)(raw), () => usage("--payload is not JSON."))));
|
|
1110
|
+
const given = (options, name) => flatten(get(options.values, name));
|
|
1111
|
+
const required = (operation, options) => (name) => {
|
|
1112
|
+
if (!contains(operation.fields, name)) return succeed$1("");
|
|
1113
|
+
const value = given(options, name);
|
|
1114
|
+
return mapError(fromOption(value), () => usage(`akter ${join(operation.command, " ")} needs --${name}.`));
|
|
1115
|
+
};
|
|
1116
|
+
const wholeNumber = (name) => (text) => mapError(decodeEffect(fromJsonString(Int))(text), () => usage(`--${name} must be a whole number.`));
|
|
1117
|
+
const fileFieldsOf = (operation, options) => {
|
|
1118
|
+
const taken = (names) => filter(names, (name) => contains(operation.fields, name) && isSome(given(options, name)));
|
|
1119
|
+
const flag = (name) => getOrElse(given(options, name), () => "");
|
|
1120
|
+
const texts = map(taken(textFields), (name) => succeed$1([name, flag(name)]));
|
|
1121
|
+
const numbers = map(taken(numberFields), (name) => map$1(wholeNumber(name)(flag(name)), (value) => [name, value]));
|
|
1122
|
+
return map$1(all([...texts, ...numbers]), fromEntries);
|
|
1123
|
+
};
|
|
1124
|
+
const unreadable = (message) => () => usage(message);
|
|
1125
|
+
const concatBytes = (before, chunk) => {
|
|
1126
|
+
const joined = new Uint8Array(before.length + chunk.length);
|
|
1127
|
+
joined.set(before);
|
|
1128
|
+
joined.set(chunk, before.length);
|
|
1129
|
+
return joined;
|
|
1130
|
+
};
|
|
1131
|
+
const contentOf = fnUntraced(function* contentOf(raw) {
|
|
1132
|
+
if (raw === fromStdin) {
|
|
1133
|
+
const stdio = yield* Stdio;
|
|
1134
|
+
return yield* runFold(stdio.stdin, () => /* @__PURE__ */ new Uint8Array(), concatBytes).pipe(mapError(unreadable("Could not read --content from stdin.")));
|
|
1135
|
+
}
|
|
1136
|
+
if (startsWith(fromFile)(raw)) {
|
|
1137
|
+
const file = slice(1)(raw);
|
|
1138
|
+
return yield* (yield* FileSystem).readFile(file).pipe(mapError(unreadable(`Could not read --content from ${file}.`)));
|
|
1139
|
+
}
|
|
1140
|
+
return yield* decodeEffect(Base64)(raw).pipe(mapError(unreadable("--content is not base64; pass @<file> or - to read the file's bytes.")));
|
|
1141
|
+
});
|
|
1142
|
+
const transferOf = fnUntraced(function* transferOf(operation, options) {
|
|
1143
|
+
const fields = yield* fileFieldsOf(operation, options);
|
|
1144
|
+
const flag = yield* required(operation, options)("content");
|
|
1145
|
+
const raw = liftPredicate(flag, () => contains(operation.fields, "content"));
|
|
1146
|
+
return {
|
|
1147
|
+
content: yield* transposeOption(map$2(raw, contentOf)),
|
|
1148
|
+
fields
|
|
1149
|
+
};
|
|
1150
|
+
});
|
|
1151
|
+
const requestOf = fnUntraced(function* requestOf(operation, options) {
|
|
1152
|
+
const url = yield* appUrl(options.url);
|
|
1153
|
+
const need = required(operation, options);
|
|
1154
|
+
const actor = yield* need("actor");
|
|
1155
|
+
const key = yield* need("key");
|
|
1156
|
+
const member = yield* need("member");
|
|
1157
|
+
const payload = yield* payloadOf(given(options, "payload"));
|
|
1158
|
+
const commandId = given(options, "commandId");
|
|
1159
|
+
const { content, fields } = yield* transferOf(operation, options);
|
|
1160
|
+
return {
|
|
1161
|
+
...yield* actorRequestOf(operation)({
|
|
1162
|
+
actor,
|
|
1163
|
+
commandId,
|
|
1164
|
+
fileFields: fields,
|
|
1165
|
+
into: map$2(given(options, "into"), keyOf),
|
|
1166
|
+
key: keyOf(key),
|
|
1167
|
+
member,
|
|
1168
|
+
payload
|
|
1169
|
+
}),
|
|
1170
|
+
content,
|
|
1171
|
+
operation,
|
|
1172
|
+
options,
|
|
1173
|
+
url
|
|
1174
|
+
};
|
|
1175
|
+
});
|
|
1176
|
+
const refused = fnUntraced(function* refused({ options, tag }, failure) {
|
|
1177
|
+
const unanswered = unansweredOf(tag)(failure);
|
|
1178
|
+
if (isSome(unanswered)) return yield* unanswered.value;
|
|
1179
|
+
const body = yield* orDie(decodeUnknownEffect(Json)(failure));
|
|
1180
|
+
if (options.json) yield* printJson(true)(body);
|
|
1181
|
+
else yield* flatMap(compactJson(body), (line) => error(`${tag} failed: ${line}`));
|
|
1182
|
+
return yield* RunnerExitedError.make({ code: refusalCode(failure) });
|
|
1183
|
+
});
|
|
1184
|
+
const printed = (json) => (reply) => printJson(json)(getOrNull(fromUndefinedOr(reply)));
|
|
1185
|
+
const unary = fnUntraced(function* unary(request) {
|
|
1186
|
+
const send = yield* unaryClient({
|
|
1187
|
+
kind: request.operation.kind,
|
|
1188
|
+
tag: request.tag
|
|
1189
|
+
});
|
|
1190
|
+
const reply = yield* timeout(send(request.tag, request.payload), callWait);
|
|
1191
|
+
return yield* printed(request.options.json)(reply);
|
|
1192
|
+
});
|
|
1193
|
+
const downloaded = fnUntraced(function* downloaded(request) {
|
|
1194
|
+
const chunks = (yield* streamClient({
|
|
1195
|
+
kind: "files/download",
|
|
1196
|
+
tag: request.tag
|
|
1197
|
+
}))(request.tag, request.payload);
|
|
1198
|
+
if (request.options.json) return yield* runForEach(chunks, printed(true));
|
|
1199
|
+
const stdout = (yield* Stdio).stdout({ endOnDone: false });
|
|
1200
|
+
return yield* runForEach(chunks, (chunk) => orDie(flatMap(decodeUnknownEffect(Base64)(chunk), (bytes) => run(make$2(bytes), stdout))));
|
|
1201
|
+
});
|
|
1202
|
+
const uploaded = fnUntraced(function* uploaded(request) {
|
|
1203
|
+
const content = getOrElse(request.content, () => /* @__PURE__ */ new Uint8Array());
|
|
1204
|
+
const receipt = yield* uploadSession({
|
|
1205
|
+
content,
|
|
1206
|
+
request,
|
|
1207
|
+
wait: callWait
|
|
1208
|
+
});
|
|
1209
|
+
return yield* printed(request.options.json)(receipt);
|
|
1210
|
+
});
|
|
1211
|
+
const streamed = fnUntraced(function* streamed(request) {
|
|
1212
|
+
const updates = (yield* streamClient({
|
|
1213
|
+
kind: request.operation.kind,
|
|
1214
|
+
tag: request.tag
|
|
1215
|
+
}))(request.tag, request.payload);
|
|
1216
|
+
const once = liftPredicate(firstOnly, () => request.operation.kind === "state");
|
|
1217
|
+
const count = orElse(once, () => request.options.count);
|
|
1218
|
+
const limited = match$1(count, {
|
|
1219
|
+
onNone: () => updates,
|
|
1220
|
+
onSome: (most) => take(updates, most)
|
|
1221
|
+
});
|
|
1222
|
+
return yield* runForEach(limited, printed(true));
|
|
1223
|
+
});
|
|
1224
|
+
const answered = fnUntraced(function* answered(request, headers) {
|
|
1225
|
+
const protocol = yield* build(akterClient({
|
|
1226
|
+
headers,
|
|
1227
|
+
url: request.url.href
|
|
1228
|
+
}));
|
|
1229
|
+
const { kind } = request.operation;
|
|
1230
|
+
if (kind === "files/download" || kind === "files/upload") {
|
|
1231
|
+
const transfer = match(kind === "files/download", {
|
|
1232
|
+
onFalse: () => uploaded(request),
|
|
1233
|
+
onTrue: () => downloaded(request)
|
|
1234
|
+
});
|
|
1235
|
+
return yield* provideContext(transfer, protocol);
|
|
1236
|
+
}
|
|
1237
|
+
if (request.operation.stream) return yield* provideContext(streamed(request), protocol);
|
|
1238
|
+
return yield* provideContext(unary(request), protocol);
|
|
1239
|
+
}, scoped, (effect, request) => matchEffect(effect, {
|
|
1240
|
+
onFailure: (failure) => refused(request, failure),
|
|
1241
|
+
onSuccess: succeed$1
|
|
1242
|
+
}), (effect, request) => catchDefect(effect, (defect) => fail(rejectedRequest(request.tag)(String(defect)))));
|
|
1243
|
+
const sent = fnUntraced(function* sent(request) {
|
|
1244
|
+
const { current } = yield* headersFor(request.url);
|
|
1245
|
+
const first = yield* current;
|
|
1246
|
+
const headers = yield* make(first);
|
|
1247
|
+
const refresh = ignore(flatMap(current, (fresh) => set(headers, fresh)));
|
|
1248
|
+
yield* forkScoped(repeat(refresh, spaced(headerCheck)));
|
|
1249
|
+
return yield* answered(request, () => getUnsafe(headers));
|
|
1250
|
+
}, scoped);
|
|
1251
|
+
const plan = (request) => printJson(request.options.json)({
|
|
1252
|
+
dryRun: true,
|
|
1253
|
+
operation: request.operation.id,
|
|
1254
|
+
payload: request.payload,
|
|
1255
|
+
tag: request.tag,
|
|
1256
|
+
url: request.url.href
|
|
1257
|
+
});
|
|
1258
|
+
const callActor = fnUntraced(function* callActor(operation, options) {
|
|
1259
|
+
const request = yield* requestOf(operation, options);
|
|
1260
|
+
if (options.dryRun) return yield* plan(request);
|
|
1261
|
+
return yield* sent(request);
|
|
1262
|
+
});
|
|
1263
|
+
var ActorCall = class extends Service()("@akterjs/cli/registry/actor-call.service/ActorCall", { make: gen(function* makeActorCall() {
|
|
1264
|
+
const context$1 = yield* context();
|
|
1265
|
+
return { call: (operation, options) => callActor(operation, options).pipe(catchTag("CliExitError", reported(options.json)), provideContext(context$1)) };
|
|
1266
|
+
}) }) {
|
|
1267
|
+
static layer = effect(this, this.make);
|
|
1268
|
+
};
|
|
1269
|
+
export { actorOperations as n, ActorCall as t };
|