experimental-ash 0.62.0 → 0.64.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 (85) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/docs/public/advanced/auth-and-route-protection.mdx +26 -20
  3. package/dist/docs/public/advanced/hooks.mdx +8 -2
  4. package/dist/docs/public/advanced/runs-and-streaming.md +6 -3
  5. package/dist/docs/public/advanced/typescript-api.md +32 -0
  6. package/dist/docs/public/channels/ash.mdx +5 -1
  7. package/dist/docs/public/channels/custom.mdx +23 -0
  8. package/dist/docs/public/frontend/README.md +8 -4
  9. package/dist/docs/public/frontend/meta.json +9 -1
  10. package/dist/docs/public/frontend/nextjs.md +4 -4
  11. package/dist/docs/public/frontend/nuxt.md +168 -0
  12. package/dist/docs/public/frontend/sveltekit.md +177 -0
  13. package/dist/docs/public/frontend/use-ash-agent-svelte.md +185 -0
  14. package/dist/docs/public/frontend/use-ash-agent-vue.md +236 -0
  15. package/dist/docs/public/frontend/use-ash-agent.md +14 -14
  16. package/dist/docs/public/getting-started.mdx +2 -0
  17. package/dist/src/channel/websocket-upgrade-server.d.ts +26 -0
  18. package/dist/src/channel/websocket-upgrade-server.js +1 -0
  19. package/dist/src/chunks/use-ash-agent-DzoSHUCb.js +1197 -0
  20. package/dist/src/chunks/use-ash-agent-KtjpWd5l.js +1229 -0
  21. package/dist/src/client/ash-agent-store.d.ts +61 -0
  22. package/dist/src/client/ash-agent-store.js +2 -0
  23. package/dist/src/client/client-error.js +1 -1
  24. package/dist/src/client/index.d.ts +2 -0
  25. package/dist/src/client/index.js +1 -1
  26. package/dist/src/compiled/.vendor-stamp.json +3 -3
  27. package/dist/src/compiled/@chat-adapter/slack/index.js +25 -25
  28. package/dist/src/compiled/@workflow/core/events-consumer.d.ts +8 -0
  29. package/dist/src/compiled/@workflow/core/index.js +2 -2
  30. package/dist/src/compiled/@workflow/core/runtime/constants.d.ts +1 -0
  31. package/dist/src/compiled/@workflow/core/runtime.js +29 -29
  32. package/dist/src/compiled/@workflow/core/version.d.ts +1 -1
  33. package/dist/src/compiled/@workflow/core/workflow.js +1 -1
  34. package/dist/src/compiled/@workflow/errors/error-codes.d.ts +2 -0
  35. package/dist/src/compiled/@workflow/errors/index.d.ts +14 -0
  36. package/dist/src/compiled/@workflow/errors/index.js +1 -1
  37. package/dist/src/compiled/@workflow/world/queue.d.ts +8 -0
  38. package/dist/src/compiled/_chunks/workflow/dist-zpK2YVVA.js +3 -0
  39. package/dist/src/compiled/_chunks/workflow/resume-hook-BFK9mgsb.js +12 -0
  40. package/dist/src/compiled/_chunks/workflow/{sleep-Bg0t23kF.js → sleep-CeJckNg2.js} +1 -1
  41. package/dist/src/compiled/_chunks/workflow/{symbols-u476uwyR.js → symbols-BWCAoPHE.js} +1 -1
  42. package/dist/src/internal/application/package.d.ts +1 -0
  43. package/dist/src/internal/application/package.js +1 -1
  44. package/dist/src/internal/nitro/host/build-application.js +1 -1
  45. package/dist/src/internal/nitro/host/channel-routes.js +2 -2
  46. package/dist/src/internal/workflow-bundle/ash-service-route-output.js +11 -1
  47. package/dist/src/packages/ash-scaffold/src/channels.js +1 -1
  48. package/dist/src/packages/ash-scaffold/src/web-template.js +5 -16
  49. package/dist/src/public/channels/auth.d.ts +45 -3
  50. package/dist/src/public/channels/auth.js +1 -1
  51. package/dist/src/public/channels/index.d.ts +1 -0
  52. package/dist/src/public/channels/index.js +1 -1
  53. package/dist/src/public/channels/slack/inbound.d.ts +3 -1
  54. package/dist/src/public/channels/slack/inbound.js +1 -1
  55. package/dist/src/public/nuxt/dev-server.d.ts +24 -0
  56. package/dist/src/public/nuxt/dev-server.js +1 -0
  57. package/dist/src/public/nuxt/index.d.ts +1 -0
  58. package/dist/src/public/nuxt/index.js +1 -0
  59. package/dist/src/public/nuxt/module.d.ts +31 -0
  60. package/dist/src/public/nuxt/module.js +1 -0
  61. package/dist/src/public/nuxt/routing.d.ts +55 -0
  62. package/dist/src/public/nuxt/routing.js +1 -0
  63. package/dist/src/public/nuxt/vercel-json.d.ts +17 -0
  64. package/dist/src/public/nuxt/vercel-json.js +1 -0
  65. package/dist/src/public/sveltekit/dev-server.d.ts +24 -0
  66. package/dist/src/public/sveltekit/dev-server.js +1 -0
  67. package/dist/src/public/sveltekit/index.d.ts +39 -0
  68. package/dist/src/public/sveltekit/index.js +1 -0
  69. package/dist/src/public/sveltekit/routing.d.ts +32 -0
  70. package/dist/src/public/sveltekit/routing.js +1 -0
  71. package/dist/src/public/sveltekit/vercel-json.d.ts +17 -0
  72. package/dist/src/public/sveltekit/vercel-json.js +1 -0
  73. package/dist/src/react/use-ash-agent.d.ts +5 -27
  74. package/dist/src/react/use-ash-agent.js +1 -2
  75. package/dist/src/svelte/index.d.ts +3 -0
  76. package/dist/src/svelte/index.js +3 -0
  77. package/dist/src/svelte/use-ash-agent.d.ts +80 -0
  78. package/dist/src/svelte/use-ash-agent.js +3 -0
  79. package/dist/src/vue/index.d.ts +3 -0
  80. package/dist/src/vue/index.js +3 -0
  81. package/dist/src/vue/use-ash-agent.d.ts +78 -0
  82. package/dist/src/vue/use-ash-agent.js +3 -0
  83. package/package.json +51 -6
  84. package/dist/src/compiled/_chunks/workflow/dist-C4EHshZE.js +0 -3
  85. package/dist/src/compiled/_chunks/workflow/resume-hook-BlALLgSA.js +0 -12
