@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 +132 -0
- package/bin/durable-stream-dev.mjs +27 -0
- package/dist/index.js +144 -0
- package/package.json +36 -0
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
|
+
}
|