nuxt-convex-module 0.14.0 → 0.14.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.
@@ -1 +1 @@
1
- <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><script type="importmap">{"imports":{"#entry":"/__nuxt-convex-module/_nuxt/CtC_CmOY.js"}}</script><link rel="stylesheet" href="/__nuxt-convex-module/_nuxt/entry.jOWrhpYd.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__nuxt-convex-module/_nuxt/CtC_CmOY.js"><script type="module" src="/__nuxt-convex-module/_nuxt/CtC_CmOY.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/__nuxt-convex-module/_nuxt/C-J9qZjr.js"></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-convex-module",buildId:"168fd639-55a2-4ae2-9c90-a285b0720204",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1794180177669,false]</script></body></html>
1
+ <!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><script type="importmap">{"imports":{"#entry":"/__nuxt-convex-module/_nuxt/CtC_CmOY.js"}}</script><link rel="stylesheet" href="/__nuxt-convex-module/_nuxt/entry.jOWrhpYd.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__nuxt-convex-module/_nuxt/CtC_CmOY.js"><script type="module" src="/__nuxt-convex-module/_nuxt/CtC_CmOY.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/__nuxt-convex-module/_nuxt/C-J9qZjr.js"></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-convex-module",buildId:"45bb65c1-9f29-4c02-bccc-cf2e35308dd6",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1794326993439,false]</script></body></html>
package/dist/module.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "compatibility": {
6
6
  "nuxt": ">=4.6.0"
7
7
  },