@@ -0,0 +1,185 @@
1
+ ---
2
+ title: "useAshAgent (Svelte)"
3
+ description: "Svelte 5 binding that drives an Ash agent session from the browser."
4
+ ---
5
+
6
+ `useAshAgent()` is a Svelte 5 binding that opens a long-lived Ash session in
7
+ the browser, lets the user send turns, and projects every stream event into
8
+ rune-friendly reactive data for your template.
9
+
10
+ The simplest usage:
11
+
12
+ ```svelte
13
+ <script lang="ts">
14
+ import { useAshAgent } from "experimental-ash/svelte";
15
+
16
+ const agent = useAshAgent();
17
+ </script>
18
+
19
+ {#each agent.data.messages as message}
20
+ <p>{message.role}: {JSON.stringify(message.parts)}</p>
21
+ {/each}
22
+ ```
23
+
24
+ ## What It Returns
25
+
26
+ `useAshAgent()` returns an object with reactive properties and action methods:
27
+
28
+ | Property | Type | Description |
29
+ | ------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
30
+ | `data` | `TData` | Projected state built by reducing every stream event through the reducer. With the default reducer this is `AshMessageData` containing a `messages` array. |
31
+ | `status` | `UseAshAgentStatus` | `"ready"`, `"submitted"`, `"streaming"`, or `"error"` |
32
+ | `error` | `Error \| undefined` | Last transport-level error |
33
+ | `events` | `readonly HandleMessageStreamEvent[]` | Raw server events for this session |
34
+ | `session` | `SessionState` | Snapshot of session state |
35
+ | `send` | `(input, options?) => Promise<void>` | Send a turn with full options |
36
+ | `sendMessage` | `(message, options?) => Promise<void>` | Shorthand: send a plain text turn |
37
+ | `stop` | `() => void` | Abort the in-flight request |
38
+ | `reset` | `() => void` | Clear state and start a new session |
39
+
40
+ The state fields are reactive getters. Read them directly from templates,
41
+ `$derived`, or `$effect`; do not prefix them with `$` like Svelte stores.
42
+
43
+ ## Send A Message
44
+
45
+ ```svelte
46
+ <script lang="ts">
47
+ import { useAshAgent } from "experimental-ash/svelte";
48
+
49
+ const agent = useAshAgent();
50
+ let message = $state("");
51
+
52
+ async function handleSubmit() {
53
+ const text = message.trim();
54
+ if (!text) return;
55
+ message = "";
56
+ await agent.sendMessage(text);
57
+ }
58
+ </script>
59
+
60
+ <form onsubmit={(event) => {
61
+ event.preventDefault();
62
+ void handleSubmit();
63
+ }}>
64
+ <input bind:value={message} placeholder="Type a message..." />
65
+ <button type="submit">Send</button>
66
+ </form>
67
+ ```
68
+
69
+ ## Send Attachments
70
+
71
+ Use `send()` for structured input. Attachments use the AI SDK `UserContent`
72
+ format:
73
+
74
+ ```ts
75
+ await agent.send({
76
+ message: [
77
+ { type: "text", text: "Describe this image." },
78
+ { type: "file", data: fileBytes, mimeType: "image/png" },
79
+ ],
80
+ });
81
+ ```
82
+
83
+ ## Respond To Human-In-The-Loop Prompts
84
+
85
+ When the agent requests human input (tool approvals, confirmations), the
86
+ `inputRequest` shows up inside `message.parts` on `dynamic-tool` parts. Send
87
+ `inputResponses` back:
88
+
89
+ ```ts
90
+ await agent.send({
91
+ inputResponses: [{ requestId: inputRequest.requestId, optionId: "approve" }],
92
+ });
93
+ ```
94
+
95
+ ## Stop And Reset
96
+
97
+ ```ts
98
+ agent.stop(); // abort the in-flight stream
99
+ agent.reset(); // wipe state and create a new session
100
+ ```
101
+
102
+ ## Resumable Sessions
103
+
104
+ Pass `initialSession` to restore a session from a prior page load:
105
+
106
+ ```ts
107
+ const agent = useAshAgent({
108
+ initialSession: savedSessionState,
109
+ initialEvents: savedEvents,
110
+ });
111
+ ```
112
+
113
+ ## Add Per-Turn Client Context
114
+
115
+ Use `prepareSend` to attach short-lived page state to each turn:
116
+
117
+ ```ts
118
+ const agent = useAshAgent({
119
+ prepareSend(input) {
120
+ return {
121
+ ...input,
122
+ clientContext: {
123
+ pathname: window.location.pathname,
124
+ },
125
+ };
126
+ },
127
+ });
128
+ ```
129
+
130
+ ## Custom Host And Auth
131
+
132
+ Use `host` for non-standard routing, and `auth` or `headers` for credentials:
133
+
134
+ ```ts
135
+ const agent = useAshAgent({
136
+ host: "https://agent.example.com",
137
+ headers: async () => ({
138
+ authorization: `Bearer ${await getAccessToken()}`,
139
+ }),
140
+ });
141
+ ```
142
+
143
+ ## Custom Reducer
144
+
145
+ Pass a custom `reducer` to control how stream events project into `data`:
146
+
147
+ ```ts
148
+ import { useAshAgent, type AshAgentReducer } from "experimental-ash/svelte";
149
+
150
+ interface MyData {
151
+ turns: number;
152
+ }
153
+
154
+ const reducer: AshAgentReducer<MyData> = {
155
+ initial: () => ({ turns: 0 }),
156
+ reduce: (data, event) => {
157
+ if (event.type === "turn.completed") {
158
+ return { turns: data.turns + 1 };
159
+ }
160
+ return data;
161
+ },
162
+ };
163
+
164
+ const agent = useAshAgent({ reducer });
165
+ // agent.data is MyData
166
+ ```
167
+
168
+ ## Lifecycle Callbacks
169
+
170
+ ```ts
171
+ const agent = useAshAgent({
172
+ onError: (error) => console.error(error),
173
+ onEvent: (event) => console.log(event.type),
174
+ onFinish: (snapshot) => console.log("done", snapshot.status),
175
+ onSessionChange: (session) => {
176
+ localStorage.setItem("ash-session", JSON.stringify(session));
177
+ },
178
+ });
179
+ ```
180
+
181
+ ## What To Read Next
182
+
183
+ - [SvelteKit](./sveltekit.md) - framework setup
184
+ - [Sessions And Streaming](../advanced/runs-and-streaming.md) - under the hood
185
+ - [TypeScript API](../advanced/typescript-api.md) - client exports
@@ -0,0 +1,236 @@
1
+ ---
2
+ title: "useAshAgent (Vue)"
3
+ description: "Vue composable that drives an Ash agent session from the browser."
4
+ ---
5
+
6
+ `useAshAgent()` is a Vue composable that opens a long-lived Ash session
7
+ in the browser, lets the user send turns, and projects every stream event
8
+ into reactive data for your template.
9
+
10
+ The simplest usage:
11
+
12
+ ```vue
13
+ <script setup lang="ts">
14
+ import { useAshAgent } from "experimental-ash/vue";
15
+
16
+ const { data } = useAshAgent();
17
+ </script>
18
+
19
+ <template>
20
+ <div v-for="message in data.messages" :key="message.id">
21
+ <p>{{ message.role }}: {{ message.parts }}</p>
22
+ </div>
23
+ </template>
24
+ ```
25
+
26
+ ## What It Returns
27
+
28
+ `useAshAgent()` returns an object of computed refs and action methods:
29
+
30
+ | Property | Type | Description |
31
+ | ------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
32
+ | `data` | `ComputedRef<TData>` | Projected state built by reducing every stream event through the reducer. With the default reducer this is `AshMessageData` containing a `messages` array. |
33
+ | `status` | `ComputedRef<UseAshAgentStatus>` | `"ready"`, `"submitted"`, `"streaming"`, or `"error"` |
34
+ | `error` | `ComputedRef<Error \| undefined>` | Last transport-level error |
35
+ | `events` | `ComputedRef<readonly HandleMessageStreamEvent[]>` | Raw server events for this session |
36
+ | `session` | `ComputedRef<SessionState>` | Snapshot of session state |
37
+ | `send` | `(input, options?) => Promise<void>` | Send a turn with full options |
38
+ | `sendMessage` | `(message, options?) => Promise<void>` | Shorthand: send a plain text turn |
39
+ | `stop` | `() => void` | Abort the in-flight request |
40
+ | `reset` | `() => void` | Clear state and start a new session |
41
+
42
+ The state properties (`data`, `status`, `error`, `events`, `session`) are
43
+ `ComputedRef`s; the rest are methods. Destructure whatever you need — refs keep
44
+ their reactivity through destructuring — and read them with `.value` in
45
+ `<script>` or unwrapped (no `.value`) in `<template>`.
46
+
47
+ ## Send A Message
48
+
49
+ ```vue
50
+ <script setup lang="ts">
51
+ import { useAshAgent } from "experimental-ash/vue";
52
+
53
+ const { sendMessage } = useAshAgent();
54
+ const message = ref("");
55
+
56
+ async function handleSubmit() {
57
+ const text = message.value.trim();
58
+ if (!text) return;
59
+ message.value = "";
60
+ await sendMessage(text);
61
+ }
62
+ </script>
63
+
64
+ <template>
65
+ <form @submit.prevent="handleSubmit">
66
+ <input v-model="message" placeholder="Type a message..." />
67
+ <button type="submit">Send</button>
68
+ </form>
69
+ </template>
70
+ ```
71
+
72
+ ## Send Attachments
73
+
74
+ Use `send()` for structured input. Attachments use the AI SDK `UserContent`
75
+ format:
76
+
77
+ ```vue
78
+ <script setup lang="ts">
79
+ import { useAshAgent } from "experimental-ash/vue";
80
+
81
+ const { send } = useAshAgent();
82
+
83
+ async function onFileChange(event: Event) {
84
+ const file = (event.target as HTMLInputElement).files?.[0];
85
+ if (!file) return;
86
+ await send({
87
+ message: [
88
+ { type: "text", text: "Describe this image." },
89
+ { type: "file", data: new Uint8Array(await file.arrayBuffer()), mediaType: file.type },
90
+ ],
91
+ });
92
+ }
93
+ </script>
94
+
95
+ <template>
96
+ <input type="file" accept="image/*" @change="onFileChange" />
97
+ </template>
98
+ ```
99
+
100
+ ## Respond To Human-In-The-Loop Prompts
101
+
102
+ When the agent requests human input (tool approvals, confirmations),
103
+ the `inputRequest` shows up inside `message.parts` on `dynamic-tool` parts.
104
+ Send `inputResponses` back:
105
+
106
+ ```vue
107
+ <script setup lang="ts">
108
+ import { useAshAgent, type AshDynamicToolPart } from "experimental-ash/vue";
109
+
110
+ const props = defineProps<{ part: AshDynamicToolPart }>();
111
+
112
+ const { send } = useAshAgent();
113
+
114
+ const inputRequest = computed(() => props.part.toolMetadata?.ash?.inputRequest);
115
+
116
+ function respond(optionId: string) {
117
+ const request = inputRequest.value;
118
+ if (!request) return;
119
+ void send({ inputResponses: [{ requestId: request.requestId, optionId }] });
120
+ }
121
+ </script>
122
+
123
+ <template>
124
+ <div v-if="inputRequest">
125
+ <p>{{ inputRequest.prompt }}</p>
126
+ <button v-for="option in inputRequest.options" :key="option.id" @click="respond(option.id)">
127
+ {{ option.label }}
128
+ </button>
129
+ </div>
130
+ </template>
131
+ ```
132
+
133
+ ## Stop And Reset
134
+
135
+ Abort the in-flight stream, or wipe state and start a fresh session:
136
+
137
+ ```vue
138
+ <script setup lang="ts">
139
+ import { useAshAgent } from "experimental-ash/vue";
140
+
141
+ const { status, stop, reset } = useAshAgent();
142
+
143
+ const isBusy = computed(() => status.value === "submitted" || status.value === "streaming");
144
+ </script>
145
+
146
+ <template>
147
+ <button :disabled="!isBusy" @click="stop()">Stop</button>
148
+ <button @click="reset()">Reset</button>
149
+ </template>
150
+ ```
151
+
152
+ ## Resumable Sessions
153
+
154
+ Pass `initialSession` to restore a session from a prior page load:
155
+
156
+ ```ts
157
+ const agent = useAshAgent({
158
+ initialSession: savedSessionState,
159
+ initialEvents: savedEvents,
160
+ });
161
+ ```
162
+
163
+ ## Add Per-Turn Client Context
164
+
165
+ Use `prepareSend` to attach short-lived page state to each turn:
166
+
167
+ ```ts
168
+ const agent = useAshAgent({
169
+ prepareSend(input) {
170
+ return {
171
+ ...input,
172
+ clientContext: {
173
+ pathname: window.location.pathname,
174
+ },
175
+ };
176
+ },
177
+ });
178
+ ```
179
+
180
+ ## Custom Host And Auth
181
+
182
+ Use `host` for non-standard routing, and `auth` or `headers` for
183
+ credentials:
184
+
185
+ ```ts
186
+ const agent = useAshAgent({
187
+ host: "https://agent.example.com",
188
+ headers: async () => ({
189
+ authorization: `Bearer ${await getAccessToken()}`,
190
+ }),
191
+ });
192
+ ```
193
+
194
+ ## Custom Reducer
195
+
196
+ Pass a custom `reducer` to control how stream events project into `data`:
197
+
198
+ ```ts
199
+ import { useAshAgent, type AshAgentReducer } from "experimental-ash/vue";
200
+
201
+ interface MyData {
202
+ turns: number;
203
+ }
204
+
205
+ const reducer: AshAgentReducer<MyData> = {
206
+ initial: () => ({ turns: 0 }),
207
+ reduce: (data, event) => {
208
+ if (event.type === "turn.completed") {
209
+ return { turns: data.turns + 1 };
210
+ }
211
+ return data;
212
+ },
213
+ };
214
+
215
+ const { data } = useAshAgent({ reducer });
216
+ // data.value is MyData
217
+ ```
218
+
219
+ ## Lifecycle Callbacks
220
+
221
+ ```ts
222
+ const agent = useAshAgent({
223
+ onError: (error) => console.error(error),
224
+ onEvent: (event) => console.log(event.type),
225
+ onFinish: (snapshot) => console.log("done", snapshot.status),
226
+ onSessionChange: (session) => {
227
+ localStorage.setItem("ash-session", JSON.stringify(session));
228
+ },
229
+ });
230
+ ```
231
+
232
+ ## What To Read Next
233
+
234
+ - [Nuxt](./nuxt.md) — module setup
235
+ - [Sessions And Streaming](../advanced/runs-and-streaming.md) — under the hood
236
+ - [TypeScript API](../advanced/typescript-api.md) — client exports
@@ -60,17 +60,17 @@ your component can call the agent without configuring a separate URL.
60
60
 
