@assistant-ui/store 0.0.6 → 0.1.2

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.
Files changed (93) hide show
  1. package/README.md +13 -13
  2. package/dist/{AssistantIf.d.ts → AuiIf.d.ts} +4 -4
  3. package/dist/AuiIf.d.ts.map +1 -0
  4. package/dist/AuiIf.js +8 -0
  5. package/dist/AuiIf.js.map +1 -0
  6. package/dist/Derived.d.ts +2 -2
  7. package/dist/Derived.d.ts.map +1 -1
  8. package/dist/Derived.js +1 -1
  9. package/dist/index.d.ts +5 -5
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +5 -5
  12. package/dist/index.js.map +1 -1
  13. package/dist/tapClientList.d.ts.map +1 -1
  14. package/dist/tapClientList.js +16 -10
  15. package/dist/tapClientList.js.map +1 -1
  16. package/dist/tapClientLookup.d.ts +3 -3
  17. package/dist/tapClientLookup.d.ts.map +1 -1
  18. package/dist/tapClientLookup.js +28 -16
  19. package/dist/tapClientLookup.js.map +1 -1
  20. package/dist/tapClientResource.d.ts +6 -2
  21. package/dist/tapClientResource.d.ts.map +1 -1
  22. package/dist/tapClientResource.js +11 -5
  23. package/dist/tapClientResource.js.map +1 -1
  24. package/dist/types/client.d.ts +4 -2
  25. package/dist/types/client.d.ts.map +1 -1
  26. package/dist/useAui.d.ts +23 -0
  27. package/dist/useAui.d.ts.map +1 -0
  28. package/dist/{useAssistantClient.js → useAui.js} +72 -35
  29. package/dist/useAui.js.map +1 -0
  30. package/dist/useAuiEvent.d.ts +3 -0
  31. package/dist/useAuiEvent.d.ts.map +1 -0
  32. package/dist/useAuiEvent.js +11 -0
  33. package/dist/useAuiEvent.js.map +1 -0
  34. package/dist/{useAssistantState.d.ts → useAuiState.d.ts} +4 -4
  35. package/dist/useAuiState.d.ts.map +1 -0
  36. package/dist/{useAssistantState.js → useAuiState.js} +6 -6
  37. package/dist/useAuiState.js.map +1 -0
  38. package/dist/utils/NotificationManager.d.ts +1 -1
  39. package/dist/utils/NotificationManager.d.ts.map +1 -1
  40. package/dist/utils/NotificationManager.js +6 -2
  41. package/dist/utils/NotificationManager.js.map +1 -1
  42. package/dist/utils/StoreResource.d.ts +1 -1
  43. package/dist/utils/StoreResource.d.ts.map +1 -1
  44. package/dist/utils/StoreResource.js +2 -4
  45. package/dist/utils/StoreResource.js.map +1 -1
  46. package/dist/utils/react-assistant-context.d.ts +5 -5
  47. package/dist/utils/react-assistant-context.d.ts.map +1 -1
  48. package/dist/utils/react-assistant-context.js +6 -6
  49. package/dist/utils/react-assistant-context.js.map +1 -1
  50. package/dist/utils/splitClients.d.ts +2 -20
  51. package/dist/utils/splitClients.d.ts.map +1 -1
  52. package/dist/utils/splitClients.js +35 -6
  53. package/dist/utils/splitClients.js.map +1 -1
  54. package/dist/utils/tap-assistant-context.d.ts.map +1 -1
  55. package/dist/utils/tap-assistant-context.js +3 -3
  56. package/dist/utils/tap-assistant-context.js.map +1 -1
  57. package/dist/utils/tap-client-stack-context.js +3 -3
  58. package/dist/utils/tap-client-stack-context.js.map +1 -1
  59. package/dist/wrapperResource.d.ts +3 -0
  60. package/dist/wrapperResource.d.ts.map +1 -0
  61. package/dist/wrapperResource.js +11 -0
  62. package/dist/wrapperResource.js.map +1 -0
  63. package/package.json +4 -3
  64. package/src/AuiIf.tsx +17 -0
  65. package/src/Derived.ts +1 -1
  66. package/src/index.ts +5 -5
  67. package/src/tapClientList.ts +27 -14
  68. package/src/tapClientLookup.ts +53 -33
  69. package/src/tapClientResource.ts +12 -6
  70. package/src/types/client.ts +2 -2
  71. package/src/{useAssistantClient.tsx → useAui.tsx} +109 -50
  72. package/src/{useAssistantEvent.ts → useAuiEvent.ts} +9 -5
  73. package/src/{useAssistantState.tsx → useAuiState.tsx} +5 -7
  74. package/src/utils/NotificationManager.ts +6 -2
  75. package/src/utils/StoreResource.ts +3 -5
  76. package/src/utils/react-assistant-context.tsx +9 -8
  77. package/src/utils/splitClients.ts +62 -17
  78. package/src/utils/tap-assistant-context.ts +5 -6
  79. package/src/utils/tap-client-stack-context.ts +4 -4
  80. package/src/wrapperResource.ts +17 -0
  81. package/dist/AssistantIf.d.ts.map +0 -1
  82. package/dist/AssistantIf.js +0 -8
  83. package/dist/AssistantIf.js.map +0 -1
  84. package/dist/useAssistantClient.d.ts +0 -17
  85. package/dist/useAssistantClient.d.ts.map +0 -1
  86. package/dist/useAssistantClient.js.map +0 -1
  87. package/dist/useAssistantEvent.d.ts +0 -3
  88. package/dist/useAssistantEvent.d.ts.map +0 -1
  89. package/dist/useAssistantEvent.js +0 -10
  90. package/dist/useAssistantEvent.js.map +0 -1
  91. package/dist/useAssistantState.d.ts.map +0 -1
  92. package/dist/useAssistantState.js.map +0 -1
  93. package/src/AssistantIf.tsx +0 -17