8
- "version": "0.14.0",
8
+ "version": "0.14.2",
9
9
  "builder": {
10
10
  "@nuxt/module-builder": "1.0.3",
11
11
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -535,7 +535,7 @@ function registerIntegrations(resolver, nuxt, options, policy, authRoute) {
535
535
  const resolve = (key, pkg) => report(key, pkg, resolveIntegrationState(options[key], isPackageInstalled(pkg, rootDir), isDeclaredDependency(pkg, rootDir)));
536
536
  const betterAuth = resolve("betterAuth", "@convex-dev/better-auth");
537
537
  if (betterAuth) {
538
- registerBetterAuth(resolver, authRoute, policy);
538
+ registerBetterAuth(resolver, authRoute, policy, nuxt.options.experimental);
539
539
  } else {
540
540
  registerBaseConvexClient(resolver);
541
541
  }
@@ -684,9 +684,12 @@ const AUTH_PROXY_SECURITY_RULES = {
684
684
  xssValidator: false,
685
685
  allowedMethodsRestricter: { methods: ["GET", "HEAD", "POST", "OPTIONS"] }
686
686
  };
687
- function registerBetterAuth(resolver, authRoute, policy) {
687
+ function registerBetterAuth(resolver, authRoute, policy, experimental) {
688
688
  addPlugin(resolver.resolve("./runtime/better-auth/vue/plugin.client"));
689
689
  addPlugin(resolver.resolve("./runtime/better-auth/vue/plugin.server"));
690
+ if (experimental.emitRouteChunkError === "automatic" && experimental.appManifest) {
691
+ addPlugin(resolver.resolve("./runtime/better-auth/nuxt/chunk-reload.client"));
692
+ }
690
693
  const noSessionVary = resolver.resolve("./runtime/better-auth/nuxt/no-session-vary");
691
694
  addNitroPlugin({ nitro2: noSessionVary, nitro3: noSessionVary });
692
695
  registerIntegrationImports(resolver, "betterAuth", policy);
@@ -0,0 +1,2 @@
1
+ declare const _default: import("nuxt/app").Plugin<Record<string, unknown>> & import("nuxt/app").ObjectPlugin<Record<string, unknown>>;
2
+ export default _default;
@@ -0,0 +1,41 @@
1
+ import { defineNuxtPlugin, reloadNuxtApp, useRuntimeConfig } from "#app";
2
+ import { onAuthClientLoadFailure } from "../vue/auth-client-loader.js";
3
+ function latestBuildURL(app) {
4
+ const root = (app.cdnURL || app.baseURL).replace(/\/+$/, "");
5
+ const dir = app.buildAssetsDir.replace(/^\/+/, "").replace(/\/+$/, "");
6
+ return `${root}/${dir}/builds/latest.json?${Date.now()}`;
7
+ }
8
+ async function newerBuild(url, buildId) {
9
+ try {
10
+ const response = await fetch(url, { cache: "no-store" });
11
+ if (!response.ok) return void 0;
12
+ const latest = await response.json();
13
+ return typeof latest.id === "string" && latest.id !== buildId ? latest : void 0;
14
+ } catch {
15
+ return void 0;
16
+ }
17
+ }
18
+ export default defineNuxtPlugin({
19
+ name: "nuxt-convex-module:better-auth:chunk-reload",
20
+ setup(nuxtApp) {
21
+ const { app } = useRuntimeConfig();
22
+ const chunkErrors = /* @__PURE__ */ new WeakSet();
23
+ nuxtApp.hook("app:chunkError", ({ error }) => {
24
+ if (typeof error === "object" && error !== null) chunkErrors.add(error);
25
+ });
26
+ let checking = false;
27
+ let announced = false;
28
+ onAuthClientLoadFailure(({ error, used }) => {
29
+ if (checking) return;
30
+ checking = true;
31
+ void newerBuild(latestBuildURL(app), app.buildId).then((build) => {
32
+ checking = false;
33
+ if (!build || typeof error !== "object" || error === null || !chunkErrors.has(error)) return;
34
+ if (used) return reloadNuxtApp();
35
+ if (announced) return;
36
+ announced = true;
37
+ void nuxtApp.callHook("app:manifest:update", build);
38
+ });
39
+ });
40
+ }
41
+ });
@@ -3,9 +3,15 @@ let loaded;
3
3
  let loading;
4
4
  let deferred = false;
5
5
  const waiting = /* @__PURE__ */ new Set();
6
+ const failing = /* @__PURE__ */ new Set();
7
+ let used = false;
6
8
  let evaluated;
7
9
  let stopSignal;
8
10
  export function loadAuthClient() {
11
+ used = true;
12
+ return load();
13
+ }
14
+ function load() {
9
15
  if (evaluated) registerAuthClient(evaluated);
10
16
  loading ??= import("#convex/auth-client").then(
11
17
  ({ authClient }) => {
@@ -14,6 +20,9 @@ export function loadAuthClient() {
14
20
  },
15
21
  (error) => {
16
22
  loading = void 0;
23
+ const failure = { error, used };
24
+ used = false;
25
+ notify(failing, failure);
17
26
  throw error;
18
27
  }
19
28
  );
@@ -33,11 +42,15 @@ export function registerAuthClient(client) {
33
42
  evaluated = void 0;
34
43
  loaded = client;
35
44
  loading ??= Promise.resolve(client);
45
+ failing.clear();
36
46
  const listeners = [...waiting];
37
47
  waiting.clear();
38
- for (const listener of listeners) {
48
+ notify(listeners, client);
49
+ }
50
+ function notify(listeners, value) {
51
+ for (const listener of [...listeners]) {
39
52
  try {
40
- listener(client);
53
+ listener(value);
41
54
  } catch (error) {
42
55
  report(error);
43
56
  }
@@ -61,12 +74,18 @@ export function onAuthClient(listener) {
61
74
  waiting.add(listener);
62
75
  return () => waiting.delete(listener);
63
76
  }
77
+ export function onAuthClientLoadFailure(listener) {
78
+ if (loaded) return () => {
79
+ };
80
+ failing.add(listener);
81
+ return () => failing.delete(listener);
82
+ }
64
83
  export function deferAuthClient() {
65
84
  if (deferred || loaded) return;
66
85
  deferred = true;
67
86
  const onVisible = () => {
68
87
  if (document.visibilityState !== "visible") return;
69
- void loadAuthClient().then(() => document.removeEventListener("visibilitychange", onVisible), () => {
88
+ void load().then(() => document.removeEventListener("visibilitychange", onVisible), () => {
70
89
  });
71
90
  };
72
91
  document.addEventListener("visibilitychange", onVisible);
@@ -75,7 +94,7 @@ export function retryAuthClientLoad() {
75
94
  if (loaded || typeof window === "undefined") return;
76
95
  const retry = () => {
77
96
  if (document.visibilityState !== "visible") return;
78
- void loadAuthClient().then(() => {
97
+ void load().then(() => {
79
98
  window.removeEventListener("online", retry);
80
99
  document.removeEventListener("visibilitychange", retry);
81
100
  }, () => {
@@ -183,4 +202,6 @@ export function __resetAuthClientLoaderForTests() {
183
202
  stopSignal = void 0;
184
203
  evaluated = void 0;
185
204
  waiting.clear();
205
+ failing.clear();
206
+ used = false;
186
207
  }
@@ -1,4 +1,4 @@
1
- import { loadAuthClient, onAuthClient } from "./auth-client-loader.js";
1
+ import { loadAuthClient } from "./auth-client-loader.js";
2
2
  const normalizePathname = (path) => {
3
3
  const slashed = path.startsWith("/") ? path : `/${path}`;
4
4
  let end = slashed.length;
@@ -18,18 +18,7 @@ export async function consumeCrossDomainOneTimeToken(options = {}) {
18
18
  const authClient = await loadAuthClient().catch(() => loadAuthClient()).catch((error) => {
19
19
  console.warn("[nuxt-convex-module] Failed to load the Better Auth client:", error);
20
20
  });
21
- if (!authClient) {
22
- onAuthClient((client) => void exchangeKeptToken(client, token, url, options));
23
- return;
24
- }
25
- await exchangeOneTimeToken(authClient, token, url, options);
26
- }
27
- async function exchangeKeptToken(authClient, token, url, options) {
28
- const current = await Promise.resolve().then(() => authClient.getSession()).catch(() => void 0);
29
- if (!current || current.error || current.data?.session) {
30
- if (!current?.data?.session) console.warn("[nuxt-convex-module] Failed to consume cross-domain one-time token:", current?.error ?? "the session lookup failed");
31
- return;
32
- }
21
+ if (!authClient) return;
33
22
  await exchangeOneTimeToken(authClient, token, url, options);
34
23
  }
35
24
  async function exchangeOneTimeToken(authClient, token, url, options) {
@@ -20,7 +20,13 @@ function name(error, prefix) {
20
20
  if (error.message !== `${prefix}${original}`) return;
21
21
  unnamed.set(error, { message: original, stack });
22
22
  const header = `${error.name}: ${original}`;
23
- if (original && stack?.startsWith(header)) error.stack = `${error.name}: ${error.message}${stack.slice(header.length)}`;
23
+ if (original && stack?.startsWith(header)) setStack(error, `${error.name}: ${error.message}${stack.slice(header.length)}`);
24
+ }
25
+ function setStack(error, stack) {
26
+ try {
27
+ error.stack = stack;
28
+ } catch {
29
+ }
24
30
  }
25
31
  export function withoutFunctionName(error, read) {
26
32
  const before = error instanceof Error ? unnamed.get(error) : void 0;
@@ -28,11 +34,11 @@ export function withoutFunctionName(error, read) {
28
34
  const named = error;
29
35
  const { message, stack } = named;
30
36
  named.message = before.message;
31
- named.stack = before.stack;
37
+ setStack(named, before.stack);
32
38
  try {
33
39
  return read(error);
34
40
  } finally {
35
41
  named.message = message;
36
- named.stack = stack;
42
+ setStack(named, stack);
37
43
  }
38
44
  }
@@ -16,9 +16,12 @@ export type UploadItemStage = 'prepare' | 'upload' | 'after';
16
16
  /**
17
17
  * A single entry in the queue managed by {@link useUploadQueue}.
18
18
  *
19
+ * @typeParam TData - The app's own data for the item, given to
20
+ * {@link VueUploadQueue.enqueue}. `undefined` when the queue takes none.
21
+ *
19
22
  * @public
20
23
  */
21
- export interface UploadQueueItem {
24
+ export interface UploadQueueItem<TData = undefined> {
22
25
  /** Stable id for this queue entry (not the storage id). */
23
26
  id: string;
24
27
  /** The file or blob being uploaded. */
@@ -48,13 +51,26 @@ export interface UploadQueueItem {
48
51
  * and for a preview.
49
52
  */
50
53
  prepared: Blob | null;
54
+ /**
55
+ * The app's data for this item, as given to `enqueue`: what the file is
56
+ * for (a document id, a kind, a position). Every hook reads it off the
57
+ * item. Deeply reactive like the rest of the item: an object reads as
58
+ * Vue's proxy of the one given, not a copy, so a change through it
59
+ * reaches that object and every item the same call queued (`toRaw`
60
+ * returns the object itself). A ref held as one of its properties reads
61
+ * unwrapped; one inside an array or a `Map` stays a ref. `undefined`
62
+ * when `enqueue` was given none.
63
+ */
64
+ data: TData;
51
65
  }
52
66
  /**
53
67
  * Options for {@link useUploadQueue}.
54
68
  *
69
+ * @typeParam TData - The data each item carries; see {@link UploadQueueItem}.
70
+ *
55
71
  * @public
56
72
  */
57
- export interface UseUploadQueueOptions {
73
+ export interface UseUploadQueueOptions<TData = undefined> {
58
74
  /** Maximum number of uploads to run at once. Defaults to `3`. */
59
75
  concurrency?: number;
60
76
  /**
@@ -63,7 +79,7 @@ export interface UseUploadQueueOptions {
63
79
  * item ends `error`, refused when `isRefusal` says so. Runs once per
64
80
  * item: a retry reuses what it returned.
65
81
  */
66
- prepare?: (file: Blob, item: UploadQueueItem, signal: AbortSignal) => Blob | Promise<Blob>;
82
+ prepare?: (file: Blob, item: UploadQueueItem<TData>, signal: AbortSignal) => Blob | Promise<Blob>;
67
83
  /**
68
84
  * Runs once the file is stored, before the item counts as a success:
69
85
  * attach the storage id to a document, run a check on it. The item stays
@@ -74,7 +90,7 @@ export interface UseUploadQueueOptions {
74
90
  * in error, so `onItemError` can delete it; the queue never deletes files,
75
91
  * and a retry forgets it before uploading again.
76
92
  */
77
- after?: (storageId: StorageId, item: UploadQueueItem, signal: AbortSignal) => void | Promise<void>;
93
+ after?: (storageId: StorageId, item: UploadQueueItem<TData>, signal: AbortSignal) => void | Promise<void>;
78
94
  /**
79
95
  * Whether an error is a refusal (the app turned the file away) rather than
80
96
  * a failure (the network, the deployment, an abort). Defaults to: the
@@ -82,30 +98,46 @@ export interface UseUploadQueueOptions {
82
98
  * An aborted item is never refused, and neither is an error this check
83
99
  * throws on.
84
100
  */
85
- isRefusal?: (error: Error, item: UploadQueueItem) => boolean;
101
+ isRefusal?: (error: Error, item: UploadQueueItem<TData>) => boolean;
86
102
  /**
87
103
  * Called when an individual item finishes uploading (after `after`). A
88
104
  * throw ends the item `error` with its storage id kept; a retry then runs
89
105
  * only this callback again, never `after` a second time.
90
106
  */
91
- onItemSuccess?: (storageId: StorageId, item: UploadQueueItem) => void | Promise<void>;
107
+ onItemSuccess?: (storageId: StorageId, item: UploadQueueItem<TData>) => void | Promise<void>;
92
108
  /** Called when an individual item fails or is refused (`item.refused` says which). */
93
- onItemError?: (error: Error, item: UploadQueueItem) => void;
94
- /** Called once every queued item has settled (succeeded or failed). */
95
- onComplete?: (items: UploadQueueItem[]) => void;
109
+ onItemError?: (error: Error, item: UploadQueueItem<TData>) => void;
110
+ /**
111
+ * Called once every queued item has settled (succeeded or failed), after
112
+ * every `onItemSuccess` has returned.
113
+ */
114
+ onComplete?: (items: UploadQueueItem<TData>[]) => void;
96
115
  }
97
116
  /** Accepted inputs to {@link VueUploadQueue.enqueue}. @public */
98
117
  export type EnqueueInput = Blob | Blob[] | FileList | null | undefined;
118
+ /**
119
+ * The `data` argument of {@link VueUploadQueue.enqueue}: optional when the
120
+ * item data may be `undefined`, required otherwise.
121
+ *
122
+ * @public
123
+ */
124
+ export type EnqueueData<TData> = undefined extends TData ? [data?: TData] : [data: TData];
99
125
  /**
100
126
  * The reactive queue returned by {@link useUploadQueue}.
101
127
  *
128
+ * @typeParam TData - The data each item carries; see {@link UploadQueueItem}.
129
+ *
102
130
  * @public
103
131
  */
104
- export interface VueUploadQueue {
132
+ export interface VueUploadQueue<TData = undefined> {
105
133
  /** Reactive list of queued items, in insertion order. */
106
- items: Readonly<Ref<UploadQueueItem[]>>;
107
- /** Add one or more files to the queue; uploading starts automatically. */
108
- enqueue: (files: EnqueueInput) => void;
134
+ items: Readonly<Ref<UploadQueueItem<TData>[]>>;
135
+ /**
136
+ * Add one or more files to the queue; uploading starts automatically.
137
+ * `data` goes on every item this call queues, as `item.data`: queue files
138
+ * one call at a time to give each its own.
139
+ */
140
+ enqueue: (files: EnqueueInput, ...data: EnqueueData<TData>) => void;
109
141
  /** `true` while any item is pending or uploading. */
110
142
  isUploading: Readonly<Ref<boolean>>;
111
143
  /** Aggregate progress across all items, from `0` to `1`. */
@@ -114,11 +146,17 @@ export interface VueUploadQueue {
114
146
  pendingCount: Readonly<Ref<number>>;
115
147
  /** Number of items currently uploading. */
116
148
  activeCount: Readonly<Ref<number>>;
117
- /** Abort all in-flight uploads (queued items stay `pending`). */
149
+ /**
150
+ * Abort all in-flight uploads. Queued items stay `pending` until the next
151
+ * `enqueue` or `retry()`, which start them again.
152
+ */
118
153
  cancel: () => void;
119
154
  /** Remove an item by id, aborting it first if it is uploading. */
120
155
  remove: (id: string) => void;
121
- /** Abort everything and empty the queue. */
156
+ /**
157
+ * Abort everything and empty the queue. Once the aborted uploads end,
158
+ * `onComplete` gets `[]`, unless files were queued since.
159
+ */
122
160
  clear: () => void;
123
161
  /**
124
162
  * Queue an errored item again, or every errored item without an id. It
@@ -143,6 +181,11 @@ export interface VueUploadQueue {
143
181
  * item says whether the app `refused` it or it failed, so the UI can show a
144
182
  * reason for one and {@link VueUploadQueue.retry} the other.
145
183
  *
184
+ * `enqueue(files, data)` attaches the app's own data to the items it
185
+ * queues, as `item.data`, so a hook knows what a file is for without a
186
+ * side map: type it with the `TData` parameter,
187
+ * `useUploadQueue<{ albumId: Id<'albums'> }>(…)`.
188
+ *
146
189
  * @example
147
190
  * ```vue
148
191
  * <script setup lang="ts">
@@ -171,6 +214,8 @@ export interface VueUploadQueue {
171
214
  * </template>
172
215
  * ```
173
216
  *
217
+ * @typeParam TData - The data each item carries, given to `enqueue`;
218
+ * `undefined` (no data) by default.
174
219
  * @param generateUploadUrl - A `FunctionReference` for the public
175
220
  * `generateUploadUrl` mutation.
176
221
  * @param options - Optional {@link UseUploadQueueOptions}.
@@ -178,6 +223,6 @@ export interface VueUploadQueue {
178
223
  *
179
224
  * @public
180
225
  */
181
- export declare function useUploadQueue(generateUploadUrl: GenerateUploadUrl, options?: UseUploadQueueOptions): VueUploadQueue;
226
+ export declare function useUploadQueue<TData = undefined>(generateUploadUrl: GenerateUploadUrl, options?: UseUploadQueueOptions<TData>): VueUploadQueue<TData>;
182
227
  /** @public */
183
228
  export declare const useConvexUploadQueue: typeof useUploadQueue;
@@ -19,6 +19,10 @@ export function useUploadQueue(generateUploadUrl, options = {}) {
19
19
  const controllers = /* @__PURE__ */ new Map();
20
20
  const afterDone = /* @__PURE__ */ new Set();
21
21
  const resend = /* @__PURE__ */ new Set();
22
+ let running = 0;
23
+ let generation = 0;
24
+ let cutOff = 0;
25
+ let held = false;
22
26
  const activeCount = computed(
23
27
  () => items.value.filter((item) => item.status === "uploading").length
24
28
  );
@@ -33,11 +37,9 @@ export function useUploadQueue(generateUploadUrl, options = {}) {
33
37
  });
34
38
  function pump() {
35
39
  if (typeof XMLHttpRequest === "undefined") return;
36
- let slots = concurrency - activeCount.value;
37
40
  for (const item of items.value) {
38
- if (slots <= 0) break;
41
+ if (held || activeCount.value >= concurrency) break;
39
42
  if (item.status !== "pending") continue;
40
- slots--;
41
43
  void run(item);
42
44
  }
43
45
  }
@@ -80,6 +82,8 @@ export function useUploadQueue(generateUploadUrl, options = {}) {
80
82
  options.onItemError?.(failure, item);
81
83
  }
82
84
  async function run(item) {
85
+ const own = generation;
86
+ running++;
83
87
  item.status = "uploading";
84
88
  const controller = new AbortController();
85
89
  controllers.set(item.id, controller);
@@ -89,8 +93,8 @@ export function useUploadQueue(generateUploadUrl, options = {}) {
89
93
  if (options.after && !afterDone.has(item.id)) {
90
94
  item.stage = "after";
91
95
  await options.after(storageId, item, signal);
92
- signal.throwIfAborted();
93
96
  afterDone.add(item.id);
97
+ signal.throwIfAborted();
94
98
  }
95
99
  item.stage = null;
96
100
  item.status = "success";
@@ -98,15 +102,20 @@ export function useUploadQueue(generateUploadUrl, options = {}) {
98
102
  } catch (caught) {
99
103
  fail(item, caught, signal);
100
104
  } finally {
101
- if (controllers.get(item.id) === controller) controllers.delete(item.id);
102
- if (pendingCount.value === 0 && activeCount.value === 0) {
103
- options.onComplete?.(items.value);
105
+ if (own !== generation) {
106
+ if (--cutOff === 0 && items.value.length === 0) options.onComplete?.([]);
104
107
  } else {
105
- pump();
108
+ running--;
109
+ if (controllers.get(item.id) === controller) controllers.delete(item.id);
110
+ if (pendingCount.value === 0 && running === 0) {
111
+ options.onComplete?.(items.value);
112
+ } else if (!signal.aborted) {
113
+ pump();
114
+ }
106
115
  }
107
116
  }
108
117
  }
109
- function enqueue(files) {
118
+ function enqueue(files, ...[data]) {
110
119
  const incoming = normalizeFiles(files);
111
120
  if (incoming.length === 0) return;
112
121
  items.value.push(
@@ -119,12 +128,16 @@ export function useUploadQueue(generateUploadUrl, options = {}) {
119
128
  error: null,
120
129
  refused: false,
121
130
  stage: null,
122
- prepared: null
131
+ prepared: null,
132
+ // `undefined` only when `TData` allows it: `EnqueueData` requires it otherwise.
133
+ data
123
134
  }))
124
135
  );
136
+ held = false;
125
137
  pump();
126
138
  }
127
139
  function cancel() {
140
+ held = true;
128
141
  for (const controller of controllers.values()) {
129
142
  controller.abort();
130
143
  }
@@ -135,6 +148,7 @@ export function useUploadQueue(generateUploadUrl, options = {}) {
135
148
  afterDone.delete(id);
136
149
  resend.delete(id);
137
150
  items.value = items.value.filter((item) => item.id !== id);
151
+ pump();
138
152
  }
139
153
  function retry(id) {
140
154
  for (const item of items.value) {
@@ -145,10 +159,14 @@ export function useUploadQueue(generateUploadUrl, options = {}) {
145
159
  if (resend.delete(item.id)) item.storageId = null;
146
160
  if (item.storageId === null) item.progress = 0;
147
161
  }
162
+ held = false;
148
163
  pump();
149
164
  }
150
165
  function clear() {
151
166
  cancel();
167
+ generation++;
168
+ cutOff += running;
169
+ running = 0;
152
170
  controllers.clear();
153
171
  afterDone.clear();
154
172
  resend.clear();
@@ -88,6 +88,6 @@ export type { ConnectionState, OptimisticUpdate, QueryJournal, } from 'convex/br
88
88
  export type { FunctionReference, FunctionReference_future, FunctionArgs, FunctionReturnType, OptionalRestArgs, ArgsAndOptions, } from 'convex/server';
89
89
  export type { Value } from 'convex/values';
90
90
  export { useUpload, useConvexUpload, uploadFile, type VueUpload, type UseUploadOptions, type UploadFileOptions, type GenerateUploadUrl, type StorageId, } from './composables/use-upload.js';
91
- export { useUploadQueue, useConvexUploadQueue, type VueUploadQueue, type UseUploadQueueOptions, type UploadQueueItem, type UploadItemStage, type UploadItemStatus, type EnqueueInput, } from './composables/use-upload-queue.js';
91
+ export { useUploadQueue, useConvexUploadQueue, type VueUploadQueue, type UseUploadQueueOptions, type UploadQueueItem, type UploadItemStage, type UploadItemStatus, type EnqueueInput, type EnqueueData, } from './composables/use-upload-queue.js';
92
92
  export { useStorageUrl, useConvexStorageUrl, type GetStorageUrl, } from './composables/use-storage-url.js';
93
93
  export { ConvexImage } from './components/convex-image.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "nuxt-convex-module",
4
- "version": "0.14.0",
4
+ "version": "0.14.2",
5
5
  "description": "Convex backend integration with live queries, mutations, actions, pagination, file storage and SSR, auto-imported and typed against your deployment. The same Vue client runs without Nuxt; Better Auth, Clerk, Auth0 and Polar are opt-in.",
6
6
  "repository": {
7
7
  "type": "git",
@@ -221,7 +221,7 @@
221
221
  "changelogen": "^0.6.2",
222
222
  "convex": "^1.49.0",
223
223
  "eslint": "^10.13.0",
224
- "fallow": "3.39.0",
224
+ "fallow": "3.41.0",
225
225
  "happy-dom": "^21.0.4",
226
226
  "installed-check": "^11.0.0",
227
227
  "nuxt": "^4.6.3",