@lenne.tech/nuxt-extensions 1.19.0 → 1.20.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/CLAUDE.md +1 -1
- package/README.md +28 -10
- package/dist/module.d.mts +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -1
- package/dist/runtime/composables/auth/use-lt-auth.js +29 -1
- package/dist/runtime/composables/auth/use-system-setup.js +12 -1
- package/dist/runtime/composables/use-lt-tus-upload.js +3 -3
- package/dist/runtime/lib/auth-client.js +1 -1
- package/dist/runtime/lib/auth-state.js +1 -1
- package/dist/runtime/plugins/auth-interceptor.client.js +4 -2
- package/dist/runtime/testing/playwright-helpers.js +1 -1
- package/package.json +23 -22
package/CLAUDE.md
CHANGED
|
@@ -347,7 +347,7 @@ fields at `app:beforeMount` and restores them at `app:mounted`, dispatching a sy
|
|
|
347
347
|
Handling" below).
|
|
348
348
|
3. **Check existing composables** before creating new ones — this module may already provide what you need
|
|
349
349
|
4. **Peer dependencies** (`better-auth`, `@better-auth/passkey`, `tus-js-client`) must be installed in the consuming project.
|
|
350
|
-
`better-auth` and `@better-auth/passkey` are pinned to **`>=1.7.
|
|
350
|
+
`better-auth` and `@better-auth/passkey` are pinned to **`>=1.7.12 <1.8.0`** — deliberately not a caret.
|
|
351
351
|
**They move in lock-step with `@lenne.tech/nest-server`.** better-auth is one protocol with two ends;
|
|
352
352
|
this module is the client end, nest-server the server end, and both declare the *same* narrow range.
|
|
353
353
|
Raising it means raising it in **both** framework repos and pinning the new version in **both** starters,
|
package/README.md
CHANGED
|
@@ -29,8 +29,8 @@ Reusable Nuxt 4 composables, components, and Better-Auth integration for lenne.t
|
|
|
29
29
|
|---|---|---|
|
|
30
30
|
| Node | `>= 22` | |
|
|
31
31
|
| Nuxt | `^4.0.0` | |
|
|
32
|
-
| `better-auth` | `>=1.7.
|
|
33
|
-
| `@better-auth/passkey` | `>=1.7.
|
|
32
|
+
| `better-auth` | `>=1.7.12 <1.8.0` | **required peer** — pin it, see below |
|
|
33
|
+
| `@better-auth/passkey` | `>=1.7.12 <1.8.0` | optional peer, passkey/WebAuthn only |
|
|
34
34
|
| `tus-js-client` | `>=4.0.0` | optional peer, resumable uploads only |
|
|
35
35
|
| `@lenne.tech/nest-server` | `>= 11.37.0` | **only if you use the backend** — see the lock-step rule |
|
|
36
36
|
|
|
@@ -38,7 +38,7 @@ Reusable Nuxt 4 composables, components, and Better-Auth integration for lenne.t
|
|
|
38
38
|
better-auth is one protocol with two ends. This module is the client end and
|
|
39
39
|
`@lenne.tech/nest-server` is the server end, and they must resolve the **same**
|
|
40
40
|
better-auth minor. better-auth breaks in *minor* releases — 1.7 removed subpath
|
|
41
|
-
exports and changed the 2FA response shape — so a caret (`^1.7.
|
|
41
|
+
exports and changed the 2FA response shape — so a caret (`^1.7.12`) would re-open the
|
|
42
42
|
hole at 1.8. If the api and the app drift apart, 2FA activation fails at runtime with a
|
|
43
43
|
generic client error while **both** projects' checks stay green: each is internally
|
|
44
44
|
consistent, and only the assembled workspace has both halves.
|
|
@@ -46,17 +46,21 @@ consistent, and only the assembled workspace has both halves.
|
|
|
46
46
|
Raising better-auth therefore means raising it in `@lenne.tech/nest-server` **and**
|
|
47
47
|
`@lenne.tech/nuxt-extensions` in one step.
|
|
48
48
|
|
|
49
|
-
**Why the floor is 1.7.
|
|
49
|
+
**Why the floor is 1.7.12:** better-auth before 1.7.12 verifies JWTs with the algorithm named
|
|
50
|
+
in the token's own header instead of the algorithm of the stored key
|
|
51
|
+
([#11925](https://github.com/better-auth/better-auth/pull/11925)). Below 1.7.7 it is also
|
|
52
|
+
affected by
|
|
50
53
|
[GHSA-965c-763c-88jm](https://github.com/better-auth/better-auth/security/advisories/GHSA-965c-763c-88jm)
|
|
51
|
-
(critical): the OAuth state can be used as a magic link to sign in as another user.
|
|
52
|
-
|
|
54
|
+
(critical): the OAuth state can be used as a magic link to sign in as another user. Both flaws
|
|
55
|
+
are in the server end, so your API must run 1.7.12 too (`@lenne.tech/nest-server` 12.0.0
|
|
56
|
+
declares the same range), and the pin below keeps both halves on it.
|
|
53
57
|
|
|
54
58
|
## Installation
|
|
55
59
|
|
|
56
60
|
```bash
|
|
57
|
-
npm install @lenne.tech/nuxt-extensions better-auth@1.7.
|
|
61
|
+
npm install @lenne.tech/nuxt-extensions better-auth@1.7.12
|
|
58
62
|
# Optional: For passkey support
|
|
59
|
-
npm install @better-auth/passkey@1.7.
|
|
63
|
+
npm install @better-auth/passkey@1.7.12
|
|
60
64
|
# Optional: For TUS file uploads
|
|
61
65
|
npm install tus-js-client
|
|
62
66
|
```
|
|
@@ -66,8 +70,8 @@ In a fullstack monorepo, pin it workspace-wide so the api and the app cannot dri
|
|
|
66
70
|
```yaml
|
|
67
71
|
# pnpm-workspace.yaml
|
|
68
72
|
overrides:
|
|
69
|
-
better-auth: 1.7.
|
|
70
|
-
'@better-auth/passkey': 1.7.
|
|
73
|
+
better-auth: 1.7.12
|
|
74
|
+
'@better-auth/passkey': 1.7.12
|
|
71
75
|
```
|
|
72
76
|
|
|
73
77
|
## Features
|
|
@@ -583,6 +587,20 @@ absolute URL is used as it is. If your project routes uploads through its own sa
|
|
|
583
587
|
instead, set `ltExtensions.tus.resolveAgainstApi: false` and the relative endpoint stays on the app
|
|
584
588
|
origin.
|
|
585
589
|
|
|
590
|
+
**Changing it per stage.** The `tus` options are part of the public runtime config, so a deployed
|
|
591
|
+
stage can override them through the environment, without a rebuild:
|
|
592
|
+
|
|
593
|
+
```bash
|
|
594
|
+
# Send this stage's uploads to another host (absolute URLs are used as they are)
|
|
595
|
+
NUXT_PUBLIC_LT_EXTENSIONS_TUS_DEFAULT_ENDPOINT=https://uploads.example.com/tus
|
|
596
|
+
# Keep a relative endpoint on the app origin (own same-origin proxy)
|
|
597
|
+
NUXT_PUBLIC_LT_EXTENSIONS_TUS_RESOLVE_AGAINST_API=false
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
Keep the relative default where you can: `NUXT_PUBLIC_API_URL` already differs per stage, so `'/tus'`
|
|
601
|
+
follows it. Uploads are sent with credentials, so a host other than the API has to receive the
|
|
602
|
+
session cookie, or every upload answers 401.
|
|
603
|
+
|
|
586
604
|
**Which id to keep.** After a successful upload, `item.fileId` is the id of the stored file, read
|
|
587
605
|
from the `X-File-Id` response header (nest-server 11.42.13+). Use it for file links and references.
|
|
588
606
|
`item.url` is the TUS upload resource; its last segment is the upload id, not the file id. `fileId`
|
package/dist/module.d.mts
CHANGED
|
@@ -3,7 +3,7 @@ import { LtExtensionsModuleOptions } from '../dist/runtime/types/index.js';
|
|
|
3
3
|
export * from '../dist/runtime/types/index.js';
|
|
4
4
|
|
|
5
5
|
declare const name = "@lenne.tech/nuxt-extensions";
|
|
6
|
-
declare const version = "1.
|
|
6
|
+
declare const version = "1.20.0";
|
|
7
7
|
declare const configKey = "ltExtensions";
|
|
8
8
|
declare const DEFAULT_LT_AUTH_STATE_COOKIE = "lt-auth-state";
|
|
9
9
|
declare const DEFAULT_LT_JWT_TOKEN_COOKIE = "lt-jwt-token";
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { defineNuxtModule, createResolver, tryResolveModule, addImports, addComp
|
|
|
2
2
|
import { pathToFileURL } from 'node:url';
|
|
3
3
|
|
|
4
4
|
const name = "@lenne.tech/nuxt-extensions";
|
|
5
|
-
const version = "1.
|
|
5
|
+
const version = "1.20.0";
|
|
6
6
|
const configKey = "ltExtensions";
|
|
7
7
|
const DEFAULT_LT_AUTH_STATE_COOKIE = "lt-auth-state";
|
|
8
8
|
const DEFAULT_LT_JWT_TOKEN_COOKIE = "lt-jwt-token";
|
|
@@ -3,6 +3,7 @@ import { ltArrayBufferToBase64Url, ltBase64UrlToUint8Array } from "../../utils/c
|
|
|
3
3
|
import { clearLtAuthCookies, getLtApiBase, getLtAuthCookieNames, resolveLtAuthState } from "../../lib/auth-state.js";
|
|
4
4
|
import { useLtAuthClient } from "../use-lt-auth-client.js";
|
|
5
5
|
const VALIDATE_SESSION_TIMEOUT_MS = 1e4;
|
|
6
|
+
let staleJwtRefresh = null;
|
|
6
7
|
function useTranslation() {
|
|
7
8
|
const nuxtApp = useNuxtApp();
|
|
8
9
|
const i18n = nuxtApp.$i18n;
|
|
@@ -139,8 +140,35 @@ export function useLtAuth() {
|
|
|
139
140
|
});
|
|
140
141
|
}
|
|
141
142
|
}
|
|
143
|
+
if (response.status === 401 && isJwtMode.value && isAuthenticated.value && !needsCookies) {
|
|
144
|
+
const freshToken = await refreshStaleJwt();
|
|
145
|
+
if (freshToken) {
|
|
146
|
+
headers.set("Authorization", `Bearer ${freshToken}`);
|
|
147
|
+
return fetch(url, {
|
|
148
|
+
...options,
|
|
149
|
+
headers,
|
|
150
|
+
credentials: "omit"
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
142
154
|
return response;
|
|
143
155
|
}
|
|
156
|
+
async function refreshStaleJwt() {
|
|
157
|
+
const mint = async () => await switchToJwtMode() ? jwtToken.value : null;
|
|
158
|
+
if (!import.meta.client) {
|
|
159
|
+
return mint();
|
|
160
|
+
}
|
|
161
|
+
if (!staleJwtRefresh) {
|
|
162
|
+
staleJwtRefresh = mint().finally(() => {
|
|
163
|
+
staleJwtRefresh = null;
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
const token = await staleJwtRefresh;
|
|
167
|
+
if (token && jwtToken.value !== token) {
|
|
168
|
+
jwtToken.value = token;
|
|
169
|
+
}
|
|
170
|
+
return token;
|
|
171
|
+
}
|
|
144
172
|
const AUTHZ_KEYS = ["banExpires", "banReason", "banned", "emailVerified", "role", "roles", "twoFactorEnabled"];
|
|
145
173
|
function mergeSessionUser(sessionUser) {
|
|
146
174
|
const cached = resolvedAuthState.value?.user;
|
|
@@ -465,7 +493,7 @@ export function useLtAuth() {
|
|
|
465
493
|
}
|
|
466
494
|
if (import.meta.client && !featuresFetched.value) {
|
|
467
495
|
featuresFetched.value = true;
|
|
468
|
-
fetchFeatures();
|
|
496
|
+
void fetchFeatures();
|
|
469
497
|
}
|
|
470
498
|
return {
|
|
471
499
|
// Auth state
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import { computed, useState } from "#imports";
|
|
2
2
|
import { ltSha256 } from "../../utils/crypto.js";
|
|
3
|
-
import { buildLtApiUrl } from "../../lib/auth-state.js";
|
|
3
|
+
import { buildLtApiUrl, resolveLtApiBaseUrl, warnMissingLtApiUrl } from "../../lib/auth-state.js";
|
|
4
|
+
function isApiUrlMissing() {
|
|
5
|
+
try {
|
|
6
|
+
return resolveLtApiBaseUrl().missing;
|
|
7
|
+
} catch {
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
4
11
|
export function useSystemSetup() {
|
|
5
12
|
const needsSetupState = useState("lt-needs-setup", () => null);
|
|
6
13
|
const needsSetup = computed(() => needsSetupState.value);
|
|
7
14
|
async function checkSetupStatus() {
|
|
15
|
+
if (import.meta.server && isApiUrlMissing()) {
|
|
16
|
+
warnMissingLtApiUrl("server");
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
8
19
|
try {
|
|
9
20
|
const url = buildLtApiUrl("/system-setup/status");
|
|
10
21
|
const data = await $fetch(url);
|
|
@@ -173,7 +173,7 @@ export function useLtTusUpload(defaultOptions = {}) {
|
|
|
173
173
|
const tusUpload = tusUploads.value.get(id);
|
|
174
174
|
if (item && tusUpload && item.status !== "uploading") {
|
|
175
175
|
updateItem(id, { startedAt: /* @__PURE__ */ new Date(), status: "uploading" });
|
|
176
|
-
tusUpload.findPreviousUploads().then((previousUploads) => {
|
|
176
|
+
void tusUpload.findPreviousUploads().then((previousUploads) => {
|
|
177
177
|
const previousUpload = previousUploads[0];
|
|
178
178
|
if (previousUpload) {
|
|
179
179
|
tusUpload.resumeFromPreviousUpload(previousUpload);
|
|
@@ -191,7 +191,7 @@ export function useLtTusUpload(defaultOptions = {}) {
|
|
|
191
191
|
function pauseUpload(id) {
|
|
192
192
|
const tusUpload = tusUploads.value.get(id);
|
|
193
193
|
if (tusUpload) {
|
|
194
|
-
tusUpload.abort();
|
|
194
|
+
void tusUpload.abort();
|
|
195
195
|
updateItem(id, { status: "paused" });
|
|
196
196
|
}
|
|
197
197
|
}
|
|
@@ -207,7 +207,7 @@ export function useLtTusUpload(defaultOptions = {}) {
|
|
|
207
207
|
function cancelUpload(id) {
|
|
208
208
|
const tusUpload = tusUploads.value.get(id);
|
|
209
209
|
if (tusUpload) {
|
|
210
|
-
tusUpload.abort();
|
|
210
|
+
void tusUpload.abort();
|
|
211
211
|
}
|
|
212
212
|
tusUploads.value.delete(id);
|
|
213
213
|
const newMap = new Map(uploadItems.value);
|
|
@@ -78,7 +78,7 @@ export function isLocalDevApiProxy() {
|
|
|
78
78
|
}
|
|
79
79
|
const MISSING_API_URL_WARNING = {
|
|
80
80
|
client: "[LtExtensions] No API URL configured. Set NUXT_PUBLIC_API_URL. API paths stay relative and resolve against the app origin, so auth and setup calls 404 unless the app is served behind a same-origin reverse proxy.",
|
|
81
|
-
server: "[LtExtensions] No API URL configured for SSR. Set NUXT_API_URL or NUXT_PUBLIC_API_URL. API paths stay relative, so server-side requests never reach the backend: $fetch
|
|
81
|
+
server: "[LtExtensions] No API URL configured for SSR. Set NUXT_API_URL or NUXT_PUBLIC_API_URL. API paths stay relative, so server-side requests never reach the backend: $fetch sends them to this Nitro server itself and global fetch rejects them as an invalid URL."
|
|
82
82
|
};
|
|
83
83
|
function stripTrailingSlashes(url) {
|
|
84
84
|
return url.replace(/\/+$/, "");
|
|
@@ -117,6 +117,8 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
117
117
|
const currentPath = router?.currentRoute?.value?.fullPath;
|
|
118
118
|
const redirectQuery = currentPath && currentPath !== loginPath ? `?redirect=${encodeURIComponent(currentPath)}` : "";
|
|
119
119
|
window.location.href = loginPath + redirectQuery;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error("[LtAuth Interceptor] Handling a 401 failed:", error);
|
|
120
122
|
} finally {
|
|
121
123
|
if (loggingOut) {
|
|
122
124
|
setTimeout(() => {
|
|
@@ -139,7 +141,7 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
139
141
|
options.onResponseError(context);
|
|
140
142
|
}
|
|
141
143
|
if (context.response?.status === 401) {
|
|
142
|
-
handleUnauthorized(url);
|
|
144
|
+
void handleUnauthorized(url);
|
|
143
145
|
}
|
|
144
146
|
}
|
|
145
147
|
});
|
|
@@ -149,7 +151,7 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
149
151
|
const response = await originalNativeFetch(input, init);
|
|
150
152
|
if (response.status === 401) {
|
|
151
153
|
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
152
|
-
handleUnauthorized(url);
|
|
154
|
+
void handleUnauthorized(url);
|
|
153
155
|
}
|
|
154
156
|
return response;
|
|
155
157
|
};
|
|
@@ -114,7 +114,7 @@ export async function waitForElement(page, selector, options = {}) {
|
|
|
114
114
|
resolve();
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
|
-
Promise.
|
|
117
|
+
void Promise.allSettled(el.getAnimations().map((a) => a.finished)).then(() => resolve());
|
|
118
118
|
});
|
|
119
119
|
});
|
|
120
120
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nuxt-extensions",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.0",
|
|
4
4
|
"description": "Reusable Nuxt 4 composables, components, and Better-Auth integration for lenne.tech projects",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -51,17 +51,17 @@
|
|
|
51
51
|
"prepack": "node scripts/sync-module-version.mjs && nuxt-module-build build",
|
|
52
52
|
"c": "pnpm run check",
|
|
53
53
|
"check": "node scripts/check.mjs",
|
|
54
|
-
"check:raw": "pnpm install --frozen-lockfile && pnpm audit && pnpm run check:suppressions && pnpm peers check && pnpm run format:check && pnpm run
|
|
55
|
-
"check:fix": "pnpm install && pnpm audit --fix && pnpm run check:suppressions && pnpm peers check && pnpm run format && pnpm run lint:fix && pnpm run version:sync && pnpm test:types && pnpm test && pnpm run build && pnpm run check:manifest",
|
|
56
|
-
"check:naf": "pnpm install && pnpm peers check && pnpm run format && pnpm run lint:fix && pnpm run version:sync && pnpm test:types && pnpm test && pnpm run build && pnpm run check:manifest",
|
|
54
|
+
"check:raw": "pnpm install --frozen-lockfile && pnpm audit && pnpm run check:suppressions && pnpm peers check && pnpm run format:check && pnpm run dev:prepare && pnpm run lint && pnpm run version:check && pnpm test:types && pnpm test && pnpm run build && pnpm run check:manifest",
|
|
55
|
+
"check:fix": "pnpm install && pnpm audit --fix && pnpm run check:suppressions && pnpm peers check && pnpm run format && pnpm run dev:prepare && pnpm run lint:fix && pnpm run version:sync && pnpm test:types && pnpm test && pnpm run build && pnpm run check:manifest",
|
|
56
|
+
"check:naf": "pnpm install && pnpm peers check && pnpm run format && pnpm run dev:prepare && pnpm run lint:fix && pnpm run version:sync && pnpm test:types && pnpm test && pnpm run build && pnpm run check:manifest",
|
|
57
57
|
"cf": "pnpm run check:fix",
|
|
58
58
|
"cnaf": "pnpm run check:naf",
|
|
59
59
|
"dev": "pnpm dev:prepare && nuxt dev playground",
|
|
60
60
|
"dev:build": "nuxt build playground",
|
|
61
61
|
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxt prepare playground",
|
|
62
62
|
"build": "nuxt-module-build build",
|
|
63
|
-
"lint": "oxlint src/ scripts/",
|
|
64
|
-
"lint:fix": "oxlint --fix src/ scripts/",
|
|
63
|
+
"lint": "oxlint --type-aware src/ scripts/",
|
|
64
|
+
"lint:fix": "oxlint --type-aware --fix src/ scripts/",
|
|
65
65
|
"format": "oxfmt --write src/",
|
|
66
66
|
"format:check": "oxfmt --check src/",
|
|
67
67
|
"version:sync": "node scripts/sync-module-version.mjs",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"test:watch": "vitest watch",
|
|
71
71
|
"test:coverage": "vitest run --coverage",
|
|
72
72
|
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit",
|
|
73
|
-
"release": "pnpm format:check && pnpm lint && pnpm run version:check && pnpm test:types && pnpm test && pnpm run check:consumer && pnpm prepack",
|
|
73
|
+
"release": "pnpm format:check && pnpm run dev:prepare && pnpm lint && pnpm run version:check && pnpm test:types && pnpm test && pnpm run check:consumer && pnpm prepack",
|
|
74
74
|
"check:consumer": "node scripts/check-consumer-build.mjs",
|
|
75
75
|
"check:manifest": "node scripts/check-package-manifest.mjs",
|
|
76
76
|
"check:suppressions": "node scripts/check-suppressions.mjs"
|
|
@@ -79,9 +79,9 @@
|
|
|
79
79
|
"@nuxt/kit": "^4.0.0"
|
|
80
80
|
},
|
|
81
81
|
"peerDependencies": {
|
|
82
|
-
"@better-auth/passkey": ">=1.7.
|
|
82
|
+
"@better-auth/passkey": ">=1.7.12 <1.8.0",
|
|
83
83
|
"@playwright/test": ">=1.0.0",
|
|
84
|
-
"better-auth": ">=1.7.
|
|
84
|
+
"better-auth": ">=1.7.12 <1.8.0",
|
|
85
85
|
"nuxt": "^4.0.0",
|
|
86
86
|
"tus-js-client": ">=4.0.0"
|
|
87
87
|
},
|
|
@@ -100,24 +100,25 @@
|
|
|
100
100
|
}
|
|
101
101
|
},
|
|
102
102
|
"devDependencies": {
|
|
103
|
-
"@better-auth/passkey": "1.7.
|
|
103
|
+
"@better-auth/passkey": "1.7.12",
|
|
104
104
|
"@nuxt/module-builder": "1.0.3",
|
|
105
|
-
"@nuxt/schema": "4.6.
|
|
106
|
-
"@nuxt/ui": "4.
|
|
107
|
-
"@playwright/test": "1.64.
|
|
105
|
+
"@nuxt/schema": "4.6.2",
|
|
106
|
+
"@nuxt/ui": "4.12.0",
|
|
107
|
+
"@playwright/test": "1.64.1",
|
|
108
108
|
"@types/node": "26.6.5",
|
|
109
|
-
"@vitest/coverage-v8": "5.0.
|
|
110
|
-
"@vue/server-renderer": "3.5.
|
|
111
|
-
"better-auth": "1.7.
|
|
109
|
+
"@vitest/coverage-v8": "5.0.5",
|
|
110
|
+
"@vue/server-renderer": "3.5.44",
|
|
111
|
+
"better-auth": "1.7.12",
|
|
112
112
|
"cross-spawn": "7.0.6",
|
|
113
|
-
"happy-dom": "
|
|
114
|
-
"nuxt": "4.6.
|
|
115
|
-
"oxfmt": "0.
|
|
116
|
-
"oxlint": "1.
|
|
117
|
-
"
|
|
113
|
+
"happy-dom": "21.0.4",
|
|
114
|
+
"nuxt": "4.6.2",
|
|
115
|
+
"oxfmt": "0.74.0",
|
|
116
|
+
"oxlint": "1.89.0",
|
|
117
|
+
"oxlint-tsgolint": "7.0.2005",
|
|
118
|
+
"reka-ui": "2.11.0",
|
|
118
119
|
"tus-js-client": "4.3.1",
|
|
119
120
|
"typescript": "5.9.3",
|
|
120
|
-
"vitest": "5.0.
|
|
121
|
+
"vitest": "5.0.5",
|
|
121
122
|
"vue-tsc": "3.3.13"
|
|
122
123
|
},
|
|
123
124
|
"keywords": [
|