jspurefix 5.9.1 → 5.9.2

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 (94) hide show
  1. package/BACKPORT_PLAN.md +20 -7
  2. package/WORKLOG.md +119 -0
  3. package/dist/benchmark/bench-report.d.ts +5 -0
  4. package/dist/benchmark/bench-report.js +91 -0
  5. package/dist/benchmark/bench-report.js.map +1 -0
  6. package/dist/benchmark/bench-runner.d.ts +12 -0
  7. package/dist/benchmark/bench-runner.js +193 -0
  8. package/dist/benchmark/bench-runner.js.map +1 -0
  9. package/dist/benchmark/bench-stats.d.ts +10 -0
  10. package/dist/benchmark/bench-stats.js +56 -0
  11. package/dist/benchmark/bench-stats.js.map +1 -0
  12. package/dist/benchmark/bench-types.d.ts +74 -0
  13. package/dist/benchmark/bench-types.js +3 -0
  14. package/dist/benchmark/bench-types.js.map +1 -0
  15. package/dist/benchmark/gc-probe.d.ts +18 -0
  16. package/dist/benchmark/gc-probe.js +103 -0
  17. package/dist/benchmark/gc-probe.js.map +1 -0
  18. package/dist/benchmark/index.d.ts +6 -0
  19. package/dist/benchmark/index.js +23 -0
  20. package/dist/benchmark/index.js.map +1 -0
  21. package/dist/benchmark/parse-bench.d.ts +28 -0
  22. package/dist/benchmark/parse-bench.js +85 -0
  23. package/dist/benchmark/parse-bench.js.map +1 -0
  24. package/dist/buffer/ascii/ascii-parser.d.ts +1 -0
  25. package/dist/buffer/ascii/ascii-parser.js +4 -1
  26. package/dist/buffer/ascii/ascii-parser.js.map +1 -1
  27. package/dist/buffer/ascii/ascii-segment-parser.js +3 -2
  28. package/dist/buffer/ascii/ascii-segment-parser.js.map +1 -1
  29. package/dist/buffer/ascii/tag-index.js.map +1 -1
  30. package/dist/buffer/msg-view.d.ts +1 -0
  31. package/dist/buffer/msg-view.js +25 -19
  32. package/dist/buffer/msg-view.js.map +1 -1
  33. package/dist/dictionary/parser/quickfix/dictionary-validator.d.ts +1 -1
  34. package/dist/dictionary/parser/quickfix/dictionary-validator.js.map +1 -1
  35. package/dist/dictionary/parser/quickfix/quick-fix-graph-parser.d.ts +1 -1
  36. package/dist/dictionary/parser/quickfix/quick-fix-graph-parser.js +1 -2
  37. package/dist/dictionary/parser/quickfix/quick-fix-graph-parser.js.map +1 -1
  38. package/dist/dictionary/parser/quickfix/validation-error.d.ts +2 -2
  39. package/dist/dictionary/parser/quickfix/validation-error.js.map +1 -1
  40. package/dist/jsfix-bench.d.ts +1 -0
  41. package/dist/jsfix-bench.js +213 -0
  42. package/dist/jsfix-bench.js.map +1 -0
  43. package/dist/store/file-session-store.js +1 -1
  44. package/dist/store/file-session-store.js.map +1 -1
  45. package/dist/store/file-session-stream-provider.d.ts +1 -0
  46. package/dist/store/file-session-stream-provider.js +13 -13
  47. package/dist/store/file-session-stream-provider.js.map +1 -1
  48. package/dist/store/fix-session-store-factory.d.ts +1 -1
  49. package/dist/store/fix-session-store-factory.js.map +1 -1
  50. package/dist/store/fix-session-store.d.ts +11 -11
  51. package/dist/store/fix-session-store.js.map +1 -1
  52. package/dist/store/memory-session-store.js.map +1 -1
  53. package/dist/store/session-stream-provider.d.ts +13 -13
  54. package/dist/store/session-stream-provider.js.map +1 -1
  55. package/dist/transport/session/fix-clock.d.ts +1 -1
  56. package/dist/transport/session/fix-clock.js.map +1 -1
  57. package/dist/transport/session/resend-request-manager.d.ts +2 -2
  58. package/dist/transport/session/resend-request-manager.js.map +1 -1
  59. package/dist/transport/session/session-sequence-coordinator.d.ts +1 -1
  60. package/dist/transport/session/session-sequence-coordinator.js.map +1 -1
  61. package/dist/transport/session/session-sequence-store.d.ts +3 -3
  62. package/dist/transport/session/session-sequence-store.js.map +1 -1
  63. package/dist/transport/tcp/tcp-initiator.js.map +1 -1
  64. package/docs/benchmarking.md +117 -0
  65. package/eslint.config.mjs +15 -1
  66. package/jsfix.test_client.txt +62 -62
  67. package/jsfix.test_server.txt +72 -72
  68. package/package.json +9 -1
  69. package/src/benchmark/bench-report.ts +101 -0
  70. package/src/benchmark/bench-runner.ts +196 -0
  71. package/src/benchmark/bench-stats.ts +86 -0
  72. package/src/benchmark/bench-types.ts +110 -0
  73. package/src/benchmark/gc-probe.ts +145 -0
  74. package/src/benchmark/index.ts +6 -0
  75. package/src/benchmark/parse-bench.ts +144 -0
  76. package/src/buffer/ascii/ascii-parser.ts +16 -1
  77. package/src/buffer/ascii/ascii-segment-parser.ts +9 -6
  78. package/src/buffer/ascii/tag-index.ts +3 -3
  79. package/src/buffer/msg-view.ts +35 -18
  80. package/src/dictionary/parser/quickfix/dictionary-validator.ts +1 -1
  81. package/src/dictionary/parser/quickfix/quick-fix-graph-parser.ts +2 -3
  82. package/src/dictionary/parser/quickfix/validation-error.ts +1 -1
  83. package/src/jsfix-bench.ts +204 -0
  84. package/src/store/file-session-store.ts +2 -2
  85. package/src/store/file-session-stream-provider.ts +17 -11
  86. package/src/store/fix-session-store-factory.ts +1 -1
  87. package/src/store/fix-session-store.ts +11 -11
  88. package/src/store/memory-session-store.ts +1 -1
  89. package/src/store/session-stream-provider.ts +13 -13
  90. package/src/transport/session/fix-clock.ts +1 -1
  91. package/src/transport/session/resend-request-manager.ts +4 -4
  92. package/src/transport/session/session-sequence-coordinator.ts +1 -1
  93. package/src/transport/session/session-sequence-store.ts +3 -3
  94. package/src/transport/tcp/tcp-initiator.ts +1 -1
