ignotum 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -7
- package/dist/cli/bin.mjs +32313 -751
- package/dist/cli/bin.mjs.map +1 -1
- package/dist/runtime/{api-Cpx57mk3.d.ts → api-D2bsAz4T.d.ts} +3 -2
- package/dist/runtime/{api-BXXIZc_Q.js → api-DtX8qPrq.js} +58 -27
- package/dist/runtime/api-DtX8qPrq.js.map +1 -0
- package/dist/runtime/client.d.ts +11 -4
- package/dist/runtime/client.js +343 -79
- package/dist/runtime/client.js.map +1 -1
- package/dist/runtime/{result-B2W-z2wG.js → descriptor-t6BOEGw9-BTHR-ZMv.js} +119 -4
- package/dist/runtime/descriptor-t6BOEGw9-BTHR-ZMv.js.map +1 -0
- package/dist/runtime/id-Btwac71X-B9OeBzvq.d.ts +9 -0
- package/dist/runtime/id-D570vudg.js +26 -0
- package/dist/runtime/id-D570vudg.js.map +1 -0
- package/dist/runtime/{index-BgWROoyk.d.ts → index-Dl_VQGmA.d.ts} +114 -31
- package/dist/runtime/internal/api.d.ts +1 -1
- package/dist/runtime/internal/api.js +1 -1
- package/dist/runtime/internal/host.d.ts +44 -0
- package/dist/runtime/internal/host.js +213 -0
- package/dist/runtime/internal/host.js.map +1 -0
- package/dist/runtime/internal/server.d.ts +1 -1
- package/dist/runtime/internal/server.js +1 -1
- package/dist/runtime/internal/types.d.ts +1 -1
- package/dist/runtime/internal/types.js +1 -1
- package/dist/runtime/{result-C1ZdsM6Y.d.ts → result-BgcHn25t.d.ts} +21 -6
- package/dist/runtime/schema-B-jMZEbs.js +227 -0
- package/dist/runtime/schema-B-jMZEbs.js.map +1 -0
- package/dist/runtime/server.d.ts +2 -2
- package/dist/runtime/server.js +2 -2
- package/package.json +15 -5
- package/src/cli/agent-files.ts +6 -2
- package/src/cli/app-configuration.ts +135 -0
- package/src/cli/bin.ts +24 -1
- package/src/cli/build/artifact.ts +95 -0
- package/src/cli/build/client.ts +103 -0
- package/src/cli/build/server.ts +526 -0
- package/src/cli/client-config.ts +141 -0
- package/src/cli/client-entry.ts +152 -0
- package/src/cli/client-plugin.ts +86 -16
- package/src/cli/codegen.ts +5 -5
- package/src/cli/command.ts +41 -9
- package/src/cli/control-client.ts +343 -0
- package/src/cli/deploy.ts +217 -0
- package/src/cli/dev.ts +43 -108
- package/src/cli/module-boundaries.ts +183 -0
- package/src/cli/{new-project.ts → new-app.ts} +86 -86
- package/src/cli/package-manager.ts +9 -9
- package/src/client/app.ts +15 -0
- package/src/client/errors.ts +1 -17
- package/src/client/index.ts +2 -0
- package/src/client/sync.ts +172 -120
- package/src/dev-runtime/database.ts +253 -96
- package/src/dev-runtime/dev-database.ts +14 -14
- package/src/dev-runtime/functions.ts +57 -88
- package/src/dev-runtime/id.ts +2 -10
- package/src/dev-runtime/migrations.ts +68 -0
- package/src/dev-runtime/sync.ts +266 -83
- package/src/internal/function-definition.ts +77 -0
- package/src/internal/host.ts +1 -0
- package/src/internal/http-paths.ts +3 -1
- package/dist/runtime/api-BXXIZc_Q.js.map +0 -1
- package/dist/runtime/result-B2W-z2wG.js.map +0 -1
- package/dist/runtime/schema-CNEVLF7D.js +0 -116
- package/dist/runtime/schema-CNEVLF7D.js.map +0 -1
package/src/client/sync.ts
CHANGED
|
@@ -3,14 +3,19 @@ import {
|
|
|
3
3
|
Deferred,
|
|
4
4
|
Duration,
|
|
5
5
|
Effect,
|
|
6
|
+
Fiber,
|
|
6
7
|
HashMap,
|
|
7
8
|
Layer,
|
|
8
9
|
ManagedRuntime,
|
|
9
|
-
Predicate,
|
|
10
10
|
Schedule,
|
|
11
11
|
Schema,
|
|
12
12
|
} from "effect";
|
|
13
13
|
import * as Socket from "effect/unstable/socket/Socket";
|
|
14
|
+
import * as BrowserCrypto from "@effect/platform-browser/BrowserCrypto";
|
|
15
|
+
|
|
16
|
+
import { encodeCanonicalJson } from "@ignotum/contracts/json";
|
|
17
|
+
import { type AppStateRevision } from "@ignotum/contracts/runtime/hosted";
|
|
18
|
+
import { IdGenerator } from "@ignotum/shared/id";
|
|
14
19
|
|
|
15
20
|
import type {
|
|
16
21
|
ErrorValue,
|
|
@@ -20,12 +25,15 @@ import type {
|
|
|
20
25
|
import { failureFromWire, pending, Result } from "@ignotum/contracts/runtime/result";
|
|
21
26
|
import {
|
|
22
27
|
ClientMessageJson,
|
|
23
|
-
|
|
28
|
+
DeploymentChangedCloseCode,
|
|
29
|
+
InvocationId,
|
|
24
30
|
ServerMessageJson,
|
|
25
31
|
SubscriptionId,
|
|
26
32
|
decodeTransportValue,
|
|
27
33
|
type ClientMessage,
|
|
28
34
|
type FunctionAddress,
|
|
35
|
+
type ServerMessage,
|
|
36
|
+
type SyncHandshake,
|
|
29
37
|
type WireResult,
|
|
30
38
|
} from "@ignotum/contracts/runtime/sync";
|
|
31
39
|
import {
|
|
@@ -33,7 +41,6 @@ import {
|
|
|
33
41
|
ConnectionUnavailable,
|
|
34
42
|
InvalidClientMessage,
|
|
35
43
|
InvalidServerMessage,
|
|
36
|
-
MutationOutcomeUnknown,
|
|
37
44
|
ServerProtocolError,
|
|
38
45
|
clientErrorReporterLayer,
|
|
39
46
|
reportClientError,
|
|
@@ -49,6 +56,7 @@ interface QueryEntry {
|
|
|
49
56
|
readonly listeners: Set<Listener>;
|
|
50
57
|
readonly subscriptionId: SubscriptionId;
|
|
51
58
|
generation: number;
|
|
59
|
+
revision: AppStateRevision | undefined;
|
|
52
60
|
references: number;
|
|
53
61
|
result: QueryResult<unknown, ErrorValue> | ClientInfrastructureError;
|
|
54
62
|
}
|
|
@@ -65,34 +73,19 @@ interface QueryCache {
|
|
|
65
73
|
readonly entries: () => Iterable<QueryEntry>;
|
|
66
74
|
readonly getById: (id: SubscriptionId) => QueryEntry | undefined;
|
|
67
75
|
readonly setError: (id: SubscriptionId, error: ClientInfrastructureError) => void;
|
|
68
|
-
readonly setResult: (
|
|
76
|
+
readonly setResult: (
|
|
77
|
+
id: SubscriptionId,
|
|
78
|
+
result: QueryResult<unknown, ErrorValue>,
|
|
79
|
+
revision: AppStateRevision,
|
|
80
|
+
) => void;
|
|
69
81
|
readonly observe: (
|
|
70
82
|
functionAddress: FunctionAddress,
|
|
71
83
|
args: Schema.Json,
|
|
72
84
|
) => Effect.Effect<QueryObserver>;
|
|
73
85
|
}
|
|
74
86
|
|
|
75
|
-
const canonicalJson = (value: Schema.Json): string => {
|
|
76
|
-
if (
|
|
77
|
-
Predicate.isNull(value) ||
|
|
78
|
-
Predicate.isString(value) ||
|
|
79
|
-
Predicate.isNumber(value) ||
|
|
80
|
-
Predicate.isBoolean(value)
|
|
81
|
-
) {
|
|
82
|
-
return JSON.stringify(value);
|
|
83
|
-
}
|
|
84
|
-
if (globalThis.Array.isArray(value)) {
|
|
85
|
-
return `[${value.map(canonicalJson).join(",")}]`;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return `{${Object.entries(value)
|
|
89
|
-
.sort(([left], [right]) => left.localeCompare(right))
|
|
90
|
-
.map(([key, child]) => `${JSON.stringify(key)}:${canonicalJson(child)}`)
|
|
91
|
-
.join(",")}}`;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
87
|
const queryKey = (functionAddress: FunctionAddress, args: Schema.Json): string =>
|
|
95
|
-
|
|
88
|
+
encodeCanonicalJson([functionAddress, args]);
|
|
96
89
|
|
|
97
90
|
const subscribeMessage = (entry: QueryEntry): QueryLifecycleMessage => ({
|
|
98
91
|
type: "Subscribe",
|
|
@@ -102,7 +95,7 @@ const subscribeMessage = (entry: QueryEntry): QueryLifecycleMessage => ({
|
|
|
102
95
|
});
|
|
103
96
|
|
|
104
97
|
const makeQueryCache = (
|
|
105
|
-
allocateId:
|
|
98
|
+
allocateId: Effect.Effect<SubscriptionId>,
|
|
106
99
|
sendLifecycleMessage: (message: QueryLifecycleMessage) => Effect.Effect<void>,
|
|
107
100
|
) => {
|
|
108
101
|
const queries = new Map<string, QueryEntry>();
|
|
@@ -121,9 +114,10 @@ const makeQueryCache = (
|
|
|
121
114
|
function: functionAddress,
|
|
122
115
|
generation: 0,
|
|
123
116
|
listeners: new Set(),
|
|
117
|
+
revision: undefined,
|
|
124
118
|
references: 0,
|
|
125
119
|
result: pending(),
|
|
126
|
-
subscriptionId: allocateId
|
|
120
|
+
subscriptionId: yield* allocateId,
|
|
127
121
|
};
|
|
128
122
|
queries.set(key, created);
|
|
129
123
|
queryKeysById.set(created.subscriptionId, key);
|
|
@@ -185,10 +179,14 @@ const makeQueryCache = (
|
|
|
185
179
|
listener();
|
|
186
180
|
}
|
|
187
181
|
},
|
|
188
|
-
setResult: (id, result) => {
|
|
182
|
+
setResult: (id, result, revision) => {
|
|
189
183
|
const key = queryKeysById.get(id);
|
|
190
184
|
const entry = key === undefined ? undefined : queries.get(key);
|
|
191
185
|
if (entry === undefined) return;
|
|
186
|
+
if (entry.revision !== undefined && revision < entry.revision) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
entry.revision = revision;
|
|
192
190
|
entry.result = result;
|
|
193
191
|
for (const listener of entry.listeners) {
|
|
194
192
|
listener();
|
|
@@ -207,7 +205,33 @@ const resultFromWire = Effect.fn("SyncClient.resultFromWire")((wire: WireResult)
|
|
|
207
205
|
),
|
|
208
206
|
);
|
|
209
207
|
|
|
210
|
-
export const
|
|
208
|
+
export const shouldReloadForCloseCode = (code: number): boolean =>
|
|
209
|
+
code === DeploymentChangedCloseCode;
|
|
210
|
+
|
|
211
|
+
const handshakesMatch = (first: SyncHandshake, next: SyncHandshake): boolean =>
|
|
212
|
+
first.appId === next.appId &&
|
|
213
|
+
first.deploymentId === next.deploymentId &&
|
|
214
|
+
first.generation === next.generation;
|
|
215
|
+
|
|
216
|
+
type HandshakeDecision = "Accept" | "Ignore" | "Reload";
|
|
217
|
+
|
|
218
|
+
const handshakeDecision = (
|
|
219
|
+
remembered: SyncHandshake | undefined,
|
|
220
|
+
acceptedOnConnection: boolean,
|
|
221
|
+
next: SyncHandshake,
|
|
222
|
+
): HandshakeDecision => {
|
|
223
|
+
if (remembered !== undefined && !handshakesMatch(remembered, next)) return "Reload";
|
|
224
|
+
return acceptedOnConnection ? "Ignore" : "Accept";
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
export const syncClientInternals = {
|
|
228
|
+
makeQueryCache,
|
|
229
|
+
handshakeDecision,
|
|
230
|
+
queryIdentity: queryKey,
|
|
231
|
+
resultFromWire,
|
|
232
|
+
handshakesMatch,
|
|
233
|
+
shouldReloadForCloseCode,
|
|
234
|
+
};
|
|
211
235
|
|
|
212
236
|
const socketUrl = (): string => {
|
|
213
237
|
const url = new URL(syncPath, globalThis.location.href);
|
|
@@ -226,12 +250,13 @@ interface SyncClientService {
|
|
|
226
250
|
) => Effect.Effect<QueryObserver, ClientInfrastructureError>;
|
|
227
251
|
}
|
|
228
252
|
|
|
229
|
-
interface
|
|
253
|
+
interface PendingInvocation {
|
|
230
254
|
readonly deferred: Deferred.Deferred<
|
|
231
255
|
IgnotumResult<unknown, ErrorValue>,
|
|
232
256
|
ClientInfrastructureError
|
|
233
257
|
>;
|
|
234
258
|
readonly function: FunctionAddress;
|
|
259
|
+
readonly message: Extract<ClientMessage, { readonly type: "Invoke" }>;
|
|
235
260
|
}
|
|
236
261
|
|
|
237
262
|
export class SyncClient extends Context.Service<SyncClient, SyncClientService>()(
|
|
@@ -240,8 +265,10 @@ export class SyncClient extends Context.Service<SyncClient, SyncClientService>()
|
|
|
240
265
|
static readonly layer = Layer.effect(
|
|
241
266
|
SyncClient,
|
|
242
267
|
Effect.gen(function* () {
|
|
243
|
-
|
|
244
|
-
let
|
|
268
|
+
const ids = yield* IdGenerator;
|
|
269
|
+
let invocations = HashMap.empty<InvocationId, PendingInvocation>();
|
|
270
|
+
let rememberedHandshake: SyncHandshake | undefined;
|
|
271
|
+
let reloadRequested = false;
|
|
245
272
|
let writer: SocketWriter | undefined;
|
|
246
273
|
|
|
247
274
|
const encode = (message: ClientMessage) =>
|
|
@@ -274,56 +301,49 @@ export class SyncClient extends Context.Service<SyncClient, SyncClientService>()
|
|
|
274
301
|
)
|
|
275
302
|
: sendWith(writer, message);
|
|
276
303
|
|
|
277
|
-
const queryCache = makeQueryCache(
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}),
|
|
287
|
-
),
|
|
304
|
+
const queryCache = makeQueryCache(ids.generate(SubscriptionId), (message) =>
|
|
305
|
+
writer === undefined
|
|
306
|
+
? Effect.void
|
|
307
|
+
: send(message).pipe(
|
|
308
|
+
Effect.catchTags({
|
|
309
|
+
ConnectionUnavailable: reportClientError,
|
|
310
|
+
InvalidClientMessage: reportClientError,
|
|
311
|
+
}),
|
|
312
|
+
),
|
|
288
313
|
);
|
|
289
314
|
|
|
290
|
-
const
|
|
291
|
-
const
|
|
292
|
-
if (
|
|
315
|
+
const rejectInvocation = (id: InvocationId, error: ClientInfrastructureError) => {
|
|
316
|
+
const pendingInvocation = HashMap.getUnsafe(invocations, id);
|
|
317
|
+
if (pendingInvocation === undefined) {
|
|
293
318
|
return Effect.void;
|
|
294
319
|
}
|
|
295
|
-
|
|
320
|
+
invocations = HashMap.remove(invocations, id);
|
|
296
321
|
return reportClientError(error).pipe(
|
|
297
|
-
Effect.andThen(Deferred.fail(
|
|
322
|
+
Effect.andThen(Deferred.fail(pendingInvocation.deferred, error)),
|
|
298
323
|
Effect.asVoid,
|
|
299
324
|
);
|
|
300
325
|
};
|
|
301
326
|
|
|
302
|
-
const handleMessage = Effect.fn("SyncClient.handleMessage")(function* (
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
InvalidServerMessage.make({
|
|
306
|
-
cause,
|
|
307
|
-
message: "The server returned an invalid sync message.",
|
|
308
|
-
}),
|
|
309
|
-
),
|
|
310
|
-
);
|
|
311
|
-
|
|
327
|
+
const handleMessage = Effect.fn("SyncClient.handleMessage")(function* (
|
|
328
|
+
message: Exclude<ServerMessage, SyncHandshake | { readonly type: "DeploymentChanged" }>,
|
|
329
|
+
) {
|
|
312
330
|
switch (message.type) {
|
|
313
|
-
case "
|
|
331
|
+
case "Snapshot": {
|
|
314
332
|
const entry = queryCache.getById(message.id);
|
|
315
333
|
if (entry === undefined) return;
|
|
316
334
|
yield* resultFromWire(message.result).pipe(
|
|
317
|
-
Effect.tap((result) =>
|
|
335
|
+
Effect.tap((result) =>
|
|
336
|
+
Effect.sync(() => queryCache.setResult(message.id, result, message.revision)),
|
|
337
|
+
),
|
|
318
338
|
);
|
|
319
339
|
return;
|
|
320
340
|
}
|
|
321
|
-
case "
|
|
322
|
-
const
|
|
323
|
-
if (
|
|
324
|
-
|
|
341
|
+
case "Result": {
|
|
342
|
+
const pendingInvocation = HashMap.getUnsafe(invocations, message.id);
|
|
343
|
+
if (pendingInvocation === undefined) return;
|
|
344
|
+
invocations = HashMap.remove(invocations, message.id);
|
|
325
345
|
yield* resultFromWire(message.result).pipe(
|
|
326
|
-
Effect.flatMap((result) => Deferred.succeed(
|
|
346
|
+
Effect.flatMap((result) => Deferred.succeed(pendingInvocation.deferred, result)),
|
|
327
347
|
);
|
|
328
348
|
return;
|
|
329
349
|
}
|
|
@@ -339,9 +359,9 @@ export class SyncClient extends Context.Service<SyncClient, SyncClientService>()
|
|
|
339
359
|
message: message.message,
|
|
340
360
|
operation: message.operation,
|
|
341
361
|
});
|
|
342
|
-
if (message.operation?.type === "
|
|
343
|
-
yield*
|
|
344
|
-
} else if (message.operation?.type === "
|
|
362
|
+
if (message.operation?.type === "Invocation") {
|
|
363
|
+
yield* rejectInvocation(message.operation.id, error);
|
|
364
|
+
} else if (message.operation?.type === "Subscription") {
|
|
345
365
|
queryCache.setError(message.operation.id, error);
|
|
346
366
|
yield* reportClientError(error);
|
|
347
367
|
} else {
|
|
@@ -352,36 +372,81 @@ export class SyncClient extends Context.Service<SyncClient, SyncClientService>()
|
|
|
352
372
|
}
|
|
353
373
|
});
|
|
354
374
|
|
|
355
|
-
const
|
|
375
|
+
const requestReload = () => {
|
|
376
|
+
if (reloadRequested) return;
|
|
377
|
+
reloadRequested = true;
|
|
378
|
+
writer = undefined;
|
|
379
|
+
globalThis.location.reload();
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
const disconnect = Effect.sync(() => {
|
|
356
383
|
writer = undefined;
|
|
357
|
-
const current = mutations;
|
|
358
|
-
mutations = HashMap.empty();
|
|
359
|
-
yield* Effect.forEach(HashMap.entries(current), ([id, pendingMutation]) => {
|
|
360
|
-
const error = MutationOutcomeUnknown.make({
|
|
361
|
-
function: pendingMutation.function,
|
|
362
|
-
id,
|
|
363
|
-
message: "The connection closed before the mutation outcome was known.",
|
|
364
|
-
});
|
|
365
|
-
return reportClientError(error).pipe(
|
|
366
|
-
Effect.andThen(Deferred.fail(pendingMutation.deferred, error)),
|
|
367
|
-
);
|
|
368
|
-
});
|
|
369
384
|
});
|
|
370
385
|
|
|
371
386
|
const connect = Effect.scoped(
|
|
372
387
|
Effect.gen(function* () {
|
|
388
|
+
if (reloadRequested) return yield* Effect.never;
|
|
373
389
|
const socket = yield* Socket.makeWebSocket(socketUrl(), {
|
|
374
|
-
closeCodeIsError: () =>
|
|
390
|
+
closeCodeIsError: (code) => {
|
|
391
|
+
if (shouldReloadForCloseCode(code)) requestReload();
|
|
392
|
+
return false;
|
|
393
|
+
},
|
|
375
394
|
});
|
|
376
395
|
const write = yield* socket.writer;
|
|
377
|
-
const
|
|
396
|
+
const handshakeReceived = yield* Deferred.make<void>();
|
|
397
|
+
let handshakeAccepted = false;
|
|
398
|
+
const acceptHandshake = Effect.fn("SyncClient.acceptHandshake")(function* (
|
|
399
|
+
handshake: SyncHandshake,
|
|
400
|
+
) {
|
|
401
|
+
const decision = handshakeDecision(rememberedHandshake, handshakeAccepted, handshake);
|
|
402
|
+
if (decision === "Reload") {
|
|
403
|
+
requestReload();
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
if (decision === "Ignore") return;
|
|
407
|
+
rememberedHandshake = handshake;
|
|
408
|
+
handshakeAccepted = true;
|
|
378
409
|
writer = write;
|
|
379
|
-
yield*
|
|
380
|
-
|
|
410
|
+
yield* Deferred.succeed(handshakeReceived, undefined);
|
|
411
|
+
yield* Effect.gen(function* () {
|
|
412
|
+
yield* Effect.forEach(queryCache.entries(), (entry) =>
|
|
413
|
+
sendWith(write, subscribeMessage(entry)),
|
|
414
|
+
);
|
|
415
|
+
yield* Effect.forEach(HashMap.values(invocations), (pendingInvocation) =>
|
|
416
|
+
sendWith(write, pendingInvocation.message),
|
|
417
|
+
);
|
|
418
|
+
}).pipe(Effect.tapError(reportClientError), Effect.ignore);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
const handleText = Effect.fn("SyncClient.handleText")(function* (text: string) {
|
|
422
|
+
const message = yield* Schema.decodeEffect(ServerMessageJson)(text).pipe(
|
|
423
|
+
Effect.mapError((cause) =>
|
|
424
|
+
InvalidServerMessage.make({
|
|
425
|
+
cause,
|
|
426
|
+
message: "The server returned an invalid sync message.",
|
|
427
|
+
}),
|
|
428
|
+
),
|
|
381
429
|
);
|
|
382
|
-
|
|
430
|
+
if (message.type === "Handshake") return yield* acceptHandshake(message);
|
|
431
|
+
if (message.type === "DeploymentChanged") {
|
|
432
|
+
requestReload();
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (!handshakeAccepted) {
|
|
436
|
+
return yield* InvalidServerMessage.make({
|
|
437
|
+
cause: new Error("The server sent a sync message before the handshake."),
|
|
438
|
+
message: "The server returned a sync message before the handshake.",
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
return yield* handleMessage(message);
|
|
442
|
+
});
|
|
383
443
|
|
|
384
|
-
yield* socket.runString(
|
|
444
|
+
const run = yield* socket.runString(handleText).pipe(Effect.forkChild);
|
|
445
|
+
yield* Effect.raceFirst(
|
|
446
|
+
Deferred.await(handshakeReceived).pipe(Effect.timeout("10 seconds")),
|
|
447
|
+
Fiber.join(run),
|
|
448
|
+
);
|
|
449
|
+
yield* Fiber.join(run);
|
|
385
450
|
}),
|
|
386
451
|
).pipe(
|
|
387
452
|
Effect.mapError((cause) =>
|
|
@@ -422,58 +487,45 @@ export class SyncClient extends Context.Service<SyncClient, SyncClientService>()
|
|
|
422
487
|
|
|
423
488
|
const mutate: SyncClientService["mutate"] = Effect.fn("SyncClient.mutate")(
|
|
424
489
|
function* (functionAddress, args) {
|
|
425
|
-
const
|
|
490
|
+
const invocationId = yield* ids.generate(InvocationId);
|
|
426
491
|
const deferred = yield* Deferred.make<
|
|
427
492
|
IgnotumResult<unknown, ErrorValue>,
|
|
428
493
|
ClientInfrastructureError
|
|
429
494
|
>();
|
|
430
|
-
mutations = HashMap.set(mutations, requestId, {
|
|
431
|
-
deferred,
|
|
432
|
-
function: functionAddress,
|
|
433
|
-
});
|
|
434
495
|
const message: ClientMessage = {
|
|
435
|
-
type: "
|
|
436
|
-
id:
|
|
496
|
+
type: "Invoke",
|
|
497
|
+
id: invocationId,
|
|
498
|
+
kind: "Mutation",
|
|
437
499
|
function: functionAddress,
|
|
438
500
|
args,
|
|
439
501
|
};
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
message: "The sync connection is not ready.",
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
|
-
return yield* sendWith(mutationWriter, message).pipe(
|
|
448
|
-
Effect.mapError((error) =>
|
|
449
|
-
Schema.is(InvalidClientMessage)(error)
|
|
450
|
-
? error
|
|
451
|
-
: MutationOutcomeUnknown.make({
|
|
452
|
-
cause: error,
|
|
453
|
-
function: functionAddress,
|
|
454
|
-
id: requestId,
|
|
455
|
-
message: "The connection failed while sending the mutation.",
|
|
456
|
-
}),
|
|
457
|
-
),
|
|
458
|
-
);
|
|
502
|
+
invocations = HashMap.set(invocations, invocationId, {
|
|
503
|
+
deferred,
|
|
504
|
+
function: functionAddress,
|
|
505
|
+
message,
|
|
459
506
|
});
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
507
|
+
if (writer !== undefined) {
|
|
508
|
+
yield* sendWith(writer, message).pipe(
|
|
509
|
+
Effect.catchTags({
|
|
510
|
+
ConnectionUnavailable: reportClientError,
|
|
511
|
+
InvalidClientMessage: (error) => rejectInvocation(invocationId, error),
|
|
512
|
+
}),
|
|
513
|
+
);
|
|
514
|
+
}
|
|
466
515
|
return yield* Deferred.await(deferred);
|
|
467
516
|
},
|
|
468
517
|
);
|
|
469
518
|
|
|
470
519
|
return SyncClient.of({ mutate, observe });
|
|
471
520
|
}),
|
|
472
|
-
).pipe(Layer.provide(Socket.layerWebSocketConstructorGlobal));
|
|
521
|
+
).pipe(Layer.provide(Layer.merge(Socket.layerWebSocketConstructorGlobal, BrowserCrypto.layer)));
|
|
473
522
|
}
|
|
474
523
|
|
|
475
524
|
export const syncRuntime = ManagedRuntime.make(
|
|
476
|
-
SyncClient.layer.pipe(
|
|
525
|
+
SyncClient.layer.pipe(
|
|
526
|
+
Layer.provideMerge(clientErrorReporterLayer),
|
|
527
|
+
Layer.provideMerge(IdGenerator.layer),
|
|
528
|
+
),
|
|
477
529
|
);
|
|
478
530
|
|
|
479
531
|
// SAFETY: Vite adds this optional property to browser development modules.
|