@ferricstore/ferricstore 0.11.4 → 0.11.6

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
@@ -18,9 +18,10 @@ Requires Node.js 22.22 or newer. The SDK ships ESM and CommonJS builds and is te
18
18
 
19
19
  ## Compatibility
20
20
 
21
- TypeScript SDK `0.11.4` requires FerricStore server `0.11.4` or newer because
22
- schedule responses now use the complete recurrence contract. The native wire
23
- protocol remains v1 (`FSNP` framing and existing opcode numbers are unchanged).
21
+ TypeScript SDK `0.11.6` requires FerricStore server `0.11.4` or newer. With
22
+ FerricStore 0.11.6 it negotiates compact Stream mode 34 for homogeneous auto-ID
23
+ `XADD` pipelines and compact Pub/Sub mode 35 for homogeneous `PUBLISH`
24
+ pipelines. Native wire protocol v1 and the generic fallback are unchanged.
24
25
  Capabilities and response-size limits are negotiated
25
26
  per connection from the HELLO-shaped startup response rather than inferred from
26
27
  a server version table.
@@ -43,7 +44,7 @@ const { FerricStoreClient, JsonCodec } = require("@ferricstore/ferricstore");
43
44
  docker run -p 6388:6388 \
44
45
  -e FERRICSTORE_PROTECTED_MODE=false \
45
46
  -v ferricstore_data:/data \
