@durable-streams/client-conformance-tests 0.1.8 → 0.2.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.
Files changed (35) hide show
  1. package/dist/adapters/typescript-adapter.cjs +72 -22
  2. package/dist/adapters/typescript-adapter.js +72 -22
  3. package/dist/{benchmark-runner-CrE6JkbX.js → benchmark-runner-81waaCzs.js} +89 -9
  4. package/dist/{benchmark-runner-Db4he452.cjs → benchmark-runner-DliEfq9k.cjs} +93 -8
  5. package/dist/cli.cjs +41 -5
  6. package/dist/cli.js +41 -5
  7. package/dist/index.cjs +2 -2
  8. package/dist/index.d.cts +50 -3
  9. package/dist/index.d.ts +50 -3
  10. package/dist/index.js +2 -2
  11. package/dist/{protocol-qb83AeUH.js → protocol-1p0soayz.js} +2 -1
  12. package/dist/{protocol-D37G3c4e.d.cts → protocol-BxZTqJmO.d.cts} +67 -5
  13. package/dist/{protocol-XeAOKBD-.cjs → protocol-IioVPNaP.cjs} +2 -1
  14. package/dist/{protocol-Mcbiq3nQ.d.ts → protocol-JuFzdV5x.d.ts} +67 -5
  15. package/dist/protocol.cjs +1 -1
  16. package/dist/protocol.d.cts +2 -2
  17. package/dist/protocol.d.ts +2 -2
  18. package/dist/protocol.js +1 -1
  19. package/package.json +8 -3
  20. package/src/adapters/typescript-adapter.ts +110 -32
  21. package/src/benchmark-runner.ts +75 -1
  22. package/src/benchmark-scenarios.ts +4 -4
  23. package/src/cli.ts +46 -5
  24. package/src/protocol.ts +75 -2
  25. package/src/runner.ts +72 -1
  26. package/src/test-cases.ts +55 -0
  27. package/test-cases/consumer/error-context.yaml +67 -0
  28. package/test-cases/consumer/json-parsing-errors.yaml +115 -0
  29. package/test-cases/consumer/read-auto.yaml +155 -0
  30. package/test-cases/consumer/read-sse.yaml +24 -0
  31. package/test-cases/consumer/retry-resilience.yaml +28 -0
  32. package/test-cases/consumer/sse-parsing-errors.yaml +121 -0
  33. package/test-cases/producer/error-context.yaml +72 -0
  34. package/test-cases/producer/idempotent-json-batching.yaml +40 -0
  35. package/test-cases/validation/input-validation.yaml +192 -0
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  const require_chunk = require('./chunk-BCwAaXi7.cjs');
3
- const require_protocol = require('./protocol-XeAOKBD-.cjs');
3
+ const require_protocol = require('./protocol-IioVPNaP.cjs');
4
4
  const node_child_process = require_chunk.__toESM(require("node:child_process"));
5
5
  const node_readline = require_chunk.__toESM(require("node:readline"));
6
6
  const node_crypto = require_chunk.__toESM(require("node:crypto"));
