@parlor/client 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +22 -2
- package/dist/action-client.d.ts +42 -0
- package/dist/action-client.d.ts.map +1 -0
- package/dist/action-client.js +92 -0
- package/dist/action-client.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin-client.d.ts +11 -0
- package/dist/plugin-client.d.ts.map +1 -0
- package/dist/plugin-client.js +14 -0
- package/dist/plugin-client.js.map +1 -0
- package/dist/query-keys.d.ts +3 -0
- package/dist/query-keys.d.ts.map +1 -0
- package/dist/query-keys.js +5 -0
- package/dist/query-keys.js.map +1 -0
- 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.d.ts +20 -0
- package/dist/react/agent-ui/activity-state.d.ts.map +1 -0
- package/dist/react/agent-ui/activity-state.js +14 -0
- package/dist/react/agent-ui/activity-state.js.map +1 -0
- package/dist/react/agent-ui/chat-activity.d.ts +13 -0
- package/dist/react/agent-ui/chat-activity.d.ts.map +1 -0
- package/dist/react/agent-ui/chat-activity.js +26 -0
- package/dist/react/agent-ui/chat-activity.js.map +1 -0
- package/dist/react/agent-ui/chat-confirm.d.ts +28 -0
- package/dist/react/agent-ui/chat-confirm.d.ts.map +1 -0
- package/dist/react/agent-ui/chat-confirm.js +140 -0
- package/dist/react/agent-ui/chat-confirm.js.map +1 -0
- package/dist/react/agent-ui/chat.d.ts +7 -0
- package/dist/react/agent-ui/chat.d.ts.map +1 -0
- package/dist/react/agent-ui/chat.js +7 -0
- package/dist/react/agent-ui/chat.js.map +1 -0
- package/dist/react/agent-ui/dev.d.ts +12 -0
- package/dist/react/agent-ui/dev.d.ts.map +1 -0
- package/dist/react/agent-ui/dev.js +80 -0
- package/dist/react/agent-ui/dev.js.map +1 -0
- package/dist/react/agent-ui/runtime.d.ts +12 -0
- package/dist/react/agent-ui/runtime.d.ts.map +1 -0
- package/dist/react/agent-ui/runtime.js +50 -0
- package/dist/react/agent-ui/runtime.js.map +1 -0
- package/dist/react/agent-ui/use-agent-activity.d.ts +3 -0
- package/dist/react/agent-ui/use-agent-activity.d.ts.map +1 -0
- package/dist/react/agent-ui/use-agent-activity.js +7 -0
- package/dist/react/agent-ui/use-agent-activity.js.map +1 -0
- package/dist/react/agent-ui/use-agent-confirmations.d.ts +8 -0
- package/dist/react/agent-ui/use-agent-confirmations.d.ts.map +1 -0
- package/dist/react/agent-ui/use-agent-confirmations.js +12 -0
- package/dist/react/agent-ui/use-agent-confirmations.js.map +1 -0
- package/dist/react/agent-ui/use-agent-view.d.ts +11 -0
- package/dist/react/agent-ui/use-agent-view.d.ts.map +1 -0
- package/dist/react/agent-ui/use-agent-view.js +25 -0
- package/dist/react/agent-ui/use-agent-view.js.map +1 -0
- package/dist/react/chat/ask-fields.d.ts +13 -0
- package/dist/react/chat/ask-fields.d.ts.map +1 -0
- package/dist/react/chat/ask-fields.js +46 -0
- package/dist/react/chat/ask-fields.js.map +1 -0
- package/dist/react/chat/chat-ask.d.ts +32 -0
- package/dist/react/chat/chat-ask.d.ts.map +1 -0
- package/dist/react/chat/chat-ask.js +69 -0
- package/dist/react/chat/chat-ask.js.map +1 -0
- package/dist/react/chat/chat-composer.d.ts +18 -0
- package/dist/react/chat/chat-composer.d.ts.map +1 -0
- package/dist/react/chat/chat-composer.js +81 -0
- package/dist/react/chat/chat-composer.js.map +1 -0
- package/dist/react/chat/chat-root.d.ts +14 -0
- package/dist/react/chat/chat-root.d.ts.map +1 -0
- package/dist/react/chat/chat-root.js +21 -0
- package/dist/react/chat/chat-root.js.map +1 -0
- package/dist/react/chat/chat-thread.d.ts +18 -0
- package/dist/react/chat/chat-thread.d.ts.map +1 -0
- package/dist/react/chat/chat-thread.js +69 -0
- package/dist/react/chat/chat-thread.js.map +1 -0
- package/dist/react/chat/context.d.ts +6 -0
- package/dist/react/chat/context.d.ts.map +1 -0
- package/dist/react/chat/context.js +24 -0
- package/dist/react/chat/context.js.map +1 -0
- package/dist/react/chat/create.d.ts +15 -0
- package/dist/react/chat/create.d.ts.map +1 -0
- package/dist/react/chat/create.js +23 -0
- package/dist/react/chat/create.js.map +1 -0
- package/dist/react/chat/open-turns.d.ts +10 -0
- package/dist/react/chat/open-turns.d.ts.map +1 -0
- package/dist/react/chat/open-turns.js +60 -0
- package/dist/react/chat/open-turns.js.map +1 -0
- package/dist/react/chat/runtime.d.ts +167 -0
- package/dist/react/chat/runtime.d.ts.map +1 -0
- package/dist/react/chat/runtime.js +939 -0
- package/dist/react/chat/runtime.js.map +1 -0
- package/dist/react/chat/use-chat.d.ts +4 -0
- package/dist/react/chat/use-chat.d.ts.map +1 -0
- package/dist/react/chat/use-chat.js +22 -0
- package/dist/react/chat/use-chat.js.map +1 -0
- package/dist/react/chat/use-device-setup.d.ts +9 -0
- package/dist/react/chat/use-device-setup.d.ts.map +1 -0
- package/dist/react/chat/use-device-setup.js +17 -0
- package/dist/react/chat/use-device-setup.js.map +1 -0
- package/dist/react/hooks.d.ts +17 -0
- package/dist/react/hooks.d.ts.map +1 -0
- package/dist/react/hooks.js +36 -0
- package/dist/react/hooks.js.map +1 -0
- package/dist/react/index.d.ts +21 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +14 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/provider.d.ts +41 -0
- package/dist/react/provider.d.ts.map +1 -0
- package/dist/react/provider.js +176 -0
- package/dist/react/provider.js.map +1 -0
- package/dist/react/retry.d.ts +5 -0
- package/dist/react/retry.d.ts.map +1 -0
- package/dist/react/retry.js +22 -0
- package/dist/react/retry.js.map +1 -0
- package/dist/react/sync-bridge.d.ts +28 -0
- package/dist/react/sync-bridge.d.ts.map +1 -0
- package/dist/react/sync-bridge.js +65 -0
- package/dist/react/sync-bridge.js.map +1 -0
- package/dist/react/sync.d.ts +51 -0
- package/dist/react/sync.d.ts.map +1 -0
- package/dist/react/sync.js +154 -0
- package/dist/react/sync.js.map +1 -0
- package/dist/react/use-capability.d.ts +6 -0
- package/dist/react/use-capability.d.ts.map +1 -0
- package/dist/react/use-capability.js +21 -0
- package/dist/react/use-capability.js.map +1 -0
- package/dist/react/use-devices.d.ts +34 -0
- package/dist/react/use-devices.d.ts.map +1 -0
- package/dist/react/use-devices.js +93 -0
- package/dist/react/use-devices.js.map +1 -0
- package/dist/react/use-link-device.d.ts +42 -0
- package/dist/react/use-link-device.d.ts.map +1 -0
- package/dist/react/use-link-device.js +58 -0
- package/dist/react/use-link-device.js.map +1 -0
- package/dist/register.d.ts +7 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +2 -0
- package/dist/register.js.map +1 -0
- 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.d.ts +121 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +5 -0
- package/dist/session.js.map +1 -0
- 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 +70 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 cychien
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @parlor/client
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The browser side of a Parlor app: the action client, and React hooks that read and write through actions and refresh when anyone, including an agent, changes the data.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
pnpm add @parlor/client @tanstack/react-query @tanstack/react-router react react-dom
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { createActionClient } from "@parlor/client";
|
|
11
|
+
import { useActionQuery } from "@parlor/client/react";
|
|
12
|
+
|
|
13
|
+
export const client = createActionClient();
|
|
14
|
+
|
|
15
|
+
export function Health() {
|
|
16
|
+
const status = useActionQuery("health" as never, {} as never);
|
|
17
|
+
return <p>{status.isSuccess ? "up" : "..."}</p>;
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`@parlor/client/router` gives TanStack Router loaders the same cache, so a page can load its data before it renders and links can preload it. Its `mount` starts the app, hydrating a page the server rendered.
|
|
22
|
+
|
|
23
|
+
In an app, register the actions once in `src/parlor.d.ts` so names and inputs are typed. Docs: https://cychien.github.io/parlor/guides/pages-and-hooks/
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ActionError, type ActionInput, type ActionMap, type ActionOutput, type Tag } from "@parlor/core";
|
|
2
|
+
export declare function generateClientId(): string;
|
|
3
|
+
export type HeadersProvider = HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
|
|
4
|
+
export interface ActionClientOptions {
|
|
5
|
+
readonly baseUrl?: string | undefined;
|
|
6
|
+
readonly basePath?: string | undefined;
|
|
7
|
+
readonly fetch?: typeof fetch | undefined;
|
|
8
|
+
readonly headers?: HeadersProvider | undefined;
|
|
9
|
+
readonly clientId?: string | undefined;
|
|
10
|
+
}
|
|
11
|
+
export interface CallOptions {
|
|
12
|
+
readonly signal?: AbortSignal | undefined;
|
|
13
|
+
}
|
|
14
|
+
export interface ActionEnvelope<T> {
|
|
15
|
+
readonly data: T;
|
|
16
|
+
readonly tags: readonly Tag[];
|
|
17
|
+
/** The sync cursor a read's data reflects, when the server tracks it. */
|
|
18
|
+
readonly cursor?: string | undefined;
|
|
19
|
+
}
|
|
20
|
+
export interface ActionClient<T extends ActionMap> {
|
|
21
|
+
readonly clientId: string;
|
|
22
|
+
readonly basePath: string;
|
|
23
|
+
call<K extends keyof T & string>(name: K, input: ActionInput<T[K]>, options?: CallOptions): Promise<ActionOutput<T[K]>>;
|
|
24
|
+
invoke<K extends keyof T & string>(name: K, input: ActionInput<T[K]>, options?: CallOptions): Promise<ActionEnvelope<ActionOutput<T[K]>>>;
|
|
25
|
+
request(path: string, init?: RequestInit): Promise<Response>;
|
|
26
|
+
}
|
|
27
|
+
export declare function toActionError(response: Response, options?: {
|
|
28
|
+
readonly missingIsNotFound?: boolean;
|
|
29
|
+
}): Promise<ActionError>;
|
|
30
|
+
export declare function invokeAt<T extends ActionMap, K extends keyof T & string>(request: ActionClient<ActionMap>["request"], path: string, input: ActionInput<T[K]>, options?: CallOptions & {
|
|
31
|
+
readonly missingIsNotFound?: boolean;
|
|
32
|
+
}): Promise<ActionEnvelope<ActionOutput<T[K]>>>;
|
|
33
|
+
export declare function resolveServerOrigin(): string;
|
|
34
|
+
/**
|
|
35
|
+
* The one name of this app's backend on every surface: the page's own origin when it is
|
|
36
|
+
* served over http(s) and is not a shell's webview origin, else the shell's `server.url`.
|
|
37
|
+
* `parlor dev` running several targets names the web target's origin for all of them.
|
|
38
|
+
* Statements are signed for it and the host is paired with it.
|
|
39
|
+
*/
|
|
40
|
+
export declare function appOrigin(serverUrl?: string, injected?: string): string;
|
|
41
|
+
export declare function createActionClient<T extends ActionMap>(options?: ActionClientOptions): ActionClient<T>;
|
|
42
|
+
//# sourceMappingURL=action-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-client.d.ts","sourceRoot":"","sources":["../src/action-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { ActionError, SHELL_ORIGINS, } from "@parlor/core";
|
|
2
|
+
const DEFAULT_BASE_PATH = "/_parlor";
|
|
3
|
+
const SOURCE_HEADER = "x-parlor-source";
|
|
4
|
+
const CLIENT_HEADER = "x-parlor-client";
|
|
5
|
+
function injectedServerUrl() {
|
|
6
|
+
const env = import.meta.env;
|
|
7
|
+
return env?.PARLOR_SERVER_URL ?? "";
|
|
8
|
+
}
|
|
9
|
+
export function generateClientId() {
|
|
10
|
+
return globalThis.crypto?.randomUUID?.() ?? Math.random().toString(36).slice(2);
|
|
11
|
+
}
|
|
12
|
+
async function resolveHeaders(provider, clientId, extra) {
|
|
13
|
+
const headers = new Headers(typeof provider === "function" ? await provider() : provider);
|
|
14
|
+
new Headers(extra).forEach((value, key) => headers.set(key, value));
|
|
15
|
+
headers.set(SOURCE_HEADER, "ui");
|
|
16
|
+
headers.set(CLIENT_HEADER, clientId);
|
|
17
|
+
return headers;
|
|
18
|
+
}
|
|
19
|
+
export async function toActionError(response, options = {}) {
|
|
20
|
+
const body = await response.json().catch(() => undefined);
|
|
21
|
+
if (ActionError.isBody(body))
|
|
22
|
+
return ActionError.fromBody(body);
|
|
23
|
+
const code = options.missingIsNotFound && response.status === 404 ? "not_found" : "internal";
|
|
24
|
+
return new ActionError(code, `Action request failed with HTTP ${response.status}`);
|
|
25
|
+
}
|
|
26
|
+
export async function invokeAt(request, path, input, options = {}) {
|
|
27
|
+
const response = await request(path, {
|
|
28
|
+
method: "POST",
|
|
29
|
+
headers: { "content-type": "application/json" },
|
|
30
|
+
body: JSON.stringify(input ?? {}),
|
|
31
|
+
signal: options.signal ?? null,
|
|
32
|
+
});
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
throw await toActionError(response, options.missingIsNotFound ? { missingIsNotFound: true } : {});
|
|
35
|
+
}
|
|
36
|
+
const body = (await response.json());
|
|
37
|
+
return {
|
|
38
|
+
data: body.data,
|
|
39
|
+
tags: body.tags ?? [],
|
|
40
|
+
...(typeof body.cursor === "string" ? { cursor: body.cursor } : {}),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export function resolveServerOrigin() {
|
|
44
|
+
return injectedServerUrl() || globalThis.location?.origin || "";
|
|
45
|
+
}
|
|
46
|
+
function injectedAppOrigin() {
|
|
47
|
+
const env = import.meta.env;
|
|
48
|
+
return env?.PARLOR_APP_ORIGIN ?? "";
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The one name of this app's backend on every surface: the page's own origin when it is
|
|
52
|
+
* served over http(s) and is not a shell's webview origin, else the shell's `server.url`.
|
|
53
|
+
* `parlor dev` running several targets names the web target's origin for all of them.
|
|
54
|
+
* Statements are signed for it and the host is paired with it.
|
|
55
|
+
*/
|
|
56
|
+
export function appOrigin(serverUrl = resolveServerOrigin(), injected = injectedAppOrigin()) {
|
|
57
|
+
if (injected)
|
|
58
|
+
return new URL(injected).origin;
|
|
59
|
+
const page = globalThis.location?.origin ?? "";
|
|
60
|
+
if (/^https?:\/\//.test(page) && !SHELL_ORIGINS.includes(page))
|
|
61
|
+
return page;
|
|
62
|
+
try {
|
|
63
|
+
return new URL(serverUrl).origin;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return serverUrl;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export function createActionClient(options = {}) {
|
|
70
|
+
const { baseUrl = injectedServerUrl(), basePath = DEFAULT_BASE_PATH, headers, } = options;
|
|
71
|
+
// Drawn on first use: workerd allows no random values while a module loads.
|
|
72
|
+
let clientId = options.clientId;
|
|
73
|
+
const ownId = () => (clientId ??= generateClientId());
|
|
74
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
75
|
+
const request = async (path, init = {}) => fetchImpl(`${baseUrl}${path}`, {
|
|
76
|
+
...init,
|
|
77
|
+
headers: await resolveHeaders(headers, ownId(), init.headers),
|
|
78
|
+
});
|
|
79
|
+
const invoke = async (name, input, callOptions = {}) => {
|
|
80
|
+
return invokeAt(request, `${basePath}/actions/${name}`, input, callOptions);
|
|
81
|
+
};
|
|
82
|
+
return {
|
|
83
|
+
get clientId() {
|
|
84
|
+
return ownId();
|
|
85
|
+
},
|
|
86
|
+
basePath,
|
|
87
|
+
request,
|
|
88
|
+
invoke,
|
|
89
|
+
call: async (name, input, callOptions) => (await invoke(name, input, callOptions)).data,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=action-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-client.js","sourceRoot":"","sources":["../src/action-client.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ActionError,\n type ActionInput,\n type ActionMap,\n type ActionOutput,\n SHELL_ORIGINS,\n type Tag,\n} from \"@parlor/core\";\n\nconst DEFAULT_BASE_PATH = \"/_parlor\";\nconst SOURCE_HEADER = \"x-parlor-source\";\nconst CLIENT_HEADER = \"x-parlor-client\";\n\nfunction injectedServerUrl(): string {\n const env = (import.meta as { env?: { PARLOR_SERVER_URL?: string } }).env;\n return env?.PARLOR_SERVER_URL ?? \"\";\n}\n\nexport function generateClientId(): string {\n return globalThis.crypto?.randomUUID?.() ?? Math.random().toString(36).slice(2);\n}\n\nexport type HeadersProvider = HeadersInit | (() => HeadersInit | Promise<HeadersInit>);\n\nexport interface ActionClientOptions {\n readonly baseUrl?: string | undefined;\n readonly basePath?: string | undefined;\n readonly fetch?: typeof fetch | undefined;\n readonly headers?: HeadersProvider | undefined;\n readonly clientId?: string | undefined;\n}\n\nexport interface CallOptions {\n readonly signal?: AbortSignal | undefined;\n}\n\nexport interface ActionEnvelope<T> {\n readonly data: T;\n readonly tags: readonly Tag[];\n /** The sync cursor a read's data reflects, when the server tracks it. */\n readonly cursor?: string | undefined;\n}\n\nexport interface ActionClient<T extends ActionMap> {\n readonly clientId: string;\n readonly basePath: string;\n call<K extends keyof T & string>(\n name: K,\n input: ActionInput<T[K]>,\n options?: CallOptions,\n ): Promise<ActionOutput<T[K]>>;\n invoke<K extends keyof T & string>(\n name: K,\n input: ActionInput<T[K]>,\n options?: CallOptions,\n ): Promise<ActionEnvelope<ActionOutput<T[K]>>>;\n request(path: string, init?: RequestInit): Promise<Response>;\n}\n\nasync function resolveHeaders(\n provider: HeadersProvider | undefined,\n clientId: string,\n extra?: HeadersInit,\n): Promise<Headers> {\n const headers = new Headers(\n typeof provider === \"function\" ? await provider() : provider,\n );\n new Headers(extra).forEach((value, key) => headers.set(key, value));\n headers.set(SOURCE_HEADER, \"ui\");\n headers.set(CLIENT_HEADER, clientId);\n return headers;\n}\n\nexport async function toActionError(\n response: Response,\n options: { readonly missingIsNotFound?: boolean } = {},\n): Promise<ActionError> {\n const body: unknown = await response.json().catch(() => undefined);\n if (ActionError.isBody(body)) return ActionError.fromBody(body);\n const code =\n options.missingIsNotFound && response.status === 404 ? \"not_found\" : \"internal\";\n return new ActionError(code, `Action request failed with HTTP ${response.status}`);\n}\n\nexport async function invokeAt<T extends ActionMap, K extends keyof T & string>(\n request: ActionClient<ActionMap>[\"request\"],\n path: string,\n input: ActionInput<T[K]>,\n options: CallOptions & { readonly missingIsNotFound?: boolean } = {},\n): Promise<ActionEnvelope<ActionOutput<T[K]>>> {\n const response = await request(path, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(input ?? {}),\n signal: options.signal ?? null,\n });\n if (!response.ok) {\n throw await toActionError(\n response,\n options.missingIsNotFound ? { missingIsNotFound: true } : {},\n );\n }\n const body = (await response.json()) as {\n data: ActionOutput<T[K]>;\n tags?: Tag[];\n cursor?: string;\n };\n return {\n data: body.data,\n tags: body.tags ?? [],\n ...(typeof body.cursor === \"string\" ? { cursor: body.cursor } : {}),\n };\n}\n\nexport function resolveServerOrigin(): string {\n return injectedServerUrl() || globalThis.location?.origin || \"\";\n}\n\nfunction injectedAppOrigin(): string {\n const env = (import.meta as { env?: { PARLOR_APP_ORIGIN?: string } }).env;\n return env?.PARLOR_APP_ORIGIN ?? \"\";\n}\n\n/**\n * The one name of this app's backend on every surface: the page's own origin when it is\n * served over http(s) and is not a shell's webview origin, else the shell's `server.url`.\n * `parlor dev` running several targets names the web target's origin for all of them.\n * Statements are signed for it and the host is paired with it.\n */\nexport function appOrigin(\n serverUrl: string = resolveServerOrigin(),\n injected: string = injectedAppOrigin(),\n): string {\n if (injected) return new URL(injected).origin;\n const page = globalThis.location?.origin ?? \"\";\n if (/^https?:\\/\\//.test(page) && !SHELL_ORIGINS.includes(page)) return page;\n try {\n return new URL(serverUrl).origin;\n } catch {\n return serverUrl;\n }\n}\n\nexport function createActionClient<T extends ActionMap>(\n options: ActionClientOptions = {},\n): ActionClient<T> {\n const {\n baseUrl = injectedServerUrl(),\n basePath = DEFAULT_BASE_PATH,\n headers,\n } = options;\n // Drawn on first use: workerd allows no random values while a module loads.\n let clientId = options.clientId;\n const ownId = () => (clientId ??= generateClientId());\n const fetchImpl = options.fetch ?? globalThis.fetch;\n\n const request = async (path: string, init: RequestInit = {}) =>\n fetchImpl(`${baseUrl}${path}`, {\n ...init,\n headers: await resolveHeaders(headers, ownId(), init.headers),\n });\n\n const invoke = async <K extends keyof T & string>(\n name: K,\n input: ActionInput<T[K]>,\n callOptions: CallOptions = {},\n ): Promise<ActionEnvelope<ActionOutput<T[K]>>> => {\n return invokeAt<T, K>(request, `${basePath}/actions/${name}`, input, callOptions);\n };\n\n return {\n get clientId() {\n return ownId();\n },\n basePath,\n request,\n invoke,\n call: async (name, input, callOptions) =>\n (await invoke(name, input, callOptions)).data,\n };\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { type ActionClient, appOrigin, type ActionClientOptions, type ActionEnvelope, type CallOptions, createActionClient, generateClientId, type HeadersProvider, } from "./action-client.js";
|
|
2
|
+
export { type AgentViewBinding, type AgentViewDefinition, type AnyAgentView, defineAgentView, type WritableField, } from "@parlor/agent-ui";
|
|
3
|
+
export { ACTION_QUERY_PREFIX, actionQueryKey } from "./query-keys.js";
|
|
4
|
+
export { createPluginClient, type PluginClient } from "./plugin-client.js";
|
|
5
|
+
export type { AccountDevice, DeviceLinker, DeviceSource, FrameworkAuth, HostAuthorizer, PendingLink, SessionDevice, SessionSnapshot, SessionSource, SessionUser, } from "./session.js";
|
|
6
|
+
export { LinkOnComputer } from "./session.js";
|
|
7
|
+
export type { Register, RegisteredActions } from "./register.js";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,YAAY,EACjB,SAAS,EACT,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,KAAK,eAAe,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,eAAe,EACf,KAAK,aAAa,GACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC3E,YAAY,EACV,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,cAAc,EACd,WAAW,EACX,aAAa,EACb,eAAe,EACf,aAAa,EACb,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,YAAY,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { appOrigin, createActionClient, generateClientId, } from "./action-client.js";
|
|
2
|
+
export { defineAgentView, } from "@parlor/agent-ui";
|
|
3
|
+
export { ACTION_QUERY_PREFIX, actionQueryKey } from "./query-keys.js";
|
|
4
|
+
export { createPluginClient } from "./plugin-client.js";
|
|
5
|
+
export { LinkOnComputer } from "./session.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,SAAS,EAIT,kBAAkB,EAClB,gBAAgB,GAEjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAIL,eAAe,GAEhB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAqB,MAAM,oBAAoB,CAAC;AAa3E,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC","sourcesContent":["export {\n type ActionClient,\n appOrigin,\n type ActionClientOptions,\n type ActionEnvelope,\n type CallOptions,\n createActionClient,\n generateClientId,\n type HeadersProvider,\n} from \"./action-client.js\";\nexport {\n type AgentViewBinding,\n type AgentViewDefinition,\n type AnyAgentView,\n defineAgentView,\n type WritableField,\n} from \"@parlor/agent-ui\";\nexport { ACTION_QUERY_PREFIX, actionQueryKey } from \"./query-keys.js\";\nexport { createPluginClient, type PluginClient } from \"./plugin-client.js\";\nexport type {\n AccountDevice,\n DeviceLinker,\n DeviceSource,\n FrameworkAuth,\n HostAuthorizer,\n PendingLink,\n SessionDevice,\n SessionSnapshot,\n SessionSource,\n SessionUser,\n} from \"./session.js\";\nexport { LinkOnComputer } from \"./session.js\";\nexport type { Register, RegisteredActions } from \"./register.js\";\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ActionMap } from "@parlor/core";
|
|
2
|
+
import { type ActionClient } from "./action-client.js";
|
|
3
|
+
/** A plugin's actions at `/_parlor/<plugin>/actions/<name>`, typed by its registry. */
|
|
4
|
+
export type PluginClient<T extends ActionMap> = Pick<ActionClient<T>, "call" | "invoke">;
|
|
5
|
+
/**
|
|
6
|
+
* Reuses the app client's base URL, headers provider (the session) and client id, so a
|
|
7
|
+
* plugin's actions travel exactly like the app's. A server without the plugin answers
|
|
8
|
+
* `not_found`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createPluginClient<T extends ActionMap>(client: Pick<ActionClient<ActionMap>, "basePath" | "clientId" | "request">, plugin: string): PluginClient<T>;
|
|
11
|
+
//# sourceMappingURL=plugin-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-client.d.ts","sourceRoot":"","sources":["../src/plugin-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,EAAE,KAAK,YAAY,EAAY,MAAM,oBAAoB,CAAC;AAEjE,uFAAuF;AACvF,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,SAAS,IAAI,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC;AAEzF;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,SAAS,EACpD,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC,EAC1E,MAAM,EAAE,MAAM,GACb,YAAY,CAAC,CAAC,CAAC,CAYjB"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { invokeAt } from "./action-client.js";
|
|
2
|
+
/**
|
|
3
|
+
* Reuses the app client's base URL, headers provider (the session) and client id, so a
|
|
4
|
+
* plugin's actions travel exactly like the app's. A server without the plugin answers
|
|
5
|
+
* `not_found`.
|
|
6
|
+
*/
|
|
7
|
+
export function createPluginClient(client, plugin) {
|
|
8
|
+
const invoke = (name, input, options) => invokeAt(client.request, `${client.basePath}/${plugin}/actions/${name}`, input, { ...options, missingIsNotFound: true });
|
|
9
|
+
return {
|
|
10
|
+
invoke,
|
|
11
|
+
call: async (name, input, options) => (await invoke(name, input, options)).data,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=plugin-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-client.js","sourceRoot":"","sources":["../src/plugin-client.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqB,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAKjE;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAA0E,EAC1E,MAAc;IAEd,MAAM,MAAM,GAA8B,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,CACjE,QAAQ,CACN,MAAM,CAAC,OAAO,EACd,GAAG,MAAM,CAAC,QAAQ,IAAI,MAAM,YAAY,IAAI,EAAE,EAC9C,KAAK,EACL,EAAE,GAAG,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,CACxC,CAAC;IACJ,OAAO;QACL,MAAM;QACN,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI;KAChF,CAAC;AACJ,CAAC","sourcesContent":["import type { ActionMap } from \"@parlor/core\";\n\nimport { type ActionClient, invokeAt } from \"./action-client.js\";\n\n/** A plugin's actions at `/_parlor/<plugin>/actions/<name>`, typed by its registry. */\nexport type PluginClient<T extends ActionMap> = Pick<ActionClient<T>, \"call\" | \"invoke\">;\n\n/**\n * Reuses the app client's base URL, headers provider (the session) and client id, so a\n * plugin's actions travel exactly like the app's. A server without the plugin answers\n * `not_found`.\n */\nexport function createPluginClient<T extends ActionMap>(\n client: Pick<ActionClient<ActionMap>, \"basePath\" | \"clientId\" | \"request\">,\n plugin: string,\n): PluginClient<T> {\n const invoke: PluginClient<T>[\"invoke\"] = (name, input, options) =>\n invokeAt<T, typeof name>(\n client.request,\n `${client.basePath}/${plugin}/actions/${name}`,\n input,\n { ...options, missingIsNotFound: true },\n );\n return {\n invoke,\n call: async (name, input, options) => (await invoke(name, input, options)).data,\n };\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-keys.d.ts","sourceRoot":"","sources":["../src/query-keys.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,YAAI,SAAS,EAAE,QAAQ,CAAU,CAAC;AAElE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,8CAE1D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-keys.js","sourceRoot":"","sources":["../src/query-keys.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAU,CAAC;AAElE,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,KAAc;IACzD,OAAO,CAAC,GAAG,mBAAmB,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAU,CAAC;AAC9D,CAAC","sourcesContent":["export const ACTION_QUERY_PREFIX = [\"_parlor\", \"action\"] as const;\n\nexport function actionQueryKey(name: string, input: unknown) {\n return [...ACTION_QUERY_PREFIX, name, input ?? {}] as const;\n}\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ActionError, ActionInput, ActionOutput } from "@parlor/core";
|
|
2
|
+
import { type QueryClient } from "@tanstack/react-query";
|
|
3
|
+
import type { ActionClient } from "../action-client.js";
|
|
4
|
+
import type { RegisteredActions } from "../register.js";
|
|
5
|
+
type Actions = RegisteredActions;
|
|
6
|
+
type Name = keyof Actions & string;
|
|
7
|
+
/** What reading an action outside a component needs; a router made by `createParlorRouter` passes it to every loader. */
|
|
8
|
+
export interface ActionQueryContext {
|
|
9
|
+
readonly client: ActionClient<RegisteredActions>;
|
|
10
|
+
readonly queryClient: QueryClient;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The query `useActionQuery(name, input)` runs, for `queryClient.prefetchQuery`, `useQueries`
|
|
14
|
+
* and the like. It reads the same cache entry and records the same read tags, so sync keeps it fresh.
|
|
15
|
+
*/
|
|
16
|
+
export declare function actionQueryOptions<K extends Name>({ client, queryClient }: ActionQueryContext, name: K, input: ActionInput<Actions[K]>): import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<unknown, ActionError, unknown, readonly unknown[]>, "queryFn"> & {
|
|
17
|
+
queryFn?: import("@tanstack/react-query").QueryFunction<unknown, readonly unknown[], never>;
|
|
18
|
+
} & import("@tanstack/react-query").QueryKeyWithDataTag<readonly unknown[], unknown, ActionError>;
|
|
19
|
+
/**
|
|
20
|
+
* The action's cached data, fetched first when there is none or when a write it reads has
|
|
21
|
+
* invalidated it since. A route `loader` awaits it so the page renders with its data, and
|
|
22
|
+
* `useActionSuspenseQuery` with the same name and input reads it and keeps it fresh.
|
|
23
|
+
*/
|
|
24
|
+
export declare function ensureActionQueryData<K extends Name>(context: ActionQueryContext, name: K, input: ActionInput<Actions[K]>): Promise<ActionOutput<Actions[K]>>;
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=action-query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-query.d.ts","sourceRoot":"","sources":["../../src/react/action-query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,EAAW,KAAK,WAAW,EAAgB,MAAM,uBAAuB,CAAC;AAEhF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAIxD,KAAK,OAAO,GAAG,iBAAiB,CAAC;AACjC,KAAK,IAAI,GAAG,MAAM,OAAO,GAAG,MAAM,CAAC;AAEnC,yHAAyH;AACzH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,iBAAiB,CAAC,CAAC;IACjD,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;CACnC;AASD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,IAAI,EAC/C,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,kBAAkB,EAC3C,IAAI,EAAE,CAAC,EACP,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;;kGAc/B;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,IAAI,EAClD,OAAO,EAAE,kBAAkB,EAC3B,IAAI,EAAE,CAAC,EACP,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAC7B,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAKnC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { hashKey, queryOptions } from "@tanstack/react-query";
|
|
2
|
+
import { actionQueryKey } from "../query-keys.js";
|
|
3
|
+
import { actionRetry } from "./retry.js";
|
|
4
|
+
import { readTagsOf } from "./sync.js";
|
|
5
|
+
/** Sync refetches what a write makes out of date, so action data stays fresh until then. */
|
|
6
|
+
const freshUntilInvalidated = ({ state, }) => (state.data === undefined || state.isInvalidated ? 0 : "static");
|
|
7
|
+
/**
|
|
8
|
+
* The query `useActionQuery(name, input)` runs, for `queryClient.prefetchQuery`, `useQueries`
|
|
9
|
+
* and the like. It reads the same cache entry and records the same read tags, so sync keeps it fresh.
|
|
10
|
+
*/
|
|
11
|
+
export function actionQueryOptions({ client, queryClient }, name, input) {
|
|
12
|
+
const queryKey = actionQueryKey(name, input);
|
|
13
|
+
return queryOptions({
|
|
14
|
+
queryKey,
|
|
15
|
+
queryFn: ({ signal, client: cache }) => readTagsOf(cache).read(hashKey(queryKey), () => client.invoke(name, input, { signal })),
|
|
16
|
+
retry: actionRetry(queryClient.getDefaultOptions().queries?.retry),
|
|
17
|
+
...(queryClient.getDefaultOptions().queries?.staleTime === undefined
|
|
18
|
+
? { staleTime: freshUntilInvalidated }
|
|
19
|
+
: {}),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The action's cached data, fetched first when there is none or when a write it reads has
|
|
24
|
+
* invalidated it since. A route `loader` awaits it so the page renders with its data, and
|
|
25
|
+
* `useActionSuspenseQuery` with the same name and input reads it and keeps it fresh.
|
|
26
|
+
*/
|
|
27
|
+
export function ensureActionQueryData(context, name, input) {
|
|
28
|
+
return context.queryClient.query({
|
|
29
|
+
...actionQueryOptions(context, name, input),
|
|
30
|
+
staleTime: freshUntilInvalidated,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=action-query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-query.js","sourceRoot":"","sources":["../../src/react/action-query.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAoB,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGhF,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAWvC,4FAA4F;AAC5F,MAAM,qBAAqB,GAAG,CAAC,EAC7B,KAAK,GAGN,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;AAEvE;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,EAAE,MAAM,EAAE,WAAW,EAAsB,EAC3C,IAAO,EACP,KAA8B;IAE9B,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC7C,OAAO,YAAY,CAAkE;QACnF,QAAQ;QACR,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,CACrC,UAAU,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,CAC7C,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,CACvC;QACH,KAAK,EAAE,WAAW,CAAC,WAAW,CAAC,iBAAiB,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC;QAClE,GAAG,CAAC,WAAW,CAAC,iBAAiB,EAAE,CAAC,OAAO,EAAE,SAAS,KAAK,SAAS;YAClE,CAAC,CAAC,EAAE,SAAS,EAAE,qBAAqB,EAAE;YACtC,CAAC,CAAC,EAAE,CAAC;KACR,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CACnC,OAA2B,EAC3B,IAAO,EACP,KAA8B;IAE9B,OAAO,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC;QAC/B,GAAG,kBAAkB,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC;QAC3C,SAAS,EAAE,qBAAqB;KACjC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { ActionError, ActionInput, ActionOutput } from \"@parlor/core\";\nimport { hashKey, type QueryClient, queryOptions } from \"@tanstack/react-query\";\n\nimport type { ActionClient } from \"../action-client.js\";\nimport { actionQueryKey } from \"../query-keys.js\";\nimport type { RegisteredActions } from \"../register.js\";\nimport { actionRetry } from \"./retry.js\";\nimport { readTagsOf } from \"./sync.js\";\n\ntype Actions = RegisteredActions;\ntype Name = keyof Actions & string;\n\n/** What reading an action outside a component needs; a router made by `createParlorRouter` passes it to every loader. */\nexport interface ActionQueryContext {\n readonly client: ActionClient<RegisteredActions>;\n readonly queryClient: QueryClient;\n}\n\n/** Sync refetches what a write makes out of date, so action data stays fresh until then. */\nconst freshUntilInvalidated = ({\n state,\n}: {\n readonly state: { readonly data: unknown; readonly isInvalidated: boolean };\n}) => (state.data === undefined || state.isInvalidated ? 0 : \"static\");\n\n/**\n * The query `useActionQuery(name, input)` runs, for `queryClient.prefetchQuery`, `useQueries`\n * and the like. It reads the same cache entry and records the same read tags, so sync keeps it fresh.\n */\nexport function actionQueryOptions<K extends Name>(\n { client, queryClient }: ActionQueryContext,\n name: K,\n input: ActionInput<Actions[K]>,\n) {\n const queryKey = actionQueryKey(name, input);\n return queryOptions<ActionOutput<Actions[K]>, ActionError, ActionOutput<Actions[K]>>({\n queryKey,\n queryFn: ({ signal, client: cache }) =>\n readTagsOf(cache).read(hashKey(queryKey), () =>\n client.invoke(name, input, { signal }),\n ),\n retry: actionRetry(queryClient.getDefaultOptions().queries?.retry),\n ...(queryClient.getDefaultOptions().queries?.staleTime === undefined\n ? { staleTime: freshUntilInvalidated }\n : {}),\n });\n}\n\n/**\n * The action's cached data, fetched first when there is none or when a write it reads has\n * invalidated it since. A route `loader` awaits it so the page renders with its data, and\n * `useActionSuspenseQuery` with the same name and input reads it and keeps it fresh.\n */\nexport function ensureActionQueryData<K extends Name>(\n context: ActionQueryContext,\n name: K,\n input: ActionInput<Actions[K]>,\n): Promise<ActionOutput<Actions[K]>> {\n return context.queryClient.query({\n ...actionQueryOptions(context, name, input),\n staleTime: freshUntilInvalidated,\n });\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AgentOrigin, UiErrorCode } from "@parlor/agent-ui";
|
|
2
|
+
import type { AgentActivity, AgentActivityKind } from "@parlor/agent-ui/client";
|
|
3
|
+
export type AgentActivityState = {
|
|
4
|
+
readonly status: "idle";
|
|
5
|
+
} | {
|
|
6
|
+
readonly status: "busy";
|
|
7
|
+
readonly op: AgentActivityKind;
|
|
8
|
+
readonly origin: AgentOrigin;
|
|
9
|
+
} | {
|
|
10
|
+
readonly status: "done";
|
|
11
|
+
readonly op: AgentActivityKind;
|
|
12
|
+
readonly origin: AgentOrigin;
|
|
13
|
+
} | {
|
|
14
|
+
readonly status: "refused";
|
|
15
|
+
readonly op: AgentActivityKind;
|
|
16
|
+
readonly origin: AgentOrigin;
|
|
17
|
+
readonly code: UiErrorCode;
|
|
18
|
+
};
|
|
19
|
+
export declare function deriveActivityState({ busy, last }: AgentActivity, faded: AgentActivity["last"]): AgentActivityState;
|
|
20
|
+
//# sourceMappingURL=activity-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity-state.d.ts","sourceRoot":"","sources":["../../../src/react/agent-ui/activity-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEhF,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC3B;IACE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,iBAAiB,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;CAC9B,GACD;IACE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,iBAAiB,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;CAC9B,GACD;IACE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,EAAE,iBAAiB,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;CAC5B,CAAC;AAEN,wBAAgB,mBAAmB,CACjC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,aAAa,EAC7B,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,GAC3B,kBAAkB,CASpB"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function deriveActivityState({ busy, last }, faded) {
|
|
2
|
+
if (!last)
|
|
3
|
+
return { status: "idle" };
|
|
4
|
+
const { op, origin, outcome } = last;
|
|
5
|
+
if (busy)
|
|
6
|
+
return { status: "busy", op, origin };
|
|
7
|
+
if (last === faded)
|
|
8
|
+
return { status: "idle" };
|
|
9
|
+
if (outcome?.status === "refused") {
|
|
10
|
+
return { status: "refused", op, origin, code: outcome.code };
|
|
11
|
+
}
|
|
12
|
+
return { status: "done", op, origin };
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=activity-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity-state.js","sourceRoot":"","sources":["../../../src/react/agent-ui/activity-state.ts"],"names":[],"mappings":"AAsBA,MAAM,UAAU,mBAAmB,CACjC,EAAE,IAAI,EAAE,IAAI,EAAiB,EAC7B,KAA4B;IAE5B,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IACrC,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACrC,IAAI,IAAI;QAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC;IAChD,IAAI,IAAI,KAAK,KAAK;QAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC9C,IAAI,OAAO,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;QAClC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;IAC/D,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC;AACxC,CAAC","sourcesContent":["import type { AgentOrigin, UiErrorCode } from \"@parlor/agent-ui\";\nimport type { AgentActivity, AgentActivityKind } from \"@parlor/agent-ui/client\";\n\nexport type AgentActivityState =\n | { readonly status: \"idle\" }\n | {\n readonly status: \"busy\";\n readonly op: AgentActivityKind;\n readonly origin: AgentOrigin;\n }\n | {\n readonly status: \"done\";\n readonly op: AgentActivityKind;\n readonly origin: AgentOrigin;\n }\n | {\n readonly status: \"refused\";\n readonly op: AgentActivityKind;\n readonly origin: AgentOrigin;\n readonly code: UiErrorCode;\n };\n\nexport function deriveActivityState(\n { busy, last }: AgentActivity,\n faded: AgentActivity[\"last\"],\n): AgentActivityState {\n if (!last) return { status: \"idle\" };\n const { op, origin, outcome } = last;\n if (busy) return { status: \"busy\", op, origin };\n if (last === faded) return { status: \"idle\" };\n if (outcome?.status === \"refused\") {\n return { status: \"refused\", op, origin, code: outcome.code };\n }\n return { status: \"done\", op, origin };\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ComponentProps, type ReactNode } from "react";
|
|
2
|
+
import { type AgentActivityState } from "./activity-state.js";
|
|
3
|
+
export interface ChatActivityProps extends Omit<ComponentProps<"span">, "children"> {
|
|
4
|
+
/** Milliseconds a finished event stays `done` or `refused` before it reads `idle`. */
|
|
5
|
+
readonly linger?: number;
|
|
6
|
+
readonly children: (state: AgentActivityState) => ReactNode;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A polite live region that says what an agent is doing on this screen. The app writes the
|
|
10
|
+
* words; style it through `[data-state]` and `[data-op]`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function Activity({ linger, children, ...props }: ChatActivityProps): import("react").JSX.Element;
|
|
13
|
+
//# sourceMappingURL=chat-activity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-activity.d.ts","sourceRoot":"","sources":["../../../src/react/agent-ui/chat-activity.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,SAAS,EAAuB,MAAM,OAAO,CAAC;AAEjF,OAAO,EAAE,KAAK,kBAAkB,EAAuB,MAAM,qBAAqB,CAAC;AAiBnF,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC;IACjF,sFAAsF;IACtF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,SAAS,CAAC;CAC7D;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,EACvB,MAA0B,EAC1B,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,iBAAiB,+BAanB"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { deriveActivityState } from "./activity-state.js";
|
|
4
|
+
import { useAgentActivity } from "./use-agent-activity.js";
|
|
5
|
+
const DEFAULT_LINGER_MS = 2000;
|
|
6
|
+
function useActivityState(lingerMs) {
|
|
7
|
+
const activity = useAgentActivity();
|
|
8
|
+
const [faded, setFaded] = useState();
|
|
9
|
+
const { busy, last } = activity;
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (busy || !last)
|
|
12
|
+
return;
|
|
13
|
+
const timer = setTimeout(() => setFaded(last), lingerMs);
|
|
14
|
+
return () => clearTimeout(timer);
|
|
15
|
+
}, [busy, last, lingerMs]);
|
|
16
|
+
return deriveActivityState(activity, faded);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A polite live region that says what an agent is doing on this screen. The app writes the
|
|
20
|
+
* words; style it through `[data-state]` and `[data-op]`.
|
|
21
|
+
*/
|
|
22
|
+
export function Activity({ linger = DEFAULT_LINGER_MS, children, ...props }) {
|
|
23
|
+
const state = useActivityState(linger);
|
|
24
|
+
return (_jsx("span", { role: "status", "aria-live": "polite", ...props, "data-state": state.status, "data-op": state.status === "idle" ? undefined : state.op, children: children(state) }));
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=chat-activity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-activity.js","sourceRoot":"","sources":["../../../src/react/agent-ui/chat-activity.tsx"],"names":[],"mappings":";AACA,OAAO,EAAuC,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjF,OAAO,EAA2B,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AACnF,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAE/B,SAAS,gBAAgB,CAAC,QAAgB;IACxC,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;IACpC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,EAAyB,CAAC;IAC5D,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC;IAChC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO;QAC1B,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC;QACzD,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC3B,OAAO,mBAAmB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;AAC9C,CAAC;AAQD;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,EACvB,MAAM,GAAG,iBAAiB,EAC1B,QAAQ,EACR,GAAG,KAAK,EACU;IAClB,MAAM,KAAK,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACvC,OAAO,CACL,eACE,IAAI,EAAC,QAAQ,eACH,QAAQ,KACd,KAAK,gBACG,KAAK,CAAC,MAAM,aACf,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,YAEtD,QAAQ,CAAC,KAAK,CAAC,GACX,CACR,CAAC;AACJ,CAAC","sourcesContent":["import type { AgentActivity } from \"@parlor/agent-ui/client\";\nimport { type ComponentProps, type ReactNode, useEffect, useState } from \"react\";\n\nimport { type AgentActivityState, deriveActivityState } from \"./activity-state.js\";\nimport { useAgentActivity } from \"./use-agent-activity.js\";\n\nconst DEFAULT_LINGER_MS = 2000;\n\nfunction useActivityState(lingerMs: number): AgentActivityState {\n const activity = useAgentActivity();\n const [faded, setFaded] = useState<AgentActivity[\"last\"]>();\n const { busy, last } = activity;\n useEffect(() => {\n if (busy || !last) return;\n const timer = setTimeout(() => setFaded(last), lingerMs);\n return () => clearTimeout(timer);\n }, [busy, last, lingerMs]);\n return deriveActivityState(activity, faded);\n}\n\nexport interface ChatActivityProps extends Omit<ComponentProps<\"span\">, \"children\"> {\n /** Milliseconds a finished event stays `done` or `refused` before it reads `idle`. */\n readonly linger?: number;\n readonly children: (state: AgentActivityState) => ReactNode;\n}\n\n/**\n * A polite live region that says what an agent is doing on this screen. The app writes the\n * words; style it through `[data-state]` and `[data-op]`.\n */\nexport function Activity({\n linger = DEFAULT_LINGER_MS,\n children,\n ...props\n}: ChatActivityProps) {\n const state = useActivityState(linger);\n return (\n <span\n role=\"status\"\n aria-live=\"polite\"\n {...props}\n data-state={state.status}\n data-op={state.status === \"idle\" ? undefined : state.op}\n >\n {children(state)}\n </span>\n );\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AgentConfirmation } from "@parlor/agent-ui/client";
|
|
2
|
+
import { type ComponentProps, type ReactNode } from "react";
|
|
3
|
+
export interface ChatConfirmationsProps extends Omit<ComponentProps<"div">, "children"> {
|
|
4
|
+
/** Shown while no agent request waits. */
|
|
5
|
+
readonly empty?: ReactNode;
|
|
6
|
+
readonly children: (confirmation: AgentConfirmation) => ReactNode;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Lists the agent requests waiting for this user, newest last. Rendering it is what tells
|
|
10
|
+
* the server this client can show a confirmation, so keep it mounted wherever the user can
|
|
11
|
+
* answer. `data-pending` carries the count while any wait. When the card holding focus goes
|
|
12
|
+
* away, focus moves to the next card, or to the list when none is left, so it never falls
|
|
13
|
+
* out of an enclosing dialog.
|
|
14
|
+
*/
|
|
15
|
+
export declare function Confirmations({ empty, children, ref, ...props }: ChatConfirmationsProps): import("react").JSX.Element;
|
|
16
|
+
export interface ChatConfirmProps extends ComponentProps<"div"> {
|
|
17
|
+
readonly confirmation: AgentConfirmation;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* One request as a labelled group. Escape inside it declines, and stops there so an
|
|
21
|
+
* enclosing dialog stays open. It never takes focus by itself, but it can hold it
|
|
22
|
+
* (`tabIndex` -1) so `Chat.Confirmations` can keep focus in the list.
|
|
23
|
+
*/
|
|
24
|
+
export declare function Confirm({ confirmation, onKeyDown, ref, ...props }: ChatConfirmProps): import("react").JSX.Element;
|
|
25
|
+
export declare function ConfirmLabel(props: Omit<ComponentProps<"p">, "id">): import("react").JSX.Element;
|
|
26
|
+
export declare function Approve({ onClick, ...props }: ComponentProps<"button">): import("react").JSX.Element;
|
|
27
|
+
export declare function Decline({ onClick, ...props }: ComponentProps<"button">): import("react").JSX.Element;
|
|
28
|
+
//# sourceMappingURL=chat-confirm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-confirm.d.ts","sourceRoot":"","sources":["../../../src/react/agent-ui/chat-confirm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EACL,KAAK,cAAc,EAInB,KAAK,SAAS,EAQf,MAAM,OAAO,CAAC;AAkCf,MAAM,WAAW,sBAAuB,SAAQ,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IACrF,0CAA0C;IAC1C,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,CAAC,YAAY,EAAE,iBAAiB,KAAK,SAAS,CAAC;CACnE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,GAAG,EACH,GAAG,KAAK,EACT,EAAE,sBAAsB,+BAoExB;AAED,MAAM,WAAW,gBAAiB,SAAQ,cAAc,CAAC,KAAK,CAAC;IAC7D,QAAQ,CAAC,YAAY,EAAE,iBAAiB,CAAC;CAC1C;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,EAAE,YAAY,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,EAAE,gBAAgB,+BA6CnF;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,+BAGlE;AAED,wBAAgB,OAAO,CAAC,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,CAAC,QAAQ,CAAC,+BAYtE;AAED,wBAAgB,OAAO,CAAC,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,CAAC,QAAQ,CAAC,+BAYtE"}
|