@ably/ai-transport 0.1.0 → 0.3.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 +93 -111
- package/dist/ably-ai-transport.js +2401 -1387
- 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 +116 -42
- package/dist/core/agent.d.ts +44 -0
- package/dist/core/channel-options.d.ts +57 -0
- package/dist/core/codec/codec-event.d.ts +9 -0
- package/dist/core/codec/decoder.d.ts +24 -24
- package/dist/core/codec/define-codec.d.ts +100 -0
- package/dist/core/codec/encoder.d.ts +10 -12
- package/dist/core/codec/field-bag.d.ts +85 -0
- package/dist/core/codec/fields.d.ts +141 -0
- package/dist/core/codec/index.d.ts +8 -2
- package/dist/core/codec/input-descriptor-decoder.d.ts +19 -0
- package/dist/core/codec/input-descriptor-encoder.d.ts +22 -0
- package/dist/core/codec/input-descriptors.d.ts +281 -0
- package/dist/core/codec/lifecycle-tracker.d.ts +10 -9
- package/dist/core/codec/output-descriptor-decoder.d.ts +29 -0
- package/dist/core/codec/output-descriptor-encoder.d.ts +31 -0
- package/dist/core/codec/output-descriptors.d.ts +237 -0
- package/dist/core/codec/types.d.ts +470 -119
- package/dist/core/codec/well-known-inputs.d.ts +52 -0
- package/dist/core/transport/agent-session.d.ts +10 -0
- package/dist/core/transport/agent-view.d.ts +296 -0
- package/dist/core/transport/client-session.d.ts +13 -0
- package/dist/core/transport/decode-fold.d.ts +55 -0
- package/dist/core/transport/headers.d.ts +121 -14
- package/dist/core/transport/index.d.ts +5 -6
- package/dist/core/transport/internal/bounded-map.d.ts +20 -0
- package/dist/core/transport/invocation.d.ts +74 -0
- package/dist/core/transport/load-history-pages.d.ts +71 -0
- package/dist/core/transport/load-history.d.ts +44 -0
- package/dist/core/transport/pipe-stream.d.ts +9 -9
- package/dist/core/transport/run-manager.d.ts +76 -0
- package/dist/core/transport/session-support.d.ts +55 -0
- package/dist/core/transport/tree.d.ts +523 -109
- package/dist/core/transport/types/agent.d.ts +375 -0
- package/dist/core/transport/types/client.d.ts +201 -0
- package/dist/core/transport/types/shared.d.ts +24 -0
- package/dist/core/transport/types/tree.d.ts +357 -0
- package/dist/core/transport/types/view.d.ts +249 -0
- package/dist/core/transport/types.d.ts +13 -553
- package/dist/core/transport/view.d.ts +390 -84
- package/dist/core/transport/wire-log.d.ts +102 -0
- package/dist/errors.d.ts +27 -10
- package/dist/index.d.ts +8 -9
- package/dist/logger.d.ts +12 -0
- package/dist/react/ably-ai-transport-react.js +1365 -1010
- 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/client-session-context.d.ts +37 -0
- package/dist/react/contexts/client-session-provider.d.ts +56 -0
- package/dist/react/create-session-hooks.d.ts +116 -0
- package/dist/react/index.d.ts +13 -12
- package/dist/react/internal/skipped-session.d.ts +8 -0
- package/dist/react/internal/use-resolved-session.d.ts +36 -0
- package/dist/react/use-ably-messages.d.ts +17 -14
- package/dist/react/use-client-session.d.ts +81 -0
- package/dist/react/use-create-view.d.ts +14 -13
- package/dist/react/use-tree.d.ts +30 -15
- package/dist/react/use-view.d.ts +81 -50
- package/dist/utils.d.ts +48 -71
- package/dist/vercel/ably-ai-transport-vercel.js +3257 -2499
- 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/decode-lifecycle.d.ts +9 -0
- package/dist/vercel/codec/events.d.ts +50 -0
- package/dist/vercel/codec/fields.d.ts +44 -0
- package/dist/vercel/codec/fold-content.d.ts +16 -0
- package/dist/vercel/codec/fold-data.d.ts +16 -0
- package/dist/vercel/codec/fold-input.d.ts +67 -0
- package/dist/vercel/codec/fold-lifecycle.d.ts +16 -0
- package/dist/vercel/codec/fold-text.d.ts +16 -0
- package/dist/vercel/codec/fold-tool-input.d.ts +17 -0
- package/dist/vercel/codec/fold-tool-output.d.ts +16 -0
- package/dist/vercel/codec/index.d.ts +7 -20
- package/dist/vercel/codec/inputs.d.ts +11 -0
- package/dist/vercel/codec/outputs.d.ts +11 -0
- package/dist/vercel/codec/reducer-state.d.ts +121 -0
- package/dist/vercel/codec/reducer.d.ts +62 -0
- package/dist/vercel/codec/tool-transitions.d.ts +2 -8
- package/dist/vercel/codec/wire-data.d.ts +34 -0
- package/dist/vercel/index.d.ts +5 -5
- package/dist/vercel/react/ably-ai-transport-vercel-react.js +2859 -9705
- package/dist/vercel/react/ably-ai-transport-vercel-react.js.map +1 -1
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs +1 -45
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs.map +1 -1
- package/dist/vercel/react/contexts/chat-transport-context.d.ts +9 -7
- package/dist/vercel/react/contexts/chat-transport-provider.d.ts +53 -41
- package/dist/vercel/react/index.d.ts +1 -2
- package/dist/vercel/react/use-chat-transport.d.ts +30 -26
- package/dist/vercel/react/use-message-sync.d.ts +17 -30
- package/dist/vercel/run-end-reason.d.ts +84 -0
- package/dist/vercel/tool-part.d.ts +21 -0
- package/dist/vercel/transport/chat-transport.d.ts +41 -24
- package/dist/vercel/transport/index.d.ts +24 -20
- package/dist/vercel/transport/run-output-stream.d.ts +54 -0
- package/dist/version.d.ts +2 -0
- package/package.json +31 -24
- package/src/constants.ts +124 -51
- package/src/core/agent.ts +92 -0
- package/src/core/channel-options.ts +89 -0
- package/src/core/codec/codec-event.ts +27 -0
- package/src/core/codec/decoder.ts +202 -105
- package/src/core/codec/define-codec.ts +432 -0
- package/src/core/codec/encoder.ts +114 -107
- package/src/core/codec/field-bag.ts +142 -0
- package/src/core/codec/fields.ts +193 -0
- package/src/core/codec/index.ts +56 -6
- package/src/core/codec/input-descriptor-decoder.ts +97 -0
- package/src/core/codec/input-descriptor-encoder.ts +150 -0
- package/src/core/codec/input-descriptors.ts +373 -0
- package/src/core/codec/lifecycle-tracker.ts +10 -9
- package/src/core/codec/output-descriptor-decoder.ts +139 -0
- package/src/core/codec/output-descriptor-encoder.ts +101 -0
- package/src/core/codec/output-descriptors.ts +307 -0
- package/src/core/codec/types.ts +505 -126
- package/src/core/codec/well-known-inputs.ts +96 -0
- package/src/core/transport/agent-session.ts +1085 -0
- package/src/core/transport/agent-view.ts +738 -0
- package/src/core/transport/client-session.ts +780 -0
- package/src/core/transport/decode-fold.ts +101 -0
- package/src/core/transport/headers.ts +234 -22
- package/src/core/transport/index.ts +27 -27
- package/src/core/transport/internal/bounded-map.ts +27 -0
- package/src/core/transport/invocation.ts +98 -0
- package/src/core/transport/load-history-pages.ts +220 -0
- package/src/core/transport/load-history.ts +271 -0
- package/src/core/transport/pipe-stream.ts +63 -39
- package/src/core/transport/run-manager.ts +243 -0
- package/src/core/transport/session-support.ts +96 -0
- package/src/core/transport/tree.ts +1293 -308
- package/src/core/transport/types/agent.ts +434 -0
- package/src/core/transport/types/client.ts +247 -0
- package/src/core/transport/types/shared.ts +27 -0
- package/src/core/transport/types/tree.ts +393 -0
- package/src/core/transport/types/view.ts +288 -0
- package/src/core/transport/types.ts +13 -706
- package/src/core/transport/view.ts +1229 -450
- package/src/core/transport/wire-log.ts +189 -0
- package/src/errors.ts +29 -9
- package/src/event-emitter.ts +3 -2
- package/src/index.ts +86 -42
- package/src/logger.ts +14 -1
- package/src/react/contexts/client-session-context.ts +41 -0
- package/src/react/contexts/client-session-provider.tsx +222 -0
- package/src/react/create-session-hooks.ts +141 -0
- package/src/react/index.ts +24 -13
- package/src/react/internal/skipped-session.ts +62 -0
- package/src/react/internal/use-resolved-session.ts +63 -0
- package/src/react/use-ably-messages.ts +32 -22
- package/src/react/use-client-session.ts +178 -0
- package/src/react/use-create-view.ts +33 -29
- package/src/react/use-tree.ts +61 -30
- package/src/react/use-view.ts +138 -96
- package/src/utils.ts +83 -131
- package/src/vercel/codec/decode-lifecycle.ts +70 -0
- package/src/vercel/codec/events.ts +85 -0
- package/src/vercel/codec/fields.ts +58 -0
- package/src/vercel/codec/fold-content.ts +54 -0
- package/src/vercel/codec/fold-data.ts +46 -0
- package/src/vercel/codec/fold-input.ts +255 -0
- package/src/vercel/codec/fold-lifecycle.ts +85 -0
- package/src/vercel/codec/fold-text.ts +55 -0
- package/src/vercel/codec/fold-tool-input.ts +86 -0
- package/src/vercel/codec/fold-tool-output.ts +79 -0
- package/src/vercel/codec/index.ts +28 -21
- package/src/vercel/codec/inputs.ts +116 -0
- package/src/vercel/codec/outputs.ts +207 -0
- package/src/vercel/codec/reducer-state.ts +169 -0
- package/src/vercel/codec/reducer.ts +191 -0
- package/src/vercel/codec/tool-transitions.ts +3 -14
- package/src/vercel/codec/wire-data.ts +64 -0
- package/src/vercel/index.ts +7 -19
- package/src/vercel/react/contexts/chat-transport-context.ts +8 -7
- package/src/vercel/react/contexts/chat-transport-provider.tsx +87 -59
- package/src/vercel/react/index.ts +3 -5
- package/src/vercel/react/use-chat-transport.ts +44 -66
- package/src/vercel/react/use-message-sync.ts +75 -39
- package/src/vercel/run-end-reason.ts +157 -0
- package/src/vercel/tool-part.ts +25 -0
- package/src/vercel/transport/chat-transport.ts +380 -98
- package/src/vercel/transport/index.ts +38 -37
- package/src/vercel/transport/run-output-stream.ts +169 -0
- package/src/version.ts +2 -0
- package/dist/core/transport/client-transport.d.ts +0 -10
- package/dist/core/transport/decode-history.d.ts +0 -43
- package/dist/core/transport/server-transport.d.ts +0 -7
- package/dist/core/transport/stream-router.d.ts +0 -29
- package/dist/core/transport/turn-manager.d.ts +0 -37
- package/dist/react/contexts/transport-context.d.ts +0 -31
- package/dist/react/contexts/transport-provider.d.ts +0 -49
- package/dist/react/create-transport-hooks.d.ts +0 -124
- package/dist/react/use-active-turns.d.ts +0 -12
- package/dist/react/use-client-transport.d.ts +0 -80
- package/dist/vercel/codec/accumulator.d.ts +0 -21
- package/dist/vercel/codec/decoder.d.ts +0 -22
- package/dist/vercel/codec/encoder.d.ts +0 -41
- package/dist/vercel/react/use-staged-add-tool-approval-response.d.ts +0 -30
- package/dist/vercel/tool-approvals.d.ts +0 -124
- package/dist/vercel/tool-events.d.ts +0 -26
- package/src/core/transport/client-transport.ts +0 -977
- package/src/core/transport/decode-history.ts +0 -485
- package/src/core/transport/server-transport.ts +0 -612
- package/src/core/transport/stream-router.ts +0 -136
- package/src/core/transport/turn-manager.ts +0 -165
- package/src/react/contexts/transport-context.ts +0 -37
- package/src/react/contexts/transport-provider.tsx +0 -164
- package/src/react/create-transport-hooks.ts +0 -144
- package/src/react/use-active-turns.ts +0 -72
- package/src/react/use-client-transport.ts +0 -197
- package/src/vercel/codec/accumulator.ts +0 -588
- package/src/vercel/codec/decoder.ts +0 -618
- package/src/vercel/codec/encoder.ts +0 -410
- package/src/vercel/react/use-staged-add-tool-approval-response.ts +0 -87
- package/src/vercel/tool-approvals.ts +0 -380
- package/src/vercel/tool-events.ts +0 -53
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Client-side stream routing.
|
|
3
|
-
*
|
|
4
|
-
* Maintains a map of turnId to ReadableStreamController. Routes decoded events
|
|
5
|
-
* to the correct stream. Closes streams on terminal events, explicit close, or
|
|
6
|
-
* error.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import * as Ably from 'ably';
|
|
10
|
-
|
|
11
|
-
import { ErrorCode } from '../../errors.js';
|
|
12
|
-
import type { Logger } from '../../logger.js';
|
|
13
|
-
import type { TurnEntry } from './types.js';
|
|
14
|
-
|
|
15
|
-
// ---------------------------------------------------------------------------
|
|
16
|
-
// Interface
|
|
17
|
-
// ---------------------------------------------------------------------------
|
|
18
|
-
|
|
19
|
-
/** Routes decoded events to the correct turn's ReadableStream. */
|
|
20
|
-
export interface StreamRouter<TEvent> {
|
|
21
|
-
/** Register a new stream for a turnId. Returns the ReadableStream the consumer reads from. */
|
|
22
|
-
createStream(turnId: string): ReadableStream<TEvent>;
|
|
23
|
-
/** Close the stream for a turnId. Returns true if a stream existed. */
|
|
24
|
-
closeStream(turnId: string): boolean;
|
|
25
|
-
/** Error the stream for a turnId. The consumer's reader will reject with the given error. Returns true if a stream existed. */
|
|
26
|
-
errorStream(turnId: string, error: Ably.ErrorInfo): boolean;
|
|
27
|
-
/** Enqueue an event to the correct stream. Returns true if routed successfully. */
|
|
28
|
-
route(turnId: string, event: TEvent): boolean;
|
|
29
|
-
/** Whether a specific turnId has an active stream. */
|
|
30
|
-
has(turnId: string): boolean;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// ---------------------------------------------------------------------------
|
|
34
|
-
// Implementation
|
|
35
|
-
// ---------------------------------------------------------------------------
|
|
36
|
-
|
|
37
|
-
// Spec: AIT-CT14
|
|
38
|
-
class DefaultStreamRouter<TEvent> implements StreamRouter<TEvent> {
|
|
39
|
-
private readonly _turns = new Map<string, TurnEntry<TEvent>>();
|
|
40
|
-
private readonly _isTerminal: (event: TEvent) => boolean;
|
|
41
|
-
private readonly _logger: Logger;
|
|
42
|
-
|
|
43
|
-
constructor(isTerminal: (event: TEvent) => boolean, logger: Logger) {
|
|
44
|
-
this._isTerminal = isTerminal;
|
|
45
|
-
this._logger = logger;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
createStream(turnId: string): ReadableStream<TEvent> {
|
|
49
|
-
this._logger.trace('StreamRouter.createStream();', { turnId });
|
|
50
|
-
|
|
51
|
-
// Build stream+controller together. ReadableStream's start() runs synchronously
|
|
52
|
-
// per spec, so the controller is captured before the constructor returns.
|
|
53
|
-
const entry: { controller?: ReadableStreamDefaultController<TEvent> } = {};
|
|
54
|
-
const stream = new ReadableStream<TEvent>({
|
|
55
|
-
start(controller) {
|
|
56
|
-
entry.controller = controller;
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
if (!entry.controller) {
|
|
60
|
-
throw new Ably.ErrorInfo(
|
|
61
|
-
'unable to create stream; ReadableStream start() was not called synchronously',
|
|
62
|
-
ErrorCode.TransportSubscriptionError,
|
|
63
|
-
500,
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
this._turns.set(turnId, { controller: entry.controller, turnId });
|
|
67
|
-
return stream;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Spec: AIT-CT14b
|
|
71
|
-
closeStream(turnId: string): boolean {
|
|
72
|
-
const turn = this._turns.get(turnId);
|
|
73
|
-
if (!turn) return false;
|
|
74
|
-
|
|
75
|
-
this._logger.debug('StreamRouter.closeStream(); closing stream', { turnId });
|
|
76
|
-
try {
|
|
77
|
-
turn.controller.close();
|
|
78
|
-
} catch {
|
|
79
|
-
/* consumer cancelled the stream */
|
|
80
|
-
}
|
|
81
|
-
this._turns.delete(turnId);
|
|
82
|
-
return true;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Spec: AIT-CT14c
|
|
86
|
-
errorStream(turnId: string, error: Ably.ErrorInfo): boolean {
|
|
87
|
-
const turn = this._turns.get(turnId);
|
|
88
|
-
if (!turn) return false;
|
|
89
|
-
|
|
90
|
-
this._logger.debug('StreamRouter.errorStream(); erroring stream', { turnId });
|
|
91
|
-
try {
|
|
92
|
-
turn.controller.error(error);
|
|
93
|
-
} catch {
|
|
94
|
-
/* consumer cancelled the stream */
|
|
95
|
-
}
|
|
96
|
-
this._turns.delete(turnId);
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Spec: AIT-CT14a
|
|
101
|
-
route(turnId: string, event: TEvent): boolean {
|
|
102
|
-
const turn = this._turns.get(turnId);
|
|
103
|
-
if (!turn) return false;
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
turn.controller.enqueue(event);
|
|
107
|
-
} catch {
|
|
108
|
-
this._turns.delete(turnId);
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (this._isTerminal(event)) {
|
|
113
|
-
this.closeStream(turnId);
|
|
114
|
-
}
|
|
115
|
-
return true;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
has(turnId: string): boolean {
|
|
119
|
-
return this._turns.has(turnId);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// ---------------------------------------------------------------------------
|
|
124
|
-
// Factory
|
|
125
|
-
// ---------------------------------------------------------------------------
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Create a StreamRouter that routes decoded events to per-turn ReadableStreams.
|
|
129
|
-
* @param isTerminal - Predicate that returns true for events that close the stream.
|
|
130
|
-
* @param logger - Logger for diagnostic output.
|
|
131
|
-
* @returns A new {@link StreamRouter} instance.
|
|
132
|
-
*/
|
|
133
|
-
export const createStreamRouter = <TEvent>(
|
|
134
|
-
isTerminal: (event: TEvent) => boolean,
|
|
135
|
-
logger: Logger,
|
|
136
|
-
): StreamRouter<TEvent> => new DefaultStreamRouter(isTerminal, logger);
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Server-side turn state management and lifecycle event publishing.
|
|
3
|
-
*
|
|
4
|
-
* Owns the authoritative turn lifecycle. Tracks active turns with their
|
|
5
|
-
* AbortControllers and clientIds. Publishes turn-start and turn-end events
|
|
6
|
-
* on the Ably channel so all clients can react to turn state changes.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type * as Ably from 'ably';
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
EVENT_TURN_END,
|
|
13
|
-
EVENT_TURN_START,
|
|
14
|
-
HEADER_FORK_OF,
|
|
15
|
-
HEADER_PARENT,
|
|
16
|
-
HEADER_TURN_CLIENT_ID,
|
|
17
|
-
HEADER_TURN_ID,
|
|
18
|
-
HEADER_TURN_REASON,
|
|
19
|
-
} from '../../constants.js';
|
|
20
|
-
import type { Logger } from '../../logger.js';
|
|
21
|
-
import type { TurnEndReason } from './types.js';
|
|
22
|
-
|
|
23
|
-
// ---------------------------------------------------------------------------
|
|
24
|
-
// Interface
|
|
25
|
-
// ---------------------------------------------------------------------------
|
|
26
|
-
|
|
27
|
-
/** Manages active turns and publishes turn lifecycle events on the channel. */
|
|
28
|
-
export interface TurnManager {
|
|
29
|
-
/** Register a new turn. Publishes turn-start on the channel. Returns AbortSignal. */
|
|
30
|
-
startTurn(
|
|
31
|
-
turnId: string,
|
|
32
|
-
clientId?: string,
|
|
33
|
-
controller?: AbortController,
|
|
34
|
-
metadata?: { parent?: string; forkOf?: string },
|
|
35
|
-
): Promise<AbortSignal>;
|
|
36
|
-
/** End a turn. Publishes turn-end on the channel. Cleans up internal state. */
|
|
37
|
-
endTurn(turnId: string, reason: TurnEndReason): Promise<void>;
|
|
38
|
-
/** Get the AbortSignal for a turn. */
|
|
39
|
-
getSignal(turnId: string): AbortSignal | undefined;
|
|
40
|
-
/** Get the clientId that owns a turn. */
|
|
41
|
-
getClientId(turnId: string): string | undefined;
|
|
42
|
-
/** Abort the signal for a turn. */
|
|
43
|
-
abort(turnId: string): void;
|
|
44
|
-
/** Get all active turn IDs. */
|
|
45
|
-
getActiveTurnIds(): string[];
|
|
46
|
-
/** Abort all active turns and clear state. */
|
|
47
|
-
close(): void;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// ---------------------------------------------------------------------------
|
|
51
|
-
// Internal state
|
|
52
|
-
// ---------------------------------------------------------------------------
|
|
53
|
-
|
|
54
|
-
interface ActiveTurnsEntry {
|
|
55
|
-
controller: AbortController;
|
|
56
|
-
clientId: string;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// ---------------------------------------------------------------------------
|
|
60
|
-
// Implementation
|
|
61
|
-
// ---------------------------------------------------------------------------
|
|
62
|
-
|
|
63
|
-
class DefaultTurnManager implements TurnManager {
|
|
64
|
-
private readonly _channel: Ably.RealtimeChannel;
|
|
65
|
-
private readonly _logger: Logger | undefined;
|
|
66
|
-
private readonly _activeTurns = new Map<string, ActiveTurnsEntry>();
|
|
67
|
-
|
|
68
|
-
constructor(channel: Ably.RealtimeChannel, logger?: Logger) {
|
|
69
|
-
this._channel = channel;
|
|
70
|
-
this._logger = logger?.withContext({ component: 'TurnManager' });
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async startTurn(
|
|
74
|
-
turnId: string,
|
|
75
|
-
clientId?: string,
|
|
76
|
-
externalController?: AbortController,
|
|
77
|
-
metadata?: { parent?: string; forkOf?: string },
|
|
78
|
-
): Promise<AbortSignal> {
|
|
79
|
-
this._logger?.trace('DefaultTurnManager.startTurn();', { turnId, clientId });
|
|
80
|
-
|
|
81
|
-
const controller = externalController ?? new AbortController();
|
|
82
|
-
const resolvedClientId = clientId ?? '';
|
|
83
|
-
this._activeTurns.set(turnId, { controller, clientId: resolvedClientId });
|
|
84
|
-
|
|
85
|
-
const headers: Record<string, string> = {
|
|
86
|
-
[HEADER_TURN_ID]: turnId,
|
|
87
|
-
[HEADER_TURN_CLIENT_ID]: resolvedClientId,
|
|
88
|
-
};
|
|
89
|
-
if (metadata?.parent !== undefined) {
|
|
90
|
-
headers[HEADER_PARENT] = metadata.parent;
|
|
91
|
-
}
|
|
92
|
-
if (metadata?.forkOf !== undefined) {
|
|
93
|
-
headers[HEADER_FORK_OF] = metadata.forkOf;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
await this._channel.publish({
|
|
97
|
-
name: EVENT_TURN_START,
|
|
98
|
-
extras: { headers },
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
this._logger?.debug('DefaultTurnManager.startTurn(); turn started', { turnId });
|
|
102
|
-
return controller.signal;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async endTurn(turnId: string, reason: TurnEndReason): Promise<void> {
|
|
106
|
-
this._logger?.trace('DefaultTurnManager.endTurn();', { turnId, reason });
|
|
107
|
-
|
|
108
|
-
const state = this._activeTurns.get(turnId);
|
|
109
|
-
const resolvedClientId = state?.clientId ?? '';
|
|
110
|
-
|
|
111
|
-
// Publish before deleting local state so that if publish fails,
|
|
112
|
-
// the turn remains in the active set and can be retried or cleaned up.
|
|
113
|
-
await this._channel.publish({
|
|
114
|
-
name: EVENT_TURN_END,
|
|
115
|
-
extras: {
|
|
116
|
-
headers: {
|
|
117
|
-
[HEADER_TURN_ID]: turnId,
|
|
118
|
-
[HEADER_TURN_CLIENT_ID]: resolvedClientId,
|
|
119
|
-
[HEADER_TURN_REASON]: reason,
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
this._activeTurns.delete(turnId);
|
|
125
|
-
this._logger?.debug('DefaultTurnManager.endTurn(); turn ended', { turnId, reason });
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
getSignal(turnId: string): AbortSignal | undefined {
|
|
129
|
-
return this._activeTurns.get(turnId)?.controller.signal;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
getClientId(turnId: string): string | undefined {
|
|
133
|
-
return this._activeTurns.get(turnId)?.clientId;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
abort(turnId: string): void {
|
|
137
|
-
this._logger?.debug('DefaultTurnManager.abort();', { turnId });
|
|
138
|
-
this._activeTurns.get(turnId)?.controller.abort();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
getActiveTurnIds(): string[] {
|
|
142
|
-
return [...this._activeTurns.keys()];
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
close(): void {
|
|
146
|
-
this._logger?.trace('DefaultTurnManager.close();', { activeTurns: this._activeTurns.size });
|
|
147
|
-
for (const state of this._activeTurns.values()) {
|
|
148
|
-
state.controller.abort();
|
|
149
|
-
}
|
|
150
|
-
this._activeTurns.clear();
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// ---------------------------------------------------------------------------
|
|
155
|
-
// Factory
|
|
156
|
-
// ---------------------------------------------------------------------------
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Create a turn manager bound to the given channel.
|
|
160
|
-
* @param channel - The Ably channel to publish lifecycle events on.
|
|
161
|
-
* @param logger - Optional logger for diagnostic output.
|
|
162
|
-
* @returns A new {@link TurnManager} instance.
|
|
163
|
-
*/
|
|
164
|
-
export const createTurnManager = (channel: Ably.RealtimeChannel, logger?: Logger): TurnManager =>
|
|
165
|
-
new DefaultTurnManager(channel, logger);
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import type * as Ably from 'ably';
|
|
2
|
-
import { createContext } from 'react';
|
|
3
|
-
|
|
4
|
-
import type { ClientTransport } from '../../core/transport/types.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* A single entry in the transport registry, holding the transport and any
|
|
8
|
-
* error that occurred during its construction.
|
|
9
|
-
*
|
|
10
|
-
* `transport` is `undefined` when construction failed.
|
|
11
|
-
* `error` is set when `createClientTransport` threw during provider render.
|
|
12
|
-
*/
|
|
13
|
-
export interface TransportSlot {
|
|
14
|
-
/** The constructed transport, or `undefined` if construction failed. */
|
|
15
|
-
transport: ClientTransport<unknown, unknown> | undefined;
|
|
16
|
-
/** Construction error from `createClientTransport`, or `undefined` on success. */
|
|
17
|
-
error: Ably.ErrorInfo | undefined;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** The shape of the TransportContext value — a record of channelName → slot. */
|
|
21
|
-
export type TransportContextValue = Readonly<Record<string, TransportSlot>>;
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Context that holds the registered {@link ClientTransport} slots, keyed by channelName.
|
|
25
|
-
* Each slot contains the transport (or `undefined` on construction failure) and any error.
|
|
26
|
-
* Populated by {@link TransportProvider}; read by {@link useClientTransport}.
|
|
27
|
-
*/
|
|
28
|
-
export const TransportContext = createContext<TransportContextValue>({});
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Context that holds the nearest (innermost) transport slot.
|
|
32
|
-
* Each {@link TransportProvider} sets this to its own slot, so descendants
|
|
33
|
-
* can access the nearest transport without knowing its channel name.
|
|
34
|
-
* `undefined` when no provider is present.
|
|
35
|
-
* Read by hooks whose `transport` argument is omitted.
|
|
36
|
-
*/
|
|
37
|
-
export const NearestTransportContext = createContext<TransportSlot | undefined>(undefined);
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TransportProvider: creates a ClientTransport and makes it available to
|
|
3
|
-
* descendants via TransportContext.
|
|
4
|
-
*
|
|
5
|
-
* Wraps children with Ably's ChannelProvider so the underlying channel
|
|
6
|
-
* lifecycle is managed in one place. An inner component calls useChannel
|
|
7
|
-
* to get the stable channel reference and creates the transport once on
|
|
8
|
-
* first render (via useRef).
|
|
9
|
-
*
|
|
10
|
-
* If createClientTransport throws, the error is stored in the TransportSlot
|
|
11
|
-
* (alongside an undefined transport) so that useClientTransport can surface it
|
|
12
|
-
* as transportError without crashing the component tree.
|
|
13
|
-
*
|
|
14
|
-
* The transport is closed when the provider truly unmounts. The close is
|
|
15
|
-
* scheduled as a microtask so that React Strict Mode's synchronous
|
|
16
|
-
* remount cycle (mount → fake-unmount → remount) can cancel it before it
|
|
17
|
-
* fires, avoiding unnecessary transport teardown in development.
|
|
18
|
-
*
|
|
19
|
-
* Multiple TransportProviders can be nested using distinct channelNames.
|
|
20
|
-
* Each provider merges its slot into the parent record so descendants
|
|
21
|
-
* can access all registered transports via useClientTransport(channelName).
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
import * as Ably from 'ably';
|
|
25
|
-
import { ChannelProvider, useChannel } from 'ably/react';
|
|
26
|
-
import { type PropsWithChildren, type ReactNode, useContext, useEffect, useMemo, useRef } from 'react';
|
|
27
|
-
|
|
28
|
-
import { createClientTransport } from '../../core/transport/client-transport.js';
|
|
29
|
-
import type { ClientTransport, ClientTransportOptions } from '../../core/transport/types.js';
|
|
30
|
-
import { ErrorCode } from '../../errors.js';
|
|
31
|
-
import type { TransportSlot } from '../contexts/transport-context.js';
|
|
32
|
-
import { NearestTransportContext, TransportContext } from '../contexts/transport-context.js';
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Props for {@link TransportProvider}.
|
|
36
|
-
*
|
|
37
|
-
* All {@link ClientTransportOptions} except `channel` (managed internally) plus `channelName`.
|
|
38
|
-
*/
|
|
39
|
-
export interface TransportProviderProps<TEvent, TMessage>
|
|
40
|
-
extends Omit<ClientTransportOptions<TEvent, TMessage>, 'channel'>, PropsWithChildren {
|
|
41
|
-
/** The Ably channel name to subscribe to. Also used as the context registry key. */
|
|
42
|
-
channelName: string;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Inner component: rendered inside ChannelProvider so useChannel resolves to
|
|
46
|
-
// the channel created by the outer wrapper.
|
|
47
|
-
const TransportProviderInner = <TEvent, TMessage>({
|
|
48
|
-
channelName,
|
|
49
|
-
children,
|
|
50
|
-
...transportOptions
|
|
51
|
-
}: TransportProviderProps<TEvent, TMessage>) => {
|
|
52
|
-
const { channel } = useChannel({ channelName });
|
|
53
|
-
const transportRef = useRef<ClientTransport<TEvent, TMessage> | undefined>(undefined);
|
|
54
|
-
const transportChannelRef = useRef<string>(channelName);
|
|
55
|
-
const transportsToDisposeRef = useRef<ClientTransport<unknown, unknown>[]>([]);
|
|
56
|
-
const pendingCloseRef = useRef(false);
|
|
57
|
-
const constructionErrorRef = useRef<Ably.ErrorInfo | undefined>(undefined);
|
|
58
|
-
|
|
59
|
-
const alreadyCreatedOrFailed = !!transportRef.current || !!constructionErrorRef.current;
|
|
60
|
-
|
|
61
|
-
if (!alreadyCreatedOrFailed || transportChannelRef.current !== channelName) {
|
|
62
|
-
transportChannelRef.current = channelName;
|
|
63
|
-
if (transportRef.current) transportsToDisposeRef.current.push(transportRef.current);
|
|
64
|
-
try {
|
|
65
|
-
transportRef.current = createClientTransport({ ...transportOptions, channel });
|
|
66
|
-
constructionErrorRef.current = undefined;
|
|
67
|
-
} catch (error) {
|
|
68
|
-
transportRef.current = undefined;
|
|
69
|
-
constructionErrorRef.current =
|
|
70
|
-
error instanceof Ably.ErrorInfo
|
|
71
|
-
? error
|
|
72
|
-
: new Ably.ErrorInfo('Unknown error while creating transport', ErrorCode.BadRequest, 400);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const parentMap = useContext(TransportContext);
|
|
77
|
-
|
|
78
|
-
// Capture ref values as locals so useMemo deps track changes correctly.
|
|
79
|
-
// CAST: TransportContext stores transports with erased generics.
|
|
80
|
-
// The generic types are fixed at the TransportProvider<TEvent, TMessage> boundary.
|
|
81
|
-
const currentTransport = transportRef.current as ClientTransport<unknown, unknown> | undefined;
|
|
82
|
-
const currentError = constructionErrorRef.current;
|
|
83
|
-
|
|
84
|
-
const slot = useMemo<TransportSlot>(
|
|
85
|
-
() => ({ transport: currentTransport, error: currentError }),
|
|
86
|
-
[currentTransport, currentError],
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
const contextValue = useMemo(() => ({ ...parentMap, [channelName]: slot }), [channelName, parentMap, slot]);
|
|
90
|
-
|
|
91
|
-
useEffect(
|
|
92
|
-
() => () => {
|
|
93
|
-
for (const transport of transportsToDisposeRef.current) void transport.close();
|
|
94
|
-
},
|
|
95
|
-
[channelName],
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
// Close the transport when the component truly unmounts. The close is
|
|
99
|
-
// scheduled as a microtask: in React Strict Mode (dev) the component
|
|
100
|
-
// remounts synchronously before any microtask can drain, so the remount's
|
|
101
|
-
// effect setup resets pendingCloseRef.current = false and cancels the
|
|
102
|
-
// close. On a real unmount no remount follows, the microtask fires, and
|
|
103
|
-
// the transport is closed.
|
|
104
|
-
useEffect(() => {
|
|
105
|
-
pendingCloseRef.current = false;
|
|
106
|
-
return () => {
|
|
107
|
-
pendingCloseRef.current = true;
|
|
108
|
-
void Promise.resolve().then(() => {
|
|
109
|
-
if (pendingCloseRef.current) {
|
|
110
|
-
void transportRef.current?.close();
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
};
|
|
114
|
-
}, []);
|
|
115
|
-
|
|
116
|
-
return (
|
|
117
|
-
<TransportContext.Provider value={contextValue}>
|
|
118
|
-
<NearestTransportContext.Provider value={slot}>{children}</NearestTransportContext.Provider>
|
|
119
|
-
</TransportContext.Provider>
|
|
120
|
-
);
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Provide a {@link ClientTransport} to descendant components.
|
|
125
|
-
*
|
|
126
|
-
* Wraps children with Ably's `ChannelProvider` using `channelName`, creates a
|
|
127
|
-
* transport from the resolved channel and the remaining options, and registers it
|
|
128
|
-
* in `TransportContext` under `channelName`. Descendants call
|
|
129
|
-
* {@link useClientTransport} with the same `channelName` to access the transport.
|
|
130
|
-
*
|
|
131
|
-
* If `createClientTransport` throws during construction, the error is surfaced
|
|
132
|
-
* through `useClientTransport` as `transportError` — the component tree does not
|
|
133
|
-
* crash and children are still rendered.
|
|
134
|
-
*
|
|
135
|
-
* ```tsx
|
|
136
|
-
* <TransportProvider channelName="ai:demo" codec={UIMessageCodec}>
|
|
137
|
-
* <Chat />
|
|
138
|
-
* </TransportProvider>
|
|
139
|
-
*
|
|
140
|
-
* // Inside Chat:
|
|
141
|
-
* const { transport, transportError } = useClientTransport({ channelName: 'ai:demo' });
|
|
142
|
-
* ```
|
|
143
|
-
*
|
|
144
|
-
* For multiple transports, nest providers with distinct channelNames:
|
|
145
|
-
*
|
|
146
|
-
* ```tsx
|
|
147
|
-
* <TransportProvider channelName="ai:main" codec={UIMessageCodec}>
|
|
148
|
-
* <TransportProvider channelName="ai:aux" codec={UIMessageCodec}>
|
|
149
|
-
* <App />
|
|
150
|
-
* </TransportProvider>
|
|
151
|
-
* </TransportProvider>
|
|
152
|
-
*
|
|
153
|
-
* // Inside App:
|
|
154
|
-
* const { transport: main } = useClientTransport({ channelName: 'ai:main' });
|
|
155
|
-
* const { transport: aux } = useClientTransport({ channelName: 'ai:aux' });
|
|
156
|
-
* ```
|
|
157
|
-
* @param props - Provider configuration including `channelName`, `codec`, and all other {@link ClientTransportOptions}.
|
|
158
|
-
* @returns A React element wrapping children with ChannelProvider and TransportContext.
|
|
159
|
-
*/
|
|
160
|
-
export const TransportProvider = <TEvent, TMessage>(props: TransportProviderProps<TEvent, TMessage>): ReactNode => (
|
|
161
|
-
<ChannelProvider channelName={props.channelName}>
|
|
162
|
-
<TransportProviderInner {...props} />
|
|
163
|
-
</ChannelProvider>
|
|
164
|
-
);
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* createTransportHooks: factory that captures TEvent and TMessage once and returns
|
|
3
|
-
* a bundle of type-safe hooks + TransportProvider. Hook call sites need no type
|
|
4
|
-
* parameters at every use — just call the hooks directly.
|
|
5
|
-
* @example
|
|
6
|
-
* // Once per app (e.g. in a shared transport.ts):
|
|
7
|
-
* export const {
|
|
8
|
-
* TransportProvider,
|
|
9
|
-
* useClientTransport,
|
|
10
|
-
* useView,
|
|
11
|
-
* useActiveTurns,
|
|
12
|
-
* } = createTransportHooks<UIMessageChunk, UIMessage>();
|
|
13
|
-
*
|
|
14
|
-
* // In page:
|
|
15
|
-
* <TransportProvider channelName="ai:demo" codec={UIMessageCodec}>
|
|
16
|
-
* <Chat />
|
|
17
|
-
* </TransportProvider>
|
|
18
|
-
*
|
|
19
|
-
* // In Chat — no type params needed, transport is implicit from nearest provider:
|
|
20
|
-
* const { nodes } = useView({ limit: 30 });
|
|
21
|
-
* const turns = useActiveTurns();
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
import type * as Ably from 'ably';
|
|
25
|
-
import type { ComponentType } from 'react';
|
|
26
|
-
|
|
27
|
-
import type { ClientTransport, View } from '../core/transport/types.js';
|
|
28
|
-
import type { TransportProviderProps } from './contexts/transport-provider.js';
|
|
29
|
-
import { TransportProvider as _TransportProvider } from './contexts/transport-provider.js';
|
|
30
|
-
import { useAblyMessages as _useAblyMessages } from './use-ably-messages.js';
|
|
31
|
-
import { useActiveTurns as _useActiveTurns } from './use-active-turns.js';
|
|
32
|
-
import type { ClientTransportHandle } from './use-client-transport.js';
|
|
33
|
-
import { useClientTransport as _useClientTransport } from './use-client-transport.js';
|
|
34
|
-
import { useCreateView as _useCreateView } from './use-create-view.js';
|
|
35
|
-
import type { TreeHandle } from './use-tree.js';
|
|
36
|
-
import { useTree as _useTree } from './use-tree.js';
|
|
37
|
-
import type { ViewHandle } from './use-view.js';
|
|
38
|
-
import { useView as _useView } from './use-view.js';
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Bundle of type-safe hooks and provider returned by {@link createTransportHooks}.
|
|
42
|
-
*
|
|
43
|
-
* `TEvent` and `TMessage` are baked in at factory creation time so no type params
|
|
44
|
-
* are needed at hook call sites.
|
|
45
|
-
*/
|
|
46
|
-
export interface TransportHooks<TEvent, TMessage> {
|
|
47
|
-
/**
|
|
48
|
-
* `TransportProvider` narrowed to `TEvent`/`TMessage`. No JSX type params needed.
|
|
49
|
-
*/
|
|
50
|
-
TransportProvider: ComponentType<TransportProviderProps<TEvent, TMessage>>;
|
|
51
|
-
/**
|
|
52
|
-
* Read the transport from context. No type params needed.
|
|
53
|
-
*
|
|
54
|
-
* Returns `{ transport, transportError }`. When no provider is found,
|
|
55
|
-
* `transportError` is set and `transport` is a stub that throws on access —
|
|
56
|
-
* the hook never throws during render.
|
|
57
|
-
*
|
|
58
|
-
* Pass `onError` to subscribe to post-construction transport errors
|
|
59
|
-
* (e.g. send failures, channel continuity loss) without wiring
|
|
60
|
-
* `transport.on('error', …)` manually.
|
|
61
|
-
*/
|
|
62
|
-
useClientTransport: (props?: {
|
|
63
|
-
/** Channel name to look up; omit to use the nearest {@link TransportProvider}. */
|
|
64
|
-
channelName?: string;
|
|
65
|
-
/** When `true`, return a stub transport that throws on any access. */
|
|
66
|
-
skip?: boolean;
|
|
67
|
-
/** Called whenever the resolved transport emits an error event. */
|
|
68
|
-
onError?: (error: Ably.ErrorInfo) => void;
|
|
69
|
-
}) => ClientTransportHandle<TEvent, TMessage>;
|
|
70
|
-
/**
|
|
71
|
-
* Subscribe to the nearest transport's view and return the visible node list with pagination.
|
|
72
|
-
* Pass `transport` to use a transport's default view, `view` to subscribe to a specific view
|
|
73
|
-
* directly. Pass `limit` to auto-load on mount. Pass `skip: true` for an empty handle.
|
|
74
|
-
*/
|
|
75
|
-
useView: (props?: {
|
|
76
|
-
/** Client transport whose default view to subscribe to; defaults to the nearest {@link TransportProvider}. */
|
|
77
|
-
transport?: ClientTransport<TEvent, TMessage> | null;
|
|
78
|
-
/** A specific {@link View} to subscribe to directly. Takes priority over `transport`. */
|
|
79
|
-
view?: View<TEvent, TMessage> | null;
|
|
80
|
-
/** When provided, auto-loads the first page on mount. */
|
|
81
|
-
limit?: number;
|
|
82
|
-
/** When `true`, skip all subscriptions and return an empty handle. */
|
|
83
|
-
skip?: boolean;
|
|
84
|
-
}) => ViewHandle<TEvent, TMessage>;
|
|
85
|
-
/**
|
|
86
|
-
* Track active turns across all clients on the channel.
|
|
87
|
-
* Pass `transport` to override; defaults to the nearest {@link TransportProvider}.
|
|
88
|
-
*/
|
|
89
|
-
useActiveTurns: (props?: {
|
|
90
|
-
/** Override transport; defaults to the nearest {@link TransportProvider}. */
|
|
91
|
-
transport?: ClientTransport<TEvent, TMessage> | null;
|
|
92
|
-
}) => Map<string, Set<string>>;
|
|
93
|
-
/**
|
|
94
|
-
* Navigate conversation branches in the transport tree.
|
|
95
|
-
* Pass `transport` to override; defaults to the nearest {@link TransportProvider}.
|
|
96
|
-
*/
|
|
97
|
-
useTree: (props?: {
|
|
98
|
-
/** Override transport; defaults to the nearest {@link TransportProvider}. */
|
|
99
|
-
transport?: ClientTransport<TEvent, TMessage>;
|
|
100
|
-
}) => TreeHandle<TMessage>;
|
|
101
|
-
/**
|
|
102
|
-
* Subscribe to raw Ably messages on the transport channel.
|
|
103
|
-
* Pass `transport` to override; defaults to the nearest {@link TransportProvider}.
|
|
104
|
-
* Pass `skip: true` to return an empty array without subscribing.
|
|
105
|
-
*/
|
|
106
|
-
useAblyMessages: (props?: {
|
|
107
|
-
/** Override transport; defaults to the nearest {@link TransportProvider}. */
|
|
108
|
-
transport?: ClientTransport<TEvent, TMessage>;
|
|
109
|
-
/** When `true`, skip all subscriptions and return an empty array. */
|
|
110
|
-
skip?: boolean;
|
|
111
|
-
}) => Ably.InboundMessage[];
|
|
112
|
-
/**
|
|
113
|
-
* Create an independent view over the same tree.
|
|
114
|
-
* Pass `transport` to override; defaults to the nearest {@link TransportProvider}.
|
|
115
|
-
* Pass `skip: true` to return an empty handle without creating a view.
|
|
116
|
-
*/
|
|
117
|
-
useCreateView: (props?: {
|
|
118
|
-
/** Override transport; defaults to the nearest {@link TransportProvider}. */
|
|
119
|
-
transport?: ClientTransport<TEvent, TMessage> | null;
|
|
120
|
-
/** When provided, auto-loads the first page on mount. */
|
|
121
|
-
limit?: number;
|
|
122
|
-
/** When `true`, skip view creation and return an empty handle. */
|
|
123
|
-
skip?: boolean;
|
|
124
|
-
}) => ViewHandle<TEvent, TMessage>;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Create a bundle of type-safe hooks and provider for a given `TEvent`/`TMessage` pair.
|
|
129
|
-
*
|
|
130
|
-
* `TEvent` and `TMessage` are captured at factory creation time; hook call sites need
|
|
131
|
-
* no type parameters. The returned hooks are thin wrappers around the standalone hooks
|
|
132
|
-
* with the types resolved.
|
|
133
|
-
* @returns A {@link TransportHooks} bundle.
|
|
134
|
-
*/
|
|
135
|
-
export const createTransportHooks = <TEvent, TMessage>(): TransportHooks<TEvent, TMessage> => ({
|
|
136
|
-
// CAST: TransportProvider is generic; factory narrows it to TEvent/TMessage.
|
|
137
|
-
TransportProvider: _TransportProvider as ComponentType<TransportProviderProps<TEvent, TMessage>>,
|
|
138
|
-
useClientTransport: (props) => _useClientTransport<TEvent, TMessage>(props ?? {}),
|
|
139
|
-
useView: (props) => _useView<TEvent, TMessage>(props ?? {}),
|
|
140
|
-
useActiveTurns: (props) => _useActiveTurns<TEvent, TMessage>(props ?? {}),
|
|
141
|
-
useTree: (props) => _useTree<TEvent, TMessage>(props ?? {}),
|
|
142
|
-
useAblyMessages: (props) => _useAblyMessages<TEvent, TMessage>(props ?? {}),
|
|
143
|
-
useCreateView: (props) => _useCreateView<TEvent, TMessage>(props ?? {}),
|
|
144
|
-
});
|