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.
Files changed (52) hide show
  1. package/dist/cli/bin.mjs +825 -403
  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 +339 -60
  8. package/dist/runtime/client.js.map +1 -1
  9. package/dist/runtime/descriptor-Cyo9FP9n-C0SRVXNW.js +154 -0
  10. package/dist/runtime/descriptor-Cyo9FP9n-C0SRVXNW.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-Dz0apuB3.d.ts +1 -0
  15. package/dist/runtime/{index-CrWg4Z0y.d.ts → index-D54flWtH.d.ts} +9 -5
  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 +9 -6
  19. package/dist/runtime/internal/host.js +15 -6
  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 +1 -1
  24. package/dist/runtime/internal/types.js +1 -1
  25. package/dist/runtime/{pagination-D-R9NR61-CpIoHFoI.d.ts → pagination-DnKg3dkI-r5ZUxBBx.d.ts} +31 -6
  26. package/dist/runtime/pagination-Dz0apuB3.d.ts +1 -0
  27. package/dist/runtime/result-DKAA4gpS.d.ts +1 -0
  28. package/dist/runtime/{schema-B9XxyRO8.js → schema-1Zs03-iS.js} +5 -3
  29. package/dist/runtime/schema-1Zs03-iS.js.map +1 -0
  30. package/dist/runtime/server.d.ts +6 -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/package.json +1 -1
  35. package/src/cli/agent-files.ts +6 -0
  36. package/src/client/hooks.ts +68 -0
  37. package/src/client/id.ts +259 -0
  38. package/src/client/index.ts +5 -2
  39. package/src/client/sync.ts +143 -10
  40. package/src/dev-runtime/functions.ts +111 -88
  41. package/src/dev-runtime/id.ts +175 -0
  42. package/src/dev-runtime/query-cache.ts +105 -0
  43. package/src/dev-runtime/sync.ts +149 -17
  44. package/src/server/index.ts +2 -0
  45. package/dist/runtime/api-DzcR7spt.js.map +0 -1
  46. package/dist/runtime/descriptor-XzDX2JDw-j3O6YHgW.js +0 -330
  47. package/dist/runtime/descriptor-XzDX2JDw-j3O6YHgW.js.map +0 -1
  48. package/dist/runtime/file-C1abuMgd.js +0 -173
  49. package/dist/runtime/file-C1abuMgd.js.map +0 -1
  50. package/dist/runtime/pagination-CX5IPbEi.d.ts +0 -1
  51. package/dist/runtime/result-DIjKM-p4.d.ts +0 -1
  52. package/dist/runtime/schema-B9XxyRO8.js.map +0 -1
@@ -1,6 +1,8 @@
1
- import { c as Result$1, g as FileValue, h as FileMimeType, i as ErrorValue, m as FileMetadata, p as FileFormat } from "./id-Cs82tq9Q-CK-maMgN.js";
2
- import "./result-DIjKM-p4.js";
3
- import { a as DefinedEnv, d as defineEnv$1, f as Secret$1, i as defineSchema$1, n as SchemaAuthoring, s as EnvironmentAuthoring, t as DefinedSchema } from "./index-CrWg4Z0y.js";
1
+ import { c as Result$1, g as FileValue, h as FileMimeType, i as ErrorValue, m as FileMetadata, p as FileFormat } from "./id-BzFHf3Wo-DGPCjgrf.js";
2
+ import "./result-DKAA4gpS.js";
3
+ import { d as User, f as UserId } from "./pagination-DnKg3dkI-r5ZUxBBx.js";
4
+ import "./id-Dz0apuB3.js";
5
+ import { a as DefinedEnv, d as defineEnv$1, f as Secret$1, i as defineSchema$1, n as SchemaAuthoring, s as EnvironmentAuthoring, t as DefinedSchema } from "./index-D54flWtH.js";
4
6
  //#region src/server/index.d.ts
5
7
  declare const Result: typeof Result$1;
6
8
  type Result<Success, Failure extends ErrorValue> = Result$1<Success, Failure>;
@@ -9,5 +11,5 @@ declare const defineSchema: typeof defineSchema$1;
9
11
  declare const Secret: typeof Secret$1;
10
12
  type Secret<Value> = Secret$1<Value>;
11
13
  //#endregion
