jspurefix 5.9.1 → 5.10.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 (145) hide show
  1. package/BACKPORT_PLAN.md +20 -7
  2. package/README.md +35 -0
  3. package/WORKLOG.md +119 -0
  4. package/dist/benchmark/bench-report.d.ts +5 -0
  5. package/dist/benchmark/bench-report.js +91 -0
  6. package/dist/benchmark/bench-report.js.map +1 -0
  7. package/dist/benchmark/bench-runner.d.ts +12 -0
  8. package/dist/benchmark/bench-runner.js +193 -0
  9. package/dist/benchmark/bench-runner.js.map +1 -0
  10. package/dist/benchmark/bench-stats.d.ts +10 -0
  11. package/dist/benchmark/bench-stats.js +56 -0
  12. package/dist/benchmark/bench-stats.js.map +1 -0
  13. package/dist/benchmark/bench-types.d.ts +74 -0
  14. package/dist/benchmark/bench-types.js +3 -0
  15. package/dist/benchmark/bench-types.js.map +1 -0
  16. package/dist/benchmark/gc-probe.d.ts +18 -0
  17. package/dist/benchmark/gc-probe.js +103 -0
  18. package/dist/benchmark/gc-probe.js.map +1 -0
  19. package/dist/benchmark/index.d.ts +6 -0
  20. package/dist/benchmark/index.js +23 -0
  21. package/dist/benchmark/index.js.map +1 -0
  22. package/dist/benchmark/parse-bench.d.ts +28 -0
  23. package/dist/benchmark/parse-bench.js +85 -0
  24. package/dist/benchmark/parse-bench.js.map +1 -0
  25. package/dist/buffer/ascii/ascii-encoder.d.ts +1 -0
  26. package/dist/buffer/ascii/ascii-encoder.js +15 -0
  27. package/dist/buffer/ascii/ascii-encoder.js.map +1 -1
  28. package/dist/buffer/ascii/ascii-parser.d.ts +1 -0
  29. package/dist/buffer/ascii/ascii-parser.js +4 -1
  30. package/dist/buffer/ascii/ascii-parser.js.map +1 -1
  31. package/dist/buffer/ascii/ascii-segment-parser.js +3 -2
  32. package/dist/buffer/ascii/ascii-segment-parser.js.map +1 -1
  33. package/dist/buffer/ascii/tag-index.js.map +1 -1
  34. package/dist/buffer/msg-encoder.d.ts +2 -0
  35. package/dist/buffer/msg-encoder.js +1 -0
  36. package/dist/buffer/msg-encoder.js.map +1 -1
  37. package/dist/buffer/msg-view.d.ts +1 -0
  38. package/dist/buffer/msg-view.js +25 -19
  39. package/dist/buffer/msg-view.js.map +1 -1
  40. package/dist/dictionary/parser/quickfix/dictionary-validator.d.ts +1 -1
  41. package/dist/dictionary/parser/quickfix/dictionary-validator.js.map +1 -1
  42. package/dist/dictionary/parser/quickfix/quick-fix-graph-parser.d.ts +1 -1
  43. package/dist/dictionary/parser/quickfix/quick-fix-graph-parser.js +1 -2
  44. package/dist/dictionary/parser/quickfix/quick-fix-graph-parser.js.map +1 -1
  45. package/dist/dictionary/parser/quickfix/validation-error.d.ts +2 -2
  46. package/dist/dictionary/parser/quickfix/validation-error.js.map +1 -1
  47. package/dist/jsfix-bench.d.ts +1 -0
  48. package/dist/jsfix-bench.js +213 -0
  49. package/dist/jsfix-bench.js.map +1 -0
  50. package/dist/runtime/session-container.d.ts +3 -0
  51. package/dist/runtime/session-container.js +8 -0
  52. package/dist/runtime/session-container.js.map +1 -1
  53. package/dist/runtime/session-launcher.d.ts +2 -1
  54. package/dist/runtime/session-launcher.js +4 -1
  55. package/dist/runtime/session-launcher.js.map +1 -1
  56. package/dist/store/file-session-store.js +1 -1
  57. package/dist/store/file-session-store.js.map +1 -1
  58. package/dist/store/file-session-stream-provider.d.ts +1 -0
  59. package/dist/store/file-session-stream-provider.js +13 -13
  60. package/dist/store/file-session-stream-provider.js.map +1 -1
  61. package/dist/store/fix-session-store-factory.d.ts +1 -1
  62. package/dist/store/fix-session-store-factory.js.map +1 -1
  63. package/dist/store/fix-session-store.d.ts +11 -11
  64. package/dist/store/fix-session-store.js.map +1 -1
  65. package/dist/store/memory-session-store.js.map +1 -1
  66. package/dist/store/session-stream-provider.d.ts +13 -13
  67. package/dist/store/session-stream-provider.js.map +1 -1
  68. package/dist/transport/ascii/ascii-msg-transmitter.d.ts +1 -0
  69. package/dist/transport/ascii/ascii-msg-transmitter.js +15 -0
  70. package/dist/transport/ascii/ascii-msg-transmitter.js.map +1 -1
  71. package/dist/transport/ascii/ascii-session-msg-factory.js +1 -7
  72. package/dist/transport/ascii/ascii-session-msg-factory.js.map +1 -1
  73. package/dist/transport/fixml/fixml-session-msg-factory.js +1 -6
  74. package/dist/transport/fixml/fixml-session-msg-factory.js.map +1 -1
  75. package/dist/transport/index.d.ts +3 -0
  76. package/dist/transport/index.js +3 -0
  77. package/dist/transport/index.js.map +1 -1
  78. package/dist/transport/msg-payload.d.ts +3 -1
  79. package/dist/transport/msg-payload.js +2 -1
  80. package/dist/transport/msg-payload.js.map +1 -1
  81. package/dist/transport/msg-transmitter.d.ts +4 -1
  82. package/dist/transport/msg-transmitter.js +18 -2
  83. package/dist/transport/msg-transmitter.js.map +1 -1
  84. package/dist/transport/send-callback.d.ts +7 -0
  85. package/dist/transport/send-callback.js +3 -0
  86. package/dist/transport/send-callback.js.map +1 -0
  87. package/dist/transport/session/fix-clock.d.ts +1 -1
  88. package/dist/transport/session/fix-clock.js.map +1 -1
  89. package/dist/transport/session/fix-session.d.ts +2 -1
  90. package/dist/transport/session/fix-session.js +12 -3
  91. package/dist/transport/session/fix-session.js.map +1 -1
  92. package/dist/transport/session/resend-request-manager.d.ts +2 -2
  93. package/dist/transport/session/resend-request-manager.js.map +1 -1
  94. package/dist/transport/session/session-description.d.ts +2 -0
  95. package/dist/transport/session/session-description.js.map +1 -1
  96. package/dist/transport/session/session-sequence-coordinator.d.ts +1 -1
  97. package/dist/transport/session/session-sequence-coordinator.js.map +1 -1
  98. package/dist/transport/session/session-sequence-store.d.ts +3 -3
  99. package/dist/transport/session/session-sequence-store.js.map +1 -1
  100. package/dist/transport/tcp/tcp-initiator.js.map +1 -1
  101. package/docs/benchmarking.md +117 -0
  102. package/docs/custom-logon.md +225 -0
  103. package/eslint.config.mjs +15 -1
  104. package/jsfix.test_client.txt +62 -62
  105. package/jsfix.test_server.txt +72 -72
  106. package/package.json +9 -1
  107. package/src/benchmark/bench-report.ts +101 -0
  108. package/src/benchmark/bench-runner.ts +196 -0
  109. package/src/benchmark/bench-stats.ts +86 -0
  110. package/src/benchmark/bench-types.ts +110 -0
  111. package/src/benchmark/gc-probe.ts +145 -0
  112. package/src/benchmark/index.ts +6 -0
  113. package/src/benchmark/parse-bench.ts +144 -0
  114. package/src/buffer/ascii/ascii-encoder.ts +20 -0
  115. package/src/buffer/ascii/ascii-parser.ts +16 -1
  116. package/src/buffer/ascii/ascii-segment-parser.ts +9 -6
  117. package/src/buffer/ascii/tag-index.ts +3 -3
  118. package/src/buffer/msg-encoder.ts +12 -0
  119. package/src/buffer/msg-view.ts +35 -18
  120. package/src/dictionary/parser/quickfix/dictionary-validator.ts +1 -1
  121. package/src/dictionary/parser/quickfix/quick-fix-graph-parser.ts +2 -3
  122. package/src/dictionary/parser/quickfix/validation-error.ts +1 -1
  123. package/src/jsfix-bench.ts +204 -0
  124. package/src/runtime/session-container.ts +20 -0
  125. package/src/runtime/session-launcher.ts +19 -2
  126. package/src/store/file-session-store.ts +2 -2
  127. package/src/store/file-session-stream-provider.ts +17 -11
  128. package/src/store/fix-session-store-factory.ts +1 -1
  129. package/src/store/fix-session-store.ts +11 -11
  130. package/src/store/memory-session-store.ts +1 -1
  131. package/src/store/session-stream-provider.ts +13 -13
  132. package/src/transport/ascii/ascii-msg-transmitter.ts +20 -0
  133. package/src/transport/ascii/ascii-session-msg-factory.ts +6 -1
  134. package/src/transport/fixml/fixml-session-msg-factory.ts +3 -1
  135. package/src/transport/index.ts +5 -0
  136. package/src/transport/msg-payload.ts +5 -1
  137. package/src/transport/msg-transmitter.ts +31 -2
  138. package/src/transport/send-callback.ts +33 -0
  139. package/src/transport/session/fix-clock.ts +1 -1
  140. package/src/transport/session/fix-session.ts +22 -3
  141. package/src/transport/session/resend-request-manager.ts +4 -4
  142. package/src/transport/session/session-description.ts +14 -0
  143. package/src/transport/session/session-sequence-coordinator.ts +1 -1
  144. package/src/transport/session/session-sequence-store.ts +3 -3
  145. package/src/transport/tcp/tcp-initiator.ts +1 -1
@@ -51,8 +51,20 @@ class MsgTransmitter extends events.EventEmitter {
51
51
  this.emit('done');
52
52
  });
53
53
  }
