@ably/ai-transport 0.0.1 → 0.1.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/README.md +54 -47
- package/dist/ably-ai-transport.js +1006 -539
- package/dist/ably-ai-transport.js.map +1 -1
- package/dist/ably-ai-transport.umd.cjs +1 -1
- package/dist/ably-ai-transport.umd.cjs.map +1 -1
- package/dist/constants.d.ts +4 -0
- package/dist/core/codec/types.d.ts +19 -2
- package/dist/core/transport/decode-history.d.ts +8 -6
- package/dist/core/transport/headers.d.ts +4 -2
- package/dist/core/transport/index.d.ts +4 -1
- package/dist/core/transport/pipe-stream.d.ts +3 -2
- package/dist/core/transport/stream-router.d.ts +11 -1
- package/dist/core/transport/tree.d.ts +171 -0
- package/dist/core/transport/turn-manager.d.ts +4 -1
- package/dist/core/transport/types.d.ts +270 -119
- package/dist/core/transport/view.d.ts +166 -0
- package/dist/errors.d.ts +19 -2
- package/dist/index.d.ts +3 -1
- package/dist/react/ably-ai-transport-react.js +1019 -486
- package/dist/react/ably-ai-transport-react.js.map +1 -1
- package/dist/react/ably-ai-transport-react.umd.cjs +1 -1
- package/dist/react/ably-ai-transport-react.umd.cjs.map +1 -1
- package/dist/react/contexts/transport-context.d.ts +31 -0
- package/dist/react/contexts/transport-provider.d.ts +49 -0
- package/dist/react/create-transport-hooks.d.ts +124 -0
- package/dist/react/index.d.ts +14 -8
- package/dist/react/use-ably-messages.d.ts +14 -8
- package/dist/react/use-active-turns.d.ts +7 -3
- package/dist/react/use-client-transport.d.ts +78 -5
- package/dist/react/use-create-view.d.ts +22 -0
- package/dist/react/use-tree.d.ts +20 -0
- package/dist/react/use-view.d.ts +79 -0
- package/dist/vercel/ably-ai-transport-vercel.js +1478 -842
- package/dist/vercel/ably-ai-transport-vercel.js.map +1 -1
- package/dist/vercel/ably-ai-transport-vercel.umd.cjs +1 -1
- package/dist/vercel/ably-ai-transport-vercel.umd.cjs.map +1 -1
- package/dist/vercel/codec/tool-transitions.d.ts +50 -0
- package/dist/vercel/index.d.ts +3 -0
- package/dist/vercel/react/ably-ai-transport-vercel-react.js +9099 -852
- package/dist/vercel/react/ably-ai-transport-vercel-react.js.map +1 -1
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs +45 -1
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs.map +1 -1
- package/dist/vercel/react/contexts/chat-transport-context.d.ts +32 -0
- package/dist/vercel/react/contexts/chat-transport-provider.d.ts +84 -0
- package/dist/vercel/react/index.d.ts +5 -0
- package/dist/vercel/react/use-chat-transport.d.ts +61 -20
- package/dist/vercel/react/use-message-sync.d.ts +41 -9
- package/dist/vercel/react/use-staged-add-tool-approval-response.d.ts +30 -0
- package/dist/vercel/tool-approvals.d.ts +124 -0
- package/dist/vercel/tool-events.d.ts +26 -0
- package/dist/vercel/transport/chat-transport.d.ts +33 -11
- package/dist/vercel/transport/index.d.ts +5 -2
- package/package.json +23 -17
- package/src/constants.ts +6 -0
- package/src/core/codec/encoder.ts +10 -1
- package/src/core/codec/types.ts +19 -3
- package/src/core/transport/client-transport.ts +382 -364
- package/src/core/transport/decode-history.ts +229 -81
- package/src/core/transport/headers.ts +6 -2
- package/src/core/transport/index.ts +13 -5
- package/src/core/transport/pipe-stream.ts +8 -5
- package/src/core/transport/server-transport.ts +212 -58
- package/src/core/transport/stream-router.ts +21 -3
- package/src/core/transport/{conversation-tree.ts → tree.ts} +192 -77
- package/src/core/transport/turn-manager.ts +28 -10
- package/src/core/transport/types.ts +318 -139
- package/src/core/transport/view.ts +840 -0
- package/src/errors.ts +21 -1
- package/src/index.ts +10 -5
- package/src/react/contexts/transport-context.ts +37 -0
- package/src/react/contexts/transport-provider.tsx +164 -0
- package/src/react/create-transport-hooks.ts +144 -0
- package/src/react/index.ts +15 -8
- package/src/react/use-ably-messages.ts +34 -16
- package/src/react/use-active-turns.ts +28 -17
- package/src/react/use-client-transport.ts +184 -24
- package/src/react/use-create-view.ts +68 -0
- package/src/react/use-tree.ts +53 -0
- package/src/react/use-view.ts +233 -0
- package/src/react/vite.config.ts +4 -1
- package/src/vercel/codec/accumulator.ts +64 -79
- package/src/vercel/codec/decoder.ts +11 -8
- package/src/vercel/codec/encoder.ts +68 -54
- package/src/vercel/codec/index.ts +0 -2
- package/src/vercel/codec/tool-transitions.ts +122 -0
- package/src/vercel/index.ts +17 -0
- package/src/vercel/react/contexts/chat-transport-context.ts +40 -0
- package/src/vercel/react/contexts/chat-transport-provider.tsx +122 -0
- package/src/vercel/react/index.ts +14 -0
- package/src/vercel/react/use-chat-transport.ts +164 -42
- package/src/vercel/react/use-message-sync.ts +77 -19
- package/src/vercel/react/use-staged-add-tool-approval-response.ts +87 -0
- package/src/vercel/react/vite.config.ts +4 -2
- package/src/vercel/tool-approvals.ts +380 -0
- package/src/vercel/tool-events.ts +53 -0
- package/src/vercel/transport/chat-transport.ts +225 -79
- package/src/vercel/transport/index.ts +14 -3
- package/dist/core/transport/conversation-tree.d.ts +0 -9
- package/dist/react/use-conversation-tree.d.ts +0 -20
- package/dist/react/use-edit.d.ts +0 -7
- package/dist/react/use-history.d.ts +0 -19
- package/dist/react/use-messages.d.ts +0 -7
- package/dist/react/use-regenerate.d.ts +0 -7
- package/dist/react/use-send.d.ts +0 -7
- package/src/react/use-conversation-tree.ts +0 -71
- package/src/react/use-edit.ts +0 -24
- package/src/react/use-history.ts +0 -111
- package/src/react/use-messages.ts +0 -32
- package/src/react/use-regenerate.ts +0 -24
- package/src/react/use-send.ts +0 -25
package/dist/constants.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export declare const HEADER_STREAM = "x-ably-stream";
|
|
|
14
14
|
export declare const HEADER_STATUS = "x-ably-status";
|
|
15
15
|
/** Header: stream identity. Set by the encoder on every streamed message; read by the decoder to correlate streams. */
|
|
16
16
|
export declare const HEADER_STREAM_ID = "x-ably-stream-id";
|
|
17
|
+
/** Header: marks a message as a discrete message part (from writeMessages). Set by publishDiscreteBatch; not set on lifecycle events from publishDiscrete. */
|
|
18
|
+
export declare const HEADER_DISCRETE = "x-ably-discrete";
|
|
17
19
|
/** Header: turn correlation ID. Set on every message in a turn. */
|
|
18
20
|
export declare const HEADER_TURN_ID = "x-ably-turn-id";
|
|
19
21
|
/** Header: message identity. Assigned per message (user or assistant). Used for optimistic reconciliation on the client. */
|
|
@@ -22,6 +24,8 @@ export declare const HEADER_MSG_ID = "x-ably-msg-id";
|
|
|
22
24
|
export declare const HEADER_TURN_CLIENT_ID = "x-ably-turn-client-id";
|
|
23
25
|
/** Header: message role (e.g. "user", "assistant"). */
|
|
24
26
|
export declare const HEADER_ROLE = "x-ably-role";
|
|
27
|
+
/** Header: the msg-id of the existing message this Ably message amends. Present on cross-turn amendment events. */
|
|
28
|
+
export declare const HEADER_AMEND = "x-ably-amend";
|
|
25
29
|
/** Header: cancel a specific turn by ID. */
|
|
26
30
|
export declare const HEADER_CANCEL_TURN_ID = "x-ably-cancel-turn-id";
|
|
27
31
|
/** Header: cancel all turns belonging to the sender's clientId. */
|
|
@@ -150,6 +150,19 @@ export interface MessageAccumulator<TEvent, TMessage> {
|
|
|
150
150
|
processOutputs(outputs: DecoderOutput<TEvent, TMessage>[]): void;
|
|
151
151
|
/** Apply an external update to a message (e.g. from an update callback). */
|
|
152
152
|
updateMessage(message: TMessage): void;
|
|
153
|
+
/**
|
|
154
|
+
* Ensure the accumulator is ready to process events for the given message.
|
|
155
|
+
* If not already active, creates internal tracking state from the message.
|
|
156
|
+
* If already active, syncs internal state with the provided message
|
|
157
|
+
* (picking up external changes like cross-turn amendments).
|
|
158
|
+
* Idempotent — safe to call before every processOutputs.
|
|
159
|
+
*/
|
|
160
|
+
initMessage(messageId: string, message: TMessage): void;
|
|
161
|
+
/**
|
|
162
|
+
* Mark a message as completed. Removes it from active tracking so it
|
|
163
|
+
* appears in {@link completedMessages}. No-op if not active.
|
|
164
|
+
*/
|
|
165
|
+
completeMessage(messageId: string): void;
|
|
153
166
|
/** All messages accumulated so far (in-progress and completed). */
|
|
154
167
|
readonly messages: TMessage[];
|
|
155
168
|
/** Only messages whose streams have finished. */
|
|
@@ -165,6 +178,12 @@ export interface EncoderOptions {
|
|
|
165
178
|
extras?: Extras;
|
|
166
179
|
/** Hook called before each Ably message is published. Mutate the message in place to add transport-level headers. */
|
|
167
180
|
onMessage?: (message: Ably.Message) => void;
|
|
181
|
+
/**
|
|
182
|
+
* Domain-level message identity. Domain encoders use this as a fallback
|
|
183
|
+
* messageId when a lifecycle chunk (e.g. `start`) does not provide one,
|
|
184
|
+
* ensuring useChat and the transport accumulator assign the same ID.
|
|
185
|
+
*/
|
|
186
|
+
messageId?: string;
|
|
168
187
|
}
|
|
169
188
|
/**
|
|
170
189
|
* The complete codec contract that a core transport needs.
|
|
@@ -183,6 +202,4 @@ export interface Codec<TEvent, TMessage> {
|
|
|
183
202
|
createAccumulator(): MessageAccumulator<TEvent, TMessage>;
|
|
184
203
|
/** Whether an event signals stream completion (finish, error, abort). */
|
|
185
204
|
isTerminal(event: TEvent): boolean;
|
|
186
|
-
/** Return a stable key for a message (used by MessageStore for upsert/delete). */
|
|
187
|
-
getMessageKey(message: TMessage): string;
|
|
188
205
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Logger } from '../../logger.js';
|
|
2
2
|
import { Codec } from '../codec/types.js';
|
|
3
|
-
import {
|
|
3
|
+
import { HistoryPage, LoadHistoryOptions } from './types.js';
|
|
4
4
|
/**
|
|
5
5
|
* decodeHistory — load conversation history from an Ably channel and
|
|
6
|
-
* return decoded messages as a
|
|
6
|
+
* return decoded messages as a paginated HistoryPage result.
|
|
7
7
|
*
|
|
8
8
|
* Uses a fresh decoder (not shared with the live subscription) to avoid
|
|
9
9
|
* state conflicts. Per-turn accumulators handle interleaved turns correctly.
|
|
@@ -19,8 +19,10 @@ import { LoadHistoryOptions, PaginatedMessages } from './types.js';
|
|
|
19
19
|
*
|
|
20
20
|
* Because Ably history returns newest-first while the decoder requires
|
|
21
21
|
* chronological order, all collected Ably messages are re-decoded from
|
|
22
|
-
* oldest to newest
|
|
23
|
-
* page boundaries correctly.
|
|
22
|
+
* oldest to newest at the point a result is built. This handles turns
|
|
23
|
+
* that span page boundaries correctly. The fetch loop uses a cheap
|
|
24
|
+
* header-based completion counter to decide when to stop paging, so the
|
|
25
|
+
* full decode runs exactly once per traversal regardless of page count.
|
|
24
26
|
*/
|
|
25
27
|
import type * as Ably from 'ably';
|
|
26
28
|
/**
|
|
@@ -36,6 +38,6 @@ import type * as Ably from 'ably';
|
|
|
36
38
|
* @param codec - The codec for decoding wire messages into domain messages.
|
|
37
39
|
* @param options - Pagination options.
|
|
38
40
|
* @param logger - Logger for diagnostic output.
|
|
39
|
-
* @returns The first page of decoded history
|
|
41
|
+
* @returns The first page of decoded history.
|
|
40
42
|
*/
|
|
41
|
-
export declare const decodeHistory: <TEvent, TMessage>(channel: Ably.RealtimeChannel, codec: Codec<TEvent, TMessage>, options: LoadHistoryOptions | undefined, logger: Logger) => Promise<
|
|
43
|
+
export declare const decodeHistory: <TEvent, TMessage>(channel: Ably.RealtimeChannel, codec: Codec<TEvent, TMessage>, options: LoadHistoryOptions | undefined, logger: Logger) => Promise<HistoryPage<TMessage>>;
|
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
* @param opts.turnId - Turn correlation ID.
|
|
13
13
|
* @param opts.msgId - Message identity.
|
|
14
14
|
* @param opts.turnClientId - ClientId of the turn initiator.
|
|
15
|
-
* @param opts.parent - Preceding message's msg-id (for branching).
|
|
15
|
+
* @param opts.parent - Preceding message's msg-id (for branching).
|
|
16
16
|
* @param opts.forkOf - Forked message's msg-id (for edit/regen).
|
|
17
|
+
* @param opts.amend - The msg-id of the existing message this message targets (cross-turn events).
|
|
17
18
|
* @returns A headers record with the `x-ably-*` transport headers set.
|
|
18
19
|
*/
|
|
19
20
|
export declare const buildTransportHeaders: (opts: {
|
|
@@ -21,6 +22,7 @@ export declare const buildTransportHeaders: (opts: {
|
|
|
21
22
|
turnId: string;
|
|
22
23
|
msgId: string;
|
|
23
24
|
turnClientId?: string;
|
|
24
|
-
parent?: string
|
|
25
|
+
parent?: string;
|
|
25
26
|
forkOf?: string;
|
|
27
|
+
amend?: string;
|
|
26
28
|
}) => Record<string, string>;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export type { ActiveTurn, AddMessageOptions, AddMessagesResult, CancelFilter, CancelRequest, ClientTransport, ClientTransportOptions, CloseOptions,
|
|
1
|
+
export type { ActiveTurn, AddMessageOptions, AddMessagesResult, CancelFilter, CancelRequest, ClientTransport, ClientTransportOptions, CloseOptions, EventsNode, MessageNode, NewTurnOptions, SendOptions, ServerTransport, ServerTransportOptions, StreamResponseOptions, StreamResult, Tree, Turn, TurnEndReason, TurnLifecycleEvent, View, } from './types.js';
|
|
2
|
+
export type { EventNode } from './types.js';
|
|
3
|
+
export type { TreeNode } from './types.js';
|
|
4
|
+
export type { TreeInternal } from './tree.js';
|
|
2
5
|
export { createServerTransport } from './server-transport.js';
|
|
3
6
|
export { createClientTransport } from './client-transport.js';
|
|
4
7
|
export { buildTransportHeaders } from './headers.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Logger } from '../../logger.js';
|
|
2
|
-
import { StreamEncoder } from '../codec/types.js';
|
|
2
|
+
import { StreamEncoder, WriteOptions } from '../codec/types.js';
|
|
3
3
|
import { StreamResult } from './types.js';
|
|
4
4
|
/**
|
|
5
5
|
* Pipe an event stream through an encoder to the channel.
|
|
@@ -10,7 +10,8 @@ import { StreamResult } from './types.js';
|
|
|
10
10
|
* @param encoder - The streaming encoder to write events through.
|
|
11
11
|
* @param signal - Abort signal to monitor for cancellation.
|
|
12
12
|
* @param onAbort - Optional callback invoked when the stream is cancelled, before the stream ends.
|
|
13
|
+
* @param resolveWriteOptions - Optional per-event hook returning {@link WriteOptions} overrides to pass to `encoder.appendEvent`.
|
|
13
14
|
* @param logger - Optional logger for diagnostic output.
|
|
14
15
|
* @returns The reason the pipe ended.
|
|
15
16
|
*/
|
|
16
|
-
export declare const pipeStream: <TEvent, TMessage>(stream: ReadableStream<TEvent>, encoder: StreamEncoder<TEvent, TMessage>, signal: AbortSignal | undefined, onAbort?: (write: (event: TEvent) => Promise<void>) => void | Promise<void>, logger?: Logger) => Promise<StreamResult>;
|
|
17
|
+
export declare const pipeStream: <TEvent, TMessage>(stream: ReadableStream<TEvent>, encoder: StreamEncoder<TEvent, TMessage>, signal: AbortSignal | undefined, onAbort?: (write: (event: TEvent) => Promise<void>) => void | Promise<void>, resolveWriteOptions?: (event: TEvent) => WriteOptions | undefined, logger?: Logger) => Promise<StreamResult>;
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import { Logger } from '../../logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Client-side stream routing.
|
|
4
|
+
*
|
|
5
|
+
* Maintains a map of turnId to ReadableStreamController. Routes decoded events
|
|
6
|
+
* to the correct stream. Closes streams on terminal events, explicit close, or
|
|
7
|
+
* error.
|
|
8
|
+
*/
|
|
9
|
+
import * as Ably from 'ably';
|
|
2
10
|
/** Routes decoded events to the correct turn's ReadableStream. */
|
|
3
11
|
export interface StreamRouter<TEvent> {
|
|
4
12
|
/** Register a new stream for a turnId. Returns the ReadableStream the consumer reads from. */
|
|
5
13
|
createStream(turnId: string): ReadableStream<TEvent>;
|
|
6
|
-
/** Close the stream for a turnId. Returns true if a stream
|
|
14
|
+
/** Close the stream for a turnId. Returns true if a stream existed. */
|
|
7
15
|
closeStream(turnId: string): boolean;
|
|
16
|
+
/** Error the stream for a turnId. The consumer's reader will reject with the given error. Returns true if a stream existed. */
|
|
17
|
+
errorStream(turnId: string, error: Ably.ErrorInfo): boolean;
|
|
8
18
|
/** Enqueue an event to the correct stream. Returns true if routed successfully. */
|
|
9
19
|
route(turnId: string, event: TEvent): boolean;
|
|
10
20
|
/** Whether a specific turnId has an active stream. */
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { Logger } from '../../logger.js';
|
|
2
|
+
import { MessageNode, Tree, TurnLifecycleEvent } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Tree — materializes a branching conversation from a flat
|
|
5
|
+
* oplog of Ably messages using serial-first ordering.
|
|
6
|
+
*
|
|
7
|
+
* Serial order (the total order assigned by Ably) is the primary mechanism
|
|
8
|
+
* for linear message sequences. `x-ably-parent` and `x-ably-fork-of` headers
|
|
9
|
+
* are only structurally meaningful at branch points — where the user is
|
|
10
|
+
* interacting with a visible message and the client always has it loaded.
|
|
11
|
+
*
|
|
12
|
+
* `upsert()` is the sole mutation method. Messages can arrive in any order
|
|
13
|
+
* (live subscription, history pages, seed data) and the tree produces the
|
|
14
|
+
* correct `flattenNodes()` output once all messages are present.
|
|
15
|
+
*
|
|
16
|
+
* The tree owns conversation state. `flattenNodes()` returns the linear node
|
|
17
|
+
* list for the currently selected branches — this is what the transport's
|
|
18
|
+
* `getMessages()` delegates to.
|
|
19
|
+
*/
|
|
20
|
+
import type * as Ably from 'ably';
|
|
21
|
+
/** Internal tree surface used by View — not part of the public Tree API. */
|
|
22
|
+
export interface TreeInternal<TMessage> extends Tree<TMessage> {
|
|
23
|
+
/**
|
|
24
|
+
* Monotonic counter that increments on structural changes (node insert,
|
|
25
|
+
* delete, serial promotion/reorder) but NOT on content-only updates
|
|
26
|
+
* (existing node's message replaced). Allows the View to skip full
|
|
27
|
+
* tree walks when only message content changed.
|
|
28
|
+
*/
|
|
29
|
+
readonly structuralVersion: number;
|
|
30
|
+
/**
|
|
31
|
+
* Flatten the tree along selected branches into a linear node list.
|
|
32
|
+
* The `selections` map provides the selected sibling's msgId at each
|
|
33
|
+
* fork point, keyed by group root msgId. Fork points not present in
|
|
34
|
+
* the map default to the latest sibling. If a selectedMsgId is not
|
|
35
|
+
* found in the sibling group (stale/deleted), falls back to latest.
|
|
36
|
+
*/
|
|
37
|
+
flattenNodes(selections: Map<string, string>): MessageNode<TMessage>[];
|
|
38
|
+
/**
|
|
39
|
+
* Get the "group root" msgId for a sibling group — the original message
|
|
40
|
+
* that all forks in the group trace back to.
|
|
41
|
+
*/
|
|
42
|
+
getGroupRoot(msgId: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* Get the sibling group that `msgId` belongs to, as full MessageNode objects.
|
|
45
|
+
* Allows callers to resolve index ↔ msgId without losing identity.
|
|
46
|
+
*/
|
|
47
|
+
getSiblingNodes(msgId: string): MessageNode<TMessage>[];
|
|
48
|
+
/** Forward a raw Ably message event to tree subscribers. */
|
|
49
|
+
emitAblyMessage(msg: Ably.InboundMessage): void;
|
|
50
|
+
/** Forward a turn lifecycle event to tree subscribers. */
|
|
51
|
+
emitTurn(event: TurnLifecycleEvent): void;
|
|
52
|
+
/** Register an active turn. */
|
|
53
|
+
trackTurn(turnId: string, clientId: string): void;
|
|
54
|
+
/** Unregister an active turn. */
|
|
55
|
+
untrackTurn(turnId: string): void;
|
|
56
|
+
}
|
|
57
|
+
export declare class DefaultTree<TMessage> implements TreeInternal<TMessage> {
|
|
58
|
+
/** All nodes indexed by msgId (x-ably-msg-id). */
|
|
59
|
+
private readonly _nodeIndex;
|
|
60
|
+
/**
|
|
61
|
+
* All nodes sorted by serial (lexicographic). Null-serial messages
|
|
62
|
+
* (optimistic inserts, seed data) sort after all serial-bearing messages,
|
|
63
|
+
* ordered among themselves by insertion sequence.
|
|
64
|
+
*/
|
|
65
|
+
private readonly _sortedList;
|
|
66
|
+
/**
|
|
67
|
+
* Parent index: parentId to set of child msgIds.
|
|
68
|
+
* Nodes with no parent are indexed under the key `null`.
|
|
69
|
+
*/
|
|
70
|
+
private readonly _parentIndex;
|
|
71
|
+
private readonly _emitter;
|
|
72
|
+
private readonly _logger;
|
|
73
|
+
/** Active turns: turnId → clientId. */
|
|
74
|
+
private readonly _turnClientIds;
|
|
75
|
+
/** Monotonically increasing counter for insertion sequence. */
|
|
76
|
+
private _seqCounter;
|
|
77
|
+
/** Incremented on structural changes; unchanged on content-only updates. */
|
|
78
|
+
private _structuralVersion;
|
|
79
|
+
get structuralVersion(): number;
|
|
80
|
+
constructor(logger: Logger);
|
|
81
|
+
/**
|
|
82
|
+
* Compare two nodes for sorted list ordering.
|
|
83
|
+
* Serial-bearing nodes sort by serial (lexicographic).
|
|
84
|
+
* Null-serial nodes sort after all serial-bearing nodes.
|
|
85
|
+
* Among null-serial nodes, sort by insertion sequence.
|
|
86
|
+
* @param a - First node to compare.
|
|
87
|
+
* @param b - Second node to compare.
|
|
88
|
+
* @returns Negative if a sorts before b, positive if after, zero if equal.
|
|
89
|
+
*/
|
|
90
|
+
private _compareNodes;
|
|
91
|
+
/**
|
|
92
|
+
* Insert a node into sortedList at the correct position via binary search.
|
|
93
|
+
* @param internal - The node to insert.
|
|
94
|
+
*/
|
|
95
|
+
private _insertSorted;
|
|
96
|
+
/**
|
|
97
|
+
* Remove a node from sortedList.
|
|
98
|
+
* @param internal - The node to remove.
|
|
99
|
+
*/
|
|
100
|
+
private _removeSorted;
|
|
101
|
+
private _addToParentIndex;
|
|
102
|
+
private _removeFromParentIndex;
|
|
103
|
+
/**
|
|
104
|
+
* Get the sibling group that `msgId` belongs to.
|
|
105
|
+
*
|
|
106
|
+
* A sibling group is: the original message + all messages whose `forkOf`
|
|
107
|
+
* points to the original (or transitively to a sibling). We find the
|
|
108
|
+
* group root by following `forkOf` chains to the earliest ancestor that
|
|
109
|
+
* has no `forkOf` (or whose `forkOf` target doesn't share the same parent).
|
|
110
|
+
* @param msgId - The msg-id to look up the sibling group for.
|
|
111
|
+
* @returns The ordered list of sibling nodes.
|
|
112
|
+
*/
|
|
113
|
+
private _getSiblingGroup;
|
|
114
|
+
/**
|
|
115
|
+
* Check if `node` belongs to the sibling group rooted at `originalId`.
|
|
116
|
+
* A node is a sibling if it IS the original or its forkOf chain leads
|
|
117
|
+
* to the original (with the same parentId).
|
|
118
|
+
* @param node - The node to check.
|
|
119
|
+
* @param originalId - The group root to match against.
|
|
120
|
+
* @returns True if the node belongs to the sibling group.
|
|
121
|
+
*/
|
|
122
|
+
private _isSiblingOf;
|
|
123
|
+
/**
|
|
124
|
+
* Get the "group root" msgId for a sibling group — the original message
|
|
125
|
+
* that all forks trace back to.
|
|
126
|
+
* @param msgId - Any msg-id in the sibling group.
|
|
127
|
+
* @returns The msg-id of the group root.
|
|
128
|
+
*/
|
|
129
|
+
getGroupRoot(msgId: string): string;
|
|
130
|
+
flattenNodes(selections: Map<string, string>): MessageNode<TMessage>[];
|
|
131
|
+
getSiblings(msgId: string): TMessage[];
|
|
132
|
+
getSiblingNodes(msgId: string): MessageNode<TMessage>[];
|
|
133
|
+
hasSiblings(msgId: string): boolean;
|
|
134
|
+
getNode(msgId: string): MessageNode<TMessage> | undefined;
|
|
135
|
+
getHeaders(msgId: string): Record<string, string> | undefined;
|
|
136
|
+
upsert(msgId: string, message: TMessage, headers: Record<string, string>, serial?: string): void;
|
|
137
|
+
delete(msgId: string): void;
|
|
138
|
+
getActiveTurnIds(): Map<string, Set<string>>;
|
|
139
|
+
on(event: 'update', handler: () => void): () => void;
|
|
140
|
+
on(event: 'ably-message', handler: (msg: Ably.InboundMessage) => void): () => void;
|
|
141
|
+
on(event: 'turn', handler: (event: TurnLifecycleEvent) => void): () => void;
|
|
142
|
+
/**
|
|
143
|
+
* Forward a raw Ably message event to tree subscribers.
|
|
144
|
+
* @param msg - The raw Ably message to emit.
|
|
145
|
+
*/
|
|
146
|
+
emitAblyMessage(msg: Ably.InboundMessage): void;
|
|
147
|
+
/**
|
|
148
|
+
* Forward a turn lifecycle event to tree subscribers.
|
|
149
|
+
* @param event - The turn lifecycle event to emit.
|
|
150
|
+
*/
|
|
151
|
+
emitTurn(event: TurnLifecycleEvent): void;
|
|
152
|
+
/**
|
|
153
|
+
* Register an active turn.
|
|
154
|
+
* @param turnId - The turn's unique identifier.
|
|
155
|
+
* @param clientId - The client that owns the turn.
|
|
156
|
+
*/
|
|
157
|
+
trackTurn(turnId: string, clientId: string): void;
|
|
158
|
+
/**
|
|
159
|
+
* Unregister an active turn.
|
|
160
|
+
* @param turnId - The turn to untrack.
|
|
161
|
+
*/
|
|
162
|
+
untrackTurn(turnId: string): void;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Create a Tree that materializes branching history from a flat oplog.
|
|
166
|
+
* @param logger - Logger for diagnostic output.
|
|
167
|
+
* @returns A new {@link DefaultTree} instance. The transport uses DefaultTree
|
|
168
|
+
* directly for internal methods (emitAblyMessage, emitTurn, trackTurn, untrackTurn).
|
|
169
|
+
* Public consumers see the narrower {@link Tree} interface.
|
|
170
|
+
*/
|
|
171
|
+
export declare const createTree: <TMessage>(logger: Logger) => DefaultTree<TMessage>;
|
|
@@ -11,7 +11,10 @@ import type * as Ably from 'ably';
|
|
|
11
11
|
/** Manages active turns and publishes turn lifecycle events on the channel. */
|
|
12
12
|
export interface TurnManager {
|
|
13
13
|
/** Register a new turn. Publishes turn-start on the channel. Returns AbortSignal. */
|
|
14
|
-
startTurn(turnId: string, clientId?: string, controller?: AbortController
|
|
14
|
+
startTurn(turnId: string, clientId?: string, controller?: AbortController, metadata?: {
|
|
15
|
+
parent?: string;
|
|
16
|
+
forkOf?: string;
|
|
17
|
+
}): Promise<AbortSignal>;
|
|
15
18
|
/** End a turn. Publishes turn-end on the channel. Cleans up internal state. */
|
|
16
19
|
endTurn(turnId: string, reason: TurnEndReason): Promise<void>;
|
|
17
20
|
/** Get the AbortSignal for a turn. */
|