12
- export { type DefinedEnv, type DefinedSchema, type EnvironmentAuthoring, type FileFormat, type FileMetadata, type FileMimeType, type FileValue, Result, type SchemaAuthoring, Secret, defineEnv, defineSchema };
14
+ export { type DefinedEnv, type DefinedSchema, type EnvironmentAuthoring, type FileFormat, type FileMetadata, type FileMimeType, type FileValue, Result, type SchemaAuthoring, Secret, type User, type UserId, defineEnv, defineSchema };
13
15
  //# sourceMappingURL=server.d.ts.map
@@ -1,5 +1,5 @@
1
- import { f as Result$1 } from "./descriptor-XzDX2JDw-j3O6YHgW.js";
2
- import { a as defineSchema$1, i as defineEnv$1, l as Secret$1 } from "./schema-B9XxyRO8.js";
1
+ import { _ as Result$1 } from "./id-Bt9XWRGL.js";
2
+ import { a as defineSchema$1, i as defineEnv$1, l as Secret$1 } from "./schema-1Zs03-iS.js";
3
3
  //#region src/server/index.ts
4
4
  const Result = Result$1;
5
5
  const defineEnv = defineEnv$1;
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","names":["contractResult","defineContractEnv","defineContractSchema","contractSecret"],"sources":["../../src/server/index.ts"],"sourcesContent":["import { Result as contractResult } from \"@ignotum/contracts/runtime/result\";\nimport type { ErrorValue, Result as ContractResult } from \"@ignotum/contracts/runtime/result\";\nimport {\n defineEnv as defineContractEnv,\n defineSchema as defineContractSchema,\n Secret as contractSecret,\n} from \"@ignotum/contracts/schema\";\nimport type { Secret as ContractSecret } from \"@ignotum/contracts/schema\";\n\nexport const Result: typeof contractResult = contractResult;\nexport type Result<Success, Failure extends ErrorValue> = ContractResult<Success, Failure>;\nexport const defineEnv: typeof defineContractEnv = defineContractEnv;\nexport const defineSchema: typeof defineContractSchema = defineContractSchema;\nexport const Secret: typeof contractSecret = contractSecret;\nexport type Secret<Value> = ContractSecret<Value>;\nexport type {\n DefinedEnv,\n DefinedSchema,\n EnvironmentAuthoring,\n FileFormat,\n FileMetadata,\n FileMimeType,\n FileValue,\n SchemaAuthoring,\n} from \"@ignotum/contracts/schema\";\n"],"mappings":";;;AASA,MAAa,SAAgCA;AAE7C,MAAa,YAAsCC;AACnD,MAAa,eAA4CC;AACzD,MAAa,SAAgCC"}
1
+ {"version":3,"file":"server.js","names":["contractResult","defineContractEnv","defineContractSchema","contractSecret"],"sources":["../../src/server/index.ts"],"sourcesContent":["import { Result as contractResult } from \"@ignotum/contracts/runtime/result\";\nimport type { ErrorValue, Result as ContractResult } from \"@ignotum/contracts/runtime/result\";\nimport {\n defineEnv as defineContractEnv,\n defineSchema as defineContractSchema,\n Secret as contractSecret,\n} from \"@ignotum/contracts/schema\";\nimport type { Secret as ContractSecret } from \"@ignotum/contracts/schema\";\n\nexport const Result: typeof contractResult = contractResult;\nexport type Result<Success, Failure extends ErrorValue> = ContractResult<Success, Failure>;\nexport const defineEnv: typeof defineContractEnv = defineContractEnv;\nexport const defineSchema: typeof defineContractSchema = defineContractSchema;\nexport const Secret: typeof contractSecret = contractSecret;\nexport type Secret<Value> = ContractSecret<Value>;\nexport type {\n DefinedEnv,\n DefinedSchema,\n EnvironmentAuthoring,\n FileFormat,\n FileMetadata,\n FileMimeType,\n FileValue,\n SchemaAuthoring,\n} from \"@ignotum/contracts/schema\";\n\nexport type { User, UserId } from \"@ignotum/contracts/id\";\n"],"mappings":";;;AASA,MAAa,SAAgCA;AAE7C,MAAa,YAAsCC;AACnD,MAAa,eAA4CC;AACzD,MAAa,SAAgCC"}
@@ -1,8 +1,8 @@
1
- import "./id-Cs82tq9Q-CK-maMgN.js";
1
+ import "./id-BzFHf3Wo-DGPCjgrf.js";
2
2
  import { Schema } from "effect";
