@lunora/react 0.0.0 → 1.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +105 -0
- package/README.md +150 -9
- package/__assets__/package-og.svg +14 -0
- package/dist/index.d.mts +499 -0
- package/dist/index.d.ts +499 -0
- package/dist/index.mjs +17 -0
- package/dist/packem_shared/Authenticated-DtKgZT2Z.mjs +33 -0
- package/dist/packem_shared/CheckoutButton-CVSry8U1.mjs +185 -0
- package/dist/packem_shared/LunoraProvider-D38Xp16l.mjs +80 -0
- package/dist/packem_shared/cache-CItk3fgN.mjs +75 -0
- package/dist/packem_shared/hydratePreloaded-BlFL9FGq.mjs +46 -0
- package/dist/packem_shared/lunoraQueryOptions-CsuWzjg1.mjs +16 -0
- package/dist/packem_shared/query-key-C5rufkEE.mjs +21 -0
- package/dist/packem_shared/query-options.d-D4okOpO8.d.mts +38 -0
- package/dist/packem_shared/query-options.d-D4okOpO8.d.ts +38 -0
- package/dist/packem_shared/use-paginated-core-CoOfcc-p.mjs +161 -0
- package/dist/packem_shared/useAuth-CNUKtOOp.mjs +129 -0
- package/dist/packem_shared/useAuthState-BiGhtSCs.mjs +36 -0
- package/dist/packem_shared/useConnectionStatus-DRSY9ldm.mjs +30 -0
- package/dist/packem_shared/useInfiniteQuery-MH0x4l8h.mjs +97 -0
- package/dist/packem_shared/useMutation-CrvMXRsk.mjs +67 -0
- package/dist/packem_shared/usePaginatedQuery-D3PTDRGS.mjs +46 -0
- package/dist/packem_shared/usePresence-D7jLuxj0.mjs +108 -0
- package/dist/packem_shared/useQuery-C5S0W-7K.mjs +41 -0
- package/dist/packem_shared/useRateLimit-DTEffQEi.mjs +64 -0
- package/dist/packem_shared/useStream-BRY9nemd.mjs +125 -0
- package/dist/packem_shared/useSubscription-CHMCjyQg.mjs +139 -0
- package/dist/server.d.mts +51 -0
- package/dist/server.d.ts +51 -0
- package/dist/server.mjs +31 -0
- package/package.json +60 -17
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { c } from 'react/compiler-runtime';
|
|
3
|
+
import { useSyncExternalStore } from 'react';
|
|
4
|
+
import { useLunora } from './LunoraProvider-D38Xp16l.mjs';
|
|
5
|
+
|
|
6
|
+
const stores = /* @__PURE__ */ new WeakMap();
|
|
7
|
+
const createStore = (client) => {
|
|
8
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
9
|
+
let user = null;
|
|
10
|
+
const notify = () => {
|
|
11
|
+
for (const listener of listeners) {
|
|
12
|
+
listener();
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
let generation = 0;
|
|
16
|
+
const setUser = (next) => {
|
|
17
|
+
if (user !== next) {
|
|
18
|
+
user = next;
|
|
19
|
+
notify();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const refresh = () => {
|
|
23
|
+
generation += 1;
|
|
24
|
+
const current = generation;
|
|
25
|
+
if (client.getAuthToken() === null) {
|
|
26
|
+
setUser(null);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
client.getCurrentUser().then((next) => {
|
|
30
|
+
if (current === generation) {
|
|
31
|
+
setUser(next);
|
|
32
|
+
}
|
|
33
|
+
return void 0;
|
|
34
|
+
}).catch(() => {
|
|
35
|
+
if (current === generation) {
|
|
36
|
+
setUser(null);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
let unsubscribeToken;
|
|
41
|
+
return {
|
|
42
|
+
getSnapshot: () => user,
|
|
43
|
+
subscribe: (onChange) => {
|
|
44
|
+
const firstSubscriber = listeners.size === 0;
|
|
45
|
+
listeners.add(onChange);
|
|
46
|
+
if (firstSubscriber) {
|
|
47
|
+
unsubscribeToken = client.onAuthTokenChange(refresh);
|
|
48
|
+
refresh();
|
|
49
|
+
}
|
|
50
|
+
return () => {
|
|
51
|
+
listeners.delete(onChange);
|
|
52
|
+
if (listeners.size === 0) {
|
|
53
|
+
unsubscribeToken?.();
|
|
54
|
+
unsubscribeToken = void 0;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
const getStore = (client) => {
|
|
61
|
+
let store = stores.get(client);
|
|
62
|
+
if (!store) {
|
|
63
|
+
store = createStore(client);
|
|
64
|
+
stores.set(client, store);
|
|
65
|
+
}
|
|
66
|
+
return store;
|
|
67
|
+
};
|
|
68
|
+
const useAuth = () => {
|
|
69
|
+
const $ = c(12);
|
|
70
|
+
const client = useLunora();
|
|
71
|
+
let t0;
|
|
72
|
+
if ($[0] !== client) {
|
|
73
|
+
t0 = getStore(client);
|
|
74
|
+
$[0] = client;
|
|
75
|
+
$[1] = t0;
|
|
76
|
+
} else {
|
|
77
|
+
t0 = $[1];
|
|
78
|
+
}
|
|
79
|
+
const store = t0;
|
|
80
|
+
let t1;
|
|
81
|
+
let t2;
|
|
82
|
+
let t3;
|
|
83
|
+
if ($[2] !== client) {
|
|
84
|
+
t1 = (onChange) => client.onAuthTokenChange(onChange);
|
|
85
|
+
t2 = () => client.getAuthToken();
|
|
86
|
+
t3 = () => client.getAuthToken();
|
|
87
|
+
$[2] = client;
|
|
88
|
+
$[3] = t1;
|
|
89
|
+
$[4] = t2;
|
|
90
|
+
$[5] = t3;
|
|
91
|
+
} else {
|
|
92
|
+
t1 = $[3];
|
|
93
|
+
t2 = $[4];
|
|
94
|
+
t3 = $[5];
|
|
95
|
+
}
|
|
96
|
+
const token = useSyncExternalStore(t1, t2, t3);
|
|
97
|
+
const user = useSyncExternalStore(store.subscribe, store.getSnapshot, _temp);
|
|
98
|
+
let t4;
|
|
99
|
+
if ($[6] !== client) {
|
|
100
|
+
t4 = (next) => {
|
|
101
|
+
client.setAuthToken(next);
|
|
102
|
+
};
|
|
103
|
+
$[6] = client;
|
|
104
|
+
$[7] = t4;
|
|
105
|
+
} else {
|
|
106
|
+
t4 = $[7];
|
|
107
|
+
}
|
|
108
|
+
const setToken = t4;
|
|
109
|
+
let t5;
|
|
110
|
+
if ($[8] !== setToken || $[9] !== token || $[10] !== user) {
|
|
111
|
+
t5 = {
|
|
112
|
+
setToken,
|
|
113
|
+
token,
|
|
114
|
+
user
|
|
115
|
+
};
|
|
116
|
+
$[8] = setToken;
|
|
117
|
+
$[9] = token;
|
|
118
|
+
$[10] = user;
|
|
119
|
+
$[11] = t5;
|
|
120
|
+
} else {
|
|
121
|
+
t5 = $[11];
|
|
122
|
+
}
|
|
123
|
+
return t5;
|
|
124
|
+
};
|
|
125
|
+
function _temp() {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export { useAuth as default };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { c } from 'react/compiler-runtime';
|
|
3
|
+
import { useSyncExternalStore } from 'react';
|
|
4
|
+
import useAuth from './useAuth-CNUKtOOp.mjs';
|
|
5
|
+
|
|
6
|
+
const subscribe = () => () => void 0;
|
|
7
|
+
const useAuthState = () => {
|
|
8
|
+
const $ = c(3);
|
|
9
|
+
const {
|
|
10
|
+
token
|
|
11
|
+
} = useAuth();
|
|
12
|
+
const hydrated = useSyncExternalStore(subscribe, _temp, _temp2);
|
|
13
|
+
const t0 = hydrated && token !== null;
|
|
14
|
+
const t1 = !hydrated;
|
|
15
|
+
let t2;
|
|
16
|
+
if ($[0] !== t0 || $[1] !== t1) {
|
|
17
|
+
t2 = {
|
|
18
|
+
isAuthenticated: t0,
|
|
19
|
+
isLoading: t1
|
|
20
|
+
};
|
|
21
|
+
$[0] = t0;
|
|
22
|
+
$[1] = t1;
|
|
23
|
+
$[2] = t2;
|
|
24
|
+
} else {
|
|
25
|
+
t2 = $[2];
|
|
26
|
+
}
|
|
27
|
+
return t2;
|
|
28
|
+
};
|
|
29
|
+
function _temp() {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
function _temp2() {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { useAuthState };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { c } from 'react/compiler-runtime';
|
|
3
|
+
import { useSyncExternalStore } from 'react';
|
|
4
|
+
import { useLunora } from './LunoraProvider-D38Xp16l.mjs';
|
|
5
|
+
|
|
6
|
+
const useConnectionStatus = () => {
|
|
7
|
+
const $ = c(4);
|
|
8
|
+
const client = useLunora();
|
|
9
|
+
let t0;
|
|
10
|
+
let t1;
|
|
11
|
+
let t2;
|
|
12
|
+
if ($[0] !== client) {
|
|
13
|
+
t0 = (onChange) => client.onConnectionStatus(() => {
|
|
14
|
+
onChange();
|
|
15
|
+
});
|
|
16
|
+
t1 = () => client.connectionStatus();
|
|
17
|
+
t2 = () => client.connectionStatus();
|
|
18
|
+
$[0] = client;
|
|
19
|
+
$[1] = t0;
|
|
20
|
+
$[2] = t1;
|
|
21
|
+
$[3] = t2;
|
|
22
|
+
} else {
|
|
23
|
+
t0 = $[1];
|
|
24
|
+
t1 = $[2];
|
|
25
|
+
t2 = $[3];
|
|
26
|
+
}
|
|
27
|
+
return useSyncExternalStore(t0, t1, t2);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export { useConnectionStatus as default };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { c } from 'react/compiler-runtime';
|
|
3
|
+
import { useRef, useEffect } from 'react';
|
|
4
|
+
import { u as usePaginatedCore } from './use-paginated-core-CoOfcc-p.mjs';
|
|
5
|
+
|
|
6
|
+
const useInfiniteQuery = (function_, args, options) => {
|
|
7
|
+
const $ = c(15);
|
|
8
|
+
const {
|
|
9
|
+
initialNumItems
|
|
10
|
+
} = options;
|
|
11
|
+
const {
|
|
12
|
+
loadMore,
|
|
13
|
+
pageResults,
|
|
14
|
+
status
|
|
15
|
+
} = usePaginatedCore(function_, args === "skip" ? "skip" : args, options);
|
|
16
|
+
const skipped = args === "skip";
|
|
17
|
+
let resolvedPages;
|
|
18
|
+
if ($[0] !== pageResults) {
|
|
19
|
+
resolvedPages = [];
|
|
20
|
+
for (const result of pageResults) {
|
|
21
|
+
if (result) {
|
|
22
|
+
resolvedPages.push(result.page);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
$[0] = pageResults;
|
|
26
|
+
$[1] = resolvedPages;
|
|
27
|
+
} else {
|
|
28
|
+
resolvedPages = $[1];
|
|
29
|
+
}
|
|
30
|
+
let t0;
|
|
31
|
+
if ($[2] !== initialNumItems || $[3] !== loadMore) {
|
|
32
|
+
t0 = {
|
|
33
|
+
defaultNumItems: initialNumItems,
|
|
34
|
+
loadMore
|
|
35
|
+
};
|
|
36
|
+
$[2] = initialNumItems;
|
|
37
|
+
$[3] = loadMore;
|
|
38
|
+
$[4] = t0;
|
|
39
|
+
} else {
|
|
40
|
+
t0 = $[4];
|
|
41
|
+
}
|
|
42
|
+
const nextRef = useRef(t0);
|
|
43
|
+
let t1;
|
|
44
|
+
if ($[5] !== initialNumItems || $[6] !== loadMore) {
|
|
45
|
+
t1 = () => {
|
|
46
|
+
nextRef.current = {
|
|
47
|
+
defaultNumItems: initialNumItems,
|
|
48
|
+
loadMore
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
$[5] = initialNumItems;
|
|
52
|
+
$[6] = loadMore;
|
|
53
|
+
$[7] = t1;
|
|
54
|
+
} else {
|
|
55
|
+
t1 = $[7];
|
|
56
|
+
}
|
|
57
|
+
useEffect(t1);
|
|
58
|
+
let t2;
|
|
59
|
+
if ($[8] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
|
|
60
|
+
t2 = (numberItems) => {
|
|
61
|
+
const {
|
|
62
|
+
defaultNumItems,
|
|
63
|
+
loadMore: load
|
|
64
|
+
} = nextRef.current;
|
|
65
|
+
load(numberItems ?? defaultNumItems);
|
|
66
|
+
};
|
|
67
|
+
$[8] = t2;
|
|
68
|
+
} else {
|
|
69
|
+
t2 = $[8];
|
|
70
|
+
}
|
|
71
|
+
const fetchNextPage = t2;
|
|
72
|
+
const t3 = status === "CanLoadMore";
|
|
73
|
+
const t4 = !skipped && status === "LoadingMore";
|
|
74
|
+
const t5 = !skipped && status === "LoadingFirstPage";
|
|
75
|
+
let t6;
|
|
76
|
+
if ($[9] !== resolvedPages || $[10] !== status || $[11] !== t3 || $[12] !== t4 || $[13] !== t5) {
|
|
77
|
+
t6 = {
|
|
78
|
+
fetchNextPage,
|
|
79
|
+
hasNextPage: t3,
|
|
80
|
+
isFetchingNextPage: t4,
|
|
81
|
+
isLoading: t5,
|
|
82
|
+
pages: resolvedPages,
|
|
83
|
+
status
|
|
84
|
+
};
|
|
85
|
+
$[9] = resolvedPages;
|
|
86
|
+
$[10] = status;
|
|
87
|
+
$[11] = t3;
|
|
88
|
+
$[12] = t4;
|
|
89
|
+
$[13] = t5;
|
|
90
|
+
$[14] = t6;
|
|
91
|
+
} else {
|
|
92
|
+
t6 = $[14];
|
|
93
|
+
}
|
|
94
|
+
return t6;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export { useInfiniteQuery as default };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useMutation as useMutation$1 } from '@tanstack/react-query';
|
|
3
|
+
import { useRef, useState, useCallback } from 'react';
|
|
4
|
+
import { useLunora } from './LunoraProvider-D38Xp16l.mjs';
|
|
5
|
+
|
|
6
|
+
const useMutation = (function_) => {
|
|
7
|
+
const client = useLunora();
|
|
8
|
+
const pendingCountRef = useRef(0);
|
|
9
|
+
const [pending, setPending] = useState(false);
|
|
10
|
+
const mutation = useMutation$1({
|
|
11
|
+
mutationFn: ({
|
|
12
|
+
args,
|
|
13
|
+
options
|
|
14
|
+
}) => client.mutation(function_, args, options),
|
|
15
|
+
// `onMutate` fires when a call starts, `onSettled` when it resolves or
|
|
16
|
+
// rejects — so overlapping calls compose and `pending` only clears once
|
|
17
|
+
// the last one settles.
|
|
18
|
+
onMutate: () => {
|
|
19
|
+
pendingCountRef.current += 1;
|
|
20
|
+
setPending(true);
|
|
21
|
+
},
|
|
22
|
+
onSettled: () => {
|
|
23
|
+
pendingCountRef.current -= 1;
|
|
24
|
+
setPending(pendingCountRef.current > 0);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
const {
|
|
28
|
+
data,
|
|
29
|
+
error,
|
|
30
|
+
isError,
|
|
31
|
+
mutateAsync,
|
|
32
|
+
reset
|
|
33
|
+
} = mutation;
|
|
34
|
+
const mutate = useCallback((args_0, options_0) => mutateAsync({
|
|
35
|
+
args: args_0,
|
|
36
|
+
options: options_0
|
|
37
|
+
}), [mutateAsync]);
|
|
38
|
+
const withOptimisticUpdate = useCallback((update) => {
|
|
39
|
+
const boundMutate = (args_1, options_1) => mutateAsync({
|
|
40
|
+
args: args_1,
|
|
41
|
+
options: {
|
|
42
|
+
optimisticUpdate: update,
|
|
43
|
+
...options_1
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
data,
|
|
48
|
+
error,
|
|
49
|
+
isError,
|
|
50
|
+
mutate: boundMutate,
|
|
51
|
+
pending,
|
|
52
|
+
reset,
|
|
53
|
+
withOptimisticUpdate
|
|
54
|
+
};
|
|
55
|
+
}, [mutateAsync, data, error, isError, pending, reset]);
|
|
56
|
+
return {
|
|
57
|
+
data,
|
|
58
|
+
error,
|
|
59
|
+
isError,
|
|
60
|
+
mutate,
|
|
61
|
+
pending,
|
|
62
|
+
reset,
|
|
63
|
+
withOptimisticUpdate
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export { useMutation };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { c } from 'react/compiler-runtime';
|
|
3
|
+
import { u as usePaginatedCore } from './use-paginated-core-CoOfcc-p.mjs';
|
|
4
|
+
|
|
5
|
+
const usePaginatedQuery = (function_, args, options) => {
|
|
6
|
+
const $ = c(7);
|
|
7
|
+
const {
|
|
8
|
+
loadMore,
|
|
9
|
+
pageResults,
|
|
10
|
+
status
|
|
11
|
+
} = usePaginatedCore(function_, args === "skip" ? "skip" : args, options);
|
|
12
|
+
let results;
|
|
13
|
+
if ($[0] !== pageResults) {
|
|
14
|
+
results = [];
|
|
15
|
+
for (const result of pageResults) {
|
|
16
|
+
if (result) {
|
|
17
|
+
results.push(...result.page);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
$[0] = pageResults;
|
|
21
|
+
$[1] = results;
|
|
22
|
+
} else {
|
|
23
|
+
results = $[1];
|
|
24
|
+
}
|
|
25
|
+
const skipped = args === "skip";
|
|
26
|
+
const t0 = !skipped && (status === "LoadingFirstPage" || status === "LoadingMore");
|
|
27
|
+
let t1;
|
|
28
|
+
if ($[2] !== loadMore || $[3] !== results || $[4] !== status || $[5] !== t0) {
|
|
29
|
+
t1 = {
|
|
30
|
+
isLoading: t0,
|
|
31
|
+
loadMore,
|
|
32
|
+
results,
|
|
33
|
+
status
|
|
34
|
+
};
|
|
35
|
+
$[2] = loadMore;
|
|
36
|
+
$[3] = results;
|
|
37
|
+
$[4] = status;
|
|
38
|
+
$[5] = t0;
|
|
39
|
+
$[6] = t1;
|
|
40
|
+
} else {
|
|
41
|
+
t1 = $[6];
|
|
42
|
+
}
|
|
43
|
+
return t1;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export { usePaginatedQuery };
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useMemo, useState, useRef, useEffect, useCallback } from 'react';
|
|
3
|
+
import { useLunora } from './LunoraProvider-D38Xp16l.mjs';
|
|
4
|
+
|
|
5
|
+
const makeSessionId = () => {
|
|
6
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
7
|
+
return crypto.randomUUID();
|
|
8
|
+
}
|
|
9
|
+
return `sess-${Math.random().toString(36).slice(2)}-${String(Date.now())}`;
|
|
10
|
+
};
|
|
11
|
+
const DEFAULT_INTERVAL_MS = 1e4;
|
|
12
|
+
const usePresence = (roomId, options) => {
|
|
13
|
+
const client = useLunora();
|
|
14
|
+
const {
|
|
15
|
+
heartbeat,
|
|
16
|
+
intervalMs = DEFAULT_INTERVAL_MS,
|
|
17
|
+
listPresent,
|
|
18
|
+
shardKey
|
|
19
|
+
} = options;
|
|
20
|
+
const generatedSessionId = useMemo(() => options.sessionId ?? makeSessionId(), [options.sessionId]);
|
|
21
|
+
const [present, setPresent] = useState(void 0);
|
|
22
|
+
const dataRef = useRef(options.data);
|
|
23
|
+
const inputsRef = useRef({
|
|
24
|
+
client,
|
|
25
|
+
heartbeat,
|
|
26
|
+
roomId,
|
|
27
|
+
sessionId: generatedSessionId,
|
|
28
|
+
shardKey
|
|
29
|
+
});
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
inputsRef.current = {
|
|
32
|
+
client,
|
|
33
|
+
heartbeat,
|
|
34
|
+
roomId,
|
|
35
|
+
sessionId: generatedSessionId,
|
|
36
|
+
shardKey
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
const sendHeartbeat = useCallback(() => {
|
|
40
|
+
const {
|
|
41
|
+
client: c,
|
|
42
|
+
heartbeat: hb,
|
|
43
|
+
roomId: room,
|
|
44
|
+
sessionId: sid,
|
|
45
|
+
shardKey: sk
|
|
46
|
+
} = inputsRef.current;
|
|
47
|
+
const args = {
|
|
48
|
+
roomId: room,
|
|
49
|
+
sessionId: sid,
|
|
50
|
+
...dataRef.current === void 0 ? {} : {
|
|
51
|
+
data: dataRef.current
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
c.mutation(hb, args, {
|
|
55
|
+
shardKey: sk
|
|
56
|
+
}).catch(() => void 0);
|
|
57
|
+
}, []);
|
|
58
|
+
const setData = useCallback((next) => {
|
|
59
|
+
dataRef.current = next;
|
|
60
|
+
sendHeartbeat();
|
|
61
|
+
}, [sendHeartbeat]);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
sendHeartbeat();
|
|
64
|
+
const handle = setInterval(sendHeartbeat, intervalMs);
|
|
65
|
+
const onVisible = () => {
|
|
66
|
+
if (document.visibilityState === "visible") {
|
|
67
|
+
sendHeartbeat();
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
document.addEventListener("visibilitychange", onVisible);
|
|
71
|
+
return () => {
|
|
72
|
+
clearInterval(handle);
|
|
73
|
+
document.removeEventListener("visibilitychange", onVisible);
|
|
74
|
+
};
|
|
75
|
+
}, [sendHeartbeat, intervalMs]);
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
const release = client.acquireConnectionContext({
|
|
78
|
+
roomId,
|
|
79
|
+
sessionId: generatedSessionId
|
|
80
|
+
}, {
|
|
81
|
+
shardKey
|
|
82
|
+
});
|
|
83
|
+
return release;
|
|
84
|
+
}, [client, roomId, generatedSessionId, shardKey]);
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
let cancelled = false;
|
|
87
|
+
const unsubscribe = client.subscribe(listPresent, {
|
|
88
|
+
roomId
|
|
89
|
+
}, (value) => {
|
|
90
|
+
if (!cancelled) {
|
|
91
|
+
setPresent(value);
|
|
92
|
+
}
|
|
93
|
+
}, {
|
|
94
|
+
shardKey
|
|
95
|
+
});
|
|
96
|
+
return () => {
|
|
97
|
+
cancelled = true;
|
|
98
|
+
unsubscribe();
|
|
99
|
+
};
|
|
100
|
+
}, [client, listPresent.__lunoraRef, roomId, shardKey]);
|
|
101
|
+
return {
|
|
102
|
+
present,
|
|
103
|
+
sessionId: generatedSessionId,
|
|
104
|
+
setData
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export { usePresence };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useQueryClient, useQuery as useQuery$1 } from '@tanstack/react-query';
|
|
3
|
+
import { useMemo, useEffect } from 'react';
|
|
4
|
+
import { g as getSubscriptionRegistry } from './cache-CItk3fgN.mjs';
|
|
5
|
+
import { useLunora } from './LunoraProvider-D38Xp16l.mjs';
|
|
6
|
+
import { l as lunoraQueryKey, a as stableStringify, s as serializeQueryKey } from './query-key-C5rufkEE.mjs';
|
|
7
|
+
|
|
8
|
+
const useQuery = (function_, args, options = {}) => {
|
|
9
|
+
const client = useLunora();
|
|
10
|
+
const queryClient = useQueryClient();
|
|
11
|
+
const {
|
|
12
|
+
shardKey
|
|
13
|
+
} = options;
|
|
14
|
+
const skipped = args === "skip";
|
|
15
|
+
const argsRecord = skipped ? {} : args;
|
|
16
|
+
const queryKey = useMemo(() => lunoraQueryKey(function_, argsRecord, shardKey), [function_.__lunoraRef, stableStringify(argsRecord), shardKey]);
|
|
17
|
+
const {
|
|
18
|
+
data
|
|
19
|
+
} = useQuery$1({
|
|
20
|
+
enabled: !skipped,
|
|
21
|
+
queryFn: () => client.query(function_, argsRecord, {
|
|
22
|
+
shardKey
|
|
23
|
+
}),
|
|
24
|
+
queryKey,
|
|
25
|
+
// Lunora is push-driven: once the initial fetch resolves, the WS owns
|
|
26
|
+
// freshness. Staleness only matters when the subscription is missing,
|
|
27
|
+
// and the registry handles that with a polling fallback.
|
|
28
|
+
staleTime: Number.POSITIVE_INFINITY
|
|
29
|
+
});
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (skipped) {
|
|
32
|
+
return () => {
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const registry = getSubscriptionRegistry(client);
|
|
36
|
+
return registry.attach(queryClient, queryKey, function_, argsRecord, shardKey);
|
|
37
|
+
}, [client, queryClient, serializeQueryKey(queryKey), skipped]);
|
|
38
|
+
return data;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export { useQuery as default };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { evaluate } from '@lunora/ratelimit';
|
|
3
|
+
import { useRef, useReducer, useCallback, useEffect } from 'react';
|
|
4
|
+
|
|
5
|
+
const useRateLimit = (config, options = {}) => {
|
|
6
|
+
const now = options.now ?? Date.now;
|
|
7
|
+
const tickMs = options.tickMs ?? 1e3;
|
|
8
|
+
const valueRef = useRef(void 0);
|
|
9
|
+
const [, forceRender] = useReducer((count) => count + 1, 0);
|
|
10
|
+
const consume = useCallback((count_0 = 1) => {
|
|
11
|
+
const {
|
|
12
|
+
status,
|
|
13
|
+
value
|
|
14
|
+
} = evaluate(config, valueRef.current, {
|
|
15
|
+
consume: true,
|
|
16
|
+
count: count_0,
|
|
17
|
+
now: now(),
|
|
18
|
+
reserve: false
|
|
19
|
+
});
|
|
20
|
+
if (value !== void 0) {
|
|
21
|
+
valueRef.current = value;
|
|
22
|
+
}
|
|
23
|
+
forceRender();
|
|
24
|
+
return status;
|
|
25
|
+
}, [config, now]);
|
|
26
|
+
const check = useCallback((count_1 = 1) => evaluate(config, valueRef.current, {
|
|
27
|
+
consume: false,
|
|
28
|
+
count: count_1,
|
|
29
|
+
now: now(),
|
|
30
|
+
reserve: false
|
|
31
|
+
}).status.ok, [config, now]);
|
|
32
|
+
const reset = useCallback(() => {
|
|
33
|
+
valueRef.current = void 0;
|
|
34
|
+
forceRender();
|
|
35
|
+
}, []);
|
|
36
|
+
const {
|
|
37
|
+
status: status_0
|
|
38
|
+
} = evaluate(config, valueRef.current, {
|
|
39
|
+
consume: false,
|
|
40
|
+
count: 1,
|
|
41
|
+
now: now(),
|
|
42
|
+
reserve: false
|
|
43
|
+
});
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (status_0.ok) {
|
|
46
|
+
return () => {
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const handle = setInterval(forceRender, tickMs);
|
|
50
|
+
return () => {
|
|
51
|
+
clearInterval(handle);
|
|
52
|
+
};
|
|
53
|
+
}, [status_0.ok, tickMs]);
|
|
54
|
+
return {
|
|
55
|
+
check,
|
|
56
|
+
consume,
|
|
57
|
+
disabled: !status_0.ok,
|
|
58
|
+
ok: status_0.ok,
|
|
59
|
+
reset,
|
|
60
|
+
retryAfter: status_0.retryAfter
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export { useRateLimit };
|