@chelone/core 0.5.0 → 0.7.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 (253) hide show
  1. package/dist/assistant/assistant-session.d.ts +28 -0
  2. package/dist/assistant/assistant-session.js +208 -0
  3. package/dist/assistant/echo-transport.d.ts +7 -0
  4. package/dist/assistant/echo-transport.js +28 -0
  5. package/dist/assistant/http-transport.d.ts +10 -0
  6. package/dist/assistant/http-transport.js +98 -0
  7. package/dist/assistant/types.d.ts +54 -0
  8. package/dist/auth/access-control.d.ts +13 -5
  9. package/dist/auth/access-control.js +25 -21
  10. package/dist/auth/auth-options.d.ts +21 -17
  11. package/dist/auth/auth-options.js +80 -48
  12. package/dist/auth/permission-matrix.d.ts +12 -0
  13. package/dist/auth/permission-matrix.js +67 -0
  14. package/dist/auth/types.d.ts +14 -4
  15. package/dist/block-editor/block-document.d.ts +60 -0
  16. package/dist/block-editor/block-document.js +143 -0
  17. package/dist/block-editor/data-blocks.d.ts +63 -0
  18. package/dist/block-editor/data-blocks.js +117 -0
  19. package/dist/chart/chart-data.d.ts +25 -0
  20. package/dist/chart/chart-data.js +61 -0
  21. package/dist/command-menu/hotkey.js +58 -47
  22. package/dist/command-menu/recents.js +8 -11
  23. package/dist/command-menu/registry.d.ts +9 -5
  24. package/dist/command-menu/registry.js +36 -46
  25. package/dist/controller/calendar-dates.js +82 -113
  26. package/dist/controller/calendar-events.js +93 -88
  27. package/dist/controller/client-list-controller.d.ts +2 -2
  28. package/dist/controller/client-list-controller.js +251 -254
  29. package/dist/controller/data-table-columns.d.ts +2 -0
  30. package/dist/controller/data-table-columns.js +50 -56
  31. package/dist/controller/deep-equal.d.ts +1 -0
  32. package/dist/controller/deep-equal.js +14 -0
  33. package/dist/controller/filter-toggle.js +36 -44
  34. package/dist/controller/in-place-editor.d.ts +42 -0
  35. package/dist/controller/in-place-editor.js +42 -0
  36. package/dist/controller/infinite-list-controller.js +220 -190
  37. package/dist/controller/inspector.d.ts +49 -0
  38. package/dist/controller/inspector.js +64 -0
  39. package/dist/controller/job-monitor.d.ts +41 -0
  40. package/dist/controller/job-monitor.js +49 -0
  41. package/dist/controller/kanban-board.d.ts +2 -3
  42. package/dist/controller/kanban-board.js +43 -24
  43. package/dist/controller/list-controller.d.ts +5 -5
  44. package/dist/controller/list-controller.js +444 -400
  45. package/dist/controller/list-params.js +96 -87
  46. package/dist/controller/pivot-grid.d.ts +36 -0
  47. package/dist/controller/pivot-grid.js +99 -0
  48. package/dist/controller/prev-next.d.ts +2 -2
  49. package/dist/controller/prev-next.js +74 -54
  50. package/dist/controller/record-controllers.d.ts +14 -6
  51. package/dist/controller/record-controllers.js +288 -257
  52. package/dist/controller/record-diff.d.ts +21 -0
  53. package/dist/controller/record-diff.js +50 -0
  54. package/dist/controller/record-timeline.d.ts +35 -0
  55. package/dist/controller/record-timeline.js +48 -0
  56. package/dist/controller/record-tree.js +57 -47
  57. package/dist/controller/record-workflow.d.ts +38 -16
  58. package/dist/controller/record-workflow.js +85 -64
  59. package/dist/controller/reference-many-controller.js +198 -192
  60. package/dist/controller/reference-one-controller.d.ts +1 -1
  61. package/dist/controller/reference-one-controller.js +63 -56
  62. package/dist/controller/saved-queries.js +104 -91
  63. package/dist/controller/translate-fallback.js +10 -6
  64. package/dist/csv/export.d.ts +2 -1
  65. package/dist/csv/export.js +43 -40
  66. package/dist/csv/import-controller.js +185 -169
  67. package/dist/csv/match-headers.js +29 -22
  68. package/dist/csv/parse-with.js +60 -54
  69. package/dist/csv/run-import.js +69 -72
  70. package/dist/csv/validate-rows.d.ts +1 -1
  71. package/dist/csv/validate-rows.js +65 -52
  72. package/dist/data/cache-ops.d.ts +5 -4
  73. package/dist/data/cache-ops.js +208 -190
  74. package/dist/data/combine-data-providers.js +25 -23
  75. package/dist/data/data-provider-log.d.ts +45 -0
  76. package/dist/data/data-provider-log.js +93 -0
  77. package/dist/data/errors.js +14 -10
  78. package/dist/data/fake-provider.d.ts +6 -3
  79. package/dist/data/fake-provider.js +129 -114
  80. package/dist/data/fetch-utils.js +61 -44
  81. package/dist/data/filter-branch.d.ts +5 -0
  82. package/dist/data/filter-branch.js +10 -0
  83. package/dist/data/get-many-aggregate.js +84 -76
  84. package/dist/data/http-error.js +17 -12
  85. package/dist/data/keys.js +50 -36
  86. package/dist/data/mutation-options.d.ts +14 -9
  87. package/dist/data/mutation-options.js +93 -109
  88. package/dist/data/query-options.js +73 -68
  89. package/dist/data/remove-empty.d.ts +3 -0
  90. package/dist/data/remove-empty.js +11 -0
  91. package/dist/data/undoable.d.ts +2 -2
  92. package/dist/data/undoable.js +190 -174
  93. package/dist/data/with-lifecycle-callbacks.d.ts +45 -17
  94. package/dist/data/with-lifecycle-callbacks.js +66 -52
  95. package/dist/environment/application-update-store.js +58 -55
  96. package/dist/environment/offline-store.js +21 -21
  97. package/dist/form/array-binding.d.ts +30 -11
  98. package/dist/form/array-binding.js +184 -166
  99. package/dist/form/choice-ids.d.ts +7 -0
  100. package/dist/form/choice-ids.js +11 -0
  101. package/dist/form/choices-controller.d.ts +3 -2
  102. package/dist/form/choices-controller.js +302 -275
  103. package/dist/form/date-format.d.ts +38 -0
  104. package/dist/form/date-format.js +89 -0
  105. package/dist/form/dirty-values.d.ts +2 -2
  106. package/dist/form/dirty-values.js +22 -11
  107. package/dist/form/duration.d.ts +22 -0
  108. package/dist/form/duration.js +49 -0
  109. package/dist/form/edit-create-form.d.ts +12 -5
  110. package/dist/form/edit-create-form.js +243 -188
  111. package/dist/form/event-value.d.ts +6 -0
  112. package/dist/form/event-value.js +17 -0
  113. package/dist/form/field-error.d.ts +14 -0
  114. package/dist/form/field-error.js +28 -0
  115. package/dist/form/field-name.d.ts +20 -0
  116. package/dist/form/field-name.js +6 -0
  117. package/dist/form/filter-form-values.js +63 -49
  118. package/dist/form/form-controller.d.ts +9 -1
  119. package/dist/form/form-controller.js +165 -160
  120. package/dist/form/form-groups.d.ts +4 -3
  121. package/dist/form/form-groups.js +79 -79
  122. package/dist/form/form-handle.d.ts +175 -0
  123. package/dist/form/form-handle.js +68 -0
  124. package/dist/form/input-binding.d.ts +56 -31
  125. package/dist/form/input-binding.js +169 -118
  126. package/dist/form/input-values.d.ts +130 -0
  127. package/dist/form/input-values.js +177 -0
  128. package/dist/form/json-text.js +51 -37
  129. package/dist/form/save-pipeline.d.ts +6 -8
  130. package/dist/form/save-pipeline.js +91 -82
  131. package/dist/form/set-form-values.d.ts +6 -0
  132. package/dist/form/set-form-values.js +10 -0
  133. package/dist/form/source.js +97 -65
  134. package/dist/form/text-of.d.ts +2 -0
  135. package/dist/form/text-of.js +7 -0
  136. package/dist/form/validators.d.ts +58 -19
  137. package/dist/form/validators.js +97 -71
  138. package/dist/geo/bbox.d.ts +5 -4
  139. package/dist/geo/bbox.js +69 -48
  140. package/dist/geo/bounds.d.ts +14 -3
  141. package/dist/geo/bounds.js +98 -68
  142. package/dist/geo/constraints.js +88 -93
  143. package/dist/geo/coordinates.js +31 -23
  144. package/dist/geo/editor.d.ts +13 -13
  145. package/dist/geo/editor.js +176 -153
  146. package/dist/geo/geocoding.d.ts +4 -4
  147. package/dist/geo/geocoding.js +38 -41
  148. package/dist/geo/geojson.d.ts +12 -74
  149. package/dist/geo/geojson.js +52 -40
  150. package/dist/geo/geometry-math.d.ts +4 -4
  151. package/dist/geo/geometry-math.js +100 -93
  152. package/dist/geo/index.d.ts +7 -6
  153. package/dist/geo/map-layers.d.ts +42 -7
  154. package/dist/geo/map-layers.js +88 -62
  155. package/dist/geo/map-style.js +31 -32
  156. package/dist/geo/messages.js +48 -45
  157. package/dist/geo/nominatim.js +88 -80
  158. package/dist/geo/osm-presets.d.ts +4 -3
  159. package/dist/geo/osm-presets.js +108 -86
  160. package/dist/geo/osm-tags.js +14 -12
  161. package/dist/geo/overpass.d.ts +14 -2
  162. package/dist/geo/overpass.js +83 -90
  163. package/dist/geo/shape-value.d.ts +9 -5
  164. package/dist/geo/shape-value.js +169 -146
  165. package/dist/geo/snap-to-roads.d.ts +3 -3
  166. package/dist/geo/snap-to-roads.js +55 -50
  167. package/dist/geo/throttle.js +36 -39
  168. package/dist/guards.d.ts +37 -0
  169. package/dist/guards.js +62 -0
  170. package/dist/i18n/default-messages.js +34 -29
  171. package/dist/i18n/english-messages.js +187 -189
  172. package/dist/i18n/i18n.d.ts +8 -2
  173. package/dist/i18n/i18n.js +47 -48
  174. package/dist/i18n/inflection.d.ts +34 -0
  175. package/dist/i18n/inflection.js +82 -0
  176. package/dist/i18n/messages.d.ts +10 -2
  177. package/dist/i18n/messages.js +17 -4
  178. package/dist/i18n/missing-keys.d.ts +19 -0
  179. package/dist/i18n/missing-keys.js +68 -0
  180. package/dist/i18n/translatable.d.ts +16 -1
  181. package/dist/i18n/translatable.js +40 -47
  182. package/dist/index.d.ts +96 -22
  183. package/dist/index.js +52 -257
  184. package/dist/json/deep-equal.d.ts +8 -0
  185. package/dist/json/deep-equal.js +24 -0
  186. package/dist/json/strip-json-comments.js +38 -42
  187. package/dist/noop.d.ts +2 -0
  188. package/dist/noop.js +4 -0
  189. package/dist/notification/error-message.d.ts +2 -0
  190. package/dist/notification/error-message.js +10 -0
  191. package/dist/notification/notification-store.d.ts +3 -2
  192. package/dist/notification/notification-store.js +41 -45
  193. package/dist/onboarding-tour/placement.d.ts +34 -0
  194. package/dist/onboarding-tour/placement.js +67 -0
  195. package/dist/onboarding-tour/tour-controller.d.ts +25 -0
  196. package/dist/onboarding-tour/tour-controller.js +50 -0
  197. package/dist/realtime/add-events-for-mutations.js +75 -81
  198. package/dist/realtime/announce-lock.d.ts +7 -0
  199. package/dist/realtime/announce-lock.js +18 -0
  200. package/dist/realtime/broadcast-channel-transport.d.ts +14 -0
  201. package/dist/realtime/broadcast-channel-transport.js +69 -0
  202. package/dist/realtime/copy-members.js +22 -14
  203. package/dist/realtime/in-memory-lock-provider.js +41 -60
  204. package/dist/realtime/live.js +95 -101
  205. package/dist/realtime/lock-options.js +16 -16
  206. package/dist/realtime/lock-session.js +91 -78
  207. package/dist/realtime/memory-transport.js +26 -67
  208. package/dist/realtime/presence.js +120 -122
  209. package/dist/realtime/realtime-data-provider.d.ts +10 -0
  210. package/dist/realtime/realtime-data-provider.js +45 -42
  211. package/dist/realtime/reconnect-backoff.d.ts +15 -0
  212. package/dist/realtime/reconnect-backoff.js +17 -0
  213. package/dist/realtime/sse-transport.d.ts +31 -0
  214. package/dist/realtime/sse-transport.js +235 -0
  215. package/dist/realtime/status-store.js +23 -28
  216. package/dist/realtime/topic-hub.d.ts +24 -0
  217. package/dist/realtime/topic-hub.js +66 -0
  218. package/dist/realtime/topics.js +10 -12
  219. package/dist/realtime/types.js +10 -12
  220. package/dist/realtime/unread-count.js +21 -21
  221. package/dist/realtime/websocket-transport.d.ts +32 -0
  222. package/dist/realtime/websocket-transport.js +306 -0
  223. package/dist/resource/describe-resource.d.ts +2 -0
  224. package/dist/resource/describe-resource.js +232 -219
  225. package/dist/resource/get-at-path.js +29 -14
  226. package/dist/resource/identifier.d.ts +18 -0
  227. package/dist/resource/identifier.js +27 -0
  228. package/dist/resource/record.d.ts +4 -0
  229. package/dist/resource/record.js +10 -0
  230. package/dist/resource/register.d.ts +7 -0
  231. package/dist/resource/registry-store.d.ts +45 -15
  232. package/dist/resource/registry-store.js +67 -60
  233. package/dist/resource/registry.d.ts +10 -0
  234. package/dist/resource/registry.js +8 -4
  235. package/dist/resource/source-path.d.ts +16 -1
  236. package/dist/rich-text/rich-text-extras.d.ts +132 -0
  237. package/dist/rich-text/rich-text-extras.js +179 -0
  238. package/dist/rich-text/rich-text-image.d.ts +124 -0
  239. package/dist/rich-text/rich-text-image.js +163 -0
  240. package/dist/rich-text/rich-text.d.ts +62 -0
  241. package/dist/rich-text/rich-text.js +142 -0
  242. package/dist/router/location.d.ts +22 -4
  243. package/dist/router/location.js +41 -18
  244. package/dist/router/match.js +47 -39
  245. package/dist/router/paths.js +40 -34
  246. package/dist/router/record-from-location.d.ts +1 -1
  247. package/dist/router/record-from-location.js +25 -17
  248. package/dist/store/adapters.js +51 -55
  249. package/dist/store/micro-store.js +57 -59
  250. package/dist/store/shallow-equal.d.ts +12 -0
  251. package/dist/store/shallow-equal.js +20 -0
  252. package/dist/store/store.js +54 -57
  253. package/package.json +13 -11