54
- send(msgType, obj) {
55
- this.encodeStream.write(new msg_payload_1.MsgPayload(msgType, obj));
54
+ send(msgType, obj, callback = null) {
55
+ this.encodeStream.write(new msg_payload_1.MsgPayload(msgType, obj, callback));
56
+ }
57
+ settle(payload, error, header) {
58
+ var _a;
59
+ const callback = payload.callback;
60
+ if (!callback)
61
+ return;
62
+ try {
63
+ callback(error, { msgType: payload.msgType, header, encoded: (_a = payload.encoded) !== null && _a !== void 0 ? _a : null });
64
+ }
65
+ catch (e) {
66
+ this.emit('error', e);
67
+ }
56
68
  }
57
69
  encoderStream() {
58
70
  const transmitter = this;
@@ -67,9 +79,13 @@ class MsgTransmitter extends events.EventEmitter {
67
79
  this.push(payload.encoded);
68
80
  const encodedTxt = transmitter.buffer.toString();
69
81
  transmitter.emit('encoded', msgType, encodedTxt, state);
82
+ state
83
+ ? transmitter.settle(payload, null, state)
84
+ : transmitter.settle(payload, new Error(`could not encode ${msgType}`), null);
70
85
  done();
71
86
  }
72
87
  catch (e) {
88
+ transmitter.settle(payload, e, null);
73
89
  done(e);
74
90
  }
75
91
  }
@@ -1 +1 @@
1
- {"version":3,"file":"msg-transmitter.js","sourceRoot":"","sources":["../../src/transport/msg-transmitter.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,mCAAkC;AAClC,+CAA0C;AAG1C,+CAAgC;AAEhC,MAAsB,cAAe,SAAQ,MAAM,CAAC,YAAY;IAI9D,YAAuC,MAAqB,EAC1C,WAA2B,EAC3B,OAA4B;QAC5C,KAAK,EAAE,CAAA;QAH8B,WAAM,GAAN,MAAM,CAAe;QAC1C,gBAAW,GAAX,WAAW,CAAgB;QAC3B,YAAO,GAAP,OAAO,CAAqB;QAE5C,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,aAAa,EAAE,CAAA;QACxC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAQ,EAAE,EAAE;YACzC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;QACvB,CAAC,CAAC,CAAA;QACF,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;YAChC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACnB,CAAC,CAAC,CAAA;IACJ,CAAC;IAGM,IAAI,CAAE,OAAe,EAAE,GAAiB;QAC7C,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,wBAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAA;IACvD,CAAC;IAOO,aAAa;QACnB,MAAM,WAAW,GAAG,IAAI,CAAA;QACxB,OAAO,IAAI,kBAAS,CAAC;YACnB,kBAAkB,EAAE,IAAI;YACxB,SAAS,CAAE,OAAmB,EAAE,QAAQ,EAAE,IAAc;gBACtD,IAAI,CAAC;oBACH,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAA;oBAC/B,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;oBAC3B,MAAM,KAAK,GAAG,WAAW,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAA;oBAC7D,OAAO,CAAC,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;oBAC5C,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;oBAC1B,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAA;oBAChD,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,CAAC,CAAA;oBACvD,IAAI,EAAE,CAAA;gBACR,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACX,IAAI,CAAC,CAAC,CAAC,CAAA;gBACT,CAAC;YACH,CAAC;SACF,CAAC,CAAA;IACJ,CAAC;CACF;AA/CD,wCA+CC","sourcesContent":["import { MsgEncoder, ElasticBuffer } from '../buffer'\nimport { FixDefinitions } from '../dictionary/definition'\nimport { Transform } from 'stream'\nimport { MsgPayload } from './msg-payload'\nimport { ILooseObject } from '../collections/collection'\nimport { ISessionDescription } from './session/session-description'\nimport * as events from 'events'\n\nexport abstract class MsgTransmitter extends events.EventEmitter {\n public readonly encodeStream: Transform\n public encoder: MsgEncoder\n\n protected constructor (public readonly buffer: ElasticBuffer,\n public readonly definitions: FixDefinitions,\n public readonly session: ISessionDescription) {\n super()\n this.encodeStream = this.encoderStream()\n this.encodeStream.on('error', (e: Error) => {\n this.emit('error', e)\n })\n this.encodeStream.on('done', () => {\n this.emit('done')\n })\n }\n\n // messages at front, byte stream at back\n public send (msgType: string, obj: ILooseObject): void {\n this.encodeStream.write(new MsgPayload(msgType, obj))\n }\n\n public abstract encodeMessage (msgType: string, obj: ILooseObject): (ILooseObject | null)\n\n // read fix messages from one side, encode buffers on other ready to pipe\n // to output stream, say a socket\n\n private encoderStream (): Transform {\n const transmitter = this\n return new Transform({\n writableObjectMode: true,\n transform (payload: MsgPayload, encoding, done: Function) {\n try {\n const msgType = payload.msgType\n transmitter.encoder.reset()\n const state = transmitter.encodeMessage(msgType, payload.obj)\n payload.encoded = transmitter.encoder.trim()\n this.push(payload.encoded)\n const encodedTxt = transmitter.buffer.toString()\n transmitter.emit('encoded', msgType, encodedTxt, state)\n done()\n } catch (e) {\n done(e)\n }\n }\n })\n }\n}\n"]}
1
+ {"version":3,"file":"msg-transmitter.js","sourceRoot":"","sources":["../../src/transport/msg-transmitter.ts"],"names":[],"mappings":"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","sourcesContent":["import { MsgEncoder, ElasticBuffer } from '../buffer'\nimport { FixDefinitions } from '../dictionary/definition'\nimport { Transform } from 'stream'\nimport { MsgPayload } from './msg-payload'\nimport { ILooseObject } from '../collections/collection'\nimport { ISessionDescription } from './session/session-description'\nimport { SendCallback } from './send-callback'\nimport * as events from 'events'\n\nexport abstract class MsgTransmitter extends events.EventEmitter {\n public readonly encodeStream: Transform\n public encoder: MsgEncoder\n\n protected constructor (public readonly buffer: ElasticBuffer,\n public readonly definitions: FixDefinitions,\n public readonly session: ISessionDescription) {\n super()\n this.encodeStream = this.encoderStream()\n this.encodeStream.on('error', (e: Error) => {\n this.emit('error', e)\n })\n this.encodeStream.on('done', () => {\n this.emit('done')\n })\n }\n\n // messages at front, byte stream at back\n public send (msgType: string, obj: ILooseObject, callback: SendCallback | null = null): void {\n this.encodeStream.write(new MsgPayload(msgType, obj, callback))\n }\n\n /**\n * Report a send outcome to whoever asked for one, without letting their code break\n * the encode stream - a throwing callback would otherwise surface as a transform\n * failure and be indistinguishable from an encoding fault.\n */\n protected settle (payload: MsgPayload, error: Error | null, header: ILooseObject | null): void {\n const callback = payload.callback\n if (!callback) return\n try {\n callback(error, { msgType: payload.msgType, header, encoded: payload.encoded ?? null })\n } catch (e) {\n this.emit('error', e)\n }\n }\n\n public abstract encodeMessage (msgType: string, obj: ILooseObject): (ILooseObject | null)\n\n // read fix messages from one side, encode buffers on other ready to pipe\n // to output stream, say a socket\n\n private encoderStream (): Transform {\n const transmitter = this\n return new Transform({\n writableObjectMode: true,\n transform (payload: MsgPayload, encoding, done: Function) {\n try {\n const msgType = payload.msgType\n transmitter.encoder.reset()\n const state = transmitter.encodeMessage(msgType, payload.obj)\n payload.encoded = transmitter.encoder.trim()\n this.push(payload.encoded)\n const encodedTxt = transmitter.buffer.toString()\n transmitter.emit('encoded', msgType, encodedTxt, state)\n // encodeMessage returns null when it could not build the message - an\n // unknown msgType, or a factory that produced no header. It reports that\n // on the error channel and carries on, so without this the callback would\n // announce success for a message that never formed.\n state\n ? transmitter.settle(payload, null, state)\n : transmitter.settle(payload, new Error(`could not encode ${msgType}`), null)\n // note: the second argument to a Transform callback is *pushed to the\n // readable side*, and this readable side is piped to the socket - so a\n // result cannot be returned that way without corrupting the byte stream.\n // Hence the per-payload callback above.\n done()\n } catch (e) {\n transmitter.settle(payload, e as Error, null)\n // still fail the stream, so the session's error handling is unchanged\n done(e)\n }\n }\n })\n }\n}\n"]}
@@ -0,0 +1,7 @@
1
+ import { ILooseObject } from '../collections/collection';
2
+ export interface SendResult {
3
+ msgType: string;
4
+ header: ILooseObject | null;
5
+ encoded: Buffer | null;
6
+ }
7
+ export type SendCallback = (error: Error | null, result: SendResult) => void;
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=send-callback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"send-callback.js","sourceRoot":"","sources":["../../src/transport/send-callback.ts"],"names":[],"mappings":"","sourcesContent":["import { ILooseObject } from '../collections/collection'\n\n/**\n * What a send produced. `header` is the StandardHeader the engine stamped on the\n * message - the sequence number it went out under, the sending time, the comp ids -\n * which is the piece applications most often need to correlate an outbound message\n * with an acknowledgement, a database row, or a resend.\n */\nexport interface SendResult {\n msgType: string\n /** the stamped StandardHeader, or null if the message never got that far */\n header: ILooseObject | null\n /** the bytes written to the transport */\n encoded: Buffer | null\n}\n\n/**\n * Called once per `send`, after the message has been encoded and pushed to the\n * transport, or as soon as encoding fails.\n *\n * `send` is otherwise fire and forget: it writes into a stream and returns, so\n * nothing tells the caller which sequence number their message went out under, and a\n * failure surfaces on the session's error channel with no way to tell which call\n * caused it. See https://github.com/TimelordUK/jspurefix/issues/86\n *\n * Note this fires when the message has been *encoded and handed to the transport*,\n * not when the socket has flushed it - the FIX session layer has no acknowledgement\n * of the latter either.\n *\n * A callback that throws is reported on the transmitter's error channel rather than\n * being allowed to tear down the encode stream.\n */\nexport type SendCallback = (error: Error | null, result: SendResult) => void\n"]}
@@ -1,5 +1,5 @@
1
1
  export interface IFixClock {
2
- now(): Date;
2
+ now: () => Date;
3
3
  }
4
4
  export declare class DefaultFixClock implements IFixClock {
5
5
  now(): Date;
@@ -1 +1 @@
1
- {"version":3,"file":"fix-clock.js","sourceRoot":"","sources":["../../../src/transport/session/fix-clock.ts"],"names":[],"mappings":";;;AAIA,MAAa,eAAe;IAC1B,GAAG;QACD,OAAO,IAAI,IAAI,EAAE,CAAA;IACnB,CAAC;CACF;AAJD,0CAIC","sourcesContent":["export interface IFixClock {\n now (): Date\n}\n\nexport class DefaultFixClock implements IFixClock {\n now (): Date {\n return new Date()\n }\n}\n"]}
1
+ {"version":3,"file":"fix-clock.js","sourceRoot":"","sources":["../../../src/transport/session/fix-clock.ts"],"names":[],"mappings":";;;AAIA,MAAa,eAAe;IAC1B,GAAG;QACD,OAAO,IAAI,IAAI,EAAE,CAAA;IACnB,CAAC;CACF;AAJD,0CAIC","sourcesContent":["export interface IFixClock {\n now: () => Date\n}\n\nexport class DefaultFixClock implements IFixClock {\n now (): Date {\n return new Date()\n }\n}\n"]}
@@ -3,6 +3,7 @@ import { IJsFixConfig, IJsFixLogger } from '../../config';
3
3
  import { FixSessionState } from './fix-session-state';
4
4
  import { MsgTransport } from '../factory';
5
5
  import { ILooseObject } from '../../collections/collection';
6
+ import { SendCallback } from '../send-callback';
6
7
  import * as events from 'events';
7
8
  import { SessionState } from './session-state';
8
9
  export declare abstract class FixSession extends events.EventEmitter {
@@ -48,7 +49,7 @@ export declare abstract class FixSession extends events.EventEmitter {
48
49
  private stopTimer;
49
50
  protected terminate(error: Error): void;
50
51
  protected peerLogout(view: MsgView): void;
51
- protected send(msgType: string, obj: ILooseObject): void;
52
+ protected send(msgType: string, obj: ILooseObject, callback?: SendCallback | null): void;
52
53
  protected sendLogout(msg: string): void;
53
54
  protected sessionLogout(): void;
54
55
  done(): void;
@@ -379,17 +379,26 @@ class FixSession extends events.EventEmitter {
379
379
  }
380
380
  }
381
381
  }
382
- send(msgType, obj) {
382
+ send(msgType, obj, callback = null) {
383
383
  var _a;
384
384
  const state = this.sessionState.state;
385
385
  switch (state) {
386
386
  case session_state_1.SessionState.Stopped: {
387
- this.sessionLogger.warning(`can't send in state ${this.stateString()}`);
387
+ const msg = `can't send in state ${this.stateString()}`;
388
+ this.sessionLogger.warning(msg);
389
+ callback === null || callback === void 0 ? void 0 : callback(new Error(msg), { msgType, header: null, encoded: null });
388
390
  break;
389
391
  }
390
392
  default: {
391
393
  this.sessionState.LastSentAt = new Date();
392
- (_a = this.transport) === null || _a === void 0 ? void 0 : _a.transmitter.send(msgType, obj);
394
+ const transmitter = (_a = this.transport) === null || _a === void 0 ? void 0 : _a.transmitter;
395
+ if (!transmitter) {
396
+ const msg = `no transport to send ${msgType} on`;
397
+ this.sessionLogger.warning(msg);
398
+ callback === null || callback === void 0 ? void 0 : callback(new Error(msg), { msgType, header: null, encoded: null });
399
+ break;
400
+ }
401
+ transmitter.send(msgType, obj, callback);
393
402
  break;
394
403
  }
395
404
  }
@@ -1 +1 @@
1
- {"version":3,"file":"fix-session.js","sourceRoot":"","sources":["../../../src/transport/session/fix-session.ts"],"names":[],"mappings":"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","sourcesContent":["import { ElasticBuffer, MsgView } from '../../buffer'\nimport { IJsFixConfig, IJsFixLogger } from '../../config'\nimport { FixSessionState } from './fix-session-state'\nimport { MsgTransport } from '../factory'\nimport { MsgTag } from '../../types'\nimport { ILooseObject } from '../../collections/collection'\n\nimport * as events from 'events'\nimport { SessionState } from './session-state'\nimport { SegmentType } from '../../buffer/segment/segment-type'\n\n/**\n * The set of listeners this session attached to one specific transport.\n *\n * The handlers must be kept as stable references - `EventEmitter.removeListener`\n * matches on identity, so subscribing with a fresh closure and unsubscribing with\n * a method reference silently leaves the listener attached (issue #153). Holding\n * the transport alongside the handlers also lets `unsubscribe` detach from the\n * transport it actually subscribed to, even when `this.transport` has already\n * moved on to a newer connection.\n */\ninterface ITransportSubscription {\n readonly transport: MsgTransport\n readonly rxOnMsg: (msgType: string, view: MsgView) => void\n readonly rxOnError: (e: Error) => void\n readonly rxOnDone: () => void\n readonly rxOnEnd: () => void\n readonly rxOnDecoded: (msgType: string, data: ElasticBuffer, ptr: number) => void\n readonly txOnError: (e: Error) => void\n readonly txOnEncoded: (msgType: string, data: string, hdr: ILooseObject) => void\n}\n\nexport abstract class FixSession extends events.EventEmitter {\n public logReceivedMsgs: boolean = false\n protected timer: NodeJS.Timeout | null = null\n protected transport: MsgTransport | null = null\n private subscription: ITransportSubscription | null = null\n public manageSession: boolean = true\n public checkMsgIntegrity: boolean = false\n protected readonly me: string\n protected readonly initiator: boolean\n protected readonly acceptor: boolean\n protected readonly sessionState: FixSessionState\n /**\n * Not readonly: a wildcard acceptor does not know which counterparty it is\n * serving until the Logon arrives, and every session on that listener would\n * otherwise log under the same name. Rebound once the identity is known.\n */\n protected sessionLogger: IJsFixLogger\n protected requestLogoutType: string\n protected respondLogoutType: string\n protected requestLogonType: string\n\n protected constructor (public readonly config: IJsFixConfig) {\n super()\n const description = config.description\n this.me = description?.application?.name ?? 'me'\n this.sessionState = new FixSessionState(\n {\n heartBeat: config.description.HeartBtInt,\n lastPeerMsgSeqNum: config.description.LastReceivedSeqNum\n })\n this.sessionLogger = config.logFactory.logger(`${this.me}:FixSession`)\n this.initiator = description?.application?.type === 'initiator'\n this.acceptor = !this.initiator\n this.checkMsgIntegrity = this.acceptor\n this.sessionState.compId = description.SenderCompId\n }\n\n stateStr (theState: SessionState): string {\n return SessionState[theState]\n }\n\n assignState (newState: SessionState): void {\n const currentState = this.sessionState.state\n const currentStateStr = this.stateStr(currentState)\n const logger = this.sessionLogger\n const msg = `current state ${currentStateStr} (${currentState}) moves to ${SessionState[newState]} (${newState})`\n logger.info(msg)\n this.sessionState.state = newState\n }\n\n public setState (state: SessionState): void {\n const logger = this.sessionLogger\n const currentState = this.sessionState.state\n const currentStateStr = this.stateStr(currentState)\n if (state === currentState) return\n switch (currentState) {\n case SessionState.ConfirmingLogout:\n case SessionState.Stopped:\n if (state !== SessionState.NetworkConnectionEstablished) {\n logger.info(`ignoring request to change state as now already in ${currentStateStr}`)\n } else {\n this.assignState(state)\n }\n break\n\n default: {\n this.assignState(state)\n }\n }\n }\n\n public getState (): SessionState {\n return this.sessionState.state\n }\n\n public lastSentSeqNum (): number {\n return this.sessionState.lastSentSeqNum()\n }\n\n public lastPeerSeqNum (): number {\n return this.sessionState.lastPeerMsgSeqNum\n }\n\n public sendLogon (): void {\n const lo = this.config.factory?.logon()\n if (lo) {\n this.send(this.requestLogonType, lo)\n }\n }\n\n private async waitPromise (): Promise<number> {\n const logger = this.sessionLogger\n if (this.initiator) {\n // Hook for subclasses to reset sequences when ResetSeqNumFlag=Y is configured —\n // must run after the store is loaded, before sendLogon stamps a seq num.\n await this.onPreLogon()\n }\n return await new Promise<any>((resolve, reject) => {\n if (this.initiator) {\n logger.debug(`initiator sending logon state = ${this.stateString()}`)\n this.sendLogon()\n this.setState(SessionState.InitiationLogonSent)\n } else {\n logger.debug(`acceptor waits for logon state = ${this.stateString()}`)\n this.setState(SessionState.WaitingForALogon)\n }\n\n // Use named handlers and detach BOTH when the promise settles. Only one of\n // 'error'/'done' fires per run; without removing the sibling the unfired\n // listener would leak on every reconnect (issue #146). `once` alone is not\n // enough for the same reason — the sibling never fires, so never self-detaches.\n const cleanup = (): void => {\n this.removeListener('error', onError)\n this.removeListener('done', onDone)\n }\n const onError = (e: Error): void => {\n cleanup()\n // read the logger now, not the one captured when the run started - a\n // wildcard acceptor rebinds it once it knows which peer it is serving\n this.sessionLogger.error(e)\n reject(e)\n }\n const onDone = (): void => {\n cleanup()\n resolve(this.transport?.id)\n }\n\n this.on('error', onError)\n this.on('done', onDone)\n })\n }\n\n public async run (transport: MsgTransport): Promise<number> {\n const logger = this.sessionLogger\n if (this.transport) {\n logger.info(`reset from previous transport. state ${this.stateString()}`)\n this.reset()\n }\n this.transport = transport\n this.subscribe()\n return await this.waitPromise()\n }\n\n protected expectedEndState (): boolean {\n switch (this.sessionState.state) {\n case SessionState.Stopped:\n case SessionState.ConfirmingLogout:\n return true\n\n default:\n return false\n }\n }\n\n protected rxOnEnd (): void {\n const logger = this.sessionLogger\n logger.info(`rx end received sessionState = [${this.sessionState.toString()}]`)\n const expectedState = this.expectedEndState()\n if (expectedState) {\n logger.info(`rx receives end state = ${this.stateString()} - stop session`)\n this.stop()\n } else {\n this.setState(SessionState.DetectBrokenNetworkConnection)\n const e = new Error(`unexpected state - transport failed? = ${this.stateString()}`)\n logger.info(`rx error ${e.message}`)\n this.terminate(e)\n }\n }\n\n protected rxOnMsg (msgType: string, view: MsgView): void {\n const logger = this.sessionLogger\n\n if (this.logReceivedMsgs) {\n const name = view.segment.type !== SegmentType.Unknown ? view?.segment?.set?.name : 'unknown'\n logger.info(`${msgType}: ${name}`)\n logger.info(`${view.toString()}`)\n }\n this.sessionState.lastReceivedAt = new Date()\n if (this.manageSession) {\n this.onMsg(msgType, view)\n } else {\n this.checkForwardMsg(msgType, view)\n }\n }\n\n protected rxOnDone (): void {\n const logger = this.sessionLogger\n logger.info('rx done received')\n this.done()\n }\n\n protected rxOnError (e: Error): void {\n const logger = this.sessionLogger\n logger.warning(`rx error event: ${e.message} ${JSON.stringify(e)}`)\n this.terminate(e)\n }\n\n protected rxOnDecoded (msgType: string, data: ElasticBuffer, ptr: number): void {\n const logger = this.sessionLogger\n logger.debug(`rx: [${msgType}] ${ptr} bytes`)\n this.onDecoded(msgType, data.toString(ptr))\n }\n\n protected txOnError (e: Error): void {\n const logger = this.sessionLogger\n logger.warning(`tx error event: ${e.message} ${JSON.stringify(e)}`)\n this.terminate(e)\n }\n\n protected txOnEncoded (msgType: string, data: string, hdr: ILooseObject): void {\n const logger = this.sessionLogger\n this.sessionState.lastHeader = hdr\n logger.debug(`tx: [${msgType}] ${data.length} bytes seqNo = ${this.lastSentSeqNum()}`)\n this.onEncoded(msgType, data)\n }\n\n /**\n * true when the event which has just fired came from the transport this session\n * is currently running. A half open socket can keep emitting long after the peer\n * has reconnected on a fresh connection - those events must not be allowed to drive\n * the live session (issue #153).\n */\n private isCurrentTransport (transport: MsgTransport, event: string): boolean {\n if (this.subscription?.transport === transport && this.transport === transport) {\n return true\n }\n const current = this.transport?.id ?? 'none'\n this.sessionLogger.warning(`ignoring '${event}' from stale transport ${transport.id} - current transport is ${current}`)\n return false\n }\n\n protected unsubscribe (): void {\n const logger = this.sessionLogger\n const subscription = this.subscription\n if (!subscription) {\n logger.debug('unsubscribe called with no active subscription')\n return\n }\n const transport = subscription.transport\n logger.info(`unsubscribe transport ${transport.id} sessionState = [${this.sessionState.toString()}]`)\n const rx = transport.receiver\n const tx = transport.transmitter\n\n rx?.removeListener('msg', subscription.rxOnMsg)\n rx?.removeListener('error', subscription.rxOnError)\n rx?.removeListener('done', subscription.rxOnDone)\n rx?.removeListener('end', subscription.rxOnEnd)\n rx?.removeListener('decoded', subscription.rxOnDecoded)\n tx?.removeListener('error', subscription.txOnError)\n tx?.removeListener('encoded', subscription.txOnEncoded)\n this.subscription = null\n logger.debug(`unsubscribe complete - rx listeners now ${rx?.listenerCount('msg') ?? 0} msg, ${rx?.listenerCount('end') ?? 0} end`)\n }\n\n protected subscribe (): void {\n const transport = this.transport\n if (!transport) {\n this.sessionLogger.warning('subscribe called with no transport')\n return\n }\n // detach from any previous transport before attaching to this one\n if (this.subscription) {\n this.unsubscribe()\n }\n const inst = this\n const subscription: ITransportSubscription = {\n transport,\n rxOnMsg: (msgType: string, view: MsgView) => {\n if (inst.isCurrentTransport(transport, 'msg')) inst.rxOnMsg(msgType, view)\n },\n rxOnError: (e: Error) => {\n if (inst.isCurrentTransport(transport, 'error')) inst.rxOnError(e)\n },\n rxOnDone: () => {\n if (inst.isCurrentTransport(transport, 'done')) inst.rxOnDone()\n },\n rxOnEnd: () => {\n if (inst.isCurrentTransport(transport, 'end')) inst.rxOnEnd()\n },\n rxOnDecoded: (msgType: string, data: ElasticBuffer, ptr: number) => {\n if (inst.isCurrentTransport(transport, 'decoded')) inst.rxOnDecoded(msgType, data, ptr)\n },\n txOnError: (e: Error) => {\n if (inst.isCurrentTransport(transport, 'tx error')) inst.txOnError(e)\n },\n txOnEncoded: (msgType: string, data: string, hdr: ILooseObject) => {\n if (inst.isCurrentTransport(transport, 'encoded')) inst.txOnEncoded(msgType, data, hdr)\n }\n }\n const rx = transport.receiver\n const tx = transport.transmitter\n rx?.on('msg', subscription.rxOnMsg)\n rx?.on('error', subscription.rxOnError)\n rx?.on('done', subscription.rxOnDone)\n rx?.on('end', subscription.rxOnEnd)\n rx?.on('decoded', subscription.rxOnDecoded)\n tx?.on('error', subscription.txOnError)\n tx?.on('encoded', subscription.txOnEncoded)\n this.subscription = subscription\n this.sessionLogger.info(`subscribed to transport ${transport.id}`)\n }\n\n protected validStateApplicationMsg (): boolean {\n switch (this.sessionState.state) {\n case SessionState.Idle:\n case SessionState.InitiateConnection:\n case SessionState.InitiationLogonSent:\n case SessionState.WaitingForALogon:\n case SessionState.HandleResendRequest:\n case SessionState.AwaitingProcessingResponseToTestRequest:\n case SessionState.AwaitingProcessingResponseToResendRequest:\n return false\n default:\n return true\n }\n }\n\n protected stateString (): string {\n return SessionState[this.sessionState.state]\n }\n\n protected checkForwardMsg (msgType: string, view: MsgView): void {\n this.sessionLogger.info(`forwarding msgType = '${msgType}' to application`)\n this.setState(SessionState.ActiveNormalSession)\n this.onApplicationMsg(msgType, view)\n }\n\n private stopTimer (): void {\n if (this.timer) {\n this.sessionLogger.info('stopTimer')\n clearInterval(this.timer)\n this.timer = null\n }\n }\n\n protected terminate (error: Error): void {\n if (this.sessionState.state === SessionState.Stopped) return\n this.sessionLogger.error(error)\n this.stopTimer()\n this.unsubscribe()\n if (this.transport) {\n this.sessionLogger.info(`terminate: kill transport ${this.transport.id}`)\n this.transport.end()\n }\n this.transport = null\n this.setState(SessionState.Stopped)\n this.onSessionStopping()\n this.emit('error', error)\n this.onStopped(error)\n }\n\n protected peerLogout (view: MsgView): void {\n const msg = view.getString(MsgTag.Text)\n const state = this.sessionState.state\n switch (state) {\n case SessionState.WaitingLogoutConfirm: {\n this.sessionLogger.info(`peer confirms logout Text = '${msg}'`)\n this.stop()\n break\n }\n\n case SessionState.InitiationLogonResponse:\n case SessionState.ActiveNormalSession:\n case SessionState.InitiationLogonReceived: {\n this.setState(SessionState.ConfirmingLogout)\n this.sessionLogger.info(`peer initiates logout Text = '${msg}'`)\n this.sessionLogout()\n }\n }\n }\n\n protected send (msgType: string, obj: ILooseObject): void {\n const state = this.sessionState.state\n switch (state) {\n case SessionState.Stopped: {\n this.sessionLogger.warning(`can't send in state ${this.stateString()}`)\n break\n }\n\n default: {\n this.sessionState.LastSentAt = new Date()\n this.transport?.transmitter.send(msgType, obj)\n break\n }\n }\n }\n\n protected sendLogout (msg: string): void {\n const factory = this.config.factory\n this.sessionLogger.info(`sending logout with ${msg}`)\n const lo = factory?.logout(this.requestLogoutType, msg)\n if (lo) {\n this.send(this.requestLogoutType, lo)\n }\n }\n\n protected sessionLogout (): void {\n const sessionState = this.sessionState\n if (sessionState.logoutSentAt) {\n return\n }\n\n switch (sessionState.state) {\n case SessionState.ActiveNormalSession:\n case SessionState.InitiationLogonResponse:\n case SessionState.InitiationLogonReceived: {\n // this instance initiates logout\n this.setState(SessionState.WaitingLogoutConfirm)\n sessionState.logoutSentAt = new Date()\n const msg = `${this.me} initiate logout`\n this.sessionLogger.info(msg)\n this.sendLogout(msg)\n break\n }\n\n case SessionState.ConfirmingLogout: {\n // this instance responds to log out\n this.setState(SessionState.ConfirmingLogout)\n sessionState.logoutSentAt = new Date()\n const msg = `${this.me} confirming logout`\n this.sessionLogger.info(msg)\n this.sendLogout(msg)\n break\n }\n\n default: {\n this.sessionLogger.info(`sessionLogout ignored as in state ${sessionState.state}`)\n }\n }\n }\n\n public done (): void {\n switch (this.sessionState.state) {\n case SessionState.InitiationLogonResponse:\n case SessionState.ActiveNormalSession:\n case SessionState.InitiationLogonReceived: {\n this.sessionLogout()\n break\n }\n\n case SessionState.Stopped:\n this.sessionLogger.info('done. session is now stopped')\n break\n\n default: {\n this.stop()\n break\n }\n }\n this.sessionLogger.info(`done. check logout sequence state ${this.stateString()}`)\n }\n\n public reset (resetSeqNum?: number | null): void {\n this.stopTimer()\n // unsubscribe from old transport before clearing reference\n this.unsubscribe()\n // reset parser to clear any partial message state from dropped connection\n const receiver: any = this.transport?.receiver\n if (receiver?.reset) {\n receiver.reset()\n }\n this.transport = null\n const resetFlag = this.config.description.ResetSeqNumFlag\n const seqNum = resetFlag ? 0 : resetSeqNum ?? this.sessionState.lastPeerMsgSeqNum\n this.sessionState.reset(seqNum) // from header def ... eventually\n this.onPrepareForReconnect()\n this.setState(SessionState.NetworkConnectionEstablished)\n }\n\n protected onPrepareForReconnect (): void {\n // Override in subclass to reset coordinator/transient state\n }\n\n /**\n * Called once as the session moves to Stopped, before the application is informed\n * via onStopped. Override to release session scoped resources - the ascii session\n * uses this to remove itself from the session registry. Mirrors cspurefix\n * FixSession.OnSessionStopping.\n */\n protected onSessionStopping (): void {\n // Default no-op\n }\n\n /**\n * Called for initiators after the store is loaded but before the Logon is sent.\n * Override to reset sequences/store when ResetSeqNumFlag=Y so the Logon goes out\n * with MsgSeqNum=1 instead of the recovered sender seq num.\n */\n protected async onPreLogon (): Promise<void> {\n // Default no-op\n }\n\n /**\n * Stop this session on behalf of an external caller - typically the session registry\n * when a new connection for the same SessionId replaces this one, but equally an\n * application which has decided the peer is gone. Mirrors cspurefix\n * FixSession.RequestStop.\n * @param reason recorded in the session log and carried on the emitted error\n */\n public requestStop (reason: string): void {\n this.sessionLogger.info(`requestStop: ${reason}`)\n this.stop(new Error(reason))\n }\n\n protected stop (error: Error | null = null): void {\n if (this.sessionState.state === SessionState.Stopped) {\n return\n }\n this.stopTimer()\n this.unsubscribe()\n this.sessionLogger.info(`stop: kill transport ${this.transport?.id ?? 'none'}`)\n this.onSessionStopping()\n this.transport?.end()\n if (error) {\n this.sessionLogger.info(`stop: emit error ${error.message}`)\n this.emit('error', error)\n } else {\n this.emit('done')\n }\n\n this.setState(SessionState.Stopped)\n this.onStopped(error ?? undefined)\n this.transport = null\n }\n\n /**\n * dispatches a message into the subclass that inherits from FixSession. The view contains\n * the parsed message which has utility methods such as toObject(). The Ascii session provides\n * an implementation to handle admin level messages such as logon, hearbeat and resest request.\n * Any application messges are dispatched via onApplicationMsg where the application can action\n * the message.\n * @param msgType the string based msg type the view represents\n * @param view container for all parsed fields representing the received message.\n * @protected\n */\n protected abstract onMsg (msgType: string, view: MsgView): void\n /**\n * the parsed txt recieved from the peer application. Given the applicaton is\n * responible for maintaining the fix log, this can be used to persist all received\n * messages.\n * @param msgType the string based msg type the view represents\n * @param txt the received message where for Ascii, the wire SOH delimeter is replaced\n * with that specified in the config e.g. '|'\n * @protected\n */\n protected abstract onDecoded (msgType: string, txt: string): void\n /**\n * the formatted txt sent to the peer application as an outbound message. Given the applicaton is\n * responible for maintaining the fix log, this can be used to persist all transmitted\n * messages. use msgType for example to persist only trade capture messages to database\n * @param msgType the msg type representing the message.\n * @param txt the sent message where for Ascii, the wire SOH delimeter is replaced\n * with that specified in the config e.g. '|'\n * @protected\n */\n protected abstract onEncoded (msgType: string, txt: string): void\n /**\n * typically all session level messages are handled by AsciiSession and these are\n * application level such as MarketDataRefresh. This will represent the applications main\n * work functiono where responses can be sent back to the peer. If manageSession has been set false\n * (not recommended) all messages are sent to this function\n * @param msgType the msg type representing the message.\n * @param view a wrapper containing the parsed message received.\n * @protected\n */\n protected abstract onApplicationMsg (msgType: string, view: MsgView): void\n /**\n * at this point the application is ready to send messages - peer login has been achieved\n * and the session can be considered ready to use. In the case of an initiator the application\n * may at this point send for security definitions or send market data subscriptions\n * @param view the login message causing session to be ready\n * @protected\n */\n protected abstract onReady (view: MsgView): void\n\n /**\n * Inform application this session has now ended - either from logout or connection dropped\n * @param error if session has been terminated via an error it is provided\n * @protected\n */\n protected abstract onStopped (error?: Error): void\n /**\n * Placeholder infomring the application of a peer login attempt.\n * @param view the login message\n * @param user extracted user from message\n * @param password extracted password from the message.\n * @protected\n */\n protected abstract onLogon (view: MsgView, user: string, password: string): boolean\n}\n"]}
1
+ {"version":3,"file":"fix-session.js","sourceRoot":"","sources":["../../../src/transport/session/fix-session.ts"],"names":[],"mappings":"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","sourcesContent":["import { ElasticBuffer, MsgView } from '../../buffer'\nimport { IJsFixConfig, IJsFixLogger } from '../../config'\nimport { FixSessionState } from './fix-session-state'\nimport { MsgTransport } from '../factory'\nimport { MsgTag } from '../../types'\nimport { ILooseObject } from '../../collections/collection'\nimport { SendCallback } from '../send-callback'\n\nimport * as events from 'events'\nimport { SessionState } from './session-state'\nimport { SegmentType } from '../../buffer/segment/segment-type'\n\n/**\n * The set of listeners this session attached to one specific transport.\n *\n * The handlers must be kept as stable references - `EventEmitter.removeListener`\n * matches on identity, so subscribing with a fresh closure and unsubscribing with\n * a method reference silently leaves the listener attached (issue #153). Holding\n * the transport alongside the handlers also lets `unsubscribe` detach from the\n * transport it actually subscribed to, even when `this.transport` has already\n * moved on to a newer connection.\n */\ninterface ITransportSubscription {\n readonly transport: MsgTransport\n readonly rxOnMsg: (msgType: string, view: MsgView) => void\n readonly rxOnError: (e: Error) => void\n readonly rxOnDone: () => void\n readonly rxOnEnd: () => void\n readonly rxOnDecoded: (msgType: string, data: ElasticBuffer, ptr: number) => void\n readonly txOnError: (e: Error) => void\n readonly txOnEncoded: (msgType: string, data: string, hdr: ILooseObject) => void\n}\n\nexport abstract class FixSession extends events.EventEmitter {\n public logReceivedMsgs: boolean = false\n protected timer: NodeJS.Timeout | null = null\n protected transport: MsgTransport | null = null\n private subscription: ITransportSubscription | null = null\n public manageSession: boolean = true\n public checkMsgIntegrity: boolean = false\n protected readonly me: string\n protected readonly initiator: boolean\n protected readonly acceptor: boolean\n protected readonly sessionState: FixSessionState\n /**\n * Not readonly: a wildcard acceptor does not know which counterparty it is\n * serving until the Logon arrives, and every session on that listener would\n * otherwise log under the same name. Rebound once the identity is known.\n */\n protected sessionLogger: IJsFixLogger\n protected requestLogoutType: string\n protected respondLogoutType: string\n protected requestLogonType: string\n\n protected constructor (public readonly config: IJsFixConfig) {\n super()\n const description = config.description\n this.me = description?.application?.name ?? 'me'\n this.sessionState = new FixSessionState(\n {\n heartBeat: config.description.HeartBtInt,\n lastPeerMsgSeqNum: config.description.LastReceivedSeqNum\n })\n this.sessionLogger = config.logFactory.logger(`${this.me}:FixSession`)\n this.initiator = description?.application?.type === 'initiator'\n this.acceptor = !this.initiator\n this.checkMsgIntegrity = this.acceptor\n this.sessionState.compId = description.SenderCompId\n }\n\n stateStr (theState: SessionState): string {\n return SessionState[theState]\n }\n\n assignState (newState: SessionState): void {\n const currentState = this.sessionState.state\n const currentStateStr = this.stateStr(currentState)\n const logger = this.sessionLogger\n const msg = `current state ${currentStateStr} (${currentState}) moves to ${SessionState[newState]} (${newState})`\n logger.info(msg)\n this.sessionState.state = newState\n }\n\n public setState (state: SessionState): void {\n const logger = this.sessionLogger\n const currentState = this.sessionState.state\n const currentStateStr = this.stateStr(currentState)\n if (state === currentState) return\n switch (currentState) {\n case SessionState.ConfirmingLogout:\n case SessionState.Stopped:\n if (state !== SessionState.NetworkConnectionEstablished) {\n logger.info(`ignoring request to change state as now already in ${currentStateStr}`)\n } else {\n this.assignState(state)\n }\n break\n\n default: {\n this.assignState(state)\n }\n }\n }\n\n public getState (): SessionState {\n return this.sessionState.state\n }\n\n public lastSentSeqNum (): number {\n return this.sessionState.lastSentSeqNum()\n }\n\n public lastPeerSeqNum (): number {\n return this.sessionState.lastPeerMsgSeqNum\n }\n\n public sendLogon (): void {\n const lo = this.config.factory?.logon()\n if (lo) {\n this.send(this.requestLogonType, lo)\n }\n }\n\n private async waitPromise (): Promise<number> {\n const logger = this.sessionLogger\n if (this.initiator) {\n // Hook for subclasses to reset sequences when ResetSeqNumFlag=Y is configured —\n // must run after the store is loaded, before sendLogon stamps a seq num.\n await this.onPreLogon()\n }\n return await new Promise<any>((resolve, reject) => {\n if (this.initiator) {\n logger.debug(`initiator sending logon state = ${this.stateString()}`)\n this.sendLogon()\n this.setState(SessionState.InitiationLogonSent)\n } else {\n logger.debug(`acceptor waits for logon state = ${this.stateString()}`)\n this.setState(SessionState.WaitingForALogon)\n }\n\n // Use named handlers and detach BOTH when the promise settles. Only one of\n // 'error'/'done' fires per run; without removing the sibling the unfired\n // listener would leak on every reconnect (issue #146). `once` alone is not\n // enough for the same reason — the sibling never fires, so never self-detaches.\n const cleanup = (): void => {\n this.removeListener('error', onError)\n this.removeListener('done', onDone)\n }\n const onError = (e: Error): void => {\n cleanup()\n // read the logger now, not the one captured when the run started - a\n // wildcard acceptor rebinds it once it knows which peer it is serving\n this.sessionLogger.error(e)\n reject(e)\n }\n const onDone = (): void => {\n cleanup()\n resolve(this.transport?.id)\n }\n\n this.on('error', onError)\n this.on('done', onDone)\n })\n }\n\n public async run (transport: MsgTransport): Promise<number> {\n const logger = this.sessionLogger\n if (this.transport) {\n logger.info(`reset from previous transport. state ${this.stateString()}`)\n this.reset()\n }\n this.transport = transport\n this.subscribe()\n return await this.waitPromise()\n }\n\n protected expectedEndState (): boolean {\n switch (this.sessionState.state) {\n case SessionState.Stopped:\n case SessionState.ConfirmingLogout:\n return true\n\n default:\n return false\n }\n }\n\n protected rxOnEnd (): void {\n const logger = this.sessionLogger\n logger.info(`rx end received sessionState = [${this.sessionState.toString()}]`)\n const expectedState = this.expectedEndState()\n if (expectedState) {\n logger.info(`rx receives end state = ${this.stateString()} - stop session`)\n this.stop()\n } else {\n this.setState(SessionState.DetectBrokenNetworkConnection)\n const e = new Error(`unexpected state - transport failed? = ${this.stateString()}`)\n logger.info(`rx error ${e.message}`)\n this.terminate(e)\n }\n }\n\n protected rxOnMsg (msgType: string, view: MsgView): void {\n const logger = this.sessionLogger\n\n if (this.logReceivedMsgs) {\n const name = view.segment.type !== SegmentType.Unknown ? view?.segment?.set?.name : 'unknown'\n logger.info(`${msgType}: ${name}`)\n logger.info(`${view.toString()}`)\n }\n this.sessionState.lastReceivedAt = new Date()\n if (this.manageSession) {\n this.onMsg(msgType, view)\n } else {\n this.checkForwardMsg(msgType, view)\n }\n }\n\n protected rxOnDone (): void {\n const logger = this.sessionLogger\n logger.info('rx done received')\n this.done()\n }\n\n protected rxOnError (e: Error): void {\n const logger = this.sessionLogger\n logger.warning(`rx error event: ${e.message} ${JSON.stringify(e)}`)\n this.terminate(e)\n }\n\n protected rxOnDecoded (msgType: string, data: ElasticBuffer, ptr: number): void {\n const logger = this.sessionLogger\n logger.debug(`rx: [${msgType}] ${ptr} bytes`)\n this.onDecoded(msgType, data.toString(ptr))\n }\n\n protected txOnError (e: Error): void {\n const logger = this.sessionLogger\n logger.warning(`tx error event: ${e.message} ${JSON.stringify(e)}`)\n this.terminate(e)\n }\n\n protected txOnEncoded (msgType: string, data: string, hdr: ILooseObject): void {\n const logger = this.sessionLogger\n this.sessionState.lastHeader = hdr\n logger.debug(`tx: [${msgType}] ${data.length} bytes seqNo = ${this.lastSentSeqNum()}`)\n this.onEncoded(msgType, data)\n }\n\n /**\n * true when the event which has just fired came from the transport this session\n * is currently running. A half open socket can keep emitting long after the peer\n * has reconnected on a fresh connection - those events must not be allowed to drive\n * the live session (issue #153).\n */\n private isCurrentTransport (transport: MsgTransport, event: string): boolean {\n if (this.subscription?.transport === transport && this.transport === transport) {\n return true\n }\n const current = this.transport?.id ?? 'none'\n this.sessionLogger.warning(`ignoring '${event}' from stale transport ${transport.id} - current transport is ${current}`)\n return false\n }\n\n protected unsubscribe (): void {\n const logger = this.sessionLogger\n const subscription = this.subscription\n if (!subscription) {\n logger.debug('unsubscribe called with no active subscription')\n return\n }\n const transport = subscription.transport\n logger.info(`unsubscribe transport ${transport.id} sessionState = [${this.sessionState.toString()}]`)\n const rx = transport.receiver\n const tx = transport.transmitter\n\n rx?.removeListener('msg', subscription.rxOnMsg)\n rx?.removeListener('error', subscription.rxOnError)\n rx?.removeListener('done', subscription.rxOnDone)\n rx?.removeListener('end', subscription.rxOnEnd)\n rx?.removeListener('decoded', subscription.rxOnDecoded)\n tx?.removeListener('error', subscription.txOnError)\n tx?.removeListener('encoded', subscription.txOnEncoded)\n this.subscription = null\n logger.debug(`unsubscribe complete - rx listeners now ${rx?.listenerCount('msg') ?? 0} msg, ${rx?.listenerCount('end') ?? 0} end`)\n }\n\n protected subscribe (): void {\n const transport = this.transport\n if (!transport) {\n this.sessionLogger.warning('subscribe called with no transport')\n return\n }\n // detach from any previous transport before attaching to this one\n if (this.subscription) {\n this.unsubscribe()\n }\n const inst = this\n const subscription: ITransportSubscription = {\n transport,\n rxOnMsg: (msgType: string, view: MsgView) => {\n if (inst.isCurrentTransport(transport, 'msg')) inst.rxOnMsg(msgType, view)\n },\n rxOnError: (e: Error) => {\n if (inst.isCurrentTransport(transport, 'error')) inst.rxOnError(e)\n },\n rxOnDone: () => {\n if (inst.isCurrentTransport(transport, 'done')) inst.rxOnDone()\n },\n rxOnEnd: () => {\n if (inst.isCurrentTransport(transport, 'end')) inst.rxOnEnd()\n },\n rxOnDecoded: (msgType: string, data: ElasticBuffer, ptr: number) => {\n if (inst.isCurrentTransport(transport, 'decoded')) inst.rxOnDecoded(msgType, data, ptr)\n },\n txOnError: (e: Error) => {\n if (inst.isCurrentTransport(transport, 'tx error')) inst.txOnError(e)\n },\n txOnEncoded: (msgType: string, data: string, hdr: ILooseObject) => {\n if (inst.isCurrentTransport(transport, 'encoded')) inst.txOnEncoded(msgType, data, hdr)\n }\n }\n const rx = transport.receiver\n const tx = transport.transmitter\n rx?.on('msg', subscription.rxOnMsg)\n rx?.on('error', subscription.rxOnError)\n rx?.on('done', subscription.rxOnDone)\n rx?.on('end', subscription.rxOnEnd)\n rx?.on('decoded', subscription.rxOnDecoded)\n tx?.on('error', subscription.txOnError)\n tx?.on('encoded', subscription.txOnEncoded)\n this.subscription = subscription\n this.sessionLogger.info(`subscribed to transport ${transport.id}`)\n }\n\n protected validStateApplicationMsg (): boolean {\n switch (this.sessionState.state) {\n case SessionState.Idle:\n case SessionState.InitiateConnection:\n case SessionState.InitiationLogonSent:\n case SessionState.WaitingForALogon:\n case SessionState.HandleResendRequest:\n case SessionState.AwaitingProcessingResponseToTestRequest:\n case SessionState.AwaitingProcessingResponseToResendRequest:\n return false\n default:\n return true\n }\n }\n\n protected stateString (): string {\n return SessionState[this.sessionState.state]\n }\n\n protected checkForwardMsg (msgType: string, view: MsgView): void {\n this.sessionLogger.info(`forwarding msgType = '${msgType}' to application`)\n this.setState(SessionState.ActiveNormalSession)\n this.onApplicationMsg(msgType, view)\n }\n\n private stopTimer (): void {\n if (this.timer) {\n this.sessionLogger.info('stopTimer')\n clearInterval(this.timer)\n this.timer = null\n }\n }\n\n protected terminate (error: Error): void {\n if (this.sessionState.state === SessionState.Stopped) return\n this.sessionLogger.error(error)\n this.stopTimer()\n this.unsubscribe()\n if (this.transport) {\n this.sessionLogger.info(`terminate: kill transport ${this.transport.id}`)\n this.transport.end()\n }\n this.transport = null\n this.setState(SessionState.Stopped)\n this.onSessionStopping()\n this.emit('error', error)\n this.onStopped(error)\n }\n\n protected peerLogout (view: MsgView): void {\n const msg = view.getString(MsgTag.Text)\n const state = this.sessionState.state\n switch (state) {\n case SessionState.WaitingLogoutConfirm: {\n this.sessionLogger.info(`peer confirms logout Text = '${msg}'`)\n this.stop()\n break\n }\n\n case SessionState.InitiationLogonResponse:\n case SessionState.ActiveNormalSession:\n case SessionState.InitiationLogonReceived: {\n this.setState(SessionState.ConfirmingLogout)\n this.sessionLogger.info(`peer initiates logout Text = '${msg}'`)\n this.sessionLogout()\n }\n }\n }\n\n /**\n * @param callback optional, invoked once the message has been encoded and handed to\n * the transport, or as soon as that fails. Without it a send is fire and forget:\n * nothing tells the caller which sequence number the message went out under, and a\n * message dropped because the session is down, or refused by the encoder, goes\n * unanswered. See https://github.com/TimelordUK/jspurefix/issues/86\n */\n protected send (msgType: string, obj: ILooseObject, callback: SendCallback | null = null): void {\n const state = this.sessionState.state\n switch (state) {\n case SessionState.Stopped: {\n const msg = `can't send in state ${this.stateString()}`\n this.sessionLogger.warning(msg)\n // the message is dropped either way - but a caller that asked to be told is\n // now told, instead of watching for a log line\n callback?.(new Error(msg), { msgType, header: null, encoded: null })\n break\n }\n\n default: {\n this.sessionState.LastSentAt = new Date()\n const transmitter = this.transport?.transmitter\n if (!transmitter) {\n const msg = `no transport to send ${msgType} on`\n this.sessionLogger.warning(msg)\n callback?.(new Error(msg), { msgType, header: null, encoded: null })\n break\n }\n transmitter.send(msgType, obj, callback)\n break\n }\n }\n }\n\n protected sendLogout (msg: string): void {\n const factory = this.config.factory\n this.sessionLogger.info(`sending logout with ${msg}`)\n const lo = factory?.logout(this.requestLogoutType, msg)\n if (lo) {\n this.send(this.requestLogoutType, lo)\n }\n }\n\n protected sessionLogout (): void {\n const sessionState = this.sessionState\n if (sessionState.logoutSentAt) {\n return\n }\n\n switch (sessionState.state) {\n case SessionState.ActiveNormalSession:\n case SessionState.InitiationLogonResponse:\n case SessionState.InitiationLogonReceived: {\n // this instance initiates logout\n this.setState(SessionState.WaitingLogoutConfirm)\n sessionState.logoutSentAt = new Date()\n const msg = `${this.me} initiate logout`\n this.sessionLogger.info(msg)\n this.sendLogout(msg)\n break\n }\n\n case SessionState.ConfirmingLogout: {\n // this instance responds to log out\n this.setState(SessionState.ConfirmingLogout)\n sessionState.logoutSentAt = new Date()\n const msg = `${this.me} confirming logout`\n this.sessionLogger.info(msg)\n this.sendLogout(msg)\n break\n }\n\n default: {\n this.sessionLogger.info(`sessionLogout ignored as in state ${sessionState.state}`)\n }\n }\n }\n\n public done (): void {\n switch (this.sessionState.state) {\n case SessionState.InitiationLogonResponse:\n case SessionState.ActiveNormalSession:\n case SessionState.InitiationLogonReceived: {\n this.sessionLogout()\n break\n }\n\n case SessionState.Stopped:\n this.sessionLogger.info('done. session is now stopped')\n break\n\n default: {\n this.stop()\n break\n }\n }\n this.sessionLogger.info(`done. check logout sequence state ${this.stateString()}`)\n }\n\n public reset (resetSeqNum?: number | null): void {\n this.stopTimer()\n // unsubscribe from old transport before clearing reference\n this.unsubscribe()\n // reset parser to clear any partial message state from dropped connection\n const receiver: any = this.transport?.receiver\n if (receiver?.reset) {\n receiver.reset()\n }\n this.transport = null\n const resetFlag = this.config.description.ResetSeqNumFlag\n const seqNum = resetFlag ? 0 : resetSeqNum ?? this.sessionState.lastPeerMsgSeqNum\n this.sessionState.reset(seqNum) // from header def ... eventually\n this.onPrepareForReconnect()\n this.setState(SessionState.NetworkConnectionEstablished)\n }\n\n protected onPrepareForReconnect (): void {\n // Override in subclass to reset coordinator/transient state\n }\n\n /**\n * Called once as the session moves to Stopped, before the application is informed\n * via onStopped. Override to release session scoped resources - the ascii session\n * uses this to remove itself from the session registry. Mirrors cspurefix\n * FixSession.OnSessionStopping.\n */\n protected onSessionStopping (): void {\n // Default no-op\n }\n\n /**\n * Called for initiators after the store is loaded but before the Logon is sent.\n * Override to reset sequences/store when ResetSeqNumFlag=Y so the Logon goes out\n * with MsgSeqNum=1 instead of the recovered sender seq num.\n */\n protected async onPreLogon (): Promise<void> {\n // Default no-op\n }\n\n /**\n * Stop this session on behalf of an external caller - typically the session registry\n * when a new connection for the same SessionId replaces this one, but equally an\n * application which has decided the peer is gone. Mirrors cspurefix\n * FixSession.RequestStop.\n * @param reason recorded in the session log and carried on the emitted error\n */\n public requestStop (reason: string): void {\n this.sessionLogger.info(`requestStop: ${reason}`)\n this.stop(new Error(reason))\n }\n\n protected stop (error: Error | null = null): void {\n if (this.sessionState.state === SessionState.Stopped) {\n return\n }\n this.stopTimer()\n this.unsubscribe()\n this.sessionLogger.info(`stop: kill transport ${this.transport?.id ?? 'none'}`)\n this.onSessionStopping()\n this.transport?.end()\n if (error) {\n this.sessionLogger.info(`stop: emit error ${error.message}`)\n this.emit('error', error)\n } else {\n this.emit('done')\n }\n\n this.setState(SessionState.Stopped)\n this.onStopped(error ?? undefined)\n this.transport = null\n }\n\n /**\n * dispatches a message into the subclass that inherits from FixSession. The view contains\n * the parsed message which has utility methods such as toObject(). The Ascii session provides\n * an implementation to handle admin level messages such as logon, hearbeat and resest request.\n * Any application messges are dispatched via onApplicationMsg where the application can action\n * the message.\n * @param msgType the string based msg type the view represents\n * @param view container for all parsed fields representing the received message.\n * @protected\n */\n protected abstract onMsg (msgType: string, view: MsgView): void\n /**\n * the parsed txt recieved from the peer application. Given the applicaton is\n * responible for maintaining the fix log, this can be used to persist all received\n * messages.\n * @param msgType the string based msg type the view represents\n * @param txt the received message where for Ascii, the wire SOH delimeter is replaced\n * with that specified in the config e.g. '|'\n * @protected\n */\n protected abstract onDecoded (msgType: string, txt: string): void\n /**\n * the formatted txt sent to the peer application as an outbound message. Given the applicaton is\n * responible for maintaining the fix log, this can be used to persist all transmitted\n * messages. use msgType for example to persist only trade capture messages to database\n * @param msgType the msg type representing the message.\n * @param txt the sent message where for Ascii, the wire SOH delimeter is replaced\n * with that specified in the config e.g. '|'\n * @protected\n */\n protected abstract onEncoded (msgType: string, txt: string): void\n /**\n * typically all session level messages are handled by AsciiSession and these are\n * application level such as MarketDataRefresh. This will represent the applications main\n * work functiono where responses can be sent back to the peer. If manageSession has been set false\n * (not recommended) all messages are sent to this function\n * @param msgType the msg type representing the message.\n * @param view a wrapper containing the parsed message received.\n * @protected\n */\n protected abstract onApplicationMsg (msgType: string, view: MsgView): void\n /**\n * at this point the application is ready to send messages - peer login has been achieved\n * and the session can be considered ready to use. In the case of an initiator the application\n * may at this point send for security definitions or send market data subscriptions\n * @param view the login message causing session to be ready\n * @protected\n */\n protected abstract onReady (view: MsgView): void\n\n /**\n * Inform application this session has now ended - either from logout or connection dropped\n * @param error if session has been terminated via an error it is provided\n * @protected\n */\n protected abstract onStopped (error?: Error): void\n /**\n * Placeholder infomring the application of a peer login attempt.\n * @param view the login message\n * @param user extracted user from message\n * @param password extracted password from the message.\n * @protected\n */\n protected abstract onLogon (view: MsgView, user: string, password: string): boolean\n}\n"]}
@@ -57,8 +57,8 @@ export declare class ResendRequestManager {
57
57
  onGapFillReceived(gapFillSeq: number, newSeqNo: number, now: Date): void;
58
58
  tick(now: Date): void;
59
59
  reset(): void;
60
- get pending(): ReadonlyArray<PendingResendRange>;
61
- get history(): ReadonlyArray<ResendRequestRecord>;
60
+ get pending(): readonly PendingResendRange[];
61
+ get history(): readonly ResendRequestRecord[];
62
62
  get pendingCount(): number;
63
63
  get hasPendingRequests(): boolean;
64
64
  private isStorming;
@@ -1 +1 @@
1
- {"version":3,"file":"resend-request-manager.js","sourceRoot":"","sources":["../../../src/transport/session/resend-request-manager.ts"],"names":[],"mappings":"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","sourcesContent":["export enum ResendActionType {\n Nothing = 'Nothing',\n SendResendRequest = 'SendResendRequest',\n SendGapFill = 'SendGapFill',\n Wait = 'Wait'\n}\n\nexport class ResendAction {\n private constructor (\n public readonly type: ResendActionType,\n public readonly begin: number | null,\n public readonly end: number | null,\n public readonly reason: string | null\n ) {}\n\n static sendResendRequest (begin: number, end: number): ResendAction {\n return new ResendAction(ResendActionType.SendResendRequest, begin, end, null)\n }\n\n static gapFill (begin: number, end: number, reason: string): ResendAction {\n return new ResendAction(ResendActionType.SendGapFill, begin, end, reason)\n }\n\n static wait (reason: string): ResendAction {\n return new ResendAction(ResendActionType.Wait, null, null, reason)\n }\n\n static nothing (): ResendAction {\n return new ResendAction(ResendActionType.Nothing, null, null, null)\n }\n\n toString (): string {\n switch (this.type) {\n case ResendActionType.SendResendRequest:\n return `SendResendRequest(${this.begin}-${this.end})`\n case ResendActionType.SendGapFill:\n return `GapFill(${this.begin}-${this.end}): ${this.reason}`\n case ResendActionType.Wait:\n return `Wait: ${this.reason}`\n case ResendActionType.Nothing:\n return 'Nothing'\n default:\n return `Unknown(${this.type})`\n }\n }\n}\n\nexport class PendingResendRange {\n private readonly receivedSeqs: Set<number> = new Set()\n\n constructor (\n public readonly begin: number,\n public readonly end: number,\n public readonly sentAt: Date\n ) {}\n\n markReceived (seqNum: number): void {\n if (seqNum >= this.begin && seqNum <= this.end) {\n this.receivedSeqs.add(seqNum)\n }\n }\n\n markRangeReceived (fromSeq: number, toSeq: number): void {\n const start = Math.max(fromSeq, this.begin)\n const stop = Math.min(toSeq, this.end)\n for (let seq = start; seq <= stop; seq++) {\n this.receivedSeqs.add(seq)\n }\n }\n\n get isFullySatisfied (): boolean {\n return this.receivedSeqs.size >= (this.end - this.begin + 1)\n }\n\n fullyCovers (begin: number, end: number): boolean {\n return begin >= this.begin && end <= this.end\n }\n\n overlaps (begin: number, end: number): boolean {\n return begin <= this.end && end >= this.begin\n }\n\n get pendingCount (): number {\n return (this.end - this.begin + 1) - this.receivedSeqs.size\n }\n\n toString (): string {\n return `Pending(${this.begin}-${this.end}, received=${this.receivedSeqs.size}/${this.end - this.begin + 1})`\n }\n}\n\nexport interface ResendRequestRecord {\n begin: number\n end: number\n sentAt: Date\n reason: string | null\n}\n\nexport interface ResendManagerConfig {\n maxPendingRequests?: number\n maxRequestsPerWindow?: number\n rateLimitWindowSeconds?: number\n requestTimeoutSeconds?: number\n}\n\n/**\n * Manages ResendRequest strategy with intelligent overlap handling and storm protection.\n *\n * Philosophy:\n * 1. Stay alive — don't let resend handling crash or overwhelm the session\n * 2. Keep receiving new messages — don't block normal message flow\n * 3. Don't make things worse — avoid storms, handle overlaps intelligently\n */\nexport class ResendRequestManager {\n private readonly pendingRequests: PendingResendRange[] = []\n private readonly requestHistory: ResendRequestRecord[] = []\n private readonly maxPendingRequests: number\n private readonly maxRequestsPerWindow: number\n private readonly rateLimitWindowMs: number\n private readonly requestTimeoutMs: number\n private readonly recentRequestTimes: Date[] = []\n\n constructor (config?: ResendManagerConfig) {\n this.maxPendingRequests = config?.maxPendingRequests ?? 1\n this.maxRequestsPerWindow = config?.maxRequestsPerWindow ?? 5\n this.rateLimitWindowMs = (config?.rateLimitWindowSeconds ?? 10) * 1000\n this.requestTimeoutMs = (config?.requestTimeoutSeconds ?? 30) * 1000\n }\n\n computeAction (expectedSeq: number, receivedSeq: number, now: Date): ResendAction {\n if (receivedSeq <= expectedSeq) {\n return ResendAction.nothing()\n }\n\n const gapBegin = expectedSeq\n const gapEnd = receivedSeq - 1\n\n this.cleanupTimedOutRequests(now)\n\n if (this.isStorming(now)) {\n return ResendAction.gapFill(gapBegin, gapEnd, 'storm protection - too many requests')\n }\n\n for (const pending of this.pendingRequests) {\n if (pending.fullyCovers(gapBegin, gapEnd)) {\n return ResendAction.wait(`fully covered by pending request ${pending}`)\n }\n }\n\n const uncovered = this.computeUncoveredRange(gapBegin, gapEnd)\n if (!uncovered) {\n return ResendAction.wait('gap partially covered, waiting for pending requests')\n }\n\n if (this.pendingRequests.length >= this.maxPendingRequests) {\n return ResendAction.wait(`max pending requests (${this.maxPendingRequests}) reached`)\n }\n\n return ResendAction.sendResendRequest(uncovered.begin, uncovered.end)\n }\n\n recordRequestSent (begin: number, end: number, now: Date, reason: string | null = null): void {\n this.pendingRequests.push(new PendingResendRange(begin, end, now))\n this.requestHistory.push({ begin, end, sentAt: now, reason })\n this.recentRequestTimes.push(now)\n }\n\n onMessageReceived (seqNum: number, possDupFlag: boolean, now: Date): void {\n for (const pending of this.pendingRequests) {\n pending.markReceived(seqNum)\n }\n this.removeFullySatisfied()\n }\n\n onGapFillReceived (gapFillSeq: number, newSeqNo: number, now: Date): void {\n for (const pending of this.pendingRequests) {\n pending.markRangeReceived(gapFillSeq, newSeqNo - 1)\n }\n this.removeFullySatisfied()\n }\n\n tick (now: Date): void {\n this.cleanupTimedOutRequests(now)\n this.cleanupRateLimitWindow(now)\n }\n\n reset (): void {\n this.pendingRequests.length = 0\n this.recentRequestTimes.length = 0\n // keep requestHistory for debugging\n }\n\n get pending (): ReadonlyArray<PendingResendRange> {\n return this.pendingRequests.slice()\n }\n\n get history (): ReadonlyArray<ResendRequestRecord> {\n return this.requestHistory.slice()\n }\n\n get pendingCount (): number {\n return this.pendingRequests.length\n }\n\n get hasPendingRequests (): boolean {\n return this.pendingRequests.length > 0\n }\n\n private isStorming (now: Date): boolean {\n this.cleanupRateLimitWindow(now)\n return this.recentRequestTimes.length >= this.maxRequestsPerWindow\n }\n\n private cleanupRateLimitWindow (now: Date): void {\n const cutoff = now.getTime() - this.rateLimitWindowMs\n while (this.recentRequestTimes.length > 0 && this.recentRequestTimes[0].getTime() < cutoff) {\n this.recentRequestTimes.shift()\n }\n }\n\n private cleanupTimedOutRequests (now: Date): void {\n const cutoff = now.getTime() - this.requestTimeoutMs\n for (let i = this.pendingRequests.length - 1; i >= 0; i--) {\n if (this.pendingRequests[i].sentAt.getTime() < cutoff) {\n this.pendingRequests.splice(i, 1)\n }\n }\n }\n\n private removeFullySatisfied (): void {\n for (let i = this.pendingRequests.length - 1; i >= 0; i--) {\n if (this.pendingRequests[i].isFullySatisfied) {\n this.pendingRequests.splice(i, 1)\n }\n }\n }\n\n private computeUncoveredRange (gapBegin: number, gapEnd: number): { begin: number, end: number } | null {\n if (this.pendingRequests.length === 0) {\n return { begin: gapBegin, end: gapEnd }\n }\n\n const covered: Set<number> = new Set()\n for (const pending of this.pendingRequests) {\n for (let seq = pending.begin; seq <= pending.end; seq++) {\n covered.add(seq)\n }\n }\n\n let uncoveredBegin: number | null = null\n let uncoveredEnd: number | null = null\n\n for (let seq = gapBegin; seq <= gapEnd; seq++) {\n if (!covered.has(seq)) {\n if (uncoveredBegin === null) uncoveredBegin = seq\n uncoveredEnd = seq\n } else if (uncoveredBegin !== null) {\n break\n }\n }\n\n if (uncoveredBegin !== null && uncoveredEnd !== null) {\n return { begin: uncoveredBegin, end: uncoveredEnd }\n }\n\n return null\n }\n}\n"]}
1
+ {"version":3,"file":"resend-request-manager.js","sourceRoot":"","sources":["../../../src/transport/session/resend-request-manager.ts"],"names":[],"mappings":"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","sourcesContent":["export enum ResendActionType {\n Nothing = 'Nothing',\n SendResendRequest = 'SendResendRequest',\n SendGapFill = 'SendGapFill',\n Wait = 'Wait'\n}\n\nexport class ResendAction {\n private constructor (\n public readonly type: ResendActionType,\n public readonly begin: number | null,\n public readonly end: number | null,\n public readonly reason: string | null\n ) {}\n\n static sendResendRequest (begin: number, end: number): ResendAction {\n return new ResendAction(ResendActionType.SendResendRequest, begin, end, null)\n }\n\n static gapFill (begin: number, end: number, reason: string): ResendAction {\n return new ResendAction(ResendActionType.SendGapFill, begin, end, reason)\n }\n\n static wait (reason: string): ResendAction {\n return new ResendAction(ResendActionType.Wait, null, null, reason)\n }\n\n static nothing (): ResendAction {\n return new ResendAction(ResendActionType.Nothing, null, null, null)\n }\n\n toString (): string {\n switch (this.type) {\n case ResendActionType.SendResendRequest:\n return `SendResendRequest(${this.begin}-${this.end})`\n case ResendActionType.SendGapFill:\n return `GapFill(${this.begin}-${this.end}): ${this.reason}`\n case ResendActionType.Wait:\n return `Wait: ${this.reason}`\n case ResendActionType.Nothing:\n return 'Nothing'\n default:\n return `Unknown(${this.type})`\n }\n }\n}\n\nexport class PendingResendRange {\n private readonly receivedSeqs = new Set<number>()\n\n constructor (\n public readonly begin: number,\n public readonly end: number,\n public readonly sentAt: Date\n ) {}\n\n markReceived (seqNum: number): void {\n if (seqNum >= this.begin && seqNum <= this.end) {\n this.receivedSeqs.add(seqNum)\n }\n }\n\n markRangeReceived (fromSeq: number, toSeq: number): void {\n const start = Math.max(fromSeq, this.begin)\n const stop = Math.min(toSeq, this.end)\n for (let seq = start; seq <= stop; seq++) {\n this.receivedSeqs.add(seq)\n }\n }\n\n get isFullySatisfied (): boolean {\n return this.receivedSeqs.size >= (this.end - this.begin + 1)\n }\n\n fullyCovers (begin: number, end: number): boolean {\n return begin >= this.begin && end <= this.end\n }\n\n overlaps (begin: number, end: number): boolean {\n return begin <= this.end && end >= this.begin\n }\n\n get pendingCount (): number {\n return (this.end - this.begin + 1) - this.receivedSeqs.size\n }\n\n toString (): string {\n return `Pending(${this.begin}-${this.end}, received=${this.receivedSeqs.size}/${this.end - this.begin + 1})`\n }\n}\n\nexport interface ResendRequestRecord {\n begin: number\n end: number\n sentAt: Date\n reason: string | null\n}\n\nexport interface ResendManagerConfig {\n maxPendingRequests?: number\n maxRequestsPerWindow?: number\n rateLimitWindowSeconds?: number\n requestTimeoutSeconds?: number\n}\n\n/**\n * Manages ResendRequest strategy with intelligent overlap handling and storm protection.\n *\n * Philosophy:\n * 1. Stay alive — don't let resend handling crash or overwhelm the session\n * 2. Keep receiving new messages — don't block normal message flow\n * 3. Don't make things worse — avoid storms, handle overlaps intelligently\n */\nexport class ResendRequestManager {\n private readonly pendingRequests: PendingResendRange[] = []\n private readonly requestHistory: ResendRequestRecord[] = []\n private readonly maxPendingRequests: number\n private readonly maxRequestsPerWindow: number\n private readonly rateLimitWindowMs: number\n private readonly requestTimeoutMs: number\n private readonly recentRequestTimes: Date[] = []\n\n constructor (config?: ResendManagerConfig) {\n this.maxPendingRequests = config?.maxPendingRequests ?? 1\n this.maxRequestsPerWindow = config?.maxRequestsPerWindow ?? 5\n this.rateLimitWindowMs = (config?.rateLimitWindowSeconds ?? 10) * 1000\n this.requestTimeoutMs = (config?.requestTimeoutSeconds ?? 30) * 1000\n }\n\n computeAction (expectedSeq: number, receivedSeq: number, now: Date): ResendAction {\n if (receivedSeq <= expectedSeq) {\n return ResendAction.nothing()\n }\n\n const gapBegin = expectedSeq\n const gapEnd = receivedSeq - 1\n\n this.cleanupTimedOutRequests(now)\n\n if (this.isStorming(now)) {\n return ResendAction.gapFill(gapBegin, gapEnd, 'storm protection - too many requests')\n }\n\n for (const pending of this.pendingRequests) {\n if (pending.fullyCovers(gapBegin, gapEnd)) {\n return ResendAction.wait(`fully covered by pending request ${pending}`)\n }\n }\n\n const uncovered = this.computeUncoveredRange(gapBegin, gapEnd)\n if (!uncovered) {\n return ResendAction.wait('gap partially covered, waiting for pending requests')\n }\n\n if (this.pendingRequests.length >= this.maxPendingRequests) {\n return ResendAction.wait(`max pending requests (${this.maxPendingRequests}) reached`)\n }\n\n return ResendAction.sendResendRequest(uncovered.begin, uncovered.end)\n }\n\n recordRequestSent (begin: number, end: number, now: Date, reason: string | null = null): void {\n this.pendingRequests.push(new PendingResendRange(begin, end, now))\n this.requestHistory.push({ begin, end, sentAt: now, reason })\n this.recentRequestTimes.push(now)\n }\n\n onMessageReceived (seqNum: number, possDupFlag: boolean, now: Date): void {\n for (const pending of this.pendingRequests) {\n pending.markReceived(seqNum)\n }\n this.removeFullySatisfied()\n }\n\n onGapFillReceived (gapFillSeq: number, newSeqNo: number, now: Date): void {\n for (const pending of this.pendingRequests) {\n pending.markRangeReceived(gapFillSeq, newSeqNo - 1)\n }\n this.removeFullySatisfied()\n }\n\n tick (now: Date): void {\n this.cleanupTimedOutRequests(now)\n this.cleanupRateLimitWindow(now)\n }\n\n reset (): void {\n this.pendingRequests.length = 0\n this.recentRequestTimes.length = 0\n // keep requestHistory for debugging\n }\n\n get pending (): readonly PendingResendRange[] {\n return this.pendingRequests.slice()\n }\n\n get history (): readonly ResendRequestRecord[] {\n return this.requestHistory.slice()\n }\n\n get pendingCount (): number {\n return this.pendingRequests.length\n }\n\n get hasPendingRequests (): boolean {\n return this.pendingRequests.length > 0\n }\n\n private isStorming (now: Date): boolean {\n this.cleanupRateLimitWindow(now)\n return this.recentRequestTimes.length >= this.maxRequestsPerWindow\n }\n\n private cleanupRateLimitWindow (now: Date): void {\n const cutoff = now.getTime() - this.rateLimitWindowMs\n while (this.recentRequestTimes.length > 0 && this.recentRequestTimes[0].getTime() < cutoff) {\n this.recentRequestTimes.shift()\n }\n }\n\n private cleanupTimedOutRequests (now: Date): void {\n const cutoff = now.getTime() - this.requestTimeoutMs\n for (let i = this.pendingRequests.length - 1; i >= 0; i--) {\n if (this.pendingRequests[i].sentAt.getTime() < cutoff) {\n this.pendingRequests.splice(i, 1)\n }\n }\n }\n\n private removeFullySatisfied (): void {\n for (let i = this.pendingRequests.length - 1; i >= 0; i--) {\n if (this.pendingRequests[i].isFullySatisfied) {\n this.pendingRequests.splice(i, 1)\n }\n }\n }\n\n private computeUncoveredRange (gapBegin: number, gapEnd: number): { begin: number, end: number } | null {\n if (this.pendingRequests.length === 0) {\n return { begin: gapBegin, end: gapEnd }\n }\n\n const covered = new Set<number>()\n for (const pending of this.pendingRequests) {\n for (let seq = pending.begin; seq <= pending.end; seq++) {\n covered.add(seq)\n }\n }\n\n let uncoveredBegin: number | null = null\n let uncoveredEnd: number | null = null\n\n for (let seq = gapBegin; seq <= gapEnd; seq++) {\n if (!covered.has(seq)) {\n if (uncoveredBegin === null) uncoveredBegin = seq\n uncoveredEnd = seq\n } else if (uncoveredBegin !== null) {\n break\n }\n }\n\n if (uncoveredBegin !== null && uncoveredEnd !== null) {\n return { begin: uncoveredBegin, end: uncoveredEnd }\n }\n\n return null\n }\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import { IMsgApplication } from '../msg-application';
2
2
  import { StoreConfig } from '../../store/store-config';
3
+ import { ILooseObject } from '../../collections/collection';
3
4
  export interface IDynamicSessionParams {
4
5
  readonly Name: string;
5
6
  readonly SenderCompId: string;
@@ -13,6 +14,7 @@ export interface IDynamicSessionParams {
13
14
  }
14
15
  export interface ISessionDescription extends IDynamicSessionParams {
15
16
  readonly application?: IMsgApplication;
17
+ readonly Logon?: ILooseObject;
16
18
  HeartBtInt: number;
17
19
  LastSentSeqNum?: number;
18
20
  readonly LastReceivedSeqNum?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"session-description.js","sourceRoot":"","sources":["../../../src/transport/session/session-description.ts"],"names":[],"mappings":"","sourcesContent":["import { IMsgApplication } from '../msg-application'\nimport { StoreConfig } from '../../store/store-config'\n\nexport interface IDynamicSessionParams {\n readonly Name: string\n readonly SenderCompId: string\n readonly TargetCompID: string\n readonly ResetSeqNumFlag: boolean\n readonly SenderSubID: string\n readonly TargetSubID: string\n readonly BeginString: string\n readonly Username: string\n readonly Password: string\n}\n\nexport interface ISessionDescription extends IDynamicSessionParams {\n readonly application?: IMsgApplication\n HeartBtInt: number\n LastSentSeqNum?: number\n readonly LastReceivedSeqNum?: number\n readonly BodyLengthChars?: number\n readonly store?: StoreConfig\n}\n"]}
1
+ {"version":3,"file":"session-description.js","sourceRoot":"","sources":["../../../src/transport/session/session-description.ts"],"names":[],"mappings":"","sourcesContent":["import { IMsgApplication } from '../msg-application'\nimport { StoreConfig } from '../../store/store-config'\nimport { ILooseObject } from '../../collections/collection'\n\nexport interface IDynamicSessionParams {\n readonly Name: string\n readonly SenderCompId: string\n readonly TargetCompID: string\n readonly ResetSeqNumFlag: boolean\n readonly SenderSubID: string\n readonly TargetSubID: string\n readonly BeginString: string\n readonly Username: string\n readonly Password: string\n}\n\nexport interface ISessionDescription extends IDynamicSessionParams {\n readonly application?: IMsgApplication\n /**\n * Extra fields to place on the outbound Logon, merged over the ones the engine\n * derives from this description. Counterparties routinely demand a tag the\n * standard Logon does not carry - an account, a default application version, a\n * free text token - and this is the no-code way to send it:\n *\n * \"Logon\": { \"Account\": \"TVKD_001\", \"DefaultApplVerID\": \"9\" }\n *\n * A field named here still has to exist on Logon in the dictionary the session\n * loads, otherwise the encoder has no tag to write it to. Set a field to null to\n * suppress one the engine would otherwise send. See issues #93, #39, #96.\n */\n readonly Logon?: ILooseObject\n HeartBtInt: number\n LastSentSeqNum?: number\n readonly LastReceivedSeqNum?: number\n readonly BodyLengthChars?: number\n readonly store?: StoreConfig\n}\n"]}
@@ -23,7 +23,7 @@ export declare class SessionSequenceCoordinator {
23
23
  onGapFillReceived(gapFillSeq: number, newSeqNo: number): Promise<void>;
24
24
  onGapDetected(expectedSeq: number, receivedSeq: number): ResendAction;
25
25
  recordResendRequestSent(begin: number, end: number): void;
26
- get pendingResendRequests(): ReadonlyArray<PendingResendRange>;
26
+ get pendingResendRequests(): readonly PendingResendRange[];
27
27
  onLogonRejectedForSequence(maxRetries?: number): boolean;
28
28
  resetLogonRetryCount(): void;
29
29
  get logonRetryCount(): number;
@@ -1 +1 @@
1
- {"version":3,"file":"session-sequence-coordinator.js","sourceRoot":"","sources":["../../../src/transport/session/session-sequence-coordinator.ts"],"names":[],"mappings":"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","sourcesContent":["import { ISessionSequenceStore } from './session-sequence-store'\nimport { IFixClock } from './fix-clock'\nimport { ResendAction, ResendManagerConfig, ResendRequestManager, PendingResendRange } from './resend-request-manager'\n\n/**\n * Coordinates all sequence-related state for a FIX session.\n *\n * This is the SINGLE SOURCE OF TRUTH for:\n * - Outgoing sequence numbers (what we send)\n * - Expected incoming sequence numbers (what we expect from peer)\n * - Resend request tracking\n * - Reset coordination\n *\n * Philosophy:\n * 1. All sequence state lives here — no scattered state across components\n * 2. All reset operations are coordinated through here\n * 3. Components query this for sequence numbers, don't maintain their own\n * 4. Fully testable without running actual sessions\n */\nexport class SessionSequenceCoordinator {\n private readonly store: ISessionSequenceStore\n private readonly resendManager: ResendRequestManager\n private readonly clock: IFixClock\n\n // THE source of truth for sequence numbers\n private nextSenderSeqNumValue: number = 1\n private expectedTargetSeqNumValue: number = 1\n\n // Track the last peer sequence we actually processed\n private lastProcessedPeerSeqNumValue: number = 0\n\n // Transient session state (reset on reconnect)\n private logonRetryCountValue: number = 0\n private timeoutRecoveryAttemptsValue: number = 0\n\n // Event callback for full session reset\n public onSessionReset: (() => Promise<void>) | null = null\n\n constructor (\n store: ISessionSequenceStore,\n clock: IFixClock,\n resendManagerConfig?: ResendManagerConfig\n ) {\n this.store = store\n this.clock = clock\n this.resendManager = new ResendRequestManager(resendManagerConfig)\n }\n\n // ── Initialization ──\n\n initializeFromStore (): void {\n this.nextSenderSeqNumValue = this.store.senderSeqNum\n this.expectedTargetSeqNumValue = this.store.targetSeqNum\n this.lastProcessedPeerSeqNumValue = this.expectedTargetSeqNumValue - 1\n }\n\n initializeFromConfig (senderSeqNum?: number, targetSeqNum?: number): void {\n if (senderSeqNum !== undefined) {\n this.nextSenderSeqNumValue = senderSeqNum\n }\n if (targetSeqNum !== undefined) {\n this.expectedTargetSeqNumValue = targetSeqNum\n this.lastProcessedPeerSeqNumValue = targetSeqNum - 1\n }\n }\n\n // ── Sequence Access (Read) ──\n\n get nextSenderSeqNum (): number {\n return this.nextSenderSeqNumValue\n }\n\n get expectedTargetSeqNum (): number {\n return this.expectedTargetSeqNumValue\n }\n\n get lastProcessedPeerSeqNum (): number {\n return this.lastProcessedPeerSeqNumValue\n }\n\n // ── Sequence Mutations (Controlled) ──\n\n /**\n * Consumes and returns the next sender sequence number.\n * Call when encoding a message (not for PossDup resends).\n */\n getNextSenderSeqNum (isPossDup: boolean = false): number {\n const seq = this.nextSenderSeqNumValue\n if (!isPossDup) {\n this.nextSenderSeqNumValue++\n }\n return seq\n }\n\n /**\n * Records that a message was successfully encoded and will be sent.\n * Updates the store's sender sequence number.\n */\n async onMessageEncoded (seqNum: number, isPossDup: boolean): Promise<void> {\n if (isPossDup) return\n await this.store.setSenderSeqNum(seqNum + 1)\n }\n\n /**\n * Called when a message is received from the peer.\n * Updates expected sequence and resend tracking.\n * Returns true if message should be processed, false if duplicate/old.\n */\n async onMessageReceived (seqNum: number, possDupFlag: boolean): Promise<boolean> {\n const now = this.clock.now()\n\n this.resendManager.onMessageReceived(seqNum, possDupFlag, now)\n\n if (possDupFlag) {\n return true\n }\n\n if (seqNum < this.expectedTargetSeqNumValue) {\n return false\n }\n\n if (seqNum === this.expectedTargetSeqNumValue) {\n this.lastProcessedPeerSeqNumValue = seqNum\n this.expectedTargetSeqNumValue = seqNum + 1\n } else if (seqNum > this.expectedTargetSeqNumValue) {\n // Gap detected — session accepts this message and moves on\n this.lastProcessedPeerSeqNumValue = seqNum\n this.expectedTargetSeqNumValue = seqNum + 1\n }\n\n await this.store.setTargetSeqNum(this.expectedTargetSeqNumValue)\n return true\n }\n\n /**\n * Called when a SequenceReset-GapFill is received.\n */\n async onGapFillReceived (gapFillSeq: number, newSeqNo: number): Promise<void> {\n const now = this.clock.now()\n this.resendManager.onGapFillReceived(gapFillSeq, newSeqNo, now)\n\n // GapFill says \"skip from gapFillSeq to newSeqNo\" — never rewind\n if (newSeqNo > this.expectedTargetSeqNumValue) {\n this.expectedTargetSeqNumValue = newSeqNo\n this.lastProcessedPeerSeqNumValue = Math.max(this.lastProcessedPeerSeqNumValue, newSeqNo - 1)\n }\n\n await this.store.setTargetSeqNum(this.expectedTargetSeqNumValue)\n }\n\n // ── Gap Detection and Resend Requests ──\n\n onGapDetected (expectedSeq: number, receivedSeq: number): ResendAction {\n const now = this.clock.now()\n return this.resendManager.computeAction(expectedSeq, receivedSeq, now)\n }\n\n recordResendRequestSent (begin: number, end: number): void {\n const now = this.clock.now()\n this.resendManager.recordRequestSent(begin, end, now)\n }\n\n get pendingResendRequests (): ReadonlyArray<PendingResendRange> {\n return this.resendManager.pending\n }\n\n // ── Logon Retry Logic ──\n\n onLogonRejectedForSequence (maxRetries: number = 10): boolean {\n this.logonRetryCountValue++\n if (this.logonRetryCountValue <= maxRetries) {\n this.nextSenderSeqNumValue++\n return true\n }\n return false\n }\n\n resetLogonRetryCount (): void {\n this.logonRetryCountValue = 0\n }\n\n get logonRetryCount (): number {\n return this.logonRetryCountValue\n }\n\n // ── Timeout Recovery ──\n\n incrementTimeoutRecovery (maxAttempts: number = 3): boolean {\n this.timeoutRecoveryAttemptsValue++\n return this.timeoutRecoveryAttemptsValue <= maxAttempts\n }\n\n resetTimeoutRecovery (): void {\n this.timeoutRecoveryAttemptsValue = 0\n }\n\n get timeoutRecoveryAttempts (): number {\n return this.timeoutRecoveryAttemptsValue\n }\n\n // ── Reset Operations ──\n\n /**\n * Prepares for reconnection on the same session.\n * Resets transient state but preserves sequence numbers.\n */\n prepareForReconnect (): void {\n this.logonRetryCountValue = 0\n this.timeoutRecoveryAttemptsValue = 0\n this.resendManager.reset()\n }\n\n /**\n * Full session reset — clears store, resets sequences to 1.\n * Call when ResetSeqNumFlag=Y is being used.\n */\n async resetSession (reason: string): Promise<void> {\n this.nextSenderSeqNumValue = 1\n this.expectedTargetSeqNumValue = 1\n this.lastProcessedPeerSeqNumValue = 0\n this.logonRetryCountValue = 0\n this.timeoutRecoveryAttemptsValue = 0\n this.resendManager.reset()\n\n await this.store.reset()\n\n if (this.onSessionReset) {\n await this.onSessionReset()\n }\n }\n\n /**\n * Handles peer's ResetSeqNumFlag=Y in their Logon.\n */\n async handlePeerReset (peerSeqNum: number, weAlsoReset: boolean): Promise<void> {\n const savedSenderSeq = weAlsoReset ? this.nextSenderSeqNumValue : null\n\n await this.store.reset()\n\n this.nextSenderSeqNumValue = savedSenderSeq ?? this.store.senderSeqNum\n this.expectedTargetSeqNumValue = peerSeqNum + 1\n this.lastProcessedPeerSeqNumValue = peerSeqNum\n this.resendManager.reset()\n\n await this.store.setTargetSeqNum(this.expectedTargetSeqNumValue)\n\n if (this.onSessionReset) {\n await this.onSessionReset()\n }\n }\n\n /**\n * Handles acceptor responding with ResetSeqNumFlag=Y when peer didn't request it.\n */\n async resetAsAcceptor (): Promise<void> {\n this.nextSenderSeqNumValue = 1\n this.expectedTargetSeqNumValue = 1\n this.lastProcessedPeerSeqNumValue = 0\n this.resendManager.reset()\n\n await this.store.reset()\n await this.store.setTargetSeqNum(1)\n }\n\n /**\n * Periodic tick — cleans up stale resend requests.\n */\n tick (): void {\n const now = this.clock.now()\n this.resendManager.tick(now)\n }\n}\n"]}
1
+ {"version":3,"file":"session-sequence-coordinator.js","sourceRoot":"","sources":["../../../src/transport/session/session-sequence-coordinator.ts"],"names":[],"mappings":"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","sourcesContent":["import { ISessionSequenceStore } from './session-sequence-store'\nimport { IFixClock } from './fix-clock'\nimport { ResendAction, ResendManagerConfig, ResendRequestManager, PendingResendRange } from './resend-request-manager'\n\n/**\n * Coordinates all sequence-related state for a FIX session.\n *\n * This is the SINGLE SOURCE OF TRUTH for:\n * - Outgoing sequence numbers (what we send)\n * - Expected incoming sequence numbers (what we expect from peer)\n * - Resend request tracking\n * - Reset coordination\n *\n * Philosophy:\n * 1. All sequence state lives here — no scattered state across components\n * 2. All reset operations are coordinated through here\n * 3. Components query this for sequence numbers, don't maintain their own\n * 4. Fully testable without running actual sessions\n */\nexport class SessionSequenceCoordinator {\n private readonly store: ISessionSequenceStore\n private readonly resendManager: ResendRequestManager\n private readonly clock: IFixClock\n\n // THE source of truth for sequence numbers\n private nextSenderSeqNumValue: number = 1\n private expectedTargetSeqNumValue: number = 1\n\n // Track the last peer sequence we actually processed\n private lastProcessedPeerSeqNumValue: number = 0\n\n // Transient session state (reset on reconnect)\n private logonRetryCountValue: number = 0\n private timeoutRecoveryAttemptsValue: number = 0\n\n // Event callback for full session reset\n public onSessionReset: (() => Promise<void>) | null = null\n\n constructor (\n store: ISessionSequenceStore,\n clock: IFixClock,\n resendManagerConfig?: ResendManagerConfig\n ) {\n this.store = store\n this.clock = clock\n this.resendManager = new ResendRequestManager(resendManagerConfig)\n }\n\n // ── Initialization ──\n\n initializeFromStore (): void {\n this.nextSenderSeqNumValue = this.store.senderSeqNum\n this.expectedTargetSeqNumValue = this.store.targetSeqNum\n this.lastProcessedPeerSeqNumValue = this.expectedTargetSeqNumValue - 1\n }\n\n initializeFromConfig (senderSeqNum?: number, targetSeqNum?: number): void {\n if (senderSeqNum !== undefined) {\n this.nextSenderSeqNumValue = senderSeqNum\n }\n if (targetSeqNum !== undefined) {\n this.expectedTargetSeqNumValue = targetSeqNum\n this.lastProcessedPeerSeqNumValue = targetSeqNum - 1\n }\n }\n\n // ── Sequence Access (Read) ──\n\n get nextSenderSeqNum (): number {\n return this.nextSenderSeqNumValue\n }\n\n get expectedTargetSeqNum (): number {\n return this.expectedTargetSeqNumValue\n }\n\n get lastProcessedPeerSeqNum (): number {\n return this.lastProcessedPeerSeqNumValue\n }\n\n // ── Sequence Mutations (Controlled) ──\n\n /**\n * Consumes and returns the next sender sequence number.\n * Call when encoding a message (not for PossDup resends).\n */\n getNextSenderSeqNum (isPossDup: boolean = false): number {\n const seq = this.nextSenderSeqNumValue\n if (!isPossDup) {\n this.nextSenderSeqNumValue++\n }\n return seq\n }\n\n /**\n * Records that a message was successfully encoded and will be sent.\n * Updates the store's sender sequence number.\n */\n async onMessageEncoded (seqNum: number, isPossDup: boolean): Promise<void> {\n if (isPossDup) return\n await this.store.setSenderSeqNum(seqNum + 1)\n }\n\n /**\n * Called when a message is received from the peer.\n * Updates expected sequence and resend tracking.\n * Returns true if message should be processed, false if duplicate/old.\n */\n async onMessageReceived (seqNum: number, possDupFlag: boolean): Promise<boolean> {\n const now = this.clock.now()\n\n this.resendManager.onMessageReceived(seqNum, possDupFlag, now)\n\n if (possDupFlag) {\n return true\n }\n\n if (seqNum < this.expectedTargetSeqNumValue) {\n return false\n }\n\n if (seqNum === this.expectedTargetSeqNumValue) {\n this.lastProcessedPeerSeqNumValue = seqNum\n this.expectedTargetSeqNumValue = seqNum + 1\n } else if (seqNum > this.expectedTargetSeqNumValue) {\n // Gap detected — session accepts this message and moves on\n this.lastProcessedPeerSeqNumValue = seqNum\n this.expectedTargetSeqNumValue = seqNum + 1\n }\n\n await this.store.setTargetSeqNum(this.expectedTargetSeqNumValue)\n return true\n }\n\n /**\n * Called when a SequenceReset-GapFill is received.\n */\n async onGapFillReceived (gapFillSeq: number, newSeqNo: number): Promise<void> {\n const now = this.clock.now()\n this.resendManager.onGapFillReceived(gapFillSeq, newSeqNo, now)\n\n // GapFill says \"skip from gapFillSeq to newSeqNo\" — never rewind\n if (newSeqNo > this.expectedTargetSeqNumValue) {\n this.expectedTargetSeqNumValue = newSeqNo\n this.lastProcessedPeerSeqNumValue = Math.max(this.lastProcessedPeerSeqNumValue, newSeqNo - 1)\n }\n\n await this.store.setTargetSeqNum(this.expectedTargetSeqNumValue)\n }\n\n // ── Gap Detection and Resend Requests ──\n\n onGapDetected (expectedSeq: number, receivedSeq: number): ResendAction {\n const now = this.clock.now()\n return this.resendManager.computeAction(expectedSeq, receivedSeq, now)\n }\n\n recordResendRequestSent (begin: number, end: number): void {\n const now = this.clock.now()\n this.resendManager.recordRequestSent(begin, end, now)\n }\n\n get pendingResendRequests (): readonly PendingResendRange[] {\n return this.resendManager.pending\n }\n\n // ── Logon Retry Logic ──\n\n onLogonRejectedForSequence (maxRetries: number = 10): boolean {\n this.logonRetryCountValue++\n if (this.logonRetryCountValue <= maxRetries) {\n this.nextSenderSeqNumValue++\n return true\n }\n return false\n }\n\n resetLogonRetryCount (): void {\n this.logonRetryCountValue = 0\n }\n\n get logonRetryCount (): number {\n return this.logonRetryCountValue\n }\n\n // ── Timeout Recovery ──\n\n incrementTimeoutRecovery (maxAttempts: number = 3): boolean {\n this.timeoutRecoveryAttemptsValue++\n return this.timeoutRecoveryAttemptsValue <= maxAttempts\n }\n\n resetTimeoutRecovery (): void {\n this.timeoutRecoveryAttemptsValue = 0\n }\n\n get timeoutRecoveryAttempts (): number {\n return this.timeoutRecoveryAttemptsValue\n }\n\n // ── Reset Operations ──\n\n /**\n * Prepares for reconnection on the same session.\n * Resets transient state but preserves sequence numbers.\n */\n prepareForReconnect (): void {\n this.logonRetryCountValue = 0\n this.timeoutRecoveryAttemptsValue = 0\n this.resendManager.reset()\n }\n\n /**\n * Full session reset — clears store, resets sequences to 1.\n * Call when ResetSeqNumFlag=Y is being used.\n */\n async resetSession (reason: string): Promise<void> {\n this.nextSenderSeqNumValue = 1\n this.expectedTargetSeqNumValue = 1\n this.lastProcessedPeerSeqNumValue = 0\n this.logonRetryCountValue = 0\n this.timeoutRecoveryAttemptsValue = 0\n this.resendManager.reset()\n\n await this.store.reset()\n\n if (this.onSessionReset) {\n await this.onSessionReset()\n }\n }\n\n /**\n * Handles peer's ResetSeqNumFlag=Y in their Logon.\n */\n async handlePeerReset (peerSeqNum: number, weAlsoReset: boolean): Promise<void> {\n const savedSenderSeq = weAlsoReset ? this.nextSenderSeqNumValue : null\n\n await this.store.reset()\n\n this.nextSenderSeqNumValue = savedSenderSeq ?? this.store.senderSeqNum\n this.expectedTargetSeqNumValue = peerSeqNum + 1\n this.lastProcessedPeerSeqNumValue = peerSeqNum\n this.resendManager.reset()\n\n await this.store.setTargetSeqNum(this.expectedTargetSeqNumValue)\n\n if (this.onSessionReset) {\n await this.onSessionReset()\n }\n }\n\n /**\n * Handles acceptor responding with ResetSeqNumFlag=Y when peer didn't request it.\n */\n async resetAsAcceptor (): Promise<void> {\n this.nextSenderSeqNumValue = 1\n this.expectedTargetSeqNumValue = 1\n this.lastProcessedPeerSeqNumValue = 0\n this.resendManager.reset()\n\n await this.store.reset()\n await this.store.setTargetSeqNum(1)\n }\n\n /**\n * Periodic tick — cleans up stale resend requests.\n */\n tick (): void {\n const now = this.clock.now()\n this.resendManager.tick(now)\n }\n}\n"]}
@@ -1,9 +1,9 @@
1
1
  export interface ISessionSequenceStore {
2
2
  senderSeqNum: number;
3
3
  targetSeqNum: number;
4
- setSenderSeqNum(value: number): Promise<void>;
5
- setTargetSeqNum(value: number): Promise<void>;
6
- reset(): Promise<void>;
4
+ setSenderSeqNum: (value: number) => Promise<void>;
5
+ setTargetSeqNum: (value: number) => Promise<void>;
6
+ reset: () => Promise<void>;
7
7
  }
8
8
  export declare class MemorySequenceStore implements ISessionSequenceStore {
9
9
  senderSeqNum: number;
@@ -1 +1 @@
1
- {"version":3,"file":"session-sequence-store.js","sourceRoot":"","sources":["../../../src/transport/session/session-sequence-store.ts"],"names":[],"mappings":";;;;;;;;;;;;AAgBA,MAAa,mBAAmB;IAAhC;QACS,iBAAY,GAAW,CAAC,CAAA;QACxB,iBAAY,GAAW,CAAC,CAAA;IAcjC,CAAC;IAZO,eAAe,CAAE,KAAa;;YAClC,IAAI,CAAC,YAAY,GAAG,KAAK,CAAA;QAC3B,CAAC;KAAA;IAEK,eAAe,CAAE,KAAa;;YAClC,IAAI,CAAC,YAAY,GAAG,KAAK,CAAA;QAC3B,CAAC;KAAA;IAEK,KAAK;;YACT,IAAI,CAAC,YAAY,GAAG,CAAC,CAAA;YACrB,IAAI,CAAC,YAAY,GAAG,CAAC,CAAA;QACvB,CAAC;KAAA;CACF;AAhBD,kDAgBC","sourcesContent":["/**\n * Minimal store interface for the SessionSequenceCoordinator.\n * Both the existing FixMsgMemoryStore (adapted) and the future\n * IFixSessionStore will implement this.\n */\nexport interface ISessionSequenceStore {\n senderSeqNum: number\n targetSeqNum: number\n setSenderSeqNum (value: number): Promise<void>\n setTargetSeqNum (value: number): Promise<void>\n reset (): Promise<void>\n}\n\n/**\n * Simple in-memory implementation for testing and as default.\n */\nexport class MemorySequenceStore implements ISessionSequenceStore {\n public senderSeqNum: number = 1\n public targetSeqNum: number = 1\n\n async setSenderSeqNum (value: number): Promise<void> {\n this.senderSeqNum = value\n }\n\n async setTargetSeqNum (value: number): Promise<void> {\n this.targetSeqNum = value\n }\n\n async reset (): Promise<void> {\n this.senderSeqNum = 1\n this.targetSeqNum = 1\n }\n}\n"]}
1
+ {"version":3,"file":"session-sequence-store.js","sourceRoot":"","sources":["../../../src/transport/session/session-sequence-store.ts"],"names":[],"mappings":";;;;;;;;;;;;AAgBA,MAAa,mBAAmB;IAAhC;QACS,iBAAY,GAAW,CAAC,CAAA;QACxB,iBAAY,GAAW,CAAC,CAAA;IAcjC,CAAC;IAZO,eAAe,CAAE,KAAa;;YAClC,IAAI,CAAC,YAAY,GAAG,KAAK,CAAA;QAC3B,CAAC;KAAA;IAEK,eAAe,CAAE,KAAa;;YAClC,IAAI,CAAC,YAAY,GAAG,KAAK,CAAA;QAC3B,CAAC;KAAA;IAEK,KAAK;;YACT,IAAI,CAAC,YAAY,GAAG,CAAC,CAAA;YACrB,IAAI,CAAC,YAAY,GAAG,CAAC,CAAA;QACvB,CAAC;KAAA;CACF;AAhBD,kDAgBC","sourcesContent":["/**\n * Minimal store interface for the SessionSequenceCoordinator.\n * Both the existing FixMsgMemoryStore (adapted) and the future\n * IFixSessionStore will implement this.\n */\nexport interface ISessionSequenceStore {\n senderSeqNum: number\n targetSeqNum: number\n setSenderSeqNum: (value: number) => Promise<void>\n setTargetSeqNum: (value: number) => Promise<void>\n reset: () => Promise<void>\n}\n\n/**\n * Simple in-memory implementation for testing and as default.\n */\nexport class MemorySequenceStore implements ISessionSequenceStore {\n public senderSeqNum: number = 1\n public targetSeqNum: number = 1\n\n async setSenderSeqNum (value: number): Promise<void> {\n this.senderSeqNum = value\n }\n\n async setTargetSeqNum (value: number): Promise<void> {\n this.targetSeqNum = value\n }\n\n async reset (): Promise<void> {\n this.senderSeqNum = 1\n this.targetSeqNum = 1\n }\n}\n"]}