nuxt-convex-module 0.0.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.
Files changed (110) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +242 -0
  3. package/dist/chunks/index.mjs +64 -0
  4. package/dist/devtools-client/200.html +1 -0
  5. package/dist/devtools-client/404.html +1 -0
  6. package/dist/devtools-client/_nuxt/B29GGJ5n.js +1 -0
  7. package/dist/devtools-client/_nuxt/BSDEpQet.js +1 -0
  8. package/dist/devtools-client/_nuxt/BY23OQOo.js +1 -0
  9. package/dist/devtools-client/_nuxt/BZYRaL-G.js +1 -0
  10. package/dist/devtools-client/_nuxt/Byw6FxnC.js +1 -0
  11. package/dist/devtools-client/_nuxt/CJJ_hcZN.js +1 -0
  12. package/dist/devtools-client/_nuxt/CS6WMFOx.js +2 -0
  13. package/dist/devtools-client/_nuxt/CZbAGcNC.js +1 -0
  14. package/dist/devtools-client/_nuxt/Cr6_aklh.js +2 -0
  15. package/dist/devtools-client/_nuxt/DNfpyBVU.js +3 -0
  16. package/dist/devtools-client/_nuxt/DPWXUmcI.js +1 -0
  17. package/dist/devtools-client/_nuxt/builds/latest.json +1 -0
  18. package/dist/devtools-client/_nuxt/builds/meta/a7708498-bd09-4de2-b5ab-19b380293c77.json +1 -0
  19. package/dist/devtools-client/_nuxt/dy_xXcv9.js +1 -0
  20. package/dist/devtools-client/_nuxt/entry.DbTJhkHT.css +1 -0
  21. package/dist/devtools-client/_nuxt/error-404.CwGGO2Kz.css +1 -0
  22. package/dist/devtools-client/_nuxt/error-500.8ZJqCQm8.css +1 -0
  23. package/dist/devtools-client/_nuxt/ncgEdbEt.js +1 -0
  24. package/dist/devtools-client/_nuxt/queries.CE_dtt9-.css +1 -0
  25. package/dist/devtools-client/auth/index.html +1 -0
  26. package/dist/devtools-client/icon.svg +6 -0
  27. package/dist/devtools-client/index.html +1 -0
  28. package/dist/devtools-client/logs/index.html +1 -0
  29. package/dist/devtools-client/queries/index.html +1 -0
  30. package/dist/module.d.ts +189 -0
  31. package/dist/module.js +489 -0
  32. package/dist/module.json +13 -0
  33. package/dist/runtime/auth0/vue/index.d.ts +58 -0
  34. package/dist/runtime/auth0/vue/index.js +33 -0
  35. package/dist/runtime/better-auth/nuxt/middleware.d.ts +14 -0
  36. package/dist/runtime/better-auth/nuxt/middleware.js +39 -0
  37. package/dist/runtime/better-auth/nuxt/proxy.d.ts +6 -0
  38. package/dist/runtime/better-auth/nuxt/proxy.js +11 -0
  39. package/dist/runtime/better-auth/nuxt/server.d.ts +106 -0
  40. package/dist/runtime/better-auth/nuxt/server.js +123 -0
  41. package/dist/runtime/better-auth/vue/auth-boundary.d.ts +131 -0
  42. package/dist/runtime/better-auth/vue/auth-boundary.js +78 -0
  43. package/dist/runtime/better-auth/vue/client.d.ts +358 -0
  44. package/dist/runtime/better-auth/vue/client.js +5 -0
  45. package/dist/runtime/better-auth/vue/cross-domain.d.ts +39 -0
  46. package/dist/runtime/better-auth/vue/cross-domain.js +45 -0
  47. package/dist/runtime/better-auth/vue/hydration.d.ts +20 -0
  48. package/dist/runtime/better-auth/vue/hydration.js +52 -0
  49. package/dist/runtime/better-auth/vue/index.d.ts +12 -0
  50. package/dist/runtime/better-auth/vue/index.js +6 -0
  51. package/dist/runtime/better-auth/vue/plugin.client.d.ts +6 -0
  52. package/dist/runtime/better-auth/vue/plugin.client.js +32 -0
  53. package/dist/runtime/better-auth/vue/plugin.server.d.ts +24 -0
  54. package/dist/runtime/better-auth/vue/plugin.server.js +42 -0
  55. package/dist/runtime/better-auth/vue/use-auth.d.ts +92 -0
  56. package/dist/runtime/better-auth/vue/use-auth.js +82 -0
  57. package/dist/runtime/clerk/vue/index.d.ts +81 -0
  58. package/dist/runtime/clerk/vue/index.js +49 -0
  59. package/dist/runtime/devtools/bridge.d.ts +12 -0
  60. package/dist/runtime/devtools/bridge.js +191 -0
  61. package/dist/runtime/devtools/plugin.client.d.ts +8 -0
  62. package/dist/runtime/devtools/plugin.client.js +34 -0
  63. package/dist/runtime/devtools/types.d.ts +78 -0
  64. package/dist/runtime/devtools/types.js +0 -0
  65. package/dist/runtime/nuxt/composables/use-async-query.d.ts +144 -0
  66. package/dist/runtime/nuxt/composables/use-async-query.js +104 -0
  67. package/dist/runtime/nuxt/config.d.ts +7 -0
  68. package/dist/runtime/nuxt/config.js +12 -0
  69. package/dist/runtime/nuxt/index.d.ts +136 -0
  70. package/dist/runtime/nuxt/index.js +91 -0
  71. package/dist/runtime/polar/vue/components.d.ts +184 -0
  72. package/dist/runtime/polar/vue/components.js +115 -0
  73. package/dist/runtime/vue/auth/helpers.d.ts +67 -0
  74. package/dist/runtime/vue/auth/helpers.js +50 -0
  75. package/dist/runtime/vue/auth/index.d.ts +125 -0
  76. package/dist/runtime/vue/auth/index.js +97 -0
  77. package/dist/runtime/vue/client.d.ts +305 -0
  78. package/dist/runtime/vue/client.js +382 -0
  79. package/dist/runtime/vue/composables/use-action.d.ts +62 -0
  80. package/dist/runtime/vue/composables/use-action.js +13 -0
  81. package/dist/runtime/vue/composables/use-connection-state.d.ts +30 -0
  82. package/dist/runtime/vue/composables/use-connection-state.js +14 -0
  83. package/dist/runtime/vue/composables/use-mutation.d.ts +76 -0
  84. package/dist/runtime/vue/composables/use-mutation.js +34 -0
  85. package/dist/runtime/vue/composables/use-paginated-query.d.ts +132 -0
  86. package/dist/runtime/vue/composables/use-paginated-query.js +513 -0
  87. package/dist/runtime/vue/composables/use-queries.d.ts +62 -0
  88. package/dist/runtime/vue/composables/use-queries.js +37 -0
  89. package/dist/runtime/vue/composables/use-query.d.ts +87 -0
  90. package/dist/runtime/vue/composables/use-query.js +53 -0
  91. package/dist/runtime/vue/composables/use-storage-url.d.ts +46 -0
  92. package/dist/runtime/vue/composables/use-storage-url.js +9 -0
  93. package/dist/runtime/vue/composables/use-subscription.d.ts +15 -0
  94. package/dist/runtime/vue/composables/use-subscription.js +33 -0
  95. package/dist/runtime/vue/composables/use-upload-queue.d.ts +117 -0
  96. package/dist/runtime/vue/composables/use-upload-queue.js +115 -0
  97. package/dist/runtime/vue/composables/use-upload.d.ts +134 -0
  98. package/dist/runtime/vue/composables/use-upload.js +114 -0
  99. package/dist/runtime/vue/hydration.d.ts +66 -0
  100. package/dist/runtime/vue/hydration.js +46 -0
  101. package/dist/runtime/vue/index.d.ts +94 -0
  102. package/dist/runtime/vue/index.js +42 -0
  103. package/dist/runtime/vue/plugin.d.ts +18 -0
  104. package/dist/runtime/vue/plugin.js +16 -0
  105. package/dist/runtime/vue/provide.d.ts +39 -0
  106. package/dist/runtime/vue/provide.js +15 -0
  107. package/dist/runtime/vue/queries-observer.d.ts +41 -0
  108. package/dist/runtime/vue/queries-observer.js +126 -0
  109. package/dist/types.d.ts +3 -0
  110. package/package.json +233 -0
