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/dist/runtime/client.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { Array as Array$1, Cause, Context, Deferred, Duration, Effect, ErrorReporter, Fiber, HashMap, Layer, ManagedRuntime, Predicate, Schedule, Schema, String } from "effect";
|
|
1
|
+
import { A as RuntimeFileMetadata, B as makeFileValue, D as encodeTransportObject, E as decodeTransportValue, F as fileIdOf, G as IdAlphabet, H as AppId, I as fileLimits, J as SessionEpoch, K as InvocationId, L as fileMimeTypes, N as fileFormats, P as fileGrantUrlOf, R as grantFileValue, U as DeploymentId, W as GeneratedId, X as TableId, Y as SubscriptionId, _ as Result$2, a as SessionResponse, b as inspectResult, d as sessionPath, f as sessionRequestsPath, i as SessionRequest, j as RuntimeFileOccurrence, k as FileId, l as developmentSelectionKey, m as upgrade, n as SessionOutcome, o as SessionState, p as initial, q as RuntimeRequestNonce, r as SessionRedirect, t as DevelopmentSelection, x as pending, y as failureFromWire, z as isFileValue } from "./id-Bt9XWRGL.js";
|
|
2
|
+
import { a as ErrorCode, c as ServerMessageJson, d as WireSuccess, f as AppStateRevision, i as DeploymentCloseCode, l as WireFailure, m as InvocationKey, n as functionPathOf, o as FunctionAddress, p as DeploymentGeneration, r as ClientMessageJson, s as Operation, u as WireResult } from "./api-CAgKDij7.js";
|
|
3
|
+
import { s as PaginationPageSize, t as ValueDescriptor } from "./descriptor-Cyo9FP9n-C0SRVXNW.js";
|
|
4
|
+
import { Array as Array$1, Cause, Context, Deferred, Duration, Effect, ErrorReporter, Fiber, HashMap, Layer, ManagedRuntime, Option, Predicate, Schedule, Schema, Semaphore, String } from "effect";
|
|
5
5
|
import { customAlphabet } from "nanoid";
|
|
6
|
-
import {
|
|
6
|
+
import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http";
|
|
7
|
+
import { useCallback, useCallback as useCallback$1, useContext, useDebugValue, useDebugValue as useDebugValue$1, useEffect, useEffect as useEffect$1, useErrorBoundary, useId as useElementId, useImperativeHandle, useLayoutEffect, useMemo, useMemo as useMemo$1, useReducer, useRef, useState, useState as useState$1 } from "preact/hooks";
|
|
7
8
|
import * as Socket from "effect/unstable/socket/Socket";
|
|
8
9
|
import * as BrowserCrypto from "@effect/platform-browser/BrowserCrypto";
|
|
9
10
|
import { Component, Fragment, cloneElement, createContext, createElement, createRef, h, isValidElement, toChildArray } from "preact";
|
|
@@ -119,6 +120,177 @@ const encodeJson = (value) => {
|
|
|
119
120
|
};
|
|
120
121
|
const encodeCanonicalJson = (value) => encodeJson(Schema.decodeSync(Schema.Json)(value));
|
|
121
122
|
//#endregion
|
|
123
|
+
//#region src/client/id.ts
|
|
124
|
+
var IdRequestError = class extends Schema.TaggedError()("IdRequestError", { message: Schema.String }) {};
|
|
125
|
+
const outcomeKey = "ignotum.id.outcome";
|
|
126
|
+
const selectionJson = Schema.fromJsonString(DevelopmentSelection);
|
|
127
|
+
var BrowserSession = class BrowserSession extends Context.Service()("ignotum/client/id/BrowserSession") {
|
|
128
|
+
static layer = Layer.effect(BrowserSession, Effect.gen(function* () {
|
|
129
|
+
const client = (yield* HttpClient.HttpClient).pipe(HttpClient.filterStatusOk);
|
|
130
|
+
const ids = yield* IdGenerator;
|
|
131
|
+
const bootstrapLock = yield* Semaphore.make(1);
|
|
132
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
133
|
+
let state = { status: "pending" };
|
|
134
|
+
let current;
|
|
135
|
+
let generation = 0;
|
|
136
|
+
let deadline = Infinity;
|
|
137
|
+
let development = false;
|
|
138
|
+
let selection;
|
|
139
|
+
let expiry;
|
|
140
|
+
const notify = () => {
|
|
141
|
+
for (const listener of listeners) listener();
|
|
142
|
+
};
|
|
143
|
+
const suspend = (error) => {
|
|
144
|
+
generation += 1;
|
|
145
|
+
deadline = Infinity;
|
|
146
|
+
if (expiry !== void 0) clearTimeout(expiry);
|
|
147
|
+
current = void 0;
|
|
148
|
+
state = error === void 0 ? {
|
|
149
|
+
status: "pending",
|
|
150
|
+
outcome: state.outcome
|
|
151
|
+
} : {
|
|
152
|
+
status: "error",
|
|
153
|
+
error,
|
|
154
|
+
outcome: state.outcome
|
|
155
|
+
};
|
|
156
|
+
notify();
|
|
157
|
+
};
|
|
158
|
+
const accept = (session, serverTime) => {
|
|
159
|
+
generation += 1;
|
|
160
|
+
if (expiry !== void 0) clearTimeout(expiry);
|
|
161
|
+
current = session;
|
|
162
|
+
state = session.user === null ? {
|
|
163
|
+
status: "signedOut",
|
|
164
|
+
outcome: state.outcome
|
|
165
|
+
} : {
|
|
166
|
+
status: "signedIn",
|
|
167
|
+
user: session.user,
|
|
168
|
+
outcome: state.outcome
|
|
169
|
+
};
|
|
170
|
+
const delay = Math.max(0, session.validUntil - serverTime);
|
|
171
|
+
deadline = performance.now() + delay;
|
|
172
|
+
if (delay < 2147483647) expiry = setTimeout(() => suspend(), delay);
|
|
173
|
+
notify();
|
|
174
|
+
};
|
|
175
|
+
const checkExpiry = () => {
|
|
176
|
+
if (current !== void 0 && performance.now() >= deadline) suspend();
|
|
177
|
+
};
|
|
178
|
+
globalThis.addEventListener?.("focus", checkExpiry);
|
|
179
|
+
globalThis.document?.addEventListener("visibilitychange", checkExpiry);
|
|
180
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => {
|
|
181
|
+
if (expiry !== void 0) clearTimeout(expiry);
|
|
182
|
+
listeners.clear();
|
|
183
|
+
globalThis.removeEventListener?.("focus", checkExpiry);
|
|
184
|
+
globalThis.document?.removeEventListener("visibilitychange", checkExpiry);
|
|
185
|
+
}));
|
|
186
|
+
const readOutcome = () => {
|
|
187
|
+
const raw = globalThis.sessionStorage.getItem(outcomeKey);
|
|
188
|
+
if (raw !== null) {
|
|
189
|
+
const decoded = Schema.decodeUnknownOption(SessionOutcome)(raw);
|
|
190
|
+
if (Option.isSome(decoded)) state = {
|
|
191
|
+
...state,
|
|
192
|
+
outcome: decoded.value
|
|
193
|
+
};
|
|
194
|
+
globalThis.sessionStorage.removeItem(outcomeKey);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
const parameters = () => {
|
|
198
|
+
if (!development || selection === void 0) return "";
|
|
199
|
+
const params = new URLSearchParams({ epoch: selection.epoch });
|
|
200
|
+
if (selection.username !== null) params.set("username", selection.username);
|
|
201
|
+
return `?${params}`;
|
|
202
|
+
};
|
|
203
|
+
const readSession = Effect.fn("BrowserSession.read")(function* () {
|
|
204
|
+
return yield* client.get(`${sessionPath}${parameters()}`, { headers: { "x-ignotum-request": "1" } }).pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(SessionResponse)));
|
|
205
|
+
});
|
|
206
|
+
let bootstrapGeneration = 0;
|
|
207
|
+
const bootstrap = bootstrapLock.withPermits(1)(Effect.gen(function* () {
|
|
208
|
+
const admittedGeneration = generation;
|
|
209
|
+
bootstrapGeneration = admittedGeneration;
|
|
210
|
+
const startedAt = performance.now();
|
|
211
|
+
let session = yield* readSession();
|
|
212
|
+
development = session.development === true;
|
|
213
|
+
if (development && selection === void 0) {
|
|
214
|
+
const saved = globalThis.sessionStorage.getItem(developmentSelectionKey);
|
|
215
|
+
const decoded = saved === null ? Option.none() : Schema.decodeOption(selectionJson)(saved);
|
|
216
|
+
selection = Option.getOrElse(decoded, () => ({
|
|
217
|
+
formatVersion: 1,
|
|
218
|
+
username: null,
|
|
219
|
+
epoch: ""
|
|
220
|
+
}));
|
|
221
|
+
if (selection.epoch === "") selection = {
|
|
222
|
+
...selection,
|
|
223
|
+
epoch: yield* ids.generate(SessionEpoch)
|
|
224
|
+
};
|
|
225
|
+
globalThis.sessionStorage.setItem(developmentSelectionKey, Schema.encodeSync(selectionJson)(selection));
|
|
226
|
+
session = yield* readSession();
|
|
227
|
+
}
|
|
228
|
+
if (generation !== admittedGeneration) {
|
|
229
|
+
if (current !== void 0) return current;
|
|
230
|
+
return yield* IdRequestError.make({ message: "Your ID session changed while loading." });
|
|
231
|
+
}
|
|
232
|
+
readOutcome();
|
|
233
|
+
if (session.outcome !== void 0) state = {
|
|
234
|
+
...state,
|
|
235
|
+
outcome: session.outcome
|
|
236
|
+
};
|
|
237
|
+
accept(session, session.serverTime + performance.now() - startedAt);
|
|
238
|
+
return session;
|
|
239
|
+
})).pipe(Effect.mapError(() => IdRequestError.make({ message: "Could not load your ID session." })), Effect.tapError((error) => Effect.sync(() => {
|
|
240
|
+
if (generation === bootstrapGeneration) suspend(error.message);
|
|
241
|
+
})));
|
|
242
|
+
const start = Effect.fn("BrowserSession.start")(function* (intent, profile, returnTo) {
|
|
243
|
+
const session = current ?? (yield* bootstrap);
|
|
244
|
+
if (intent === "requestProfile" && session.user === null) return yield* IdRequestError.make({ message: "Sign in before requesting profile information." });
|
|
245
|
+
const request = yield* Schema.decodeUnknownEffect(SessionRequest)({
|
|
246
|
+
intent,
|
|
247
|
+
profile,
|
|
248
|
+
sessionEpoch: session.sessionEpoch,
|
|
249
|
+
returnTo: returnTo ?? `${globalThis.location.pathname}${globalThis.location.search}${globalThis.location.hash}`
|
|
250
|
+
}).pipe(Effect.mapError(() => IdRequestError.make({ message: "The ID request is invalid." })));
|
|
251
|
+
const redirect = yield* HttpClientRequest.post(sessionRequestsPath).pipe(HttpClientRequest.setHeader("x-ignotum-request", "1"), HttpClientRequest.bodyJsonUnsafe(request), client.execute, Effect.flatMap(HttpClientResponse.schemaBodyJson(SessionRedirect)), Effect.mapError(() => IdRequestError.make({ message: "Could not start the ID request." })));
|
|
252
|
+
if (current?.sessionEpoch !== session.sessionEpoch) return yield* IdRequestError.make({ message: "Your ID session changed. Try again." });
|
|
253
|
+
globalThis.location.assign(redirect.redirectUrl);
|
|
254
|
+
});
|
|
255
|
+
const signOut = Effect.gen(function* () {
|
|
256
|
+
const session = current ?? (yield* bootstrap);
|
|
257
|
+
yield* HttpClientRequest.delete(sessionPath).pipe(HttpClientRequest.setHeader("x-ignotum-request", "1"), HttpClientRequest.bodyJsonUnsafe({ sessionEpoch: session.sessionEpoch }), client.execute, Effect.mapError(() => IdRequestError.make({ message: "Could not sign out." })));
|
|
258
|
+
suspend();
|
|
259
|
+
if (development) {
|
|
260
|
+
selection = {
|
|
261
|
+
formatVersion: 1,
|
|
262
|
+
username: null,
|
|
263
|
+
epoch: yield* ids.generate(SessionEpoch)
|
|
264
|
+
};
|
|
265
|
+
globalThis.sessionStorage.setItem(developmentSelectionKey, Schema.encodeSync(selectionJson)(selection));
|
|
266
|
+
}
|
|
267
|
+
globalThis.location.reload();
|
|
268
|
+
});
|
|
269
|
+
return BrowserSession.of({
|
|
270
|
+
snapshot: () => {
|
|
271
|
+
checkExpiry();
|
|
272
|
+
return state;
|
|
273
|
+
},
|
|
274
|
+
current: () => {
|
|
275
|
+
checkExpiry();
|
|
276
|
+
return current;
|
|
277
|
+
},
|
|
278
|
+
subscribe: (listener) => {
|
|
279
|
+
listeners.add(listener);
|
|
280
|
+
return () => {
|
|
281
|
+
listeners.delete(listener);
|
|
282
|
+
};
|
|
283
|
+
},
|
|
284
|
+
accept,
|
|
285
|
+
suspend,
|
|
286
|
+
bootstrap,
|
|
287
|
+
start,
|
|
288
|
+
signOut,
|
|
289
|
+
socketParameters: parameters
|
|
290
|
+
});
|
|
291
|
+
})).pipe(Layer.provide(FetchHttpClient.layer));
|
|
292
|
+
};
|
|
293
|
+
//#endregion
|
|
122
294
|
//#region src/client/errors.ts
|
|
123
295
|
/** @effect-diagnostics globalConsole:skip-file */
|
|
124
296
|
var ConnectionUnavailable = class extends Schema.TaggedError()("ConnectionUnavailable", {
|
|
@@ -198,7 +370,9 @@ const IndexPointInvalidation = Schema.Struct({
|
|
|
198
370
|
index: IndexIdentity,
|
|
199
371
|
key: EncodedIndexKey
|
|
200
372
|
});
|
|
373
|
+
const IdDependency = Schema.Struct({ type: Schema.Literal("Id") });
|
|
201
374
|
const ReadDependency = Schema.Union([
|
|
375
|
+
IdDependency,
|
|
202
376
|
TableDependency,
|
|
203
377
|
DocumentDependency,
|
|
204
378
|
IndexRangeDependency
|
|
@@ -263,7 +437,8 @@ const InvocationBase = {
|
|
|
263
437
|
generation: DeploymentGeneration,
|
|
264
438
|
invocationKey: InvocationKey,
|
|
265
439
|
function: FunctionAddress,
|
|
266
|
-
args: Schema.Json
|
|
440
|
+
args: Schema.Json,
|
|
441
|
+
identity: Schema.optional(SessionState)
|
|
267
442
|
};
|
|
268
443
|
const QueryInvocation = Schema.Struct({
|
|
269
444
|
...InvocationBase,
|
|
@@ -286,6 +461,7 @@ var RuntimeDeploymentUnavailable = class extends Schema.TaggedError()("RuntimeDe
|
|
|
286
461
|
message: Schema.String
|
|
287
462
|
}) {};
|
|
288
463
|
const RuntimeInvocationRejectionCode = Schema.Literals([
|
|
464
|
+
"SessionExpired",
|
|
289
465
|
"FunctionUnavailable",
|
|
290
466
|
"InvalidArguments",
|
|
291
467
|
"InvocationIdConflict",
|
|
@@ -554,55 +730,67 @@ const subscribeMessage = (entry) => ({
|
|
|
554
730
|
const makeQueryCache = (allocateId, sendLifecycleMessage) => {
|
|
555
731
|
const queries = /* @__PURE__ */ new Map();
|
|
556
732
|
const queriesById = /* @__PURE__ */ new Map();
|
|
733
|
+
const observe = Effect.fn("SyncClient.QueryCache.observe")(function* (functionAddress, args) {
|
|
734
|
+
const key = queryKey(functionAddress, args);
|
|
735
|
+
let entry = queries.get(key);
|
|
736
|
+
if (entry === void 0) {
|
|
737
|
+
const created = {
|
|
738
|
+
args,
|
|
739
|
+
function: functionAddress,
|
|
740
|
+
generation: 0,
|
|
741
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
742
|
+
revision: void 0,
|
|
743
|
+
references: 0,
|
|
744
|
+
result: pending(),
|
|
745
|
+
subscriptionId: yield* allocateId
|
|
746
|
+
};
|
|
747
|
+
queries.set(key, created);
|
|
748
|
+
queriesById.set(created.subscriptionId, created);
|
|
749
|
+
yield* sendLifecycleMessage(subscribeMessage(created));
|
|
750
|
+
entry = created;
|
|
751
|
+
}
|
|
752
|
+
const observed = entry;
|
|
753
|
+
observed.references += 1;
|
|
754
|
+
observed.generation += 1;
|
|
755
|
+
let released = false;
|
|
756
|
+
return {
|
|
757
|
+
getSnapshot: () => observed.result,
|
|
758
|
+
subscribe: (listener) => {
|
|
759
|
+
observed.listeners.add(listener);
|
|
760
|
+
return () => observed.listeners.delete(listener);
|
|
761
|
+
},
|
|
762
|
+
release: Effect.gen(function* () {
|
|
763
|
+
if (released) return;
|
|
764
|
+
released = true;
|
|
765
|
+
observed.references -= 1;
|
|
766
|
+
if (observed.references !== 0) return;
|
|
767
|
+
const generation = ++observed.generation;
|
|
768
|
+
yield* Effect.sleep("25 millis");
|
|
769
|
+
if (observed.references !== 0 || observed.generation !== generation || queries.get(key) !== observed) return;
|
|
770
|
+
queries.delete(key);
|
|
771
|
+
queriesById.delete(observed.subscriptionId);
|
|
772
|
+
yield* sendLifecycleMessage({
|
|
773
|
+
type: "Unsubscribe",
|
|
774
|
+
id: observed.subscriptionId
|
|
775
|
+
});
|
|
776
|
+
})
|
|
777
|
+
};
|
|
778
|
+
});
|
|
779
|
+
const clear = (entry) => {
|
|
780
|
+
entry.result = pending();
|
|
781
|
+
entry.revision = void 0;
|
|
782
|
+
for (const listener of entry.listeners) listener();
|
|
783
|
+
};
|
|
557
784
|
return {
|
|
785
|
+
invalidate: (ids) => {
|
|
786
|
+
for (const entry of queries.values()) if (ids === void 0 || ids.includes(entry.subscriptionId)) clear(entry);
|
|
787
|
+
},
|
|
788
|
+
invalidatePrivate: () => {
|
|
789
|
+
for (const entry of queries.values()) if (entry.dependsOnId !== false) clear(entry);
|
|
790
|
+
},
|
|
558
791
|
entries: () => queries.values(),
|
|
559
792
|
getById: (id) => queriesById.get(id),
|
|
560
|
-
observe
|
|
561
|
-
const key = queryKey(functionAddress, args);
|
|
562
|
-
let entry = queries.get(key);
|
|
563
|
-
if (entry === void 0) {
|
|
564
|
-
const created = {
|
|
565
|
-
args,
|
|
566
|
-
function: functionAddress,
|
|
567
|
-
generation: 0,
|
|
568
|
-
listeners: /* @__PURE__ */ new Set(),
|
|
569
|
-
revision: void 0,
|
|
570
|
-
references: 0,
|
|
571
|
-
result: pending(),
|
|
572
|
-
subscriptionId: yield* allocateId
|
|
573
|
-
};
|
|
574
|
-
queries.set(key, created);
|
|
575
|
-
queriesById.set(created.subscriptionId, created);
|
|
576
|
-
yield* sendLifecycleMessage(subscribeMessage(created));
|
|
577
|
-
entry = created;
|
|
578
|
-
}
|
|
579
|
-
const observed = entry;
|
|
580
|
-
observed.references += 1;
|
|
581
|
-
observed.generation += 1;
|
|
582
|
-
let released = false;
|
|
583
|
-
return {
|
|
584
|
-
getSnapshot: () => observed.result,
|
|
585
|
-
subscribe: (listener) => {
|
|
586
|
-
observed.listeners.add(listener);
|
|
587
|
-
return () => observed.listeners.delete(listener);
|
|
588
|
-
},
|
|
589
|
-
release: Effect.gen(function* () {
|
|
590
|
-
if (released) return;
|
|
591
|
-
released = true;
|
|
592
|
-
observed.references -= 1;
|
|
593
|
-
if (observed.references !== 0) return;
|
|
594
|
-
const generation = ++observed.generation;
|
|
595
|
-
yield* Effect.sleep("25 millis");
|
|
596
|
-
if (observed.references !== 0 || observed.generation !== generation || queries.get(key) !== observed) return;
|
|
597
|
-
queries.delete(key);
|
|
598
|
-
queriesById.delete(observed.subscriptionId);
|
|
599
|
-
yield* sendLifecycleMessage({
|
|
600
|
-
type: "Unsubscribe",
|
|
601
|
-
id: observed.subscriptionId
|
|
602
|
-
});
|
|
603
|
-
})
|
|
604
|
-
};
|
|
605
|
-
}),
|
|
793
|
+
observe,
|
|
606
794
|
setResult: (id, result, revision) => {
|
|
607
795
|
const entry = queriesById.get(id);
|
|
608
796
|
if (entry === void 0) return;
|
|
@@ -644,10 +832,12 @@ const socketUrl = () => {
|
|
|
644
832
|
var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync/SyncClient") {
|
|
645
833
|
static layer = Layer.effect(SyncClient, Effect.gen(function* () {
|
|
646
834
|
const ids = yield* IdGenerator;
|
|
835
|
+
const session = yield* BrowserSession;
|
|
647
836
|
let invocations = HashMap.empty();
|
|
648
837
|
let rememberedHandshake;
|
|
649
838
|
let reloadRequested = false;
|
|
650
839
|
let writer;
|
|
840
|
+
let sessionSuspended;
|
|
651
841
|
const encode = (message) => Schema.encodeEffect(ClientMessageJson)(message).pipe(Effect.mapError((cause) => InvalidClientMessage.make({
|
|
652
842
|
cause,
|
|
653
843
|
message: "Could not encode an Ignotum sync message."
|
|
@@ -661,17 +851,47 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
|
|
|
661
851
|
ConnectionUnavailable: reportClientError,
|
|
662
852
|
InvalidClientMessage: reportClientError
|
|
663
853
|
})));
|
|
854
|
+
const unsubscribeSession = session.subscribe(() => {
|
|
855
|
+
if (session.current() === void 0) {
|
|
856
|
+
queryCache.invalidatePrivate();
|
|
857
|
+
writer = void 0;
|
|
858
|
+
if (sessionSuspended !== void 0) Deferred.doneUnsafe(sessionSuspended, Effect.void);
|
|
859
|
+
}
|
|
860
|
+
});
|
|
861
|
+
yield* Effect.addFinalizer(() => Effect.sync(unsubscribeSession));
|
|
664
862
|
const rejectInvocation = (id, error) => {
|
|
665
|
-
const pendingInvocation = HashMap.
|
|
863
|
+
const pendingInvocation = Option.getOrUndefined(HashMap.get(invocations, id));
|
|
666
864
|
if (pendingInvocation === void 0) return Effect.void;
|
|
667
865
|
invocations = HashMap.remove(invocations, id);
|
|
668
866
|
return reportClientError(error).pipe(Effect.andThen(Deferred.fail(pendingInvocation.prepared, error)), Effect.andThen(Deferred.fail(pendingInvocation.deferred, error)), Effect.asVoid);
|
|
669
867
|
};
|
|
670
868
|
const handleMessage = Effect.fn("SyncClient.handleMessage")(function* (message) {
|
|
671
869
|
switch (message.type) {
|
|
870
|
+
case "Session":
|
|
871
|
+
if (message.event === "Updated") {
|
|
872
|
+
const previous = session.current();
|
|
873
|
+
if (previous !== void 0 && previous.sessionEpoch !== message.session.sessionEpoch) return;
|
|
874
|
+
if (previous?.viewRevision !== message.session.viewRevision) queryCache.invalidatePrivate();
|
|
875
|
+
session.accept(message.session, message.serverTime);
|
|
876
|
+
} else {
|
|
877
|
+
if (rememberedHandshake?.session?.sessionEpoch !== message.sessionEpoch) return;
|
|
878
|
+
session.suspend(message.event === "Unavailable" ? "Your ID session is temporarily unavailable." : void 0);
|
|
879
|
+
if (message.event !== "Unavailable") yield* Effect.forEach(HashMap.keys(invocations), (id) => rejectInvocation(id, ServerError.make({
|
|
880
|
+
code: "SessionChanged",
|
|
881
|
+
message: "The session changed. This operation may already have completed."
|
|
882
|
+
})));
|
|
883
|
+
}
|
|
884
|
+
return;
|
|
885
|
+
case "Invalidate":
|
|
886
|
+
if (message.sessionEpoch === session.current()?.sessionEpoch) queryCache.invalidate(message.ids);
|
|
887
|
+
return;
|
|
672
888
|
case "Snapshot": {
|
|
889
|
+
const current = session.current();
|
|
890
|
+
if (current === void 0 || message.identity?.sessionEpoch !== current.sessionEpoch) return;
|
|
891
|
+
if (message.identity.dependsOnId && message.identity.viewRevision !== current.viewRevision) return;
|
|
673
892
|
const entry = queryCache.getById(message.id);
|
|
674
893
|
if (entry === void 0) return;
|
|
894
|
+
entry.dependsOnId = message.identity.dependsOnId;
|
|
675
895
|
if (entry.revision !== void 0 && message.revision < entry.revision) return;
|
|
676
896
|
yield* resultFromWire(message.result, message.files ?? []).pipe(Effect.tap((result) => Effect.sync(() => queryCache.setResult(message.id, result, message.revision))), Effect.catchTags({ InvalidServerMessage: (error) => {
|
|
677
897
|
queryCache.setResult(message.id, error, message.revision);
|
|
@@ -680,12 +900,14 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
|
|
|
680
900
|
return;
|
|
681
901
|
}
|
|
682
902
|
case "Preparation": {
|
|
683
|
-
|
|
903
|
+
if (message.sessionEpoch !== session.current()?.sessionEpoch) return;
|
|
904
|
+
const pendingInvocation = Option.getOrUndefined(HashMap.get(invocations, message.id));
|
|
684
905
|
if (pendingInvocation !== void 0) yield* Deferred.succeed(pendingInvocation.prepared, message.uploads);
|
|
685
906
|
return;
|
|
686
907
|
}
|
|
687
908
|
case "Result": {
|
|
688
|
-
|
|
909
|
+
if (message.sessionEpoch !== session.current()?.sessionEpoch) return;
|
|
910
|
+
const pendingInvocation = Option.getOrUndefined(HashMap.get(invocations, message.id));
|
|
689
911
|
if (pendingInvocation === void 0) return;
|
|
690
912
|
yield* resultFromWire(message.result).pipe(Effect.flatMap((result) => {
|
|
691
913
|
invocations = HashMap.remove(invocations, message.id);
|
|
@@ -694,6 +916,7 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
|
|
|
694
916
|
return;
|
|
695
917
|
}
|
|
696
918
|
case "Error": {
|
|
919
|
+
if (message.sessionEpoch !== session.current()?.sessionEpoch) return;
|
|
697
920
|
const error = message.operation === void 0 ? ServerError.make({
|
|
698
921
|
code: message.code,
|
|
699
922
|
message: message.message
|
|
@@ -722,7 +945,8 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
|
|
|
722
945
|
});
|
|
723
946
|
const connect = Effect.scoped(Effect.gen(function* () {
|
|
724
947
|
if (reloadRequested) return yield* Effect.never;
|
|
725
|
-
|
|
948
|
+
yield* session.bootstrap.pipe(Effect.mapError((error) => ConnectionUnavailable.make({ message: error.message })));
|
|
949
|
+
const socket = yield* Socket.makeWebSocket(`${socketUrl()}${session.socketParameters()}`, { closeCodeIsError: (code) => {
|
|
726
950
|
if (shouldReloadForCloseCode(code)) requestReload();
|
|
727
951
|
return false;
|
|
728
952
|
} });
|
|
@@ -736,6 +960,22 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
|
|
|
736
960
|
return;
|
|
737
961
|
}
|
|
738
962
|
if (decision === "Ignore") return;
|
|
963
|
+
if (handshake.session === void 0 || handshake.serverTime === void 0) {
|
|
964
|
+
requestReload();
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
if (rememberedHandshake?.session !== void 0 && rememberedHandshake.session.sessionEpoch !== handshake.session.sessionEpoch) {
|
|
968
|
+
queryCache.invalidatePrivate();
|
|
969
|
+
yield* Effect.forEach(HashMap.keys(invocations), (id) => rejectInvocation(id, ServerError.make({
|
|
970
|
+
code: "SessionChanged",
|
|
971
|
+
message: "The session changed. This operation may already have completed."
|
|
972
|
+
})));
|
|
973
|
+
} else if (rememberedHandshake?.session?.viewRevision !== handshake.session.viewRevision) queryCache.invalidatePrivate();
|
|
974
|
+
yield* Effect.forEach(HashMap.entries(invocations), ([id, pending]) => pending.sessionEpoch === handshake.session.sessionEpoch ? Effect.void : rejectInvocation(id, ServerError.make({
|
|
975
|
+
code: "SessionChanged",
|
|
976
|
+
message: "The session changed. This operation may already have completed."
|
|
977
|
+
})));
|
|
978
|
+
session.accept(handshake.session, handshake.serverTime);
|
|
739
979
|
rememberedHandshake = handshake;
|
|
740
980
|
handshakeAccepted = true;
|
|
741
981
|
writer = write;
|
|
@@ -761,9 +1001,14 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
|
|
|
761
1001
|
});
|
|
762
1002
|
return yield* handleMessage(message);
|
|
763
1003
|
});
|
|
1004
|
+
const suspended = yield* Deferred.make();
|
|
1005
|
+
sessionSuspended = suspended;
|
|
1006
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => {
|
|
1007
|
+
if (sessionSuspended === suspended) sessionSuspended = void 0;
|
|
1008
|
+
}));
|
|
764
1009
|
const run = yield* socket.runString(handleText).pipe(Effect.forkChild);
|
|
765
1010
|
yield* Effect.raceFirst(Deferred.await(handshakeReceived).pipe(Effect.timeout("10 seconds")), Fiber.join(run));
|
|
766
|
-
yield* Fiber.join(run);
|
|
1011
|
+
yield* Effect.raceFirst(Fiber.join(run), Deferred.await(suspended));
|
|
767
1012
|
})).pipe(Effect.mapError((cause) => Schema.is(ClientInfrastructureError)(cause) ? cause : ConnectionUnavailable.make({
|
|
768
1013
|
cause,
|
|
769
1014
|
message: "The sync connection was lost."
|
|
@@ -774,6 +1019,7 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
|
|
|
774
1019
|
return yield* queryCache.observe(functionAddress, args);
|
|
775
1020
|
});
|
|
776
1021
|
const mutate = Effect.fn("SyncClient.mutate")(function* (functionAddress, args) {
|
|
1022
|
+
const admittedSession = session.current() ?? rememberedHandshake?.session ?? (yield* session.bootstrap.pipe(Effect.mapError((error) => ConnectionUnavailable.make({ message: error.message }))));
|
|
777
1023
|
const invocationId = yield* ids.generate(InvocationId);
|
|
778
1024
|
const preparedArguments = yield* prepareMutationArguments(args).pipe(Effect.provideService(IdGenerator, ids), Effect.mapError((cause) => InvalidMutationArguments.make({
|
|
779
1025
|
cause,
|
|
@@ -806,6 +1052,7 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
|
|
|
806
1052
|
files: preparedArguments.fileIds
|
|
807
1053
|
};
|
|
808
1054
|
invocations = HashMap.set(invocations, invocationId, {
|
|
1055
|
+
sessionEpoch: admittedSession.sessionEpoch,
|
|
809
1056
|
deferred,
|
|
810
1057
|
function: functionAddress,
|
|
811
1058
|
message,
|
|
@@ -867,7 +1114,7 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
|
|
|
867
1114
|
concurrency: 3,
|
|
868
1115
|
discard: true
|
|
869
1116
|
});
|
|
870
|
-
const pendingInvocation = HashMap.
|
|
1117
|
+
const pendingInvocation = Option.getOrUndefined(HashMap.get(invocations, invocationId));
|
|
871
1118
|
if (pendingInvocation === void 0) return yield* Deferred.await(deferred);
|
|
872
1119
|
invocations = HashMap.set(invocations, invocationId, {
|
|
873
1120
|
...pendingInvocation,
|
|
@@ -889,7 +1136,7 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
|
|
|
889
1136
|
});
|
|
890
1137
|
})).pipe(Layer.provide(Layer.merge(Socket.layerWebSocketConstructorGlobal, BrowserCrypto.layer)));
|
|
891
1138
|
};
|
|
892
|
-
const syncRuntime = ManagedRuntime.make(SyncClient.layer.pipe(Layer.provideMerge(clientErrorReporterLayer), Layer.provideMerge(IdGenerator.layer)));
|
|
1139
|
+
const syncRuntime = ManagedRuntime.make(SyncClient.layer.pipe(Layer.provideMerge(BrowserSession.layer), Layer.provideMerge(clientErrorReporterLayer), Layer.provideMerge(IdGenerator.layer)));
|
|
893
1140
|
const hot = import.meta.hot;
|
|
894
1141
|
if (hot !== void 0) hot.dispose(() => void syncRuntime.dispose());
|
|
895
1142
|
//#endregion
|
|
@@ -1135,6 +1382,38 @@ const useMutation = (reference) => useMemo$1(() => {
|
|
|
1135
1382
|
};
|
|
1136
1383
|
return mutate;
|
|
1137
1384
|
}, [reference]);
|
|
1385
|
+
function useId() {
|
|
1386
|
+
const [state, setState] = useState$1({ status: "pending" });
|
|
1387
|
+
useEffect$1(() => {
|
|
1388
|
+
let active = true;
|
|
1389
|
+
let unsubscribe;
|
|
1390
|
+
syncRuntime.runPromise(BrowserSession).then((session) => {
|
|
1391
|
+
if (!active) return;
|
|
1392
|
+
const update = () => setState(session.snapshot());
|
|
1393
|
+
unsubscribe = session.subscribe(update);
|
|
1394
|
+
update();
|
|
1395
|
+
});
|
|
1396
|
+
return () => {
|
|
1397
|
+
active = false;
|
|
1398
|
+
unsubscribe?.();
|
|
1399
|
+
};
|
|
1400
|
+
}, []);
|
|
1401
|
+
const actions = useMemo$1(() => ({
|
|
1402
|
+
signIn: (options) => syncRuntime.runPromise(Effect.gen(function* () {
|
|
1403
|
+
return yield* (yield* BrowserSession).start("signIn", options?.profile ?? [], options?.returnTo);
|
|
1404
|
+
})),
|
|
1405
|
+
requestProfile: (fields, options) => syncRuntime.runPromise(Effect.gen(function* () {
|
|
1406
|
+
return yield* (yield* BrowserSession).start("requestProfile", fields, options?.returnTo);
|
|
1407
|
+
})),
|
|
1408
|
+
signOut: () => syncRuntime.runPromise(Effect.gen(function* () {
|
|
1409
|
+
return yield* (yield* BrowserSession).signOut;
|
|
1410
|
+
}))
|
|
1411
|
+
}), []);
|
|
1412
|
+
return {
|
|
1413
|
+
...state,
|
|
1414
|
+
...actions
|
|
1415
|
+
};
|
|
1416
|
+
}
|
|
1138
1417
|
//#endregion
|
|
1139
1418
|
//#region src/client/app.ts
|
|
1140
1419
|
const app = (definition) => {
|
|
@@ -1145,6 +1424,6 @@ const app = (definition) => {
|
|
|
1145
1424
|
//#region src/client/index.ts
|
|
1146
1425
|
const Result = { match: Result$2.match };
|
|
1147
1426
|
//#endregion
|
|
1148
|
-
export { Component, Files, Fragment, Query, Result, app, cloneElement, createContext, createElement, createRef, h, isValidElement, toChildArray, useCallback, useContext, useDebugValue, useEffect, useErrorBoundary, useId, useImperativeHandle, useLayoutEffect, useMemo, useMutation, usePaginatedQuery, useQuery, useReducer, useRef, useState };
|
|
1427
|
+
export { Component, Files, Fragment, Query, Result, app, cloneElement, createContext, createElement, createRef, h, isValidElement, toChildArray, useCallback, useContext, useDebugValue, useEffect, useElementId, useErrorBoundary, useId, useImperativeHandle, useLayoutEffect, useMemo, useMutation, usePaginatedQuery, useQuery, useReducer, useRef, useState };
|
|
1149
1428
|
|
|
1150
1429
|
//# sourceMappingURL=client.js.map
|