posipaki 0.45.1 → 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.1";
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-COXVfzR6.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;
@@ -650,7 +640,7 @@ var StreamStore = class {
650
640
  if (seen.has(value)) return value;
651
641
  seen.add(value);
652
642
  if (Array.isArray(value)) return value.map((item) => this.pvtEncode(item, seen));
653
- if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) return this.offerBytes(asOctets(value));
643
+ if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) return this.offerBytes(value);
654
644
  if (!isPlainObject(value)) return value;
655
645
  const copy = {};
656
646
  for (const [key, item] of Object.entries(value)) copy[key] = this.pvtEncode(item, seen);
@@ -738,7 +728,7 @@ var StreamStore = class {
738
728
  }
739
729
  pvtDecode(value, seen) {
740
730
  if (typeof value !== "object" || value === null) return value;
741
- 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);
742
732
  if (isBlobRef(value)) {
743
733
  const { id, size, type, name } = value.$blob;
744
734
  return this.pvtValueFor(id, "blob", {
@@ -774,7 +764,6 @@ var StreamStore = class {
774
764
  }
775
765
  if (incoming.value) return incoming.value;
776
766
  incoming.form = form;
777
- const value = incoming.value = this.pvtBuild(incoming.reader, form, facts);
778
767
  if (facts !== void 0 && facts.size > this.maxBytes) {
779
768
  const reason = `stream ${id} is ${facts.size} bytes, over this side's bound of ${this.maxBytes}`;
780
769
  incoming.done = true;
@@ -785,8 +774,7 @@ var StreamStore = class {
785
774
  reason: "over the reader's bound"
786
775
  } });
787
776
  }
788
- if (value instanceof RemoteBytes) value.start();
789
- return value;
777
+ return incoming.value = this.pvtBuild(incoming.reader, form, facts);
790
778
  }
791
779
  /** The value a name the far side announced stands for — or one that says it was never announced. */
792
780
  pvtValueForNamed(name) {
@@ -801,10 +789,27 @@ var StreamStore = class {
801
789
  return failed;
802
790
  }
803
791
  pvtBuild(reader, form, facts) {
804
- if (form === "blob" && facts !== void 0) return new RemoteBlob(reader, facts);
805
- 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);
806
799
  return reader;
807
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
+ }
808
813
  pvtAsk(incoming) {
809
814
  if (incoming.done || this.pvtClosed) return;
810
815
  const outstanding = incoming.asked - incoming.consumed;
@@ -962,9 +967,70 @@ var StreamStore = class {
962
967
  }
963
968
  };
964
969
  /** A typed array or an `ArrayBuffer` as the octets the wire carries. */
965
- function asOctets(value) {
966
- if (ArrayBuffer.isView(value)) return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
967
- 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`);
968
1034
  }
969
1035
  function reasonOf(error) {
970
1036
  return error instanceof Error ? error.message : String(error);