@@ -0,0 +1,513 @@
1
+ import { getFunctionName } from "convex/server";
2
+ import { ConvexError, compareValues, convexToJson } from "convex/values";
3
+ import { computed, shallowRef, toValue } from "vue";
4
+ import { useConvex } from "../client.js";
5
+ import { useConvexQueries } from "./use-queries.js";
6
+ const splitQuery = (key, splitCursor, continueCursor) => (prevState) => {
7
+ const queries = { ...prevState.queries };
8
+ const splitKey1 = prevState.nextPageKey;
9
+ const splitKey2 = prevState.nextPageKey + 1;
10
+ const nextPageKey = prevState.nextPageKey + 2;
11
+ queries[splitKey1] = {
12
+ query: prevState.query,
13
+ args: {
14
+ ...prevState.args,
15
+ paginationOpts: {
16
+ ...prevState.queries[key].args.paginationOpts,
17
+ endCursor: splitCursor
18
+ }
19
+ }
20
+ };
21
+ queries[splitKey2] = {
22
+ query: prevState.query,
23
+ args: {
24
+ ...prevState.args,
25
+ paginationOpts: {
26
+ ...prevState.queries[key].args.paginationOpts,
27
+ cursor: splitCursor,
28
+ endCursor: continueCursor
29
+ }
30
+ }
31
+ };
32
+ const ongoingSplits = { ...prevState.ongoingSplits };
33
+ ongoingSplits[key] = [splitKey1, splitKey2];
34
+ return {
35
+ ...prevState,
36
+ nextPageKey,
37
+ queries,
38
+ ongoingSplits
39
+ };
40
+ };
41
+ const completeSplitQuery = (key) => (prevState) => {
42
+ const completedSplit = prevState.ongoingSplits[key];
43
+ if (completedSplit === void 0) {
44
+ return prevState;
45
+ }
46
+ const queries = { ...prevState.queries };
47
+ delete queries[key];
48
+ const ongoingSplits = { ...prevState.ongoingSplits };
49
+ delete ongoingSplits[key];
50
+ let pageKeys = prevState.pageKeys.slice();
51
+ const pageIndex = prevState.pageKeys.findIndex((v) => v === key);
52
+ if (pageIndex >= 0) {
53
+ pageKeys = [
54
+ ...prevState.pageKeys.slice(0, pageIndex),
55
+ ...completedSplit,
56
+ ...prevState.pageKeys.slice(pageIndex + 1)
57
+ ];
58
+ }
59
+ return {
60
+ ...prevState,
61
+ queries,
62
+ pageKeys,
63
+ ongoingSplits
64
+ };
65
+ };
66
+ function isPaginatedQueryValue(value) {
67
+ return typeof value === "object" && value !== null && "page" in value && Array.isArray(value.page) && "isDone" in value && "continueCursor" in value;
68
+ }
69
+ const INTERNAL_ERROR_STATUS = "Error";
70
+ const SPLIT_PAGE_STATUSES = ["SplitRecommended", "SplitRequired"];
71
+ export function usePaginatedQuery(query, args, options) {
72
+ const internal = usePaginatedQueryInternal(
73
+ () => query,
74
+ args,
75
+ () => options
76
+ );
77
+ return splitToRefs(internal);
78
+ }
79
+ function splitToRefs(internal) {
80
+ const current = () => {
81
+ const value = internal.value;
82
+ if (value.status === INTERNAL_ERROR_STATUS) throw value.error;
83
+ return value;
84
+ };
85
+ return {
86
+ results: computed(() => current().results),
87
+ status: computed(() => current().status),
88
+ isLoading: computed(() => current().isLoading),
89
+ loadMore: (numItems) => {
90
+ current().loadMore(numItems);
91
+ }
92
+ };
93
+ }
94
+ function usePaginatedQueryInternal(query, args, options) {
95
+ const initialOptions = toValue(options);
96
+ if (typeof initialOptions?.initialNumItems !== "number" || initialOptions.initialNumItems < 0) {
97
+ throw new Error(
98
+ `\`options.initialNumItems\` must be a positive number. Received \`${initialOptions?.initialNumItems}\`.`
99
+ );
100
+ }
101
+ const convex = useConvex();
102
+ const logger = convex.logger;
103
+ function createInitialState(q, currentArgs, skip, initialNumItems) {
104
+ const id = nextPaginationId();
105
+ return {
106
+ query: q,
107
+ args: currentArgs,
108
+ id,
109
+ nextPageKey: 1,
110
+ pageKeys: skip ? [] : [0],
111
+ queries: skip ? {} : {
112
+ 0: {
113
+ query: q,
114
+ args: {
115
+ ...currentArgs,
116
+ paginationOpts: {
117
+ numItems: initialNumItems,
118
+ cursor: null,
119
+ id
120
+ }
121
+ }
122
+ }
123
+ },
124
+ ongoingSplits: {},
125
+ skip
126
+ };
127
+ }
128
+ const initialQuery = toValue(query);
129
+ const initialRawArgs = toValue(args);
130
+ const initialSkip = initialRawArgs === "skip";
131
+ const initialArgsObject = initialSkip ? {} : initialRawArgs;
132
+ const { initialNumItems: initialInitialNumItems } = toValue(options);
133
+ const state = shallowRef(
134
+ createInitialState(initialQuery, initialArgsObject, initialSkip, initialInitialNumItems)
135
+ );
136
+ const queriesInput = computed(() => {
137
+ const q = toValue(query);
138
+ const rawArgs = toValue(args);
139
+ const skip = rawArgs === "skip";
140
+ const argsObject = skip ? {} : rawArgs;
141
+ const { initialNumItems } = toValue(options);
142
+ const currentState = state.value;
143
+ const needsReset = getFunctionName(q) !== getFunctionName(currentState.query) || JSON.stringify(convexToJson(argsObject)) !== JSON.stringify(convexToJson(currentState.args)) || skip !== currentState.skip;
144
+ if (needsReset) {
145
+ state.value = createInitialState(q, argsObject, skip, initialNumItems);
146
+ }
147
+ return state.value.queries;
148
+ });
149
+ const allResults = useConvexQueries(queriesInput);
150
+ function resetState() {
151
+ const q = toValue(query);
152
+ const rawArgs = toValue(args);
153
+ const skip = rawArgs === "skip";
154
+ const argsObject = skip ? {} : rawArgs;
155
+ const { initialNumItems } = toValue(options);
156
+ state.value = createInitialState(q, argsObject, skip, initialNumItems);
157
+ }
158
+ return computed(() => {
159
+ const currState = state.value;
160
+ const { initialNumItems } = toValue(options);
161
+ const resultsObject = allResults.value;
162
+ let lastResult;
163
+ let maybeError;
164
+ const allItems = [];
165
+ for (const pageKey of currState.pageKeys) {
166
+ const currResult = resultsObject[pageKey];
167
+ if (currResult === void 0) {
168
+ lastResult = void 0;
169
+ break;
170
+ }
171
+ if (currResult instanceof Error) {
172
+ const isInvalidCursor = currResult.message.includes("InvalidCursor") || currResult instanceof ConvexError && typeof currResult.data === "object" && currResult.data?.isConvexSystemError === true && currResult.data?.paginationError === "InvalidCursor";
173
+ if (isInvalidCursor) {
174
+ logger.warn?.(
175
+ "usePaginatedQuery hit error, resetting pagination state: " + currResult.message
176
+ );
177
+ resetState();
178
+ return {
179
+ results: [],
180
+ status: "LoadingFirstPage",
181
+ isLoading: true,
182
+ loadMore: noopLoadMore
183
+ };
184
+ }
185
+ maybeError = currResult;
186
+ break;
187
+ }
188
+ if (!isPaginatedQueryValue(currResult)) {
189
+ throw new Error("Paginated query returned a non-pagination result.");
190
+ }
191
+ const result = currResult;
192
+ const ongoingSplit = currState.ongoingSplits[pageKey];
193
+ if (ongoingSplit !== void 0) {
194
+ if (resultsObject[ongoingSplit[0]] !== void 0 && resultsObject[ongoingSplit[1]] !== void 0) {
195
+ state.value = completeSplitQuery(pageKey)(state.value);
196
+ }
197
+ } else if (result.splitCursor && (SPLIT_PAGE_STATUSES.includes(result.pageStatus) || result.page.length > initialNumItems * 2)) {
198
+ state.value = splitQuery(pageKey, result.splitCursor, result.continueCursor)(state.value);
199
+ }
200
+ if (result.pageStatus === SPLIT_PAGE_STATUSES[1]) {
201
+ lastResult = void 0;
202
+ break;
203
+ }
204
+ allItems.push(...result.page);
205
+ lastResult = result;
206
+ }
207
+ if (maybeError !== void 0) {
208
+ return {
209
+ results: allItems,
210
+ status: INTERNAL_ERROR_STATUS,
211
+ isLoading: false,
212
+ error: maybeError,
213
+ loadMore: noopLoadMore
214
+ };
215
+ }
216
+ if (lastResult === void 0) {
217
+ if (currState.nextPageKey === 1) {
218
+ return {
219
+ results: allItems,
220
+ status: "LoadingFirstPage",
221
+ isLoading: true,
222
+ loadMore: noopLoadMore
223
+ };
224
+ }
225
+ return {
226
+ results: allItems,
227
+ status: "LoadingMore",
228
+ isLoading: true,
229
+ loadMore: noopLoadMore
230
+ };
231
+ }
232
+ if (lastResult.isDone) {
233
+ return {
234
+ results: allItems,
235
+ status: "Exhausted",
236
+ isLoading: false,
237
+ loadMore: noopLoadMore
238
+ };
239
+ }
240
+ const continueCursor = lastResult.continueCursor;
241
+ let alreadyLoadingMore = false;
242
+ return {
243
+ results: allItems,
244
+ status: "CanLoadMore",
245
+ isLoading: false,
246
+ loadMore: (numItems) => {
247
+ if (alreadyLoadingMore) return;
248
+ alreadyLoadingMore = true;
249
+ const prev = state.value;
250
+ const pageKeys = [...prev.pageKeys, prev.nextPageKey];
251
+ const queries = { ...prev.queries };
252
+ queries[prev.nextPageKey] = {
253
+ query: prev.query,
254
+ // Pair the cursor with the args snapshot captured in state (not the
255
+ // live args input) so the page query stays consistent with the
256
+ // session that produced the cursor, mirroring upstream's
257
+ // `prevState.args`.
258
+ args: {
259
+ ...prev.args,
260
+ paginationOpts: {
261
+ numItems,
262
+ cursor: continueCursor,
263
+ id: prev.id
264
+ }
265
+ }
266
+ };
267
+ state.value = {
268
+ ...prev,
269
+ nextPageKey: prev.nextPageKey + 1,
270
+ pageKeys,
271
+ queries
272
+ };
273
+ }
274
+ };
275
+ });
276
+ }
277
+ let paginationId = 0;
278
+ function nextPaginationId() {
279
+ return ++paginationId;
280
+ }
281
+ export function resetPaginationId() {
282
+ paginationId = 0;
283
+ }
284
+ function noopLoadMore(_numItems) {
285
+ }
286
+ export const useConvexPaginatedQuery = usePaginatedQuery;
287
+ export function optimisticallyUpdateValueInPaginatedQuery(localStore, query, args, updateValue) {
288
+ const expectedArgs = JSON.stringify(convexToJson(args));
289
+ for (const queryResult of localStore.getAllQueries(query)) {
290
+ if (queryResult.value !== void 0) {
291
+ const { paginationOpts: _, ...innerArgs } = queryResult.args;
292
+ if (JSON.stringify(convexToJson(innerArgs)) === expectedArgs) {
293
+ const value = queryResult.value;
294
+ if (typeof value === "object" && value !== null && Array.isArray(value.page)) {
295
+ localStore.setQuery(query, queryResult.args, {
296
+ ...value,
297
+ page: value.page.map(updateValue)
298
+ });
299
+ }
300
+ }
301
+ }
302
+ }
303
+ }
304
+ export function insertAtTop(options) {
305
+ const { paginatedQuery, argsToMatch, localQueryStore, item } = options;
306
+ const queries = localQueryStore.getAllQueries(paginatedQuery);
307
+ const queriesThatMatch = queries.filter((q) => matchesArgs(q.args, argsToMatch));
308
+ const firstPage = queriesThatMatch.find(
309
+ (q) => q.args.paginationOpts.cursor === null
310
+ );
311
+ if (firstPage === void 0 || firstPage.value === void 0) {
312
+ return;
313
+ }
314
+ const value = firstPage.value;
315
+ localQueryStore.setQuery(paginatedQuery, firstPage.args, {
316
+ ...value,
317
+ page: [item, ...value.page]
318
+ });
319
+ }
320
+ export function insertAtBottomIfLoaded(options) {
321
+ const { paginatedQuery, argsToMatch, localQueryStore, item } = options;
322
+ const queries = localQueryStore.getAllQueries(paginatedQuery);
323
+ const queriesThatMatch = queries.filter((q) => matchesArgs(q.args, argsToMatch));
324
+ const lastPage = queriesThatMatch.find(
325
+ (q) => q.value !== void 0 && q.value.isDone
326
+ );
327
+ if (lastPage === void 0) return;
328
+ const value = lastPage.value;
329
+ localQueryStore.setQuery(paginatedQuery, lastPage.args, {
330
+ ...value,
331
+ page: [...value.page, item]
332
+ });
333
+ }
334
+ export function insertAtPosition(options) {
335
+ const {
336
+ paginatedQuery,
337
+ argsToMatch,
338
+ sortOrder,
339
+ sortKeyFromItem,
340
+ localQueryStore,
341
+ item
342
+ } = options;
343
+ const queries = localQueryStore.getAllQueries(paginatedQuery);
344
+ const queryGroups = {};
345
+ for (const q of queries) {
346
+ if (argsToMatch !== void 0 && !Object.keys(argsToMatch).every(
347
+ (k) => argsToMatch[k] === q.args[k]
348
+ )) {
349
+ continue;
350
+ }
351
+ const key = JSON.stringify(
352
+ Object.fromEntries(
353
+ Object.entries(q.args).map(([k, v]) => [
354
+ k,
355
+ k === "paginationOpts" ? v.id : v
356
+ ])
357
+ )
358
+ );
359
+ queryGroups[key] ??= [];
360
+ queryGroups[key].push(q);
361
+ }
362
+ for (const pageQueries of Object.values(queryGroups)) {
363
+ insertAtPositionInPages({
364
+ pageQueries,
365
+ paginatedQuery,
366
+ sortOrder,
367
+ sortKeyFromItem,
368
+ localQueryStore,
369
+ item
370
+ });
371
+ }
372
+ }
373
+ function insertAtPositionInPages(options) {
374
+ const {
375
+ pageQueries,
376
+ sortOrder,
377
+ sortKeyFromItem,
378
+ localQueryStore,
379
+ item,
380
+ paginatedQuery
381
+ } = options;
382
+ const insertedKey = sortKeyFromItem(item);
383
+ const loadedPages = pageQueries.filter(
384
+ (q) => q.value !== void 0 && q.value.page.length > 0
385
+ );
386
+ const sortedPages = loadedPages.slice().sort((a, b) => {
387
+ const aKey = sortKeyFromItem(
388
+ a.value.page[0]
389
+ );
390
+ const bKey = sortKeyFromItem(
391
+ b.value.page[0]
392
+ );
393
+ return sortOrder === "asc" ? compareValues(aKey, bKey) : compareValues(bKey, aKey);
394
+ });
395
+ const firstLoadedPage = sortedPages[0];
396
+ if (firstLoadedPage === void 0) return;
397
+ const firstValue = firstLoadedPage.value;
398
+ const firstPageKey = sortKeyFromItem(firstValue.page[0]);
399
+ const isBeforeFirstPage = sortOrder === "asc" ? compareValues(insertedKey, firstPageKey) <= 0 : compareValues(insertedKey, firstPageKey) >= 0;
400
+ if (isBeforeFirstPage) {
401
+ if (firstLoadedPage.args.paginationOpts.cursor === null) {
402
+ localQueryStore.setQuery(paginatedQuery, firstLoadedPage.args, {
403
+ ...firstValue,
404
+ page: [item, ...firstValue.page]
405
+ });
406
+ }
407
+ return;
408
+ }
409
+ const lastLoadedPage = sortedPages[sortedPages.length - 1];
410
+ if (lastLoadedPage === void 0) return;
411
+ const lastValue = lastLoadedPage.value;
412
+ const lastPageKey = sortKeyFromItem(
413
+ lastValue.page[lastValue.page.length - 1]
414
+ );
415
+ const isAfterLastPage = sortOrder === "asc" ? compareValues(insertedKey, lastPageKey) >= 0 : compareValues(insertedKey, lastPageKey) <= 0;
416
+ if (isAfterLastPage) {
417
+ if (lastValue.isDone) {
418
+ localQueryStore.setQuery(paginatedQuery, lastLoadedPage.args, {
419
+ ...lastValue,
420
+ page: [...lastValue.page, item]
421
+ });
422
+ }
423
+ return;
424
+ }
425
+ const successorPageIndex = sortedPages.findIndex((p) => {
426
+ const headKey = sortKeyFromItem(
427
+ p.value.page[0]
428
+ );
429
+ return sortOrder === "asc" ? compareValues(headKey, insertedKey) > 0 : compareValues(headKey, insertedKey) < 0;
430
+ });
431
+ const pageToUpdate = successorPageIndex === -1 ? sortedPages[sortedPages.length - 1] : sortedPages[successorPageIndex - 1];
432
+ if (pageToUpdate === void 0) return;
433
+ const pageValue = pageToUpdate.value;
434
+ const indexWithinPage = pageValue.page.findIndex((e) => {
435
+ const k = sortKeyFromItem(e);
436
+ return sortOrder === "asc" ? compareValues(k, insertedKey) >= 0 : compareValues(k, insertedKey) <= 0;
437
+ });
438
+ const newPage = indexWithinPage === -1 ? [...pageValue.page, item] : [
439
+ ...pageValue.page.slice(0, indexWithinPage),
440
+ item,
441
+ ...pageValue.page.slice(indexWithinPage)
442
+ ];
443
+ localQueryStore.setQuery(paginatedQuery, pageToUpdate.args, {
444
+ ...pageValue,
445
+ page: newPage
446
+ });
447
+ }
448
+ function matchesArgs(args, argsToMatch) {
449
+ if (argsToMatch === void 0) return true;
450
+ return Object.keys(argsToMatch).every(
451
+ (k) => compareValues(argsToMatch[k], args[k]) === 0
452
+ );
453
+ }
454
+ export function usePaginatedQuery_experimental(queryOrOptions, args, options) {
455
+ const firstValue = toValue(queryOrOptions);
456
+ const isObjectOptions = typeof firstValue === "object" && firstValue !== null && "query" in firstValue;
457
+ if (isObjectOptions) {
458
+ const optsGetter = () => toValue(
459
+ queryOrOptions
460
+ );
461
+ const internal2 = usePaginatedQueryInternal(
462
+ () => optsGetter().query,
463
+ () => optsGetter().args,
464
+ () => ({ initialNumItems: optsGetter().initialNumItems })
465
+ );
466
+ return computed(
467
+ () => reshapeToObjectForm2(internal2.value, optsGetter().throwOnError ?? false)
468
+ );
469
+ }
470
+ const query = queryOrOptions;
471
+ const internal = usePaginatedQueryInternal(
472
+ () => query,
473
+ args,
474
+ () => options
475
+ );
476
+ return splitToRefs(internal);
477
+ }
478
+ function reshapeToObjectForm2(internal, throwOnError) {
479
+ const { results, loadMore } = internal;
480
+ if (internal.status === INTERNAL_ERROR_STATUS) {
481
+ if (throwOnError) {
482
+ throw internal.error;
483
+ }
484
+ return {
485
+ // Upstream returns an empty array on error, never the pages loaded
486
+ // before the failure.
487
+ data: [],
488
+ status: "error",
489
+ canLoadMore: false,
490
+ isLoading: false,
491
+ error: internal.error,
492
+ loadMore
493
+ };
494
+ }
495
+ if (internal.status === "LoadingFirstPage" || internal.status === "LoadingMore") {
496
+ return {
497
+ data: internal.status === "LoadingFirstPage" ? void 0 : results,
498
+ status: "pending",
499
+ canLoadMore: false,
500
+ isLoading: true,
501
+ error: void 0,
502
+ loadMore
503
+ };
504
+ }
505
+ return {
506
+ data: results,
507
+ status: "success",
508
+ canLoadMore: internal.status === "CanLoadMore",
509
+ isLoading: false,
510
+ error: void 0,
511
+ loadMore
512
+ };
513
+ }
@@ -0,0 +1,62 @@
1
+ import { type MaybeRefOrGetter, type ShallowRef } from 'vue';
2
+ import { type CreateWatch } from '../queries-observer.js';
3
+ import type { RequestForQueries } from 'convex/react';
4
+ /**
5
+ * Load a variable number of reactive Convex queries.
6
+ *
7
+ * `useQueries` is similar to {@link useQuery} but it allows
8
+ * loading multiple queries which can be useful for loading a dynamic number
9
+ * of queries without violating the rules of composables.
10
+ *
11
+ * This composable accepts an object whose keys are identifiers for each query and the
12
+ * values are objects of `{ query: FunctionReference, args: Record<string, Value> }`. The
13
+ * `query` is a FunctionReference for the Convex query function to load, and the `args` are
14
+ * the arguments to that function.
15
+ *
16
+ * The composable returns an object that maps each identifier to the result of the query,
17
+ * `undefined` if the query is still loading, or an instance of `Error` if the query
18
+ * threw an exception.
19
+ *
20
+ * For example if you loaded a query like:
21
+ * ```typescript
22
+ * const results = useQueries({
23
+ * messagesInGeneral: {
24
+ * query: "listMessages",
25
+ * args: { channel: "#general" }
26
+ * }
27
+ * });
28
+ * ```
29
+ * then the result would look like:
30
+ * ```typescript
31
+ * {
32
+ * messagesInGeneral: [{
33
+ * channel: "#general",
34
+ * body: "hello"
35
+ * _id: ...,
36
+ * _creationTime: ...
37
+ * }]
38
+ * }
39
+ * ```
40
+ *
41
+ * This composable returns a reactive result that updates
42
+ * whenever any of the query results change.
43
+ *
44
+ * Throws an error if no Convex client has been provided (see {@link useConvex}).
45
+ *
46
+ * @param queries - An object mapping identifiers to objects of
47
+ * `{query: string, args: Record<string, Value> }` describing which query
48
+ * functions to fetch. Reactive (ref/computed/getter) or plain.
49
+ * @returns A shallow ref of an object with the same keys as the input. The
50
+ * values are the result of the query function, `undefined` if it's still
51
+ * loading, or an `Error` if it threw an exception.
52
+ *
53
+ * @public
54
+ */
55
+ export declare function useQueries(queries: MaybeRefOrGetter<RequestForQueries>): ShallowRef<Record<string, any | undefined | Error>>;
56
+ /**
57
+ * Internal version of `useQueries` that is exported for testing.
58
+ */
59
+ export declare function useQueriesHelper(queries: MaybeRefOrGetter<RequestForQueries>, createWatch: CreateWatch): ShallowRef<Record<string, any | undefined | Error>>;
60
+ export type { RequestForQueries } from 'convex/react';
61
+ /** @public */
62
+ export declare const useConvexQueries: typeof useQueries;
@@ -0,0 +1,37 @@
1
+ import { onScopeDispose, shallowRef, toValue, watchEffect } from "vue";
2
+ import { useConvexOrThrow } from "../client.js";
3
+ import { QueriesObserver } from "../queries-observer.js";
4
+ export function useQueries(queries) {
5
+ const convex = useConvexOrThrow("useQuery");
6
+ const createWatch = (query, args, {
7
+ journal,
8
+ paginationOptions
9
+ }) => {
10
+ if (paginationOptions) {
11
+ return convex.watchPaginatedQuery(query, args, paginationOptions);
12
+ } else {
13
+ return convex.watchQuery(query, args, journal ? { journal } : {});
14
+ }
15
+ };
16
+ return useQueriesHelper(queries, createWatch);
17
+ }
18
+ export function useQueriesHelper(queries, createWatch) {
19
+ const observer = new QueriesObserver(createWatch);
20
+ const results = shallowRef({});
21
+ watchEffect((onCleanup) => {
22
+ const currentQueries = toValue(queries);
23
+ if (import.meta.server) {
24
+ results.value = observer.getLocalResults(currentQueries);
25
+ return;
26
+ }
27
+ observer.setQueries(currentQueries);
28
+ results.value = observer.getLocalResults(currentQueries);
29
+ const unsubscribe = observer.subscribe(() => {
30
+ results.value = observer.getLocalResults(toValue(queries));
31
+ });
32
+ onCleanup(unsubscribe);
33
+ });
34
+ onScopeDispose(() => observer.destroy());
35
+ return results;
36
+ }
37
+ export const useConvexQueries = useQueries;
@@ -0,0 +1,87 @@
1
+ import type { FunctionArgs, FunctionReference } from 'convex/server';
2
+ import { type ComputedRef, type MaybeRefOrGetter } from 'vue';
3
+ import type { UseQueryResult as ConvexUseQueryResult } from 'convex/react';
4
+ export type OptionalRestArgsOrSkip<FuncRef extends FunctionReference<any>> = FuncRef['_args'] extends Record<string, never> ? [args?: MaybeRefOrGetter<Record<string, never> | 'skip'>] : [args: MaybeRefOrGetter<FuncRef['_args'] | 'skip'>];
5
+ /**
6
+ * Result returned by object-form {@link useQuery_experimental}.
7
+ *
8
+ * @public
9
+ */
10
+ export type UseQueryResult<QueryResult, ThrowOnError extends boolean = false> = ConvexUseQueryResult<QueryResult, ThrowOnError>;
11
+ type UseQueryOptions<Query extends FunctionReference<'query'>, ThrowOnError extends boolean> = {
12
+ query: Query;
13
+ args: MaybeRefOrGetter<FunctionArgs<Query> | 'skip'>;
14
+ throwOnError?: ThrowOnError;
15
+ };
16
+ /**
17
+ * Load a reactive query within a Vue component.
18
+ *
19
+ * This Vue composable subscribes to a Convex query and updates the returned
20
+ * ref whenever the query result changes. The subscription is managed
21
+ * automatically -- it starts when the component mounts and stops when it
22
+ * unmounts.
23
+ *
24
+ * Throws an error if no Convex client has been provided.
25
+ *
26
+ * @example
27
+ * ```vue
28
+ * <script setup lang="ts">
29
+ * import { useQuery } from '#imports'
30
+ * import { api } from '#convex/api'
31
+ *
32
+ * // Reactively loads tasks; the ref updates when data changes:
33
+ * const tasks = useQuery(api.tasks.list, { completed: false })
34
+ * // tasks.value is `undefined` while loading
35
+ *
36
+ * // Pass "skip" (or a reactive getter returning it) to conditionally
37
+ * // disable the query:
38
+ * const profile = useQuery(
39
+ * api.users.get,
40
+ * computed(() => userId.value ? { userId: userId.value } : 'skip'),
41
+ * )
42
+ * </script>
43
+ * ```
44
+ *
45
+ * @param query - a {@link server.FunctionReference} for the public query to run
46
+ * like `api.dir1.dir2.filename.func`.
47
+ * @param args - The arguments to the query function or the string `"skip"` if
48
+ * the query should not be loaded. Accepts a ref, computed, or getter for
49
+ * reactive args.
50
+ * @returns a computed ref with the result of the query. Contains `undefined`
51
+ * while loading.
52
+ *
53
+ * @public
54
+ */
55
+ export declare function useQuery<Query extends FunctionReference<'query'>>(query: Query, ...args: OptionalRestArgsOrSkip<Query>): ComputedRef<Query['_returnType'] | undefined>;
56
+ /**
57
+ * Load a reactive query within a Vue component using an options object.
58
+ *
59
+ * This is an experimental form of {@link useQuery} that accepts a single
60
+ * {@link UseQueryOptions} object instead of positional arguments.
61
+ *
62
+ * Consumers are expected to check the returned object `status` field to
63
+ * make proper use of the result. If an error occurs, it will be present
64
+ * in the result object unless `throwOnError` is `true`, in which case
65
+ * the error will be thrown instead.
66
+ *
67
+ * @example
68
+ * ```vue
69
+ * <script setup lang="ts">
70
+ * import { useQuery_experimental as useQuery } from '#imports'
71
+ * import { api } from '#convex/api'
72
+ *
73
+ * const state = useQuery({ query: api.tasks.list, args: { completed: false } })
74
+ * // state.value.status: 'pending' | 'success' | 'error'
75
+ * </script>
76
+ * ```
77
+ *
78
+ * @param options - Query options. Pass `args: "skip"` to disable the query.
79
+ * @returns a computed ref with the current query state as a
80
+ * {@link UseQueryResult} object.
81
+ *
82
+ * @public
83
+ */
84
+ export declare function useQuery_experimental<Query extends FunctionReference<'query'>, ThrowOnError extends boolean = false>(options: UseQueryOptions<Query, ThrowOnError>): ComputedRef<UseQueryResult<Query['_returnType'], ThrowOnError>>;
85
+ /** @public */
86
+ export declare const useConvexQuery: typeof useQuery;
87
+ export {};