3
3
  //#region ../contracts/dist/runtime/sync.d.ts
4
4
  declare const FunctionAddress: Schema.TemplateLiteral<readonly ["api.", Schema.String, ".", Schema.String]>;
5
5
  type FunctionAddress = typeof FunctionAddress.Type;
6
6
  //#endregion
7
7
  export { FunctionAddress as t };
8
- //# sourceMappingURL=sync-avN7NkcU.d.ts.map
8
+ //# sourceMappingURL=sync-Bs8J3fIr.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ignotum",
3
- "version": "0.0.12",
3
+ "version": "0.0.13",
4
4
  "description": "Ignotum is an opinionated TypeScript application cloud.",
5
5
  "author": "Johannes Schießl",
6
6
  "repository": {
@@ -1,3 +1,6 @@
1
+ import authentication from "../../../../docs/user/authentication.md?raw";
2
+ import profileSharing from "../../../../docs/user/profile-sharing.md?raw";
3
+ import localAuthentication from "../../../../docs/user/local-authentication.md?raw";
1
4
  import agents from "../../../../docs/agent/AGENTS.md?raw";
2
5
  import skill from "../../../../docs/agent/skills/ignotum/SKILL.md?raw";
3
6
  import api from "../../../../docs/user/api.md?raw";
@@ -38,6 +41,9 @@ export interface AppFile {
38
41
  }
39
42
 
40
43
  const references = [
44
+ ["authentication.md", authentication],
45
+ ["profile-sharing.md", profileSharing],
46
+ ["local-authentication.md", localAuthentication],
41
47
  ["api.md", api],
42
48
  ["app-entry.md", appEntry],
43
49
  ["cli.md", cli],
@@ -1,3 +1,5 @@
1
+ import { BrowserSession, type IdState } from "./id.js";
2
+ import type { ProfileField } from "@ignotum/contracts/id";
1
3
  import { Effect, Schema } from "effect";
2
4
  import { useCallback, useDebugValue, useEffect, useMemo, useState } from "preact/hooks";
3
5
 
@@ -297,3 +299,69 @@ export const useMutation = <Args extends object | void, Success, Failure extends
297
299
  // both call shapes normalize omitted arguments to the empty JSON object.
298
300
  return mutate as Mutation<Args, Success, Failure>;
299
301
  }, [reference]);
