@lenne.tech/nuxt-extensions 1.14.0 → 1.15.1

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 CHANGED
@@ -259,5 +259,14 @@ fields at `app:beforeMount` and restores them at `app:mounted`, dispatching a sy
259
259
  1. **ALWAYS read source code** in `dist/runtime/` to understand available composables and components
260
260
  2. **Use `useLtAuth()`** for authentication — never implement auth manually
261
261
  3. **Check existing composables** before creating new ones — this module may already provide what you need
262
- 4. **Peer dependencies** (`better-auth`, `@better-auth/passkey`, `tus-js-client`) must be installed in the consuming project
262
+ 4. **Peer dependencies** (`better-auth`, `@better-auth/passkey`, `tus-js-client`) must be installed in the consuming project.
263
+ `better-auth` and `@better-auth/passkey` are pinned to **`>=1.7.1 <1.8.0`** — deliberately not a caret.
264
+ **They move in lock-step with `@lenne.tech/nest-server`.** better-auth is one protocol with two ends;
265
+ this module is the client end, nest-server the server end, and both declare the *same* narrow range.
266
+ Raising it means raising it in **both** framework repos and pinning the new version in **both** starters,
267
+ in one release. Never half of it, not even "just to unblock the frontend" — a split resolves as api on one
268
+ minor and app on another, 2FA activation fails at runtime, and *both* repos' `check` stays green because
269
+ each is internally consistent and only the assembled workspace has both halves.
270
+ `test/peer-dependency-ranges.test.ts` guards the declared range against the version actually tested;
271
+ `test/better-auth-contract.test.ts` pins the consumed better-auth surface.
263
272
  5. **AI types collision pitfall:** `LtAiPromptInput` is the CRUD input for the user-facing `LtAiPrompt` entity (used by `useLtAiPrompts().create/update`). The execution payload for `useLtAi.prompt()` / `.promptStream()` is `LtAiPromptRunInput`. Do not conflate them.
package/README.md CHANGED
@@ -23,16 +23,48 @@ Reusable Nuxt 4 composables, components, and Better-Auth integration for lenne.t
23
23
  > npx @lenne.tech/cli fullstack init my-project
24
24
  > ```
25
25
 
26
+ ## Requirements
27
+
28
+ | Dependency | Required version | Notes |
29
+ |---|---|---|
30
+ | Node | `>= 22` | |
31
+ | Nuxt | `^4.0.0` | |
32
+ | `better-auth` | `>=1.7.1 <1.8.0` | **required peer** — pin it, see below |
33
+ | `@better-auth/passkey` | `>=1.7.1 <1.8.0` | optional peer, passkey/WebAuthn only |
34
+ | `tus-js-client` | `>=4.0.0` | optional peer, resumable uploads only |
35
+ | `@lenne.tech/nest-server` | `>= 11.37.0` | **only if you use the backend** — see the lock-step rule |
36
+
37
+ **Why `better-auth` is pinned to one minor line, and why you must not widen it:**
38
+ better-auth is one protocol with two ends. This module is the client end and
39
+ `@lenne.tech/nest-server` is the server end, and they must resolve the **same**
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.1`) would re-open the
42
+ hole at 1.8. If the api and the app drift apart, 2FA activation fails at runtime with a
43
+ generic client error while **both** projects' checks stay green: each is internally
44
+ consistent, and only the assembled workspace has both halves.
45
+
46
+ Raising better-auth therefore means raising it in `@lenne.tech/nest-server` **and**
47
+ `@lenne.tech/nuxt-extensions` in one step.
48
+
26
49
  ## Installation
27
50
 
28
51
  ```bash
29
- npm install @lenne.tech/nuxt-extensions better-auth
52
+ npm install @lenne.tech/nuxt-extensions better-auth@1.7.1
30
53
  # Optional: For passkey support
31
- npm install @better-auth/passkey
54
+ npm install @better-auth/passkey@1.7.1
32
55
  # Optional: For TUS file uploads
33
56
  npm install tus-js-client
34
57
  ```
35
58
 
