@assistant-ui/store 0.0.0 → 0.0.1
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/dist/AssistantContext.js +1 -0
- package/dist/AssistantContext.js.map +1 -1
- package/dist/EventContext.d.ts +65 -0
- package/dist/EventContext.d.ts.map +1 -0
- package/dist/EventContext.js +62 -0
- package/dist/EventContext.js.map +1 -0
- package/dist/StoreContext.d.ts +9 -0
- package/dist/StoreContext.d.ts.map +1 -0
- package/dist/StoreContext.js +20 -0
- package/dist/StoreContext.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/useAssistantClient.d.ts +4 -1
- package/dist/useAssistantClient.d.ts.map +1 -1
- package/dist/useAssistantClient.js +65 -46
- package/dist/useAssistantClient.js.map +1 -1
- package/dist/useAssistantEvent.d.ts +3 -0
- package/dist/useAssistantEvent.d.ts.map +1 -0
- package/dist/useAssistantEvent.js +17 -0
- package/dist/useAssistantEvent.js.map +1 -0
- package/package.json +3 -3
- package/src/AssistantContext.tsx +1 -1
- package/src/EventContext.ts +184 -0
- package/src/StoreContext.ts +28 -0
- package/src/index.ts +18 -0
- package/src/types.ts +9 -0
- package/src/useAssistantClient.tsx +78 -49
- package/src/useAssistantEvent.ts +22 -0
package/dist/AssistantContext.js
CHANGED
|
@@ -20,6 +20,7 @@ var AssistantContext = createContext(
|
|
|
20
20
|
new Proxy({}, {
|
|
21
21
|
get(_, prop) {
|
|
22
22
|
if (prop === "subscribe") return NO_OP_SUBSCRIBE;
|
|
23
|
+
if (prop === "on") return NO_OP_SUBSCRIBE;
|
|
23
24
|
if (prop === "flushSync") return NO_OP_FLUSH_SYNC;
|
|
24
25
|
if (hasRegisteredScope(prop))
|
|
25
26
|
return NO_OP_SCOPE_FIELD;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/AssistantContext.tsx"],"sourcesContent":["import React, { createContext, useContext } from \"react\";\nimport type { AssistantClient, AssistantScopes, ScopeField } from \"./types\";\nimport { hasRegisteredScope } from \"./ScopeRegistry\";\n\nconst NO_OP_SUBSCRIBE = () => () => {};\nconst NO_OP_FLUSH_SYNC = () => {};\nconst NO_OP_SCOPE_FIELD = (() => {\n const fn = (() => {\n throw new Error(\n \"You need to wrap this component/hook in <AssistantProvider>\",\n );\n }) as ScopeField<never>;\n fn.source = null;\n fn.query = null;\n return fn;\n})();\n\n/**\n * React Context for the AssistantClient\n */\nexport const AssistantContext = createContext<AssistantClient>(\n new Proxy({} as AssistantClient, {\n get(_, prop: string) {\n // Allow access to subscribe and flushSync without error\n if (prop === \"subscribe\") return NO_OP_SUBSCRIBE;\n\n if (prop === \"flushSync\") return NO_OP_FLUSH_SYNC;\n\n // If this is a registered scope, return a function that errors when called or accessed\n if (hasRegisteredScope(prop as keyof AssistantScopes))\n return NO_OP_SCOPE_FIELD;\n\n return null;\n },\n }),\n);\n\nexport const useAssistantContextValue = (): AssistantClient => {\n return useContext(AssistantContext);\n};\n\n/**\n * Provider component for AssistantClient\n *\n * @example\n * ```typescript\n * <AssistantProvider client={client}>\n * <YourApp />\n * </AssistantProvider>\n * ```\n */\nexport const AssistantProvider = ({\n client,\n children,\n}: {\n client: AssistantClient;\n children: React.ReactNode;\n}): React.ReactElement => {\n return (\n <AssistantContext.Provider value={client}>\n {children}\n </AssistantContext.Provider>\n );\n};\n"],"mappings":";AAAA,SAAgB,eAAe,kBAAkB;AAEjD,SAAS,0BAA0B;AAyD/B;AAvDJ,IAAM,kBAAkB,MAAM,MAAM;AAAC;AACrC,IAAM,mBAAmB,MAAM;AAAC;AAChC,IAAM,qBAAqB,MAAM;AAC/B,QAAM,MAAM,MAAM;AAChB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,KAAG,SAAS;AACZ,KAAG,QAAQ;AACX,SAAO;AACT,GAAG;AAKI,IAAM,mBAAmB;AAAA,EAC9B,IAAI,MAAM,CAAC,GAAsB;AAAA,IAC/B,IAAI,GAAG,MAAc;AAEnB,UAAI,SAAS,YAAa,QAAO;
|
|
1
|
+
{"version":3,"sources":["../src/AssistantContext.tsx"],"sourcesContent":["import React, { createContext, useContext } from \"react\";\nimport type { AssistantClient, AssistantScopes, ScopeField } from \"./types\";\nimport { hasRegisteredScope } from \"./ScopeRegistry\";\n\nconst NO_OP_SUBSCRIBE = () => () => {};\nconst NO_OP_FLUSH_SYNC = () => {};\nconst NO_OP_SCOPE_FIELD = (() => {\n const fn = (() => {\n throw new Error(\n \"You need to wrap this component/hook in <AssistantProvider>\",\n );\n }) as ScopeField<never>;\n fn.source = null;\n fn.query = null;\n return fn;\n})();\n\n/**\n * React Context for the AssistantClient\n */\nexport const AssistantContext = createContext<AssistantClient>(\n new Proxy({} as AssistantClient, {\n get(_, prop: string) {\n // Allow access to subscribe and flushSync without error\n if (prop === \"subscribe\") return NO_OP_SUBSCRIBE;\n if (prop === \"on\") return NO_OP_SUBSCRIBE;\n if (prop === \"flushSync\") return NO_OP_FLUSH_SYNC;\n\n // If this is a registered scope, return a function that errors when called or accessed\n if (hasRegisteredScope(prop as keyof AssistantScopes))\n return NO_OP_SCOPE_FIELD;\n\n return null;\n },\n }),\n);\n\nexport const useAssistantContextValue = (): AssistantClient => {\n return useContext(AssistantContext);\n};\n\n/**\n * Provider component for AssistantClient\n *\n * @example\n * ```typescript\n * <AssistantProvider client={client}>\n * <YourApp />\n * </AssistantProvider>\n * ```\n */\nexport const AssistantProvider = ({\n client,\n children,\n}: {\n client: AssistantClient;\n children: React.ReactNode;\n}): React.ReactElement => {\n return (\n <AssistantContext.Provider value={client}>\n {children}\n </AssistantContext.Provider>\n );\n};\n"],"mappings":";AAAA,SAAgB,eAAe,kBAAkB;AAEjD,SAAS,0BAA0B;AAyD/B;AAvDJ,IAAM,kBAAkB,MAAM,MAAM;AAAC;AACrC,IAAM,mBAAmB,MAAM;AAAC;AAChC,IAAM,qBAAqB,MAAM;AAC/B,QAAM,MAAM,MAAM;AAChB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,KAAG,SAAS;AACZ,KAAG,QAAQ;AACX,SAAO;AACT,GAAG;AAKI,IAAM,mBAAmB;AAAA,EAC9B,IAAI,MAAM,CAAC,GAAsB;AAAA,IAC/B,IAAI,GAAG,MAAc;AAEnB,UAAI,SAAS,YAAa,QAAO;AACjC,UAAI,SAAS,KAAM,QAAO;AAC1B,UAAI,SAAS,YAAa,QAAO;AAGjC,UAAI,mBAAmB,IAA6B;AAClD,eAAO;AAET,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACH;AAEO,IAAM,2BAA2B,MAAuB;AAC7D,SAAO,WAAW,gBAAgB;AACpC;AAYO,IAAM,oBAAoB,CAAC;AAAA,EAChC;AAAA,EACA;AACF,MAG0B;AACxB,SACE,oBAAC,iBAAiB,UAAjB,EAA0B,OAAO,QAC/B,UACH;AAEJ;","names":[]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { Unsubscribe } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Module augmentation interface for custom events.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* declare module "@assistant-ui/store" {
|
|
8
|
+
* interface AssistantEventRegistry {
|
|
9
|
+
* "thread.run-start": { threadId: string };
|
|
10
|
+
* "custom.my-event": { data: string };
|
|
11
|
+
* }
|
|
12
|
+
* }
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export interface AssistantEventRegistry {
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Module augmentation interface for event scope configuration.
|
|
19
|
+
* Maps event sources to their parent scopes.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* declare module "@assistant-ui/store" {
|
|
24
|
+
* interface AssistantEventScopeConfig {
|
|
25
|
+
* composer: "thread" | "message";
|
|
26
|
+
* thread: never;
|
|
27
|
+
* }
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export interface AssistantEventScopeConfig {
|
|
32
|
+
}
|
|
33
|
+
export type AssistantEventMap = AssistantEventRegistry & {
|
|
34
|
+
"*": {
|
|
35
|
+
[K in Exclude<keyof AssistantEventRegistry, "*">]: {
|
|
36
|
+
event: K;
|
|
37
|
+
payload: AssistantEventRegistry[K];
|
|
38
|
+
};
|
|
39
|
+
}[Exclude<keyof AssistantEventRegistry, "*">];
|
|
40
|
+
};
|
|
41
|
+
export type AssistantEvent = keyof AssistantEventMap;
|
|
42
|
+
export type EventSource<T extends AssistantEvent = AssistantEvent> = T extends `${infer Source}.${string}` ? Source : never;
|
|
43
|
+
export type SourceByScope<TScope extends AssistantEventScope<AssistantEvent>> = (TScope extends "*" ? EventSource : never) | (TScope extends keyof AssistantEventScopeConfig ? TScope : never) | {
|
|
44
|
+
[K in keyof AssistantEventScopeConfig]: TScope extends AssistantEventScopeConfig[K] ? K : never;
|
|
45
|
+
}[keyof AssistantEventScopeConfig];
|
|
46
|
+
export type AssistantEventScope<TEvent extends AssistantEvent> = "*" | EventSource<TEvent> | (EventSource<TEvent> extends keyof AssistantEventScopeConfig ? AssistantEventScopeConfig[EventSource<TEvent>] : never);
|
|
47
|
+
export type AssistantEventSelector<TEvent extends AssistantEvent> = TEvent | {
|
|
48
|
+
scope: AssistantEventScope<TEvent>;
|
|
49
|
+
event: TEvent;
|
|
50
|
+
};
|
|
51
|
+
export declare const normalizeEventSelector: <TEvent extends AssistantEvent>(selector: AssistantEventSelector<TEvent>) => {
|
|
52
|
+
scope: AssistantEventScope<TEvent>;
|
|
53
|
+
event: TEvent;
|
|
54
|
+
};
|
|
55
|
+
export declare const checkEventScope: <TEvent extends AssistantEvent, TExpectedScope extends AssistantEventScope<AssistantEvent>>(expectedScope: TExpectedScope, scope: AssistantEventScope<TEvent>, _event: TEvent) => _event is Extract<TEvent, `${SourceByScope<TExpectedScope>}.${string}`>;
|
|
56
|
+
export type AssistantEventCallback<TEvent extends AssistantEvent> = (payload: AssistantEventMap[TEvent]) => void;
|
|
57
|
+
export type EventManager = {
|
|
58
|
+
on<TEvent extends AssistantEvent>(event: TEvent, callback: AssistantEventCallback<TEvent>): Unsubscribe;
|
|
59
|
+
emit<TEvent extends Exclude<AssistantEvent, "*">>(event: TEvent, payload: AssistantEventMap[TEvent]): void;
|
|
60
|
+
};
|
|
61
|
+
export declare const EventManager: () => import("@assistant-ui/tap").ResourceElement<{
|
|
62
|
+
on: <TEvent extends AssistantEvent>(event: TEvent, callback: AssistantEventCallback<TEvent>) => () => void;
|
|
63
|
+
emit: <TEvent extends Exclude<AssistantEvent, "*">>(event: TEvent, payload: AssistantEventMap[TEvent]) => void;
|
|
64
|
+
}, undefined>;
|
|
65
|
+
//# sourceMappingURL=EventContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EventContext.d.ts","sourceRoot":"","sources":["../src/EventContext.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,sBAAsB;CAAG;AAE1C;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,yBAAyB;CAAG;AAE7C,MAAM,MAAM,iBAAiB,GAAG,sBAAsB,GAAG;IAEvD,GAAG,EAAE;SACF,CAAC,IAAI,OAAO,CAAC,MAAM,sBAAsB,EAAE,GAAG,CAAC,GAAG;YACjD,KAAK,EAAE,CAAC,CAAC;YACT,OAAO,EAAE,sBAAsB,CAAC,CAAC,CAAC,CAAC;SACpC;KACF,CAAC,OAAO,CAAC,MAAM,sBAAsB,EAAE,GAAG,CAAC,CAAC,CAAC;CAC/C,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,MAAM,iBAAiB,CAAC;AAErD,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,IAC/D,CAAC,SAAS,GAAG,MAAM,MAAM,IAAI,MAAM,EAAE,GAAG,MAAM,GAAG,KAAK,CAAC;AAEzD,MAAM,MAAM,aAAa,CAAC,MAAM,SAAS,mBAAmB,CAAC,cAAc,CAAC,IACxE,CAAC,MAAM,SAAS,GAAG,GAAG,WAAW,GAAG,KAAK,CAAC,GAC1C,CAAC,MAAM,SAAS,MAAM,yBAAyB,GAAG,MAAM,GAAG,KAAK,CAAC,GACjE;KACG,CAAC,IAAI,MAAM,yBAAyB,GAAG,MAAM,SAAS,yBAAyB,CAAC,CAAC,CAAC,GAC/E,CAAC,GACD,KAAK;CACV,CAAC,MAAM,yBAAyB,CAAC,CAAC;AAEvC,MAAM,MAAM,mBAAmB,CAAC,MAAM,SAAS,cAAc,IACzD,GAAG,GACH,WAAW,CAAC,MAAM,CAAC,GACnB,CAAC,WAAW,CAAC,MAAM,CAAC,SAAS,MAAM,yBAAyB,GACxD,yBAAyB,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,GAC9C,KAAK,CAAC,CAAC;AAEf,MAAM,MAAM,sBAAsB,CAAC,MAAM,SAAS,cAAc,IAC5D,MAAM,GACN;IACE,KAAK,EAAE,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEN,eAAO,MAAM,sBAAsB,GAAI,MAAM,SAAS,cAAc,EAClE,UAAU,sBAAsB,CAAC,MAAM,CAAC;;;CAczC,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,MAAM,SAAS,cAAc,EAC7B,cAAc,SAAS,mBAAmB,CAAC,cAAc,CAAC,EAE1D,eAAe,cAAc,EAC7B,OAAO,mBAAmB,CAAC,MAAM,CAAC,EAClC,QAAQ,MAAM,KACb,MAAM,IAAI,OAAO,CAAC,MAAM,EAAE,GAAG,aAAa,CAAC,cAAc,CAAC,IAAI,MAAM,EAAE,CAExE,CAAC;AAEF,MAAM,MAAM,sBAAsB,CAAC,MAAM,SAAS,cAAc,IAAI,CAClE,OAAO,EAAE,iBAAiB,CAAC,MAAM,CAAC,KAC/B,IAAI,CAAC;AAEV,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,CAAC,MAAM,SAAS,cAAc,EAC9B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,sBAAsB,CAAC,MAAM,CAAC,GACvC,WAAW,CAAC;IACf,IAAI,CAAC,MAAM,SAAS,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC,EAC9C,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,iBAAiB,CAAC,MAAM,CAAC,GACjC,IAAI,CAAC;CACT,CAAC;AAeF,eAAO,MAAM,YAAY;SAvBpB,MAAM,SAAS,cAAc;WAI3B,MAAM,SAAS,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC;aAwEhD,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// src/EventContext.ts
|
|
2
|
+
import { resource, tapMemo } from "@assistant-ui/tap";
|
|
3
|
+
var normalizeEventSelector = (selector) => {
|
|
4
|
+
if (typeof selector === "string") {
|
|
5
|
+
const source = selector.split(".")[0];
|
|
6
|
+
return {
|
|
7
|
+
scope: source,
|
|
8
|
+
event: selector
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
scope: selector.scope,
|
|
13
|
+
event: selector.event
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
var checkEventScope = (expectedScope, scope, _event) => {
|
|
17
|
+
return scope === expectedScope;
|
|
18
|
+
};
|
|
19
|
+
var EventManager = resource(() => {
|
|
20
|
+
const events = tapMemo(() => {
|
|
21
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
22
|
+
return {
|
|
23
|
+
on: (event, callback) => {
|
|
24
|
+
if (!listeners.has(event)) {
|
|
25
|
+
listeners.set(event, /* @__PURE__ */ new Set());
|
|
26
|
+
}
|
|
27
|
+
const eventListeners = listeners.get(event);
|
|
28
|
+
eventListeners.add(callback);
|
|
29
|
+
return () => {
|
|
30
|
+
eventListeners.delete(callback);
|
|
31
|
+
if (eventListeners.size === 0) {
|
|
32
|
+
listeners.delete(event);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
emit: (event, payload) => {
|
|
37
|
+
const eventListeners = listeners.get(event);
|
|
38
|
+
const wildcardListeners = listeners.get("*");
|
|
39
|
+
if (!eventListeners && !wildcardListeners) return;
|
|
40
|
+
queueMicrotask(() => {
|
|
41
|
+
if (eventListeners) {
|
|
42
|
+
for (const callback of eventListeners) {
|
|
43
|
+
callback(payload);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (wildcardListeners) {
|
|
47
|
+
for (const callback of wildcardListeners) {
|
|
48
|
+
callback({ event, payload });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}, []);
|
|
55
|
+
return events;
|
|
56
|
+
});
|
|
57
|
+
export {
|
|
58
|
+
EventManager,
|
|
59
|
+
checkEventScope,
|
|
60
|
+
normalizeEventSelector
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=EventContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/EventContext.ts"],"sourcesContent":["import { resource, tapMemo } from \"@assistant-ui/tap\";\nimport type { Unsubscribe } from \"./types\";\n\n/**\n * Module augmentation interface for custom events.\n *\n * @example\n * ```typescript\n * declare module \"@assistant-ui/store\" {\n * interface AssistantEventRegistry {\n * \"thread.run-start\": { threadId: string };\n * \"custom.my-event\": { data: string };\n * }\n * }\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface AssistantEventRegistry {}\n\n/**\n * Module augmentation interface for event scope configuration.\n * Maps event sources to their parent scopes.\n *\n * @example\n * ```typescript\n * declare module \"@assistant-ui/store\" {\n * interface AssistantEventScopeConfig {\n * composer: \"thread\" | \"message\";\n * thread: never;\n * }\n * }\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface AssistantEventScopeConfig {}\n\nexport type AssistantEventMap = AssistantEventRegistry & {\n // Catch-all\n \"*\": {\n [K in Exclude<keyof AssistantEventRegistry, \"*\">]: {\n event: K;\n payload: AssistantEventRegistry[K];\n };\n }[Exclude<keyof AssistantEventRegistry, \"*\">];\n};\n\nexport type AssistantEvent = keyof AssistantEventMap;\n\nexport type EventSource<T extends AssistantEvent = AssistantEvent> =\n T extends `${infer Source}.${string}` ? Source : never;\n\nexport type SourceByScope<TScope extends AssistantEventScope<AssistantEvent>> =\n | (TScope extends \"*\" ? EventSource : never)\n | (TScope extends keyof AssistantEventScopeConfig ? TScope : never)\n | {\n [K in keyof AssistantEventScopeConfig]: TScope extends AssistantEventScopeConfig[K]\n ? K\n : never;\n }[keyof AssistantEventScopeConfig];\n\nexport type AssistantEventScope<TEvent extends AssistantEvent> =\n | \"*\"\n | EventSource<TEvent>\n | (EventSource<TEvent> extends keyof AssistantEventScopeConfig\n ? AssistantEventScopeConfig[EventSource<TEvent>]\n : never);\n\nexport type AssistantEventSelector<TEvent extends AssistantEvent> =\n | TEvent\n | {\n scope: AssistantEventScope<TEvent>;\n event: TEvent;\n };\n\nexport const normalizeEventSelector = <TEvent extends AssistantEvent>(\n selector: AssistantEventSelector<TEvent>,\n) => {\n if (typeof selector === \"string\") {\n const source = selector.split(\".\")[0] as AssistantEventScope<TEvent>;\n return {\n scope: source,\n event: selector,\n };\n }\n\n return {\n scope: selector.scope,\n event: selector.event,\n };\n};\n\nexport const checkEventScope = <\n TEvent extends AssistantEvent,\n TExpectedScope extends AssistantEventScope<AssistantEvent>,\n>(\n expectedScope: TExpectedScope,\n scope: AssistantEventScope<TEvent>,\n _event: TEvent,\n): _event is Extract<TEvent, `${SourceByScope<TExpectedScope>}.${string}`> => {\n return scope === expectedScope;\n};\n\nexport type AssistantEventCallback<TEvent extends AssistantEvent> = (\n payload: AssistantEventMap[TEvent],\n) => void;\n\nexport type EventManager = {\n on<TEvent extends AssistantEvent>(\n event: TEvent,\n callback: AssistantEventCallback<TEvent>,\n ): Unsubscribe;\n emit<TEvent extends Exclude<AssistantEvent, \"*\">>(\n event: TEvent,\n payload: AssistantEventMap[TEvent],\n ): void;\n};\n\ntype ListenerMap = Omit<\n Map<AssistantEvent, Set<AssistantEventCallback<AssistantEvent>>>,\n \"get\" | \"set\"\n> & {\n get<TEvent extends AssistantEvent>(\n event: TEvent,\n ): Set<AssistantEventCallback<TEvent>> | undefined;\n set<TEvent extends AssistantEvent>(\n event: TEvent,\n value: Set<AssistantEventCallback<TEvent>>,\n ): void;\n};\n\nexport const EventManager = resource(() => {\n const events = tapMemo(() => {\n const listeners: ListenerMap = new Map();\n\n return {\n on: (event, callback) => {\n if (!listeners.has(event)) {\n listeners.set(event, new Set());\n }\n\n const eventListeners = listeners.get(event)!;\n eventListeners.add(callback);\n\n return () => {\n eventListeners.delete(callback);\n if (eventListeners.size === 0) {\n listeners.delete(event);\n }\n };\n },\n\n emit: (event, payload) => {\n const eventListeners = listeners.get(event);\n const wildcardListeners = listeners.get(\"*\");\n\n if (!eventListeners && !wildcardListeners) return;\n\n // make sure state updates flush\n queueMicrotask(() => {\n // Emit to specific event listeners\n if (eventListeners) {\n for (const callback of eventListeners) {\n callback(payload);\n }\n }\n\n // Emit to wildcard listeners\n if (wildcardListeners) {\n for (const callback of wildcardListeners) {\n (\n callback as (payload: {\n event: typeof event;\n payload: typeof payload;\n }) => void\n )({ event, payload });\n }\n }\n });\n },\n } satisfies EventManager;\n }, []);\n\n return events;\n});\n"],"mappings":";AAAA,SAAS,UAAU,eAAe;AA0E3B,IAAM,yBAAyB,CACpC,aACG;AACH,MAAI,OAAO,aAAa,UAAU;AAChC,UAAM,SAAS,SAAS,MAAM,GAAG,EAAE,CAAC;AACpC,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AAAA,IACL,OAAO,SAAS;AAAA,IAChB,OAAO,SAAS;AAAA,EAClB;AACF;AAEO,IAAM,kBAAkB,CAI7B,eACA,OACA,WAC4E;AAC5E,SAAO,UAAU;AACnB;AA8BO,IAAM,eAAe,SAAS,MAAM;AACzC,QAAM,SAAS,QAAQ,MAAM;AAC3B,UAAM,YAAyB,oBAAI,IAAI;AAEvC,WAAO;AAAA,MACL,IAAI,CAAC,OAAO,aAAa;AACvB,YAAI,CAAC,UAAU,IAAI,KAAK,GAAG;AACzB,oBAAU,IAAI,OAAO,oBAAI,IAAI,CAAC;AAAA,QAChC;AAEA,cAAM,iBAAiB,UAAU,IAAI,KAAK;AAC1C,uBAAe,IAAI,QAAQ;AAE3B,eAAO,MAAM;AACX,yBAAe,OAAO,QAAQ;AAC9B,cAAI,eAAe,SAAS,GAAG;AAC7B,sBAAU,OAAO,KAAK;AAAA,UACxB;AAAA,QACF;AAAA,MACF;AAAA,MAEA,MAAM,CAAC,OAAO,YAAY;AACxB,cAAM,iBAAiB,UAAU,IAAI,KAAK;AAC1C,cAAM,oBAAoB,UAAU,IAAI,GAAG;AAE3C,YAAI,CAAC,kBAAkB,CAAC,kBAAmB;AAG3C,uBAAe,MAAM;AAEnB,cAAI,gBAAgB;AAClB,uBAAW,YAAY,gBAAgB;AACrC,uBAAS,OAAO;AAAA,YAClB;AAAA,UACF;AAGA,cAAI,mBAAmB;AACrB,uBAAW,YAAY,mBAAmB;AACxC,cACE,SAIA,EAAE,OAAO,QAAQ,CAAC;AAAA,YACtB;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO;AACT,CAAC;","names":[]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EventManager } from "./EventContext";
|
|
2
|
+
import type { AssistantClient } from "./types";
|
|
3
|
+
export type StoreContextValue = {
|
|
4
|
+
events: EventManager;
|
|
5
|
+
parent: AssistantClient;
|
|
6
|
+
};
|
|
7
|
+
export declare const withStoreContextProvider: <TResult>(value: StoreContextValue, fn: () => TResult) => TResult;
|
|
8
|
+
export declare const tapStoreContext: () => StoreContextValue;
|
|
9
|
+
//# sourceMappingURL=StoreContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StoreContext.d.ts","sourceRoot":"","sources":["../src/StoreContext.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,eAAe,CAAC;CACzB,CAAC;AAIF,eAAO,MAAM,wBAAwB,GAAI,OAAO,EAC9C,OAAO,iBAAiB,EACxB,IAAI,MAAM,OAAO,YAGlB,CAAC;AAEF,eAAO,MAAM,eAAe,yBAK3B,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// src/StoreContext.ts
|
|
2
|
+
import {
|
|
3
|
+
createContext,
|
|
4
|
+
tapContext,
|
|
5
|
+
withContextProvider
|
|
6
|
+
} from "@assistant-ui/tap";
|
|
7
|
+
var StoreContext = createContext(null);
|
|
8
|
+
var withStoreContextProvider = (value, fn) => {
|
|
9
|
+
return withContextProvider(StoreContext, value, fn);
|
|
10
|
+
};
|
|
11
|
+
var tapStoreContext = () => {
|
|
12
|
+
const ctx = tapContext(StoreContext);
|
|
13
|
+
if (!ctx) throw new Error("Store context is not available");
|
|
14
|
+
return ctx;
|
|
15
|
+
};
|
|
16
|
+
export {
|
|
17
|
+
tapStoreContext,
|
|
18
|
+
withStoreContextProvider
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=StoreContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/StoreContext.ts"],"sourcesContent":["import {\n createContext,\n tapContext,\n withContextProvider,\n} from \"@assistant-ui/tap\";\nimport type { EventManager } from \"./EventContext\";\nimport type { AssistantClient } from \"./types\";\n\nexport type StoreContextValue = {\n events: EventManager;\n parent: AssistantClient;\n};\n\nconst StoreContext = createContext<StoreContextValue | null>(null);\n\nexport const withStoreContextProvider = <TResult>(\n value: StoreContextValue,\n fn: () => TResult,\n) => {\n return withContextProvider(StoreContext, value, fn);\n};\n\nexport const tapStoreContext = () => {\n const ctx = tapContext(StoreContext);\n if (!ctx) throw new Error(\"Store context is not available\");\n\n return ctx;\n};\n"],"mappings":";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AASP,IAAM,eAAe,cAAwC,IAAI;AAE1D,IAAM,2BAA2B,CACtC,OACA,OACG;AACH,SAAO,oBAAoB,cAAc,OAAO,EAAE;AACpD;AAEO,IAAM,kBAAkB,MAAM;AACnC,QAAM,MAAM,WAAW,YAAY;AACnC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,gCAAgC;AAE1D,SAAO;AACT;","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,4 +10,9 @@ export { tapStoreList } from "./tapStoreList";
|
|
|
10
10
|
export type { TapStoreListConfig } from "./tapStoreList";
|
|
11
11
|
export { registerAssistantScope } from "./ScopeRegistry";
|
|
12
12
|
export type { AssistantScopeRegistry } from "./types";
|
|
13
|
+
export { tapStoreContext } from "./StoreContext";
|
|
14
|
+
export type { StoreContextValue } from "./StoreContext";
|
|
15
|
+
export { useAssistantEvent } from "./useAssistantEvent";
|
|
16
|
+
export { normalizeEventSelector, checkEventScope } from "./EventContext";
|
|
17
|
+
export type { AssistantEvent, AssistantEventRegistry, AssistantEventScopeConfig, AssistantEventMap, AssistantEventScope, AssistantEventSelector, AssistantEventCallback, EventSource, SourceByScope, EventManager, } from "./EventContext";
|
|
13
18
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,YAAY,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,YAAY,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAGtD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACzE,YAAY,EACV,cAAc,EACd,sBAAsB,EACtB,yBAAyB,EACzB,iBAAiB,EACjB,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,EACtB,WAAW,EACX,aAAa,EACb,YAAY,GACb,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -7,14 +7,21 @@ import { tapApi } from "./tapApi.js";
|
|
|
7
7
|
import { tapLookupResources } from "./tapLookupResources.js";
|
|
8
8
|
import { tapStoreList } from "./tapStoreList.js";
|
|
9
9
|
import { registerAssistantScope } from "./ScopeRegistry.js";
|
|
10
|
+
import { tapStoreContext } from "./StoreContext.js";
|
|
11
|
+
import { useAssistantEvent } from "./useAssistantEvent.js";
|
|
12
|
+
import { normalizeEventSelector, checkEventScope } from "./EventContext.js";
|
|
10
13
|
export {
|
|
11
14
|
AssistantProvider,
|
|
12
15
|
DerivedScope,
|
|
16
|
+
checkEventScope,
|
|
17
|
+
normalizeEventSelector,
|
|
13
18
|
registerAssistantScope,
|
|
14
19
|
tapApi,
|
|
15
20
|
tapLookupResources,
|
|
21
|
+
tapStoreContext,
|
|
16
22
|
tapStoreList,
|
|
17
23
|
useAssistantClient,
|
|
24
|
+
useAssistantEvent,
|
|
18
25
|
useAssistantState
|
|
19
26
|
};
|
|
20
27
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { useAssistantClient } from \"./useAssistantClient\";\nexport { useAssistantState } from \"./useAssistantState\";\nexport { AssistantProvider } from \"./AssistantContext\";\nexport type { AssistantScopes, AssistantClient, AssistantState } from \"./types\";\nexport { DerivedScope } from \"./DerivedScope\";\nexport type { ApiObject } from \"./tapApi\";\nexport { tapApi } from \"./tapApi\";\nexport { tapLookupResources } from \"./tapLookupResources\";\nexport { tapStoreList } from \"./tapStoreList\";\nexport type { TapStoreListConfig } from \"./tapStoreList\";\nexport { registerAssistantScope } from \"./ScopeRegistry\";\n\nexport type { AssistantScopeRegistry } from \"./types\";\n"],"mappings":";AAAA,SAAS,0BAA0B;AACnC,SAAS,yBAAyB;AAClC,SAAS,yBAAyB;AAElC,SAAS,oBAAoB;AAE7B,SAAS,cAAc;AACvB,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;AAE7B,SAAS,8BAA8B;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { useAssistantClient } from \"./useAssistantClient\";\nexport { useAssistantState } from \"./useAssistantState\";\nexport { AssistantProvider } from \"./AssistantContext\";\nexport type { AssistantScopes, AssistantClient, AssistantState } from \"./types\";\nexport { DerivedScope } from \"./DerivedScope\";\nexport type { ApiObject } from \"./tapApi\";\nexport { tapApi } from \"./tapApi\";\nexport { tapLookupResources } from \"./tapLookupResources\";\nexport { tapStoreList } from \"./tapStoreList\";\nexport type { TapStoreListConfig } from \"./tapStoreList\";\nexport { registerAssistantScope } from \"./ScopeRegistry\";\n\nexport type { AssistantScopeRegistry } from \"./types\";\n\n// Events & Store Context\nexport { tapStoreContext } from \"./StoreContext\";\nexport type { StoreContextValue } from \"./StoreContext\";\nexport { useAssistantEvent } from \"./useAssistantEvent\";\nexport { normalizeEventSelector, checkEventScope } from \"./EventContext\";\nexport type {\n AssistantEvent,\n AssistantEventRegistry,\n AssistantEventScopeConfig,\n AssistantEventMap,\n AssistantEventScope,\n AssistantEventSelector,\n AssistantEventCallback,\n EventSource,\n SourceByScope,\n EventManager,\n} from \"./EventContext\";\n"],"mappings":";AAAA,SAAS,0BAA0B;AACnC,SAAS,yBAAyB;AAClC,SAAS,yBAAyB;AAElC,SAAS,oBAAoB;AAE7B,SAAS,cAAc;AACvB,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;AAE7B,SAAS,8BAA8B;AAKvC,SAAS,uBAAuB;AAEhC,SAAS,yBAAyB;AAClC,SAAS,wBAAwB,uBAAuB;","names":[]}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ResourceElement } from "@assistant-ui/tap";
|
|
2
|
+
import type { AssistantEvent, AssistantEventCallback, AssistantEventSelector } from "./EventContext";
|
|
2
3
|
/**
|
|
3
4
|
* Module augmentation interface for assistant-ui store type extensions.
|
|
4
5
|
*
|
|
@@ -82,5 +83,6 @@ export type AssistantClient = {
|
|
|
82
83
|
} & {
|
|
83
84
|
subscribe(listener: () => void): Unsubscribe;
|
|
84
85
|
flushSync(): void;
|
|
86
|
+
on<TEvent extends AssistantEvent>(selector: AssistantEventSelector<TEvent>, callback: AssistantEventCallback<TEvent>): Unsubscribe;
|
|
85
87
|
};
|
|
86
88
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EACV,cAAc,EACd,sBAAsB,EACtB,sBAAsB,EACvB,MAAM,gBAAgB,CAAC;AAmBxB;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,sBAAsB;CAAG;AAE1C,MAAM,MAAM,eAAe,GAAG,MAAM,sBAAsB,SAAS,KAAK,GACpE,MAAM,CAAC,+BAA+B,EAAE,eAAe,CAAC,GACxD;KAAG,CAAC,IAAI,MAAM,sBAAsB,GAAG,sBAAsB,CAAC,CAAC,CAAC;CAAE,CAAC;AAEvE;;GAEG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,eAAe,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;AAE/D;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,eAAe,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,eAAe,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;AAE/D;;;GAGG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,eAAe,IAAI,CAAC,MAAM,UAAU,CAAC,CAAC,CAAC,CAAC,GACvE,CACI;IACE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IACvB,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;CACtB,GACD;IACE,MAAM,EAAE,IAAI,CAAC;IACb,KAAK,EAAE,IAAI,CAAC;CACb,CACJ,CAAC;AAEJ;;GAEG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,eAAe,IAAI;IACzD,GAAG,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC;IAChD,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IACvB,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;CACtB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,eAAe,IAAI,eAAe,CAAC;IAClE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;CACpB,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;KACvB,CAAC,IAAI,MAAM,eAAe,CAAC,CAAC,EAAE,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;CAC9D,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC;AAErC;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;KAC1B,CAAC,IAAI,MAAM,eAAe,GAAG,UAAU,CACtC,eAAe,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CACxC;CACF,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;KAC3B,CAAC,IAAI,MAAM,eAAe,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;CAC7D,GAAG;IACF,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,WAAW,CAAC;IAC7C,SAAS,IAAI,IAAI,CAAC;IAClB,EAAE,CAAC,MAAM,SAAS,cAAc,EAC9B,QAAQ,EAAE,sBAAsB,CAAC,MAAM,CAAC,EACxC,QAAQ,EAAE,sBAAsB,CAAC,MAAM,CAAC,GACvC,WAAW,CAAC;CAChB,CAAC"}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import type { AssistantClient, AssistantScopes, ScopesInput, ScopeField } from "./types";
|
|
2
|
+
import { type AssistantEvent, type AssistantEventCallback, type AssistantEventSelector } from "./EventContext";
|
|
2
3
|
/**
|
|
3
4
|
* Hook to mount and access root scopes
|
|
4
5
|
*/
|
|
5
|
-
export declare const useRootScopes: (rootScopes: ScopesInput) => {
|
|
6
|
+
export declare const useRootScopes: (rootScopes: ScopesInput, parent: AssistantClient) => {
|
|
6
7
|
scopes: {};
|
|
8
|
+
on: <TEvent extends AssistantEvent>(selector: AssistantEventSelector<TEvent>, callback: AssistantEventCallback<TEvent>) => () => void;
|
|
7
9
|
subscribe?: never;
|
|
8
10
|
flushSync?: never;
|
|
9
11
|
} | {
|
|
10
12
|
scopes: { [K in "ERROR: No scopes were defined"]: ScopeField<AssistantScopes[K]>; };
|
|
11
13
|
subscribe: (callback: () => void) => () => void;
|
|
12
14
|
flushSync: () => void;
|
|
15
|
+
on: <TEvent extends AssistantEvent>(selector: AssistantEventSelector<TEvent>, callback: AssistantEventCallback<TEvent>) => () => void;
|
|
13
16
|
};
|
|
14
17
|
/**
|
|
15
18
|
* Hook to mount and access derived scopes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAssistantClient.d.ts","sourceRoot":"","sources":["../src/useAssistantClient.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useAssistantClient.d.ts","sourceRoot":"","sources":["../src/useAssistantClient.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,WAAW,EACX,UAAU,EAGX,MAAM,SAAS,CAAC;AAIjB,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC5B,MAAM,gBAAgB,CAAC;AA2GxB;;GAEG;AACH,eAAO,MAAM,aAAa,GACxB,YAAY,WAAW,EACvB,QAAQ,eAAe;;SApDT,MAAM,SAAS,cAAc,YAC7B,sBAAsB,CAAC,MAAM,CAAC,YAC9B,sBAAsB,CAAC,MAAM,CAAC;;;;YAoBjC,GACF,CAAC,mCAAuB,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,GAC3D;0BACqB,MAAM,IAAI;;SAzBxB,MAAM,SAAS,cAAc,YAC7B,sBAAsB,CAAC,MAAM,CAAC,YAC9B,sBAAsB,CAAC,MAAM,CAAC;CAqD7C,CAAC;AAqEF;;GAEG;AACH,eAAO,MAAM,gBAAgB,GAC3B,eAAe,WAAW,EAC1B,cAAc,eAAe;;CAK9B,CAAC;AA0BF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,CAAC;AACtD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,eAAe,CAAC"}
|
|
@@ -6,11 +6,17 @@ import {
|
|
|
6
6
|
tapMemo,
|
|
7
7
|
tapResource,
|
|
8
8
|
tapResources,
|
|
9
|
-
tapEffectEvent
|
|
9
|
+
tapEffectEvent,
|
|
10
|
+
tapInlineResource
|
|
10
11
|
} from "@assistant-ui/tap";
|
|
11
12
|
import { asStore } from "./asStore.js";
|
|
12
13
|
import { useAssistantContextValue } from "./AssistantContext.js";
|
|
13
14
|
import { splitScopes } from "./utils/splitScopes.js";
|
|
15
|
+
import {
|
|
16
|
+
EventManager,
|
|
17
|
+
normalizeEventSelector
|
|
18
|
+
} from "./EventContext.js";
|
|
19
|
+
import { withStoreContextProvider } from "./StoreContext.js";
|
|
14
20
|
var RootScopeResource = resource(
|
|
15
21
|
({
|
|
16
22
|
scopeName,
|
|
@@ -32,52 +38,64 @@ var RootScopeResource = resource(
|
|
|
32
38
|
}, [scopeName, store]);
|
|
33
39
|
}
|
|
34
40
|
);
|
|
35
|
-
var RootScopesResource = resource(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
var RootScopesResource = resource(
|
|
42
|
+
({ scopes, parent }) => {
|
|
43
|
+
const events = tapInlineResource(EventManager());
|
|
44
|
+
const resultEntries = withStoreContextProvider(
|
|
45
|
+
{ events, parent },
|
|
46
|
+
() => tapResources(
|
|
47
|
+
Object.entries(scopes).map(
|
|
48
|
+
([scopeName, element]) => RootScopeResource(
|
|
49
|
+
{
|
|
50
|
+
scopeName,
|
|
51
|
+
element
|
|
52
|
+
},
|
|
53
|
+
{ key: scopeName }
|
|
54
|
+
)
|
|
55
|
+
)
|
|
44
56
|
)
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
resultEntries.map(([scopeName, { scopeFunction }]) => [
|
|
56
|
-
scopeName,
|
|
57
|
-
scopeFunction
|
|
58
|
-
])
|
|
59
|
-
),
|
|
60
|
-
subscribe: (callback) => {
|
|
61
|
-
const unsubscribes = resultEntries.map(([, { subscribe }]) => {
|
|
62
|
-
return subscribe(() => {
|
|
63
|
-
console.log("Callback called for");
|
|
64
|
-
callback();
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
return () => {
|
|
68
|
-
unsubscribes.forEach((unsubscribe) => unsubscribe());
|
|
57
|
+
);
|
|
58
|
+
const on = (selector, callback) => {
|
|
59
|
+
const { event } = normalizeEventSelector(selector);
|
|
60
|
+
return events.on(event, callback);
|
|
61
|
+
};
|
|
62
|
+
return tapMemo(() => {
|
|
63
|
+
if (resultEntries.length === 0) {
|
|
64
|
+
return {
|
|
65
|
+
scopes: {},
|
|
66
|
+
on
|
|
69
67
|
};
|
|
70
|
-
},
|
|
71
|
-
flushSync: () => {
|
|
72
|
-
resultEntries.forEach(([, { flushSync }]) => {
|
|
73
|
-
flushSync();
|
|
74
|
-
});
|
|
75
68
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
|
|
69
|
+
return {
|
|
70
|
+
scopes: Object.fromEntries(
|
|
71
|
+
resultEntries.map(([scopeName, { scopeFunction }]) => [
|
|
72
|
+
scopeName,
|
|
73
|
+
scopeFunction
|
|
74
|
+
])
|
|
75
|
+
),
|
|
76
|
+
subscribe: (callback) => {
|
|
77
|
+
const unsubscribes = resultEntries.map(([, { subscribe }]) => {
|
|
78
|
+
return subscribe(() => {
|
|
79
|
+
console.log("Callback called for");
|
|
80
|
+
callback();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
return () => {
|
|
84
|
+
unsubscribes.forEach((unsubscribe) => unsubscribe());
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
flushSync: () => {
|
|
88
|
+
resultEntries.forEach(([, { flushSync }]) => {
|
|
89
|
+
flushSync();
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
on
|
|
93
|
+
};
|
|
94
|
+
}, [...resultEntries, events]);
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
var useRootScopes = (rootScopes, parent) => {
|
|
98
|
+
return useResource(RootScopesResource({ scopes: rootScopes, parent }));
|
|
81
99
|
};
|
|
82
100
|
var DerivedScopeResource = resource(
|
|
83
101
|
({
|
|
@@ -126,7 +144,7 @@ var useDerivedScopes = (derivedScopes, parentClient) => {
|
|
|
126
144
|
var useExtendedAssistantClientImpl = (scopes) => {
|
|
127
145
|
const baseClient = useAssistantContextValue();
|
|
128
146
|
const { rootScopes, derivedScopes } = splitScopes(scopes);
|
|
129
|
-
const rootFields = useRootScopes(rootScopes);
|
|
147
|
+
const rootFields = useRootScopes(rootScopes, baseClient);
|
|
130
148
|
const derivedFields = useDerivedScopes(derivedScopes, baseClient);
|
|
131
149
|
return useMemo(() => {
|
|
132
150
|
return {
|
|
@@ -134,7 +152,8 @@ var useExtendedAssistantClientImpl = (scopes) => {
|
|
|
134
152
|
...rootFields.scopes,
|
|
135
153
|
...derivedFields,
|
|
136
154
|
subscribe: rootFields.subscribe ?? baseClient.subscribe,
|
|
137
|
-
flushSync: rootFields.flushSync ?? baseClient.flushSync
|
|
155
|
+
flushSync: rootFields.flushSync ?? baseClient.flushSync,
|
|
156
|
+
on: rootFields.on ?? baseClient.on
|
|
138
157
|
};
|
|
139
158
|
}, [baseClient, rootFields, derivedFields]);
|
|
140
159
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/useAssistantClient.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { useResource } from \"@assistant-ui/tap/react\";\nimport {\n resource,\n tapMemo,\n tapResource,\n tapResources,\n tapEffectEvent,\n ResourceElement,\n} from \"@assistant-ui/tap\";\nimport type {\n AssistantClient,\n AssistantScopes,\n ScopesInput,\n ScopeField,\n ScopeInput,\n DerivedScopeProps,\n} from \"./types\";\nimport { asStore } from \"./asStore\";\nimport { useAssistantContextValue } from \"./AssistantContext\";\nimport { splitScopes } from \"./utils/splitScopes\";\n\n/**\n * Resource for a single root scope\n * Returns a tuple of [scopeName, {scopeFunction, subscribe, flushSync}]\n */\nconst RootScopeResource = resource(\n <K extends keyof AssistantScopes>({\n scopeName,\n element,\n }: {\n scopeName: K;\n element: ScopeInput<AssistantScopes[K]>;\n }) => {\n const store = tapResource(asStore(element));\n\n return tapMemo(() => {\n const scopeFunction = (() => store.getState().api) as ScopeField<\n AssistantScopes[K]\n >;\n scopeFunction.source = \"root\";\n scopeFunction.query = {} as AssistantScopes[K][\"query\"];\n\n return [\n scopeName,\n {\n scopeFunction,\n subscribe: store.subscribe,\n flushSync: store.flushSync,\n },\n ] as const;\n }, [scopeName, store]);\n },\n);\n\n/**\n * Resource for all root scopes\n * Mounts each root scope and returns an object mapping scope names to their stores\n */\nconst RootScopesResource = resource((scopes: ScopesInput) => {\n const resultEntries = tapResources(\n Object.entries(scopes).map(([scopeName, element]) =>\n RootScopeResource(\n {\n scopeName: scopeName as keyof AssistantScopes,\n element: element as ScopeInput<\n AssistantScopes[keyof AssistantScopes]\n >,\n },\n { key: scopeName },\n ),\n ),\n );\n\n return tapMemo(() => {\n if (resultEntries.length === 0) {\n return {\n scopes: {},\n };\n }\n\n return {\n scopes: Object.fromEntries(\n resultEntries.map(([scopeName, { scopeFunction }]) => [\n scopeName,\n scopeFunction,\n ]),\n ) as {\n [K in keyof typeof scopes]: ScopeField<AssistantScopes[K]>;\n },\n subscribe: (callback: () => void) => {\n const unsubscribes = resultEntries.map(([, { subscribe }]) => {\n return subscribe(() => {\n console.log(\"Callback called for\");\n callback();\n });\n });\n return () => {\n unsubscribes.forEach((unsubscribe) => unsubscribe());\n };\n },\n flushSync: () => {\n resultEntries.forEach(([, { flushSync }]) => {\n flushSync();\n });\n },\n };\n }, [...resultEntries]);\n});\n\n/**\n * Hook to mount and access root scopes\n */\nexport const useRootScopes = (rootScopes: ScopesInput) => {\n return useResource(RootScopesResource(rootScopes));\n};\n\n/**\n * Resource for a single derived scope\n * Returns a tuple of [scopeName, scopeFunction] where scopeFunction has source and query\n */\nconst DerivedScopeResource = resource(\n <K extends keyof AssistantScopes>({\n scopeName,\n element,\n parentClient,\n }: {\n scopeName: K;\n element: ResourceElement<\n AssistantScopes[K],\n DerivedScopeProps<AssistantScopes[K]>\n >;\n parentClient: AssistantClient;\n }) => {\n const get = tapEffectEvent(element.props.get);\n const source = element.props.source;\n const query = element.props.query;\n return tapMemo(() => {\n const scopeFunction = (() => get(parentClient)) as ScopeField<\n AssistantScopes[K]\n >;\n scopeFunction.source = source;\n scopeFunction.query = query;\n\n return [scopeName, scopeFunction] as const;\n }, [scopeName, get, source, JSON.stringify(query), parentClient]);\n },\n);\n\n/**\n * Resource for all derived scopes\n * Builds stable scope functions with source and query metadata\n */\nconst DerivedScopesResource = resource(\n ({\n scopes,\n parentClient,\n }: {\n scopes: ScopesInput;\n parentClient: AssistantClient;\n }) => {\n const resultEntries = tapResources(\n Object.entries(scopes).map(([scopeName, element]) =>\n DerivedScopeResource(\n {\n scopeName: scopeName as keyof AssistantScopes,\n element: element as ScopeInput<\n AssistantScopes[keyof AssistantScopes]\n >,\n parentClient,\n },\n { key: scopeName },\n ),\n ),\n );\n\n return tapMemo(() => {\n return Object.fromEntries(resultEntries) as {\n [K in keyof typeof scopes]: ScopeField<AssistantScopes[K]>;\n };\n }, [...resultEntries]);\n },\n);\n\n/**\n * Hook to mount and access derived scopes\n */\nexport const useDerivedScopes = (\n derivedScopes: ScopesInput,\n parentClient: AssistantClient,\n) => {\n return useResource(\n DerivedScopesResource({ scopes: derivedScopes, parentClient }),\n );\n};\n\nconst useExtendedAssistantClientImpl = (\n scopes: ScopesInput,\n): AssistantClient => {\n const baseClient = useAssistantContextValue();\n const { rootScopes, derivedScopes } = splitScopes(scopes);\n\n // Mount the scopes to keep them alive\n const rootFields = useRootScopes(rootScopes);\n const derivedFields = useDerivedScopes(derivedScopes, baseClient);\n\n return useMemo(() => {\n // Merge base client with extended client\n // If baseClient is the default proxy, spreading it will be a no-op\n return {\n ...baseClient,\n ...rootFields.scopes,\n ...derivedFields,\n subscribe: rootFields.subscribe ?? baseClient.subscribe,\n flushSync: rootFields.flushSync ?? baseClient.flushSync,\n } as AssistantClient;\n }, [baseClient, rootFields, derivedFields]);\n};\n\n/**\n * Hook to access or extend the AssistantClient\n *\n * @example Without config - returns the client from context:\n * ```typescript\n * const client = useAssistantClient();\n * const fooState = client.foo.getState();\n * ```\n *\n * @example With config - creates a new client with additional scopes:\n * ```typescript\n * const client = useAssistantClient({\n * message: DerivedScope({\n * source: \"thread\",\n * query: { type: \"index\", index: 0 },\n * get: () => messageApi,\n * }),\n * });\n * ```\n */\nexport function useAssistantClient(): AssistantClient;\nexport function useAssistantClient(scopes: ScopesInput): AssistantClient;\nexport function useAssistantClient(scopes?: ScopesInput): AssistantClient {\n if (scopes) {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useExtendedAssistantClientImpl(scopes);\n } else {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useAssistantContextValue();\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/useAssistantClient.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { useResource } from \"@assistant-ui/tap/react\";\nimport {\n resource,\n tapMemo,\n tapResource,\n tapResources,\n tapEffectEvent,\n tapInlineResource,\n ResourceElement,\n} from \"@assistant-ui/tap\";\nimport type {\n AssistantClient,\n AssistantScopes,\n ScopesInput,\n ScopeField,\n ScopeInput,\n DerivedScopeProps,\n} from \"./types\";\nimport { asStore } from \"./asStore\";\nimport { useAssistantContextValue } from \"./AssistantContext\";\nimport { splitScopes } from \"./utils/splitScopes\";\nimport {\n EventManager,\n normalizeEventSelector,\n type AssistantEvent,\n type AssistantEventCallback,\n type AssistantEventSelector,\n} from \"./EventContext\";\nimport { withStoreContextProvider } from \"./StoreContext\";\n\n/**\n * Resource for a single root scope\n * Returns a tuple of [scopeName, {scopeFunction, subscribe, flushSync}]\n */\nconst RootScopeResource = resource(\n <K extends keyof AssistantScopes>({\n scopeName,\n element,\n }: {\n scopeName: K;\n element: ScopeInput<AssistantScopes[K]>;\n }) => {\n const store = tapResource(asStore(element));\n\n return tapMemo(() => {\n const scopeFunction = (() => store.getState().api) as ScopeField<\n AssistantScopes[K]\n >;\n scopeFunction.source = \"root\";\n scopeFunction.query = {} as AssistantScopes[K][\"query\"];\n\n return [\n scopeName,\n {\n scopeFunction,\n subscribe: store.subscribe,\n flushSync: store.flushSync,\n },\n ] as const;\n }, [scopeName, store]);\n },\n);\n\n/**\n * Resource for all root scopes\n * Mounts each root scope and returns an object mapping scope names to their stores\n */\nconst RootScopesResource = resource(\n ({ scopes, parent }: { scopes: ScopesInput; parent: AssistantClient }) => {\n const events = tapInlineResource(EventManager());\n\n const resultEntries = withStoreContextProvider({ events, parent }, () =>\n tapResources(\n Object.entries(scopes).map(([scopeName, element]) =>\n RootScopeResource(\n {\n scopeName: scopeName as keyof AssistantScopes,\n element: element as ScopeInput<\n AssistantScopes[keyof AssistantScopes]\n >,\n },\n { key: scopeName },\n ),\n ),\n ),\n );\n\n const on = <TEvent extends AssistantEvent>(\n selector: AssistantEventSelector<TEvent>,\n callback: AssistantEventCallback<TEvent>,\n ) => {\n const { event } = normalizeEventSelector(selector);\n return events.on(event, callback);\n };\n\n return tapMemo(() => {\n if (resultEntries.length === 0) {\n return {\n scopes: {},\n on,\n };\n }\n\n return {\n scopes: Object.fromEntries(\n resultEntries.map(([scopeName, { scopeFunction }]) => [\n scopeName,\n scopeFunction,\n ]),\n ) as {\n [K in keyof typeof scopes]: ScopeField<AssistantScopes[K]>;\n },\n subscribe: (callback: () => void) => {\n const unsubscribes = resultEntries.map(([, { subscribe }]) => {\n return subscribe(() => {\n console.log(\"Callback called for\");\n callback();\n });\n });\n return () => {\n unsubscribes.forEach((unsubscribe) => unsubscribe());\n };\n },\n flushSync: () => {\n resultEntries.forEach(([, { flushSync }]) => {\n flushSync();\n });\n },\n on,\n };\n }, [...resultEntries, events]);\n },\n);\n\n/**\n * Hook to mount and access root scopes\n */\nexport const useRootScopes = (\n rootScopes: ScopesInput,\n parent: AssistantClient,\n) => {\n return useResource(RootScopesResource({ scopes: rootScopes, parent }));\n};\n\n/**\n * Resource for a single derived scope\n * Returns a tuple of [scopeName, scopeFunction] where scopeFunction has source and query\n */\nconst DerivedScopeResource = resource(\n <K extends keyof AssistantScopes>({\n scopeName,\n element,\n parentClient,\n }: {\n scopeName: K;\n element: ResourceElement<\n AssistantScopes[K],\n DerivedScopeProps<AssistantScopes[K]>\n >;\n parentClient: AssistantClient;\n }) => {\n const get = tapEffectEvent(element.props.get);\n const source = element.props.source;\n const query = element.props.query;\n return tapMemo(() => {\n const scopeFunction = (() => get(parentClient)) as ScopeField<\n AssistantScopes[K]\n >;\n scopeFunction.source = source;\n scopeFunction.query = query;\n\n return [scopeName, scopeFunction] as const;\n }, [scopeName, get, source, JSON.stringify(query), parentClient]);\n },\n);\n\n/**\n * Resource for all derived scopes\n * Builds stable scope functions with source and query metadata\n */\nconst DerivedScopesResource = resource(\n ({\n scopes,\n parentClient,\n }: {\n scopes: ScopesInput;\n parentClient: AssistantClient;\n }) => {\n const resultEntries = tapResources(\n Object.entries(scopes).map(([scopeName, element]) =>\n DerivedScopeResource(\n {\n scopeName: scopeName as keyof AssistantScopes,\n element: element as ScopeInput<\n AssistantScopes[keyof AssistantScopes]\n >,\n parentClient,\n },\n { key: scopeName },\n ),\n ),\n );\n\n return tapMemo(() => {\n return Object.fromEntries(resultEntries) as {\n [K in keyof typeof scopes]: ScopeField<AssistantScopes[K]>;\n };\n }, [...resultEntries]);\n },\n);\n\n/**\n * Hook to mount and access derived scopes\n */\nexport const useDerivedScopes = (\n derivedScopes: ScopesInput,\n parentClient: AssistantClient,\n) => {\n return useResource(\n DerivedScopesResource({ scopes: derivedScopes, parentClient }),\n );\n};\n\nconst useExtendedAssistantClientImpl = (\n scopes: ScopesInput,\n): AssistantClient => {\n const baseClient = useAssistantContextValue();\n const { rootScopes, derivedScopes } = splitScopes(scopes);\n\n // Mount the scopes to keep them alive\n const rootFields = useRootScopes(rootScopes, baseClient);\n const derivedFields = useDerivedScopes(derivedScopes, baseClient);\n\n return useMemo(() => {\n // Merge base client with extended client\n // If baseClient is the default proxy, spreading it will be a no-op\n return {\n ...baseClient,\n ...rootFields.scopes,\n ...derivedFields,\n subscribe: rootFields.subscribe ?? baseClient.subscribe,\n flushSync: rootFields.flushSync ?? baseClient.flushSync,\n on: rootFields.on ?? baseClient.on,\n } as AssistantClient;\n }, [baseClient, rootFields, derivedFields]);\n};\n\n/**\n * Hook to access or extend the AssistantClient\n *\n * @example Without config - returns the client from context:\n * ```typescript\n * const client = useAssistantClient();\n * const fooState = client.foo.getState();\n * ```\n *\n * @example With config - creates a new client with additional scopes:\n * ```typescript\n * const client = useAssistantClient({\n * message: DerivedScope({\n * source: \"thread\",\n * query: { type: \"index\", index: 0 },\n * get: () => messageApi,\n * }),\n * });\n * ```\n */\nexport function useAssistantClient(): AssistantClient;\nexport function useAssistantClient(scopes: ScopesInput): AssistantClient;\nexport function useAssistantClient(scopes?: ScopesInput): AssistantClient {\n if (scopes) {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useExtendedAssistantClientImpl(scopes);\n } else {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useAssistantContextValue();\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { AssistantEvent, AssistantEventCallback, AssistantEventSelector } from "./EventContext";
|
|
2
|
+
export declare const useAssistantEvent: <TEvent extends AssistantEvent>(selector: AssistantEventSelector<TEvent>, callback: AssistantEventCallback<TEvent>) => void;
|
|
3
|
+
//# sourceMappingURL=useAssistantEvent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAssistantEvent.d.ts","sourceRoot":"","sources":["../src/useAssistantEvent.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,cAAc,EACd,sBAAsB,EACtB,sBAAsB,EACvB,MAAM,gBAAgB,CAAC;AAGxB,eAAO,MAAM,iBAAiB,GAAI,MAAM,SAAS,cAAc,EAC7D,UAAU,sBAAsB,CAAC,MAAM,CAAC,EACxC,UAAU,sBAAsB,CAAC,MAAM,CAAC,SAUzC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// src/useAssistantEvent.ts
|
|
2
|
+
import { useEffect, useEffectEvent } from "react";
|
|
3
|
+
import { useAssistantClient } from "./useAssistantClient.js";
|
|
4
|
+
import { normalizeEventSelector } from "./EventContext.js";
|
|
5
|
+
var useAssistantEvent = (selector, callback) => {
|
|
6
|
+
const client = useAssistantClient();
|
|
7
|
+
const callbackRef = useEffectEvent(callback);
|
|
8
|
+
const { scope, event } = normalizeEventSelector(selector);
|
|
9
|
+
useEffect(
|
|
10
|
+
() => client.on({ scope, event }, callbackRef),
|
|
11
|
+
[client, scope, event]
|
|
12
|
+
);
|
|
13
|
+
};
|
|
14
|
+
export {
|
|
15
|
+
useAssistantEvent
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=useAssistantEvent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/useAssistantEvent.ts"],"sourcesContent":["import { useEffect, useEffectEvent } from \"react\";\nimport { useAssistantClient } from \"./useAssistantClient\";\nimport type {\n AssistantEvent,\n AssistantEventCallback,\n AssistantEventSelector,\n} from \"./EventContext\";\nimport { normalizeEventSelector } from \"./EventContext\";\n\nexport const useAssistantEvent = <TEvent extends AssistantEvent>(\n selector: AssistantEventSelector<TEvent>,\n callback: AssistantEventCallback<TEvent>,\n) => {\n const client = useAssistantClient();\n const callbackRef = useEffectEvent(callback);\n\n const { scope, event } = normalizeEventSelector(selector);\n useEffect(\n () => client.on({ scope, event }, callbackRef),\n [client, scope, event],\n );\n};\n"],"mappings":";AAAA,SAAS,WAAW,sBAAsB;AAC1C,SAAS,0BAA0B;AAMnC,SAAS,8BAA8B;AAEhC,IAAM,oBAAoB,CAC/B,UACA,aACG;AACH,QAAM,SAAS,mBAAmB;AAClC,QAAM,cAAc,eAAe,QAAQ;AAE3C,QAAM,EAAE,OAAO,MAAM,IAAI,uBAAuB,QAAQ;AACxD;AAAA,IACE,MAAM,OAAO,GAAG,EAAE,OAAO,MAAM,GAAG,WAAW;AAAA,IAC7C,CAAC,QAAQ,OAAO,KAAK;AAAA,EACvB;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/store",
|
|
3
3
|
"description": "Tap-based state management for @assistant-ui",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.1",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"exports": {
|
|
@@ -20,14 +20,14 @@
|
|
|
20
20
|
],
|
|
21
21
|
"sideEffects": false,
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@assistant-ui/tap": "0.3.
|
|
23
|
+
"@assistant-ui/tap": "0.3.1"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"react": "^18.0.0 || ^19.0.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/node": "^24.10.1",
|
|
30
|
-
"@types/react": "19.2.
|
|
30
|
+
"@types/react": "19.2.7",
|
|
31
31
|
"tsx": "^4.20.6",
|
|
32
32
|
"@assistant-ui/x-buildutils": "0.0.1"
|
|
33
33
|
},
|
package/src/AssistantContext.tsx
CHANGED
|
@@ -23,7 +23,7 @@ export const AssistantContext = createContext<AssistantClient>(
|
|
|
23
23
|
get(_, prop: string) {
|
|
24
24
|
// Allow access to subscribe and flushSync without error
|
|
25
25
|
if (prop === "subscribe") return NO_OP_SUBSCRIBE;
|
|
26
|
-
|
|
26
|
+
if (prop === "on") return NO_OP_SUBSCRIBE;
|
|
27
27
|
if (prop === "flushSync") return NO_OP_FLUSH_SYNC;
|
|
28
28
|
|
|
29
29
|
// If this is a registered scope, return a function that errors when called or accessed
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { resource, tapMemo } from "@assistant-ui/tap";
|
|
2
|
+
import type { Unsubscribe } from "./types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Module augmentation interface for custom events.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* declare module "@assistant-ui/store" {
|
|
10
|
+
* interface AssistantEventRegistry {
|
|
11
|
+
* "thread.run-start": { threadId: string };
|
|
12
|
+
* "custom.my-event": { data: string };
|
|
13
|
+
* }
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
18
|
+
export interface AssistantEventRegistry {}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Module augmentation interface for event scope configuration.
|
|
22
|
+
* Maps event sources to their parent scopes.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* declare module "@assistant-ui/store" {
|
|
27
|
+
* interface AssistantEventScopeConfig {
|
|
28
|
+
* composer: "thread" | "message";
|
|
29
|
+
* thread: never;
|
|
30
|
+
* }
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
35
|
+
export interface AssistantEventScopeConfig {}
|
|
36
|
+
|
|
37
|
+
export type AssistantEventMap = AssistantEventRegistry & {
|
|
38
|
+
// Catch-all
|
|
39
|
+
"*": {
|
|
40
|
+
[K in Exclude<keyof AssistantEventRegistry, "*">]: {
|
|
41
|
+
event: K;
|
|
42
|
+
payload: AssistantEventRegistry[K];
|
|
43
|
+
};
|
|
44
|
+
}[Exclude<keyof AssistantEventRegistry, "*">];
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type AssistantEvent = keyof AssistantEventMap;
|
|
48
|
+
|
|
49
|
+
export type EventSource<T extends AssistantEvent = AssistantEvent> =
|
|
50
|
+
T extends `${infer Source}.${string}` ? Source : never;
|
|
51
|
+
|
|
52
|
+
export type SourceByScope<TScope extends AssistantEventScope<AssistantEvent>> =
|
|
53
|
+
| (TScope extends "*" ? EventSource : never)
|
|
54
|
+
| (TScope extends keyof AssistantEventScopeConfig ? TScope : never)
|
|
55
|
+
| {
|
|
56
|
+
[K in keyof AssistantEventScopeConfig]: TScope extends AssistantEventScopeConfig[K]
|
|
57
|
+
? K
|
|
58
|
+
: never;
|
|
59
|
+
}[keyof AssistantEventScopeConfig];
|
|
60
|
+
|
|
61
|
+
export type AssistantEventScope<TEvent extends AssistantEvent> =
|
|
62
|
+
| "*"
|
|
63
|
+
| EventSource<TEvent>
|
|
64
|
+
| (EventSource<TEvent> extends keyof AssistantEventScopeConfig
|
|
65
|
+
? AssistantEventScopeConfig[EventSource<TEvent>]
|
|
66
|
+
: never);
|
|
67
|
+
|
|
68
|
+
export type AssistantEventSelector<TEvent extends AssistantEvent> =
|
|
69
|
+
| TEvent
|
|
70
|
+
| {
|
|
71
|
+
scope: AssistantEventScope<TEvent>;
|
|
72
|
+
event: TEvent;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const normalizeEventSelector = <TEvent extends AssistantEvent>(
|
|
76
|
+
selector: AssistantEventSelector<TEvent>,
|
|
77
|
+
) => {
|
|
78
|
+
if (typeof selector === "string") {
|
|
79
|
+
const source = selector.split(".")[0] as AssistantEventScope<TEvent>;
|
|
80
|
+
return {
|
|
81
|
+
scope: source,
|
|
82
|
+
event: selector,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
scope: selector.scope,
|
|
88
|
+
event: selector.event,
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const checkEventScope = <
|
|
93
|
+
TEvent extends AssistantEvent,
|
|
94
|
+
TExpectedScope extends AssistantEventScope<AssistantEvent>,
|
|
95
|
+
>(
|
|
96
|
+
expectedScope: TExpectedScope,
|
|
97
|
+
scope: AssistantEventScope<TEvent>,
|
|
98
|
+
_event: TEvent,
|
|
99
|
+
): _event is Extract<TEvent, `${SourceByScope<TExpectedScope>}.${string}`> => {
|
|
100
|
+
return scope === expectedScope;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export type AssistantEventCallback<TEvent extends AssistantEvent> = (
|
|
104
|
+
payload: AssistantEventMap[TEvent],
|
|
105
|
+
) => void;
|
|
106
|
+
|
|
107
|
+
export type EventManager = {
|
|
108
|
+
on<TEvent extends AssistantEvent>(
|
|
109
|
+
event: TEvent,
|
|
110
|
+
callback: AssistantEventCallback<TEvent>,
|
|
111
|
+
): Unsubscribe;
|
|
112
|
+
emit<TEvent extends Exclude<AssistantEvent, "*">>(
|
|
113
|
+
event: TEvent,
|
|
114
|
+
payload: AssistantEventMap[TEvent],
|
|
115
|
+
): void;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
type ListenerMap = Omit<
|
|
119
|
+
Map<AssistantEvent, Set<AssistantEventCallback<AssistantEvent>>>,
|
|
120
|
+
"get" | "set"
|
|
121
|
+
> & {
|
|
122
|
+
get<TEvent extends AssistantEvent>(
|
|
123
|
+
event: TEvent,
|
|
124
|
+
): Set<AssistantEventCallback<TEvent>> | undefined;
|
|
125
|
+
set<TEvent extends AssistantEvent>(
|
|
126
|
+
event: TEvent,
|
|
127
|
+
value: Set<AssistantEventCallback<TEvent>>,
|
|
128
|
+
): void;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export const EventManager = resource(() => {
|
|
132
|
+
const events = tapMemo(() => {
|
|
133
|
+
const listeners: ListenerMap = new Map();
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
on: (event, callback) => {
|
|
137
|
+
if (!listeners.has(event)) {
|
|
138
|
+
listeners.set(event, new Set());
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const eventListeners = listeners.get(event)!;
|
|
142
|
+
eventListeners.add(callback);
|
|
143
|
+
|
|
144
|
+
return () => {
|
|
145
|
+
eventListeners.delete(callback);
|
|
146
|
+
if (eventListeners.size === 0) {
|
|
147
|
+
listeners.delete(event);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
emit: (event, payload) => {
|
|
153
|
+
const eventListeners = listeners.get(event);
|
|
154
|
+
const wildcardListeners = listeners.get("*");
|
|
155
|
+
|
|
156
|
+
if (!eventListeners && !wildcardListeners) return;
|
|
157
|
+
|
|
158
|
+
// make sure state updates flush
|
|
159
|
+
queueMicrotask(() => {
|
|
160
|
+
// Emit to specific event listeners
|
|
161
|
+
if (eventListeners) {
|
|
162
|
+
for (const callback of eventListeners) {
|
|
163
|
+
callback(payload);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Emit to wildcard listeners
|
|
168
|
+
if (wildcardListeners) {
|
|
169
|
+
for (const callback of wildcardListeners) {
|
|
170
|
+
(
|
|
171
|
+
callback as (payload: {
|
|
172
|
+
event: typeof event;
|
|
173
|
+
payload: typeof payload;
|
|
174
|
+
}) => void
|
|
175
|
+
)({ event, payload });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
},
|
|
180
|
+
} satisfies EventManager;
|
|
181
|
+
}, []);
|
|
182
|
+
|
|
183
|
+
return events;
|
|
184
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
tapContext,
|
|
4
|
+
withContextProvider,
|
|
5
|
+
} from "@assistant-ui/tap";
|
|
6
|
+
import type { EventManager } from "./EventContext";
|
|
7
|
+
import type { AssistantClient } from "./types";
|
|
8
|
+
|
|
9
|
+
export type StoreContextValue = {
|
|
10
|
+
events: EventManager;
|
|
11
|
+
parent: AssistantClient;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const StoreContext = createContext<StoreContextValue | null>(null);
|
|
15
|
+
|
|
16
|
+
export const withStoreContextProvider = <TResult>(
|
|
17
|
+
value: StoreContextValue,
|
|
18
|
+
fn: () => TResult,
|
|
19
|
+
) => {
|
|
20
|
+
return withContextProvider(StoreContext, value, fn);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const tapStoreContext = () => {
|
|
24
|
+
const ctx = tapContext(StoreContext);
|
|
25
|
+
if (!ctx) throw new Error("Store context is not available");
|
|
26
|
+
|
|
27
|
+
return ctx;
|
|
28
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -11,3 +11,21 @@ export type { TapStoreListConfig } from "./tapStoreList";
|
|
|
11
11
|
export { registerAssistantScope } from "./ScopeRegistry";
|
|
12
12
|
|
|
13
13
|
export type { AssistantScopeRegistry } from "./types";
|
|
14
|
+
|
|
15
|
+
// Events & Store Context
|
|
16
|
+
export { tapStoreContext } from "./StoreContext";
|
|
17
|
+
export type { StoreContextValue } from "./StoreContext";
|
|
18
|
+
export { useAssistantEvent } from "./useAssistantEvent";
|
|
19
|
+
export { normalizeEventSelector, checkEventScope } from "./EventContext";
|
|
20
|
+
export type {
|
|
21
|
+
AssistantEvent,
|
|
22
|
+
AssistantEventRegistry,
|
|
23
|
+
AssistantEventScopeConfig,
|
|
24
|
+
AssistantEventMap,
|
|
25
|
+
AssistantEventScope,
|
|
26
|
+
AssistantEventSelector,
|
|
27
|
+
AssistantEventCallback,
|
|
28
|
+
EventSource,
|
|
29
|
+
SourceByScope,
|
|
30
|
+
EventManager,
|
|
31
|
+
} from "./EventContext";
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { ResourceElement } from "@assistant-ui/tap";
|
|
2
|
+
import type {
|
|
3
|
+
AssistantEvent,
|
|
4
|
+
AssistantEventCallback,
|
|
5
|
+
AssistantEventSelector,
|
|
6
|
+
} from "./EventContext";
|
|
2
7
|
|
|
3
8
|
/**
|
|
4
9
|
* Definition of a scope in the assistant client (internal type)
|
|
@@ -117,4 +122,8 @@ export type AssistantClient = {
|
|
|
117
122
|
} & {
|
|
118
123
|
subscribe(listener: () => void): Unsubscribe;
|
|
119
124
|
flushSync(): void;
|
|
125
|
+
on<TEvent extends AssistantEvent>(
|
|
126
|
+
selector: AssistantEventSelector<TEvent>,
|
|
127
|
+
callback: AssistantEventCallback<TEvent>,
|
|
128
|
+
): Unsubscribe;
|
|
120
129
|
};
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
tapResource,
|
|
7
7
|
tapResources,
|
|
8
8
|
tapEffectEvent,
|
|
9
|
+
tapInlineResource,
|
|
9
10
|
ResourceElement,
|
|
10
11
|
} from "@assistant-ui/tap";
|
|
11
12
|
import type {
|
|
@@ -19,6 +20,14 @@ import type {
|
|
|
19
20
|
import { asStore } from "./asStore";
|
|
20
21
|
import { useAssistantContextValue } from "./AssistantContext";
|
|
21
22
|
import { splitScopes } from "./utils/splitScopes";
|
|
23
|
+
import {
|
|
24
|
+
EventManager,
|
|
25
|
+
normalizeEventSelector,
|
|
26
|
+
type AssistantEvent,
|
|
27
|
+
type AssistantEventCallback,
|
|
28
|
+
type AssistantEventSelector,
|
|
29
|
+
} from "./EventContext";
|
|
30
|
+
import { withStoreContextProvider } from "./StoreContext";
|
|
22
31
|
|
|
23
32
|
/**
|
|
24
33
|
* Resource for a single root scope
|
|
@@ -57,62 +66,81 @@ const RootScopeResource = resource(
|
|
|
57
66
|
* Resource for all root scopes
|
|
58
67
|
* Mounts each root scope and returns an object mapping scope names to their stores
|
|
59
68
|
*/
|
|
60
|
-
const RootScopesResource = resource(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
const RootScopesResource = resource(
|
|
70
|
+
({ scopes, parent }: { scopes: ScopesInput; parent: AssistantClient }) => {
|
|
71
|
+
const events = tapInlineResource(EventManager());
|
|
72
|
+
|
|
73
|
+
const resultEntries = withStoreContextProvider({ events, parent }, () =>
|
|
74
|
+
tapResources(
|
|
75
|
+
Object.entries(scopes).map(([scopeName, element]) =>
|
|
76
|
+
RootScopeResource(
|
|
77
|
+
{
|
|
78
|
+
scopeName: scopeName as keyof AssistantScopes,
|
|
79
|
+
element: element as ScopeInput<
|
|
80
|
+
AssistantScopes[keyof AssistantScopes]
|
|
81
|
+
>,
|
|
82
|
+
},
|
|
83
|
+
{ key: scopeName },
|
|
84
|
+
),
|
|
85
|
+
),
|
|
71
86
|
),
|
|
72
|
-
)
|
|
73
|
-
);
|
|
87
|
+
);
|
|
74
88
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
};
|
|
80
|
-
|
|
89
|
+
const on = <TEvent extends AssistantEvent>(
|
|
90
|
+
selector: AssistantEventSelector<TEvent>,
|
|
91
|
+
callback: AssistantEventCallback<TEvent>,
|
|
92
|
+
) => {
|
|
93
|
+
const { event } = normalizeEventSelector(selector);
|
|
94
|
+
return events.on(event, callback);
|
|
95
|
+
};
|
|
81
96
|
|
|
82
|
-
return {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
]),
|
|
88
|
-
) as {
|
|
89
|
-
[K in keyof typeof scopes]: ScopeField<AssistantScopes[K]>;
|
|
90
|
-
},
|
|
91
|
-
subscribe: (callback: () => void) => {
|
|
92
|
-
const unsubscribes = resultEntries.map(([, { subscribe }]) => {
|
|
93
|
-
return subscribe(() => {
|
|
94
|
-
console.log("Callback called for");
|
|
95
|
-
callback();
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
return () => {
|
|
99
|
-
unsubscribes.forEach((unsubscribe) => unsubscribe());
|
|
97
|
+
return tapMemo(() => {
|
|
98
|
+
if (resultEntries.length === 0) {
|
|
99
|
+
return {
|
|
100
|
+
scopes: {},
|
|
101
|
+
on,
|
|
100
102
|
};
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
scopes: Object.fromEntries(
|
|
107
|
+
resultEntries.map(([scopeName, { scopeFunction }]) => [
|
|
108
|
+
scopeName,
|
|
109
|
+
scopeFunction,
|
|
110
|
+
]),
|
|
111
|
+
) as {
|
|
112
|
+
[K in keyof typeof scopes]: ScopeField<AssistantScopes[K]>;
|
|
113
|
+
},
|
|
114
|
+
subscribe: (callback: () => void) => {
|
|
115
|
+
const unsubscribes = resultEntries.map(([, { subscribe }]) => {
|
|
116
|
+
return subscribe(() => {
|
|
117
|
+
console.log("Callback called for");
|
|
118
|
+
callback();
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
return () => {
|
|
122
|
+
unsubscribes.forEach((unsubscribe) => unsubscribe());
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
flushSync: () => {
|
|
126
|
+
resultEntries.forEach(([, { flushSync }]) => {
|
|
127
|
+
flushSync();
|
|
128
|
+
});
|
|
129
|
+
},
|
|
130
|
+
on,
|
|
131
|
+
};
|
|
132
|
+
}, [...resultEntries, events]);
|
|
133
|
+
},
|
|
134
|
+
);
|
|
110
135
|
|
|
111
136
|
/**
|
|
112
137
|
* Hook to mount and access root scopes
|
|
113
138
|
*/
|
|
114
|
-
export const useRootScopes = (
|
|
115
|
-
|
|
139
|
+
export const useRootScopes = (
|
|
140
|
+
rootScopes: ScopesInput,
|
|
141
|
+
parent: AssistantClient,
|
|
142
|
+
) => {
|
|
143
|
+
return useResource(RootScopesResource({ scopes: rootScopes, parent }));
|
|
116
144
|
};
|
|
117
145
|
|
|
118
146
|
/**
|
|
@@ -201,7 +229,7 @@ const useExtendedAssistantClientImpl = (
|
|
|
201
229
|
const { rootScopes, derivedScopes } = splitScopes(scopes);
|
|
202
230
|
|
|
203
231
|
// Mount the scopes to keep them alive
|
|
204
|
-
const rootFields = useRootScopes(rootScopes);
|
|
232
|
+
const rootFields = useRootScopes(rootScopes, baseClient);
|
|
205
233
|
const derivedFields = useDerivedScopes(derivedScopes, baseClient);
|
|
206
234
|
|
|
207
235
|
return useMemo(() => {
|
|
@@ -213,6 +241,7 @@ const useExtendedAssistantClientImpl = (
|
|
|
213
241
|
...derivedFields,
|
|
214
242
|
subscribe: rootFields.subscribe ?? baseClient.subscribe,
|
|
215
243
|
flushSync: rootFields.flushSync ?? baseClient.flushSync,
|
|
244
|
+
on: rootFields.on ?? baseClient.on,
|
|
216
245
|
} as AssistantClient;
|
|
217
246
|
}, [baseClient, rootFields, derivedFields]);
|
|
218
247
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useEffect, useEffectEvent } from "react";
|
|
2
|
+
import { useAssistantClient } from "./useAssistantClient";
|
|
3
|
+
import type {
|
|
4
|
+
AssistantEvent,
|
|
5
|
+
AssistantEventCallback,
|
|
6
|
+
AssistantEventSelector,
|
|
7
|
+
} from "./EventContext";
|
|
8
|
+
import { normalizeEventSelector } from "./EventContext";
|
|
9
|
+
|
|
10
|
+
export const useAssistantEvent = <TEvent extends AssistantEvent>(
|
|
11
|
+
selector: AssistantEventSelector<TEvent>,
|
|
12
|
+
callback: AssistantEventCallback<TEvent>,
|
|
13
|
+
) => {
|
|
14
|
+
const client = useAssistantClient();
|
|
15
|
+
const callbackRef = useEffectEvent(callback);
|
|
16
|
+
|
|
17
|
+
const { scope, event } = normalizeEventSelector(selector);
|
|
18
|
+
useEffect(
|
|
19
|
+
() => client.on({ scope, event }, callbackRef),
|
|
20
|
+
[client, scope, event],
|
|
21
|
+
);
|
|
22
|
+
};
|