@technomoron/api-server-base 2.0.0-beta.19 → 2.0.0-beta.20
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/dist/cjs/api-server-base.cjs +48 -21
- package/dist/cjs/auth-api/auth-module.js +16 -30
- package/dist/cjs/auth-api/mem-auth-store.js +5 -4
- package/dist/cjs/auth-api/sql-auth-store.js +6 -4
- package/dist/cjs/auth-api/user-id.d.ts +1 -0
- package/dist/cjs/auth-api/user-id.js +7 -0
- package/dist/cjs/auth-cookie-options.js +10 -1
- package/dist/cjs/oauth/memory.d.ts +6 -0
- package/dist/cjs/oauth/memory.js +43 -4
- package/dist/cjs/oauth/models.d.ts +1 -0
- package/dist/cjs/oauth/models.js +7 -18
- package/dist/cjs/oauth/sequelize.d.ts +5 -52
- package/dist/cjs/oauth/sequelize.js +34 -93
- package/dist/cjs/oauth/types.d.ts +1 -0
- package/dist/cjs/passkey/base.d.ts +1 -0
- package/dist/cjs/passkey/memory.d.ts +7 -0
- package/dist/cjs/passkey/memory.js +47 -5
- package/dist/cjs/passkey/models.js +2 -5
- package/dist/cjs/passkey/sequelize.d.ts +5 -29
- package/dist/cjs/passkey/sequelize.js +48 -191
- package/dist/cjs/passkey/service.d.ts +1 -0
- package/dist/cjs/passkey/service.js +52 -15
- package/dist/cjs/passkey/types.d.ts +1 -0
- package/dist/cjs/sequelize-utils.d.ts +5 -0
- package/dist/cjs/sequelize-utils.js +40 -0
- package/dist/cjs/token/base.js +3 -1
- package/dist/cjs/token/memory.d.ts +6 -0
- package/dist/cjs/token/memory.js +32 -7
- package/dist/cjs/token/sequelize.d.ts +0 -3
- package/dist/cjs/token/sequelize.js +50 -81
- package/dist/cjs/token/types.d.ts +1 -1
- package/dist/cjs/user/base.d.ts +1 -0
- package/dist/cjs/user/base.js +11 -4
- package/dist/cjs/user/memory.d.ts +2 -0
- package/dist/cjs/user/memory.js +8 -2
- package/dist/cjs/user/sequelize.js +12 -22
- package/dist/esm/api-server-base.js +48 -21
- package/dist/esm/auth-api/auth-module.js +16 -30
- package/dist/esm/auth-api/mem-auth-store.js +5 -4
- package/dist/esm/auth-api/sql-auth-store.js +6 -4
- package/dist/esm/auth-api/user-id.d.ts +1 -0
- package/dist/esm/auth-api/user-id.js +6 -0
- package/dist/esm/auth-cookie-options.js +10 -1
- package/dist/esm/oauth/memory.d.ts +6 -0
- package/dist/esm/oauth/memory.js +44 -5
- package/dist/esm/oauth/models.d.ts +1 -0
- package/dist/esm/oauth/models.js +2 -15
- package/dist/esm/oauth/sequelize.d.ts +5 -52
- package/dist/esm/oauth/sequelize.js +21 -80
- package/dist/esm/oauth/types.d.ts +1 -0
- package/dist/esm/passkey/base.d.ts +1 -0
- package/dist/esm/passkey/memory.d.ts +7 -0
- package/dist/esm/passkey/memory.js +47 -5
- package/dist/esm/passkey/models.js +1 -4
- package/dist/esm/passkey/sequelize.d.ts +5 -29
- package/dist/esm/passkey/sequelize.js +47 -190
- package/dist/esm/passkey/service.d.ts +1 -0
- package/dist/esm/passkey/service.js +52 -15
- package/dist/esm/passkey/types.d.ts +1 -0
- package/dist/esm/sequelize-utils.d.ts +5 -0
- package/dist/esm/sequelize-utils.js +36 -0
- package/dist/esm/token/base.js +3 -1
- package/dist/esm/token/memory.d.ts +6 -0
- package/dist/esm/token/memory.js +32 -7
- package/dist/esm/token/sequelize.d.ts +0 -3
- package/dist/esm/token/sequelize.js +51 -82
- package/dist/esm/token/types.d.ts +1 -1
- package/dist/esm/user/base.d.ts +1 -0
- package/dist/esm/user/base.js +11 -4
- package/dist/esm/user/memory.d.ts +2 -0
- package/dist/esm/user/memory.js +8 -2
- package/dist/esm/user/sequelize.js +13 -23
- package/package.json +5 -5
|
@@ -1,21 +1,9 @@
|
|
|
1
1
|
import { DataTypes, Model, Op } from 'sequelize';
|
|
2
2
|
import { normalizeStringUserId } from '../auth-api/user-id.js';
|
|
3
|
-
import {
|
|
3
|
+
import { applyTablePrefix, decodeStringArray, encodeStringArray, integerIdType, tableOptions } from '../sequelize-utils.js';
|
|
4
4
|
import { TokenStore } from './base.js';
|
|
5
5
|
class TokenModel extends Model {
|
|
6
6
|
}
|
|
7
|
-
function tokenTableOptions(sequelize, tablePrefix) {
|
|
8
|
-
const opts = {
|
|
9
|
-
sequelize,
|
|
10
|
-
tableName: applyTablePrefix(tablePrefix, 'jwttokens'),
|
|
11
|
-
timestamps: false
|
|
12
|
-
};
|
|
13
|
-
if (DIALECTS_SUPPORTING_UNSIGNED.has(sequelize.getDialect())) {
|
|
14
|
-
opts.charset = 'utf8mb4';
|
|
15
|
-
opts.collate = 'utf8mb4_unicode_ci';
|
|
16
|
-
}
|
|
17
|
-
return opts;
|
|
18
|
-
}
|
|
19
7
|
function initTokenModel(sequelize, options = {}) {
|
|
20
8
|
const tableName = applyTablePrefix(options.tablePrefix, 'jwttokens');
|
|
21
9
|
const usePrefixedIndexNames = tableName !== 'jwttokens';
|
|
@@ -23,9 +11,7 @@ function initTokenModel(sequelize, options = {}) {
|
|
|
23
11
|
const refreshIndexName = usePrefixedIndexNames ? `${tableName}_refresh_unique` : 'jwt_refresh_unique';
|
|
24
12
|
TokenModel.init({
|
|
25
13
|
token_id: {
|
|
26
|
-
type:
|
|
27
|
-
? DataTypes.INTEGER.UNSIGNED
|
|
28
|
-
: DataTypes.INTEGER,
|
|
14
|
+
type: integerIdType(sequelize),
|
|
29
15
|
autoIncrement: true,
|
|
30
16
|
allowNull: false,
|
|
31
17
|
primaryKey: true
|
|
@@ -122,7 +108,7 @@ function initTokenModel(sequelize, options = {}) {
|
|
|
122
108
|
defaultValue: '[]'
|
|
123
109
|
}
|
|
124
110
|
}, {
|
|
125
|
-
...
|
|
111
|
+
...tableOptions(sequelize, 'jwttokens', options.tablePrefix, { timestamps: false }),
|
|
126
112
|
indexes: [
|
|
127
113
|
{ name: accessIndexName, unique: true, fields: ['access'] },
|
|
128
114
|
{ name: refreshIndexName, unique: true, fields: ['refresh'] }
|
|
@@ -161,43 +147,50 @@ export class SequelizeTokenStore extends TokenStore {
|
|
|
161
147
|
const lastSeenAt = normalized.lastSeenAt ?? issuedAt;
|
|
162
148
|
const sessionCookie = normalized.sessionCookie ?? false;
|
|
163
149
|
const removalWhere = { user_id: resolvedUserId };
|
|
164
|
-
if (
|
|
150
|
+
if (record.domain !== undefined) {
|
|
165
151
|
removalWhere.domain = domain;
|
|
166
152
|
}
|
|
167
|
-
if (
|
|
153
|
+
if (record.fingerprint !== undefined) {
|
|
168
154
|
removalWhere.fingerprint = fingerprint;
|
|
169
155
|
}
|
|
170
156
|
if (normalized.clientId) {
|
|
171
157
|
removalWhere.client_id = normalized.clientId;
|
|
172
158
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
159
|
+
const sequelize = this.Tokens.sequelize;
|
|
160
|
+
if (!sequelize) {
|
|
161
|
+
throw new Error('Token model is not bound to a Sequelize instance');
|
|
162
|
+
}
|
|
163
|
+
await sequelize.transaction(async (transaction) => {
|
|
164
|
+
await this.Tokens.destroy({ where: removalWhere, transaction });
|
|
165
|
+
// Access/refresh columns are unique. Remove stale collisions before insert to avoid
|
|
166
|
+
// transient uniqueness failures during retries/rotation edge-cases.
|
|
167
|
+
await this.Tokens.destroy({
|
|
168
|
+
where: {
|
|
169
|
+
[Op.or]: [{ access: normalized.accessToken ?? '' }, { refresh: normalized.refreshToken }]
|
|
170
|
+
},
|
|
171
|
+
transaction
|
|
172
|
+
});
|
|
173
|
+
await this.Tokens.create({
|
|
174
|
+
user_id: resolvedUserId,
|
|
175
|
+
real_user_id: resolvedRealUserId,
|
|
176
|
+
access: normalized.accessToken ?? '',
|
|
177
|
+
refresh: normalized.refreshToken,
|
|
178
|
+
expires: normalized.expires,
|
|
179
|
+
issued_at: issuedAt,
|
|
180
|
+
last_seen_at: lastSeenAt,
|
|
181
|
+
domain,
|
|
182
|
+
fingerprint,
|
|
183
|
+
label,
|
|
184
|
+
browser,
|
|
185
|
+
device,
|
|
186
|
+
ip,
|
|
187
|
+
os,
|
|
188
|
+
client_id: normalized.clientId ?? null,
|
|
189
|
+
scope: this.encodeScope(normalized.scope),
|
|
190
|
+
login_type: loginType,
|
|
191
|
+
refresh_ttl_seconds: refreshTtlSeconds,
|
|
192
|
+
session_cookie: sessionCookie
|
|
193
|
+
}, { transaction });
|
|
201
194
|
});
|
|
202
195
|
}
|
|
203
196
|
async get(query, opts) {
|
|
@@ -266,11 +259,11 @@ export class SequelizeTokenStore extends TokenStore {
|
|
|
266
259
|
where.client_id = params.clientId;
|
|
267
260
|
}
|
|
268
261
|
const updates = {};
|
|
269
|
-
if (params.accessToken !== undefined) {
|
|
270
|
-
updates.access = params.accessToken
|
|
262
|
+
if (params.accessToken !== undefined && params.accessToken !== null) {
|
|
263
|
+
updates.access = params.accessToken;
|
|
271
264
|
}
|
|
272
|
-
if (params.expires !== undefined) {
|
|
273
|
-
updates.expires = params.expires
|
|
265
|
+
if (params.expires !== undefined && params.expires !== null) {
|
|
266
|
+
updates.expires = params.expires;
|
|
274
267
|
}
|
|
275
268
|
if (params.scope !== undefined) {
|
|
276
269
|
updates.scope = this.encodeScope(params.scope);
|
|
@@ -308,11 +301,11 @@ export class SequelizeTokenStore extends TokenStore {
|
|
|
308
301
|
if (params.sessionCookie !== undefined) {
|
|
309
302
|
updates.session_cookie = params.sessionCookie;
|
|
310
303
|
}
|
|
311
|
-
if (params.issuedAt !== undefined) {
|
|
312
|
-
updates.issued_at = params.issuedAt
|
|
304
|
+
if (params.issuedAt !== undefined && params.issuedAt !== null) {
|
|
305
|
+
updates.issued_at = params.issuedAt;
|
|
313
306
|
}
|
|
314
|
-
if (params.lastSeenAt !== undefined) {
|
|
315
|
-
updates.last_seen_at = params.lastSeenAt
|
|
307
|
+
if (params.lastSeenAt !== undefined && params.lastSeenAt !== null) {
|
|
308
|
+
updates.last_seen_at = params.lastSeenAt;
|
|
316
309
|
}
|
|
317
310
|
if (Object.keys(updates).length === 0) {
|
|
318
311
|
return false;
|
|
@@ -349,41 +342,17 @@ export class SequelizeTokenStore extends TokenStore {
|
|
|
349
342
|
}
|
|
350
343
|
return value;
|
|
351
344
|
}
|
|
352
|
-
encodeStringArray(values) {
|
|
353
|
-
return JSON.stringify(values ?? []);
|
|
354
|
-
}
|
|
355
|
-
decodeStringArray(raw) {
|
|
356
|
-
if (!raw) {
|
|
357
|
-
return [];
|
|
358
|
-
}
|
|
359
|
-
try {
|
|
360
|
-
const parsed = JSON.parse(raw);
|
|
361
|
-
if (Array.isArray(parsed)) {
|
|
362
|
-
return parsed.filter((entry) => typeof entry === 'string' && entry.length > 0);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
catch {
|
|
366
|
-
// ignore malformed values
|
|
367
|
-
}
|
|
368
|
-
return raw
|
|
369
|
-
.split(/\s+/)
|
|
370
|
-
.map((entry) => entry.trim())
|
|
371
|
-
.filter((entry) => entry.length > 0);
|
|
372
|
-
}
|
|
373
345
|
encodeScope(scope) {
|
|
374
346
|
if (!scope || (Array.isArray(scope) && scope.length === 0)) {
|
|
375
347
|
return '[]';
|
|
376
348
|
}
|
|
377
349
|
if (Array.isArray(scope)) {
|
|
378
|
-
return
|
|
350
|
+
return encodeStringArray(scope);
|
|
379
351
|
}
|
|
380
|
-
return
|
|
381
|
-
}
|
|
382
|
-
decodeScope(raw) {
|
|
383
|
-
return this.decodeStringArray(raw);
|
|
352
|
+
return encodeStringArray(scope.split(/\s+/).filter((entry) => entry.length > 0));
|
|
384
353
|
}
|
|
385
354
|
toTokenRecord(model) {
|
|
386
|
-
const scope =
|
|
355
|
+
const scope = decodeStringArray(model.scope);
|
|
387
356
|
const normalized = this.normalizeToken({
|
|
388
357
|
userId: model.user_id,
|
|
389
358
|
refreshToken: model.refresh,
|
package/dist/esm/user/base.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export declare abstract class UserStore<User, PublicUser> {
|
|
|
9
9
|
bcryptRounds?: number;
|
|
10
10
|
bcryptPepper?: string;
|
|
11
11
|
});
|
|
12
|
+
private applyPepper;
|
|
12
13
|
protected hashPassword(plain: string): Promise<string>;
|
|
13
14
|
verifyPassword(plain: string, hashed: string): Promise<boolean>;
|
|
14
15
|
protected normalizeUserInput(input: Partial<CreateUserInput>): CreateUserInput;
|
package/dist/esm/user/base.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHmac } from 'node:crypto';
|
|
1
2
|
import bcrypt from 'bcryptjs';
|
|
2
3
|
export class UserStore {
|
|
3
4
|
constructor(opts = {}) {
|
|
@@ -9,12 +10,18 @@ export class UserStore {
|
|
|
9
10
|
this.bcryptPepper =
|
|
10
11
|
typeof opts.bcryptPepper === 'string' && opts.bcryptPepper.length > 0 ? opts.bcryptPepper : undefined;
|
|
11
12
|
}
|
|
13
|
+
applyPepper(plain) {
|
|
14
|
+
if (!this.bcryptPepper) {
|
|
15
|
+
return plain;
|
|
16
|
+
}
|
|
17
|
+
return createHmac('sha256', this.bcryptPepper).update(plain).digest('hex');
|
|
18
|
+
}
|
|
12
19
|
async hashPassword(plain) {
|
|
13
|
-
const candidate = this.
|
|
20
|
+
const candidate = this.applyPepper(plain);
|
|
14
21
|
return bcrypt.hash(candidate, this.bcryptRounds);
|
|
15
22
|
}
|
|
16
23
|
async verifyPassword(plain, hashed) {
|
|
17
|
-
const candidate = this.
|
|
24
|
+
const candidate = this.applyPepper(plain);
|
|
18
25
|
return bcrypt.compare(candidate, hashed);
|
|
19
26
|
}
|
|
20
27
|
normalizeUserInput(input) {
|
|
@@ -29,8 +36,8 @@ export class UserStore {
|
|
|
29
36
|
toPublic(user) {
|
|
30
37
|
const mapped = this.toPublicUser(user);
|
|
31
38
|
if (mapped && typeof mapped === 'object') {
|
|
32
|
-
const
|
|
33
|
-
|
|
39
|
+
const rest = { ...mapped };
|
|
40
|
+
delete rest.password;
|
|
34
41
|
return rest;
|
|
35
42
|
}
|
|
36
43
|
return mapped;
|
|
@@ -14,12 +14,14 @@ export interface MemoryUserStoreOptions<UserAttributes extends MemoryUserAttribu
|
|
|
14
14
|
toPublic?: PublicUserMapper<UserAttributes, PublicUserShape>;
|
|
15
15
|
userIdFactory?: () => number;
|
|
16
16
|
startingUserId?: number;
|
|
17
|
+
maxUsers?: number;
|
|
17
18
|
}
|
|
18
19
|
export declare class MemoryUserStore<UserAttributes extends MemoryUserAttributes = MemoryUserAttributes, PublicUserShape extends Omit<UserAttributes, 'password'> = Omit<UserAttributes, 'password'>> extends UserStore<UserAttributes, PublicUserShape> {
|
|
19
20
|
private readonly usersById;
|
|
20
21
|
private readonly loginToId;
|
|
21
22
|
private readonly emailToId;
|
|
22
23
|
private readonly userIdFactory;
|
|
24
|
+
private readonly maxUsers?;
|
|
23
25
|
private nextUserId;
|
|
24
26
|
constructor(options?: MemoryUserStoreOptions<UserAttributes, PublicUserShape>);
|
|
25
27
|
findUser(identifier: AuthIdentifier | string): Promise<UserAttributes | null>;
|
package/dist/esm/user/memory.js
CHANGED
|
@@ -3,7 +3,6 @@ import { UserStore } from './base.js';
|
|
|
3
3
|
function cloneUser(user) {
|
|
4
4
|
return { ...user };
|
|
5
5
|
}
|
|
6
|
-
const normalizeUserId = normalizeNumericUserId;
|
|
7
6
|
export class MemoryUserStore extends UserStore {
|
|
8
7
|
constructor(options = {}) {
|
|
9
8
|
super({
|
|
@@ -15,6 +14,10 @@ export class MemoryUserStore extends UserStore {
|
|
|
15
14
|
this.loginToId = new Map();
|
|
16
15
|
this.emailToId = new Map();
|
|
17
16
|
this.nextUserId = Number.isFinite(options.startingUserId) ? Number(options.startingUserId) : 1;
|
|
17
|
+
this.maxUsers =
|
|
18
|
+
typeof options.maxUsers === 'number' && Number.isFinite(options.maxUsers) && options.maxUsers > 0
|
|
19
|
+
? Math.floor(options.maxUsers)
|
|
20
|
+
: undefined;
|
|
18
21
|
this.userIdFactory =
|
|
19
22
|
options.userIdFactory ??
|
|
20
23
|
(() => {
|
|
@@ -49,7 +52,7 @@ export class MemoryUserStore extends UserStore {
|
|
|
49
52
|
}
|
|
50
53
|
async findById(id) {
|
|
51
54
|
try {
|
|
52
|
-
const numeric =
|
|
55
|
+
const numeric = normalizeNumericUserId(id);
|
|
53
56
|
const user = this.usersById.get(numeric);
|
|
54
57
|
return user ? cloneUser(user) : null;
|
|
55
58
|
}
|
|
@@ -67,6 +70,9 @@ export class MemoryUserStore extends UserStore {
|
|
|
67
70
|
if (this.usersById.has(userId)) {
|
|
68
71
|
throw new Error(`User ${userId} already exists`);
|
|
69
72
|
}
|
|
73
|
+
if (this.maxUsers !== undefined && this.usersById.size >= this.maxUsers) {
|
|
74
|
+
throw new Error('MemoryUserStore maxUsers limit reached');
|
|
75
|
+
}
|
|
70
76
|
if (this.loginToId.has(normalizedInput.login)) {
|
|
71
77
|
throw new Error(`User with login ${normalizedInput.login} already exists`);
|
|
72
78
|
}
|
|
@@ -1,22 +1,7 @@
|
|
|
1
|
-
import { DataTypes, Model, Op } from 'sequelize';
|
|
1
|
+
import { DataTypes, Model, Op, UniqueConstraintError } from 'sequelize';
|
|
2
2
|
import { normalizeNumericUserId } from '../auth-api/user-id.js';
|
|
3
|
-
import {
|
|
3
|
+
import { integerIdType, tableOptions } from '../sequelize-utils.js';
|
|
4
4
|
import { UserStore } from './base.js';
|
|
5
|
-
function integerIdType(sequelize) {
|
|
6
|
-
return DIALECTS_SUPPORTING_UNSIGNED.has(sequelize.getDialect()) ? DataTypes.INTEGER.UNSIGNED : DataTypes.INTEGER;
|
|
7
|
-
}
|
|
8
|
-
function userTableOptions(sequelize, tablePrefix) {
|
|
9
|
-
const opts = {
|
|
10
|
-
sequelize,
|
|
11
|
-
tableName: applyTablePrefix(tablePrefix, 'users'),
|
|
12
|
-
timestamps: false
|
|
13
|
-
};
|
|
14
|
-
if (DIALECTS_SUPPORTING_UNSIGNED.has(sequelize.getDialect())) {
|
|
15
|
-
opts.charset = 'utf8mb4';
|
|
16
|
-
opts.collate = 'utf8mb4_unicode_ci';
|
|
17
|
-
}
|
|
18
|
-
return opts;
|
|
19
|
-
}
|
|
20
5
|
export class AuthUserModel extends Model {
|
|
21
6
|
}
|
|
22
7
|
export function initAuthUserModel(sequelize, options = {}) {
|
|
@@ -43,7 +28,7 @@ export function initAuthUserModel(sequelize, options = {}) {
|
|
|
43
28
|
allowNull: false
|
|
44
29
|
}
|
|
45
30
|
}, {
|
|
46
|
-
...
|
|
31
|
+
...tableOptions(sequelize, 'users', options.tablePrefix, { timestamps: false })
|
|
47
32
|
});
|
|
48
33
|
return AuthUserModel;
|
|
49
34
|
}
|
|
@@ -110,11 +95,16 @@ export class SequelizeUserStore extends UserStore {
|
|
|
110
95
|
if (providedId !== undefined && providedId !== null && Number.isFinite(providedId)) {
|
|
111
96
|
defaults.user_id = Number(providedId);
|
|
112
97
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
|
|
98
|
+
try {
|
|
99
|
+
const model = await this.Users.create(defaults);
|
|
100
|
+
return this.toUserRecord(model);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
if (error instanceof UniqueConstraintError) {
|
|
104
|
+
throw new Error(`User with login ${rest.login} or email ${rest.email} already exists`);
|
|
105
|
+
}
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
118
108
|
}
|
|
119
109
|
async upsertUser(input) {
|
|
120
110
|
const normalized = this.normalizeUserInput(input);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@technomoron/api-server-base",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.20",
|
|
4
4
|
"description": "Api Server Skeleton / Base Class",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/index.cjs",
|
|
@@ -68,11 +68,7 @@
|
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
70
|
"@simplewebauthn/server": "^13.2.2",
|
|
71
|
-
"@types/cookie-parser": "^1.4.10",
|
|
72
|
-
"@types/cors": "^2.8.19",
|
|
73
71
|
"@types/express": "^5.0.6",
|
|
74
|
-
"@types/jsonwebtoken": "^9.0.10",
|
|
75
|
-
"@types/multer": "^2.0.0",
|
|
76
72
|
"bcryptjs": "^3.0.3",
|
|
77
73
|
"cookie-parser": "^1.4.7",
|
|
78
74
|
"cors": "^2.8.6",
|
|
@@ -81,7 +77,11 @@
|
|
|
81
77
|
"multer": "^2.0.2"
|
|
82
78
|
},
|
|
83
79
|
"devDependencies": {
|
|
80
|
+
"@types/cookie-parser": "^1.4.10",
|
|
81
|
+
"@types/cors": "^2.8.19",
|
|
84
82
|
"@types/express-serve-static-core": "^5.1.1",
|
|
83
|
+
"@types/jsonwebtoken": "^9.0.10",
|
|
84
|
+
"@types/multer": "^2.0.0",
|
|
85
85
|
"@types/supertest": "^6.0.3",
|
|
86
86
|
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
|
87
87
|
"@typescript-eslint/parser": "^8.54.0",
|