@durable-streams/server 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 durable-stream
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/package.json CHANGED
@@ -1,35 +1,53 @@
1
1
  {
2
2
  "name": "@durable-streams/server",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Node.js reference server implementation for Durable Streams",
5
5
  "author": "Durable Stream contributors",
6
6
  "license": "Apache-2.0",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/durable-streams/durable-streams.git",
10
+ "directory": "packages/server"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/durable-streams/durable-streams/issues"
14
+ },
15
+ "keywords": [
16
+ "durable-streams",
17
+ "streaming",
18
+ "server",
19
+ "typescript"
20
+ ],
7
21
  "type": "module",
8
- "main": "./dist/index.js",
22
+ "main": "./dist/index.cjs",
23
+ "module": "./dist/index.js",
9
24
  "types": "./dist/index.d.ts",
10
25
  "exports": {
11
26
  ".": {
12
- "import": "./dist/index.js",
13
- "types": "./dist/index.d.ts"
14
- }
15
- },
16
- "scripts": {
17
- "build": "tsdown",
18
- "dev": "tsdown --watch",
19
- "typecheck": "tsc --noEmit"
27
+ "import": {
28
+ "types": "./dist/index.d.ts",
29
+ "default": "./dist/index.js"
30
+ },
31
+ "require": {
32
+ "types": "./dist/index.d.cts",
33
+ "default": "./dist/index.cjs"
34
+ }
35
+ },
36
+ "./package.json": "./package.json"
20
37
  },
38
+ "sideEffects": false,
21
39
  "dependencies": {
22
- "@durable-streams/client": "workspace:*",
23
- "@durable-streams/state": "workspace:*",
24
40
  "@neophi/sieve-cache": "^1.0.0",
25
- "lmdb": "^3.3.0"
41
+ "lmdb": "^3.3.0",
42
+ "@durable-streams/client": "0.1.1",
43
+ "@durable-streams/state": "0.1.1"
26
44
  },
27
45
  "devDependencies": {
28
- "@durable-streams/server-conformance-tests": "workspace:*",
29
46
  "@types/node": "^22.0.0",
30
47
  "tsdown": "^0.9.0",
31
48
  "typescript": "^5.0.0",
32
- "vitest": "^3.2.4"
49
+ "vitest": "^3.2.4",
50
+ "@durable-streams/server-conformance-tests": "0.1.1"
33
51
  },
34
52
  "files": [
35
53
  "dist",
@@ -37,5 +55,10 @@
37
55
  ],
38
56
  "engines": {
39
57
  "node": ">=18.0.0"
58
+ },
59
+ "scripts": {
60
+ "build": "tsdown",
61
+ "dev": "tsdown --watch",
62
+ "typecheck": "tsc --noEmit"
40
63
  }
