@pylo/react 0.10.1 → 0.11.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/dist/{chunk-FKUNSR3E.js → chunk-C3KAJKBM.js} +309 -52
- package/dist/{chunk-BPRHGSZF.js → chunk-OPMVPQER.js} +23 -4
- package/dist/hooks.d.ts +22 -6
- package/dist/hooks.js +2 -2
- package/dist/index.d.ts +4 -5
- package/dist/index.js +4 -2
- package/dist/{provider-4DBl_6p0.d.ts → provider-DF_-sV-r.d.ts} +20 -3
- package/dist/provider.d.ts +1 -2
- package/dist/provider.js +3 -1
- package/package.json +6 -2
- package/dist/transport-55cSrvQo.d.ts +0 -14
|
@@ -1,14 +1,131 @@
|
|
|
1
1
|
import {
|
|
2
|
+
usePyloRealtime,
|
|
2
3
|
usePyloTransport
|
|
3
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-OPMVPQER.js";
|
|
5
|
+
|
|
6
|
+
// src/hooks.ts
|
|
7
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
8
|
+
|
|
9
|
+
// src/live.ts
|
|
10
|
+
var IMMUTABLE_SORT_FIELDS = /* @__PURE__ */ new Set(["id", "integer_id", "created_at"]);
|
|
11
|
+
function applyRowEvent(row, event) {
|
|
12
|
+
if (event.removed) return { data: null, refetch: false };
|
|
13
|
+
return { data: { ...row, ...event.data }, refetch: false };
|
|
14
|
+
}
|
|
15
|
+
function applyListEvent(list, event, sortby, placement) {
|
|
16
|
+
const result = applyPagesEvent({ pages: [list], pageParams: [1] }, event, sortby, placement);
|
|
17
|
+
return { data: result.data.pages[0], refetch: result.refetch };
|
|
18
|
+
}
|
|
19
|
+
function applyPagesEvent(data, event, sortby, placement = { firstPage: true, keepOverflow: true }) {
|
|
20
|
+
const pageIndex = data.pages.findIndex((page) => page.data.some((row) => row.id === event.id));
|
|
21
|
+
if (event.removed) {
|
|
22
|
+
if (pageIndex === -1) return { data, refetch: false };
|
|
23
|
+
return {
|
|
24
|
+
data: updatePage(
|
|
25
|
+
data,
|
|
26
|
+
pageIndex,
|
|
27
|
+
(page) => adjustTotal({ ...page, data: page.data.filter((row) => row.id !== event.id) }, pageIndex, -1)
|
|
28
|
+
),
|
|
29
|
+
refetch: "later"
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const incoming = event.data;
|
|
33
|
+
if (pageIndex !== -1) {
|
|
34
|
+
const page = data.pages[pageIndex];
|
|
35
|
+
const current = page.data.find((row) => row.id === event.id);
|
|
36
|
+
const merged = { ...current, ...incoming };
|
|
37
|
+
const sort = sortChange(current, merged, sortby);
|
|
38
|
+
if (sort === "changed") return { data, refetch: "soon" };
|
|
39
|
+
return {
|
|
40
|
+
data: updatePage(data, pageIndex, (p) => ({
|
|
41
|
+
...p,
|
|
42
|
+
data: p.data.map((row) => row.id === event.id ? merged : row)
|
|
43
|
+
})),
|
|
44
|
+
refetch: sort === "unknown" ? "later" : false
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (event.kind === "create" && isNewestFirst(sortby) && data.pages.length > 0) {
|
|
48
|
+
if (!placement.firstPage) return { data, refetch: "later" };
|
|
49
|
+
const inserted = updatePage(
|
|
50
|
+
data,
|
|
51
|
+
0,
|
|
52
|
+
(page2) => adjustTotal({ ...page2, data: [incoming, ...page2.data] }, 0, 1)
|
|
53
|
+
);
|
|
54
|
+
if (placement.keepOverflow) return { data: inserted, refetch: false };
|
|
55
|
+
const { perPage } = placement;
|
|
56
|
+
if (perPage === void 0) return { data: inserted, refetch: "later" };
|
|
57
|
+
const page = inserted.pages[0];
|
|
58
|
+
if (page.data.length <= perPage) return { data: inserted, refetch: false };
|
|
59
|
+
const trimmed = recountPages({ ...page, data: page.data.slice(0, perPage) }, perPage);
|
|
60
|
+
if (!trimmed) return { data: inserted, refetch: "later" };
|
|
61
|
+
return { data: updatePage(inserted, 0, () => trimmed), refetch: false };
|
|
62
|
+
}
|
|
63
|
+
return { data, refetch: "later" };
|
|
64
|
+
}
|
|
65
|
+
function updatePage(data, index, update) {
|
|
66
|
+
return { ...data, pages: data.pages.map((page, i) => i === index ? update(page) : page) };
|
|
67
|
+
}
|
|
68
|
+
function recountPages(page, perPage) {
|
|
69
|
+
const pagination = page.pagination;
|
|
70
|
+
if (!pagination) return page;
|
|
71
|
+
const total = pagination.total;
|
|
72
|
+
const dependsOnTotal = "last_page" in pagination || "has_more_pages" in pagination;
|
|
73
|
+
if (!dependsOnTotal) return page;
|
|
74
|
+
if (typeof total !== "number") return null;
|
|
75
|
+
const lastPage = Math.max(1, Math.ceil(total / perPage));
|
|
76
|
+
return {
|
|
77
|
+
...page,
|
|
78
|
+
pagination: {
|
|
79
|
+
...pagination,
|
|
80
|
+
..."last_page" in pagination ? { last_page: lastPage } : {},
|
|
81
|
+
..."has_more_pages" in pagination ? { has_more_pages: lastPage > 1 } : {}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function adjustTotal(page, pageIndex, delta) {
|
|
86
|
+
var _a;
|
|
87
|
+
const total = (_a = page.pagination) == null ? void 0 : _a.total;
|
|
88
|
+
if (pageIndex !== 0 || typeof total !== "number") return page;
|
|
89
|
+
return { ...page, pagination: { ...page.pagination, total: Math.max(0, total + delta) } };
|
|
90
|
+
}
|
|
91
|
+
function isNewestFirst(sortby) {
|
|
92
|
+
const first = sortby == null ? void 0 : sortby[0];
|
|
93
|
+
if (!first) return true;
|
|
94
|
+
return (first.field === "integer_id" || first.field === "created_at") && first.order === "desc";
|
|
95
|
+
}
|
|
96
|
+
function sortChange(before, after, sortby) {
|
|
97
|
+
let unknown = false;
|
|
98
|
+
for (const { field } of sortby != null ? sortby : []) {
|
|
99
|
+
if (IMMUTABLE_SORT_FIELDS.has(field)) continue;
|
|
100
|
+
const previous = readPath(before, field);
|
|
101
|
+
const next = readPath(after, field);
|
|
102
|
+
if (next === void 0) {
|
|
103
|
+
unknown = true;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (JSON.stringify(previous) !== JSON.stringify(next)) return "changed";
|
|
107
|
+
}
|
|
108
|
+
return unknown ? "unknown" : "unchanged";
|
|
109
|
+
}
|
|
110
|
+
function readPath(row, path) {
|
|
111
|
+
let value = row;
|
|
112
|
+
for (const key of path.split(".")) {
|
|
113
|
+
if (value && typeof value === "object" && "data" in value && !(key in value)) {
|
|
114
|
+
value = value.data;
|
|
115
|
+
}
|
|
116
|
+
if (!value || typeof value !== "object") return void 0;
|
|
117
|
+
value = value[key];
|
|
118
|
+
}
|
|
119
|
+
return value;
|
|
120
|
+
}
|
|
4
121
|
|
|
5
122
|
// src/hooks.ts
|
|
6
|
-
import { useCallback, useRef, useState } from "react";
|
|
7
123
|
import {
|
|
8
124
|
useQuery,
|
|
9
125
|
useInfiniteQuery,
|
|
10
126
|
useMutation,
|
|
11
|
-
useQueryClient
|
|
127
|
+
useQueryClient,
|
|
128
|
+
hashKey
|
|
12
129
|
} from "@tanstack/react-query";
|
|
13
130
|
import {
|
|
14
131
|
buildListQuery,
|
|
@@ -38,13 +155,91 @@ import {
|
|
|
38
155
|
uploadToUrl,
|
|
39
156
|
toUploadPart
|
|
40
157
|
} from "@pylo/core";
|
|
158
|
+
function patchableWith(headers) {
|
|
159
|
+
return headers === void 0 || Object.keys(headers).length === 0;
|
|
160
|
+
}
|
|
161
|
+
function withId(select) {
|
|
162
|
+
return { ...select, id: true };
|
|
163
|
+
}
|
|
164
|
+
function dedupeById(rows) {
|
|
165
|
+
const seen = /* @__PURE__ */ new Set();
|
|
166
|
+
return rows.filter((row) => {
|
|
167
|
+
const id = row == null ? void 0 : row.id;
|
|
168
|
+
if (id === void 0) return true;
|
|
169
|
+
if (seen.has(id)) return false;
|
|
170
|
+
seen.add(id);
|
|
171
|
+
return true;
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
var MAX_PAGES_TO_RESYNC = 5;
|
|
175
|
+
function invalidateEntity(queryClient, realtime, entity) {
|
|
176
|
+
const live = realtime !== null && realtime.client.isLive(entity);
|
|
177
|
+
return queryClient.invalidateQueries({
|
|
178
|
+
queryKey: ["pylo", entity],
|
|
179
|
+
...live ? { predicate: (query) => !realtime.liveQueries.has(query.queryHash) } : {}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
41
182
|
var INFINITE_PAGINATION_SELECT = { current_page: true, has_more_pages: true };
|
|
42
183
|
function createPyloHooks(options) {
|
|
43
|
-
var _a;
|
|
184
|
+
var _a, _b;
|
|
44
185
|
const useTransport = (_a = options == null ? void 0 : options.useTransport) != null ? _a : usePyloTransport;
|
|
186
|
+
const useRealtime = (_b = options == null ? void 0 : options.useRealtime) != null ? _b : usePyloRealtime;
|
|
45
187
|
const globalHeaders = options == null ? void 0 : options.headers;
|
|
188
|
+
function useLive(entity, queryKey, subscription, apply, beforeResync) {
|
|
189
|
+
const realtime = useRealtime();
|
|
190
|
+
const queryClient = useQueryClient();
|
|
191
|
+
const keyHash = hashKey(queryKey);
|
|
192
|
+
const latest = useRef({ queryKey, subscription, apply, beforeResync });
|
|
193
|
+
latest.current = { queryKey, subscription, apply, beforeResync };
|
|
194
|
+
const enabled = realtime !== null && subscription !== null;
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
var _a2;
|
|
197
|
+
if (!enabled) return;
|
|
198
|
+
const { queryKey: queryKey2, subscription: subscription2 } = latest.current;
|
|
199
|
+
let timer;
|
|
200
|
+
let fireAt = Infinity;
|
|
201
|
+
const scheduleRefetch = (delayMs) => {
|
|
202
|
+
const at = Date.now() + delayMs;
|
|
203
|
+
if (timer !== void 0 && fireAt <= at) return;
|
|
204
|
+
clearTimeout(timer);
|
|
205
|
+
fireAt = at;
|
|
206
|
+
timer = setTimeout(() => {
|
|
207
|
+
timer = void 0;
|
|
208
|
+
fireAt = Infinity;
|
|
209
|
+
void queryClient.invalidateQueries({ queryKey: queryKey2, exact: true });
|
|
210
|
+
}, delayMs);
|
|
211
|
+
};
|
|
212
|
+
const counts = realtime.liveQueries;
|
|
213
|
+
counts.set(keyHash, ((_a2 = counts.get(keyHash)) != null ? _a2 : 0) + 1);
|
|
214
|
+
const sub = realtime.client.subscribe(entity, subscription2, {
|
|
215
|
+
onEvent: (event) => {
|
|
216
|
+
let refetch = false;
|
|
217
|
+
queryClient.setQueryData(queryKey2, (old) => {
|
|
218
|
+
if (old === void 0) return old;
|
|
219
|
+
const result = latest.current.apply(old, event);
|
|
220
|
+
refetch = result.refetch;
|
|
221
|
+
return result.data;
|
|
222
|
+
});
|
|
223
|
+
if (refetch) scheduleRefetch(refetch === "soon" ? 250 : 1500);
|
|
224
|
+
},
|
|
225
|
+
onResync: () => {
|
|
226
|
+
var _a3, _b2;
|
|
227
|
+
(_b2 = (_a3 = latest.current).beforeResync) == null ? void 0 : _b2.call(_a3);
|
|
228
|
+
void queryClient.invalidateQueries({ queryKey: queryKey2, exact: true });
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
return () => {
|
|
232
|
+
var _a3;
|
|
233
|
+
clearTimeout(timer);
|
|
234
|
+
sub.unsubscribe();
|
|
235
|
+
const left = ((_a3 = counts.get(keyHash)) != null ? _a3 : 1) - 1;
|
|
236
|
+
if (left > 0) counts.set(keyHash, left);
|
|
237
|
+
else counts.delete(keyHash);
|
|
238
|
+
};
|
|
239
|
+
}, [enabled, realtime, queryClient, entity, keyHash]);
|
|
240
|
+
}
|
|
46
241
|
function usePyloList(entity, queryOptions) {
|
|
47
|
-
var _a2,
|
|
242
|
+
var _a2, _b2, _c;
|
|
48
243
|
const transport = useTransport();
|
|
49
244
|
const merged = mergeHeaders(globalHeaders, queryOptions == null ? void 0 : queryOptions.headers);
|
|
50
245
|
const queryKey = [
|
|
@@ -61,17 +256,30 @@ function createPyloHooks(options) {
|
|
|
61
256
|
const result = useQuery({
|
|
62
257
|
queryKey,
|
|
63
258
|
queryFn: async () => {
|
|
64
|
-
const { query, variables } = buildListQuery(
|
|
65
|
-
|
|
66
|
-
queryOptions
|
|
67
|
-
);
|
|
259
|
+
const { query, variables } = buildListQuery(entity, {
|
|
260
|
+
...queryOptions,
|
|
261
|
+
select: withId(queryOptions.select)
|
|
262
|
+
});
|
|
68
263
|
const data = await transport(query, variables, merged);
|
|
69
264
|
const listKey = `${entity}List`;
|
|
70
265
|
return data[listKey];
|
|
71
266
|
}
|
|
72
267
|
});
|
|
268
|
+
const patchable = patchableWith(merged);
|
|
269
|
+
useLive(
|
|
270
|
+
entity,
|
|
271
|
+
queryKey,
|
|
272
|
+
queryOptions.live ? { select: queryOptions.select, filter: queryOptions.filter } : null,
|
|
273
|
+
(data, event) => {
|
|
274
|
+
var _a3, _b3, _c2, _d;
|
|
275
|
+
return patchable ? applyListEvent(data, event, (_a3 = queryOptions.filter) == null ? void 0 : _a3.sortby, {
|
|
276
|
+
firstPage: ((_c2 = (_b3 = queryOptions.pagination) == null ? void 0 : _b3.page) != null ? _c2 : 1) === 1,
|
|
277
|
+
perPage: (_d = queryOptions.pagination) == null ? void 0 : _d.per_page
|
|
278
|
+
}) : { data, refetch: "later" };
|
|
279
|
+
}
|
|
280
|
+
);
|
|
73
281
|
return {
|
|
74
|
-
data: (
|
|
282
|
+
data: (_b2 = (_a2 = result.data) == null ? void 0 : _a2.data) != null ? _b2 : void 0,
|
|
75
283
|
..."_pagination" in queryOptions.select && { pagination: (_c = result.data) == null ? void 0 : _c.pagination },
|
|
76
284
|
isLoading: result.isLoading,
|
|
77
285
|
error: result.error,
|
|
@@ -81,25 +289,41 @@ function createPyloHooks(options) {
|
|
|
81
289
|
function usePyloInfiniteList(entity, infiniteOptions) {
|
|
82
290
|
var _a2;
|
|
83
291
|
const transport = useTransport();
|
|
292
|
+
const queryClient = useQueryClient();
|
|
84
293
|
const perPage = (_a2 = infiniteOptions == null ? void 0 : infiniteOptions.perPage) != null ? _a2 : 20;
|
|
85
294
|
const merged = mergeHeaders(globalHeaders, infiniteOptions == null ? void 0 : infiniteOptions.headers);
|
|
295
|
+
const queryKey = [
|
|
296
|
+
"pylo",
|
|
297
|
+
entity,
|
|
298
|
+
"infiniteList",
|
|
299
|
+
{
|
|
300
|
+
perPage,
|
|
301
|
+
filter: infiniteOptions == null ? void 0 : infiniteOptions.filter,
|
|
302
|
+
select: infiniteOptions == null ? void 0 : infiniteOptions.select,
|
|
303
|
+
headers: merged
|
|
304
|
+
}
|
|
305
|
+
];
|
|
306
|
+
const patchable = patchableWith(merged);
|
|
307
|
+
useLive(
|
|
308
|
+
entity,
|
|
309
|
+
queryKey,
|
|
310
|
+
infiniteOptions.live ? { select: infiniteOptions.select, filter: infiniteOptions.filter } : null,
|
|
311
|
+
(data, event) => {
|
|
312
|
+
var _a3;
|
|
313
|
+
return patchable ? applyPagesEvent(data, event, (_a3 = infiniteOptions.filter) == null ? void 0 : _a3.sortby) : { data, refetch: "later" };
|
|
314
|
+
},
|
|
315
|
+
() => queryClient.setQueryData(
|
|
316
|
+
queryKey,
|
|
317
|
+
(old) => old && old.pages.length > MAX_PAGES_TO_RESYNC ? { pages: old.pages.slice(0, 1), pageParams: old.pageParams.slice(0, 1) } : old
|
|
318
|
+
)
|
|
319
|
+
);
|
|
86
320
|
return useInfiniteQuery({
|
|
87
|
-
queryKey
|
|
88
|
-
"pylo",
|
|
89
|
-
entity,
|
|
90
|
-
"infiniteList",
|
|
91
|
-
{
|
|
92
|
-
perPage,
|
|
93
|
-
filter: infiniteOptions == null ? void 0 : infiniteOptions.filter,
|
|
94
|
-
select: infiniteOptions == null ? void 0 : infiniteOptions.select,
|
|
95
|
-
headers: merged
|
|
96
|
-
}
|
|
97
|
-
],
|
|
321
|
+
queryKey,
|
|
98
322
|
queryFn: async ({ pageParam }) => {
|
|
99
323
|
const select = infiniteOptions.select;
|
|
100
324
|
const runtimeOptions = {
|
|
101
325
|
select: {
|
|
102
|
-
...select,
|
|
326
|
+
...withId(select),
|
|
103
327
|
_pagination: { ...select._pagination, ...INFINITE_PAGINATION_SELECT }
|
|
104
328
|
},
|
|
105
329
|
filter: infiniteOptions == null ? void 0 : infiniteOptions.filter,
|
|
@@ -121,8 +345,8 @@ function createPyloHooks(options) {
|
|
|
121
345
|
return void 0;
|
|
122
346
|
},
|
|
123
347
|
select: (queryData) => {
|
|
124
|
-
const allData =
|
|
125
|
-
(page) => page.data
|
|
348
|
+
const allData = dedupeById(
|
|
349
|
+
queryData.pages.flatMap((page) => page.data)
|
|
126
350
|
);
|
|
127
351
|
return {
|
|
128
352
|
...queryData,
|
|
@@ -135,14 +359,24 @@ function createPyloHooks(options) {
|
|
|
135
359
|
function usePyloById(entity, id, queryOptions) {
|
|
136
360
|
const transport = useTransport();
|
|
137
361
|
const merged = mergeHeaders(globalHeaders, queryOptions == null ? void 0 : queryOptions.headers);
|
|
362
|
+
const queryKey = ["pylo", entity, "byId", id, { select: queryOptions == null ? void 0 : queryOptions.select, headers: merged }];
|
|
363
|
+
const patchable = patchableWith(merged);
|
|
364
|
+
useLive(
|
|
365
|
+
entity,
|
|
366
|
+
queryKey,
|
|
367
|
+
queryOptions.live && id ? {
|
|
368
|
+
select: queryOptions.select,
|
|
369
|
+
filter: { query: [{ condition: { field: "id", operator: "equal", value: id } }] }
|
|
370
|
+
} : null,
|
|
371
|
+
(data, event) => patchable ? applyRowEvent(data, event) : { data, refetch: "later" }
|
|
372
|
+
);
|
|
138
373
|
return useQuery({
|
|
139
|
-
queryKey
|
|
374
|
+
queryKey,
|
|
140
375
|
queryFn: async () => {
|
|
141
|
-
const { query, variables } = buildByIdQuery(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
);
|
|
376
|
+
const { query, variables } = buildByIdQuery(entity, id, {
|
|
377
|
+
...queryOptions,
|
|
378
|
+
select: withId(queryOptions.select)
|
|
379
|
+
});
|
|
146
380
|
const data = await transport(query, variables, merged);
|
|
147
381
|
const byIdKey = `${entity}ById`;
|
|
148
382
|
const result = data[byIdKey];
|
|
@@ -231,6 +465,7 @@ function createPyloHooks(options) {
|
|
|
231
465
|
function usePyloUpsert(entity, mutationOptions) {
|
|
232
466
|
const transport = useTransport();
|
|
233
467
|
const queryClient = useQueryClient();
|
|
468
|
+
const realtime = useRealtime();
|
|
234
469
|
const merged = mergeHeaders(
|
|
235
470
|
mergeHeaders(globalHeaders, mutationOptions == null ? void 0 : mutationOptions.headers),
|
|
236
471
|
flagsToHeaders(mutationOptions != null ? mutationOptions : {})
|
|
@@ -250,11 +485,7 @@ function createPyloHooks(options) {
|
|
|
250
485
|
},
|
|
251
486
|
onSuccess: (data, variables, onMutateResult, context) => {
|
|
252
487
|
var _a2;
|
|
253
|
-
if (!isDryRun)
|
|
254
|
-
void queryClient.invalidateQueries({
|
|
255
|
-
queryKey: ["pylo", entity]
|
|
256
|
-
});
|
|
257
|
-
}
|
|
488
|
+
if (!isDryRun) void invalidateEntity(queryClient, realtime, entity);
|
|
258
489
|
(_a2 = mutationOptions == null ? void 0 : mutationOptions.onSuccess) == null ? void 0 : _a2.call(mutationOptions, data, variables, onMutateResult, context);
|
|
259
490
|
}
|
|
260
491
|
});
|
|
@@ -262,6 +493,7 @@ function createPyloHooks(options) {
|
|
|
262
493
|
function usePyloBulkUpsert(entity, mutationOptions) {
|
|
263
494
|
const transport = useTransport();
|
|
264
495
|
const queryClient = useQueryClient();
|
|
496
|
+
const realtime = useRealtime();
|
|
265
497
|
const merged = mergeHeaders(
|
|
266
498
|
mergeHeaders(globalHeaders, mutationOptions == null ? void 0 : mutationOptions.headers),
|
|
267
499
|
flagsToHeaders(mutationOptions != null ? mutationOptions : {})
|
|
@@ -281,11 +513,7 @@ function createPyloHooks(options) {
|
|
|
281
513
|
},
|
|
282
514
|
onSuccess: (data, variables, onMutateResult, context) => {
|
|
283
515
|
var _a2;
|
|
284
|
-
if (!isDryRun)
|
|
285
|
-
void queryClient.invalidateQueries({
|
|
286
|
-
queryKey: ["pylo", entity]
|
|
287
|
-
});
|
|
288
|
-
}
|
|
516
|
+
if (!isDryRun) void invalidateEntity(queryClient, realtime, entity);
|
|
289
517
|
(_a2 = mutationOptions == null ? void 0 : mutationOptions.onSuccess) == null ? void 0 : _a2.call(mutationOptions, data, variables, onMutateResult, context);
|
|
290
518
|
}
|
|
291
519
|
});
|
|
@@ -293,6 +521,7 @@ function createPyloHooks(options) {
|
|
|
293
521
|
function usePyloDelete(entity, mutationOptions) {
|
|
294
522
|
const transport = useTransport();
|
|
295
523
|
const queryClient = useQueryClient();
|
|
524
|
+
const realtime = useRealtime();
|
|
296
525
|
const merged = mergeHeaders(
|
|
297
526
|
mergeHeaders(globalHeaders, mutationOptions == null ? void 0 : mutationOptions.headers),
|
|
298
527
|
flagsToHeaders(mutationOptions != null ? mutationOptions : {})
|
|
@@ -313,11 +542,7 @@ function createPyloHooks(options) {
|
|
|
313
542
|
},
|
|
314
543
|
onSuccess: (data, variables, onMutateResult, context) => {
|
|
315
544
|
var _a2;
|
|
316
|
-
if (!isDryRun)
|
|
317
|
-
void queryClient.invalidateQueries({
|
|
318
|
-
queryKey: ["pylo", entity]
|
|
319
|
-
});
|
|
320
|
-
}
|
|
545
|
+
if (!isDryRun) void invalidateEntity(queryClient, realtime, entity);
|
|
321
546
|
(_a2 = mutationOptions == null ? void 0 : mutationOptions.onSuccess) == null ? void 0 : _a2.call(mutationOptions, data, variables, onMutateResult, context);
|
|
322
547
|
}
|
|
323
548
|
});
|
|
@@ -403,7 +628,7 @@ function createPyloHooks(options) {
|
|
|
403
628
|
optionsRef.current = hookOptions;
|
|
404
629
|
const startUpload = useCallback(
|
|
405
630
|
async (files, overrides) => {
|
|
406
|
-
var _a2,
|
|
631
|
+
var _a2, _b2, _c, _d;
|
|
407
632
|
const opts = { ...optionsRef.current, ...overrides };
|
|
408
633
|
const merged = mergeHeaders(globalHeaders, opts.headers);
|
|
409
634
|
const list = Array.isArray(files) ? files : [files];
|
|
@@ -419,13 +644,13 @@ function createPyloHooks(options) {
|
|
|
419
644
|
const loadedPerFile = new Array(list.length).fill(0);
|
|
420
645
|
const doneFlags = new Array(list.length).fill(false);
|
|
421
646
|
const reportProgress = () => {
|
|
422
|
-
var _a3,
|
|
647
|
+
var _a3, _b3;
|
|
423
648
|
const loaded = loadedPerFile.reduce((sum, bytes) => sum + bytes, 0);
|
|
424
649
|
const percent = grandTotal !== void 0 ? grandTotal > 0 ? Math.round(loaded / grandTotal * 100) : 0 : Math.round(
|
|
425
650
|
doneFlags.filter(Boolean).length / doneFlags.length * 100
|
|
426
651
|
);
|
|
427
652
|
setProgress(percent);
|
|
428
|
-
(
|
|
653
|
+
(_b3 = (_a3 = optionsRef.current) == null ? void 0 : _a3.onProgress) == null ? void 0 : _b3.call(_a3, {
|
|
429
654
|
loaded,
|
|
430
655
|
total: grandTotal != null ? grandTotal : loaded,
|
|
431
656
|
percent
|
|
@@ -438,7 +663,7 @@ function createPyloHooks(options) {
|
|
|
438
663
|
const input = buildCreateUploadInput(opts, list.length);
|
|
439
664
|
const results = await Promise.all(
|
|
440
665
|
list.map(async (_source, index) => {
|
|
441
|
-
var _a3,
|
|
666
|
+
var _a3, _b3;
|
|
442
667
|
const part = parts[index];
|
|
443
668
|
const data = await transport(
|
|
444
669
|
CREATE_UPLOAD_MUTATION,
|
|
@@ -460,7 +685,7 @@ function createPyloHooks(options) {
|
|
|
460
685
|
id: uploadUrl.id,
|
|
461
686
|
fileName: part.fileName,
|
|
462
687
|
mimeType: part.mimeType,
|
|
463
|
-
size: (
|
|
688
|
+
size: (_b3 = part.size) != null ? _b3 : 0
|
|
464
689
|
};
|
|
465
690
|
})
|
|
466
691
|
);
|
|
@@ -476,7 +701,7 @@ function createPyloHooks(options) {
|
|
|
476
701
|
void queryClient.invalidateQueries({ queryKey: ["pylo", "pyloMedia"] });
|
|
477
702
|
setUploadedFiles(results);
|
|
478
703
|
setProgress(100);
|
|
479
|
-
(
|
|
704
|
+
(_b2 = (_a2 = optionsRef.current) == null ? void 0 : _a2.onSuccess) == null ? void 0 : _b2.call(_a2, results);
|
|
480
705
|
return results;
|
|
481
706
|
} catch (thrown) {
|
|
482
707
|
controller.abort();
|
|
@@ -521,6 +746,37 @@ function createPyloHooks(options) {
|
|
|
521
746
|
staleTime: (_a2 = queryOptions == null ? void 0 : queryOptions.staleTimeMs) != null ? _a2 : 4 * 60 * 1e3
|
|
522
747
|
});
|
|
523
748
|
}
|
|
749
|
+
function usePyloSubscription(entity, subscribeOptions, onEvent, handlers) {
|
|
750
|
+
const realtime = useRealtime();
|
|
751
|
+
const latest = useRef({ subscribeOptions, onEvent, handlers });
|
|
752
|
+
latest.current = { subscribeOptions, onEvent, handlers };
|
|
753
|
+
const enabled = realtime !== null && subscribeOptions.enabled !== false;
|
|
754
|
+
const key = hashKey([
|
|
755
|
+
subscribeOptions.select,
|
|
756
|
+
subscribeOptions.filter,
|
|
757
|
+
subscribeOptions.on
|
|
758
|
+
]);
|
|
759
|
+
useEffect(() => {
|
|
760
|
+
if (!enabled) return;
|
|
761
|
+
const { select, filter, on } = latest.current.subscribeOptions;
|
|
762
|
+
const sub = realtime.client.subscribe(
|
|
763
|
+
entity,
|
|
764
|
+
{ select, filter, on },
|
|
765
|
+
{
|
|
766
|
+
onEvent: (event) => latest.current.onEvent(event),
|
|
767
|
+
onResync: () => {
|
|
768
|
+
var _a2, _b2;
|
|
769
|
+
return (_b2 = (_a2 = latest.current.handlers) == null ? void 0 : _a2.onResync) == null ? void 0 : _b2.call(_a2);
|
|
770
|
+
},
|
|
771
|
+
onError: (error) => {
|
|
772
|
+
var _a2, _b2;
|
|
773
|
+
return (_b2 = (_a2 = latest.current.handlers) == null ? void 0 : _a2.onError) == null ? void 0 : _b2.call(_a2, error);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
);
|
|
777
|
+
return () => sub.unsubscribe();
|
|
778
|
+
}, [enabled, realtime, entity, key]);
|
|
779
|
+
}
|
|
524
780
|
return {
|
|
525
781
|
usePyloList,
|
|
526
782
|
usePyloInfiniteList,
|
|
@@ -536,7 +792,8 @@ function createPyloHooks(options) {
|
|
|
536
792
|
usePyloEventPropertyKeys,
|
|
537
793
|
usePyloEventFieldValues,
|
|
538
794
|
usePyloUpload,
|
|
539
|
-
usePyloFileUrl
|
|
795
|
+
usePyloFileUrl,
|
|
796
|
+
usePyloSubscription
|
|
540
797
|
};
|
|
541
798
|
}
|
|
542
799
|
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
useSyncExternalStore
|
|
9
9
|
} from "react";
|
|
10
10
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
11
|
-
import { createPyloClient, resolveEndpoint } from "@pylo/core";
|
|
11
|
+
import { createPyloClient, createRealtime, resolveEndpoint } from "@pylo/core";
|
|
12
12
|
import { ME_QUERY } from "@pylo/auth";
|
|
13
13
|
|
|
14
14
|
// src/session/store.ts
|
|
@@ -378,7 +378,7 @@ var LOGGED_OUT = { status: "loggedOut", token: null };
|
|
|
378
378
|
var NO_OP = () => () => {
|
|
379
379
|
};
|
|
380
380
|
function PyloProvider(props) {
|
|
381
|
-
var _a, _b;
|
|
381
|
+
var _a, _b, _c;
|
|
382
382
|
const queryClient = useQueryClient();
|
|
383
383
|
const endpoint = resolveEndpoint(props.endpoint);
|
|
384
384
|
const storeRef = useRef(null);
|
|
@@ -421,6 +421,20 @@ function PyloProvider(props) {
|
|
|
421
421
|
const getTokenRef = useRef(getTokenProp);
|
|
422
422
|
getTokenRef.current = getTokenProp;
|
|
423
423
|
const isEscapeHatch = getTokenProp !== void 0;
|
|
424
|
+
const transportRef = useRef(null);
|
|
425
|
+
const realtimeRef = useRef(null);
|
|
426
|
+
(_c = realtimeRef.current) != null ? _c : realtimeRef.current = {
|
|
427
|
+
client: createRealtime({
|
|
428
|
+
request: (query, variables) => transportRef.current(query, variables),
|
|
429
|
+
auth: async () => {
|
|
430
|
+
const token = isEscapeHatch ? await getTokenRef.current() : await store.getToken();
|
|
431
|
+
return { token };
|
|
432
|
+
}
|
|
433
|
+
}),
|
|
434
|
+
liveQueries: /* @__PURE__ */ new Map()
|
|
435
|
+
};
|
|
436
|
+
const realtime = realtimeRef.current;
|
|
437
|
+
useEffect(() => () => realtime.client.close(), [realtime]);
|
|
424
438
|
const value = useMemo(() => {
|
|
425
439
|
const getToken = isEscapeHatch ? () => getTokenRef.current() : () => store.getToken();
|
|
426
440
|
const transport = createDirectTransport({
|
|
@@ -428,13 +442,17 @@ function PyloProvider(props) {
|
|
|
428
442
|
getToken,
|
|
429
443
|
...store ? { onUnauthorized: () => store.refresh() } : {}
|
|
430
444
|
});
|
|
431
|
-
|
|
432
|
-
|
|
445
|
+
transportRef.current = transport;
|
|
446
|
+
return { transport, endpoint, state, store, social, getToken, realtime };
|
|
447
|
+
}, [endpoint, state, store, social, isEscapeHatch, realtime]);
|
|
433
448
|
return /* @__PURE__ */ jsx(PyloContext.Provider, { value, children: props.children });
|
|
434
449
|
}
|
|
435
450
|
function usePyloTransport() {
|
|
436
451
|
return usePyloContext().transport;
|
|
437
452
|
}
|
|
453
|
+
function usePyloRealtime() {
|
|
454
|
+
return usePyloContext().realtime;
|
|
455
|
+
}
|
|
438
456
|
function usePyloClient() {
|
|
439
457
|
const { endpoint, getToken } = usePyloContext();
|
|
440
458
|
return useMemo(
|
|
@@ -495,6 +513,7 @@ export {
|
|
|
495
513
|
createDirectTransport,
|
|
496
514
|
PyloProvider,
|
|
497
515
|
usePyloTransport,
|
|
516
|
+
usePyloRealtime,
|
|
498
517
|
usePyloClient,
|
|
499
518
|
usePyloAuth
|
|
500
519
|
};
|
package/dist/hooks.d.ts
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
|
-
import { T as Transport } from './
|
|
1
|
+
import { T as Transport, L as LiveRealtime } from './provider-DF_-sV-r.js';
|
|
2
2
|
import { UseQueryResult, UseInfiniteQueryResult, UseMutationOptions, UseMutationResult } from '@tanstack/react-query';
|
|
3
|
-
import { EntityNameWith, ListSelect, FilterInput, RequestOptions, WithPagination, EntityRelationPath, UploadAttachTarget, UploadProgress, PyloUploadedFile, UploadSource, ListSelectConstraint, ListOptions, EntityResult, PaginationData, SelectConstraint, ByIdOptions, EntityName, AggregateMetricInput, AggregateGroupByInput, AggregateOptions, AggregateResult, EventMetricInput, EventGroupByInput, EventAggregateOptions, UpsertInput, MutationRequestOptions, PyloEvent, PyloEventInput, PaginationSelect, EventListOptions, PyloEventListResult, PyloEventPropertyKeysOptions, PyloEventProperty, PyloEventFieldValuesOptions, PyloEventFieldValue } from '@pylo/core';
|
|
3
|
+
import { EntityNameWith, ListSelect, FilterInput, RequestOptions, WithPagination, EntityRelationPath, UploadAttachTarget, UploadProgress, PyloUploadedFile, UploadSource, ListSelectConstraint, ListOptions, EntityResult, PaginationData, SelectConstraint, ByIdOptions, EntityName, AggregateMetricInput, AggregateGroupByInput, AggregateOptions, AggregateResult, EventMetricInput, EventGroupByInput, EventAggregateOptions, UpsertInput, MutationRequestOptions, PyloEvent, PyloEventInput, PaginationSelect, EventListOptions, PyloEventListResult, PyloEventPropertyKeysOptions, PyloEventProperty, PyloEventFieldValuesOptions, PyloEventFieldValue, SubscribeOptions, RealtimeHandlers } from '@pylo/core';
|
|
4
|
+
import 'react/jsx-runtime';
|
|
5
|
+
import 'react';
|
|
6
|
+
import '@pylo/auth';
|
|
4
7
|
|
|
5
8
|
interface HooksOptions {
|
|
6
9
|
/** Defaults to reading the transport `PyloProvider` supplies. */
|
|
7
10
|
useTransport?: () => Transport;
|
|
11
|
+
/** Defaults to the connection `PyloProvider` supplies; `null` turns `live` off. */
|
|
12
|
+
useRealtime?: () => LiveRealtime | null;
|
|
8
13
|
headers?: Record<string, string>;
|
|
9
14
|
}
|
|
15
|
+
interface LiveOption {
|
|
16
|
+
/**
|
|
17
|
+
* Keep the result current over the realtime connection: updates and removals
|
|
18
|
+
* are patched into the cache, anything the cache can't place correctly
|
|
19
|
+
* triggers a background refetch.
|
|
20
|
+
*/
|
|
21
|
+
live?: boolean;
|
|
22
|
+
}
|
|
10
23
|
type ListHookResult<T, Sel = unknown> = {
|
|
11
24
|
data: T[] | undefined;
|
|
12
25
|
isLoading: boolean;
|
|
@@ -21,7 +34,7 @@ type InfiniteListOptions<S, E extends EntityNameWith<S, "list">, Sel extends Lis
|
|
|
21
34
|
perPage?: number;
|
|
22
35
|
select: Sel;
|
|
23
36
|
filter?: FilterInput;
|
|
24
|
-
} & RequestOptions;
|
|
37
|
+
} & RequestOptions & LiveOption;
|
|
25
38
|
type InfinitePagination<Sel> = Pick<PaginationData, "current_page" | "has_more_pages" | (Sel extends {
|
|
26
39
|
_pagination: infer PS;
|
|
27
40
|
} ? keyof PS & keyof PaginationData : never)>;
|
|
@@ -63,7 +76,7 @@ interface UploadHookResult<S> {
|
|
|
63
76
|
reset: () => void;
|
|
64
77
|
}
|
|
65
78
|
declare function createPyloHooks<S>(options?: HooksOptions): {
|
|
66
|
-
usePyloList: <E extends EntityNameWith<S, "list">, const Sel extends ListSelectConstraint<S, E, Sel>>(entity: E, queryOptions: ListOptions<S, E, Sel> & RequestOptions) => ListHookResult<EntityResult<S, E, Sel>, Sel>;
|
|
79
|
+
usePyloList: <E extends EntityNameWith<S, "list">, const Sel extends ListSelectConstraint<S, E, Sel>>(entity: E, queryOptions: ListOptions<S, E, Sel> & RequestOptions & LiveOption) => ListHookResult<EntityResult<S, E, Sel>, Sel>;
|
|
67
80
|
usePyloInfiniteList: <E extends EntityNameWith<S, "list">, const Sel extends ListSelectConstraint<S, E, Sel>>(entity: E, infiniteOptions: InfiniteListOptions<S, E, Sel>) => UseInfiniteQueryResult<{
|
|
68
81
|
data: Array<EntityResult<S, E, Sel>>;
|
|
69
82
|
pages: Array<{
|
|
@@ -71,7 +84,7 @@ declare function createPyloHooks<S>(options?: HooksOptions): {
|
|
|
71
84
|
pagination: InfinitePagination<Sel>;
|
|
72
85
|
}>;
|
|
73
86
|
}>;
|
|
74
|
-
usePyloById: <E extends EntityNameWith<S, "byId">, const Sel extends SelectConstraint<S, E, Sel>>(entity: E, id: string | null | undefined, queryOptions: ByIdOptions<S, E, Sel> & RequestOptions) => UseQueryResult<EntityResult<S, E, Sel> | null>;
|
|
87
|
+
usePyloById: <E extends EntityNameWith<S, "byId">, const Sel extends SelectConstraint<S, E, Sel>>(entity: E, id: string | null | undefined, queryOptions: ByIdOptions<S, E, Sel> & RequestOptions & LiveOption) => UseQueryResult<EntityResult<S, E, Sel> | null>;
|
|
75
88
|
usePyloMe: <const Sel extends SelectConstraint<S, "me" & EntityName<S>, Sel>>(queryOptions: ByIdOptions<S, "me" & EntityName<S>, Sel> & RequestOptions) => UseQueryResult<EntityResult<S, "me" & EntityName<S>, Sel>>;
|
|
76
89
|
usePyloAggregate: <E extends EntityName<S>, const M extends Record<string, AggregateMetricInput<S, E>>, const G extends readonly AggregateGroupByInput<S, E>[] = []>(entity: E, queryOptions: AggregateOptions<S, E, M, G> & RequestOptions) => UseQueryResult<AggregateResult<M, G>>;
|
|
77
90
|
usePyloEventAggregate: <const M extends Record<string, EventMetricInput>, const G extends readonly EventGroupByInput[] = []>(queryOptions: EventAggregateOptions<M, G> & RequestOptions) => UseQueryResult<AggregateResult<M, G>>;
|
|
@@ -98,6 +111,9 @@ declare function createPyloHooks<S>(options?: HooksOptions): {
|
|
|
98
111
|
usePyloFileUrl: (id: string | null | undefined, queryOptions?: {
|
|
99
112
|
staleTimeMs?: number;
|
|
100
113
|
} & RequestOptions) => UseQueryResult<string>;
|
|
114
|
+
usePyloSubscription: <E extends EntityNameWith<S, "list">, const Sel extends SelectConstraint<S, E, Sel>>(entity: E, subscribeOptions: SubscribeOptions<S, E, Sel> & {
|
|
115
|
+
enabled?: boolean;
|
|
116
|
+
}, onEvent: RealtimeHandlers<EntityResult<S, E, Sel>>["onEvent"], handlers?: Omit<RealtimeHandlers<EntityResult<S, E, Sel>>, "onEvent">) => void;
|
|
101
117
|
};
|
|
102
118
|
|
|
103
|
-
export { type InfiniteListOptions, type ListHookResult, type StartUploadOptions, type UploadHookOptions, type UploadHookResult, createPyloHooks };
|
|
119
|
+
export { type InfiniteListOptions, type ListHookResult, type LiveOption, type StartUploadOptions, type UploadHookOptions, type UploadHookResult, createPyloHooks };
|
package/dist/hooks.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { EntityName, EntitySelect, EntityResult } from '@pylo/core';
|
|
2
|
-
export { AggregateFilterInput, AggregateFunction, AggregateGroupByInput, AggregateInput, AggregateInterval, AggregateIntervalUnit, AggregateMetricInput, AggregateOptions, AggregateResult, AggregateRow, AggregateRows, AggregateSortInput, AggregateSortKey, AggregateTotal, ByIdOptions, CallableEntityName, CreateUploadInput, CreateUploadUrlOptions, DimensionInput, DimensionValue, EntityAggregateApi, EntityCapability, EntityFields, EntityMethodName, EntityName, EntityNameWith, EntityRelationPath, EntityRelations, EntityResult, EntitySelect, EventAggregateOptions, EventGroupByInput, EventListFilterInput, EventListOptions, EventMetricInput, FieldName, FilesClient, FilterInput, HasCapability, IngestEvents, ListOptions, ListResult, Me, MetricValue, MutationRequestOptions, NumericFieldName, PaginationData, PaginationInput, PyloError, PyloEvent, PyloEventFieldValue, PyloEventFieldValuesOptions, PyloEventInput, PyloEventListResult, PyloEventProperty, PyloEventPropertyKeysOptions, PyloUploadedFile, QueryInput, QueryInputCondition, QueryOperator, RequestOptions, RowKeyOf, SearchValueInput, SortInput, SortOrder, SystemEntityName, TimeBucketInput, UploadAttachTarget, UploadOptions, UploadProgress, UploadSource, UploadUrl, UpsertInput, VirtualEntityName } from '@pylo/core';
|
|
2
|
+
export { AggregateFilterInput, AggregateFunction, AggregateGroupByInput, AggregateInput, AggregateInterval, AggregateIntervalUnit, AggregateMetricInput, AggregateOptions, AggregateResult, AggregateRow, AggregateRows, AggregateSortInput, AggregateSortKey, AggregateTotal, ByIdOptions, CallableEntityName, CreateUploadInput, CreateUploadUrlOptions, DimensionInput, DimensionValue, EntityAggregateApi, EntityCapability, EntityFields, EntityMethodName, EntityName, EntityNameWith, EntityRelationPath, EntityRelations, EntityResult, EntitySelect, EventAggregateOptions, EventGroupByInput, EventListFilterInput, EventListOptions, EventMetricInput, FieldName, FilesClient, FilterInput, HasCapability, IngestEvents, ListOptions, ListResult, Me, MetricValue, MutationRequestOptions, NumericFieldName, PaginationData, PaginationInput, PyloError, PyloEvent, PyloEventFieldValue, PyloEventFieldValuesOptions, PyloEventInput, PyloEventListResult, PyloEventProperty, PyloEventPropertyKeysOptions, PyloUploadedFile, QueryInput, QueryInputCondition, QueryOperator, RealtimeClient, RealtimeEvent, RealtimeEventKind, RealtimeHandlers, RealtimeStatus, RealtimeSubscription, RequestOptions, RowKeyOf, SearchValueInput, SortInput, SortOrder, SubscribeOptions, SystemEntityName, TimeBucketInput, UploadAttachTarget, UploadOptions, UploadProgress, UploadSource, UploadUrl, UpsertInput, VirtualEntityName } from '@pylo/core';
|
|
3
3
|
import { AuthResult, SocialProvider } from '@pylo/auth';
|
|
4
4
|
export { AuthResult, HandoffCrypto, PyloUser, SocialProvider } from '@pylo/auth';
|
|
5
|
-
import { P as PyloStorage, a as PyloAuth } from './provider-
|
|
6
|
-
export { O as OpenAuthSession, b as PyloProvider, c as PyloProviderProps, S as SocialLogin, d as SocialLoginResult, e as
|
|
7
|
-
export {
|
|
8
|
-
export { InfiniteListOptions, ListHookResult, StartUploadOptions, UploadHookOptions, UploadHookResult, createPyloHooks } from './hooks.js';
|
|
5
|
+
import { P as PyloStorage, a as PyloAuth } from './provider-DF_-sV-r.js';
|
|
6
|
+
export { D as DirectTransportOptions, L as LiveRealtime, O as OpenAuthSession, b as PyloProvider, c as PyloProviderProps, S as SocialLogin, d as SocialLoginResult, T as Transport, e as createDirectTransport, f as createLocalStorageAdapter, g as createMemoryStorage, u as usePyloClient, h as usePyloRealtime, i as usePyloTransport } from './provider-DF_-sV-r.js';
|
|
7
|
+
export { InfiniteListOptions, ListHookResult, LiveOption, StartUploadOptions, UploadHookOptions, UploadHookResult, createPyloHooks } from './hooks.js';
|
|
9
8
|
import 'react/jsx-runtime';
|
|
10
9
|
import 'react';
|
|
11
10
|
import '@tanstack/react-query';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createPyloHooks
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-C3KAJKBM.js";
|
|
4
4
|
import {
|
|
5
5
|
PyloProvider,
|
|
6
6
|
createDirectTransport,
|
|
@@ -9,8 +9,9 @@ import {
|
|
|
9
9
|
createSessionStore,
|
|
10
10
|
usePyloAuth,
|
|
11
11
|
usePyloClient,
|
|
12
|
+
usePyloRealtime,
|
|
12
13
|
usePyloTransport
|
|
13
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-OPMVPQER.js";
|
|
14
15
|
|
|
15
16
|
// src/index.ts
|
|
16
17
|
import { PyloError } from "@pylo/core";
|
|
@@ -25,5 +26,6 @@ export {
|
|
|
25
26
|
createSessionStore,
|
|
26
27
|
usePyloAuth2 as usePyloAuth,
|
|
27
28
|
usePyloClient,
|
|
29
|
+
usePyloRealtime,
|
|
28
30
|
usePyloTransport
|
|
29
31
|
};
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { PyloClient } from '@pylo/core';
|
|
3
|
+
import { RealtimeClient, PyloClient } from '@pylo/core';
|
|
4
4
|
import { SocialProvider, AuthResult, PyloUser, HandoffCrypto } from '@pylo/auth';
|
|
5
|
-
import { T as Transport } from './transport-55cSrvQo.js';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* The three operations a session needs from a key-value store. Async because
|
|
@@ -40,6 +39,23 @@ interface SocialLogin<P extends string = SocialProvider, U extends string = stri
|
|
|
40
39
|
completeLogin(url?: string): Promise<SocialLoginResult>;
|
|
41
40
|
}
|
|
42
41
|
|
|
42
|
+
/**
|
|
43
|
+
* How the hooks reach the API. `@pylo/react` talks to it directly; `@pylo/nextjs`
|
|
44
|
+
* posts to its route handler so the token can stay in an httpOnly cookie.
|
|
45
|
+
*/
|
|
46
|
+
type Transport = (query: string, variables: Record<string, unknown>, headers?: Record<string, string>) => Promise<unknown>;
|
|
47
|
+
interface DirectTransportOptions {
|
|
48
|
+
endpoint: string;
|
|
49
|
+
getToken: () => Promise<string | null>;
|
|
50
|
+
/** Called once on an unauthorized response; returns the token to retry with. */
|
|
51
|
+
onUnauthorized?: () => Promise<string | null>;
|
|
52
|
+
}
|
|
53
|
+
declare function createDirectTransport(options: DirectTransportOptions): Transport;
|
|
54
|
+
|
|
55
|
+
interface LiveRealtime {
|
|
56
|
+
client: RealtimeClient;
|
|
57
|
+
liveQueries: Map<string, number>;
|
|
58
|
+
}
|
|
43
59
|
interface PyloProviderProps {
|
|
44
60
|
children: ReactNode;
|
|
45
61
|
endpoint?: string;
|
|
@@ -60,6 +76,7 @@ interface PyloProviderProps {
|
|
|
60
76
|
}
|
|
61
77
|
declare function PyloProvider(props: PyloProviderProps): react_jsx_runtime.JSX.Element;
|
|
62
78
|
declare function usePyloTransport(): Transport;
|
|
79
|
+
declare function usePyloRealtime(): LiveRealtime;
|
|
63
80
|
declare function usePyloClient<S>(): PyloClient<S>;
|
|
64
81
|
interface PyloAuth<P extends string = SocialProvider, U extends string = string> extends SocialLogin<P, U> {
|
|
65
82
|
isLoading: boolean;
|
|
@@ -71,4 +88,4 @@ interface PyloAuth<P extends string = SocialProvider, U extends string = string>
|
|
|
71
88
|
}
|
|
72
89
|
declare function usePyloAuth(): PyloAuth;
|
|
73
90
|
|
|
74
|
-
export { type OpenAuthSession as O, type PyloStorage as P, type SocialLogin as S, type PyloAuth as a, PyloProvider as b, type PyloProviderProps as c, type SocialLoginResult as d,
|
|
91
|
+
export { type DirectTransportOptions as D, type LiveRealtime as L, type OpenAuthSession as O, type PyloStorage as P, type SocialLogin as S, type Transport as T, type PyloAuth as a, PyloProvider as b, type PyloProviderProps as c, type SocialLoginResult as d, createDirectTransport as e, createLocalStorageAdapter as f, createMemoryStorage as g, usePyloRealtime as h, usePyloTransport as i, usePyloAuth as j, usePyloClient as u };
|
package/dist/provider.d.ts
CHANGED
|
@@ -2,5 +2,4 @@ import 'react/jsx-runtime';
|
|
|
2
2
|
import 'react';
|
|
3
3
|
import '@pylo/core';
|
|
4
4
|
import '@pylo/auth';
|
|
5
|
-
export { a as PyloAuth, b as PyloProvider, c as PyloProviderProps,
|
|
6
|
-
import './transport-55cSrvQo.js';
|
|
5
|
+
export { L as LiveRealtime, a as PyloAuth, b as PyloProvider, c as PyloProviderProps, j as usePyloAuth, u as usePyloClient, h as usePyloRealtime, i as usePyloTransport } from './provider-DF_-sV-r.js';
|
package/dist/provider.js
CHANGED
|
@@ -2,11 +2,13 @@ import {
|
|
|
2
2
|
PyloProvider,
|
|
3
3
|
usePyloAuth,
|
|
4
4
|
usePyloClient,
|
|
5
|
+
usePyloRealtime,
|
|
5
6
|
usePyloTransport
|
|
6
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-OPMVPQER.js";
|
|
7
8
|
export {
|
|
8
9
|
PyloProvider,
|
|
9
10
|
usePyloAuth,
|
|
10
11
|
usePyloClient,
|
|
12
|
+
usePyloRealtime,
|
|
11
13
|
usePyloTransport
|
|
12
14
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pylo/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Type-safe React SDK for Pylo — hooks, session provider, client",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@pylo/auth": "0.2.0",
|
|
40
|
-
"@pylo/core": "0.
|
|
40
|
+
"@pylo/core": "0.12.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"@tanstack/react-query": ">=5.0.0",
|
|
@@ -45,9 +45,13 @@
|
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@tanstack/react-query": "^5.0.0",
|
|
48
|
+
"@testing-library/react": "^16.3.3",
|
|
48
49
|
"@types/node": "^22.15.21",
|
|
49
50
|
"@types/react": "^19.0.0",
|
|
51
|
+
"@types/react-dom": "^19.3.0",
|
|
52
|
+
"happy-dom": "^21.0.3",
|
|
50
53
|
"react": "^19.0.0",
|
|
54
|
+
"react-dom": "^19.2.4",
|
|
51
55
|
"tsup": "^8.5.1",
|
|
52
56
|
"typescript": "^5.9.3"
|
|
53
57
|
},
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* How the hooks reach the API. `@pylo/react` talks to it directly; `@pylo/nextjs`
|
|
3
|
-
* posts to its route handler so the token can stay in an httpOnly cookie.
|
|
4
|
-
*/
|
|
5
|
-
type Transport = (query: string, variables: Record<string, unknown>, headers?: Record<string, string>) => Promise<unknown>;
|
|
6
|
-
interface DirectTransportOptions {
|
|
7
|
-
endpoint: string;
|
|
8
|
-
getToken: () => Promise<string | null>;
|
|
9
|
-
/** Called once on an unauthorized response; returns the token to retry with. */
|
|
10
|
-
onUnauthorized?: () => Promise<string | null>;
|
|
11
|
-
}
|
|
12
|
-
declare function createDirectTransport(options: DirectTransportOptions): Transport;
|
|
13
|
-
|
|
14
|
-
export { type DirectTransportOptions as D, type Transport as T, createDirectTransport as c };
|