59
+ In a fullstack monorepo, pin it workspace-wide so the api and the app cannot drift:
60
+
61
+ ```yaml
62
+ # pnpm-workspace.yaml
63
+ overrides:
64
+ better-auth: 1.7.1
65
+ '@better-auth/passkey': 1.7.1
66
+ ```
67
+
36
68
  ## Features
37
69
 
38
70
  - **Better-Auth Integration** - Login, 2FA, Passkey/WebAuthn support
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.14.0";
6
+ declare const version = "1.15.1";
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "configKey": "ltExtensions",
3
3
  "name": "@lenne.tech/nuxt-extensions",
4
- "version": "1.14.0",
4
+ "version": "1.15.1",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.3",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -1,7 +1,8 @@
1
1
  import { defineNuxtModule, createResolver, tryResolveModule, addImports, addComponent, addPlugin, addRouteMiddleware } from '@nuxt/kit';
2
+ import { pathToFileURL } from 'node:url';
2
3
 
3
4
  const name = "@lenne.tech/nuxt-extensions";
4
- const version = "1.14.0";
5
+ const version = "1.15.1";
5
6
  const configKey = "ltExtensions";
6
7
  const DEFAULT_LT_AUTH_STATE_COOKIE = "lt-auth-state";
7
8
  const DEFAULT_LT_JWT_TOKEN_COOKIE = "lt-jwt-token";
@@ -74,7 +75,7 @@ const module$1 = defineNuxtModule({
74
75
  preHydrationInput: { ...defaultOptions.preHydrationInput, ...options.preHydrationInput },
75
76
  tus: { ...defaultOptions.tus, ...options.tus }
76
77
  };
77
- const passkeyAvailable = !!await tryResolveModule("@better-auth/passkey/client", new URL(import.meta.url));
78
+ const passkeyAvailable = !!await tryResolveModule("@better-auth/passkey/client", [new URL(import.meta.url), pathToFileURL(`${nuxt.options.rootDir}/`)]);
78
79
  if (!passkeyAvailable) {
79
80
  const passkeyStub = resolve("./runtime/lib/passkey-stub");
80
81
  nuxt.options.alias["@better-auth/passkey/client"] = passkeyStub;
@@ -233,11 +234,14 @@ const module$1 = defineNuxtModule({
233
234
  }
234
235
  nuxt.options.build.transpile.push(resolve("./runtime"));
235
236
  nuxt.options.build.transpile.push("tus-js-client");
236
- console.log(`[${name}] Module loaded with config:`, {
237
- auth: resolvedOptions.auth?.enabled ? "enabled" : "disabled",
238
- i18nAutoMerge: resolvedOptions.i18n?.autoMerge,
239
- tusEndpoint: resolvedOptions.tus?.defaultEndpoint
240
- });
237
+ console.log(`[${name}] v${version}`);
238
+ if (nuxt.options.dev) {
239
+ console.log(`[${name}] Module loaded with config:`, {
240
+ auth: resolvedOptions.auth?.enabled ? "enabled" : "disabled",
241
+ i18nAutoMerge: resolvedOptions.i18n?.autoMerge,
242
+ tusEndpoint: resolvedOptions.tus?.defaultEndpoint
243
+ });
244
+ }
241
245
  }
242
246
  });
243
247
 
@@ -11,6 +11,16 @@
11
11
  * so the factory below is normally never called. It stays a no-op (rather than
12
12
  * throwing) to keep the direct `createLtAuthClient()` entry point working, whose
13
13
  * own `enablePasskey` default is `true`.
14
+ *
15
+ * WHY this file exports more than `passkeyClient`:
16
+ * the alias is installed into `nuxt.options.alias` and `nuxt.options.nitro.alias`,
17
+ * which rewrites the specifier for the WHOLE consumer app — not just for our
18
+ * `auth-client.ts`. A consumer (or another dependency) importing any other name
19
+ * from `@better-auth/passkey/client` would then hit
20
+ * `"PASSKEY_ERROR_CODES" is not exported by …/passkey-stub` — the same class of
21
+ * unresolved-export build failure this stub exists to prevent, merely relocated.
22
+ * So the stub mirrors the real package's full export surface.
23
+ * `test/optional-peers.test.ts` pins that surface against the real package.
14
24
  */