@@ -0,0 +1,28 @@
1
+ import { type MicroStore } from "../store/micro-store.js";
2
+ import type { AssistantMessage, AssistantTool, AssistantToolCall, AssistantTransport } from "./types.js";
3
+ export interface AssistantSessionState {
4
+ messages: AssistantMessage[];
5
+ status: "idle" | "streaming" | "error";
6
+ error: Error | null;
7
+ }
8
+ export interface CreateAssistantSessionOptions {
9
+ transport: AssistantTransport;
10
+ tools: Record<string, AssistantTool>;
11
+ /** Follow-up turns after tool calls before the session gives up. Default 5. */
12
+ maxToolRounds?: number;
13
+ /** Answers for tools that set `confirm`. Without it they never run. */
14
+ confirmTool?: (call: AssistantToolCall) => boolean | Promise<boolean>;
15
+ }
16
+ export interface AssistantSession extends MicroStore<AssistantSessionState> {
17
+ /** Resolves when the turn, with its tool rounds, ends. */
18
+ send(text: string): Promise<void>;
19
+ /**
20
+ * After an error, runs the last user message again: whatever the failed
21
+ * turn added after it is dropped and no second user message is added.
22
+ * Does nothing unless the status is `error`.
23
+ */
24
+ retry(): Promise<void>;
25
+ stop(): void;
26
+ reset(): void;
27
+ }
28
+ export declare function createAssistantSession({ transport, tools, maxToolRounds, confirmTool, }: CreateAssistantSessionOptions): AssistantSession;
@@ -0,0 +1,208 @@
1
+ import { createMicroStore } from "../store/micro-store.js";
2
+ // src/assistant/assistant-session.ts
3
+ var toError = (error) => error instanceof Error ? error : new Error(String(error));
4
+ var issuesText = (issues) => issues.map((issue) => issue.message).join("; ");
5
+ /** Resolves with `undefined` when the signal aborts before the promise settles. */
6
+ function raceAbort(promise, signal) {
7
+ return new Promise((resolve, reject) => {
8
+ const onAbort = () => resolve(void 0);
9
+ if (signal.aborted) return onAbort();
10
+ signal.addEventListener("abort", onAbort, { once: true });
11
+ promise.then(resolve, reject).finally(() => {
12
+ signal.removeEventListener("abort", onAbort);
13
+ });
14
+ });
15
+ }
16
+ function createAssistantSession({ transport, tools, maxToolRounds = 5, confirmTool }) {
17
+ const store = createMicroStore({
18
+ messages: [],
19
+ status: "idle",
20
+ error: null
21
+ });
22
+ const declarations = Object.fromEntries(Object.entries(tools).map(([name, { description, parameters }]) => [name, {
23
+ description,
24
+ parameters
25
+ }]));
26
+ let active = null;
27
+ let destroyed = false;
28
+ store.onDestroy(() => {
29
+ destroyed = true;
30
+ active?.abort();
31
+ active = null;
32
+ });
33
+ async function runTool(call, signal) {
34
+ const base = {
35
+ role: "tool",
36
+ id: call.id,
37
+ toolName: call.toolName
38
+ };
39
+ const definition = Object.hasOwn(tools, call.toolName) ? tools[call.toolName] : void 0;
40
+ if (!definition) return {
41
+ ...base,
42
+ error: `Unknown tool: ${call.toolName}`
43
+ };
44
+ const stopped = {
45
+ ...base,
46
+ error: "Stopped"
47
+ };
48
+ try {
49
+ let args = call.args;
50
+ if (definition.schema) {
51
+ const outcome = await definition.schema["~standard"].validate(args);
52
+ if (signal.aborted) return stopped;
53
+ if (outcome.issues) return {
54
+ ...base,
55
+ error: `Invalid arguments: ${issuesText(outcome.issues)}`
56
+ };
57
+ args = outcome.value;
58
+ }
59
+ if (definition.confirm) {
60
+ const answer = await confirmTool?.(call);
61
+ if (signal.aborted) return stopped;
62
+ if (!answer) return {
63
+ ...base,
64
+ error: "The user declined to run this tool"
65
+ };
66
+ }
67
+ return {
68
+ ...base,
69
+ result: await definition.handler(args)
70
+ };
71
+ } catch (error) {
72
+ return {
73
+ ...base,
74
+ error: toError(error).message
75
+ };
76
+ }
77
+ }
78
+ async function run(controller) {
79
+ const { signal } = controller;
80
+ const update = (patch) => {
81
+ if (!signal.aborted) store.setState(patch);
82
+ };
83
+ for (let round = 0;; round++) {
84
+ let content = "";
85
+ let index = -1;
86
+ const calls = [];
87
+ let iterator;
88
+ try {
89
+ iterator = transport.send(store.getSnapshot().messages, declarations, { signal })[Symbol.asyncIterator]();
90
+ for (;;) {
91
+ const step = await raceAbort(iterator.next(), signal);
92
+ if (!step || step.done || signal.aborted) break;
93
+ const chunk = step.value;
94
+ if (chunk.type === "done") break;
95
+ if (chunk.type === "tool-call") {
96
+ const { id, toolName, args } = chunk;
97
+ calls.push({
98
+ id,
99
+ toolName,
100
+ args
101
+ });
102
+ continue;
103
+ }
104
+ content += chunk.delta;
105
+ const text = content;
106
+ if (index < 0) {
107
+ index = store.getSnapshot().messages.length;
108
+ update((s) => ({ messages: [...s.messages, {
109
+ role: "assistant",
110
+ content: text
111
+ }] }));
112
+ } else update((s) => ({ messages: s.messages.map((m, i) => i === index ? {
113
+ role: "assistant",
114
+ content: text
115
+ } : m) }));
116
+ }
117
+ } catch (error) {
118
+ if (!signal.aborted) update(() => ({
119
+ status: "error",
120
+ error: toError(error)
121
+ }));
122
+ return;
123
+ } finally {
124
+ iterator?.return?.().catch(() => {});
125
+ }
126
+ if (signal.aborted) return;
127
+ if (calls.length === 0) break;
128
+ if (round >= maxToolRounds) {
129
+ update(() => ({
130
+ status: "error",
131
+ error: /* @__PURE__ */ new Error(`The assistant kept calling tools after ${maxToolRounds} rounds`)
132
+ }));
133
+ return;
134
+ }
135
+ const results = [];
136
+ for (const call of calls) {
137
+ const result = await raceAbort(runTool(call, signal), signal);
138
+ if (!result) return;
139
+ results.push(result);
140
+ if (signal.aborted) return;
141
+ }
142
+ const assistant = {
143
+ role: "assistant",
144
+ content,
145
+ toolCalls: calls
146
+ };
147
+ update((s) => ({ messages: index < 0 ? [
148
+ ...s.messages,
149
+ assistant,
150
+ ...results
151
+ ] : [...s.messages.map((m, i) => i === index ? assistant : m), ...results] }));
152
+ }
153
+ update(() => ({ status: "idle" }));
154
+ }
155
+ async function start(controller) {
156
+ active = controller;
157
+ try {
158
+ await run(controller);
159
+ } finally {
160
+ if (active === controller) active = null;
161
+ }
162
+ }
163
+ return {
164
+ ...store,
165
+ async send(text) {
166
+ if (destroyed) return;
167
+ if (!text.trim() || store.getSnapshot().status === "streaming") return;
168
+ store.setState((s) => ({
169
+ messages: [...s.messages, {
170
+ role: "user",
171
+ content: text
172
+ }],
173
+ status: "streaming",
174
+ error: null
175
+ }));
176
+ await start(new AbortController());
177
+ },
178
+ async retry() {
179
+ if (destroyed || store.getSnapshot().status !== "error") return;
180
+ const { messages } = store.getSnapshot();
181
+ let last = messages.length - 1;
182
+ while (last >= 0 && messages[last]?.role !== "user") last--;
183
+ if (last < 0) return;
184
+ store.setState({
185
+ messages: messages.slice(0, last + 1),
186
+ status: "streaming",
187
+ error: null
188
+ });
189
+ await start(new AbortController());
190
+ },
191
+ stop() {
192
+ if (!active) return;
193
+ active.abort();
194
+ active = null;
195
+ store.setState({ status: "idle" });
196
+ },
197
+ reset() {
198
+ active?.abort();
199
+ active = null;
200
+ store.setState({
201
+ messages: [],
202
+ status: "idle",
203
+ error: null
204
+ });
205
+ }
206
+ };
207
+ }
208
+ export { createAssistantSession };
@@ -0,0 +1,7 @@
1
+ import type { AssistantTransport } from "./types.js";
2
+ export interface EchoAssistantTransportOptions {
3
+ /** Waits this long before each character. */
4
+ delayMs?: number;
5
+ }
6
+ /** Replies `Echo: <last user text>` one character at a time, with no model behind it. */
7
+ export declare function echoAssistantTransport({ delayMs, }?: EchoAssistantTransportOptions): AssistantTransport;
@@ -0,0 +1,28 @@
1
+ // src/assistant/echo-transport.ts
2
+ var wait = (ms, signal) => new Promise((resolve) => {
3
+ const timer = setTimeout(done, ms);
4
+ function done() {
5
+ clearTimeout(timer);
6
+ signal.removeEventListener("abort", done);
7
+ resolve();
8
+ }
9
+ signal.addEventListener("abort", done, { once: true });
10
+ });
11
+ /** Replies `Echo: <last user text>` one character at a time, with no model behind it. */
12
+ function echoAssistantTransport({ delayMs = 0 } = {}) {
13
+ return { async *send(messages, _tools, { signal }) {
14
+ let said = "";
15
+ for (const message of messages) if (message.role === "user") said = message.content;
16
+ const reply = `Echo: ${said}`;
17
+ for (const delta of reply) {
18
+ if (delayMs > 0) await wait(delayMs, signal);
19
+ if (signal.aborted) return;
20
+ yield {
21
+ type: "text",
22
+ delta
23
+ };
24
+ }
25
+ yield { type: "done" };
26
+ } };
27
+ }
28
+ export { echoAssistantTransport };
@@ -0,0 +1,10 @@
1
+ import type { AssistantTransport } from "./types.js";
2
+ export interface HttpAssistantTransportOptions {
3
+ url: string;
4
+ /** Resolved for each request so tokens can change between turns. */
5
+ headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
6
+ credentials?: RequestCredentials;
7
+ fetch?: typeof globalThis.fetch;
8
+ }
9
+ /** Posts a conversation to an app endpoint that replies with NDJSON AssistantChunks. */
10
+ export declare function httpAssistantTransport(options: HttpAssistantTransportOptions): AssistantTransport;
@@ -0,0 +1,98 @@
1
+ import { HttpError } from "../data/http-error.js";
2
+ import { isJsonObject, isRecord } from "../guards.js";
3
+ // src/assistant/http-transport.ts
4
+ function parseChunk(line, lineNumber) {
5
+ const invalid = () => /* @__PURE__ */ new Error(`Invalid assistant chunk at line ${lineNumber}`);
6
+ let chunk;
7
+ try {
8
+ chunk = JSON.parse(line);
9
+ } catch {
10
+ throw invalid();
11
+ }
12
+ if (isRecord(chunk)) switch (chunk.type) {
13
+ case "text":
14
+ if (typeof chunk.delta === "string") return {
15
+ type: "text",
16
+ delta: chunk.delta
17
+ };
18
+ break;
19
+ case "tool-call":
20
+ if (typeof chunk.id === "string" && typeof chunk.toolName === "string" && isJsonObject(chunk.args)) return {
21
+ type: "tool-call",
22
+ id: chunk.id,
23
+ toolName: chunk.toolName,
24
+ args: chunk.args
25
+ };
26
+ break;
27
+ case "done": return { type: "done" };
28
+ }
29
+ throw invalid();
30
+ }
31
+ /** Posts a conversation to an app endpoint that replies with NDJSON AssistantChunks. */
32
+ function httpAssistantTransport(options) {
33
+ const doFetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
34
+ return { async *send(messages, tools, { signal }) {
35
+ try {
36
+ if (signal.aborted) return;
37
+ const headers = new Headers(typeof options.headers === "function" ? await options.headers() : options.headers);
38
+ if (signal.aborted) return;
39
+ headers.set("Content-Type", "application/json");
40
+ const response = await doFetch(options.url, {
41
+ method: "POST",
42
+ headers,
43
+ credentials: options.credentials,
44
+ body: JSON.stringify({
45
+ messages,
46
+ tools
47
+ }),
48
+ signal
49
+ });
50
+ if (!response.ok) throw new HttpError(`Assistant request failed (HTTP ${response.status})`, response.status, await response.text());
51
+ const reader = response.body?.getReader();
52
+ if (reader) {
53
+ const decoder = new TextDecoder();
54
+ let pending = "";
55
+ let lineNumber = 0;
56
+ let released = false;
57
+ const release = () => {
58
+ if (released) return;
59
+ released = true;
60
+ reader.cancel().catch(() => {});
61
+ reader.releaseLock();
62
+ };
63
+ signal.addEventListener("abort", release, { once: true });
64
+ try {
65
+ for (;;) {
66
+ if (signal.aborted) return;
67
+ const step = await reader.read();
68
+ if (signal.aborted) return;
69
+ pending += decoder.decode(step.value, { stream: !step.done });
70
+ if (step.done) pending += "\n";
71
+ let end = pending.indexOf("\n");
72
+ while (end >= 0) {
73
+ if (signal.aborted) return;
74
+ lineNumber++;
75
+ const line = pending.slice(0, end).trim();
76
+ pending = pending.slice(end + 1);
77
+ if (line) {
78
+ const chunk = parseChunk(line, lineNumber);
79
+ yield chunk;
80
+ if (chunk.type === "done") return;
81
+ }
82
+ end = pending.indexOf("\n");
83
+ }
84
+ if (step.done) break;
85
+ }
86
+ } finally {
87
+ signal.removeEventListener("abort", release);
88
+ release();
89
+ }
90
+ }
91
+ if (signal.aborted) return;
92
+ yield { type: "done" };
93
+ } catch (error) {
94
+ if (!signal.aborted) throw error;
95
+ }
96
+ } };
97
+ }
98
+ export { httpAssistantTransport };
@@ -0,0 +1,54 @@
1
+ import type { StandardSchemaV1 } from "@tanstack/form-core";
2
+ import type { JsonObject, JsonValue } from "../guards.js";
3
+ /** A tool call the model asked for. `id` ties the result back to it. */
4
+ export interface AssistantToolCall {
5
+ id: string;
6
+ toolName: string;
7
+ args: JsonObject;
8
+ }
9
+ export type AssistantMessage = {
10
+ role: "user";
11
+ content: string;
12
+ } | {
13
+ role: "assistant";
14
+ content: string;
15
+ /** Present once the calls ran and their results follow as tool messages. */
16
+ toolCalls?: AssistantToolCall[];
17
+ } | {
18
+ role: "tool";
19
+ /** The `id` of the call this answers. */
20
+ id: string;
21
+ toolName: string;
22
+ /** What the handler returned; absent when `error` is set. */
23
+ result?: JsonValue;
24
+ error?: string;
25
+ };
26
+ export type AssistantChunk = {
27
+ type: "text";
28
+ delta: string;
29
+ } | ({
30
+ type: "tool-call";
31
+ } & AssistantToolCall) | {
32
+ type: "done";
33
+ };
34
+ export interface AssistantTool {
35
+ description: string;
36
+ /** A JSON Schema object, handed to the transport as is. */
37
+ parameters: JsonObject;
38
+ /** Method syntax keeps a handler with narrower `args` assignable. */
39
+ handler(args: JsonObject): JsonValue | undefined | Promise<JsonValue | undefined>;
40
+ /** Checks the arguments before `handler` runs; its output reaches `handler`. */
41
+ schema?: StandardSchemaV1<JsonObject, JsonObject>;
42
+ /** Ask the user through the session's `confirmTool` before this tool runs. */
43
+ confirm?: boolean;
44
+ }
45
+ /** What a transport learns about a tool: no handler, schema or confirm. */
46
+ export interface AssistantToolDeclaration {
47
+ description: string;
48
+ parameters: JsonObject;
49
+ }
50
+ export interface AssistantTransport {
51
+ send(messages: readonly AssistantMessage[], tools: Readonly<Record<string, AssistantToolDeclaration>>, options: {
52
+ signal: AbortSignal;
53
+ }): AsyncIterable<AssistantChunk>;
54
+ }
@@ -1,9 +1,17 @@
1
+ import type { ResourceRecord } from "../resource/record.js";
1
2
  import type { CanAccessParams } from "./types.js";
