ignotum 0.0.12 → 0.0.14

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.
Files changed (60) hide show
  1. package/dist/cli/bin.mjs +1183 -447
  2. package/dist/cli/bin.mjs.map +1 -1
  3. package/dist/runtime/{api-DzcR7spt.js → api-CAgKDij7.js} +38 -2
  4. package/dist/runtime/api-CAgKDij7.js.map +1 -0
  5. package/dist/runtime/{api-5XSrIeqW.d.ts → api-Cv3hMbzo.d.ts} +4 -4
  6. package/dist/runtime/client.d.ts +39 -9
  7. package/dist/runtime/client.js +362 -63
  8. package/dist/runtime/client.js.map +1 -1
  9. package/dist/runtime/descriptor-DCCz5AHd-_nhlnu8U.js +298 -0
  10. package/dist/runtime/descriptor-DCCz5AHd-_nhlnu8U.js.map +1 -0
  11. package/dist/runtime/id-Bt9XWRGL.js +423 -0
  12. package/dist/runtime/id-Bt9XWRGL.js.map +1 -0
  13. package/dist/runtime/{id-Cs82tq9Q-CK-maMgN.d.ts → id-BzFHf3Wo-DGPCjgrf.d.ts} +2 -2
  14. package/dist/runtime/id-E7JKqQ9r.d.ts +1 -0
  15. package/dist/runtime/index-DWU-7TIh.d.ts +185 -0
  16. package/dist/runtime/internal/api.d.ts +1 -1
  17. package/dist/runtime/internal/api.js +1 -1
  18. package/dist/runtime/internal/host.d.ts +28 -7
  19. package/dist/runtime/internal/host.js +37 -8
  20. package/dist/runtime/internal/host.js.map +1 -1
  21. package/dist/runtime/internal/server.d.ts +1 -1
  22. package/dist/runtime/internal/server.js +1 -1
  23. package/dist/runtime/internal/types.d.ts +2 -1
  24. package/dist/runtime/internal/types.js +1 -1
  25. package/dist/runtime/{pagination-D-R9NR61-CpIoHFoI.d.ts → pagination-CpYCaRYD-C2Rpp4Cw.d.ts} +42 -7
  26. package/dist/runtime/pagination-E7JKqQ9r.d.ts +1 -0
  27. package/dist/runtime/result-DKAA4gpS.d.ts +1 -0
  28. package/dist/runtime/{schema-B9XxyRO8.js → schema-DqogEydG.js} +41 -12
  29. package/dist/runtime/schema-DqogEydG.js.map +1 -0
  30. package/dist/runtime/server.d.ts +7 -4
  31. package/dist/runtime/server.js +2 -2
  32. package/dist/runtime/server.js.map +1 -1
  33. package/dist/runtime/{sync-avN7NkcU.d.ts → sync-Bs8J3fIr.d.ts} +2 -2
  34. package/dist/runtime/types-C1iDP2Yf-BnkfBOQf.d.ts +224 -0
  35. package/package.json +2 -2
  36. package/src/cli/agent-files.ts +10 -0
  37. package/src/cli/build/server.ts +35 -6
  38. package/src/cli/command.ts +25 -2
  39. package/src/cli/deploy.ts +34 -11
  40. package/src/client/hooks.ts +68 -0
  41. package/src/client/id.ts +259 -0
  42. package/src/client/index.ts +5 -2
  43. package/src/client/sync.ts +144 -11
  44. package/src/dev-runtime/database.ts +77 -2
  45. package/src/dev-runtime/functions.ts +111 -88
  46. package/src/dev-runtime/id.ts +190 -0
  47. package/src/dev-runtime/migrations.ts +9 -0
  48. package/src/dev-runtime/query-cache.ts +105 -0
  49. package/src/dev-runtime/sync.ts +149 -17
  50. package/src/internal/host.ts +1 -1
  51. package/src/server/index.ts +2 -0
  52. package/dist/runtime/api-DzcR7spt.js.map +0 -1
  53. package/dist/runtime/descriptor-XzDX2JDw-j3O6YHgW.js +0 -330
  54. package/dist/runtime/descriptor-XzDX2JDw-j3O6YHgW.js.map +0 -1
  55. package/dist/runtime/file-C1abuMgd.js +0 -173
  56. package/dist/runtime/file-C1abuMgd.js.map +0 -1
  57. package/dist/runtime/index-CrWg4Z0y.d.ts +0 -398
  58. package/dist/runtime/pagination-CX5IPbEi.d.ts +0 -1
  59. package/dist/runtime/result-DIjKM-p4.d.ts +0 -1
  60. package/dist/runtime/schema-B9XxyRO8.js.map +0 -1
