@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 +40 -38
- package/dist/create.d.ts.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +134 -119
- package/package.json +3 -3
- package/src/create.ts +110 -180
- package/src/index.ts +10 -8
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
|
-
*
|
|
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
|
-
* //
|
|
19
|
-
* const
|
|
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
|
-
* //
|
|
22
|
-
* const
|
|
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
|
|
29
|
-
export interface
|
|
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
|
|
38
|
-
export interface
|
|
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
|
|
49
|
-
export interface
|
|
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
|
|
58
|
-
export interface
|
|
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
|
|
75
|
+
/** Query endpoint with SolidJS primitives */
|
|
74
76
|
export interface QueryEndpoint<TInput, TOutput> {
|
|
75
|
-
/**
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
|
85
|
+
/** Mutation endpoint with SolidJS primitives */
|
|
87
86
|
export interface MutationEndpoint<TInput, TOutput> {
|
|
88
|
-
/**
|
|
89
|
-
(options
|
|
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?:
|
|
94
|
-
})
|
|
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
|
-
*
|
|
106
|
-
*
|
|
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
|
*
|
package/dist/create.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"AAAA
|
|
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
|
-
* //
|
|
19
|
-
* const
|
|
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
|
-
* //
|
|
22
|
-
* const
|
|
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
|
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: (
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
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 (
|
|
540
|
-
const
|
|
541
|
-
|
|
542
|
-
|
|
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.
|
|
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
|
|
570
|
-
|
|
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
|
-
|
|
607
|
-
|
|
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
|
-
|
|
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 {
|
|
1055
|
-
function
|
|
1056
|
-
|
|
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(
|
|
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(
|
|
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
|
|
1146
|
-
|
|
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(
|
|
1188
|
+
const endpoint = getEndpoint();
|
|
1163
1189
|
const result = await endpoint({ input: options.input, select: options.select });
|
|
1164
|
-
|
|
1165
|
-
setData(() => mutationResult.data);
|
|
1190
|
+
setData(() => result.data);
|
|
1166
1191
|
setLoading(false);
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
return
|
|
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
|
-
|
|
1175
|
-
|
|
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
|
|
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 === "
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
current =
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
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
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
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
|
-
|
|
1248
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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.
|
|
35
|
-
"@sylphx/lens-core": "^2.
|
|
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
|
-
*
|
|
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
|
-
* //
|
|
19
|
-
* const
|
|
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
|
-
* //
|
|
22
|
-
* const
|
|
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,
|
|
36
|
+
import { type Accessor, createSignal, onCleanup } from "solid-js";
|
|
35
37
|
|
|
36
38
|
// =============================================================================
|
|
37
39
|
// Types
|
|
38
40
|
// =============================================================================
|
|
39
41
|
|
|
40
|
-
/** Query
|
|
41
|
-
export interface
|
|
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
|
|
51
|
-
export interface
|
|
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
|
|
63
|
-
export interface
|
|
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
|
|
73
|
-
export interface
|
|
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
|
|
88
|
+
/** Query endpoint with SolidJS primitives */
|
|
87
89
|
export interface QueryEndpoint<TInput, TOutput> {
|
|
88
|
-
/**
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
|
101
|
+
/** Mutation endpoint with SolidJS primitives */
|
|
102
102
|
export interface MutationEndpoint<TInput, TOutput> {
|
|
103
|
-
/**
|
|
104
|
-
(options?:
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
//
|
|
128
|
+
// Primitive Factories
|
|
130
129
|
// =============================================================================
|
|
131
130
|
|
|
132
131
|
/**
|
|
133
|
-
* Create
|
|
132
|
+
* Create createQuery primitive for a specific endpoint
|
|
134
133
|
*/
|
|
135
|
-
function
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
229
|
+
* Create createMutation primitive for a specific endpoint
|
|
261
230
|
*/
|
|
262
|
-
function
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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(
|
|
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(() =>
|
|
252
|
+
setData(() => result.data);
|
|
295
253
|
setLoading(false);
|
|
296
254
|
|
|
297
|
-
|
|
298
|
-
|
|
255
|
+
primitiveOptions?.onSuccess?.(result.data);
|
|
256
|
+
primitiveOptions?.onSettled?.();
|
|
299
257
|
|
|
300
|
-
return
|
|
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
|
-
|
|
307
|
-
|
|
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
|
|
341
|
-
const
|
|
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
|
-
*
|
|
347
|
-
*
|
|
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
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
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
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
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
|
|
464
|
-
return
|
|
392
|
+
const endpoint = current as (options: unknown) => unknown;
|
|
393
|
+
return endpoint(args[0]);
|
|
465
394
|
},
|
|
466
395
|
};
|
|
467
396
|
|
|
468
|
-
|
|
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
|
-
* //
|
|
19
|
-
* const
|
|
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
|
-
* //
|
|
22
|
-
* const
|
|
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
|
|
34
|
-
type
|
|
35
|
+
type MutationPrimitiveOptions,
|
|
36
|
+
type MutationPrimitiveResult,
|
|
35
37
|
type QueryEndpoint,
|
|
36
|
-
type
|
|
37
|
-
type
|
|
38
|
+
type QueryPrimitiveOptions,
|
|
39
|
+
type QueryPrimitiveResult,
|
|
38
40
|
type TypedClient,
|
|
39
41
|
} from "./create.js";
|
|
40
42
|
|