61
61
  `useAshAgent()` returns the current UI state plus commands:
62
62
 
63
- | Field | What it is |
64
- | ------------- | ------------------------------------------------------------------------------------------------ |
65
- | `data` | Projected UI state from the reducer. Defaults to `{ messages }`. |
66
- | `status` | `"ready"`, `"submitted"`, `"streaming"`, or `"error"`. Drives the composer. |
67
- | `error` | The last `Error` thrown, if any. |
68
- | `events` | Raw Ash stream events for this session — see [Sessions And Streaming](../runs-and-streaming.md). |
69
- | `session` | Serializable session cursor (`sessionId`, `continuationToken`, `streamIndex`). |
70
- | `sendMessage` | Send plain text. |
71
- | `send` | Send the full turn payload (multi-part messages, HITL responses). |
72
- | `stop` | Abort the active request. |
73
- | `reset` | Clear local events, data, errors, and the local session cursor. |
63
+ | Field | What it is |
64
+ | ------------- | --------------------------------------------------------------------------------------------------------- |
65
+ | `data` | Projected UI state from the reducer. Defaults to `{ messages }`. |
66
+ | `status` | `"ready"`, `"submitted"`, `"streaming"`, or `"error"`. Drives the composer. |
67
+ | `error` | The last `Error` thrown, if any. |
68
+ | `events` | Raw Ash stream events for this session — see [Sessions And Streaming](../advanced/runs-and-streaming.md). |
69
+ | `session` | Serializable session cursor (`sessionId`, `continuationToken`, `streamIndex`). |
70
+ | `sendMessage` | Send plain text. |
71
+ | `send` | Send the full turn payload (multi-part messages, HITL responses). |
72
+ | `stop` | Abort the active request. |
73
+ | `reset` | Clear local events, data, errors, and the local session cursor. |
74
74
 