@@ -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"]}
@@ -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":";;;;;;;;;;;;AAEA,qEAAsH;AAiBtH,MAAa,0BAA0B;IAmBrC,YACE,KAA4B,EAC5B,KAAgB,EAChB,mBAAyC;QAhBnC,0BAAqB,GAAW,CAAC,CAAA;QACjC,8BAAyB,GAAW,CAAC,CAAA;QAGrC,iCAA4B,GAAW,CAAC,CAAA;QAGxC,yBAAoB,GAAW,CAAC,CAAA;QAChC,iCAA4B,GAAW,CAAC,CAAA;QAGzC,mBAAc,GAAiC,IAAI,CAAA;QAOxD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,aAAa,GAAG,IAAI,6CAAoB,CAAC,mBAAmB,CAAC,CAAA;IACpE,CAAC;IAID,mBAAmB;QACjB,IAAI,CAAC,qBAAqB,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAA;QACpD,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAA;QACxD,IAAI,CAAC,4BAA4B,GAAG,IAAI,CAAC,yBAAyB,GAAG,CAAC,CAAA;IACxE,CAAC;IAED,oBAAoB,CAAE,YAAqB,EAAE,YAAqB;QAChE,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAC/B,IAAI,CAAC,qBAAqB,GAAG,YAAY,CAAA;QAC3C,CAAC;QACD,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAC/B,IAAI,CAAC,yBAAyB,GAAG,YAAY,CAAA;YAC7C,IAAI,CAAC,4BAA4B,GAAG,YAAY,GAAG,CAAC,CAAA;QACtD,CAAC;IACH,CAAC;IAID,IAAI,gBAAgB;QAClB,OAAO,IAAI,CAAC,qBAAqB,CAAA;IACnC,CAAC;IAED,IAAI,oBAAoB;QACtB,OAAO,IAAI,CAAC,yBAAyB,CAAA;IACvC,CAAC;IAED,IAAI,uBAAuB;QACzB,OAAO,IAAI,CAAC,4BAA4B,CAAA;IAC1C,CAAC;IAQD,mBAAmB,CAAE,YAAqB,KAAK;QAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,qBAAqB,CAAA;QACtC,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,IAAI,CAAC,qBAAqB,EAAE,CAAA;QAC9B,CAAC;QACD,OAAO,GAAG,CAAA;IACZ,CAAC;IAMK,gBAAgB,CAAE,MAAc,EAAE,SAAkB;;YACxD,IAAI,SAAS;gBAAE,OAAM;YACrB,MAAM,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;QAC9C,CAAC;KAAA;IAOK,iBAAiB,CAAE,MAAc,EAAE,WAAoB;;YAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAA;YAE5B,IAAI,CAAC,aAAa,CAAC,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,CAAC,CAAA;YAE9D,IAAI,WAAW,EAAE,CAAC;gBAChB,OAAO,IAAI,CAAA;YACb,CAAC;YAED,IAAI,MAAM,GAAG,IAAI,CAAC,yBAAyB,EAAE,CAAC;gBAC5C,OAAO,KAAK,CAAA;YACd,CAAC;YAED,IAAI,MAAM,KAAK,IAAI,CAAC,yBAAyB,EAAE,CAAC;gBAC9C,IAAI,CAAC,4BAA4B,GAAG,MAAM,CAAA;gBAC1C,IAAI,CAAC,yBAAyB,GAAG,MAAM,GAAG,CAAC,CAAA;YAC7C,CAAC;iBAAM,IAAI,MAAM,GAAG,IAAI,CAAC,yBAAyB,EAAE,CAAC;gBAEnD,IAAI,CAAC,4BAA4B,GAAG,MAAM,CAAA;gBAC1C,IAAI,CAAC,yBAAyB,GAAG,MAAM,GAAG,CAAC,CAAA;YAC7C,CAAC;YAED,MAAM,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAA;YAChE,OAAO,IAAI,CAAA;QACb,CAAC;KAAA;IAKK,iBAAiB,CAAE,UAAkB,EAAE,QAAgB;;YAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAA;YAC5B,IAAI,CAAC,aAAa,CAAC,iBAAiB,CAAC,UAAU,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAA;YAG/D,IAAI,QAAQ,GAAG,IAAI,CAAC,yBAAyB,EAAE,CAAC;gBAC9C,IAAI,CAAC,yBAAyB,GAAG,QAAQ,CAAA;gBACzC,IAAI,CAAC,4BAA4B,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,4BAA4B,EAAE,QAAQ,GAAG,CAAC,CAAC,CAAA;YAC/F,CAAC;YAED,MAAM,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAA;QAClE,CAAC;KAAA;IAID,aAAa,CAAE,WAAmB,EAAE,WAAmB;QACrD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAA;QAC5B,OAAO,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,WAAW,EAAE,WAAW,EAAE,GAAG,CAAC,CAAA;IACxE,CAAC;IAED,uBAAuB,CAAE,KAAa,EAAE,GAAW;QACjD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAA;QAC5B,IAAI,CAAC,aAAa,CAAC,iBAAiB,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA;IACvD,CAAC;IAED,IAAI,qBAAqB;QACvB,OAAO,IAAI,CAAC,aAAa,CAAC,OAAO,CAAA;IACnC,CAAC;IAID,0BAA0B,CAAE,aAAqB,EAAE;QACjD,IAAI,CAAC,oBAAoB,EAAE,CAAA;QAC3B,IAAI,IAAI,CAAC,oBAAoB,IAAI,UAAU,EAAE,CAAC;YAC5C,IAAI,CAAC,qBAAqB,EAAE,CAAA;YAC5B,OAAO,IAAI,CAAA;QACb,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;IAED,oBAAoB;QAClB,IAAI,CAAC,oBAAoB,GAAG,CAAC,CAAA;IAC/B,CAAC;IAED,IAAI,eAAe;QACjB,OAAO,IAAI,CAAC,oBAAoB,CAAA;IAClC,CAAC;IAID,wBAAwB,CAAE,cAAsB,CAAC;QAC/C,IAAI,CAAC,4BAA4B,EAAE,CAAA;QACnC,OAAO,IAAI,CAAC,4BAA4B,IAAI,WAAW,CAAA;IACzD,CAAC;IAED,oBAAoB;QAClB,IAAI,CAAC,4BAA4B,GAAG,CAAC,CAAA;IACvC,CAAC;IAED,IAAI,uBAAuB;QACzB,OAAO,IAAI,CAAC,4BAA4B,CAAA;IAC1C,CAAC;IAQD,mBAAmB;QACjB,IAAI,CAAC,oBAAoB,GAAG,CAAC,CAAA;QAC7B,IAAI,CAAC,4BAA4B,GAAG,CAAC,CAAA;QACrC,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAA;IAC5B,CAAC;IAMK,YAAY,CAAE,MAAc;;YAChC,IAAI,CAAC,qBAAqB,GAAG,CAAC,CAAA;YAC9B,IAAI,CAAC,yBAAyB,GAAG,CAAC,CAAA;YAClC,IAAI,CAAC,4BAA4B,GAAG,CAAC,CAAA;YACrC,IAAI,CAAC,oBAAoB,GAAG,CAAC,CAAA;YAC7B,IAAI,CAAC,4BAA4B,GAAG,CAAC,CAAA;YACrC,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAA;YAE1B,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;YAExB,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;gBACxB,MAAM,IAAI,CAAC,cAAc,EAAE,CAAA;YAC7B,CAAC;QACH,CAAC;KAAA;IAKK,eAAe,CAAE,UAAkB,EAAE,WAAoB;;YAC7D,MAAM,cAAc,GAAG,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC,IAAI,CAAA;YAEtE,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;YAExB,IAAI,CAAC,qBAAqB,GAAG,cAAc,aAAd,cAAc,cAAd,cAAc,GAAI,IAAI,CAAC,KAAK,CAAC,YAAY,CAAA;YACtE,IAAI,CAAC,yBAAyB,GAAG,UAAU,GAAG,CAAC,CAAA;YAC/C,IAAI,CAAC,4BAA4B,GAAG,UAAU,CAAA;YAC9C,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAA;YAE1B,MAAM,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAA;YAEhE,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;gBACxB,MAAM,IAAI,CAAC,cAAc,EAAE,CAAA;YAC7B,CAAC;QACH,CAAC;KAAA;IAKK,eAAe;;YACnB,IAAI,CAAC,qBAAqB,GAAG,CAAC,CAAA;YAC9B,IAAI,CAAC,yBAAyB,GAAG,CAAC,CAAA;YAClC,IAAI,CAAC,4BAA4B,GAAG,CAAC,CAAA;YACrC,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAA;YAE1B,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;YACxB,MAAM,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,CAAA;QACrC,CAAC;KAAA;IAKD,IAAI;QACF,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAA;QAC5B,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC9B,CAAC;CACF;AA5PD,gEA4PC","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"]}
@@ -1 +1 @@
1
- {"version":3,"file":"tcp-initiator.js","sourceRoot":"","sources":["../../../src/transport/tcp/tcp-initiator.ts"],"names":[],"mappings":"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","sourcesContent":["import { FixInitiator } from '../fix-initiator'\nimport { MsgTransport } from '../factory'\n\nimport { IJsFixConfig, IJsFixLogger } from '../../config'\nimport { TcpDuplex, FixDuplex } from '../duplex'\n\nimport * as util from 'util'\nimport { connect as tlsConnect, ConnectionOptions, TLSSocket } from 'tls'\nimport { createConnection } from 'net'\nimport Timeout = NodeJS.Timeout\nimport { TlsOptionsFactory } from './tls-options-factory'\nimport { describeConnectionOptions, describeNegotiated, describePeerCertificate } from './tls-diagnostics'\nimport { inject, injectable } from 'tsyringe'\nimport { DITokens } from '../../runtime/di-tokens'\nimport { ITcpTransportDescription } from './tcp-transport-description'\n\nexport enum InitiatorState {\n Idle = 1,\n Connecting = 2,\n Connected = 3,\n Stopped = 4\n}\n\n@injectable()\nexport class TcpInitiator extends FixInitiator {\n public tcp: ITcpTransportDescription | null\n public state: InitiatorState = InitiatorState.Idle\n private readonly logger: IJsFixLogger\n private duplex: FixDuplex\n private th: Timeout | null = null\n\n constructor (@inject(DITokens.IJsFixConfig) public readonly jsFixConfig: IJsFixConfig) {\n super(jsFixConfig.description.application ?? null)\n const name = this.application?.name ?? 'initiator'\n this.logger = jsFixConfig.logFactory.logger(`${name}:TcpInitiator`)\n if (!this.application) {\n throw new Error('no application in session description.')\n }\n this.tcp = this.application.tcp ?? null\n if (!this.tcp) {\n throw new Error('no tcp in session description need tcp { host: hostname, port: port }')\n }\n }\n\n public end (): void {\n this.clearTimer()\n switch (this.state) {\n case InitiatorState.Connected: {\n this.logger.info('end')\n this.duplex.end()\n this.state = InitiatorState.Stopped\n break\n }\n\n default: {\n this.logger.info(`end :state ${this.state}`)\n this.state = InitiatorState.Stopped\n break\n }\n }\n }\n\n public async connect (timeoutSeconds: number): Promise<MsgTransport> {\n return await new Promise<MsgTransport>(async (resolve, reject) => {\n switch (this.state) {\n case InitiatorState.Idle: {\n this.state = InitiatorState.Connecting\n this.logger.info(`connecting with timeout ${timeoutSeconds}`)\n this.tryConnect()\n .then((t: MsgTransport) => { resolve(t) })\n .catch((first: Error) => {\n // carry the first failure into repeatConnect. it used to be discarded, so a\n // connect() whose timeout expires before the first retry interval fires - the\n // default case, timeout and reconnectSeconds are both 5 - reported a generic\n // \"timeout whilst connecting\" and threw away the reason. see #94.\n this.logger.info(`first connect attempt failed: ${first.message}`)\n this.repeatConnect(timeoutSeconds, first)\n .then((t: MsgTransport) => { resolve(t) })\n .catch((e: Error) => { reject(e) })\n })\n break\n }\n\n default: {\n const e: Error = new Error('connect not valid from non idle state')\n this.logger.warning(`rejecting from state ${this.state}`)\n reject(e)\n }\n }\n })\n }\n\n private async unsecureDuplex (): Promise<TcpDuplex> {\n const tcp = this.tcp\n return await new Promise<TcpDuplex>((resolve, reject) => {\n try {\n this.logger.info('unsecureDuplex try to connect to endPoint')\n const socket = tcp\n ? createConnection(tcp, () => {\n try {\n this.logger.info('net.createConnection cb, resolving')\n if (socket) {\n const tcpDuplex = new TcpDuplex(socket)\n resolve(tcpDuplex)\n } else {\n reject(new Error('no socket in tcp initiator'))\n }\n } catch (e) {\n reject(e)\n }\n })\n : null\n socket?.on('error', (err) => {\n reject(err)\n })\n } catch (e) {\n reject(e)\n }\n })\n }\n\n private async tlsDuplex (): Promise<TcpDuplex > {\n return await new Promise<TcpDuplex>((resolve, reject) => {\n let tlsSocket: TLSSocket | null = null\n const tcp = this.tcp\n const connectionOptions: ConnectionOptions | null = tcp ? TlsOptionsFactory.getTlsConnectionOptions(tcp) : null\n if (connectionOptions) {\n try {\n this.logger.info(`tls connect with ${describeConnectionOptions(connectionOptions)}`)\n tlsSocket = tlsConnect(connectionOptions, () => {\n if (!tlsSocket) return null\n this.logger.info(`client connected ${tlsSocket.authorized ? 'authorized' : 'unauthorized'}`)\n this.logger.info(`tls negotiated ${describeNegotiated(tlsSocket)}`)\n this.logger.info(`tls peer certificate ${describePeerCertificate(tlsSocket)}`)\n // https://github.com/TimelordUK/jspurefix/issues/94 - honour rejectUnauthorized:\n // false rather than always rejecting on an unauthorized cert. Node's tls.connect()\n // never throws for an unauthorized peer by itself; it's the caller's job to decide\n // what to do with tlsSocket.authorized, and rejectUnauthorized is how a caller opts\n // out of that check - so only reject here when the caller didn't opt out.\n if (!tlsSocket.authorized && connectionOptions.rejectUnauthorized !== false) {\n const error = tlsSocket.authorizationError\n this.logger.warning(`rejecting from state ${this.state} authorizationError ${error}`)\n tlsSocket.end()\n reject(error)\n } else {\n if (!tlsSocket.authorized) {\n this.logger.warning(`proceeding despite unauthorized TLS peer (rejectUnauthorized: false): ${tlsSocket.authorizationError}`)\n }\n tlsSocket.setEncoding('utf8')\n const tlsDuplex = new TcpDuplex(tlsSocket)\n if (tcp?.tls?.enableTrace) {\n this.logger.info('enabling tls session trace')\n tlsSocket.enableTrace()\n }\n this.logger.info('tlsDuplex resolving')\n resolve(tlsDuplex)\n }\n })\n tlsSocket.on('error', (err) => {\n // previously silent - the socket error is the real reason a tls session failed,\n // and without it the caller only ever sees repeatConnect's generic timeout.\n this.logger.warning(`tls socket error from state ${this.state}: ${err.message}`)\n reject(err)\n })\n } catch (e) {\n reject(e)\n }\n }\n })\n }\n\n private async tryConnect (): Promise < MsgTransport > {\n return await new Promise<MsgTransport>((resolve, reject) => {\n const tcp = this.tcp\n const connectionOptions: ConnectionOptions | null = tcp ? TlsOptionsFactory.getTlsConnectionOptions(tcp) : null\n const connector = connectionOptions ? this.tlsDuplex() : this.unsecureDuplex()\n this.logger.info(`tryConnect ${tcp?.host}:${tcp?.port}`)\n connector.then(duplex => {\n this.duplex = duplex\n resolve(new MsgTransport(0, this.jsFixConfig, duplex))\n }).catch(e => {\n reject(e)\n })\n })\n }\n\n public clearTimer (): void {\n if (this.th) {\n clearInterval(this.th)\n this.th = null\n }\n }\n\n private async repeatConnect (timeoutSeconds: number, initialError?: Error): Promise < MsgTransport > {\n return await new Promise<MsgTransport>(async (resolve, reject) => {\n const application = this.application\n const promisify = util.promisify\n const timeoutPromise = promisify(setTimeout)\n const reconnectSeconds = application?.reconnectSeconds ?? 5\n let retries = 0\n let lastError: Error = initialError as Error\n const name = application?.name ?? 'initiator'\n this.th = setInterval(() => {\n ++retries\n this.tryConnect()\n .then((t: MsgTransport) => {\n this.state = InitiatorState.Connected\n this.clearTimer()\n resolve(t)\n }).catch((e: Error) => {\n lastError = e\n this.logger.info(`${name}: retries ${retries} ${e.message}`)\n })\n }, reconnectSeconds * 1000)\n timeoutPromise(timeoutSeconds * 1000).then(() => {\n this.clearTimer()\n this.state = InitiatorState.Stopped\n const e = lastError ?? new Error(`${name}: timeout of ${timeoutSeconds} whilst connecting`)\n this.logger.warning(`${name}: giving up after ${timeoutSeconds}s and ${retries} retries, last error: ${e.message}`)\n reject(e)\n }).catch(e => {\n reject(e)\n })\n })\n }\n}\n"]}
1
+ {"version":3,"file":"tcp-initiator.js","sourceRoot":"","sources":["../../../src/transport/tcp/tcp-initiator.ts"],"names":[],"mappings":"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","sourcesContent":["import { FixInitiator } from '../fix-initiator'\nimport { MsgTransport } from '../factory'\n\nimport { IJsFixConfig, IJsFixLogger } from '../../config'\nimport { TcpDuplex, FixDuplex } from '../duplex'\n\nimport * as util from 'util'\nimport { connect as tlsConnect, ConnectionOptions, TLSSocket } from 'tls'\nimport { createConnection } from 'net'\nimport Timeout = NodeJS.Timeout\nimport { TlsOptionsFactory } from './tls-options-factory'\nimport { describeConnectionOptions, describeNegotiated, describePeerCertificate } from './tls-diagnostics'\nimport { inject, injectable } from 'tsyringe'\nimport { DITokens } from '../../runtime/di-tokens'\nimport { ITcpTransportDescription } from './tcp-transport-description'\n\nexport enum InitiatorState {\n Idle = 1,\n Connecting = 2,\n Connected = 3,\n Stopped = 4\n}\n\n@injectable()\nexport class TcpInitiator extends FixInitiator {\n public tcp: ITcpTransportDescription | null\n public state: InitiatorState = InitiatorState.Idle\n private readonly logger: IJsFixLogger\n private duplex: FixDuplex\n private th: Timeout | null = null\n\n constructor (@inject(DITokens.IJsFixConfig) public readonly jsFixConfig: IJsFixConfig) {\n super(jsFixConfig.description.application ?? null)\n const name = this.application?.name ?? 'initiator'\n this.logger = jsFixConfig.logFactory.logger(`${name}:TcpInitiator`)\n if (!this.application) {\n throw new Error('no application in session description.')\n }\n this.tcp = this.application.tcp ?? null\n if (!this.tcp) {\n throw new Error('no tcp in session description need tcp { host: hostname, port: port }')\n }\n }\n\n public end (): void {\n this.clearTimer()\n switch (this.state) {\n case InitiatorState.Connected: {\n this.logger.info('end')\n this.duplex.end()\n this.state = InitiatorState.Stopped\n break\n }\n\n default: {\n this.logger.info(`end :state ${this.state}`)\n this.state = InitiatorState.Stopped\n break\n }\n }\n }\n\n public async connect (timeoutSeconds: number): Promise<MsgTransport> {\n return await new Promise<MsgTransport>(async (resolve, reject) => {\n switch (this.state) {\n case InitiatorState.Idle: {\n this.state = InitiatorState.Connecting\n this.logger.info(`connecting with timeout ${timeoutSeconds}`)\n this.tryConnect()\n .then((t: MsgTransport) => { resolve(t) })\n .catch((first: Error) => {\n // carry the first failure into repeatConnect. it used to be discarded, so a\n // connect() whose timeout expires before the first retry interval fires - the\n // default case, timeout and reconnectSeconds are both 5 - reported a generic\n // \"timeout whilst connecting\" and threw away the reason. see #94.\n this.logger.info(`first connect attempt failed: ${first.message}`)\n this.repeatConnect(timeoutSeconds, first)\n .then((t: MsgTransport) => { resolve(t) })\n .catch((e: Error) => { reject(e) })\n })\n break\n }\n\n default: {\n const e: Error = new Error('connect not valid from non idle state')\n this.logger.warning(`rejecting from state ${this.state}`)\n reject(e)\n }\n }\n })\n }\n\n private async unsecureDuplex (): Promise<TcpDuplex> {\n const tcp = this.tcp\n return await new Promise<TcpDuplex>((resolve, reject) => {\n try {\n this.logger.info('unsecureDuplex try to connect to endPoint')\n const socket = tcp\n ? createConnection(tcp, () => {\n try {\n this.logger.info('net.createConnection cb, resolving')\n if (socket) {\n const tcpDuplex = new TcpDuplex(socket)\n resolve(tcpDuplex)\n } else {\n reject(new Error('no socket in tcp initiator'))\n }\n } catch (e) {\n reject(e)\n }\n })\n : null\n socket?.on('error', (err) => {\n reject(err)\n })\n } catch (e) {\n reject(e)\n }\n })\n }\n\n private async tlsDuplex (): Promise<TcpDuplex > {\n return await new Promise<TcpDuplex>((resolve, reject) => {\n let tlsSocket: TLSSocket | null = null\n const tcp = this.tcp\n const connectionOptions: ConnectionOptions | null = tcp ? TlsOptionsFactory.getTlsConnectionOptions(tcp) : null\n if (connectionOptions) {\n try {\n this.logger.info(`tls connect with ${describeConnectionOptions(connectionOptions)}`)\n tlsSocket = tlsConnect(connectionOptions, () => {\n if (!tlsSocket) return null\n this.logger.info(`client connected ${tlsSocket.authorized ? 'authorized' : 'unauthorized'}`)\n this.logger.info(`tls negotiated ${describeNegotiated(tlsSocket)}`)\n this.logger.info(`tls peer certificate ${describePeerCertificate(tlsSocket)}`)\n // https://github.com/TimelordUK/jspurefix/issues/94 - honour rejectUnauthorized:\n // false rather than always rejecting on an unauthorized cert. Node's tls.connect()\n // never throws for an unauthorized peer by itself; it's the caller's job to decide\n // what to do with tlsSocket.authorized, and rejectUnauthorized is how a caller opts\n // out of that check - so only reject here when the caller didn't opt out.\n if (!tlsSocket.authorized && connectionOptions.rejectUnauthorized !== false) {\n const error = tlsSocket.authorizationError\n this.logger.warning(`rejecting from state ${this.state} authorizationError ${error}`)\n tlsSocket.end()\n reject(error)\n } else {\n if (!tlsSocket.authorized) {\n this.logger.warning(`proceeding despite unauthorized TLS peer (rejectUnauthorized: false): ${tlsSocket.authorizationError}`)\n }\n tlsSocket.setEncoding('utf8')\n const tlsDuplex = new TcpDuplex(tlsSocket)\n if (tcp?.tls?.enableTrace) {\n this.logger.info('enabling tls session trace')\n tlsSocket.enableTrace()\n }\n this.logger.info('tlsDuplex resolving')\n resolve(tlsDuplex)\n }\n })\n tlsSocket.on('error', (err) => {\n // previously silent - the socket error is the real reason a tls session failed,\n // and without it the caller only ever sees repeatConnect's generic timeout.\n this.logger.warning(`tls socket error from state ${this.state}: ${err.message}`)\n reject(err)\n })\n } catch (e) {\n reject(e)\n }\n }\n })\n }\n\n private async tryConnect (): Promise < MsgTransport > {\n return await new Promise<MsgTransport>((resolve, reject) => {\n const tcp = this.tcp\n const connectionOptions: ConnectionOptions | null = tcp ? TlsOptionsFactory.getTlsConnectionOptions(tcp) : null\n const connector = connectionOptions ? this.tlsDuplex() : this.unsecureDuplex()\n this.logger.info(`tryConnect ${tcp?.host}:${tcp?.port}`)\n connector.then(duplex => {\n this.duplex = duplex\n resolve(new MsgTransport(0, this.jsFixConfig, duplex))\n }).catch(e => {\n reject(e)\n })\n })\n }\n\n public clearTimer (): void {\n if (this.th) {\n clearInterval(this.th)\n this.th = null\n }\n }\n\n private async repeatConnect (timeoutSeconds: number, initialError?: Error): Promise < MsgTransport > {\n return await new Promise<MsgTransport>(async (resolve, reject) => {\n const application = this.application\n const promisify = util.promisify\n const timeoutPromise = promisify(setTimeout)\n const reconnectSeconds = application?.reconnectSeconds ?? 5\n let retries = 0\n let lastError: Error | undefined = initialError\n const name = application?.name ?? 'initiator'\n this.th = setInterval(() => {\n ++retries\n this.tryConnect()\n .then((t: MsgTransport) => {\n this.state = InitiatorState.Connected\n this.clearTimer()\n resolve(t)\n }).catch((e: Error) => {\n lastError = e\n this.logger.info(`${name}: retries ${retries} ${e.message}`)\n })\n }, reconnectSeconds * 1000)\n timeoutPromise(timeoutSeconds * 1000).then(() => {\n this.clearTimer()\n this.state = InitiatorState.Stopped\n const e = lastError ?? new Error(`${name}: timeout of ${timeoutSeconds} whilst connecting`)\n this.logger.warning(`${name}: giving up after ${timeoutSeconds}s and ${retries} retries, last error: ${e.message}`)\n reject(e)\n }).catch(e => {\n reject(e)\n })\n })\n }\n}\n"]}
@@ -0,0 +1,117 @@
1
+ # Benchmarking
2
+
3
+ `jsfix-bench` drives fix bytes through the real parser and reports what the work
4
+ costs in time and in garbage. It exists so a change to a hot path can be argued
5
+ about with a number rather than an opinion.
6
+
7
+ ```
8
+ npm run build
9
+ npm run bench
10
+ ```
11
+
12
+ Always under `--expose-gc`, which the npm scripts do for you. Without it rounds
13
+ cannot be isolated from one another and the retained heap figures mean very
14
+ little; the report says so at the top when it is missing.
15
+
16
+ ## What it measures
17
+
18
+ Two cases by default, over the same input:
19
+
20
+ | case | what it covers |
21
+ | --- | --- |
22
+ | `parse:view` | tokenise, discover the structure, build the `MsgView` |
23
+ | `parse:object` | the above, then `toObject()` to materialise the object graph |
24
+
25
+ Both matter. `parse:view` is the engine's own cost. `parse:object` is what a
26
+ drop copy consumer actually pays, and the gap between them is the price of
27
+ materialising a message rather than reading fields out of the view.
28
+
29
+ ## Reading the report
30
+
31
+ ```
32
+ parse:object
33
+ rounds 8 x 4000 msgs (warmup 4000)
34
+ per msg median 239.922us min 231.305us p95 242.623us max 242.623us
35
+ throughput 4,168 msg/sec
36
+ noise 1.80% of median (a change smaller than this means nothing)
37
+ gc pass 4000 msgs uninterrupted: 25 minor 0 major 5.8ms paused
38
+ gc rate 6250.0 minor/M msgs 0.0 major/M msgs 1445ms paused/M msgs
39
+ retained -0.00MB -0.6 bytes/msg heap at end of pass 116.08MB
40
+ ```
41
+
42
+ - **rounds** each contribute one timing sample. One long run cannot tell you
43
+ whether a number repeats.
44
+ - **min** is the closest reading to the true cost of the work - a round can only
45
+ be made slower by interference, never faster - while **median** is what to
46
+ expect in practice.
47
+ - **noise** is the run to run spread. Treat it as the resolution of the
48
+ instrument: a change smaller than this is not evidence of anything.
49
+ - **gc rate** is collections per million messages. Node will not tell you how
50
+ many bytes a stretch of code allocated, so scavenge frequency is the stand in.
51
+ In practice it is far more repeatable than the timings - the same build tends
52
+ to reproduce it exactly - which makes it the more sensitive of the two signals
53
+ for spotting an allocation change.
54
+ - **retained** is what the pass left behind after a forced collection, not what
55
+ it churned through. A steady state parser should sit near zero. Churn is what
56
+ the gc figures are for.
57
+
58
+ ## Why timing and memory are measured separately
59
+
60
+ Isolating a timed round means forcing a collection between rounds, and a forced
61
+ collection is itself a gc event - counting those would report the harness rather
62
+ than the engine. So the timed rounds collect between them and report no gc
63
+ figures, and a separate uninterrupted pass runs with the probe attached and
64
+ reports no timings. BenchmarkDotNet splits its memory diagnoser out of the
65
+ timed iterations for the same reason.
66
+
67
+ Node also dispatches a gc entry on the turn *after* the collection, and the
68
+ benchmark loop is synchronous, so the runner yields until delivery goes quiet
69
+ before reading the totals. A single yield loses most of them.
70
+
71
+ ## Comparing against a baseline
72
+
73
+ ```
74
+ npm run build
75
+ node --expose-gc dist/jsfix-bench --save=benchmarks/baseline.json --label="before"
76
+ # ... make a change, rebuild ...
77
+ node --expose-gc dist/jsfix-bench --baseline=benchmarks/baseline.json
78
+ ```
79
+
80
+ ```
81
+ parse:object
82
+ per msg 239.922us -> 243.523us +1.50% noise (noise floor +/-2.75%)
83
+ minor gc 6250.0 -> 6250.0 per M msgs +0.00%
84
+ ```
85
+
86
+ A difference is only called `FASTER` or `SLOWER` when it is larger than the
87
+ spread both runs displayed. Anything else is reported as `noise`, because that
88
+ is what it is. Baselines are specific to the machine and node build that took
89
+ them - the report warns when they differ - so `benchmarks/` is not committed.
90
+
91
+ ## Options
92
+
93
+ | flag | meaning |
94
+ | --- | --- |
95
+ | `--fix=<path>` | fix log to parse |
96
+ | `--dict=<name>` | dictionary, e.g. `qf44`, `repo44`, `qf50sp2` |
97
+ | `--delimiter=<char>` | field delimiter in the log, defaults to `\|` |
98
+ | `--depth=view,object` | which stages to measure |
99
+ | `--rounds=<n>` | measured rounds, one sample each |
100
+ | `--msgs=<n>` | messages per round |
101
+ | `--warmup=<n>` | messages discarded before measuring |
102
+ | `--save=<path>` | write the suite to json |
103
+ | `--baseline=<path>` | compare against a saved suite |
104
+ | `--json` | print the suite as json instead of a report |
105
+
106
+ Ready made suites: `bench:er`, `bench:er2`, `bench:nos`, `bench:md`, `bench:tc`.
107
+
108
+ Message counts are rounded up to a whole number of passes over the input file,
109
+ since a pass cannot be stopped part way. A log holding one message gives the
110
+ finest control.
111
+
112
+ ## Warmup
113
+
114
+ The default warmup is 20,000 messages. V8 needs to see a function run several
115
+ thousand times before it stops interpreting it, so an unwarmed first round
116
+ measures the wrong code. If you lower it, check the noise figure has not risen -
117
+ that is what an under-warmed run looks like.
package/eslint.config.mjs CHANGED
@@ -106,6 +106,8 @@ export default [
106
106
  'prefer-template': 'off',
107
107
  'radix': 'off',
108
108
  'require-unicode-regexp': 'off',
109
+ // named capture groups need a target of es2018, this package emits es6
110
+ 'prefer-named-capture-group': 'off',
109
111
  'promise/avoid-new': 'off',
110
112
  'promise/no-multiple-resolved': 'off',
111
113
  'import/enforce-node-protocol-usage': 'off',
@@ -114,6 +116,18 @@ export default [
114
116
  }
115
117
  },
