@durable-streams/cli 0.1.2 → 0.1.4

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 CHANGED
@@ -4,31 +4,38 @@ A command-line tool for interacting with durable streams.
4
4
 
5
5
  ## Installation
6
6
 
7
- ### Local Development
7
+ ### From npm
8
8
 
9
9
  ```bash
10
- # Install dependencies
11
- pnpm install
10
+ # Global installation
11
+ npm install -g @durable-streams/cli
12
12
 
13
- # Build the CLI (for production bin)
14
- pnpm build
13
+ # Or run directly with npx
14
+ npx @durable-streams/cli create my-stream
15
+ npx @durable-streams/cli read my-stream
15
16
  ```
16
17
 
17
- ### Global Installation for Development
18
-
19
- For development, you can link the CLI globally with live TypeScript execution (no rebuild needed):
18
+ ### From source (for development)
20
19
 
21
20
  ```bash
22
- # From the CLI package directory
21
+ # Clone the repository
22
+ git clone https://github.com/durable-streams/durable-streams.git
23
+ cd durable-streams
24
+
25
+ # Install dependencies
26
+ pnpm install
27
+
28
+ # Build the CLI
29
+ pnpm build
30
+
31
+ # Link globally for development (uses tsx, no rebuild needed)
32
+ cd packages/cli
23
33
  pnpm link:dev
24
34
 
25
35
  # Now you can use durable-stream-dev anywhere
26
- # Changes to src/index.ts are immediately available
27
36
  durable-stream-dev create my-stream
28
37
  ```
29
38
 
30
- This uses `tsx` to run the TypeScript source directly, so you see changes immediately without rebuilding.
31
-
32
39
  ## Quick Start
33
40
 
34
41
  The easiest way to get started is to run the local development server and use the CLI:
@@ -63,6 +70,11 @@ durable-stream-dev read my-stream
63
70
 
64
71
  - `STREAM_URL` - Base URL of the stream server (default: `http://localhost:4437`)
65
72
 
73
+ ### Write Options
74
+
75
+ - `--content-type <type>` - Content-Type for the message (default: `application/octet-stream`)
76
+ - `--json` - Shorthand for `--content-type application/json`
77
+
66
78
  ### Commands
67
79
 
68
80
  #### Create a stream
@@ -80,6 +92,12 @@ durable-stream-dev write <stream_id> "Hello, world!"
80
92
  # Pipe content from stdin
81
93
  echo "Hello from stdin" | durable-stream-dev write <stream_id>
82
94
  cat file.txt | durable-stream-dev write <stream_id>
95
+
96
+ # Specify content type
97
+ durable-stream-dev write <stream_id> '{"key": "value"}' --content-type application/json
98
+
99
+ # Shorthand for JSON
100
+ durable-stream-dev write <stream_id> '{"key": "value"}' --json
83
101
  ```
84
102
 
85
103
  #### Read from a stream
package/dist/index.cjs CHANGED
@@ -23,9 +23,39 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  }) : target, mod));
24
24
 
25
25
  //#endregion
26
+ const node_path = __toESM(require("node:path"));
26
27
  const node_process = __toESM(require("node:process"));
28
+ const node_url = __toESM(require("node:url"));
27
29
  const __durable_streams_client = __toESM(require("@durable-streams/client"));
28
30
 
31
+ //#region src/parseWriteArgs.ts
32
+ /**
33
+ * Parse write command arguments, extracting content-type flags and content.
34
+ * @param args - Arguments after the stream_id (starting from index 2)
35
+ * @returns Parsed content type and content string
36
+ * @throws Error if --content-type is missing its value or if unknown flags are provided
37
+ */
38
+ function parseWriteArgs(args) {
39
+ let contentType = `application/octet-stream`;
40
+ const contentParts = [];
41
+ for (let i = 0; i < args.length; i++) {
42
+ const arg = args[i];
43
+ if (arg === `--json`) contentType = `application/json`;
44
+ else if (arg === `--content-type`) {
45
+ const nextArg = args[i + 1];
46
+ if (!nextArg || nextArg.startsWith(`--`)) throw new Error(`--content-type requires a value`);
47
+ contentType = nextArg;
48
+ i++;
49
+ } else if (arg.startsWith(`--`)) throw new Error(`unknown flag: ${arg}`);
50
+ else contentParts.push(arg);
51
+ }
52
+ return {
53
+ contentType,
54
+ content: contentParts.join(` `)
55
+ };
56
+ }
57
+
58
+ //#endregion
29
59
  //#region src/index.ts
30
60
  const STREAM_URL = process.env.STREAM_URL || `http://localhost:4437`;
