@gurezo/web-serial-rxjs 3.1.0 → 4.0.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 (42) hide show
  1. package/README.ja.md +4 -7
  2. package/README.md +4 -7
  3. package/dist/errors/serial-error-code.d.ts +0 -21
  4. package/dist/errors/serial-error-code.d.ts.map +1 -1
  5. package/dist/errors/serial-error-context.d.ts +1 -6
  6. package/dist/errors/serial-error-context.d.ts.map +1 -1
  7. package/dist/index.d.ts +6 -5
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.mjs +221 -427
  10. package/dist/index.mjs.map +4 -4
  11. package/dist/internal/branded-numbers.d.ts +1 -8
  12. package/dist/internal/branded-numbers.d.ts.map +1 -1
  13. package/dist/session/create-serial-session.d.ts +6 -4
  14. package/dist/session/create-serial-session.d.ts.map +1 -1
  15. package/dist/session/index.d.ts +2 -2
  16. package/dist/session/index.d.ts.map +1 -1
  17. package/dist/session/internal/error-severity.d.ts +0 -1
  18. package/dist/session/internal/error-severity.d.ts.map +1 -1
  19. package/dist/session/internal/has-web-serial-support.d.ts +6 -5
  20. package/dist/session/internal/has-web-serial-support.d.ts.map +1 -1
  21. package/dist/session/internal/line-buffer.d.ts +17 -0
  22. package/dist/session/internal/line-buffer.d.ts.map +1 -1
  23. package/dist/session/internal/port-teardown.d.ts +30 -0
  24. package/dist/session/internal/port-teardown.d.ts.map +1 -0
  25. package/dist/session/internal/receive-pipeline.d.ts +8 -5
  26. package/dist/session/internal/receive-pipeline.d.ts.map +1 -1
  27. package/dist/session/internal/session-error-reporter.d.ts +0 -1
  28. package/dist/session/internal/session-error-reporter.d.ts.map +1 -1
  29. package/dist/session/internal/session-lifecycle.d.ts +3 -5
  30. package/dist/session/internal/session-lifecycle.d.ts.map +1 -1
  31. package/dist/session/serial-session-options.d.ts +62 -94
  32. package/dist/session/serial-session-options.d.ts.map +1 -1
  33. package/dist/session/serial-session.d.ts +9 -75
  34. package/dist/session/serial-session.d.ts.map +1 -1
  35. package/dist/session/session-runtime.d.ts.map +1 -1
  36. package/dist/terminal/create-terminal-buffer.d.ts +24 -1
  37. package/dist/terminal/create-terminal-buffer.d.ts.map +1 -1
  38. package/dist/types.d.ts +5 -0
  39. package/dist/types.d.ts.map +1 -1
  40. package/package.json +2 -2
  41. package/dist/session/internal/receive-replay-buffer.d.ts +0 -38
  42. package/dist/session/internal/receive-replay-buffer.d.ts.map +0 -1
package/dist/index.mjs CHANGED
@@ -4,13 +4,7 @@ function assertNever(value) {
4
4
  }
5
5
 
6
6
  // src/session/create-serial-session.ts
7
- import {
8
- BehaviorSubject as BehaviorSubject3,
9
- distinctUntilChanged,
10
- map as map2,
11
- Observable as Observable5,
12
- Subject as Subject3
13
- } from "rxjs";
7
+ import { Observable as Observable4, Subject as Subject2 } from "rxjs";
14
8
 
15
9
  // src/errors/serial-error-code.ts