@@ -1,4 +1,4 @@
1
- import { createContext, tapContext, withContextProvider, tapMemo, } from "@assistant-ui/tap";
1
+ import { createResourceContext, tap, withContextProvider, tapMemo, } from "@assistant-ui/tap";
2
2
  /**
3
3
  * Symbol used to get the client index from a ClientProxy.
4
4
  */
@@ -9,12 +9,12 @@ export const SYMBOL_CLIENT_INDEX = Symbol("assistant-ui.store.clientIndex");
9
9
  export const getClientIndex = (client) => {
10
10
  return client[SYMBOL_CLIENT_INDEX];
11
11
  };
12
- const ClientStackContext = createContext([]);
12
+ const ClientStackContext = createResourceContext([]);
13
13
  /**
14
14
  * Get the current client stack inside a tap resource.
15
15
  */
16
16
  export const tapClientStack = () => {
17
- return tapContext(ClientStackContext);
17
+ return tap(ClientStackContext);
18
18
  };
19
19
  /**
20
20
  * Execute a callback with a client pushed onto the stack.
@@ -1 +1 @@
1
- {"version":3,"file":"tap-client-stack-context.js","sourceRoot":"","sources":["../../src/utils/tap-client-stack-context.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,UAAU,EACV,mBAAmB,EACnB,OAAO,GACR,MAAM,mBAAmB,CAAC;AAG3B;;GAEG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,gCAAgC,CAAC,CAAC;AAE5E;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAqB,EAAU,EAAE;IAC9D,OAAQ,MAAuD,CAC7D,mBAAmB,CACpB,CAAC;AACJ,CAAC,CAAC;AAOF,MAAM,kBAAkB,GAAG,aAAa,CAAc,EAAE,CAAC,CAAC;AAE1D;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,GAAgB,EAAE;IAC9C,OAAO,UAAU,CAAC,kBAAkB,CAAC,CAAC;AACxC,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,MAAqB,EACrB,QAAiB,EACd,EAAE;IACL,MAAM,YAAY,GAAG,cAAc,EAAE,CAAC;IACtC,MAAM,QAAQ,GAAG,OAAO,CACtB,GAAG,EAAE,CAAC,CAAC,GAAG,YAAY,EAAE,MAAM,CAAC,EAC/B,CAAC,YAAY,EAAE,MAAM,CAAC,CACvB,CAAC;IACF,OAAO,mBAAmB,CAAC,kBAAkB,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACrE,CAAC,CAAC"}
1
+ {"version":3,"file":"tap-client-stack-context.js","sourceRoot":"","sources":["../../src/utils/tap-client-stack-context.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,GAAG,EACH,mBAAmB,EACnB,OAAO,GACR,MAAM,mBAAmB,CAAC;AAG3B;;GAEG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,gCAAgC,CAAC,CAAC;AAE5E;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAqB,EAAU,EAAE;IAC9D,OAAQ,MAAuD,CAC7D,mBAAmB,CACpB,CAAC;AACJ,CAAC,CAAC;AAOF,MAAM,kBAAkB,GAAG,qBAAqB,CAAc,EAAE,CAAC,CAAC;AAElE;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,GAAgB,EAAE;IAC9C,OAAO,GAAG,CAAC,kBAAkB,CAAC,CAAC;AACjC,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,MAAqB,EACrB,QAAiB,EACd,EAAE;IACL,MAAM,YAAY,GAAG,cAAc,EAAE,CAAC;IACtC,MAAM,QAAQ,GAAG,OAAO,CACtB,GAAG,EAAE,CAAC,CAAC,GAAG,YAAY,EAAE,MAAM,CAAC,EAC/B,CAAC,YAAY,EAAE,MAAM,CAAC,CACvB,CAAC;IACF,OAAO,mBAAmB,CAAC,kBAAkB,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACrE,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { type ResourceElement, Resource } from "@assistant-ui/tap";
2
+ export declare const wrapperResource: <R, P>(fn: (props: ResourceElement<P>) => R) => Resource<R, ResourceElement<P>>;
3
+ //# sourceMappingURL=wrapperResource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrapperResource.d.ts","sourceRoot":"","sources":["../src/wrapperResource.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,eAAe,EACpB,QAAQ,EAGT,MAAM,mBAAmB,CAAC;AAE3B,eAAO,MAAM,eAAe,GAAI,CAAC,EAAE,CAAC,EAClC,IAAI,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,KACnC,QAAQ,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,CAOhC,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { resource, withKey, } from "@assistant-ui/tap";
2
+ export const wrapperResource = (fn) => {
3
+ const res = resource(fn);
4
+ return (props) => {
5
+ const el = res(props);
6
+ if (props.key === undefined)
7
+ return el;
8
+ return withKey(props.key, el);
9
+ };
10
+ };
11
+ //# sourceMappingURL=wrapperResource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrapperResource.js","sourceRoot":"","sources":["../src/wrapperResource.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,QAAQ,EACR,OAAO,GACR,MAAM,mBAAmB,CAAC;AAE3B,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,EAAoC,EACH,EAAE;IACnC,MAAM,GAAG,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IACzB,OAAO,CAAC,KAAyB,EAAE,EAAE;QACnC,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;QACtB,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS;YAAE,OAAO,EAAE,CAAC;QACvC,OAAO,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAChC,CAAC,CAAC;AACJ,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/store",
3
- "version": "0.0.6",
3
+ "version": "0.1.2",
4
4
  "description": "Tap-based state management for @assistant-ui",
5
5
  "keywords": [
6
6
  "state-management",
@@ -28,7 +28,8 @@
28
28
  ],
29
29
  "sideEffects": false,
30
30
  "dependencies": {
31
- "@assistant-ui/tap": "^0.3.6"
31
+ "@assistant-ui/tap": "^0.4.2",
32
+ "use-effect-event": "^2.0.3"
32
33
  },
33
34
  "peerDependencies": {
34
35
  "@types/react": "*",
@@ -41,7 +42,7 @@
41
42
  },
42
43
  "devDependencies": {
43
44
  "@types/react": "^19.2.9",
44
- "react": "^19.2.3",
45
+ "react": "^19.2.4",
45
46
  "@assistant-ui/x-buildutils": "0.0.1"
46
47
  },
47
48
  "publishConfig": {
package/src/AuiIf.tsx ADDED
@@ -0,0 +1,17 @@
1
+ "use client";
2
+
3
+ import type { FC, PropsWithChildren } from "react";
4
+ import { useAuiState } from "./useAuiState";
5
+ import type { AssistantState } from "./types/client";
6
+
7
+ export namespace AuiIf {
8
+ export type Props = PropsWithChildren<{ condition: AuiIf.Condition }>;
9
+ export type Condition = (state: AssistantState) => boolean;
10
+ }
11
+
12
+ export const AuiIf: FC<AuiIf.Props> = ({ children, condition }) => {
13
+ const result = useAuiState(condition);
14
+ return result ? children : null;
15
+ };
16
+
17
+ AuiIf.displayName = "AuiIf";
package/src/Derived.ts CHANGED
@@ -16,7 +16,7 @@ import type {
16
16
  *
17
17
  * @example
18
18
  * ```typescript
19
- * const aui = useAssistantClient({
19
+ * const aui = useAui({
20
20
  * message: Derived({
21
21
  * source: "thread",
22
22
  * query: { index: 0 },
package/src/index.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  // hooks
2
- export { useAssistantClient } from "./useAssistantClient";
3
- export { useAssistantState } from "./useAssistantState";
4
- export { useAssistantEvent } from "./useAssistantEvent";
2
+ export { useAui } from "./useAui";
3
+ export { useAuiState } from "./useAuiState";
4
+ export { useAuiEvent } from "./useAuiEvent";
5
5
 
6
6
  // components
7
- export { AssistantIf } from "./AssistantIf";
8
- export { AssistantProvider } from "./utils/react-assistant-context";
7
+ export { AuiIf } from "./AuiIf";
8
+ export { AuiProvider } from "./utils/react-assistant-context";
9
9
 
10
10
  // resources
11
11
  export { Derived } from "./Derived";
@@ -1,23 +1,24 @@
1
- import { tapState } from "@assistant-ui/tap";
1
+ import { tapConst, tapState, withKey } from "@assistant-ui/tap";
2
2
  import type { ContravariantResource } from "@assistant-ui/tap";
3
3
  import { tapClientLookup } from "./tapClientLookup";
4
4
  import type { ClientMethods, ClientOutputOf } from "./types/client";
5
5
 
6
+ type DataHandle<TData> = { data: TData | undefined; hasData: boolean };
7
+
6
8
  const createProps = <TData>(
7
9
  key: string,
8
- data: TData,
10
+ data: DataHandle<TData>,
9
11
  remove: () => void,
10
12
  ): tapClientList.ResourceProps<TData> => {
11
- let initialData: { data: TData } | undefined = { data };
12
13
  return {
13
14
  key,
14
15
  getInitialData: () => {
15
- if (!initialData) {
16
- throw new Error("getInitialData may only be called once");
16
+ if (!data.hasData) {
17
+ throw new Error(
18
+ "getInitialData may only be called during initial render",
19
+ );
17
20
  }
18
- const data = initialData.data;
19
- initialData = undefined;
20
- return data;
21
+ return data.data!;
21
22
  },
22
23
  remove,
23
24
  };
@@ -34,13 +35,16 @@ export const tapClientList = <TData, TState, TMethods extends ClientMethods>(
34
35
 
35
36
  type Props = tapClientList.ResourceProps<TData>;
36
37
 
38
+ const initialDataHandles: DataHandle<TData>[] = tapConst(() => [], []);
39
+
37
40
  const [items, setItems] = tapState<Record<string, Props>>(() => {
38
41
  const entries: [string, Props][] = [];
39
42
  for (const data of initialValues) {
40
43
  const key = getKey(data);
44
+ const handle = { data, hasData: true };
41
45
  entries.push([
42
46
  key,
43
- createProps(key, data, () => {
47
+ createProps(key, handle, () => {
44
48
  setItems((items) => {
45
49
  const newItems = { ...items };
46
50
  delete newItems[key];
@@ -48,16 +52,22 @@ export const tapClientList = <TData, TState, TMethods extends ClientMethods>(
48
52
  });
49
53
  }),
50
54
  ]);
55
+ initialDataHandles.push(handle);
51
56
  }
52
57
  return Object.fromEntries(entries);
53
58
  });
54
59
 
55
- const lookup = tapClientLookup<TState, TMethods, Record<string, Props>>(
56
- items,
57
- Resource,
58
- [Resource],
60
+ const lookup = tapClientLookup<TState, TMethods>(
61
+ () =>
62
+ Object.values(items).map((props) => withKey(props.key, Resource(props))),
63
+ [items, Resource],
59
64
  );
60
65
 
66
+ initialDataHandles.forEach((handle) => {
67
+ handle.data = undefined;
68
+ handle.hasData = false;
69
+ });
70
+
61
71
  const add = (data: TData) => {
62
72
  const key = getKey(data);
63
73
  setItems((items) => {
@@ -67,9 +77,12 @@ export const tapClientList = <TData, TState, TMethods extends ClientMethods>(
67
77
  );
68
78
  }
69
79
 
80
+ const handle = { data, hasData: true };
81
+ initialDataHandles.push(handle);
82
+
70
83
  return {
71
84
  ...items,
72
- [key]: createProps(key, data, () => {
85
+ [key]: createProps(key, handle, () => {
73
86
  setItems((items) => {
74
87
  const newItems = { ...items };
75
88
  delete newItems[key];
@@ -1,56 +1,76 @@
1
- import { ResourceElement, tapMemo, tapResources } from "@assistant-ui/tap";
1
+ import {
2
+ tapInlineResource,
3
+ tapMemo,
4
+ tapResources,
5
+ type ResourceElement,
6
+ } from "@assistant-ui/tap";
2
7
  import type { ClientMethods, ClientOutputOf } from "./types/client";
3
8
  import { ClientResource } from "./tapClientResource";
9
+ import { wrapperResource } from "./wrapperResource";
4
10
 
5
- export const tapClientLookup = <
6
- TState,
7
- TMethods extends ClientMethods,
8
- M extends Record<string | number | symbol, any>,
9
- >(
10
- map: M,
11
- getElement: (
12
- t: M[keyof M],
13
- key: keyof M,
14
- ) => ResourceElement<ClientOutputOf<TState, TMethods>>,
15
- getElementDeps: any[],
11
+ const ClientResourceWithKey = wrapperResource(
12
+ <TState, TMethods extends ClientMethods>(
13
+ el: ResourceElement<ClientOutputOf<TState, TMethods>>,
14
+ ) => {
15
+ if (el.key === undefined) {
16
+ throw new Error("tapClientResource: Element has no key");
17
+ }
18
+ return tapInlineResource(ClientResource(el)) as ClientOutputOf<
19
+ TState,
20
+ TMethods
21
+ > & { key: string | number };
22
+ },
23
+ );
24
+
25
+ export function tapClientLookup<TState, TMethods extends ClientMethods>(
26
+ getElements: () => readonly ResourceElement<
27
+ ClientOutputOf<TState, TMethods>
28
+ >[],
29
+ getElementsDeps: readonly unknown[],
16
30
  ): {
17
31
  state: TState[];
18
- get: (lookup: { index: number } | { key: keyof M }) => TMethods;
19
- } => {
32
+ get: (lookup: { index: number } | { key: string }) => TMethods;
33
+ } {
20
34
  const resources = tapResources(
21
- map,
22
- (t, key) => ClientResource(getElement(t, key)),
23
- getElementDeps,
35
+ () => getElements().map((el) => ClientResourceWithKey(el)),
36
+ // biome-ignore lint/correctness/useExhaustiveDependencies: getElementsDeps is passed through from caller
37
+ getElementsDeps,
24
38
  );
25
- const keys = tapMemo(() => Object.keys(map) as (keyof M)[], [map]);
39
+
40
+ const keys = tapMemo(() => Object.keys(resources), [resources]);
41
+
42
+ // For arrays, track element key -> index mapping
43
+ const keyToIndex = tapMemo(() => {
44
+ return resources.reduce(
45
+ (acc, resource, index) => {
46
+ acc[resource.key] = index;
47
+ return acc;
48
+ },
49
+ {} as Record<string, number>,
50
+ );
51
+ }, [resources]);
26
52
 
27
53
  const state = tapMemo(() => {
28
- const result = new Array(keys.length);
29
- for (let i = 0; i < keys.length; i++) {
30
- result[i] = resources[keys[i]!].state;
31
- }
32
- return result;
33
- }, [keys, resources]);
54
+ return resources.map((r) => r.state);
55
+ }, [resources]);
34
56
 
35
57
  return {
36
58
  state,
37
- get: (lookup: { index: number } | { key: keyof M }) => {
59
+ get: (lookup: { index: number } | { key: string }) => {
38
60
  if ("index" in lookup) {
39
61
  if (lookup.index < 0 || lookup.index >= keys.length) {
40
62
  throw new Error(
41
63
  `tapClientLookup: Index ${lookup.index} out of bounds (length: ${keys.length})`,
42
64
  );
43
65
  }
44
- return resources[keys[lookup.index]!]!.methods;
66
+ return resources[lookup.index]!.methods;
45
67
  }
46
68
 
47
- const value = resources[lookup.key];
48
- if (!value) {
49
- throw new Error(
50
- `tapClientLookup: Key "${String(lookup.key)}" not found`,
51
- );
69
+ const index = keyToIndex[lookup.key];
70
+ if (index === undefined) {
71
+ throw new Error(`tapClientLookup: Key "${lookup.key}" not found`);
52
72
  }
53
- return value.methods;
73
+ return resources[index]!.methods;
54
74
  },
55
75
  };
56
- };
76
+ }
@@ -4,7 +4,6 @@ import {
4
4
  tapRef,
5
5
  type ResourceElement,
6
6
  tapResource,
7
- resource,
8
7
  tapInlineResource,
9
8
  } from "@assistant-ui/tap";
10
9
  import type { ClientMethods, ClientOutputOf } from "./types/client";
@@ -17,6 +16,7 @@ import {
17
16
  BaseProxyHandler,
18
17
  handleIntrospectionProp,
19
18
  } from "./utils/BaseProxyHandler";
19
+ import { wrapperResource } from "./wrapperResource";
20
20
 
21
21
  /**
22
22
  * Symbol used internally to get state from ClientProxy.
@@ -57,6 +57,8 @@ function getOrCreateProxyFn(prop: string | symbol) {
57
57
  const method = this[SYMBOL_GET_OUTPUT].methods[prop];
58
58
  if (!method)
59
59
  throw new Error(`Method "${String(prop)}" is not implemented.`);
60
+ if (typeof method !== "function")
61
+ throw new Error(`"${String(prop)}" is not a function.`);
60
62
  return method(...args);
61
63
  };
62
64
  fieldAccessFns.set(prop, template);
@@ -82,7 +84,9 @@ class ClientProxyHandler
82
84
  if (prop === SYMBOL_CLIENT_INDEX) return this.index;
83
85
  const introspection = handleIntrospectionProp(prop, "ClientProxy");
84
86
  if (introspection !== false) return introspection;
85
- return getOrCreateProxyFn(prop);
87
+ const value = this.outputRef.current.methods[prop];
88
+ if (typeof value === "function") return getOrCreateProxyFn(prop);
89
+ return value;
86
90
  }
87
91
 
88
92
  ownKeys(): ArrayLike<string | symbol> {
@@ -114,10 +118,12 @@ class ClientProxyHandler
114
118
  * });
115
119
  * ```
116
120
  */
