@parlor/client 0.1.0 → 0.3.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.
Files changed (96) hide show
  1. package/README.md +2 -0
  2. package/dist/action-client.d.ts +2 -0
  3. package/dist/action-client.d.ts.map +1 -1
  4. package/dist/action-client.js +13 -4
  5. package/dist/action-client.js.map +1 -1
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/plugin-client.js.map +1 -1
  10. package/dist/query-keys.js.map +1 -1
  11. package/dist/react/action-query.d.ts +26 -0
  12. package/dist/react/action-query.d.ts.map +1 -0
  13. package/dist/react/action-query.js +33 -0
  14. package/dist/react/action-query.js.map +1 -0
  15. package/dist/react/agent-ui/activity-state.js.map +1 -1
  16. package/dist/react/agent-ui/chat-activity.js.map +1 -1
  17. package/dist/react/agent-ui/chat-confirm.js.map +1 -1
  18. package/dist/react/agent-ui/chat.js.map +1 -1
  19. package/dist/react/agent-ui/dev.js.map +1 -1
  20. package/dist/react/agent-ui/runtime.js.map +1 -1
  21. package/dist/react/agent-ui/use-agent-activity.js.map +1 -1
  22. package/dist/react/agent-ui/use-agent-confirmations.js.map +1 -1
  23. package/dist/react/agent-ui/use-agent-view.js.map +1 -1
  24. package/dist/react/chat/ask-fields.js.map +1 -1
  25. package/dist/react/chat/chat-ask.d.ts +3 -2
  26. package/dist/react/chat/chat-ask.d.ts.map +1 -1
  27. package/dist/react/chat/chat-ask.js +3 -2
  28. package/dist/react/chat/chat-ask.js.map +1 -1
  29. package/dist/react/chat/chat-composer.d.ts.map +1 -1
  30. package/dist/react/chat/chat-composer.js +1 -1
  31. package/dist/react/chat/chat-composer.js.map +1 -1
  32. package/dist/react/chat/chat-root.js.map +1 -1
  33. package/dist/react/chat/chat-thread.js.map +1 -1
  34. package/dist/react/chat/context.js.map +1 -1
  35. package/dist/react/chat/create.js.map +1 -1
  36. package/dist/react/chat/open-turns.js.map +1 -1
  37. package/dist/react/chat/runtime.d.ts +29 -4
  38. package/dist/react/chat/runtime.d.ts.map +1 -1
  39. package/dist/react/chat/runtime.js +82 -11
  40. package/dist/react/chat/runtime.js.map +1 -1
  41. package/dist/react/chat/use-chat.d.ts.map +1 -1
  42. package/dist/react/chat/use-chat.js +2 -0
  43. package/dist/react/chat/use-chat.js.map +1 -1
  44. package/dist/react/chat/use-device-setup.js.map +1 -1
  45. package/dist/react/hooks.d.ts +7 -1
  46. package/dist/react/hooks.d.ts.map +1 -1
  47. package/dist/react/hooks.js +17 -13
  48. package/dist/react/hooks.js.map +1 -1
  49. package/dist/react/index.d.ts +3 -2
  50. package/dist/react/index.d.ts.map +1 -1
  51. package/dist/react/index.js +2 -1
  52. package/dist/react/index.js.map +1 -1
  53. package/dist/react/provider.d.ts +6 -3
  54. package/dist/react/provider.d.ts.map +1 -1
  55. package/dist/react/provider.js +61 -11
  56. package/dist/react/provider.js.map +1 -1
  57. package/dist/react/retry.js.map +1 -1
  58. package/dist/react/sync-bridge.d.ts +4 -1
  59. package/dist/react/sync-bridge.d.ts.map +1 -1
  60. package/dist/react/sync-bridge.js.map +1 -1
  61. package/dist/react/sync.d.ts +41 -2
  62. package/dist/react/sync.d.ts.map +1 -1
  63. package/dist/react/sync.js +127 -9
  64. package/dist/react/sync.js.map +1 -1
  65. package/dist/react/use-capability.js.map +1 -1
  66. package/dist/react/use-devices.js.map +1 -1
  67. package/dist/react/use-link-device.js.map +1 -1
  68. package/dist/register.js.map +1 -1
  69. package/dist/router/errors.d.ts +13 -0
  70. package/dist/router/errors.d.ts.map +1 -0
  71. package/dist/router/errors.js +16 -0
  72. package/dist/router/errors.js.map +1 -0
  73. package/dist/router/index.d.ts +4 -0
  74. package/dist/router/index.d.ts.map +1 -0
  75. package/dist/router/index.js +4 -0
  76. package/dist/router/index.js.map +1 -0
  77. package/dist/router/mount.d.ts +22 -0
  78. package/dist/router/mount.d.ts.map +1 -0
  79. package/dist/router/mount.js +27 -0
  80. package/dist/router/mount.js.map +1 -0
  81. package/dist/router/router.d.ts +29 -0
  82. package/dist/router/router.d.ts.map +1 -0
  83. package/dist/router/router.js +44 -0
  84. package/dist/router/router.js.map +1 -0
  85. package/dist/session.d.ts +7 -1
  86. package/dist/session.d.ts.map +1 -1
  87. package/dist/session.js.map +1 -1
  88. package/dist/ssr/index.d.ts +2 -0
  89. package/dist/ssr/index.d.ts.map +1 -0
  90. package/dist/ssr/index.js +2 -0
  91. package/dist/ssr/index.js.map +1 -0
  92. package/dist/ssr/render.d.ts +18 -0
  93. package/dist/ssr/render.d.ts.map +1 -0
  94. package/dist/ssr/render.js +96 -0
  95. package/dist/ssr/render.js.map +1 -0
  96. package/package.json +15 -7