2
- /** One grant. `record` predicate (when present) must pass against a provided record. */
3
- export interface Permission {
3
+ /**
4
+ * One grant. `record` predicate (when present) must pass against a provided
5
+ * record, and reads it as `TRecord` — the same record shape the check is given.
6
+ */
7
+ export interface Permission<TRecord extends ResourceRecord = ResourceRecord> {
4
8
  action: string | string[];
5
9
  resource: string;
6
- record?: (record: unknown) => boolean;
10
+ record?: (record: TRecord) => boolean;
7
11
  }
8
- /** Deny-by-default permission matcher (OR across permissions). */
9
- export declare function canAccessWithPermissions(permissions: Permission[], params: CanAccessParams): boolean;
12
+ /**
13
+ * Deny-by-default permission matcher (OR across permissions). `TRecord` is fixed
14
+ * by the permissions and the params together, so a grant's predicate and the
15
+ * record it reads are the same type.
16
+ */
17
+ export declare function canAccessWithPermissions<TRecord extends ResourceRecord = ResourceRecord>(permissions: Permission<TRecord>[], params: CanAccessParams<TRecord>): boolean;
@@ -1,27 +1,31 @@
1
+ // src/auth/access-control.ts
1
2
  function matchesAction(granted, action) {
2
- const list = Array.isArray(granted) ? granted : [granted];
3
- return list.includes("*") || list.includes(action);
3
+ const list = Array.isArray(granted) ? granted : [granted];
4
+ return list.includes("*") || list.includes(action);
4
5
  }
5
6
  function matchesResource(granted, resource) {
6
- if (granted === "*") return true;
7
- if (granted.endsWith(".*")) return resource.startsWith(granted.slice(0, -1));
8
- return granted === resource;
7
+ if (granted === "*") return true;
8
+ if (granted.endsWith(".*")) return resource.startsWith(granted.slice(0, -1));
9
+ return granted === resource;
9
10
  }
11
+ /**
12
+ * Deny-by-default permission matcher (OR across permissions). `TRecord` is fixed
13
+ * by the permissions and the params together, so a grant's predicate and the
14
+ * record it reads are the same type.
15
+ */
10
16
  function canAccessWithPermissions(permissions, params) {
11
- return permissions.some((permission) => {
12
- if (!matchesAction(permission.action, params.action)) return false;
13
- if (!matchesResource(permission.resource, params.resource)) return false;
14
- if (permission.record) {
15
- if (params.record == null) return false;
16
- try {
17
- return permission.record(params.record);
18
- } catch {
19
- return false;
20
- }
21
- }
22
- return true;
23
- });
17
+ return permissions.some((permission) => {
18
+ if (!matchesAction(permission.action, params.action)) return false;
19
+ if (!matchesResource(permission.resource, params.resource)) return false;
20
+ if (permission.record) {
21
+ if (params.record == null) return false;
22
+ try {
23
+ return permission.record(params.record);
24
+ } catch {
25
+ return false;
26
+ }
27
+ }
28
+ return true;
29
+ });
24
30
  }
25
- export {
26
- canAccessWithPermissions
27
- };
31
+ export { canAccessWithPermissions };
@@ -1,19 +1,25 @@
1
+ import { type SkipToken } from "@tanstack/query-core";
1
2
  import type { AuthProvider, CanAccessParams, UserIdentity } from "./types.js";
2
- /** Plain option objects, same convention as the data factories (P2). */
3
- export declare function identityQueryOptions(authProvider: AuthProvider): {
4
- queryKey: readonly ["auth", "getIdentity"];
5
- queryFn: () => Promise<UserIdentity>;
6
- };
7
- export declare function canAccessQueryOptions(authProvider: AuthProvider, params: CanAccessParams): {
8
- queryKey: readonly ["auth", "canAccess", CanAccessParams];
3
+ /** The option shape of the factories that skip with no provider. */
4
+ interface SkippableQueryOptions<TKey, TData> {
5
+ queryKey: TKey;
6
+ queryFn: SkipToken | (() => Promise<TData>);
7
+ }
8
+ /** What `getPermissions` takes: the key slot of {@link permissionsQueryOptions}. */
9
+ type PermissionsParams = Parameters<NonNullable<AuthProvider["getPermissions"]>>[0];
10
+ /**
11
+ * Plain option objects, same convention as the data factories (P2). With no
12
+ * provider, `identityQueryOptions`, `authStateQueryOptions` and
13
+ * `permissionsQueryOptions` set `queryFn: skipToken`, so the query never runs.
14
+ */
15
+ export declare function identityQueryOptions(authProvider: AuthProvider | undefined): SkippableQueryOptions<readonly ["auth", "getIdentity"], UserIdentity>;
16
+ export declare function canAccessQueryOptions(authProvider: AuthProvider | undefined, params: CanAccessParams): {
17
+ queryKey: readonly ["auth", "canAccess", CanAccessParams<import("../index.js").ResourceRecord<import("../index.js").Identifier>>];
9
18
  queryFn: () => Promise<boolean>;
10
19
  };
11
- export declare function authStateQueryOptions(authProvider: AuthProvider): {
12
- queryKey: readonly ["auth", "checkAuth"];
13
- queryFn: () => Promise<{
14
- authenticated: boolean;
15
- }>;
16
- };
20
+ export declare function authStateQueryOptions(authProvider: AuthProvider | undefined): SkippableQueryOptions<readonly ["auth", "checkAuth"], {
21
+ authenticated: boolean;
22
+ }>;
17
23
  /**
18
24
  * Fetch the current user permissions via `authProvider.getPermissions`.
19
25
  * No `getPermissions` on the provider = permissions not configured =
@@ -22,10 +28,7 @@ export declare function authStateQueryOptions(authProvider: AuthProvider): {
22
28
  * a queryFn resolving `undefined`. Mirrors {@link canAccessQueryOptions}: keyed
23
29
  * by the passed `params` so distinct param objects cache independently.
24
30
  */
25
- export declare function permissionsQueryOptions(authProvider: AuthProvider, params?: unknown): {
26
- queryKey: readonly ["auth", "getPermissions", unknown];
27
- queryFn: () => Promise<unknown>;
28
- };
31
+ export declare function permissionsQueryOptions<TPermissions>(authProvider: AuthProvider<TPermissions> | undefined, params?: unknown): SkippableQueryOptions<readonly ["auth", "getPermissions", PermissionsParams], TPermissions | null>;
29
32
  /**
30
33
  * The triage result of {@link checkError}. Vanilla (react/vue-free) so both
31
34
  * adapters share the reject-payload parsing before doing logout/notify/redirect.
@@ -55,3 +58,4 @@ export interface CheckErrorOutcome {
55
58
  * redirect; only an auth rejection ever logs the user out.
56
59
  */
57
60
  export declare function checkError(authProvider: AuthProvider, error: unknown): Promise<CheckErrorOutcome>;
61
+ export {};