posipaki 0.45.0 → 0.46.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/dist/index.d.ts CHANGED
@@ -20,7 +20,7 @@ declare function defineActor<Args, InternalState, InMsg extends Message, OutMsg
20
20
  //#endregion
21
21
  //#region src/version.d.ts
22
22
  /** posipaki's release version. Must equal package.json's `version`. */
23
- declare const LIB_VERSION = "0.45.0";
23
+ declare const LIB_VERSION = "0.46.0";
24
24
  //#endregion
25
25
  export { type ActorConfig, type ActorContext, type ActorDecorated, type ActorDefinition, type ActorPlugin, type ActorReflection, type AnyProcess, AsyncProcess, type AsyncProcessFn, type ExitMessage, type HandlerFn, type HandlerOptions, type HookResult, LIB_VERSION, type Message, type MethodOptions, type PluginTransform, Process, type ProcessCtx, type ProcessFn, type SenderInfo, type SenderOrigin, type SpawnedFrom, type StopMessage, type WithSender, type WithoutSender, asyncify, callHook, chainHook, defineActor, defineMessages, mergeConfigs, propagateError, runDispatch, runDispatchAsync, spawn, spawnAsync, stopPropagation };
26
26
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { i as runDispatch } from "./util-Brtfgv68.js";
2
2
  import { a as spawnAsync, i as runDispatchAsync, o as asyncify, t as AsyncProcess } from "./process.async-B1FqZyD1.js";
3
3
  import { n as chainHook, o as propagateError, r as mergeConfigs, s as stopPropagation, t as callHook } from "./hooks-CgXhmY4r.js";
4
4
  import { n as defineMessages, t as defineActor } from "./define-actor-DR1P1PA5.js";
5
- import { t as LIB_VERSION } from "./version-yonmO5bG.js";
5
+ import { t as LIB_VERSION } from "./version-DAQCfED6.js";
6
6
  //#region src/process.ts
7
7
  /** @deprecated Use {@link spawnAsync} instead. */
8
8
  function spawn(fn, pname, tp) {
@@ -146,6 +146,14 @@ function announcedOctets(frame) {
146
146
  function json1Channel(transport) {
147
147
  const frameReaders = /* @__PURE__ */ new Set();
148
148
  const segmentReaders = /* @__PURE__ */ new Set();
149
+ const closeReaders = /* @__PURE__ */ new Set();
150
+ /** The wire goes once, whoever ends it: the peer hanging up, or this side closing it. */
151
+ let closed = false;
152
+ const sayClosed = () => {
153
+ if (closed) return;
154
+ closed = true;
155
+ for (const reader of [...closeReaders]) reader();
156
+ };
149
157
  /** The head whose octets are on their way: set between the line and the run it announced. */
150
158
  let expecting = null;
151
159
  const deliver = (frame) => {
@@ -170,6 +178,7 @@ function json1Channel(transport) {
170
178
  }
171
179
  for (const reader of [...segmentReaders]) reader(frame, bytes);
172
180
  });
181
+ transport.onClose(sayClosed);
173
182
  return {
174
183
  send(frame) {
175
184
  return transport.send(encode(frame));
@@ -191,10 +200,11 @@ function json1Channel(transport) {
191
200
  };
192
201
  },
193
202
  onClose(handler) {
194
- transport.onClose(handler);
203
+ closeReaders.add(handler);
195
204
  },
196
- close() {
197
- return transport.close();
205
+ async close() {
206
+ sayClosed();
207
+ await transport.close();
198
208
  }
199
209
  };
200
210
  }
@@ -438,45 +448,6 @@ var RemoteBlob = class {
438
448
  }
439
449
  };
440
450
  /**
441
- * Bytes that crossed a connection: the sender's array, taken apart into a stream and put back
442
- * together here as the chunks arrive. Not lazy — the sender was asked the moment the reference
443
- * arrived — so awaiting this is waiting for the last chunk, not for a read to start.
444
- */
445
- var RemoteBytes = class {
446
- constructor(body, size) {
447
- this.filling = null;
448
- this.body = body;
449
- this.size = size;
450
- }
451
- /** Start re-assembling. Called by the store once its record for this stream is in place. */
452
- start() {
453
- this.bytes().catch(() => {});
454
- }
455
- /** The octets, once they are all here. */
456
- bytes() {
457
- this.filling ??= this.pvtFill();
458
- return this.filling;
459
- }
460
- /** The same octets as an array's buffer, for a caller that wants the platform's shape. */
461
- async arrayBuffer() {
462
- return (await this.bytes()).buffer;
463
- }
464
- then(onfulfilled, onrejected) {
465
- return this.bytes().then(onfulfilled, onrejected);
466
- }
467
- async pvtFill() {
468
- const all = new Uint8Array(this.size);
469
- let at = 0;
470
- for await (const chunk of this.body) {
471
- if (at + chunk.length > all.length) throw new Error(`the array is longer than the ${this.size} bytes it stated`);
472
- all.set(chunk, at);
473
- at += chunk.length;
474
- }
475
- if (at !== all.length) throw new Error(`the array is short: ${at} of ${this.size} octets arrived`);
476
- return all;
477
- }
478
- };
479
- /**
480
451
  * One connection's streams, as this side sees them.
481
452
  *
482
453
  * Both directions are in one table, because both are the same numbering: what this side offers
@@ -490,6 +461,7 @@ var StreamStore = class {
490
461
  this.pvtRefused = /* @__PURE__ */ new Map();
491
462
  this.pvtNamesOut = /* @__PURE__ */ new Map();
492
463
  this.pvtNamesIn = /* @__PURE__ */ new Map();
464
+ this.pvtPending = [];
493
465
  this.pvtNamedListeners = /* @__PURE__ */ new Set();
494
466
  this.pvtSegmentOff = null;
495
467
  this.pvtClosed = false;
@@ -532,14 +504,18 @@ var StreamStore = class {
532
504
  } };
