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 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/index.ts","../../node_modules/tsup/assets/cjs_shims.js","../../src/cli/cli.ts","../../src/shared/types.ts","../../src/server/errors.ts","../../src/server/server.ts","../../src/shared/path.ts","../../src/server/crypto.ts","../../src/server/lock.ts","../../src/server/rate-limiter.ts","../../src/shared/clock.ts","../../src/server/validate.ts","../../src/server/storage/base/app.ts","../../src/server/storage/base/storage.ts","../../src/server/storage/base/table.ts","../../src/server/storage/base/user.ts","../../src/server/storage/memory/table.ts","../../src/server/storage/memory/app.ts","../../src/server/storage/memory/storage.ts","../../src/server/storage/memory/user.ts","../../src/server/sync.ts","../../src/server/storage/file/storage.ts","../../src/server/storage/file/app.ts","../../src/server/storage/file/table.ts","../../src/server/storage/file/user.ts","../../src/server/storage/sqlite/storage.ts","../../src/server/storage/sqlite/table.ts","../../src/server/storage/sqlite/app.ts","../../src/server/storage/sqlite/user.ts","../../src/cli/args.ts","../../src/cli/backend.ts","../../src/cli/commands/apps.ts","../../src/cli/commands/help.ts","../../src/cli/commands/maintenance.ts","../../src/cli/commands/serve.ts","../../src/cli/commands/status.ts","../../src/cli/commands/tables.ts","../../src/cli/commands/users.ts"],"sourcesContent":["/**\n * TetherDB CLI — Command-line interface and administration tools.\n *\n * @module tetherdb/cli\n */\n\nexport { runCli } from './cli.js';\n","// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","#!/usr/bin/env node\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n type Storage,\n TetherServerError,\n TetherServerErrorCode,\n} from '../server/index.js';\nimport { parseCliArgs } from './args.js';\nimport { createBackend } from './backend.js';\nimport {\n handleAppsCommand,\n handleMaintenanceCommand,\n handleServeCommand,\n handleStatusCommand,\n handleTablesCommand,\n handleUsersCommand,\n printHelp,\n} from './commands/index.js';\n\n/**\n * Standard command line interface for TetherDB.\n *\n * @param args - CLI arguments (defaults to process.argv.slice(2)).\n */\nexport async function runCli(\n args: string[] = process.argv.slice(2),\n): Promise<void> {\n if (isHelpRequested(args)) {\n printHelp();\n return;\n }\n let storage: Storage | undefined;\n try {\n const { command, positionalArgs, port, host, backend, dir } =\n parseCliArgs(args);\n storage = createBackend(backend, dir);\n switch (command) {\n case 'serve':\n await handleServeCommand(storage, backend, dir, port, host);\n break;\n case 'status':\n await handleStatusCommand(storage, positionalArgs);\n break;\n case 'maintenance':\n await handleMaintenanceCommand(storage, positionalArgs);\n break;\n case 'apps':\n await handleAppsCommand(storage, positionalArgs);\n break;\n case 'tables':\n await handleTablesCommand(storage, positionalArgs);\n break;\n case 'users':\n await handleUsersCommand(storage, positionalArgs);\n break;\n default:\n throw new TetherServerError(\n TetherServerErrorCode.ConfigurationError,\n `Unknown command: \"${command}\"`,\n );\n }\n await storage.close?.();\n } catch (err) {\n console.error('Command failed:', (err as Error).message);\n await storage?.close?.();\n process.exit(1);\n }\n}\n\nif (\n process.argv[1] &&\n fileURLToPath(import.meta.url) === path.resolve(process.argv[1])\n) {\n runCli().catch((err) => {\n console.error('Failed to start TetherDB CLI:', err);\n process.exit(1);\n });\n}\n\n// -- Private Helpers --------------------------------------------------------\n\nfunction isHelpRequested(args: string[]): boolean {\n return args.some(\n (arg) =>\n arg === 'help' ||\n arg === '--help' ||\n arg === '-h' ||\n arg === '-?' ||\n arg === '/?' ||\n arg === '-help',\n );\n}\n","/**\n * The type of mutation operation performed on a record.\n */\nexport enum OperationType {\n /** Insert or update a record payload. */\n Put = 'put',\n /** Delete a record (tombstone). */\n Delete = 'delete',\n}\n\n/**\n * Represents an individual mutation operation record to be synced.\n *\n * @typeParam T - The data type of the record payload.\n */\nexport interface ChangeRecord<T = unknown> {\n /** The target table name. */\n table: string;\n /** The unique record identifier within the table. */\n id: string;\n /** The mutation operation type. */\n op: OperationType;\n /** The data payload for 'put' operations. */\n data?: T;\n /** Monotonic epoch timestamp when the change was initiated. */\n timestamp: number;\n /** Identifier of the client that originated the change. */\n clientId: string;\n /** Incremental version counter for the record. */\n version?: number;\n /** Server-assigned global sequential index. */\n seq?: number;\n}\n\n/**\n * Represents a persisted record with local metadata.\n *\n * @typeParam T - The data type of the stored record value.\n */\nexport interface StoredRecord<T = unknown> {\n /** The unique record identifier. */\n id: string;\n /** The stored value payload. */\n data: T;\n /** Epoch timestamp of the last write. */\n timestamp: number;\n /** Record revision version. */\n version: number;\n /** Flag indicating whether the record is marked as deleted (tombstone). */\n deleted?: boolean;\n /** Identifier of client that performed the write. */\n clientId?: string;\n}\n\n/**\n * Represents a single record entry in a full database snapshot.\n *\n * @typeParam T - The data type of the record payload.\n */\nexport interface SnapshotRecord<T = unknown> extends StoredRecord<T> {\n /** The table name. */\n table: string;\n}\n\n/**\n * Current wire protocol version number.\n */\nexport const PROTOCOL_VERSION = 1;\n\n/**\n * Types of messages sent from the client to the server over the WebSocket sync connection.\n */\nexport enum ClientMessageType {\n /** Authenticate connection with user token and initial sync sequence. */\n Auth = 'auth',\n /** Submit a batch of local pending changes to the server. */\n ChangeBatch = 'change_batch',\n /** Heartbeat ping message to verify connection liveness. */\n Ping = 'ping',\n}\n\n/**\n * Client authentication handshake message.\n */\nexport interface AuthClientMessage {\n type: ClientMessageType.Auth;\n /** Wire protocol version number (must equal `PROTOCOL_VERSION`). */\n protocolVersion: number;\n /** Signed authentication session token. */\n token: string;\n /** Unique client instance identifier. */\n clientId: string;\n /** Last synchronized sequence number known to the client. */\n lastSyncSeq?: number;\n /** Application namespace identifier. */\n appId: string;\n /** Optional client capabilities supported by this client session. */\n capabilities?: string[];\n}\n\n/**\n * Client mutation batch message.\n */\nexport interface ChangeBatchClientMessage {\n type: ClientMessageType.ChangeBatch;\n /** Unique client instance identifier. */\n clientId: string;\n /** Unique batch correlation identifier. */\n batchId: string;\n /** Array of change operations to apply. */\n changes: ChangeRecord[];\n}\n\n/**\n * Client heartbeat ping message.\n */\nexport interface PingClientMessage {\n type: ClientMessageType.Ping;\n}\n\n/**\n * Discriminated union of all messages sent from client to server.\n */\nexport type ClientMessage =\n | AuthClientMessage\n | ChangeBatchClientMessage\n | PingClientMessage;\n\n/**\n * Types of messages sent from the server to the client over the WebSocket sync connection.\n */\nexport enum ServerMessageType {\n /** Authentication succeeded. */\n AuthSuccess = 'auth_success',\n /** Authentication failed. */\n AuthError = 'auth_error',\n /** Full dataset snapshot sent when client connects without prior sync sequence. */\n SyncSnapshot = 'sync_snapshot',\n /** Delta diff of changes that occurred since client's lastSyncSeq. */\n SyncDiff = 'sync_diff',\n /** Confirmation that a client change batch was applied. */\n ChangeAck = 'change_ack',\n /** Real-time broadcast of changes applied by another client session of the same user. */\n BroadcastChanges = 'broadcast_changes',\n /** Heartbeat pong response. */\n Pong = 'pong',\n /** General server error notification. */\n Error = 'error',\n}\n\n/**\n * Server authentication success response message.\n */\nexport interface AuthSuccessServerMessage {\n type: ServerMessageType.AuthSuccess;\n /** Wire protocol version number. */\n protocolVersion: number;\n /** Authenticated user account identifier. */\n userId: string;\n /** Current global sequence number of the user's data on the server. */\n currentSeq: number;\n /** Refreshed session token for sliding session validity. */\n token?: string;\n /** Optional server capabilities supported by this server instance. */\n capabilities?: string[];\n}\n\n/**\n * Server authentication failure response message.\n */\nexport interface AuthErrorServerMessage {\n type: ServerMessageType.AuthError;\n /** Error description message. */\n message: string;\n}\n\n/**\n * Server full dataset snapshot message.\n */\nexport interface SyncSnapshotServerMessage {\n type: ServerMessageType.SyncSnapshot;\n /** Sequence number corresponding to the snapshot state. */\n seq: number;\n /** All active records across tables. */\n snapshot: SnapshotRecord[];\n}\n\n/**\n * Server incremental delta diff message.\n */\nexport interface SyncDiffServerMessage {\n type: ServerMessageType.SyncDiff;\n /** Starting sequence number (exclusive). */\n fromSeq: number;\n /** Ending sequence number (inclusive). */\n toSeq: number;\n /** Array of applied changes in sequential order. */\n changes: ChangeRecord[];\n}\n\n/**\n * Server batch acknowledgement message.\n */\nexport interface ChangeAckServerMessage {\n type: ServerMessageType.ChangeAck;\n /** The correlation identifier of the acknowledged batch. */\n batchId: string;\n /** The new global sequence number after applying the batch. */\n appliedSeq: number;\n}\n\n/**\n * Server real-time changes broadcast message.\n */\nexport interface BroadcastChangesServerMessage {\n type: ServerMessageType.BroadcastChanges;\n /** Client ID that originated the change. */\n fromClientId: string;\n /** Global sequence number assigned to these changes. */\n seq: number;\n /** Array of applied changes. */\n changes: ChangeRecord[];\n}\n\n/**\n * Server heartbeat pong response message.\n */\nexport interface PongServerMessage {\n type: ServerMessageType.Pong;\n}\n\n/**\n * Server error notification message.\n */\nexport interface ErrorServerMessage {\n type: ServerMessageType.Error;\n /** Error description message. */\n message: string;\n}\n\n/**\n * Discriminated union of all messages sent from server to client.\n */\nexport type ServerMessage =\n | AuthSuccessServerMessage\n | AuthErrorServerMessage\n | SyncSnapshotServerMessage\n | SyncDiffServerMessage\n | ChangeAckServerMessage\n | BroadcastChangesServerMessage\n | PongServerMessage\n | ErrorServerMessage;\n\n/**\n * Metadata stored locally tracking synchronization progress.\n */\nexport interface SyncMetadata {\n /** The latest sequence number synchronized with the server. */\n lastSyncSeq: number;\n /** Epoch timestamp of the last successful synchronization. */\n lastSyncTimestamp: number;\n /** Unique client instance identifier. */\n clientId: string;\n}\n","/**\n * Error codes identifying broader server-side error categories.\n */\nexport enum TetherServerErrorCode {\n /** The requested input or parameter is invalid or malformed. */\n InvalidInput,\n /** The requested resource (user, application, or table) was not found. */\n NotFound,\n /** The resource (user, application, or table) already exists. */\n AlreadyExists,\n /** Authentication is missing, invalid, or expired. */\n Unauthorized,\n /** The provided credentials are invalid. */\n AuthenticationFailed,\n /** A storage capacity or payload size limit was exceeded. */\n LimitExceeded,\n /** Configuration or command-line option is invalid. */\n ConfigurationError,\n /** The requested operation is not supported by this backend or engine. */\n NotSupported,\n /** An unexpected internal server error occurred. */\n InternalError,\n}\n\n/**\n * Dedicated error class for TetherDB server errors.\n * Error messages are user-safe and contain no internal technical details.\n */\nexport class TetherServerError extends Error {\n /** Error category code identifying the broad error type. */\n readonly code: TetherServerErrorCode;\n\n /**\n * Initializes a new `TetherServerError`.\n *\n * @param code - The error category code.\n * @param message - User-safe error description message.\n */\n constructor(code: TetherServerErrorCode, message?: string) {\n super(message ?? getDefaultServerErrorMessage(code));\n this.name = 'TetherServerError';\n this.code = code;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n// -- Private Helpers --------------------------------------------------------\n\nfunction getDefaultServerErrorMessage(code: TetherServerErrorCode): string {\n switch (code) {\n case TetherServerErrorCode.InvalidInput:\n return 'Invalid request parameter';\n case TetherServerErrorCode.NotFound:\n return 'Requested resource not found';\n case TetherServerErrorCode.AlreadyExists:\n return 'Resource already exists';\n case TetherServerErrorCode.Unauthorized:\n return 'Authentication required';\n case TetherServerErrorCode.AuthenticationFailed:\n return 'Authentication failed';\n case TetherServerErrorCode.LimitExceeded:\n return 'Request or resource limit exceeded';\n case TetherServerErrorCode.ConfigurationError:\n return 'Server configuration error';\n case TetherServerErrorCode.NotSupported:\n return 'Operation not supported';\n case TetherServerErrorCode.InternalError:\n return 'Internal server error';\n }\n}\n","import * as http from 'node:http';\nimport { WebSocketServer } from 'ws';\nimport { normalizeBasePath } from '../shared/path.js';\nimport { verifyDummyPasswordHash } from './crypto.js';\nimport { TetherServerError, TetherServerErrorCode } from './errors.js';\nimport { acquireServerLock, type ServerLockHandle } from './lock.js';\nimport { RateLimiter } from './rate-limiter.js';\nimport type { Storage, UserStorage } from './storage/index.js';\nimport { MemoryStorage } from './storage/memory/index.js';\nimport { Sync } from './sync.js';\nimport { normalizePassword, normalizeUsername } from './validate.js';\n\n/**\n * Rate limiting and resource control options for authentication endpoints and sync streams.\n */\nexport interface RateLimitOptions {\n /** Maximum login attempts per IP within the time window (defaults to 100). */\n ipLoginMaxRequests?: number;\n /** Maximum login attempts per target username within the time window (defaults to 20). */\n userLoginMaxRequests?: number;\n /** Maximum registration attempts per IP within the time window (defaults to 100). */\n ipRegisterMaxRequests?: number;\n /** Maximum WebSocket connection handshakes per IP within the time window (defaults to 100). */\n ipSyncMaxRequests?: number;\n /** Maximum concurrent active WebSocket connections allowed per user channel (defaults to 20). */\n maxConcurrentConnectionsPerUser?: number;\n /** Maximum duration in milliseconds to wait for authentication before terminating socket (defaults to 10,000ms). */\n authTimeoutMs?: number;\n /** Sliding window duration in milliseconds (defaults to 60,000ms / 1 minute). */\n windowMs?: number;\n /** Consecutive failed attempts before progressive backoff begins (defaults to 5). */\n maxFailures?: number;\n /** Initial backoff duration in milliseconds (defaults to 1,000ms). */\n initialBackoffMs?: number;\n /** Maximum backoff duration in milliseconds (defaults to 900,000ms / 15 minutes). */\n maxBackoffMs?: number;\n}\n\n/**\n * Options for configuring Cross-Origin Resource Sharing (CORS) on HTTP endpoints.\n */\nexport interface CorsOptions {\n /**\n * Allowed origin(s). Can be `'*'` for unrestricted access, a specific origin string (e.g. `'https://example.com'`),\n * an array of allowed origin strings, `true` to reflect the request's `Origin` header, or `false` to disable CORS headers.\n * Defaults to `'*'`.\n */\n origin?: string | string[] | boolean;\n /** Whether to set `Access-Control-Allow-Credentials: true` (defaults to false). */\n credentials?: boolean;\n /** Allowed request headers for preflight OPTIONS checks (defaults to `['Content-Type', 'Authorization']`). */\n allowedHeaders?: string[];\n /** Exposed response headers (Access-Control-Expose-Headers). */\n exposedHeaders?: string[];\n /** Maximum age in seconds to cache preflight responses (Access-Control-Max-Age). */\n maxAge?: number;\n}\n\n/**\n * Pluggable logger interface for TetherServer logging.\n */\nexport interface TetherLogger {\n /** Logs debug information. */\n debug(message: string, ...args: unknown[]): void;\n /** Logs operational information. */\n info(message: string, ...args: unknown[]): void;\n /** Logs warning conditions. */\n warn(message: string, ...args: unknown[]): void;\n /** Logs error conditions. */\n error(message: string, ...args: unknown[]): void;\n}\n\n/**\n * Configuration options for the TetherServer.\n */\nexport interface TetherServerOptions {\n /** Custom storage instance. Defaults to MemoryStorage if not defined. */\n storage?: Storage;\n /** Base path for HTTP REST endpoints (defaults to ''). */\n basePath?: string;\n /** Path for WebSocket upgrade requests (defaults to '/sync'). */\n webSocketPath?: string;\n /** Whether user self-registration is allowed via `/auth/register` (defaults to true). */\n allowRegistration?: boolean;\n /** Rate limiting options for auth and sync endpoints, or `false` to disable rate limiting (defaults to true). */\n rateLimiting?: boolean | RateLimitOptions;\n /** Whether to trust the `X-Forwarded-For` header for resolving client IP addresses (defaults to false). */\n trustProxy?: boolean;\n /** CORS options for HTTP endpoints, `false` to disable CORS headers, or `true` for default permissive CORS (defaults to true). */\n cors?: boolean | CorsOptions;\n /** Optional custom logger instance, or `false` to silence internal server logs (defaults to `console`). */\n logger?: TetherLogger | false;\n}\n\n/**\n * Options for starting the standard server launcher.\n */\nexport interface StartServerOptions extends TetherServerOptions {\n /** Port number to bind (defaults to 8080 or PORT environment variable). */\n port?: number;\n /** Host interface to bind (defaults to '0.0.0.0'). */\n host?: string;\n}\n\n/**\n * Result returned when launching a server using `startServer()`.\n */\nexport interface RunningServer {\n /** The TetherServer instance. */\n server: TetherServer;\n /** The running Node.js HTTP server instance. */\n httpServer: http.Server;\n /** Bound port number. */\n port: number;\n /** Bound host address. */\n host: string;\n /** Closes both HTTP and WebSocket server cleanly. */\n close(): Promise<void>;\n}\n\n/**\n * Starts a complete standalone HTTP & WebSocket synchronization server.\n *\n * @param options - Start options including port, host, storage, and limits.\n * @returns Handle to the running server.\n */\nexport async function startServer(\n options: StartServerOptions = {},\n): Promise<RunningServer> {\n const port =\n options.port ??\n (process.env.PORT ? Number.parseInt(process.env.PORT, 10) : 8080);\n const host = options.host ?? '0.0.0.0';\n\n const server = new TetherServer(options);\n const httpServer = await server.listen(port, host);\n const addr = httpServer.address();\n const boundPort = typeof addr === 'object' && addr ? addr.port : port;\n\n return {\n server,\n httpServer,\n port: boundPort,\n host,\n close: async () => {\n await server.close();\n },\n };\n}\n\n/**\n * Unified HTTP and WebSocket server handling authentication endpoints (`/auth/register`, `/auth/login`)\n * and real-time streaming connections (`/sync`).\n */\nexport class TetherServer {\n /** Underlying storage engine for users, apps, and tables. */\n readonly storage: Storage;\n /** Real-time synchronization connection and broadcast coordinator. */\n readonly sync: Sync;\n /** Base path for HTTP REST endpoints. */\n readonly basePath: string;\n /** Path for WebSocket upgrade requests. */\n readonly webSocketPath: string;\n readonly trustProxy: boolean;\n private readonly allowRegistration: boolean;\n private readonly corsConfig: CorsOptions | null;\n private readonly logger: TetherLogger | null;\n private readonly ipLoginLimiter: RateLimiter | null;\n private readonly userLoginLimiter: RateLimiter | null;\n private readonly ipRegisterLimiter: RateLimiter | null;\n private _httpServer: http.Server | null = null;\n private _webSocketServer: WebSocketServer | null = null;\n private lockHandle: ServerLockHandle | null = null;\n\n /**\n * Initializes a new TetherServer instance.\n *\n * @param options - Configuration options for storage, endpoints, and rate limiting.\n */\n constructor(options: TetherServerOptions = {}) {\n this.storage = options.storage ?? new MemoryStorage();\n this.basePath = normalizeBasePath(options.basePath ?? '');\n this.webSocketPath = options.webSocketPath ?? `${this.basePath}/sync`;\n this.allowRegistration = options.allowRegistration ?? true;\n this.trustProxy = options.trustProxy ?? false;\n this.corsConfig =\n options.cors === false\n ? null\n : typeof options.cors === 'object'\n ? options.cors\n : {};\n this.logger = options.logger === false ? null : (options.logger ?? console);\n\n const rateLimitConfig = options.rateLimiting ?? true;\n if (rateLimitConfig === false) {\n this.ipLoginLimiter = null;\n this.userLoginLimiter = null;\n this.ipRegisterLimiter = null;\n this.sync = new Sync(this.storage, {\n maxConcurrentConnectionsPerUser: 1_000,\n authTimeoutMs: 0,\n rateLimiter: null,\n logger: this.logger,\n });\n } else {\n const opts: RateLimitOptions =\n typeof rateLimitConfig === 'object' ? rateLimitConfig : {};\n const windowMs = opts.windowMs ?? 60_000;\n const maxFailures = opts.maxFailures ?? 5;\n const initialBackoffMs = opts.initialBackoffMs ?? 1_000;\n const maxBackoffMs = opts.maxBackoffMs ?? 900_000;\n\n this.ipLoginLimiter = new RateLimiter({\n windowMs,\n maxRequests: opts.ipLoginMaxRequests ?? 100,\n maxFailures,\n initialBackoffMs,\n maxBackoffMs,\n });\n this.userLoginLimiter = new RateLimiter({\n windowMs,\n maxRequests: opts.userLoginMaxRequests ?? 20,\n maxFailures,\n initialBackoffMs,\n maxBackoffMs,\n });\n this.ipRegisterLimiter = new RateLimiter({\n windowMs,\n maxRequests: opts.ipRegisterMaxRequests ?? 100,\n });\n\n const syncLimiter = new RateLimiter({\n windowMs,\n maxRequests: opts.ipSyncMaxRequests ?? 100,\n maxFailures,\n initialBackoffMs,\n maxBackoffMs,\n });\n\n this.sync = new Sync(this.storage, {\n maxConcurrentConnectionsPerUser:\n opts.maxConcurrentConnectionsPerUser ?? 20,\n authTimeoutMs: opts.authTimeoutMs ?? 10_000,\n rateLimiter: syncLimiter,\n logger: this.logger,\n });\n }\n }\n\n /**\n * Active Node.js HTTP server instance, or `null` if not listening.\n */\n get httpServer(): http.Server | null {\n return this._httpServer;\n }\n\n /**\n * Active WebSocketServer instance, or `null` if not listening.\n */\n get webSocketServer(): WebSocketServer | null {\n return this._webSocketServer;\n }\n\n /**\n * Declares an application and its tables.\n * Registers the application and any declared tables if not already present.\n *\n * @param appId - Application identifier.\n * @param tables - Array of table names.\n */\n async declareApp(appId: string, tables: string[] = []): Promise<void> {\n let app = await this.storage.getApp(appId);\n if (!app) {\n app = await this.storage.createApp(appId);\n }\n for (const table of tables) {\n const existing = await app.getTable(table);\n if (!existing) {\n await app.createTable(table);\n }\n }\n }\n\n /**\n * Declares a user account with the specified username and password.\n * Creates the user if not already registered, or updates the existing user's password.\n *\n * @param username - Username for the account.\n * @param password - Plaintext password for the account.\n * @returns UserStorage handle for the declared user.\n */\n async declareUser(username: string, password: string): Promise<UserStorage> {\n const user = await this.storage.getUserByUsername(username);\n if (user) {\n await user.changePassword(password);\n return user;\n }\n return this.storage.createUser(username, password);\n }\n\n /**\n * Attaches WebSocket synchronization handling to an existing HTTP server.\n *\n * @param server - The HTTP server instance to attach to.\n */\n attach(server: http.Server): void {\n if (!this._webSocketServer) {\n this._webSocketServer = new WebSocketServer({\n noServer: true,\n perMessageDeflate: {\n zlibDeflateOptions: {\n level: 6,\n memLevel: 8,\n },\n threshold: 1024,\n clientNoContextTakeover: true,\n serverNoContextTakeover: true,\n },\n });\n this._webSocketServer.on('connection', (ws, req) => {\n const ip = req ? this.getClientIp(req) : '127.0.0.1';\n this.sync.handleConnection(ws, ip);\n });\n }\n server.on('upgrade', (req, socket, head) => {\n const url = new URL(\n req.url ?? '',\n `http://${req.headers.host ?? 'localhost'}`,\n );\n if (url.pathname === this.webSocketPath) {\n this._webSocketServer?.handleUpgrade(req, socket, head, (ws) => {\n this._webSocketServer?.emit('connection', ws, req);\n });\n } else {\n socket.destroy();\n }\n });\n }\n\n /**\n * Starts the HTTP and WebSocket server listening on the specified port and host.\n *\n * @param port - Port number to bind. Defaults to 8080.\n * @param host - Host interface to bind. Defaults to '0.0.0.0'.\n * @returns The active Node.js HTTP server instance.\n */\n async listen(port = 8080, host = '0.0.0.0'): Promise<http.Server> {\n const storageBaseDir = (\n this.storage as { baseDir?: string; inMemory?: boolean }\n ).baseDir;\n const isMemory =\n (this.storage as { inMemory?: boolean }).inMemory ??\n this.storage instanceof MemoryStorage;\n\n if (storageBaseDir && !isMemory) {\n const status = await this.storage.getStatus();\n this.lockHandle = acquireServerLock(storageBaseDir, {\n port,\n host,\n backend: status.backend,\n });\n }\n\n return new Promise<http.Server>((resolve, reject) => {\n this._httpServer = http.createServer(async (req, res) => {\n const handled = await this.handleHttpRequest(req, res);\n if (!handled) {\n this.sendJson(res, 404, { error: 'Not found' });\n }\n });\n this.attach(this._httpServer);\n this._httpServer.listen(port, host, () => {\n if (this._httpServer) {\n const addr = this._httpServer.address();\n const actualPort =\n typeof addr === 'object' && addr ? addr.port : port;\n if (\n this.lockHandle &&\n storageBaseDir &&\n this.lockHandle.info.port !== actualPort\n ) {\n this.lockHandle.release();\n this.lockHandle = acquireServerLock(storageBaseDir, {\n port: actualPort,\n host,\n backend: this.lockHandle.info.backend,\n });\n }\n resolve(this._httpServer);\n }\n });\n this._httpServer.on('error', (err) => {\n if (this.lockHandle) {\n this.lockHandle.release();\n this.lockHandle = null;\n }\n reject(err);\n });\n });\n }\n\n /**\n * Closes active HTTP server and WebSocket server listeners.\n */\n async close(): Promise<void> {\n if (this.lockHandle) {\n this.lockHandle.release();\n this.lockHandle = null;\n }\n return new Promise<void>((resolve, reject) => {\n if (this._webSocketServer) {\n for (const client of this._webSocketServer.clients) {\n try {\n client.terminate();\n } catch {\n // Ignore termination errors on shutdown\n }\n }\n this._webSocketServer.close();\n this._webSocketServer = null;\n }\n if (this._httpServer) {\n try {\n this._httpServer.closeAllConnections?.();\n } catch {\n // Ignore\n }\n this._httpServer.close((err) => {\n this._httpServer = null;\n if (err) reject(err);\n else resolve();\n });\n } else {\n resolve();\n }\n });\n }\n\n /**\n * Handles incoming HTTP requests for authentication and discovery endpoints.\n *\n * @param req - Incoming HTTP request.\n * @param res - Server HTTP response.\n * @returns `true` if the request was handled by TetherDB; `false` if the path did not match.\n */\n async handleHttpRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n ): Promise<boolean> {\n const url = new URL(\n req.url ?? '/',\n `http://${req.headers.host ?? 'localhost'}`,\n );\n const method = req.method?.toUpperCase();\n\n if (method === 'OPTIONS') {\n this.handleOptions(req, res);\n return true;\n }\n\n try {\n if (method === 'GET' && url.pathname === `${this.basePath}/health`) {\n this.handleHealth(req, res);\n return true;\n }\n\n if (method === 'GET' && url.pathname === `${this.basePath}/ready`) {\n await this.handleReady(req, res);\n return true;\n }\n\n if (method === 'GET' && url.pathname === `${this.basePath}/metrics`) {\n await this.handleMetrics(req, res);\n return true;\n }\n\n if (\n this.allowRegistration &&\n method === 'POST' &&\n url.pathname === `${this.basePath}/auth/register`\n ) {\n await this.handleRegister(req, res);\n return true;\n }\n\n if (method === 'POST' && url.pathname === `${this.basePath}/auth/login`) {\n await this.handleLogin(req, res);\n return true;\n }\n\n return false;\n } catch (err) {\n const status = getHttpStatusForError(err);\n if (status >= 500) {\n this.logger?.error('Error handling HTTP request:', err);\n } else {\n this.logger?.debug('Client error handling HTTP request:', err);\n }\n const msg = err instanceof Error ? err.message : 'Internal server error';\n this.sendJson(res, status, { error: msg }, req);\n return true;\n }\n }\n\n // -- Private Helpers ------------------------------------------------------\n\n private getCorsHeaders(req?: http.IncomingMessage): Record<string, string> {\n if (!this.corsConfig) return {};\n\n const headers: Record<string, string> = {\n 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',\n 'Access-Control-Allow-Headers': (\n this.corsConfig.allowedHeaders ?? ['Content-Type', 'Authorization']\n ).join(', '),\n };\n\n if (\n this.corsConfig.exposedHeaders &&\n this.corsConfig.exposedHeaders.length > 0\n ) {\n headers['Access-Control-Expose-Headers'] =\n this.corsConfig.exposedHeaders.join(', ');\n }\n\n if (this.corsConfig.maxAge !== undefined) {\n headers['Access-Control-Max-Age'] = String(this.corsConfig.maxAge);\n }\n\n const reqOrigin = req?.headers.origin;\n const origin = this.corsConfig.origin ?? '*';\n\n if (origin === '*') {\n if (this.corsConfig.credentials) {\n if (reqOrigin) {\n headers['Access-Control-Allow-Origin'] = reqOrigin;\n headers.Vary = 'Origin';\n }\n } else {\n headers['Access-Control-Allow-Origin'] = '*';\n }\n } else if (typeof origin === 'string') {\n headers['Access-Control-Allow-Origin'] = origin;\n headers.Vary = 'Origin';\n } else if (Array.isArray(origin)) {\n if (reqOrigin && origin.includes(reqOrigin)) {\n headers['Access-Control-Allow-Origin'] = reqOrigin;\n headers.Vary = 'Origin';\n }\n } else if (origin === true && reqOrigin) {\n headers['Access-Control-Allow-Origin'] = reqOrigin;\n headers.Vary = 'Origin';\n }\n\n if (this.corsConfig.credentials) {\n headers['Access-Control-Allow-Credentials'] = 'true';\n }\n\n return headers;\n }\n\n private sendJson(\n res: http.ServerResponse,\n status: number,\n data: unknown,\n req?: http.IncomingMessage,\n ) {\n res.writeHead(status, {\n 'Content-Type': 'application/json',\n ...this.getCorsHeaders(req),\n });\n res.end(JSON.stringify(data));\n }\n\n private async readJsonBody(req: http.IncomingMessage): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let body = '';\n req.on('data', (chunk) => {\n body += chunk;\n if (body.length > 1024 * 1024) {\n reject(\n new TetherServerError(\n TetherServerErrorCode.LimitExceeded,\n 'Payload exceeds maximum allowed size',\n ),\n );\n }\n });\n req.on('end', () => {\n try {\n resolve(body ? JSON.parse(body) : {});\n } catch {\n reject(\n new TetherServerError(\n TetherServerErrorCode.InvalidInput,\n 'Invalid JSON payload',\n ),\n );\n }\n });\n req.on('error', reject);\n });\n }\n\n private handleOptions(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n ): void {\n res.writeHead(204, this.getCorsHeaders(req));\n res.end();\n }\n\n private handleHealth(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n ): void {\n this.sendJson(\n res,\n 200,\n {\n status: 'ok',\n uptime: process.uptime(),\n },\n req,\n );\n }\n\n private async handleReady(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n ): Promise<void> {\n try {\n await this.storage.getApps();\n this.sendJson(res, 200, { status: 'ready' }, req);\n } catch (err) {\n const message =\n err instanceof Error ? err.message : 'Storage unavailable';\n this.logger?.error('Storage readiness error:', err);\n this.sendJson(res, 503, { status: 'unready', error: message }, req);\n }\n }\n\n private async handleMetrics(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n ): Promise<void> {\n const apps = await this.storage.getApps();\n this.sendJson(\n res,\n 200,\n {\n uptime: process.uptime(),\n connectedClients: this.sync.connectedClientsCount,\n appsCount: apps.length,\n memoryUsage: process.memoryUsage(),\n },\n req,\n );\n }\n\n private async handleRegister(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n ): Promise<void> {\n const ip = this.getClientIp(req);\n if (this.ipRegisterLimiter && !this.ipRegisterLimiter.consume(ip)) {\n this.sendJson(res, 429, { error: 'Too many registration requests' }, req);\n return;\n }\n\n const credentials = await this.readCredentials(req, res);\n if (!credentials) return;\n\n try {\n const user = await this.storage.createUser(\n credentials.username,\n credentials.password,\n );\n const token = await user.createToken();\n this.sendJson(\n res,\n 201,\n {\n userId: user.id,\n username: user.username,\n token,\n },\n req,\n );\n } catch (err) {\n const status = getHttpStatusForError(err);\n if (status >= 500) {\n this.logger?.error('Registration error:', err);\n } else {\n this.logger?.debug('Client registration error:', err);\n }\n const msg = err instanceof Error ? err.message : 'Registration error';\n this.sendJson(res, status, { error: msg }, req);\n }\n }\n\n private async handleLogin(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n ): Promise<void> {\n const ip = this.getClientIp(req);\n if (this.ipLoginLimiter && !this.ipLoginLimiter.consume(ip)) {\n this.sendJson(res, 429, { error: 'Too many login attempts' }, req);\n return;\n }\n\n const credentials = await this.readCredentials(req, res);\n if (!credentials) return;\n\n const userKey = `${ip}:${credentials.username}`;\n if (this.userLoginLimiter && !this.userLoginLimiter.consume(userKey)) {\n this.sendJson(\n res,\n 429,\n { error: 'Too many login attempts for this account' },\n req,\n );\n return;\n }\n\n const user = await this.storage.getUserByUsername(credentials.username);\n const valid = user\n ? await user.verifyPassword(credentials.password)\n : await verifyDummyPasswordHash(credentials.password);\n\n if (!user || !valid) {\n this.ipLoginLimiter?.recordFailure(ip);\n this.userLoginLimiter?.recordFailure(userKey);\n this.sendJson(\n res,\n 401,\n {\n error: 'Invalid username or password',\n },\n req,\n );\n return;\n }\n\n this.ipLoginLimiter?.reset(ip);\n this.userLoginLimiter?.reset(userKey);\n\n const token = await user.createToken();\n this.sendJson(\n res,\n 200,\n {\n userId: user.id,\n username: user.username,\n token,\n },\n req,\n );\n }\n\n private async readCredentials(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n ): Promise<{ username: string; password: string } | null> {\n const body = await this.readJsonBody(req);\n const { username, password } = body as {\n username?: string;\n password?: string;\n };\n const normUsername = normalizeUsername(username ?? '');\n const normPassword = normalizePassword(password ?? '');\n if (!normUsername || !normPassword) {\n this.sendJson(\n res,\n 400,\n {\n error: 'Missing or invalid required field: username and password',\n },\n req,\n );\n return null;\n }\n return { username: normUsername, password: normPassword };\n }\n\n private getClientIp(req: http.IncomingMessage): string {\n if (this.trustProxy) {\n const forwarded = req.headers['x-forwarded-for'];\n if (typeof forwarded === 'string') {\n const first = forwarded.split(',')[0].trim();\n if (first) return first;\n }\n }\n return req.socket.remoteAddress ?? '127.0.0.1';\n }\n}\n\n// -- Private Helpers --------------------------------------------------------\n\nfunction getHttpStatusForError(err: unknown): number {\n if (err instanceof TetherServerError) {\n switch (err.code) {\n case TetherServerErrorCode.InvalidInput:\n case TetherServerErrorCode.ConfigurationError:\n return 400;\n case TetherServerErrorCode.Unauthorized:\n case TetherServerErrorCode.AuthenticationFailed:\n return 401;\n case TetherServerErrorCode.NotFound:\n return 404;\n case TetherServerErrorCode.AlreadyExists:\n return 409;\n case TetherServerErrorCode.LimitExceeded:\n return 413;\n case TetherServerErrorCode.NotSupported:\n return 501;\n case TetherServerErrorCode.InternalError:\n return 500;\n }\n }\n return 500;\n}\n","/**\n * Utility functions for path normalization across client and server.\n */\n\n/**\n * Normalizes a base path ensuring it starts with a leading slash and has no trailing slash.\n * An empty string or single slash normalizes to an empty string.\n *\n * @param path - The base path to normalize.\n * @returns Normalized base path (e.g. '/api' or '').\n */\nexport function normalizeBasePath(path: string): string {\n if (path === '' || path === '/') return '';\n if (path.endsWith('/')) path = path.slice(0, path.length - 1);\n if (!path.startsWith('/')) path = `/${path}`;\n return path === '/' ? '' : path;\n}\n","import * as crypto from 'node:crypto';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n/** Default token expiration window in seconds (7 days). */\nexport const DEFAULT_TOKEN_EXPIRES_IN = 7 * 24 * 60 * 60;\n\n/**\n * Loads a persistent HMAC signing secret from `<baseDir>/.secret`, or generates and saves one\n * with restricted permissions (`0o600`) if it does not yet exist.\n *\n * @param baseDir - Directory path where `.secret` is stored.\n * @returns 64-character hex secret string.\n */\nexport function getOrCreateKeyfileSecret(baseDir: string): string {\n const secretPath = path.join(baseDir, '.secret');\n try {\n if (fs.existsSync(secretPath)) {\n const existing = fs.readFileSync(secretPath, 'utf-8').trim();\n if (existing.length >= 32) {\n return existing;\n }\n }\n } catch {\n // Ignore read error and fallback to creation\n }\n\n const generated = crypto.randomBytes(32).toString('hex');\n try {\n fs.mkdirSync(baseDir, { recursive: true });\n fs.writeFileSync(secretPath, generated, {\n encoding: 'utf-8',\n mode: 0o600,\n });\n } catch {\n // If writing fails, return generated in-memory secret\n }\n return generated;\n}\n\n/**\n * Hashes a plaintext password using standard scrypt key derivation.\n *\n * @param password - The plaintext password to hash.\n * @returns Formatted hash string `scrypt$salt$derivedKey`.\n */\nexport async function hashPassword(password: string): Promise<string> {\n const salt = crypto.randomBytes(16).toString('hex');\n return new Promise((resolve, reject) => {\n crypto.scrypt(\n password.normalize('NFKC'),\n salt,\n 64,\n SCRYPT_OPTIONS,\n (err, derivedKey) => {\n if (err) return reject(err);\n resolve(`scrypt$${salt}$${derivedKey.toString('hex')}`);\n },\n );\n });\n}\n\n/**\n * Verifies a plaintext password against a stored scrypt hash using timing-safe comparison.\n *\n * @param password - The plaintext password to check.\n * @param storedHash - Stored hash string `scrypt$salt$derivedKey`.\n * @returns `true` if password matches; otherwise `false`.\n */\nexport async function verifyPasswordHash(\n password: string,\n storedHash: string,\n): Promise<boolean> {\n if (!storedHash || typeof storedHash !== 'string') return false;\n const parts = storedHash.split('$');\n if (parts.length !== 3 || parts[0] !== 'scrypt') return false;\n\n const [, salt, expectedHex] = parts;\n return new Promise((resolve) => {\n crypto.scrypt(\n password.normalize('NFKC'),\n salt,\n 64,\n SCRYPT_OPTIONS,\n (err, derivedKey) => {\n if (err) return resolve(false);\n const expectedBuf = Buffer.from(expectedHex, 'hex');\n if (derivedKey.length !== expectedBuf.length) return resolve(false);\n resolve(crypto.timingSafeEqual(derivedKey, expectedBuf));\n },\n );\n });\n}\n\n/**\n * Performs a constant-time dummy password verification to prevent user enumeration timing attacks.\n *\n * @param password - The plaintext password supplied by client.\n * @returns Always resolves to `false`.\n */\nexport async function verifyDummyPasswordHash(\n password: string,\n): Promise<boolean> {\n if (!dummyPasswordHashPromise) {\n dummyPasswordHashPromise = hashPassword('TetherDB:dummy_seed_password');\n }\n const dummyHash = await dummyPasswordHashPromise;\n return verifyPasswordHash(password, dummyHash);\n}\n\n/**\n * Generates a signed, URL-safe session token.\n *\n * @param userId - User account identifier.\n * @param username - Normalized username.\n * @param secret - Signing secret.\n * @param expiresInSeconds - Token expiration duration.\n * @returns Signed token string.\n */\nexport function createSessionToken(\n userId: string,\n username: string,\n secret: string,\n expiresInSeconds = DEFAULT_TOKEN_EXPIRES_IN,\n): string {\n const expiresAt = Math.floor(Date.now() / 1000) + expiresInSeconds;\n const payload = JSON.stringify({\n userId,\n username,\n expiresAt,\n });\n const payloadB64 = Buffer.from(payload, 'utf-8').toString('base64url');\n const signature = crypto\n .createHmac('sha256', secret)\n .update(payloadB64)\n .digest('base64url');\n return `${payloadB64}.${signature}`;\n}\n\n/**\n * Verifies a signed session token and returns decoded payload.\n *\n * @param token - Token string to verify.\n * @param secret - Signing secret.\n * @returns Decoded payload or `null` if invalid or expired.\n */\nexport function verifySessionToken(\n token: string,\n secret: string,\n): { userId: string; username: string; expiresAt: number } | null {\n if (!token || typeof token !== 'string') return null;\n const parts = token.split('.');\n if (parts.length !== 2) return null;\n\n const [payloadB64, signature] = parts;\n const expectedSig = crypto\n .createHmac('sha256', secret)\n .update(payloadB64)\n .digest('base64url');\n\n if (\n signature.length !== expectedSig.length ||\n !crypto.timingSafeEqual(\n Buffer.from(signature, 'utf-8'),\n Buffer.from(expectedSig, 'utf-8'),\n )\n ) {\n return null;\n }\n\n try {\n const raw = Buffer.from(payloadB64, 'base64url').toString('utf-8');\n const parsed = JSON.parse(raw);\n if (\n typeof parsed !== 'object' ||\n parsed === null ||\n typeof parsed.userId !== 'string' ||\n !parsed.userId ||\n typeof parsed.username !== 'string' ||\n !parsed.username ||\n typeof parsed.expiresAt !== 'number' ||\n !Number.isFinite(parsed.expiresAt) ||\n parsed.expiresAt < Math.floor(Date.now() / 1000)\n ) {\n return null;\n }\n return {\n userId: parsed.userId,\n username: parsed.username,\n expiresAt: parsed.expiresAt,\n };\n } catch {\n return null;\n }\n}\n\n// -- Private Helpers --------------------------------------------------------\n\n/** Default scrypt parameters matching standard cryptographic best practices. */\nconst SCRYPT_OPTIONS: crypto.ScryptOptions = {\n N: process.env.NODE_ENV === 'test' ? 512 : 16384,\n r: 8,\n p: 1,\n maxmem: 32 * 1024 * 1024,\n};\n\nlet dummyPasswordHashPromise: Promise<string> | null = null;\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { TetherServerError, TetherServerErrorCode } from './errors.js';\n\n/**\n * Metadata recorded inside the server lockfile.\n */\nexport interface ServerLockInfo {\n /** Process identifier running the server. */\n pid: number;\n /** Port number the server is bound to. */\n port: number;\n /** Host interface the server is bound to. */\n host: string;\n /** Storage backend type ('sqlite', 'file', or 'memory'). */\n backend: string;\n /** Epoch timestamp when the server acquired the lock. */\n startedAt: number;\n}\n\n/**\n * Handle representing an active exclusive server lock.\n */\nexport interface ServerLockHandle {\n /** Lock metadata information. */\n readonly info: ServerLockInfo;\n /** Path to the active lockfile on disk. */\n readonly lockPath: string;\n /** Releases the lock and removes the lockfile. */\n release(): void;\n}\n\n/**\n * Checks whether a given operating system process ID is currently alive.\n *\n * @param pid - Process ID to check.\n * @returns `true` if the process is active; otherwise `false`.\n */\nexport function isProcessAlive(pid: number): boolean {\n if (!pid || pid <= 0) return false;\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n return code === 'EPERM';\n }\n}\n\n/**\n * Reads active server lock metadata from a storage base directory if present and live.\n * Stale locks from terminated processes are ignored.\n *\n * @param baseDir - Storage base directory.\n * @returns ServerLockInfo if an active server is running, or `null`.\n */\nexport function readServerLock(baseDir: string): ServerLockInfo | null {\n const lockPath = path.join(baseDir, 'server.lock');\n try {\n if (!fs.existsSync(lockPath)) return null;\n const content = fs.readFileSync(lockPath, 'utf-8');\n const info = JSON.parse(content) as ServerLockInfo;\n if (typeof info.pid === 'number' && isProcessAlive(info.pid)) {\n return info;\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Acquires an exclusive server lock on the specified directory to prevent multiple instances\n * from running against the same data storage.\n *\n * @param baseDir - Directory path where the lockfile will be maintained.\n * @param details - Port, host, and backend details to write to the lockfile.\n * @returns ServerLockHandle representing the active lock.\n * @throws TetherServerError if another active server already holds the lock.\n */\nexport function acquireServerLock(\n baseDir: string,\n details: { port: number; host: string; backend: string },\n): ServerLockHandle {\n fs.mkdirSync(baseDir, { recursive: true });\n const lockPath = path.join(baseDir, 'server.lock');\n\n const existing = readServerLock(baseDir);\n if (existing && existing.pid !== process.pid) {\n throw new TetherServerError(\n TetherServerErrorCode.AlreadyExists,\n 'A TetherDB server is already running on this data directory',\n );\n }\n\n // If a stale lockfile exists from a dead process, remove it\n if (fs.existsSync(lockPath)) {\n try {\n fs.unlinkSync(lockPath);\n } catch {\n // Ignore\n }\n }\n\n const info: ServerLockInfo = {\n pid: process.pid,\n port: details.port,\n host: details.host,\n backend: details.backend,\n startedAt: Date.now(),\n };\n\n fs.writeFileSync(lockPath, JSON.stringify(info, null, 2), {\n encoding: 'utf-8',\n mode: 0o600,\n });\n\n let isReleased = false;\n const release = () => {\n if (isReleased) return;\n isReleased = true;\n try {\n if (fs.existsSync(lockPath)) {\n const current = JSON.parse(\n fs.readFileSync(lockPath, 'utf-8'),\n ) as ServerLockInfo;\n if (current.pid === process.pid) {\n fs.unlinkSync(lockPath);\n }\n }\n } catch {\n // Ignore cleanup error on shutdown\n }\n };\n\n return {\n info,\n lockPath,\n release,\n };\n}\n","/**\n * Configuration options for a RateLimiter instance.\n */\nexport interface RateLimiterOptions {\n /** Time window in milliseconds (defaults to 60,000ms / 1 minute). */\n windowMs?: number;\n /** Maximum number of allowed requests within the time window. */\n maxRequests?: number;\n /** Number of consecutive failures before applying progressive backoff (defaults to 3). */\n maxFailures?: number;\n /** Initial backoff duration in milliseconds after exceeding maxFailures (defaults to 1,000ms). */\n initialBackoffMs?: number;\n /** Maximum backoff duration in milliseconds (defaults to 900,000ms / 15 minutes). */\n maxBackoffMs?: number;\n /** Maximum number of tracking entries kept in memory before evicting (defaults to 10,000). */\n maxEntries?: number;\n}\n\n/**\n * In-memory sliding-window rate limiter with failure-based exponential backoff.\n */\nexport class RateLimiter {\n private readonly store = new Map<string, RateLimitEntry>();\n private readonly windowMs: number;\n private readonly maxRequests: number;\n private readonly maxFailures: number;\n private readonly initialBackoffMs: number;\n private readonly maxBackoffMs: number;\n private readonly maxEntries: number;\n\n /**\n * Initializes a new RateLimiter instance.\n *\n * @param options - Configuration options for window size, request limits, and backoff.\n */\n constructor(options: RateLimiterOptions = {}) {\n this.windowMs = options.windowMs ?? 60_000;\n this.maxRequests = options.maxRequests ?? 60;\n this.maxFailures = options.maxFailures ?? 3;\n this.initialBackoffMs = options.initialBackoffMs ?? 1_000;\n this.maxBackoffMs = options.maxBackoffMs ?? 900_000;\n this.maxEntries = options.maxEntries ?? 10_000;\n }\n\n /**\n * Returns the current number of tracked keys in memory.\n */\n get size(): number {\n return this.store.size;\n }\n\n /**\n * Checks whether the given key is currently rate limited or blocked by backoff cooldown.\n *\n * @param key - Identifier (e.g. IP address or username).\n * @param now - Current timestamp in milliseconds (defaults to Date.now()).\n * @returns `true` if requests for this key are limited; `false` otherwise.\n */\n isLimited(key: string, now = Date.now()): boolean {\n const entry = this.store.get(key);\n if (!entry) return false;\n\n if (entry.blockedUntil > now) {\n return true;\n }\n\n if (entry.resetAt <= now) {\n this.store.delete(key);\n return false;\n }\n\n return entry.count >= this.maxRequests;\n }\n\n /**\n * Consumes one attempt for the given key if not currently limited.\n *\n * @param key - Identifier.\n * @param now - Current timestamp in milliseconds.\n * @returns `true` if request was allowed and consumed; `false` if rate limited.\n */\n consume(key: string, now = Date.now()): boolean {\n if (this.isLimited(key, now)) {\n return false;\n }\n\n const entry = this.store.get(key);\n if (!entry || (entry.resetAt <= now && entry.blockedUntil <= now)) {\n this.setEntry(\n key,\n {\n count: 1,\n resetAt: now + this.windowMs,\n failures: 0,\n blockedUntil: 0,\n },\n now,\n );\n return true;\n }\n\n entry.count++;\n return true;\n }\n\n /**\n * Records a failed attempt for the given key and applies progressive exponential backoff.\n *\n * @param key - Identifier.\n * @param now - Current timestamp in milliseconds.\n * @returns Cooldown duration in milliseconds if blocked, or 0 if under failure threshold.\n */\n recordFailure(key: string, now = Date.now()): number {\n let entry = this.store.get(key);\n if (!entry || (entry.resetAt <= now && entry.blockedUntil <= now)) {\n entry = {\n count: 1,\n resetAt: now + this.windowMs,\n failures: 0,\n blockedUntil: 0,\n };\n this.setEntry(key, entry, now);\n }\n\n entry.failures++;\n\n if (entry.failures >= this.maxFailures) {\n const exponent = entry.failures - this.maxFailures;\n const backoff = Math.min(\n this.initialBackoffMs * 2 ** exponent,\n this.maxBackoffMs,\n );\n entry.blockedUntil = now + backoff;\n return backoff;\n }\n\n return 0;\n }\n\n /**\n * Resets all failure counters and request tracking for the given key.\n *\n * @param key - Identifier to reset.\n */\n reset(key: string): void {\n this.store.delete(key);\n }\n\n /**\n * Clears all stored rate limit entries.\n */\n clear(): void {\n this.store.clear();\n }\n\n /**\n * Purges expired entries from the internal store.\n *\n * @param now - Current timestamp in milliseconds.\n */\n cleanup(now = Date.now()): void {\n for (const [key, entry] of this.store.entries()) {\n if (entry.resetAt <= now && entry.blockedUntil <= now) {\n this.store.delete(key);\n }\n }\n }\n\n // -- Private Helpers --------------------------------------------------------\n\n private setEntry(key: string, entry: RateLimitEntry, now: number): void {\n if (this.store.size >= this.maxEntries && !this.store.has(key)) {\n this.cleanup(now);\n if (this.store.size >= this.maxEntries) {\n const oldestKey = this.store.keys().next().value;\n if (oldestKey !== undefined) {\n this.store.delete(oldestKey);\n }\n }\n }\n this.store.set(key, entry);\n }\n}\n\n// -- Private Helpers --------------------------------------------------------\n\ninterface RateLimitEntry {\n count: number;\n resetAt: number;\n failures: number;\n blockedUntil: number;\n}\n","import type { ChangeRecord, StoredRecord } from './types.js';\n\n/**\n * Determines whether an incoming mutation should overwrite an existing stored record\n * using Last-Write-Wins (LWW) conflict resolution.\n *\n * Conflict resolution rules:\n * 1. If no existing record exists, returns `true`.\n * 2. If incoming timestamp is strictly greater, returns `true`.\n * 3. If incoming timestamp is strictly less, returns `false`.\n * 4. If timestamps are equal, performs deterministic lexicographical tie-breaking using `clientId`.\n *\n * @param incoming - The candidate change record with timestamp and optional client metadata.\n * @param existing - The current record stored locally or on the server.\n * @returns `true` if the incoming change wins the conflict and should overwrite the existing record; otherwise `false`.\n */\nexport function shouldOverwrite(\n incoming: Pick<ChangeRecord, 'timestamp'> & {\n clientId?: string;\n version?: number;\n },\n existing?: Pick<StoredRecord, 'timestamp' | 'version'> & {\n clientId?: string;\n },\n): boolean {\n if (!existing) return true;\n\n if (incoming.timestamp > existing.timestamp) {\n return true;\n }\n if (incoming.timestamp < existing.timestamp) {\n return false;\n }\n\n const incomingClient = incoming.clientId ?? '';\n const existingClient = existing.clientId ?? '';\n return incomingClient >= existingClient;\n}\n","/**\n * Security, input validation, and sanitization utilities for the TetherDB server.\n * Protects server-side storage and synchronization from injection and traversal attacks.\n *\n * @module tetherdb/server/validate\n */\n\nimport { TetherServerError, TetherServerErrorCode } from './errors.js';\n\n/** Minimum allowed username character length. */\nexport const MIN_USERNAME_LENGTH = 4;\n\n/** Maximum allowed username character length. */\nexport const MAX_USERNAME_LENGTH = 128;\n\n/** Minimum allowed password character length. */\nexport const MIN_PASSWORD_LENGTH = 4;\n\n/** Maximum allowed password character length. */\nexport const MAX_PASSWORD_LENGTH = 512;\n\n/** Maximum allowable future timestamp drift in milliseconds (5 minutes). */\nexport const MAX_FUTURE_TIMESTAMP_DRIFT_MS = 5 * 60 * 1000;\n\n/**\n * Validates a change timestamp, ensuring it is a valid finite epoch number\n * and does not exceed the maximum allowable future drift.\n *\n * @param timestamp - The epoch timestamp in milliseconds.\n * @param maxFutureDriftMs - Optional maximum allowable future drift in ms (defaults to 5 minutes).\n * @returns The validated timestamp.\n * @throws TetherServerError if timestamp is not finite, is non-positive, or exceeds drift bounds.\n */\nexport function validateTimestamp(\n timestamp: number,\n maxFutureDriftMs = MAX_FUTURE_TIMESTAMP_DRIFT_MS,\n): number {\n if (\n typeof timestamp !== 'number' ||\n !Number.isFinite(timestamp) ||\n timestamp <= 0\n ) {\n throw new TetherServerError(\n TetherServerErrorCode.InvalidInput,\n 'Invalid timestamp',\n );\n }\n if (timestamp > Date.now() + maxFutureDriftMs) {\n throw new TetherServerError(\n TetherServerErrorCode.InvalidInput,\n 'Timestamp drift exceeds maximum allowable threshold',\n );\n }\n return timestamp;\n}\n\n/**\n * Validates a user ID string ensuring it is safe for filesystem use.\n *\n * @param userId - The user ID to validate.\n * @returns The validated user ID.\n * @throws TetherServerError if the user ID is invalid or contains unsafe characters.\n */\nexport function validateUserId(userId: string): string {\n return validateFilesystemSafe(userId, 'user ID');\n}\n\n/**\n * Validates an application namespace identifier.\n *\n * @param appId - The application ID to validate.\n * @returns The validated application ID.\n * @throws TetherServerError if the application ID is invalid.\n */\nexport function validateAppId(appId: string): string {\n return validateFilesystemSafe(appId, 'application ID');\n}\n\n/**\n * Validates a table name ensuring it is safe for filesystem use.\n *\n * @param tableName - The table name to validate.\n * @returns The validated table name.\n * @throws TetherServerError if the table name is invalid.\n */\nexport function validateTableName(tableName: string): string {\n return validateFilesystemSafe(tableName, 'table name');\n}\n\n/**\n * Validates a record ID ensuring it is a non-empty string within size limits.\n *\n * @param id - The record identifier to validate.\n * @returns The validated record ID.\n * @throws TetherServerError if the record ID is invalid or exceeds max length.\n */\nexport function validateRecordId(id: string): string {\n if (typeof id !== 'string' || id.length === 0 || id.length > 512) {\n throw new TetherServerError(\n TetherServerErrorCode.InvalidInput,\n 'Invalid record ID',\n );\n }\n return id;\n}\n\n/**\n * Normalizes a username by trimming whitespace and converting to lowercase.\n *\n * @param username - The raw username string.\n * @returns The normalized username (lowercase and trimmed).\n */\nexport function normalizeUsername(username: string): string {\n return typeof username === 'string' ? username.trim().toLowerCase() : '';\n}\n\n/**\n * Validates and normalizes a username for account creation or authentication.\n * Usernames must be between 4 and 128 characters long.\n *\n * @param username - The username to validate.\n * @returns The validated and normalized username (trimmed and lowercase).\n * @throws TetherServerError if the username is invalid or out of length bounds.\n */\nexport function validateUsername(username: string): string {\n if (typeof username !== 'string') {\n throw new TetherServerError(\n TetherServerErrorCode.InvalidInput,\n `Username must be between ${MIN_USERNAME_LENGTH} and ${MAX_USERNAME_LENGTH} characters`,\n );\n }\n const normalized = normalizeUsername(username);\n if (\n normalized.length < MIN_USERNAME_LENGTH ||\n normalized.length > MAX_USERNAME_LENGTH\n ) {\n throw new TetherServerError(\n TetherServerErrorCode.InvalidInput,\n `Username must be between ${MIN_USERNAME_LENGTH} and ${MAX_USERNAME_LENGTH} characters`,\n );\n }\n return normalized;\n}\n\n/**\n * Normalizes a password by trimming surrounding whitespace.\n *\n * @param password - The raw password string.\n * @returns The normalized password (trimmed).\n */\nexport function normalizePassword(password: string): string {\n return typeof password === 'string' ? password.trim() : '';\n}\n\n/**\n * Validates a password for user account creation or authentication.\n *\n * @param password - The password string to validate.\n * @returns The validated password.\n * @throws TetherServerError if the password is not a string, is empty, or exceeds length bounds.\n */\nexport function validatePassword(password: string): string {\n if (typeof password !== 'string') {\n throw new TetherServerError(\n TetherServerErrorCode.InvalidInput,\n 'Password must be a valid non-empty string',\n );\n }\n const normalized = normalizePassword(password);\n if (\n normalized.length < MIN_PASSWORD_LENGTH ||\n normalized.length > MAX_PASSWORD_LENGTH\n ) {\n throw new TetherServerError(\n TetherServerErrorCode.InvalidInput,\n `Password must be between ${MIN_PASSWORD_LENGTH} and ${MAX_PASSWORD_LENGTH} characters`,\n );\n }\n return normalized;\n}\n\n/**\n * Validates a client correlation or batch ID.\n *\n * @param id - The batch or client identifier.\n * @param name - Identifier type description (e.g. 'batchId', 'clientId').\n * @returns The validated ID.\n * @throws TetherServerError if the identifier format is invalid.\n */\nexport function validateIdentifier(id: string, name = 'identifier'): string {\n if (typeof id !== 'string' || !/^[a-zA-Z0-9_-]{2,128}$/.test(id)) {\n throw new TetherServerError(\n TetherServerErrorCode.InvalidInput,\n `Invalid ${name}`,\n );\n }\n return id;\n}\n\n/**\n * Estimates the byte size of an arbitrary JavaScript value or object when serialized.\n *\n * @param value - The value to measure.\n * @returns Size in bytes.\n */\nexport function calculateByteSize(value: unknown): number {\n if (value === null || value === undefined) return 0;\n if (typeof value === 'string') return Buffer.byteLength(value, 'utf-8');\n if (typeof value === 'number') return 8;\n if (typeof value === 'boolean') return 4;\n try {\n return Buffer.byteLength(JSON.stringify(value), 'utf-8');\n } catch {\n return 0;\n }\n}\n\n/**\n * Calculates a 2-character hex/hash bucket for directory partitioning by user ID.\n *\n * @param userId - Unique user identifier.\n * @returns 2-character bucket string (e.g. 'f4', '0a').\n */\nexport function getUserBucket(userId: string): string {\n const safeId = validateUserId(userId);\n const clean = safeId.replace(/[^a-zA-Z0-9]/g, '').toLowerCase();\n return clean.length >= 2 ? clean.slice(0, 2) : clean.padStart(2, '0');\n}\n\n// -- Private Helpers --------------------------------------------------------\n\nfunction validateFilesystemSafe(id: string, name: string): string {\n if (typeof id !== 'string' || !/^[a-zA-Z0-9_-]{1,64}$/.test(id)) {\n throw new TetherServerError(\n TetherServerErrorCode.InvalidInput,\n `Invalid ${name}`,\n );\n }\n return id;\n}\n","import {\n type ChangeRecord,\n OperationType,\n type StoredRecord,\n} from '../../../shared/types.js';\nimport type { AppStorage } from '../app.js';\nimport type { TableStorage } from '../table.js';\nimport type { UserStorage } from '../user.js';\n\n/**\n * Common abstract base class for AppStorage implementations.\n */\nexport abstract class AppBaseStorage implements AppStorage {\n readonly id: string;\n\n constructor(id: string) {\n this.id = id;\n }\n\n /** Creates/registers a new table within this application. */\n abstract createTable(name: string): Promise<TableStorage>;\n\n /** Retrieves a table handle if it exists. */\n abstract getTable(name: string): Promise<TableStorage | undefined>;\n\n /** Lists all registered tables in this application. */\n abstract getTables(): Promise<TableStorage[]>;\n\n /** Applies batch mutation changes for a user. */\n abstract applyChanges(\n user: UserStorage,\n changes: ChangeRecord[],\n ): Promise<{ applied: ChangeRecord[]; newSeq: number }>;\n\n /** Retrieves changes for a user since a given sequence number. */\n abstract getChangesSince(\n user: UserStorage,\n fromSeq: number,\n ): Promise<{\n changes: ChangeRecord[];\n currentSeq: number;\n requiresSnapshot?: boolean;\n }>;\n\n /** Returns the current global sequence number for a user within this application. */\n abstract getCurrentSeq(user: UserStorage): Promise<number>;\n\n /** Deletes this entire application and all associated data. */\n abstract delete(): Promise<boolean>;\n}\n\n/**\n * Applies a change to an existing (or undefined) record and assigns the sequence number.\n */\nexport function applyChangeToRecord(\n change: ChangeRecord,\n existing: StoredRecord | undefined,\n seq: number,\n): {\n updatedRecord: StoredRecord;\n appliedChange: ChangeRecord & { seq: number };\n} {\n const isDeleted = change.op === OperationType.Delete;\n const nextVersion = (existing?.version ?? 0) + 1;\n\n const updatedRecord: StoredRecord = {\n id: change.id,\n version: nextVersion,\n timestamp: change.timestamp,\n clientId: change.clientId,\n deleted: isDeleted,\n data: isDeleted ? null : (change.data ?? null),\n };\n\n const appliedChange: ChangeRecord & { seq: number } = {\n seq,\n table: change.table,\n id: change.id,\n op: change.op,\n version: nextVersion,\n timestamp: change.timestamp,\n clientId: change.clientId,\n data: isDeleted ? undefined : change.data,\n };\n\n return { updatedRecord, appliedChange };\n}\n","import { verifySessionToken } from '../../crypto.js';\nimport { TetherServerError, TetherServerErrorCode } from '../../errors.js';\nimport { validateAppId } from '../../validate.js';\nimport type { AppStorage } from '../app.js';\nimport type {\n MaintenanceResult,\n Storage,\n StorageOptions,\n StorageStatus,\n} from '../storage.js';\nimport type { UserStorage } from '../user.js';\n\n/**\n * Common abstract base class for Storage implementations.\n */\nexport abstract class BaseStorage implements Storage {\n readonly options?: StorageOptions;\n\n constructor(options?: StorageOptions) {\n this.options = options;\n }\n\n /** Backend persistence type name ('file', 'memory', 'sqlite'). */\n abstract readonly backend: string;\n\n /** Secret key used for signing session tokens. */\n abstract readonly secret: string;\n\n /** Optional storage base directory if disk-backed. */\n protected getBaseDir(): string | undefined {\n return undefined;\n }\n\n abstract createApp(id: string): Promise<AppStorage>;\n abstract getApp(id: string): Promise<AppStorage | undefined>;\n abstract getApps(): Promise<AppStorage[]>;\n abstract createUser(username: string, password: string): Promise<UserStorage>;\n abstract getUser(id: string): Promise<UserStorage | undefined>;\n abstract getUserByUsername(\n username: string,\n ): Promise<UserStorage | undefined>;\n abstract getUsers(): Promise<UserStorage[]>;\n abstract checkpoint(appId?: string): Promise<MaintenanceResult>;\n abstract vacuum(appId?: string): Promise<MaintenanceResult>;\n abstract prune(\n appId?: string,\n keepCount?: number,\n ): Promise<MaintenanceResult>;\n\n async getUserByToken(token: string): Promise<UserStorage | undefined> {\n const payload = verifySessionToken(token, this.secret);\n if (!payload) return undefined;\n return this.getUser(payload.userId);\n }\n\n async getStatus(appId?: string): Promise<StorageStatus> {\n const users = await this.getUsers();\n const allApps = await this.getApps();\n const targetApps = filterTargetApps(allApps, appId);\n const apps = await buildAppSummaries(targetApps);\n\n const status: StorageStatus = {\n backend: this.backend,\n usersCount: users.length,\n appsCount: allApps.length,\n apps,\n };\n const baseDir = this.getBaseDir();\n if (baseDir !== undefined) {\n status.baseDir = baseDir;\n }\n return status;\n }\n}\n\n/**\n * Filters the list of applications by an optional target appId, validating format and existence.\n */\nexport function filterTargetApps(\n allApps: AppStorage[],\n appId?: string,\n): AppStorage[] {\n const targetApps = appId\n ? allApps.filter((a) => a.id === validateAppId(appId))\n : allApps;\n\n if (appId && targetApps.length === 0) {\n throw new TetherServerError(\n TetherServerErrorCode.NotFound,\n `Application \"${appId}\" not found`,\n );\n }\n\n return targetApps;\n}\n\n/**\n * Builds array of application summaries including their table names.\n */\nexport async function buildAppSummaries(\n apps: AppStorage[],\n): Promise<Array<{ id: string; tables: string[] }>> {\n const appSummaries: Array<{ id: string; tables: string[] }> = [];\n for (const app of apps) {\n const tables = await app.getTables();\n appSummaries.push({\n id: app.id,\n tables: tables.map((t) => t.name),\n });\n }\n return appSummaries;\n}\n","import type {\n ChangeRecord,\n SnapshotRecord,\n StoredRecord,\n} from '../../../shared/types.js';\nimport type { AppStorage } from '../app.js';\nimport type { TableStorage } from '../table.js';\nimport type { UserStorage } from '../user.js';\n\n/**\n * Common abstract base class for TableStorage implementations.\n */\nexport abstract class TableBaseStorage implements TableStorage {\n readonly name: string;\n readonly app: AppStorage;\n\n constructor(name: string, app: AppStorage) {\n this.name = name;\n this.app = app;\n }\n\n /** Retrieves a single record for a user. */\n abstract getRecord(\n user: UserStorage,\n id: string,\n ): Promise<StoredRecord | undefined>;\n\n /** Retrieves all active records in this table for a user. */\n abstract getAllRecords(user: UserStorage): Promise<SnapshotRecord[]>;\n\n /** Deletes this table and its data. */\n abstract delete(): Promise<boolean>;\n\n /** Applies batch mutation changes to this table. */\n async applyChanges(\n user: UserStorage,\n changes: ChangeRecord[],\n ): Promise<{ applied: ChangeRecord[]; newSeq: number }> {\n return this.app.applyChanges(\n user,\n targetChangesForTable(this.name, this.app.id, changes),\n );\n }\n}\n\n/**\n * Filters non-deleted records and attaches table name for snapshot responses.\n */\nexport function filterActiveRecords(\n tableName: string,\n records: Iterable<StoredRecord>,\n): SnapshotRecord[] {\n const items: SnapshotRecord[] = [];\n for (const rec of records) {\n if (!rec.deleted) {\n items.push({\n ...rec,\n table: tableName,\n });\n }\n }\n return items;\n}\n\n/**\n * Targets generic changes with table name and app ID.\n */\nexport function targetChangesForTable(\n tableName: string,\n appId: string,\n changes: ChangeRecord[],\n): ChangeRecord[] {\n return changes.map((c) => ({\n ...c,\n table: tableName,\n appId,\n }));\n}\n","import {\n createSessionToken,\n hashPassword,\n verifyPasswordHash,\n verifySessionToken,\n} from '../../crypto.js';\nimport { normalizePassword, validatePassword } from '../../validate.js';\nimport type { UserStorage } from '../user.js';\n\n/**\n * Common abstract base class for UserStorage implementations.\n */\nexport abstract class UserBaseStorage implements UserStorage {\n readonly id: string;\n readonly username: string;\n readonly createdAt: number;\n\n constructor(id: string, username: string, createdAt: number) {\n this.id = id;\n this.username = username;\n this.createdAt = createdAt;\n }\n\n /** Retrieves the secret key used for signing session tokens. */\n protected abstract getSecret(): string;\n\n /** Verifies if the plaintext password matches. */\n abstract verifyPassword(password: string): Promise<boolean>;\n\n /** Updates user credentials with a new password. */\n abstract changePassword(newPassword: string): Promise<void>;\n\n /** Deletes the user account and associated data. */\n abstract delete(): Promise<boolean>;\n\n /** Creates a signed session token for this user. */\n async createToken(expiresInSeconds?: number): Promise<string> {\n return createSessionToken(\n this.id,\n this.username,\n this.getSecret(),\n expiresInSeconds,\n );\n }\n\n /** Verifies whether the session token is valid for this user. */\n async verifyToken(token: string): Promise<boolean> {\n const payload = verifySessionToken(token, this.getSecret());\n return payload !== null && payload.userId === this.id;\n }\n}\n\n/**\n * Verifies a candidate plaintext password against a stored bcrypt hash.\n */\nexport async function verifyUserPassword(\n password: string,\n passwordHash: string | null | undefined,\n): Promise<boolean> {\n if (!passwordHash) return false;\n const normalized = normalizePassword(password);\n if (!normalized) return false;\n return verifyPasswordHash(normalized, passwordHash);\n}\n\n/**\n * Hashes a validated plaintext password.\n */\nexport async function hashUserPassword(newPassword: string): Promise<string> {\n const valid = validatePassword(newPassword);\n return hashPassword(valid);\n}\n","import type { SnapshotRecord, StoredRecord } from '../../../shared/types.js';\nimport { validateRecordId } from '../../validate.js';\nimport { filterActiveRecords, TableBaseStorage } from '../base/index.js';\nimport type { UserStorage } from '../user.js';\nimport type { AppMemoryStorage } from './app.js';\nimport type { MemoryStorage } from './storage.js';\n\n/**\n * In-memory implementation of `TableStorage`.\n */\nexport class TableMemoryStorage extends TableBaseStorage {\n declare readonly app: AppMemoryStorage;\n private storage: MemoryStorage;\n\n constructor(name: string, app: AppMemoryStorage, storage: MemoryStorage) {\n super(name, app);\n this.storage = storage;\n }\n\n async getRecord(\n user: UserStorage,\n id: string,\n ): Promise<StoredRecord | undefined> {\n const safeId = validateRecordId(id);\n const userState = this.storage.getUserState(user.id, this.app.id);\n const tableMap = userState.tables.get(this.name);\n const record = tableMap?.get(safeId);\n\n if (!record || record.deleted) {\n return undefined;\n }\n\n return record;\n }\n\n async getAllRecords(user: UserStorage): Promise<SnapshotRecord[]> {\n const userState = this.storage.getUserState(user.id, this.app.id);\n const tableMap = userState.tables.get(this.name);\n return tableMap ? filterActiveRecords(this.name, tableMap.values()) : [];\n }\n\n async delete(): Promise<boolean> {\n return this.app.deleteTable(this.name);\n }\n}\n","import { shouldOverwrite } from '../../../shared/clock.js';\nimport {\n type ChangeRecord,\n OperationType,\n type StoredRecord,\n} from '../../../shared/types.js';\nimport { TetherServerError, TetherServerErrorCode } from '../../errors.js';\nimport {\n calculateByteSize,\n validateRecordId,\n validateTableName,\n validateTimestamp,\n} from '../../validate.js';\nimport { AppBaseStorage, applyChangeToRecord } from '../base/index.js';\nimport type { TableStorage } from '../table.js';\nimport type { UserStorage } from '../user.js';\nimport type { MemoryStorage } from './storage.js';\nimport { TableMemoryStorage } from './table.js';\n\n/**\n * In-memory implementation of `AppStorage`.\n */\nexport class AppMemoryStorage extends AppBaseStorage {\n private tables: Map<string, TableMemoryStorage> = new Map();\n private storage: MemoryStorage;\n\n constructor(id: string, storage: MemoryStorage) {\n super(id);\n this.storage = storage;\n }\n\n async createTable(name: string): Promise<TableStorage> {\n const safeName = validateTableName(name);\n if (this.tables.has(safeName)) {\n throw new TetherServerError(\n TetherServerErrorCode.AlreadyExists,\n 'Table already exists in this application',\n );\n }\n const table = new TableMemoryStorage(safeName, this, this.storage);\n this.tables.set(safeName, table);\n return table;\n }\n\n async getTable(name: string): Promise<TableStorage | undefined> {\n const safeName = validateTableName(name);\n return this.tables.get(safeName);\n }\n\n async getTables(): Promise<TableStorage[]> {\n return Array.from(this.tables.values());\n }\n\n async applyChanges(\n user: UserStorage,\n changes: ChangeRecord[],\n ): Promise<{ applied: ChangeRecord[]; newSeq: number }> {\n const userState = this.storage.getUserState(user.id, this.id);\n\n const maxRecords = this.storage.options.maxRecordsPerTable ?? 10000;\n const maxRecordSize = this.storage.options.maxRecordSizeBytes ?? 512 * 1024;\n const maxChangelog = this.storage.options.maxChangelogEntries ?? 1000;\n\n // Phase 1: Pre-validate all changes in the batch\n for (const change of changes) {\n const tableName = validateTableName(change.table);\n validateRecordId(change.id);\n validateTimestamp(change.timestamp);\n\n if (!this.tables.has(tableName)) {\n throw new TetherServerError(\n TetherServerErrorCode.NotFound,\n 'Table not found',\n );\n }\n\n const payloadBytes = calculateByteSize(change.data);\n if (payloadBytes > maxRecordSize) {\n throw new TetherServerError(\n TetherServerErrorCode.LimitExceeded,\n 'Record payload exceeds maximum allowed size',\n );\n }\n }\n\n // Phase 2: Stage mutations against cloned table maps\n const stagedTables = new Map<string, Map<string, StoredRecord>>();\n const stagedApplied: (ChangeRecord & { seq: number })[] = [];\n let stagedCurrentSeq = userState.currentSeq;\n let stagedMinSeq = userState.minSeq;\n\n for (const change of changes) {\n const tableName = validateTableName(change.table);\n const recordId = validateRecordId(change.id);\n\n let tableMap = stagedTables.get(tableName);\n if (!tableMap) {\n const existingTableMap = userState.tables.get(tableName);\n tableMap = new Map(existingTableMap);\n stagedTables.set(tableName, tableMap);\n }\n\n if (\n change.op === OperationType.Put &&\n !tableMap.has(recordId) &&\n tableMap.size >= maxRecords\n ) {\n throw new TetherServerError(\n TetherServerErrorCode.LimitExceeded,\n 'Table record limit reached',\n );\n }\n\n const existing = tableMap.get(recordId);\n const shouldApply = !existing || shouldOverwrite(change, existing);\n\n if (shouldApply) {\n stagedCurrentSeq++;\n const assignedSeq = stagedCurrentSeq;\n\n if (stagedMinSeq === 0) {\n stagedMinSeq = 1;\n }\n\n const { updatedRecord, appliedChange } = applyChangeToRecord(\n change,\n existing,\n assignedSeq,\n );\n\n tableMap.set(recordId, updatedRecord);\n stagedApplied.push(appliedChange);\n }\n }\n\n // Phase 3: Commit staged modifications atomically to userState\n for (const [tableName, stagedMap] of stagedTables.entries()) {\n userState.tables.set(tableName, stagedMap);\n }\n userState.currentSeq = stagedCurrentSeq;\n userState.minSeq = stagedMinSeq;\n userState.changelog.push(...stagedApplied);\n\n if (userState.changelog.length > maxChangelog) {\n const pruneCount = userState.changelog.length - maxChangelog;\n userState.changelog.splice(0, pruneCount);\n if (userState.changelog.length > 0) {\n userState.minSeq = userState.changelog[0].seq;\n }\n }\n\n return { applied: stagedApplied, newSeq: userState.currentSeq };\n }\n\n async getChangesSince(\n user: UserStorage,\n fromSeq: number,\n ): Promise<{\n changes: ChangeRecord[];\n currentSeq: number;\n requiresSnapshot?: boolean;\n }> {\n const userState = this.storage.getUserState(user.id, this.id);\n const currentSeq = userState.currentSeq;\n const minSeq = userState.minSeq;\n\n if ((fromSeq < minSeq && minSeq > 0) || fromSeq > currentSeq) {\n return { changes: [], currentSeq, requiresSnapshot: true };\n }\n\n const changes = userState.changelog.filter((c) => c.seq > fromSeq);\n return { changes, currentSeq, requiresSnapshot: false };\n }\n\n async getCurrentSeq(user: UserStorage): Promise<number> {\n const userState = this.storage.getUserState(user.id, this.id);\n return userState.currentSeq;\n }\n\n async delete(): Promise<boolean> {\n return this.storage.deleteApp(this.id);\n }\n\n deleteTable(name: string): boolean {\n const safeName = validateTableName(name);\n const deleted = this.tables.delete(safeName);\n this.storage.deleteTableInUserStates(this.id, safeName);\n return deleted;\n }\n}\n","import * as crypto from 'node:crypto';\nimport { hashPassword } from '../../crypto.js';\nimport { TetherServerError, TetherServerErrorCode } from '../../errors.js';\nimport {\n normalizeUsername,\n validateAppId,\n validatePassword,\n validateUserId,\n validateUsername,\n} from '../../validate.js';\nimport type { AppStorage } from '../app.js';\nimport { BaseStorage, filterTargetApps } from '../base/index.js';\nimport type { MaintenanceResult, StorageOptions } from '../storage.js';\nimport type { UserStorage } from '../user.js';\nimport { AppMemoryStorage } from './app.js';\nimport { type MemoryUserData, UserMemoryStorage } from './user.js';\n\nexport interface UserState {\n currentSeq: number;\n minSeq: number;\n tables: Map<\n string,\n Map<string, import('../../../shared/types.js').StoredRecord>\n >;\n changelog: Array<\n import('../../../shared/types.js').ChangeRecord & { seq: number }\n >;\n}\n\nexport interface MemoryStorageOptions extends StorageOptions {}\n\n/**\n * In-memory implementation of `Storage`.\n */\nexport class MemoryStorage extends BaseStorage {\n readonly backend = 'memory';\n private apps: Map<string, AppMemoryStorage> = new Map();\n private userStates: Map<string, UserState> = new Map(); // key = `${appId}:${userId}`\n private users: Map<string, MemoryUserData> = new Map(); // key = userId\n private usersByUsername: Map<string, string> = new Map(); // username -> userId\n readonly secret: string;\n readonly options: MemoryStorageOptions;\n\n constructor(options: MemoryStorageOptions = {}) {\n super(options);\n this.options = options;\n this.secret = options.secret ?? crypto.randomBytes(32).toString('hex');\n }\n\n getUserState(userId: string, appId: string): UserState {\n const safeAppId = validateAppId(appId);\n const safeUserId = validateUserId(userId);\n const key = `${safeAppId}:${safeUserId}`;\n let state = this.userStates.get(key);\n if (!state) {\n state = {\n currentSeq: 0,\n minSeq: 0,\n tables: new Map(),\n changelog: [],\n };\n this.userStates.set(key, state);\n }\n return state;\n }\n\n deleteUserState(userId: string): boolean {\n const safeUserId = validateUserId(userId);\n let deleted = false;\n for (const key of Array.from(this.userStates.keys())) {\n if (key.endsWith(`:${safeUserId}`)) {\n this.userStates.delete(key);\n deleted = true;\n }\n }\n return deleted;\n }\n\n deleteAppUserStates(appId: string): void {\n const safeAppId = validateAppId(appId);\n for (const key of Array.from(this.userStates.keys())) {\n if (key.startsWith(`${safeAppId}:`)) {\n this.userStates.delete(key);\n }\n }\n }\n\n deleteTableInUserStates(appId: string, tableName: string): void {\n const safeAppId = validateAppId(appId);\n for (const [key, state] of this.userStates.entries()) {\n if (key.startsWith(`${safeAppId}:`)) {\n state.tables.delete(tableName);\n }\n }\n }\n\n async createApp(id: string): Promise<AppStorage> {\n const safeId = validateAppId(id);\n if (this.apps.has(safeId)) {\n throw new TetherServerError(\n TetherServerErrorCode.AlreadyExists,\n 'Application already exists',\n );\n }\n const app = new AppMemoryStorage(safeId, this);\n this.apps.set(safeId, app);\n return app;\n }\n\n async getApp(id: string): Promise<AppStorage | undefined> {\n const safeId = validateAppId(id);\n return this.apps.get(safeId);\n }\n\n async getApps(): Promise<AppStorage[]> {\n return Array.from(this.apps.values());\n }\n\n async createUser(username: string, password: string): Promise<UserStorage> {\n const safeUsername = validateUsername(username);\n const validPassword = validatePassword(password);\n if (this.usersByUsername.has(safeUsername)) {\n throw new TetherServerError(\n TetherServerErrorCode.AlreadyExists,\n 'Username is already registered',\n );\n }\n\n const userId = crypto.randomUUID();\n const passwordHash = await hashPassword(validPassword);\n const userData: MemoryUserData = {\n id: userId,\n username: safeUsername,\n passwordHash,\n createdAt: Date.now(),\n };\n\n this.users.set(userId, userData);\n this.usersByUsername.set(safeUsername, userId);\n return new UserMemoryStorage(userData, this);\n }\n\n getUserData(userId: string): MemoryUserData | undefined {\n return this.users.get(userId);\n }\n\n async getUser(id: string): Promise<UserStorage | undefined> {\n const safeUserId = validateUserId(id);\n const data = this.users.get(safeUserId);\n if (data) {\n return new UserMemoryStorage(data, this);\n }\n return undefined;\n }\n\n async getUserByUsername(username: string): Promise<UserStorage | undefined> {\n const safeUsername = normalizeUsername(username);\n if (!safeUsername) return undefined;\n const userId = this.usersByUsername.get(safeUsername);\n if (!userId) return undefined;\n const data = this.users.get(userId);\n if (data) {\n return new UserMemoryStorage(data, this);\n }\n return undefined;\n }\n\n async getUsers(): Promise<UserStorage[]> {\n return Array.from(this.users.values()).map(\n (data) => new UserMemoryStorage(data, this),\n );\n }\n\n deleteUser(id: string): boolean {\n const safeUserId = validateUserId(id);\n this.deleteUserState(safeUserId);\n const data = this.users.get(safeUserId);\n if (data) {\n this.usersByUsername.delete(data.username);\n this.users.delete(safeUserId);\n return true;\n }\n return false;\n }\n\n deleteApp(id: string): boolean {\n const safeId = validateAppId(id);\n this.deleteAppUserStates(safeId);\n return this.apps.delete(safeId);\n }\n\n async checkpoint(appId?: string): Promise<MaintenanceResult> {\n throw new TetherServerError(\n TetherServerErrorCode.NotSupported,\n `Checkpoint operation is not supported by memory storage${appId ? ` (app: ${appId})` : ''}`,\n );\n }\n\n async vacuum(appId?: string): Promise<MaintenanceResult> {\n throw new TetherServerError(\n TetherServerErrorCode.NotSupported,\n `Vacuum operation is not supported by memory storage${appId ? ` (app: ${appId})` : ''}`,\n );\n }\n\n async prune(appId?: string, keepCount?: number): Promise<MaintenanceResult> {\n const keep = keepCount ?? this.options.maxChangelogEntries ?? 1000;\n const allApps = await this.getApps();\n const targetApps = filterTargetApps(allApps, appId);\n\n let totalPruned = 0;\n for (const app of targetApps) {\n for (const [key, state] of this.userStates.entries()) {\n if (key.startsWith(`${app.id}:`)) {\n if (state.changelog.length > keep) {\n const pruneCount = state.changelog.length - keep;\n state.changelog.splice(0, pruneCount);\n if (state.changelog.length > 0) {\n state.minSeq = state.changelog[0].seq;\n }\n totalPruned += pruneCount;\n }\n }\n }\n }\n\n return {\n action: 'prune',\n backend: 'memory',\n appId,\n affectedCount: totalPruned,\n message: `Prune completed successfully. Removed ${totalPruned} changelog record(s)`,\n };\n }\n\n async close(): Promise<void> {\n this.apps.clear();\n this.userStates.clear();\n this.users.clear();\n this.usersByUsername.clear();\n }\n}\n","import { TetherServerError, TetherServerErrorCode } from '../../errors.js';\nimport {\n hashUserPassword,\n UserBaseStorage,\n verifyUserPassword,\n} from '../base/index.js';\nimport type { MemoryStorage } from './storage.js';\n\nexport interface MemoryUserData {\n id: string;\n username: string;\n passwordHash: string | null;\n createdAt: number;\n}\n\n/**\n * In-memory implementation of `UserStorage`.\n */\nexport class UserMemoryStorage extends UserBaseStorage {\n private storage: MemoryStorage;\n\n constructor(data: MemoryUserData, storage: MemoryStorage) {\n super(data.id, data.username, data.createdAt);\n this.storage = storage;\n }\n\n protected getSecret(): string {\n return this.storage.secret;\n }\n\n private getUserData(): MemoryUserData {\n const data = this.storage.getUserData(this.id);\n if (!data) {\n throw new TetherServerError(\n TetherServerErrorCode.NotFound,\n 'User not found',\n );\n }\n return data;\n }\n\n async verifyPassword(password: string): Promise<boolean> {\n const data = this.getUserData();\n return verifyUserPassword(password, data.passwordHash);\n }\n\n async changePassword(newPassword: string): Promise<void> {\n const data = this.getUserData();\n data.passwordHash = await hashUserPassword(newPassword);\n }\n\n async delete(): Promise<boolean> {\n return this.storage.deleteUser(this.id);\n }\n}\n","import type { WebSocket } from 'ws';\nimport {\n type AuthClientMessage,\n type ChangeBatchClientMessage,\n type ClientMessage,\n ClientMessageType,\n PROTOCOL_VERSION,\n type ServerMessage,\n ServerMessageType,\n type SnapshotRecord,\n} from '../shared/types.js';\nimport { TetherServerError, TetherServerErrorCode } from './errors.js';\nimport type { RateLimiter } from './rate-limiter.js';\nimport type { TetherLogger } from './server.js';\nimport type { AppStorage } from './storage/app.js';\nimport type { Storage } from './storage/storage.js';\nimport type { UserStorage } from './storage/user.js';\nimport {\n calculateByteSize,\n validateAppId,\n validateIdentifier,\n} from './validate.js';\n\n/**\n * Configuration options for the WebSocket synchronization coordinator.\n */\nexport interface SyncOptions {\n /** Maximum number of concurrent active connections allowed per user channel (defaults to 20). */\n maxConcurrentConnectionsPerUser?: number;\n /** Maximum duration in milliseconds to wait for authentication before terminating socket (defaults to 10,000ms). */\n authTimeoutMs?: number;\n /** Optional rate limiter for connection handshakes and invalid token tracking. */\n rateLimiter?: RateLimiter | null;\n /** Optional logger instance (or null to suppress internal error logs). */\n logger?: TetherLogger | null;\n}\n\n/**\n * Real-time WebSocket synchronization coordinator managing authentication handshakes,\n * snapshot/diff delivery, change ingestion, acknowledgments, and peer broadcasts per application and user.\n */\nexport class Sync {\n private readonly storage: Storage;\n private readonly maxConcurrentConnectionsPerUser: number;\n private readonly authTimeoutMs: number;\n private readonly rateLimiter: RateLimiter | null;\n private readonly logger: TetherLogger | null;\n private readonly userClients = new Map<string, Set<ActiveClient>>(); // key = `${appId}:${userId}`\n private readonly webSocketToClient = new Map<WebSocket, ActiveClient>();\n private readonly pendingAuthTimers = new Map<WebSocket, NodeJS.Timeout>();\n private readonly webSocketToIp = new Map<WebSocket, string>();\n\n /**\n * Initializes a new Sync coordinator instance.\n *\n * @param storage - Pluggable backend storage engine.\n * @param options - Configuration options for concurrency limits, auth timeout, and rate limiting.\n */\n constructor(storage: Storage, options: SyncOptions = {}) {\n this.storage = storage;\n this.maxConcurrentConnectionsPerUser =\n options.maxConcurrentConnectionsPerUser ?? 20;\n this.authTimeoutMs = options.authTimeoutMs ?? 10_000;\n this.rateLimiter = options.rateLimiter ?? null;\n this.logger = options.logger ?? null;\n }\n\n /**\n * Total number of currently active authenticated WebSocket client connections.\n */\n get connectedClientsCount(): number {\n return this.webSocketToClient.size;\n }\n\n /**\n * Handles an incoming WebSocket connection, binding message, error, and disconnection events.\n *\n * @param webSocket - Active WebSocket connection.\n * @param clientIp - Remote client IP address.\n */\n handleConnection(webSocket: WebSocket, clientIp = '127.0.0.1'): void {\n this.webSocketToIp.set(webSocket, clientIp);\n\n if (this.rateLimiter && !this.rateLimiter.consume(clientIp)) {\n this.send(webSocket, {\n type: ServerMessageType.AuthError,\n message: 'Too many connection attempts',\n });\n webSocket.close();\n return;\n }\n\n if (this.authTimeoutMs > 0) {\n const timer = setTimeout(() => {\n if (!this.webSocketToClient.has(webSocket)) {\n this.send(webSocket, {\n type: ServerMessageType.AuthError,\n message: 'Authentication timeout',\n });\n webSocket.close();\n }\n }, this.authTimeoutMs);\n this.pendingAuthTimers.set(webSocket, timer);\n }\n\n let messageQueue: Promise<void> = Promise.resolve();\n\n webSocket.on('message', (data) => {\n const client = this.webSocketToClient.get(webSocket);\n const userContext = client\n ? ` (app: \"${client.appId}\", user: \"${client.user.id}\", client: \"${client.clientId}\")`\n : '';\n\n messageQueue = messageQueue\n .then(async () => {\n try {\n const raw = typeof data === 'string' ? data : data.toString();\n const msg = JSON.parse(raw) as ClientMessage;\n await this.handleMessage(webSocket, msg);\n } catch (err) {\n const message =\n err instanceof Error ? err.message : 'Unknown server error';\n this.logger?.error(\n `[TetherServer.Sync] Error processing WebSocket message${userContext}:`,\n err,\n );\n this.send(webSocket, {\n type: ServerMessageType.Error,\n message,\n });\n }\n })\n .catch((err) => {\n this.logger?.error(\n `[TetherServer.Sync] Unhandled error in message queue${userContext}:`,\n err,\n );\n });\n });\n\n webSocket.on('error', (err) => {\n const client = this.webSocketToClient.get(webSocket);\n const userContext = client\n ? ` (app: \"${client.appId}\", user: \"${client.user.id}\", client: \"${client.clientId}\")`\n : '';\n this.logger?.error(\n `[TetherServer.Sync] WebSocket connection error${userContext}:`,\n err,\n );\n this.cleanupConnection(webSocket);\n });\n\n webSocket.on('close', () => {\n this.cleanupConnection(webSocket);\n });\n }\n\n /**\n * Routes and executes incoming client protocol messages.\n *\n * @param webSocket - The connection that sent the message.\n * @param msg - Parsed client protocol message.\n */\n async handleMessage(webSocket: WebSocket, msg: ClientMessage): Promise<void> {\n if (!msg || typeof msg !== 'object' || typeof msg.type !== 'string') {\n throw new TetherServerError(\n TetherServerErrorCode.InvalidInput,\n 'Invalid message format',\n );\n }\n\n switch (msg.type) {\n case ClientMessageType.Auth:\n await this.handleAuthMessage(webSocket, msg);\n break;\n\n case ClientMessageType.ChangeBatch:\n await this.handleChangeBatchMessage(webSocket, msg);\n break;\n\n case ClientMessageType.Ping:\n this.handlePingMessage(webSocket);\n break;\n\n default:\n throw new TetherServerError(\n TetherServerErrorCode.InvalidInput,\n 'Unsupported message type',\n );\n }\n }\n\n // -- Private Message Handlers ---------------------------------------------\n\n private async handleAuthMessage(\n webSocket: WebSocket,\n msg: AuthClientMessage,\n ): Promise<void> {\n const ip = this.webSocketToIp.get(webSocket) ?? '127.0.0.1';\n\n const authTimer = this.pendingAuthTimers.get(webSocket);\n if (authTimer) {\n clearTimeout(authTimer);\n this.pendingAuthTimers.delete(webSocket);\n }\n\n if (msg.protocolVersion !== PROTOCOL_VERSION) {\n this.rateLimiter?.recordFailure(ip);\n this.send(webSocket, {\n type: ServerMessageType.AuthError,\n message: `Unsupported protocol version: expected ${PROTOCOL_VERSION}, got ${msg.protocolVersion}`,\n });\n webSocket.close();\n return;\n }\n\n if (typeof msg.token !== 'string' || !msg.token) {\n this.rateLimiter?.recordFailure(ip);\n this.send(webSocket, {\n type: ServerMessageType.AuthError,\n message: 'Missing or invalid authentication token',\n });\n webSocket.close();\n return;\n }\n\n const user = await this.storage.getUserByToken(msg.token);\n if (!user) {\n this.rateLimiter?.recordFailure(ip);\n this.send(webSocket, {\n type: ServerMessageType.AuthError,\n message: 'Invalid or expired authentication token',\n });\n webSocket.close();\n return;\n }\n\n if (typeof msg.appId !== 'string' || !msg.appId) {\n this.send(webSocket, {\n type: ServerMessageType.AuthError,\n message: 'Missing required field: appId',\n });\n webSocket.close();\n return;\n }\n\n const appId = validateAppId(msg.appId);\n const app = await this.storage.getApp(appId);\n if (!app) {\n this.send(webSocket, {\n type: ServerMessageType.AuthError,\n message: 'Application not found',\n });\n webSocket.close();\n return;\n }\n\n const channelKey = `${appId}:${user.id}`;\n let set = this.userClients.get(channelKey);\n if (!set) {\n set = new Set();\n this.userClients.set(channelKey, set);\n }\n\n if (set.size >= this.maxConcurrentConnectionsPerUser) {\n this.send(webSocket, {\n type: ServerMessageType.AuthError,\n message: 'Maximum concurrent connections exceeded for this user',\n });\n webSocket.close();\n return;\n }\n\n this.rateLimiter?.reset(ip);\n\n const clientId = validateIdentifier(\n msg.clientId ?? 'client_anon',\n 'clientId',\n );\n const existingClient = this.webSocketToClient.get(webSocket);\n if (existingClient) {\n const oldChannelKey = `${existingClient.appId}:${existingClient.user.id}`;\n const oldSet = this.userClients.get(oldChannelKey);\n if (oldSet) {\n oldSet.delete(existingClient);\n if (oldSet.size === 0) {\n this.userClients.delete(oldChannelKey);\n }\n }\n this.webSocketToClient.delete(webSocket);\n }\n\n const client: ActiveClient = {\n webSocket,\n clientId,\n user,\n appId,\n };\n\n this.webSocketToClient.set(webSocket, client);\n set.add(client);\n\n const currentSeq = await app.getCurrentSeq(user);\n const refreshedToken = await user.createToken();\n this.send(webSocket, {\n type: ServerMessageType.AuthSuccess,\n protocolVersion: PROTOCOL_VERSION,\n userId: user.id,\n currentSeq,\n token: refreshedToken,\n });\n\n // Initial sync: snapshot or diff\n await this.performSync(client, msg.lastSyncSeq);\n }\n\n private async handleChangeBatchMessage(\n webSocket: WebSocket,\n msg: ChangeBatchClientMessage,\n ): Promise<void> {\n const client = this.webSocketToClient.get(webSocket);\n if (!client) {\n this.send(webSocket, {\n type: ServerMessageType.AuthError,\n message: 'Not authenticated',\n });\n return;\n }\n\n if (!Array.isArray(msg.changes)) {\n throw new TetherServerError(\n TetherServerErrorCode.InvalidInput,\n 'Invalid change batch: changes must be an array',\n );\n }\n\n const maxBatchSize =\n this.storage.options?.maxBatchSizeBytes ?? 5 * 1024 * 1024;\n const batchBytes = calculateByteSize(msg.changes);\n if (batchBytes > maxBatchSize) {\n throw new TetherServerError(\n TetherServerErrorCode.LimitExceeded,\n 'Change batch exceeds maximum allowed size',\n );\n }\n\n const batchId = validateIdentifier(msg.batchId, 'batchId');\n\n const app = await this.storage.getApp(client.appId);\n if (!app) {\n throw new TetherServerError(\n TetherServerErrorCode.NotFound,\n 'Application not found',\n );\n }\n\n const { applied, newSeq } = await app.applyChanges(\n client.user,\n msg.changes,\n );\n\n // Acknowledge to sender\n this.send(webSocket, {\n type: ServerMessageType.ChangeAck,\n batchId,\n appliedSeq: newSeq,\n });\n\n // Broadcast applied changes to other active clients of the same app and user\n if (applied.length > 0) {\n this.broadcastToAppUser(client.appId, client.user.id, client.clientId, {\n type: ServerMessageType.BroadcastChanges,\n fromClientId: client.clientId,\n changes: applied,\n seq: newSeq,\n });\n }\n }\n\n private handlePingMessage(webSocket: WebSocket): void {\n this.send(webSocket, {\n type: ServerMessageType.Pong,\n });\n }\n\n // -- Private Helpers ------------------------------------------------------\n\n private cleanupConnection(webSocket: WebSocket): void {\n const authTimer = this.pendingAuthTimers.get(webSocket);\n if (authTimer) {\n clearTimeout(authTimer);\n this.pendingAuthTimers.delete(webSocket);\n }\n this.webSocketToIp.delete(webSocket);\n\n const client = this.webSocketToClient.get(webSocket);\n if (!client) return;\n\n this.webSocketToClient.delete(webSocket);\n const channelKey = `${client.appId}:${client.user.id}`;\n const set = this.userClients.get(channelKey);\n if (set) {\n set.delete(client);\n if (set.size === 0) {\n this.userClients.delete(channelKey);\n }\n }\n }\n\n private send(webSocket: WebSocket, msg: ServerMessage): void {\n if (webSocket.readyState === 1 /* OPEN */) {\n webSocket.send(JSON.stringify(msg));\n }\n }\n\n private async performSync(\n client: ActiveClient,\n lastSyncSeq?: number,\n ): Promise<void> {\n const app = await this.storage.getApp(client.appId);\n if (!app) {\n throw new TetherServerError(\n TetherServerErrorCode.NotFound,\n 'Application not found',\n );\n }\n\n const seq = lastSyncSeq ?? 0;\n if (seq === 0) {\n // Client has no sync point: deliver full snapshot for this app\n const snapshot = await this.getAppSnapshot(app, client.user);\n const currentSeq = await app.getCurrentSeq(client.user);\n this.send(client.webSocket, {\n type: ServerMessageType.SyncSnapshot,\n seq: currentSeq,\n snapshot,\n });\n } else {\n // Client has lastSyncSeq: deliver diff or snapshot if compacted\n const { changes, currentSeq, requiresSnapshot } =\n await app.getChangesSince(client.user, seq);\n\n // If changelog was pruned or compacted, deliver full snapshot\n if (requiresSnapshot) {\n const snapshot = await this.getAppSnapshot(app, client.user);\n this.send(client.webSocket, {\n type: ServerMessageType.SyncSnapshot,\n seq: currentSeq,\n snapshot,\n });\n } else {\n this.send(client.webSocket, {\n type: ServerMessageType.SyncDiff,\n fromSeq: seq,\n toSeq: currentSeq,\n changes,\n });\n }\n }\n }\n\n private async getAppSnapshot(\n app: AppStorage,\n user: UserStorage,\n ): Promise<SnapshotRecord[]> {\n const tables = await app.getTables();\n const snapshot: SnapshotRecord[] = [];\n for (const table of tables) {\n const records = await table.getAllRecords(user);\n snapshot.push(...records);\n }\n return snapshot;\n }\n\n private broadcastToAppUser(\n appId: string,\n userId: string,\n excludeClientId: string,\n msg: ServerMessage,\n ): void {\n const channelKey = `${appId}:${userId}`;\n const clients = this.userClients.get(channelKey);\n if (!clients) return;\n\n for (const client of clients) {\n if (client.clientId !== excludeClientId) {\n this.send(client.webSocket, msg);\n }\n }\n }\n}\n\n// -- Private Helpers --------------------------------------------------------\n\ninterface ActiveClient {\n webSocket: WebSocket;\n clientId: string;\n user: UserStorage;\n appId: string;\n}\n","import * as crypto from 'node:crypto';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { getOrCreateKeyfileSecret, hashPassword } from '../../crypto.js';\nimport { TetherServerError, TetherServerErrorCode } from '../../errors.js';\nimport { readServerLock } from '../../lock.js';\nimport {\n getUserBucket,\n normalizeUsername,\n validateAppId,\n validatePassword,\n validateUserId,\n validateUsername,\n} from '../../validate.js';\nimport type { AppStorage } from '../app.js';\nimport { BaseStorage, filterTargetApps } from '../base/index.js';\nimport type { MaintenanceResult, StorageOptions } from '../storage.js';\nimport type { UserStorage } from '../user.js';\nimport { AppFileStorage } from './app.js';\nimport { UserFileStorage } from './user.js';\n\nexport interface FileUserData {\n id: string;\n username: string;\n passwordHash: string | null;\n createdAt: number;\n}\n\nexport interface FileAppData {\n id: string;\n createdAt: number;\n}\n\nexport interface FileStorageOptions extends StorageOptions {\n baseDir?: string;\n}\n\n/**\n * Filesystem-backed implementation of `Storage`.\n */\nexport class FileStorage extends BaseStorage {\n readonly backend = 'file';\n readonly baseDir: string;\n readonly options: FileStorageOptions;\n readonly secret: string;\n private userLocks: Map<string, Promise<unknown>> = new Map();\n\n constructor(options: FileStorageOptions = {}) {\n super(options);\n this.options = options;\n this.baseDir = path.resolve(options.baseDir ?? '.data');\n this.secret = options.secret ?? getOrCreateKeyfileSecret(this.baseDir);\n }\n\n protected override getBaseDir(): string | undefined {\n return this.baseDir;\n }\n\n private get usersFile(): string {\n return path.join(this.baseDir, 'users.json');\n }\n\n private get appsFile(): string {\n return path.join(this.baseDir, 'apps.json');\n }\n\n async withLock<T>(key: string, fn: () => Promise<T>): Promise<T> {\n const prevLock = this.userLocks.get(key) ?? Promise.resolve();\n let releaseLock!: () => void;\n const currentLock = new Promise<void>((resolve) => {\n releaseLock = resolve;\n });\n this.userLocks.set(\n key,\n prevLock.then(() => currentLock),\n );\n\n try {\n await prevLock;\n return await fn();\n } finally {\n releaseLock();\n if (this.userLocks.get(key) === currentLock) {\n this.userLocks.delete(key);\n }\n }\n }\n\n async withUserLock<T>(\n userId: string,\n appId: string,\n fn: () => Promise<T>,\n ): Promise<T> {\n return this.withLock(`${appId}:${userId}`, fn);\n }\n\n private async readUsersFile(): Promise<Map<string, FileUserData>> {\n try {\n const content = await fs.readFile(this.usersFile, 'utf-8');\n const list = JSON.parse(content) as FileUserData[];\n const map = new Map<string, FileUserData>();\n for (const u of list) {\n map.set(u.id, u);\n }\n return map;\n } catch {\n return new Map();\n }\n }\n\n private async writeUsersFile(\n users: Map<string, FileUserData>,\n ): Promise<void> {\n await writeFileAtomic(\n this.usersFile,\n JSON.stringify(Array.from(users.values()), null, 2),\n );\n }\n\n private async readAppsFile(): Promise<Map<string, FileAppData>> {\n try {\n const content = await fs.readFile(this.appsFile, 'utf-8');\n const list = JSON.parse(content) as FileAppData[];\n const map = new Map<string, FileAppData>();\n for (const a of list) {\n map.set(a.id, a);\n }\n return map;\n } catch {\n return new Map();\n }\n }\n\n private async writeAppsFile(apps: Map<string, FileAppData>): Promise<void> {\n await writeFileAtomic(\n this.appsFile,\n JSON.stringify(Array.from(apps.values()), null, 2),\n );\n }\n\n async findUserDataById(id: string): Promise<FileUserData | undefined> {\n const users = await this.readUsersFile();\n return users.get(id);\n }\n\n async updateUserData(\n id: string,\n update: Partial<FileUserData>,\n ): Promise<void> {\n assertNoActiveServerLock(this.baseDir);\n return this.withLock('__users__', async () => {\n const users = await this.readUsersFile();\n const existing = users.get(id);\n if (!existing) {\n throw new TetherServerError(\n TetherServerErrorCode.NotFound,\n 'User not found',\n );\n }\n users.set(id, { ...existing, ...update });\n await this.writeUsersFile(users);\n });\n }\n\n async createApp(id: string): Promise<AppStorage> {\n assertNoActiveServerLock(this.baseDir);\n const safeId = validateAppId(id);\n return this.withLock('__apps__', async () => {\n const apps = await this.readAppsFile();\n if (apps.has(safeId)) {\n throw new TetherServerError(\n TetherServerErrorCode.AlreadyExists,\n 'Application already exists',\n );\n }\n\n const appDir = path.join(this.baseDir, safeId);\n await fs.mkdir(appDir, { recursive: true });\n\n const now = Date.now();\n apps.set(safeId, { id: safeId, createdAt: now });\n await this.writeAppsFile(apps);\n\n const manifestFile = path.join(appDir, 'manifest.json');\n await writeFileAtomic(\n manifestFile,\n JSON.stringify(\n {\n id: safeId,\n tables: [],\n createdAt: now,\n version: 1,\n },\n null,\n 2,\n ),\n );\n\n return new AppFileStorage(safeId, this);\n });\n }\n\n async getApp(id: string): Promise<AppStorage | undefined> {\n const safeId = validateAppId(id);\n const apps = await this.readAppsFile();\n if (apps.has(safeId)) {\n return new AppFileStorage(safeId, this);\n }\n // Fallback: check if manifest.json exists on disk\n const manifestFile = path.join(this.baseDir, safeId, 'manifest.json');\n try {\n await fs.stat(manifestFile);\n apps.set(safeId, { id: safeId, createdAt: Date.now() });\n await this.writeAppsFile(apps);\n return new AppFileStorage(safeId, this);\n } catch {\n return undefined;\n }\n }\n\n async getApps(): Promise<AppStorage[]> {\n const apps = await this.readAppsFile();\n return Array.from(apps.values())\n .sort((a, b) => a.id.localeCompare(b.id))\n .map((a) => new AppFileStorage(a.id, this));\n }\n\n async createUser(username: string, password: string): Promise<UserStorage> {\n assertNoActiveServerLock(this.baseDir);\n const safeUsername = validateUsername(username);\n const validPassword = validatePassword(password);\n const passwordHash = await hashPassword(validPassword);\n\n return this.withLock('__users__', async () => {\n const users = await this.readUsersFile();\n\n for (const u of users.values()) {\n if (u.username.toLowerCase() === safeUsername.toLowerCase()) {\n throw new TetherServerError(\n TetherServerErrorCode.AlreadyExists,\n 'Username is already registered',\n );\n }\n }\n\n const userId = crypto.randomUUID();\n const userData: FileUserData = {\n id: userId,\n username: safeUsername,\n passwordHash,\n createdAt: Date.now(),\n };\n\n users.set(userId, userData);\n await this.writeUsersFile(users);\n return new UserFileStorage(userData, this);\n });\n }\n\n async getUser(id: string): Promise<UserStorage | undefined> {\n const safeUserId = validateUserId(id);\n const data = await this.findUserDataById(safeUserId);\n if (data) {\n return new UserFileStorage(data, this);\n }\n return undefined;\n }\n\n async getUserByUsername(username: string): Promise<UserStorage | undefined> {\n const safeUsername = normalizeUsername(username);\n if (!safeUsername) return undefined;\n const users = await this.readUsersFile();\n for (const u of users.values()) {\n if (u.username.toLowerCase() === safeUsername) {\n return new UserFileStorage(u, this);\n }\n }\n return undefined;\n }\n\n async getUsers(): Promise<UserStorage[]> {\n const users = await this.readUsersFile();\n return Array.from(users.values()).map(\n (data) => new UserFileStorage(data, this),\n );\n }\n\n async deleteUser(id: string): Promise<boolean> {\n assertNoActiveServerLock(this.baseDir);\n const safeUserId = validateUserId(id);\n return this.withLock('__users__', async () => {\n let deleted = false;\n\n const apps = await this.getApps();\n const bucket = getUserBucket(safeUserId);\n for (const app of apps) {\n const userDir = path.join(\n this.baseDir,\n app.id,\n 'users',\n bucket,\n safeUserId,\n );\n try {\n await fs.rm(userDir, { recursive: true, force: true });\n deleted = true;\n } catch {\n // Ignore\n }\n }\n\n const users = await this.readUsersFile();\n if (users.has(safeUserId)) {\n users.delete(safeUserId);\n await this.writeUsersFile(users);\n deleted = true;\n }\n\n return deleted;\n });\n }\n\n async deleteApp(id: string): Promise<boolean> {\n assertNoActiveServerLock(this.baseDir);\n const safeId = validateAppId(id);\n return this.withLock('__apps__', async () => {\n const apps = await this.readAppsFile();\n let deleted = false;\n\n if (apps.has(safeId)) {\n apps.delete(safeId);\n await this.writeAppsFile(apps);\n deleted = true;\n }\n\n const appDir = path.join(this.baseDir, safeId);\n try {\n await fs.rm(appDir, { recursive: true, force: true });\n deleted = true;\n } catch {\n // Ignore\n }\n\n return deleted;\n });\n }\n\n async checkpoint(appId?: string): Promise<MaintenanceResult> {\n throw new TetherServerError(\n TetherServerErrorCode.NotSupported,\n `Checkpoint operation is not supported by file storage${appId ? ` (app: ${appId})` : ''}`,\n );\n }\n\n async vacuum(appId?: string): Promise<MaintenanceResult> {\n throw new TetherServerError(\n TetherServerErrorCode.NotSupported,\n `Vacuum operation is not supported by file storage${appId ? ` (app: ${appId})` : ''}`,\n );\n }\n\n async prune(appId?: string, keepCount?: number): Promise<MaintenanceResult> {\n const keep = keepCount ?? this.options.maxChangelogEntries ?? 1000;\n const allApps = await this.getApps();\n const targetApps = filterTargetApps(allApps, appId);\n\n const users = await this.getUsers();\n let totalPruned = 0;\n\n for (const app of targetApps) {\n for (const user of users) {\n await this.withUserLock(user.id, app.id, async () => {\n const userDir = path.join(\n this.baseDir,\n app.id,\n 'users',\n getUserBucket(user.id),\n user.id,\n );\n const syncFile = path.join(userDir, 'sync.jsonl');\n const metaFile = path.join(userDir, 'meta.json');\n\n try {\n const content = await fs.readFile(syncFile, 'utf-8');\n const lines = content\n .split('\\n')\n .map((l) => l.trim())\n .filter(Boolean);\n if (lines.length > keep) {\n const pruneCount = lines.length - keep;\n const keptLines = lines.slice(pruneCount);\n const firstKept = JSON.parse(keptLines[0]) as { seq: number };\n await writeFileAtomic(syncFile, `${keptLines.join('\\n')}\\n`);\n try {\n const metaContent = await fs.readFile(metaFile, 'utf-8');\n const meta = JSON.parse(metaContent) as {\n currentSeq: number;\n minSeq: number;\n };\n meta.minSeq = firstKept.seq;\n await writeFileAtomic(metaFile, JSON.stringify(meta, null, 2));\n } catch {\n // Ignore meta read failure\n }\n totalPruned += pruneCount;\n }\n } catch {\n // User sync file doesn't exist or is empty\n }\n });\n }\n }\n\n return {\n action: 'prune',\n backend: 'file',\n appId,\n affectedCount: totalPruned,\n message: `Prune completed successfully. Removed ${totalPruned} changelog record(s)`,\n };\n }\n\n async close(): Promise<void> {\n this.userLocks.clear();\n }\n}\n\n// -- Private Helpers --------------------------------------------------------\n\n/**\n * Validates that no external TetherDB server process is actively running on this data directory.\n *\n * @param baseDir - Storage base directory.\n * @throws TetherServerError if an active server lock is held by another process.\n */\nexport function assertNoActiveServerLock(baseDir: string): void {\n const lock = readServerLock(baseDir);\n if (lock && lock.pid !== process.pid) {\n throw new TetherServerError(\n TetherServerErrorCode.NotSupported,\n `Cannot modify file storage directly while server is running (PID ${lock.pid})`,\n );\n }\n}\n\n/**\n * Writes data atomically to a file using a unique temp file and atomic rename.\n *\n * @param filePath - The destination file path.\n * @param content - File content to write.\n */\nexport async function writeFileAtomic(\n filePath: string,\n content: string,\n): Promise<void> {\n const dir = path.dirname(filePath);\n await fs.mkdir(dir, { recursive: true });\n const tmpPath = `${filePath}.${crypto.randomBytes(6).toString('hex')}.tmp`;\n await fs.writeFile(tmpPath, content, 'utf-8');\n await fs.rename(tmpPath, filePath);\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { shouldOverwrite } from '../../../shared/clock.js';\nimport {\n type ChangeRecord,\n OperationType,\n type StoredRecord,\n} from '../../../shared/types.js';\nimport { TetherServerError, TetherServerErrorCode } from '../../errors.js';\nimport {\n calculateByteSize,\n getUserBucket,\n validateRecordId,\n validateTableName,\n validateTimestamp,\n validateUserId,\n} from '../../validate.js';\nimport { AppBaseStorage, applyChangeToRecord } from '../base/index.js';\nimport type { TableStorage } from '../table.js';\nimport type { UserStorage } from '../user.js';\nimport {\n assertNoActiveServerLock,\n type FileStorage,\n writeFileAtomic,\n} from './storage.js';\nimport { TableFileStorage } from './table.js';\n\nexport interface AppManifest {\n id: string;\n tables: string[];\n createdAt: number;\n version: number;\n}\n\ninterface UserMeta {\n currentSeq: number;\n minSeq: number;\n}\n\n/**\n * Filesystem-backed application namespace storage implementation.\n */\nexport class AppFileStorage extends AppBaseStorage {\n readonly storage: FileStorage;\n\n constructor(id: string, storage: FileStorage) {\n super(id);\n this.storage = storage;\n }\n\n get appDir(): string {\n return path.join(this.storage.baseDir, this.id);\n }\n\n private get manifestFile(): string {\n return path.join(this.appDir, 'manifest.json');\n }\n\n getUserDir(userId: string): string {\n const safeUserId = validateUserId(userId);\n const bucket = getUserBucket(safeUserId);\n return path.join(this.appDir, 'users', bucket, safeUserId);\n }\n\n private getUserMetaFile(userId: string): string {\n return path.join(this.getUserDir(userId), 'meta.json');\n }\n\n private getUserSyncFile(userId: string): string {\n return path.join(this.getUserDir(userId), 'sync.jsonl');\n }\n\n getUserTablesDir(userId: string): string {\n return path.join(this.getUserDir(userId), 'tables');\n }\n\n getUserTableFile(userId: string, tableName: string): string {\n return path.join(this.getUserTablesDir(userId), `${tableName}.json`);\n }\n\n private async readManifest(): Promise<AppManifest> {\n try {\n const content = await fs.readFile(this.manifestFile, 'utf-8');\n return JSON.parse(content) as AppManifest;\n } catch {\n return {\n id: this.id,\n tables: [],\n createdAt: Date.now(),\n version: 1,\n };\n }\n }\n\n private async writeManifest(manifest: AppManifest): Promise<void> {\n await writeFileAtomic(this.manifestFile, JSON.stringify(manifest, null, 2));\n }\n\n private async readUserMeta(userId: string): Promise<UserMeta> {\n try {\n const content = await fs.readFile(this.getUserMetaFile(userId), 'utf-8');\n return JSON.parse(content) as UserMeta;\n } catch {\n return { currentSeq: 0, minSeq: 0 };\n }\n }\n\n private async writeUserMeta(userId: string, meta: UserMeta): Promise<void> {\n await writeFileAtomic(\n this.getUserMetaFile(userId),\n JSON.stringify(meta, null, 2),\n );\n }\n\n private async readUserSync(\n userId: string,\n ): Promise<Array<ChangeRecord & { seq: number }>> {\n try {\n const content = await fs.readFile(this.getUserSyncFile(userId), 'utf-8');\n const lines = content.split('\\n');\n const changes: Array<ChangeRecord & { seq: number }> = [];\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed) {\n changes.push(JSON.parse(trimmed) as ChangeRecord & { seq: number });\n }\n }\n return changes;\n } catch {\n return [];\n }\n }\n\n private async appendUserSync(\n userId: string,\n changes: Array<ChangeRecord & { seq: number }>,\n ): Promise<void> {\n if (changes.length === 0) return;\n await fs.mkdir(this.getUserDir(userId), { recursive: true });\n const content = `${changes.map((c) => JSON.stringify(c)).join('\\n')}\\n`;\n await fs.appendFile(this.getUserSyncFile(userId), content, 'utf-8');\n }\n\n private async rewriteUserSync(\n userId: string,\n changes: Array<ChangeRecord & { seq: number }>,\n ): Promise<void> {\n const content =\n changes.length > 0\n ? `${changes.map((c) => JSON.stringify(c)).join('\\n')}\\n`\n : '';\n await writeFileAtomic(this.getUserSyncFile(userId), content);\n }\n\n async readTableRecords(\n userId: string,\n tableName: string,\n ): Promise<Map<string, StoredRecord>> {\n try {\n const content = await fs.readFile(\n this.getUserTableFile(userId, tableName),\n 'utf-8',\n );\n const list = JSON.parse(content) as StoredRecord[];\n const map = new Map<string, StoredRecord>();\n for (const rec of list) {\n map.set(rec.id, rec);\n }\n return map;\n } catch {\n return new Map();\n }\n }\n\n private async writeTableRecords(\n userId: string,\n tableName: string,\n records: Map<string, StoredRecord>,\n ): Promise<void> {\n await writeFileAtomic(\n this.getUserTableFile(userId, tableName),\n JSON.stringify(Array.from(records.values()), null, 2),\n );\n }\n\n async createTable(name: string): Promise<TableStorage> {\n assertNoActiveServerLock(this.storage.baseDir);\n const safeName = validateTableName(name);\n const manifest = await this.readManifest();\n if (manifest.tables.includes(safeName)) {\n throw new TetherServerError(\n TetherServerErrorCode.AlreadyExists,\n 'Table already exists in this application',\n );\n }\n manifest.tables.push(safeName);\n manifest.tables.sort();\n await this.writeManifest(manifest);\n return new TableFileStorage(safeName, this);\n }\n\n async getTable(name: string): Promise<TableStorage | undefined> {\n const safeName = validateTableName(name);\n const manifest = await this.readManifest();\n if (manifest.tables.includes(safeName)) {\n return new TableFileStorage(safeName, this);\n }\n return undefined;\n }\n\n async getTables(): Promise<TableStorage[]> {\n const manifest = await this.readManifest();\n return manifest.tables.map((name) => new TableFileStorage(name, this));\n }\n\n async applyChanges(\n user: UserStorage,\n changes: ChangeRecord[],\n ): Promise<{ applied: ChangeRecord[]; newSeq: number }> {\n assertNoActiveServerLock(this.storage.baseDir);\n return this.storage.withUserLock(user.id, this.id, async () => {\n const manifest = await this.readManifest();\n const registeredTables = new Set(manifest.tables);\n const safeUserId = validateUserId(user.id);\n const applied: ChangeRecord[] = [];\n\n const maxRecords = this.storage.options.maxRecordsPerTable ?? 10000;\n const maxRecordSize =\n this.storage.options.maxRecordSizeBytes ?? 512 * 1024;\n const maxChangelog = this.storage.options.maxChangelogEntries ?? 1000;\n\n const meta = await this.readUserMeta(safeUserId);\n const syncChanges = await this.readUserSync(safeUserId);\n\n // Cache of modified tables\n const modifiedTables = new Map<string, Map<string, StoredRecord>>();\n\n for (const change of changes) {\n const tableName = validateTableName(change.table);\n const recordId = validateRecordId(change.id);\n validateTimestamp(change.timestamp);\n\n if (!registeredTables.has(tableName)) {\n throw new TetherServerError(\n TetherServerErrorCode.NotFound,\n 'Table not found',\n );\n }\n\n let tableRecords = modifiedTables.get(tableName);\n if (!tableRecords) {\n tableRecords = await this.readTableRecords(safeUserId, tableName);\n modifiedTables.set(tableName, tableRecords);\n }\n\n if (\n change.op === OperationType.Put &&\n !tableRecords.has(recordId) &&\n tableRecords.size >= maxRecords\n ) {\n throw new TetherServerError(\n TetherServerErrorCode.LimitExceeded,\n 'Table record limit reached',\n );\n }\n\n const payloadBytes = calculateByteSize(change.data);\n if (payloadBytes > maxRecordSize) {\n throw new TetherServerError(\n TetherServerErrorCode.LimitExceeded,\n 'Record payload exceeds maximum allowed size',\n );\n }\n\n const existing = tableRecords.get(recordId);\n const shouldApply = !existing || shouldOverwrite(change, existing);\n\n if (shouldApply) {\n meta.currentSeq++;\n const assignedSeq = meta.currentSeq;\n\n if (meta.minSeq === 0) {\n meta.minSeq = 1;\n }\n\n const { updatedRecord, appliedChange } = applyChangeToRecord(\n change,\n existing,\n assignedSeq,\n );\n\n tableRecords.set(recordId, updatedRecord);\n applied.push(appliedChange);\n syncChanges.push(appliedChange);\n }\n }\n\n if (applied.length > 0) {\n for (const [tableName, records] of modifiedTables.entries()) {\n await this.writeTableRecords(safeUserId, tableName, records);\n }\n\n if (syncChanges.length > maxChangelog) {\n const pruneCount = syncChanges.length - maxChangelog;\n syncChanges.splice(0, pruneCount);\n if (syncChanges.length > 0) {\n meta.minSeq = syncChanges[0].seq;\n }\n await this.rewriteUserSync(safeUserId, syncChanges);\n } else {\n await this.appendUserSync(\n safeUserId,\n applied as Array<ChangeRecord & { seq: number }>,\n );\n }\n\n await this.writeUserMeta(safeUserId, meta);\n }\n\n return { applied, newSeq: meta.currentSeq };\n });\n }\n\n async getChangesSince(\n user: UserStorage,\n fromSeq: number,\n ): Promise<{\n changes: ChangeRecord[];\n currentSeq: number;\n requiresSnapshot?: boolean;\n }> {\n const safeUserId = validateUserId(user.id);\n const meta = await this.readUserMeta(safeUserId);\n const currentSeq = meta.currentSeq;\n const minSeq = meta.minSeq;\n\n if ((fromSeq < minSeq && minSeq > 0) || fromSeq > currentSeq) {\n return { changes: [], currentSeq, requiresSnapshot: true };\n }\n\n const syncChanges = await this.readUserSync(safeUserId);\n const changes = syncChanges.filter((c) => c.seq > fromSeq);\n return { changes, currentSeq, requiresSnapshot: false };\n }\n\n async getCurrentSeq(user: UserStorage): Promise<number> {\n const safeUserId = validateUserId(user.id);\n const meta = await this.readUserMeta(safeUserId);\n return meta.currentSeq;\n }\n\n async delete(): Promise<boolean> {\n return this.storage.deleteApp(this.id);\n }\n\n async deleteTable(name: string): Promise<boolean> {\n assertNoActiveServerLock(this.storage.baseDir);\n const safeName = validateTableName(name);\n const manifest = await this.readManifest();\n const idx = manifest.tables.indexOf(safeName);\n if (idx === -1) return false;\n\n manifest.tables.splice(idx, 1);\n await this.writeManifest(manifest);\n\n // Delete table file from all user directories\n const usersRoot = path.join(this.appDir, 'users');\n try {\n const buckets = await fs.readdir(usersRoot, { withFileTypes: true });\n for (const bucket of buckets) {\n if (bucket.isDirectory()) {\n const bucketDir = path.join(usersRoot, bucket.name);\n const users = await fs.readdir(bucketDir, { withFileTypes: true });\n for (const user of users) {\n if (user.isDirectory()) {\n const tableFile = path.join(\n bucketDir,\n user.name,\n 'tables',\n `${safeName}.json`,\n );\n try {\n await fs.rm(tableFile, { force: true });\n } catch {\n // Ignore\n }\n }\n }\n }\n }\n } catch {\n // Ignore if users directory doesn't exist\n }\n\n return true;\n }\n}\n","import type { SnapshotRecord, StoredRecord } from '../../../shared/types.js';\nimport { validateRecordId, validateUserId } from '../../validate.js';\nimport { filterActiveRecords, TableBaseStorage } from '../base/index.js';\nimport type { UserStorage } from '../user.js';\nimport type { AppFileStorage } from './app.js';\n\n/**\n * Filesystem-backed implementation of `TableStorage`.\n */\nexport class TableFileStorage extends TableBaseStorage {\n declare readonly app: AppFileStorage;\n\n async getRecord(\n user: UserStorage,\n id: string,\n ): Promise<StoredRecord | undefined> {\n const safeUserId = validateUserId(user.id);\n const safeId = validateRecordId(id);\n const map = await this.app.readTableRecords(safeUserId, this.name);\n const record = map.get(safeId);\n if (!record || record.deleted) {\n return undefined;\n }\n return record;\n }\n\n async getAllRecords(user: UserStorage): Promise<SnapshotRecord[]> {\n const safeUserId = validateUserId(user.id);\n const map = await this.app.readTableRecords(safeUserId, this.name);\n return filterActiveRecords(this.name, map.values());\n }\n\n async delete(): Promise<boolean> {\n return this.app.deleteTable(this.name);\n }\n}\n","import {\n hashUserPassword,\n UserBaseStorage,\n verifyUserPassword,\n} from '../base/index.js';\nimport type { FileStorage, FileUserData } from './storage.js';\n\n/**\n * Filesystem-backed implementation of `UserStorage`.\n */\nexport class UserFileStorage extends UserBaseStorage {\n private storage: FileStorage;\n\n constructor(data: FileUserData, storage: FileStorage) {\n super(data.id, data.username, data.createdAt);\n this.storage = storage;\n }\n\n protected getSecret(): string {\n return this.storage.secret;\n }\n\n async verifyPassword(password: string): Promise<boolean> {\n const user = await this.storage.findUserDataById(this.id);\n return verifyUserPassword(password, user?.passwordHash);\n }\n\n async changePassword(newPassword: string): Promise<void> {\n const newHash = await hashUserPassword(newPassword);\n await this.storage.updateUserData(this.id, { passwordHash: newHash });\n }\n\n async delete(): Promise<boolean> {\n return this.storage.deleteUser(this.id);\n }\n}\n","import * as crypto from 'node:crypto';\nimport * as fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport * as path from 'node:path';\nimport type { DatabaseSync, StatementSync } from 'node:sqlite';\nimport { getOrCreateKeyfileSecret, hashPassword } from '../../crypto.js';\nimport { TetherServerError, TetherServerErrorCode } from '../../errors.js';\nimport {\n getUserBucket,\n normalizeUsername,\n validateAppId,\n validatePassword,\n validateUserId,\n validateUsername,\n} from '../../validate.js';\nimport type { AppStorage } from '../app.js';\nimport { BaseStorage, filterTargetApps } from '../base/index.js';\nimport type { MaintenanceResult, StorageOptions } from '../storage.js';\nimport type { UserStorage } from '../user.js';\nimport { AppSqliteStorage } from './app.js';\nimport { UserSqliteStorage } from './user.js';\n\nexport interface SqliteUserData {\n id: string;\n username: string;\n passwordHash: string | null;\n createdAt: number;\n}\n\nexport interface UsersDbHandle {\n db: DatabaseSync;\n stmtFindById: StatementSync;\n stmtFindByUsername: StatementSync;\n stmtInsertUser: StatementSync;\n stmtUpdatePassword: StatementSync;\n stmtDeleteUser: StatementSync;\n stmtListUsers: StatementSync;\n}\n\nexport interface AppsDbHandle {\n db: DatabaseSync;\n stmtFindApp: StatementSync;\n stmtInsertApp: StatementSync;\n stmtListApps: StatementSync;\n stmtDeleteApp: StatementSync;\n stmtFindTable: StatementSync;\n stmtInsertTable: StatementSync;\n stmtListTables: StatementSync;\n stmtDeleteTable: StatementSync;\n stmtDeleteAppTables: StatementSync;\n}\n\nexport interface UserAppDbHandle {\n db: DatabaseSync;\n stmtGetRecord: StatementSync;\n stmtGetRecordForUpdate: StatementSync;\n stmtGetSnapshot: StatementSync;\n stmtGetSnapshotByTable: StatementSync;\n stmtInsertRecord: StatementSync;\n stmtUpdateRecord: StatementSync;\n stmtGetMeta: StatementSync;\n stmtSetMeta: StatementSync;\n stmtInsertChangelog: StatementSync;\n stmtGetChangelogSince: StatementSync;\n stmtPruneChangelog: StatementSync;\n stmtCountTableRecords: StatementSync;\n stmtDeleteTableRecords: StatementSync;\n stmtDeleteTableChangelog: StatementSync;\n}\n\nexport interface SqliteStorageOptions extends StorageOptions {\n baseDir?: string;\n inMemory?: boolean;\n /** Maximum number of open SQLite user database handles retained in LRU cache (default: 100). */\n maxOpenDatabases?: number;\n}\n\n/**\n * SQLite-backed implementation of `Storage`.\n */\nexport class SqliteStorage extends BaseStorage {\n readonly backend = 'sqlite';\n readonly baseDir: string;\n readonly inMemory: boolean;\n readonly maxOpenDatabases: number;\n readonly options: SqliteStorageOptions;\n readonly secret: string;\n private usersHandle: UsersDbHandle | null = null;\n private appsHandle: AppsDbHandle | null = null;\n private userAppDbs: Map<string, UserAppDbHandle> = new Map();\n\n constructor(options: SqliteStorageOptions = {}) {\n super(options);\n this.options = options;\n this.inMemory = Boolean(\n options.inMemory ||\n options.baseDir === ':memory:' ||\n (!options.baseDir && options.inMemory),\n );\n this.maxOpenDatabases = options.maxOpenDatabases ?? 100;\n this.baseDir = path.resolve(options.baseDir ?? '.data');\n this.secret =\n options.secret ??\n (this.inMemory\n ? crypto.randomBytes(32).toString('hex')\n : getOrCreateKeyfileSecret(this.baseDir));\n\n if (!this.inMemory) {\n try {\n fs.mkdirSync(this.baseDir, { recursive: true });\n } catch {\n // Ignore directory creation error\n }\n }\n }\n\n protected override getBaseDir(): string | undefined {\n return this.inMemory ? ':memory:' : this.baseDir;\n }\n\n getUsersDb(): UsersDbHandle {\n if (this.usersHandle) return this.usersHandle;\n\n const require = createRequire(import.meta.url);\n const { DatabaseSync } = require('node:sqlite') as {\n DatabaseSync: new (path: string) => DatabaseSync;\n };\n\n const dbPath = this.inMemory\n ? ':memory:'\n : path.join(this.baseDir, 'users.sqlite');\n\n const db = new DatabaseSync(dbPath);\n\n db.exec('PRAGMA busy_timeout = 5000;');\n db.exec('PRAGMA foreign_keys = ON;');\n if (!this.inMemory) {\n db.exec('PRAGMA journal_mode = WAL;');\n db.exec('PRAGMA synchronous = NORMAL;');\n }\n\n initUsersSchema(db);\n\n this.usersHandle = {\n db,\n stmtFindById: db.prepare(\n 'SELECT id, username, password_hash, created_at FROM users WHERE id = ?',\n ),\n stmtFindByUsername: db.prepare(\n 'SELECT id, username, password_hash, created_at FROM users WHERE username = ?',\n ),\n stmtInsertUser: db.prepare(\n 'INSERT INTO users (id, username, password_hash, created_at) VALUES (?, ?, ?, ?)',\n ),\n stmtUpdatePassword: db.prepare(\n 'UPDATE users SET password_hash = ? WHERE id = ?',\n ),\n stmtDeleteUser: db.prepare('DELETE FROM users WHERE id = ?'),\n stmtListUsers: db.prepare(\n 'SELECT id, username, password_hash, created_at FROM users ORDER BY created_at ASC',\n ),\n };\n\n return this.usersHandle;\n }\n\n getAppsDb(): AppsDbHandle {\n if (this.appsHandle) return this.appsHandle;\n\n const require = createRequire(import.meta.url);\n const { DatabaseSync } = require('node:sqlite') as {\n DatabaseSync: new (path: string) => DatabaseSync;\n };\n\n const dbPath = this.inMemory\n ? ':memory:'\n : path.join(this.baseDir, 'apps.sqlite');\n\n const db = new DatabaseSync(dbPath);\n\n db.exec('PRAGMA busy_timeout = 5000;');\n db.exec('PRAGMA foreign_keys = ON;');\n if (!this.inMemory) {\n db.exec('PRAGMA journal_mode = WAL;');\n db.exec('PRAGMA synchronous = NORMAL;');\n }\n\n initAppsSchema(db);\n\n this.appsHandle = {\n db,\n stmtFindApp: db.prepare('SELECT id, created_at FROM apps WHERE id = ?'),\n stmtInsertApp: db.prepare(\n 'INSERT INTO apps (id, created_at) VALUES (?, ?)',\n ),\n stmtListApps: db.prepare('SELECT id FROM apps ORDER BY id ASC'),\n stmtDeleteApp: db.prepare('DELETE FROM apps WHERE id = ?'),\n stmtFindTable: db.prepare(\n 'SELECT name, created_at FROM tables WHERE app_id = ? AND name = ?',\n ),\n stmtInsertTable: db.prepare(\n 'INSERT INTO tables (app_id, name, created_at) VALUES (?, ?, ?)',\n ),\n stmtListTables: db.prepare(\n 'SELECT name FROM tables WHERE app_id = ? ORDER BY name ASC',\n ),\n stmtDeleteTable: db.prepare(\n 'DELETE FROM tables WHERE app_id = ? AND name = ?',\n ),\n stmtDeleteAppTables: db.prepare('DELETE FROM tables WHERE app_id = ?'),\n };\n\n return this.appsHandle;\n }\n\n getUserAppDb(appId: string, userId: string): UserAppDbHandle {\n const safeAppId = validateAppId(appId);\n const safeUserId = validateUserId(userId);\n const cacheKey = `${safeAppId}:${safeUserId}`;\n\n let handle = this.userAppDbs.get(cacheKey);\n if (handle) {\n // Refresh LRU order (delete & re-insert to position at end of iteration)\n this.userAppDbs.delete(cacheKey);\n this.userAppDbs.set(cacheKey, handle);\n return handle;\n }\n\n // Evict oldest open handle if limit reached for file-backed storage\n if (!this.inMemory && this.userAppDbs.size >= this.maxOpenDatabases) {\n const oldestKey = this.userAppDbs.keys().next().value;\n if (oldestKey) {\n const oldestHandle = this.userAppDbs.get(oldestKey);\n try {\n oldestHandle?.db.close();\n } catch {\n // Ignore\n }\n this.userAppDbs.delete(oldestKey);\n }\n }\n\n const require = createRequire(import.meta.url);\n const { DatabaseSync } = require('node:sqlite') as {\n DatabaseSync: new (path: string) => DatabaseSync;\n };\n\n let dbPath: string;\n if (this.inMemory) {\n dbPath = ':memory:';\n } else {\n const bucket = getUserBucket(safeUserId);\n const userDir = path.join(this.baseDir, safeAppId, bucket);\n try {\n fs.mkdirSync(userDir, { recursive: true });\n } catch {\n // Ignore\n }\n dbPath = path.join(userDir, `${safeUserId}.sqlite`);\n }\n\n const db = new DatabaseSync(dbPath);\n\n db.exec('PRAGMA busy_timeout = 5000;');\n db.exec('PRAGMA foreign_keys = ON;');\n if (!this.inMemory) {\n db.exec('PRAGMA journal_mode = WAL;');\n db.exec('PRAGMA synchronous = NORMAL;');\n }\n\n initUserAppSchema(db);\n\n handle = {\n db,\n stmtGetRecord: db.prepare(\n 'SELECT table_name, id, version, timestamp, client_id, deleted, data FROM records WHERE table_name = ? AND id = ? AND deleted = 0',\n ),\n stmtGetRecordForUpdate: db.prepare(\n 'SELECT table_name, id, version, timestamp, client_id, deleted, data FROM records WHERE table_name = ? AND id = ?',\n ),\n stmtGetSnapshot: db.prepare(\n 'SELECT table_name, id, version, timestamp, client_id, deleted, data FROM records WHERE deleted = 0',\n ),\n stmtGetSnapshotByTable: db.prepare(\n 'SELECT table_name, id, version, timestamp, client_id, deleted, data FROM records WHERE table_name = ? AND deleted = 0',\n ),\n stmtInsertRecord: db.prepare(\n 'INSERT INTO records (table_name, id, version, timestamp, client_id, deleted, data) VALUES (?, ?, ?, ?, ?, ?, ?)',\n ),\n stmtUpdateRecord: db.prepare(\n 'UPDATE records SET version = ?, timestamp = ?, client_id = ?, deleted = ?, data = ? WHERE table_name = ? AND id = ?',\n ),\n stmtGetMeta: db.prepare(\n 'SELECT current_seq, min_seq FROM user_meta LIMIT 1',\n ),\n stmtSetMeta: db.prepare(\n 'INSERT OR REPLACE INTO user_meta (rowid, current_seq, min_seq) VALUES (1, ?, ?)',\n ),\n stmtInsertChangelog: db.prepare(\n 'INSERT INTO changelog (seq, table_name, id, op, version, timestamp, client_id, deleted, data) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',\n ),\n stmtGetChangelogSince: db.prepare(\n 'SELECT seq, table_name, id, op, version, timestamp, client_id, deleted, data FROM changelog WHERE seq > ? ORDER BY seq ASC',\n ),\n stmtPruneChangelog: db.prepare('DELETE FROM changelog WHERE seq < ?'),\n stmtCountTableRecords: db.prepare(\n 'SELECT COUNT(*) as count FROM records WHERE table_name = ? AND deleted = 0',\n ),\n stmtDeleteTableRecords: db.prepare(\n 'DELETE FROM records WHERE table_name = ?',\n ),\n stmtDeleteTableChangelog: db.prepare(\n 'DELETE FROM changelog WHERE table_name = ?',\n ),\n };\n\n this.userAppDbs.set(cacheKey, handle);\n return handle;\n }\n\n findUserDataById(id: string): SqliteUserData | undefined {\n const usersDb = this.getUsersDb();\n const row = usersDb.stmtFindById.get(id) as\n | {\n id: string;\n username: string;\n password_hash: string | null;\n created_at: number;\n }\n | undefined;\n if (!row) return undefined;\n return {\n id: row.id,\n username: row.username,\n passwordHash: row.password_hash,\n createdAt: row.created_at,\n };\n }\n\n updateUserData(id: string, passwordHash: string): void {\n const usersDb = this.getUsersDb();\n usersDb.stmtUpdatePassword.run(passwordHash, id);\n }\n\n async createApp(id: string): Promise<AppStorage> {\n const safeId = validateAppId(id);\n const appsDb = this.getAppsDb();\n const existing = appsDb.stmtFindApp.get(safeId);\n if (existing) {\n throw new TetherServerError(\n TetherServerErrorCode.AlreadyExists,\n 'Application already exists',\n );\n }\n\n appsDb.stmtInsertApp.run(safeId, Date.now());\n\n if (!this.inMemory) {\n const appDir = path.join(this.baseDir, safeId);\n try {\n fs.mkdirSync(appDir, { recursive: true });\n } catch {\n // Ignore\n }\n }\n\n return new AppSqliteStorage(safeId, this);\n }\n\n async getApp(id: string): Promise<AppStorage | undefined> {\n const safeId = validateAppId(id);\n const appsDb = this.getAppsDb();\n const existing = appsDb.stmtFindApp.get(safeId);\n if (existing) {\n return new AppSqliteStorage(safeId, this);\n }\n return undefined;\n }\n\n async getApps(): Promise<AppStorage[]> {\n const appsDb = this.getAppsDb();\n const rows = appsDb.stmtListApps.all() as Array<{ id: string }>;\n return rows.map((r) => new AppSqliteStorage(r.id, this));\n }\n\n async createUser(username: string, password: string): Promise<UserStorage> {\n const safeUsername = validateUsername(username);\n const validPassword = validatePassword(password);\n const usersDb = this.getUsersDb();\n\n const existing = usersDb.stmtFindByUsername.get(safeUsername);\n if (existing) {\n throw new TetherServerError(\n TetherServerErrorCode.AlreadyExists,\n 'Username is already registered',\n );\n }\n\n const userId = crypto.randomUUID();\n const passwordHash = await hashPassword(validPassword);\n const createdAt = Date.now();\n\n usersDb.stmtInsertUser.run(userId, safeUsername, passwordHash, createdAt);\n const userData: SqliteUserData = {\n id: userId,\n username: safeUsername,\n passwordHash,\n createdAt,\n };\n\n return new UserSqliteStorage(userData, this);\n }\n\n async getUser(id: string): Promise<UserStorage | undefined> {\n const safeUserId = validateUserId(id);\n const data = this.findUserDataById(safeUserId);\n if (data) {\n return new UserSqliteStorage(data, this);\n }\n return undefined;\n }\n\n async getUserByUsername(username: string): Promise<UserStorage | undefined> {\n const safeUsername = normalizeUsername(username);\n if (!safeUsername) return undefined;\n const usersDb = this.getUsersDb();\n const row = usersDb.stmtFindByUsername.get(safeUsername) as\n | {\n id: string;\n username: string;\n password_hash: string | null;\n created_at: number;\n }\n | undefined;\n if (!row) return undefined;\n const data: SqliteUserData = {\n id: row.id,\n username: row.username,\n passwordHash: row.password_hash,\n createdAt: row.created_at,\n };\n return new UserSqliteStorage(data, this);\n }\n\n async getUsers(): Promise<UserStorage[]> {\n const usersDb = this.getUsersDb();\n const rows = usersDb.stmtListUsers.all() as Array<{\n id: string;\n username: string;\n password_hash: string | null;\n created_at: number;\n }>;\n\n return rows.map(\n (r) =>\n new UserSqliteStorage(\n {\n id: r.id,\n username: r.username,\n passwordHash: r.password_hash,\n createdAt: r.created_at,\n },\n this,\n ),\n );\n }\n\n deleteUser(id: string): boolean {\n const safeUserId = validateUserId(id);\n let deleted = false;\n\n // Close and remove in-memory handles for this user\n for (const [key, handle] of Array.from(this.userAppDbs.entries())) {\n if (key.endsWith(`:${safeUserId}`)) {\n try {\n handle.db.close();\n } catch {\n // Ignore\n }\n this.userAppDbs.delete(key);\n }\n }\n\n // Delete user database files across all app directories\n if (!this.inMemory) {\n const bucket = getUserBucket(safeUserId);\n const apps = this.getAppsDb().stmtListApps.all() as Array<{ id: string }>;\n for (const app of apps) {\n const userDbFile = path.join(\n this.baseDir,\n app.id,\n bucket,\n `${safeUserId}.sqlite`,\n );\n const walFile = `${userDbFile}-wal`;\n const shmFile = `${userDbFile}-shm`;\n try {\n fs.unlinkSync(userDbFile);\n deleted = true;\n } catch {\n // Ignore\n }\n try {\n fs.unlinkSync(walFile);\n } catch {\n // Ignore\n }\n try {\n fs.unlinkSync(shmFile);\n } catch {\n // Ignore\n }\n }\n }\n\n const usersDb = this.getUsersDb();\n const res = usersDb.stmtDeleteUser.run(safeUserId) as { changes: number };\n if (res.changes > 0) deleted = true;\n\n return deleted;\n }\n\n deleteApp(id: string): boolean {\n const safeId = validateAppId(id);\n let deleted = false;\n\n // Close and remove in-memory user-app handles\n for (const [key, handle] of Array.from(this.userAppDbs.entries())) {\n if (key.startsWith(`${safeId}:`)) {\n try {\n handle.db.close();\n } catch {\n // Ignore\n }\n this.userAppDbs.delete(key);\n }\n }\n\n const appsDb = this.getAppsDb();\n appsDb.stmtDeleteAppTables.run(safeId);\n const res = appsDb.stmtDeleteApp.run(safeId) as { changes: number };\n if (res.changes > 0) deleted = true;\n\n if (!this.inMemory) {\n const appDir = path.join(this.baseDir, safeId);\n try {\n fs.rmSync(appDir, { recursive: true, force: true });\n deleted = true;\n } catch {\n // Ignore\n }\n }\n\n return deleted;\n }\n\n deleteTable(appId: string, tableName: string): boolean {\n const safeAppId = validateAppId(appId);\n const appsDb = this.getAppsDb();\n const res = appsDb.stmtDeleteTable.run(safeAppId, tableName) as {\n changes: number;\n };\n if (res.changes === 0) return false;\n\n // Clean up table data in any cached active handles for this app\n for (const [key, handle] of this.userAppDbs.entries()) {\n if (key.startsWith(`${safeAppId}:`)) {\n try {\n handle.stmtDeleteTableRecords.run(tableName);\n handle.stmtDeleteTableChangelog.run(tableName);\n } catch {\n // Ignore\n }\n }\n }\n\n return true;\n }\n\n async checkpoint(appId?: string): Promise<MaintenanceResult> {\n const allApps = await this.getApps();\n const targetApps = filterTargetApps(allApps, appId);\n const users = await this.getUsers();\n const count = this.executeOnAllDbs(\n 'PRAGMA wal_checkpoint(TRUNCATE);',\n targetApps,\n users,\n );\n\n return {\n action: 'checkpoint',\n backend: 'sqlite',\n appId,\n affectedCount: count,\n message: `Checkpoint completed successfully across ${count} database(s)`,\n };\n }\n\n async vacuum(appId?: string): Promise<MaintenanceResult> {\n const allApps = await this.getApps();\n const targetApps = filterTargetApps(allApps, appId);\n const users = await this.getUsers();\n const count = this.executeOnAllDbs('VACUUM;', targetApps, users);\n\n return {\n action: 'vacuum',\n backend: 'sqlite',\n appId,\n affectedCount: count,\n message: `Vacuum completed successfully across ${count} database(s)`,\n };\n }\n\n private executeOnAllDbs(\n sql: string,\n targetApps: AppStorage[],\n users: UserStorage[],\n ): number {\n let count = 0;\n this.getUsersDb().db.exec(sql);\n count++;\n this.getAppsDb().db.exec(sql);\n count++;\n\n for (const app of targetApps) {\n for (const user of users) {\n if (!this.inMemory) {\n const bucket = getUserBucket(user.id);\n const dbPath = path.join(\n this.baseDir,\n app.id,\n bucket,\n `${user.id}.sqlite`,\n );\n if (!fs.existsSync(dbPath)) continue;\n }\n const handle = this.getUserAppDb(app.id, user.id);\n handle.db.exec(sql);\n count++;\n }\n }\n return count;\n }\n\n async prune(appId?: string, keepCount?: number): Promise<MaintenanceResult> {\n const keep = keepCount ?? this.options.maxChangelogEntries ?? 1000;\n const allApps = await this.getApps();\n const targetApps = filterTargetApps(allApps, appId);\n\n const users = await this.getUsers();\n let totalPruned = 0;\n\n for (const app of targetApps) {\n for (const user of users) {\n if (!this.inMemory) {\n const bucket = getUserBucket(user.id);\n const dbPath = path.join(\n this.baseDir,\n app.id,\n bucket,\n `${user.id}.sqlite`,\n );\n if (!fs.existsSync(dbPath)) continue;\n }\n const handle = this.getUserAppDb(app.id, user.id);\n const metaRow = handle.stmtGetMeta.get() as\n | { current_seq: number; min_seq: number }\n | undefined;\n if (!metaRow || metaRow.current_seq === 0) continue;\n\n const currentSeq = metaRow.current_seq;\n const newMinSeq = Math.max(1, currentSeq - keep + 1);\n if (newMinSeq > metaRow.min_seq) {\n const res = handle.stmtPruneChangelog.run(newMinSeq) as {\n changes: number;\n };\n totalPruned += res.changes;\n handle.stmtSetMeta.run(currentSeq, newMinSeq);\n }\n }\n }\n\n return {\n action: 'prune',\n backend: 'sqlite',\n appId,\n affectedCount: totalPruned,\n message: `Prune completed successfully. Removed ${totalPruned} changelog record(s)`,\n };\n }\n\n async close(): Promise<void> {\n if (this.usersHandle) {\n try {\n this.usersHandle.db.close();\n } catch {\n // Ignore\n }\n this.usersHandle = null;\n }\n if (this.appsHandle) {\n try {\n this.appsHandle.db.close();\n } catch {\n // Ignore\n }\n this.appsHandle = null;\n }\n for (const handle of this.userAppDbs.values()) {\n try {\n handle.db.close();\n } catch {\n // Ignore\n }\n }\n this.userAppDbs.clear();\n }\n}\n\n// -- Private SQLite Schema & Migration Helpers ------------------------------\n\nconst USERS_DB_SCHEMA_VERSION = 1;\nconst APPS_DB_SCHEMA_VERSION = 1;\nconst USER_APP_DB_SCHEMA_VERSION = 1;\n\nfunction getSchemaVersion(db: DatabaseSync): number {\n const row = db.prepare('PRAGMA user_version;').get() as\n | { user_version?: number }\n | undefined;\n return row?.user_version ?? 0;\n}\n\nfunction setSchemaVersion(db: DatabaseSync, version: number): void {\n db.exec(`PRAGMA user_version = ${version};`);\n}\n\nfunction initUsersSchema(db: DatabaseSync): void {\n const currentVersion = getSchemaVersion(db);\n if (currentVersion < 1) {\n db.exec(`\n CREATE TABLE IF NOT EXISTS users (\n id TEXT PRIMARY KEY,\n username TEXT UNIQUE NOT NULL,\n password_hash TEXT,\n created_at INTEGER NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_users_username ON users (username);\n `);\n setSchemaVersion(db, USERS_DB_SCHEMA_VERSION);\n }\n}\n\nfunction initAppsSchema(db: DatabaseSync): void {\n const currentVersion = getSchemaVersion(db);\n if (currentVersion < 1) {\n db.exec(`\n CREATE TABLE IF NOT EXISTS apps (\n id TEXT PRIMARY KEY,\n created_at INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS tables (\n app_id TEXT NOT NULL,\n name TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n PRIMARY KEY (app_id, name)\n );\n CREATE INDEX IF NOT EXISTS idx_tables_app ON tables (app_id);\n `);\n setSchemaVersion(db, APPS_DB_SCHEMA_VERSION);\n }\n}\n\nfunction initUserAppSchema(db: DatabaseSync): void {\n const currentVersion = getSchemaVersion(db);\n if (currentVersion < 1) {\n db.exec(`\n CREATE TABLE IF NOT EXISTS records (\n table_name TEXT NOT NULL,\n id TEXT NOT NULL,\n version INTEGER NOT NULL,\n timestamp INTEGER NOT NULL,\n client_id TEXT NOT NULL,\n deleted INTEGER NOT NULL,\n data TEXT,\n PRIMARY KEY (table_name, id)\n );\n\n CREATE INDEX IF NOT EXISTS idx_records_lookup\n ON records (table_name, deleted);\n\n CREATE TABLE IF NOT EXISTS changelog (\n seq INTEGER PRIMARY KEY,\n table_name TEXT NOT NULL,\n id TEXT NOT NULL,\n op TEXT NOT NULL,\n version INTEGER NOT NULL,\n timestamp INTEGER NOT NULL,\n client_id TEXT NOT NULL,\n deleted INTEGER NOT NULL,\n data TEXT\n );\n\n CREATE INDEX IF NOT EXISTS idx_changelog_sync\n ON changelog (seq);\n\n CREATE TABLE IF NOT EXISTS user_meta (\n current_seq INTEGER NOT NULL,\n min_seq INTEGER NOT NULL\n );\n `);\n setSchemaVersion(db, USER_APP_DB_SCHEMA_VERSION);\n }\n}\n","import type { SnapshotRecord, StoredRecord } from '../../../shared/types.js';\nimport { validateRecordId, validateUserId } from '../../validate.js';\nimport { TableBaseStorage } from '../base/table.js';\nimport type { UserStorage } from '../user.js';\nimport type { AppSqliteStorage } from './app.js';\n\ninterface RawRecordRow {\n table_name: string;\n id: string;\n version: number;\n timestamp: number;\n client_id: string;\n deleted: number;\n data: string | null;\n}\n\n/**\n * SQLite-backed implementation of `TableStorage`.\n */\nexport class TableSqliteStorage extends TableBaseStorage {\n declare readonly app: AppSqliteStorage;\n\n async delete(): Promise<boolean> {\n return this.app.deleteTable(this.name);\n }\n private parseData(raw: string | null): unknown {\n if (raw === null || raw === undefined) return null;\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n }\n\n async getRecord(\n user: UserStorage,\n id: string,\n ): Promise<StoredRecord | undefined> {\n const safeUserId = validateUserId(user.id);\n const safeId = validateRecordId(id);\n const handle = this.app.getUserDb(safeUserId);\n\n const row = handle.stmtGetRecord.get(this.name, safeId) as\n | RawRecordRow\n | undefined;\n if (!row) return undefined;\n\n return {\n id: row.id,\n version: row.version,\n timestamp: row.timestamp,\n clientId: row.client_id ?? '',\n deleted: Boolean(row.deleted),\n data: this.parseData(row.data),\n };\n }\n\n async getAllRecords(user: UserStorage): Promise<SnapshotRecord[]> {\n const safeUserId = validateUserId(user.id);\n const handle = this.app.getUserDb(safeUserId);\n const rows = handle.stmtGetSnapshotByTable.all(\n this.name,\n ) as unknown as RawRecordRow[];\n\n return rows.map((row) => ({\n table: row.table_name,\n id: row.id,\n version: row.version,\n timestamp: row.timestamp,\n clientId: row.client_id ?? '',\n data: this.parseData(row.data),\n }));\n }\n}\n","import { shouldOverwrite } from '../../../shared/clock.js';\nimport { type ChangeRecord, OperationType } from '../../../shared/types.js';\nimport { TetherServerError, TetherServerErrorCode } from '../../errors.js';\nimport {\n calculateByteSize,\n validateRecordId,\n validateTableName,\n validateTimestamp,\n validateUserId,\n} from '../../validate.js';\nimport { AppBaseStorage } from '../base/app.js';\nimport type { TableStorage } from '../table.js';\nimport type { UserStorage } from '../user.js';\nimport type { SqliteStorage, UserAppDbHandle } from './storage.js';\nimport { TableSqliteStorage } from './table.js';\n\ninterface RawRecordRow {\n table_name: string;\n id: string;\n version: number;\n timestamp: number;\n client_id: string;\n deleted: number;\n data: string | null;\n}\n\ninterface RawChangelogRow {\n seq: number;\n table_name: string;\n id: string;\n op: string;\n version: number;\n timestamp: number;\n client_id: string;\n deleted: number;\n data: string | null;\n}\n\ninterface RawMetaRow {\n current_seq: number;\n min_seq: number;\n}\n\n/**\n * SQLite-backed application namespace storage implementation.\n */\nexport class AppSqliteStorage extends AppBaseStorage {\n readonly storage: SqliteStorage;\n\n constructor(id: string, storage: SqliteStorage) {\n super(id);\n this.storage = storage;\n }\n\n getUserDb(userId: string): UserAppDbHandle {\n return this.storage.getUserAppDb(this.id, userId);\n }\n\n private parseData(raw: string | null): unknown {\n if (raw === null || raw === undefined) return null;\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n }\n\n private serializeData(data: unknown): string | null {\n if (data === null || data === undefined) return null;\n if (typeof data === 'string') return data;\n return JSON.stringify(data);\n }\n\n async createTable(name: string): Promise<TableStorage> {\n const safeName = validateTableName(name);\n const appsDb = this.storage.getAppsDb();\n const existing = appsDb.stmtFindTable.get(this.id, safeName);\n if (existing) {\n throw new TetherServerError(\n TetherServerErrorCode.AlreadyExists,\n 'Table already exists in this application',\n );\n }\n appsDb.stmtInsertTable.run(this.id, safeName, Date.now());\n return new TableSqliteStorage(safeName, this);\n }\n\n async getTable(name: string): Promise<TableStorage | undefined> {\n const safeName = validateTableName(name);\n const appsDb = this.storage.getAppsDb();\n const row = appsDb.stmtFindTable.get(this.id, safeName);\n if (row) {\n return new TableSqliteStorage(safeName, this);\n }\n return undefined;\n }\n\n async getTables(): Promise<TableStorage[]> {\n const appsDb = this.storage.getAppsDb();\n const rows = appsDb.stmtListTables.all(this.id) as Array<{ name: string }>;\n return rows.map((r) => new TableSqliteStorage(r.name, this));\n }\n\n async applyChanges(\n user: UserStorage,\n changes: ChangeRecord[],\n ): Promise<{ applied: ChangeRecord[]; newSeq: number }> {\n const safeUserId = validateUserId(user.id);\n const appsDb = this.storage.getAppsDb();\n const handle = this.getUserDb(safeUserId);\n const applied: ChangeRecord[] = [];\n\n const maxRecords = this.storage.options.maxRecordsPerTable ?? 10000;\n const maxRecordSize = this.storage.options.maxRecordSizeBytes ?? 512 * 1024;\n const maxChangelog = this.storage.options.maxChangelogEntries ?? 1000;\n\n handle.db.exec('BEGIN IMMEDIATE TRANSACTION');\n try {\n const metaRow = handle.stmtGetMeta.get() as RawMetaRow | undefined;\n let currentSeq = metaRow?.current_seq ?? 0;\n let minSeq = metaRow?.min_seq ?? 0;\n const checkedTables = new Set<string>();\n\n for (const change of changes) {\n const tableName = validateTableName(change.table);\n const recordId = validateRecordId(change.id);\n validateTimestamp(change.timestamp);\n\n if (!checkedTables.has(tableName)) {\n const tableExists = appsDb.stmtFindTable.get(this.id, tableName);\n if (!tableExists) {\n throw new TetherServerError(\n TetherServerErrorCode.NotFound,\n 'Table not found',\n );\n }\n checkedTables.add(tableName);\n }\n\n const existingRecord = handle.stmtGetRecordForUpdate.get(\n tableName,\n recordId,\n ) as RawRecordRow | undefined;\n\n if (change.op === OperationType.Put && !existingRecord) {\n const countRow = handle.stmtCountTableRecords.get(tableName) as\n | { count: number }\n | undefined;\n const tableCount = countRow?.count ?? 0;\n if (tableCount >= maxRecords) {\n throw new TetherServerError(\n TetherServerErrorCode.LimitExceeded,\n 'Table record limit reached',\n );\n }\n }\n\n const payloadBytes = calculateByteSize(change.data);\n if (payloadBytes > maxRecordSize) {\n throw new TetherServerError(\n TetherServerErrorCode.LimitExceeded,\n 'Record payload exceeds maximum allowed size',\n );\n }\n\n const shouldApply =\n !existingRecord ||\n shouldOverwrite(\n {\n timestamp: change.timestamp,\n clientId: change.clientId,\n version: change.version,\n },\n {\n timestamp: existingRecord.timestamp,\n clientId: existingRecord.client_id ?? '',\n version: existingRecord.version,\n },\n );\n\n if (shouldApply) {\n currentSeq++;\n const assignedSeq = currentSeq;\n\n if (minSeq === 0) {\n minSeq = 1;\n }\n\n const isDeleted = change.op === OperationType.Delete;\n const nextVersion = (existingRecord?.version ?? 0) + 1;\n const serializedData = isDeleted\n ? null\n : this.serializeData(change.data);\n\n if (existingRecord) {\n handle.stmtUpdateRecord.run(\n nextVersion,\n change.timestamp,\n change.clientId ?? '',\n isDeleted ? 1 : 0,\n serializedData,\n tableName,\n recordId,\n );\n } else {\n handle.stmtInsertRecord.run(\n tableName,\n recordId,\n nextVersion,\n change.timestamp,\n change.clientId ?? '',\n isDeleted ? 1 : 0,\n serializedData,\n );\n }\n\n handle.stmtInsertChangelog.run(\n assignedSeq,\n tableName,\n recordId,\n change.op,\n nextVersion,\n change.timestamp,\n change.clientId ?? '',\n isDeleted ? 1 : 0,\n serializedData,\n );\n\n applied.push({\n seq: assignedSeq,\n table: tableName,\n id: recordId,\n op: change.op,\n version: nextVersion,\n timestamp: change.timestamp,\n clientId: change.clientId,\n data: isDeleted ? undefined : change.data,\n });\n }\n }\n\n if (currentSeq - minSeq + 1 > maxChangelog) {\n const newMinSeq = currentSeq - maxChangelog + 1;\n handle.stmtPruneChangelog.run(newMinSeq);\n minSeq = newMinSeq;\n }\n\n if (applied.length > 0) {\n handle.stmtSetMeta.run(currentSeq, minSeq);\n }\n\n handle.db.exec('COMMIT');\n return { applied, newSeq: currentSeq };\n } catch (err) {\n handle.db.exec('ROLLBACK');\n throw err;\n }\n }\n\n async getChangesSince(\n user: UserStorage,\n fromSeq: number,\n ): Promise<{\n changes: ChangeRecord[];\n currentSeq: number;\n requiresSnapshot?: boolean;\n }> {\n const safeUserId = validateUserId(user.id);\n const handle = this.getUserDb(safeUserId);\n const metaRow = handle.stmtGetMeta.get() as RawMetaRow | undefined;\n const currentSeq = metaRow?.current_seq ?? 0;\n const minSeq = metaRow?.min_seq ?? 0;\n\n if ((fromSeq < minSeq && minSeq > 0) || fromSeq > currentSeq) {\n return { changes: [], currentSeq, requiresSnapshot: true };\n }\n\n const rows = handle.stmtGetChangelogSince.all(\n fromSeq,\n ) as unknown as RawChangelogRow[];\n\n const changes: ChangeRecord[] = rows.map((row) => ({\n seq: row.seq,\n table: row.table_name,\n id: row.id,\n op:\n row.op === OperationType.Delete\n ? OperationType.Delete\n : OperationType.Put,\n version: row.version,\n timestamp: row.timestamp,\n clientId: row.client_id ?? '',\n data:\n row.op === OperationType.Delete ? undefined : this.parseData(row.data),\n }));\n\n return { changes, currentSeq, requiresSnapshot: false };\n }\n\n async getCurrentSeq(user: UserStorage): Promise<number> {\n const safeUserId = validateUserId(user.id);\n const handle = this.getUserDb(safeUserId);\n const metaRow = handle.stmtGetMeta.get() as RawMetaRow | undefined;\n return metaRow?.current_seq ?? 0;\n }\n\n async delete(): Promise<boolean> {\n return this.storage.deleteApp(this.id);\n }\n\n deleteTable(name: string): boolean {\n return this.storage.deleteTable(this.id, name);\n }\n}\n","import {\n hashUserPassword,\n UserBaseStorage,\n verifyUserPassword,\n} from '../base/index.js';\nimport type { SqliteStorage, SqliteUserData } from './storage.js';\n\n/**\n * SQLite-backed implementation of `UserStorage`.\n */\nexport class UserSqliteStorage extends UserBaseStorage {\n private storage: SqliteStorage;\n\n constructor(data: SqliteUserData, storage: SqliteStorage) {\n super(data.id, data.username, data.createdAt);\n this.storage = storage;\n }\n\n protected getSecret(): string {\n return this.storage.secret;\n }\n\n async verifyPassword(password: string): Promise<boolean> {\n const user = this.storage.findUserDataById(this.id);\n return verifyUserPassword(password, user?.passwordHash);\n }\n\n async changePassword(newPassword: string): Promise<void> {\n const newHash = await hashUserPassword(newPassword);\n this.storage.updateUserData(this.id, newHash);\n }\n\n async delete(): Promise<boolean> {\n return this.storage.deleteUser(this.id);\n }\n}\n","import { TetherServerError, TetherServerErrorCode } from '../server/index.js';\nimport type { BackendType } from './backend.js';\n\n/**\n * Parsed CLI arguments and configuration options.\n */\nexport interface ParsedCliArgs {\n command: string;\n positionalArgs: string[];\n port: number;\n host: string;\n backend: BackendType;\n dir: string;\n}\n\n/**\n * Parses raw command-line arguments into structured options.\n *\n * @param args - Command line arguments.\n * @returns Parsed CLI configuration.\n */\nexport function parseCliArgs(args: string[]): ParsedCliArgs {\n const positionalArgs: string[] = [];\n let port = process.env.PORT ? Number.parseInt(process.env.PORT, 10) : 8080;\n let host = process.env.HOST ?? '0.0.0.0';\n let backend: BackendType = 'memory';\n let dir = '.data';\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg.startsWith('--port=')) port = Number.parseInt(arg.slice(7), 10);\n else if (arg === '-p') port = Number.parseInt(args[++i] ?? '', 10);\n else if (arg.startsWith('--host=')) host = arg.slice(7);\n else if (arg === '-H') host = args[++i] ?? host;\n else if (arg === '--memory') backend = 'memory';\n else if (arg === '--file' || arg.startsWith('--file=')) {\n backend = 'file';\n if (arg.startsWith('--file=')) dir = arg.slice(7);\n } else if (arg === '--sqlite' || arg.startsWith('--sqlite=')) {\n backend = 'sqlite';\n if (arg.startsWith('--sqlite=')) dir = arg.slice(9);\n } else if (arg.startsWith('-')) {\n throw new TetherServerError(\n TetherServerErrorCode.ConfigurationError,\n `Unknown or invalid option: \"${arg}\"`,\n );\n } else {\n positionalArgs.push(arg);\n }\n }\n\n return {\n command: positionalArgs[0] ?? 'serve',\n positionalArgs,\n port,\n host,\n backend,\n dir,\n };\n}\n","import * as path from 'node:path';\nimport {\n FileStorage,\n MemoryStorage,\n SqliteStorage,\n type Storage,\n type StorageOptions,\n TetherServerError,\n TetherServerErrorCode,\n} from '../server/index.js';\n\n/**\n * Backend persistence type for TetherDB server.\n */\nexport type BackendType = 'memory' | 'file' | 'sqlite';\n\n/**\n * Instantiates the matching Storage implementation for a given backend type and directory.\n *\n * @param backend - Target backend ('memory', 'file', or 'sqlite'). Defaults to 'memory'.\n * @param baseDir - Directory path for file and sqlite backends. Defaults to '.data'.\n * @param options - Optional storage configuration and limits.\n * @returns Instantiated `Storage` engine.\n */\nexport function createBackend(\n backend: BackendType = 'memory',\n baseDir = '.data',\n options?: StorageOptions,\n): Storage {\n const resolvedDir = path.resolve(baseDir);\n switch (backend) {\n case 'memory':\n return new MemoryStorage(options);\n case 'sqlite':\n return new SqliteStorage({ baseDir: resolvedDir, ...options });\n case 'file':\n return new FileStorage({ baseDir: resolvedDir, ...options });\n default:\n throw new TetherServerError(\n TetherServerErrorCode.ConfigurationError,\n `Unknown backend type: \"${backend}\". Expected 'memory', 'file', or 'sqlite'`,\n );\n }\n}\n","import {\n type Storage,\n TetherServerError,\n TetherServerErrorCode,\n} from '../../server/index.js';\n\n/**\n * Handles the 'apps' command family (list, add, rm).\n *\n * @param storage - Instantiated Storage engine.\n * @param args - Positional arguments: `[command, action, appId]`.\n */\nexport async function handleAppsCommand(\n storage: Storage,\n [, action = 'list', appId]: string[],\n): Promise<void> {\n if (action === 'list') {\n const apps = await storage.getApps();\n if (!apps.length) return console.log('No applications found.');\n console.log(`Applications (${apps.length}):`);\n for (const app of apps) {\n const tables = await app.getTables();\n const tableList =\n tables.length > 0 ? tables.map((t) => t.name).join(', ') : 'no tables';\n console.log(` • ${app.id} (tables: ${tableList})`);\n }\n } else if (action === 'add') {\n if (!appId) {\n throw new TetherServerError(\n TetherServerErrorCode.ConfigurationError,\n 'Missing application ID',\n );\n }\n if (await storage.getApp(appId)) {\n return console.log(`Application already exists: ${appId}`);\n }\n await storage.createApp(appId);\n console.log(`Created application: ${appId}`);\n } else if (action === 'rm') {\n if (!appId) {\n throw new TetherServerError(\n TetherServerErrorCode.ConfigurationError,\n 'Missing application ID',\n );\n }\n const app = await storage.getApp(appId);\n if (!app) return console.log(`Application not found: ${appId}`);\n await app.delete();\n console.log(`Deleted application: ${appId}`);\n } else {\n throw new TetherServerError(\n TetherServerErrorCode.ConfigurationError,\n `Unknown apps action: \"${action}\"`,\n );\n }\n}\n","/**\n * Displays command-line interface usage instructions.\n */\nexport function printHelp(): void {\n console.log(`\nTetherDB CLI\n\nUsage:\n npx tetherdb [command] [options]\n tetherdb [command] [options]\n\nCommands:\n serve (default) Start HTTP and WebSocket synchronization server\n status [appid] Display storage and database statistics\n maintenance checkpoint [appid] Truncate WAL files for SQLite databases\n maintenance vacuum [appid] Reclaim disk space and defragment database files\n maintenance prune [appid] [keepCount] Prune changelogs older than retention threshold\n apps [list] List all applications\n apps add <appid> Register a new application\n apps rm <appid> Delete an application and its data\n tables [list] <appid> List tables for an application\n tables add <appid> <table1> [table2...] Add one or more tables to an application\n tables rm <appid> <table1> [table2...] Remove table(s) from an application\n users [list] List all registered user accounts\n users add <username> <password> Register a new user account\n users rm <userid> Delete a user account and associated data\n\nServer Options (for 'serve'):\n --port=<number>, -p <number> Port number to bind (default: 8080 or PORT env)\n --host=<string>, -H <string> Host address to bind (default: 0.0.0.0 or HOST env)\n\nBackend Options (for all commands):\n --memory Run in-memory without disk persistence (default)\n --file[=<dir>] Use filesystem directory for auth and data (default: .data)\n --sqlite[=<dir>] Use SQLite databases for auth and data (default: .data)\n --help, -h Show this help message\n`);\n}\n","import {\n type Storage,\n TetherServerError,\n TetherServerErrorCode,\n} from '../../server/index.js';\n\n/**\n * Handles the 'maintenance' command to execute maintenance routines.\n *\n * @param storage - Instantiated Storage engine.\n * @param positionalArgs - Positional CLI arguments (e.g. ['maintenance', 'checkpoint', 'my-app']).\n */\nexport async function handleMaintenanceCommand(\n storage: Storage,\n positionalArgs: string[],\n): Promise<void> {\n const action = positionalArgs[1];\n const appId = positionalArgs[2];\n\n if (!action) {\n throw new TetherServerError(\n TetherServerErrorCode.InvalidInput,\n 'Missing maintenance action. Expected \"checkpoint\", \"vacuum\", or \"prune\"',\n );\n }\n\n switch (action) {\n case 'checkpoint': {\n const result = await storage.checkpoint(appId);\n console.log(result.message);\n break;\n }\n\n case 'vacuum': {\n const result = await storage.vacuum(appId);\n console.log(result.message);\n break;\n }\n\n case 'prune': {\n const keepStr = positionalArgs[3];\n const keepCount = keepStr ? Number.parseInt(keepStr, 10) : undefined;\n const result = await storage.prune(appId, keepCount);\n console.log(result.message);\n break;\n }\n\n default:\n throw new TetherServerError(\n TetherServerErrorCode.InvalidInput,\n `Unknown maintenance action: \"${action}\". Expected \"checkpoint\", \"vacuum\", or \"prune\"`,\n );\n }\n}\n","import * as path from 'node:path';\nimport {\n type RunningServer,\n type Storage,\n startServer,\n} from '../../server/index.js';\nimport type { BackendType } from '../backend.js';\n\n/**\n * Handles the 'serve' command to launch the HTTP and WebSocket synchronization server.\n *\n * @param storage - Instantiated Storage engine.\n * @param backend - Storage backend type ('memory', 'file', or 'sqlite').\n * @param dir - Data directory for file-based backends.\n * @param port - HTTP port to bind.\n * @param host - Network interface to bind.\n * @returns Handle to the running server.\n */\nexport async function handleServeCommand(\n storage: Storage,\n backend: BackendType,\n dir: string,\n port: number,\n host: string,\n): Promise<RunningServer> {\n const running = await startServer({ port, host, storage });\n const hostLabel = running.host === '0.0.0.0' ? 'localhost' : running.host;\n const storageInfo =\n backend === 'memory'\n ? 'in-memory (ephemeral)'\n : `${backend} (${path.resolve(dir)})`;\n\n console.log(\n `TetherDB server listening at: http://${hostLabel}:${running.port}${running.server.basePath}`,\n );\n console.log(\n `WebSocket sync endpoint: ws://${hostLabel}:${running.port}${running.server.webSocketPath}`,\n );\n console.log(`Storage backend: ${storageInfo}`);\n\n const shutdown = async () => {\n console.log('Stopping TetherDB server...');\n try {\n await running.close();\n await storage.close?.();\n } finally {\n process.exit(0);\n }\n };\n\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n\n return running;\n}\n","import type { Storage } from '../../server/index.js';\nimport { readServerLock } from '../../server/lock.js';\n\n/**\n * Handles the 'status' command to display storage backend statistics.\n *\n * @param storage - Instantiated Storage engine.\n * @param positionalArgs - Positional CLI arguments (e.g. ['status', 'my-app']).\n */\nexport async function handleStatusCommand(\n storage: Storage,\n positionalArgs: string[],\n): Promise<void> {\n const appId = positionalArgs[1];\n const status = await storage.getStatus(appId);\n\n console.log('TetherDB Storage Status:');\n console.log(` Backend: ${status.backend}`);\n if (status.baseDir) {\n console.log(` Directory: ${status.baseDir}`);\n }\n if (status.baseDir && status.backend !== 'memory') {\n const lock = readServerLock(status.baseDir);\n if (lock) {\n console.log(\n ` Server: Running (PID: ${lock.pid}, Port: ${lock.port}, Host: ${lock.host})`,\n );\n } else {\n console.log(' Server: Stopped');\n }\n }\n console.log(` Users: ${status.usersCount}`);\n console.log(` Total Apps: ${status.appsCount}`);\n\n if (status.apps && status.apps.length > 0) {\n console.log('\\nApplications:');\n for (const app of status.apps) {\n console.log(` - App: ${app.id}`);\n console.log(\n ` Tables (${app.tables.length}): ${app.tables.length > 0 ? app.tables.join(', ') : '(none)'}`,\n );\n }\n } else if (appId) {\n console.log(`\\nNo tables found for application \"${appId}\".`);\n }\n}\n","import {\n type Storage,\n TetherServerError,\n TetherServerErrorCode,\n} from '../../server/index.js';\n\n/**\n * Handles the 'tables' command family (list, add, rm, <appid>).\n *\n * @param storage - Instantiated Storage engine.\n * @param args - Positional arguments: `[command, action, appId, ...tableNames]`.\n */\nexport async function handleTablesCommand(\n storage: Storage,\n args: string[],\n): Promise<void> {\n const action = args[1] ?? 'list';\n if (action === 'add' || action === 'rm') {\n const appId = args[2];\n if (!appId) {\n throw new TetherServerError(\n TetherServerErrorCode.ConfigurationError,\n 'Missing application ID',\n );\n }\n const tableNames = args.slice(3);\n if (!tableNames.length) {\n throw new TetherServerError(\n TetherServerErrorCode.ConfigurationError,\n 'Missing table name',\n );\n }\n const app = await storage.getApp(appId);\n if (!app) {\n throw new TetherServerError(\n TetherServerErrorCode.NotFound,\n `Application \"${appId}\" not found`,\n );\n }\n for (const tableName of tableNames) {\n if (action === 'add') {\n if (await app.getTable(tableName)) {\n console.log(\n `Table \"${tableName}\" already exists in application \"${appId}\"`,\n );\n } else {\n await app.createTable(tableName);\n console.log(`Added table \"${tableName}\" to application \"${appId}\"`);\n }\n } else {\n const table = await app.getTable(tableName);\n if (!table) {\n console.log(\n `Table \"${tableName}\" not found in application \"${appId}\"`,\n );\n } else {\n await table.delete();\n console.log(\n `Removed table \"${tableName}\" from application \"${appId}\"`,\n );\n }\n }\n }\n } else {\n const appId = action === 'list' ? args[2] : action;\n if (!appId) {\n throw new TetherServerError(\n TetherServerErrorCode.ConfigurationError,\n 'Missing application ID',\n );\n }\n const app = await storage.getApp(appId);\n if (!app) {\n throw new TetherServerError(\n TetherServerErrorCode.NotFound,\n `Application \"${appId}\" not found`,\n );\n }\n const tables = await app.getTables();\n if (!tables.length) {\n console.log(`No tables found for application \"${appId}\".`);\n } else {\n console.log(`Tables for application \"${appId}\" (${tables.length}):`);\n for (const t of tables) console.log(` • ${t.name}`);\n }\n }\n}\n","import {\n type Storage,\n TetherServerError,\n TetherServerErrorCode,\n} from '../../server/index.js';\n\n/**\n * Handles the 'users' command family (list, add, rm).\n *\n * @param storage - Instantiated Storage engine.\n * @param args - Positional arguments: `[command, action, arg1, arg2]`.\n */\nexport async function handleUsersCommand(\n storage: Storage,\n [, action = 'list', arg1, arg2]: string[],\n): Promise<void> {\n if (action === 'list') {\n const users = await storage.getUsers();\n if (!users.length) return console.log('No registered users found.');\n console.log(`Registered users (${users.length}):`);\n for (const u of users) {\n console.log(\n ` • [${u.id}] ${u.username} (created: ${new Date(u.createdAt).toISOString()})`,\n );\n }\n } else if (action === 'add') {\n if (!arg1) {\n throw new TetherServerError(\n TetherServerErrorCode.ConfigurationError,\n 'Missing username',\n );\n }\n if (!arg2) {\n throw new TetherServerError(\n TetherServerErrorCode.ConfigurationError,\n 'Missing password',\n );\n }\n const user = await storage.createUser(arg1, arg2);\n console.log(`Created user: [${user.id}] ${user.username}`);\n } else if (action === 'rm') {\n if (!arg1) {\n throw new TetherServerError(\n TetherServerErrorCode.ConfigurationError,\n 'Missing user ID',\n );\n }\n const user = await storage.getUser(arg1);\n if (!user) return console.log(`User not found: ${arg1}`);\n await user.delete();\n console.log(`Deleted user: ${arg1}`);\n } else {\n throw new TetherServerError(\n TetherServerErrorCode.ConfigurationError,\n `Unknown users action: \"${action}\"`,\n );\n }\n}\n"],"mappings":"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","names":["path","path","resolve","fs","path","crypto","resolve","crypto","fs","path","fs","path","resolve","crypto","fs","path","require","path","path"]}
|