@@ -8,6 +8,7 @@ const __durable_streams_server = require_chunk.__toESM(require("@durable-streams
8
8
  const node_fs = require_chunk.__toESM(require("node:fs"));
9
9
  const node_path = require_chunk.__toESM(require("node:path"));
10
10
  const yaml = require_chunk.__toESM(require("yaml"));
11
+ const node_fs_promises = require_chunk.__toESM(require("node:fs/promises"));
11
12
  const __durable_streams_client = require_chunk.__toESM(require("@durable-streams/client"));
12
13
 
13
14
  //#region src/test-cases.ts
@@ -178,7 +179,7 @@ async function executeOperation(op, ctx) {
178
179
  }
179
180
  case `append`: {
180
181
  const path = resolveVariables(op.path, variables);
181
- const data = op.data ? resolveVariables(op.data, variables) : ``;
182
+ const data = op.json !== void 0 ? JSON.stringify(op.json) : op.data ? resolveVariables(op.data, variables) : ``;
182
183
  const result = await client.send({
183
184
  type: `append`,
184
185
  path,
@@ -413,7 +414,8 @@ async function executeOperation(op, ctx) {
413
414
  probability: op.probability,
414
415
  method: op.method,
415
416
  corruptBody: op.corruptBody,
416
- jitterMs: op.jitterMs
417
+ jitterMs: op.jitterMs,
418
+ injectSseEvent: op.injectSseEvent
417
419
  })
418
420
  });
419
421
  const faultTypes = [];
@@ -424,6 +426,7 @@ async function executeOperation(op, ctx) {
424
426
  if (op.truncateBodyBytes != null) faultTypes.push(`truncate=${op.truncateBodyBytes}b`);
425
427
  if (op.corruptBody) faultTypes.push(`corrupt`);
426
428
  if (op.probability != null) faultTypes.push(`p=${op.probability}`);
429
+ if (op.injectSseEvent) faultTypes.push(`sse:${op.injectSseEvent.eventType}`);
427
430
  const faultDesc = faultTypes.join(`,`) || `unknown`;
428
431
  if (verbose) console.log(` inject-error ${path} [${faultDesc}]x${op.count ?? 1}: ${response.ok ? `ok` : `failed`}`);
429
432
  if (!response.ok) return { error: `Failed to inject fault: ${response.status}` };
@@ -463,6 +466,17 @@ async function executeOperation(op, ctx) {
463
466
  if (verbose) console.log(` clear-dynamic: ${result.success ? `ok` : `failed`}`);
464
467
  return { result };
465
468
  }
469
+ case `validate`: {
470
+ const result = await client.send({
471
+ type: `validate`,
472
+ target: op.target
473
+ }, commandTimeout);
474
+ if (verbose) {
475
+ const targetType = op.target.target;
476
+ console.log(` validate ${targetType}: ${result.success ? `ok` : `failed`}`);
477
+ }
478
+ return { result };
479
+ }
466
480
  default: return { error: `Unknown operation: ${op.action}` };
467
481
  }
468
482
  }
@@ -487,6 +501,13 @@ function validateExpectation(result, expect) {
487
501
  if (result.success) return `Expected error ${expect.errorCode}, but operation succeeded`;
488
502
  if (isErrorResult(result) && result.errorCode !== expect.errorCode) return `Expected error code ${expect.errorCode}, got ${result.errorCode}`;
489
503
  }
504
+ if (expect.messageContains !== void 0) {
505
+ if (result.success) return `Expected error with message containing ${JSON.stringify(expect.messageContains)}, but operation succeeded`;
506
+ if (isErrorResult(result)) {
507
+ const missing = expect.messageContains.filter((s) => !result.message.toLowerCase().includes(s.toLowerCase()));
508
+ if (missing.length > 0) return `Expected error message to contain [${expect.messageContains.join(`, `)}], missing: [${missing.join(`, `)}]. Actual message: "${result.message}"`;
509
+ }
510
+ }
490
511
  if (expect.data !== void 0 && isReadResult(result)) {
491
512
  const actualData = result.chunks.map((c) => c.data).join(``);
492
513
  if (actualData !== expect.data) return `Expected data "${expect.data}", got "${actualData}"`;
@@ -559,6 +580,13 @@ function validateExpectation(result, expect) {
559
580
  if (expect.producerReceivedSeq !== void 0 && isAppendResult(result)) {
560
581
  if (result.producerReceivedSeq !== expect.producerReceivedSeq) return `Expected producerReceivedSeq=${expect.producerReceivedSeq}, got ${result.producerReceivedSeq}`;
561
582
  }
583
+ if (expect.valid !== void 0) {
584
+ if (expect.valid === true && !result.success) return `Expected validation to pass, but it failed`;
585
+ if (expect.valid === false && result.success) return `Expected validation to fail, but it passed`;
586
+ }
587
+ if (expect.errorContains !== void 0 && isErrorResult(result)) {
588
+ if (!result.message.includes(expect.errorContains)) return `Expected error message to contain "${expect.errorContains}", got "${result.message}"`;
589
+ }
562
590
  return null;
563
591
  }
564
592
  /**
@@ -570,9 +598,16 @@ function featureToProperty(feature) {
570
598
  sse: `sse`,
571
599
  "long-poll": `longPoll`,
572
600
  longPoll: `longPoll`,
601
+ auto: `auto`,
573
602
  streaming: `streaming`,
574
603
  dynamicHeaders: `dynamicHeaders`,
575
- "dynamic-headers": `dynamicHeaders`
604
+ "dynamic-headers": `dynamicHeaders`,
605
+ retryOptions: `retryOptions`,
606
+ "retry-options": `retryOptions`,
607
+ batchItems: `batchItems`,
608
+ "batch-items": `batchItems`,
609
+ strictZeroValidation: `strictZeroValidation`,
610
+ "strict-zero-validation": `strictZeroValidation`
576
611
  };
577
612
  return map[feature];
578
613
  }
@@ -877,7 +912,7 @@ const smallMessageThroughputScenario = {
877
912
  createOperation: (ctx) => ({
878
913
  op: `throughput_append`,
879
914
  path: `${ctx.basePath}/throughput-small`,
880
- count: 1e4,
915
+ count: 1e5,
881
916
  size: 100,
882
917
  concurrency: 200
883
918
  })
@@ -920,8 +955,8 @@ const readThroughputScenario = {
920
955
  expectedCount: ctx.setupData.expectedCount
921
956
  }),
922
957
  setup: (ctx) => {
923
- ctx.setupData.expectedCount = 1e4;
924
- return Promise.resolve({ data: { expectedCount: 1e4 } });
958
+ ctx.setupData.expectedCount = 1e5;
959
+ return Promise.resolve({ data: { expectedCount: 1e5 } });
925
960
  }
926
961
  };
927
962
  const sseLatencyScenario = {
@@ -1113,7 +1148,7 @@ async function runScenario(scenario, client, serverUrl, clientFeatures, verbose,
1113
1148
  n: i,
1114
1149
  data: `message-${i}-padding-for-size`
1115
1150
  });
1116
- await Promise.all(messages.map((msg) => ds.append(msg)));
1151
+ await Promise.all(messages.map((msg) => ds.append(JSON.stringify(msg))));
1117
1152
  }
1118
1153
  }
1119
1154
  if (verbose) log(` Warmup: ${scenario.config.warmupIterations} iterations...`);
@@ -1431,8 +1466,58 @@ async function runBenchmarks(options) {
1431
1466
  }
1432
1467
  return summary;
1433
1468
  }
1469
+ /**
1470
+ * Reads benchmark JSON files from a directory and generates a combined markdown report.
1471
+ * Each subdirectory should contain a benchmark-results.json file.
1472
+ */
1473
+ async function aggregateBenchmarkResults(resultsDir) {
1474
+ const entries = await (0, node_fs_promises.readdir)(resultsDir, { withFileTypes: true });
1475
+ const summaries = [];
1476
+ for (const entry of entries) {
1477
+ if (!entry.isDirectory()) continue;
1478
+ const jsonPath = (0, node_path.join)(resultsDir, entry.name, `benchmark-results.json`);
1479
+ try {
1480
+ const content = await (0, node_fs_promises.readFile)(jsonPath, `utf-8`);
1481
+ const summary = JSON.parse(content);
1482
+ summary.results = summary.results.map((r) => ({
1483
+ ...r,
1484
+ scenario: r.scenario
1485
+ }));
1486
+ summaries.push(summary);
1487
+ } catch {
1488
+ console.error(`Skipping ${entry.name}: no valid benchmark-results.json`);
1489
+ }
1490
+ }
1491
+ if (summaries.length === 0) return `# Client Benchmark Results\n\nNo benchmark results found.\n`;
1492
+ summaries.sort((a, b) => a.adapter.localeCompare(b.adapter));
1493
+ const lines = [];
1494
+ lines.push(`# Client Benchmark Results`);
1495
+ lines.push(``);
1496
+ const totalPassed = summaries.reduce((sum, s) => sum + s.passed, 0);
1497
+ const totalFailed = summaries.reduce((sum, s) => sum + s.failed, 0);
1498
+ const totalSkipped = summaries.reduce((sum, s) => sum + s.skipped, 0);
1499
+ lines.push(`| Client | Passed | Failed | Skipped | Status |`);
1500
+ lines.push(`|--------|--------|--------|---------|--------|`);
1501
+ for (const summary of summaries) {
1502
+ const status = summary.failed === 0 ? `✓` : `✗`;
1503
+ lines.push(`| ${summary.adapter} | ${summary.passed} | ${summary.failed} | ${summary.skipped} | ${status} |`);
1504
+ }
1505
+ lines.push(`| **Total** | **${totalPassed}** | **${totalFailed}** | **${totalSkipped}** | ${totalFailed === 0 ? `✓` : `✗`} |`);
1506
+ lines.push(``);
1507
+ for (const summary of summaries) {
1508
+ lines.push(generateMarkdownReport(summary));
1509
+ lines.push(``);
1510
+ }
1511
+ return lines.join(`\n`);
1512
+ }
1434
1513
 
1435
1514
  //#endregion
1515
+ Object.defineProperty(exports, 'aggregateBenchmarkResults', {
1516
+ enumerable: true,
1517
+ get: function () {
1518
+ return aggregateBenchmarkResults;
1519
+ }
1520
+ });
1436
1521
  Object.defineProperty(exports, 'allScenarios', {
1437
1522
  enumerable: true,
1438
1523
  get: function () {
package/dist/cli.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
- require('./protocol-XeAOKBD-.cjs');
4
- const require_benchmark_runner = require('./benchmark-runner-Db4he452.cjs');
3
+ require('./protocol-IioVPNaP.cjs');
4
+ const require_benchmark_runner = require('./benchmark-runner-DliEfq9k.cjs');
5
5
 
6
6
  //#region src/cli.ts
7
7
  const HELP = `
@@ -10,6 +10,7 @@ Durable Streams Client Conformance Test Suite
10
10
  Usage:
11
11
  npx @durable-streams/client-conformance-tests --run <adapter> [options]
12
12
  npx @durable-streams/client-conformance-tests --bench <adapter> [options]
13
+ npx @durable-streams/client-conformance-tests --report <dir>
13
14
 
14
15
  Arguments:
15
16
  <adapter> Path to client adapter executable, or "ts" for built-in TypeScript adapter
@@ -31,6 +32,10 @@ Benchmark Options:
31
32
  Can be specified multiple times
32
33
  --format <fmt> Output format: console, json, markdown (default: console)
33
34
 
35
+ Report Options:
36
+ --report <dir> Aggregate benchmark results from JSON files in directory
37
+ Each subdirectory should contain a benchmark-results.json file
38
+
34
39
  Common Options:
35
40
  --verbose Show detailed output for each operation
36
41
  --port <port> Port for reference server (default: random)
@@ -62,6 +67,10 @@ Benchmark Examples:
62
67
  # Output as JSON for CI
63
68
  npx @durable-streams/client-conformance-tests --bench ts --format json
64
69
 
70
+ Report Examples:
71
+ # Aggregate benchmark results from CI artifacts
72
+ npx @durable-streams/client-conformance-tests --report ./benchmark-results
73
+
65
74
  Implementing a Client Adapter:
66
75
  A client adapter is an executable that communicates via stdin/stdout using
67
76
  JSON-line protocol. See the documentation for the protocol specification
@@ -82,6 +91,7 @@ Implementing a Client Adapter:
82
91
  function parseArgs(args) {
83
92
  let mode = null;
84
93
  let clientAdapter = ``;
94
+ let resultsDir = ``;
85
95
  const suites = [];
86
96
  const tags = [];
87
97
  let failFast = false;
@@ -114,6 +124,14 @@ function parseArgs(args) {
114
124
  return null;
115
125
  }
116
126
  clientAdapter = args[i];
127
+ } else if (arg === `--report`) {
128
+ mode = `report`;
129
+ i++;
130
+ if (i >= args.length) {
131
+ console.error(`Error: --report requires a directory path`);
132
+ return null;
133
+ }
134
+ resultsDir = args[i];
117
135
  } else if (arg === `--suite`) {
118
136
  i++;
119
137
  if (i >= args.length) {
@@ -206,8 +224,23 @@ function parseArgs(args) {
206
224
  }
207
225
  i++;
208
226
  }
209
- if (!mode || !clientAdapter) {
210
- console.error(`Error: --run <adapter> or --bench <adapter> is required`);
227
+ if (!mode) {
228
+ console.error(`Error: --run <adapter>, --bench <adapter>, or --report <dir> is required`);
229
+ console.log(`\nRun with --help for usage information`);
230
+ return null;
231
+ }
232
+ if (mode === `report`) {
233
+ if (!resultsDir) {
234
+ console.error(`Error: --report requires a directory path`);
235
+ return null;
236
+ }
237
+ return {
238
+ mode: `report`,
239
+ resultsDir
240
+ };
241
+ }
242
+ if (!clientAdapter) {
243
+ console.error(`Error: --run <adapter> or --bench <adapter> requires an adapter path`);
211
244
  console.log(`\nRun with --help for usage information`);
212
245
  return null;
213
246
  }
@@ -252,9 +285,12 @@ async function main() {
252
285
  if (parsed.mode === `conformance`) {
253
286
  const summary = await require_benchmark_runner.runConformanceTests(parsed.options);
254
287
  if (summary.failed > 0) process.exit(1);
255
- } else {
288
+ } else if (parsed.mode === `benchmark`) {
256
289
  const summary = await require_benchmark_runner.runBenchmarks(parsed.options);
257
290
  if (summary.failed > 0) process.exit(1);
291
+ } else {
292
+ const report = await require_benchmark_runner.aggregateBenchmarkResults(parsed.resultsDir);
293
+ console.log(report);
258
294
  }
259
295
  } catch (err) {
260
296
  console.error(`Error running ${parsed.mode}:`, err);
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import "./protocol-qb83AeUH.js";
3
- import { runBenchmarks, runConformanceTests } from "./benchmark-runner-CrE6JkbX.js";
2
+ import "./protocol-1p0soayz.js";
3
+ import { aggregateBenchmarkResults, runBenchmarks, runConformanceTests } from "./benchmark-runner-81waaCzs.js";
4
4
 
5
5
  //#region src/cli.ts
6
6
  const HELP = `
@@ -9,6 +9,7 @@ Durable Streams Client Conformance Test Suite
9
9
  Usage:
10
10
  npx @durable-streams/client-conformance-tests --run <adapter> [options]
11
11
  npx @durable-streams/client-conformance-tests --bench <adapter> [options]
12
+ npx @durable-streams/client-conformance-tests --report <dir>
12
13
 
13
14
  Arguments:
14
15
  <adapter> Path to client adapter executable, or "ts" for built-in TypeScript adapter
@@ -30,6 +31,10 @@ Benchmark Options:
30
31
  Can be specified multiple times
31
32
  --format <fmt> Output format: console, json, markdown (default: console)
32
33
 
34
+ Report Options:
35
+ --report <dir> Aggregate benchmark results from JSON files in directory
36
+ Each subdirectory should contain a benchmark-results.json file
37
+
33
38
  Common Options:
34
39
  --verbose Show detailed output for each operation
35
40
  --port <port> Port for reference server (default: random)
@@ -61,6 +66,10 @@ Benchmark Examples:
61
66
  # Output as JSON for CI
62
67
  npx @durable-streams/client-conformance-tests --bench ts --format json
63
68
 
69
+ Report Examples:
70
+ # Aggregate benchmark results from CI artifacts
71
+ npx @durable-streams/client-conformance-tests --report ./benchmark-results
72
+
64
73
  Implementing a Client Adapter:
65
74
  A client adapter is an executable that communicates via stdin/stdout using
66
75
  JSON-line protocol. See the documentation for the protocol specification
@@ -81,6 +90,7 @@ Implementing a Client Adapter:
81
90
  function parseArgs(args) {
82
91
  let mode = null;
83
92
  let clientAdapter = ``;
93
+ let resultsDir = ``;
84
94
  const suites = [];
85
95
  const tags = [];
86
96
  let failFast = false;
@@ -113,6 +123,14 @@ function parseArgs(args) {
113
123
  return null;
114
124
  }
115
125
  clientAdapter = args[i];
126
+ } else if (arg === `--report`) {
127
+ mode = `report`;
128
+ i++;
129
+ if (i >= args.length) {
130
+ console.error(`Error: --report requires a directory path`);
131
+ return null;
132
+ }
133
+ resultsDir = args[i];
116
134
  } else if (arg === `--suite`) {
117
135
  i++;
118
136
  if (i >= args.length) {
@@ -205,8 +223,23 @@ function parseArgs(args) {
205
223
  }
206
224
  i++;
207
225
  }
208
- if (!mode || !clientAdapter) {
209
- console.error(`Error: --run <adapter> or --bench <adapter> is required`);
226
+ if (!mode) {
227
+ console.error(`Error: --run <adapter>, --bench <adapter>, or --report <dir> is required`);
228
+ console.log(`\nRun with --help for usage information`);
229
+ return null;
230
+ }
231
+ if (mode === `report`) {
232
+ if (!resultsDir) {
233
+ console.error(`Error: --report requires a directory path`);
234
+ return null;
235
+ }
236
+ return {
237
+ mode: `report`,
238
+ resultsDir
239
+ };
240
+ }
241
+ if (!clientAdapter) {
242
+ console.error(`Error: --run <adapter> or --bench <adapter> requires an adapter path`);
210
243
  console.log(`\nRun with --help for usage information`);
211
244
  return null;
212
245
  }
@@ -251,9 +284,12 @@ async function main() {
251
284
  if (parsed.mode === `conformance`) {
252
285
  const summary = await runConformanceTests(parsed.options);
253
286
  if (summary.failed > 0) process.exit(1);
254
- } else {
287
+ } else if (parsed.mode === `benchmark`) {
255
288
  const summary = await runBenchmarks(parsed.options);
256
289
  if (summary.failed > 0) process.exit(1);
290
+ } else {
291
+ const report = await aggregateBenchmarkResults(parsed.resultsDir);
292
+ console.log(report);
257
293
  }
258
294
  } catch (err) {
259
295
  console.error(`Error running ${parsed.mode}:`, err);
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
- const require_protocol = require('./protocol-XeAOKBD-.cjs');
2
- const require_benchmark_runner = require('./benchmark-runner-Db4he452.cjs');
1
+ const require_protocol = require('./protocol-IioVPNaP.cjs');
2
+ const require_benchmark_runner = require('./benchmark-runner-DliEfq9k.cjs');
3
3
 
4
4
  exports.ErrorCodes = require_protocol.ErrorCodes
5
5
  exports.allScenarios = require_benchmark_runner.allScenarios
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { AppendCommand, AppendResult, BenchmarkAppendOp, BenchmarkCommand, BenchmarkCreateOp, BenchmarkOperation, BenchmarkReadOp, BenchmarkResult, BenchmarkRoundtripOp, BenchmarkStats, BenchmarkThroughputAppendOp, BenchmarkThroughputReadOp, ClearDynamicCommand, ClearDynamicResult, ConnectCommand, ConnectResult, CreateCommand, CreateResult, DeleteCommand, DeleteResult, ErrorCode, ErrorCodes, ErrorResult, HeadCommand, HeadResult, IdempotentAppendBatchCommand, IdempotentAppendBatchResult, IdempotentAppendCommand, IdempotentAppendResult, InitCommand, InitResult, ReadChunk, ReadCommand, ReadResult, SetDynamicHeaderCommand, SetDynamicHeaderResult, SetDynamicParamCommand, SetDynamicParamResult, ShutdownCommand, ShutdownResult, TestCommand, TestResult, calculateStats, decodeBase64, encodeBase64, formatStats, parseCommand, parseResult, serializeCommand, serializeResult } from "./protocol-D37G3c4e.cjs";
1
+ import { AppendCommand, AppendResult, BenchmarkAppendOp, BenchmarkCommand, BenchmarkCreateOp, BenchmarkOperation, BenchmarkReadOp, BenchmarkResult, BenchmarkRoundtripOp, BenchmarkStats, BenchmarkThroughputAppendOp, BenchmarkThroughputReadOp, ClearDynamicCommand, ClearDynamicResult, ConnectCommand, ConnectResult, CreateCommand, CreateResult, DeleteCommand, DeleteResult, ErrorCode, ErrorCodes, ErrorResult, HeadCommand, HeadResult, IdempotentAppendBatchCommand, IdempotentAppendBatchResult, IdempotentAppendCommand, IdempotentAppendResult, InitCommand, InitResult, ReadChunk, ReadCommand, ReadResult, SetDynamicHeaderCommand, SetDynamicHeaderResult, SetDynamicParamCommand, SetDynamicParamResult, ShutdownCommand, ShutdownResult, TestCommand, TestResult, ValidateCommand, ValidateIdempotentProducer, ValidateResult, ValidateRetryOptions, ValidateTarget, calculateStats, decodeBase64, encodeBase64, formatStats, parseCommand, parseResult, serializeCommand, serializeResult } from "./protocol-BxZTqJmO.cjs";
2
2
 
3
3
  //#region src/test-cases.d.ts
4
4
  interface TestSuite {
@@ -83,6 +83,8 @@ interface AppendOperation {
83
83
  path: string;
84
84
  /** Data to append (string) */
85
85
  data?: string;
86
+ /** JSON data to append (will be stringified) */
87
+ json?: unknown;
86
88
  /** Binary data (base64 encoded) */
87
89
  binaryData?: string;
88
90
  /** Sequence number for ordering (Stream-Seq header) */
@@ -328,6 +330,13 @@ interface InjectErrorOperation {
328
330
  corruptBody?: boolean;
329
331
  /** Add jitter to delay (random 0-jitterMs added to delayMs) */
330
332
  jitterMs?: number;
333
+ /** Inject an SSE event with custom type and data (for testing SSE parsing) */
334
+ injectSseEvent?: {
335
+ /** Event type (e.g., "unknown", "control", "data") */
336
+ eventType: string;
337
+ /** Event data (will be sent as-is) */
338
+ data: string;
339
+ };
331
340
  }
332
341
  /**
333
342
  * Clear all injected errors.
@@ -365,9 +374,45 @@ interface ClearDynamicOperation {
365
374
  action: `clear-dynamic`;
366
375
  }
367
376
  /**
377
+ * Validate client-side input parameters.
378
+ * Tests that clients properly validate inputs before making network requests.
379
+ */
380
+ interface ValidateOperation {
381
+ action: `validate`;
382
+ /** What to validate */
383
+ target: ValidateTarget$1;
384
+ expect?: ValidateExpectation;
385
+ }
386
+ /**
387
+ * Validation target types.
388
+ */
389
+ type ValidateTarget$1 = ValidateRetryOptionsTarget | ValidateIdempotentProducerTarget;
390
+ interface ValidateRetryOptionsTarget {
391
+ target: `retry-options`;
392
+ maxRetries?: number;
393
+ initialDelayMs?: number;
394
+ maxDelayMs?: number;
395
+ multiplier?: number;
396
+ }
397
+ interface ValidateIdempotentProducerTarget {
398
+ target: `idempotent-producer`;
399
+ producerId?: string;
400
+ epoch?: number;
401
+ maxBatchBytes?: number;
402
+ maxBatchItems?: number;
403
+ }
404
+ interface ValidateExpectation {
405
+ /** If true, validation should pass */
406
+ valid?: boolean;
407
+ /** Expected error code if validation fails */
408
+ errorCode?: string;
409
+ /** Expected error message substring if validation fails */
410
+ errorContains?: string;
411
+ }
412
+ /**
368
413
  * All possible test operations.
369
414
  */
370
- type TestOperation = CreateOperation | ConnectOperation | AppendOperation | AppendBatchOperation | IdempotentAppendOperation | IdempotentAppendBatchOperation | ReadOperation | HeadOperation | DeleteOperation | WaitOperation | SetOperation | AssertOperation | ServerAppendOperation | AwaitOperation | InjectErrorOperation | ClearErrorsOperation | SetDynamicHeaderOperation | SetDynamicParamOperation | ClearDynamicOperation;
415
+ type TestOperation = CreateOperation | ConnectOperation | AppendOperation | AppendBatchOperation | IdempotentAppendOperation | IdempotentAppendBatchOperation | ReadOperation | HeadOperation | DeleteOperation | WaitOperation | SetOperation | AssertOperation | ServerAppendOperation | AwaitOperation | InjectErrorOperation | ClearErrorsOperation | SetDynamicHeaderOperation | SetDynamicParamOperation | ClearDynamicOperation | ValidateOperation;
371
416
  /**
372
417
  * Base expectation fields.
373
418
  */
@@ -376,6 +421,8 @@ interface BaseExpectation {
376
421
  status?: number;
377
422
  /** Expected error code (if operation should fail) */
378
423
  errorCode?: string;
424
+ /** Strings that should be present in error message (for context validation) */
425
+ messageContains?: Array<string>;
379
426
  /** Store result in variable */
380
427
  storeAs?: string;
381
428
  }
@@ -620,4 +667,4 @@ interface BenchmarkSummary {
620
667
  declare function runBenchmarks(options: BenchmarkRunnerOptions): Promise<BenchmarkSummary>;
621
668
 
622
669
  //#endregion
623
- export { AppendCommand, AppendResult, BenchmarkAppendOp, BenchmarkCommand, BenchmarkCreateOp, BenchmarkCriteria, BenchmarkOperation, BenchmarkReadOp, BenchmarkResult, BenchmarkRoundtripOp, BenchmarkRunnerOptions, BenchmarkScenario, BenchmarkScenarioConfig, BenchmarkStats, BenchmarkSummary, BenchmarkThroughputAppendOp, BenchmarkThroughputReadOp, ClearDynamicCommand, ClearDynamicResult, ClientFeature, ConnectCommand, ConnectResult, CreateCommand, CreateResult, DeleteCommand, DeleteResult, ErrorCode, ErrorCodes, ErrorResult, HeadCommand, HeadResult, IdempotentAppendBatchCommand, IdempotentAppendBatchResult, IdempotentAppendCommand, IdempotentAppendResult, InitCommand, InitResult, ReadChunk, ReadCommand, ReadResult, RunSummary, RunnerOptions, ScenarioContext, ScenarioResult, SetDynamicHeaderCommand, SetDynamicHeaderResult, SetDynamicParamCommand, SetDynamicParamResult, ShutdownCommand, ShutdownResult, TestCase, TestCommand, TestOperation, TestResult, TestRunResult, TestSuite, allScenarios, calculateStats, countTests, decodeBase64, encodeBase64, filterByCategory, formatStats, getScenarioById, getScenariosByCategory, loadEmbeddedTestSuites, loadTestSuites, parseCommand, parseResult, runBenchmarks, runConformanceTests, scenariosByCategory, serializeCommand, serializeResult };
670
+ export { AppendCommand, AppendResult, BenchmarkAppendOp, BenchmarkCommand, BenchmarkCreateOp, BenchmarkCriteria, BenchmarkOperation, BenchmarkReadOp, BenchmarkResult, BenchmarkRoundtripOp, BenchmarkRunnerOptions, BenchmarkScenario, BenchmarkScenarioConfig, BenchmarkStats, BenchmarkSummary, BenchmarkThroughputAppendOp, BenchmarkThroughputReadOp, ClearDynamicCommand, ClearDynamicResult, ClientFeature, ConnectCommand, ConnectResult, CreateCommand, CreateResult, DeleteCommand, DeleteResult, ErrorCode, ErrorCodes, ErrorResult, HeadCommand, HeadResult, IdempotentAppendBatchCommand, IdempotentAppendBatchResult, IdempotentAppendCommand, IdempotentAppendResult, InitCommand, InitResult, ReadChunk, ReadCommand, ReadResult, RunSummary, RunnerOptions, ScenarioContext, ScenarioResult, SetDynamicHeaderCommand, SetDynamicHeaderResult, SetDynamicParamCommand, SetDynamicParamResult, ShutdownCommand, ShutdownResult, TestCase, TestCommand, TestOperation, TestResult, TestRunResult, TestSuite, ValidateCommand, ValidateIdempotentProducer, ValidateResult, ValidateRetryOptions, ValidateTarget, allScenarios, calculateStats, countTests, decodeBase64, encodeBase64, filterByCategory, formatStats, getScenarioById, getScenariosByCategory, loadEmbeddedTestSuites, loadTestSuites, parseCommand, parseResult, runBenchmarks, runConformanceTests, scenariosByCategory, serializeCommand, serializeResult };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AppendCommand, AppendResult, BenchmarkAppendOp, BenchmarkCommand, BenchmarkCreateOp, BenchmarkOperation, BenchmarkReadOp, BenchmarkResult, BenchmarkRoundtripOp, BenchmarkStats, BenchmarkThroughputAppendOp, BenchmarkThroughputReadOp, ClearDynamicCommand, ClearDynamicResult, ConnectCommand, ConnectResult, CreateCommand, CreateResult, DeleteCommand, DeleteResult, ErrorCode, ErrorCodes$1 as ErrorCodes, ErrorResult, HeadCommand, HeadResult, IdempotentAppendBatchCommand, IdempotentAppendBatchResult, IdempotentAppendCommand, IdempotentAppendResult, InitCommand, InitResult, ReadChunk, ReadCommand, ReadResult, SetDynamicHeaderCommand, SetDynamicHeaderResult, SetDynamicParamCommand, SetDynamicParamResult, ShutdownCommand, ShutdownResult, TestCommand, TestResult, calculateStats$1 as calculateStats, decodeBase64$1 as decodeBase64, encodeBase64$1 as encodeBase64, formatStats$1 as formatStats, parseCommand$1 as parseCommand, parseResult$1 as parseResult, serializeCommand$1 as serializeCommand, serializeResult$1 as serializeResult } from "./protocol-Mcbiq3nQ.js";
1
+ import { AppendCommand, AppendResult, BenchmarkAppendOp, BenchmarkCommand, BenchmarkCreateOp, BenchmarkOperation, BenchmarkReadOp, BenchmarkResult, BenchmarkRoundtripOp, BenchmarkStats, BenchmarkThroughputAppendOp, BenchmarkThroughputReadOp, ClearDynamicCommand, ClearDynamicResult, ConnectCommand, ConnectResult, CreateCommand, CreateResult, DeleteCommand, DeleteResult, ErrorCode, ErrorCodes$1 as ErrorCodes, ErrorResult, HeadCommand, HeadResult, IdempotentAppendBatchCommand, IdempotentAppendBatchResult, IdempotentAppendCommand, IdempotentAppendResult, InitCommand, InitResult, ReadChunk, ReadCommand, ReadResult, SetDynamicHeaderCommand, SetDynamicHeaderResult, SetDynamicParamCommand, SetDynamicParamResult, ShutdownCommand, ShutdownResult, TestCommand, TestResult, ValidateCommand, ValidateIdempotentProducer, ValidateResult, ValidateRetryOptions, ValidateTarget, calculateStats$1 as calculateStats, decodeBase64$1 as decodeBase64, encodeBase64$1 as encodeBase64, formatStats$1 as formatStats, parseCommand$1 as parseCommand, parseResult$1 as parseResult, serializeCommand$1 as serializeCommand, serializeResult$1 as serializeResult } from "./protocol-JuFzdV5x.js";
2
2
 
3
3
  //#region src/test-cases.d.ts
4
4
  interface TestSuite {
@@ -83,6 +83,8 @@ interface AppendOperation {
83
83
  path: string;
84
84
  /** Data to append (string) */
85
85
  data?: string;
86
+ /** JSON data to append (will be stringified) */
87
+ json?: unknown;
86
88
  /** Binary data (base64 encoded) */
87
89
  binaryData?: string;
88
90
  /** Sequence number for ordering (Stream-Seq header) */
@@ -328,6 +330,13 @@ interface InjectErrorOperation {
328
330
  corruptBody?: boolean;
329
331
  /** Add jitter to delay (random 0-jitterMs added to delayMs) */
330
332
  jitterMs?: number;
333
+ /** Inject an SSE event with custom type and data (for testing SSE parsing) */
334
+ injectSseEvent?: {
335
+ /** Event type (e.g., "unknown", "control", "data") */
336
+ eventType: string;
337
+ /** Event data (will be sent as-is) */
338
+ data: string;
339
+ };
331
340
  }
332
341
  /**
333
342
  * Clear all injected errors.
@@ -365,9 +374,45 @@ interface ClearDynamicOperation {
365
374
  action: `clear-dynamic`;
366
375
  }
367
376
  /**
377
+ * Validate client-side input parameters.
378
+ * Tests that clients properly validate inputs before making network requests.
379
+ */
380
+ interface ValidateOperation {
381
+ action: `validate`;
382
+ /** What to validate */
383
+ target: ValidateTarget$1;
384
+ expect?: ValidateExpectation;
385
+ }
386
+ /**
387
+ * Validation target types.
388
+ */
389
+ type ValidateTarget$1 = ValidateRetryOptionsTarget | ValidateIdempotentProducerTarget;
390
+ interface ValidateRetryOptionsTarget {
391
+ target: `retry-options`;
392
+ maxRetries?: number;
393
+ initialDelayMs?: number;
394
+ maxDelayMs?: number;
395
+ multiplier?: number;
396
+ }
397
+ interface ValidateIdempotentProducerTarget {
398
+ target: `idempotent-producer`;
399
+ producerId?: string;
400
+ epoch?: number;
401
+ maxBatchBytes?: number;
402
+ maxBatchItems?: number;
403
+ }
404
+ interface ValidateExpectation {
405
+ /** If true, validation should pass */
406
+ valid?: boolean;
407
+ /** Expected error code if validation fails */
408
+ errorCode?: string;
409
+ /** Expected error message substring if validation fails */
410
+ errorContains?: string;
411
+ }
412
+ /**
368
413
  * All possible test operations.
369
414
  */
370
- type TestOperation = CreateOperation | ConnectOperation | AppendOperation | AppendBatchOperation | IdempotentAppendOperation | IdempotentAppendBatchOperation | ReadOperation | HeadOperation | DeleteOperation | WaitOperation | SetOperation | AssertOperation | ServerAppendOperation | AwaitOperation | InjectErrorOperation | ClearErrorsOperation | SetDynamicHeaderOperation | SetDynamicParamOperation | ClearDynamicOperation;
415
+ type TestOperation = CreateOperation | ConnectOperation | AppendOperation | AppendBatchOperation | IdempotentAppendOperation | IdempotentAppendBatchOperation | ReadOperation | HeadOperation | DeleteOperation | WaitOperation | SetOperation | AssertOperation | ServerAppendOperation | AwaitOperation | InjectErrorOperation | ClearErrorsOperation | SetDynamicHeaderOperation | SetDynamicParamOperation | ClearDynamicOperation | ValidateOperation;
371
416
  /**
372
417
  * Base expectation fields.
373
418
  */
@@ -376,6 +421,8 @@ interface BaseExpectation {
376
421
  status?: number;
377
422
  /** Expected error code (if operation should fail) */
378
423
  errorCode?: string;
424
+ /** Strings that should be present in error message (for context validation) */
425
+ messageContains?: Array<string>;
379
426
  /** Store result in variable */
380
427
  storeAs?: string;
381
428
  }
@@ -620,4 +667,4 @@ interface BenchmarkSummary {
620
667
  declare function runBenchmarks(options: BenchmarkRunnerOptions): Promise<BenchmarkSummary>;
621
668
 
622
669
  //#endregion
623
- export { AppendCommand, AppendResult, BenchmarkAppendOp, BenchmarkCommand, BenchmarkCreateOp, BenchmarkCriteria, BenchmarkOperation, BenchmarkReadOp, BenchmarkResult, BenchmarkRoundtripOp, BenchmarkRunnerOptions, BenchmarkScenario, BenchmarkScenarioConfig, BenchmarkStats, BenchmarkSummary, BenchmarkThroughputAppendOp, BenchmarkThroughputReadOp, ClearDynamicCommand, ClearDynamicResult, ClientFeature, ConnectCommand, ConnectResult, CreateCommand, CreateResult, DeleteCommand, DeleteResult, ErrorCode, ErrorCodes, ErrorResult, HeadCommand, HeadResult, IdempotentAppendBatchCommand, IdempotentAppendBatchResult, IdempotentAppendCommand, IdempotentAppendResult, InitCommand, InitResult, ReadChunk, ReadCommand, ReadResult, RunSummary, RunnerOptions, ScenarioContext, ScenarioResult, SetDynamicHeaderCommand, SetDynamicHeaderResult, SetDynamicParamCommand, SetDynamicParamResult, ShutdownCommand, ShutdownResult, TestCase, TestCommand, TestOperation, TestResult, TestRunResult, TestSuite, allScenarios, calculateStats, countTests, decodeBase64, encodeBase64, filterByCategory, formatStats, getScenarioById, getScenariosByCategory, loadEmbeddedTestSuites, loadTestSuites, parseCommand, parseResult, runBenchmarks, runConformanceTests, scenariosByCategory, serializeCommand, serializeResult };
670
+ export { AppendCommand, AppendResult, BenchmarkAppendOp, BenchmarkCommand, BenchmarkCreateOp, BenchmarkCriteria, BenchmarkOperation, BenchmarkReadOp, BenchmarkResult, BenchmarkRoundtripOp, BenchmarkRunnerOptions, BenchmarkScenario, BenchmarkScenarioConfig, BenchmarkStats, BenchmarkSummary, BenchmarkThroughputAppendOp, BenchmarkThroughputReadOp, ClearDynamicCommand, ClearDynamicResult, ClientFeature, ConnectCommand, ConnectResult, CreateCommand, CreateResult, DeleteCommand, DeleteResult, ErrorCode, ErrorCodes, ErrorResult, HeadCommand, HeadResult, IdempotentAppendBatchCommand, IdempotentAppendBatchResult, IdempotentAppendCommand, IdempotentAppendResult, InitCommand, InitResult, ReadChunk, ReadCommand, ReadResult, RunSummary, RunnerOptions, ScenarioContext, ScenarioResult, SetDynamicHeaderCommand, SetDynamicHeaderResult, SetDynamicParamCommand, SetDynamicParamResult, ShutdownCommand, ShutdownResult, TestCase, TestCommand, TestOperation, TestResult, TestRunResult, TestSuite, ValidateCommand, ValidateIdempotentProducer, ValidateResult, ValidateRetryOptions, ValidateTarget, allScenarios, calculateStats, countTests, decodeBase64, encodeBase64, filterByCategory, formatStats, getScenarioById, getScenariosByCategory, loadEmbeddedTestSuites, loadTestSuites, parseCommand, parseResult, runBenchmarks, runConformanceTests, scenariosByCategory, serializeCommand, serializeResult };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ErrorCodes, calculateStats, decodeBase64, encodeBase64, formatStats, parseCommand, parseResult, serializeCommand, serializeResult } from "./protocol-qb83AeUH.js";
2
- import { allScenarios, countTests, filterByCategory, getScenarioById, getScenariosByCategory, loadEmbeddedTestSuites, loadTestSuites, runBenchmarks, runConformanceTests, scenariosByCategory } from "./benchmark-runner-CrE6JkbX.js";
1
+ import { ErrorCodes, calculateStats, decodeBase64, encodeBase64, formatStats, parseCommand, parseResult, serializeCommand, serializeResult } from "./protocol-1p0soayz.js";
2
+ import { allScenarios, countTests, filterByCategory, getScenarioById, getScenariosByCategory, loadEmbeddedTestSuites, loadTestSuites, runBenchmarks, runConformanceTests, scenariosByCategory } from "./benchmark-runner-81waaCzs.js";
3
3
 
4
4
  export { ErrorCodes, allScenarios, calculateStats, countTests, decodeBase64, encodeBase64, filterByCategory, formatStats, getScenarioById, getScenariosByCategory, loadEmbeddedTestSuites, loadTestSuites, parseCommand, parseResult, runBenchmarks, runConformanceTests, scenariosByCategory, serializeCommand, serializeResult };
@@ -48,7 +48,8 @@ const ErrorCodes = {
48
48
  UNEXPECTED_STATUS: `UNEXPECTED_STATUS`,
49
49
  PARSE_ERROR: `PARSE_ERROR`,
50
50
  INTERNAL_ERROR: `INTERNAL_ERROR`,
51
- NOT_SUPPORTED: `NOT_SUPPORTED`
51
+ NOT_SUPPORTED: `NOT_SUPPORTED`,
52
+ INVALID_ARGUMENT: `INVALID_ARGUMENT`
52
53
  };
53
54
  /**
54
55
  * Calculate statistics from an array of durations in nanoseconds.