15
25
  /**
16
26
  * No-op replacement for the real `passkeyClient()` Better-Auth client plugin.
@@ -21,3 +31,86 @@
21
31
  export declare function passkeyClient(): {
22
32
  id: "lt-passkey-unavailable";
23
33
  };
34
+ /**
35
+ * Mirror of the real package's error-code table.
36
+ *
37
+ * Frozen and value-complete so a consumer that reads a code for a message or an
38
+ * equality check still gets a defined value instead of a `TypeError` on
39
+ * `undefined`. The codes are the contract; the messages are best-effort copies.
40
+ */
41
+ export declare const PASSKEY_ERROR_CODES: Readonly<{
42
+ AUTH_CANCELLED: {
43
+ code: string;
44
+ message: string;
45
+ };
46
+ AUTHENTICATION_FAILED: {
47
+ code: string;
48
+ message: string;
49
+ };
50
+ CHALLENGE_NOT_FOUND: {
51
+ code: string;
52
+ message: string;
53
+ };
54
+ FAILED_TO_UPDATE_PASSKEY: {
55
+ code: string;
56
+ message: string;
57
+ };
58
+ FAILED_TO_VERIFY_REGISTRATION: {
59
+ code: string;
60
+ message: string;
61
+ };
62
+ PASSKEY_NOT_FOUND: {
63
+ code: string;
64
+ message: string;
65
+ };
66
+ PREVIOUSLY_REGISTERED: {
67
+ code: string;
68
+ message: string;
69
+ };
70
+ REGISTRATION_CANCELLED: {
71
+ code: string;
72
+ message: string;
73
+ };
74
+ RESOLVE_USER_REQUIRED: {
75
+ code: string;
76
+ message: string;
77
+ };
78
+ RESOLVED_USER_INVALID: {
79
+ code: string;
80
+ message: string;
81
+ };
82
+ SESSION_REQUIRED: {
83
+ code: string;
84
+ message: string;
85
+ };
86
+ UNABLE_TO_CREATE_SESSION: {
87
+ code: string;
88
+ message: string;
89
+ };
90
+ UNKNOWN_ERROR: {
91
+ code: string;
92
+ message: string;
93
+ };
94
+ USER_NOT_FOUND: {
95
+ code: string;
96
+ message: string;
97
+ };
98
+ YOU_ARE_NOT_ALLOWED_TO_REGISTER_THIS_PASSKEY: {
99
+ code: string;
100
+ message: string;
101
+ };
102
+ }>;
103
+ /**
104
+ * No-op replacement for the real `getPasskeyActions()`.
105
+ *
106
+ * Every action rejects rather than resolving falsely: a passkey ceremony that
107
+ * silently "succeeds" without the package would leave the caller believing a
108
+ * credential exists. The rejection names the missing dependency.
109
+ */
110
+ export declare function getPasskeyActions(..._args: unknown[]): {
111
+ addPasskey: () => Promise<never>;
112
+ deletePasskey: () => Promise<never>;
113
+ listUserPasskeys: () => Promise<never>;
114
+ signInPasskey: () => Promise<never>;
115
+ updatePasskey: () => Promise<never>;
116
+ };
@@ -1,10 +1,50 @@
1
1
  let warned = false;