@@ -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
- #tags = new Map();
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
- if (event.type === "removed")
8
- this.#tags.delete(event.query.queryHash);
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
- record(queryHash, tags) {
12
- this.#tags.set(queryHash, tags);
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.#tags.get(queryHash);
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, reads] of this.#tags) {
20
- if (reads.some((read) => events.some((e) => e.tags.some((write) => tagsOverlap(read, write))))) {
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
- const hashes = registry.affected(events);
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({
@@ -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":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE/D,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAsB7C,MAAM,IAAI,GAAoB,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAE/C,MAAM,SAAS,GAAG,CAAC,KAAc,EAAU,EAAE;IAC3C,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;QAC/D,OAAO,2DAA2D,CAAC;IACrE,CAAC;IACD,OAAO,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO;QAC5C,CAAC,CAAC,KAAK,CAAC,OAAO;QACf,CAAC,CAAC,+BAA+B,CAAC;AACtC,CAAC,CAAC;AAEF,MAAM,UAAU,aAAa;IAU3B,MAAM,EAAE,IAAI,EAAE,GAAG,YAAY,EAAE,CAAC;IAChC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAkB,IAAI,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAG,MAAM,CAA0B,SAAS,CAAC,CAAC;IAC3D,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,EAAE,WAAW,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC;IAE5E,MAAM,KAAK,GAAG,WAAW,CACvB,KAAK,EAAE,IAAyE,EAAE,EAAE;QAClF,IAAI,CAAC,IAAI,EAAE,WAAW,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,OAAO;QACjD,QAAQ,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YAC1C,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;YACvB,MAAM,MAAM,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC5D,QAAQ,CACN,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACrB,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE;gBAC7B,CAAC,CAAC;oBACE,IAAI,EAAE,aAAa;oBACnB,MAAM;oBACN,OAAO,EAAE,+BAA+B,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,EAAE;iBACnE,CACN,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACzD,CAAC;IACH,CAAC,EACD,CAAC,IAAI,CAAC,CACP,CAAC;IAEF,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACrC,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;QAC7B,IAAI,CAAC,IAAI,EAAE,KAAK,IAAI,CAAC,IAAI;YAAE,OAAO;QAClC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACvB,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;YAC5B,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAChD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACzD,CAAC;IACH,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;QAC5B,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,OAAO,CACZ,GAAG,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EACnD,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,CAC1C,CAAC;AACJ,CAAC","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"]}
@@ -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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/router/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAwB,MAAM,cAAc,CAAC;AACjE,OAAO,EAAwC,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAOxF,MAAM,cAAc,GAAsB,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC;AAE1E;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAM,GAAsB,EAAE;IAE9B,MAAM,QAAQ,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAC;IAClD,OAAO,CAAC,KAAK,EAAE,EAAE;QACf,MAAM,KAAK,GAAG,KAAK,YAAY,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9E,MAAM,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAoB,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACpD,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { ActionError, type ActionErrorCode } from \"@parlor/core\";\nimport { type AnyRedirect, type NotFoundError, notFound } from \"@tanstack/react-router\";\n\n/** For each `ActionError` code, the `redirect(...)` or `notFound(...)` a route throws instead. */\nexport type ActionErrorRoutes = {\n readonly [C in ActionErrorCode]?: (error: ActionError) => AnyRedirect | NotFoundError;\n};\n\nconst DEFAULT_ROUTES: ActionErrorRoutes = { not_found: () => notFound() };\n\n/**\n * Rethrows an `ActionError` as what `routes` names for its code, `not_found` as `notFound()`\n * unless `routes` says otherwise, and anything else unchanged for the route's `errorComponent`.\n * Pass it as a route's `onError`, or to `.catch` in a loader.\n */\nexport function routeActionErrors(\n routes: ActionErrorRoutes = {},\n): (error: unknown) => never {\n const handlers = { ...DEFAULT_ROUTES, ...routes };\n return (error) => {\n const route = error instanceof ActionError ? handlers[error.code] : undefined;\n throw route ? route(error as ActionError) : error;\n };\n}\n"]}
@@ -0,0 +1,4 @@
1
+ export { type ActionErrorRoutes, routeActionErrors } from "./errors.js";
2
+ export { type CreateParlorApp, mount, type ParlorApp } from "./mount.js";
3
+ export { createParlorRouter, type ParlorRouterContext, type ParlorRouterOptions, } from "./router.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/router/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EACL,kBAAkB,EAClB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GACzB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { routeActionErrors } from "./errors.js";
2
+ export { mount } from "./mount.js";
3
+ export { createParlorRouter, } from "./router.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/router/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAwB,KAAK,EAAkB,MAAM,YAAY,CAAC;AACzE,OAAO,EACL,kBAAkB,GAGnB,MAAM,aAAa,CAAC","sourcesContent":["export { type ActionErrorRoutes, routeActionErrors } from \"./errors.js\";\nexport { type CreateParlorApp, mount, type ParlorApp } from \"./mount.js\";\nexport {\n createParlorRouter,\n type ParlorRouterContext,\n type ParlorRouterOptions,\n} from \"./router.js\";\n"]}
@@ -0,0 +1,22 @@
1
+ import type { AnyRouter } from "@tanstack/react-router";
2
+ import type { ReactNode } from "react";
3
+ import type { ActionClient } from "../action-client.js";
4
+ import type { RegisteredActions } from "../register.js";
5
+ /** Set on the page's container when the server rendered the app into it. */
6
+ export declare const SERVER_RENDERED = "data-parlor-ssr";
7
+ /** One instance of the app: its router and the element that renders it. */
8
+ export interface ParlorApp<TRouter extends AnyRouter = AnyRouter> {
9
+ readonly router: TRouter;
10
+ readonly element: ReactNode;
11
+ }
12
+ /**
13
+ * Builds the app around an action client. The browser calls it once; with `web.ssr`, the
14
+ * server calls it for every page it renders, with a client that reaches the app in process.
15
+ */
16
+ export type CreateParlorApp<TRouter extends AnyRouter = AnyRouter> = (client: ActionClient<RegisteredActions>) => ParlorApp<TRouter>;
17
+ /**
18
+ * Starts the app in `container`: it hydrates what the server rendered there, and renders from
19
+ * scratch otherwise, as on a page the server did not render and in the native shells.
20
+ */
21
+ export declare function mount(container: Element, { router, element }: ParlorApp): void;
22
+ //# sourceMappingURL=mount.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../../src/router/mount.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,4EAA4E;AAC5E,eAAO,MAAM,eAAe,oBAAoB,CAAC;AAEjD,2EAA2E;AAC3E,MAAM,WAAW,SAAS,CAAC,OAAO,SAAS,SAAS,GAAG,SAAS;IAC9D,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,CAAC,OAAO,SAAS,SAAS,GAAG,SAAS,IAAI,CACnE,MAAM,EAAE,YAAY,CAAC,iBAAiB,CAAC,KACpC,SAAS,CAAC,OAAO,CAAC,CAAC;AAMxB;;;GAGG;AACH,wBAAgB,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,SAAS,GAAG,IAAI,CAiB9E"}
@@ -0,0 +1,27 @@
1
+ import { hydrate } from "@tanstack/react-router/ssr/client";
2
+ import { createRoot, hydrateRoot } from "react-dom/client";
3
+ /** Set on the page's container when the server rendered the app into it. */
4
+ export const SERVER_RENDERED = "data-parlor-ssr";
5
+ function render(container, element) {
6
+ createRoot(container).render(element);
7
+ }
8
+ /**
9
+ * Starts the app in `container`: it hydrates what the server rendered there, and renders from
10
+ * scratch otherwise, as on a page the server did not render and in the native shells.
11
+ */
12
+ export function mount(container, { router, element }) {
13
+ if (!container.hasAttribute(SERVER_RENDERED)) {
14
+ render(container, element);
15
+ return;
16
+ }
17
+ hydrate(router)
18
+ .finally(() => globalThis.window?.$_TSR?.h())
19
+ .then(() => {
20
+ hydrateRoot(container, element);
21
+ }, (error) => {
22
+ console.error("[parlor] Could not hydrate the server-rendered page:", error);
23
+ container.replaceChildren();
24
+ render(container, element);
25
+ });
26
+ }
27
+ //# sourceMappingURL=mount.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mount.js","sourceRoot":"","sources":["../../src/router/mount.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,mCAAmC,CAAC;AAE5D,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAK3D,4EAA4E;AAC5E,MAAM,CAAC,MAAM,eAAe,GAAG,iBAAiB,CAAC;AAgBjD,SAAS,MAAM,CAAC,SAAkB,EAAE,OAAkB;IACpD,UAAU,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,KAAK,CAAC,SAAkB,EAAE,EAAE,MAAM,EAAE,OAAO,EAAa;IACtE,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,eAAe,CAAC,EAAE,CAAC;QAC7C,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IACD,OAAO,CAAC,MAAM,CAAC;SACZ,OAAO,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;SAC5C,IAAI,CACH,GAAG,EAAE;QACH,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC,EACD,CAAC,KAAc,EAAE,EAAE;QACjB,OAAO,CAAC,KAAK,CAAC,sDAAsD,EAAE,KAAK,CAAC,CAAC;QAC7E,SAAS,CAAC,eAAe,EAAE,CAAC;QAC5B,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC7B,CAAC,CACF,CAAC;AACN,CAAC","sourcesContent":["import type { AnyRouter } from \"@tanstack/react-router\";\nimport { hydrate } from \"@tanstack/react-router/ssr/client\";\nimport type { ReactNode } from \"react\";\nimport { createRoot, hydrateRoot } from \"react-dom/client\";\n\nimport type { ActionClient } from \"../action-client.js\";\nimport type { RegisteredActions } from \"../register.js\";\n\n/** Set on the page's container when the server rendered the app into it. */\nexport const SERVER_RENDERED = \"data-parlor-ssr\";\n\n/** One instance of the app: its router and the element that renders it. */\nexport interface ParlorApp<TRouter extends AnyRouter = AnyRouter> {\n readonly router: TRouter;\n readonly element: ReactNode;\n}\n\n/**\n * Builds the app around an action client. The browser calls it once; with `web.ssr`, the\n * server calls it for every page it renders, with a client that reaches the app in process.\n */\nexport type CreateParlorApp<TRouter extends AnyRouter = AnyRouter> = (\n client: ActionClient<RegisteredActions>,\n) => ParlorApp<TRouter>;\n\nfunction render(container: Element, element: ReactNode): void {\n createRoot(container).render(element);\n}\n\n/**\n * Starts the app in `container`: it hydrates what the server rendered there, and renders from\n * scratch otherwise, as on a page the server did not render and in the native shells.\n */\nexport function mount(container: Element, { router, element }: ParlorApp): void {\n if (!container.hasAttribute(SERVER_RENDERED)) {\n render(container, element);\n return;\n }\n hydrate(router)\n .finally(() => globalThis.window?.$_TSR?.h())\n .then(\n () => {\n hydrateRoot(container, element);\n },\n (error: unknown) => {\n console.error(\"[parlor] Could not hydrate the server-rendered page:\", error);\n container.replaceChildren();\n render(container, element);\n },\n );\n}\n"]}
@@ -0,0 +1,29 @@
1
+ import { QueryClient } from "@tanstack/react-query";
2
+ import { type AnyRoute, type RouterConstructorOptions, type RouterHistory, type TrailingSlashOption } from "@tanstack/react-router";
3
+ import type { ActionClient } from "../action-client.js";
4
+ import type { ActionQueryContext } from "../react/action-query.js";
5
+ import type { RegisteredActions } from "../register.js";
6
+ /** The router context Parlor's loaders read: `createRootRouteWithContext<ParlorRouterContext>()`. */
7
+ export type ParlorRouterContext = ActionQueryContext;
8
+ type Dehydrated = Record<string, unknown>;
9
+ type RouterOptions<TRouteTree extends AnyRoute, TTrailingSlashOption extends TrailingSlashOption, TDefaultStructuralSharingOption extends boolean, TRouterHistory extends RouterHistory> = RouterConstructorOptions<TRouteTree, TTrailingSlashOption, TDefaultStructuralSharingOption, TRouterHistory, Dehydrated>;
10
+ type AppContext<TRouteTree extends AnyRoute> = Omit<NonNullable<RouterOptions<TRouteTree, "never", false, RouterHistory>["context"]>, keyof ParlorRouterContext>;
11
+ export type ParlorRouterOptions<TRouteTree extends AnyRoute, TTrailingSlashOption extends TrailingSlashOption = "never", TDefaultStructuralSharingOption extends boolean = false, TRouterHistory extends RouterHistory = RouterHistory> = Omit<RouterOptions<TRouteTree, TTrailingSlashOption, TDefaultStructuralSharingOption, TRouterHistory>, "context"> & {
12
+ readonly client: ActionClient<RegisteredActions>;
13
+ readonly queryClient?: QueryClient | undefined;
14
+ } & ({} extends AppContext<TRouteTree> ? {
15
+ readonly context?: AppContext<TRouteTree>;
16
+ } : {
17
+ readonly context: AppContext<TRouteTree>;
18
+ });
19
+ /**
20
+ * `createRouter` with `client` and `queryClient` (a new one unless given) in every loader's
21
+ * `context`, links that preload on hover, and TanStack Query deciding when data is stale
22
+ * (`defaultPreloadStaleTime: 0`). `FrameworkProvider` given this router uses its `queryClient`.
23
+ *
24
+ * A route renders on the server only when it sets `ssr: true` (`defaultSsr: false`). The query
25
+ * cache a server render filled, with its read tags, comes along to the browser, so sync keeps it fresh.
26
+ */
27
+ export declare function createParlorRouter<TRouteTree extends AnyRoute, TTrailingSlashOption extends TrailingSlashOption = "never", TDefaultStructuralSharingOption extends boolean = false, TRouterHistory extends RouterHistory = RouterHistory>(options: ParlorRouterOptions<TRouteTree, TTrailingSlashOption, TDefaultStructuralSharingOption, TRouterHistory>): import("@tanstack/router-core").RouterCore<TRouteTree, TTrailingSlashOption, TDefaultStructuralSharingOption, TRouterHistory, Dehydrated>;
28
+ export {};
29
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/router/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,WAAW,EACZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,KAAK,QAAQ,EAEb,KAAK,wBAAwB,EAC7B,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACzB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEnE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,qGAAqG;AACrG,MAAM,MAAM,mBAAmB,GAAG,kBAAkB,CAAC;AAErD,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE1C,KAAK,aAAa,CAChB,UAAU,SAAS,QAAQ,EAC3B,oBAAoB,SAAS,mBAAmB,EAChD,+BAA+B,SAAS,OAAO,EAC/C,cAAc,SAAS,aAAa,IAClC,wBAAwB,CAC1B,UAAU,EACV,oBAAoB,EACpB,+BAA+B,EAC/B,cAAc,EACd,UAAU,CACX,CAAC;AAEF,KAAK,UAAU,CAAC,UAAU,SAAS,QAAQ,IAAI,IAAI,CACjD,WAAW,CAAC,aAAa,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,SAAS,CAAC,CAAC,EAChF,MAAM,mBAAmB,CAC1B,CAAC;AAEF,MAAM,MAAM,mBAAmB,CAC7B,UAAU,SAAS,QAAQ,EAC3B,oBAAoB,SAAS,mBAAmB,GAAG,OAAO,EAC1D,+BAA+B,SAAS,OAAO,GAAG,KAAK,EACvD,cAAc,SAAS,aAAa,GAAG,aAAa,IAClD,IAAI,CACN,aAAa,CACX,UAAU,EACV,oBAAoB,EACpB,+BAA+B,EAC/B,cAAc,CACf,EACD,SAAS,CACV,GAAG;IACF,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,iBAAiB,CAAC,CAAC;IACjD,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CAChD,GAAG,CAAC,EAAE,SAAS,UAAU,CAAC,UAAU,CAAC,GAChC;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC,UAAU,CAAC,CAAA;CAAE,GAC7C;IAAE,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,UAAU,CAAC,CAAA;CAAE,CAAC,CAAC;AAqBpD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,SAAS,QAAQ,EAC3B,oBAAoB,SAAS,mBAAmB,GAAG,OAAO,EAC1D,+BAA+B,SAAS,OAAO,GAAG,KAAK,EACvD,cAAc,SAAS,aAAa,GAAG,aAAa,EAEpD,OAAO,EAAE,mBAAmB,CAC1B,UAAU,EACV,oBAAoB,EACpB,+BAA+B,EAC/B,cAAc,CACf,6IA+BF"}
@@ -0,0 +1,44 @@
1
+ import { dehydrate, hydrate, QueryClient, } from "@tanstack/react-query";
2
+ import { createRouter, } from "@tanstack/react-router";
3
+ import { readTagsOf } from "../react/sync.js";
4
+ const QUERY_CACHE = "parlorQueryCache";
5
+ function dehydrateQueryCache(queryClient) {
6
+ return { state: dehydrate(queryClient), readTags: readTagsOf(queryClient).entries() };
7
+ }
8
+ function hydrateQueryCache(queryClient, cache) {
9
+ if (!cache)
10
+ return;
11
+ const { state, readTags } = cache;
12
+ hydrate(queryClient, state);
13
+ const registry = readTagsOf(queryClient);
14
+ for (const [hash, tags, cursor] of readTags)
15
+ registry.record(hash, tags, cursor);
16
+ }
17
+ /**
18
+ * `createRouter` with `client` and `queryClient` (a new one unless given) in every loader's
19
+ * `context`, links that preload on hover, and TanStack Query deciding when data is stale
20
+ * (`defaultPreloadStaleTime: 0`). `FrameworkProvider` given this router uses its `queryClient`.
21
+ *
22
+ * A route renders on the server only when it sets `ssr: true` (`defaultSsr: false`). The query
23
+ * cache a server render filled, with its read tags, comes along to the browser, so sync keeps it fresh.
24
+ */
25
+ export function createParlorRouter(options) {
26
+ const { client, queryClient = new QueryClient(), context, dehydrate: dehydrateApp, hydrate: hydrateApp, ...rest } = options;
27
+ const routerOptions = {
28
+ defaultPreload: "intent",
29
+ defaultPreloadStaleTime: 0,
30
+ defaultSsr: false,
31
+ ...rest,
32
+ context: { ...context, client, queryClient },
33
+ dehydrate: async () => ({
34
+ ...(await dehydrateApp?.()),
35
+ [QUERY_CACHE]: dehydrateQueryCache(queryClient),
36
+ }),
37
+ hydrate: async (dehydrated) => {
38
+ hydrateQueryCache(queryClient, dehydrated[QUERY_CACHE]);
39
+ await hydrateApp?.(dehydrated);
40
+ },
41
+ };
42
+ return createRouter(routerOptions);
43
+ }
44
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/router/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,SAAS,EACT,OAAO,EACP,WAAW,GACZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAEL,YAAY,GAIb,MAAM,wBAAwB,CAAC;AAIhC,OAAO,EAAkB,UAAU,EAAE,MAAM,kBAAkB,CAAC;AA8C9D,MAAM,WAAW,GAAG,kBAAkB,CAAC;AAOvC,SAAS,mBAAmB,CAAC,WAAwB;IACnD,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,WAAW,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;AACxF,CAAC;AAED,SAAS,iBAAiB,CAAC,WAAwB,EAAE,KAAc;IACjE,IAAI,CAAC,KAAK;QAAE,OAAO;IACnB,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,KAA6B,CAAC;IAC1D,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAC5B,MAAM,QAAQ,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IACzC,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,QAAQ;QAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AACnF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAMhC,OAKC;IAED,MAAM,EACJ,MAAM,EACN,WAAW,GAAG,IAAI,WAAW,EAAE,EAC/B,OAAO,EACP,SAAS,EAAE,YAAY,EACvB,OAAO,EAAE,UAAU,EACnB,GAAG,IAAI,EACR,GAAG,OAAO,CAAC;IACZ,MAAM,aAAa,GAAG;QACpB,cAAc,EAAE,QAAQ;QACxB,uBAAuB,EAAE,CAAC;QAC1B,UAAU,EAAE,KAAK;QACjB,GAAG,IAAI;QACP,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE;QAC5C,SAAS,EAAE,KAAK,IAAyB,EAAE,CAAC,CAAC;YAC3C,GAAG,CAAC,MAAM,YAAY,EAAE,EAAE,CAAC;YAC3B,CAAC,WAAW,CAAC,EAAE,mBAAmB,CAAC,WAAW,CAAC;SAChD,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,UAAsB,EAAE,EAAE;YACxC,iBAAiB,CAAC,WAAW,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;YACxD,MAAM,UAAU,EAAE,CAAC,UAAU,CAAC,CAAC;QACjC,CAAC;KAMF,CAAC;IACF,OAAO,YAAY,CAAC,aAAa,CAAC,CAAC;AACrC,CAAC","sourcesContent":["import {\n type DehydratedState,\n dehydrate,\n hydrate,\n QueryClient,\n} from \"@tanstack/react-query\";\nimport {\n type AnyRoute,\n createRouter,\n type RouterConstructorOptions,\n type RouterHistory,\n type TrailingSlashOption,\n} from \"@tanstack/react-router\";\n\nimport type { ActionClient } from \"../action-client.js\";\nimport type { ActionQueryContext } from \"../react/action-query.js\";\nimport { type ReadEntry, readTagsOf } from \"../react/sync.js\";\nimport type { RegisteredActions } from \"../register.js\";\n\n/** The router context Parlor's loaders read: `createRootRouteWithContext<ParlorRouterContext>()`. */\nexport type ParlorRouterContext = ActionQueryContext;\n\ntype Dehydrated = Record<string, unknown>;\n\ntype RouterOptions<\n TRouteTree extends AnyRoute,\n TTrailingSlashOption extends TrailingSlashOption,\n TDefaultStructuralSharingOption extends boolean,\n TRouterHistory extends RouterHistory,\n> = RouterConstructorOptions<\n TRouteTree,\n TTrailingSlashOption,\n TDefaultStructuralSharingOption,\n TRouterHistory,\n Dehydrated\n>;\n\ntype AppContext<TRouteTree extends AnyRoute> = Omit<\n NonNullable<RouterOptions<TRouteTree, \"never\", false, RouterHistory>[\"context\"]>,\n keyof ParlorRouterContext\n>;\n\nexport type ParlorRouterOptions<\n TRouteTree extends AnyRoute,\n TTrailingSlashOption extends TrailingSlashOption = \"never\",\n TDefaultStructuralSharingOption extends boolean = false,\n TRouterHistory extends RouterHistory = RouterHistory,\n> = Omit<\n RouterOptions<\n TRouteTree,\n TTrailingSlashOption,\n TDefaultStructuralSharingOption,\n TRouterHistory\n >,\n \"context\"\n> & {\n readonly client: ActionClient<RegisteredActions>;\n readonly queryClient?: QueryClient | undefined;\n} & ({} extends AppContext<TRouteTree>\n ? { readonly context?: AppContext<TRouteTree> }\n : { readonly context: AppContext<TRouteTree> });\n\nconst QUERY_CACHE = \"parlorQueryCache\";\n\ninterface DehydratedQueryCache {\n readonly state: DehydratedState;\n readonly readTags: readonly ReadEntry[];\n}\n\nfunction dehydrateQueryCache(queryClient: QueryClient): DehydratedQueryCache {\n return { state: dehydrate(queryClient), readTags: readTagsOf(queryClient).entries() };\n}\n\nfunction hydrateQueryCache(queryClient: QueryClient, cache: unknown): void {\n if (!cache) return;\n const { state, readTags } = cache as DehydratedQueryCache;\n hydrate(queryClient, state);\n const registry = readTagsOf(queryClient);\n for (const [hash, tags, cursor] of readTags) registry.record(hash, tags, cursor);\n}\n\n/**\n * `createRouter` with `client` and `queryClient` (a new one unless given) in every loader's\n * `context`, links that preload on hover, and TanStack Query deciding when data is stale\n * (`defaultPreloadStaleTime: 0`). `FrameworkProvider` given this router uses its `queryClient`.\n *\n * A route renders on the server only when it sets `ssr: true` (`defaultSsr: false`). The query\n * cache a server render filled, with its read tags, comes along to the browser, so sync keeps it fresh.\n */\nexport function createParlorRouter<\n TRouteTree extends AnyRoute,\n TTrailingSlashOption extends TrailingSlashOption = \"never\",\n TDefaultStructuralSharingOption extends boolean = false,\n TRouterHistory extends RouterHistory = RouterHistory,\n>(\n options: ParlorRouterOptions<\n TRouteTree,\n TTrailingSlashOption,\n TDefaultStructuralSharingOption,\n TRouterHistory\n >,\n) {\n const {\n client,\n queryClient = new QueryClient(),\n context,\n dehydrate: dehydrateApp,\n hydrate: hydrateApp,\n ...rest\n } = options;\n const routerOptions = {\n defaultPreload: \"intent\",\n defaultPreloadStaleTime: 0,\n defaultSsr: false,\n ...rest,\n context: { ...context, client, queryClient },\n dehydrate: async (): Promise<Dehydrated> => ({\n ...(await dehydrateApp?.()),\n [QUERY_CACHE]: dehydrateQueryCache(queryClient),\n }),\n hydrate: async (dehydrated: Dehydrated) => {\n hydrateQueryCache(queryClient, dehydrated[QUERY_CACHE]);\n await hydrateApp?.(dehydrated);\n },\n } as RouterOptions<\n TRouteTree,\n TTrailingSlashOption,\n TDefaultStructuralSharingOption,\n TRouterHistory\n >;\n return createRouter(routerOptions);\n}\n"]}
package/dist/session.d.ts CHANGED
@@ -90,6 +90,8 @@ export interface DeviceLinker {
90
90
  /** It approved `connect` on the computer, so its first prompt links it. */
91
91
  readonly pairing: boolean;
92
92
  }>;