75
75
  Most chat UIs only need `data.messages` and `status`. Reach for `events` when
76
76
  you want to render lower-level activity (tool calls, reasoning deltas) the
@@ -286,7 +286,7 @@ const agent = useAshAgent({
286
286
  ```
287
287
 
288
288
  `agent.data.tools` now drives the UI. The reducer sees the full Ash event
289
- stream — see [Sessions And Streaming](../runs-and-streaming.md) — plus
289
+ stream — see [Sessions And Streaming](../advanced/runs-and-streaming.md) — plus
290
290
  client-side projection events (`client.message.submitted`,
291
291
  `client.message.failed`, `client.input.responded`) for optimistic updates.
292
292
 
@@ -323,6 +323,6 @@ To change credentials without remounting, pass a function to `auth` or
323
323
  ## What To Read Next
324
324
 
325
325
  - [Next.js](./nextjs.md)
326
- - [Sessions And Streaming](../runs-and-streaming.md)
326
+ - [Sessions And Streaming](../advanced/runs-and-streaming.md)
327
327
  - [Human In The Loop](../human-in-the-loop.md)
328
- - [Hooks](../hooks.md)
328
+ - [Hooks](../advanced/hooks.mdx)
@@ -174,3 +174,5 @@ curl -X POST http://127.0.0.1:3000/ash/v1/session/<sessionId> \
174
174
  - [Skills](./skills.md) for on-demand procedures
175
175
  - [Tools](./tools.mdx) for typed integrations
176
176
  - [Sessions And Streaming](./runs-and-streaming.md) for the durable session model
177
+ - [Nuxt Integration](./frontend/nuxt.md) for Nuxt apps
178
+ - [`useAshAgent` (Vue)](./frontend/use-ash-agent-vue.md) for Vue composable usage
@@ -0,0 +1,26 @@
1
+ import { type Server } from "node:http";
2
+ import type { WebSocketRouteHandler } from "#channel/routes.js";
3
+ /**
4
+ * Escape hatch for SDKs and frameworks that need a Node HTTP server-shaped
5
+ * upgrade target inside an Ash `WS()` route.
6
+ *
7
+ * Prefer normal `WS()` lifecycle hooks for Ash-owned websocket behavior. Use
8
+ * this bridge only when an integration expects to register
9
+ * `httpServer.on("upgrade", ...)` handlers itself. The server is intentionally
10
+ * not listening on a port; Ash forwards only the matched route's raw Node
11
+ * upgrade into it.
12
+ */
13
+ export interface WebSocketUpgradeServerBridge {
14
+ readonly route: WebSocketRouteHandler;
15
+ readonly server: Server;
16
+ }
17
+ /**
18
+ * Creates an escape-hatch Node HTTP server facade plus a `WS()` route handler
19
+ * that forwards matched raw upgrade events into that server.
20
+ *
21
+ * Prefer authoring websocket behavior directly with `WS()` hooks. Reach for
22
+ * this only when an SDK or framework binds to `http.Server` upgrade events,
23
+ * such as `engine.attach(server, path, ...)`. It works only on hosts where
24
+ * Nitro exposes a Node upgrade tuple for the matched WebSocket route.
25
+ */
26
+ export declare function createWebSocketUpgradeServer(): WebSocketUpgradeServerBridge;
@@ -0,0 +1 @@
1
+ import{createServer}from"node:http";function createWebSocketUpgradeServer(){let t=createServer();return{route:createWebSocketUpgradeRoute(t),server:t}}function createWebSocketUpgradeRoute(e){return()=>({upgrade(t){return dispatchNodeUpgrade(e,t)}})}async function dispatchNodeUpgrade(e,t){let n=resolveNodeUpgrade(t);if(n===null)return Response.json({error:`This WebSocket route cannot expose a Node upgrade event on the current host.`,ok:!1},{status:501});let r=e.listeners(`upgrade`);if(r.length===0)return Response.json({error:`No upgrade listeners are registered on this WebSocket server bridge.`,ok:!1},{status:500});for(let t of r)await Promise.resolve(t.call(e,n.request,n.socket,n.head));return{handled:!0}}function resolveNodeUpgrade(e){let t=e.runtime?.node,n=t?.req,r=t?.upgrade?.socket,i=t?.upgrade?.head;if(n===void 0||r===void 0||i===void 0)return null;if(typeof n.url!=`string`||n.url.length===0){let t=new URL(e.url);n.url=`${t.pathname}${t.search}`}return{head:i,request:n,socket:r}}export{createWebSocketUpgradeServer};