302
+
303
+ export type IdOptions = {
304
+ readonly profile?: ReadonlyArray<ProfileField>;
305
+ readonly returnTo?: string;
306
+ };
307
+ export type IdResult = IdState & {
308
+ readonly signIn: (options?: IdOptions) => Promise<void>;
309
+ readonly requestProfile: (
310
+ fields: ReadonlyArray<ProfileField>,
311
+ options?: { readonly returnTo?: string },
312
+ ) => Promise<void>;
313
+ readonly signOut: () => Promise<void>;
314
+ };
315
+
316
+ export function useId(): IdResult {
317
+ const [state, setState] = useState<IdState>({ status: "pending" });
318
+ useEffect(() => {
319
+ let active = true;
320
+ let unsubscribe: (() => void) | undefined;
321
+ void syncRuntime.runPromise(BrowserSession).then((session) => {
322
+ if (!active) return;
323
+ const update = () => setState(session.snapshot());
324
+ unsubscribe = session.subscribe(update);
325
+ update();
326
+ });
327
+ return () => {
328
+ active = false;
329
+ unsubscribe?.();
330
+ };
331
+ }, []);
332
+ const actions = useMemo(
333
+ () => ({
334
+ signIn: (options?: IdOptions) =>
335
+ syncRuntime.runPromise(
336
+ Effect.gen(function* () {
337
+ return yield* (yield* BrowserSession).start(
338
+ "signIn",
339
+ options?.profile ?? [],
340
+ options?.returnTo,
341
+ );
342
+ }),
343
+ ),
344
+ requestProfile: (
345
+ fields: ReadonlyArray<ProfileField>,
346
+ options?: { readonly returnTo?: string },
347
+ ) =>
348
+ syncRuntime.runPromise(
349
+ Effect.gen(function* () {
350
+ return yield* (yield* BrowserSession).start(
351
+ "requestProfile",
352
+ fields,
353
+ options?.returnTo,
354
+ );
355
+ }),
356
+ ),
357
+ signOut: () =>
358
+ syncRuntime.runPromise(
359
+ Effect.gen(function* () {
360
+ return yield* (yield* BrowserSession).signOut;
361
+ }),
362
+ ),
363
+ }),
364
+ [],
365
+ );
366
+ return { ...state, ...actions };
367
+ }
@@ -0,0 +1,259 @@
1
+ import { Context, Effect, Layer, Option, Schema, Semaphore } from "effect";
2
+ import {
3
+ FetchHttpClient,
4
+ HttpClient,
5
+ HttpClientRequest,
6
+ HttpClientResponse,
7
+ } from "effect/unstable/http";
8
+ import {
9
+ DevelopmentSelection,
10
+ developmentSelectionKey,
11
+ SessionOutcome,
12
+ SessionRedirect,
13
+ SessionRequest,
14
+ SessionResponse,
15
+ sessionPath,
16
+ sessionRequestsPath,
17
+ type ProfileField,
18
+ type SessionState,
19
+ type User,
20
+ } from "@ignotum/contracts/id";
21
+ import { IdGenerator } from "@ignotum/shared/id";
22
+ import { SessionEpoch } from "@ignotum/contracts/runtime/id";
23
+
24
+ export class IdRequestError extends Schema.TaggedError<IdRequestError>()("IdRequestError", {
25
+ message: Schema.String,
26
+ }) {}
27
+
28
+ export type IdState =
29
+ | { readonly status: "pending"; readonly outcome?: SessionOutcome }
30
+ | { readonly status: "error"; readonly error: string; readonly outcome?: SessionOutcome }
31
+ | { readonly status: "signedOut"; readonly outcome?: SessionOutcome }
32
+ | { readonly status: "signedIn"; readonly user: User; readonly outcome?: SessionOutcome };
33
+
34
+ const outcomeKey = "ignotum.id.outcome";
35
+ const selectionJson = Schema.fromJsonString(DevelopmentSelection);
36
+
37
+ export class BrowserSession extends Context.Service<
38
+ BrowserSession,
39
+ {
40
+ readonly snapshot: () => IdState;
41
+ readonly current: () => SessionState | undefined;
42
+ readonly subscribe: (listener: () => void) => () => void;
43
+ readonly bootstrap: Effect.Effect<SessionState, IdRequestError>;
44
+ readonly accept: (session: SessionState, serverTime: number) => void;
45
+ readonly suspend: (error?: string) => void;
46
+ readonly socketParameters: () => string;
47
+ readonly start: (
48
+ intent: "signIn" | "requestProfile",
49
+ profile: ReadonlyArray<ProfileField>,
50
+ returnTo?: string,
51
+ ) => Effect.Effect<void, IdRequestError>;
52
+ readonly signOut: Effect.Effect<void, IdRequestError>;
53
+ }
54
+ >()("ignotum/client/id/BrowserSession") {
55
+ static readonly layer = Layer.effect(
56
+ BrowserSession,
57
+ Effect.gen(function* () {
58
+ const client = (yield* HttpClient.HttpClient).pipe(HttpClient.filterStatusOk);
59
+ const ids = yield* IdGenerator;
60
+ const bootstrapLock = yield* Semaphore.make(1);
61
+ const listeners = new Set<() => void>();
62
+ let state: IdState = { status: "pending" };
63
+ let current: SessionState | undefined;
64
+ let generation = 0;
65
+ let deadline = Infinity;
66
+ let development = false;
67
+ let selection: DevelopmentSelection | undefined;
68
+ let expiry: ReturnType<typeof setTimeout> | undefined;
69
+ const notify = () => {
70
+ for (const listener of listeners) listener();
71
+ };
72
+ const suspend = (error?: string) => {
73
+ generation += 1;
74
+ deadline = Infinity;
75
+ if (expiry !== undefined) clearTimeout(expiry);
76
+ current = undefined;
77
+ state =
78
+ error === undefined
79
+ ? { status: "pending", outcome: state.outcome }
80
+ : { status: "error", error, outcome: state.outcome };
81
+ notify();
82
+ };
83
+ const accept = (session: SessionState, serverTime: number) => {
84
+ generation += 1;
85
+ if (expiry !== undefined) clearTimeout(expiry);
86
+ current = session;
87
+ state =
88
+ session.user === null
89
+ ? { status: "signedOut", outcome: state.outcome }
90
+ : { status: "signedIn", user: session.user, outcome: state.outcome };
91
+ const delay = Math.max(0, session.validUntil - serverTime);
92
+ deadline = performance.now() + delay;
93
+ if (delay < 2_147_483_647) expiry = setTimeout(() => suspend(), delay);
94
+ notify();
95
+ };
96
+ const checkExpiry = () => {
97
+ if (current !== undefined && performance.now() >= deadline) suspend();
98
+ };
99
+ globalThis.addEventListener?.("focus", checkExpiry);
100
+ globalThis.document?.addEventListener("visibilitychange", checkExpiry);
101
+ yield* Effect.addFinalizer(() =>
102
+ Effect.sync(() => {
103
+ if (expiry !== undefined) clearTimeout(expiry);
104
+ listeners.clear();
105
+ globalThis.removeEventListener?.("focus", checkExpiry);
106
+ globalThis.document?.removeEventListener("visibilitychange", checkExpiry);
107
+ }),
108
+ );
109
+
110
+ const readOutcome = () => {
111
+ const raw = globalThis.sessionStorage.getItem(outcomeKey);
112
+ if (raw !== null) {
113
+ const decoded = Schema.decodeUnknownOption(SessionOutcome)(raw);
114
+ if (Option.isSome(decoded)) state = { ...state, outcome: decoded.value };
115
+ globalThis.sessionStorage.removeItem(outcomeKey);
116
+ }
117
+ };
118
+ const parameters = () => {
119
+ if (!development || selection === undefined) return "";
120
+ const params = new URLSearchParams({ epoch: selection.epoch });
121
+ if (selection.username !== null) params.set("username", selection.username);
122
+ return `?${params}`;
123
+ };
124
+ const readSession = Effect.fn("BrowserSession.read")(function* () {
125
+ return yield* client
126
+ .get(`${sessionPath}${parameters()}`, { headers: { "x-ignotum-request": "1" } })
127
+ .pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(SessionResponse)));
128
+ });
129
+ let bootstrapGeneration = 0;
130
+ const bootstrap = bootstrapLock
131
+ .withPermits(1)(
132
+ Effect.gen(function* () {
133
+ const admittedGeneration = generation;
134
+ bootstrapGeneration = admittedGeneration;
135
+ const startedAt = performance.now();
136
+ let session = yield* readSession();
137
+ development = session.development === true;
138
+ if (development && selection === undefined) {
139
+ const saved = globalThis.sessionStorage.getItem(developmentSelectionKey);
140
+ const decoded =
141
+ saved === null
142
+ ? Option.none<DevelopmentSelection>()
143
+ : Schema.decodeOption(selectionJson)(saved);
144
+ selection = Option.getOrElse(decoded, () => ({
145
+ formatVersion: 1 as const,
146
+ username: null,
147
+ epoch: "",
148
+ }));
149
+ if (selection.epoch === "")
150
+ selection = { ...selection, epoch: yield* ids.generate(SessionEpoch) };
151
+ globalThis.sessionStorage.setItem(
152
+ developmentSelectionKey,
153
+ Schema.encodeSync(selectionJson)(selection),
154
+ );
155
+ session = yield* readSession();
156
+ }
157
+ if (generation !== admittedGeneration) {
158
+ if (current !== undefined) return current;
159
+ return yield* IdRequestError.make({
160
+ message: "Your ID session changed while loading.",
161
+ });
162
+ }
163
+ readOutcome();
164
+ if (session.outcome !== undefined) state = { ...state, outcome: session.outcome };
165
+ accept(session, session.serverTime + performance.now() - startedAt);
166
+ return session;
167
+ }),
168
+ )
169
+ .pipe(
170
+ Effect.mapError(() =>
171
+ IdRequestError.make({ message: "Could not load your ID session." }),
172
+ ),
173
+ Effect.tapError((error) =>
174
+ Effect.sync(() => {
175
+ if (generation === bootstrapGeneration) suspend(error.message);
176
+ }),
177
+ ),
178
+ );
179
+
180
+ const start = Effect.fn("BrowserSession.start")(function* (
181
+ intent: "signIn" | "requestProfile",
182
+ profile: ReadonlyArray<ProfileField>,
183
+ returnTo?: string,
184
+ ) {
185
+ const session = current ?? (yield* bootstrap);
186
+ if (intent === "requestProfile" && session.user === null)
187
+ return yield* IdRequestError.make({
188
+ message: "Sign in before requesting profile information.",
189
+ });
190
+ const request = yield* Schema.decodeUnknownEffect(SessionRequest)({
191
+ intent,
192
+ profile,
193
+ sessionEpoch: session.sessionEpoch,
194
+ returnTo:
195
+ returnTo ??
196
+ `${globalThis.location.pathname}${globalThis.location.search}${globalThis.location.hash}`,
197
+ }).pipe(
198
+ Effect.mapError(() => IdRequestError.make({ message: "The ID request is invalid." })),
199
+ );
200
+ const redirect = yield* HttpClientRequest.post(sessionRequestsPath).pipe(
201
+ HttpClientRequest.setHeader("x-ignotum-request", "1"),
202
+ HttpClientRequest.bodyJsonUnsafe(request),
203
+ client.execute,
204
+ Effect.flatMap(HttpClientResponse.schemaBodyJson(SessionRedirect)),
205
+ Effect.mapError(() =>
206
+ IdRequestError.make({ message: "Could not start the ID request." }),
207
+ ),
208
+ );
209
+ if (current?.sessionEpoch !== session.sessionEpoch)
210
+ return yield* IdRequestError.make({ message: "Your ID session changed. Try again." });
211
+ globalThis.location.assign(redirect.redirectUrl);
212
+ });
213
+ const signOut = Effect.gen(function* () {
214
+ const session = current ?? (yield* bootstrap);
215
+ yield* HttpClientRequest.delete(sessionPath).pipe(
216
+ HttpClientRequest.setHeader("x-ignotum-request", "1"),
217
+ HttpClientRequest.bodyJsonUnsafe({ sessionEpoch: session.sessionEpoch }),
218
+ client.execute,
219
+ Effect.mapError(() => IdRequestError.make({ message: "Could not sign out." })),
220
+ );
221
+ suspend();
222
+ if (development) {
223
+ selection = {
224
+ formatVersion: 1,
225
+ username: null,
226
+ epoch: yield* ids.generate(SessionEpoch),
227
+ };
228
+ globalThis.sessionStorage.setItem(
229
+ developmentSelectionKey,
230
+ Schema.encodeSync(selectionJson)(selection),
231
+ );
232
+ }
233
+ globalThis.location.reload();
234
+ });
235
+ return BrowserSession.of({
236
+ snapshot: () => {
237
+ checkExpiry();
238
+ return state;
239
+ },
240
+ current: () => {
241
+ checkExpiry();
242
+ return current;
243
+ },
244
+ subscribe: (listener) => {
245
+ listeners.add(listener);
246
+ return () => {
247
+ listeners.delete(listener);
248
+ };
249
+ },
250
+ accept,
251
+ suspend,
252
+ bootstrap,
253
+ start,
254
+ signOut,
255
+ socketParameters: parameters,
256
+ });
257
+ }),
258
+ ).pipe(Layer.provide(FetchHttpClient.layer));
259
+ }
@@ -16,7 +16,7 @@ export type {
16
16
  FileValue,
17
17
  } from "@ignotum/contracts/schema/file";
18
18
  export { Files } from "./files.js";
19
- export { useMutation, usePaginatedQuery, useQuery } from "./hooks.js";
19
+ export { useId, useMutation, usePaginatedQuery, useQuery } from "./hooks.js";
20
20
  export type { PaginatedQueryOptions, PaginatedQueryValue, PaginationStatus } from "./hooks.js";
21
21
  export { Query } from "./query.js";
22
22
  export type { QuerySkip } from "./query.js";
@@ -89,7 +89,7 @@ export {
89
89
  useDebugValue,
90
90
  useEffect,
91
91
  useErrorBoundary,
92
- useId,
92
+ useId as useElementId,
93
93
  useImperativeHandle,
94
94
  useLayoutEffect,
95
95
  useMemo,
@@ -98,3 +98,6 @@ export {
98
98
  useState,
99
99
  } from "preact/hooks";
100
100
  export type { Dispatch, Reducer, StateUpdater } from "preact/hooks";
101
+
102
+ export type { User, UserId, ProfileField } from "@ignotum/contracts/id";
103
+ export type { IdResult, IdOptions } from "./hooks.js";