31
61
  function printUsage() {
@@ -37,6 +67,10 @@ Usage:
37
67
  durable-stream read <stream_id> Follow a stream and write to stdout
38
68
  durable-stream delete <stream_id> Delete a stream
39
69
 
70
+ Write Options:
71
+ --content-type <type> Content-Type for the message (default: application/octet-stream)
72
+ --json Shorthand for --content-type application/json
73
+
40
74
  Environment Variables:
41
75
  STREAM_URL Base URL of the stream server (default: http://localhost:4437)
42
76
  `);
@@ -54,10 +88,13 @@ async function createStream(streamId) {
54
88
  process.exit(1);
55
89
  }
56
90
  }
57
- async function writeStream(streamId, content) {
91
+ async function writeStream(streamId, contentType, content) {
58
92
  const url = `${STREAM_URL}/v1/stream/${streamId}`;
59
93
  try {
60
- const stream = new __durable_streams_client.DurableStream({ url });
94
+ const stream = new __durable_streams_client.DurableStream({
95
+ url,
96
+ contentType
97
+ });
61
98
  if (content) {
62
99
  const processedContent = content.replace(/\\n/g, `\n`).replace(/\\t/g, `\t`).replace(/\\r/g, `\r`).replace(/\\\\/g, `\\`);
63
100
  await stream.append(processedContent);
@@ -126,9 +163,15 @@ async function main() {
126
163
  process.exit(1);
127
164
  }
128
165
  const streamId = args[1];
129
- const content = args.slice(2).join(` `);
130
- if (!node_process.stdin.isTTY) await writeStream(streamId);
131
- else if (content) await writeStream(streamId, content);
166
+ let parsed;
167
+ try {
168
+ parsed = parseWriteArgs(args.slice(2));
169
+ } catch (error) {
170
+ if (error instanceof Error) node_process.stderr.write(`Error: ${error.message}\n`);
171
+ process.exit(1);
172
+ }
173
+ if (!node_process.stdin.isTTY) await writeStream(streamId, parsed.contentType);
174
+ else if (parsed.content) await writeStream(streamId, parsed.contentType, parsed.content);
132
175
  else {
133
176
  node_process.stderr.write(`Error: content required (provide as argument or pipe to stdin)\n`);
134
177
  printUsage();
@@ -160,9 +203,16 @@ async function main() {
160
203
  process.exit(1);
161
204
  }
162
205
  }
163
- main().catch((error) => {
206
+ function isMainModule() {
207
+ if (!process.argv[1]) return false;
208
+ const scriptPath = (0, node_path.resolve)(process.argv[1]);
209
+ const modulePath = (0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href);
210
+ return scriptPath === modulePath;
211
+ }
212
+ if (isMainModule()) main().catch((error) => {
164
213
  node_process.stderr.write(`Fatal error: ${error.message}\n`);
165
214
  process.exit(1);
166
215
  });
167
216
 
168
- //#endregion
217
+ //#endregion
218
+ exports.parseWriteArgs = parseWriteArgs
package/dist/index.d.cts CHANGED
@@ -1 +1,15 @@
1
- export { };
1
+ //#region src/parseWriteArgs.d.ts
2
+ interface ParsedWriteArgs {
3
+ contentType: string;
4
+ content: string;
5
+ }
6
+ /**
7
+ * Parse write command arguments, extracting content-type flags and content.
8
+ * @param args - Arguments after the stream_id (starting from index 2)
9
+ * @returns Parsed content type and content string
10
+ * @throws Error if --content-type is missing its value or if unknown flags are provided
11
+ */
12
+ declare function parseWriteArgs(args: Array<string>): ParsedWriteArgs;
13
+
14
+ //#endregion
15
+ export { ParsedWriteArgs, parseWriteArgs };
package/dist/index.d.ts CHANGED
@@ -1 +1,15 @@
1
- export { };
1
+ //#region src/parseWriteArgs.d.ts
2
+ interface ParsedWriteArgs {
3
+ contentType: string;
4
+ content: string;
5
+ }
6
+ /**
7
+ * Parse write command arguments, extracting content-type flags and content.
8
+ * @param args - Arguments after the stream_id (starting from index 2)
9
+ * @returns Parsed content type and content string
10
+ * @throws Error if --content-type is missing its value or if unknown flags are provided
11
+ */
12
+ declare function parseWriteArgs(args: Array<string>): ParsedWriteArgs;
13
+
14
+ //#endregion
15
+ export { ParsedWriteArgs, parseWriteArgs };
package/dist/index.js CHANGED
@@ -1,7 +1,37 @@
1
1
  #!/usr/bin/env node
2
+ import { resolve } from "node:path";
2
3
  import { stderr, stdin, stdout } from "node:process";
4
+ import { fileURLToPath } from "node:url";
3
5
  import { DurableStream } from "@durable-streams/client";
4
6
 
7
+ //#region src/parseWriteArgs.ts
8
+ /**
9
+ * Parse write command arguments, extracting content-type flags and content.
10
+ * @param args - Arguments after the stream_id (starting from index 2)
11
+ * @returns Parsed content type and content string
12
+ * @throws Error if --content-type is missing its value or if unknown flags are provided
13
+ */
14
+ function parseWriteArgs(args) {
15
+ let contentType = `application/octet-stream`;
16
+ const contentParts = [];
17
+ for (let i = 0; i < args.length; i++) {
18
+ const arg = args[i];
19
+ if (arg === `--json`) contentType = `application/json`;
20
+ else if (arg === `--content-type`) {
21
+ const nextArg = args[i + 1];
22
+ if (!nextArg || nextArg.startsWith(`--`)) throw new Error(`--content-type requires a value`);
23
+ contentType = nextArg;
24
+ i++;
25
+ } else if (arg.startsWith(`--`)) throw new Error(`unknown flag: ${arg}`);
26
+ else contentParts.push(arg);
27
+ }
28
+ return {
29
+ contentType,
30
+ content: contentParts.join(` `)
31
+ };
32
+ }
33
+
34
+ //#endregion
5
35
  //#region src/index.ts
6
36
  const STREAM_URL = process.env.STREAM_URL || `http://localhost:4437`;
7
37
  function printUsage() {
@@ -13,6 +43,10 @@ Usage:
13
43
  durable-stream read <stream_id> Follow a stream and write to stdout
14
44
  durable-stream delete <stream_id> Delete a stream
15
45
 
46
+ Write Options:
47
+ --content-type <type> Content-Type for the message (default: application/octet-stream)
48
+ --json Shorthand for --content-type application/json
49
+
16
50
  Environment Variables:
17
51
  STREAM_URL Base URL of the stream server (default: http://localhost:4437)
18
52
  `);
@@ -30,10 +64,13 @@ async function createStream(streamId) {
30
64
  process.exit(1);
31
65
  }
32
66
  }
33
- async function writeStream(streamId, content) {
67
+ async function writeStream(streamId, contentType, content) {
34
68
  const url = `${STREAM_URL}/v1/stream/${streamId}`;
35
69
  try {
36
- const stream = new DurableStream({ url });
70
+ const stream = new DurableStream({
71
+ url,
72
+ contentType
73
+ });
37
74
  if (content) {
38
75
  const processedContent = content.replace(/\\n/g, `\n`).replace(/\\t/g, `\t`).replace(/\\r/g, `\r`).replace(/\\\\/g, `\\`);
39
76
  await stream.append(processedContent);
@@ -43,8 +80,8 @@ async function writeStream(streamId, content) {
43
80
  stdin.on(`data`, (chunk) => {
44
81
  chunks.push(chunk);
45
82
  });
46
- await new Promise((resolve, reject) => {
47
- stdin.on(`end`, resolve);
83
+ await new Promise((resolve$1, reject) => {
84
+ stdin.on(`end`, resolve$1);
48
85
  stdin.on(`error`, reject);
49
86
  });
50
87
  const data = Buffer.concat(chunks);
@@ -102,9 +139,15 @@ async function main() {
102
139
  process.exit(1);
103
140
  }
104
141
  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);
142
+ let parsed;
143
+ try {
144
+ parsed = parseWriteArgs(args.slice(2));
145
+ } catch (error) {
146
+ if (error instanceof Error) stderr.write(`Error: ${error.message}\n`);
147
+ process.exit(1);
148
+ }
149
+ if (!stdin.isTTY) await writeStream(streamId, parsed.contentType);
150
+ else if (parsed.content) await writeStream(streamId, parsed.contentType, parsed.content);
108
151
  else {
109
152
  stderr.write(`Error: content required (provide as argument or pipe to stdin)\n`);
110
153
  printUsage();
@@ -136,9 +179,16 @@ async function main() {
136
179
  process.exit(1);
137
180
  }
138
181
  }
139
- main().catch((error) => {
182
+ function isMainModule() {
183
+ if (!process.argv[1]) return false;
184
+ const scriptPath = resolve(process.argv[1]);
185
+ const modulePath = fileURLToPath(import.meta.url);
186
+ return scriptPath === modulePath;
187
+ }
188
+ if (isMainModule()) main().catch((error) => {
140
189
  stderr.write(`Fatal error: ${error.message}\n`);
141
190
  process.exit(1);
142
191
  });
143
192
 
144
- //#endregion
193
+ //#endregion
194
+ export { parseWriteArgs };
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@durable-streams/cli",
3
3
  "description": "CLI tool for working with Durable Streams",
4
- "version": "0.1.2",
4
+ "version": "0.1.4",
5
5
  "author": "Durable Stream contributors",
6
6
  "bin": {
7
+ "cli": "./dist/index.js",
7
8
  "durable-stream": "./dist/index.js",
8
9
  "durable-stream-dev": "./bin/durable-stream-dev.mjs"
9
10
  },
@@ -11,14 +12,15 @@
11
12
  "url": "https://github.com/durable-streams/durable-streams/issues"
12
13
  },
13
14
  "dependencies": {
14
- "@durable-streams/client": "0.1.2"
15
+ "@durable-streams/client": "0.1.3"
15
16
  },
16
17
  "devDependencies": {
17
18
  "@types/node": "^22.15.21",
18
19
  "tsdown": "^0.9.0",
19
20
  "tsx": "^4.19.2",
20
21
  "typescript": "^5.5.2",
21
- "@durable-streams/server": "0.1.2"
22
+ "vitest": "^3.1.3",
23
+ "@durable-streams/server": "0.1.4"
22
24
  },
23
25
  "engines": {
24
26
  "node": ">=18.0.0"
@@ -61,6 +63,7 @@
61
63
  "build": "tsdown",
62
64
  "dev": "tsdown --watch",
63
65
  "link:dev": "pnpm link --global",
64
- "start:dev": "tsx --watch example-server.ts"
66
+ "start:dev": "tsx --watch example-server.ts",
67
+ "test": "vitest run"
65
68
  }
66
69
  }