533
505
  }
534
506
  /**
535
- * Offer bytes this side already holds. The reference states the length, so the far side can
536
- * check it against its bound and size the array before the first chunk arrives — a typed array
537
- * is one piece, so the wire carries it as a stream of one.
507
+ * Offer bytes this side already holds. The reference states the length, so the far side can check
508
+ * it against its bound and make room before the first chunk arrives — and, when the value is a typed
509
+ * array, the element kind and count too, so the same kind of array is made on the other side. The
510
+ * octets are one piece, and the wire carries them as a stream of one.
538
511
  */
539
- offerBytes(bytes) {
512
+ offerBytes(value) {
513
+ const { bytes, type, length } = octetsOf(value);
540
514
  return { $bytes: {
541
515
  id: this.pvtAnnounce(this.pvtOnePiece(bytes)),
542
- size: bytes.length
516
+ size: bytes.length,
517
+ ...type === void 0 ? {} : { type },
518
+ ...length === void 0 ? {} : { length }
543
519
  } };
544
520
  }
545
521
  pvtAnnounce(source, name) {
@@ -583,8 +559,22 @@ var StreamStore = class {
583
559
  }
584
560
  /** A value just read: every reference in it becomes something this side can read. */
585
561
  decode(value) {
562
+ this.pvtPending = [];
586
563
  return this.pvtDecode(value, /* @__PURE__ */ new Set());
587
564
  }
565
+ /**
566
+ * What the octets of a just-decoded value still owe. A message is handed to its process only once
567
+ * this has settled, so a typed array in a body is whole when the handler reads it; a stream, a blob
568
+ * and a plain value carry no octets of their own and settle at once.
569
+ */
570
+ owed() {
571
+ if (this.pvtPending.length === 0) return null;
572
+ return Promise.all(this.pvtPending).then(() => void 0);
573
+ }
574
+ /** The same question as a promise, for a caller that would rather always await. */
575
+ settled() {
576
+ return this.owed() ?? Promise.resolve();
577
+ }
588
578
  /** Drop every stream of this connection, in both directions. Called when it ends. */
589
579
  close() {
590
580
  if (this.pvtClosed) return;
@@ -593,6 +583,7 @@ var StreamStore = class {
593
583
  this.pvtSegmentOff?.();
594
584
  for (const offered of this.pvtOffers.values()) {
595
585
  offered.dropped = true;
586
+ Promise.resolve(this.channel.send({ $streamDone: { id: offered.id } })).catch(() => {});
596
587
  offered.source.return?.();
597
588
  }
598
589
  this.pvtOffers.clear();
@@ -649,7 +640,7 @@ var StreamStore = class {
649
640
  if (seen.has(value)) return value;
650
641
  seen.add(value);
651
642
  if (Array.isArray(value)) return value.map((item) => this.pvtEncode(item, seen));
652
- if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) return this.offerBytes(asOctets(value));
643
+ if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) return this.offerBytes(value);
653
644
  if (!isPlainObject(value)) return value;
654
645
  const copy = {};
655
646
  for (const [key, item] of Object.entries(value)) copy[key] = this.pvtEncode(item, seen);
@@ -737,7 +728,7 @@ var StreamStore = class {
737
728
  }
738
729
  pvtDecode(value, seen) {
739
730
  if (typeof value !== "object" || value === null) return value;
740
- if (isBytesRef(value)) return this.pvtValueFor(value.$bytes.id, "bytes", { size: value.$bytes.size });
731
+ if (isBytesRef(value)) return this.pvtValueFor(value.$bytes.id, "bytes", value.$bytes);
741
732
  if (isBlobRef(value)) {
742
733
  const { id, size, type, name } = value.$blob;
743
734
  return this.pvtValueFor(id, "blob", {
@@ -773,7 +764,6 @@ var StreamStore = class {
773
764
  }
774
765
  if (incoming.value) return incoming.value;
775
766
  incoming.form = form;
776
- const value = incoming.value = this.pvtBuild(incoming.reader, form, facts);
777
767
  if (facts !== void 0 && facts.size > this.maxBytes) {
778
768
  const reason = `stream ${id} is ${facts.size} bytes, over this side's bound of ${this.maxBytes}`;
779
769
  incoming.done = true;
@@ -784,8 +774,7 @@ var StreamStore = class {
784
774
  reason: "over the reader's bound"
785
775
  } });
786
776
  }
787
- if (value instanceof RemoteBytes) value.start();
788
- return value;
777
+ return incoming.value = this.pvtBuild(incoming.reader, form, facts);
789
778
  }
790
779
  /** The value a name the far side announced stands for — or one that says it was never announced. */
791
780
  pvtValueForNamed(name) {
@@ -800,10 +789,27 @@ var StreamStore = class {
800
789
  return failed;
801
790
  }
802
791
  pvtBuild(reader, form, facts) {
803
- if (form === "blob" && facts !== void 0) return new RemoteBlob(reader, facts);
804
- if (form === "bytes" && facts !== void 0) return new RemoteBytes(reader, facts.size);
792
+ if (facts === void 0) return reader;
793
+ if (form === "blob") return new RemoteBlob(reader, {
794
+ size: facts.size,
795
+ type: facts.type,
796
+ name: "name" in facts ? facts.name : void 0
797
+ });
798
+ if (form === "bytes") return this.pvtArray(reader, facts);
805
799
  return reader;
806
800
  }
801
+ /**
802
+ * The array the far side is sending: the kind it was, made here at the size its reference stated,
803
+ * and filled as the octets land. The fill loop is the stream's one consumer, so the credit
804
+ * machinery asks for exactly what can be stored and the array is complete before `settled` resolves.
805
+ */
806
+ pvtArray(reader, facts) {
807
+ const value = facts.size > this.maxBytes ? /* @__PURE__ */ new Uint8Array(0) : allocateBytes(facts.size, facts.type, "length" in facts ? facts.length : void 0);
808
+ const filling = fillArray(value, reader, facts.size);
809
+ filling.catch(() => {});
810
+ this.pvtPending.push(filling);
811
+ return value;
812
+ }
807
813
  pvtAsk(incoming) {
808
814
  if (incoming.done || this.pvtClosed) return;
809
815
  const outstanding = incoming.asked - incoming.consumed;
@@ -961,9 +967,70 @@ var StreamStore = class {
961
967
  }
962
968
  };
963
969
  /** A typed array or an `ArrayBuffer` as the octets the wire carries. */
964
- function asOctets(value) {
965
- if (ArrayBuffer.isView(value)) return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
966
- return new Uint8Array(value);
970
+ /** How many octets an element of each kind takes. A name that came off the wire is looked up here
971
+ * rather than handed to `globalThis`, so a peer cannot name a constructor this side would run. */
972
+ const TYPED_ARRAY_STRIDE = /* @__PURE__ */ new Map([
973
+ ["Int8Array", 1],
974
+ ["Uint8Array", 1],
975
+ ["Uint8ClampedArray", 1],
976
+ ["Int16Array", 2],
977
+ ["Uint16Array", 2],
978
+ ["Int32Array", 4],
979
+ ["Uint32Array", 4],
980
+ ["Float32Array", 4],
981
+ ["Float64Array", 8],
982
+ ["BigInt64Array", 8],
983
+ ["BigUint64Array", 8]
984
+ ]);
985
+ function isTypedArrayName(name) {
986
+ return TYPED_ARRAY_STRIDE.has(name);
987
+ }
988
+ /** The octets of a value about to cross — and, for a typed array, what they are made of. */
989
+ function octetsOf(value) {
990
+ if (!ArrayBuffer.isView(value)) return { bytes: new Uint8Array(value) };
991
+ const bytes = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
992
+ if (value instanceof DataView) return { bytes };
993
+ const name = Object.prototype.toString.call(value).slice(8, -1);
994
+ if (!isTypedArrayName(name)) return { bytes };
995
+ const stride = TYPED_ARRAY_STRIDE.get(name) ?? 1;
996
+ if (bytes.length % stride !== 0) return { bytes };
997
+ return {
998
+ bytes,
999
+ type: name,
1000
+ length: bytes.length / stride
1001
+ };
1002
+ }
1003
+ /** The sender's kind of array, made at the element count its reference stated. */
1004
+ function allocateBytes(size, type, length) {
1005
+ if (type === void 0 || !isTypedArrayName(type)) return new Uint8Array(size);
1006
+ const stride = TYPED_ARRAY_STRIDE.get(type) ?? 1;
1007
+ if (size % stride !== 0) return new Uint8Array(size);
1008
+ const count = length !== void 0 && length * stride === size ? length : size / stride;
1009
+ switch (type) {
1010
+ case "Int8Array": return new Int8Array(count);
1011
+ case "Uint8Array": return new Uint8Array(count);
1012
+ case "Uint8ClampedArray": return new Uint8ClampedArray(count);
1013
+ case "Int16Array": return new Int16Array(count);
1014
+ case "Uint16Array": return new Uint16Array(count);
1015
+ case "Int32Array": return new Int32Array(count);
1016
+ case "Uint32Array": return new Uint32Array(count);
1017
+ case "Float32Array": return new Float32Array(count);
1018
+ case "Float64Array": return new Float64Array(count);
1019
+ case "BigInt64Array": return new BigInt64Array(count);
1020
+ case "BigUint64Array": return new BigUint64Array(count);
1021
+ }
1022
+ }
1023
+ /** Fill the array as its octets arrive. A view of the same memory is how every element kind is
1024
+ * written the same way, and how a `BigInt64Array` is filled without mixing element types. */
1025
+ async function fillArray(value, body, size) {
1026
+ const octets = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
1027
+ let at = 0;
1028
+ for await (const chunk of body) {
1029
+ if (at + chunk.length > octets.length) throw new Error(`the array is longer than the ${size} bytes it stated`);
1030
+ octets.set(chunk, at);
1031
+ at += chunk.length;
1032
+ }
1033
+ if (at !== octets.length) throw new Error(`the array is short: ${at} of ${size} octets arrived`);
967
1034
  }
968
1035
  function reasonOf(error) {
969
1036
  return error instanceof Error ? error.message : String(error);
@@ -1067,10 +1134,14 @@ function readChunks(stream, onChunk, onEnd) {
1067
1134
  };
1068
1135
  stream.on("data", onData);
1069
1136
  stream.once("end", finish);
1137
+ stream.once("close", finish);
1138
+ stream.once("error", finish);
1070
1139
  return { stop() {
1071
1140
  stopped = true;
1072
1141
  stream.off("data", onData);
1073
1142
  stream.off("end", finish);
1143
+ stream.off("close", finish);
1144
+ stream.off("error", finish);
1074
1145
  } };
1075
1146
  }
1076
1147
  //#endregion