@technomoron/api-server-base 2.0.0-beta.21 → 2.0.0-beta.23
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/common/types.cjs +10 -0
- package/dist/cjs/common/types.d.ts +137 -0
- package/dist/cjs/{api-module.cjs → server/src/api-module.cjs} +8 -0
- package/dist/{esm → cjs/server/src}/api-module.d.ts +15 -0
- package/dist/cjs/{api-server-base.cjs → server/src/api-server-base.cjs} +669 -627
- package/dist/{esm → cjs/server/src}/api-server-base.d.ts +105 -78
- package/dist/cjs/{auth-api/auth-module.js → server/src/auth-api/auth-module.cjs} +96 -76
- package/dist/cjs/{auth-api → server/src/auth-api}/auth-module.d.ts +1 -1
- package/dist/cjs/{auth-api/compat-auth-storage.js → server/src/auth-api/compat-auth-storage.cjs} +4 -4
- package/dist/cjs/{auth-api/mem-auth-store.js → server/src/auth-api/mem-auth-store.cjs} +7 -7
- package/dist/cjs/{auth-api/module.js → server/src/auth-api/module.cjs} +1 -1
- package/dist/cjs/server/src/auth-api/schemas.cjs +171 -0
- package/dist/cjs/server/src/auth-api/schemas.d.ts +21 -0
- package/dist/cjs/{auth-api/sql-auth-store.js → server/src/auth-api/sql-auth-store.cjs} +8 -8
- package/dist/cjs/{auth-api/user-id.js → server/src/auth-api/user-id.cjs} +12 -3
- package/dist/{esm → cjs/server/src}/auth-cookie-options.d.ts +5 -3
- package/dist/cjs/server/src/base/client-info.cjs +285 -0
- package/dist/cjs/server/src/base/client-info.d.ts +27 -0
- package/dist/cjs/server/src/base/error-utils.cjs +50 -0
- package/dist/cjs/server/src/base/error-utils.d.ts +16 -0
- package/dist/cjs/server/src/base/request-utils.cjs +27 -0
- package/dist/cjs/server/src/base/request-utils.d.ts +8 -0
- package/dist/cjs/{index.cjs → server/src/index.cjs} +24 -15
- package/dist/{esm → cjs/server/src}/index.d.ts +7 -0
- package/dist/cjs/server/src/limiter/auth-rate-limiter.cjs +35 -0
- package/dist/cjs/server/src/limiter/auth-rate-limiter.d.ts +12 -0
- package/dist/cjs/server/src/limiter/fixed-window.cjs +41 -0
- package/dist/cjs/server/src/limiter/fixed-window.d.ts +11 -0
- package/dist/cjs/{oauth/base.js → server/src/oauth/base.cjs} +1 -0
- package/dist/cjs/{oauth → server/src/oauth}/base.d.ts +8 -1
- package/dist/cjs/{oauth/memory.js → server/src/oauth/memory.cjs} +7 -4
- package/dist/{esm → cjs/server/src}/oauth/memory.d.ts +1 -1
- package/dist/cjs/{oauth/models.js → server/src/oauth/models.cjs} +2 -2
- package/dist/cjs/{oauth/sequelize.js → server/src/oauth/sequelize.cjs} +11 -7
- package/dist/{esm → cjs/server/src}/oauth/sequelize.d.ts +1 -1
- package/dist/cjs/{passkey/base.js → server/src/passkey/base.cjs} +1 -0
- package/dist/{esm → cjs/server/src}/passkey/base.d.ts +11 -0
- package/dist/cjs/{passkey/memory.js → server/src/passkey/memory.cjs} +2 -2
- package/dist/cjs/{passkey/models.js → server/src/passkey/models.cjs} +1 -1
- package/dist/cjs/{passkey/sequelize.js → server/src/passkey/sequelize.cjs} +3 -3
- package/dist/cjs/{passkey/service.js → server/src/passkey/service.cjs} +17 -3
- package/dist/{esm → cjs/server/src}/passkey/service.d.ts +1 -1
- package/dist/cjs/{sequelize-utils.js → server/src/sequelize-utils.cjs} +4 -5
- package/dist/cjs/{token/base.js → server/src/token/base.cjs} +4 -0
- package/dist/{esm → cjs/server/src}/token/base.d.ts +7 -0
- package/dist/cjs/{token/memory.js → server/src/token/memory.cjs} +15 -20
- package/dist/cjs/{token/sequelize.js → server/src/token/sequelize.cjs} +25 -11
- package/dist/cjs/server/src/upload/memory.cjs +92 -0
- package/dist/cjs/server/src/upload/memory.d.ts +17 -0
- package/dist/cjs/server/src/upload/tus-module.cjs +270 -0
- package/dist/cjs/server/src/upload/tus-module.d.ts +38 -0
- package/dist/cjs/server/src/upload/types.d.ts +8 -0
- package/dist/cjs/{user/base.js → server/src/user/base.cjs} +1 -0
- package/dist/cjs/{user → server/src/user}/base.d.ts +9 -0
- package/dist/cjs/{user/memory.js → server/src/user/memory.cjs} +29 -7
- package/dist/cjs/{user/sequelize.js → server/src/user/sequelize.cjs} +33 -8
- package/dist/cjs/server/src/user/types.cjs +2 -0
- package/dist/esm/common/types.d.ts +137 -0
- package/dist/esm/common/types.js +9 -0
- package/dist/{cjs → esm/server/src}/api-module.d.ts +15 -0
- package/dist/esm/{api-module.js → server/src/api-module.js} +8 -0
- package/dist/{cjs → esm/server/src}/api-server-base.d.ts +105 -78
- package/dist/esm/{api-server-base.js → server/src/api-server-base.js} +658 -616
- package/dist/esm/{auth-api → server/src/auth-api}/auth-module.d.ts +1 -1
- package/dist/esm/{auth-api → server/src/auth-api}/auth-module.js +92 -72
- package/dist/esm/{auth-api → server/src/auth-api}/compat-auth-storage.js +3 -3
- package/dist/esm/server/src/auth-api/schemas.d.ts +21 -0
- package/dist/esm/server/src/auth-api/schemas.js +168 -0
- package/dist/esm/{auth-api → server/src/auth-api}/user-id.js +12 -3
- package/dist/{cjs → esm/server/src}/auth-cookie-options.d.ts +5 -3
- package/dist/esm/server/src/base/client-info.d.ts +27 -0
- package/dist/esm/server/src/base/client-info.js +282 -0
- package/dist/esm/server/src/base/error-utils.d.ts +16 -0
- package/dist/esm/server/src/base/error-utils.js +44 -0
- package/dist/esm/server/src/base/request-utils.d.ts +8 -0
- package/dist/esm/server/src/base/request-utils.js +23 -0
- package/dist/{cjs → esm/server/src}/index.d.ts +7 -0
- package/dist/esm/{index.js → server/src/index.js} +4 -0
- package/dist/esm/server/src/limiter/auth-rate-limiter.d.ts +12 -0
- package/dist/esm/server/src/limiter/auth-rate-limiter.js +32 -0
- package/dist/esm/server/src/limiter/fixed-window.d.ts +11 -0
- package/dist/esm/server/src/limiter/fixed-window.js +37 -0
- package/dist/esm/{oauth → server/src/oauth}/base.d.ts +8 -1
- package/dist/esm/server/src/oauth/base.js +3 -0
- package/dist/{cjs → esm/server/src}/oauth/memory.d.ts +1 -1
- package/dist/esm/{oauth → server/src/oauth}/memory.js +5 -2
- package/dist/{cjs → esm/server/src}/oauth/sequelize.d.ts +1 -1
- package/dist/esm/{oauth → server/src/oauth}/sequelize.js +6 -2
- package/dist/{cjs → esm/server/src}/passkey/base.d.ts +11 -0
- package/dist/esm/server/src/passkey/base.js +3 -0
- package/dist/{cjs → esm/server/src}/passkey/service.d.ts +1 -1
- package/dist/esm/{passkey → server/src/passkey}/service.js +17 -3
- package/dist/esm/{sequelize-utils.js → server/src/sequelize-utils.js} +4 -5
- package/dist/{cjs → esm/server/src}/token/base.d.ts +7 -0
- package/dist/esm/{token → server/src/token}/base.js +4 -0
- package/dist/esm/{token → server/src/token}/memory.js +14 -19
- package/dist/esm/{token → server/src/token}/sequelize.js +22 -8
- package/dist/esm/server/src/upload/memory.d.ts +17 -0
- package/dist/esm/server/src/upload/memory.js +86 -0
- package/dist/esm/server/src/upload/tus-module.d.ts +38 -0
- package/dist/esm/server/src/upload/tus-module.js +266 -0
- package/dist/esm/server/src/upload/types.d.ts +8 -0
- package/dist/esm/{user → server/src/user}/base.d.ts +9 -0
- package/dist/esm/{user → server/src/user}/base.js +1 -0
- package/dist/esm/{user → server/src/user}/memory.js +27 -5
- package/dist/esm/{user → server/src/user}/sequelize.js +30 -5
- package/dist/esm/server/src/user/types.js +1 -0
- package/docs/swagger/openapi.json +411 -125
- package/package.json +129 -134
- package/README.txt +0 -213
- package/dist/esm/oauth/base.js +0 -2
- package/dist/esm/passkey/base.js +0 -2
- /package/dist/cjs/{auth-api → server/src/auth-api}/compat-auth-storage.d.ts +0 -0
- /package/dist/cjs/{auth-api → server/src/auth-api}/mem-auth-store.d.ts +0 -0
- /package/dist/cjs/{auth-api → server/src/auth-api}/module.d.ts +0 -0
- /package/dist/cjs/{auth-api → server/src/auth-api}/sql-auth-store.d.ts +0 -0
- /package/dist/cjs/{auth-api/storage.js → server/src/auth-api/storage.cjs} +0 -0
- /package/dist/cjs/{auth-api → server/src/auth-api}/storage.d.ts +0 -0
- /package/dist/cjs/{auth-api/types.js → server/src/auth-api/types.cjs} +0 -0
- /package/dist/cjs/{auth-api → server/src/auth-api}/types.d.ts +0 -0
- /package/dist/cjs/{auth-api → server/src/auth-api}/user-id.d.ts +0 -0
- /package/dist/cjs/{auth-cookie-options.js → server/src/auth-cookie-options.cjs} +0 -0
- /package/dist/cjs/{oauth → server/src/oauth}/models.d.ts +0 -0
- /package/dist/cjs/{oauth/types.js → server/src/oauth/types.cjs} +0 -0
- /package/dist/cjs/{oauth → server/src/oauth}/types.d.ts +0 -0
- /package/dist/cjs/{passkey/config.js → server/src/passkey/config.cjs} +0 -0
- /package/dist/cjs/{passkey → server/src/passkey}/config.d.ts +0 -0
- /package/dist/cjs/{passkey → server/src/passkey}/memory.d.ts +0 -0
- /package/dist/cjs/{passkey → server/src/passkey}/models.d.ts +0 -0
- /package/dist/cjs/{passkey → server/src/passkey}/sequelize.d.ts +0 -0
- /package/dist/cjs/{passkey/types.js → server/src/passkey/types.cjs} +0 -0
- /package/dist/cjs/{passkey → server/src/passkey}/types.d.ts +0 -0
- /package/dist/cjs/{sequelize-utils.d.ts → server/src/sequelize-utils.d.ts} +0 -0
- /package/dist/cjs/{token → server/src/token}/memory.d.ts +0 -0
- /package/dist/cjs/{token → server/src/token}/sequelize.d.ts +0 -0
- /package/dist/cjs/{token/types.js → server/src/token/types.cjs} +0 -0
- /package/dist/cjs/{token → server/src/token}/types.d.ts +0 -0
- /package/dist/cjs/{user/types.js → server/src/upload/types.cjs} +0 -0
- /package/dist/cjs/{user → server/src/user}/memory.d.ts +0 -0
- /package/dist/cjs/{user → server/src/user}/sequelize.d.ts +0 -0
- /package/dist/cjs/{user → server/src/user}/types.d.ts +0 -0
- /package/dist/esm/{auth-api → server/src/auth-api}/compat-auth-storage.d.ts +0 -0
- /package/dist/esm/{auth-api → server/src/auth-api}/mem-auth-store.d.ts +0 -0
- /package/dist/esm/{auth-api → server/src/auth-api}/mem-auth-store.js +0 -0
- /package/dist/esm/{auth-api → server/src/auth-api}/module.d.ts +0 -0
- /package/dist/esm/{auth-api → server/src/auth-api}/module.js +0 -0
- /package/dist/esm/{auth-api → server/src/auth-api}/sql-auth-store.d.ts +0 -0
- /package/dist/esm/{auth-api → server/src/auth-api}/sql-auth-store.js +0 -0
- /package/dist/esm/{auth-api → server/src/auth-api}/storage.d.ts +0 -0
- /package/dist/esm/{auth-api → server/src/auth-api}/storage.js +0 -0
- /package/dist/esm/{auth-api → server/src/auth-api}/types.d.ts +0 -0
- /package/dist/esm/{auth-api → server/src/auth-api}/types.js +0 -0
- /package/dist/esm/{auth-api → server/src/auth-api}/user-id.d.ts +0 -0
- /package/dist/esm/{auth-cookie-options.js → server/src/auth-cookie-options.js} +0 -0
- /package/dist/esm/{oauth → server/src/oauth}/models.d.ts +0 -0
- /package/dist/esm/{oauth → server/src/oauth}/models.js +0 -0
- /package/dist/esm/{oauth → server/src/oauth}/types.d.ts +0 -0
- /package/dist/esm/{oauth → server/src/oauth}/types.js +0 -0
- /package/dist/esm/{passkey → server/src/passkey}/config.d.ts +0 -0
- /package/dist/esm/{passkey → server/src/passkey}/config.js +0 -0
- /package/dist/esm/{passkey → server/src/passkey}/memory.d.ts +0 -0
- /package/dist/esm/{passkey → server/src/passkey}/memory.js +0 -0
- /package/dist/esm/{passkey → server/src/passkey}/models.d.ts +0 -0
- /package/dist/esm/{passkey → server/src/passkey}/models.js +0 -0
- /package/dist/esm/{passkey → server/src/passkey}/sequelize.d.ts +0 -0
- /package/dist/esm/{passkey → server/src/passkey}/sequelize.js +0 -0
- /package/dist/esm/{passkey → server/src/passkey}/types.d.ts +0 -0
- /package/dist/esm/{passkey → server/src/passkey}/types.js +0 -0
- /package/dist/esm/{sequelize-utils.d.ts → server/src/sequelize-utils.d.ts} +0 -0
- /package/dist/esm/{token → server/src/token}/memory.d.ts +0 -0
- /package/dist/esm/{token → server/src/token}/sequelize.d.ts +0 -0
- /package/dist/esm/{token → server/src/token}/types.d.ts +0 -0
- /package/dist/esm/{token → server/src/token}/types.js +0 -0
- /package/dist/esm/{user → server/src/upload}/types.js +0 -0
- /package/dist/esm/{user → server/src/user}/memory.d.ts +0 -0
- /package/dist/esm/{user → server/src/user}/sequelize.d.ts +0 -0
- /package/dist/esm/{user → server/src/user}/types.d.ts +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FixedWindowRateLimiter = void 0;
|
|
4
|
+
class FixedWindowRateLimiter {
|
|
5
|
+
constructor(maxKeys = 10000) {
|
|
6
|
+
this.maxKeys = maxKeys;
|
|
7
|
+
this.buckets = new Map();
|
|
8
|
+
}
|
|
9
|
+
check(key, max, windowMs) {
|
|
10
|
+
if (!key || max <= 0 || windowMs <= 0) {
|
|
11
|
+
return { allowed: true, retryAfterSec: 0 };
|
|
12
|
+
}
|
|
13
|
+
const now = Date.now();
|
|
14
|
+
const bucket = this.buckets.get(key);
|
|
15
|
+
if (!bucket || now - bucket.windowStartMs >= windowMs) {
|
|
16
|
+
this.buckets.delete(key);
|
|
17
|
+
this.buckets.set(key, { windowStartMs: now, count: 1 });
|
|
18
|
+
this.prune();
|
|
19
|
+
return { allowed: true, retryAfterSec: 0 };
|
|
20
|
+
}
|
|
21
|
+
bucket.count += 1;
|
|
22
|
+
// Refresh insertion order to keep active entries at the end for pruning.
|
|
23
|
+
this.buckets.delete(key);
|
|
24
|
+
this.buckets.set(key, bucket);
|
|
25
|
+
if (bucket.count <= max) {
|
|
26
|
+
return { allowed: true, retryAfterSec: 0 };
|
|
27
|
+
}
|
|
28
|
+
const retryAfterSec = Math.max(1, Math.ceil((bucket.windowStartMs + windowMs - now) / 1000));
|
|
29
|
+
return { allowed: false, retryAfterSec };
|
|
30
|
+
}
|
|
31
|
+
prune() {
|
|
32
|
+
while (this.buckets.size > this.maxKeys) {
|
|
33
|
+
const oldest = this.buckets.keys().next().value;
|
|
34
|
+
if (!oldest) {
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
this.buckets.delete(oldest);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.FixedWindowRateLimiter = FixedWindowRateLimiter;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type RateLimitDecision = {
|
|
2
|
+
allowed: boolean;
|
|
3
|
+
retryAfterSec: number;
|
|
4
|
+
};
|
|
5
|
+
export declare class FixedWindowRateLimiter {
|
|
6
|
+
private readonly maxKeys;
|
|
7
|
+
private readonly buckets;
|
|
8
|
+
constructor(maxKeys?: number);
|
|
9
|
+
check(key: string, max: number, windowMs: number): RateLimitDecision;
|
|
10
|
+
private prune;
|
|
11
|
+
}
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import type { AuthCode, OAuthClient } from './types.js';
|
|
2
|
+
/** Base contract for OAuth client/auth-code persistence backends. */
|
|
2
3
|
export declare abstract class OAuthStore {
|
|
4
|
+
/** Fetch an OAuth client by id. */
|
|
3
5
|
abstract getClient(clientId: string): Promise<OAuthClient | null>;
|
|
6
|
+
/** Create an OAuth client. */
|
|
4
7
|
abstract createClient(input: OAuthClient): Promise<OAuthClient>;
|
|
8
|
+
/** Verify an OAuth client secret. */
|
|
5
9
|
abstract verifyClientSecret(clientId: string, secret: string | null): Promise<boolean>;
|
|
10
|
+
/** Persist an authorization code. */
|
|
6
11
|
abstract createAuthCode(code: AuthCode): Promise<void>;
|
|
7
|
-
|
|
12
|
+
/** Consume an authorization code once. When clientId is provided, only consume if it matches. */
|
|
13
|
+
abstract consumeAuthCode(code: string, clientId?: string): Promise<AuthCode | null>;
|
|
14
|
+
/** Close underlying resources. */
|
|
8
15
|
abstract close(): Promise<void>;
|
|
9
16
|
}
|
|
10
17
|
export type { OAuthClient, AuthCode } from './types.js';
|
|
@@ -5,8 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.MemoryOAuthStore = void 0;
|
|
7
7
|
const bcryptjs_1 = __importDefault(require("bcryptjs"));
|
|
8
|
-
const user_id_js_1 = require("../auth-api/user-id.
|
|
9
|
-
const base_js_1 = require("./base.
|
|
8
|
+
const user_id_js_1 = require("../auth-api/user-id.cjs");
|
|
9
|
+
const base_js_1 = require("./base.cjs");
|
|
10
10
|
function cloneClient(client) {
|
|
11
11
|
if (!client) {
|
|
12
12
|
return null;
|
|
@@ -17,7 +17,7 @@ function cloneClient(client) {
|
|
|
17
17
|
name: client.name,
|
|
18
18
|
redirectUris: [...client.redirectUris],
|
|
19
19
|
scope: client.scope ? [...client.scope] : undefined,
|
|
20
|
-
metadata: client.metadata ?
|
|
20
|
+
metadata: client.metadata ? JSON.parse(JSON.stringify(client.metadata)) : undefined,
|
|
21
21
|
firstParty: client.firstParty
|
|
22
22
|
};
|
|
23
23
|
}
|
|
@@ -89,11 +89,14 @@ class MemoryOAuthStore extends base_js_1.OAuthStore {
|
|
|
89
89
|
this.codes.set(record.code, record);
|
|
90
90
|
this.enforceCodeCapacity();
|
|
91
91
|
}
|
|
92
|
-
async consumeAuthCode(code) {
|
|
92
|
+
async consumeAuthCode(code, clientId) {
|
|
93
93
|
const record = this.codes.get(code);
|
|
94
94
|
if (!record) {
|
|
95
95
|
return null;
|
|
96
96
|
}
|
|
97
|
+
if (clientId && record.clientId !== clientId) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
97
100
|
if (record.expiresAt.getTime() <= Date.now()) {
|
|
98
101
|
this.codes.delete(code);
|
|
99
102
|
return null;
|
|
@@ -15,7 +15,7 @@ export declare class MemoryOAuthStore extends OAuthStore {
|
|
|
15
15
|
createClient(input: OAuthClient): Promise<OAuthClient>;
|
|
16
16
|
verifyClientSecret(clientId: string, secret: string | null): Promise<boolean>;
|
|
17
17
|
createAuthCode(code: AuthCode): Promise<void>;
|
|
18
|
-
consumeAuthCode(code: string): Promise<AuthCode | null>;
|
|
18
|
+
consumeAuthCode(code: string, clientId?: string): Promise<AuthCode | null>;
|
|
19
19
|
close(): Promise<void>;
|
|
20
20
|
private enforceClientCapacity;
|
|
21
21
|
private enforceCodeCapacity;
|
|
@@ -4,8 +4,8 @@ exports.OAuthCodeModel = exports.OAuthClientModel = exports.tableOptions = expor
|
|
|
4
4
|
exports.initOAuthClientModel = initOAuthClientModel;
|
|
5
5
|
exports.initOAuthCodeModel = initOAuthCodeModel;
|
|
6
6
|
const sequelize_1 = require("sequelize");
|
|
7
|
-
const sequelize_utils_js_1 = require("../sequelize-utils.
|
|
8
|
-
var sequelize_utils_js_2 = require("../sequelize-utils.
|
|
7
|
+
const sequelize_utils_js_1 = require("../sequelize-utils.cjs");
|
|
8
|
+
var sequelize_utils_js_2 = require("../sequelize-utils.cjs");
|
|
9
9
|
Object.defineProperty(exports, "integerIdType", { enumerable: true, get: function () { return sequelize_utils_js_2.integerIdType; } });
|
|
10
10
|
Object.defineProperty(exports, "tableOptions", { enumerable: true, get: function () { return sequelize_utils_js_2.tableOptions; } });
|
|
11
11
|
class OAuthClientModel extends sequelize_1.Model {
|
|
@@ -6,11 +6,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.SequelizeOAuthStore = exports.initOAuthCodeModel = exports.initOAuthClientModel = exports.OAuthCodeModel = exports.OAuthClientModel = void 0;
|
|
7
7
|
const bcryptjs_1 = __importDefault(require("bcryptjs"));
|
|
8
8
|
const sequelize_1 = require("sequelize");
|
|
9
|
-
const user_id_js_1 = require("../auth-api/user-id.
|
|
10
|
-
const sequelize_utils_js_1 = require("../sequelize-utils.
|
|
11
|
-
const base_js_1 = require("./base.
|
|
12
|
-
const models_js_1 = require("./models.
|
|
13
|
-
var models_js_2 = require("./models.
|
|
9
|
+
const user_id_js_1 = require("../auth-api/user-id.cjs");
|
|
10
|
+
const sequelize_utils_js_1 = require("../sequelize-utils.cjs");
|
|
11
|
+
const base_js_1 = require("./base.cjs");
|
|
12
|
+
const models_js_1 = require("./models.cjs");
|
|
13
|
+
var models_js_2 = require("./models.cjs");
|
|
14
14
|
Object.defineProperty(exports, "OAuthClientModel", { enumerable: true, get: function () { return models_js_2.OAuthClientModel; } });
|
|
15
15
|
Object.defineProperty(exports, "OAuthCodeModel", { enumerable: true, get: function () { return models_js_2.OAuthCodeModel; } });
|
|
16
16
|
Object.defineProperty(exports, "initOAuthClientModel", { enumerable: true, get: function () { return models_js_2.initOAuthClientModel; } });
|
|
@@ -107,13 +107,17 @@ class SequelizeOAuthStore extends base_js_1.OAuthStore {
|
|
|
107
107
|
metadata: serializeMetadata(code.metadata)
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
|
-
async consumeAuthCode(code) {
|
|
110
|
+
async consumeAuthCode(code, clientId) {
|
|
111
111
|
const sequelize = this.codes.sequelize;
|
|
112
112
|
if (!sequelize) {
|
|
113
113
|
throw new Error('Code model is not bound to a Sequelize instance');
|
|
114
114
|
}
|
|
115
115
|
return sequelize.transaction({ isolationLevel: sequelize_1.Transaction.ISOLATION_LEVELS.READ_COMMITTED }, async (transaction) => {
|
|
116
|
-
const
|
|
116
|
+
const where = { code };
|
|
117
|
+
if (clientId) {
|
|
118
|
+
where.client_id = clientId;
|
|
119
|
+
}
|
|
120
|
+
const model = await this.codes.findOne({ where, transaction, lock: true });
|
|
117
121
|
if (!model) {
|
|
118
122
|
return null;
|
|
119
123
|
}
|
|
@@ -23,7 +23,7 @@ export declare class SequelizeOAuthStore extends OAuthStore {
|
|
|
23
23
|
createClient(input: OAuthClient): Promise<OAuthClient>;
|
|
24
24
|
verifyClientSecret(clientId: string, clientSecret: string | null): Promise<boolean>;
|
|
25
25
|
createAuthCode(code: AuthCode): Promise<void>;
|
|
26
|
-
consumeAuthCode(code: string): Promise<AuthCode | null>;
|
|
26
|
+
consumeAuthCode(code: string, clientId?: string): Promise<AuthCode | null>;
|
|
27
27
|
close(): Promise<void>;
|
|
28
28
|
private toOAuthClient;
|
|
29
29
|
private toAuthCode;
|
|
@@ -1,17 +1,28 @@
|
|
|
1
1
|
import type { PasskeyChallengeRecord, PasskeyStorageAdapter, PasskeyUserDescriptor, StoredPasskeyCredential } from './types.js';
|
|
2
2
|
import type { AuthIdentifier } from '../auth-api/types.js';
|
|
3
|
+
/** Base contract for passkey credential/challenge persistence backends. */
|
|
3
4
|
export declare abstract class PasskeyStore implements PasskeyStorageAdapter {
|
|
5
|
+
/** Resolve a passkey user descriptor by id/login. */
|
|
4
6
|
abstract resolveUser(params: {
|
|
5
7
|
userId?: AuthIdentifier;
|
|
6
8
|
login?: string;
|
|
7
9
|
}): Promise<PasskeyUserDescriptor | null>;
|
|
10
|
+
/** List passkey credentials for a user. */
|
|
8
11
|
abstract listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
|
|
12
|
+
/** Delete a credential by binary/base64url id. */
|
|
9
13
|
abstract deleteCredential(credentialId: Buffer | string): Promise<boolean>;
|
|
14
|
+
/** Find a credential by binary id. */
|
|
10
15
|
abstract findCredentialById(credentialId: Buffer): Promise<StoredPasskeyCredential | null>;
|
|
16
|
+
/** Save a credential record. */
|
|
11
17
|
abstract saveCredential(record: StoredPasskeyCredential): Promise<void>;
|
|
18
|
+
/** Update signature counter for a credential. */
|
|
12
19
|
abstract updateCredentialCounter(credentialId: Buffer, counter: number): Promise<void>;
|
|
20
|
+
/** Save a challenge record. */
|
|
13
21
|
abstract saveChallenge(record: PasskeyChallengeRecord): Promise<void>;
|
|
22
|
+
/** Read a challenge without consuming it. */
|
|
14
23
|
abstract getChallenge(challenge: string): Promise<PasskeyChallengeRecord | null>;
|
|
24
|
+
/** Consume and return a challenge. */
|
|
15
25
|
abstract consumeChallenge(challenge: string): Promise<PasskeyChallengeRecord | null>;
|
|
26
|
+
/** Cleanup expired challenges. */
|
|
16
27
|
abstract cleanupChallenges(now: Date): Promise<void>;
|
|
17
28
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MemoryPasskeyStore = void 0;
|
|
4
|
-
const user_id_js_1 = require("../auth-api/user-id.
|
|
5
|
-
const base_js_1 = require("./base.
|
|
4
|
+
const user_id_js_1 = require("../auth-api/user-id.cjs");
|
|
5
|
+
const base_js_1 = require("./base.cjs");
|
|
6
6
|
function encodeCredentialId(value) {
|
|
7
7
|
return Buffer.isBuffer(value) ? value.toString('base64') : value;
|
|
8
8
|
}
|
|
@@ -4,7 +4,7 @@ exports.PasskeyChallengeModel = exports.PasskeyCredentialModel = void 0;
|
|
|
4
4
|
exports.initPasskeyCredentialModel = initPasskeyCredentialModel;
|
|
5
5
|
exports.initPasskeyChallengeModel = initPasskeyChallengeModel;
|
|
6
6
|
const sequelize_1 = require("sequelize");
|
|
7
|
-
const sequelize_utils_js_1 = require("../sequelize-utils.
|
|
7
|
+
const sequelize_utils_js_1 = require("../sequelize-utils.cjs");
|
|
8
8
|
class PasskeyCredentialModel extends sequelize_1.Model {
|
|
9
9
|
}
|
|
10
10
|
exports.PasskeyCredentialModel = PasskeyCredentialModel;
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SequelizePasskeyStore = void 0;
|
|
4
4
|
const sequelize_1 = require("sequelize");
|
|
5
|
-
const user_id_js_1 = require("../auth-api/user-id.
|
|
6
|
-
const base_js_1 = require("./base.
|
|
7
|
-
const models_js_1 = require("./models.
|
|
5
|
+
const user_id_js_1 = require("../auth-api/user-id.cjs");
|
|
6
|
+
const base_js_1 = require("./base.cjs");
|
|
7
|
+
const models_js_1 = require("./models.cjs");
|
|
8
8
|
function encodeCredentialId(value) {
|
|
9
9
|
return Buffer.isBuffer(value) ? value.toString('base64') : value;
|
|
10
10
|
}
|
|
@@ -161,7 +161,19 @@ class PasskeyService {
|
|
|
161
161
|
async listUserCredentials(userId) {
|
|
162
162
|
return this.adapter.listUserCredentials(userId);
|
|
163
163
|
}
|
|
164
|
-
async deleteCredential(credentialId) {
|
|
164
|
+
async deleteCredential(credentialId, userId) {
|
|
165
|
+
if (userId !== undefined) {
|
|
166
|
+
const credentials = await this.adapter.listUserCredentials(userId);
|
|
167
|
+
const target = Buffer.isBuffer(credentialId) ? credentialId : Buffer.from(String(credentialId), 'base64');
|
|
168
|
+
const owns = credentials.some((c) => {
|
|
169
|
+
const stored = Buffer.isBuffer(c.credentialId)
|
|
170
|
+
? c.credentialId
|
|
171
|
+
: Buffer.from(String(c.credentialId), 'base64');
|
|
172
|
+
return stored.equals(target);
|
|
173
|
+
});
|
|
174
|
+
if (!owns)
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
165
177
|
return this.adapter.deleteCredential(credentialId);
|
|
166
178
|
}
|
|
167
179
|
async createChallenge(params) {
|
|
@@ -287,7 +299,9 @@ class PasskeyService {
|
|
|
287
299
|
const attestationResponse = params.response.response;
|
|
288
300
|
const credentialIdPrimary = toBufferOrNull(registrationInfo.credentialID);
|
|
289
301
|
const credentialIdFallback = toBufferOrNull(params.response.id);
|
|
290
|
-
const credentialId = credentialIdPrimary && credentialIdPrimary.length > 0
|
|
302
|
+
const credentialId = credentialIdPrimary && credentialIdPrimary.length > 0
|
|
303
|
+
? credentialIdPrimary
|
|
304
|
+
: (credentialIdFallback ?? Buffer.alloc(0));
|
|
291
305
|
const publicKeyPrimary = toBufferOrNull(registrationInfo.credentialPublicKey);
|
|
292
306
|
let publicKeyFallback = toBufferOrNull(attestationResponse?.publicKey);
|
|
293
307
|
if ((!publicKeyPrimary || publicKeyPrimary.length === 0) && attestationResponse?.attestationObject) {
|
|
@@ -330,7 +344,7 @@ class PasskeyService {
|
|
|
330
344
|
publicKey: storedPublicKey,
|
|
331
345
|
counter: registrationInfo.counter ?? 0,
|
|
332
346
|
transports: sanitizeTransports(params.response.transports),
|
|
333
|
-
backedUp: registrationInfo.credentialDeviceType === 'multiDevice',
|
|
347
|
+
backedUp: registrationInfo.credentialBackedUp ?? registrationInfo.credentialDeviceType === 'multiDevice',
|
|
334
348
|
deviceType: registrationInfo.credentialDeviceType,
|
|
335
349
|
label: toOptionalString(params.label),
|
|
336
350
|
createdDomain: toOptionalString(params.domain),
|
|
@@ -8,7 +8,7 @@ export declare class PasskeyService {
|
|
|
8
8
|
private readonly logger;
|
|
9
9
|
constructor(config: PasskeyServiceConfig, adapter: PasskeyStorageAdapter, logger?: Logger);
|
|
10
10
|
listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
|
|
11
|
-
deleteCredential(credentialId: Buffer | string): Promise<boolean>;
|
|
11
|
+
deleteCredential(credentialId: Buffer | string, userId?: AuthIdentifier): Promise<boolean>;
|
|
12
12
|
createChallenge(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
|
|
13
13
|
verifyResponse(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
|
|
14
14
|
private createRegistrationChallenge;
|
|
@@ -48,10 +48,9 @@ function decodeStringArray(raw) {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
catch {
|
|
51
|
-
//
|
|
51
|
+
// Malformed JSON — return empty rather than guessing via whitespace split.
|
|
52
|
+
// A whitespace-split fallback could silently grant unintended permissions
|
|
53
|
+
// if a scope/role column contains a corrupted value like "admin user".
|
|
52
54
|
}
|
|
53
|
-
return
|
|
54
|
-
.split(/\s+/)
|
|
55
|
-
.map((entry) => entry.trim())
|
|
56
|
-
.filter((entry) => entry.length > 0);
|
|
55
|
+
return [];
|
|
57
56
|
}
|
|
@@ -42,6 +42,9 @@ function normalizeTokenInternal(input) {
|
|
|
42
42
|
const userId = String(input.userId);
|
|
43
43
|
const ruid = input.ruid === undefined || input.ruid === null ? undefined : String(input.ruid);
|
|
44
44
|
const expires = input.expires ? new Date(input.expires) : new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
|
|
45
|
+
if (Number.isNaN(expires.getTime())) {
|
|
46
|
+
throw new Error(`Invalid token expiry value: ${String(input.expires)}`);
|
|
47
|
+
}
|
|
45
48
|
const issuedAt = input.issuedAt ? new Date(input.issuedAt) : new Date();
|
|
46
49
|
const lastSeenAt = input.lastSeenAt ? new Date(input.lastSeenAt) : issuedAt;
|
|
47
50
|
const scope = normalizeScope(input.scope);
|
|
@@ -71,6 +74,7 @@ function normalizeTokenInternal(input) {
|
|
|
71
74
|
sessionCookie
|
|
72
75
|
};
|
|
73
76
|
}
|
|
77
|
+
/** Base contract for token/session persistence backends plus shared JWT helpers. */
|
|
74
78
|
class TokenStore {
|
|
75
79
|
// Instance helpers
|
|
76
80
|
normalizeToken(token) {
|
|
@@ -17,20 +17,27 @@ export interface JwtDecodeResult<T> {
|
|
|
17
17
|
data?: T;
|
|
18
18
|
error?: string;
|
|
19
19
|
}
|
|
20
|
+
/** Base contract for token/session persistence backends plus shared JWT helpers. */
|
|
20
21
|
export declare abstract class TokenStore {
|
|
22
|
+
/** Create/persist a token record. */
|
|
21
23
|
abstract save(record: Token): Promise<void>;
|
|
24
|
+
/** Read a token record by partial query. */
|
|
22
25
|
abstract get(query: Partial<Token>, opts?: {
|
|
23
26
|
includeExpired?: boolean;
|
|
24
27
|
}): Promise<Token | null>;
|
|
28
|
+
/** Delete token records matching a partial query. */
|
|
25
29
|
abstract delete(query: Partial<Token>): Promise<number>;
|
|
30
|
+
/** Update a token identified by refresh token. */
|
|
26
31
|
abstract update(update: Partial<Token> & {
|
|
27
32
|
refreshToken: string;
|
|
28
33
|
}): Promise<boolean>;
|
|
34
|
+
/** List tokens for a specific user. */
|
|
29
35
|
abstract list(userId: string | number, opts?: {
|
|
30
36
|
limit?: number;
|
|
31
37
|
offset?: number;
|
|
32
38
|
includeExpired?: boolean;
|
|
33
39
|
}): Promise<Token[]>;
|
|
40
|
+
/** Close underlying resources. */
|
|
34
41
|
abstract close(): Promise<void>;
|
|
35
42
|
normalizeToken(token: Partial<Token>): Token;
|
|
36
43
|
jwtSign(payload: JwtSignPayload, secret: string, expiresInSeconds: number, options?: SignOptions): JwtSignResult;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MemoryTokenStore = void 0;
|
|
4
|
-
const base_js_1 = require("./base.
|
|
4
|
+
const base_js_1 = require("./base.cjs");
|
|
5
5
|
function comparableUserId(value) {
|
|
6
6
|
if (value === undefined) {
|
|
7
7
|
return undefined;
|
|
@@ -18,16 +18,16 @@ function cloneToken(record) {
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
function matchesQuery(record, query, includeExpired) {
|
|
21
|
-
if (query.refreshToken && record.refreshToken !== query.refreshToken) {
|
|
21
|
+
if (query.refreshToken !== undefined && record.refreshToken !== query.refreshToken) {
|
|
22
22
|
return false;
|
|
23
23
|
}
|
|
24
|
-
if (query.accessToken && record.accessToken !== query.accessToken) {
|
|
24
|
+
if (query.accessToken !== undefined && record.accessToken !== query.accessToken) {
|
|
25
25
|
return false;
|
|
26
26
|
}
|
|
27
27
|
if (query.userId !== undefined && comparableUserId(record.userId) !== comparableUserId(query.userId)) {
|
|
28
28
|
return false;
|
|
29
29
|
}
|
|
30
|
-
if (query.clientId && record.clientId !== query.clientId) {
|
|
30
|
+
if (query.clientId !== undefined && record.clientId !== query.clientId) {
|
|
31
31
|
return false;
|
|
32
32
|
}
|
|
33
33
|
if (query.domain !== undefined && (record.domain ?? '') !== (query.domain ?? '')) {
|
|
@@ -39,11 +39,14 @@ function matchesQuery(record, query, includeExpired) {
|
|
|
39
39
|
if (query.loginType !== undefined && record.loginType !== (query.loginType ?? undefined)) {
|
|
40
40
|
return false;
|
|
41
41
|
}
|
|
42
|
-
if (query.label && record.label !== query.label) {
|
|
42
|
+
if (query.label !== undefined && record.label !== query.label) {
|
|
43
43
|
return false;
|
|
44
44
|
}
|
|
45
|
-
if (!includeExpired
|
|
46
|
-
|
|
45
|
+
if (!includeExpired) {
|
|
46
|
+
const expiresTime = record.expires ? record.expires.getTime() : Infinity;
|
|
47
|
+
if (expiresTime < Date.now()) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
47
50
|
}
|
|
48
51
|
return true;
|
|
49
52
|
}
|
|
@@ -166,16 +169,14 @@ class MemoryTokenStore extends base_js_1.TokenStore {
|
|
|
166
169
|
const merged = { ...token };
|
|
167
170
|
const maybeAssign = (key) => {
|
|
168
171
|
const value = params[key];
|
|
169
|
-
if (value !== undefined) {
|
|
172
|
+
if (value !== undefined && value !== null) {
|
|
170
173
|
merged[key] = value;
|
|
171
174
|
}
|
|
172
175
|
};
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
merged.expires = params.expires;
|
|
178
|
-
}
|
|
176
|
+
maybeAssign('accessToken');
|
|
177
|
+
maybeAssign('expires');
|
|
178
|
+
maybeAssign('issuedAt');
|
|
179
|
+
maybeAssign('lastSeenAt');
|
|
179
180
|
maybeAssign('scope');
|
|
180
181
|
maybeAssign('label');
|
|
181
182
|
maybeAssign('domain');
|
|
@@ -186,12 +187,6 @@ class MemoryTokenStore extends base_js_1.TokenStore {
|
|
|
186
187
|
maybeAssign('os');
|
|
187
188
|
maybeAssign('refreshTtlSeconds');
|
|
188
189
|
maybeAssign('loginType');
|
|
189
|
-
if (params.issuedAt !== undefined && params.issuedAt !== null) {
|
|
190
|
-
merged.issuedAt = params.issuedAt;
|
|
191
|
-
}
|
|
192
|
-
if (params.lastSeenAt !== undefined && params.lastSeenAt !== null) {
|
|
193
|
-
merged.lastSeenAt = params.lastSeenAt;
|
|
194
|
-
}
|
|
195
190
|
maybeAssign('sessionCookie');
|
|
196
191
|
const normalized = this.normalizeToken(merged);
|
|
197
192
|
const previousUserId = token.userId;
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SequelizeTokenStore = void 0;
|
|
4
4
|
const sequelize_1 = require("sequelize");
|
|
5
|
-
const user_id_js_1 = require("../auth-api/user-id.
|
|
6
|
-
const sequelize_utils_js_1 = require("../sequelize-utils.
|
|
7
|
-
const base_js_1 = require("./base.
|
|
5
|
+
const user_id_js_1 = require("../auth-api/user-id.cjs");
|
|
6
|
+
const sequelize_utils_js_1 = require("../sequelize-utils.cjs");
|
|
7
|
+
const base_js_1 = require("./base.cjs");
|
|
8
8
|
class TokenModel extends sequelize_1.Model {
|
|
9
9
|
}
|
|
10
10
|
function initTokenModel(sequelize, options = {}) {
|
|
@@ -167,12 +167,20 @@ class SequelizeTokenStore extends base_js_1.TokenStore {
|
|
|
167
167
|
await this.Tokens.destroy({ where: removalWhere, transaction });
|
|
168
168
|
// Access/refresh columns are unique. Remove stale collisions before insert to avoid
|
|
169
169
|
// transient uniqueness failures during retries/rotation edge-cases.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
|
|
170
|
+
// Only include non-empty token values to prevent matching unrelated rows.
|
|
171
|
+
const collisionConditions = [];
|
|
172
|
+
if (normalized.accessToken) {
|
|
173
|
+
collisionConditions.push({ access: normalized.accessToken });
|
|
174
|
+
}
|
|
175
|
+
if (normalized.refreshToken) {
|
|
176
|
+
collisionConditions.push({ refresh: normalized.refreshToken });
|
|
177
|
+
}
|
|
178
|
+
if (collisionConditions.length > 0) {
|
|
179
|
+
await this.Tokens.destroy({
|
|
180
|
+
where: { [sequelize_1.Op.or]: collisionConditions },
|
|
181
|
+
transaction
|
|
182
|
+
});
|
|
183
|
+
}
|
|
176
184
|
await this.Tokens.create({
|
|
177
185
|
user_id: resolvedUserId,
|
|
178
186
|
real_user_id: resolvedRealUserId,
|
|
@@ -313,8 +321,14 @@ class SequelizeTokenStore extends base_js_1.TokenStore {
|
|
|
313
321
|
if (Object.keys(updates).length === 0) {
|
|
314
322
|
return false;
|
|
315
323
|
}
|
|
316
|
-
const
|
|
317
|
-
|
|
324
|
+
const sequelize = this.Tokens.sequelize;
|
|
325
|
+
if (!sequelize) {
|
|
326
|
+
throw new Error('Token model is not bound to a Sequelize instance');
|
|
327
|
+
}
|
|
328
|
+
return sequelize.transaction(async (transaction) => {
|
|
329
|
+
const [updated] = await this.Tokens.update(updates, { where, transaction });
|
|
330
|
+
return updated > 0;
|
|
331
|
+
});
|
|
318
332
|
}
|
|
319
333
|
async list(userId, opts = {}) {
|
|
320
334
|
const where = { user_id: this.normalizeUserId(userId) };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MemoryTusUploadStore = exports.TusUploadExceedsLengthError = exports.TusUploadOffsetError = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
function cloneRecord(record) {
|
|
6
|
+
return {
|
|
7
|
+
...record,
|
|
8
|
+
metadata: { ...record.metadata },
|
|
9
|
+
createdAt: new Date(record.createdAt),
|
|
10
|
+
updatedAt: new Date(record.updatedAt),
|
|
11
|
+
...(record.completedAt ? { completedAt: new Date(record.completedAt) } : {})
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
class TusUploadOffsetError extends Error {
|
|
15
|
+
constructor(currentOffset) {
|
|
16
|
+
super('Upload offset does not match current offset');
|
|
17
|
+
this.currentOffset = currentOffset;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.TusUploadOffsetError = TusUploadOffsetError;
|
|
21
|
+
class TusUploadExceedsLengthError extends Error {
|
|
22
|
+
constructor() {
|
|
23
|
+
super('Upload exceeds declared length');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.TusUploadExceedsLengthError = TusUploadExceedsLengthError;
|
|
27
|
+
class MemoryTusUploadStore {
|
|
28
|
+
constructor() {
|
|
29
|
+
this.uploads = new Map();
|
|
30
|
+
this.chunks = new Map();
|
|
31
|
+
}
|
|
32
|
+
async createUpload(input) {
|
|
33
|
+
const id = input.id?.trim() || (0, node_crypto_1.randomUUID)();
|
|
34
|
+
if (this.uploads.has(id)) {
|
|
35
|
+
throw new Error(`Upload ${id} already exists`);
|
|
36
|
+
}
|
|
37
|
+
const now = new Date();
|
|
38
|
+
const record = {
|
|
39
|
+
id,
|
|
40
|
+
length: Math.max(0, Math.floor(input.length)),
|
|
41
|
+
offset: 0,
|
|
42
|
+
metadata: { ...input.metadata },
|
|
43
|
+
...(input.userId ? { userId: input.userId } : {}),
|
|
44
|
+
createdAt: now,
|
|
45
|
+
updatedAt: now
|
|
46
|
+
};
|
|
47
|
+
this.uploads.set(id, record);
|
|
48
|
+
this.chunks.set(id, []);
|
|
49
|
+
return cloneRecord(record);
|
|
50
|
+
}
|
|
51
|
+
async getUpload(uploadId) {
|
|
52
|
+
const found = this.uploads.get(uploadId);
|
|
53
|
+
return found ? cloneRecord(found) : null;
|
|
54
|
+
}
|
|
55
|
+
async appendUpload(input) {
|
|
56
|
+
const current = this.uploads.get(input.uploadId);
|
|
57
|
+
if (!current) {
|
|
58
|
+
throw new Error('Upload not found');
|
|
59
|
+
}
|
|
60
|
+
if (input.offset !== current.offset) {
|
|
61
|
+
throw new TusUploadOffsetError(current.offset);
|
|
62
|
+
}
|
|
63
|
+
const nextOffset = current.offset + input.chunk.length;
|
|
64
|
+
if (nextOffset > current.length) {
|
|
65
|
+
throw new TusUploadExceedsLengthError();
|
|
66
|
+
}
|
|
67
|
+
const now = new Date();
|
|
68
|
+
const updated = {
|
|
69
|
+
...current,
|
|
70
|
+
offset: nextOffset,
|
|
71
|
+
updatedAt: now,
|
|
72
|
+
...(nextOffset === current.length ? { completedAt: now } : {})
|
|
73
|
+
};
|
|
74
|
+
this.uploads.set(input.uploadId, updated);
|
|
75
|
+
if (input.chunk.length > 0) {
|
|
76
|
+
this.chunks.get(input.uploadId)?.push(Buffer.from(input.chunk));
|
|
77
|
+
}
|
|
78
|
+
return cloneRecord(updated);
|
|
79
|
+
}
|
|
80
|
+
async deleteUpload(uploadId) {
|
|
81
|
+
this.chunks.delete(uploadId);
|
|
82
|
+
return this.uploads.delete(uploadId);
|
|
83
|
+
}
|
|
84
|
+
readUpload(uploadId) {
|
|
85
|
+
const chunks = this.chunks.get(uploadId);
|
|
86
|
+
if (!chunks) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
return Buffer.concat(chunks);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
exports.MemoryTusUploadStore = MemoryTusUploadStore;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { TusAppendInput, TusCreateUploadInput, TusUploadRecord, TusUploadStore } from './types.js';
|
|
2
|
+
export declare class TusUploadOffsetError extends Error {
|
|
3
|
+
readonly currentOffset: number;
|
|
4
|
+
constructor(currentOffset: number);
|
|
5
|
+
}
|
|
6
|
+
export declare class TusUploadExceedsLengthError extends Error {
|
|
7
|
+
constructor();
|
|
8
|
+
}
|
|
9
|
+
export declare class MemoryTusUploadStore implements TusUploadStore {
|
|
10
|
+
private readonly uploads;
|
|
11
|
+
private readonly chunks;
|
|
12
|
+
createUpload(input: TusCreateUploadInput): Promise<TusUploadRecord>;
|
|
13
|
+
getUpload(uploadId: string): Promise<TusUploadRecord | null>;
|
|
14
|
+
appendUpload(input: TusAppendInput): Promise<TusUploadRecord>;
|
|
15
|
+
deleteUpload(uploadId: string): Promise<boolean>;
|
|
16
|
+
readUpload(uploadId: string): Buffer | null;
|
|
17
|
+
}
|