@laguzjs/client 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +30 -2
- package/dist/http.d.ts +9 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +36 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/pool.d.ts +52 -0
- package/dist/pool.d.ts.map +1 -0
- package/dist/pool.js +339 -0
- package/dist/pool.js.map +1 -0
- package/dist/react.d.ts +45 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +127 -0
- package/dist/react.js.map +1 -0
- package/dist/selection.d.ts +68 -0
- package/dist/selection.d.ts.map +1 -0
- package/dist/selection.js +161 -0
- package/dist/selection.js.map +1 -0
- package/package.json +59 -4
- package/src/http.ts +42 -0
- package/src/index.ts +6 -0
- package/src/pool.ts +441 -0
- package/src/react.ts +151 -0
- package/src/selection.ts +187 -0
package/dist/react.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
2
|
+
import { ready } from "./pool.js";
|
|
3
|
+
import { toTree, view } from "./selection.js";
|
|
4
|
+
const IDLE = Object.freeze({ has: {} });
|
|
5
|
+
const unsubscribed = () => () => { };
|
|
6
|
+
// React runs the subscribe step of every useSyncExternalStore in a commit in
|
|
7
|
+
// one synchronous pass, so everything a commit renders lands in one batch.
|
|
8
|
+
// Subscribing happens after paint. Registering during render would start the
|
|
9
|
+
// request a little earlier, but leaks when React discards a concurrent render.
|
|
10
|
+
function useEntry(pool, root, args, fields, minStaleTime = 0) {
|
|
11
|
+
const key = JSON.stringify([root, args, fields]);
|
|
12
|
+
const tree = useMemo(() => toTree(fields), [key]);
|
|
13
|
+
const subscribe = useCallback((cb) => (args ? pool.watch(root, args, tree, cb, minStaleTime) : unsubscribed()), [pool, key]);
|
|
14
|
+
const snap = useSyncExternalStore(subscribe, () => (args ? pool.read(root, args) : IDLE));
|
|
15
|
+
const data = useMemo(() => view(snap.data, fields), [snap.data, key]);
|
|
16
|
+
return { snap, tree, data };
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Read `fields` of `root(args)`. Pass `args: null` to skip the query, for
|
|
20
|
+
* example until the value it depends on has loaded.
|
|
21
|
+
*/
|
|
22
|
+
export function useData(pool, root, args, fields) {
|
|
23
|
+
const { snap, tree, data } = useEntry(pool, root, args, fields);
|
|
24
|
+
return {
|
|
25
|
+
data: data,
|
|
26
|
+
error: snap.error,
|
|
27
|
+
loading: !!args && !snap.error && !ready(snap, tree),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Like {@link useData}, but suspends until the data is ready and throws errors
|
|
32
|
+
* to the nearest error boundary. Call `pool.invalidate(root)` when the boundary
|
|
33
|
+
* resets to retry.
|
|
34
|
+
*
|
|
35
|
+
* Give each component its own `<Suspense>` boundary. React stops rendering the
|
|
36
|
+
* siblings of a component that suspends, so components sharing a boundary are
|
|
37
|
+
* fetched in two requests instead of one.
|
|
38
|
+
*/
|
|
39
|
+
export function useSuspenseData(pool, root, args, fields) {
|
|
40
|
+
// Data that just resolved shouldn't be refetched when the component commits.
|
|
41
|
+
const { snap, tree, data } = useEntry(pool, root, args, fields, 1000);
|
|
42
|
+
if (snap.error)
|
|
43
|
+
throw snap.error;
|
|
44
|
+
if (!ready(snap, tree))
|
|
45
|
+
throw pool.load(root, args, tree);
|
|
46
|
+
return data;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* An infinite list. `page(prev, index)` describes page `index` from the data of
|
|
50
|
+
* the previous page (its cursor), or returns null after the last page.
|
|
51
|
+
*
|
|
52
|
+
* Every page is a regular cache entry: refreshing refetches all loaded pages in
|
|
53
|
+
* one request, and pages whose cursor moved are fetched right after.
|
|
54
|
+
* `prefetch` keeps that many pages loaded ahead, so `loadMore` shows them at once.
|
|
55
|
+
*/
|
|
56
|
+
export function useInfiniteData(pool, page, { prefetch = 0 } = {}) {
|
|
57
|
+
const [shown, setShown] = useState(1);
|
|
58
|
+
// Page n needs page n-1's data for its cursor, so walk the cache in order.
|
|
59
|
+
const pages = [];
|
|
60
|
+
const views = [];
|
|
61
|
+
let end = false;
|
|
62
|
+
for (let i = 0; i < shown + prefetch; i++) {
|
|
63
|
+
const p = page(views[i - 1], i);
|
|
64
|
+
if (!p) {
|
|
65
|
+
end = true;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
pages.push(p);
|
|
69
|
+
const snap = pool.read(p.root, p.args);
|
|
70
|
+
if (!ready(snap, toTree(p.fields)))
|
|
71
|
+
break;
|
|
72
|
+
views.push(view(snap.data, p.fields));
|
|
73
|
+
}
|
|
74
|
+
// Re-render when any page changes; the walk above then reads the new data.
|
|
75
|
+
const notify = useRef(() => { });
|
|
76
|
+
const subscribe = useCallback((cb) => {
|
|
77
|
+
notify.current = cb;
|
|
78
|
+
return () => {
|
|
79
|
+
notify.current = () => { };
|
|
80
|
+
};
|
|
81
|
+
}, []);
|
|
82
|
+
const lastSnaps = useRef([]);
|
|
83
|
+
useSyncExternalStore(subscribe, () => {
|
|
84
|
+
const snaps = pages.map((p) => pool.read(p.root, p.args));
|
|
85
|
+
const prev = lastSnaps.current;
|
|
86
|
+
if (snaps.length === prev.length && snaps.every((s, i) => s === prev[i]))
|
|
87
|
+
return prev;
|
|
88
|
+
lastSnaps.current = snaps;
|
|
89
|
+
return snaps;
|
|
90
|
+
});
|
|
91
|
+
// Keep one subscription per page for as long as the page is in the list.
|
|
92
|
+
// Resubscribing every page when the list grows would count as a new mount
|
|
93
|
+
// for each of them and refetch them all.
|
|
94
|
+
const watches = useRef(new Map());
|
|
95
|
+
const key = JSON.stringify(pages);
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
const next = new Map();
|
|
98
|
+
for (const p of pages) {
|
|
99
|
+
const k = JSON.stringify(p);
|
|
100
|
+
next.set(k, watches.current.get(k) ?? pool.watch(p.root, p.args, p.fields, () => notify.current()));
|
|
101
|
+
watches.current.delete(k);
|
|
102
|
+
}
|
|
103
|
+
// Whatever is left belongs to pages that dropped out, e.g. after a cursor moved.
|
|
104
|
+
watches.current.forEach((unwatch) => unwatch());
|
|
105
|
+
watches.current = next;
|
|
106
|
+
}, [pool, key]);
|
|
107
|
+
useEffect(() => () => {
|
|
108
|
+
watches.current.forEach((unwatch) => unwatch());
|
|
109
|
+
watches.current = new Map();
|
|
110
|
+
}, [pool]);
|
|
111
|
+
const loaded = Math.min(views.length, shown);
|
|
112
|
+
const hasMore = loaded === shown &&
|
|
113
|
+
(views.length > shown || (!end && page(views[loaded - 1], loaded) !== null));
|
|
114
|
+
const error = pages.map((p) => pool.read(p.root, p.args).error).find(Boolean);
|
|
115
|
+
return {
|
|
116
|
+
pages: views.slice(0, shown),
|
|
117
|
+
hasMore,
|
|
118
|
+
loading: views.length === 0 && !error,
|
|
119
|
+
loadingMore: views.length > 0 && views.length < shown && !error,
|
|
120
|
+
error,
|
|
121
|
+
loadMore: () => {
|
|
122
|
+
if (hasMore)
|
|
123
|
+
setShown(loaded + 1);
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a consumer selects on a root field.
|
|
3
|
+
*
|
|
4
|
+
* - `"name"`: a scalar field
|
|
5
|
+
* - `{ projects: ["id"] }`: a nested selection
|
|
6
|
+
* - `{ projects: { args: { first: 3 }, fields: ["id"] } }`: a field with arguments
|
|
7
|
+
* - `{ recent: { field: "projects", args: { first: 3 }, fields: ["id"] } }`: the same field under another name
|
|
8
|
+
*/
|
|
9
|
+
export type Fields = (string | {
|
|
10
|
+
[out: string]: Fields | FieldArg;
|
|
11
|
+
})[];
|
|
12
|
+
export type FieldArg = {
|
|
13
|
+
field?: string;
|
|
14
|
+
args?: Record<string, unknown>;
|
|
15
|
+
fields?: Fields;
|
|
16
|
+
};
|
|
17
|
+
/** A merged selection. Keys are `field` or `field(args)`; a leaf is `{}`. */
|
|
18
|
+
export type Tree = {
|
|
19
|
+
[key: string]: Tree;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* An argument value read from another root field's result, for queries that
|
|
23
|
+
* depend on each other. See {@link ref}.
|
|
24
|
+
*/
|
|
25
|
+
export type Ref = {
|
|
26
|
+
$ref: [root: string, args: object, path: string];
|
|
27
|
+
};
|
|
28
|
+
export declare function name(s: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Use the value at `path` in `root(args)` as an argument:
|
|
31
|
+
* `ref("user", { id: 1 }, "teamId")`. Refs can be nested.
|
|
32
|
+
*/
|
|
33
|
+
export declare function ref(root: string, args: object, path: string): Ref;
|
|
34
|
+
export declare const isRef: (v: unknown) => v is Ref;
|
|
35
|
+
export declare function collectRefs(v: unknown): Ref[];
|
|
36
|
+
export declare const pathTree: (path: string) => Tree;
|
|
37
|
+
export declare const getPath: (data: any, path: string) => unknown;
|
|
38
|
+
export declare function toTree(fields: Fields): Tree;
|
|
39
|
+
export declare function merge(a: Tree, b: Tree): Tree;
|
|
40
|
+
/** The part of `want` that `have` doesn't cover, or null. */
|
|
41
|
+
export declare function diff(want: Tree, have: Tree): Tree | null;
|
|
42
|
+
/**
|
|
43
|
+
* True when fetching `tree` would add fields inside a list we already hold.
|
|
44
|
+
* Merging two fetches of a list by index is wrong if the list changed in
|
|
45
|
+
* between, so the caller refetches the whole selection instead.
|
|
46
|
+
*/
|
|
47
|
+
export declare function touchesList(tree: Tree, data: any): boolean;
|
|
48
|
+
/** The key under which `field(args)` is stored in raw entry data. */
|
|
49
|
+
export declare const fieldKey: (field: string, args?: object) => string;
|
|
50
|
+
/** Project raw entry data onto one consumer's selection, with its own output names. */
|
|
51
|
+
export declare function view(data: any, fields: Fields): any;
|
|
52
|
+
export declare function printTree(tree: Tree): string;
|
|
53
|
+
/**
|
|
54
|
+
* Print a JS value as a GraphQL literal. JSON string escaping is valid GraphQL
|
|
55
|
+
* string escaping. Object keys are sorted so equal arguments print the same.
|
|
56
|
+
* Refs print through `onRef`, or as a readable key when it is absent.
|
|
57
|
+
* Enum values are not supported yet: strings always print quoted.
|
|
58
|
+
*/
|
|
59
|
+
export declare function printValue(v: any, onRef?: (r: Ref) => string): string;
|
|
60
|
+
export declare function printArgs(args: object, onRef?: (r: Ref) => string): string;
|
|
61
|
+
/** `root(args) { fields }`: the text the pool sends for a single consumer. */
|
|
62
|
+
export declare const printField: (root: string, args: object, fields: Fields) => string;
|
|
63
|
+
/**
|
|
64
|
+
* Merge a partial result into cached data. Two arrays only meet here when they
|
|
65
|
+
* come from the same fetch shape, because `touchesList` forces whole refetches.
|
|
66
|
+
*/
|
|
67
|
+
export declare function mergeData(a: any, b: any): any;
|
|
68
|
+
//# sourceMappingURL=selection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection.d.ts","sourceRoot":"","sources":["../src/selection.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,MAAM,MAAM,GAAG,CAAC,MAAM,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAA;CAAE,CAAC,EAAE,CAAC;AACvE,MAAM,MAAM,QAAQ,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3F,6EAA6E;AAC7E,MAAM,MAAM,IAAI,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAE3C;;;GAGG;AACH,MAAM,MAAM,GAAG,GAAG;IAAE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC;AAIvE,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAGtC;AAED;;;GAGG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,GAAG,CAMjE;AAED,eAAO,MAAM,KAAK,MAAO,OAAO,KAAG,CAAC,IAAI,GAAyD,CAAC;AAElG,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,GAAG,EAAE,CAI7C;AAED,eAAO,MAAM,QAAQ,SAAU,MAAM,KAAG,IACiC,CAAC;AAE1E,eAAO,MAAM,OAAO,SAAU,GAAG,QAAQ,MAAM,KAAG,OACU,CAAC;AAE7D,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAe3C;AAED,wBAAgB,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,GAAG,IAAI,CAI5C;AAED,6DAA6D;AAC7D,wBAAgB,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAOxD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAI1D;AAkBD,qEAAqE;AACrE,eAAO,MAAM,QAAQ,UAAW,MAAM,SAAQ,MAAM,KAAQ,MACvB,CAAC;AAEtC,uFAAuF;AACvF,wBAAgB,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG,CAmBnD;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAK5C;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,MAAM,GAAG,MAAM,CAWrE;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,MAAM,GAAG,MAAM,CAG1E;AAED,8EAA8E;AAC9E,eAAO,MAAM,UAAU,SAAU,MAAM,QAAQ,MAAM,UAAU,MAAM,KAAG,MACd,CAAC;AAE3D;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,GAAG,GAAG,CAQ7C"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
const NAME = /^[_A-Za-z][_0-9A-Za-z]*$/;
|
|
2
|
+
export function name(s) {
|
|
3
|
+
if (!NAME.test(s))
|
|
4
|
+
throw new Error(`invalid GraphQL name: ${s}`);
|
|
5
|
+
return s;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Use the value at `path` in `root(args)` as an argument:
|
|
9
|
+
* `ref("user", { id: 1 }, "teamId")`. Refs can be nested.
|
|
10
|
+
*/
|
|
11
|
+
export function ref(root, args, path) {
|
|
12
|
+
for (const segment of path.split(".")) {
|
|
13
|
+
if (name(segment).includes("__"))
|
|
14
|
+
throw new Error(`ref path segments can't contain "__": ${segment}`);
|
|
15
|
+
}
|
|
16
|
+
return { $ref: [name(root), args, path] };
|
|
17
|
+
}
|
|
18
|
+
export const isRef = (v) => v !== null && typeof v === "object" && "$ref" in v;
|
|
19
|
+
export function collectRefs(v) {
|
|
20
|
+
if (isRef(v))
|
|
21
|
+
return [v];
|
|
22
|
+
if (v && typeof v === "object")
|
|
23
|
+
return Object.values(v).flatMap(collectRefs);
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
export const pathTree = (path) => path.split(".").reduceRight((tree, key) => ({ [key]: tree }), {});
|
|
27
|
+
export const getPath = (data, path) => path.split(".").reduce((value, key) => value?.[key], data);
|
|
28
|
+
export function toTree(fields) {
|
|
29
|
+
const tree = {};
|
|
30
|
+
for (const f of fields) {
|
|
31
|
+
if (typeof f === "string") {
|
|
32
|
+
tree[name(f)] ??= {};
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
for (const [out, v] of Object.entries(f)) {
|
|
36
|
+
name(out);
|
|
37
|
+
const key = Array.isArray(v) ? out : canonical(v.field ?? out, v.args ?? {});
|
|
38
|
+
const sub = Array.isArray(v) ? v : (v.fields ?? []);
|
|
39
|
+
tree[key] = merge(tree[key] ?? {}, toTree(sub));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return tree;
|
|
43
|
+
}
|
|
44
|
+
export function merge(a, b) {
|
|
45
|
+
const tree = { ...a };
|
|
46
|
+
for (const key in b)
|
|
47
|
+
tree[key] = merge(tree[key] ?? {}, b[key]);
|
|
48
|
+
return tree;
|
|
49
|
+
}
|
|
50
|
+
/** The part of `want` that `have` doesn't cover, or null. */
|
|
51
|
+
export function diff(want, have) {
|
|
52
|
+
let missing = null;
|
|
53
|
+
for (const key in want) {
|
|
54
|
+
const sub = have[key] ? diff(want[key], have[key]) : want[key];
|
|
55
|
+
if (sub)
|
|
56
|
+
(missing ??= {})[key] = sub;
|
|
57
|
+
}
|
|
58
|
+
return missing;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* True when fetching `tree` would add fields inside a list we already hold.
|
|
62
|
+
* Merging two fetches of a list by index is wrong if the list changed in
|
|
63
|
+
* between, so the caller refetches the whole selection instead.
|
|
64
|
+
*/
|
|
65
|
+
export function touchesList(tree, data) {
|
|
66
|
+
if (!data || typeof data !== "object")
|
|
67
|
+
return false;
|
|
68
|
+
if (Array.isArray(data))
|
|
69
|
+
return true;
|
|
70
|
+
return Object.keys(tree).some((key) => touchesList(tree[key], data[responseKey(key)]));
|
|
71
|
+
}
|
|
72
|
+
const canonical = (field, args) => name(field) + printArgs(args);
|
|
73
|
+
// FNV-1a in base 36: a short, stable alias suffix.
|
|
74
|
+
function hash(s) {
|
|
75
|
+
let h = 2166136261;
|
|
76
|
+
for (let i = 0; i < s.length; i++)
|
|
77
|
+
h = Math.imul(h ^ s.charCodeAt(i), 16777619);
|
|
78
|
+
return (h >>> 0).toString(36);
|
|
79
|
+
}
|
|
80
|
+
// Fields with arguments are aliased: two consumers may select the same field
|
|
81
|
+
// with different arguments, and both results have to coexist in one entry.
|
|
82
|
+
function responseKey(key) {
|
|
83
|
+
const paren = key.indexOf("(");
|
|
84
|
+
return paren < 0 ? key : `${key.slice(0, paren)}_${hash(key)}`;
|
|
85
|
+
}
|
|
86
|
+
/** The key under which `field(args)` is stored in raw entry data. */
|
|
87
|
+
export const fieldKey = (field, args = {}) => responseKey(canonical(field, args));
|
|
88
|
+
/** Project raw entry data onto one consumer's selection, with its own output names. */
|
|
89
|
+
export function view(data, fields) {
|
|
90
|
+
if (data == null || typeof data !== "object")
|
|
91
|
+
return data;
|
|
92
|
+
if (Array.isArray(data))
|
|
93
|
+
return data.map((item) => view(item, fields));
|
|
94
|
+
const out = {};
|
|
95
|
+
for (const f of fields) {
|
|
96
|
+
if (typeof f === "string") {
|
|
97
|
+
out[f] = data[f];
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
for (const [key, v] of Object.entries(f)) {
|
|
101
|
+
if (Array.isArray(v)) {
|
|
102
|
+
out[key] = view(data[key], v);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
const raw = data[fieldKey(v.field ?? key, v.args)];
|
|
106
|
+
out[key] = v.fields ? view(raw, v.fields) : raw;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
export function printTree(tree) {
|
|
113
|
+
const keys = Object.keys(tree);
|
|
114
|
+
if (!keys.length)
|
|
115
|
+
return "";
|
|
116
|
+
const field = (key) => (responseKey(key) === key ? key : `${responseKey(key)}: ${key}`);
|
|
117
|
+
return ` { ${keys.map((key) => field(key) + printTree(tree[key])).join(" ")} }`;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Print a JS value as a GraphQL literal. JSON string escaping is valid GraphQL
|
|
121
|
+
* string escaping. Object keys are sorted so equal arguments print the same.
|
|
122
|
+
* Refs print through `onRef`, or as a readable key when it is absent.
|
|
123
|
+
* Enum values are not supported yet: strings always print quoted.
|
|
124
|
+
*/
|
|
125
|
+
export function printValue(v, onRef) {
|
|
126
|
+
if (isRef(v))
|
|
127
|
+
return onRef ? onRef(v) : `@${v.$ref[0]}${printArgs(v.$ref[1])}.${v.$ref[2]}`;
|
|
128
|
+
if (typeof v?.toJSON === "function")
|
|
129
|
+
v = v.toJSON();
|
|
130
|
+
if (Array.isArray(v))
|
|
131
|
+
return `[${v.map((x) => printValue(x, onRef)).join(", ")}]`;
|
|
132
|
+
if (v && typeof v === "object") {
|
|
133
|
+
const keys = Object.keys(v)
|
|
134
|
+
.filter((key) => v[key] !== undefined)
|
|
135
|
+
.sort();
|
|
136
|
+
return `{${keys.map((key) => `${name(key)}: ${printValue(v[key], onRef)}`).join(", ")}}`;
|
|
137
|
+
}
|
|
138
|
+
return JSON.stringify(v ?? null);
|
|
139
|
+
}
|
|
140
|
+
export function printArgs(args, onRef) {
|
|
141
|
+
const inner = printValue(args, onRef).slice(1, -1);
|
|
142
|
+
return inner ? `(${inner})` : "";
|
|
143
|
+
}
|
|
144
|
+
/** `root(args) { fields }`: the text the pool sends for a single consumer. */
|
|
145
|
+
export const printField = (root, args, fields) => name(root) + printArgs(args) + printTree(toTree(fields));
|
|
146
|
+
/**
|
|
147
|
+
* Merge a partial result into cached data. Two arrays only meet here when they
|
|
148
|
+
* come from the same fetch shape, because `touchesList` forces whole refetches.
|
|
149
|
+
*/
|
|
150
|
+
export function mergeData(a, b) {
|
|
151
|
+
if (Array.isArray(a) && Array.isArray(b))
|
|
152
|
+
return b.map((item, i) => mergeData(a[i], item));
|
|
153
|
+
if (a && b && typeof a === "object" && typeof b === "object" && !Array.isArray(b)) {
|
|
154
|
+
const out = { ...a };
|
|
155
|
+
for (const key in b)
|
|
156
|
+
out[key] = mergeData(a[key], b[key]);
|
|
157
|
+
return out;
|
|
158
|
+
}
|
|
159
|
+
return b;
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=selection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection.js","sourceRoot":"","sources":["../src/selection.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,61 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@laguzjs/client",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Batch the GraphQL data needs of a React commit into one request, streamed back field by field.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"batching",
|
|
7
|
+
"data-fetching",
|
|
8
|
+
"graphql",
|
|
9
|
+
"hooks",
|
|
10
|
+
"react",
|
|
11
|
+
"streaming"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://laguz.maxenceleguery.net",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/maxenceleguery/laguz/issues"
|
|
16
|
+
},
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"author": "Maxence Leguery",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/maxenceleguery/laguz.git",
|
|
22
|
+
"directory": "packages/client"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"src",
|
|
27
|
+
"!src/**/*.test.ts"
|
|
28
|
+
],
|
|
29
|
+
"type": "module",
|
|
30
|
+
"sideEffects": false,
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"source": "./src/index.ts",
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"bun": "./src/index.ts",
|
|
36
|
+
"default": "./dist/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./react": {
|
|
39
|
+
"source": "./src/react.ts",
|
|
40
|
+
"types": "./dist/react.d.ts",
|
|
41
|
+
"bun": "./src/react.ts",
|
|
42
|
+
"default": "./dist/react.js"
|
|
43
|
+
},
|
|
44
|
+
"./package.json": "./package.json"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public",
|
|
48
|
+
"provenance": true
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"react": ">=18"
|
|
55
|
+
},
|
|
56
|
+
"peerDependenciesMeta": {
|
|
57
|
+
"react": {
|
|
58
|
+
"optional": true
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
package/src/http.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Fetcher, Response as GqlResponse } from "./pool.ts";
|
|
2
|
+
|
|
3
|
+
export type HttpOptions = {
|
|
4
|
+
/** Ask for one NDJSON line per root field. Falls back to JSON if the server doesn't stream. */
|
|
5
|
+
stream?: boolean;
|
|
6
|
+
headers?: Record<string, string>;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/** A fetcher that POSTs `{ query }` to `url`. */
|
|
10
|
+
export function httpFetcher(url: string, options: HttpOptions = {}): Fetcher {
|
|
11
|
+
return (query) => {
|
|
12
|
+
const res = fetch(url, {
|
|
13
|
+
method: "POST",
|
|
14
|
+
headers: {
|
|
15
|
+
"content-type": "application/json",
|
|
16
|
+
accept: options.stream ? "application/x-ndjson, application/json" : "application/json",
|
|
17
|
+
...options.headers,
|
|
18
|
+
},
|
|
19
|
+
body: JSON.stringify({ query }),
|
|
20
|
+
});
|
|
21
|
+
return options.stream ? lines(res) : res.then((r) => r.json());
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function* lines(pending: Promise<Response>): AsyncGenerator<GqlResponse> {
|
|
26
|
+
const res = await pending;
|
|
27
|
+
if (!res.headers.get("content-type")?.includes("ndjson")) {
|
|
28
|
+
yield await res.json();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
let buffer = "";
|
|
32
|
+
for await (const text of res.body!.pipeThrough(new TextDecoderStream())) {
|
|
33
|
+
buffer += text;
|
|
34
|
+
let newline;
|
|
35
|
+
while ((newline = buffer.indexOf("\n")) >= 0) {
|
|
36
|
+
const line = buffer.slice(0, newline);
|
|
37
|
+
buffer = buffer.slice(newline + 1);
|
|
38
|
+
if (line.trim()) yield JSON.parse(line);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (buffer.trim()) yield JSON.parse(buffer);
|
|
42
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createPool, ready } from "./pool.ts";
|
|
2
|
+
export type { Fetcher, GqlError, Match, Options, Pool, Response, Snapshot } from "./pool.ts";
|
|
3
|
+
export { fieldKey, printField, ref, toTree, view } from "./selection.ts";
|
|
4
|
+
export type { FieldArg, Fields, Ref, Tree } from "./selection.ts";
|
|
5
|
+
export { httpFetcher } from "./http.ts";
|
|
6
|
+
export type { HttpOptions } from "./http.ts";
|