117
- export const ClientResource = resource(
121
+ export const ClientResource = wrapperResource(
118
122
  <TState, TMethods extends ClientMethods>(
119
123
  element: ResourceElement<ClientOutputOf<TState, TMethods>>,
120
- ): ClientOutputOf<TState, TMethods> => {
124
+ ): ClientOutputOf<TState, TMethods> & {
125
+ key: string | number | undefined;
126
+ } => {
121
127
  const valueRef = tapRef(
122
128
  null as unknown as ClientOutputOf<TState, TMethods>,
123
129
  );
@@ -129,7 +135,7 @@ export const ClientResource = resource(
129
135
  {} as TMethods,
130
136
  new ClientProxyHandler(valueRef, index),
131
137
  ),
132
- [],
138
+ [index],
133
139
  );
134
140
 
135
141
  const value = tapWithClientStack(methods, () => tapResource(element));
@@ -141,7 +147,7 @@ export const ClientResource = resource(
141
147
  valueRef.current = value;
142
148
  });
143
149
 
144
- return { methods, state: value.state };
150
+ return { methods, state: value.state, key: element.key };
145
151
  },
146
152
  );
147
153
 
@@ -9,7 +9,7 @@ import type {
9
9
  * Base type for methods that can be called on a client.
10
10
  */
11
11
  export interface ClientMethods {
12
- [key: string | symbol]: (...args: any[]) => any;
12
+ [key: string | symbol]: ((...args: any[]) => any) | ClientMethods;
13
13
  }
14
14
 
15
15
  type ClientMetaType = { source: ClientNames; query: Record<string, unknown> };
@@ -170,7 +170,7 @@ export type AssistantClientAccessor<K extends ClientNames> =
170
170
  | ClientMeta<K>
171
171
  | { source: "root"; query: Record<string, never> }
172
172
  | { source: null; query: null }
173
- );
173
+ ) & { name: K };
174
174
 
175
175
  /**
176
176
  * The assistant client type with all registered clients.