@durable-streams/cli 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/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # Durable Streams CLI
2
+
3
+ A command-line tool for interacting with durable streams.
4
+
5
+ ## Installation
6
+
7
+ ### Local Development
8
+
9
+ ```bash
10
+ # Install dependencies
11
+ pnpm install
12
+
13
+ # Build the CLI (for production bin)
14
+ pnpm build
15
+ ```
16
+
17
+ ### Global Installation for Development
18
+
19
+ For development, you can link the CLI globally with live TypeScript execution (no rebuild needed):
20
+
21
+ ```bash
22
+ # From the CLI package directory
23
+ pnpm link:dev
24
+
25
+ # Now you can use durable-stream-dev anywhere
26
+ # Changes to src/index.ts are immediately available
27
+ durable-stream-dev create my-stream
28
+ ```
29
+
30
+ This uses `tsx` to run the TypeScript source directly, so you see changes immediately without rebuilding.
31
+
32
+ ## Quick Start
33
+
34
+ The easiest way to get started is to run the local development server and use the CLI:
35
+
36
+ ### Terminal 1: Start the local server
37
+
38
+ ```bash
39
+ pnpm start:dev
40
+ ```
41
+
42
+ This will start a Durable Streams server at `http://localhost:4437` with live reloading.
43
+
44
+ ### Terminal 2: Use the CLI
45
+
46
+ ```bash
47
+ # Set the server URL (optional, defaults to http://localhost:4437)
48
+ export STREAM_URL=http://localhost:4437
49
+
50
+ # Create a stream
51
+ durable-stream-dev create my-stream
52
+
53
+ # Write to the stream
54
+ durable-stream-dev write my-stream "Hello, world!"
55
+
56
+ # Read from the stream (follows live)
57
+ durable-stream-dev read my-stream
58
+ ```
59
+
60
+ ## Usage
61
+
62
+ ### Environment Variables
63
+
64
+ - `STREAM_URL` - Base URL of the stream server (default: `http://localhost:4437`)
65
+
66
+ ### Commands
67
+
68
+ #### Create a stream
69
+
70
+ ```bash
71
+ durable-stream-dev create <stream_id>
72
+ ```
73
+
74
+ #### Write to a stream
75
+
76
+ ```bash
77
+ # Write content as arguments
78
+ durable-stream-dev write <stream_id> "Hello, world!"
79
+
80
+ # Pipe content from stdin
81
+ echo "Hello from stdin" | durable-stream-dev write <stream_id>
82
+ cat file.txt | durable-stream-dev write <stream_id>
83
+ ```
84
+
85
+ #### Read from a stream
86
+
87
+ ```bash
88
+ # Follows the stream and outputs new data to stdout
89
+ durable-stream-dev read <stream_id>
90
+ ```
91
+
92
+ #### Delete a stream
93
+
94
+ ```bash
95
+ durable-stream-dev delete <stream_id>
96
+ ```
97
+
98
+ ## Complete Example Workflow
99
+
100
+ ```bash
101
+ # Terminal 1: Start the local development server
102
+ pnpm start:dev
103
+
104
+ # Terminal 2: Set up the stream
105
+ export STREAM_URL=http://localhost:4437
106
+ durable-stream-dev create test-stream
107
+
108
+ # Terminal 3: Start reading (will show data as it arrives)
109
+ export STREAM_URL=http://localhost:4437
110
+ durable-stream-dev read test-stream
111
+
112
+ # Back in Terminal 2: Write data and watch it appear in Terminal 3
113
+ durable-stream-dev write test-stream "First message"
114
+ durable-stream-dev write test-stream "Second message"
115
+ echo "Piped content!" | durable-stream-dev write test-stream
116
+ ```
117
+
118
+ ## Development
119
+
120
+ ```bash
121
+ # Start the example server with live reloading
122
+ pnpm start:dev
123
+
124
+ # Watch mode for CLI development (rebuilds dist/)
125
+ pnpm dev
126
+
127
+ # Build
128
+ pnpm build
129
+
130
+ # Link globally for development (uses tsx, no rebuild needed)
131
+ pnpm link:dev
132
+ ```
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+
3
+ /* eslint-disable stylistic/quotes */
4
+
5
+ /**
6
+ * Development wrapper that uses tsx to run the TypeScript source directly.
7
+ * This allows you to use `pnpm link --global` and see changes immediately
8
+ * without rebuilding.
9
+ */
10
+
11
+ import { spawn } from "node:child_process"
12
+ import { fileURLToPath } from "node:url"
13
+ import { dirname, join } from "node:path"
14
+
15
+ const __filename = fileURLToPath(import.meta.url)
16
+ const __dirname = dirname(__filename)
17
+
18
+ const srcPath = join(__dirname, "..", "src", "index.ts")
19
+
20
+ // Run tsx with the source file
21
+ const child = spawn("tsx", [srcPath, ...process.argv.slice(2)], {
22
+ stdio: "inherit",
23
+ })
24
+
25
+ child.on("exit", (code) => {
26
+ process.exit(code ?? 0)
27
+ })
package/dist/index.js ADDED
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env node
2
+ import { stderr, stdin, stdout } from "node:process";
3
+ import { DurableStream } from "@durable-streams/client";
4
+
5
+ //#region src/index.ts
6
+ const STREAM_URL = process.env.STREAM_URL || `http://localhost:4437`;
7
+ function printUsage() {
8
+ console.error(`
9
+ Usage:
10
+ durable-stream create <stream_id> Create a new stream
11
+ durable-stream write <stream_id> <content> Write content to a stream
12
+ cat file.txt | durable-stream write <stream_id> Write stdin to a stream
13
+ durable-stream read <stream_id> Follow a stream and write to stdout
14
+ durable-stream delete <stream_id> Delete a stream
15
+
16
+ Environment Variables:
17
+ STREAM_URL Base URL of the stream server (default: http://localhost:4437)
18
+ `);
19
+ }
20
+ async function createStream(streamId) {
21
+ const url = `${STREAM_URL}/v1/stream/${streamId}`;
22
+ try {
23
+ await DurableStream.create({
24
+ url,
25
+ contentType: `application/octet-stream`
26
+ });
27
+ console.log(`Created stream: ${streamId}`);
28
+ } catch (error) {
29
+ if (error instanceof Error) stderr.write(`Error creating stream: ${error.message}\n`);
30
+ process.exit(1);
31
+ }
32
+ }
33
+ async function writeStream(streamId, content) {
34
+ const url = `${STREAM_URL}/v1/stream/${streamId}`;
35
+ try {
36
+ const stream = new DurableStream({ url });
37
+ if (content) {
38
+ const processedContent = content.replace(/\\n/g, `\n`).replace(/\\t/g, `\t`).replace(/\\r/g, `\r`).replace(/\\\\/g, `\\`);
39
+ await stream.append(processedContent);
40
+ console.log(`Wrote ${processedContent.length} bytes to ${streamId}`);
41
+ } else {
42
+ const chunks = [];
43
+ stdin.on(`data`, (chunk) => {
44
+ chunks.push(chunk);
45
+ });
46
+ await new Promise((resolve, reject) => {
47
+ stdin.on(`end`, resolve);
48
+ stdin.on(`error`, reject);
49
+ });
50
+ const data = Buffer.concat(chunks);
51
+ await stream.append(data);
52
+ console.log(`Wrote ${data.length} bytes to ${streamId}`);
53
+ }
54
+ } catch (error) {
55
+ if (error instanceof Error) stderr.write(`Error writing to stream: ${error.message}\n`);
56
+ process.exit(1);
57
+ }
58
+ }
59
+ async function readStream(streamId) {
60
+ const url = `${STREAM_URL}/v1/stream/${streamId}`;
61
+ try {
62
+ const stream = new DurableStream({ url });
63
+ const res = await stream.stream({ live: `auto` });
64
+ for await (const chunk of res.bodyStream()) if (chunk.length > 0) stdout.write(chunk);
65
+ } catch (error) {
66
+ if (error instanceof Error) stderr.write(`Error reading stream: ${error.message}\n`);
67
+ process.exit(1);
68
+ }
69
+ }
70
+ async function deleteStream(streamId) {
71
+ const url = `${STREAM_URL}/v1/stream/${streamId}`;
72
+ try {
73
+ const stream = new DurableStream({ url });
74
+ await stream.delete();
75
+ console.log(`Deleted stream: ${streamId}`);
76
+ } catch (error) {
77
+ if (error instanceof Error) stderr.write(`Error deleting stream: ${error.message}\n`);
78
+ process.exit(1);
79
+ }
80
+ }
81
+ async function main() {
82
+ const args = process.argv.slice(2);
83
+ if (args.length < 1) {
84
+ printUsage();
85
+ process.exit(1);
86
+ }
87
+ const command = args[0];
88
+ switch (command) {
89
+ case `create`: {
90
+ if (args.length < 2) {
91
+ stderr.write(`Error: stream_id required\n`);
92
+ printUsage();
93
+ process.exit(1);
94
+ }
95
+ await createStream(args[1]);
96
+ break;
97
+ }
98
+ case `write`: {
99
+ if (args.length < 2) {
100
+ stderr.write(`Error: stream_id required\n`);
101
+ printUsage();
102
+ process.exit(1);
103
+ }
104
+ const streamId = args[1];
105
+ const content = args.slice(2).join(` `);
106
+ if (!stdin.isTTY) await writeStream(streamId);
107
+ else if (content) await writeStream(streamId, content);
108
+ else {
109
+ stderr.write(`Error: content required (provide as argument or pipe to stdin)\n`);
110
+ printUsage();
111
+ process.exit(1);
112
+ }
113
+ break;
114
+ }
115
+ case `read`: {
116
+ if (args.length < 2) {
117
+ stderr.write(`Error: stream_id required\n`);
118
+ printUsage();
119
+ process.exit(1);
120
+ }
121
+ await readStream(args[1]);
122
+ break;
123
+ }
124
+ case `delete`: {
125
+ if (args.length < 2) {
126
+ stderr.write(`Error: stream_id required\n`);
127
+ printUsage();
128
+ process.exit(1);
129
+ }
130
+ await deleteStream(args[1]);
131
+ break;
132
+ }
133
+ default:
134
+ stderr.write(`Error: unknown command '${command}'\n`);
135
+ printUsage();
136
+ process.exit(1);
137
+ }
138
+ }
139
+ main().catch((error) => {
140
+ stderr.write(`Fatal error: ${error.message}\n`);
141
+ process.exit(1);
142
+ });
143
+
144
+ //#endregion
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@durable-streams/cli",
3
+ "version": "0.1.0",
4
+ "description": "CLI tool for working with Durable Streams",
5
+ "author": "Durable Stream contributors",
6
+ "license": "Apache-2.0",
7
+ "type": "module",
8
+ "bin": {
9
+ "durable-stream": "./dist/index.js",
10
+ "durable-stream-dev": "./bin/durable-stream-dev.mjs"
11
+ },
12
+ "main": "./dist/index.js",
13
+ "files": [
14
+ "dist",
15
+ "bin"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsdown",
19
+ "dev": "tsdown --watch",
20
+ "start:dev": "tsx --watch example-server.ts",
21
+ "link:dev": "pnpm link --global"
22
+ },
23
+ "dependencies": {
24
+ "@durable-streams/client": "workspace:*"
25
+ },
26
+ "devDependencies": {
27
+ "@durable-streams/server": "workspace:*",
28
+ "@types/node": "^22.15.21",
29
+ "tsdown": "^0.9.0",
30
+ "tsx": "^4.19.2",
31
+ "typescript": "^5.5.2"
32
+ },
33
+ "engines": {
34
+ "node": ">=18.0.0"
35
+ }
36
+ }