auth-vir 3.0.1 → 3.1.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/README.md +7 -8
- package/dist/auth-client/backend-auth.client.d.ts +23 -0
- package/dist/auth-client/backend-auth.client.js +110 -17
- package/dist/auth-client/frontend-auth.client.d.ts +4 -2
- package/dist/auth-client/frontend-auth.client.js +14 -22
- package/dist/auth.d.ts +8 -7
- package/dist/auth.js +6 -10
- package/dist/cookie.js +3 -1
- package/dist/csrf-token-store.d.ts +21 -0
- package/dist/csrf-token-store.js +35 -0
- package/dist/csrf-token.d.ts +16 -15
- package/dist/csrf-token.js +13 -10
- package/dist/generated/internal/class.js +2 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/jwt/jwt.js +16 -5
- package/dist/jwt/user-jwt.js +3 -1
- package/dist/mock-csrf-token-store.d.ts +64 -0
- package/dist/mock-csrf-token-store.js +107 -0
- package/package.json +8 -7
- package/src/auth-client/backend-auth.client.ts +169 -26
- package/src/auth-client/frontend-auth.client.ts +15 -25
- package/src/auth.ts +9 -15
- package/src/cookie.ts +3 -1
- package/src/csrf-token-store.ts +54 -0
- package/src/csrf-token.ts +25 -25
- package/src/generated/internal/class.ts +2 -2
- package/src/index.ts +2 -1
- package/src/jwt/jwt.ts +16 -5
- package/src/jwt/user-jwt.ts +3 -1
- package/src/mock-csrf-token-store.ts +141 -0
- package/dist/mock-local-storage.d.ts +0 -33
- package/dist/mock-local-storage.js +0 -56
- package/src/mock-local-storage.ts +0 -72
package/dist/csrf-token.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type PartialWithUndefined, type SelectFrom } from '@augment-vir/common';
|
|
2
2
|
import { type AnyDuration } from 'date-vir';
|
|
3
3
|
import { type RequireExactlyOne } from 'type-fest';
|
|
4
|
+
import { type CsrfTokenStore } from './csrf-token-store.js';
|
|
4
5
|
/**
|
|
5
6
|
* Shape definition for {@link CsrfToken}.
|
|
6
7
|
*
|
|
@@ -100,18 +101,18 @@ export declare function extractCsrfTokenHeader(response: Readonly<PartialWithUnd
|
|
|
100
101
|
allowedClockSkew: Readonly<AnyDuration>;
|
|
101
102
|
}>): Readonly<GetCsrfTokenResult>;
|
|
102
103
|
/**
|
|
103
|
-
* Stores the given CSRF token into
|
|
104
|
+
* Stores the given CSRF token into IndexedDB.
|
|
104
105
|
*
|
|
105
106
|
* @category Auth : Client
|
|
106
107
|
*/
|
|
107
108
|
export declare function storeCsrfToken(csrfToken: Readonly<CsrfToken>, options: Readonly<CsrfHeaderNameOption> & PartialWithUndefined<{
|
|
108
109
|
/**
|
|
109
|
-
* Allows mocking or overriding the
|
|
110
|
+
* Allows mocking or overriding the default CSRF token store.
|
|
110
111
|
*
|
|
111
|
-
* @default
|
|
112
|
+
* @default getDefaultCsrfTokenStore()
|
|
112
113
|
*/
|
|
113
|
-
|
|
114
|
-
}>): void
|
|
114
|
+
csrfTokenStore: CsrfTokenStore;
|
|
115
|
+
}>): Promise<void>;
|
|
115
116
|
/**
|
|
116
117
|
* Parse a raw CSRF token JSON string.
|
|
117
118
|
*
|
|
@@ -134,29 +135,29 @@ export declare function parseCsrfToken(value: string | undefined | null, options
|
|
|
134
135
|
*/
|
|
135
136
|
export declare function getCurrentCsrfToken(options: Readonly<CsrfHeaderNameOption> & PartialWithUndefined<{
|
|
136
137
|
/**
|
|
137
|
-
* Allows mocking or overriding the
|
|
138
|
+
* Allows mocking or overriding the default CSRF token store.
|
|
138
139
|
*
|
|
139
|
-
* @default
|
|
140
|
+
* @default getDefaultCsrfTokenStore()
|
|
140
141
|
*/
|
|
141
|
-
|
|
142
|
+
csrfTokenStore: CsrfTokenStore;
|
|
142
143
|
/**
|
|
143
144
|
* Allowed clock skew tolerance for CSRF token expiration checks.
|
|
144
145
|
*
|
|
145
146
|
* @default {minutes: 5}
|
|
146
147
|
*/
|
|
147
148
|
allowedClockSkew: Readonly<AnyDuration>;
|
|
148
|
-
}>): Readonly<GetCsrfTokenResult
|
|
149
|
+
}>): Promise<Readonly<GetCsrfTokenResult>>;
|
|
149
150
|
/**
|
|
150
|
-
* Wipes the current stored CSRF token. This should be used by client (frontend) code to
|
|
151
|
-
*
|
|
151
|
+
* Wipes the current stored CSRF token. This should be used by client (frontend) code to react to a
|
|
152
|
+
* session timeout.
|
|
152
153
|
*
|
|
153
154
|
* @category Auth : Client
|
|
154
155
|
*/
|
|
155
156
|
export declare function wipeCurrentCsrfToken(options: Readonly<CsrfHeaderNameOption> & PartialWithUndefined<{
|
|
156
157
|
/**
|
|
157
|
-
* Allows mocking or overriding the
|
|
158
|
+
* Allows mocking or overriding the default CSRF token store.
|
|
158
159
|
*
|
|
159
|
-
* @default
|
|
160
|
+
* @default getDefaultCsrfTokenStore()
|
|
160
161
|
*/
|
|
161
|
-
|
|
162
|
-
}>): void
|
|
162
|
+
csrfTokenStore: CsrfTokenStore;
|
|
163
|
+
}>): Promise<void>;
|
package/dist/csrf-token.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomString, wrapInTry, } from '@augment-vir/common';
|
|
2
2
|
import { calculateRelativeDate, fullDateShape, getNowInUtcTimezone, isDateAfter, } from 'date-vir';
|
|
3
3
|
import { defineShape, parseJsonWithShape } from 'object-shape-tester';
|
|
4
|
+
import { getDefaultCsrfTokenStore } from './csrf-token-store.js';
|
|
4
5
|
/**
|
|
5
6
|
* Shape definition for {@link CsrfToken}.
|
|
6
7
|
*
|
|
@@ -17,7 +18,9 @@ export const csrfTokenShape = defineShape({
|
|
|
17
18
|
* @category Internal
|
|
18
19
|
* @default {minutes: 5}
|
|
19
20
|
*/
|
|
20
|
-
export const defaultAllowedClockSkew = {
|
|
21
|
+
export const defaultAllowedClockSkew = {
|
|
22
|
+
minutes: 5,
|
|
23
|
+
};
|
|
21
24
|
/**
|
|
22
25
|
* Generates a random, cryptographically secure CSRF token.
|
|
23
26
|
*
|
|
@@ -74,12 +77,12 @@ export function extractCsrfTokenHeader(response, csrfHeaderNameOption, options)
|
|
|
74
77
|
return parseCsrfToken(rawCsrfToken, options);
|
|
75
78
|
}
|
|
76
79
|
/**
|
|
77
|
-
* Stores the given CSRF token into
|
|
80
|
+
* Stores the given CSRF token into IndexedDB.
|
|
78
81
|
*
|
|
79
82
|
* @category Auth : Client
|
|
80
83
|
*/
|
|
81
|
-
export function storeCsrfToken(csrfToken, options) {
|
|
82
|
-
(options.
|
|
84
|
+
export async function storeCsrfToken(csrfToken, options) {
|
|
85
|
+
await (options.csrfTokenStore || (await getDefaultCsrfTokenStore())).setCsrfToken(JSON.stringify(csrfToken));
|
|
83
86
|
}
|
|
84
87
|
/**
|
|
85
88
|
* Parse a raw CSRF token JSON string.
|
|
@@ -122,17 +125,17 @@ export function parseCsrfToken(value, options) {
|
|
|
122
125
|
*
|
|
123
126
|
* @category Auth : Client
|
|
124
127
|
*/
|
|
125
|
-
export function getCurrentCsrfToken(options) {
|
|
126
|
-
const rawCsrfToken = (options.
|
|
128
|
+
export async function getCurrentCsrfToken(options) {
|
|
129
|
+
const rawCsrfToken = (await (options.csrfTokenStore || (await getDefaultCsrfTokenStore())).getCsrfToken()) ||
|
|
127
130
|
undefined;
|
|
128
131
|
return parseCsrfToken(rawCsrfToken, options);
|
|
129
132
|
}
|
|
130
133
|
/**
|
|
131
|
-
* Wipes the current stored CSRF token. This should be used by client (frontend) code to
|
|
132
|
-
*
|
|
134
|
+
* Wipes the current stored CSRF token. This should be used by client (frontend) code to react to a
|
|
135
|
+
* session timeout.
|
|
133
136
|
*
|
|
134
137
|
* @category Auth : Client
|
|
135
138
|
*/
|
|
136
|
-
export function wipeCurrentCsrfToken(options) {
|
|
137
|
-
|
|
139
|
+
export async function wipeCurrentCsrfToken(options) {
|
|
140
|
+
await (options.csrfTokenStore || (await getDefaultCsrfTokenStore())).deleteCsrfToken();
|
|
138
141
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,10 +3,11 @@ export * from './auth-client/frontend-auth.client.js';
|
|
|
3
3
|
export * from './auth-client/is-session-refresh-ready.js';
|
|
4
4
|
export * from './auth.js';
|
|
5
5
|
export * from './cookie.js';
|
|
6
|
+
export * from './csrf-token-store.js';
|
|
6
7
|
export * from './csrf-token.js';
|
|
7
8
|
export * from './hash.js';
|
|
8
9
|
export * from './headers.js';
|
|
9
10
|
export * from './jwt/jwt-keys.js';
|
|
10
11
|
export * from './jwt/jwt.js';
|
|
11
12
|
export * from './jwt/user-jwt.js';
|
|
12
|
-
export * from './mock-
|
|
13
|
+
export * from './mock-csrf-token-store.js';
|
package/dist/index.js
CHANGED
|
@@ -3,10 +3,11 @@ export * from './auth-client/frontend-auth.client.js';
|
|
|
3
3
|
export * from './auth-client/is-session-refresh-ready.js';
|
|
4
4
|
export * from './auth.js';
|
|
5
5
|
export * from './cookie.js';
|
|
6
|
+
export * from './csrf-token-store.js';
|
|
6
7
|
export * from './csrf-token.js';
|
|
7
8
|
export * from './hash.js';
|
|
8
9
|
export * from './headers.js';
|
|
9
10
|
export * from './jwt/jwt-keys.js';
|
|
10
11
|
export * from './jwt/jwt.js';
|
|
11
12
|
export * from './jwt/user-jwt.js';
|
|
12
|
-
export * from './mock-
|
|
13
|
+
export * from './mock-csrf-token-store.js';
|
package/dist/jwt/jwt.js
CHANGED
|
@@ -2,8 +2,13 @@ import { assertWrap, check } from '@augment-vir/assert';
|
|
|
2
2
|
import { calculateRelativeDate, convertDuration, createFullDateInUserTimezone, createUtcFullDate, getNowInUtcTimezone, toTimestamp, } from 'date-vir';
|
|
3
3
|
import { EncryptJWT, jwtDecrypt, jwtVerify, SignJWT } from 'jose';
|
|
4
4
|
import { defaultAllowedClockSkew } from '../csrf-token.js';
|
|
5
|
-
const encryptionProtectedHeader = {
|
|
6
|
-
|
|
5
|
+
const encryptionProtectedHeader = {
|
|
6
|
+
alg: 'dir',
|
|
7
|
+
enc: 'A256GCM',
|
|
8
|
+
};
|
|
9
|
+
const signingProtectedHeader = {
|
|
10
|
+
alg: 'HS512',
|
|
11
|
+
};
|
|
7
12
|
/**
|
|
8
13
|
* JWT uses seconds since the epoch per RFC 7519, whereas `toTimestamp` uses milliseconds.
|
|
9
14
|
*
|
|
@@ -28,7 +33,9 @@ export function parseJwtTimestamp(seconds) {
|
|
|
28
33
|
export async function createJwt(
|
|
29
34
|
/** The data to be included in the JWT. */
|
|
30
35
|
data, params) {
|
|
31
|
-
const rawJwt = new SignJWT({
|
|
36
|
+
const rawJwt = new SignJWT({
|
|
37
|
+
data,
|
|
38
|
+
})
|
|
32
39
|
.setProtectedHeader(signingProtectedHeader)
|
|
33
40
|
.setIssuedAt(params.issuedAt
|
|
34
41
|
? toJwtTimestamp(createFullDateInUserTimezone(params.issuedAt))
|
|
@@ -40,7 +47,9 @@ data, params) {
|
|
|
40
47
|
rawJwt.setNotBefore(toJwtTimestamp(createFullDateInUserTimezone(params.notValidUntil)));
|
|
41
48
|
}
|
|
42
49
|
const signedJwt = await rawJwt.sign(params.jwtKeys.signingKey);
|
|
43
|
-
return await new EncryptJWT({
|
|
50
|
+
return await new EncryptJWT({
|
|
51
|
+
jwt: signedJwt,
|
|
52
|
+
})
|
|
44
53
|
.setProtectedHeader(encryptionProtectedHeader)
|
|
45
54
|
.encrypt(params.jwtKeys.encryptionKey);
|
|
46
55
|
}
|
|
@@ -58,7 +67,9 @@ export async function parseJwt(encryptedJwt, params) {
|
|
|
58
67
|
else if (!check.isString(decryptedJwt.payload.jwt)) {
|
|
59
68
|
throw new TypeError('Decrypted jwt is not a string.');
|
|
60
69
|
}
|
|
61
|
-
const clockToleranceSeconds = convertDuration(params.allowedClockSkew || defaultAllowedClockSkew, {
|
|
70
|
+
const clockToleranceSeconds = convertDuration(params.allowedClockSkew || defaultAllowedClockSkew, {
|
|
71
|
+
seconds: true,
|
|
72
|
+
}).seconds;
|
|
62
73
|
const verifiedJwt = await jwtVerify(decryptedJwt.payload.jwt, params.jwtKeys.signingKey, {
|
|
63
74
|
issuer: params.issuer,
|
|
64
75
|
audience: params.audience,
|
package/dist/jwt/user-jwt.js
CHANGED
|
@@ -19,7 +19,9 @@ export const userJwtDataShape = defineShape({
|
|
|
19
19
|
* enforce the max session duration. If not present, the session is considered to have started
|
|
20
20
|
* when the JWT was issued.
|
|
21
21
|
*/
|
|
22
|
-
sessionStartedAt: optionalShape(0, {
|
|
22
|
+
sessionStartedAt: optionalShape(0, {
|
|
23
|
+
alsoUndefined: true,
|
|
24
|
+
}),
|
|
23
25
|
});
|
|
24
26
|
/**
|
|
25
27
|
* Creates a new signed and encrypted {@link JwtUserData} when a client (frontend) successfully
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type CsrfTokenStore } from './csrf-token-store.js';
|
|
2
|
+
/**
|
|
3
|
+
* `accessRecord` type for {@link createMockLocalStorage}'s output.
|
|
4
|
+
*
|
|
5
|
+
* @category Internal
|
|
6
|
+
*/
|
|
7
|
+
export type MockLocalStorageAccessRecord = {
|
|
8
|
+
getItem: string[];
|
|
9
|
+
removeItem: string[];
|
|
10
|
+
setItem: {
|
|
11
|
+
key: string;
|
|
12
|
+
value: string;
|
|
13
|
+
}[];
|
|
14
|
+
key: number[];
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Create an empty `accessRecord` object, this is to be used in conjunction with
|
|
18
|
+
* {@link createMockLocalStorage}.
|
|
19
|
+
*
|
|
20
|
+
* @category Mock
|
|
21
|
+
*/
|
|
22
|
+
export declare function createEmptyMockLocalStorageAccessRecord(): MockLocalStorageAccessRecord;
|
|
23
|
+
/**
|
|
24
|
+
* Create a LocalStorage mock.
|
|
25
|
+
*
|
|
26
|
+
* @category Mock
|
|
27
|
+
*/
|
|
28
|
+
export declare function createMockLocalStorage(
|
|
29
|
+
/** Set values in here to initialize the mocked localStorage data store contents. */
|
|
30
|
+
init?: Record<string, string>): {
|
|
31
|
+
localStorage: Storage;
|
|
32
|
+
store: Record<string, string>;
|
|
33
|
+
accessRecord: MockLocalStorageAccessRecord;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* `accessRecord` type for {@link createMockCsrfTokenStore}'s output.
|
|
37
|
+
*
|
|
38
|
+
* @category Internal
|
|
39
|
+
*/
|
|
40
|
+
export type MockCsrfTokenStoreAccessRecord = {
|
|
41
|
+
getCsrfToken: number;
|
|
42
|
+
setCsrfToken: string[];
|
|
43
|
+
deleteCsrfToken: number;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Create an empty `accessRecord` object, this is to be used in conjunction with
|
|
47
|
+
* {@link createMockCsrfTokenStore}.
|
|
48
|
+
*
|
|
49
|
+
* @category Mock
|
|
50
|
+
*/
|
|
51
|
+
export declare function createEmptyMockCsrfTokenStoreAccessRecord(): MockCsrfTokenStoreAccessRecord;
|
|
52
|
+
/**
|
|
53
|
+
* Create a mock {@link CsrfTokenStore} backed by a simple in-memory object, for use in tests.
|
|
54
|
+
*
|
|
55
|
+
* @category Mock
|
|
56
|
+
*/
|
|
57
|
+
export declare function createMockCsrfTokenStore(
|
|
58
|
+
/** Set an initial value to initialize the mocked store contents. */
|
|
59
|
+
init?: string | undefined): {
|
|
60
|
+
csrfTokenStore: CsrfTokenStore;
|
|
61
|
+
/** The current value held in the mock store. */
|
|
62
|
+
readonly storedValue: string | undefined;
|
|
63
|
+
accessRecord: MockCsrfTokenStoreAccessRecord;
|
|
64
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create an empty `accessRecord` object, this is to be used in conjunction with
|
|
3
|
+
* {@link createMockLocalStorage}.
|
|
4
|
+
*
|
|
5
|
+
* @category Mock
|
|
6
|
+
*/
|
|
7
|
+
export function createEmptyMockLocalStorageAccessRecord() {
|
|
8
|
+
return {
|
|
9
|
+
getItem: [],
|
|
10
|
+
removeItem: [],
|
|
11
|
+
setItem: [],
|
|
12
|
+
key: [],
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create a LocalStorage mock.
|
|
17
|
+
*
|
|
18
|
+
* @category Mock
|
|
19
|
+
*/
|
|
20
|
+
export function createMockLocalStorage(
|
|
21
|
+
/** Set values in here to initialize the mocked localStorage data store contents. */
|
|
22
|
+
init = {}) {
|
|
23
|
+
const store = init;
|
|
24
|
+
const accessRecord = createEmptyMockLocalStorageAccessRecord();
|
|
25
|
+
const mockLocalStorage = {
|
|
26
|
+
clear() {
|
|
27
|
+
Object.keys(store).forEach((key) => {
|
|
28
|
+
delete store[key];
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
getItem(key) {
|
|
32
|
+
accessRecord.getItem.push(key);
|
|
33
|
+
return store[key] ?? null;
|
|
34
|
+
},
|
|
35
|
+
get length() {
|
|
36
|
+
return Object.keys(store).length;
|
|
37
|
+
},
|
|
38
|
+
key(index) {
|
|
39
|
+
accessRecord.key.push(index);
|
|
40
|
+
return Object.keys(store)[index] ?? null;
|
|
41
|
+
},
|
|
42
|
+
removeItem(key) {
|
|
43
|
+
accessRecord.removeItem.push(key);
|
|
44
|
+
delete store[key];
|
|
45
|
+
},
|
|
46
|
+
setItem(key, value) {
|
|
47
|
+
accessRecord.setItem.push({
|
|
48
|
+
key,
|
|
49
|
+
value,
|
|
50
|
+
});
|
|
51
|
+
store[key] = value;
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
return {
|
|
55
|
+
localStorage: mockLocalStorage,
|
|
56
|
+
store,
|
|
57
|
+
accessRecord,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Create an empty `accessRecord` object, this is to be used in conjunction with
|
|
62
|
+
* {@link createMockCsrfTokenStore}.
|
|
63
|
+
*
|
|
64
|
+
* @category Mock
|
|
65
|
+
*/
|
|
66
|
+
export function createEmptyMockCsrfTokenStoreAccessRecord() {
|
|
67
|
+
return {
|
|
68
|
+
getCsrfToken: 0,
|
|
69
|
+
setCsrfToken: [],
|
|
70
|
+
deleteCsrfToken: 0,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Create a mock {@link CsrfTokenStore} backed by a simple in-memory object, for use in tests.
|
|
75
|
+
*
|
|
76
|
+
* @category Mock
|
|
77
|
+
*/
|
|
78
|
+
export function createMockCsrfTokenStore(
|
|
79
|
+
/** Set an initial value to initialize the mocked store contents. */
|
|
80
|
+
init) {
|
|
81
|
+
let storedValue = init;
|
|
82
|
+
const accessRecord = createEmptyMockCsrfTokenStoreAccessRecord();
|
|
83
|
+
const csrfTokenStore = {
|
|
84
|
+
getCsrfToken() {
|
|
85
|
+
accessRecord.getCsrfToken++;
|
|
86
|
+
return Promise.resolve(storedValue);
|
|
87
|
+
},
|
|
88
|
+
setCsrfToken(value) {
|
|
89
|
+
accessRecord.setCsrfToken.push(value);
|
|
90
|
+
storedValue = value;
|
|
91
|
+
return Promise.resolve();
|
|
92
|
+
},
|
|
93
|
+
deleteCsrfToken() {
|
|
94
|
+
accessRecord.deleteCsrfToken++;
|
|
95
|
+
storedValue = undefined;
|
|
96
|
+
return Promise.resolve();
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
return {
|
|
100
|
+
csrfTokenStore,
|
|
101
|
+
/** The current value held in the mock store. */
|
|
102
|
+
get storedValue() {
|
|
103
|
+
return storedValue;
|
|
104
|
+
},
|
|
105
|
+
accessRecord,
|
|
106
|
+
};
|
|
107
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "auth-vir",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "Auth made easy and secure via JWT cookies, CSRF tokens, and password hashing helpers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"auth",
|
|
@@ -41,20 +41,21 @@
|
|
|
41
41
|
"test:web": "virmator test web"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@augment-vir/assert": "^31.
|
|
45
|
-
"@augment-vir/common": "^31.
|
|
46
|
-
"date-vir": "^8.
|
|
44
|
+
"@augment-vir/assert": "^31.68.1",
|
|
45
|
+
"@augment-vir/common": "^31.68.1",
|
|
46
|
+
"date-vir": "^8.2.1",
|
|
47
47
|
"detect-activity": "^1.0.0",
|
|
48
48
|
"hash-wasm": "^4.12.0",
|
|
49
|
-
"jose": "^6.1
|
|
49
|
+
"jose": "^6.2.1",
|
|
50
|
+
"local-db-client": "^1.0.0",
|
|
50
51
|
"object-shape-tester": "^6.11.0",
|
|
51
52
|
"type-fest": "^5.4.4",
|
|
52
53
|
"url-vir": "^2.1.7"
|
|
53
54
|
},
|
|
54
55
|
"devDependencies": {
|
|
55
|
-
"@augment-vir/test": "^31.
|
|
56
|
+
"@augment-vir/test": "^31.68.1",
|
|
56
57
|
"@prisma/client": "^6.19.2",
|
|
57
|
-
"@types/node": "^25.3.
|
|
58
|
+
"@types/node": "^25.3.3",
|
|
58
59
|
"@web/dev-server-esbuild": "^1.0.5",
|
|
59
60
|
"@web/test-runner": "^0.20.2",
|
|
60
61
|
"@web/test-runner-commands": "^0.9.0",
|