@parlor/client 0.1.0 → 0.2.0
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/README.md +2 -0
- package/dist/action-client.d.ts +2 -0
- package/dist/action-client.d.ts.map +1 -1
- package/dist/action-client.js +13 -4
- package/dist/action-client.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/plugin-client.js.map +1 -1
- package/dist/query-keys.js.map +1 -1
- package/dist/react/action-query.d.ts +26 -0
- package/dist/react/action-query.d.ts.map +1 -0
- package/dist/react/action-query.js +33 -0
- package/dist/react/action-query.js.map +1 -0
- package/dist/react/agent-ui/activity-state.js.map +1 -1
- package/dist/react/agent-ui/chat-activity.js.map +1 -1
- package/dist/react/agent-ui/chat-confirm.js.map +1 -1
- package/dist/react/agent-ui/chat.js.map +1 -1
- package/dist/react/agent-ui/dev.js.map +1 -1
- package/dist/react/agent-ui/runtime.js.map +1 -1
- package/dist/react/agent-ui/use-agent-activity.js.map +1 -1
- package/dist/react/agent-ui/use-agent-confirmations.js.map +1 -1
- package/dist/react/agent-ui/use-agent-view.js.map +1 -1
- package/dist/react/chat/ask-fields.js.map +1 -1
- package/dist/react/chat/chat-ask.js.map +1 -1
- package/dist/react/chat/chat-composer.d.ts.map +1 -1
- package/dist/react/chat/chat-composer.js +1 -1
- package/dist/react/chat/chat-composer.js.map +1 -1
- package/dist/react/chat/chat-root.js.map +1 -1
- package/dist/react/chat/chat-thread.js.map +1 -1
- package/dist/react/chat/context.js.map +1 -1
- package/dist/react/chat/create.js.map +1 -1
- package/dist/react/chat/open-turns.js.map +1 -1
- package/dist/react/chat/runtime.d.ts +12 -1
- package/dist/react/chat/runtime.d.ts.map +1 -1
- package/dist/react/chat/runtime.js +12 -1
- package/dist/react/chat/runtime.js.map +1 -1
- package/dist/react/chat/use-chat.js.map +1 -1
- package/dist/react/chat/use-device-setup.js.map +1 -1
- package/dist/react/hooks.d.ts +7 -1
- package/dist/react/hooks.d.ts.map +1 -1
- package/dist/react/hooks.js +17 -13
- package/dist/react/hooks.js.map +1 -1
- package/dist/react/index.d.ts +3 -2
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +2 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/provider.d.ts +6 -3
- package/dist/react/provider.d.ts.map +1 -1
- package/dist/react/provider.js +61 -11
- package/dist/react/provider.js.map +1 -1
- package/dist/react/retry.js.map +1 -1
- package/dist/react/sync-bridge.d.ts +4 -1
- package/dist/react/sync-bridge.d.ts.map +1 -1
- package/dist/react/sync-bridge.js.map +1 -1
- package/dist/react/sync.d.ts +41 -2
- package/dist/react/sync.d.ts.map +1 -1
- package/dist/react/sync.js +127 -9
- package/dist/react/sync.js.map +1 -1
- package/dist/react/use-capability.js.map +1 -1
- package/dist/react/use-devices.js.map +1 -1
- package/dist/react/use-link-device.js.map +1 -1
- package/dist/register.js.map +1 -1
- package/dist/router/errors.d.ts +13 -0
- package/dist/router/errors.d.ts.map +1 -0
- package/dist/router/errors.js +16 -0
- package/dist/router/errors.js.map +1 -0
- package/dist/router/index.d.ts +4 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +4 -0
- package/dist/router/index.js.map +1 -0
- package/dist/router/mount.d.ts +22 -0
- package/dist/router/mount.d.ts.map +1 -0
- package/dist/router/mount.js +27 -0
- package/dist/router/mount.js.map +1 -0
- package/dist/router/router.d.ts +29 -0
- package/dist/router/router.d.ts.map +1 -0
- package/dist/router/router.js +44 -0
- package/dist/router/router.js.map +1 -0
- package/dist/session.js.map +1 -1
- package/dist/ssr/index.d.ts +2 -0
- package/dist/ssr/index.d.ts.map +1 -0
- package/dist/ssr/index.js +2 -0
- package/dist/ssr/index.js.map +1 -0
- package/dist/ssr/render.d.ts +18 -0
- package/dist/ssr/render.d.ts.map +1 -0
- package/dist/ssr/render.js +96 -0
- package/dist/ssr/render.js.map +1 -0
- package/package.json +15 -7
package/dist/react/provider.js
CHANGED
|
@@ -4,9 +4,12 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
|
4
4
|
import { createContext, useContext, useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore, } from "react";
|
|
5
5
|
import { createAgentUiRuntime } from "./agent-ui/runtime.js";
|
|
6
6
|
import { SyncBridge } from "./sync-bridge.js";
|
|
7
|
-
import { invalidateForEvents,
|
|
7
|
+
import { invalidateForEvents, invalidateForReset, readTagsOf, } from "./sync.js";
|
|
8
8
|
const FrameworkContext = createContext(null);
|
|
9
9
|
const SIGNED_OUT = { status: "signed-out" };
|
|
10
|
+
/** A server render does not know the session yet, and hydration must render what it rendered. */
|
|
11
|
+
const LOADING = { status: "loading" };
|
|
12
|
+
const serverSession = () => LOADING;
|
|
10
13
|
const NO_SESSION = {
|
|
11
14
|
current: () => SIGNED_OUT,
|
|
12
15
|
subscribe: () => () => { },
|
|
@@ -14,10 +17,16 @@ const NO_SESSION = {
|
|
|
14
17
|
function identityOf(session) {
|
|
15
18
|
return session.status === "signed-in" ? `user:${session.user.id}` : session.status;
|
|
16
19
|
}
|
|
20
|
+
function routerQueryClient(router) {
|
|
21
|
+
const context = router?.options.context;
|
|
22
|
+
if (typeof context !== "object" || context === null || !("queryClient" in context))
|
|
23
|
+
return undefined;
|
|
24
|
+
return context.queryClient instanceof QueryClient ? context.queryClient : undefined;
|
|
25
|
+
}
|
|
17
26
|
/** Counts changes of the signed-in user after the first settled answer. */
|
|
18
27
|
function useSettledIdentityEpoch(auth) {
|
|
19
28
|
const source = auth ?? NO_SESSION;
|
|
20
|
-
const identity = identityOf(useSyncExternalStore(source.subscribe, source.current));
|
|
29
|
+
const identity = identityOf(useSyncExternalStore(source.subscribe, source.current, serverSession));
|
|
21
30
|
const [tracked, setTracked] = useState({ settled: undefined, epoch: 0 });
|
|
22
31
|
if (identity !== "loading" && tracked.settled !== identity) {
|
|
23
32
|
setTracked({
|
|
@@ -33,12 +42,12 @@ export function useIdentityEpoch() {
|
|
|
33
42
|
return useContext(IdentityEpochContext);
|
|
34
43
|
}
|
|
35
44
|
export function FrameworkProvider({ client, queryClient, sync = true, router, auth, children, }) {
|
|
36
|
-
const [ownQueryClient] = useState(() => queryClient ?? new QueryClient());
|
|
45
|
+
const [ownQueryClient] = useState(() => queryClient ?? routerQueryClient(router) ?? new QueryClient());
|
|
37
46
|
const [value] = useState(() => {
|
|
38
47
|
const bridge = new SyncBridge();
|
|
39
48
|
return {
|
|
40
49
|
client,
|
|
41
|
-
readTags:
|
|
50
|
+
readTags: readTagsOf(ownQueryClient),
|
|
42
51
|
agentUi: createAgentUiRuntime({ client, queryClient: ownQueryClient, router }),
|
|
43
52
|
auth,
|
|
44
53
|
sync: bridge,
|
|
@@ -52,9 +61,9 @@ export function FrameworkProvider({ client, queryClient, sync = true, router, au
|
|
|
52
61
|
return;
|
|
53
62
|
appliedEpoch.current = epoch;
|
|
54
63
|
void ownQueryClient.resetQueries();
|
|
64
|
+
void router?.invalidate();
|
|
55
65
|
value.agentUi.reset();
|
|
56
|
-
}, [epoch, ownQueryClient, value]);
|
|
57
|
-
useEffect(() => value.readTags.attach(ownQueryClient), [value, ownQueryClient]);
|
|
66
|
+
}, [epoch, ownQueryClient, router, value]);
|
|
58
67
|
useEffect(() => value.agentUi.start(), [value]);
|
|
59
68
|
useEffect(() => {
|
|
60
69
|
if (!import.meta.env.DEV)
|
|
@@ -72,23 +81,62 @@ export function FrameworkProvider({ client, queryClient, sync = true, router, au
|
|
|
72
81
|
}, [value]);
|
|
73
82
|
return (_jsx(FrameworkContext.Provider, { value: value, children: _jsx(IdentityEpochContext.Provider, { value: epoch, children: _jsxs(QueryClientProvider, { client: ownQueryClient, children: [sync && (_jsx(SyncConnection, { value: value, queryClient: ownQueryClient }, epoch)), children] }) }) }));
|
|
74
83
|
}
|
|
75
|
-
/**
|
|
84
|
+
/** The server refuses a signed-out visitor sync, unless it signs nobody in (`anonymous`). */
|
|
85
|
+
function syncsAs(session) {
|
|
86
|
+
return !session || session.status === "signed-in" || session.status === "anonymous";
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* One sync connection; remounted with a fresh cursor whenever the signed-in user changes. It
|
|
90
|
+
* runs while the session can sync, and after the server refuses it, until the session changes.
|
|
91
|
+
*/
|
|
76
92
|
function SyncConnection({ value, queryClient, }) {
|
|
93
|
+
// Data loaded outside action queries (plugin calls, the auth client) has no cursor but is read after this render.
|
|
94
|
+
const [loadedAfter] = useState(() => value.readTags.latest());
|
|
77
95
|
useEffect(() => {
|
|
78
|
-
const { client } = value;
|
|
96
|
+
const { client, auth } = value;
|
|
97
|
+
let refused;
|
|
79
98
|
const syncClient = createSyncClient({
|
|
80
99
|
request: (path, init) => client.request(path, init),
|
|
81
100
|
basePath: client.basePath,
|
|
82
101
|
clientId: client.clientId,
|
|
102
|
+
startAt: () => value.readTags.position(loadedAfter),
|
|
83
103
|
onEvents: (events) => {
|
|
84
104
|
void invalidateForEvents(queryClient, value.readTags, events);
|
|
85
105
|
value.bridge.emit(events);
|
|
86
106
|
},
|
|
107
|
+
onReset: (cursor) => {
|
|
108
|
+
void invalidateForReset(queryClient, value.readTags, cursor);
|
|
109
|
+
value.bridge.resumed();
|
|
110
|
+
},
|
|
87
111
|
isHidden: () => globalThis.document?.visibilityState === "hidden",
|
|
88
112
|
channels: [value.agentUi],
|
|
89
113
|
onNegotiated: (transport) => value.bridge.negotiated(transport),
|
|
90
114
|
onResume: () => value.bridge.resumed(),
|
|
115
|
+
...(auth
|
|
116
|
+
? {
|
|
117
|
+
onUnauthorized: () => {
|
|
118
|
+
refused = auth.current();
|
|
119
|
+
syncClient.stop();
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
: {}),
|
|
91
123
|
});
|
|
124
|
+
let settled;
|
|
125
|
+
const follow = () => {
|
|
126
|
+
const session = auth?.current();
|
|
127
|
+
if (session?.status === "loading")
|
|
128
|
+
return;
|
|
129
|
+
if (session !== refused)
|
|
130
|
+
refused = undefined;
|
|
131
|
+
if (session)
|
|
132
|
+
settled ??= identityOf(session);
|
|
133
|
+
// The provider remounts this connection for another user.
|
|
134
|
+
const anotherUser = session !== undefined && identityOf(session) !== settled;
|
|
135
|
+
if (syncsAs(session) && refused === undefined && !anotherUser)
|
|
136
|
+
syncClient.start();
|
|
137
|
+
else
|
|
138
|
+
syncClient.stop();
|
|
139
|
+
};
|
|
92
140
|
const reconnectWhenVisible = () => {
|
|
93
141
|
if (globalThis.document?.visibilityState !== "hidden")
|
|
94
142
|
syncClient.reconnectNow();
|
|
@@ -97,14 +145,16 @@ function SyncConnection({ value, queryClient, }) {
|
|
|
97
145
|
globalThis.addEventListener?.("online", reconnect);
|
|
98
146
|
globalThis.document?.addEventListener("visibilitychange", reconnectWhenVisible);
|
|
99
147
|
const detach = value.bridge.attach(syncClient);
|
|
100
|
-
|
|
148
|
+
const unfollow = auth?.subscribe(follow);
|
|
149
|
+
follow();
|
|
101
150
|
return () => {
|
|
151
|
+
unfollow?.();
|
|
102
152
|
globalThis.removeEventListener?.("online", reconnect);
|
|
103
153
|
globalThis.document?.removeEventListener("visibilitychange", reconnectWhenVisible);
|
|
104
154
|
syncClient.stop();
|
|
105
155
|
detach();
|
|
106
156
|
};
|
|
107
|
-
}, [value, queryClient]);
|
|
157
|
+
}, [value, queryClient, loadedAfter]);
|
|
108
158
|
return null;
|
|
109
159
|
}
|
|
110
160
|
export function useFramework() {
|
|
@@ -121,6 +171,6 @@ export function useSession() {
|
|
|
121
171
|
const { auth } = useFramework();
|
|
122
172
|
if (!auth)
|
|
123
173
|
throw new Error("useSession needs <FrameworkProvider auth={...}>");
|
|
124
|
-
return useSyncExternalStore(auth.subscribe, auth.current);
|
|
174
|
+
return useSyncExternalStore(auth.subscribe, auth.current, serverSession);
|
|
125
175
|
}
|
|
126
176
|
//# sourceMappingURL=provider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/react/provider.tsx"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/react/provider.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { AgentUiClient } from \"@parlor/agent-ui/client\";\nimport { createSyncClient, type SyncClient } from \"@parlor/sync/client\";\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport type { AnyRouter } from \"@tanstack/react-router\";\nimport {\n createContext,\n type ReactNode,\n useContext,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\n\nimport type { ActionClient } from \"../action-client.js\";\nimport type { RegisteredActions } from \"../register.js\";\nimport type { FrameworkAuth, SessionSnapshot, SessionSource } from \"../session.js\";\nimport { createAgentUiRuntime } from \"./agent-ui/runtime.js\";\nimport { type FrameworkSync, SyncBridge } from \"./sync-bridge.js\";\nimport {\n invalidateForEvents,\n invalidateForReset,\n type ReadTagRegistry,\n readTagsOf,\n} from \"./sync.js\";\n\ninterface FrameworkContextValue {\n readonly client: ActionClient<RegisteredActions>;\n readonly readTags: ReadTagRegistry;\n readonly agentUi: AgentUiClient;\n readonly auth: FrameworkAuth | undefined;\n readonly sync: FrameworkSync;\n readonly bridge: SyncBridge;\n}\n\nconst FrameworkContext = createContext<FrameworkContextValue | null>(null);\n\nexport interface FrameworkProviderProps {\n readonly client: ActionClient<RegisteredActions>;\n /** Defaults to the router's `context.queryClient`, so loaders and hooks share one cache. */\n readonly queryClient?: QueryClient | undefined;\n readonly sync?: boolean | undefined;\n readonly router?: AnyRouter | undefined;\n /**\n * When the signed-in user changes, cached queries reset and sync reconnects as them. Sync\n * waits for a signed-in or `anonymous` session, and after the server refuses it, for the\n * session to change. An auth client that can also sign (`device`) and pair hosts\n * (`authorizeHost`) gives the chat both.\n */\n readonly auth?: FrameworkAuth | undefined;\n readonly children: ReactNode;\n}\n\nconst SIGNED_OUT: SessionSnapshot = { status: \"signed-out\" };\n\n/** A server render does not know the session yet, and hydration must render what it rendered. */\nconst LOADING: SessionSnapshot = { status: \"loading\" };\nconst serverSession = () => LOADING;\n\nconst NO_SESSION: SessionSource = {\n current: () => SIGNED_OUT,\n subscribe: () => () => {},\n};\n\nfunction identityOf(session: SessionSnapshot): string {\n return session.status === \"signed-in\" ? `user:${session.user.id}` : session.status;\n}\n\nfunction routerQueryClient(router: AnyRouter | undefined): QueryClient | undefined {\n const context: unknown = router?.options.context;\n if (typeof context !== \"object\" || context === null || !(\"queryClient\" in context))\n return undefined;\n return context.queryClient instanceof QueryClient ? context.queryClient : undefined;\n}\n\n/** Counts changes of the signed-in user after the first settled answer. */\nfunction useSettledIdentityEpoch(auth: SessionSource | undefined): number {\n const source = auth ?? NO_SESSION;\n const identity = identityOf(\n useSyncExternalStore(source.subscribe, source.current, serverSession),\n );\n const [tracked, setTracked] = useState<{\n readonly settled: string | undefined;\n readonly epoch: number;\n }>({ settled: undefined, epoch: 0 });\n if (identity !== \"loading\" && tracked.settled !== identity) {\n setTracked({\n settled: identity,\n epoch: tracked.settled === undefined ? 0 : tracked.epoch + 1,\n });\n }\n return tracked.epoch;\n}\n\nconst IdentityEpochContext = createContext(0);\n\n/** Changes whenever the signed-in user does; state kept per user is keyed on it. */\nexport function useIdentityEpoch(): number {\n return useContext(IdentityEpochContext);\n}\n\nexport function FrameworkProvider({\n client,\n queryClient,\n sync = true,\n router,\n auth,\n children,\n}: FrameworkProviderProps) {\n const [ownQueryClient] = useState(\n () => queryClient ?? routerQueryClient(router) ?? new QueryClient(),\n );\n const [value] = useState<FrameworkContextValue>(() => {\n const bridge = new SyncBridge();\n return {\n client,\n readTags: readTagsOf(ownQueryClient),\n agentUi: createAgentUiRuntime({ client, queryClient: ownQueryClient, router }),\n auth,\n sync: bridge,\n bridge,\n };\n });\n const epoch = useSettledIdentityEpoch(auth);\n const appliedEpoch = useRef(epoch);\n\n useLayoutEffect(() => {\n if (appliedEpoch.current === epoch) return;\n appliedEpoch.current = epoch;\n void ownQueryClient.resetQueries();\n void router?.invalidate();\n value.agentUi.reset();\n }, [epoch, ownQueryClient, router, value]);\n\n useEffect(() => value.agentUi.start(), [value]);\n\n useEffect(() => {\n if (!import.meta.env.DEV) return;\n let uninstall: (() => void) | undefined;\n let active = true;\n void import(\"./agent-ui/dev.js\").then(({ installAgentUiDevtools }) => {\n if (active)\n uninstall = installAgentUiDevtools(value.agentUi, value.client.clientId);\n });\n return () => {\n active = false;\n uninstall?.();\n };\n }, [value]);\n\n return (\n <FrameworkContext.Provider value={value}>\n <IdentityEpochContext.Provider value={epoch}>\n <QueryClientProvider client={ownQueryClient}>\n {sync && (\n <SyncConnection key={epoch} value={value} queryClient={ownQueryClient} />\n )}\n {children}\n </QueryClientProvider>\n </IdentityEpochContext.Provider>\n </FrameworkContext.Provider>\n );\n}\n\n/** The server refuses a signed-out visitor sync, unless it signs nobody in (`anonymous`). */\nfunction syncsAs(session: SessionSnapshot | undefined): boolean {\n return !session || session.status === \"signed-in\" || session.status === \"anonymous\";\n}\n\n/**\n * One sync connection; remounted with a fresh cursor whenever the signed-in user changes. It\n * runs while the session can sync, and after the server refuses it, until the session changes.\n */\nfunction SyncConnection({\n value,\n queryClient,\n}: {\n readonly value: FrameworkContextValue;\n readonly queryClient: QueryClient;\n}) {\n // Data loaded outside action queries (plugin calls, the auth client) has no cursor but is read after this render.\n const [loadedAfter] = useState(() => value.readTags.latest());\n useEffect(() => {\n const { client, auth } = value;\n let refused: SessionSnapshot | undefined;\n const syncClient: SyncClient = createSyncClient({\n request: (path, init) => client.request(path, init),\n basePath: client.basePath,\n clientId: client.clientId,\n startAt: () => value.readTags.position(loadedAfter),\n onEvents: (events) => {\n void invalidateForEvents(queryClient, value.readTags, events);\n value.bridge.emit(events);\n },\n onReset: (cursor) => {\n void invalidateForReset(queryClient, value.readTags, cursor);\n value.bridge.resumed();\n },\n isHidden: () => globalThis.document?.visibilityState === \"hidden\",\n channels: [value.agentUi],\n onNegotiated: (transport) => value.bridge.negotiated(transport),\n onResume: () => value.bridge.resumed(),\n ...(auth\n ? {\n onUnauthorized: () => {\n refused = auth.current();\n syncClient.stop();\n },\n }\n : {}),\n });\n let settled: string | undefined;\n const follow = () => {\n const session = auth?.current();\n if (session?.status === \"loading\") return;\n if (session !== refused) refused = undefined;\n if (session) settled ??= identityOf(session);\n // The provider remounts this connection for another user.\n const anotherUser = session !== undefined && identityOf(session) !== settled;\n if (syncsAs(session) && refused === undefined && !anotherUser) syncClient.start();\n else syncClient.stop();\n };\n const reconnectWhenVisible = () => {\n if (globalThis.document?.visibilityState !== \"hidden\") syncClient.reconnectNow();\n };\n const reconnect = () => syncClient.reconnectNow();\n globalThis.addEventListener?.(\"online\", reconnect);\n globalThis.document?.addEventListener(\"visibilitychange\", reconnectWhenVisible);\n const detach = value.bridge.attach(syncClient);\n const unfollow = auth?.subscribe(follow);\n follow();\n return () => {\n unfollow?.();\n globalThis.removeEventListener?.(\"online\", reconnect);\n globalThis.document?.removeEventListener(\"visibilitychange\", reconnectWhenVisible);\n syncClient.stop();\n detach();\n };\n }, [value, queryClient, loadedAfter]);\n return null;\n}\n\nexport function useFramework(): FrameworkContextValue {\n const value = useContext(FrameworkContext);\n if (!value) throw new Error(\"useActionClient must be used inside <FrameworkProvider>\");\n return value;\n}\n\nexport function useActionClient(): ActionClient<RegisteredActions> {\n return useFramework().client;\n}\n\n/** The session of the `auth` given to `FrameworkProvider`. */\nexport function useSession(): SessionSnapshot {\n const { auth } = useFramework();\n if (!auth) throw new Error(\"useSession needs <FrameworkProvider auth={...}>\");\n return useSyncExternalStore(auth.subscribe, auth.current, serverSession);\n}\n"]}
|
package/dist/react/retry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/react/retry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAwB,MAAM,cAAc,CAAC;AAKjE,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B,MAAM,wBAAwB,GAA+C;IAC3E,SAAS,EAAE,KAAK;IAChB,aAAa,EAAE,KAAK;IACpB,QAAQ,EAAE,KAAK;IACf,oBAAoB,EAAE,IAAI;IAC1B,wBAAwB,EAAE,IAAI;IAC9B,eAAe,EAAE,KAAK;IACtB,SAAS,EAAE,KAAK;IAChB,QAAQ,EAAE,IAAI;CACf,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,IAAI,GAAU,eAAe;IACvD,OAAO,CAAC,YAAoB,EAAE,KAAY,EAAW,EAAE;QACrD,IAAI,KAAK,YAAY,WAAW,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC,IAAI,CAAC;YACvE,OAAO,KAAK,CAAC;QACf,IAAI,OAAO,IAAI,KAAK,UAAU;YAAE,OAAO,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QACjE,OAAO,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI,YAAY,GAAG,IAAI,CAAC,CAAC;IAClE,CAAC,CAAC;AACJ,CAAC"}
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/react/retry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAwB,MAAM,cAAc,CAAC;AAKjE,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B,MAAM,wBAAwB,GAA+C;IAC3E,SAAS,EAAE,KAAK;IAChB,aAAa,EAAE,KAAK;IACpB,QAAQ,EAAE,KAAK;IACf,oBAAoB,EAAE,IAAI;IAC1B,wBAAwB,EAAE,IAAI;IAC9B,eAAe,EAAE,KAAK;IACtB,SAAS,EAAE,KAAK;IAChB,QAAQ,EAAE,IAAI;CACf,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,IAAI,GAAU,eAAe;IACvD,OAAO,CAAC,YAAoB,EAAE,KAAY,EAAW,EAAE;QACrD,IAAI,KAAK,YAAY,WAAW,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC,IAAI,CAAC;YACvE,OAAO,KAAK,CAAC;QACf,IAAI,OAAO,IAAI,KAAK,UAAU;YAAE,OAAO,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QACjE,OAAO,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI,YAAY,GAAG,IAAI,CAAC,CAAC;IAClE,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { ActionError, type ActionErrorCode } from \"@parlor/core\";\nimport type { UseQueryOptions } from \"@tanstack/react-query\";\n\ntype Retry = NonNullable<UseQueryOptions[\"retry\"]>;\n\nconst DEFAULT_RETRIES = 3;\n\nconst RETRY_CAN_CHANGE_OUTCOME: Readonly<Record<ActionErrorCode, boolean>> = {\n not_found: false,\n invalid_input: false,\n declined: false,\n confirmation_timeout: true,\n confirmation_unavailable: true,\n unauthenticated: false,\n forbidden: false,\n internal: true,\n};\n\nexport function actionRetry(base: Retry = DEFAULT_RETRIES) {\n return (failureCount: number, error: Error): boolean => {\n if (error instanceof ActionError && !RETRY_CAN_CHANGE_OUTCOME[error.code])\n return false;\n if (typeof base === \"function\") return base(failureCount, error);\n return base === true || (base !== false && failureCount < base);\n };\n}\n"]}
|
|
@@ -3,7 +3,10 @@ import type { SyncClient } from "@parlor/sync/client";
|
|
|
3
3
|
export interface FrameworkSync {
|
|
4
4
|
addChannel(channel: ClientChannel): () => void;
|
|
5
5
|
onEvents(listener: (events: readonly SyncEvent[]) => void): () => void;
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* The connection came back after a failure, or the server could not replay every event
|
|
8
|
+
* this client missed; reread what could have changed unseen.
|
|
9
|
+
*/
|
|
7
10
|
onResume(listener: () => void): () => void;
|
|
8
11
|
/** What the server negotiated; `undefined` until it has, and while no client is attached. */
|
|
9
12
|
transport(): "sse" | "poll" | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-bridge.d.ts","sourceRoot":"","sources":["../../src/react/sync-bridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,IAAI,CAAC;IAC/C,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACvE
|
|
1
|
+
{"version":3,"file":"sync-bridge.d.ts","sourceRoot":"","sources":["../../src/react/sync-bridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,IAAI,CAAC;IAC/C,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACvE;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IAC3C,6FAA6F;IAC7F,SAAS,IAAI,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC;IACxC,2DAA2D;IAC3D,YAAY,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,KAAK,GAAG,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACzE;AAED,qBAAa,UAAW,YAAW,aAAa;;IAS9C,UAAU,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,IAAI,CAQ7C;IAED,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAGrE;IAED,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAGzC;IAED,SAAS,IAAI,KAAK,GAAG,MAAM,GAAG,SAAS,CAEtC;IAED,YAAY,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,KAAK,GAAG,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAGtE;IAED,UAAU,CAAC,SAAS,EAAE,KAAK,GAAG,MAAM,GAAG,IAAI,CAG1C;IAED,IAAI,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,GAAG,IAAI,CAEvC;IAED,OAAO,IAAI,IAAI,CAEd;IAED,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,IAAI,CAUrC;CAOF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-bridge.js","sourceRoot":"","sources":["../../src/react/sync-bridge.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sync-bridge.js","sourceRoot":"","sources":["../../src/react/sync-bridge.ts"],"names":[],"mappings":"AAiBA,MAAM,OAAO,UAAU;IACZ,SAAS,GAAG,IAAI,GAAG,EAAiB,CAAC;IACrC,UAAU,GAAG,IAAI,GAAG,EAA0C,CAAC;IAC/D,UAAU,GAAG,IAAI,GAAG,EAA6B,CAAC;IAClD,SAAS,GAAG,IAAI,GAAG,EAAc,CAAC;IAClC,YAAY,GAAG,IAAI,GAAG,EAAuC,CAAC;IACvE,OAAO,CAAyB;IAChC,UAAU,CAA6B;IAEvC,UAAU,CAAC,OAAsB;QAC/B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACpB,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC/B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;YACjC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAClC,CAAC,CAAC;IACJ,CAAC;IAED,QAAQ,CAAC,QAAgD;QACvD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC9B,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAChD,CAAC;IAED,QAAQ,CAAC,QAAoB;QAC3B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC7B,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/C,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,YAAY,CAAC,QAA6C;QACxD,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAChC,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAClD,CAAC;IAED,UAAU,CAAC,SAAyB;QAClC,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC;YAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;IACrE,CAAC;IAED,IAAI,CAAC,MAA4B;QAC/B,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC;YAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAChE,CAAC;IAED,OAAO;QACL,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC;YAAE,QAAQ,EAAE,CAAC;IACzD,CAAC;IAED,MAAM,CAAC,MAAkB;QACvB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,SAAS;YAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC1D,OAAO,GAAG,EAAE;YACV,IAAI,IAAI,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;gBAC5B,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;gBACzB,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;YAC9B,CAAC;YACD,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QAC1B,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,OAAsB;QAC1B,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;QACjC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IACjE,CAAC;CACF","sourcesContent":["import type { ClientChannel, SyncEvent } from \"@parlor/sync\";\nimport type { SyncClient } from \"@parlor/sync/client\";\n\nexport interface FrameworkSync {\n addChannel(channel: ClientChannel): () => void;\n onEvents(listener: (events: readonly SyncEvent[]) => void): () => void;\n /**\n * The connection came back after a failure, or the server could not replay every event\n * this client missed; reread what could have changed unseen.\n */\n onResume(listener: () => void): () => void;\n /** What the server negotiated; `undefined` until it has, and while no client is attached. */\n transport(): \"sse\" | \"poll\" | undefined;\n /** The server negotiated a transport, on every connect. */\n onNegotiated(listener: (transport: \"sse\" | \"poll\") => void): () => void;\n}\n\nexport class SyncBridge implements FrameworkSync {\n readonly #channels = new Set<ClientChannel>();\n readonly #listeners = new Set<(events: readonly SyncEvent[]) => void>();\n readonly #detachers = new Map<ClientChannel, () => void>();\n readonly #resumers = new Set<() => void>();\n readonly #negotiators = new Set<(transport: \"sse\" | \"poll\") => void>();\n #client: SyncClient | undefined;\n #transport: \"sse\" | \"poll\" | undefined;\n\n addChannel(channel: ClientChannel): () => void {\n this.#channels.add(channel);\n this.#join(channel);\n return () => {\n this.#channels.delete(channel);\n this.#detachers.get(channel)?.();\n this.#detachers.delete(channel);\n };\n }\n\n onEvents(listener: (events: readonly SyncEvent[]) => void): () => void {\n this.#listeners.add(listener);\n return () => this.#listeners.delete(listener);\n }\n\n onResume(listener: () => void): () => void {\n this.#resumers.add(listener);\n return () => this.#resumers.delete(listener);\n }\n\n transport(): \"sse\" | \"poll\" | undefined {\n return this.#transport;\n }\n\n onNegotiated(listener: (transport: \"sse\" | \"poll\") => void): () => void {\n this.#negotiators.add(listener);\n return () => this.#negotiators.delete(listener);\n }\n\n negotiated(transport: \"sse\" | \"poll\"): void {\n this.#transport = transport;\n for (const listener of [...this.#negotiators]) listener(transport);\n }\n\n emit(events: readonly SyncEvent[]): void {\n for (const listener of [...this.#listeners]) listener(events);\n }\n\n resumed(): void {\n for (const listener of [...this.#resumers]) listener();\n }\n\n attach(client: SyncClient): () => void {\n this.#client = client;\n for (const channel of this.#channels) this.#join(channel);\n return () => {\n if (this.#client === client) {\n this.#client = undefined;\n this.#transport = undefined;\n }\n this.#detachers.clear();\n };\n }\n\n #join(channel: ClientChannel): void {\n if (!this.#client) return;\n this.#detachers.get(channel)?.();\n this.#detachers.set(channel, this.#client.addChannel(channel));\n }\n}\n"]}
|
package/dist/react/sync.d.ts
CHANGED
|
@@ -1,12 +1,51 @@
|
|
|
1
1
|
import type { Tag } from "@parlor/core";
|
|
2
|
-
import type
|
|
2
|
+
import { type SyncCursor, type SyncEvent } from "@parlor/sync";
|
|
3
3
|
import type { QueryClient } from "@tanstack/react-query";
|
|
4
|
+
import type { ActionEnvelope } from "../action-client.js";
|
|
5
|
+
interface Read {
|
|
6
|
+
readonly tags: readonly Tag[];
|
|
7
|
+
/** Every event up to it is in the data; undefined when the server did not say. */
|
|
8
|
+
readonly cursor: SyncCursor | undefined;
|
|
9
|
+
}
|
|
10
|
+
/** One recorded read: its query hash, read tags and the encoded cursor its data reflects. */
|
|
11
|
+
export type ReadEntry = readonly [string, readonly Tag[], string?];
|
|
12
|
+
/**
|
|
13
|
+
* What each action query read and how far into the sync log its data reaches, so an event
|
|
14
|
+
* invalidates only the queries whose data it is newer than.
|
|
15
|
+
*/
|
|
4
16
|
export declare class ReadTagRegistry {
|
|
5
17
|
#private;
|
|
6
18
|
attach(queryClient: QueryClient): () => void;
|
|
7
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Runs one fetch of a query and records what its answer read. An event that arrived while
|
|
21
|
+
* it was in flight and is newer than the answer invalidates the data once it lands.
|
|
22
|
+
*/
|
|
23
|
+
read<T>(queryHash: string, fetch: () => Promise<ActionEnvelope<T>>): Promise<T>;
|
|
24
|
+
record(queryHash: string, tags: readonly Tag[], cursor?: string): Read;
|
|
8
25
|
tagsFor(queryHash: string): readonly Tag[] | undefined;
|
|
26
|
+
/** Every query's reads, to carry a server-rendered cache to the browser. */
|
|
27
|
+
entries(): ReadEntry[];
|
|
28
|
+
/** Sync events that arrived; fetches in flight keep them to check their answers against. */
|
|
29
|
+
observe(events: readonly SyncEvent[]): void;
|
|
9
30
|
affected(events: readonly SyncEvent[]): Set<string>;
|
|
31
|
+
/**
|
|
32
|
+
* Sync was reset to `cursor` without the events before it: every query whose data may
|
|
33
|
+
* predate it, and fetches in flight, whose answers are checked when they land.
|
|
34
|
+
*/
|
|
35
|
+
reset(cursor: SyncCursor): Set<string>;
|
|
36
|
+
/** The newest cursor any answer or event has shown, encoded; every later read reflects it. */
|
|
37
|
+
latest(): string;
|
|
38
|
+
/**
|
|
39
|
+
* The oldest point of the sync log that data held or being fetched may predate, and no
|
|
40
|
+
* later than `notAfter`, encoded. A stream that starts there misses nothing and replays
|
|
41
|
+
* no event that data already shows.
|
|
42
|
+
*/
|
|
43
|
+
position(notAfter?: string): string;
|
|
10
44
|
}
|
|
45
|
+
/** The one registry of `queryClient`, shared by hooks, loaders and sync; it lives as long as the client. */
|
|
46
|
+
export declare function readTagsOf(queryClient: QueryClient): ReadTagRegistry;
|
|
47
|
+
/** Invalidates every query whose data may predate the cursor sync was reset to. */
|
|
48
|
+
export declare function invalidateForReset(queryClient: QueryClient, registry: ReadTagRegistry, cursor: string): Promise<void>;
|
|
11
49
|
export declare function invalidateForEvents(queryClient: QueryClient, registry: ReadTagRegistry, events: readonly SyncEvent[]): Promise<void>;
|
|
50
|
+
export {};
|
|
12
51
|
//# sourceMappingURL=sync.d.ts.map
|
package/dist/react/sync.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/react/sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAExC,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/react/sync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAExC,OAAO,EAML,KAAK,UAAU,EACf,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG1D,UAAU,IAAI;IACZ,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,EAAE,CAAC;IAC9B,kFAAkF;IAClF,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC;CACzC;AAUD,6FAA6F;AAC7F,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;AAkBnE;;;GAGG;AACH,qBAAa,eAAe;;IAM1B,MAAM,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,IAAI,CAiB3C;IAED;;;OAGG;IACG,IAAI,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAiBpF;IAED,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAQrE;IAED,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,EAAE,GAAG,SAAS,CAErD;IAED,4EAA4E;IAC5E,OAAO,IAAI,SAAS,EAAE,CAIrB;IAED,4FAA4F;IAC5F,OAAO,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,GAAG,IAAI,CAG1C;IAED,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAQlD;IAED;;;OAGG;IACH,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,CAMrC;IAED,8FAA8F;IAC9F,MAAM,IAAI,MAAM,CAEf;IAED;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAUlC;CACF;AAID,4GAA4G;AAC5G,wBAAgB,UAAU,CAAC,WAAW,EAAE,WAAW,GAAG,eAAe,CAQpE;AAED,mFAAmF;AACnF,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,eAAe,EACzB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,eAAe,EACzB,MAAM,EAAE,SAAS,SAAS,EAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CAGf"}
|
package/dist/react/sync.js
CHANGED
|
@@ -1,31 +1,149 @@
|
|
|
1
1
|
import { tagsOverlap } from "@parlor/core";
|
|
2
|
+
import { compareCursors, cursorOf, decodeCursor, encodeCursor, INITIAL_CURSOR, } from "@parlor/sync";
|
|
2
3
|
import { ACTION_QUERY_PREFIX } from "../query-keys.js";
|
|
4
|
+
const encoded = (cursor) => compareCursors(cursor, INITIAL_CURSOR) === 0 ? "" : encodeCursor(cursor);
|
|
5
|
+
const later = (a, b) => (compareCursors(a, b) > 0 ? a : b);
|
|
6
|
+
const newer = (event, cursor) => cursor === undefined || compareCursors(cursorOf(event), cursor) > 0;
|
|
7
|
+
const overlaps = (reads, event) => reads.some((read) => event.tags.some((write) => tagsOverlap(read, write)));
|
|
8
|
+
const predates = (read, reset) => reset !== undefined &&
|
|
9
|
+
read.tags.length > 0 &&
|
|
10
|
+
(read.cursor === undefined || compareCursors(read.cursor, reset) < 0);
|
|
11
|
+
/**
|
|
12
|
+
* What each action query read and how far into the sync log its data reaches, so an event
|
|
13
|
+
* invalidates only the queries whose data it is newer than.
|
|
14
|
+
*/
|
|
3
15
|
export class ReadTagRegistry {
|
|
4
|
-
#
|
|
16
|
+
#reads = new Map();
|
|
17
|
+
#fetches = new Set();
|
|
18
|
+
#outdated = new Set();
|
|
19
|
+
#seen = INITIAL_CURSOR;
|
|
5
20
|
attach(queryClient) {
|
|
6
21
|
return queryClient.getQueryCache().subscribe((event) => {
|
|
7
|
-
|
|
8
|
-
|
|
22
|
+
const hash = event.query.queryHash;
|
|
23
|
+
if (event.type === "removed") {
|
|
24
|
+
this.#reads.delete(hash);
|
|
25
|
+
this.#outdated.delete(hash);
|
|
26
|
+
}
|
|
27
|
+
else if (event.type === "updated" &&
|
|
28
|
+
event.action.type === "success" &&
|
|
29
|
+
this.#outdated.delete(hash)) {
|
|
30
|
+
void queryClient.invalidateQueries({
|
|
31
|
+
queryKey: event.query.queryKey,
|
|
32
|
+
exact: true,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
9
35
|
});
|
|
10
36
|
}
|
|
11
|
-
|
|
12
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Runs one fetch of a query and records what its answer read. An event that arrived while
|
|
39
|
+
* it was in flight and is newer than the answer invalidates the data once it lands.
|
|
40
|
+
*/
|
|
41
|
+
async read(queryHash, fetch) {
|
|
42
|
+
const pending = { floor: this.#seen, arrived: [], reset: undefined };
|
|
43
|
+
this.#fetches.add(pending);
|
|
44
|
+
try {
|
|
45
|
+
const { data, tags, cursor } = await fetch();
|
|
46
|
+
const read = this.record(queryHash, tags, cursor);
|
|
47
|
+
if (predates(read, pending.reset) ||
|
|
48
|
+
pending.arrived.some((event) => newer(event, read.cursor) && overlaps(tags, event)))
|
|
49
|
+
this.#outdated.add(queryHash);
|
|
50
|
+
return data;
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
this.#fetches.delete(pending);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
record(queryHash, tags, cursor) {
|
|
57
|
+
const read = {
|
|
58
|
+
tags,
|
|
59
|
+
cursor: cursor === undefined ? undefined : decodeCursor(cursor),
|
|
60
|
+
};
|
|
61
|
+
this.#reads.set(queryHash, read);
|
|
62
|
+
if (read.cursor)
|
|
63
|
+
this.#seen = later(read.cursor, this.#seen);
|
|
64
|
+
return read;
|
|
13
65
|
}
|
|
14
66
|
tagsFor(queryHash) {
|
|
15
|
-
return this.#
|
|
67
|
+
return this.#reads.get(queryHash)?.tags;
|
|
68
|
+
}
|
|
69
|
+
/** Every query's reads, to carry a server-rendered cache to the browser. */
|
|
70
|
+
entries() {
|
|
71
|
+
return [...this.#reads].map(([hash, { tags, cursor }]) => cursor === undefined ? [hash, tags] : [hash, tags, encodeCursor(cursor)]);
|
|
72
|
+
}
|
|
73
|
+
/** Sync events that arrived; fetches in flight keep them to check their answers against. */
|
|
74
|
+
observe(events) {
|
|
75
|
+
for (const event of events)
|
|
76
|
+
this.#seen = later(cursorOf(event), this.#seen);
|
|
77
|
+
for (const fetch of this.#fetches)
|
|
78
|
+
fetch.arrived.push(...events);
|
|
16
79
|
}
|
|
17
80
|
affected(events) {
|
|
18
81
|
const hashes = new Set();
|
|
19
|
-
for (const [hash,
|
|
20
|
-
if (
|
|
82
|
+
for (const [hash, { tags, cursor }] of this.#reads) {
|
|
83
|
+
if (events.some((event) => newer(event, cursor) && overlaps(tags, event))) {
|
|
21
84
|
hashes.add(hash);
|
|
22
85
|
}
|
|
23
86
|
}
|
|
24
87
|
return hashes;
|
|
25
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Sync was reset to `cursor` without the events before it: every query whose data may
|
|
91
|
+
* predate it, and fetches in flight, whose answers are checked when they land.
|
|
92
|
+
*/
|
|
93
|
+
reset(cursor) {
|
|
94
|
+
this.#seen = later(cursor, this.#seen);
|
|
95
|
+
for (const fetch of this.#fetches)
|
|
96
|
+
fetch.reset = cursor;
|
|
97
|
+
const hashes = new Set();
|
|
98
|
+
for (const [hash, read] of this.#reads)
|
|
99
|
+
if (predates(read, cursor))
|
|
100
|
+
hashes.add(hash);
|
|
101
|
+
return hashes;
|
|
102
|
+
}
|
|
103
|
+
/** The newest cursor any answer or event has shown, encoded; every later read reflects it. */
|
|
104
|
+
latest() {
|
|
105
|
+
return encoded(this.#seen);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The oldest point of the sync log that data held or being fetched may predate, and no
|
|
109
|
+
* later than `notAfter`, encoded. A stream that starts there misses nothing and replays
|
|
110
|
+
* no event that data already shows.
|
|
111
|
+
*/
|
|
112
|
+
position(notAfter) {
|
|
113
|
+
let oldest = notAfter === undefined ? undefined : decodeCursor(notAfter);
|
|
114
|
+
const consider = (cursor) => {
|
|
115
|
+
if (oldest === undefined || compareCursors(cursor, oldest) < 0)
|
|
116
|
+
oldest = cursor;
|
|
117
|
+
};
|
|
118
|
+
for (const { tags, cursor } of this.#reads.values()) {
|
|
119
|
+
if (tags.length > 0)
|
|
120
|
+
consider(cursor ?? INITIAL_CURSOR);
|
|
121
|
+
}
|
|
122
|
+
for (const { floor } of this.#fetches)
|
|
123
|
+
consider(floor);
|
|
124
|
+
return encoded(oldest ?? this.#seen);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const registries = new WeakMap();
|
|
128
|
+
/** The one registry of `queryClient`, shared by hooks, loaders and sync; it lives as long as the client. */
|
|
129
|
+
export function readTagsOf(queryClient) {
|
|
130
|
+
let registry = registries.get(queryClient);
|
|
131
|
+
if (!registry) {
|
|
132
|
+
registry = new ReadTagRegistry();
|
|
133
|
+
registry.attach(queryClient);
|
|
134
|
+
registries.set(queryClient, registry);
|
|
135
|
+
}
|
|
136
|
+
return registry;
|
|
137
|
+
}
|
|
138
|
+
/** Invalidates every query whose data may predate the cursor sync was reset to. */
|
|
139
|
+
export function invalidateForReset(queryClient, registry, cursor) {
|
|
140
|
+
return invalidateHashes(queryClient, registry.reset(decodeCursor(cursor)));
|
|
26
141
|
}
|
|
27
142
|
export function invalidateForEvents(queryClient, registry, events) {
|
|
28
|
-
|
|
143
|
+
registry.observe(events);
|
|
144
|
+
return invalidateHashes(queryClient, registry.affected(events));
|
|
145
|
+
}
|
|
146
|
+
function invalidateHashes(queryClient, hashes) {
|
|
29
147
|
if (hashes.size === 0)
|
|
30
148
|
return Promise.resolve();
|
|
31
149
|
return queryClient.invalidateQueries({
|
package/dist/react/sync.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/react/sync.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAI3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD,MAAM,OAAO,eAAe;IACjB,KAAK,GAAG,IAAI,GAAG,EAA0B,CAAC;IAEnD,MAAM,CAAC,WAAwB;QAC7B,OAAO,WAAW,CAAC,aAAa,EAAE,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE;YACrD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;gBAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACzE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,SAAiB,EAAE,IAAoB;QAC5C,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,OAAO,CAAC,SAAiB;QACvB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACnC,CAAC;IAED,QAAQ,CAAC,MAA4B;QACnC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;QACjC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACvC,IACE,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAClB,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CACrE,EACD,CAAC;gBACD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AAED,MAAM,UAAU,mBAAmB,CACjC,WAAwB,EACxB,QAAyB,EACzB,MAA4B;IAE5B,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzC,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAChD,OAAO,WAAW,CAAC,iBAAiB,CAAC;QACnC,QAAQ,EAAE,mBAAmB;QAC7B,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC;KAClD,CAAC,CAAC;AACL,CAAC"}
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/react/sync.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Tag } from \"@parlor/core\";\nimport { tagsOverlap } from \"@parlor/core\";\nimport {\n compareCursors,\n cursorOf,\n decodeCursor,\n encodeCursor,\n INITIAL_CURSOR,\n type SyncCursor,\n type SyncEvent,\n} from \"@parlor/sync\";\nimport type { QueryClient } from \"@tanstack/react-query\";\n\nimport type { ActionEnvelope } from \"../action-client.js\";\nimport { ACTION_QUERY_PREFIX } from \"../query-keys.js\";\n\ninterface Read {\n readonly tags: readonly Tag[];\n /** Every event up to it is in the data; undefined when the server did not say. */\n readonly cursor: SyncCursor | undefined;\n}\n\ninterface Fetch {\n /** Its answer reflects at least this: the newest cursor seen before it was sent. */\n readonly floor: SyncCursor;\n readonly arrived: SyncEvent[];\n /** The newest reset while it was in flight: events up to it were never delivered. */\n reset: SyncCursor | undefined;\n}\n\n/** One recorded read: its query hash, read tags and the encoded cursor its data reflects. */\nexport type ReadEntry = readonly [string, readonly Tag[], string?];\n\nconst encoded = (cursor: SyncCursor) =>\n compareCursors(cursor, INITIAL_CURSOR) === 0 ? \"\" : encodeCursor(cursor);\n\nconst later = (a: SyncCursor, b: SyncCursor) => (compareCursors(a, b) > 0 ? a : b);\n\nconst newer = (event: SyncEvent, cursor: SyncCursor | undefined) =>\n cursor === undefined || compareCursors(cursorOf(event), cursor) > 0;\n\nconst overlaps = (reads: readonly Tag[], event: SyncEvent) =>\n reads.some((read) => event.tags.some((write) => tagsOverlap(read, write)));\n\nconst predates = (read: Read, reset: SyncCursor | undefined) =>\n reset !== undefined &&\n read.tags.length > 0 &&\n (read.cursor === undefined || compareCursors(read.cursor, reset) < 0);\n\n/**\n * What each action query read and how far into the sync log its data reaches, so an event\n * invalidates only the queries whose data it is newer than.\n */\nexport class ReadTagRegistry {\n readonly #reads = new Map<string, Read>();\n readonly #fetches = new Set<Fetch>();\n readonly #outdated = new Set<string>();\n #seen: SyncCursor = INITIAL_CURSOR;\n\n attach(queryClient: QueryClient): () => void {\n return queryClient.getQueryCache().subscribe((event) => {\n const hash = event.query.queryHash;\n if (event.type === \"removed\") {\n this.#reads.delete(hash);\n this.#outdated.delete(hash);\n } else if (\n event.type === \"updated\" &&\n event.action.type === \"success\" &&\n this.#outdated.delete(hash)\n ) {\n void queryClient.invalidateQueries({\n queryKey: event.query.queryKey,\n exact: true,\n });\n }\n });\n }\n\n /**\n * Runs one fetch of a query and records what its answer read. An event that arrived while\n * it was in flight and is newer than the answer invalidates the data once it lands.\n */\n async read<T>(queryHash: string, fetch: () => Promise<ActionEnvelope<T>>): Promise<T> {\n const pending: Fetch = { floor: this.#seen, arrived: [], reset: undefined };\n this.#fetches.add(pending);\n try {\n const { data, tags, cursor } = await fetch();\n const read = this.record(queryHash, tags, cursor);\n if (\n predates(read, pending.reset) ||\n pending.arrived.some(\n (event) => newer(event, read.cursor) && overlaps(tags, event),\n )\n )\n this.#outdated.add(queryHash);\n return data;\n } finally {\n this.#fetches.delete(pending);\n }\n }\n\n record(queryHash: string, tags: readonly Tag[], cursor?: string): Read {\n const read: Read = {\n tags,\n cursor: cursor === undefined ? undefined : decodeCursor(cursor),\n };\n this.#reads.set(queryHash, read);\n if (read.cursor) this.#seen = later(read.cursor, this.#seen);\n return read;\n }\n\n tagsFor(queryHash: string): readonly Tag[] | undefined {\n return this.#reads.get(queryHash)?.tags;\n }\n\n /** Every query's reads, to carry a server-rendered cache to the browser. */\n entries(): ReadEntry[] {\n return [...this.#reads].map(([hash, { tags, cursor }]) =>\n cursor === undefined ? [hash, tags] : [hash, tags, encodeCursor(cursor)],\n );\n }\n\n /** Sync events that arrived; fetches in flight keep them to check their answers against. */\n observe(events: readonly SyncEvent[]): void {\n for (const event of events) this.#seen = later(cursorOf(event), this.#seen);\n for (const fetch of this.#fetches) fetch.arrived.push(...events);\n }\n\n affected(events: readonly SyncEvent[]): Set<string> {\n const hashes = new Set<string>();\n for (const [hash, { tags, cursor }] of this.#reads) {\n if (events.some((event) => newer(event, cursor) && overlaps(tags, event))) {\n hashes.add(hash);\n }\n }\n return hashes;\n }\n\n /**\n * Sync was reset to `cursor` without the events before it: every query whose data may\n * predate it, and fetches in flight, whose answers are checked when they land.\n */\n reset(cursor: SyncCursor): Set<string> {\n this.#seen = later(cursor, this.#seen);\n for (const fetch of this.#fetches) fetch.reset = cursor;\n const hashes = new Set<string>();\n for (const [hash, read] of this.#reads) if (predates(read, cursor)) hashes.add(hash);\n return hashes;\n }\n\n /** The newest cursor any answer or event has shown, encoded; every later read reflects it. */\n latest(): string {\n return encoded(this.#seen);\n }\n\n /**\n * The oldest point of the sync log that data held or being fetched may predate, and no\n * later than `notAfter`, encoded. A stream that starts there misses nothing and replays\n * no event that data already shows.\n */\n position(notAfter?: string): string {\n let oldest = notAfter === undefined ? undefined : decodeCursor(notAfter);\n const consider = (cursor: SyncCursor) => {\n if (oldest === undefined || compareCursors(cursor, oldest) < 0) oldest = cursor;\n };\n for (const { tags, cursor } of this.#reads.values()) {\n if (tags.length > 0) consider(cursor ?? INITIAL_CURSOR);\n }\n for (const { floor } of this.#fetches) consider(floor);\n return encoded(oldest ?? this.#seen);\n }\n}\n\nconst registries = new WeakMap<QueryClient, ReadTagRegistry>();\n\n/** The one registry of `queryClient`, shared by hooks, loaders and sync; it lives as long as the client. */\nexport function readTagsOf(queryClient: QueryClient): ReadTagRegistry {\n let registry = registries.get(queryClient);\n if (!registry) {\n registry = new ReadTagRegistry();\n registry.attach(queryClient);\n registries.set(queryClient, registry);\n }\n return registry;\n}\n\n/** Invalidates every query whose data may predate the cursor sync was reset to. */\nexport function invalidateForReset(\n queryClient: QueryClient,\n registry: ReadTagRegistry,\n cursor: string,\n): Promise<void> {\n return invalidateHashes(queryClient, registry.reset(decodeCursor(cursor)));\n}\n\nexport function invalidateForEvents(\n queryClient: QueryClient,\n registry: ReadTagRegistry,\n events: readonly SyncEvent[],\n): Promise<void> {\n registry.observe(events);\n return invalidateHashes(queryClient, registry.affected(events));\n}\n\nfunction invalidateHashes(queryClient: QueryClient, hashes: Set<string>): Promise<void> {\n if (hashes.size === 0) return Promise.resolve();\n return queryClient.invalidateQueries({\n queryKey: ACTION_QUERY_PREFIX,\n predicate: (query) => hashes.has(query.queryHash),\n });\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-capability.js","sourceRoot":"","sources":["../../src/react/use-capability.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE5C,MAAM,SAAS,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAW,CAAC;AAIrE,MAAM,UAAU,aAAa,CAC3B,UAAmC;IAEnC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAqB;QACrD,GAAG,SAAS;QACZ,SAAS,EAAE,IAAI;KAChB,CAAC,CAAC;IACH,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,MAAM,GAAG,IAAI,CAAC;QAClB,KAAK,iBAAiB,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;YACjD,IAAI,MAAM;gBAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,EAAE;YACV,MAAM,GAAG,KAAK,CAAC;QACjB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IACjB,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
1
|
+
{"version":3,"file":"use-capability.js","sourceRoot":"","sources":["../../src/react/use-capability.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,iBAAiB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE5C,MAAM,SAAS,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAW,CAAC;AAIrE,MAAM,UAAU,aAAa,CAC3B,UAAmC;IAEnC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAqB;QACrD,GAAG,SAAS;QACZ,SAAS,EAAE,IAAI;KAChB,CAAC,CAAC;IACH,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,MAAM,GAAG,IAAI,CAAC;QAClB,KAAK,iBAAiB,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;YACjD,IAAI,MAAM;gBAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,EAAE;YACV,MAAM,GAAG,KAAK,CAAC;QACjB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IACjB,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["import {\n type CapabilityDefinition,\n type CapabilityHandle,\n resolveCapability,\n} from \"@parlor/capabilities\";\nimport { useEffect, useState } from \"react\";\n\nconst RESOLVING = { available: false, reason: \"resolving\" } as const;\n\nexport type CapabilityState<T> = CapabilityHandle<T> & { readonly resolving?: boolean };\n\nexport function useCapability<T>(\n definition: CapabilityDefinition<T>,\n): CapabilityState<T> {\n const [state, setState] = useState<CapabilityState<T>>({\n ...RESOLVING,\n resolving: true,\n });\n useEffect(() => {\n let active = true;\n void resolveCapability(definition).then((handle) => {\n if (active) setState(handle);\n });\n return () => {\n active = false;\n };\n }, [definition]);\n return state;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-devices.js","sourceRoot":"","sources":["../../src/react/use-devices.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-devices.js","sourceRoot":"","sources":["../../src/react/use-devices.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE1E,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,WAAW,GAAG,qBAAqB,CAAC;AAS1C,MAAM,IAAI,GAAsB,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAEjD,wGAAwG;AACxG,MAAM,UAAU,WAAW,CACzB,MAAgC,EAChC,KAA+B;IAE/B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;IACxD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACzE,OAAO,KAAK,CAAC,MAAM,CACjB,CAAC,MAAM,EAAE,EAAE,CACT,MAAM,CAAC,OAAO;QACd,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,IAAI;QAC3B,CAAC,MAAM,CAAC,OAAO;QACf,MAAM,CAAC,QAAQ,KAAK,IAAI,CAC3B,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU;IAUxB,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,YAAY,EAAE,CAAC;IACtC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAuC,SAAS,CAAC,CAAC;IACxF,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAA2B,EAAE,CAAC,CAAC;IACnE,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAoB,IAAI,CAAC,CAAC;IAChE,MAAM,IAAI,GAAG,MAAM,CAAuC,SAAS,CAAC,CAAC;IACrE,MAAM,MAAM,GAAG,MAAM,CAAsB,KAAK,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC;IAClE,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,EAAE,WAAW,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC;IAElE,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,IAAI,EAAE,WAAW;YAAE,OAAO;QAC/B,IAAI,IAAI,GAAG,IAAI,CAAC;QAChB,MAAM,IAAI,GAAG,GAAG,EAAE,CAChB,IAAI,CAAC,WAAY,EAAE,CAAC,IAAI,CACtB,CAAC,MAAM,EAAE,EAAE;YACT,IAAI,CAAC,IAAI;gBAAE,OAAO;YAClB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC;YAC5B,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;YACtB,UAAU,CAAC,MAAM,CAAC,CAAC;YACnB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACxD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;YAC3D,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;gBACnB,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACnD,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,CAAC,CAAC;aAC5E,CAAC,CAAC;QACL,CAAC,EACD,GAAG,EAAE,CAAC,SAAS,CAChB,CAAC;QACJ,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,KAAK,IAAI,EAAE,CAAC;QACZ,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,EAAE;YACnC,IACE,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EACjF,CAAC;gBACD,KAAK,IAAI,EAAE,CAAC;YACd,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,EAAE;YACV,IAAI,GAAG,KAAK,CAAC;YACb,GAAG,EAAE,CAAC;QACR,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IAEjB,MAAM,MAAM,GAAG,WAAW,CACxB,KAAK,EAAE,EAAU,EAAE,EAAE;QACnB,IAAI,CAAC,IAAI,EAAE,YAAY;YAAE,OAAO,KAAK,CAAC;QACtC,UAAU,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC;QACrC,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;YAC5B,UAAU,CAAC,IAAI,CAAC,CAAC;YACjB,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;gBAC9D,UAAU,CAAC;oBACT,IAAI,EAAE,aAAa;oBACnB,EAAE;oBACF,OAAO,EAAE,kCAAkC,SAAS,EAAE,aAAa,EAAE,EAAE;iBACxE,CAAC,CAAC;gBACH,OAAO,KAAK,CAAC;YACf,CAAC;YACD,UAAU,CAAC;gBACT,IAAI,EAAE,OAAO;gBACb,EAAE;gBACF,OAAO,EACL,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB;oBACxD,CAAC,CAAC,4DAA4D;oBAC9D,CAAC,CAAC,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO;wBACvC,CAAC,CAAC,KAAK,CAAC,OAAO;wBACf,CAAC,CAAC,+BAA+B;aACxC,CAAC,CAAC;YACH,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC,EACD,CAAC,IAAI,CAAC,CACP,CAAC;IAEF,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,WAAW,CACzB,CAAC,EAAU,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,EAChF,EAAE,CACH,CAAC;IAEF,OAAO,OAAO,CACZ,GAAG,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EACvE,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,CAC9D,CAAC;AACJ,CAAC","sourcesContent":["import { tagsOverlap } from \"@parlor/core\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\nimport { appOrigin } from \"../action-client.js\";\nimport type { AccountDevice } from \"../session.js\";\nimport { useFramework } from \"./provider.js\";\n\nconst DEVICES_TAG = \"parlor_auth_devices\";\n\n/** What removing a device is doing: confirming, or showing the command a device without Face ID or Touch ID needs. */\nexport type RemoveDeviceState =\n | { readonly step: \"idle\" }\n | { readonly step: \"removing\"; readonly id: string }\n | { readonly step: \"on-computer\"; readonly id: string; readonly command: string }\n | { readonly step: \"error\"; readonly id: string; readonly message: string };\n\nconst IDLE: RemoveDeviceState = { step: \"idle\" };\n\n/** Devices another device linked since `before` was read; this device's own links are not news here. */\nexport function newlyLinked(\n before: readonly AccountDevice[],\n after: readonly AccountDevice[],\n): AccountDevice[] {\n const self = after.find((device) => device.current)?.id;\n const was = new Map(before.map((device) => [device.id, device.vouched]));\n return after.filter(\n (device) =>\n device.vouched &&\n was.get(device.id) !== true &&\n !device.current &&\n device.linkedBy !== self,\n );\n}\n\n/**\n * The account's devices, read again whenever one is added, linked or removed; the ones another\n * device linked while this page was open, to tell the user; and removing one with Face ID or\n * Touch ID.\n */\nexport function useDevices(): {\n readonly available: boolean;\n readonly devices: readonly AccountDevice[] | undefined;\n readonly linked: readonly AccountDevice[];\n readonly removal: RemoveDeviceState;\n /** Whether it was removed. */\n remove(id: string): Promise<boolean>;\n dismiss(id: string): void;\n reset(): void;\n} {\n const { auth, sync } = useFramework();\n const [devices, setDevices] = useState<readonly AccountDevice[] | undefined>(undefined);\n const [linked, setLinked] = useState<readonly AccountDevice[]>([]);\n const [removal, setRemoval] = useState<RemoveDeviceState>(IDLE);\n const last = useRef<readonly AccountDevice[] | undefined>(undefined);\n const reload = useRef<() => Promise<void>>(async () => undefined);\n const available = Boolean(auth?.listDevices && auth.removeDevice);\n\n useEffect(() => {\n if (!auth?.listDevices) return;\n let live = true;\n const load = () =>\n auth.listDevices!().then(\n (listed) => {\n if (!live) return;\n const before = last.current;\n last.current = listed;\n setDevices(listed);\n const fresh = before ? newlyLinked(before, listed) : [];\n const present = new Set(listed.map((device) => device.id));\n setLinked((shown) => [\n ...shown.filter((device) => present.has(device.id)),\n ...fresh.filter((device) => !shown.some((entry) => entry.id === device.id)),\n ]);\n },\n () => undefined,\n );\n reload.current = load;\n void load();\n const off = sync.onEvents((events) => {\n if (\n events.some((event) => event.tags.some((tag) => tagsOverlap(tag, [DEVICES_TAG])))\n ) {\n void load();\n }\n });\n return () => {\n live = false;\n off();\n };\n }, [auth, sync]);\n\n const remove = useCallback(\n async (id: string) => {\n if (!auth?.removeDevice) return false;\n setRemoval({ step: \"removing\", id });\n try {\n await auth.removeDevice(id);\n setRemoval(IDLE);\n await reload.current();\n return true;\n } catch (error) {\n if (error instanceof Error && error.name === \"LinkOnComputer\") {\n setRemoval({\n step: \"on-computer\",\n id,\n command: `npx @parlor/agent-host devices ${appOrigin()} --remove ${id}`,\n });\n return false;\n }\n setRemoval({\n step: \"error\",\n id,\n message:\n error instanceof Error && error.name === \"NotAllowedError\"\n ? \"Face ID or Touch ID was cancelled, so nothing was removed.\"\n : error instanceof Error && error.message\n ? error.message\n : \"That did not work. Try again.\",\n });\n return false;\n }\n },\n [auth],\n );\n\n const reset = useCallback(() => setRemoval(IDLE), []);\n const dismiss = useCallback(\n (id: string) => setLinked((shown) => shown.filter((device) => device.id !== id)),\n [],\n );\n\n return useMemo(\n () => ({ available, devices, linked, removal, remove, dismiss, reset }),\n [available, devices, linked, removal, remove, dismiss, reset],\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-link-device.js","sourceRoot":"","sources":["../../src/react/use-link-device.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-link-device.js","sourceRoot":"","sources":["../../src/react/use-link-device.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useMemo, useRef, useState } from \"react\";\n\nimport { appOrigin } from \"../action-client.js\";\nimport type { PendingLink } from \"../session.js\";\nimport { useFramework } from \"./provider.js\";\n\n/**\n * Linking another device from this one: `check` the code it shows, `confirm` with Face ID or\n * Touch ID. A device without a presence key gets the command that links from the computer.\n */\nexport type LinkDeviceState =\n | { readonly step: \"idle\" }\n | { readonly step: \"checking\" }\n | {\n readonly step: \"confirm\";\n readonly device: { readonly name: string; readonly platform: string };\n }\n | { readonly step: \"linking\" }\n | { readonly step: \"linked\"; readonly name: string }\n | {\n readonly step: \"on-computer\";\n readonly device: { readonly name: string; readonly platform: string };\n readonly command: string;\n }\n | { readonly step: \"error\"; readonly message: string };\n\nconst IDLE: LinkDeviceState = { step: \"idle\" };\n\nconst messageOf = (error: unknown): string => {\n if (error instanceof Error && error.name === \"NotAllowedError\") {\n return \"Face ID or Touch ID was cancelled, so nothing was linked.\";\n }\n return error instanceof Error && error.message\n ? error.message\n : \"That did not work. Try again.\";\n};\n\nexport function useLinkDevice(): {\n readonly available: boolean;\n readonly state: LinkDeviceState;\n /** A typed code, or the device id and commitment a scanned QR carried. */\n check(\n from: string | { readonly deviceId: string; readonly commitment: string },\n ): Promise<void>;\n confirm(): Promise<void>;\n reset(): void;\n} {\n const { auth } = useFramework();\n const [state, setState] = useState<LinkDeviceState>(IDLE);\n const pending = useRef<PendingLink | undefined>(undefined);\n const available = Boolean(auth?.resolveLink && auth.vouch && auth.canVouch);\n\n const check = useCallback(\n async (from: string | { readonly deviceId: string; readonly commitment: string }) => {\n if (!auth?.resolveLink || !auth.canVouch) return;\n setState({ step: \"checking\" });\n try {\n const link = await auth.resolveLink(from);\n pending.current = link;\n const device = { name: link.name, platform: link.platform };\n setState(\n (await auth.canVouch())\n ? { step: \"confirm\", device }\n : {\n step: \"on-computer\",\n device,\n command: `npx @parlor/agent-host link ${appOrigin()} ${link.code}`,\n },\n );\n } catch (error) {\n setState({ step: \"error\", message: messageOf(error) });\n }\n },\n [auth],\n );\n\n const confirm = useCallback(async () => {\n const link = pending.current;\n if (!auth?.vouch || !link) return;\n setState({ step: \"linking\" });\n try {\n await auth.vouch(link);\n pending.current = undefined;\n setState({ step: \"linked\", name: link.name });\n } catch (error) {\n setState({ step: \"error\", message: messageOf(error) });\n }\n }, [auth]);\n\n const reset = useCallback(() => {\n pending.current = undefined;\n setState(IDLE);\n }, []);\n\n return useMemo(\n () => ({ available, state, check, confirm, reset }),\n [available, state, check, confirm, reset],\n );\n}\n"]}
|
package/dist/register.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"","sourcesContent":["import type { ActionMap } from \"@parlor/core\";\n\nexport interface Register {}\n\nexport type RegisteredActions = Register extends { actions: infer A extends ActionMap }\n ? A\n : ActionMap;\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ActionError, type ActionErrorCode } from "@parlor/core";
|
|
2
|
+
import { type AnyRedirect, type NotFoundError } from "@tanstack/react-router";
|
|
3
|
+
/** For each `ActionError` code, the `redirect(...)` or `notFound(...)` a route throws instead. */
|
|
4
|
+
export type ActionErrorRoutes = {
|
|
5
|
+
readonly [C in ActionErrorCode]?: (error: ActionError) => AnyRedirect | NotFoundError;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Rethrows an `ActionError` as what `routes` names for its code, `not_found` as `notFound()`
|
|
9
|
+
* unless `routes` says otherwise, and anything else unchanged for the route's `errorComponent`.
|
|
10
|
+
* Pass it as a route's `onError`, or to `.catch` in a loader.
|
|
11
|
+
*/
|
|
12
|
+
export declare function routeActionErrors(routes?: ActionErrorRoutes): (error: unknown) => never;
|
|
13
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/router/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAY,MAAM,wBAAwB,CAAC;AAExF,kGAAkG;AAClG,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,CAAC,IAAI,eAAe,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,WAAW,GAAG,aAAa;CACtF,CAAC;AAIF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,GAAE,iBAAsB,GAC7B,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,CAM3B"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ActionError } from "@parlor/core";
|
|
2
|
+
import { notFound } from "@tanstack/react-router";
|
|
3
|
+
const DEFAULT_ROUTES = { not_found: () => notFound() };
|
|
4
|
+
/**
|
|
5
|
+
* Rethrows an `ActionError` as what `routes` names for its code, `not_found` as `notFound()`
|
|
6
|
+
* unless `routes` says otherwise, and anything else unchanged for the route's `errorComponent`.
|
|
7
|
+
* Pass it as a route's `onError`, or to `.catch` in a loader.
|
|
8
|
+
*/
|
|
9
|
+
export function routeActionErrors(routes = {}) {
|
|
10
|
+
const handlers = { ...DEFAULT_ROUTES, ...routes };
|
|
11
|
+
return (error) => {
|
|
12
|
+
const route = error instanceof ActionError ? handlers[error.code] : undefined;
|
|
13
|
+
throw route ? route(error) : error;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=errors.js.map
|