@assistant-ui/store 0.0.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/LICENSE +21 -0
- package/README.md +295 -0
- package/dist/AssistantContext.d.ts +22 -0
- package/dist/AssistantContext.d.ts.map +1 -0
- package/dist/AssistantContext.js +44 -0
- package/dist/AssistantContext.js.map +1 -0
- package/dist/DerivedScope.d.ts +18 -0
- package/dist/DerivedScope.d.ts.map +1 -0
- package/dist/DerivedScope.js +11 -0
- package/dist/DerivedScope.js.map +1 -0
- package/dist/ScopeRegistry.d.ts +41 -0
- package/dist/ScopeRegistry.d.ts.map +1 -0
- package/dist/ScopeRegistry.js +17 -0
- package/dist/ScopeRegistry.js.map +1 -0
- package/dist/asStore.d.ts +20 -0
- package/dist/asStore.d.ts.map +1 -0
- package/dist/asStore.js +23 -0
- package/dist/asStore.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/tapApi.d.ts +36 -0
- package/dist/tapApi.d.ts.map +1 -0
- package/dist/tapApi.js +52 -0
- package/dist/tapApi.js.map +1 -0
- package/dist/tapLookupResources.d.ts +44 -0
- package/dist/tapLookupResources.d.ts.map +1 -0
- package/dist/tapLookupResources.js +21 -0
- package/dist/tapLookupResources.js.map +1 -0
- package/dist/tapStoreList.d.ts +76 -0
- package/dist/tapStoreList.d.ts.map +1 -0
- package/dist/tapStoreList.js +46 -0
- package/dist/tapStoreList.js.map +1 -0
- package/dist/types.d.ts +86 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/dist/useAssistantClient.d.ts +42 -0
- package/dist/useAssistantClient.d.ts.map +1 -0
- package/dist/useAssistantClient.js +153 -0
- package/dist/useAssistantClient.js.map +1 -0
- package/dist/useAssistantState.d.ts +18 -0
- package/dist/useAssistantState.d.ts.map +1 -0
- package/dist/useAssistantState.js +53 -0
- package/dist/useAssistantState.js.map +1 -0
- package/dist/utils/splitScopes.d.ts +24 -0
- package/dist/utils/splitScopes.d.ts.map +1 -0
- package/dist/utils/splitScopes.js +18 -0
- package/dist/utils/splitScopes.js.map +1 -0
- package/package.json +50 -0
- package/src/AssistantContext.tsx +64 -0
- package/src/DerivedScope.ts +21 -0
- package/src/ScopeRegistry.ts +58 -0
- package/src/asStore.ts +40 -0
- package/src/index.ts +13 -0
- package/src/tapApi.ts +91 -0
- package/src/tapLookupResources.ts +62 -0
- package/src/tapStoreList.ts +133 -0
- package/src/types.ts +120 -0
- package/src/useAssistantClient.tsx +250 -0
- package/src/useAssistantState.tsx +80 -0
- package/src/utils/splitScopes.ts +38 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { ResourceElement } from "@assistant-ui/tap";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Definition of a scope in the assistant client (internal type)
|
|
5
|
+
* @template TValue - The API type (must include getState() and any actions)
|
|
6
|
+
* @template TSource - The parent scope name (or "root" for top-level scopes)
|
|
7
|
+
* @template TQuery - The query parameters needed to access this scope from its source
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export type ScopeDefinition<
|
|
11
|
+
TValue = any,
|
|
12
|
+
TSource extends string | "root" = any,
|
|
13
|
+
TQuery = any,
|
|
14
|
+
> = {
|
|
15
|
+
value: TValue;
|
|
16
|
+
source: TSource;
|
|
17
|
+
query: TQuery;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Module augmentation interface for assistant-ui store type extensions.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* declare module "@assistant-ui/store" {
|
|
26
|
+
* interface AssistantScopeRegistry {
|
|
27
|
+
* foo: {
|
|
28
|
+
* value: { getState: () => { bar: string }; updateBar: (bar: string) => void };
|
|
29
|
+
* source: "root";
|
|
30
|
+
* query: Record<string, never>;
|
|
31
|
+
* };
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
37
|
+
export interface AssistantScopeRegistry {}
|
|
38
|
+
|
|
39
|
+
export type AssistantScopes = keyof AssistantScopeRegistry extends never
|
|
40
|
+
? Record<"ERROR: No scopes were defined", ScopeDefinition>
|
|
41
|
+
: { [K in keyof AssistantScopeRegistry]: AssistantScopeRegistry[K] };
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Helper type to extract the value type from a scope definition
|
|
45
|
+
*/
|
|
46
|
+
export type ScopeValue<T extends ScopeDefinition> = T["value"];
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Helper type to extract the source type from a scope definition
|
|
50
|
+
*/
|
|
51
|
+
export type ScopeSource<T extends ScopeDefinition> = T["source"];
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Helper type to extract the query type from a scope definition
|
|
55
|
+
*/
|
|
56
|
+
export type ScopeQuery<T extends ScopeDefinition> = T["query"];
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Type for a scope field - a function that returns the current API value,
|
|
60
|
+
* with source and query metadata attached
|
|
61
|
+
*/
|
|
62
|
+
export type ScopeField<T extends ScopeDefinition> = (() => ScopeValue<T>) &
|
|
63
|
+
(
|
|
64
|
+
| {
|
|
65
|
+
source: ScopeSource<T>;
|
|
66
|
+
query: ScopeQuery<T>;
|
|
67
|
+
}
|
|
68
|
+
| {
|
|
69
|
+
source: null;
|
|
70
|
+
query: null;
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Props passed to a derived scope resource element
|
|
76
|
+
*/
|
|
77
|
+
export type DerivedScopeProps<T extends ScopeDefinition> = {
|
|
78
|
+
get: (parent: AssistantClient) => ScopeValue<T>;
|
|
79
|
+
source: ScopeSource<T>;
|
|
80
|
+
query: ScopeQuery<T>;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Input type for scope definitions - ResourceElement that returns the API value
|
|
85
|
+
* Can optionally include source/query metadata via DerivedScope
|
|
86
|
+
*/
|
|
87
|
+
export type ScopeInput<T extends ScopeDefinition> = ResourceElement<{
|
|
88
|
+
api: ScopeValue<T>;
|
|
89
|
+
}>;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Map of scope names to their input definitions
|
|
93
|
+
*/
|
|
94
|
+
export type ScopesInput = {
|
|
95
|
+
[K in keyof AssistantScopes]?: ScopeInput<AssistantScopes[K]>;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Unsubscribe function type
|
|
100
|
+
*/
|
|
101
|
+
export type Unsubscribe = () => void;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* State type extracted from all scopes
|
|
105
|
+
*/
|
|
106
|
+
export type AssistantState = {
|
|
107
|
+
[K in keyof AssistantScopes]: ReturnType<
|
|
108
|
+
AssistantScopes[K]["value"]["getState"]
|
|
109
|
+
>;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The assistant client type with all registered scopes
|
|
114
|
+
*/
|
|
115
|
+
export type AssistantClient = {
|
|
116
|
+
[K in keyof AssistantScopes]: ScopeField<AssistantScopes[K]>;
|
|
117
|
+
} & {
|
|
118
|
+
subscribe(listener: () => void): Unsubscribe;
|
|
119
|
+
flushSync(): void;
|
|
120
|
+
};
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { useResource } from "@assistant-ui/tap/react";
|
|
3
|
+
import {
|
|
4
|
+
resource,
|
|
5
|
+
tapMemo,
|
|
6
|
+
tapResource,
|
|
7
|
+
tapResources,
|
|
8
|
+
tapEffectEvent,
|
|
9
|
+
ResourceElement,
|
|
10
|
+
} from "@assistant-ui/tap";
|
|
11
|
+
import type {
|
|
12
|
+
AssistantClient,
|
|
13
|
+
AssistantScopes,
|
|
14
|
+
ScopesInput,
|
|
15
|
+
ScopeField,
|
|
16
|
+
ScopeInput,
|
|
17
|
+
DerivedScopeProps,
|
|
18
|
+
} from "./types";
|
|
19
|
+
import { asStore } from "./asStore";
|
|
20
|
+
import { useAssistantContextValue } from "./AssistantContext";
|
|
21
|
+
import { splitScopes } from "./utils/splitScopes";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Resource for a single root scope
|
|
25
|
+
* Returns a tuple of [scopeName, {scopeFunction, subscribe, flushSync}]
|
|
26
|
+
*/
|
|
27
|
+
const RootScopeResource = resource(
|
|
28
|
+
<K extends keyof AssistantScopes>({
|
|
29
|
+
scopeName,
|
|
30
|
+
element,
|
|
31
|
+
}: {
|
|
32
|
+
scopeName: K;
|
|
33
|
+
element: ScopeInput<AssistantScopes[K]>;
|
|
34
|
+
}) => {
|
|
35
|
+
const store = tapResource(asStore(element));
|
|
36
|
+
|
|
37
|
+
return tapMemo(() => {
|
|
38
|
+
const scopeFunction = (() => store.getState().api) as ScopeField<
|
|
39
|
+
AssistantScopes[K]
|
|
40
|
+
>;
|
|
41
|
+
scopeFunction.source = "root";
|
|
42
|
+
scopeFunction.query = {} as AssistantScopes[K]["query"];
|
|
43
|
+
|
|
44
|
+
return [
|
|
45
|
+
scopeName,
|
|
46
|
+
{
|
|
47
|
+
scopeFunction,
|
|
48
|
+
subscribe: store.subscribe,
|
|
49
|
+
flushSync: store.flushSync,
|
|
50
|
+
},
|
|
51
|
+
] as const;
|
|
52
|
+
}, [scopeName, store]);
|
|
53
|
+
},
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Resource for all root scopes
|
|
58
|
+
* Mounts each root scope and returns an object mapping scope names to their stores
|
|
59
|
+
*/
|
|
60
|
+
const RootScopesResource = resource((scopes: ScopesInput) => {
|
|
61
|
+
const resultEntries = tapResources(
|
|
62
|
+
Object.entries(scopes).map(([scopeName, element]) =>
|
|
63
|
+
RootScopeResource(
|
|
64
|
+
{
|
|
65
|
+
scopeName: scopeName as keyof AssistantScopes,
|
|
66
|
+
element: element as ScopeInput<
|
|
67
|
+
AssistantScopes[keyof AssistantScopes]
|
|
68
|
+
>,
|
|
69
|
+
},
|
|
70
|
+
{ key: scopeName },
|
|
71
|
+
),
|
|
72
|
+
),
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
return tapMemo(() => {
|
|
76
|
+
if (resultEntries.length === 0) {
|
|
77
|
+
return {
|
|
78
|
+
scopes: {},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
scopes: Object.fromEntries(
|
|
84
|
+
resultEntries.map(([scopeName, { scopeFunction }]) => [
|
|
85
|
+
scopeName,
|
|
86
|
+
scopeFunction,
|
|
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());
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
flushSync: () => {
|
|
103
|
+
resultEntries.forEach(([, { flushSync }]) => {
|
|
104
|
+
flushSync();
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}, [...resultEntries]);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Hook to mount and access root scopes
|
|
113
|
+
*/
|
|
114
|
+
export const useRootScopes = (rootScopes: ScopesInput) => {
|
|
115
|
+
return useResource(RootScopesResource(rootScopes));
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Resource for a single derived scope
|
|
120
|
+
* Returns a tuple of [scopeName, scopeFunction] where scopeFunction has source and query
|
|
121
|
+
*/
|
|
122
|
+
const DerivedScopeResource = resource(
|
|
123
|
+
<K extends keyof AssistantScopes>({
|
|
124
|
+
scopeName,
|
|
125
|
+
element,
|
|
126
|
+
parentClient,
|
|
127
|
+
}: {
|
|
128
|
+
scopeName: K;
|
|
129
|
+
element: ResourceElement<
|
|
130
|
+
AssistantScopes[K],
|
|
131
|
+
DerivedScopeProps<AssistantScopes[K]>
|
|
132
|
+
>;
|
|
133
|
+
parentClient: AssistantClient;
|
|
134
|
+
}) => {
|
|
135
|
+
const get = tapEffectEvent(element.props.get);
|
|
136
|
+
const source = element.props.source;
|
|
137
|
+
const query = element.props.query;
|
|
138
|
+
return tapMemo(() => {
|
|
139
|
+
const scopeFunction = (() => get(parentClient)) as ScopeField<
|
|
140
|
+
AssistantScopes[K]
|
|
141
|
+
>;
|
|
142
|
+
scopeFunction.source = source;
|
|
143
|
+
scopeFunction.query = query;
|
|
144
|
+
|
|
145
|
+
return [scopeName, scopeFunction] as const;
|
|
146
|
+
}, [scopeName, get, source, JSON.stringify(query), parentClient]);
|
|
147
|
+
},
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Resource for all derived scopes
|
|
152
|
+
* Builds stable scope functions with source and query metadata
|
|
153
|
+
*/
|
|
154
|
+
const DerivedScopesResource = resource(
|
|
155
|
+
({
|
|
156
|
+
scopes,
|
|
157
|
+
parentClient,
|
|
158
|
+
}: {
|
|
159
|
+
scopes: ScopesInput;
|
|
160
|
+
parentClient: AssistantClient;
|
|
161
|
+
}) => {
|
|
162
|
+
const resultEntries = tapResources(
|
|
163
|
+
Object.entries(scopes).map(([scopeName, element]) =>
|
|
164
|
+
DerivedScopeResource(
|
|
165
|
+
{
|
|
166
|
+
scopeName: scopeName as keyof AssistantScopes,
|
|
167
|
+
element: element as ScopeInput<
|
|
168
|
+
AssistantScopes[keyof AssistantScopes]
|
|
169
|
+
>,
|
|
170
|
+
parentClient,
|
|
171
|
+
},
|
|
172
|
+
{ key: scopeName },
|
|
173
|
+
),
|
|
174
|
+
),
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
return tapMemo(() => {
|
|
178
|
+
return Object.fromEntries(resultEntries) as {
|
|
179
|
+
[K in keyof typeof scopes]: ScopeField<AssistantScopes[K]>;
|
|
180
|
+
};
|
|
181
|
+
}, [...resultEntries]);
|
|
182
|
+
},
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Hook to mount and access derived scopes
|
|
187
|
+
*/
|
|
188
|
+
export const useDerivedScopes = (
|
|
189
|
+
derivedScopes: ScopesInput,
|
|
190
|
+
parentClient: AssistantClient,
|
|
191
|
+
) => {
|
|
192
|
+
return useResource(
|
|
193
|
+
DerivedScopesResource({ scopes: derivedScopes, parentClient }),
|
|
194
|
+
);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const useExtendedAssistantClientImpl = (
|
|
198
|
+
scopes: ScopesInput,
|
|
199
|
+
): AssistantClient => {
|
|
200
|
+
const baseClient = useAssistantContextValue();
|
|
201
|
+
const { rootScopes, derivedScopes } = splitScopes(scopes);
|
|
202
|
+
|
|
203
|
+
// Mount the scopes to keep them alive
|
|
204
|
+
const rootFields = useRootScopes(rootScopes);
|
|
205
|
+
const derivedFields = useDerivedScopes(derivedScopes, baseClient);
|
|
206
|
+
|
|
207
|
+
return useMemo(() => {
|
|
208
|
+
// Merge base client with extended client
|
|
209
|
+
// If baseClient is the default proxy, spreading it will be a no-op
|
|
210
|
+
return {
|
|
211
|
+
...baseClient,
|
|
212
|
+
...rootFields.scopes,
|
|
213
|
+
...derivedFields,
|
|
214
|
+
subscribe: rootFields.subscribe ?? baseClient.subscribe,
|
|
215
|
+
flushSync: rootFields.flushSync ?? baseClient.flushSync,
|
|
216
|
+
} as AssistantClient;
|
|
217
|
+
}, [baseClient, rootFields, derivedFields]);
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Hook to access or extend the AssistantClient
|
|
222
|
+
*
|
|
223
|
+
* @example Without config - returns the client from context:
|
|
224
|
+
* ```typescript
|
|
225
|
+
* const client = useAssistantClient();
|
|
226
|
+
* const fooState = client.foo.getState();
|
|
227
|
+
* ```
|
|
228
|
+
*
|
|
229
|
+
* @example With config - creates a new client with additional scopes:
|
|
230
|
+
* ```typescript
|
|
231
|
+
* const client = useAssistantClient({
|
|
232
|
+
* message: DerivedScope({
|
|
233
|
+
* source: "thread",
|
|
234
|
+
* query: { type: "index", index: 0 },
|
|
235
|
+
* get: () => messageApi,
|
|
236
|
+
* }),
|
|
237
|
+
* });
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
export function useAssistantClient(): AssistantClient;
|
|
241
|
+
export function useAssistantClient(scopes: ScopesInput): AssistantClient;
|
|
242
|
+
export function useAssistantClient(scopes?: ScopesInput): AssistantClient {
|
|
243
|
+
if (scopes) {
|
|
244
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
245
|
+
return useExtendedAssistantClientImpl(scopes);
|
|
246
|
+
} else {
|
|
247
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
248
|
+
return useAssistantContextValue();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { useMemo, useSyncExternalStore, useDebugValue } from "react";
|
|
2
|
+
import type { AssistantClient, AssistantState } from "./types";
|
|
3
|
+
import { useAssistantClient } from "./useAssistantClient";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Proxied state that lazily accesses scope states
|
|
7
|
+
*/
|
|
8
|
+
class ProxiedAssistantState {
|
|
9
|
+
#client: AssistantClient;
|
|
10
|
+
|
|
11
|
+
constructor(client: AssistantClient) {
|
|
12
|
+
this.#client = client;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
#getScope<K extends keyof AssistantState>(key: K): AssistantState[K] {
|
|
16
|
+
const scopeField = this.#client[key];
|
|
17
|
+
if (!scopeField) {
|
|
18
|
+
throw new Error(`Scope "${String(key)}" not found in client`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const api = scopeField();
|
|
22
|
+
const state = api.getState();
|
|
23
|
+
return state as AssistantState[K];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Create a Proxy to dynamically handle property access
|
|
27
|
+
static create(client: AssistantClient): AssistantState {
|
|
28
|
+
const instance = new ProxiedAssistantState(client);
|
|
29
|
+
return new Proxy(instance, {
|
|
30
|
+
get(target, prop) {
|
|
31
|
+
if (typeof prop === "string" && prop in client) {
|
|
32
|
+
return target.#getScope(prop as keyof AssistantState);
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
},
|
|
36
|
+
}) as unknown as AssistantState;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Hook to access a slice of the assistant state with automatic subscription
|
|
42
|
+
*
|
|
43
|
+
* @param selector - Function to select a slice of the state
|
|
44
|
+
* @returns The selected state slice
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* const client = useAssistantClient({
|
|
49
|
+
* foo: RootScope({ ... }),
|
|
50
|
+
* });
|
|
51
|
+
*
|
|
52
|
+
* const bar = useAssistantState((state) => state.foo.bar);
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export const useAssistantState = <T,>(
|
|
56
|
+
selector: (state: AssistantState) => T,
|
|
57
|
+
): T => {
|
|
58
|
+
const client = useAssistantClient();
|
|
59
|
+
|
|
60
|
+
const proxiedState = useMemo(
|
|
61
|
+
() => ProxiedAssistantState.create(client),
|
|
62
|
+
[client],
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const slice = useSyncExternalStore(
|
|
66
|
+
client.subscribe,
|
|
67
|
+
() => selector(proxiedState),
|
|
68
|
+
() => selector(proxiedState),
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
useDebugValue(slice);
|
|
72
|
+
|
|
73
|
+
if (slice instanceof ProxiedAssistantState) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
"You tried to return the entire AssistantState. This is not supported due to technical limitations.",
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return slice;
|
|
80
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { DerivedScope } from "../DerivedScope";
|
|
2
|
+
import type { AssistantScopes, ScopeInput, ScopesInput } from "../types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Splits a scopes object into root scopes and derived scopes.
|
|
6
|
+
*
|
|
7
|
+
* @param scopes - The scopes input object to split
|
|
8
|
+
* @returns An object with { rootScopes, derivedScopes }
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const scopes = {
|
|
13
|
+
* foo: RootScope({ ... }),
|
|
14
|
+
* bar: DerivedScope({ ... }),
|
|
15
|
+
* };
|
|
16
|
+
*
|
|
17
|
+
* const { rootScopes, derivedScopes } = splitScopes(scopes);
|
|
18
|
+
* // rootScopes = { foo: ... }
|
|
19
|
+
* // derivedScopes = { bar: ... }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function splitScopes(scopes: ScopesInput) {
|
|
23
|
+
const rootScopes: ScopesInput = {};
|
|
24
|
+
const derivedScopes: ScopesInput = {};
|
|
25
|
+
|
|
26
|
+
for (const [key, scopeElement] of Object.entries(scopes) as [
|
|
27
|
+
keyof ScopesInput,
|
|
28
|
+
ScopeInput<AssistantScopes[keyof ScopesInput]>,
|
|
29
|
+
][]) {
|
|
30
|
+
if (scopeElement.type === DerivedScope) {
|
|
31
|
+
derivedScopes[key] = scopeElement;
|
|
32
|
+
} else {
|
|
33
|
+
rootScopes[key] = scopeElement;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return { rootScopes, derivedScopes };
|
|
38
|
+
}
|