experimental-a2 0.8.0 → 0.9.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/AGENTS.md +11 -0
- package/CHANGELOG.md +36 -0
- package/README.md +29 -0
- package/dist/actor-client.d.ts +46 -0
- package/dist/actor-client.d.ts.map +1 -0
- package/dist/actor-client.js +54 -0
- package/dist/actor-client.js.map +1 -0
- package/dist/actor-react.d.ts +54 -0
- package/dist/actor-react.d.ts.map +1 -0
- package/dist/actor-react.js +79 -0
- package/dist/actor-react.js.map +1 -0
- package/dist/actor-shared-BACubf4x.d.ts +136 -0
- package/dist/actor-shared-BACubf4x.d.ts.map +1 -0
- package/dist/actor-shared-DI7J5upy.js +127 -0
- package/dist/actor-shared-DI7J5upy.js.map +1 -0
- package/dist/actor.browser.d.ts +1 -0
- package/dist/actor.browser.js +13 -0
- package/dist/actor.browser.js.map +1 -0
- package/dist/actor.d.ts +176 -0
- package/dist/actor.d.ts.map +1 -0
- package/dist/actor.js +437 -0
- package/dist/actor.js.map +1 -0
- package/dist/ai-server.d.ts +2 -2
- package/dist/ai-server.js +2 -2
- package/dist/ai.d.ts +2 -2
- package/dist/client.d.ts +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +4 -4
- package/dist/client.js.map +1 -1
- package/dist/{errors-BQuJpe82.js → errors-DCk6ch5n.js} +16 -2
- package/dist/{errors-BQuJpe82.js.map → errors-DCk6ch5n.js.map} +1 -1
- package/dist/{idempotent-replay-DuqEkYA7.js → idempotent-replay-DVOlyYbx.js} +2 -2
- package/dist/{idempotent-replay-DuqEkYA7.js.map → idempotent-replay-DVOlyYbx.js.map} +1 -1
- package/dist/index.d.ts +16 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/react.d.ts +1 -1
- package/dist/{contract-jIfaR085.d.ts → reducer-DJKWm3cp.d.ts} +39 -39
- package/dist/reducer-DJKWm3cp.d.ts.map +1 -0
- package/dist/scheduler-qstash.d.ts +2 -2
- package/dist/scheduler-qstash.js +2 -2
- package/dist/scheduler-vercel.d.ts +2 -2
- package/dist/scheduler-vercel.js +1 -1
- package/dist/{server-B2XNevQA.js → server-CBET-jSz.js} +6 -6
- package/dist/server-CBET-jSz.js.map +1 -0
- package/dist/{server-DjPhHnbI.d.ts → server-CKY3_lbw.d.ts} +3 -3
- package/dist/{server-DjPhHnbI.d.ts.map → server-CKY3_lbw.d.ts.map} +1 -1
- package/dist/server.d.ts +3 -3
- package/dist/server.js +1 -1
- package/dist/{store-RJO35BMj.d.ts → store-DGHeBtIQ.d.ts} +2 -2
- package/dist/{store-RJO35BMj.d.ts.map → store-DGHeBtIQ.d.ts.map} +1 -1
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-memory.js +2 -2
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-postgres.js +2 -2
- package/dist/{store-redis-core-DT01r4GZ.js → store-redis-core-z-ykbyMg.js} +3 -3
- package/dist/{store-redis-core-DT01r4GZ.js.map → store-redis-core-z-ykbyMg.js.map} +1 -1
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.js +2 -2
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.js +2 -2
- package/dist/store-sqlite.d.ts +1 -1
- package/dist/store-sqlite.js +2 -2
- package/dist/{wire-B6te_wns.js → wire--yji6mO3.js} +2 -2
- package/dist/{wire-B6te_wns.js.map → wire--yji6mO3.js.map} +1 -1
- package/docs/actors/01-introduction.mdx +189 -0
- package/docs/actors/02-concurrency.mdx +154 -0
- package/docs/actors/03-timers.mdx +120 -0
- package/docs/actors/04-routes.mdx +352 -0
- package/docs/actors/meta.ts +1 -0
- package/docs/concepts/meta.ts +1 -0
- package/docs/guides/07-examples.mdx +56 -0
- package/docs/guides/meta.ts +1 -0
- package/docs/index.mdx +16 -0
- package/docs/reference/02-errors.mdx +33 -0
- package/docs/reference/meta.ts +1 -0
- package/examples/README.md +15 -0
- package/examples/playground/AGENTS.md +11 -0
- package/examples/playground/DEPLOY.md +106 -0
- package/examples/playground/README.md +19 -0
- package/examples/playground/activity-feed.test.ts +10 -0
- package/examples/playground/app/agent/[agentId]/agent-client.tsx +376 -0
- package/examples/playground/app/agent/[agentId]/page.tsx +29 -0
- package/examples/playground/app/agent/events/route.ts +4 -0
- package/examples/playground/app/agent/model.ts +3 -0
- package/examples/playground/app/agent/new-agent-session.tsx +98 -0
- package/examples/playground/app/agent/page.tsx +25 -0
- package/examples/playground/app/agent/scheduler/route.ts +5 -0
- package/examples/playground/app/agent/server.ts +153 -0
- package/examples/playground/app/agent/session.ts +13 -0
- package/examples/playground/app/canvas/[canvasId]/canvas-client.tsx +682 -0
- package/examples/playground/app/canvas/[canvasId]/canvas-replay.test.ts +68 -0
- package/examples/playground/app/canvas/[canvasId]/canvas-replay.ts +19 -0
- package/examples/playground/app/canvas/[canvasId]/page.tsx +22 -0
- package/examples/playground/app/canvas/[canvasId]/session.ts +19 -0
- package/examples/playground/app/canvas/events/route.ts +13 -0
- package/examples/playground/app/canvas/model.ts +94 -0
- package/examples/playground/app/canvas/open-canvas.tsx +40 -0
- package/examples/playground/app/canvas/page.tsx +20 -0
- package/examples/playground/app/canvas/server.ts +9 -0
- package/examples/playground/app/chat/[chatId]/agent-stream-drawer.test.tsx +118 -0
- package/examples/playground/app/chat/[chatId]/agent-stream-drawer.tsx +316 -0
- package/examples/playground/app/chat/[chatId]/chat-client.tsx +922 -0
- package/examples/playground/app/chat/[chatId]/chat-view.test.ts +152 -0
- package/examples/playground/app/chat/[chatId]/chat-view.ts +101 -0
- package/examples/playground/app/chat/[chatId]/composer.test.ts +44 -0
- package/examples/playground/app/chat/[chatId]/composer.ts +30 -0
- package/examples/playground/app/chat/[chatId]/page.tsx +30 -0
- package/examples/playground/app/chat/[chatId]/session.ts +7 -0
- package/examples/playground/app/chat/events/route.ts +7 -0
- package/examples/playground/app/chat/model.test.ts +155 -0
- package/examples/playground/app/chat/model.ts +310 -0
- package/examples/playground/app/chat/new-conversation.tsx +16 -0
- package/examples/playground/app/chat/page.tsx +25 -0
- package/examples/playground/app/chat/scheduler/route.ts +5 -0
- package/examples/playground/app/chat/server.ts +184 -0
- package/examples/playground/app/components/activity-feed.tsx +54 -0
- package/examples/playground/app/components/connection-pill.tsx +29 -0
- package/examples/playground/app/counter/counter-client.tsx +72 -0
- package/examples/playground/app/counter/events/route.ts +4 -0
- package/examples/playground/app/counter/model.test.ts +36 -0
- package/examples/playground/app/counter/model.ts +31 -0
- package/examples/playground/app/counter/page.tsx +24 -0
- package/examples/playground/app/counter/server.ts +9 -0
- package/examples/playground/app/counter/session.ts +13 -0
- package/examples/playground/app/documents/[documentId]/code-editor.tsx +80 -0
- package/examples/playground/app/documents/[documentId]/document-client.tsx +525 -0
- package/examples/playground/app/documents/[documentId]/page.tsx +23 -0
- package/examples/playground/app/documents/[documentId]/session.ts +7 -0
- package/examples/playground/app/documents/events/route.ts +4 -0
- package/examples/playground/app/documents/model.ts +55 -0
- package/examples/playground/app/documents/open-document.tsx +40 -0
- package/examples/playground/app/documents/page.tsx +22 -0
- package/examples/playground/app/documents/server.ts +9 -0
- package/examples/playground/app/globals.css +2078 -0
- package/examples/playground/app/layout.tsx +44 -0
- package/examples/playground/app/orders/[orderId]/order-client.tsx +140 -0
- package/examples/playground/app/orders/[orderId]/page.tsx +29 -0
- package/examples/playground/app/orders/[orderId]/session.ts +11 -0
- package/examples/playground/app/orders/create/route.ts +30 -0
- package/examples/playground/app/orders/events/route.ts +4 -0
- package/examples/playground/app/orders/model.ts +79 -0
- package/examples/playground/app/orders/new-order-form.tsx +98 -0
- package/examples/playground/app/orders/page.tsx +22 -0
- package/examples/playground/app/orders/scheduler/route.ts +5 -0
- package/examples/playground/app/orders/server.ts +50 -0
- package/examples/playground/app/page.tsx +111 -0
- package/examples/playground/app/recovery/[recoveryId]/page.tsx +31 -0
- package/examples/playground/app/recovery/[recoveryId]/recovery-client.tsx +144 -0
- package/examples/playground/app/recovery/[recoveryId]/session.ts +7 -0
- package/examples/playground/app/recovery/events/route.ts +3 -0
- package/examples/playground/app/recovery/model.ts +55 -0
- package/examples/playground/app/recovery/new-recovery-session.tsx +20 -0
- package/examples/playground/app/recovery/page.tsx +22 -0
- package/examples/playground/app/recovery/scheduler/route.ts +7 -0
- package/examples/playground/app/recovery/server.ts +53 -0
- package/examples/playground/app/recovery/start/route.ts +41 -0
- package/examples/playground/app/vault/[vaultId]/route.ts +19 -0
- package/examples/playground/app/vault/page.tsx +12 -0
- package/examples/playground/app/vault/server.ts +9 -0
- package/examples/playground/app/vault/vault-client.tsx +124 -0
- package/examples/playground/app/vault/vault.test.ts +147 -0
- package/examples/playground/app/vault/vault.ts +119 -0
- package/examples/playground/css.d.ts +4 -0
- package/examples/playground/lib/store.ts +15 -0
- package/examples/playground/next-env.d.ts +5 -0
- package/examples/playground/next.config.ts +10 -0
- package/examples/playground/package.json +46 -0
- package/examples/playground/tsconfig.json +37 -0
- package/examples/playground/vercel.json +40 -0
- package/package.json +11 -2
- package/src/actor-client.ts +132 -0
- package/src/actor-react.ts +143 -0
- package/src/actor-shared.ts +356 -0
- package/src/actor.browser.ts +12 -0
- package/src/actor.ts +914 -0
- package/src/client.ts +9 -1
- package/src/errors.ts +15 -0
- package/src/index.ts +1 -1
- package/src/server.ts +13 -3
- package/dist/contract-jIfaR085.d.ts.map +0 -1
- package/dist/server-B2XNevQA.js.map +0 -1
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ContractEvent } from 'experimental-a2'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import { counter, counterReducer } from './model'
|
|
4
|
+
|
|
5
|
+
type CounterEvents = typeof counter.events
|
|
6
|
+
type CounterEvent<
|
|
7
|
+
K extends keyof CounterEvents & string = keyof CounterEvents & string,
|
|
8
|
+
> = ContractEvent<CounterEvents, K>
|
|
9
|
+
|
|
10
|
+
const event = <K extends keyof CounterEvents & string>(
|
|
11
|
+
index: number,
|
|
12
|
+
type: K,
|
|
13
|
+
): CounterEvent<K> =>
|
|
14
|
+
({
|
|
15
|
+
id: `event-${index}`,
|
|
16
|
+
type,
|
|
17
|
+
payload: {},
|
|
18
|
+
index,
|
|
19
|
+
sessionId: 'shared',
|
|
20
|
+
createdAt: new Date(index * 1_000),
|
|
21
|
+
}) as CounterEvent<K>
|
|
22
|
+
|
|
23
|
+
describe('counter reducer', () => {
|
|
24
|
+
it('folds increments and decrements in log order', () => {
|
|
25
|
+
const state = [
|
|
26
|
+
event(1, 'incremented'),
|
|
27
|
+
event(2, 'incremented'),
|
|
28
|
+
event(3, 'decremented'),
|
|
29
|
+
].reduce(
|
|
30
|
+
(current, nextEvent) => counterReducer.fold(current, nextEvent),
|
|
31
|
+
counterReducer.initialState,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
expect(state).toEqual({ value: 1 })
|
|
35
|
+
})
|
|
36
|
+
})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as a2 from 'experimental-a2'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
|
|
4
|
+
export const counter = a2.contract({
|
|
5
|
+
name: 'counter',
|
|
6
|
+
events: {
|
|
7
|
+
incremented: z.object({}),
|
|
8
|
+
decremented: z.object({}),
|
|
9
|
+
},
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
const counterState = z.object({ value: z.number().int() })
|
|
13
|
+
|
|
14
|
+
export type CounterState = z.output<typeof counterState>
|
|
15
|
+
|
|
16
|
+
export const counterReducer = counter
|
|
17
|
+
.reducer({
|
|
18
|
+
name: 'counter-value',
|
|
19
|
+
initialState: { value: 0 },
|
|
20
|
+
stateSchema: counterState,
|
|
21
|
+
})
|
|
22
|
+
.fold((state, event) => {
|
|
23
|
+
switch (event.type) {
|
|
24
|
+
case 'incremented':
|
|
25
|
+
return { value: state.value + 1 }
|
|
26
|
+
case 'decremented':
|
|
27
|
+
return { value: state.value - 1 }
|
|
28
|
+
default:
|
|
29
|
+
return state
|
|
30
|
+
}
|
|
31
|
+
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import { counterReducer } from './model'
|
|
3
|
+
import { counterServer } from './server'
|
|
4
|
+
import { CounterClient } from './counter-client'
|
|
5
|
+
import { SessionProvider } from './session'
|
|
6
|
+
|
|
7
|
+
export const dynamic = 'force-dynamic'
|
|
8
|
+
|
|
9
|
+
const COUNTER_SESSION_ID = 'shared'
|
|
10
|
+
|
|
11
|
+
export default async function CounterPage(): Promise<ReactNode> {
|
|
12
|
+
const session = counterServer.session(COUNTER_SESSION_ID)
|
|
13
|
+
const { state, index } = await session.state(counterReducer)
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<SessionProvider
|
|
17
|
+
sessionId={COUNTER_SESSION_ID}
|
|
18
|
+
initialState={state}
|
|
19
|
+
initialIndex={index}
|
|
20
|
+
>
|
|
21
|
+
<CounterClient />
|
|
22
|
+
</SessionProvider>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import { createClient } from 'experimental-a2/client'
|
|
3
|
+
import { createReact } from 'experimental-a2/react'
|
|
4
|
+
import { counterReducer } from './model'
|
|
5
|
+
|
|
6
|
+
export const counterClient = createClient({
|
|
7
|
+
reducer: counterReducer,
|
|
8
|
+
api: '/counter/events',
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
export const { SessionProvider, useSession } = createReact({
|
|
12
|
+
client: counterClient,
|
|
13
|
+
})
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
|
+
import { useEffect, useRef, useState } from 'react'
|
|
4
|
+
import { basicSetup } from 'codemirror'
|
|
5
|
+
import { Compartment, EditorState } from '@codemirror/state'
|
|
6
|
+
import { EditorView, placeholder } from '@codemirror/view'
|
|
7
|
+
import { yCollab } from 'y-codemirror.next'
|
|
8
|
+
import type { Awareness } from 'y-protocols/awareness'
|
|
9
|
+
import type * as Y from 'yjs'
|
|
10
|
+
|
|
11
|
+
const bodyPlaceholder =
|
|
12
|
+
'Start writing. Open this URL in another tab to collaborate…'
|
|
13
|
+
|
|
14
|
+
export function CodeMirrorEditor({
|
|
15
|
+
doc,
|
|
16
|
+
awareness,
|
|
17
|
+
disabled,
|
|
18
|
+
}: {
|
|
19
|
+
doc: Y.Doc
|
|
20
|
+
awareness: Awareness
|
|
21
|
+
disabled: boolean
|
|
22
|
+
}): ReactNode {
|
|
23
|
+
const host = useRef<HTMLDivElement>(null)
|
|
24
|
+
const view = useRef<EditorView | null>(null)
|
|
25
|
+
const [editable] = useState(() => new Compartment())
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (!host.current) return
|
|
29
|
+
|
|
30
|
+
const body = doc.getText('body')
|
|
31
|
+
const editor = new EditorView({
|
|
32
|
+
parent: host.current,
|
|
33
|
+
state: EditorState.create({
|
|
34
|
+
doc: body.toString(),
|
|
35
|
+
extensions: [
|
|
36
|
+
basicSetup,
|
|
37
|
+
EditorView.lineWrapping,
|
|
38
|
+
EditorView.contentAttributes.of({
|
|
39
|
+
'aria-label': 'Document body',
|
|
40
|
+
spellcheck: 'true',
|
|
41
|
+
}),
|
|
42
|
+
placeholder(bodyPlaceholder),
|
|
43
|
+
// Awareness rides the session's presence plane: cursors and
|
|
44
|
+
// selections replicate last-writer-wins and expire, while only
|
|
45
|
+
// document changes enter the durable log.
|
|
46
|
+
yCollab(body, awareness),
|
|
47
|
+
// Upstream keeps the caret when the editor blurs (its clear
|
|
48
|
+
// branch requires focus, so it never runs); an unfocused
|
|
49
|
+
// editor should show no phantom caret.
|
|
50
|
+
EditorView.updateListener.of((update) => {
|
|
51
|
+
if (update.focusChanged && !update.view.hasFocus) {
|
|
52
|
+
awareness.setLocalStateField('cursor', null)
|
|
53
|
+
}
|
|
54
|
+
}),
|
|
55
|
+
editable.of([
|
|
56
|
+
EditorState.readOnly.of(disabled),
|
|
57
|
+
EditorView.editable.of(!disabled),
|
|
58
|
+
]),
|
|
59
|
+
],
|
|
60
|
+
}),
|
|
61
|
+
})
|
|
62
|
+
view.current = editor
|
|
63
|
+
|
|
64
|
+
return () => {
|
|
65
|
+
editor.destroy()
|
|
66
|
+
view.current = null
|
|
67
|
+
}
|
|
68
|
+
}, [doc, awareness, editable])
|
|
69
|
+
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
view.current?.dispatch({
|
|
72
|
+
effects: editable.reconfigure([
|
|
73
|
+
EditorState.readOnly.of(disabled),
|
|
74
|
+
EditorView.editable.of(!disabled),
|
|
75
|
+
]),
|
|
76
|
+
})
|
|
77
|
+
}, [disabled, editable])
|
|
78
|
+
|
|
79
|
+
return <div className="document-body" ref={host} />
|
|
80
|
+
}
|