116
118
  {
117
- ignores: ['dist/**', 'node_modules/**']
119
+ files: ['src/test/**/*.ts'],
120
+ rules: {
121
+ // tests assert a value is present with expect(x).toBeDefined() and then use x!.
122
+ // the compiler cannot follow that, and rewriting each site as an explicit throw
123
+ // buries the assertion the test is actually about.
124
+ '@typescript-eslint/no-non-null-assertion': 'off',
125
+ // save/restore of a static around an awaited body is not a race in a jest file,
126
+ // which runs its tests serially.
127
+ 'require-atomic-updates': 'off'
128
+ }
129
+ },
130
+ {
131
+ ignores: ['dist/**', 'node_modules/**', 'coverage/**']
118
132
  }
119
133
  ]
@@ -1,62 +1,62 @@
1
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:15.489|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=104|
2
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:51:15.494|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=148|
3
- 8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=20260801-14:51:15.495|7=1|16=1|10=236|
4
- 8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=20260801-14:51:15.500|122=20260801-14:51:15.500|123=Y|36=2|10=077|
5
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:51:17.499|58=5|10=218|
6
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:20.313|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=086|
7
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:51:20.315|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=136|
8
- 8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=20260801-14:51:20.315|7=1|16=0|10=222|
9
- 8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=20260801-14:51:20.318|122=20260801-14:51:20.318|123=Y|36=2|10=083|
10
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:51:22.318|58=5|10=204|
11
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:24.804|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=095|
12
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:51:24.806|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=145|
13
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=2|57=fix|52=20260801-14:51:24.806|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=098|
14
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:25.344|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=095|
15
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:51:25.345|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=144|
16
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:51:26.347|58=5|10=210|
17
- 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260801-14:51:26.807|58=5|10=212|
18
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:29.063|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=097|
19
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:51:29.065|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=147|
20
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:51:30.068|58=5|10=205|
21
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:32.552|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=094|
22
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:51:32.553|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=143|
23
- 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=0|57=fix|52=20260801-14:51:33.554|58=5|10=206|
24
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:36.035|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=094|
25
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=5|57=fix|52=20260801-14:51:36.036|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=147|
26
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:51:37.039|58=5|10=210|
27
- 8=FIX.4.4|9=0000110|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:39.686|98=0|141=Y|553=js-client|554=pwd-client|10=177|
28
- 8=FIX.4.4|9=0000125|35=3|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:51:39.687|45=1|372=A|373=1|58=msgType A missing required tag 108|10=011|
29
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:44.447|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=100|
30
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:51:44.449|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=150|
31
- 8=FIX.4.4|9=0000111|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:51:44.451|45=2|372=ZZ|373=11|58=msgType ZZ unknown|10=144|
32
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260801-14:51:46.451|58=5|10=209|
33
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:48.955|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=108|
34
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:51:48.956|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=157|
35
- 8=FIX.4.4|9=0000146|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:51:48.958|45=2|372=0|373=5|58=msgType 0 checksum failed. received = 95 computed = 143|10=081|
36
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260801-14:51:50.959|58=5|10=217|
37
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:52.957|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=105|
38
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:51:57.567|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=107|
39
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:51:57.568|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=156|
40
- 8=FIX.4.4|9=0000145|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:51:57.569|45=2|372=0|373=5|58=msgType 0 checksum failed. received = 95 computed = 59|10=036|
41
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260801-14:51:59.570|58=5|10=215|
42
- 8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:52:02.309|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=042|
43
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:52:02.310|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=132|
44
- 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260801-14:52:04.315|112=Sat, 01 Aug 2026 14:52:04 GMT|10=120|
45
- 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260801-14:52:06.317|112=Sat, 01 Aug 2026 14:52:06 GMT|10=127|
46
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:52:08.313|58=5|10=204|
47
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:52:10.828|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=097|
48
- 8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:52:10.829|98=0|108=2|141=Y|553=js-server|554=pwd-server|10=096|
49
- 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:52:12.833|112=Sat, 01 Aug 2026 14:52:12 GMT|10=123|
50
- 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=3|57=fix|52=20260801-14:52:14.838|112=Sat, 01 Aug 2026 14:52:14 GMT|10=133|
51
- 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=4|57=fix|52=20260801-14:52:16.841|112=Sat, 01 Aug 2026 14:52:16 GMT|10=132|
52
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=5|57=fix|52=20260801-14:52:18.831|58=5|10=213|
53
- 8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:52:21.327|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=043|
54
- 8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:52:21.328|98=0|108=5|141=Y|553=js-server|554=pwd-server|10=095|
55
- 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260801-14:52:23.334|112=Sat, 01 Aug 2026 14:52:23 GMT|10=123|
56
- 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260801-14:52:25.336|112=Sat, 01 Aug 2026 14:52:25 GMT|10=130|
57
- 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:52:26.339|112=Sat, 01 Aug 2026 14:52:26 GMT|10=134|
58
- 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=4|57=fix|52=20260801-14:52:27.338|112=Sat, 01 Aug 2026 14:52:27 GMT|10=137|
59
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260801-14:52:29.330|58=5|10=207|
60
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260801-14:54:22.785|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=104|
61
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260801-14:54:22.794|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=152|
62
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260801-14:54:23.799|58=5|10=221|
1
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:47:29.192|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=108|
2
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:47:29.199|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=163|
3
+ 8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=20260802-16:47:29.200|7=1|16=1|10=233|
4
+ 8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=20260802-16:47:29.205|122=20260802-16:47:29.204|123=Y|36=2|10=106|
5
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:47:31.203|58=5|10=205|
6
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:47:34.035|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=100|
7
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:47:34.037|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=150|
8
+ 8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=20260802-16:47:34.038|7=1|16=0|10=237|
9
+ 8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=20260802-16:47:34.041|122=20260802-16:47:34.041|123=Y|36=2|10=095|
10
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:47:36.041|58=5|10=210|
11
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:47:38.525|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=108|
12
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:47:38.527|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=158|
13
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=2|57=fix|52=20260802-16:47:38.527|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=111|
14
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:47:39.070|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=104|
15
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:47:39.071|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=153|
16
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:47:40.075|58=5|10=212|
17
+ 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260802-16:47:40.529|58=5|10=217|
18
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:47:42.868|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=113|
19
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:47:42.870|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=154|
20
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:47:43.872|58=5|10=220|
21
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:47:46.667|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=114|
22
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:47:46.668|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=163|
23
+ 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=0|57=fix|52=20260802-16:47:47.670|58=5|10=218|
24
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:47:50.181|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=100|
25
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=5|57=fix|52=20260802-16:47:50.182|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=153|
26
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:47:51.184|58=5|10=215|
27
+ 8=FIX.4.4|9=0000110|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:47:53.673|98=0|141=Y|553=js-client|554=pwd-client|10=177|
28
+ 8=FIX.4.4|9=0000125|35=3|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:47:53.674|45=1|372=A|373=1|58=msgType A missing required tag 108|10=011|
29
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:47:58.294|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=113|
30
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:47:58.296|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=163|
31
+ 8=FIX.4.4|9=0000111|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:47:58.298|45=2|372=ZZ|373=11|58=msgType ZZ unknown|10=166|
32
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260802-16:48:00.298|58=5|10=217|
33
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:48:03.059|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=103|
34
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:48:03.061|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=144|
35
+ 8=FIX.4.4|9=0000146|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:48:03.063|45=2|372=0|373=5|58=msgType 0 checksum failed. received = 95 computed = 143|10=068|
36
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260802-16:48:05.063|58=5|10=212|
37
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:48:07.062|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=101|
38
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:48:11.555|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=103|
39
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:48:11.556|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=152|
40
+ 8=FIX.4.4|9=0000145|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:48:11.558|45=2|372=0|373=5|58=msgType 0 checksum failed. received = 95 computed = 59|10=033|
41
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260802-16:48:13.559|58=5|10=221|
42
+ 8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:48:16.035|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=051|
43
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:48:16.036|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=150|
44
+ 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260802-16:48:18.042|112=Sun, 02 Aug 2026 16:48:18 GMT|10=157|
45
+ 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260802-16:48:20.242|112=Sun, 02 Aug 2026 16:48:20 GMT|10=146|
46
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:48:22.039|58=5|10=213|
47
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:48:24.515|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=103|
48
+ 8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:48:24.516|98=0|108=2|141=Y|553=js-server|554=pwd-server|10=102|
49
+ 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:48:26.523|112=Sun, 02 Aug 2026 16:48:26 GMT|10=159|
50
+ 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=3|57=fix|52=20260802-16:48:28.525|112=Sun, 02 Aug 2026 16:48:28 GMT|10=166|
51
+ 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=4|57=fix|52=20260802-16:48:30.531|112=Sun, 02 Aug 2026 16:48:30 GMT|10=150|
52
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=5|57=fix|52=20260802-16:48:32.518|58=5|10=219|
53
+ 8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:48:35.238|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=057|
54
+ 8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:48:35.239|98=0|108=5|141=Y|553=js-server|554=pwd-server|10=109|
55
+ 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260802-16:48:37.244|112=Sun, 02 Aug 2026 16:48:37 GMT|10=163|
56
+ 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260802-16:48:39.248|112=Sun, 02 Aug 2026 16:48:39 GMT|10=172|
57
+ 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:48:40.250|112=Sun, 02 Aug 2026 16:48:40 GMT|10=148|
58
+ 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=4|57=fix|52=20260802-16:48:41.251|112=Sun, 02 Aug 2026 16:48:41 GMT|10=153|
59
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260802-16:48:43.242|58=5|10=213|
60
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260802-16:50:41.579|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=105|
61
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260802-16:50:41.589|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=154|
62
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260802-16:50:42.593|58=5|10=213|