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/server.d.ts
CHANGED
|
@@ -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-
|
|
2
|
-
import "./result-
|
|
3
|
-
import {
|
|
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
|
package/dist/runtime/server.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as defineSchema$1, i as defineEnv$1, l as Secret$1 } from "./schema-
|
|
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-
|
|
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-
|
|
8
|
+
//# sourceMappingURL=sync-Bs8J3fIr.d.ts.map
|
package/package.json
CHANGED
package/src/cli/agent-files.ts
CHANGED
|
@@ -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],
|
package/src/client/hooks.ts
CHANGED
|
@@ -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
|
+
}
|
package/src/client/id.ts
ADDED
|
@@ -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
|
+
}
|
package/src/client/index.ts
CHANGED
|
@@ -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";
|