tetherdb 0.1.0
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/LICENSE +21 -0
- package/README.md +277 -0
- package/bin/tetherdb.js +7 -0
- package/dist/cli/args.d.cts +20 -0
- package/dist/cli/args.d.ts +20 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/backend.d.cts +15 -0
- package/dist/cli/backend.d.ts +15 -0
- package/dist/cli/backend.d.ts.map +1 -0
- package/dist/cli/cli.d.cts +8 -0
- package/dist/cli/cli.d.ts +8 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/cli/commands/apps.d.cts +9 -0
- package/dist/cli/commands/apps.d.ts +9 -0
- package/dist/cli/commands/apps.d.ts.map +1 -0
- package/dist/cli/commands/help.d.cts +5 -0
- package/dist/cli/commands/help.d.ts +5 -0
- package/dist/cli/commands/help.d.ts.map +1 -0
- package/dist/cli/commands/index.d.cts +8 -0
- package/dist/cli/commands/index.d.ts +8 -0
- package/dist/cli/commands/index.d.ts.map +1 -0
- package/dist/cli/commands/maintenance.d.cts +9 -0
- package/dist/cli/commands/maintenance.d.ts +9 -0
- package/dist/cli/commands/maintenance.d.ts.map +1 -0
- package/dist/cli/commands/serve.d.cts +14 -0
- package/dist/cli/commands/serve.d.ts +14 -0
- package/dist/cli/commands/serve.d.ts.map +1 -0
- package/dist/cli/commands/status.d.cts +9 -0
- package/dist/cli/commands/status.d.ts +9 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/tables.d.cts +9 -0
- package/dist/cli/commands/tables.d.ts +9 -0
- package/dist/cli/commands/tables.d.ts.map +1 -0
- package/dist/cli/commands/users.d.cts +9 -0
- package/dist/cli/commands/users.d.ts +9 -0
- package/dist/cli/commands/users.d.ts.map +1 -0
- package/dist/cli/index.cjs +3999 -0
- package/dist/cli/index.cjs.map +1 -0
- package/dist/cli/index.d.cts +7 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +3958 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/client/auth.d.cts +155 -0
- package/dist/client/auth.d.ts +155 -0
- package/dist/client/auth.d.ts.map +1 -0
- package/dist/client/client.d.cts +122 -0
- package/dist/client/client.d.ts +122 -0
- package/dist/client/client.d.ts.map +1 -0
- package/dist/client/errors.d.cts +39 -0
- package/dist/client/errors.d.ts +39 -0
- package/dist/client/errors.d.ts.map +1 -0
- package/dist/client/index.cjs +1742 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.cts +12 -0
- package/dist/client/index.d.ts +12 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +1707 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/shared/event.d.cts +29 -0
- package/dist/client/shared/event.d.ts +29 -0
- package/dist/client/shared/event.d.ts.map +1 -0
- package/dist/client/shared/id.d.cts +5 -0
- package/dist/client/shared/id.d.ts +5 -0
- package/dist/client/shared/id.d.ts.map +1 -0
- package/dist/client/storage.d.cts +183 -0
- package/dist/client/storage.d.ts +183 -0
- package/dist/client/storage.d.ts.map +1 -0
- package/dist/client/sync.d.cts +126 -0
- package/dist/client/sync.d.ts +126 -0
- package/dist/client/sync.d.ts.map +1 -0
- package/dist/client/table.d.cts +146 -0
- package/dist/client/table.d.ts +146 -0
- package/dist/client/table.d.ts.map +1 -0
- package/dist/index.cjs +1742 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1707 -0
- package/dist/index.js.map +1 -0
- package/dist/server/crypto.d.cts +55 -0
- package/dist/server/crypto.d.ts +55 -0
- package/dist/server/crypto.d.ts.map +1 -0
- package/dist/server/errors.d.cts +39 -0
- package/dist/server/errors.d.ts +39 -0
- package/dist/server/errors.d.ts.map +1 -0
- package/dist/server/index.cjs +3621 -0
- package/dist/server/index.cjs.map +1 -0
- package/dist/server/index.d.cts +10 -0
- package/dist/server/index.d.ts +10 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +3569 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/lock.d.cts +56 -0
- package/dist/server/lock.d.ts +56 -0
- package/dist/server/lock.d.ts.map +1 -0
- package/dist/server/rate-limiter.d.cts +81 -0
- package/dist/server/rate-limiter.d.ts +81 -0
- package/dist/server/rate-limiter.d.ts.map +1 -0
- package/dist/server/server.d.cts +206 -0
- package/dist/server/server.d.ts +206 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/storage/app.d.cts +70 -0
- package/dist/server/storage/app.d.ts +70 -0
- package/dist/server/storage/app.d.ts.map +1 -0
- package/dist/server/storage/base/app.d.cts +42 -0
- package/dist/server/storage/base/app.d.ts +42 -0
- package/dist/server/storage/base/app.d.ts.map +1 -0
- package/dist/server/storage/base/index.d.cts +5 -0
- package/dist/server/storage/base/index.d.ts +5 -0
- package/dist/server/storage/base/index.d.ts.map +1 -0
- package/dist/server/storage/base/storage.d.cts +40 -0
- package/dist/server/storage/base/storage.d.ts +40 -0
- package/dist/server/storage/base/storage.d.ts.map +1 -0
- package/dist/server/storage/base/table.d.cts +32 -0
- package/dist/server/storage/base/table.d.ts +32 -0
- package/dist/server/storage/base/table.d.ts.map +1 -0
- package/dist/server/storage/base/user.d.cts +31 -0
- package/dist/server/storage/base/user.d.ts +31 -0
- package/dist/server/storage/base/user.d.ts.map +1 -0
- package/dist/server/storage/file/app.d.cts +50 -0
- package/dist/server/storage/file/app.d.ts +50 -0
- package/dist/server/storage/file/app.d.ts.map +1 -0
- package/dist/server/storage/file/index.d.cts +5 -0
- package/dist/server/storage/file/index.d.ts +5 -0
- package/dist/server/storage/file/index.d.ts.map +1 -0
- package/dist/server/storage/file/storage.d.cts +67 -0
- package/dist/server/storage/file/storage.d.ts +67 -0
- package/dist/server/storage/file/storage.d.ts.map +1 -0
- package/dist/server/storage/file/table.d.cts +14 -0
- package/dist/server/storage/file/table.d.ts +14 -0
- package/dist/server/storage/file/table.d.ts.map +1 -0
- package/dist/server/storage/file/user.d.cts +14 -0
- package/dist/server/storage/file/user.d.ts +14 -0
- package/dist/server/storage/file/user.d.ts.map +1 -0
- package/dist/server/storage/index.d.cts +14 -0
- package/dist/server/storage/index.d.ts +14 -0
- package/dist/server/storage/index.d.ts.map +1 -0
- package/dist/server/storage/memory/app.d.cts +29 -0
- package/dist/server/storage/memory/app.d.ts +29 -0
- package/dist/server/storage/memory/app.d.ts.map +1 -0
- package/dist/server/storage/memory/index.d.cts +5 -0
- package/dist/server/storage/memory/index.d.ts +5 -0
- package/dist/server/storage/memory/index.d.ts.map +1 -0
- package/dist/server/storage/memory/storage.d.cts +47 -0
- package/dist/server/storage/memory/storage.d.ts +47 -0
- package/dist/server/storage/memory/storage.d.ts.map +1 -0
- package/dist/server/storage/memory/table.d.cts +17 -0
- package/dist/server/storage/memory/table.d.ts +17 -0
- package/dist/server/storage/memory/table.d.ts.map +1 -0
- package/dist/server/storage/memory/user.d.cts +21 -0
- package/dist/server/storage/memory/user.d.ts +21 -0
- package/dist/server/storage/memory/user.d.ts.map +1 -0
- package/dist/server/storage/sqlite/app.d.cts +31 -0
- package/dist/server/storage/sqlite/app.d.ts +31 -0
- package/dist/server/storage/sqlite/app.d.ts.map +1 -0
- package/dist/server/storage/sqlite/index.d.cts +5 -0
- package/dist/server/storage/sqlite/index.d.ts +5 -0
- package/dist/server/storage/sqlite/index.d.ts.map +1 -0
- package/dist/server/storage/sqlite/storage.d.cts +92 -0
- package/dist/server/storage/sqlite/storage.d.ts +92 -0
- package/dist/server/storage/sqlite/storage.d.ts.map +1 -0
- package/dist/server/storage/sqlite/table.d.cts +15 -0
- package/dist/server/storage/sqlite/table.d.ts +15 -0
- package/dist/server/storage/sqlite/table.d.ts.map +1 -0
- package/dist/server/storage/sqlite/user.d.cts +14 -0
- package/dist/server/storage/sqlite/user.d.ts +14 -0
- package/dist/server/storage/sqlite/user.d.ts.map +1 -0
- package/dist/server/storage/storage.d.cts +152 -0
- package/dist/server/storage/storage.d.ts +152 -0
- package/dist/server/storage/storage.d.ts.map +1 -0
- package/dist/server/storage/table.d.cts +45 -0
- package/dist/server/storage/table.d.ts +45 -0
- package/dist/server/storage/table.d.ts.map +1 -0
- package/dist/server/storage/user.d.cts +45 -0
- package/dist/server/storage/user.d.ts +45 -0
- package/dist/server/storage/user.d.ts.map +1 -0
- package/dist/server/sync.d.cts +67 -0
- package/dist/server/sync.d.ts +67 -0
- package/dist/server/sync.d.ts.map +1 -0
- package/dist/server/validate.d.cts +113 -0
- package/dist/server/validate.d.ts +113 -0
- package/dist/server/validate.d.ts.map +1 -0
- package/dist/shared/clock.d.cts +22 -0
- package/dist/shared/clock.d.ts +22 -0
- package/dist/shared/clock.d.ts.map +1 -0
- package/dist/shared/path.d.cts +12 -0
- package/dist/shared/path.d.ts +12 -0
- package/dist/shared/path.d.ts.map +1 -0
- package/dist/shared/types.d.cts +234 -0
- package/dist/shared/types.d.ts +234 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/package.json +133 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metadata recorded inside the server lockfile.
|
|
3
|
+
*/
|
|
4
|
+
export interface ServerLockInfo {
|
|
5
|
+
/** Process identifier running the server. */
|
|
6
|
+
pid: number;
|
|
7
|
+
/** Port number the server is bound to. */
|
|
8
|
+
port: number;
|
|
9
|
+
/** Host interface the server is bound to. */
|
|
10
|
+
host: string;
|
|
11
|
+
/** Storage backend type ('sqlite', 'file', or 'memory'). */
|
|
12
|
+
backend: string;
|
|
13
|
+
/** Epoch timestamp when the server acquired the lock. */
|
|
14
|
+
startedAt: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Handle representing an active exclusive server lock.
|
|
18
|
+
*/
|
|
19
|
+
export interface ServerLockHandle {
|
|
20
|
+
/** Lock metadata information. */
|
|
21
|
+
readonly info: ServerLockInfo;
|
|
22
|
+
/** Path to the active lockfile on disk. */
|
|
23
|
+
readonly lockPath: string;
|
|
24
|
+
/** Releases the lock and removes the lockfile. */
|
|
25
|
+
release(): void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Checks whether a given operating system process ID is currently alive.
|
|
29
|
+
*
|
|
30
|
+
* @param pid - Process ID to check.
|
|
31
|
+
* @returns `true` if the process is active; otherwise `false`.
|
|
32
|
+
*/
|
|
33
|
+
export declare function isProcessAlive(pid: number): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Reads active server lock metadata from a storage base directory if present and live.
|
|
36
|
+
* Stale locks from terminated processes are ignored.
|
|
37
|
+
*
|
|
38
|
+
* @param baseDir - Storage base directory.
|
|
39
|
+
* @returns ServerLockInfo if an active server is running, or `null`.
|
|
40
|
+
*/
|
|
41
|
+
export declare function readServerLock(baseDir: string): ServerLockInfo | null;
|
|
42
|
+
/**
|
|
43
|
+
* Acquires an exclusive server lock on the specified directory to prevent multiple instances
|
|
44
|
+
* from running against the same data storage.
|
|
45
|
+
*
|
|
46
|
+
* @param baseDir - Directory path where the lockfile will be maintained.
|
|
47
|
+
* @param details - Port, host, and backend details to write to the lockfile.
|
|
48
|
+
* @returns ServerLockHandle representing the active lock.
|
|
49
|
+
* @throws TetherServerError if another active server already holds the lock.
|
|
50
|
+
*/
|
|
51
|
+
export declare function acquireServerLock(baseDir: string, details: {
|
|
52
|
+
port: number;
|
|
53
|
+
host: string;
|
|
54
|
+
backend: string;
|
|
55
|
+
}): ServerLockHandle;
|
|
56
|
+
//# sourceMappingURL=lock.d.ts.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metadata recorded inside the server lockfile.
|
|
3
|
+
*/
|
|
4
|
+
export interface ServerLockInfo {
|
|
5
|
+
/** Process identifier running the server. */
|
|
6
|
+
pid: number;
|
|
7
|
+
/** Port number the server is bound to. */
|
|
8
|
+
port: number;
|
|
9
|
+
/** Host interface the server is bound to. */
|
|
10
|
+
host: string;
|
|
11
|
+
/** Storage backend type ('sqlite', 'file', or 'memory'). */
|
|
12
|
+
backend: string;
|
|
13
|
+
/** Epoch timestamp when the server acquired the lock. */
|
|
14
|
+
startedAt: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Handle representing an active exclusive server lock.
|
|
18
|
+
*/
|
|
19
|
+
export interface ServerLockHandle {
|
|
20
|
+
/** Lock metadata information. */
|
|
21
|
+
readonly info: ServerLockInfo;
|
|
22
|
+
/** Path to the active lockfile on disk. */
|
|
23
|
+
readonly lockPath: string;
|
|
24
|
+
/** Releases the lock and removes the lockfile. */
|
|
25
|
+
release(): void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Checks whether a given operating system process ID is currently alive.
|
|
29
|
+
*
|
|
30
|
+
* @param pid - Process ID to check.
|
|
31
|
+
* @returns `true` if the process is active; otherwise `false`.
|
|
32
|
+
*/
|
|
33
|
+
export declare function isProcessAlive(pid: number): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Reads active server lock metadata from a storage base directory if present and live.
|
|
36
|
+
* Stale locks from terminated processes are ignored.
|
|
37
|
+
*
|
|
38
|
+
* @param baseDir - Storage base directory.
|
|
39
|
+
* @returns ServerLockInfo if an active server is running, or `null`.
|
|
40
|
+
*/
|
|
41
|
+
export declare function readServerLock(baseDir: string): ServerLockInfo | null;
|
|
42
|
+
/**
|
|
43
|
+
* Acquires an exclusive server lock on the specified directory to prevent multiple instances
|
|
44
|
+
* from running against the same data storage.
|
|
45
|
+
*
|
|
46
|
+
* @param baseDir - Directory path where the lockfile will be maintained.
|
|
47
|
+
* @param details - Port, host, and backend details to write to the lockfile.
|
|
48
|
+
* @returns ServerLockHandle representing the active lock.
|
|
49
|
+
* @throws TetherServerError if another active server already holds the lock.
|
|
50
|
+
*/
|
|
51
|
+
export declare function acquireServerLock(baseDir: string, details: {
|
|
52
|
+
port: number;
|
|
53
|
+
host: string;
|
|
54
|
+
backend: string;
|
|
55
|
+
}): ServerLockHandle;
|
|
56
|
+
//# sourceMappingURL=lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../src/server/lock.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,2CAA2C;IAC3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,kDAAkD;IAClD,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CASnD;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAarE;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvD,gBAAgB,CAyDlB"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration options for a RateLimiter instance.
|
|
3
|
+
*/
|
|
4
|
+
export interface RateLimiterOptions {
|
|
5
|
+
/** Time window in milliseconds (defaults to 60,000ms / 1 minute). */
|
|
6
|
+
windowMs?: number;
|
|
7
|
+
/** Maximum number of allowed requests within the time window. */
|
|
8
|
+
maxRequests?: number;
|
|
9
|
+
/** Number of consecutive failures before applying progressive backoff (defaults to 3). */
|
|
10
|
+
maxFailures?: number;
|
|
11
|
+
/** Initial backoff duration in milliseconds after exceeding maxFailures (defaults to 1,000ms). */
|
|
12
|
+
initialBackoffMs?: number;
|
|
13
|
+
/** Maximum backoff duration in milliseconds (defaults to 900,000ms / 15 minutes). */
|
|
14
|
+
maxBackoffMs?: number;
|
|
15
|
+
/** Maximum number of tracking entries kept in memory before evicting (defaults to 10,000). */
|
|
16
|
+
maxEntries?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* In-memory sliding-window rate limiter with failure-based exponential backoff.
|
|
20
|
+
*/
|
|
21
|
+
export declare class RateLimiter {
|
|
22
|
+
private readonly store;
|
|
23
|
+
private readonly windowMs;
|
|
24
|
+
private readonly maxRequests;
|
|
25
|
+
private readonly maxFailures;
|
|
26
|
+
private readonly initialBackoffMs;
|
|
27
|
+
private readonly maxBackoffMs;
|
|
28
|
+
private readonly maxEntries;
|
|
29
|
+
/**
|
|
30
|
+
* Initializes a new RateLimiter instance.
|
|
31
|
+
*
|
|
32
|
+
* @param options - Configuration options for window size, request limits, and backoff.
|
|
33
|
+
*/
|
|
34
|
+
constructor(options?: RateLimiterOptions);
|
|
35
|
+
/**
|
|
36
|
+
* Returns the current number of tracked keys in memory.
|
|
37
|
+
*/
|
|
38
|
+
get size(): number;
|
|
39
|
+
/**
|
|
40
|
+
* Checks whether the given key is currently rate limited or blocked by backoff cooldown.
|
|
41
|
+
*
|
|
42
|
+
* @param key - Identifier (e.g. IP address or username).
|
|
43
|
+
* @param now - Current timestamp in milliseconds (defaults to Date.now()).
|
|
44
|
+
* @returns `true` if requests for this key are limited; `false` otherwise.
|
|
45
|
+
*/
|
|
46
|
+
isLimited(key: string, now?: number): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Consumes one attempt for the given key if not currently limited.
|
|
49
|
+
*
|
|
50
|
+
* @param key - Identifier.
|
|
51
|
+
* @param now - Current timestamp in milliseconds.
|
|
52
|
+
* @returns `true` if request was allowed and consumed; `false` if rate limited.
|
|
53
|
+
*/
|
|
54
|
+
consume(key: string, now?: number): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Records a failed attempt for the given key and applies progressive exponential backoff.
|
|
57
|
+
*
|
|
58
|
+
* @param key - Identifier.
|
|
59
|
+
* @param now - Current timestamp in milliseconds.
|
|
60
|
+
* @returns Cooldown duration in milliseconds if blocked, or 0 if under failure threshold.
|
|
61
|
+
*/
|
|
62
|
+
recordFailure(key: string, now?: number): number;
|
|
63
|
+
/**
|
|
64
|
+
* Resets all failure counters and request tracking for the given key.
|
|
65
|
+
*
|
|
66
|
+
* @param key - Identifier to reset.
|
|
67
|
+
*/
|
|
68
|
+
reset(key: string): void;
|
|
69
|
+
/**
|
|
70
|
+
* Clears all stored rate limit entries.
|
|
71
|
+
*/
|
|
72
|
+
clear(): void;
|
|
73
|
+
/**
|
|
74
|
+
* Purges expired entries from the internal store.
|
|
75
|
+
*
|
|
76
|
+
* @param now - Current timestamp in milliseconds.
|
|
77
|
+
*/
|
|
78
|
+
cleanup(now?: number): void;
|
|
79
|
+
private setEntry;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration options for a RateLimiter instance.
|
|
3
|
+
*/
|
|
4
|
+
export interface RateLimiterOptions {
|
|
5
|
+
/** Time window in milliseconds (defaults to 60,000ms / 1 minute). */
|
|
6
|
+
windowMs?: number;
|
|
7
|
+
/** Maximum number of allowed requests within the time window. */
|
|
8
|
+
maxRequests?: number;
|
|
9
|
+
/** Number of consecutive failures before applying progressive backoff (defaults to 3). */
|
|
10
|
+
maxFailures?: number;
|
|
11
|
+
/** Initial backoff duration in milliseconds after exceeding maxFailures (defaults to 1,000ms). */
|
|
12
|
+
initialBackoffMs?: number;
|
|
13
|
+
/** Maximum backoff duration in milliseconds (defaults to 900,000ms / 15 minutes). */
|
|
14
|
+
maxBackoffMs?: number;
|
|
15
|
+
/** Maximum number of tracking entries kept in memory before evicting (defaults to 10,000). */
|
|
16
|
+
maxEntries?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* In-memory sliding-window rate limiter with failure-based exponential backoff.
|
|
20
|
+
*/
|
|
21
|
+
export declare class RateLimiter {
|
|
22
|
+
private readonly store;
|
|
23
|
+
private readonly windowMs;
|
|
24
|
+
private readonly maxRequests;
|
|
25
|
+
private readonly maxFailures;
|
|
26
|
+
private readonly initialBackoffMs;
|
|
27
|
+
private readonly maxBackoffMs;
|
|
28
|
+
private readonly maxEntries;
|
|
29
|
+
/**
|
|
30
|
+
* Initializes a new RateLimiter instance.
|
|
31
|
+
*
|
|
32
|
+
* @param options - Configuration options for window size, request limits, and backoff.
|
|
33
|
+
*/
|
|
34
|
+
constructor(options?: RateLimiterOptions);
|
|
35
|
+
/**
|
|
36
|
+
* Returns the current number of tracked keys in memory.
|
|
37
|
+
*/
|
|
38
|
+
get size(): number;
|
|
39
|
+
/**
|
|
40
|
+
* Checks whether the given key is currently rate limited or blocked by backoff cooldown.
|
|
41
|
+
*
|
|
42
|
+
* @param key - Identifier (e.g. IP address or username).
|
|
43
|
+
* @param now - Current timestamp in milliseconds (defaults to Date.now()).
|
|
44
|
+
* @returns `true` if requests for this key are limited; `false` otherwise.
|
|
45
|
+
*/
|
|
46
|
+
isLimited(key: string, now?: number): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Consumes one attempt for the given key if not currently limited.
|
|
49
|
+
*
|
|
50
|
+
* @param key - Identifier.
|
|
51
|
+
* @param now - Current timestamp in milliseconds.
|
|
52
|
+
* @returns `true` if request was allowed and consumed; `false` if rate limited.
|
|
53
|
+
*/
|
|
54
|
+
consume(key: string, now?: number): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Records a failed attempt for the given key and applies progressive exponential backoff.
|
|
57
|
+
*
|
|
58
|
+
* @param key - Identifier.
|
|
59
|
+
* @param now - Current timestamp in milliseconds.
|
|
60
|
+
* @returns Cooldown duration in milliseconds if blocked, or 0 if under failure threshold.
|
|
61
|
+
*/
|
|
62
|
+
recordFailure(key: string, now?: number): number;
|
|
63
|
+
/**
|
|
64
|
+
* Resets all failure counters and request tracking for the given key.
|
|
65
|
+
*
|
|
66
|
+
* @param key - Identifier to reset.
|
|
67
|
+
*/
|
|
68
|
+
reset(key: string): void;
|
|
69
|
+
/**
|
|
70
|
+
* Clears all stored rate limit entries.
|
|
71
|
+
*/
|
|
72
|
+
clear(): void;
|
|
73
|
+
/**
|
|
74
|
+
* Purges expired entries from the internal store.
|
|
75
|
+
*
|
|
76
|
+
* @param now - Current timestamp in milliseconds.
|
|
77
|
+
*/
|
|
78
|
+
cleanup(now?: number): void;
|
|
79
|
+
private setEntry;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/server/rate-limiter.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0FAA0F;IAC1F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kGAAkG;IAClG,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qFAAqF;IACrF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8FAA8F;IAC9F,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;IAC3D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC;;;;OAIG;IACH,YAAY,OAAO,GAAE,kBAAuB,EAO3C;IAED;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;;;;OAMG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,OAAO,CAchD;IAED;;;;;;OAMG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,OAAO,CAsB9C;IAED;;;;;;OAMG;IACH,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,MAAM,CAyBnD;IAED;;;;OAIG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEvB;IAED;;OAEG;IACH,KAAK,IAAI,IAAI,CAEZ;IAED;;;;OAIG;IACH,OAAO,CAAC,GAAG,SAAa,GAAG,IAAI,CAM9B;IAID,OAAO,CAAC,QAAQ;CAYjB"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import * as http from 'node:http';
|
|
2
|
+
import { WebSocketServer } from 'ws';
|
|
3
|
+
import type { Storage, UserStorage } from './storage/index.cjs';
|
|
4
|
+
import { Sync } from './sync.cjs';
|
|
5
|
+
/**
|
|
6
|
+
* Rate limiting and resource control options for authentication endpoints and sync streams.
|
|
7
|
+
*/
|
|
8
|
+
export interface RateLimitOptions {
|
|
9
|
+
/** Maximum login attempts per IP within the time window (defaults to 100). */
|
|
10
|
+
ipLoginMaxRequests?: number;
|
|
11
|
+
/** Maximum login attempts per target username within the time window (defaults to 20). */
|
|
12
|
+
userLoginMaxRequests?: number;
|
|
13
|
+
/** Maximum registration attempts per IP within the time window (defaults to 100). */
|
|
14
|
+
ipRegisterMaxRequests?: number;
|
|
15
|
+
/** Maximum WebSocket connection handshakes per IP within the time window (defaults to 100). */
|
|
16
|
+
ipSyncMaxRequests?: number;
|
|
17
|
+
/** Maximum concurrent active WebSocket connections allowed per user channel (defaults to 20). */
|
|
18
|
+
maxConcurrentConnectionsPerUser?: number;
|
|
19
|
+
/** Maximum duration in milliseconds to wait for authentication before terminating socket (defaults to 10,000ms). */
|
|
20
|
+
authTimeoutMs?: number;
|
|
21
|
+
/** Sliding window duration in milliseconds (defaults to 60,000ms / 1 minute). */
|
|
22
|
+
windowMs?: number;
|
|
23
|
+
/** Consecutive failed attempts before progressive backoff begins (defaults to 5). */
|
|
24
|
+
maxFailures?: number;
|
|
25
|
+
/** Initial backoff duration in milliseconds (defaults to 1,000ms). */
|
|
26
|
+
initialBackoffMs?: number;
|
|
27
|
+
/** Maximum backoff duration in milliseconds (defaults to 900,000ms / 15 minutes). */
|
|
28
|
+
maxBackoffMs?: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Options for configuring Cross-Origin Resource Sharing (CORS) on HTTP endpoints.
|
|
32
|
+
*/
|
|
33
|
+
export interface CorsOptions {
|
|
34
|
+
/**
|
|
35
|
+
* Allowed origin(s). Can be `'*'` for unrestricted access, a specific origin string (e.g. `'https://example.com'`),
|
|
36
|
+
* an array of allowed origin strings, `true` to reflect the request's `Origin` header, or `false` to disable CORS headers.
|
|
37
|
+
* Defaults to `'*'`.
|
|
38
|
+
*/
|
|
39
|
+
origin?: string | string[] | boolean;
|
|
40
|
+
/** Whether to set `Access-Control-Allow-Credentials: true` (defaults to false). */
|
|
41
|
+
credentials?: boolean;
|
|
42
|
+
/** Allowed request headers for preflight OPTIONS checks (defaults to `['Content-Type', 'Authorization']`). */
|
|
43
|
+
allowedHeaders?: string[];
|
|
44
|
+
/** Exposed response headers (Access-Control-Expose-Headers). */
|
|
45
|
+
exposedHeaders?: string[];
|
|
46
|
+
/** Maximum age in seconds to cache preflight responses (Access-Control-Max-Age). */
|
|
47
|
+
maxAge?: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Pluggable logger interface for TetherServer logging.
|
|
51
|
+
*/
|
|
52
|
+
export interface TetherLogger {
|
|
53
|
+
/** Logs debug information. */
|
|
54
|
+
debug(message: string, ...args: unknown[]): void;
|
|
55
|
+
/** Logs operational information. */
|
|
56
|
+
info(message: string, ...args: unknown[]): void;
|
|
57
|
+
/** Logs warning conditions. */
|
|
58
|
+
warn(message: string, ...args: unknown[]): void;
|
|
59
|
+
/** Logs error conditions. */
|
|
60
|
+
error(message: string, ...args: unknown[]): void;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Configuration options for the TetherServer.
|
|
64
|
+
*/
|
|
65
|
+
export interface TetherServerOptions {
|
|
66
|
+
/** Custom storage instance. Defaults to MemoryStorage if not defined. */
|
|
67
|
+
storage?: Storage;
|
|
68
|
+
/** Base path for HTTP REST endpoints (defaults to ''). */
|
|
69
|
+
basePath?: string;
|
|
70
|
+
/** Path for WebSocket upgrade requests (defaults to '/sync'). */
|
|
71
|
+
webSocketPath?: string;
|
|
72
|
+
/** Whether user self-registration is allowed via `/auth/register` (defaults to true). */
|
|
73
|
+
allowRegistration?: boolean;
|
|
74
|
+
/** Rate limiting options for auth and sync endpoints, or `false` to disable rate limiting (defaults to true). */
|
|
75
|
+
rateLimiting?: boolean | RateLimitOptions;
|
|
76
|
+
/** Whether to trust the `X-Forwarded-For` header for resolving client IP addresses (defaults to false). */
|
|
77
|
+
trustProxy?: boolean;
|
|
78
|
+
/** CORS options for HTTP endpoints, `false` to disable CORS headers, or `true` for default permissive CORS (defaults to true). */
|
|
79
|
+
cors?: boolean | CorsOptions;
|
|
80
|
+
/** Optional custom logger instance, or `false` to silence internal server logs (defaults to `console`). */
|
|
81
|
+
logger?: TetherLogger | false;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Options for starting the standard server launcher.
|
|
85
|
+
*/
|
|
86
|
+
export interface StartServerOptions extends TetherServerOptions {
|
|
87
|
+
/** Port number to bind (defaults to 8080 or PORT environment variable). */
|
|
88
|
+
port?: number;
|
|
89
|
+
/** Host interface to bind (defaults to '0.0.0.0'). */
|
|
90
|
+
host?: string;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Result returned when launching a server using `startServer()`.
|
|
94
|
+
*/
|
|
95
|
+
export interface RunningServer {
|
|
96
|
+
/** The TetherServer instance. */
|
|
97
|
+
server: TetherServer;
|
|
98
|
+
/** The running Node.js HTTP server instance. */
|
|
99
|
+
httpServer: http.Server;
|
|
100
|
+
/** Bound port number. */
|
|
101
|
+
port: number;
|
|
102
|
+
/** Bound host address. */
|
|
103
|
+
host: string;
|
|
104
|
+
/** Closes both HTTP and WebSocket server cleanly. */
|
|
105
|
+
close(): Promise<void>;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Starts a complete standalone HTTP & WebSocket synchronization server.
|
|
109
|
+
*
|
|
110
|
+
* @param options - Start options including port, host, storage, and limits.
|
|
111
|
+
* @returns Handle to the running server.
|
|
112
|
+
*/
|
|
113
|
+
export declare function startServer(options?: StartServerOptions): Promise<RunningServer>;
|
|
114
|
+
/**
|
|
115
|
+
* Unified HTTP and WebSocket server handling authentication endpoints (`/auth/register`, `/auth/login`)
|
|
116
|
+
* and real-time streaming connections (`/sync`).
|
|
117
|
+
*/
|
|
118
|
+
export declare class TetherServer {
|
|
119
|
+
/** Underlying storage engine for users, apps, and tables. */
|
|
120
|
+
readonly storage: Storage;
|
|
121
|
+
/** Real-time synchronization connection and broadcast coordinator. */
|
|
122
|
+
readonly sync: Sync;
|
|
123
|
+
/** Base path for HTTP REST endpoints. */
|
|
124
|
+
readonly basePath: string;
|
|
125
|
+
/** Path for WebSocket upgrade requests. */
|
|
126
|
+
readonly webSocketPath: string;
|
|
127
|
+
readonly trustProxy: boolean;
|
|
128
|
+
private readonly allowRegistration;
|
|
129
|
+
private readonly corsConfig;
|
|
130
|
+
private readonly logger;
|
|
131
|
+
private readonly ipLoginLimiter;
|
|
132
|
+
private readonly userLoginLimiter;
|
|
133
|
+
private readonly ipRegisterLimiter;
|
|
134
|
+
private _httpServer;
|
|
135
|
+
private _webSocketServer;
|
|
136
|
+
private lockHandle;
|
|
137
|
+
/**
|
|
138
|
+
* Initializes a new TetherServer instance.
|
|
139
|
+
*
|
|
140
|
+
* @param options - Configuration options for storage, endpoints, and rate limiting.
|
|
141
|
+
*/
|
|
142
|
+
constructor(options?: TetherServerOptions);
|
|
143
|
+
/**
|
|
144
|
+
* Active Node.js HTTP server instance, or `null` if not listening.
|
|
145
|
+
*/
|
|
146
|
+
get httpServer(): http.Server | null;
|
|
147
|
+
/**
|
|
148
|
+
* Active WebSocketServer instance, or `null` if not listening.
|
|
149
|
+
*/
|
|
150
|
+
get webSocketServer(): WebSocketServer | null;
|
|
151
|
+
/**
|
|
152
|
+
* Declares an application and its tables.
|
|
153
|
+
* Registers the application and any declared tables if not already present.
|
|
154
|
+
*
|
|
155
|
+
* @param appId - Application identifier.
|
|
156
|
+
* @param tables - Array of table names.
|
|
157
|
+
*/
|
|
158
|
+
declareApp(appId: string, tables?: string[]): Promise<void>;
|
|
159
|
+
/**
|
|
160
|
+
* Declares a user account with the specified username and password.
|
|
161
|
+
* Creates the user if not already registered, or updates the existing user's password.
|
|
162
|
+
*
|
|
163
|
+
* @param username - Username for the account.
|
|
164
|
+
* @param password - Plaintext password for the account.
|
|
165
|
+
* @returns UserStorage handle for the declared user.
|
|
166
|
+
*/
|
|
167
|
+
declareUser(username: string, password: string): Promise<UserStorage>;
|
|
168
|
+
/**
|
|
169
|
+
* Attaches WebSocket synchronization handling to an existing HTTP server.
|
|
170
|
+
*
|
|
171
|
+
* @param server - The HTTP server instance to attach to.
|
|
172
|
+
*/
|
|
173
|
+
attach(server: http.Server): void;
|
|
174
|
+
/**
|
|
175
|
+
* Starts the HTTP and WebSocket server listening on the specified port and host.
|
|
176
|
+
*
|
|
177
|
+
* @param port - Port number to bind. Defaults to 8080.
|
|
178
|
+
* @param host - Host interface to bind. Defaults to '0.0.0.0'.
|
|
179
|
+
* @returns The active Node.js HTTP server instance.
|
|
180
|
+
*/
|
|
181
|
+
listen(port?: number, host?: string): Promise<http.Server>;
|
|
182
|
+
/**
|
|
183
|
+
* Closes active HTTP server and WebSocket server listeners.
|
|
184
|
+
*/
|
|
185
|
+
close(): Promise<void>;
|
|
186
|
+
/**
|
|
187
|
+
* Handles incoming HTTP requests for authentication and discovery endpoints.
|
|
188
|
+
*
|
|
189
|
+
* @param req - Incoming HTTP request.
|
|
190
|
+
* @param res - Server HTTP response.
|
|
191
|
+
* @returns `true` if the request was handled by TetherDB; `false` if the path did not match.
|
|
192
|
+
*/
|
|
193
|
+
handleHttpRequest(req: http.IncomingMessage, res: http.ServerResponse): Promise<boolean>;
|
|
194
|
+
private getCorsHeaders;
|
|
195
|
+
private sendJson;
|
|
196
|
+
private readJsonBody;
|
|
197
|
+
private handleOptions;
|
|
198
|
+
private handleHealth;
|
|
199
|
+
private handleReady;
|
|
200
|
+
private handleMetrics;
|
|
201
|
+
private handleRegister;
|
|
202
|
+
private handleLogin;
|
|
203
|
+
private readCredentials;
|
|
204
|
+
private getClientIp;
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=server.d.ts.map
|