@sylphx/lens-solid 2.1.1 → 2.1.3

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/create.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * @sylphx/lens-solid - Create Client
3
3
  *
4
4
  * Creates a typed Lens client with SolidJS primitives.
5
- * Each endpoint can be called directly as a primitive or via .fetch() for promises.
5
+ * Base client methods work in vanilla JS, primitives are extensions.
6
6
  *
7
7
  * @example
8
8
  * ```tsx
@@ -15,18 +15,20 @@
15
15
  * transport: httpTransport({ url: '/api/lens' }),
16
16
  * });
17
17
  *
18
- * // In component
19
- * const { data, loading } = client.user.get({ input: { id } });
18
+ * // Vanilla JS (anywhere - SSR, utilities, event handlers)
19
+ * const user = await client.user.get({ input: { id } });
20
+ * client.user.get({ input: { id } }).subscribe(data => console.log(data));
20
21
  *
21
- * // In SSR
22
- * const user = await client.user.get.fetch({ input: { id } });
22
+ * // SolidJS primitives (in components)
23
+ * const { data, loading } = client.user.get.createQuery({ input: { id } });
24
+ * const { mutate, loading } = client.user.create.createMutation();
23
25
  * ```
24
26
  */
25
- import { type LensClientConfig, type SelectionObject, type TypedClientConfig } from "@sylphx/lens-client";
27
+ import { type LensClientConfig, type QueryResult, type SelectionObject, type TypedClientConfig } from "@sylphx/lens-client";
26
28
  import type { MutationDef, QueryDef, RouterDef, RouterRoutes } from "@sylphx/lens-core";
27
29
  import { type Accessor } from "solid-js";