93
+ /** Called when another tab changes what `linkState` reads, such as approving `connect`. */
94
+ subscribeLink?(listener: () => void): () => void;
93
95
  /** Enrolls this device, with Face ID or Touch ID unless `presence` is false, and shows its code. */
94
96
  startLink(options?: {
95
97
  readonly presence?: boolean;
@@ -116,6 +118,10 @@ export interface DeviceLinker {
116
118
  /** Removes a device, confirmed with Face ID or Touch ID; throws `LinkOnComputer` without them. */
117
119
  removeDevice(id: string): Promise<void>;
118
120
  }
121
+ /** Ends a computer's connection: its sidebar turns and its coding agents both lose the app. */
122
+ export interface ComputerDisconnector {
123
+ disconnectComputer(hostId: string): Promise<void>;
124
+ }
119
125
  /** What `FrameworkProvider` takes as `auth`; signing, host pairing and linking are optional. */
120
- export type FrameworkAuth = SessionSource & Partial<DeviceSource & HostAuthorizer & DeviceLinker>;
126
+ export type FrameworkAuth = SessionSource & Partial<DeviceSource & HostAuthorizer & DeviceLinker & ComputerDisconnector>;
121
127
  //# sourceMappingURL=session.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAmFA,6FAA6F;AAC7F,MAAM,OAAO,cAAe,SAAQ,KAAK;IACrB,IAAI,GAAG,gBAAgB,CAAC;CAC3C"}
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAmFA,6FAA6F;AAC7F,MAAM,OAAO,cAAe,SAAQ,KAAK;IACrB,IAAI,GAAG,gBAAgB,CAAC;CAC3C","sourcesContent":["import type { HostPairing, PairingDevice } from \"@parlor/capabilities\";\nimport type { AnswerToSign, ItemSignature, PromptToSign } from \"@parlor/chat\";\nimport type { DeviceId } from \"@parlor/core\";\n\nexport interface SessionUser {\n readonly id: string;\n readonly name: string;\n readonly email: string;\n}\n\n/** `anonymous`: the server signs nobody in, so everyone uses the app without an account. */\nexport type SessionSnapshot =\n | { readonly status: \"loading\" }\n | { readonly status: \"anonymous\" }\n | { readonly status: \"signed-out\" }\n | { readonly status: \"signed-in\"; readonly user: SessionUser };\n\n/**\n * Who is signed in on this device, as the app's auth client sees it. `current` returns the\n * same object until the session changes.\n */\nexport interface SessionSource {\n current(): SessionSnapshot;\n subscribe(listener: () => void): () => void;\n}\n\n/** This device's signing identity, as the chat needs it. */\nexport interface SessionDevice {\n readonly id: DeviceId;\n /** The public half of its keys, as the account registered them. */\n readonly keys: PairingDevice;\n /** With `presence`, the platform asks for Touch ID, Face ID or a passkey first. */\n signPrompt(\n prompt: PromptToSign,\n options?: { readonly presence?: boolean },\n ): Promise<ItemSignature>;\n /** With `presence`, the platform asks for Touch ID, Face ID or a passkey first. */\n signAnswer(\n answer: AnswerToSign,\n options: { readonly presence: boolean },\n ): Promise<ItemSignature>;\n}\n\nexport interface DeviceSource {\n /**\n * Enrolls this device with the account on first use; undefined where it cannot sign.\n * `presence: false` enrolls it without a presence key. Rejects with the capability's\n * `PresenceSetupCancelled` when the user dismisses the passkey prompt.\n */\n device(options?: { readonly presence?: boolean }): Promise<SessionDevice | undefined>;\n /** `needed` while the first `device()` would ask the user to create a passkey. */\n deviceSetup?(): Promise<\"needed\" | \"ready\">;\n}\n\nexport interface HostAuthorizer {\n /** For pairing the host on this computer: a code for it, approved by this session. */\n authorizeHost(pairing: HostPairing): Promise<string>;\n}\n\n/** A device waiting to be linked, its keys checked against the code it shows. */\nexport interface PendingLink {\n readonly id: string;\n readonly name: string;\n readonly platform: string;\n readonly commitment: string;\n /** The short code the device shows, for `parlor-host link` on the computer. */\n readonly code: string;\n}\n\n/** One of the account's devices, as the devices list shows it. */\nexport interface AccountDevice {\n readonly id: string;\n readonly name: string;\n readonly platform: string;\n /** This device. */\n readonly current: boolean;\n readonly createdAt: string;\n readonly vouched: boolean;\n readonly linking: boolean;\n /** The device whose vouch linked it last. */\n readonly linkedBy?: string | undefined;\n}\n\n/** Thrown by `vouch` on a device with no presence key: the computer itself links it then. */\nexport class LinkOnComputer extends Error {\n override readonly name = \"LinkOnComputer\";\n}\n\n/** Linking devices: a new one shows a code, a linked one checks it and vouches for it. */\nexport interface DeviceLinker {\n /** Never prompts: a device not created yet reads as none. */\n linkState(): Promise<{\n readonly deviceId?: string | undefined;\n readonly vouched: boolean;\n /** It approved `connect` on the computer, so its first prompt links it. */\n readonly pairing: boolean;\n }>;\n /** Called when another tab changes what `linkState` reads, such as approving `connect`. */\n subscribeLink?(listener: () => void): () => void;\n /** Enrolls this device, with Face ID or Touch ID unless `presence` is false, and shows its code. */\n startLink(options?: { readonly presence?: boolean }): Promise<{\n readonly deviceId: string;\n readonly code: string;\n /** The whole hash the code is the start of; the QR carries it. */\n readonly commitment: string;\n readonly expiresAt: string;\n }>;\n /**\n * The device showing `code`, or the one whose QR named its id and whole commitment; throws\n * when the keys the app returns do not match.\n */\n resolveLink(\n from: string | { readonly deviceId: string; readonly commitment: string },\n ): Promise<PendingLink>;\n /** Vouches with this device's presence key; throws `LinkOnComputer` when it has none. */\n vouch(link: PendingLink): Promise<void>;\n /** Whether this device has a presence key to vouch with; never prompts. */\n canVouch(): Promise<boolean>;\n listDevices(): Promise<readonly AccountDevice[]>;\n /** Removes a device, confirmed with Face ID or Touch ID; throws `LinkOnComputer` without them. */\n removeDevice(id: string): Promise<void>;\n}\n\n/** Ends a computer's connection: its sidebar turns and its coding agents both lose the app. */\nexport interface ComputerDisconnector {\n disconnectComputer(hostId: string): Promise<void>;\n}\n\n/** What `FrameworkProvider` takes as `auth`; signing, host pairing and linking are optional. */\nexport type FrameworkAuth = SessionSource &\n Partial<DeviceSource & HostAuthorizer & DeviceLinker & ComputerDisconnector>;\n"]}
@@ -0,0 +1,2 @@
1
+ export { createRenderer, type RenderOptions, type RenderPage } from "./render.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ssr/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { createRenderer } from "./render.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ssr/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAuC,MAAM,aAAa,CAAC","sourcesContent":["export { createRenderer, type RenderOptions, type RenderPage } from \"./render.js\";\n"]}
@@ -0,0 +1,18 @@
1
+ import { type CreateParlorApp } from "../router/mount.js";
2
+ export interface RenderOptions {
3
+ /** The app's `index.html` as the browser gets it; the page renders into its `#root`. */
4
+ readonly template: string;
5
+ /** Reaches the app's server: in process, through its Durable Object, or over the network in dev. */
6
+ readonly fetch: (request: Request) => Promise<Response>;
7
+ /** Goes on every inline script; the response's policy allows it. */
8
+ readonly nonce: string;
9
+ }
10
+ /** The page, or undefined when a route it matches does not render on the server. */
11
+ export type RenderPage = (request: Request, options: RenderOptions) => Promise<Response | undefined>;
12
+ /**
13
+ * Renders one document request with TanStack Router's SSR handler: the loaders run against
14
+ * the app's server with the request's cookie and authorization, then React renders the
15
+ * matched routes and the router's state, query cache included, goes along for hydration.
16
+ */
17
+ export declare function createRenderer(createApp: CreateParlorApp): RenderPage;
18
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/ssr/render.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,eAAe,EAAmB,MAAM,oBAAoB,CAAC;AAE3E,MAAM,WAAW,aAAa;IAC5B,wFAAwF;IACxF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,oGAAoG;IACpG,QAAQ,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxD,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,oFAAoF;AACpF,MAAM,MAAM,UAAU,GAAG,CACvB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,aAAa,KACnB,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;AAkFnC;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,eAAe,GAAG,UAAU,CAoCrE"}