experimental-a2 0.5.1 → 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.
- package/CHANGELOG.md +67 -0
- package/dist/ai-server.d.ts +4 -5
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +8 -7
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +334 -2
- package/dist/ai.d.ts.map +1 -0
- package/dist/ai.js +1 -1
- package/dist/client.d.ts +202 -2
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +1025 -1
- package/dist/client.js.map +1 -0
- package/dist/errors-BQuJpe82.js.map +1 -1
- package/dist/index.d.ts +22 -3
- package/dist/index.d.ts.map +1 -0
- package/dist/{internal-DstsI6Re.js → internal-DRXJ56EI.js} +5 -28
- package/dist/internal-DRXJ56EI.js.map +1 -0
- package/dist/react.d.ts +41 -7
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +74 -35
- package/dist/react.js.map +1 -1
- package/dist/scheduler-qstash.d.ts +3 -3
- package/dist/scheduler-qstash.js +4 -5
- package/dist/scheduler-qstash.js.map +1 -1
- package/dist/scheduler-vercel.d.ts +2 -2
- package/dist/scheduler-vercel.js +4 -4
- package/dist/scheduler-vercel.js.map +1 -1
- package/dist/{server-Duw6MVlB.js → server-286j79Mt.js} +708 -79
- package/dist/server-286j79Mt.js.map +1 -0
- package/dist/{server-DpvjhdoE.d.ts → server-DgXmORIq.d.ts} +67 -49
- package/dist/server-DgXmORIq.d.ts.map +1 -0
- package/dist/server.d.ts +3 -3
- package/dist/server.js +1 -1
- package/dist/store-N8PXxDAS.js.map +1 -1
- package/dist/{store-DysUkTH3.d.ts → store-flRz1OWh.d.ts} +2 -57
- package/dist/store-flRz1OWh.d.ts.map +1 -0
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-memory.d.ts.map +1 -1
- package/dist/store-memory.js +1 -59
- package/dist/store-memory.js.map +1 -1
- package/dist/{store-polling-dSeLxzfb.js → store-polling-6DW7F1DT.js} +2 -2
- package/dist/{store-polling-dSeLxzfb.js.map → store-polling-6DW7F1DT.js.map} +1 -1
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-postgres.js +1 -81
- package/dist/store-postgres.js.map +1 -1
- package/dist/{store-redis-core-BFLwz0Wj.js → store-redis-core-DEYO8Ryv.js} +48 -134
- package/dist/store-redis-core-DEYO8Ryv.js.map +1 -0
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.js +2 -3
- package/dist/store-redis-http.js.map +1 -1
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.js +3 -4
- package/dist/store-redis.js.map +1 -1
- package/dist/store-sqlite.d.ts +1 -1
- package/dist/store-sqlite.d.ts.map +1 -1
- package/dist/store-sqlite.js +1 -72
- package/dist/store-sqlite.js.map +1 -1
- package/dist/{wire-BFQmSJ-9.js → wire-B6te_wns.js} +4 -3
- package/dist/wire-B6te_wns.js.map +1 -0
- package/docs/guides/03-react.mdx +118 -29
- package/docs/guides/05-production.mdx +9 -11
- package/docs/guides/06-ai-agents.mdx +8 -14
- package/docs/guides/09-presence.mdx +19 -21
- package/docs/guides/10-transports.mdx +104 -86
- package/docs/reference/01-api.mdx +186 -279
- package/docs/reference/02-errors.mdx +5 -7
- package/package.json +1 -14
- package/src/ai-server.ts +9 -5
- package/src/client.ts +71 -35
- package/src/errors.ts +1 -0
- package/src/internal.ts +3 -62
- package/src/push-envelope.ts +24 -21
- package/src/react.ts +118 -44
- package/src/scheduler-qstash.ts +3 -3
- package/src/scheduler-vercel.ts +2 -2
- package/src/server-fetch.ts +344 -0
- package/src/server.ts +73 -225
- package/src/session-socket.ts +36 -20
- package/src/sse.ts +2 -2
- package/src/store-memory.ts +0 -81
- package/src/store-postgres.ts +0 -100
- package/src/store-redis-core.ts +47 -211
- package/src/store-redis-http.ts +0 -1
- package/src/store-redis.ts +0 -1
- package/src/store-sqlite.ts +0 -119
- package/src/store.ts +0 -60
- package/src/wire.ts +2 -1
- package/dist/ai-D_PGS-JR.d.ts +0 -334
- package/dist/ai-D_PGS-JR.d.ts.map +0 -1
- package/dist/cli-B3VuxoDe.js +0 -599
- package/dist/cli-B3VuxoDe.js.map +0 -1
- package/dist/cli-bin.d.ts +0 -1
- package/dist/cli-bin.js +0 -7
- package/dist/cli-bin.js.map +0 -1
- package/dist/cli.d.ts +0 -20
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -2
- package/dist/client-BKlyLiOU.js +0 -1008
- package/dist/client-BKlyLiOU.js.map +0 -1
- package/dist/client-D7mvIXrF.d.ts +0 -191
- package/dist/client-D7mvIXrF.d.ts.map +0 -1
- package/dist/devtools-J_jZ2vQf.d.ts +0 -152
- package/dist/devtools-J_jZ2vQf.d.ts.map +0 -1
- package/dist/devtools-kJJaORn-.js +0 -340
- package/dist/devtools-kJJaORn-.js.map +0 -1
- package/dist/devtools-server.browser.d.ts +0 -1
- package/dist/devtools-server.browser.js +0 -6
- package/dist/devtools-server.browser.js.map +0 -1
- package/dist/devtools-server.d.ts +0 -23
- package/dist/devtools-server.d.ts.map +0 -1
- package/dist/devtools-server.js +0 -1270
- package/dist/devtools-server.js.map +0 -1
- package/dist/devtools.d.ts +0 -2
- package/dist/devtools.js +0 -2
- package/dist/errors-W6nwJ-fm.d.ts +0 -21
- package/dist/errors-W6nwJ-fm.d.ts.map +0 -1
- package/dist/http.d.ts +0 -151
- package/dist/http.d.ts.map +0 -1
- package/dist/http.js +0 -706
- package/dist/http.js.map +0 -1
- package/dist/inspection-DaxB5jM2.js +0 -13
- package/dist/inspection-DaxB5jM2.js.map +0 -1
- package/dist/internal-DstsI6Re.js.map +0 -1
- package/dist/platform-B4TnJtWu.js +0 -34
- package/dist/platform-B4TnJtWu.js.map +0 -1
- package/dist/server-DpvjhdoE.d.ts.map +0 -1
- package/dist/server-Duw6MVlB.js.map +0 -1
- package/dist/store-DysUkTH3.d.ts.map +0 -1
- package/dist/store-redis-core-BFLwz0Wj.js.map +0 -1
- package/dist/testing.browser.d.ts +0 -1
- package/dist/testing.browser.js +0 -6
- package/dist/testing.browser.js.map +0 -1
- package/dist/testing.d.ts +0 -32
- package/dist/testing.d.ts.map +0 -1
- package/dist/testing.js +0 -103
- package/dist/testing.js.map +0 -1
- package/dist/wire-BFQmSJ-9.js.map +0 -1
- package/docs/guides/07-devtools.mdx +0 -229
- package/src/cli-bin.ts +0 -5
- package/src/cli.ts +0 -1046
- package/src/devtools-app.ts +0 -989
- package/src/devtools-server.browser.ts +0 -5
- package/src/devtools-server.ts +0 -604
- package/src/devtools.ts +0 -716
- package/src/http.ts +0 -394
- package/src/inspection.ts +0 -39
- package/src/testing.browser.ts +0 -5
- package/src/testing.ts +0 -185
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AppendInput,
|
|
3
|
+
ContractEvent,
|
|
4
|
+
EventDefs,
|
|
5
|
+
PresenceDefs,
|
|
6
|
+
} from './contract.ts'
|
|
7
|
+
import { A2Error } from './errors.ts'
|
|
8
|
+
import { invocationDeadlineMs } from './platform.ts'
|
|
9
|
+
import {
|
|
10
|
+
invalidPushBody,
|
|
11
|
+
parsePushBody,
|
|
12
|
+
type ParsedPushEvent,
|
|
13
|
+
type ParsedPushPresence,
|
|
14
|
+
} from './push-envelope.ts'
|
|
15
|
+
import type { A2Server } from './server.ts'
|
|
16
|
+
import {
|
|
17
|
+
sessionsSocket,
|
|
18
|
+
type A2Socket,
|
|
19
|
+
type SocketSession,
|
|
20
|
+
} from './session-socket.ts'
|
|
21
|
+
import { sseResponse } from './sse.ts'
|
|
22
|
+
import { asA2Error, errorStatus, errorToWire, eventToWire } from './wire.ts'
|
|
23
|
+
|
|
24
|
+
type SuggestedEventType<D extends EventDefs> =
|
|
25
|
+
(keyof D & string) | (string & Record<never, never>)
|
|
26
|
+
|
|
27
|
+
type SuggestedPresenceValues<P extends PresenceDefs> = Readonly<
|
|
28
|
+
{ [F in keyof P & string]?: unknown } & Record<string, unknown>
|
|
29
|
+
>
|
|
30
|
+
|
|
31
|
+
export type A2PushEvent<D extends EventDefs = EventDefs> = {
|
|
32
|
+
readonly type: SuggestedEventType<D>
|
|
33
|
+
readonly payload: unknown
|
|
34
|
+
readonly id?: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type A2PushPresence<P extends PresenceDefs = PresenceDefs> = {
|
|
38
|
+
readonly participant: string
|
|
39
|
+
readonly values: SuggestedPresenceValues<P>
|
|
40
|
+
readonly seen?: number
|
|
41
|
+
readonly at?: number
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type A2Operation<
|
|
45
|
+
D extends EventDefs = EventDefs,
|
|
46
|
+
P extends PresenceDefs = Record<never, never>,
|
|
47
|
+
> =
|
|
48
|
+
| {
|
|
49
|
+
readonly type: 'stream'
|
|
50
|
+
readonly sessionId: string
|
|
51
|
+
readonly startAfter: number
|
|
52
|
+
readonly transport: 'http' | 'websocket'
|
|
53
|
+
}
|
|
54
|
+
| {
|
|
55
|
+
readonly type: 'history'
|
|
56
|
+
readonly sessionId: string
|
|
57
|
+
readonly gte: number
|
|
58
|
+
readonly lte: number
|
|
59
|
+
readonly transport: 'http'
|
|
60
|
+
}
|
|
61
|
+
| {
|
|
62
|
+
readonly type: 'push'
|
|
63
|
+
readonly sessionId: string
|
|
64
|
+
readonly events: readonly A2PushEvent<D>[]
|
|
65
|
+
readonly presence?: A2PushPresence<P>
|
|
66
|
+
readonly transport: 'http' | 'websocket'
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type UpgradeWebSocket = (
|
|
70
|
+
attach: (socket: A2Socket) => void,
|
|
71
|
+
) => Response | Promise<Response>
|
|
72
|
+
|
|
73
|
+
export type ServerFetchOptions<
|
|
74
|
+
D extends EventDefs = EventDefs,
|
|
75
|
+
P extends PresenceDefs = Record<never, never>,
|
|
76
|
+
> = {
|
|
77
|
+
authorize?: (operation: A2Operation<D, P>) => boolean | Promise<boolean>
|
|
78
|
+
upgradeWebSocket?: UpgradeWebSocket
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type ServerIngressContext = {
|
|
82
|
+
sessionId: string
|
|
83
|
+
events: readonly ParsedPushEvent[]
|
|
84
|
+
presence?: ParsedPushPresence
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
type ServerFetchHooks = {
|
|
88
|
+
validateIngress?(context: ServerIngressContext): void | PromiseLike<void>
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const serverFetchHooks = new WeakMap<object, ServerFetchHooks>()
|
|
92
|
+
|
|
93
|
+
export function setServerFetchHooks<
|
|
94
|
+
D extends EventDefs,
|
|
95
|
+
P extends PresenceDefs,
|
|
96
|
+
>(server: Pick<A2Server<D, P>, 'fetch'>, hooks: ServerFetchHooks): void {
|
|
97
|
+
serverFetchHooks.set(server.fetch, hooks)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
type FetchServer<D extends EventDefs, P extends PresenceDefs> = Pick<
|
|
101
|
+
A2Server<D, P>,
|
|
102
|
+
'contract' | 'session'
|
|
103
|
+
>
|
|
104
|
+
|
|
105
|
+
type ServerFetch<D extends EventDefs, P extends PresenceDefs> = {
|
|
106
|
+
(request: Request): Promise<Response>
|
|
107
|
+
(request: Request, options: ServerFetchOptions<D, P>): Promise<Response>
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const parseHistoryBounds = (
|
|
111
|
+
gte: string | null,
|
|
112
|
+
lte: string | null,
|
|
113
|
+
): { gte: number; lte: number } => {
|
|
114
|
+
if (gte === null || lte === null) {
|
|
115
|
+
throw new A2Error(
|
|
116
|
+
'INVALID_PAYLOAD',
|
|
117
|
+
'a history read takes both gte and lte',
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
const bounds = { gte: Number(gte), lte: Number(lte) }
|
|
121
|
+
for (const [name, value] of Object.entries(bounds)) {
|
|
122
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
123
|
+
throw new A2Error(
|
|
124
|
+
'INVALID_PAYLOAD',
|
|
125
|
+
`history ${name} must be a non-negative safe integer`,
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (bounds.gte > bounds.lte) {
|
|
130
|
+
throw new A2Error(
|
|
131
|
+
'INVALID_PAYLOAD',
|
|
132
|
+
'history gte must be less than or equal to lte',
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
return bounds
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const parseResumeIndex = (raw: string | null): number => {
|
|
139
|
+
if (raw === null) return 0
|
|
140
|
+
const index = Number(raw)
|
|
141
|
+
if (!Number.isSafeInteger(index) || index < 0) {
|
|
142
|
+
throw new A2Error(
|
|
143
|
+
'INVALID_PAYLOAD',
|
|
144
|
+
'stream index must be a non-negative safe integer',
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
return index
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const errorResponse = (error: unknown): Response => {
|
|
151
|
+
const a2error = asA2Error(error)
|
|
152
|
+
return Response.json(errorToWire(a2error), {
|
|
153
|
+
status: errorStatus(a2error.code),
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const forbidden = (): A2Error =>
|
|
158
|
+
new A2Error('FORBIDDEN', 'the operation is not authorized')
|
|
159
|
+
|
|
160
|
+
const freezePush = <D extends EventDefs, P extends PresenceDefs>(
|
|
161
|
+
sessionId: string,
|
|
162
|
+
events: ParsedPushEvent[],
|
|
163
|
+
presence: ParsedPushPresence | undefined,
|
|
164
|
+
transport: 'http' | 'websocket',
|
|
165
|
+
): A2Operation<D, P> & { type: 'push' } => {
|
|
166
|
+
for (const event of events) Object.freeze(event)
|
|
167
|
+
Object.freeze(events)
|
|
168
|
+
if (presence !== undefined) {
|
|
169
|
+
Object.freeze(presence.values)
|
|
170
|
+
Object.freeze(presence)
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
type: 'push',
|
|
174
|
+
sessionId,
|
|
175
|
+
events,
|
|
176
|
+
...(presence === undefined
|
|
177
|
+
? {}
|
|
178
|
+
: { presence: presence as A2PushPresence<P> }),
|
|
179
|
+
transport,
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function createServerFetch<
|
|
184
|
+
D extends EventDefs,
|
|
185
|
+
P extends PresenceDefs = Record<never, never>,
|
|
186
|
+
>(server: FetchServer<D, P>): ServerFetch<D, P> {
|
|
187
|
+
const presence = Object.keys(server.contract.presence).length > 0
|
|
188
|
+
|
|
189
|
+
const fetch = async (
|
|
190
|
+
request: Request,
|
|
191
|
+
options?: ServerFetchOptions<D, P>,
|
|
192
|
+
): Promise<Response> => {
|
|
193
|
+
const authorized = async (
|
|
194
|
+
operation: A2Operation<D, P>,
|
|
195
|
+
): Promise<boolean> => {
|
|
196
|
+
try {
|
|
197
|
+
return (await options?.authorize?.(Object.freeze(operation))) !== false
|
|
198
|
+
} catch (cause) {
|
|
199
|
+
throw new A2Error('STORE_UNAVAILABLE', 'authorization failed', {
|
|
200
|
+
cause,
|
|
201
|
+
})
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const authorize = async (operation: A2Operation<D, P>): Promise<void> => {
|
|
206
|
+
if (!(await authorized(operation))) throw forbidden()
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const validateIngress = async (
|
|
210
|
+
context: ServerIngressContext,
|
|
211
|
+
): Promise<void> => {
|
|
212
|
+
await serverFetchHooks.get(fetch)?.validateIngress?.(context)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const attach = (socket: A2Socket): void => {
|
|
216
|
+
const deadline = invocationDeadlineMs()
|
|
217
|
+
sessionsSocket<D>(
|
|
218
|
+
async (sessionId, startAfter) => {
|
|
219
|
+
const operation: A2Operation<D, P> = {
|
|
220
|
+
type: 'stream',
|
|
221
|
+
sessionId,
|
|
222
|
+
startAfter,
|
|
223
|
+
transport: 'websocket',
|
|
224
|
+
}
|
|
225
|
+
return (await authorized(operation))
|
|
226
|
+
? server.session(sessionId)
|
|
227
|
+
: null
|
|
228
|
+
},
|
|
229
|
+
socket,
|
|
230
|
+
{
|
|
231
|
+
presence,
|
|
232
|
+
...(deadline === null ? {} : { deadline }),
|
|
233
|
+
gatePush: async (sessionId, events, pushedPresence) => {
|
|
234
|
+
try {
|
|
235
|
+
await authorize(
|
|
236
|
+
freezePush<D, P>(
|
|
237
|
+
sessionId,
|
|
238
|
+
events,
|
|
239
|
+
pushedPresence,
|
|
240
|
+
'websocket',
|
|
241
|
+
),
|
|
242
|
+
)
|
|
243
|
+
await validateIngress({
|
|
244
|
+
sessionId,
|
|
245
|
+
events,
|
|
246
|
+
...(pushedPresence === undefined
|
|
247
|
+
? {}
|
|
248
|
+
: { presence: pushedPresence }),
|
|
249
|
+
})
|
|
250
|
+
return null
|
|
251
|
+
} catch (error) {
|
|
252
|
+
return asA2Error(error)
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
try {
|
|
260
|
+
if (request.method === 'GET') {
|
|
261
|
+
if (request.headers.get('upgrade')?.toLowerCase() === 'websocket') {
|
|
262
|
+
if (options?.upgradeWebSocket === undefined) {
|
|
263
|
+
return new Response(
|
|
264
|
+
'WebSocket upgrade requested, but server.fetch() has no upgrade implementation',
|
|
265
|
+
{ status: 426 },
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
return await options.upgradeWebSocket(attach)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const { searchParams } = new URL(request.url)
|
|
272
|
+
const sessionId = searchParams.get('sessionId')
|
|
273
|
+
if (sessionId === null || sessionId.length === 0) {
|
|
274
|
+
throw new A2Error('INVALID_PAYLOAD', 'missing sessionId')
|
|
275
|
+
}
|
|
276
|
+
const gteRaw = searchParams.get('gte')
|
|
277
|
+
const lteRaw = searchParams.get('lte')
|
|
278
|
+
if (gteRaw !== null || lteRaw !== null) {
|
|
279
|
+
const bounds = parseHistoryBounds(gteRaw, lteRaw)
|
|
280
|
+
await authorize({
|
|
281
|
+
type: 'history',
|
|
282
|
+
sessionId,
|
|
283
|
+
...bounds,
|
|
284
|
+
transport: 'http',
|
|
285
|
+
})
|
|
286
|
+
const events = await server.session(sessionId).history(bounds)
|
|
287
|
+
const covered = events.length === bounds.lte - bounds.gte + 1
|
|
288
|
+
return Response.json(events.map(eventToWire), {
|
|
289
|
+
headers: { 'a2-history-covered': String(covered) },
|
|
290
|
+
})
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const startAfter = parseResumeIndex(searchParams.get('index'))
|
|
294
|
+
await authorize({
|
|
295
|
+
type: 'stream',
|
|
296
|
+
sessionId,
|
|
297
|
+
startAfter,
|
|
298
|
+
transport: 'http',
|
|
299
|
+
})
|
|
300
|
+
const target = server.session(sessionId) as unknown as SocketSession
|
|
301
|
+
return sseResponse(
|
|
302
|
+
presence
|
|
303
|
+
? target.stream({ startAfter, presence: true })
|
|
304
|
+
: target.stream({ startAfter }),
|
|
305
|
+
)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (request.method === 'POST') {
|
|
309
|
+
const body = await parsePushBody(request)
|
|
310
|
+
await authorize(
|
|
311
|
+
freezePush<D, P>(body.sessionId, body.events, body.presence, 'http'),
|
|
312
|
+
)
|
|
313
|
+
await validateIngress({
|
|
314
|
+
sessionId: body.sessionId,
|
|
315
|
+
events: body.events,
|
|
316
|
+
...(body.presence === undefined ? {} : { presence: body.presence }),
|
|
317
|
+
})
|
|
318
|
+
const session = server.session(body.sessionId)
|
|
319
|
+
if (body.presence !== undefined) {
|
|
320
|
+
const target = session as unknown as SocketSession
|
|
321
|
+
if (typeof target.setPresence !== 'function') {
|
|
322
|
+
throw invalidPushBody('presence is not declared by this contract')
|
|
323
|
+
}
|
|
324
|
+
await target.setPresence(body.presence)
|
|
325
|
+
}
|
|
326
|
+
const appended: ContractEvent<D>[] =
|
|
327
|
+
body.events.length === 0
|
|
328
|
+
? []
|
|
329
|
+
: await session.append(
|
|
330
|
+
...(body.events as unknown as AppendInput<D>[]),
|
|
331
|
+
)
|
|
332
|
+
return Response.json(appended.map(eventToWire))
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return new Response('Method Not Allowed', {
|
|
336
|
+
status: 405,
|
|
337
|
+
headers: { allow: 'GET, POST' },
|
|
338
|
+
})
|
|
339
|
+
} catch (error) {
|
|
340
|
+
return errorResponse(error)
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return fetch
|
|
344
|
+
}
|