28
- /** Query hook options */
29
- export interface QueryHookOptions<TInput> {
30
+ /** Query primitive options */
31
+ export interface QueryPrimitiveOptions<TInput> {
30
32
  /** Query input parameters */
31
33
  input?: TInput;
32
34
  /** Field selection */
@@ -34,8 +36,8 @@ export interface QueryHookOptions<TInput> {
34
36
  /** Skip query execution */
35
37
  skip?: boolean;
36
38
  }
37
- /** Query hook result */
38
- export interface QueryHookResult<T> {
39
+ /** Query primitive result */
40
+ export interface QueryPrimitiveResult<T> {
39
41
  /** Reactive data accessor */
40
42
  data: Accessor<T | null>;
41
43
  /** Reactive loading state */
@@ -45,8 +47,8 @@ export interface QueryHookResult<T> {
45
47
  /** Refetch the query */
46
48
  refetch: () => void;
47
49
  }
48
- /** Mutation hook options */
49
- export interface MutationHookOptions<TOutput> {
50
+ /** Mutation primitive options */
51
+ export interface MutationPrimitiveOptions<TOutput> {
50
52
  /** Called on successful mutation */
51
53
  onSuccess?: (data: TOutput) => void;
52
54
  /** Called on mutation error */
@@ -54,8 +56,8 @@ export interface MutationHookOptions<TOutput> {
54
56
  /** Called when mutation settles (success or error) */
55
57
  onSettled?: () => void;
56
58
  }
57
- /** Mutation hook result */
58
- export interface MutationHookResult<TInput, TOutput> {
59
+ /** Mutation primitive result */
60
+ export interface MutationPrimitiveResult<TInput, TOutput> {
59
61
  /** Execute the mutation */
60
62
  mutate: (options: {
61
63
  input: TInput;
@@ -70,28 +72,27 @@ export interface MutationHookResult<TInput, TOutput> {
70
72
  /** Reset mutation state */
71
73
  reset: () => void;
72
74
  }
73
- /** Query endpoint type */
75
+ /** Query endpoint with SolidJS primitives */
74
76
  export interface QueryEndpoint<TInput, TOutput> {
75
- /** Primitive call (in component) */
76
- <_TSelect extends SelectionObject = Record<string, never>>(options: TInput extends void ? QueryHookOptions<void> | void : QueryHookOptions<TInput>): QueryHookResult<TOutput>;
77
- /** Promise call (SSR) */
78
- fetch: <TSelect extends SelectionObject = Record<string, never>>(options: TInput extends void ? {
79
- input?: void;
80
- select?: TSelect;
81
- } | void : {
82
- input: TInput;
83
- select?: TSelect;
84
- }) => Promise<TOutput>;
77
+ /** Vanilla JS call - returns QueryResult (Promise + Observable) */
78
+ (options?: {
79
+ input?: TInput;
80
+ select?: SelectionObject;
81
+ }): QueryResult<TOutput>;
82
+ /** SolidJS primitive for reactive queries */
83
+ createQuery: (options?: TInput extends void ? QueryPrimitiveOptions<void> | void : QueryPrimitiveOptions<TInput>) => QueryPrimitiveResult<TOutput>;
85
84
  }
86
- /** Mutation endpoint type */
85
+ /** Mutation endpoint with SolidJS primitives */
87
86
  export interface MutationEndpoint<TInput, TOutput> {
88
- /** Primitive call (in component) */
89
- (options?: MutationHookOptions<TOutput>): MutationHookResult<TInput, TOutput>;
90
- /** Promise call (SSR) */
91
- fetch: <TSelect extends SelectionObject = Record<string, never>>(options: {
87
+ /** Vanilla JS call - returns Promise */
88
+ (options: {
92
89
  input: TInput;
93
- select?: TSelect;
94
- }) => Promise<TOutput>;
90
+ select?: SelectionObject;
91
+ }): Promise<{
92
+ data: TOutput;
93
+ }>;
94
+ /** SolidJS primitive for mutations */
95
+ createMutation: (options?: MutationPrimitiveOptions<TOutput>) => MutationPrimitiveResult<TInput, TOutput>;
95
96
  }
96
97
  /** Infer client type from router routes */
97
98
  type InferTypedClient<TRoutes extends RouterRoutes> = {
@@ -102,9 +103,8 @@ export type TypedClient<TRouter extends RouterDef> = TRouter extends RouterDef<i
102
103
  /**
103
104
  * Create a Lens client with SolidJS primitives.
104
105
  *
105
- * Each endpoint can be called:
106
- * - Directly as a primitive: `client.user.get({ input: { id } })`
107
- * - Via .fetch() for promises: `await client.user.get.fetch({ input: { id } })`
106
+ * Base client methods work in vanilla JS (SSR, utilities, event handlers).
107
+ * SolidJS primitives are available as `.createQuery()` and `.createMutation()`.
108
108
  *
109
109
  * @example
110
110
  * ```tsx
@@ -117,14 +117,16 @@ export type TypedClient<TRouter extends RouterDef> = TRouter extends RouterDef<i
117
117
  * transport: httpTransport({ url: '/api/lens' }),
118
118
  * });
119
119
  *
120
+ * // Vanilla JS (anywhere)
121
+ * const user = await client.user.get({ input: { id } });
122
+ *
120
123
  * // Component usage
121
124
  * function UserProfile(props: { id: string }) {
122
- * const { data, loading, error } = client.user.get({
125
+ * const { data, loading, error } = client.user.get.createQuery({
123
126
  * input: { id: props.id },
124
- * select: { name: true },
125
127
  * });
126
128
  *
127
- * const { mutate, loading: saving } = client.user.update({
129
+ * const { mutate, loading: saving } = client.user.update.createMutation({
128
130
  * onSuccess: () => console.log('Updated!'),
129
131
  * });
130
132
  *
@@ -1 +1 @@
1
- {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -15,14 +15,16 @@
15
15
  * transport: httpTransport({ url: '/api/lens' }),
16
16
  * });
17
17
  *
18
- * // Component usage
19
- * const { data, loading } = client.user.get({ input: { id } });
18
+ * // Vanilla JS (anywhere - SSR, utilities, event handlers)
19
+ * const user = await client.user.get({ input: { id } });
20
+ * client.user.get({ input: { id } }).subscribe(data => console.log(data));
20
21
  *
21
- * // SSR usage
22
- * const user = await client.user.get.fetch({ input: { id } });
22
+ * // SolidJS primitives (in components)
23
+ * const { data, loading } = client.user.get.createQuery({ input: { id } });
24
+ * const { mutate } = client.user.create.createMutation();
23
25
  * ```
24
26
  */
25
- export { createClient, type MutationEndpoint, type MutationHookOptions, type MutationHookResult, type QueryEndpoint, type QueryHookOptions, type QueryHookResult, type TypedClient, } from "./create.js";
27
+ export { createClient, type MutationEndpoint, type MutationPrimitiveOptions, type MutationPrimitiveResult, type QueryEndpoint, type QueryPrimitiveOptions, type QueryPrimitiveResult, type TypedClient, } from "./create.js";
26
28
  export { LensProvider, type LensProviderProps, useLensClient } from "./context.js";
27
29
  export { type CreateLazyQueryResult, type CreateMutationResult, type CreateQueryOptions, type CreateQueryResult, createLazyQuery, createMutation, createQuery, type MutationFn, type QueryInput, } from "./primitives.js";
28
30
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAMH,OAAO,EACN,YAAY,EACZ,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,WAAW,GAChB,MAAM,aAAa,CAAC;AAMrB,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAMnF,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,eAAe,EAEf,cAAc,EAEd,WAAW,EACX,KAAK,UAAU,EAEf,KAAK,UAAU,GACf,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAMH,OAAO,EACN,YAAY,EACZ,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,WAAW,GAChB,MAAM,aAAa,CAAC;AAMrB,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAMnF,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,eAAe,EAEf,cAAc,EAEd,WAAW,EACX,KAAK,UAAU,EAEf,KAAK,UAAU,GACf,MAAM,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -397,7 +397,7 @@ class ClientImpl {
397
397
  connectPromise = null;
398
398
  subscriptions = new Map;
399
399
  queryResultCache = new Map;
400
- callbackWrappers = new WeakMap;
400
+ observerEntries = new WeakMap;
401
401
  constructor(config) {
402
402
  this.transport = config.transport;
403
403
  this.plugins = config.plugins ?? [];
@@ -499,8 +499,21 @@ class ClientImpl {
499
499
  generateId(type, path) {
500
500
  return `${type}-${path}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
501
501
  }
502
+ inputHashCache = new WeakMap;
502
503
  makeQueryKey(path, input) {
503
- return `${path}:${JSON.stringify(input ?? null)}`;
504
+ if (input === undefined || input === null) {
505
+ return `${path}:null`;
506
+ }
507
+ if (typeof input !== "object") {
508
+ return `${path}:${String(input)}`;
509
+ }
510
+ const obj = input;
511
+ let hash = this.inputHashCache.get(obj);
512
+ if (!hash) {
513
+ hash = JSON.stringify(input);
514
+ this.inputHashCache.set(obj, hash);
515
+ }
516
+ return `${path}:${hash}`;
504
517
  }
505
518
  executeQuery(path, input, select) {
506
519
  const key = this.makeQueryKey(path, input);
@@ -511,7 +524,9 @@ class ClientImpl {
511
524
  if (!this.subscriptions.has(key)) {
512
525
  this.subscriptions.set(key, {
513
526
  data: null,
514
- callbacks: new Set
527
+ error: null,
528
+ completed: false,
529
+ observers: new Set
515
530
  });
516
531
  }
517
532
  const sub = this.subscriptions.get(key);
@@ -519,33 +534,48 @@ class ClientImpl {
519
534
  get value() {
520
535
  return sub.data;
521
536
  },
522
- subscribe: (callback) => {
523
- if (callback) {
524
- const typedCallback = callback;
525
- let wrapped = this.callbackWrappers.get(typedCallback);
526
- if (!wrapped) {
527
- wrapped = (data) => callback(data);
528
- this.callbackWrappers.set(typedCallback, wrapped);
529
- }
530
- sub.callbacks.add(wrapped);
531
- if (sub.data !== null) {
532
- callback(sub.data);
533
- }
537
+ subscribe: (observerOrCallback) => {
538
+ let entry;
539
+ if (typeof observerOrCallback === "function") {
540
+ const callback = observerOrCallback;
541
+ entry = { next: (data) => callback(data) };
542
+ } else if (observerOrCallback && typeof observerOrCallback === "object") {
543
+ const observer = observerOrCallback;
544
+ entry = {
545
+ next: observer.next ? (data) => observer.next(data) : undefined,
546
+ error: observer.error,
547
+ complete: observer.complete
548
+ };
549
+ } else {
550
+ entry = {};
551
+ }
552
+ if (observerOrCallback) {
553
+ this.observerEntries.set(observerOrCallback, entry);
554
+ }
555
+ sub.observers.add(entry);
556
+ if (sub.error && entry.error) {
557
+ entry.error(sub.error);
558
+ } else if (sub.data !== null && entry.next) {
559
+ entry.next(sub.data);
560
+ }
561
+ if (sub.completed && entry.complete) {
562
+ entry.complete();
534
563
  }
535
564
  if (!sub.unsubscribe) {
536
565
  this.startSubscription(path, input, key);
537
566
  }
538
567
  return () => {
539
- if (callback) {
540
- const typedCallback = callback;
541
- const wrapped = this.callbackWrappers.get(typedCallback);
542
- if (wrapped) {
543
- sub.callbacks.delete(wrapped);
568
+ if (observerOrCallback) {
569
+ const storedEntry = this.observerEntries.get(observerOrCallback);
570
+ if (storedEntry) {
571
+ sub.observers.delete(storedEntry);
544
572
  }
545
573
  }
546
- if (sub.callbacks.size === 0 && sub.unsubscribe) {
574
+ if (sub.observers.size === 0 && sub.unsubscribe) {
547
575
  sub.unsubscribe();
548
576
  sub.unsubscribe = undefined;
577
+ this.subscriptions.delete(key);
578
+ this.queryResultCache.delete(key);
549
579
  }
550
580
  };
551
581
  },
@@ -566,11 +596,16 @@ class ClientImpl {
566
596
  throw response.error;
567
597
  }
568
598
  sub.data = response.data;
569
- for (const cb of sub.callbacks) {
570
- cb(response.data);
599
+ for (const observer of sub.observers) {
600
+ observer.next?.(response.data);
571
601
  }
572
602
  return onfulfilled ? onfulfilled(response.data) : response.data;
573
603
  } catch (error) {
604
+ const err = error instanceof Error ? error : new Error(String(error));
605
+ sub.error = err;
606
+ for (const observer of sub.observers) {
607
+ observer.error?.(err);
608
+ }
574
609
  if (onrejected) {
575
610
  return onrejected(error);
576
611
  }
@@ -603,13 +638,31 @@ class ClientImpl {
603
638
  next: (result) => {
604
639
  if (result.data !== undefined) {
605
640
  sub.data = result.data;
606
- for (const cb of sub.callbacks) {
607
- cb(result.data);
641
+ sub.error = null;
642
+ for (const observer of sub.observers) {
643
+ observer.next?.(result.data);
644
+ }
645
+ }
646
+ if (result.error) {
647
+ const err = result.error instanceof Error ? result.error : new Error(String(result.error));
648
+ sub.error = err;
649
+ for (const observer of sub.observers) {
650
+ observer.error?.(err);
608
651
  }
609
652
  }
610
653
  },
611
- error: () => {},
612
- complete: () => {}
654
+ error: (err) => {
655
+ sub.error = err;
656
+ for (const observer of sub.observers) {
657
+ observer.error?.(err);
658
+ }
659
+ },
660
+ complete: () => {
661
+ sub.completed = true;
662
+ for (const observer of sub.observers) {
663
+ observer.complete?.();
664
+ }
665
+ }
613
666
  });
614
667
  sub.unsubscribe = () => subscription.unsubscribe();
615
668
  }
@@ -1051,17 +1104,9 @@ class SubscriptionRegistry {
1051
1104
  }
1052
1105
 
1053
1106
  // src/create.ts
1054
- import { createEffect, createSignal, onCleanup } from "solid-js";
1055
- function createQueryHook(baseClient, path) {
1056
- const getEndpoint = (p) => {
1057
- const parts = p.split(".");
1058
- let current = baseClient;
1059
- for (const part of parts) {
1060
- current = current[part];
1061
- }
1062
- return current;
1063
- };
1064
- const useQueryPrimitive = (options) => {
1107
+ import { createSignal, onCleanup } from "solid-js";
1108
+ function createQueryPrimitiveFactory(getEndpoint) {
1109
+ return function createQuery(options) {
1065
1110
  const [data, setData] = createSignal(null);
1066
1111
  const [loading, setLoading] = createSignal(!options?.skip);
1067
1112
  const [error, setError] = createSignal(null);
@@ -1077,7 +1122,7 @@ function createQueryHook(baseClient, path) {
1077
1122
  setError(null);
1078
1123
  return;
1079
1124
  }
1080
- const endpoint = getEndpoint(path);
1125
+ const endpoint = getEndpoint();
1081
1126
  const query = endpoint({ input: options?.input, select: options?.select });
1082
1127
  setLoading(true);
1083
1128
  setError(null);
@@ -1097,10 +1142,6 @@ function createQueryHook(baseClient, path) {
1097
1142
  });
1098
1143
  };
1099
1144
  executeQuery();
1100
- createEffect(() => {
1101
- const _ = JSON.stringify(options);
1102
- executeQuery();
1103
- });
1104
1145
  onCleanup(() => {
1105
1146
  if (unsubscribe) {
1106
1147
  unsubscribe();
@@ -1114,7 +1155,7 @@ function createQueryHook(baseClient, path) {
1114
1155
  }
1115
1156
  setLoading(true);
1116
1157
  setError(null);
1117
- const endpoint = getEndpoint(path);
1158
+ const endpoint = getEndpoint();
1118
1159
  const query = endpoint({ input: options?.input, select: options?.select });
1119
1160
  if (query) {
1120
1161
  unsubscribe = query.subscribe((value) => {
@@ -1134,24 +1175,9 @@ function createQueryHook(baseClient, path) {
1134
1175
  };
1135
1176
  return { data, loading, error, refetch };
1136
1177
  };
1137
- const fetch2 = async (options) => {
1138
- const endpoint = getEndpoint(path);
1139
- const queryResult = endpoint({ input: options?.input, select: options?.select });
1140
- return queryResult.then((data) => data);
1141
- };
1142
- useQueryPrimitive.fetch = fetch2;
1143
- return useQueryPrimitive;
1144
1178
  }
1145
- function createMutationHook(baseClient, path) {
1146
- const getEndpoint = (p) => {
1147
- const parts = p.split(".");
1148
- let current = baseClient;
1149
- for (const part of parts) {
1150
- current = current[part];
1151
- }
1152
- return current;
1153
- };
1154
- const useMutationPrimitive = (hookOptions) => {
1179
+ function createMutationPrimitiveFactory(getEndpoint) {
1180
+ return function createMutation(primitiveOptions) {
1155
1181
  const [data, setData] = createSignal(null);
1156
1182
  const [loading, setLoading] = createSignal(false);
1157
1183
  const [error, setError] = createSignal(null);
@@ -1159,20 +1185,19 @@ function createMutationHook(baseClient, path) {
1159
1185
  setLoading(true);
1160
1186
  setError(null);
1161
1187
  try {
1162
- const endpoint = getEndpoint(path);
1188
+ const endpoint = getEndpoint();
1163
1189
  const result = await endpoint({ input: options.input, select: options.select });
1164
- const mutationResult = result;
1165
- setData(() => mutationResult.data);
1190
+ setData(() => result.data);
1166
1191
  setLoading(false);
1167
- hookOptions?.onSuccess?.(mutationResult.data);
1168
- hookOptions?.onSettled?.();
1169
- return mutationResult.data;
1192
+ primitiveOptions?.onSuccess?.(result.data);
1193
+ primitiveOptions?.onSettled?.();
1194
+ return result.data;
1170
1195
  } catch (err) {
1171
1196
  const mutationError = err instanceof Error ? err : new Error(String(err));
1172
1197
  setError(mutationError);
1173
1198
  setLoading(false);
1174
- hookOptions?.onError?.(mutationError);
1175
- hookOptions?.onSettled?.();
1199
+ primitiveOptions?.onError?.(mutationError);
1200
+ primitiveOptions?.onSettled?.();
1176
1201
  throw mutationError;
1177
1202
  }
1178
1203
  };
@@ -1183,16 +1208,8 @@ function createMutationHook(baseClient, path) {
1183
1208
  };
1184
1209
  return { mutate, loading, error, data, reset };
1185
1210
  };
1186
- const fetch2 = async (options) => {
1187
- const endpoint = getEndpoint(path);
1188
- const result = await endpoint({ input: options.input, select: options.select });
1189
- const mutationResult = result;
1190
- return mutationResult.data;
1191
- };
1192
- useMutationPrimitive.fetch = fetch2;
1193
- return useMutationPrimitive;
1194
1211
  }
1195
- var hookCache = new Map;
1212
+ var primitiveCache = new Map;
1196
1213
  function createClient2(config) {
1197
1214
  const baseClient = createClient(config);
1198
1215
  function createProxy(path) {
@@ -1201,21 +1218,35 @@ function createClient2(config) {
1201
1218
  if (typeof prop === "symbol")
1202
1219
  return;
1203
1220
  const key = prop;
1204
- if (key === "fetch") {
1205
- return async (options) => {
1206
- const parts = path.split(".");
1207
- let current = baseClient;
1208
- for (const part of parts) {
1209
- current = current[part];
1210
- }
1211
- const endpointFn = current;
1212
- const queryResult = endpointFn(options);
1213
- const result = await queryResult;
1214
- if (result && typeof result === "object" && "data" in result && Object.keys(result).length === 1) {
1215
- return result.data;
1216
- }
1217
- return result;
1218
- };
1221
+ if (key === "createQuery") {
1222
+ const cacheKey = `${path}:createQuery`;
1223
+ if (!primitiveCache.has(cacheKey)) {
1224
+ const getEndpoint = () => {
1225
+ const parts = path.split(".");
1226
+ let current = baseClient;
1227
+ for (const part of parts) {
1228
+ current = current[part];
1229
+ }
1230
+ return current;
1231
+ };
1232
+ primitiveCache.set(cacheKey, createQueryPrimitiveFactory(getEndpoint));
1233
+ }
1234
+ return primitiveCache.get(cacheKey);
1235
+ }
1236
+ if (key === "createMutation") {
1237
+ const cacheKey = `${path}:createMutation`;
1238
+ if (!primitiveCache.has(cacheKey)) {
1239
+ const getEndpoint = () => {
1240
+ const parts = path.split(".");
1241
+ let current = baseClient;
1242
+ for (const part of parts) {
1243
+ current = current[part];
1244
+ }
1245
+ return current;
1246
+ };
1247
+ primitiveCache.set(cacheKey, createMutationPrimitiveFactory(getEndpoint));
1248
+ }
1249
+ return primitiveCache.get(cacheKey);
1219
1250
  }
1220
1251
  if (key === "then")
1221
1252
  return;
@@ -1225,33 +1256,17 @@ function createClient2(config) {
1225
1256
  return createProxy(newPath);
1226
1257
  },
1227
1258
  apply(_target, _thisArg, args) {
1228
- const options = args[0];
1229
- const isQueryOptions = options && (("input" in options) || ("select" in options) || ("skip" in options));
1230
- const isMutationOptions = !options || !isQueryOptions && (Object.keys(options).length === 0 || ("onSuccess" in options) || ("onError" in options) || ("onSettled" in options));
1231
- const cacheKeyQuery = `${path}:query`;
1232
- const cacheKeyMutation = `${path}:mutation`;
1233
- if (isQueryOptions) {
1234
- if (!hookCache.has(cacheKeyQuery)) {
1235
- hookCache.set(cacheKeyQuery, createQueryHook(baseClient, path));
1236
- }
1237
- const hook2 = hookCache.get(cacheKeyQuery);
1238
- return hook2(options);
1239
- }
1240
- if (isMutationOptions) {
1241
- if (!hookCache.has(cacheKeyMutation)) {
1242
- hookCache.set(cacheKeyMutation, createMutationHook(baseClient, path));
1243
- }
1244
- const hook2 = hookCache.get(cacheKeyMutation);
1245
- return hook2(options);
1259
+ const parts = path.split(".");
1260
+ let current = baseClient;
1261
+ for (const part of parts) {
1262
+ current = current[part];
1246
1263
  }
1247
- if (!hookCache.has(cacheKeyQuery)) {
1248
- hookCache.set(cacheKeyQuery, createQueryHook(baseClient, path));
1249
- }
1250
- const hook = hookCache.get(cacheKeyQuery);
1251
- return hook(options);
1264
+ const endpoint = current;
1265
+ return endpoint(args[0]);
1252
1266
  }
1253
1267
  };
1254
- return new Proxy(() => {}, handler);
1268
+ const proxy = new Proxy(() => {}, handler);
1269
+ return proxy;
1255
1270
  }
1256
1271
  return createProxy("");
1257
1272
  }
@@ -1271,7 +1286,7 @@ function useLensClient() {
1271
1286
  return client;
1272
1287
  }
1273
1288
  // src/primitives.ts
1274
- import { createEffect as createEffect2, createSignal as createSignal2, onCleanup as onCleanup2 } from "solid-js";
1289
+ import { createEffect, createSignal as createSignal2, onCleanup as onCleanup2 } from "solid-js";
1275
1290
  function resolveQuery(input) {
1276
1291
  return typeof input === "function" ? input() : input;
1277
1292
  }
@@ -1310,7 +1325,7 @@ function createQuery(queryInput, options) {
1310
1325
  };
1311
1326
  const initialQuery = resolveQuery(queryInput);
1312
1327
  executeQuery(initialQuery);
1313
- createEffect2(() => {
1328
+ createEffect(() => {
1314
1329
  const queryResult = resolveQuery(queryInput);
1315
1330
  if (queryResult !== initialQuery) {
1316
1331
  executeQuery(queryResult);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sylphx/lens-solid",
3
- "version": "2.1.1",
3
+ "version": "2.1.3",
4
4
  "description": "SolidJS bindings for Lens API framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -31,8 +31,8 @@
31
31
  "author": "SylphxAI",
32
32
  "license": "MIT",
33
33
  "dependencies": {
34
- "@sylphx/lens-client": "^2.2.0",
35
- "@sylphx/lens-core": "^2.1.0"
34
+ "@sylphx/lens-client": "^2.3.0",
35
+ "@sylphx/lens-core": "^2.2.0"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "solid-js": ">=1.8.0"
package/src/create.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * @sylphx/lens-solid - Create Client
3
3
  *
4
4
  * Creates a typed Lens client with SolidJS primitives.
5
- * Each endpoint can be called directly as a primitive or via .fetch() for promises.
5
+ * Base client methods work in vanilla JS, primitives are extensions.
6
6
  *
7
7
  * @example
8
8
  * ```tsx
@@ -15,11 +15,13 @@
15
15
  * transport: httpTransport({ url: '/api/lens' }),
16
16
  * });
17
17
  *
18
- * // In component
19
- * const { data, loading } = client.user.get({ input: { id } });
18
+ * // Vanilla JS (anywhere - SSR, utilities, event handlers)
19
+ * const user = await client.user.get({ input: { id } });
20
+ * client.user.get({ input: { id } }).subscribe(data => console.log(data));
20
21
  *
21
- * // In SSR
22
- * const user = await client.user.get.fetch({ input: { id } });
22
+ * // SolidJS primitives (in components)
23
+ * const { data, loading } = client.user.get.createQuery({ input: { id } });
24
+ * const { mutate, loading } = client.user.create.createMutation();
23
25
  * ```
24
26
  */
25
27
 
@@ -31,14 +33,14 @@ import {
31
33
  type TypedClientConfig,
32
34
  } from "@sylphx/lens-client";
33
35
  import type { MutationDef, QueryDef, RouterDef, RouterRoutes } from "@sylphx/lens-core";
34
- import { type Accessor, createEffect, createSignal, onCleanup } from "solid-js";
36
+ import { type Accessor, createSignal, onCleanup } from "solid-js";
35
37
 
36
38
  // =============================================================================
37
39
  // Types
38
40
  // =============================================================================
39
41
 
40
- /** Query hook options */
41
- export interface QueryHookOptions<TInput> {
42
+ /** Query primitive options */
43
+ export interface QueryPrimitiveOptions<TInput> {
42
44
  /** Query input parameters */
43
45
  input?: TInput;
44
46
  /** Field selection */
@@ -47,8 +49,8 @@ export interface QueryHookOptions<TInput> {
47
49
  skip?: boolean;
48
50
  }
49
51
 
50
- /** Query hook result */
51
- export interface QueryHookResult<T> {
52
+ /** Query primitive result */
53
+ export interface QueryPrimitiveResult<T> {
52
54
  /** Reactive data accessor */
53
55
  data: Accessor<T | null>;
54
56
  /** Reactive loading state */
@@ -59,8 +61,8 @@ export interface QueryHookResult<T> {
59
61
  refetch: () => void;
60
62
  }
61
63
 
62
- /** Mutation hook options */
63
- export interface MutationHookOptions<TOutput> {
64
+ /** Mutation primitive options */
65
+ export interface MutationPrimitiveOptions<TOutput> {
64
66
  /** Called on successful mutation */
65
67
  onSuccess?: (data: TOutput) => void;
66
68
  /** Called on mutation error */
@@ -69,8 +71,8 @@ export interface MutationHookOptions<TOutput> {
69
71
  onSettled?: () => void;
70
72
  }
71
73
 
72
- /** Mutation hook result */
73
- export interface MutationHookResult<TInput, TOutput> {
74
+ /** Mutation primitive result */
75
+ export interface MutationPrimitiveResult<TInput, TOutput> {
74
76
  /** Execute the mutation */
75
77
  mutate: (options: { input: TInput; select?: SelectionObject }) => Promise<TOutput>;
76
78
  /** Reactive loading state */
@@ -83,31 +85,28 @@ export interface MutationHookResult<TInput, TOutput> {
83
85
  reset: () => void;
84
86
  }
85
87
 
86
- /** Query endpoint type */
88
+ /** Query endpoint with SolidJS primitives */
87
89
  export interface QueryEndpoint<TInput, TOutput> {
88
- /** Primitive call (in component) */
89
- <_TSelect extends SelectionObject = Record<string, never>>(
90
- options: TInput extends void ? QueryHookOptions<void> | void : QueryHookOptions<TInput>,
91
- ): QueryHookResult<TOutput>;
92
-
93
- /** Promise call (SSR) */
94
- fetch: <TSelect extends SelectionObject = Record<string, never>>(
95
- options: TInput extends void
96
- ? { input?: void; select?: TSelect } | void
97
- : { input: TInput; select?: TSelect },
98
- ) => Promise<TOutput>;
90
+ /** Vanilla JS call - returns QueryResult (Promise + Observable) */
91
+ (options?: { input?: TInput; select?: SelectionObject }): QueryResult<TOutput>;
92
+
93
+ /** SolidJS primitive for reactive queries */
94
+ createQuery: (
95
+ options?: TInput extends void
96
+ ? QueryPrimitiveOptions<void> | void
97
+ : QueryPrimitiveOptions<TInput>,
98
+ ) => QueryPrimitiveResult<TOutput>;
99
99
  }
100
100
 
101
- /** Mutation endpoint type */
101
+ /** Mutation endpoint with SolidJS primitives */
102
102
  export interface MutationEndpoint<TInput, TOutput> {
103
- /** Primitive call (in component) */
104
- (options?: MutationHookOptions<TOutput>): MutationHookResult<TInput, TOutput>;
105
-
106
- /** Promise call (SSR) */
107
- fetch: <TSelect extends SelectionObject = Record<string, never>>(options: {
108
- input: TInput;
109
- select?: TSelect;
110
- }) => Promise<TOutput>;
103
+ /** Vanilla JS call - returns Promise */
104
+ (options: { input: TInput; select?: SelectionObject }): Promise<{ data: TOutput }>;
105
+
106
+ /** SolidJS primitive for mutations */
107
+ createMutation: (
108
+ options?: MutationPrimitiveOptions<TOutput>,
109
+ ) => MutationPrimitiveResult<TInput, TOutput>;
111
110
  }
112
111
 
113
112
  /** Infer client type from router routes */
@@ -126,26 +125,18 @@ export type TypedClient<TRouter extends RouterDef> =
126
125
  TRouter extends RouterDef<infer TRoutes> ? InferTypedClient<TRoutes> : never;
127
126
 
128
127
  // =============================================================================
129
- // Hook Factories
128
+ // Primitive Factories
130
129
  // =============================================================================
131
130
 
132
131
  /**
133
- * Create a query primitive for a specific endpoint
132
+ * Create createQuery primitive for a specific endpoint
134
133
  */
135
- function createQueryHook<TInput, TOutput>(
136
- baseClient: unknown,
137
- path: string,
138
- ): QueryEndpoint<TInput, TOutput> {
139
- const getEndpoint = (p: string) => {
140
- const parts = p.split(".");
141
- let current: unknown = baseClient;
142
- for (const part of parts) {
143
- current = (current as Record<string, unknown>)[part];
144
- }
145
- return current as (options: unknown) => QueryResult<TOutput>;
146
- };
147
-
148
- const useQueryPrimitive = (options?: QueryHookOptions<TInput>): QueryHookResult<TOutput> => {
134
+ function createQueryPrimitiveFactory<TInput, TOutput>(
135
+ getEndpoint: () => (options: unknown) => QueryResult<TOutput>,
136
+ ) {
137
+ return function createQuery(
138
+ options?: QueryPrimitiveOptions<TInput>,
139
+ ): QueryPrimitiveResult<TOutput> {
149
140
  const [data, setData] = createSignal<TOutput | null>(null);
150
141
  const [loading, setLoading] = createSignal(!options?.skip);
151
142
  const [error, setError] = createSignal<Error | null>(null);
@@ -165,7 +156,7 @@ function createQueryHook<TInput, TOutput>(
165
156
  return;
166
157
  }
167
158
 
168
- const endpoint = getEndpoint(path);
159
+ const endpoint = getEndpoint();
169
160
  const query = endpoint({ input: options?.input, select: options?.select });
170
161
 
171
162
  setLoading(true);
@@ -191,16 +182,9 @@ function createQueryHook<TInput, TOutput>(
191
182
  );
192
183
  };
193
184
 
194
- // Execute initial query synchronously
185
+ // Execute initial query
195
186
  executeQuery();
196
187
 
197
- // Use createEffect for reactive updates when options change
198
- createEffect(() => {
199
- // Access options to track them (Solid will re-run when they change)
200
- const _ = JSON.stringify(options);
201
- executeQuery();
202
- });
203
-
204
188
  onCleanup(() => {
205
189
  if (unsubscribe) {
206
190
  unsubscribe();
@@ -215,7 +199,7 @@ function createQueryHook<TInput, TOutput>(
215
199
  }
216
200
  setLoading(true);
217
201
  setError(null);
218
- const endpoint = getEndpoint(path);
202
+ const endpoint = getEndpoint();
219
203
  const query = endpoint({ input: options?.input, select: options?.select });
220
204
  if (query) {
221
205
  unsubscribe = query.subscribe((value) => {
@@ -239,42 +223,17 @@ function createQueryHook<TInput, TOutput>(
239
223
 
240
224
  return { data, loading, error, refetch };
241
225
  };
242
-
243
- // Fetch method for promises (SSR)
244
- const fetch = async (options?: {
245
- input?: TInput;
246
- select?: SelectionObject;
247
- }): Promise<TOutput> => {
248
- const endpoint = getEndpoint(path);
249
- const queryResult = endpoint({ input: options?.input, select: options?.select });
250
- return queryResult.then((data) => data);
251
- };
252
-
253
- // Attach fetch method to the hook function
254
- useQueryPrimitive.fetch = fetch;
255
-
256
- return useQueryPrimitive as unknown as QueryEndpoint<TInput, TOutput>;
257
226
  }
258
227
 
259
228
  /**
260
- * Create a mutation primitive for a specific endpoint
229
+ * Create createMutation primitive for a specific endpoint
261
230
  */
262
- function createMutationHook<TInput, TOutput>(
263
- baseClient: unknown,
264
- path: string,
265
- ): MutationEndpoint<TInput, TOutput> {
266
- const getEndpoint = (p: string) => {
267
- const parts = p.split(".");
268
- let current: unknown = baseClient;
269
- for (const part of parts) {
270
- current = (current as Record<string, unknown>)[part];
271
- }
272
- return current as (options: unknown) => QueryResult<{ data: TOutput }>;
273
- };
274
-
275
- const useMutationPrimitive = (
276
- hookOptions?: MutationHookOptions<TOutput>,
277
- ): MutationHookResult<TInput, TOutput> => {
231
+ function createMutationPrimitiveFactory<TInput, TOutput>(
232
+ getEndpoint: () => (options: unknown) => Promise<{ data: TOutput }>,
233
+ ) {
234
+ return function createMutation(
235
+ primitiveOptions?: MutationPrimitiveOptions<TOutput>,
236
+ ): MutationPrimitiveResult<TInput, TOutput> {
278
237
  const [data, setData] = createSignal<TOutput | null>(null);
279
238
  const [loading, setLoading] = createSignal(false);
280
239
  const [error, setError] = createSignal<Error | null>(null);
@@ -287,24 +246,23 @@ function createMutationHook<TInput, TOutput>(
287
246
  setError(null);
288
247
 
289
248
  try {
290
- const endpoint = getEndpoint(path);
249
+ const endpoint = getEndpoint();
291
250
  const result = await endpoint({ input: options.input, select: options.select });
292
- const mutationResult = result as unknown as { data: TOutput };
293
251
 
294
- setData(() => mutationResult.data);
252
+ setData(() => result.data);
295
253
  setLoading(false);
296
254
 
297
- hookOptions?.onSuccess?.(mutationResult.data);
298
- hookOptions?.onSettled?.();
255
+ primitiveOptions?.onSuccess?.(result.data);
256
+ primitiveOptions?.onSettled?.();
299
257
 
300
- return mutationResult.data;
258
+ return result.data;
301
259
  } catch (err) {
302
260
  const mutationError = err instanceof Error ? err : new Error(String(err));
303
261
  setError(mutationError);
304
262
  setLoading(false);
305
263
 
306
- hookOptions?.onError?.(mutationError);
307
- hookOptions?.onSettled?.();
264
+ primitiveOptions?.onError?.(mutationError);
265
+ primitiveOptions?.onSettled?.();
308
266
 
309
267
  throw mutationError;
310
268
  }
@@ -318,34 +276,20 @@ function createMutationHook<TInput, TOutput>(
318
276
 
319
277
  return { mutate, loading, error, data, reset };
320
278
  };
321
-
322
- // Fetch method for promises (SSR)
323
- const fetch = async (options: { input: TInput; select?: SelectionObject }): Promise<TOutput> => {
324
- const endpoint = getEndpoint(path);
325
- const result = await endpoint({ input: options.input, select: options.select });
326
- const mutationResult = result as unknown as { data: TOutput };
327
- return mutationResult.data;
328
- };
329
-
330
- // Attach fetch method to the hook function
331
- useMutationPrimitive.fetch = fetch;
332
-
333
- return useMutationPrimitive as unknown as MutationEndpoint<TInput, TOutput>;
334
279
  }
335
280
 
336
281
  // =============================================================================
337
282
  // Create Client
338
283
  // =============================================================================
339
284
 
340
- // Cache for hook functions
341
- const hookCache = new Map<string, unknown>();
285
+ // Cache for primitive functions to ensure stable references
286
+ const primitiveCache = new Map<string, unknown>();
342
287
 
343
288
  /**
344
289
  * Create a Lens client with SolidJS primitives.
345
290
  *
346
- * Each endpoint can be called:
347
- * - Directly as a primitive: `client.user.get({ input: { id } })`
348
- * - Via .fetch() for promises: `await client.user.get.fetch({ input: { id } })`
291
+ * Base client methods work in vanilla JS (SSR, utilities, event handlers).
292
+ * SolidJS primitives are available as `.createQuery()` and `.createMutation()`.
349
293
  *
350
294
  * @example
351
295
  * ```tsx
@@ -358,14 +302,16 @@ const hookCache = new Map<string, unknown>();
358
302
  * transport: httpTransport({ url: '/api/lens' }),
359
303
  * });
360
304
  *
305
+ * // Vanilla JS (anywhere)
306
+ * const user = await client.user.get({ input: { id } });
307
+ *
361
308
  * // Component usage
362
309
  * function UserProfile(props: { id: string }) {
363
- * const { data, loading, error } = client.user.get({
310
+ * const { data, loading, error } = client.user.get.createQuery({
364
311
  * input: { id: props.id },
365
- * select: { name: true },
366
312
  * });
367
313
  *
368
- * const { mutate, loading: saving } = client.user.update({
314
+ * const { mutate, loading: saving } = client.user.update.createMutation({
369
315
  * onSuccess: () => console.log('Updated!'),
370
316
  * });
371
317
  *
@@ -386,6 +332,7 @@ const hookCache = new Map<string, unknown>();
386
332
  export function createClient<TRouter extends RouterDef>(
387
333
  config: LensClientConfig | TypedClientConfig<{ router: TRouter }>,
388
334
  ): TypedClient<TRouter> {
335
+ // Create base client for transport
389
336
  const baseClient = createBaseClient(config as LensClientConfig);
390
337
 
391
338
  function createProxy(path: string): unknown {
@@ -394,27 +341,38 @@ export function createClient<TRouter extends RouterDef>(
394
341
  if (typeof prop === "symbol") return undefined;
395
342
  const key = prop as string;
396
343
 
397
- if (key === "fetch") {
398
- return async (options: unknown) => {
399
- const parts = path.split(".");
400
- let current: unknown = baseClient;
401
- for (const part of parts) {
402
- current = (current as Record<string, unknown>)[part];
403
- }
404
- const endpointFn = current as (opts: unknown) => QueryResult<unknown>;
405
- const queryResult = endpointFn(options);
406
- const result = await queryResult;
407
-
408
- if (
409
- result &&
410
- typeof result === "object" &&
411
- "data" in result &&
412
- Object.keys(result).length === 1
413
- ) {
414
- return (result as { data: unknown }).data;
415
- }
416
- return result;
417
- };
344
+ // Handle .createQuery() - SolidJS primitive for queries
345
+ if (key === "createQuery") {
346
+ const cacheKey = `${path}:createQuery`;
347
+ if (!primitiveCache.has(cacheKey)) {
348
+ const getEndpoint = () => {
349
+ const parts = path.split(".");
350
+ let current: unknown = baseClient;
351
+ for (const part of parts) {
352
+ current = (current as Record<string, unknown>)[part];
353
+ }
354
+ return current as (options: unknown) => QueryResult<unknown>;
355
+ };
356
+ primitiveCache.set(cacheKey, createQueryPrimitiveFactory(getEndpoint));
357
+ }
358
+ return primitiveCache.get(cacheKey);
359
+ }
360
+
361
+ // Handle .createMutation() - SolidJS primitive for mutations
362
+ if (key === "createMutation") {
363
+ const cacheKey = `${path}:createMutation`;
364
+ if (!primitiveCache.has(cacheKey)) {
365
+ const getEndpoint = () => {
366
+ const parts = path.split(".");
367
+ let current: unknown = baseClient;
368
+ for (const part of parts) {
369
+ current = (current as Record<string, unknown>)[part];
370
+ }
371
+ return current as (options: unknown) => Promise<{ data: unknown }>;
372
+ };
373
+ primitiveCache.set(cacheKey, createMutationPrimitiveFactory(getEndpoint));
374
+ }
375
+ return primitiveCache.get(cacheKey);
418
376
  }
419
377
 
420
378
  if (key === "then") return undefined;
@@ -425,47 +383,19 @@ export function createClient<TRouter extends RouterDef>(
425
383
  },
426
384
 
427
385
  apply(_target, _thisArg, args) {
428
- const options = args[0] as Record<string, unknown> | undefined;
429
-
430
- const isQueryOptions =
431
- options && ("input" in options || "select" in options || "skip" in options);
432
-
433
- const isMutationOptions =
434
- !options ||
435
- (!isQueryOptions &&
436
- (Object.keys(options).length === 0 ||
437
- "onSuccess" in options ||
438
- "onError" in options ||
439
- "onSettled" in options));
440
-
441
- const cacheKeyQuery = `${path}:query`;
442
- const cacheKeyMutation = `${path}:mutation`;
443
-
444
- if (isQueryOptions) {
445
- if (!hookCache.has(cacheKeyQuery)) {
446
- hookCache.set(cacheKeyQuery, createQueryHook(baseClient, path));
447
- }
448
- const hook = hookCache.get(cacheKeyQuery) as (opts: unknown) => unknown;
449
- return hook(options);
450
- }
451
-
452
- if (isMutationOptions) {
453
- if (!hookCache.has(cacheKeyMutation)) {
454
- hookCache.set(cacheKeyMutation, createMutationHook(baseClient, path));
455
- }
456
- const hook = hookCache.get(cacheKeyMutation) as (opts: unknown) => unknown;
457
- return hook(options);
458
- }
459
-
460
- if (!hookCache.has(cacheKeyQuery)) {
461
- hookCache.set(cacheKeyQuery, createQueryHook(baseClient, path));
386
+ // Direct call - delegate to base client (returns QueryResult or Promise)
387
+ const parts = path.split(".");
388
+ let current: unknown = baseClient;
389
+ for (const part of parts) {
390
+ current = (current as Record<string, unknown>)[part];
462
391
  }
463
- const hook = hookCache.get(cacheKeyQuery) as (opts: unknown) => unknown;
464
- return hook(options);
392
+ const endpoint = current as (options: unknown) => unknown;
393
+ return endpoint(args[0]);
465
394
  },
466
395
  };
467
396
 
468
- return new Proxy((() => {}) as (...args: unknown[]) => unknown, handler);
397
+ const proxy = new Proxy((() => {}) as (...args: unknown[]) => unknown, handler);
398
+ return proxy;
469
399
  }
470
400
 
471
401
  return createProxy("") as TypedClient<TRouter>;
package/src/index.ts CHANGED
@@ -15,11 +15,13 @@
15
15
  * transport: httpTransport({ url: '/api/lens' }),
16
16
  * });
17
17
  *
18
- * // Component usage
19
- * const { data, loading } = client.user.get({ input: { id } });
18
+ * // Vanilla JS (anywhere - SSR, utilities, event handlers)
19
+ * const user = await client.user.get({ input: { id } });
20
+ * client.user.get({ input: { id } }).subscribe(data => console.log(data));
20
21
  *
21
- * // SSR usage
22
- * const user = await client.user.get.fetch({ input: { id } });
22
+ * // SolidJS primitives (in components)
23
+ * const { data, loading } = client.user.get.createQuery({ input: { id } });
24
+ * const { mutate } = client.user.create.createMutation();
23
25
  * ```
24
26
  */
25
27
 
@@ -30,11 +32,11 @@
30
32
  export {
31
33
  createClient,
32
34
  type MutationEndpoint,
33
- type MutationHookOptions,
34
- type MutationHookResult,
35
+ type MutationPrimitiveOptions,
36
+ type MutationPrimitiveResult,
35
37
  type QueryEndpoint,
36
- type QueryHookOptions,
37
- type QueryHookResult,
38
+ type QueryPrimitiveOptions,
39
+ type QueryPrimitiveResult,
38
40
  type TypedClient,
39
41
  } from "./create.js";
40
42