nid-webauthn-emulator 0.2.8 → 0.2.9
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/README.ja.md +24 -5
- package/README.md +28 -1
- package/dist/spec/test-utils/unit-test.spec.d.ts +1 -0
- package/dist/spec/test-utils/unit-test.spec.js +116 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/libs/encode-utils.js +1 -1
- package/dist/src/test-utils/unit-test.d.ts +31 -0
- package/dist/src/test-utils/unit-test.js +60 -0
- package/package.json +6 -6
package/README.ja.md
CHANGED
|
@@ -201,13 +201,32 @@ test.describe("Passkeys Tests", { tag: ["@daily"] }, () => {
|
|
|
201
201
|
await page.evaluate(BrowserInjection.HookWebAuthnApis);
|
|
202
202
|
```
|
|
203
203
|
|
|
204
|
-
|
|
204
|
+
## ユニットテストでの WebAuthn API フック
|
|
205
205
|
|
|
206
|
-
- `navigator.credentials
|
|
207
|
-
- `navigator.credentials.create` の定義を上書きし、`window.webAuthnEmulatorCreate` を呼び出す
|
|
208
|
-
- `PublicKeyCredential.signalUnknownCredential` の定義を追加し、`window.webAuthnEmulatorSignalUnknownCredential` を呼び出す
|
|
206
|
+
Vitest/Jest などで WebAuthn API を差し替えたい場合は、`src/test-utils/unit-test.ts` の `createPasskeysEmulator` を使うと `PublicKeyCredential` / `navigator.credentials` を簡単にモックできます。
|
|
209
207
|
|
|
210
|
-
|
|
208
|
+
```TypeScript
|
|
209
|
+
import { createPasskeysEmulator } from "nid-webauthn-emulator";
|
|
210
|
+
|
|
211
|
+
const { interface: methods, addPasskey } = createPasskeysEmulator({
|
|
212
|
+
origin: "http://localhost",
|
|
213
|
+
rpId: "localhost",
|
|
214
|
+
autofill: true, // false にすると isConditionalMediationAvailable が false を返す
|
|
215
|
+
// creationException / requestException を指定すると DOMException が発生する挙動を注入できます
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
params?.existingPasskeys?.forEach((passkey) => {
|
|
219
|
+
addPasskey(passkey); // テスト開始前に登録済みパスキーを追加
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// 既存の実装を消し、エミュレータを差し込む
|
|
223
|
+
const undefinedProperty = { value: undefined, configurable: true };
|
|
224
|
+
Object.defineProperty(window, "PublicKeyCredential", undefinedProperty);
|
|
225
|
+
Object.defineProperty(window.navigator, "credentials", undefinedProperty);
|
|
226
|
+
|
|
227
|
+
vi.spyOn(window, "PublicKeyCredential", "get").mockReturnValue(methods.publicKeyCredentials);
|
|
228
|
+
vi.spyOn(window.navigator, "credentials", "get").mockReturnValue(methods.credentialsContainer);
|
|
229
|
+
```
|
|
211
230
|
|
|
212
231
|
## ライセンス
|
|
213
232
|
|
package/README.md
CHANGED
|
@@ -210,8 +210,35 @@ await page.evaluate(BrowserInjection.HookWebAuthnApis);
|
|
|
210
210
|
|
|
211
211
|
This ensures that the `WebAuthnEmulator` methods defined earlier with `exposeFunction` are executed when `navigator.credentials.get` and `navigator.credentials.create` are called. These processes include serialization and deserialization of data for communication between the test context and Playwright context.
|
|
212
212
|
|
|
213
|
+
## Hooking WebAuthn APIs in Unit Tests
|
|
214
|
+
|
|
215
|
+
If you want to swap out WebAuthn APIs in Vitest/Jest, you can mock `PublicKeyCredential` / `navigator.credentials` using `createPasskeysEmulator` from `src/test-utils/unit-test.ts`.
|
|
216
|
+
|
|
217
|
+
```TypeScript
|
|
218
|
+
import { createPasskeysEmulator } from "nid-webauthn-emulator";
|
|
219
|
+
|
|
220
|
+
const { interface: methods, addPasskey } = createPasskeysEmulator({
|
|
221
|
+
origin: "http://localhost",
|
|
222
|
+
rpId: "localhost",
|
|
223
|
+
autofill: true, // returns false from isConditionalMediationAvailable when set to false
|
|
224
|
+
// specify creationException / requestException to inject a DOMException
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
params?.existingPasskeys?.forEach((passkey) => {
|
|
228
|
+
addPasskey(passkey); // add pre-registered passkeys before the test starts
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// remove existing implementations and inject the emulator
|
|
232
|
+
const undefinedProperty = { value: undefined, configurable: true };
|
|
233
|
+
Object.defineProperty(window, "PublicKeyCredential", undefinedProperty);
|
|
234
|
+
Object.defineProperty(window.navigator, "credentials", undefinedProperty);
|
|
235
|
+
|
|
236
|
+
vi.spyOn(window, "PublicKeyCredential", "get").mockReturnValue(methods.publicKeyCredentials);
|
|
237
|
+
vi.spyOn(window.navigator, "credentials", "get").mockReturnValue(methods.credentialsContainer);
|
|
238
|
+
```
|
|
239
|
+
|
|
213
240
|
## License
|
|
214
241
|
|
|
215
242
|
MIT License
|
|
216
243
|
|
|
217
|
-
Copyright (C) 2024 Nikkei Inc.
|
|
244
|
+
Copyright (C) 2024 Nikkei Inc.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const globals_1 = require("@jest/globals");
|
|
7
|
+
const authenticator_emulator_1 = require("../../src/authenticator/authenticator-emulator");
|
|
8
|
+
const encode_utils_1 = __importDefault(require("../../src/libs/encode-utils"));
|
|
9
|
+
const unit_test_1 = require("../../src/test-utils/unit-test");
|
|
10
|
+
const webauthn_model_json_1 = require("../../src/webauthn/webauthn-model-json");
|
|
11
|
+
const globalScope = globalThis;
|
|
12
|
+
const originalLocation = globalScope.location;
|
|
13
|
+
const originalBtoa = globalScope.btoa;
|
|
14
|
+
const originalAtob = globalScope.atob;
|
|
15
|
+
(0, globals_1.beforeAll)(() => {
|
|
16
|
+
if (!globalScope.btoa) {
|
|
17
|
+
globalScope.btoa = (data) => Buffer.from(data, "binary").toString("base64");
|
|
18
|
+
}
|
|
19
|
+
if (!globalScope.atob) {
|
|
20
|
+
globalScope.atob = (base64) => Buffer.from(base64, "base64").toString("binary");
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
const clearDefaultRepository = () => {
|
|
24
|
+
const repository = new authenticator_emulator_1.AuthenticatorEmulator().params.credentialsRepository;
|
|
25
|
+
repository?.loadCredentials().forEach((credential) => {
|
|
26
|
+
repository.deleteCredential(credential);
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
(0, globals_1.beforeEach)(() => {
|
|
30
|
+
clearDefaultRepository();
|
|
31
|
+
});
|
|
32
|
+
(0, globals_1.afterEach)(() => {
|
|
33
|
+
globalScope.location = originalLocation;
|
|
34
|
+
clearDefaultRepository();
|
|
35
|
+
});
|
|
36
|
+
(0, globals_1.afterAll)(() => {
|
|
37
|
+
globalScope.location = originalLocation;
|
|
38
|
+
globalScope.btoa = originalBtoa;
|
|
39
|
+
globalScope.atob = originalAtob;
|
|
40
|
+
});
|
|
41
|
+
(0, globals_1.describe)("createPasskeysEmulator", () => {
|
|
42
|
+
(0, globals_1.test)("exposes default passkeys-like interfaces", async () => {
|
|
43
|
+
const emulator = (0, unit_test_1.createPasskeysEmulator)();
|
|
44
|
+
(0, globals_1.expect)(await emulator.methods.publicKeyCredentials.isConditionalMediationAvailable?.()).toBe(true);
|
|
45
|
+
(0, globals_1.expect)(await emulator.methods.publicKeyCredentials.getClientCapabilities()).toEqual({ conditionalGet: true });
|
|
46
|
+
(0, globals_1.expect)(await emulator.methods.publicKeyCredentials.isUserVerifyingPlatformAuthenticatorAvailable()).toBe(true);
|
|
47
|
+
(0, globals_1.expect)(emulator.methods.publicKeyCredentials.parseCreationOptionsFromJSON).toBe(webauthn_model_json_1.parseCreationOptionsFromJSON);
|
|
48
|
+
(0, globals_1.expect)(emulator.methods.publicKeyCredentials.parseRequestOptionsFromJSON).toBe(webauthn_model_json_1.parseRequestOptionsFromJSON);
|
|
49
|
+
});
|
|
50
|
+
(0, globals_1.test)("addPasskey stores credentials for custom rpId and allows authentication", async () => {
|
|
51
|
+
globalScope.location = { origin: "https://example.com" };
|
|
52
|
+
const emulator = (0, unit_test_1.createPasskeysEmulator)({ origin: "https://example.com", rpId: "example.com" });
|
|
53
|
+
emulator.addPasskey("user-123");
|
|
54
|
+
const repository = emulator.instance.authenticator.params.credentialsRepository;
|
|
55
|
+
const credentials = repository?.loadCredentials() ?? [];
|
|
56
|
+
(0, globals_1.expect)(credentials).toHaveLength(1);
|
|
57
|
+
(0, globals_1.expect)(credentials[0].publicKeyCredentialSource.rpId.value).toBe("example.com");
|
|
58
|
+
const credentialId = encode_utils_1.default.encodeBase64Url(credentials[0].publicKeyCredentialSource.id);
|
|
59
|
+
const requestOptions = emulator.methods.publicKeyCredentials.parseRequestOptionsFromJSON({
|
|
60
|
+
challenge: encode_utils_1.default.encodeBase64Url(Buffer.from("challenge")),
|
|
61
|
+
rpId: "example.com",
|
|
62
|
+
allowCredentials: [{ type: "public-key", id: credentialId }],
|
|
63
|
+
});
|
|
64
|
+
const assertion = (await emulator.methods.credentialsContainer.get({
|
|
65
|
+
publicKey: requestOptions,
|
|
66
|
+
}));
|
|
67
|
+
(0, globals_1.expect)(assertion.id).toBe(credentialId);
|
|
68
|
+
const userHandle = assertion.response.userHandle;
|
|
69
|
+
(0, globals_1.expect)(encode_utils_1.default.encodeBase64Url(new Uint8Array(userHandle ?? new ArrayBuffer(0)))).toBe(encode_utils_1.default.encodeBase64Url(Buffer.from("user-123")));
|
|
70
|
+
});
|
|
71
|
+
(0, globals_1.test)("creationException bubbles up as DOMException", async () => {
|
|
72
|
+
globalScope.location = { origin: "http://localhost" };
|
|
73
|
+
const emulator = (0, unit_test_1.createPasskeysEmulator)({ creationException: "AbortError" });
|
|
74
|
+
const creationOptions = emulator.methods.publicKeyCredentials.parseCreationOptionsFromJSON({
|
|
75
|
+
challenge: encode_utils_1.default.encodeBase64Url(Buffer.from("challenge")),
|
|
76
|
+
rp: { name: "Test RP", id: "localhost" },
|
|
77
|
+
user: {
|
|
78
|
+
id: encode_utils_1.default.encodeBase64Url(Buffer.from("creation-user")),
|
|
79
|
+
name: "creation-user",
|
|
80
|
+
displayName: "",
|
|
81
|
+
},
|
|
82
|
+
pubKeyCredParams: [{ alg: -7, type: "public-key" }],
|
|
83
|
+
});
|
|
84
|
+
const createCall = emulator.methods.credentialsContainer.create({ publicKey: creationOptions });
|
|
85
|
+
await (0, globals_1.expect)(createCall).rejects.toThrow(DOMException);
|
|
86
|
+
await (0, globals_1.expect)(createCall).rejects.toMatchObject({ name: "AbortError" });
|
|
87
|
+
});
|
|
88
|
+
(0, globals_1.test)("requestException is thrown when fetching assertions", async () => {
|
|
89
|
+
globalScope.location = { origin: "http://localhost" };
|
|
90
|
+
const emulator = (0, unit_test_1.createPasskeysEmulator)({ requestException: "InvalidStateError" });
|
|
91
|
+
emulator.addPasskey("auth-user");
|
|
92
|
+
const repository = emulator.instance.authenticator.params.credentialsRepository;
|
|
93
|
+
const credentials = repository?.loadCredentials() ?? [];
|
|
94
|
+
(0, globals_1.expect)(credentials).toHaveLength(1);
|
|
95
|
+
const credentialId = encode_utils_1.default.encodeBase64Url(credentials[0].publicKeyCredentialSource.id);
|
|
96
|
+
const requestOptions = emulator.methods.publicKeyCredentials.parseRequestOptionsFromJSON({
|
|
97
|
+
challenge: encode_utils_1.default.encodeBase64Url(Buffer.from("request")),
|
|
98
|
+
rpId: "localhost",
|
|
99
|
+
allowCredentials: [{ type: "public-key", id: credentialId }],
|
|
100
|
+
});
|
|
101
|
+
const parsedId = encode_utils_1.default.encodeBase64Url(new Uint8Array(requestOptions.allowCredentials?.[0].id ?? new ArrayBuffer(0)));
|
|
102
|
+
(0, globals_1.expect)(parsedId).toBe(credentialId);
|
|
103
|
+
try {
|
|
104
|
+
await emulator.methods.credentialsContainer.get({ publicKey: requestOptions });
|
|
105
|
+
throw new Error("Expected DOMException");
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
(0, globals_1.expect)(error).toBeInstanceOf(DOMException);
|
|
109
|
+
(0, globals_1.expect)(error.name).toBe("InvalidStateError");
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
(0, globals_1.test)("disables conditional mediation when autofill flag is false", async () => {
|
|
113
|
+
const emulator = (0, unit_test_1.createPasskeysEmulator)({ autofill: false });
|
|
114
|
+
await (0, globals_1.expect)(emulator.methods.publicKeyCredentials.isConditionalMediationAvailable?.()).resolves.toBe(false);
|
|
115
|
+
});
|
|
116
|
+
});
|
package/dist/src/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export * from "./authenticator/ctap-model";
|
|
|
5
5
|
export * from "./repository/credentials-file-repository";
|
|
6
6
|
export * from "./repository/credentials-memory-repository";
|
|
7
7
|
export * from "./repository/credentials-repository";
|
|
8
|
+
export * from "./test-utils/unit-test";
|
|
8
9
|
export * from "./webauthn/cose-key";
|
|
9
10
|
export * from "./webauthn/webauthn-emulator";
|
|
10
11
|
export * from "./webauthn/webauthn-model";
|
package/dist/src/index.js
CHANGED
|
@@ -23,6 +23,7 @@ __exportStar(require("./authenticator/ctap-model"), exports);
|
|
|
23
23
|
__exportStar(require("./repository/credentials-file-repository"), exports);
|
|
24
24
|
__exportStar(require("./repository/credentials-memory-repository"), exports);
|
|
25
25
|
__exportStar(require("./repository/credentials-repository"), exports);
|
|
26
|
+
__exportStar(require("./test-utils/unit-test"), exports);
|
|
26
27
|
__exportStar(require("./webauthn/cose-key"), exports);
|
|
27
28
|
__exportStar(require("./webauthn/webauthn-emulator"), exports);
|
|
28
29
|
__exportStar(require("./webauthn/webauthn-model"), exports);
|
|
@@ -46,7 +46,7 @@ function encodeCbor(data) {
|
|
|
46
46
|
if (typeof value === "object" && value !== null) {
|
|
47
47
|
const encodedData = new Map();
|
|
48
48
|
for (const [k, v] of Object.entries(value)) {
|
|
49
|
-
const ki = Number.parseInt(k);
|
|
49
|
+
const ki = Number.parseInt(k, 10);
|
|
50
50
|
if (Number.isNaN(ki)) {
|
|
51
51
|
encodedData.set(k, encoder(v));
|
|
52
52
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { WebAuthnEmulator } from "../webauthn/webauthn-emulator";
|
|
2
|
+
import { type AllAcceptedCredentialsOptionsJSON, type CurrentUserDetailsOptionsJSON, type UnknownCredentialOptionsJSON } from "../webauthn/webauthn-model-json";
|
|
3
|
+
type PasskeysEmulatorParams = {
|
|
4
|
+
origin?: string;
|
|
5
|
+
autofill?: boolean;
|
|
6
|
+
creationException?: string;
|
|
7
|
+
requestException?: string;
|
|
8
|
+
rpId?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare const createPasskeysEmulator: (params?: PasskeysEmulatorParams) => {
|
|
11
|
+
instance: WebAuthnEmulator;
|
|
12
|
+
addPasskey: (userId: string) => import("../webauthn/webauthn-model-json").RegistrationResponseJSON;
|
|
13
|
+
methods: {
|
|
14
|
+
credentialsContainer: CredentialsContainer;
|
|
15
|
+
publicKeyCredentials: {
|
|
16
|
+
new (): PublicKeyCredential;
|
|
17
|
+
prototype: PublicKeyCredential;
|
|
18
|
+
getClientCapabilities(): Promise<PublicKeyCredentialClientCapabilities>;
|
|
19
|
+
isConditionalMediationAvailable(): Promise<boolean>;
|
|
20
|
+
isUserVerifyingPlatformAuthenticatorAvailable(): Promise<boolean>;
|
|
21
|
+
parseCreationOptionsFromJSON(options: PublicKeyCredentialCreationOptionsJSON): PublicKeyCredentialCreationOptions;
|
|
22
|
+
parseRequestOptionsFromJSON(options: PublicKeyCredentialRequestOptionsJSON): PublicKeyCredentialRequestOptions;
|
|
23
|
+
} & {
|
|
24
|
+
signalUnknownCredential(options: UnknownCredentialOptionsJSON): Promise<void>;
|
|
25
|
+
signalAllAcceptedCredentials(options: AllAcceptedCredentialsOptionsJSON): Promise<void>;
|
|
26
|
+
signalCurrentUserDetails(options: CurrentUserDetailsOptionsJSON): Promise<void>;
|
|
27
|
+
isConditionalMediationAvailable?(): Promise<boolean>;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createPasskeysEmulator = void 0;
|
|
4
|
+
const authenticator_emulator_1 = require("../authenticator/authenticator-emulator");
|
|
5
|
+
const webauthn_emulator_1 = require("../webauthn/webauthn-emulator");
|
|
6
|
+
const webauthn_model_json_1 = require("../webauthn/webauthn-model-json");
|
|
7
|
+
const createInteraction = (exception) => exception
|
|
8
|
+
? () => {
|
|
9
|
+
throw new DOMException("test", exception);
|
|
10
|
+
}
|
|
11
|
+
: undefined;
|
|
12
|
+
const addTestPasskey = (emulator, origin, userId, rpId = "localhost") => {
|
|
13
|
+
const creationOption = {
|
|
14
|
+
challenge: "EA3d-yrkJAfNWICZ7It5ErO0XngxTe32L0t8IjEj9r8",
|
|
15
|
+
rp: { name: "Test RP", id: rpId },
|
|
16
|
+
user: {
|
|
17
|
+
id: (0, webauthn_model_json_1.encodeBase64Url)(Buffer.from(userId)),
|
|
18
|
+
name: "test",
|
|
19
|
+
displayName: "",
|
|
20
|
+
},
|
|
21
|
+
pubKeyCredParams: [
|
|
22
|
+
{ alg: -8, type: "public-key" },
|
|
23
|
+
{ alg: -7, type: "public-key" },
|
|
24
|
+
{ alg: -257, type: "public-key" },
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
return emulator.createJSON(origin, creationOption);
|
|
28
|
+
};
|
|
29
|
+
const createPasskeysEmulator = (params) => {
|
|
30
|
+
const authenticator = new authenticator_emulator_1.AuthenticatorEmulator({
|
|
31
|
+
userMakeCredentialInteraction: createInteraction(params?.creationException),
|
|
32
|
+
userGetAssertionInteraction: createInteraction(params?.requestException),
|
|
33
|
+
});
|
|
34
|
+
const instance = new webauthn_emulator_1.WebAuthnEmulator(authenticator);
|
|
35
|
+
const origin = params?.origin ?? "http://localhost";
|
|
36
|
+
const publicKeyCredentials = {
|
|
37
|
+
isConditionalMediationAvailable: async () => Promise.resolve(params?.autofill ?? true),
|
|
38
|
+
signalUnknownCredential: async (options) => instance.signalUnknownCredential(options),
|
|
39
|
+
signalAllAcceptedCredentials: async (options) => instance.signalAllAcceptedCredentials(options),
|
|
40
|
+
signalCurrentUserDetails: async (options) => instance.signalCurrentUserDetails(options),
|
|
41
|
+
getClientCapabilities: async () => ({ conditionalGet: true }),
|
|
42
|
+
isUserVerifyingPlatformAuthenticatorAvailable: async () => true,
|
|
43
|
+
parseCreationOptionsFromJSON: webauthn_model_json_1.parseCreationOptionsFromJSON,
|
|
44
|
+
parseRequestOptionsFromJSON: webauthn_model_json_1.parseRequestOptionsFromJSON,
|
|
45
|
+
};
|
|
46
|
+
const credentialsContainer = {
|
|
47
|
+
get: async (options) => instance.get(origin, options ?? {}),
|
|
48
|
+
create: async (options) => instance.create(origin, options ?? {}),
|
|
49
|
+
};
|
|
50
|
+
const addPasskey = (userId) => addTestPasskey(instance, origin, userId, params?.rpId);
|
|
51
|
+
return {
|
|
52
|
+
instance,
|
|
53
|
+
addPasskey,
|
|
54
|
+
methods: {
|
|
55
|
+
credentialsContainer,
|
|
56
|
+
publicKeyCredentials,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
exports.createPasskeysEmulator = createPasskeysEmulator;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nid-webauthn-emulator",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"test",
|
|
6
6
|
"e2e",
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
"main": "dist/src/index.js",
|
|
16
16
|
"types": "dist/src/index.d.ts",
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"@biomejs/biome": "^2.
|
|
18
|
+
"@biomejs/biome": "^2.3.8",
|
|
19
19
|
"@jest/globals": "^30.2.0",
|
|
20
|
-
"@simplewebauthn/server": "^13.2.
|
|
21
|
-
"@tsconfig/recommended": "^1.0.
|
|
22
|
-
"@types/node": "^24.
|
|
20
|
+
"@simplewebauthn/server": "^13.2.2",
|
|
21
|
+
"@tsconfig/recommended": "^1.0.13",
|
|
22
|
+
"@types/node": "^24.10.1",
|
|
23
23
|
"jest": "^30.2.0",
|
|
24
24
|
"ts-jest": "^29.4.5",
|
|
25
25
|
"ts-node": "^10.9.2",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"cbor": "^10.0.11",
|
|
30
|
-
"tldts": "^7.0.
|
|
30
|
+
"tldts": "^7.0.19"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
33
|
"check": "biome check .",
|