2
- export function passkeyClient() {
3
- if (!warned) {
4
- warned = true;
5
- console.warn(
6
- '[@lenne.tech/nuxt-extensions] Passkey support is unavailable: the optional peer dependency "@better-auth/passkey" is not installed. Install it to use passkeys, or pass `enablePasskey: false` to createLtAuthClient() to silence this warning.'
7
- );
2
+ function warnOnce(entryPoint) {
3
+ if (warned) {
4
+ return;
8
5
  }
6
+ warned = true;
7
+ console.warn(
8
+ `[@lenne.tech/nuxt-extensions] Passkey support is unavailable: the optional peer dependency "@better-auth/passkey" is not installed. Install it to use passkeys, or pass \`enablePasskey: false\` to createLtAuthClient() to silence this warning. (reached via ${entryPoint})`
9
+ );
10
+ }
11
+ export function passkeyClient() {
12
+ warnOnce("passkeyClient()");
9
13
  return { id: "lt-passkey-unavailable" };
10
14
  }
15
+ export const PASSKEY_ERROR_CODES = Object.freeze({
16
+ AUTH_CANCELLED: { code: "AUTH_CANCELLED", message: "Auth cancelled" },
17
+ AUTHENTICATION_FAILED: { code: "AUTHENTICATION_FAILED", message: "Authentication failed" },
18
+ CHALLENGE_NOT_FOUND: { code: "CHALLENGE_NOT_FOUND", message: "Challenge not found" },
19
+ FAILED_TO_UPDATE_PASSKEY: { code: "FAILED_TO_UPDATE_PASSKEY", message: "Failed to update passkey" },
20
+ FAILED_TO_VERIFY_REGISTRATION: { code: "FAILED_TO_VERIFY_REGISTRATION", message: "Failed to verify registration" },
21
+ PASSKEY_NOT_FOUND: { code: "PASSKEY_NOT_FOUND", message: "Passkey not found" },
22
+ PREVIOUSLY_REGISTERED: { code: "PREVIOUSLY_REGISTERED", message: "Previously registered" },
23
+ REGISTRATION_CANCELLED: { code: "REGISTRATION_CANCELLED", message: "Registration cancelled" },
24
+ RESOLVE_USER_REQUIRED: {
25
+ code: "RESOLVE_USER_REQUIRED",
26
+ message: "Passkey registration requires either an authenticated session or a resolveUser callback when requireSession is false"
27
+ },
28
+ RESOLVED_USER_INVALID: { code: "RESOLVED_USER_INVALID", message: "Resolved user is invalid" },
29
+ SESSION_REQUIRED: { code: "SESSION_REQUIRED", message: "Passkey registration requires an authenticated session" },
30
+ UNABLE_TO_CREATE_SESSION: { code: "UNABLE_TO_CREATE_SESSION", message: "Unable to create session" },
31
+ UNKNOWN_ERROR: { code: "UNKNOWN_ERROR", message: "Unknown error" },
32
+ USER_NOT_FOUND: { code: "USER_NOT_FOUND", message: "User not found" },
33
+ YOU_ARE_NOT_ALLOWED_TO_REGISTER_THIS_PASSKEY: {
34
+ code: "YOU_ARE_NOT_ALLOWED_TO_REGISTER_THIS_PASSKEY",
35
+ message: "You are not allowed to register this passkey"
36
+ }
37
+ });
38
+ export function getPasskeyActions(..._args) {
39
+ warnOnce("getPasskeyActions()");
40
+ const unavailable = async () => {
41
+ throw new Error('[@lenne.tech/nuxt-extensions] Passkey action unavailable: the optional peer dependency "@better-auth/passkey" is not installed.');
42
+ };
43
+ return {
44
+ addPasskey: unavailable,
45
+ deletePasskey: unavailable,
46
+ listUserPasskeys: unavailable,
47
+ signInPasskey: unavailable,
48
+ updatePasskey: unavailable
49
+ };
50
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nuxt-extensions",
3
- "version": "1.14.0",
3
+ "version": "1.15.1",
4
4
  "description": "Reusable Nuxt 4 composables, components, and Better-Auth integration for lenne.tech projects",
5
5
  "repository": {
6
6
  "type": "git",
@@ -78,9 +78,9 @@
78
78
  "@nuxt/kit": "^4.0.0"
79
79
  },
80
80
  "peerDependencies": {
81
- "@better-auth/passkey": ">=1.0.0",
81
+ "@better-auth/passkey": ">=1.7.1 <1.8.0",
82
82
  "@playwright/test": ">=1.0.0",
83
- "better-auth": ">=1.0.0",
83
+ "better-auth": ">=1.7.1 <1.8.0",
84
84
  "nuxt": "^4.0.0",
85
85
  "tus-js-client": ">=4.0.0"
86
86
  },