@@ -1,9 +1,10 @@
1
- import { C as RuntimeRequestNonce, T as TableId, _ as AppId, a as RuntimeFileMetadata, b as IdAlphabet, c as fileFormats, d as fileLimits, f as fileMimeTypes, h as makeFileValue, l as fileGrantUrlOf, m as isFileValue, o as RuntimeFileOccurrence, p as grantFileValue, r as FileId, u as fileIdOf, v as DeploymentId, w as SubscriptionId, x as InvocationId, y as GeneratedId } from "./file-C1abuMgd.js";
2
- import { S as encodeTransportObject, c as initial, f as Result$2, g as pending, h as inspectResult, l as upgrade, m as failureFromWire, s as PaginationPageSize, t as ValueDescriptor, x as decodeTransportValue } from "./descriptor-XzDX2JDw-j3O6YHgW.js";
3
- 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-DzcR7spt.js";
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 { d as PaginationPageSize, t as ValueDescriptor } from "./descriptor-DCCz5AHd-_nhlnu8U.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 { useCallback, useCallback as useCallback$1, useContext, useDebugValue, useDebugValue as useDebugValue$1, useEffect, useEffect as useEffect$1, useErrorBoundary, useId, useImperativeHandle, useLayoutEffect, useMemo, useMemo as useMemo$1, useReducer, useRef, useState, useState as useState$1 } from "preact/hooks";
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",
@@ -473,11 +649,25 @@ const ServerBuildManifestV3 = Schema.Struct({
473
649
  environment: Schema.optional(ServerEnvironmentArtifact),
474
650
  functions: Schema.Array(ServerFunctionArtifact)
475
651
  });
