experimental-a2 0.9.0 → 0.11.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 +53 -0
- package/dist/actor-D_54lz_1.d.ts +310 -0
- package/dist/actor-D_54lz_1.d.ts.map +1 -0
- package/dist/actor-client.d.ts +13 -4
- package/dist/actor-client.d.ts.map +1 -1
- package/dist/actor-client.js +63 -7
- package/dist/actor-client.js.map +1 -1
- package/dist/actor-react.d.ts +5 -4
- package/dist/actor-react.d.ts.map +1 -1
- package/dist/actor-react.js +16 -2
- package/dist/actor-react.js.map +1 -1
- package/dist/actor.d.ts +2 -176
- package/dist/actor.js +13 -2
- package/dist/actor.js.map +1 -1
- package/dist/ai-server.d.ts +1 -1
- package/dist/ai-server.js +2 -2
- package/dist/ai.d.ts +1 -1
- package/dist/ai.js +1 -1
- package/dist/client-Bf6uSEAk.js +1342 -0
- package/dist/client-Bf6uSEAk.js.map +1 -0
- package/dist/client-P_NNNRM-.d.ts +243 -0
- package/dist/client-P_NNNRM-.d.ts.map +1 -0
- package/dist/client.d.ts +2 -202
- package/dist/client.js +2 -1026
- package/dist/errors-DCk6ch5n.js.map +1 -1
- package/dist/errors-DvhSXnxk.d.ts +28 -0
- package/dist/errors-DvhSXnxk.d.ts.map +1 -0
- package/dist/index.d.ts +3 -35
- package/dist/{internal-DRXJ56EI.js → internal-Dq2qYxou.js} +2 -2
- package/dist/{internal-DRXJ56EI.js.map → internal-Dq2qYxou.js.map} +1 -1
- package/dist/platform-B4TnJtWu.js +34 -0
- package/dist/platform-B4TnJtWu.js.map +1 -0
- package/dist/react.d.ts +3 -1
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +2 -1
- package/dist/react.js.map +1 -1
- package/dist/scheduler-qstash.d.ts +2 -2
- package/dist/scheduler-qstash.js +3 -2
- package/dist/scheduler-qstash.js.map +1 -1
- package/dist/scheduler-vercel.d.ts +2 -2
- package/dist/scheduler-vercel.js +2 -2
- package/dist/{server-CBET-jSz.js → server-Dkz2a84E.js} +295 -133
- package/dist/server-Dkz2a84E.js.map +1 -0
- package/dist/{server-CKY3_lbw.d.ts → server-DwPrMqHB.d.ts} +4 -2
- package/dist/server-DwPrMqHB.d.ts.map +1 -0
- package/dist/server.d.ts +2 -2
- package/dist/server.js +1 -1
- package/dist/{store-DGHeBtIQ.d.ts → store-DtDOWLSn.d.ts} +4 -5
- package/dist/{store-DGHeBtIQ.d.ts.map → store-DtDOWLSn.d.ts.map} +1 -1
- package/dist/store-N8PXxDAS.js.map +1 -1
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-memory.js +1 -1
- package/dist/{store-polling-6DW7F1DT.js → store-polling-CmxUbV93.js} +56 -7
- package/dist/store-polling-CmxUbV93.js.map +1 -0
- package/dist/store-postgres.d.ts +6 -4
- package/dist/store-postgres.d.ts.map +1 -1
- package/dist/store-postgres.js +701 -53
- package/dist/store-postgres.js.map +1 -1
- package/dist/store-presence-polling-C7-XZyW9.js +94 -0
- package/dist/store-presence-polling-C7-XZyW9.js.map +1 -0
- package/dist/store-redis-http.d.ts +2 -2
- package/dist/store-redis-http.d.ts.map +1 -1
- package/dist/store-redis-http.js +184 -38
- package/dist/store-redis-http.js.map +1 -1
- package/dist/{store-redis-core-z-ykbyMg.js → store-redis-notify-BUCyXOn0.js} +491 -27
- package/dist/store-redis-notify-BUCyXOn0.js.map +1 -0
- package/dist/store-redis.d.ts +5 -13
- package/dist/store-redis.d.ts.map +1 -1
- package/dist/store-redis.js +27 -271
- package/dist/store-redis.js.map +1 -1
- package/dist/store-sqlite.d.ts +1 -1
- package/dist/store-sqlite.js +1 -1
- package/dist/{wire--yji6mO3.js → wire-BO5wWCb1.js} +18 -16
- package/dist/wire-BO5wWCb1.js.map +1 -0
- package/docs/actors/04-routes.mdx +40 -8
- package/docs/guides/03-react.mdx +52 -10
- package/docs/guides/05-production.mdx +54 -5
- package/docs/guides/09-presence.mdx +12 -7
- package/docs/guides/10-transports.mdx +85 -22
- package/docs/reference/01-api.mdx +90 -28
- package/docs/reference/02-errors.mdx +45 -11
- package/examples/playground/package.json +1 -1
- package/package.json +1 -1
- package/src/actor-client.ts +114 -14
- package/src/actor-react.ts +18 -3
- package/src/actor.ts +16 -1
- package/src/client-errors.ts +51 -0
- package/src/client.ts +577 -175
- package/src/errors.ts +4 -9
- package/src/index.ts +1 -1
- package/src/internal.ts +1 -1
- package/src/postgres-notification-scope.ts +134 -0
- package/src/postgres-notifications.ts +244 -0
- package/src/postgres-pool.ts +65 -0
- package/src/postgres-resources.ts +185 -0
- package/src/presence-recovery.ts +120 -0
- package/src/react.ts +3 -0
- package/src/redis-http-subscriptions.ts +199 -0
- package/src/server-fetch.ts +51 -23
- package/src/server.ts +146 -19
- package/src/session-socket.ts +241 -88
- package/src/sse.ts +17 -0
- package/src/store-polling.ts +32 -12
- package/src/store-postgres.ts +292 -75
- package/src/store-presence-polling.ts +125 -0
- package/src/store-redis-core.ts +37 -30
- package/src/store-redis-http.ts +51 -45
- package/src/store-redis-notify.ts +464 -0
- package/src/store-redis.ts +9 -364
- package/src/store.ts +3 -4
- package/src/stream-activity.ts +32 -0
- package/src/wire.ts +39 -15
- package/dist/actor-shared-BACubf4x.d.ts +0 -136
- package/dist/actor-shared-BACubf4x.d.ts.map +0 -1
- package/dist/actor.d.ts.map +0 -1
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/server-CBET-jSz.js.map +0 -1
- package/dist/server-CKY3_lbw.d.ts.map +0 -1
- package/dist/store-polling-6DW7F1DT.js.map +0 -1
- package/dist/store-redis-core-z-ykbyMg.js.map +0 -1
- package/dist/wire--yji6mO3.js.map +0 -1
package/src/store-redis-core.ts
CHANGED
|
@@ -44,23 +44,15 @@ export type RedisStoreCoreOptions = {
|
|
|
44
44
|
clock: Clock
|
|
45
45
|
ids: IdSource
|
|
46
46
|
keyPrefix: string
|
|
47
|
-
|
|
48
|
-
* Disposable wake-up fired after a write appends new events to a
|
|
49
|
-
* watched session — `count` is the session's new event count. The
|
|
50
|
-
* watched check runs inside the write script, so unwatched sessions
|
|
51
|
-
* cost no wake-up command at all. Losing one is harmless: live
|
|
52
|
-
* feeds re-read on a safety cadence, so notifications only affect
|
|
53
|
-
* latency, never delivery.
|
|
54
|
-
*/
|
|
55
|
-
notify?: (sessionId: string, count: number) => void
|
|
47
|
+
presenceSource: string
|
|
56
48
|
}
|
|
57
49
|
|
|
58
50
|
export type RedisStoreCore = Omit<A2Store, 'stream' | 'presence'> & {
|
|
59
51
|
/**
|
|
60
52
|
* The storage half of the presence capability, shared by both
|
|
61
|
-
* transports. `set` returns the applied patch
|
|
62
|
-
* field lost the LWW race
|
|
63
|
-
*
|
|
53
|
+
* transports. `set` returns the applied patch for local loopback,
|
|
54
|
+
* or null when every field lost the LWW race. The script publishes
|
|
55
|
+
* the applied fields for remote subscribers.
|
|
64
56
|
*/
|
|
65
57
|
presence: {
|
|
66
58
|
set(
|
|
@@ -77,7 +69,7 @@ export type RedisStoreCore = Omit<A2Store, 'stream' | 'presence'> & {
|
|
|
77
69
|
afterIndex: number,
|
|
78
70
|
throughIndex?: number,
|
|
79
71
|
): Promise<StoredEvent[]>
|
|
80
|
-
/** XRANGE
|
|
72
|
+
/** One bounded XRANGE page of public events for live feeds. */
|
|
81
73
|
readEvents(sessionId: string, afterIndex: number): Promise<Event[]>
|
|
82
74
|
/**
|
|
83
75
|
* Declare the session watched for `ttlMs` — writes publish wake-ups
|
|
@@ -96,8 +88,8 @@ const READ_PAGE_SIZE = 256
|
|
|
96
88
|
* KEYS: store stream, counter, global id hash, metadata, pending, ready,
|
|
97
89
|
* watched.
|
|
98
90
|
* ARGV: sessionId, nowMs, n, then per event:
|
|
99
|
-
* id, type, payloadJson, causeJson, lane, settled.
|
|
100
|
-
* Returns ['empty', hasPending] | ['ok', firstIndex, hasPending
|
|
91
|
+
* id, type, payloadJson, causeJson, lane, settled; finally notify channel.
|
|
92
|
+
* Returns ['empty', hasPending] | ['ok', firstIndex, hasPending] |
|
|
101
93
|
* ['dup', hasPending, ...indexes] | ['partial', count] |
|
|
102
94
|
* ['foreign', eventId] | ['no_parent', idx] | ['superseded', idx].
|
|
103
95
|
*/
|
|
@@ -178,8 +170,10 @@ for i = 1, n do
|
|
|
178
170
|
end
|
|
179
171
|
redis.call('SET', KEYS[2], tostring(base + n))
|
|
180
172
|
has_pending = redis.call('ZCARD', KEYS[5]) > 0 and '1' or '0'
|
|
181
|
-
|
|
182
|
-
|
|
173
|
+
if redis.call('EXISTS', KEYS[7]) == 1 then
|
|
174
|
+
redis.pcall('PUBLISH', ARGV[4 + n * 6], tostring(base + n))
|
|
175
|
+
end
|
|
176
|
+
return { 'ok', tostring(base + 1), has_pending }
|
|
183
177
|
`
|
|
184
178
|
|
|
185
179
|
/**
|
|
@@ -350,7 +344,7 @@ return result
|
|
|
350
344
|
* KEYS: store, counter, ids, meta, pending, ready, watched,
|
|
351
345
|
* event snapshot pins, snapshot pin counts.
|
|
352
346
|
* ARGV: sessionId, nowMs, parent index, attempt, n, then per child:
|
|
353
|
-
* id, type, payloadJson, lane, settled.
|
|
347
|
+
* id, type, payloadJson, lane, settled; finally notify channel.
|
|
354
348
|
*/
|
|
355
349
|
const COMPLETE_ATTEMPT_LUA = `
|
|
356
350
|
local function release_snapshot_pins()
|
|
@@ -485,8 +479,10 @@ for i = 1, n do
|
|
|
485
479
|
end
|
|
486
480
|
end
|
|
487
481
|
redis.call('SET', KEYS[2], tostring(total + n))
|
|
488
|
-
|
|
489
|
-
|
|
482
|
+
if n > 0 and redis.call('EXISTS', KEYS[7]) == 1 then
|
|
483
|
+
redis.pcall('PUBLISH', ARGV[6 + n * 5], tostring(total + n))
|
|
484
|
+
end
|
|
485
|
+
return { 'ok', tostring(total + 1) }
|
|
490
486
|
`
|
|
491
487
|
|
|
492
488
|
/**
|
|
@@ -549,7 +545,9 @@ return 1
|
|
|
549
545
|
|
|
550
546
|
/**
|
|
551
547
|
* KEYS: presence hash. ARGV: atMs, ttlMs, n, then per entry:
|
|
552
|
-
* hashField, valueJson ('' deletes)
|
|
548
|
+
* hashField, valueJson ('' deletes), patchFieldJson; finally channel and
|
|
549
|
+
* message prefix. Wire fragments stay JSON text to preserve arrays
|
|
550
|
+
* and numeric precision through Lua. Field-wise LWW by the sender's
|
|
553
551
|
* `at`: a stored entry with a strictly newer stamp survives; ties go
|
|
554
552
|
* to the incoming write (memory-tier parity). Returns the applied
|
|
555
553
|
* 1-based entry positions. The whole-key PEXPIRE is the coarse bound,
|
|
@@ -561,9 +559,11 @@ return 1
|
|
|
561
559
|
const PRESENCE_SET_LUA = `
|
|
562
560
|
local at = tonumber(ARGV[1])
|
|
563
561
|
local applied = {}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
562
|
+
local published = {}
|
|
563
|
+
local n = tonumber(ARGV[3])
|
|
564
|
+
for i = 1, n do
|
|
565
|
+
local field = ARGV[3 + (i - 1) * 3 + 1]
|
|
566
|
+
local value = ARGV[3 + (i - 1) * 3 + 2]
|
|
567
567
|
local existing = redis.call('HGET', KEYS[1], field)
|
|
568
568
|
if not existing or tonumber(cjson.decode(existing)['a']) <= at then
|
|
569
569
|
if value == '' then
|
|
@@ -572,9 +572,13 @@ for i = 1, tonumber(ARGV[3]) do
|
|
|
572
572
|
redis.call('HSET', KEYS[1], field, value)
|
|
573
573
|
end
|
|
574
574
|
applied[#applied + 1] = tostring(i)
|
|
575
|
+
published[#published + 1] = ARGV[3 + (i - 1) * 3 + 3]
|
|
575
576
|
end
|
|
576
577
|
end
|
|
577
|
-
if #applied > 0 then
|
|
578
|
+
if #applied > 0 then
|
|
579
|
+
redis.call('PEXPIRE', KEYS[1], ARGV[2])
|
|
580
|
+
redis.pcall('PUBLISH', ARGV[4 + n * 3], ARGV[5 + n * 3] .. table.concat(published, ',') .. '}}')
|
|
581
|
+
end
|
|
578
582
|
return applied
|
|
579
583
|
`
|
|
580
584
|
|
|
@@ -766,7 +770,7 @@ const wrap = async <T>(fn: () => Promise<T>): Promise<T> => {
|
|
|
766
770
|
export function createRedisStoreCore(
|
|
767
771
|
options: RedisStoreCoreOptions,
|
|
768
772
|
): RedisStoreCore {
|
|
769
|
-
const { call, clock
|
|
773
|
+
const { call, clock } = options
|
|
770
774
|
const generateId = options.ids
|
|
771
775
|
const prefix = options.keyPrefix
|
|
772
776
|
|
|
@@ -1004,6 +1008,7 @@ export function createRedisStoreCore(
|
|
|
1004
1008
|
e.lane ?? '',
|
|
1005
1009
|
e.settled ? '1' : '0',
|
|
1006
1010
|
]),
|
|
1011
|
+
`${prefix}:${sessionId}:notify`,
|
|
1007
1012
|
]
|
|
1008
1013
|
const reply = (await evalScript(
|
|
1009
1014
|
APPEND_LUA,
|
|
@@ -1060,7 +1065,6 @@ export function createRedisStoreCore(
|
|
|
1060
1065
|
}
|
|
1061
1066
|
}
|
|
1062
1067
|
const base = Number(reply[1]) - 1
|
|
1063
|
-
if (reply[3] === '1') notify?.(sessionId, base + withIds.length)
|
|
1064
1068
|
return {
|
|
1065
1069
|
events: withIds.map((e, i) => ({
|
|
1066
1070
|
id: e.id,
|
|
@@ -1187,6 +1191,7 @@ export function createRedisStoreCore(
|
|
|
1187
1191
|
event.lane ?? '',
|
|
1188
1192
|
event.settled === true ? '1' : '0',
|
|
1189
1193
|
]),
|
|
1194
|
+
`${prefix}:${sessionId}:notify`,
|
|
1190
1195
|
],
|
|
1191
1196
|
)) as string[]
|
|
1192
1197
|
const outcome = reply[0]
|
|
@@ -1206,9 +1211,6 @@ export function createRedisStoreCore(
|
|
|
1206
1211
|
const cause = { index, attempt }
|
|
1207
1212
|
if (outcome === 'ok') {
|
|
1208
1213
|
const base = Number(reply[1]) - 1
|
|
1209
|
-
if (events.length > 0 && reply[2] === '1') {
|
|
1210
|
-
notify?.(sessionId, base + events.length)
|
|
1211
|
-
}
|
|
1212
1214
|
return {
|
|
1213
1215
|
outcome: 'completed',
|
|
1214
1216
|
events: events.map((event, i) => ({
|
|
@@ -1428,7 +1430,10 @@ export function createRedisStoreCore(
|
|
|
1428
1430
|
a: atMs,
|
|
1429
1431
|
e: expiresAtMs,
|
|
1430
1432
|
}),
|
|
1433
|
+
`${json(field)}:${json(value)}`,
|
|
1431
1434
|
]),
|
|
1435
|
+
presenceKey(ns),
|
|
1436
|
+
`${json({ src: options.presenceSource, participant, seen: meta.seen, at: atMs }).slice(0, -1)},"values":{`,
|
|
1432
1437
|
],
|
|
1433
1438
|
)) as string[]
|
|
1434
1439
|
if (applied.length === 0) return null
|
|
@@ -1492,6 +1497,8 @@ export function createRedisStoreCore(
|
|
|
1492
1497
|
storeKey(sessionId),
|
|
1493
1498
|
`${afterIndex + 1}-0`,
|
|
1494
1499
|
'+',
|
|
1500
|
+
'COUNT',
|
|
1501
|
+
READ_PAGE_SIZE,
|
|
1495
1502
|
)) as StreamEntry[]
|
|
1496
1503
|
return entries.map((entry) => toEvent(sessionId, entry))
|
|
1497
1504
|
})
|
package/src/store-redis-http.ts
CHANGED
|
@@ -1,21 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* experimental-a2/store-redis-http — the same Redis Streams store over a
|
|
3
|
-
* provider REST API (Upstash-shaped: POST one command as a JSON array,
|
|
4
|
-
* receive `{ result }` or `{ error }`). The storage semantics live in
|
|
5
|
-
* store-redis-core.ts, shared with experimental-a2/store-redis.
|
|
6
|
-
*
|
|
7
|
-
* Fully connectionless: every command is one `fetch`, so it works
|
|
8
|
-
* where a Redis-protocol connection cannot exist or cannot be spared.
|
|
9
|
-
* Without a connection there is no push and no pub/sub, so `stream()`
|
|
10
|
-
* is the shared activity-adaptive poll loop (25ms while events flow,
|
|
11
|
-
* backing off to a 250ms idle ceiling), and presence is the degraded
|
|
12
|
-
* tier: `set`/`read` from the shared core, no `subscribe`. No peer
|
|
13
|
-
* dependencies.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
1
|
import { A2Error } from './errors.ts'
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
2
|
+
import { NOTIFY_TIMINGS } from './internal.ts'
|
|
3
|
+
import { redisHttpSubscriptions } from './redis-http-subscriptions.ts'
|
|
4
|
+
import {
|
|
5
|
+
createRedisNotifyStore,
|
|
6
|
+
type RedisConnection,
|
|
7
|
+
} from './store-redis-notify.ts'
|
|
19
8
|
import {
|
|
20
9
|
RANDOM_IDS,
|
|
21
10
|
SYSTEM_CLOCK,
|
|
@@ -38,7 +27,7 @@ export type RedisHttpStoreOptions = {
|
|
|
38
27
|
}
|
|
39
28
|
|
|
40
29
|
export type RedisHttpStore = A2Store & {
|
|
41
|
-
/**
|
|
30
|
+
/** Close live subscriptions and pending reconnects. */
|
|
42
31
|
close(): Promise<void>
|
|
43
32
|
}
|
|
44
33
|
|
|
@@ -47,6 +36,8 @@ export function redisHttp(options: RedisHttpStoreOptions): RedisHttpStore {
|
|
|
47
36
|
throw new TypeError('redisHttp() needs a url and a token')
|
|
48
37
|
}
|
|
49
38
|
const { url, token } = options
|
|
39
|
+
let polling = false
|
|
40
|
+
let fallbackCheck: Promise<void> | undefined
|
|
50
41
|
|
|
51
42
|
const call = async (
|
|
52
43
|
command: string,
|
|
@@ -61,6 +52,9 @@ export function redisHttp(options: RedisHttpStoreOptions): RedisHttpStore {
|
|
|
61
52
|
'content-type': 'application/json',
|
|
62
53
|
},
|
|
63
54
|
body: JSON.stringify([command, ...args.map(String)]),
|
|
55
|
+
...(command === 'PING'
|
|
56
|
+
? { signal: AbortSignal.timeout(NOTIFY_TIMINGS.safetyReadMs) }
|
|
57
|
+
: {}),
|
|
64
58
|
})
|
|
65
59
|
} catch (err) {
|
|
66
60
|
throw new A2Error('STORE_UNAVAILABLE', 'redis rest request failed', {
|
|
@@ -80,36 +74,48 @@ export function redisHttp(options: RedisHttpStoreOptions): RedisHttpStore {
|
|
|
80
74
|
return body.result
|
|
81
75
|
}
|
|
82
76
|
|
|
83
|
-
|
|
77
|
+
let subscriber: RedisConnection | undefined
|
|
78
|
+
let onReconnect: ((channel: string) => void) | undefined
|
|
79
|
+
const client: RedisConnection = {
|
|
84
80
|
call,
|
|
81
|
+
duplicate: () =>
|
|
82
|
+
(subscriber ??= redisHttpSubscriptions({
|
|
83
|
+
url,
|
|
84
|
+
token,
|
|
85
|
+
onReconnect: (channel) => onReconnect?.(channel),
|
|
86
|
+
fallback: {
|
|
87
|
+
enabled: () => polling,
|
|
88
|
+
enable() {
|
|
89
|
+
fallbackCheck ??= call('PING')
|
|
90
|
+
.then((result) => {
|
|
91
|
+
if (result !== 'PONG')
|
|
92
|
+
throw new A2Error(
|
|
93
|
+
'STORE_UNAVAILABLE',
|
|
94
|
+
'redis rest capability check failed',
|
|
95
|
+
)
|
|
96
|
+
polling = true
|
|
97
|
+
return undefined
|
|
98
|
+
})
|
|
99
|
+
.catch((error: unknown) => {
|
|
100
|
+
fallbackCheck = undefined
|
|
101
|
+
throw error
|
|
102
|
+
})
|
|
103
|
+
return fallbackCheck
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
})),
|
|
107
|
+
on() {},
|
|
108
|
+
disconnect: () => subscriber?.disconnect(),
|
|
109
|
+
}
|
|
110
|
+
return createRedisNotifyStore({
|
|
111
|
+
client: () => Promise.resolve(client),
|
|
112
|
+
closeClient: async () => client.disconnect(),
|
|
113
|
+
polling: () => polling,
|
|
114
|
+
onReconnect: (listener) => {
|
|
115
|
+
onReconnect = listener
|
|
116
|
+
},
|
|
85
117
|
clock: options.clock ?? SYSTEM_CLOCK,
|
|
86
118
|
ids: options.ids ?? RANDOM_IDS,
|
|
87
119
|
keyPrefix: options.keyPrefix ?? 'a2',
|
|
88
120
|
})
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
append: core.append,
|
|
92
|
-
read: core.read,
|
|
93
|
-
claimAvailable: core.claimAvailable,
|
|
94
|
-
renewClaims: core.renewClaims,
|
|
95
|
-
completeAttempt: core.completeAttempt,
|
|
96
|
-
failAttempt: core.failAttempt,
|
|
97
|
-
readState: core.readState,
|
|
98
|
-
putSnapshots: core.putSnapshots,
|
|
99
|
-
presence: {
|
|
100
|
-
async set(ns, participant, values, meta) {
|
|
101
|
-
await core.presence.set(ns, participant, values, meta)
|
|
102
|
-
},
|
|
103
|
-
read: core.presence.read,
|
|
104
|
-
},
|
|
105
|
-
|
|
106
|
-
stream(sessionId, opts) {
|
|
107
|
-
return pollingStream(
|
|
108
|
-
(afterIndex) => core.readRange(sessionId, afterIndex),
|
|
109
|
-
opts?.startAfter !== undefined ? { startAfter: opts.startAfter } : {},
|
|
110
|
-
)
|
|
111
|
-
},
|
|
112
|
-
|
|
113
|
-
async close() {},
|
|
114
|
-
}
|
|
115
121
|
}
|