46
- ghcr.io/ferricstore/ferricstore:0.11.4
47
+ quay.io/ferricstore/ferricstore:0.11.6
47
48
  ```
48
49
 
49
50
  ## Query durable runs
package/dist/index.cjs CHANGED
@@ -4632,7 +4632,7 @@ function compactKeyPipelinePayload(opcode, args, mode, maxBodyBytes) {
4632
4632
  }
4633
4633
  return { flags: FLAG_CUSTOM_PAYLOAD, opcode, payload: out };
4634
4634
  }
4635
- function compactPipelinePayload(commands, maxBodyBytes) {
4635
+ function compactPipelinePayload(commands, maxBodyBytes, allowStreamXAdd = true, allowPubSubPublish = true) {
4636
4636
  if (commands.length === 0) {
4637
4637
  assertCompactPayloadFits(6, maxBodyBytes);
4638
4638
  const out = import_node_buffer14.Buffer.allocUnsafe(6);
@@ -4644,8 +4644,115 @@ function compactPipelinePayload(commands, maxBodyBytes) {
4644
4644
  }
4645
4645
  if (commandNameIs(commands[0]?.[0], "SET")) return compactSetPipelinePayload(commands, maxBodyBytes);
4646
4646
  if (commandNameIs(commands[0]?.[0], "GET")) return compactGetPipelinePayload(commands, maxBodyBytes);
4647
+ if (commandNameIs(commands[0]?.[0], "XADD")) {
4648
+ return allowStreamXAdd ? compactStreamXAddPipelinePayload(commands, maxBodyBytes) : void 0;
4649
+ }
4650
+ if (commandNameIs(commands[0]?.[0], "PUBLISH")) {
4651
+ return allowPubSubPublish ? compactPubSubPublishPipelinePayload(commands, maxBodyBytes) : void 0;
4652
+ }
4647
4653
  return void 0;
4648
4654
  }
4655
+ function compactPubSubPublishPipelinePayload(commands, maxBodyBytes) {
4656
+ const minimum = 6 + commands.length * 8;
4657
+ if (!compactPayloadFits(minimum, maxBodyBytes)) {
4658
+ if (!compactPipelineEligible(commands, "PUBLISH", 3)) return void 0;
4659
+ assertCompactPayloadFits(minimum, maxBodyBytes);
4660
+ }
4661
+ const argumentByteLengths = new Uint32Array(commands.length * 2);
4662
+ const argumentValues = new Array(commands.length * 2);
4663
+ let total = 6;
4664
+ for (let index = 0; index < commands.length; index += 1) {
4665
+ const command = commands[index];
4666
+ if (!Object.hasOwn(commands, index) || !Array.isArray(command)) {
4667
+ throw new TypeError("pipeline commands must be a dense array of command arrays");
4668
+ }
4669
+ if (command.length !== 3 || !Object.hasOwn(command, 0) || !Object.hasOwn(command, 1) || !Object.hasOwn(command, 2)) {
4670
+ if (command.length === 3) throw new TypeError("pipeline command arguments must be dense");
4671
+ return void 0;
4672
+ }
4673
+ if (!commandNameIs(command[0], "PUBLISH")) return void 0;
4674
+ const channel = command[1];
4675
+ const message = command[2];
4676
+ if (!isCompactBinaryScalar(channel) || !isCompactBinaryScalar(message)) return void 0;
4677
+ const channelByteLength = compactBinaryByteLength(channel);
4678
+ const messageByteLength = compactBinaryByteLength(message);
4679
+ argumentValues[index * 2] = channel;
4680
+ argumentValues[index * 2 + 1] = message;
4681
+ argumentByteLengths[index * 2] = channelByteLength;
4682
+ argumentByteLengths[index * 2 + 1] = messageByteLength;
4683
+ total += 8 + channelByteLength + messageByteLength;
4684
+ }
4685
+ assertCompactPayloadFits(total, maxBodyBytes);
4686
+ const out = import_node_buffer14.Buffer.allocUnsafe(total);
4687
+ let offset = writeCompactPipelineHeader(out, 128 | 35, commands.length);
4688
+ for (let index = 0; index < commands.length; index += 1) {
4689
+ offset = writeBinaryValue(
4690
+ out,
4691
+ offset,
4692
+ argumentValues[index * 2],
4693
+ argumentByteLengths[index * 2] ?? 0
4694
+ );
4695
+ offset = writeBinaryValue(
4696
+ out,
4697
+ offset,
4698
+ argumentValues[index * 2 + 1],
4699
+ argumentByteLengths[index * 2 + 1] ?? 0
4700
+ );
4701
+ }
4702
+ return out;
4703
+ }
4704
+ function compactStreamXAddPipelinePayload(commands, maxBodyBytes) {
4705
+ let argumentCount = 0;
4706
+ let minimum = 6;
4707
+ for (const command of commands) {
4708
+ if (!Array.isArray(command)) {
4709
+ throw new TypeError("pipeline commands must be a dense array of command arrays");
4710
+ }
4711
+ if (command.length < 5 || (command.length - 3) % 2 !== 0) return void 0;
4712
+ for (let argumentIndex = 0; argumentIndex < command.length; argumentIndex += 1) {
4713
+ if (!Object.hasOwn(command, argumentIndex)) {
4714
+ throw new TypeError("pipeline command arguments must be dense");
4715
+ }
4716
+ }
4717
+ if (!commandTokenIs(command[0], "XADD") || !commandTokenIs(command[2], "*")) return void 0;
4718
+ if ((command.length - 3) / 2 > 65535) return void 0;
4719
+ for (let argumentIndex = 1; argumentIndex < command.length; argumentIndex += 1) {
4720
+ if (argumentIndex !== 2 && !isCompactBinaryScalar(command[argumentIndex])) return void 0;
4721
+ }
4722
+ argumentCount += command.length - 2;
4723
+ minimum += 2 + (command.length - 2) * 4;
4724
+ }
4725
+ assertCompactPayloadFits(minimum, maxBodyBytes);
4726
+ const byteLengths = new Uint32Array(argumentCount);
4727
+ let lengthIndex = 0;
4728
+ let total = 6;
4729
+ for (const command of commands) {
4730
+ total += 2;
4731
+ for (let argumentIndex = 1; argumentIndex < command.length; argumentIndex += 1) {
4732
+ if (argumentIndex === 2) continue;
4733
+ const value = command[argumentIndex];
4734
+ const byteLength = compactBinaryByteLength(value);
4735
+ byteLengths[lengthIndex] = byteLength;
4736
+ lengthIndex += 1;
4737
+ total += 4 + byteLength;
4738
+ }
4739
+ }
4740
+ assertCompactPayloadFits(total, maxBodyBytes);
4741
+ const out = import_node_buffer14.Buffer.allocUnsafe(total);
4742
+ let offset = writeCompactPipelineHeader(out, 128 | 34, commands.length);
4743
+ lengthIndex = 0;
4744
+ for (const command of commands) {
4745
+ offset = writeBinaryValue(out, offset, command[1], byteLengths[lengthIndex] ?? 0);
4746
+ lengthIndex += 1;
4747
+ out.writeUInt16BE((command.length - 3) / 2, offset);
4748
+ offset += 2;
4749
+ for (let argumentIndex = 3; argumentIndex < command.length; argumentIndex += 1) {
4750
+ offset = writeBinaryValue(out, offset, command[argumentIndex], byteLengths[lengthIndex] ?? 0);
4751
+ lengthIndex += 1;
4752
+ }
4753
+ }
4754
+ return out;
4755
+ }
4649
4756
  function compactSetPipelinePayload(commands, maxBodyBytes) {
4650
4757
  let total = 6;
4651
4758
  const minimum = total + commands.length * 8;
@@ -5227,10 +5334,19 @@ function decodeValueAt(data, offset, limits, budget, depth) {
5227
5334
  const count = data.readUInt32BE(offset);
5228
5335
  requireValueContainer(count, depth, limits, budget);
5229
5336
  offset += 4;
5230
- const values = [];
5337
+ const values = new Array(count);
5338
+ if (count === 3) {
5339
+ const first = decodeValueAt(data, offset, limits, budget, depth + 1);
5340
+ const second = decodeValueAt(data, first.offset, limits, budget, depth + 1);
5341
+ const third = decodeValueAt(data, second.offset, limits, budget, depth + 1);
5342
+ values[0] = first.value;
5343
+ values[1] = second.value;
5344
+ values[2] = third.value;
5345
+ return { value: values, offset: third.offset };
5346
+ }
5231
5347
  for (let index = 0; index < count; index += 1) {
5232
5348
  const read = decodeValueAt(data, offset, limits, budget, depth + 1);
5233
- values.push(read.value);
5349
+ values[index] = read.value;
5234
5350
  offset = read.offset;
5235
5351
  }
5236
5352
  return { value: values, offset };
@@ -5547,6 +5663,8 @@ function nativeNegotiation(value) {
5547
5663
  compactResponseOpcodes: parseCompactResponseOpcodes(
5548
5664
  field(responseCodecs, "compact_response_opcodes")
5549
5665
  ),
5666
+ compactPubSubPublish: strictUnsigned16(field(field(field(capabilities, "pipeline"), "modes"), "pubsub_publish")) === 35,
5667
+ compactStreamXAdd: strictUnsigned16(field(field(field(capabilities, "pipeline"), "modes"), "stream_xadd_auto")) === 34,
5550
5668
  flowQuery: parseFlowQueryNegotiation(capabilities, schemas),
5551
5669
  ...maxResponseBytes == null ? {} : { maxResponseBytes }
5552
5670
  };
@@ -6839,8 +6957,13 @@ function buildProtocolCommand(args, maxBodyBytes = Number.MAX_SAFE_INTEGER, allo
6839
6957
  }
6840
6958
  return commandExec(args);
6841
6959
  }
6842
- function tryPipelineCommand(commands, maxBodyBytes = Number.MAX_SAFE_INTEGER) {
6843
- const compact = compactPipelinePayload(commands, maxBodyBytes);
6960
+ function tryPipelineCommand(commands, maxBodyBytes = Number.MAX_SAFE_INTEGER, allowStreamXAdd = true, allowPubSubPublish = true) {
6961
+ const compact = compactPipelinePayload(
6962
+ commands,
6963
+ maxBodyBytes,
6964
+ allowStreamXAdd,
6965
+ allowPubSubPublish
6966
+ );
6844
6967
  if (compact != null) {
6845
6968
  return {
6846
6969
  compactResponseItems: commands.length,
@@ -7341,13 +7464,18 @@ function surfaceIndividualCommandErrors(results, rejected, hasRejected, options)
7341
7464
  }
7342
7465
 
7343
7466
  // src/native-pipeline-execution.ts
7344
- async function executeNativeFusedPipeline(host, commands, laneId, options, maxPipelineCommands, maxRequestFrameBytes) {
7467
+ async function executeNativeFusedPipeline(host, commands, laneId, options, maxPipelineCommands, maxRequestFrameBytes, compactStreamXAdd = true, compactPubSubPublish = true) {
7345
7468
  commands = snapshotPipelineCommands(commands);
7346
7469
  options = snapshotPipelineOptions(options) ?? options;
7347
7470
  if (commands.length === 0) return [];
7348
7471
  if (maxPipelineCommands === 0 || commands.length > maxPipelineCommands) return void 0;
7349
7472
  try {
7350
- const pipeline = tryPipelineCommand(commands, maxRequestFrameBytes);
7473
+ const pipeline = tryPipelineCommand(
7474
+ commands,
7475
+ maxRequestFrameBytes,
7476
+ compactStreamXAdd,
7477
+ compactPubSubPublish
7478
+ );
7351
7479
  if (pipeline == null) return void 0;
7352
7480
  const response = await host.executeProtocolCommand(pipeline, laneId);
7353
7481
  return unwrapPipelineResponse(response, options, commands.length);
@@ -7356,7 +7484,7 @@ async function executeNativeFusedPipeline(host, commands, laneId, options, maxPi
7356
7484
  throw error;
7357
7485
  }
7358
7486
  }
7359
- async function executeNativePipeline(host, commands, laneId, options, maxPipelineCommands, maxRequestFrameBytes) {
7487
+ async function executeNativePipeline(host, commands, laneId, options, maxPipelineCommands, maxRequestFrameBytes, compactStreamXAdd = true, compactPubSubPublish = true) {
7360
7488
  commands = snapshotPipelineCommands(commands);
7361
7489
  options = snapshotPipelineOptions(options) ?? options;
7362
7490
  if (commands.length === 0) return [];
@@ -7364,7 +7492,15 @@ async function executeNativePipeline(host, commands, laneId, options, maxPipelin
7364
7492
  return await executeIndividually(host, commands, laneId, pipelineFallbackOptions(commands, options));
7365
7493
  }
7366
7494
  if (commands.length <= maxPipelineCommands) {
7367
- return await executeChunk(host, commands, laneId, options, maxRequestFrameBytes);
7495
+ return await executeChunk(
7496
+ host,
7497
+ commands,
7498
+ laneId,
7499
+ options,
7500
+ maxRequestFrameBytes,
7501
+ compactStreamXAdd,
7502
+ compactPubSubPublish
7503
+ );
7368
7504
  }
7369
7505
  const collectingOptions = pipelineErrorCollectingOptions(options);
7370
7506
  const results = new Array(commands.length);
@@ -7376,7 +7512,9 @@ async function executeNativePipeline(host, commands, laneId, options, maxPipelin
7376
7512
  chunk,
7377
7513
  laneId,
7378
7514
  pipelineSliceOptions(collectingOptions, start, start + chunk.length),
7379
- maxRequestFrameBytes
7515
+ maxRequestFrameBytes,
7516
+ compactStreamXAdd,
7517
+ compactPubSubPublish
7380
7518
  );
7381
7519
  for (let index = 0; index < chunkResults.length; index += 1) results[start + index] = chunkResults[index];
7382
7520
  rejected = collectPipelineRejections(chunkResults, start, rejected);
@@ -7384,9 +7522,14 @@ async function executeNativePipeline(host, commands, laneId, options, maxPipelin
7384
7522
  if (rejected != null) attachPipelineItemRejectionFlags(results, rejected);
7385
7523
  return surfaceFirstPipelineItemError(results, options);
7386
7524
  }
7387
- async function executeChunk(host, commands, laneId, options, maxRequestFrameBytes) {
7525
+ async function executeChunk(host, commands, laneId, options, maxRequestFrameBytes, compactStreamXAdd, compactPubSubPublish) {
7388
7526
  try {
7389
- const pipeline = tryPipelineCommand(commands, maxRequestFrameBytes);
7527
+ const pipeline = tryPipelineCommand(
7528
+ commands,
7529
+ maxRequestFrameBytes,
7530
+ compactStreamXAdd,
7531
+ compactPubSubPublish
7532
+ );
7390
7533
  if (pipeline != null) {
7391
7534
  const response = await host.executeProtocolCommand(pipeline, laneId);
7392
7535
  return unwrapPipelineResponse(response, options, commands.length);
@@ -7404,14 +7547,18 @@ async function executeChunk(host, commands, laneId, options, maxRequestFrameByte
7404
7547
  commands.slice(0, middle),
7405
7548
  laneId,
7406
7549
  pipelineSliceOptions(collectingOptions, 0, middle),
7407
- maxRequestFrameBytes
7550
+ maxRequestFrameBytes,
7551
+ compactStreamXAdd,
7552
+ compactPubSubPublish
7408
7553
  );
7409
7554
  const second = await executeChunk(
7410
7555
  host,
7411
7556
  commands.slice(middle),
7412
7557
  laneId,
7413
7558
  pipelineSliceOptions(collectingOptions, middle, commands.length),
7414
- maxRequestFrameBytes
7559
+ maxRequestFrameBytes,
7560
+ compactStreamXAdd,
7561
+ compactPubSubPublish
7415
7562
  );
7416
7563
  return surfaceFirstPipelineItemError(mergePipelineResults(first, second), options);
7417
7564
  }
@@ -8045,6 +8192,8 @@ var NativeConnectionCapabilities = class {
8045
8192
  }
8046
8193
  configuredRequestFrameBytes;
8047
8194
  compactResponseOpcodes = EMPTY_COMPACT_RESPONSE_OPCODES;
8195
+ compactPubSubPublish = false;
8196
+ compactStreamXAdd = false;
8048
8197
  flowQuery;
8049
8198
  requestFrameBytes;
8050
8199
  negotiatedRequestFrameBytes;
@@ -8052,6 +8201,8 @@ var NativeConnectionCapabilities = class {
8052
8201
  const limits = startupLimits(value);
8053
8202
  const negotiation = nativeNegotiation(value);
8054
8203
  this.compactResponseOpcodes = negotiation.compactResponseOpcodes;
8204
+ this.compactPubSubPublish = negotiation.compactPubSubPublish;
8205
+ this.compactStreamXAdd = negotiation.compactStreamXAdd;
8055
8206
  this.flowQuery = negotiation.flowQuery;
8056
8207
  if (limits.frameBytes != null) {
8057
8208
  this.negotiatedRequestFrameBytes = Math.min(
@@ -8277,7 +8428,9 @@ var NativeAdapter = class _NativeAdapter {
8277
8428
  laneId,
8278
8429
  options,
8279
8430
  this.maxPipelineCommands,
8280
- this.capabilities.requestFrameBytes
8431
+ this.capabilities.requestFrameBytes,
8432
+ this.capabilities.compactStreamXAdd,
8433
+ this.capabilities.compactPubSubPublish
8281
8434
  );
8282
8435
  }
8283
8436
  /** @internal Execute an ordered pipeline on a topology-selected lane. */
@@ -8288,7 +8441,9 @@ var NativeAdapter = class _NativeAdapter {
8288
8441
  laneId,
8289
8442
  options,
8290
8443
  this.maxPipelineCommands,
8291
- this.capabilities.requestFrameBytes
8444
+ this.capabilities.requestFrameBytes,
8445
+ this.capabilities.compactStreamXAdd,
8446
+ this.capabilities.compactPubSubPublish
8292
8447
  );
8293
8448
  }
8294
8449
  async close() {
@@ -20371,7 +20526,7 @@ var WorkflowWorker = class {
20371
20526
  };
20372
20527
 
20373
20528
  // src/version.ts
20374
- var FERRICSTORE_SDK_VERSION = "0.11.4";
20529
+ var FERRICSTORE_SDK_VERSION = "0.11.6";
20375
20530
  var FERRICSTORE_MINIMUM_SERVER_VERSION = "0.11.4";
20376
20531
  var FERRICSTORE_NATIVE_PROTOCOL_VERSION = 1;
20377
20532
  // Annotate the CommonJS export names for ESM import in node: