ignotum 0.0.12 → 0.0.13
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/dist/cli/bin.mjs +825 -403
- package/dist/cli/bin.mjs.map +1 -1
- package/dist/runtime/{api-DzcR7spt.js → api-CAgKDij7.js} +38 -2
- package/dist/runtime/api-CAgKDij7.js.map +1 -0
- package/dist/runtime/{api-5XSrIeqW.d.ts → api-Cv3hMbzo.d.ts} +4 -4
- package/dist/runtime/client.d.ts +39 -9
- package/dist/runtime/client.js +339 -60
- package/dist/runtime/client.js.map +1 -1
- package/dist/runtime/descriptor-Cyo9FP9n-C0SRVXNW.js +154 -0
- package/dist/runtime/descriptor-Cyo9FP9n-C0SRVXNW.js.map +1 -0
- package/dist/runtime/id-Bt9XWRGL.js +423 -0
- package/dist/runtime/id-Bt9XWRGL.js.map +1 -0
- package/dist/runtime/{id-Cs82tq9Q-CK-maMgN.d.ts → id-BzFHf3Wo-DGPCjgrf.d.ts} +2 -2
- package/dist/runtime/id-Dz0apuB3.d.ts +1 -0
- package/dist/runtime/{index-CrWg4Z0y.d.ts → index-D54flWtH.d.ts} +9 -5
- package/dist/runtime/internal/api.d.ts +1 -1
- package/dist/runtime/internal/api.js +1 -1
- package/dist/runtime/internal/host.d.ts +9 -6
- package/dist/runtime/internal/host.js +15 -6
- package/dist/runtime/internal/host.js.map +1 -1
- 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/{pagination-D-R9NR61-CpIoHFoI.d.ts → pagination-DnKg3dkI-r5ZUxBBx.d.ts} +31 -6
- package/dist/runtime/pagination-Dz0apuB3.d.ts +1 -0
- package/dist/runtime/result-DKAA4gpS.d.ts +1 -0
- package/dist/runtime/{schema-B9XxyRO8.js → schema-1Zs03-iS.js} +5 -3
- package/dist/runtime/schema-1Zs03-iS.js.map +1 -0
- package/dist/runtime/server.d.ts +6 -4
- package/dist/runtime/server.js +2 -2
- package/dist/runtime/server.js.map +1 -1
- package/dist/runtime/{sync-avN7NkcU.d.ts → sync-Bs8J3fIr.d.ts} +2 -2
- package/package.json +1 -1
- package/src/cli/agent-files.ts +6 -0
- package/src/client/hooks.ts +68 -0
- package/src/client/id.ts +259 -0
- package/src/client/index.ts +5 -2
- package/src/client/sync.ts +143 -10
- package/src/dev-runtime/functions.ts +111 -88
- package/src/dev-runtime/id.ts +175 -0
- package/src/dev-runtime/query-cache.ts +105 -0
- package/src/dev-runtime/sync.ts +149 -17
- package/src/server/index.ts +2 -0
- package/dist/runtime/api-DzcR7spt.js.map +0 -1
- package/dist/runtime/descriptor-XzDX2JDw-j3O6YHgW.js +0 -330
- package/dist/runtime/descriptor-XzDX2JDw-j3O6YHgW.js.map +0 -1
- package/dist/runtime/file-C1abuMgd.js +0 -173
- package/dist/runtime/file-C1abuMgd.js.map +0 -1
- package/dist/runtime/pagination-CX5IPbEi.d.ts +0 -1
- package/dist/runtime/result-DIjKM-p4.d.ts +0 -1
- package/dist/runtime/schema-B9XxyRO8.js.map +0 -1
package/src/client/sync.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BrowserSession } from "./id.js";
|
|
1
2
|
import {
|
|
2
3
|
Context,
|
|
3
4
|
Deferred,
|
|
@@ -7,6 +8,7 @@ import {
|
|
|
7
8
|
HashMap,
|
|
8
9
|
Layer,
|
|
9
10
|
ManagedRuntime,
|
|
11
|
+
Option,
|
|
10
12
|
Schedule,
|
|
11
13
|
Schema,
|
|
12
14
|
} from "effect";
|
|
@@ -59,6 +61,7 @@ interface QueryEntry {
|
|
|
59
61
|
readonly listeners: Set<Listener>;
|
|
60
62
|
readonly subscriptionId: SubscriptionId;
|
|
61
63
|
generation: number;
|
|
64
|
+
dependsOnId?: boolean;
|
|
62
65
|
revision: AppStateRevision | undefined;
|
|
63
66
|
references: number;
|
|
64
67
|
result: QueryResult<unknown, ErrorValue> | ClientInfrastructureError;
|
|
@@ -73,6 +76,8 @@ interface QueryObserver {
|
|
|
73
76
|
type QueryLifecycleMessage = Extract<ClientMessage, { readonly type: "Subscribe" | "Unsubscribe" }>;
|
|
74
77
|
|
|
75
78
|
interface QueryCache {
|
|
79
|
+
readonly invalidate: (ids?: ReadonlyArray<SubscriptionId>) => void;
|
|
80
|
+
readonly invalidatePrivate: () => void;
|
|
76
81
|
readonly entries: () => Iterable<QueryEntry>;
|
|
77
82
|
readonly getById: (id: SubscriptionId) => QueryEntry | undefined;
|
|
78
83
|
readonly setResult: (
|
|
@@ -165,7 +170,19 @@ const makeQueryCache = (
|
|
|
165
170
|
} satisfies QueryObserver;
|
|
166
171
|
});
|
|
167
172
|
|
|
173
|
+
const clear = (entry: QueryEntry) => {
|
|
174
|
+
entry.result = pending();
|
|
175
|
+
entry.revision = undefined;
|
|
176
|
+
for (const listener of entry.listeners) listener();
|
|
177
|
+
};
|
|
168
178
|
return {
|
|
179
|
+
invalidate: (ids) => {
|
|
180
|
+
for (const entry of queries.values())
|
|
181
|
+
if (ids === undefined || ids.includes(entry.subscriptionId)) clear(entry);
|
|
182
|
+
},
|
|
183
|
+
invalidatePrivate: () => {
|
|
184
|
+
for (const entry of queries.values()) if (entry.dependsOnId !== false) clear(entry);
|
|
185
|
+
},
|
|
169
186
|
entries: () => queries.values(),
|
|
170
187
|
getById: (id: SubscriptionId) => queriesById.get(id),
|
|
171
188
|
observe,
|
|
@@ -251,6 +268,7 @@ interface SyncClientService {
|
|
|
251
268
|
}
|
|
252
269
|
|
|
253
270
|
interface PendingInvocation {
|
|
271
|
+
readonly sessionEpoch: string;
|
|
254
272
|
readonly deferred: Deferred.Deferred<
|
|
255
273
|
IgnotumResult<unknown, ErrorValue>,
|
|
256
274
|
ClientInfrastructureError
|
|
@@ -270,10 +288,12 @@ export class SyncClient extends Context.Service<SyncClient, SyncClientService>()
|
|
|
270
288
|
SyncClient,
|
|
271
289
|
Effect.gen(function* () {
|
|
272
290
|
const ids = yield* IdGenerator;
|
|
291
|
+
const session = yield* BrowserSession;
|
|
273
292
|
let invocations = HashMap.empty<InvocationId, PendingInvocation>();
|
|
274
293
|
let rememberedHandshake: SyncHandshake | undefined;
|
|
275
294
|
let reloadRequested = false;
|
|
276
295
|
let writer: SocketWriter | undefined;
|
|
296
|
+
let sessionSuspended: Deferred.Deferred<void> | undefined;
|
|
277
297
|
|
|
278
298
|
const encode = (message: ClientMessage) =>
|
|
279
299
|
Schema.encodeEffect(ClientMessageJson)(message).pipe(
|
|
@@ -316,8 +336,17 @@ export class SyncClient extends Context.Service<SyncClient, SyncClientService>()
|
|
|
316
336
|
),
|
|
317
337
|
);
|
|
318
338
|
|
|
339
|
+
const unsubscribeSession = session.subscribe(() => {
|
|
340
|
+
if (session.current() === undefined) {
|
|
341
|
+
queryCache.invalidatePrivate();
|
|
342
|
+
writer = undefined;
|
|
343
|
+
if (sessionSuspended !== undefined) Deferred.doneUnsafe(sessionSuspended, Effect.void);
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
yield* Effect.addFinalizer(() => Effect.sync(unsubscribeSession));
|
|
347
|
+
|
|
319
348
|
const rejectInvocation = (id: InvocationId, error: ClientInfrastructureError) => {
|
|
320
|
-
const pendingInvocation = HashMap.
|
|
349
|
+
const pendingInvocation = Option.getOrUndefined(HashMap.get(invocations, id));
|
|
321
350
|
if (pendingInvocation === undefined) {
|
|
322
351
|
return Effect.void;
|
|
323
352
|
}
|
|
@@ -333,9 +362,52 @@ export class SyncClient extends Context.Service<SyncClient, SyncClientService>()
|
|
|
333
362
|
message: Exclude<ServerMessage, SyncHandshake | { readonly type: "Deployment" }>,
|
|
334
363
|
) {
|
|
335
364
|
switch (message.type) {
|
|
365
|
+
case "Session": {
|
|
366
|
+
if (message.event === "Updated") {
|
|
367
|
+
const previous = session.current();
|
|
368
|
+
if (previous !== undefined && previous.sessionEpoch !== message.session.sessionEpoch)
|
|
369
|
+
return;
|
|
370
|
+
if (previous?.viewRevision !== message.session.viewRevision)
|
|
371
|
+
queryCache.invalidatePrivate();
|
|
372
|
+
session.accept(message.session, message.serverTime);
|
|
373
|
+
} else {
|
|
374
|
+
if (rememberedHandshake?.session?.sessionEpoch !== message.sessionEpoch) return;
|
|
375
|
+
session.suspend(
|
|
376
|
+
message.event === "Unavailable"
|
|
377
|
+
? "Your ID session is temporarily unavailable."
|
|
378
|
+
: undefined,
|
|
379
|
+
);
|
|
380
|
+
if (message.event !== "Unavailable") {
|
|
381
|
+
yield* Effect.forEach(HashMap.keys(invocations), (id) =>
|
|
382
|
+
rejectInvocation(
|
|
383
|
+
id,
|
|
384
|
+
ServerError.make({
|
|
385
|
+
code: "SessionChanged",
|
|
386
|
+
message: "The session changed. This operation may already have completed.",
|
|
387
|
+
}),
|
|
388
|
+
),
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
case "Invalidate": {
|
|
395
|
+
if (message.sessionEpoch === session.current()?.sessionEpoch)
|
|
396
|
+
queryCache.invalidate(message.ids);
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
336
399
|
case "Snapshot": {
|
|
400
|
+
const current = session.current();
|
|
401
|
+
if (current === undefined || message.identity?.sessionEpoch !== current.sessionEpoch)
|
|
402
|
+
return;
|
|
403
|
+
if (
|
|
404
|
+
message.identity.dependsOnId &&
|
|
405
|
+
message.identity.viewRevision !== current.viewRevision
|
|
406
|
+
)
|
|
407
|
+
return;
|
|
337
408
|
const entry = queryCache.getById(message.id);
|
|
338
409
|
if (entry === undefined) return;
|
|
410
|
+
entry.dependsOnId = message.identity.dependsOnId;
|
|
339
411
|
if (entry.revision !== undefined && message.revision < entry.revision) return;
|
|
340
412
|
yield* resultFromWire(message.result, message.files ?? []).pipe(
|
|
341
413
|
Effect.tap((result) =>
|
|
@@ -351,14 +423,16 @@ export class SyncClient extends Context.Service<SyncClient, SyncClientService>()
|
|
|
351
423
|
return;
|
|
352
424
|
}
|
|
353
425
|
case "Preparation": {
|
|
354
|
-
|
|
426
|
+
if (message.sessionEpoch !== session.current()?.sessionEpoch) return;
|
|
427
|
+
const pendingInvocation = Option.getOrUndefined(HashMap.get(invocations, message.id));
|
|
355
428
|
if (pendingInvocation !== undefined) {
|
|
356
429
|
yield* Deferred.succeed(pendingInvocation.prepared, message.uploads);
|
|
357
430
|
}
|
|
358
431
|
return;
|
|
359
432
|
}
|
|
360
433
|
case "Result": {
|
|
361
|
-
|
|
434
|
+
if (message.sessionEpoch !== session.current()?.sessionEpoch) return;
|
|
435
|
+
const pendingInvocation = Option.getOrUndefined(HashMap.get(invocations, message.id));
|
|
362
436
|
if (pendingInvocation === undefined) return;
|
|
363
437
|
yield* resultFromWire(message.result).pipe(
|
|
364
438
|
Effect.flatMap((result) => {
|
|
@@ -372,6 +446,7 @@ export class SyncClient extends Context.Service<SyncClient, SyncClientService>()
|
|
|
372
446
|
return;
|
|
373
447
|
}
|
|
374
448
|
case "Error": {
|
|
449
|
+
if (message.sessionEpoch !== session.current()?.sessionEpoch) return;
|
|
375
450
|
const error =
|
|
376
451
|
message.operation === undefined
|
|
377
452
|
? ServerError.make({
|
|
@@ -410,12 +485,18 @@ export class SyncClient extends Context.Service<SyncClient, SyncClientService>()
|
|
|
410
485
|
const connect = Effect.scoped(
|
|
411
486
|
Effect.gen(function* () {
|
|
412
487
|
if (reloadRequested) return yield* Effect.never;
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
488
|
+
yield* session.bootstrap.pipe(
|
|
489
|
+
Effect.mapError((error) => ConnectionUnavailable.make({ message: error.message })),
|
|
490
|
+
);
|
|
491
|
+
const socket = yield* Socket.makeWebSocket(
|
|
492
|
+
`${socketUrl()}${session.socketParameters()}`,
|
|
493
|
+
{
|
|
494
|
+
closeCodeIsError: (code) => {
|
|
495
|
+
if (shouldReloadForCloseCode(code)) requestReload();
|
|
496
|
+
return false;
|
|
497
|
+
},
|
|
417
498
|
},
|
|
418
|
-
|
|
499
|
+
);
|
|
419
500
|
const write = yield* socket.writer;
|
|
420
501
|
const handshakeReceived = yield* Deferred.make<void>();
|
|
421
502
|
let handshakeAccepted = false;
|
|
@@ -428,6 +509,41 @@ export class SyncClient extends Context.Service<SyncClient, SyncClientService>()
|
|
|
428
509
|
return;
|
|
429
510
|
}
|
|
430
511
|
if (decision === "Ignore") return;
|
|
512
|
+
if (handshake.session === undefined || handshake.serverTime === undefined) {
|
|
513
|
+
requestReload();
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
if (
|
|
517
|
+
rememberedHandshake?.session !== undefined &&
|
|
518
|
+
rememberedHandshake.session.sessionEpoch !== handshake.session.sessionEpoch
|
|
519
|
+
) {
|
|
520
|
+
queryCache.invalidatePrivate();
|
|
521
|
+
yield* Effect.forEach(HashMap.keys(invocations), (id) =>
|
|
522
|
+
rejectInvocation(
|
|
523
|
+
id,
|
|
524
|
+
ServerError.make({
|
|
525
|
+
code: "SessionChanged",
|
|
526
|
+
message: "The session changed. This operation may already have completed.",
|
|
527
|
+
}),
|
|
528
|
+
),
|
|
529
|
+
);
|
|
530
|
+
} else if (
|
|
531
|
+
rememberedHandshake?.session?.viewRevision !== handshake.session.viewRevision
|
|
532
|
+
) {
|
|
533
|
+
queryCache.invalidatePrivate();
|
|
534
|
+
}
|
|
535
|
+
yield* Effect.forEach(HashMap.entries(invocations), ([id, pending]) =>
|
|
536
|
+
pending.sessionEpoch === handshake.session!.sessionEpoch
|
|
537
|
+
? Effect.void
|
|
538
|
+
: rejectInvocation(
|
|
539
|
+
id,
|
|
540
|
+
ServerError.make({
|
|
541
|
+
code: "SessionChanged",
|
|
542
|
+
message: "The session changed. This operation may already have completed.",
|
|
543
|
+
}),
|
|
544
|
+
),
|
|
545
|
+
);
|
|
546
|
+
session.accept(handshake.session, handshake.serverTime);
|
|
431
547
|
rememberedHandshake = handshake;
|
|
432
548
|
handshakeAccepted = true;
|
|
433
549
|
writer = write;
|
|
@@ -465,12 +581,19 @@ export class SyncClient extends Context.Service<SyncClient, SyncClientService>()
|
|
|
465
581
|
return yield* handleMessage(message);
|
|
466
582
|
});
|
|
467
583
|
|
|
584
|
+
const suspended = yield* Deferred.make<void>();
|
|
585
|
+
sessionSuspended = suspended;
|
|
586
|
+
yield* Effect.addFinalizer(() =>
|
|
587
|
+
Effect.sync(() => {
|
|
588
|
+
if (sessionSuspended === suspended) sessionSuspended = undefined;
|
|
589
|
+
}),
|
|
590
|
+
);
|
|
468
591
|
const run = yield* socket.runString(handleText).pipe(Effect.forkChild);
|
|
469
592
|
yield* Effect.raceFirst(
|
|
470
593
|
Deferred.await(handshakeReceived).pipe(Effect.timeout("10 seconds")),
|
|
471
594
|
Fiber.join(run),
|
|
472
595
|
);
|
|
473
|
-
yield* Fiber.join(run);
|
|
596
|
+
yield* Effect.raceFirst(Fiber.join(run), Deferred.await(suspended));
|
|
474
597
|
}),
|
|
475
598
|
).pipe(
|
|
476
599
|
Effect.mapError((cause) =>
|
|
@@ -511,6 +634,12 @@ export class SyncClient extends Context.Service<SyncClient, SyncClientService>()
|
|
|
511
634
|
|
|
512
635
|
const mutate: SyncClientService["mutate"] = Effect.fn("SyncClient.mutate")(
|
|
513
636
|
function* (functionAddress, args) {
|
|
637
|
+
const admittedSession =
|
|
638
|
+
session.current() ??
|
|
639
|
+
rememberedHandshake?.session ??
|
|
640
|
+
(yield* session.bootstrap.pipe(
|
|
641
|
+
Effect.mapError((error) => ConnectionUnavailable.make({ message: error.message })),
|
|
642
|
+
));
|
|
514
643
|
const invocationId = yield* ids.generate(InvocationId);
|
|
515
644
|
const preparedArguments = yield* prepareMutationArguments(args).pipe(
|
|
516
645
|
Effect.provideService(IdGenerator, ids),
|
|
@@ -559,6 +688,7 @@ export class SyncClient extends Context.Service<SyncClient, SyncClientService>()
|
|
|
559
688
|
files: preparedArguments.fileIds,
|
|
560
689
|
};
|
|
561
690
|
invocations = HashMap.set(invocations, invocationId, {
|
|
691
|
+
sessionEpoch: admittedSession.sessionEpoch,
|
|
562
692
|
deferred,
|
|
563
693
|
function: functionAddress,
|
|
564
694
|
message,
|
|
@@ -630,7 +760,9 @@ export class SyncClient extends Context.Service<SyncClient, SyncClientService>()
|
|
|
630
760
|
}),
|
|
631
761
|
{ concurrency: 3, discard: true },
|
|
632
762
|
);
|
|
633
|
-
const pendingInvocation =
|
|
763
|
+
const pendingInvocation = Option.getOrUndefined(
|
|
764
|
+
HashMap.get(invocations, invocationId),
|
|
765
|
+
);
|
|
634
766
|
if (pendingInvocation === undefined) return yield* Deferred.await(deferred);
|
|
635
767
|
invocations = HashMap.set(invocations, invocationId, {
|
|
636
768
|
...pendingInvocation,
|
|
@@ -663,6 +795,7 @@ export class SyncClient extends Context.Service<SyncClient, SyncClientService>()
|
|
|
663
795
|
|
|
664
796
|
export const syncRuntime = ManagedRuntime.make(
|
|
665
797
|
SyncClient.layer.pipe(
|
|
798
|
+
Layer.provideMerge(BrowserSession.layer),
|
|
666
799
|
Layer.provideMerge(clientErrorReporterLayer),
|
|
667
800
|
Layer.provideMerge(IdGenerator.layer),
|
|
668
801
|
),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { makeIdContext, type IdContext, type SessionState } from "@ignotum/contracts/id";
|
|
1
2
|
import {
|
|
2
3
|
FunctionUnavailable,
|
|
3
4
|
InvalidArguments,
|
|
@@ -61,7 +62,9 @@ import {
|
|
|
61
62
|
export { FunctionUnavailable, InvalidArguments, UnknownFunction, WrongFunctionKind };
|
|
62
63
|
export type { FunctionArguments, FunctionKind };
|
|
63
64
|
|
|
64
|
-
export type RuntimeHandlerContext = LocalMutationContext | LocalQueryContext
|
|
65
|
+
export type RuntimeHandlerContext = (LocalMutationContext | LocalQueryContext) & {
|
|
66
|
+
readonly id: IdContext;
|
|
67
|
+
};
|
|
65
68
|
export type RuntimeFunctionDefinition = InternalRuntimeFunctionDefinition<RuntimeHandlerContext>;
|
|
66
69
|
|
|
67
70
|
const loadDevelopmentEnvironment = Effect.fn("DevelopmentEnvironment.load")(function* (
|
|
@@ -243,6 +246,7 @@ export class FunctionExecutor extends Context.Service<
|
|
|
243
246
|
functionAddress: FunctionAddress,
|
|
244
247
|
kind: FunctionKind,
|
|
245
248
|
resolved: ResolvedFunction,
|
|
249
|
+
identity?: SessionState,
|
|
246
250
|
) => Effect.Effect<RuntimeInvocationResult>;
|
|
247
251
|
}
|
|
248
252
|
>()("ignotum/dev-runtime/functions/FunctionExecutor") {
|
|
@@ -282,96 +286,113 @@ export class FunctionExecutor extends Context.Service<
|
|
|
282
286
|
);
|
|
283
287
|
|
|
284
288
|
return FunctionExecutor.of({
|
|
285
|
-
execute: Effect.fn("FunctionExecutor.execute")(
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
).pipe(
|
|
293
|
-
Effect.matchEffect({
|
|
294
|
-
onFailure: (error) => encodeFailure(resolved.definition, error),
|
|
295
|
-
onSuccess: (value) => encodeSuccess(resolved.definition, value),
|
|
289
|
+
execute: Effect.fn("FunctionExecutor.execute")(
|
|
290
|
+
function* (functionAddress, kind, resolved, identity) {
|
|
291
|
+
let readId = false;
|
|
292
|
+
const id = makeIdContext(
|
|
293
|
+
Effect.sync(() => {
|
|
294
|
+
readId = true;
|
|
295
|
+
return identity?.user ?? null;
|
|
296
296
|
}),
|
|
297
297
|
);
|
|
298
|
+
const invoke = (context: LocalMutationContext | LocalQueryContext) =>
|
|
299
|
+
Effect.gen(() =>
|
|
300
|
+
resolved.definition.handler(
|
|
301
|
+
Object.freeze({ ...context, id, env: resolved.environment ?? Object.freeze({}) }),
|
|
302
|
+
resolved.args,
|
|
303
|
+
),
|
|
304
|
+
).pipe(
|
|
305
|
+
Effect.matchEffect({
|
|
306
|
+
onFailure: (error) => encodeFailure(resolved.definition, error),
|
|
307
|
+
onSuccess: (value) => encodeSuccess(resolved.definition, value),
|
|
308
|
+
}),
|
|
309
|
+
);
|
|
298
310
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
result: value,
|
|
311
|
-
dependencies,
|
|
312
|
-
observedRevision,
|
|
313
|
-
} as const;
|
|
314
|
-
return files.length === 0 ? result : { ...result, files };
|
|
315
|
-
},
|
|
316
|
-
),
|
|
317
|
-
)
|
|
318
|
-
: mutationSemaphore.withPermits(1)(
|
|
319
|
-
database
|
|
320
|
-
.trackedMutationTransaction(resolved.schema, (context) =>
|
|
321
|
-
invoke(context).pipe(
|
|
322
|
-
Effect.flatMap((result) =>
|
|
323
|
-
result.type === "Failure"
|
|
324
|
-
? Effect.fail(MutationApplicationFailure.make({ error: result.error }))
|
|
325
|
-
: Effect.succeed(result),
|
|
326
|
-
),
|
|
327
|
-
),
|
|
328
|
-
)
|
|
329
|
-
.pipe(
|
|
330
|
-
Effect.map(
|
|
331
|
-
({ committedRevision, invalidations, value }): RuntimeInvocationResult => ({
|
|
332
|
-
type: "Mutation",
|
|
311
|
+
const execution =
|
|
312
|
+
kind === "Query"
|
|
313
|
+
? database.trackedQueryTransaction(resolved.schema, invoke).pipe(
|
|
314
|
+
Effect.map(
|
|
315
|
+
({ dependencies, observedRevision, value }): RuntimeInvocationResult => {
|
|
316
|
+
const files =
|
|
317
|
+
value.type === "Success" && value.value !== undefined
|
|
318
|
+
? encodedFileOccurrencesOf(value.value)
|
|
319
|
+
: [];
|
|
320
|
+
const result = {
|
|
321
|
+
type: "Query",
|
|
333
322
|
result: value,
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
323
|
+
dependencies: readId
|
|
324
|
+
? [...dependencies, { type: "Id" as const }]
|
|
325
|
+
: dependencies,
|
|
326
|
+
observedRevision,
|
|
327
|
+
} as const;
|
|
328
|
+
return files.length === 0 ? result : { ...result, files };
|
|
329
|
+
},
|
|
330
|
+
),
|
|
331
|
+
)
|
|
332
|
+
: mutationSemaphore.withPermits(1)(
|
|
333
|
+
database
|
|
334
|
+
.trackedMutationTransaction(resolved.schema, (context) =>
|
|
335
|
+
invoke(context).pipe(
|
|
336
|
+
Effect.flatMap((result) =>
|
|
337
|
+
result.type === "Failure"
|
|
338
|
+
? Effect.fail(
|
|
339
|
+
MutationApplicationFailure.make({ error: result.error }),
|
|
340
|
+
)
|
|
341
|
+
: Effect.succeed(result),
|
|
342
|
+
),
|
|
343
|
+
),
|
|
344
|
+
)
|
|
345
|
+
.pipe(
|
|
346
|
+
Effect.map(
|
|
347
|
+
({
|
|
348
|
+
committedRevision,
|
|
349
|
+
invalidations,
|
|
350
|
+
value,
|
|
351
|
+
}): RuntimeInvocationResult => ({
|
|
341
352
|
type: "Mutation",
|
|
342
|
-
result:
|
|
353
|
+
result: value,
|
|
354
|
+
invalidations,
|
|
355
|
+
committedRevision,
|
|
343
356
|
}),
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
Effect.gen(function* () {
|
|
354
|
-
const requestId = yield* ids.generate(RequestId);
|
|
355
|
-
yield* Effect.logError(`${functionAddress} failed during execution.`).pipe(
|
|
356
|
-
Effect.annotateLogs({ cause, function: functionAddress, requestId }),
|
|
357
|
+
),
|
|
358
|
+
Effect.catchTags({
|
|
359
|
+
MutationApplicationFailure: ({ error }) =>
|
|
360
|
+
Effect.succeed<RuntimeInvocationResult>({
|
|
361
|
+
type: "Mutation",
|
|
362
|
+
result: { type: "Failure", error },
|
|
363
|
+
}),
|
|
364
|
+
}),
|
|
365
|
+
),
|
|
357
366
|
);
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
367
|
+
|
|
368
|
+
return yield* execution.pipe(
|
|
369
|
+
Effect.orDie,
|
|
370
|
+
Effect.catchCauseIf(
|
|
371
|
+
(cause) => !Cause.hasInterruptsOnly(cause),
|
|
372
|
+
(cause) =>
|
|
373
|
+
Effect.gen(function* () {
|
|
374
|
+
const requestId = yield* ids.generate(RequestId);
|
|
375
|
+
yield* Effect.logError(`${functionAddress} failed during execution.`).pipe(
|
|
376
|
+
Effect.annotateLogs({ cause, function: functionAddress, requestId }),
|
|
377
|
+
);
|
|
378
|
+
const result = {
|
|
379
|
+
type: "Failure" as const,
|
|
380
|
+
error: { _tag: "InternalServerError", requestId },
|
|
381
|
+
};
|
|
382
|
+
if (kind === "Mutation") {
|
|
383
|
+
return { type: "Mutation" as const, result };
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
type: "Query" as const,
|
|
387
|
+
result,
|
|
388
|
+
dependencies: readId ? [{ type: "Id" as const }] : [],
|
|
389
|
+
observedRevision: yield* database.revision,
|
|
390
|
+
};
|
|
391
|
+
}),
|
|
392
|
+
),
|
|
393
|
+
);
|
|
394
|
+
},
|
|
395
|
+
),
|
|
375
396
|
});
|
|
376
397
|
}),
|
|
377
398
|
);
|
|
@@ -384,10 +405,12 @@ export const functionRuntimeLayer = Layer.effect(
|
|
|
384
405
|
const registry = yield* FunctionRegistry;
|
|
385
406
|
const executor = yield* FunctionExecutor;
|
|
386
407
|
return FunctionRuntime.of({
|
|
387
|
-
prepare: Effect.fn("DevFunctionRuntime.prepare")(
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
408
|
+
prepare: Effect.fn("DevFunctionRuntime.prepare")(
|
|
409
|
+
function* (functionAddress, kind, args, identity) {
|
|
410
|
+
const resolved = yield* registry.resolve(functionAddress, kind, args);
|
|
411
|
+
return { execute: executor.execute(functionAddress, kind, resolved, identity) };
|
|
412
|
+
},
|
|
413
|
+
),
|
|
391
414
|
});
|
|
392
415
|
}),
|
|
393
416
|
);
|