476
- upgrade(ServerBuildManifestThroughV2, ServerBuildManifestV3, (manifest) => ({
652
+ const ServerBuildManifestThroughV3 = upgrade(ServerBuildManifestThroughV2, ServerBuildManifestV3, (manifest) => ({
477
653
  formatVersion: 3,
478
654
  schema: manifest.schema,
479
655
  functions: manifest.functions
480
656
  }));
657
+ const ServerBuildManifestV4 = Schema.Struct({
658
+ formatVersion: Schema.Literal(4),
659
+ schema: ArtifactReference,
660
+ environment: Schema.optional(ServerEnvironmentArtifact),
661
+ functions: Schema.Array(ServerFunctionArtifact),
662
+ schemaEvolution: Schema.optional(Schema.Struct({
663
+ bundle: ArtifactReference,
664
+ sourceMap: ArtifactReference
665
+ }))
666
+ });
667
+ upgrade(ServerBuildManifestThroughV3, ServerBuildManifestV4, (manifest) => ({
668
+ ...manifest,
669
+ formatVersion: 4
670
+ }));
481
671
  ArtifactPath.make("inventory.json");
482
672
  ArtifactPath.make("client/manifest.json");
483
673
  ArtifactPath.make("client/shell.html");
@@ -489,6 +679,7 @@ ArtifactPath.make("server/environment.json");
489
679
  //#endregion
490
680
  //#region ../contracts/dist/runtime/transport.js
491
681
  const runtimeInvocationPath = "/v1/invoke";
682
+ const runtimeConsolePath = "/v1/console/read";
492
683
  const runtimeRevisionPath = "/v1/revision";
493
684
  const runtimeIndexPreparePath = "/v1/indexes/prepare";
494
685
  const runtimeIndexCommitPath = "/v1/indexes/commit";
@@ -496,6 +687,7 @@ const runtimeFileEffectsPath = "/v1/files/effects";
496
687
  const runtimeFileEffectsAcknowledgePath = "/v1/files/effects/acknowledge";
497
688
  const appSyncPath = `/_ignotum/v1/sync`;
498
689
  const RuntimeRequestPath = Schema.Literals([
690
+ runtimeConsolePath,
499
691
  runtimeInvocationPath,
500
692
  runtimeRevisionPath,
501
693
  runtimeIndexPreparePath,
@@ -523,9 +715,13 @@ Schema.Struct({
523
715
  });
524
716
  Schema.Struct({
525
717
  appId: AppId,
526
- deploymentId: DeploymentId
718
+ deploymentId: DeploymentId,
719
+ generation: Schema.optional(Schema.Natural)
527
720
  });
528
- Schema.Struct({ completed: Schema.Literal(true) });
721
+ Schema.Union([Schema.Struct({ completed: Schema.Literal(true) }), Schema.Struct({
722
+ completed: Schema.Literal(false),
723
+ message: Schema.String
724
+ })]);
529
725
  const RuntimeErrorCode = Schema.Literals([
530
726
  "InvalidRequest",
531
727
  "InvalidSignature",
@@ -554,55 +750,67 @@ const subscribeMessage = (entry) => ({
554
750
  const makeQueryCache = (allocateId, sendLifecycleMessage) => {
555
751
  const queries = /* @__PURE__ */ new Map();
556
752
  const queriesById = /* @__PURE__ */ new Map();
753
+ const observe = Effect.fn("SyncClient.QueryCache.observe")(function* (functionAddress, args) {
754
+ const key = queryKey(functionAddress, args);
755
+ let entry = queries.get(key);
756
+ if (entry === void 0) {
757
+ const created = {
758
+ args,
759
+ function: functionAddress,
760
+ generation: 0,
761
+ listeners: /* @__PURE__ */ new Set(),
762
+ revision: void 0,
763
+ references: 0,
764
+ result: pending(),
765
+ subscriptionId: yield* allocateId
766
+ };
767
+ queries.set(key, created);
768
+ queriesById.set(created.subscriptionId, created);
769
+ yield* sendLifecycleMessage(subscribeMessage(created));
770
+ entry = created;
771
+ }
772
+ const observed = entry;
773
+ observed.references += 1;
774
+ observed.generation += 1;
775
+ let released = false;
776
+ return {
777
+ getSnapshot: () => observed.result,
778
+ subscribe: (listener) => {
779
+ observed.listeners.add(listener);
780
+ return () => observed.listeners.delete(listener);
781
+ },
782
+ release: Effect.gen(function* () {
783
+ if (released) return;
784
+ released = true;
785
+ observed.references -= 1;
786
+ if (observed.references !== 0) return;
787
+ const generation = ++observed.generation;
788
+ yield* Effect.sleep("25 millis");
789
+ if (observed.references !== 0 || observed.generation !== generation || queries.get(key) !== observed) return;
790
+ queries.delete(key);
791
+ queriesById.delete(observed.subscriptionId);
792
+ yield* sendLifecycleMessage({
793
+ type: "Unsubscribe",
794
+ id: observed.subscriptionId
795
+ });
796
+ })
797
+ };
798
+ });
799
+ const clear = (entry) => {
800
+ entry.result = pending();
801
+ entry.revision = void 0;
802
+ for (const listener of entry.listeners) listener();
803
+ };
557
804
  return {
805
+ invalidate: (ids) => {
806
+ for (const entry of queries.values()) if (ids === void 0 || ids.includes(entry.subscriptionId)) clear(entry);
807
+ },
808
+ invalidatePrivate: () => {
809
+ for (const entry of queries.values()) if (entry.dependsOnId !== false) clear(entry);
810
+ },
558
811
  entries: () => queries.values(),
559
812
  getById: (id) => queriesById.get(id),
560
- observe: Effect.fn("SyncClient.QueryCache.observe")(function* (functionAddress, args) {
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
- }),
813
+ observe,
606
814
  setResult: (id, result, revision) => {
607
815
  const entry = queriesById.get(id);
608
816
  if (entry === void 0) return;
@@ -644,10 +852,12 @@ const socketUrl = () => {
644
852
  var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync/SyncClient") {
645
853
  static layer = Layer.effect(SyncClient, Effect.gen(function* () {
646
854
  const ids = yield* IdGenerator;
855
+ const session = yield* BrowserSession;
647
856
  let invocations = HashMap.empty();
648
857
  let rememberedHandshake;
649
858
  let reloadRequested = false;
650
859
  let writer;
860
+ let sessionSuspended;
651
861
  const encode = (message) => Schema.encodeEffect(ClientMessageJson)(message).pipe(Effect.mapError((cause) => InvalidClientMessage.make({
652
862
  cause,
653
863
  message: "Could not encode an Ignotum sync message."
@@ -661,17 +871,47 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
661
871
  ConnectionUnavailable: reportClientError,
662
872
  InvalidClientMessage: reportClientError
663
873
  })));
874
+ const unsubscribeSession = session.subscribe(() => {
875
+ if (session.current() === void 0) {
876
+ queryCache.invalidatePrivate();
877
+ writer = void 0;
878
+ if (sessionSuspended !== void 0) Deferred.doneUnsafe(sessionSuspended, Effect.void);
879
+ }
880
+ });
881
+ yield* Effect.addFinalizer(() => Effect.sync(unsubscribeSession));
664
882
  const rejectInvocation = (id, error) => {
665
- const pendingInvocation = HashMap.getUnsafe(invocations, id);
883
+ const pendingInvocation = Option.getOrUndefined(HashMap.get(invocations, id));
666
884
  if (pendingInvocation === void 0) return Effect.void;
667
885
  invocations = HashMap.remove(invocations, id);
668
886
  return reportClientError(error).pipe(Effect.andThen(Deferred.fail(pendingInvocation.prepared, error)), Effect.andThen(Deferred.fail(pendingInvocation.deferred, error)), Effect.asVoid);
669
887
  };
670
888
  const handleMessage = Effect.fn("SyncClient.handleMessage")(function* (message) {
671
889
  switch (message.type) {
890
+ case "Session":
891
+ if (message.event === "Updated") {
892
+ const previous = session.current();
893
+ if (previous !== void 0 && previous.sessionEpoch !== message.session.sessionEpoch) return;
894
+ if (previous?.viewRevision !== message.session.viewRevision) queryCache.invalidatePrivate();
895
+ session.accept(message.session, message.serverTime);
896
+ } else {
897
+ if (rememberedHandshake?.session?.sessionEpoch !== message.sessionEpoch) return;
898
+ session.suspend(message.event === "Unavailable" ? "Your ID session is temporarily unavailable." : void 0);
899
+ if (message.event !== "Unavailable") yield* Effect.forEach(HashMap.keys(invocations), (id) => rejectInvocation(id, ServerError.make({
900
+ code: "SessionChanged",
901
+ message: "The session changed. This operation may already have completed."
902
+ })));
903
+ }
904
+ return;
905
+ case "Invalidate":
906
+ if (message.sessionEpoch === session.current()?.sessionEpoch) queryCache.invalidate(message.ids);
907
+ return;
672
908
  case "Snapshot": {
909
+ const current = session.current();
910
+ if (current === void 0 || message.identity?.sessionEpoch !== current.sessionEpoch) return;
911
+ if (message.identity.dependsOnId && message.identity.viewRevision !== current.viewRevision) return;
673
912
  const entry = queryCache.getById(message.id);
674
913
  if (entry === void 0) return;
914
+ entry.dependsOnId = message.identity.dependsOnId;
675
915
  if (entry.revision !== void 0 && message.revision < entry.revision) return;
676
916
  yield* resultFromWire(message.result, message.files ?? []).pipe(Effect.tap((result) => Effect.sync(() => queryCache.setResult(message.id, result, message.revision))), Effect.catchTags({ InvalidServerMessage: (error) => {
677
917
  queryCache.setResult(message.id, error, message.revision);
@@ -680,12 +920,14 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
680
920
  return;
681
921
  }
682
922
  case "Preparation": {
683
- const pendingInvocation = HashMap.getUnsafe(invocations, message.id);
923
+ if (message.sessionEpoch !== session.current()?.sessionEpoch) return;
924
+ const pendingInvocation = Option.getOrUndefined(HashMap.get(invocations, message.id));
684
925
  if (pendingInvocation !== void 0) yield* Deferred.succeed(pendingInvocation.prepared, message.uploads);
685
926
  return;
686
927
  }
687
928
  case "Result": {
688
- const pendingInvocation = HashMap.getUnsafe(invocations, message.id);
929
+ if (message.sessionEpoch !== session.current()?.sessionEpoch) return;
930
+ const pendingInvocation = Option.getOrUndefined(HashMap.get(invocations, message.id));
689
931
  if (pendingInvocation === void 0) return;
690
932
  yield* resultFromWire(message.result).pipe(Effect.flatMap((result) => {
691
933
  invocations = HashMap.remove(invocations, message.id);
@@ -694,6 +936,7 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
694
936
  return;
695
937
  }
696
938
  case "Error": {
939
+ if (message.sessionEpoch !== session.current()?.sessionEpoch) return;
697
940
  const error = message.operation === void 0 ? ServerError.make({
698
941
  code: message.code,
699
942
  message: message.message
@@ -722,7 +965,8 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
722
965
  });
723
966
  const connect = Effect.scoped(Effect.gen(function* () {
724
967
  if (reloadRequested) return yield* Effect.never;
725
- const socket = yield* Socket.makeWebSocket(socketUrl(), { closeCodeIsError: (code) => {
968
+ yield* session.bootstrap.pipe(Effect.mapError((error) => ConnectionUnavailable.make({ message: error.message })));
969
+ const socket = yield* Socket.makeWebSocket(`${socketUrl()}${session.socketParameters()}`, { closeCodeIsError: (code) => {
726
970
  if (shouldReloadForCloseCode(code)) requestReload();
727
971
  return false;
728
972
  } });
@@ -736,6 +980,22 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
736
980
  return;
737
981
  }
738
982
  if (decision === "Ignore") return;
983
+ if (handshake.session === void 0 || handshake.serverTime === void 0) {
984
+ requestReload();
985
+ return;
986
+ }
987
+ if (rememberedHandshake?.session !== void 0 && rememberedHandshake.session.sessionEpoch !== handshake.session.sessionEpoch) {
988
+ queryCache.invalidatePrivate();
989
+ yield* Effect.forEach(HashMap.keys(invocations), (id) => rejectInvocation(id, ServerError.make({
990
+ code: "SessionChanged",
991
+ message: "The session changed. This operation may already have completed."
992
+ })));
993
+ } else if (rememberedHandshake?.session?.viewRevision !== handshake.session.viewRevision) queryCache.invalidatePrivate();
994
+ yield* Effect.forEach(HashMap.entries(invocations), ([id, pending]) => pending.sessionEpoch === handshake.session.sessionEpoch ? Effect.void : rejectInvocation(id, ServerError.make({
995
+ code: "SessionChanged",
996
+ message: "The session changed. This operation may already have completed."
997
+ })));
998
+ session.accept(handshake.session, handshake.serverTime);
739
999
  rememberedHandshake = handshake;
740
1000
  handshakeAccepted = true;
741
1001
  writer = write;
@@ -761,9 +1021,14 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
761
1021
  });
762
1022
  return yield* handleMessage(message);
763
1023
  });
1024
+ const suspended = yield* Deferred.make();
1025
+ sessionSuspended = suspended;
1026
+ yield* Effect.addFinalizer(() => Effect.sync(() => {
1027
+ if (sessionSuspended === suspended) sessionSuspended = void 0;
1028
+ }));
764
1029
  const run = yield* socket.runString(handleText).pipe(Effect.forkChild);
765
1030
  yield* Effect.raceFirst(Deferred.await(handshakeReceived).pipe(Effect.timeout("10 seconds")), Fiber.join(run));
766
- yield* Fiber.join(run);
1031
+ yield* Effect.raceFirst(Fiber.join(run), Deferred.await(suspended));
767
1032
  })).pipe(Effect.mapError((cause) => Schema.is(ClientInfrastructureError)(cause) ? cause : ConnectionUnavailable.make({
768
1033
  cause,
769
1034
  message: "The sync connection was lost."
@@ -774,6 +1039,7 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
774
1039
  return yield* queryCache.observe(functionAddress, args);
775
1040
  });
776
1041
  const mutate = Effect.fn("SyncClient.mutate")(function* (functionAddress, args) {
1042
+ const admittedSession = session.current() ?? rememberedHandshake?.session ?? (yield* session.bootstrap.pipe(Effect.mapError((error) => ConnectionUnavailable.make({ message: error.message }))));
777
1043
  const invocationId = yield* ids.generate(InvocationId);
778
1044
  const preparedArguments = yield* prepareMutationArguments(args).pipe(Effect.provideService(IdGenerator, ids), Effect.mapError((cause) => InvalidMutationArguments.make({
779
1045
  cause,
@@ -806,6 +1072,7 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
806
1072
  files: preparedArguments.fileIds
807
1073
  };
808
1074
  invocations = HashMap.set(invocations, invocationId, {
1075
+ sessionEpoch: admittedSession.sessionEpoch,
809
1076
  deferred,
810
1077
  function: functionAddress,
811
1078
  message,
@@ -867,7 +1134,7 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
867
1134
  concurrency: 3,
868
1135
  discard: true
869
1136
  });
870
- const pendingInvocation = HashMap.getUnsafe(invocations, invocationId);
1137
+ const pendingInvocation = Option.getOrUndefined(HashMap.get(invocations, invocationId));
871
1138
  if (pendingInvocation === void 0) return yield* Deferred.await(deferred);
872
1139
  invocations = HashMap.set(invocations, invocationId, {
873
1140
  ...pendingInvocation,
@@ -889,7 +1156,7 @@ var SyncClient = class SyncClient extends Context.Service()("ignotum/client/sync
889
1156
  });
890
1157
  })).pipe(Layer.provide(Layer.merge(Socket.layerWebSocketConstructorGlobal, BrowserCrypto.layer)));
891
1158
  };
892
- const syncRuntime = ManagedRuntime.make(SyncClient.layer.pipe(Layer.provideMerge(clientErrorReporterLayer), Layer.provideMerge(IdGenerator.layer)));
1159
+ const syncRuntime = ManagedRuntime.make(SyncClient.layer.pipe(Layer.provideMerge(BrowserSession.layer), Layer.provideMerge(clientErrorReporterLayer), Layer.provideMerge(IdGenerator.layer)));
893
1160
  const hot = import.meta.hot;
894
1161
  if (hot !== void 0) hot.dispose(() => void syncRuntime.dispose());
895
1162
  //#endregion
@@ -1135,6 +1402,38 @@ const useMutation = (reference) => useMemo$1(() => {
1135
1402
  };
1136
1403
  return mutate;
1137
1404
  }, [reference]);
1405
+ function useId() {
1406
+ const [state, setState] = useState$1({ status: "pending" });
1407
+ useEffect$1(() => {
1408
+ let active = true;
1409
+ let unsubscribe;
1410
+ syncRuntime.runPromise(BrowserSession).then((session) => {
1411
+ if (!active) return;
1412
+ const update = () => setState(session.snapshot());
1413
+ unsubscribe = session.subscribe(update);
1414
+ update();
1415
+ });
1416
+ return () => {
1417
+ active = false;
1418
+ unsubscribe?.();
1419
+ };
1420
+ }, []);
1421
+ const actions = useMemo$1(() => ({
1422
+ signIn: (options) => syncRuntime.runPromise(Effect.gen(function* () {
1423
+ return yield* (yield* BrowserSession).start("signIn", options?.profile ?? [], options?.returnTo);
1424
+ })),
1425
+ requestProfile: (fields, options) => syncRuntime.runPromise(Effect.gen(function* () {
1426
+ return yield* (yield* BrowserSession).start("requestProfile", fields, options?.returnTo);
1427
+ })),
1428
+ signOut: () => syncRuntime.runPromise(Effect.gen(function* () {
1429
+ return yield* (yield* BrowserSession).signOut;
1430
+ }))
1431
+ }), []);
1432
+ return {
1433
+ ...state,
1434
+ ...actions
1435
+ };
1436
+ }
1138
1437
  //#endregion
1139
1438
  //#region src/client/app.ts
1140
1439
  const app = (definition) => {
@@ -1145,6 +1444,6 @@ const app = (definition) => {
1145
1444
  //#region src/client/index.ts
1146
1445
  const Result = { match: Result$2.match };
1147
1446
  //#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 };
1447
+ 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
1448
 
1150
1449
  //# sourceMappingURL=client.js.map