41
- }
64
+ }
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/server.ts","../src/store.ts"],"sourcesContent":["/**\n * HTTP server for durable streams testing.\n */\n\nimport { createServer } from \"node:http\"\nimport { StreamStore } from \"./store\"\nimport type { IncomingMessage, Server, ServerResponse } from \"node:http\"\nimport type { TestServerOptions } from \"./types\"\n\n// Protocol headers\nconst STREAM_OFFSET_HEADER = `stream-offset`\nconst STREAM_CURSOR_HEADER = `stream-cursor`\nconst STREAM_UP_TO_DATE_HEADER = `stream-up-to-date`\nconst STREAM_SEQ_HEADER = `stream-seq`\nconst STREAM_TTL_HEADER = `stream-ttl`\nconst STREAM_EXPIRES_AT_HEADER = `stream-expires-at`\n\n// Query params\nconst OFFSET_QUERY_PARAM = `offset`\nconst LIVE_QUERY_PARAM = `live`\nconst CURSOR_QUERY_PARAM = `cursor`\n\n/**\n * In-memory HTTP server for testing durable streams.\n */\nexport class DurableStreamTestServer {\n readonly store: StreamStore\n private server: Server | null = null\n private options: Required<TestServerOptions>\n private _url: string | null = null\n\n constructor(options: TestServerOptions = {}) {\n this.store = new StreamStore()\n this.options = {\n port: options.port ?? 0,\n host: options.host ?? `127.0.0.1`,\n longPollTimeout: options.longPollTimeout ?? 30_000,\n }\n }\n\n /**\n * Start the server.\n */\n async start(): Promise<string> {\n if (this.server) {\n throw new Error(`Server already started`)\n }\n\n return new Promise((resolve, reject) => {\n this.server = createServer((req, res) => {\n this.handleRequest(req, res).catch((err) => {\n console.error(`Request error:`, err)\n if (!res.headersSent) {\n res.writeHead(500, { \"content-type\": `text/plain` })\n res.end(`Internal server error`)\n }\n })\n })\n\n this.server.on(`error`, reject)\n\n this.server.listen(this.options.port, this.options.host, () => {\n const addr = this.server!.address()\n if (typeof addr === `string`) {\n this._url = addr\n } else if (addr) {\n this._url = `http://${this.options.host}:${addr.port}`\n }\n resolve(this._url!)\n })\n })\n }\n\n /**\n * Stop the server.\n */\n async stop(): Promise<void> {\n if (!this.server) {\n return\n }\n\n return new Promise((resolve, reject) => {\n this.server!.close((err) => {\n if (err) {\n reject(err)\n } else {\n this.server = null\n this._url = null\n resolve()\n }\n })\n })\n }\n\n /**\n * Get the server URL.\n */\n get url(): string {\n if (!this._url) {\n throw new Error(`Server not started`)\n }\n return this._url\n }\n\n /**\n * Clear all streams.\n */\n clear(): void {\n this.store.clear()\n }\n\n // ============================================================================\n // Request handling\n // ============================================================================\n\n private async handleRequest(\n req: IncomingMessage,\n res: ServerResponse\n ): Promise<void> {\n const url = new URL(req.url ?? `/`, `http://${req.headers.host}`)\n const path = url.pathname\n const method = req.method?.toUpperCase()\n\n // CORS headers for browser testing\n res.setHeader(`access-control-allow-origin`, `*`)\n res.setHeader(\n `access-control-allow-methods`,\n `GET, POST, PUT, DELETE, HEAD, OPTIONS`\n )\n res.setHeader(\n `access-control-allow-headers`,\n `content-type, authorization, ${STREAM_SEQ_HEADER}, ${STREAM_TTL_HEADER}, ${STREAM_EXPIRES_AT_HEADER}`\n )\n res.setHeader(\n `access-control-expose-headers`,\n `${STREAM_OFFSET_HEADER}, ${STREAM_CURSOR_HEADER}, ${STREAM_UP_TO_DATE_HEADER}, etag, content-type`\n )\n\n // Handle CORS preflight\n if (method === `OPTIONS`) {\n res.writeHead(204)\n res.end()\n return\n }\n\n try {\n switch (method) {\n case `PUT`:\n await this.handleCreate(path, req, res)\n break\n case `HEAD`:\n this.handleHead(path, res)\n break\n case `GET`:\n await this.handleRead(path, url, res)\n break\n case `POST`:\n await this.handleAppend(path, req, res)\n break\n case `DELETE`:\n this.handleDelete(path, res)\n break\n default:\n res.writeHead(405, { \"content-type\": `text/plain` })\n res.end(`Method not allowed`)\n }\n } catch (err) {\n if (err instanceof Error) {\n if (err.message.includes(`not found`)) {\n res.writeHead(404, { \"content-type\": `text/plain` })\n res.end(`Stream not found`)\n } else if (err.message.includes(`already exists`)) {\n res.writeHead(409, { \"content-type\": `text/plain` })\n res.end(`Stream already exists`)\n } else if (err.message.includes(`Sequence conflict`)) {\n res.writeHead(409, { \"content-type\": `text/plain` })\n res.end(`Sequence conflict`)\n } else if (err.message.includes(`Content-type mismatch`)) {\n res.writeHead(400, { \"content-type\": `text/plain` })\n res.end(`Content-type mismatch`)\n } else {\n throw err\n }\n } else {\n throw err\n }\n }\n }\n\n /**\n * Handle PUT - create stream\n */\n private async handleCreate(\n path: string,\n req: IncomingMessage,\n res: ServerResponse\n ): Promise<void> {\n const contentType = req.headers[`content-type`]\n const ttlHeader = req.headers[STREAM_TTL_HEADER] as string | undefined\n const expiresAtHeader = req.headers[STREAM_EXPIRES_AT_HEADER] as\n | string\n | undefined\n\n // Read body if present\n const body = await this.readBody(req)\n\n this.store.create(path, {\n contentType,\n ttlSeconds: ttlHeader ? parseInt(ttlHeader, 10) : undefined,\n expiresAt: expiresAtHeader,\n initialData: body.length > 0 ? body : undefined,\n })\n\n const stream = this.store.get(path)!\n\n res.writeHead(201, {\n \"content-type\": contentType ?? `application/octet-stream`,\n [STREAM_OFFSET_HEADER]: stream.currentOffset,\n })\n res.end()\n }\n\n /**\n * Handle HEAD - get metadata\n */\n private handleHead(path: string, res: ServerResponse): void {\n const stream = this.store.get(path)\n if (!stream) {\n res.writeHead(404, { \"content-type\": `text/plain` })\n res.end()\n return\n }\n\n const headers: Record<string, string> = {\n [STREAM_OFFSET_HEADER]: stream.currentOffset,\n }\n\n if (stream.contentType) {\n headers[`content-type`] = stream.contentType\n }\n\n // Generate ETag: {path}:{offset}\n headers[`etag`] =\n `\"${Buffer.from(path).toString(`base64`)}:${stream.currentOffset}\"`\n\n res.writeHead(200, headers)\n res.end()\n }\n\n /**\n * Handle GET - read data\n */\n private async handleRead(\n path: string,\n url: URL,\n res: ServerResponse\n ): Promise<void> {\n const stream = this.store.get(path)\n if (!stream) {\n res.writeHead(404, { \"content-type\": `text/plain` })\n res.end(`Stream not found`)\n return\n }\n\n const offset = url.searchParams.get(OFFSET_QUERY_PARAM) ?? undefined\n const live = url.searchParams.get(LIVE_QUERY_PARAM)\n const cursor = url.searchParams.get(CURSOR_QUERY_PARAM) ?? undefined\n\n // Read current messages\n let { messages, upToDate } = this.store.read(path, offset)\n\n // Only wait in long-poll if:\n // 1. long-poll mode is enabled\n // 2. Client provided an offset (not first request)\n // 3. Client's offset matches current offset (already caught up)\n // 4. No new messages\n const clientIsCaughtUp = offset && offset === stream.currentOffset\n if (live === `long-poll` && clientIsCaughtUp && messages.length === 0) {\n const result = await this.store.waitForMessages(\n path,\n offset,\n this.options.longPollTimeout\n )\n\n if (result.timedOut) {\n // Return 204 No Content on timeout\n res.writeHead(204, {\n [STREAM_OFFSET_HEADER]: offset,\n })\n res.end()\n return\n }\n\n messages = result.messages\n upToDate = true\n }\n\n // Build response\n const headers: Record<string, string> = {}\n\n if (stream.contentType) {\n headers[`content-type`] = stream.contentType\n }\n\n // Set offset header to the last message's offset, or current if no messages\n const lastMessage = messages[messages.length - 1]\n headers[STREAM_OFFSET_HEADER] = lastMessage?.offset ?? stream.currentOffset\n\n // Echo cursor if provided\n if (cursor) {\n headers[STREAM_CURSOR_HEADER] = cursor\n }\n\n // Set up-to-date header\n if (upToDate) {\n headers[STREAM_UP_TO_DATE_HEADER] = `true`\n }\n\n // Concatenate all message data\n const totalSize = messages.reduce((sum, m) => sum + m.data.length, 0)\n const responseData = new Uint8Array(totalSize)\n let offset2 = 0\n for (const msg of messages) {\n responseData.set(msg.data, offset2)\n offset2 += msg.data.length\n }\n\n res.writeHead(200, headers)\n res.end(Buffer.from(responseData))\n }\n\n /**\n * Handle POST - append data\n */\n private async handleAppend(\n path: string,\n req: IncomingMessage,\n res: ServerResponse\n ): Promise<void> {\n const contentType = req.headers[`content-type`]\n const seq = req.headers[STREAM_SEQ_HEADER] as string | undefined\n\n const body = await this.readBody(req)\n\n if (body.length === 0) {\n res.writeHead(400, { \"content-type\": `text/plain` })\n res.end(`Empty body`)\n return\n }\n\n const message = this.store.append(path, body, { seq, contentType })\n\n res.writeHead(200, {\n [STREAM_OFFSET_HEADER]: message.offset,\n })\n res.end()\n }\n\n /**\n * Handle DELETE - delete stream\n */\n private handleDelete(path: string, res: ServerResponse): void {\n if (!this.store.has(path)) {\n res.writeHead(404, { \"content-type\": `text/plain` })\n res.end(`Stream not found`)\n return\n }\n\n this.store.delete(path)\n res.writeHead(204)\n res.end()\n }\n\n // ============================================================================\n // Helpers\n // ============================================================================\n\n private readBody(req: IncomingMessage): Promise<Uint8Array> {\n return new Promise((resolve, reject) => {\n const chunks: Array<Buffer> = []\n\n req.on(`data`, (chunk: Buffer) => {\n chunks.push(chunk)\n })\n\n req.on(`end`, () => {\n const body = Buffer.concat(chunks)\n resolve(new Uint8Array(body))\n })\n\n req.on(`error`, reject)\n })\n }\n}\n","/**\n * In-memory stream storage.\n */\n\nimport type { PendingLongPoll, Stream, StreamMessage } from \"./types\"\n\n/**\n * In-memory store for durable streams.\n */\nexport class StreamStore {\n private streams = new Map<string, Stream>()\n private pendingLongPolls: Array<PendingLongPoll> = []\n\n /**\n * Create a new stream.\n * @throws Error if stream already exists\n */\n create(\n path: string,\n options: {\n contentType?: string\n ttlSeconds?: number\n expiresAt?: string\n initialData?: Uint8Array\n } = {}\n ): Stream {\n if (this.streams.has(path)) {\n throw new Error(`Stream already exists: ${path}`)\n }\n\n const stream: Stream = {\n path,\n contentType: options.contentType,\n messages: [],\n currentOffset: `0_0`,\n ttlSeconds: options.ttlSeconds,\n expiresAt: options.expiresAt,\n createdAt: Date.now(),\n }\n\n // If initial data is provided, append it\n if (options.initialData && options.initialData.length > 0) {\n this.appendToStream(stream, options.initialData)\n }\n\n this.streams.set(path, stream)\n return stream\n }\n\n /**\n * Get a stream by path.\n */\n get(path: string): Stream | undefined {\n return this.streams.get(path)\n }\n\n /**\n * Check if a stream exists.\n */\n has(path: string): boolean {\n return this.streams.has(path)\n }\n\n /**\n * Delete a stream.\n */\n delete(path: string): boolean {\n // Cancel any pending long-polls for this stream\n this.cancelLongPollsForStream(path)\n return this.streams.delete(path)\n }\n\n /**\n * Append data to a stream.\n * @throws Error if stream doesn't exist\n * @throws Error if seq is lower than lastSeq\n */\n append(\n path: string,\n data: Uint8Array,\n options: { seq?: string; contentType?: string } = {}\n ): StreamMessage {\n const stream = this.streams.get(path)\n if (!stream) {\n throw new Error(`Stream not found: ${path}`)\n }\n\n // Check content type match\n if (\n options.contentType &&\n stream.contentType &&\n options.contentType !== stream.contentType\n ) {\n throw new Error(\n `Content-type mismatch: expected ${stream.contentType}, got ${options.contentType}`\n )\n }\n\n // Check sequence for writer coordination\n if (options.seq !== undefined) {\n if (stream.lastSeq !== undefined && options.seq <= stream.lastSeq) {\n throw new Error(\n `Sequence conflict: ${options.seq} <= ${stream.lastSeq}`\n )\n }\n stream.lastSeq = options.seq\n }\n\n const message = this.appendToStream(stream, data)\n\n // Notify any pending long-polls\n this.notifyLongPolls(path)\n\n return message\n }\n\n /**\n * Read messages from a stream starting at the given offset.\n */\n read(\n path: string,\n offset?: string\n ): { messages: Array<StreamMessage>; upToDate: boolean } {\n const stream = this.streams.get(path)\n if (!stream) {\n throw new Error(`Stream not found: ${path}`)\n }\n\n if (!offset || offset === `0_0`) {\n // Read from beginning\n return {\n messages: [...stream.messages],\n upToDate: true,\n }\n }\n\n // Find messages after the given offset\n const offsetIndex = this.findOffsetIndex(stream, offset)\n if (offsetIndex === -1) {\n // Offset is at or past the end\n return {\n messages: [],\n upToDate: true,\n }\n }\n\n return {\n messages: stream.messages.slice(offsetIndex),\n upToDate: true,\n }\n }\n\n /**\n * Wait for new messages (long-poll).\n */\n async waitForMessages(\n path: string,\n offset: string,\n timeoutMs: number\n ): Promise<{ messages: Array<StreamMessage>; timedOut: boolean }> {\n const stream = this.streams.get(path)\n if (!stream) {\n throw new Error(`Stream not found: ${path}`)\n }\n\n // Check if there are already new messages\n const { messages } = this.read(path, offset)\n if (messages.length > 0) {\n return { messages, timedOut: false }\n }\n\n // Wait for new messages\n return new Promise((resolve) => {\n const timeoutId = setTimeout(() => {\n // Remove from pending\n this.removePendingLongPoll(pending)\n resolve({ messages: [], timedOut: true })\n }, timeoutMs)\n\n const pending: PendingLongPoll = {\n path,\n offset,\n resolve: (msgs) => {\n clearTimeout(timeoutId)\n this.removePendingLongPoll(pending)\n resolve({ messages: msgs, timedOut: false })\n },\n timeoutId,\n }\n\n this.pendingLongPolls.push(pending)\n })\n }\n\n /**\n * Get the current offset for a stream.\n */\n getCurrentOffset(path: string): string | undefined {\n return this.streams.get(path)?.currentOffset\n }\n\n /**\n * Clear all streams.\n */\n clear(): void {\n // Cancel all pending long-polls\n for (const pending of this.pendingLongPolls) {\n clearTimeout(pending.timeoutId)\n }\n this.pendingLongPolls = []\n this.streams.clear()\n }\n\n /**\n * Get all stream paths.\n */\n list(): Array<string> {\n return Array.from(this.streams.keys())\n }\n\n // ============================================================================\n // Private helpers\n // ============================================================================\n\n private appendToStream(stream: Stream, data: Uint8Array): StreamMessage {\n // Parse current offset\n const parts = stream.currentOffset.split(`_`).map(Number)\n const readSeq = parts[0]!\n const byteOffset = parts[1]!\n\n // Calculate new offset\n const newByteOffset = byteOffset + data.length\n const newOffset = `${readSeq}_${newByteOffset}`\n\n const message: StreamMessage = {\n data,\n offset: newOffset,\n timestamp: Date.now(),\n }\n\n stream.messages.push(message)\n stream.currentOffset = newOffset\n\n return message\n }\n\n private findOffsetIndex(stream: Stream, offset: string): number {\n // Find the first message with an offset greater than the given offset\n for (let i = 0; i < stream.messages.length; i++) {\n if (this.compareOffsets(stream.messages[i]!.offset, offset) > 0) {\n return i\n }\n }\n return -1 // No messages after the offset\n }\n\n private compareOffsets(a: string, b: string): number {\n const aParts = a.split(`_`).map(Number)\n const bParts = b.split(`_`).map(Number)\n const aSeq = aParts[0]!\n const aOffset = aParts[1]!\n const bSeq = bParts[0]!\n const bOffset = bParts[1]!\n\n if (aSeq !== bSeq) {\n return aSeq - bSeq\n }\n return aOffset - bOffset\n }\n\n private notifyLongPolls(path: string): void {\n const toNotify = this.pendingLongPolls.filter((p) => p.path === path)\n\n for (const pending of toNotify) {\n const { messages } = this.read(path, pending.offset)\n if (messages.length > 0) {\n pending.resolve(messages)\n }\n }\n }\n\n private cancelLongPollsForStream(path: string): void {\n const toCancel = this.pendingLongPolls.filter((p) => p.path === path)\n for (const pending of toCancel) {\n clearTimeout(pending.timeoutId)\n pending.resolve([])\n }\n this.pendingLongPolls = this.pendingLongPolls.filter((p) => p.path !== path)\n }\n\n private removePendingLongPoll(pending: PendingLongPoll): void {\n const index = this.pendingLongPolls.indexOf(pending)\n if (index !== -1) {\n this.pendingLongPolls.splice(index, 1)\n }\n }\n}\n"],"mappings":"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","names":[]}