16
10
  var SerialErrorCode = {
@@ -134,16 +128,6 @@ var SerialErrorCode = {
134
128
  * or ensure the device sends line terminators.
135
129
  */
136
130
  LINE_BUFFER_OVERFLOW: "LINE_BUFFER_OVERFLOW",
137
- /**
138
- * Invalid receive replay options provided.
139
- *
140
- * This error occurs when {@link SerialSessionOptions.receiveReplay} values are
141
- * out of range, such as a non-integer `bufferSize` or `maxChars`.
142
- *
143
- * **Suggested action**: Verify `receiveReplay` options match the documented
144
- * ranges and value types.
145
- */
146
- INVALID_RECEIVE_REPLAY_OPTIONS: "INVALID_RECEIVE_REPLAY_OPTIONS",
147
131
  /**
148
132
  * Invalid terminal buffer options provided.
149
133
  *
@@ -174,17 +158,6 @@ var SerialErrorCode = {
174
158
  * ranges and value types.
175
159
  */
176
160
  INVALID_CONNECTION_OPTIONS: "INVALID_CONNECTION_OPTIONS",
177
- /**
178
- * Receive replay buffer exceeded its configured character limit.
179
- *
180
- * This error occurs when buffered chunks on {@link SerialSession.receiveReplay$}
181
- * exceed {@link SerialSessionOptions.receiveReplay} `maxChars`. Oldest chunks
182
- * are discarded to bound memory; the session remains connected.
183
- *
184
- * **Suggested action**: Increase `receiveReplay.maxChars`, reduce chunk size at
185
- * the source, or subscribe earlier to avoid relying on a large replay buffer.
186
- */
187
- RECEIVE_REPLAY_BUFFER_OVERFLOW: "RECEIVE_REPLAY_BUFFER_OVERFLOW",
188
161
  /**
189
162
  * Session has been disposed and can no longer be used.
190
163
  *
@@ -211,6 +184,83 @@ var SerialErrorCode = {
211
184
  // src/terminal/create-terminal-buffer.ts
212
185
  import { map, scan, shareReplay } from "rxjs";
213
186
 
187
+ // src/errors/serial-error-context.ts
188
+ var CAUSE_CONTEXT_CODES = /* @__PURE__ */ new Set([
189
+ SerialErrorCode.PORT_NOT_AVAILABLE,
190
+ SerialErrorCode.PORT_OPEN_FAILED,
191
+ SerialErrorCode.READ_FAILED,
192
+ SerialErrorCode.WRITE_FAILED,
193
+ SerialErrorCode.CONNECTION_LOST,
194
+ SerialErrorCode.OPERATION_CANCELLED,
195
+ SerialErrorCode.UNKNOWN
196
+ ]);
197
+ function isCauseContextCode(code) {
198
+ return CAUSE_CONTEXT_CODES.has(code);
199
+ }
200
+
201
+ // src/errors/serial-error.ts
202
+ var ErrorWithCaptureStackTrace = Error;
203
+ var SerialError = class _SerialError extends Error {
204
+ /**
205
+ * Creates a new SerialError instance.
206
+ *
207
+ * @param code - The error code identifying the type of error
208
+ * @param message - A human-readable error message
209
+ * @param originalError - The original error that caused this SerialError, if any.
210
+ * @deprecated Prefer passing `{ cause }` via {@link context}. When omitted,
211
+ * cause-bearing codes derive `{ cause }` from this argument for backward
212
+ * compatibility.
213
+ * @param context - Structured metadata for the error code. For cause-bearing
214
+ * codes, pass `{ cause }` here. When omitted, cause-bearing codes derive
215
+ * `{ cause }` from the legacy {@link originalError} argument.
216
+ */
217
+ constructor(code, message, originalError, context) {
218
+ super(message);
219
+ this.name = "SerialError";
220
+ this.code = code;
221
+ if (context !== void 0) {
222
+ this.context = context;
223
+ } else if (originalError !== void 0 && isCauseContextCode(code)) {
224
+ this.context = { cause: originalError };
225
+ } else {
226
+ this.context = void 0;
227
+ }
228
+ if (originalError !== void 0) {
229
+ this.originalError = originalError;
230
+ } else if (isCauseContextCode(code) && this.context !== void 0) {
231
+ const causeContext = this.context;
232
+ if (causeContext.cause instanceof Error) {
233
+ this.originalError = causeContext.cause;
234
+ }
235
+ }
236
+ if (ErrorWithCaptureStackTrace.captureStackTrace) {
237
+ ErrorWithCaptureStackTrace.captureStackTrace(this, _SerialError);
238
+ }
239
+ }
240
+ /**
241
+ * Check if the error matches a specific error code.
242
+ *
243
+ * This is a convenience method for checking the error code without directly
244
+ * comparing the code property. When this method returns `true`, TypeScript
245
+ * narrows `this.code` and `this.context` to the shapes defined for the
246
+ * provided `code` argument.
247
+ *
248
+ * @param code - The error code to check against
249
+ * @returns Type predicate: `true` if this error's code matches the provided
250
+ * code (and `this.code` / `this.context` are narrowed), `false` otherwise
251
+ *
252
+ * @example
253
+ * ```typescript
254
+ * if (error.is(SerialErrorCode.LINE_BUFFER_OVERFLOW)) {
255
+ * // error.code and error.context.maxChars are narrowed
256
+ * }
257
+ * ```
258
+ */
259
+ is(code) {
260
+ return this.code === code;
261
+ }
262
+ };
263
+
214
264
  // src/internal/branded-numbers.ts
215
265
  function brandBaudRate(value) {
216
266
  return value;
@@ -218,9 +268,6 @@ function brandBaudRate(value) {
218
268
  function brandSerialPortBufferSize(value) {
219
269
  return value;
220
270
  }
221
- function brandReceiveReplayBufferSize(value) {
222
- return value;
223
- }
224
271
  function brandMaxChars(value) {
225
272
  return value;
226
273
  }
@@ -468,12 +515,40 @@ var DEFAULT_TERMINAL_BUFFER_OPTIONS = {
468
515
  maxChars: 1048576,
469
516
  stripAnsi: true
470
517
  };
471
- function resolveTerminalBufferLimits(options) {
472
- const maxLines = options?.maxLines ?? DEFAULT_TERMINAL_BUFFER_OPTIONS.maxLines;
473
- const maxChars = options?.maxChars ?? DEFAULT_TERMINAL_BUFFER_OPTIONS.maxChars;
518
+ function resolveTerminalBufferOptions(options) {
519
+ const merged = {
520
+ ...DEFAULT_TERMINAL_BUFFER_OPTIONS,
521
+ ...options
522
+ };
523
+ const { maxLines, maxChars } = merged;
524
+ if (!Number.isSafeInteger(maxLines) || maxLines < 0) {
525
+ throw new SerialError(
526
+ SerialErrorCode.INVALID_TERMINAL_BUFFER_OPTIONS,
527
+ `Invalid terminalBuffer.maxLines: ${maxLines}. Must be a safe integer >= 0.`,
528
+ void 0,
529
+ {
530
+ field: "terminalBuffer.maxLines",
531
+ value: maxLines,
532
+ constraint: "non-negative-safe-integer"
533
+ }
534
+ );
535
+ }
536
+ if (!Number.isSafeInteger(maxChars) || maxChars < 0) {
537
+ throw new SerialError(
538
+ SerialErrorCode.INVALID_TERMINAL_BUFFER_OPTIONS,
539
+ `Invalid terminalBuffer.maxChars: ${maxChars}. Must be a safe integer >= 0.`,
540
+ void 0,
541
+ {
542
+ field: "terminalBuffer.maxChars",
543
+ value: maxChars,
544
+ constraint: "non-negative-safe-integer"
545
+ }
546
+ );
547
+ }
474
548
  return {
475
549
  maxLines: brandMaxLines(maxLines),
476
- maxChars: brandMaxChars(maxChars)
550
+ maxChars: brandMaxChars(maxChars),
551
+ stripAnsi: merged.stripAnsi
477
552
  };
478
553
  }
479
554
  var initialTerminalState = {
@@ -481,9 +556,12 @@ var initialTerminalState = {
481
556
  currentLine: ""
482
557
  };
483
558
  function createTerminalBuffer(receive$, options) {
484
- const limits = resolveTerminalBufferLimits(options);
485
- const stripAnsi = options?.stripAnsi ?? DEFAULT_TERMINAL_BUFFER_OPTIONS.stripAnsi;
486
- const ansiStripper = stripAnsi ? createAnsiStripper() : null;
559
+ const resolved = resolveTerminalBufferOptions(options);
560
+ const limits = {
561
+ maxLines: resolved.maxLines,
562
+ maxChars: resolved.maxChars
563
+ };
564
+ const ansiStripper = resolved.stripAnsi ? createAnsiStripper() : null;
487
565
  const text$ = receive$.pipe(
488
566
  scan((state, chunk) => {
489
567
  const normalized = ansiStripper !== null ? ansiStripper.feed(chunk) : chunk;
@@ -497,105 +575,62 @@ function createTerminalBuffer(receive$, options) {
497
575
  }
498
576
 
499
577
  // src/session/internal/has-web-serial-support.ts
500
- function hasWebSerialSupport() {
578
+ function isWebSerialSupported() {
501
579
  return typeof navigator !== "undefined" && "serial" in navigator && navigator.serial !== void 0 && navigator.serial !== null;
502
580
  }
503
581
 
504
- // src/session/internal/receive-pipeline.ts
505
- import {
506
- BehaviorSubject,
507
- share,
508
- Subject as Subject2,
509
- switchMap
510
- } from "rxjs";
511
-
512
- // src/errors/serial-error-context.ts
513
- var CAUSE_CONTEXT_CODES = /* @__PURE__ */ new Set([
514
- SerialErrorCode.PORT_NOT_AVAILABLE,
515
- SerialErrorCode.PORT_OPEN_FAILED,
516
- SerialErrorCode.READ_FAILED,
517
- SerialErrorCode.WRITE_FAILED,
518
- SerialErrorCode.CONNECTION_LOST,
519
- SerialErrorCode.OPERATION_CANCELLED,
520
- SerialErrorCode.UNKNOWN
521
- ]);
522
- function isCauseContextCode(code) {
523
- return CAUSE_CONTEXT_CODES.has(code);
524
- }
525
-
526
- // src/errors/serial-error.ts
527
- var ErrorWithCaptureStackTrace = Error;
528
- var SerialError = class _SerialError extends Error {
529
- /**
530
- * Creates a new SerialError instance.
531
- *
532
- * @param code - The error code identifying the type of error
533
- * @param message - A human-readable error message
534
- * @param originalError - The original error that caused this SerialError, if any.
535
- * @deprecated Prefer passing `{ cause }` via {@link context}. When omitted,
536
- * cause-bearing codes derive `{ cause }` from this argument for backward
537
- * compatibility.
538
- * @param context - Structured metadata for the error code. For cause-bearing
539
- * codes, pass `{ cause }` here. When omitted, cause-bearing codes derive
540
- * `{ cause }` from the legacy {@link originalError} argument.
541
- */
542
- constructor(code, message, originalError, context) {
543
- super(message);
544
- this.name = "SerialError";
545
- this.code = code;
546
- if (context !== void 0) {
547
- this.context = context;
548
- } else if (originalError !== void 0 && isCauseContextCode(code)) {
549
- this.context = { cause: originalError };
550
- } else {
551
- this.context = void 0;
582
+ // src/session/internal/port-teardown.ts
583
+ function createPortTeardown(deps) {
584
+ const { receivePipeline } = deps;
585
+ const teardownPump = async (pump) => {
586
+ receivePipeline.clearLineBuffer();
587
+ if (pump) {
588
+ await pump.stop();
552
589
  }
553
- if (originalError !== void 0) {
554
- this.originalError = originalError;
555
- } else if (isCauseContextCode(code) && this.context !== void 0) {
556
- const causeContext = this.context;
557
- if (causeContext.cause instanceof Error) {
558
- this.originalError = causeContext.cause;
559
- }
590
+ };
591
+ const closePortSafely = async (port) => {
592
+ if (!port) {
593
+ return;
560
594
  }
561
- if (ErrorWithCaptureStackTrace.captureStackTrace) {
562
- ErrorWithCaptureStackTrace.captureStackTrace(this, _SerialError);
595
+ try {
596
+ await port.close();
597
+ } catch {
563
598
  }
564
- }
565
- /**
566
- * Check if the error matches a specific error code.
567
- *
568
- * This is a convenience method for checking the error code without directly
569
- * comparing the code property. When this method returns `true`, TypeScript
570
- * narrows `this.code` and `this.context` to the shapes defined for the
571
- * provided `code` argument.
572
- *
573
- * @param code - The error code to check against
574
- * @returns Type predicate: `true` if this error's code matches the provided
575
- * code (and `this.code` / `this.context` are narrowed), `false` otherwise
576
- *
577
- * @example
578
- * ```typescript
579
- * if (error.is(SerialErrorCode.LINE_BUFFER_OVERFLOW)) {
580
- * // error.code and error.context.maxChars are narrowed
581
- * }
582
- * ```
583
- */
584
- is(code) {
585
- return this.code === code;
586
- }
587
- };
599
+ };
600
+ return { teardownPump, closePortSafely };
601
+ }
602
+
603
+ // src/session/internal/receive-pipeline.ts
604
+ import { Subject } from "rxjs";
588
605
 
589
606
  // src/session/internal/line-buffer.ts
590
607
  var DEFAULT_LINE_BUFFER_OPTIONS = {
591
608
  maxChars: 1048576
592
609
  };
593
- function createLineBuffer(options) {
594
- const limits = {
595
- maxChars: brandMaxChars(
596
- options?.maxChars ?? DEFAULT_LINE_BUFFER_OPTIONS.maxChars
597
- )
610
+ function resolveLineBufferOptions(options) {
611
+ const merged = {
612
+ ...DEFAULT_LINE_BUFFER_OPTIONS,
613
+ ...options
598
614
  };
615
+ const { maxChars } = merged;
616
+ if (!Number.isSafeInteger(maxChars) || maxChars < 0) {
617
+ throw new SerialError(
618
+ SerialErrorCode.INVALID_LINE_BUFFER_OPTIONS,
619
+ `Invalid lineBuffer.maxChars: ${maxChars}. Must be a safe integer >= 0.`,
620
+ void 0,
621
+ {
622
+ field: "lineBuffer.maxChars",
623
+ value: maxChars,
624
+ constraint: "non-negative-safe-integer"
625
+ }
626
+ );
627
+ }
628
+ return {
629
+ maxChars: brandMaxChars(maxChars)
630
+ };
631
+ }
632
+ function createLineBuffer(options) {
633
+ const limits = resolveLineBufferOptions(options);
599
634
  const tokenizer = createNewlineTokenizer("line");
600
635
  const clear = () => {
601
636
  tokenizer.clear();
@@ -614,129 +649,30 @@ function createLineBuffer(options) {
614
649
  return { feed, clear };
615
650
  }
616
651
 
617
- // src/session/internal/receive-replay-buffer.ts
618
- import { Observable, Subject } from "rxjs";
619
- function totalChars(chunks) {
620
- return chunks.reduce((sum, chunk) => sum + chunk.length, 0);
621
- }
622
- function createReceiveReplayBuffer(options) {
623
- const chunks = [];
624
- const live$ = new Subject();
625
- let completed = false;
626
- const trim = () => {
627
- let overflowed = false;
628
- while (chunks.length > options.bufferSize) {
629
- chunks.shift();
630
- overflowed = true;
631
- }
632
- if (options.maxChars > 0) {
633
- let chars = totalChars(chunks);
634
- while (chars > options.maxChars && chunks.length > 1) {
635
- chars -= chunks.shift().length;
636
- overflowed = true;
637
- }
638
- }
639
- return overflowed;
640
- };
641
- const next = (chunk) => {
642
- if (completed) {
643
- return { overflowed: false };
644
- }
645
- chunks.push(chunk);
646
- const overflowed = trim();
647
- live$.next(chunk);
648
- return { overflowed };
649
- };
650
- const asObservable = () => new Observable((subscriber) => {
651
- for (const chunk of chunks) {
652
- subscriber.next(chunk);
653
- }
654
- if (completed) {
655
- subscriber.complete();
656
- return;
657
- }
658
- return live$.subscribe(subscriber);
659
- });
660
- const complete = () => {
661
- if (completed) {
662
- return;
663
- }
664
- completed = true;
665
- chunks.length = 0;
666
- live$.complete();
667
- };
668
- return { next, asObservable, complete };
669
- }
670
-
671
652
  // src/session/internal/receive-pipeline.ts
672
653
  function createReceivePipeline(deps) {
673
- const { resolvedOptions, reportError } = deps;
674
- const receiveSubject = new Subject2();
675
- const linesSubject = new Subject2();
654
+ const { resolvedOptions } = deps;
655
+ const receiveSubject = new Subject();
656
+ const linesSubject = new Subject();
657
+ const bufferErrorsSubject = new Subject();
676
658
  const lineBuffer = createLineBuffer(resolvedOptions.lineBuffer);
677
659
  const receive$ = receiveSubject.asObservable();
678
660
  const lines$ = linesSubject.asObservable();
679
- const receiveReplayStream$ = resolvedOptions.receiveReplay.enabled ? new BehaviorSubject(receive$) : null;
680
- let activeReceiveReplay = null;
681
- const clearReplay = () => {
682
- if (receiveReplayStream$) {
683
- if (activeReceiveReplay) {
684
- activeReceiveReplay.complete();
685
- activeReceiveReplay = null;
686
- }
687
- receiveReplayStream$.next(receive$);
688
- }
689
- };
690
- const startLiveReceiveReplay = () => {
691
- if (!receiveReplayStream$) {
692
- return;
693
- }
694
- if (activeReceiveReplay) {
695
- activeReceiveReplay.complete();
696
- activeReceiveReplay = null;
697
- }
698
- const buffer = createReceiveReplayBuffer({
699
- bufferSize: resolvedOptions.receiveReplay.bufferSize,
700
- maxChars: resolvedOptions.receiveReplay.maxChars
701
- });
702
- activeReceiveReplay = buffer;
703
- receiveReplayStream$.next(buffer.asObservable());
704
- };
705
- const receiveReplay$ = receiveReplayStream$ ? receiveReplayStream$.pipe(switchMap((inner) => inner), share()) : receive$;
661
+ const bufferErrors$ = bufferErrorsSubject.asObservable();
706
662
  const clearLineBuffer = () => {
707
663
  lineBuffer.clear();
708
664
  };
709
665
  const handleChunk = (text) => {
710
666
  receiveSubject.next(text);
711
- if (activeReceiveReplay) {
712
- const { overflowed: overflowed2 } = activeReceiveReplay.next(text);
713
- if (overflowed2) {
714
- reportError(
715
- new SerialError(
716
- SerialErrorCode.RECEIVE_REPLAY_BUFFER_OVERFLOW,
717
- "Receive replay buffer exceeded configured limits; oldest chunks were discarded",
718
- void 0,
719
- {
720
- maxChars: resolvedOptions.receiveReplay.maxChars,
721
- bufferSize: resolvedOptions.receiveReplay.bufferSize
722
- }
723
- ),
724
- {
725
- fallbackCode: SerialErrorCode.RECEIVE_REPLAY_BUFFER_OVERFLOW
726
- }
727
- );
728
- }
729
- }
730
667
  const { lines, overflowed } = lineBuffer.feed(text);
731
668
  if (overflowed) {
732
- reportError(
669
+ bufferErrorsSubject.next(
733
670
  new SerialError(
734
671
  SerialErrorCode.LINE_BUFFER_OVERFLOW,
735
672
  "Line buffer exceeded maxChars; leading data was discarded",
736
673
  void 0,
737
674
  { maxChars: resolvedOptions.lineBuffer.maxChars }
738
- ),
739
- { fallbackCode: SerialErrorCode.LINE_BUFFER_OVERFLOW }
675
+ )
740
676
  );
741
677
  }
742
678
  for (const line of lines) {
@@ -746,14 +682,12 @@ function createReceivePipeline(deps) {
746
682
  const complete = () => {
747
683
  receiveSubject.complete();
748
684
  linesSubject.complete();
749
- receiveReplayStream$?.complete();
685
+ bufferErrorsSubject.complete();
750
686
  };
751
687
  return {
752
688
  receive$,
753
689
  lines$,
754
- receiveReplay$,
755
- clearReplay,
756
- startLiveReceiveReplay,
690
+ bufferErrors$,
757
691
  clearLineBuffer,
758
692
  handleChunk,
759
693
  complete
@@ -768,7 +702,6 @@ var ERROR_SEVERITY = {
768
702
  [SerialErrorCode.OPERATION_CANCELLED]: "fatal",
769
703
  [SerialErrorCode.UNKNOWN]: "fatal",
770
704
  [SerialErrorCode.LINE_BUFFER_OVERFLOW]: "non-fatal",
771
- [SerialErrorCode.RECEIVE_REPLAY_BUFFER_OVERFLOW]: "non-fatal",
772
705
  [SerialErrorCode.BROWSER_NOT_SUPPORTED]: "non-fatal",
773
706
  [SerialErrorCode.PORT_ALREADY_OPEN]: "non-fatal",
774
707
  [SerialErrorCode.PORT_NOT_OPEN]: "non-fatal",
@@ -807,7 +740,7 @@ function normalizeSerialError(error, options) {
807
740
  }
808
741
 
809
742
  // src/session/session-runtime.ts
810
- import { BehaviorSubject as BehaviorSubject2 } from "rxjs";
743
+ import { BehaviorSubject } from "rxjs";
811
744
 
812
745
  // src/session/serial-session-state.ts
813
746
  var SerialSessionStatus = {
@@ -904,7 +837,7 @@ function getRuntimePump(runtime) {
904
837
  }
905
838
  function createSessionRuntimeController(initial) {
906
839
  let runtime = initial;
907
- const subject = new BehaviorSubject2(
840
+ const subject = new BehaviorSubject(
908
841
  runtimeToPublicState(initial)
909
842
  );
910
843
  const transition = (next) => {
@@ -921,8 +854,9 @@ function createSessionRuntimeController(initial) {
921
854
  }
922
855
  return false;
923
856
  }
857
+ const publicState = runtimeToPublicState(next);
924
858
  runtime = next;
925
- subject.next(runtimeToPublicState(next));
859
+ subject.next(publicState);
926
860
  return true;
927
861
  };
928
862
  return {
@@ -952,7 +886,6 @@ function createSessionErrorReporter(deps) {
952
886
  errorsSubject,
953
887
  sendQueue,
954
888
  isDisposed,
955
- updatePortInfo,
956
889
  teardownPump,
957
890
  closePortSafely
958
891
  } = deps;
@@ -972,7 +905,6 @@ function createSessionErrorReporter(deps) {
972
905
  const pump = getRuntimePump(runtime);
973
906
  controller.transition(createErrorRuntime(serialError));
974
907
  sendQueue.clear();
975
- updatePortInfo(null);
976
908
  void teardownPump(pump).then(() => closePortSafely(portToClose));
977
909
  }
978
910
  return serialError;
@@ -981,7 +913,7 @@ function createSessionErrorReporter(deps) {
981
913
  }
982
914
 
983
915
  // src/session/internal/session-lifecycle.ts
984
- import { Observable as Observable3 } from "rxjs";
916
+ import { Observable as Observable2 } from "rxjs";
985
917
 
986
918
  // src/session/internal/build-request-options.ts
987
919
  function buildRequestOptions(options) {
@@ -1094,31 +1026,13 @@ function createSessionLifecycle(deps) {
1094
1026
  resolvedOptions,
1095
1027
  sendQueue,
1096
1028
  receivePipeline,
1097
- portInfoSubject,
1098
1029
  errorsSubject,
1099
1030
  isDisposed,
1031
+ teardownPump,
1032
+ closePortSafely,
1100
1033
  reportError,
1101
1034
  createDisposedError
1102
1035
  } = deps;
1103
- const updatePortInfo = (port) => {
1104
- portInfoSubject.next(port ? port.getInfo() : null);
1105
- };
1106
- const teardownPump = async (pump) => {
1107
- receivePipeline.clearReplay();
1108
- receivePipeline.clearLineBuffer();
1109
- if (pump) {
1110
- await pump.stop();
1111
- }
1112
- };
1113
- const closePortSafely = async (port) => {
1114
- if (!port) {
1115
- return;
1116
- }
1117
- try {
1118
- await port.close();
1119
- } catch {
1120
- }
1121
- };
1122
1036
  const teardownFromSnapshot = async (snapshot) => {
1123
1037
  if (snapshot.status === SerialSessionStatus.Connecting) {
1124
1038
  snapshot.cancel();
@@ -1129,7 +1043,6 @@ function createSessionLifecycle(deps) {
1129
1043
  const pump = getRuntimePump(snapshot);
1130
1044
  await teardownPump(pump);
1131
1045
  await closePortSafely(portToClose);
1132
- updatePortInfo(null);
1133
1046
  }
1134
1047
  receivePipeline.clearLineBuffer();
1135
1048
  };
@@ -1137,9 +1050,8 @@ function createSessionLifecycle(deps) {
1137
1050
  controller.complete();
1138
1051
  errorsSubject.complete();
1139
1052
  receivePipeline.complete();
1140
- portInfoSubject.complete();
1141
1053
  };
1142
- const dispose$ = () => new Observable3((subscriber) => {
1054
+ const dispose$ = () => new Observable2((subscriber) => {
1143
1055
  if (isDisposed()) {
1144
1056
  subscriber.next();
1145
1057
  subscriber.complete();
@@ -1181,12 +1093,12 @@ function createSessionLifecycle(deps) {
1181
1093
  }
1182
1094
  }
1183
1095
  };
1184
- const connect$ = () => new Observable3((subscriber) => {
1096
+ const connect$ = () => new Observable2((subscriber) => {
1185
1097
  if (isDisposed()) {
1186
1098
  subscriber.error(createDisposedError());
1187
1099
  return;
1188
1100
  }
1189
- if (!hasWebSerialSupport()) {
1101
+ if (!isWebSerialSupported()) {
1190
1102
  const error = reportError(
1191
1103
  new SerialError(
1192
1104
  SerialErrorCode.BROWSER_NOT_SUPPORTED,
@@ -1249,9 +1161,6 @@ function createSessionLifecycle(deps) {
1249
1161
  return;
1250
1162
  }
1251
1163
  receivePipeline.clearLineBuffer();
1252
- if (resolvedOptions.receiveReplay.enabled) {
1253
- receivePipeline.startLiveReceiveReplay();
1254
- }
1255
1164
  const pump = createReadPump(selectedPort, {
1256
1165
  onChunk: receivePipeline.handleChunk,
1257
1166
  onError: (pumpError) => reportError(pumpError, {
@@ -1281,7 +1190,6 @@ function createSessionLifecycle(deps) {
1281
1190
  return;
1282
1191
  }
1283
1192
  controller.transition(createConnectedRuntime(selectedPort, pump));
1284
- updatePortInfo(selectedPort);
1285
1193
  subscriber.next();
1286
1194
  subscriber.complete();
1287
1195
  };
@@ -1290,7 +1198,7 @@ function createSessionLifecycle(deps) {
1290
1198
  cancelInFlightConnect();
1291
1199
  };
1292
1200
  });
1293
- const disconnect$ = () => new Observable3((subscriber) => {
1201
+ const disconnect$ = () => new Observable2((subscriber) => {
1294
1202
  if (isDisposed()) {
1295
1203
  subscriber.next();
1296
1204
  subscriber.complete();
@@ -1330,7 +1238,6 @@ function createSessionLifecycle(deps) {
1330
1238
  try {
1331
1239
  await portToClose.close();
1332
1240
  } catch (error) {
1333
- updatePortInfo(null);
1334
1241
  const serialError = reportError(error, {
1335
1242
  fallbackCode: SerialErrorCode.CONNECTION_LOST,
1336
1243
  messagePrefix: "Failed to close port"
@@ -1339,7 +1246,6 @@ function createSessionLifecycle(deps) {
1339
1246
  return;
1340
1247
  }
1341
1248
  }
1342
- updatePortInfo(null);
1343
1249
  if (!isDisposed()) {
1344
1250
  controller.transition(createIdleRuntime());
1345
1251
  }
@@ -1359,21 +1265,18 @@ function createSessionLifecycle(deps) {
1359
1265
  connect$,
1360
1266
  disconnect$,
1361
1267
  dispose$,
1362
- writeToPort,
1363
- teardownPump,
1364
- closePortSafely,
1365
- updatePortInfo
1268
+ writeToPort
1366
1269
  };
1367
1270
  }
1368
1271
 
1369
1272
  // src/session/send-queue.ts
1370
- import { Observable as Observable4, defer } from "rxjs";
1273
+ import { Observable as Observable3, defer } from "rxjs";
1371
1274
  function createSendQueue() {
1372
1275
  let chain = Promise.resolve();
1373
1276
  return {
1374
1277
  enqueue(operation) {
1375
1278
  return defer(
1376
- () => new Observable4((subscriber) => {
1279
+ () => new Observable3((subscriber) => {
1377
1280
  let cancelled = false;
1378
1281
  const run = async () => {
1379
1282
  try {
@@ -1463,107 +1366,6 @@ function validateSerialPortFilters(filters) {
1463
1366
  }
1464
1367
 
1465
1368
  // src/session/serial-session-options.ts
1466
- var MAX_RECEIVE_REPLAY_BUFFER_SIZE = 65536;
1467
- var MAX_RECEIVE_REPLAY_MAX_CHARS = 1048576;
1468
- var DEFAULT_RECEIVE_REPLAY = {
1469
- enabled: false,
1470
- bufferSize: 512,
1471
- maxChars: 0
1472
- };
1473
- function resolveReceiveReplayOptions(options) {
1474
- const merged = {
1475
- ...DEFAULT_RECEIVE_REPLAY,
1476
- ...options
1477
- };
1478
- const { bufferSize, maxChars } = merged;
1479
- if (!Number.isSafeInteger(bufferSize) || bufferSize < 1 || bufferSize > MAX_RECEIVE_REPLAY_BUFFER_SIZE) {
1480
- throw new SerialError(
1481
- SerialErrorCode.INVALID_RECEIVE_REPLAY_OPTIONS,
1482
- `Invalid receiveReplay.bufferSize: ${bufferSize}. Must be a safe integer between 1 and ${MAX_RECEIVE_REPLAY_BUFFER_SIZE}.`,
1483
- void 0,
1484
- {
1485
- field: "receiveReplay.bufferSize",
1486
- value: bufferSize,
1487
- constraint: "receive-replay-buffer-size-range"
1488
- }
1489
- );
1490
- }
1491
- if (!Number.isSafeInteger(maxChars) || maxChars < 0 || maxChars > MAX_RECEIVE_REPLAY_MAX_CHARS) {
1492
- throw new SerialError(
1493
- SerialErrorCode.INVALID_RECEIVE_REPLAY_OPTIONS,
1494
- `Invalid receiveReplay.maxChars: ${maxChars}. Must be a safe integer between 0 and ${MAX_RECEIVE_REPLAY_MAX_CHARS}.`,
1495
- void 0,
1496
- {
1497
- field: "receiveReplay.maxChars",
1498
- value: maxChars,
1499
- constraint: "receive-replay-max-chars-range"
1500
- }
1501
- );
1502
- }
1503
- return {
1504
- enabled: merged.enabled,
1505
- bufferSize: brandReceiveReplayBufferSize(bufferSize),
1506
- maxChars: brandMaxChars(maxChars)
1507
- };
1508
- }
1509
- function resolveTerminalBufferOptions(options) {
1510
- const merged = {
1511
- ...DEFAULT_TERMINAL_BUFFER_OPTIONS,
1512
- ...options
1513
- };
1514
- const { maxLines, maxChars } = merged;
1515
- if (!Number.isSafeInteger(maxLines) || maxLines < 0) {
1516
- throw new SerialError(
1517
- SerialErrorCode.INVALID_TERMINAL_BUFFER_OPTIONS,
1518
- `Invalid terminalBuffer.maxLines: ${maxLines}. Must be a safe integer >= 0.`,
1519
- void 0,
1520
- {
1521
- field: "terminalBuffer.maxLines",
1522
- value: maxLines,
1523
- constraint: "non-negative-safe-integer"
1524
- }
1525
- );
1526
- }
1527
- if (!Number.isSafeInteger(maxChars) || maxChars < 0) {
1528
- throw new SerialError(
1529
- SerialErrorCode.INVALID_TERMINAL_BUFFER_OPTIONS,
1530
- `Invalid terminalBuffer.maxChars: ${maxChars}. Must be a safe integer >= 0.`,
1531
- void 0,
1532
- {
1533
- field: "terminalBuffer.maxChars",
1534
- value: maxChars,
1535
- constraint: "non-negative-safe-integer"
1536
- }
1537
- );
1538
- }
1539
- return {
1540
- maxLines: brandMaxLines(maxLines),
1541
- maxChars: brandMaxChars(maxChars),
1542
- stripAnsi: merged.stripAnsi
1543
- };
1544
- }
1545
- function resolveLineBufferOptions(options) {
1546
- const merged = {
1547
- ...DEFAULT_LINE_BUFFER_OPTIONS,
1548
- ...options
1549
- };
1550
- const { maxChars } = merged;
1551
- if (!Number.isSafeInteger(maxChars) || maxChars < 0) {
1552
- throw new SerialError(
1553
- SerialErrorCode.INVALID_LINE_BUFFER_OPTIONS,
1554
- `Invalid lineBuffer.maxChars: ${maxChars}. Must be a safe integer >= 0.`,
1555
- void 0,
1556
- {
1557
- field: "lineBuffer.maxChars",
1558
- value: maxChars,
1559
- constraint: "non-negative-safe-integer"
1560
- }
1561
- );
1562
- }
1563
- return {
1564
- maxChars: brandMaxChars(maxChars)
1565
- };
1566
- }
1567
1369
  var DEFAULT_SERIAL_SESSION_OPTIONS = {
1568
1370
  baudRate: brandBaudRate(9600),
1569
1371
  dataBits: 8,
@@ -1571,7 +1373,6 @@ var DEFAULT_SERIAL_SESSION_OPTIONS = {
1571
1373
  parity: "none",
1572
1374
  bufferSize: brandSerialPortBufferSize(255),
1573
1375
  flowControl: "none",
1574
- receiveReplay: resolveReceiveReplayOptions(),
1575
1376
  terminalBuffer: resolveTerminalBufferOptions(),
1576
1377
  lineBuffer: resolveLineBufferOptions()
1577
1378
  };
@@ -1585,7 +1386,8 @@ function resolveConnectionOptions(options) {
1585
1386
  flowControl: DEFAULT_SERIAL_SESSION_OPTIONS.flowControl,
1586
1387
  ...options
1587
1388
  };
1588
- const { baudRate, bufferSize } = merged;
1389
+ const baudRate = merged.baudRate ?? DEFAULT_SERIAL_SESSION_OPTIONS.baudRate;
1390
+ const bufferSize = merged.bufferSize ?? DEFAULT_SERIAL_SESSION_OPTIONS.bufferSize;
1589
1391
  if (!Number.isSafeInteger(baudRate) || baudRate <= 0) {
1590
1392
  throw new SerialError(
1591
1393
  SerialErrorCode.INVALID_CONNECTION_OPTIONS,
@@ -1598,15 +1400,25 @@ function resolveConnectionOptions(options) {
1598
1400
  }
1599
1401
  );
1600
1402
  }
1403
+ if (!Number.isSafeInteger(bufferSize) || bufferSize <= 0) {
1404
+ throw new SerialError(
1405
+ SerialErrorCode.INVALID_CONNECTION_OPTIONS,
1406
+ `Invalid bufferSize: ${bufferSize}. Must be a safe integer > 0.`,
1407
+ void 0,
1408
+ {
1409
+ field: "bufferSize",
1410
+ value: bufferSize,
1411
+ constraint: "positive-safe-integer"
1412
+ }
1413
+ );
1414
+ }
1601
1415
  return {
1602
1416
  dataBits: merged.dataBits ?? DEFAULT_SERIAL_SESSION_OPTIONS.dataBits,
1603
1417
  stopBits: merged.stopBits ?? DEFAULT_SERIAL_SESSION_OPTIONS.stopBits,
1604
1418
  parity: merged.parity ?? DEFAULT_SERIAL_SESSION_OPTIONS.parity,
1605
1419
  flowControl: merged.flowControl ?? DEFAULT_SERIAL_SESSION_OPTIONS.flowControl,
1606
1420
  baudRate: brandBaudRate(baudRate),
1607
- bufferSize: brandSerialPortBufferSize(
1608
- bufferSize ?? DEFAULT_SERIAL_SESSION_OPTIONS.bufferSize
1609
- )
1421
+ bufferSize: brandSerialPortBufferSize(bufferSize)
1610
1422
  };
1611
1423
  }
1612
1424
  function resolveSerialSessionOptions(options) {
@@ -1615,7 +1427,6 @@ function resolveSerialSessionOptions(options) {
1615
1427
  return {
1616
1428
  ...connection,
1617
1429
  ...filters !== void 0 ? { filters } : {},
1618
- receiveReplay: resolveReceiveReplayOptions(options?.receiveReplay),
1619
1430
  terminalBuffer: resolveTerminalBufferOptions(options?.terminalBuffer),
1620
1431
  lineBuffer: resolveLineBufferOptions(options?.lineBuffer)
1621
1432
  };
@@ -1625,62 +1436,52 @@ function resolveSerialSessionOptions(options) {
1625
1436
  function createSerialSession(options) {
1626
1437
  const resolvedOptions = resolveSerialSessionOptions(options);
1627
1438
  const controller = createSessionRuntimeController(
1628
- createInitialRuntime(hasWebSerialSupport())
1439
+ createInitialRuntime(isWebSerialSupported())
1629
1440
  );
1630
- const errorsSubject = new Subject3();
1441
+ const errorsSubject = new Subject2();
1631
1442
  const sendQueue = createSendQueue();
1632
1443
  const textEncoder = new TextEncoder();
1633
- const portInfoSubject = new BehaviorSubject3(null);
1634
1444
  const isDisposed = () => controller.status === SerialSessionStatus.Disposed;
1635
- const errorReporterRef = {};
1636
- const receivePipeline = createReceivePipeline({
1637
- resolvedOptions,
1638
- reportError: (error, options2) => errorReporterRef.reportError(error, options2)
1445
+ const receivePipeline = createReceivePipeline({ resolvedOptions });
1446
+ const { teardownPump, closePortSafely } = createPortTeardown({
1447
+ receivePipeline
1639
1448
  });
1640
- const lifecycle = createSessionLifecycle({
1449
+ const { reportError, createDisposedError } = createSessionErrorReporter({
1641
1450
  controller,
1642
- resolvedOptions,
1643
- sendQueue,
1644
- receivePipeline,
1645
- portInfoSubject,
1646
1451
  errorsSubject,
1452
+ sendQueue,
1647
1453
  isDisposed,
1648
- reportError: (error, options2) => errorReporterRef.reportError(error, options2),
1649
- createDisposedError: () => errorReporterRef.createDisposedError()
1454
+ teardownPump,
1455
+ closePortSafely
1650
1456
  });
1651
- const { reportError, createDisposedError } = createSessionErrorReporter({
1457
+ receivePipeline.bufferErrors$.subscribe(
1458
+ (error) => reportError(error, { fallbackCode: error.code })
1459
+ );
1460
+ const lifecycle = createSessionLifecycle({
1652
1461
  controller,
1653
- errorsSubject,
1462
+ resolvedOptions,
1654
1463
  sendQueue,
1464
+ receivePipeline,
1465
+ errorsSubject,
1655
1466
  isDisposed,
1656
- updatePortInfo: lifecycle.updatePortInfo,
1657
- teardownPump: lifecycle.teardownPump,
1658
- closePortSafely: lifecycle.closePortSafely
1467
+ teardownPump,
1468
+ closePortSafely,
1469
+ reportError,
1470
+ createDisposedError
1659
1471
  });
1660
- errorReporterRef.reportError = reportError;
1661
- errorReporterRef.createDisposedError = createDisposedError;
1662
- const { receive$, lines$, receiveReplay$ } = receivePipeline;
1472
+ const { receive$, lines$ } = receivePipeline;
1663
1473
  const errors$ = errorsSubject.asObservable();
1664
1474
  const terminalText$ = createTerminalBuffer(
1665
1475
  receive$,
1666
1476
  resolvedOptions.terminalBuffer
1667
1477
  ).text$;
1668
- const isConnected$ = controller.state$.pipe(
1669
- map2((state) => state.status === SerialSessionStatus.Connected),
1670
- distinctUntilChanged()
1671
- );
1672
- const portInfo$ = portInfoSubject.asObservable();
1673
1478
  return {
1674
- isBrowserSupported() {
1675
- return hasWebSerialSupport();
1676
- },
1677
1479
  connect$: lifecycle.connect$,
1678
1480
  disconnect$: lifecycle.disconnect$,
1679
1481
  dispose$: lifecycle.dispose$,
1680
- destroy$: lifecycle.dispose$,
1681
1482
  send$(data) {
1682
1483
  if (isDisposed()) {
1683
- return new Observable5((subscriber) => {
1484
+ return new Observable4((subscriber) => {
1684
1485
  subscriber.error(createDisposedError());
1685
1486
  });
1686
1487
  }
@@ -1697,15 +1498,9 @@ function createSerialSession(options) {
1697
1498
  });
1698
1499
  },
1699
1500
  state$: controller.state$,
1700
- isConnected$,
1701
- portInfo$,
1702
- getPortInfo() {
1703
- return portInfoSubject.getValue();
1704
- },
1705
1501
  errors$,
1706
1502
  receive$,
1707
1503
  terminalText$,
1708
- receiveReplay$,
1709
1504
  lines$
1710
1505
  };
1711
1506
  }
@@ -1717,8 +1512,6 @@ function isConnectedSessionState(state) {
1717
1512
  export {
1718
1513
  DEFAULT_LINE_BUFFER_OPTIONS,
1719
1514
  DEFAULT_TERMINAL_BUFFER_OPTIONS,
1720
- MAX_RECEIVE_REPLAY_BUFFER_SIZE,
1721
- MAX_RECEIVE_REPLAY_MAX_CHARS,
1722
1515
  SerialError,
1723
1516
  SerialErrorCode,
1724
1517
  SerialSessionStatus,
@@ -1726,6 +1519,7 @@ export {
1726
1519
  createSerialSession,
1727
1520
  createTerminalBuffer,
1728
1521
  isConnectedSessionState,
1522
+ isWebSerialSupported,
1729
1523
  resolveSerialSessionOptions
1730
1524
  };
1731
1525
  //# sourceMappingURL=index.mjs.map