jspurefix 4.1.2 → 5.1.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 (167) hide show
  1. package/BACKPORT_PLAN.md +214 -0
  2. package/README.md +1 -2
  3. package/data/examples/FIX.4.4/fixsim/fix.txt +46 -0
  4. package/data/session/fixsim-qf44-initiator.json +21 -0
  5. package/dist/buffer/ascii/ascii-parser-state.js +0 -7
  6. package/dist/buffer/ascii/ascii-parser-state.js.map +1 -1
  7. package/dist/buffer/ascii/ascii-parser.d.ts +1 -0
  8. package/dist/buffer/ascii/ascii-parser.js +8 -2
  9. package/dist/buffer/ascii/ascii-parser.js.map +1 -1
  10. package/dist/buffer/ascii/ascii-segment-parser.js +36 -0
  11. package/dist/buffer/ascii/ascii-segment-parser.js.map +1 -1
  12. package/dist/buffer/ascii/tag-index.d.ts +20 -0
  13. package/dist/buffer/ascii/tag-index.js +89 -0
  14. package/dist/buffer/ascii/tag-index.js.map +1 -0
  15. package/dist/buffer/msg-view.js +17 -4
  16. package/dist/buffer/msg-view.js.map +1 -1
  17. package/dist/buffer/segment/segment-description.d.ts +3 -0
  18. package/dist/buffer/segment/segment-description.js +8 -0
  19. package/dist/buffer/segment/segment-description.js.map +1 -1
  20. package/dist/buffer/segment/segment-view.d.ts +13 -0
  21. package/dist/buffer/segment/segment-view.js +27 -0
  22. package/dist/buffer/segment/segment-view.js.map +1 -0
  23. package/dist/collections/collection.js.map +1 -1
  24. package/dist/config/js-fix-logger.js.map +1 -1
  25. package/dist/store/fix-msg-ascii-store-resend.js.map +1 -1
  26. package/dist/transport/session/fix-session.js +6 -0
  27. package/dist/transport/session/fix-session.js.map +1 -1
  28. package/dist/transport/tcp/tcp-initiator-connector.js.map +1 -1
  29. package/dist/types/FIX.5.0SP2/quickfix/enum/all-enum.js.map +1 -1
  30. package/dist/types/FIX.5.0SP2/repo/enum/all-enum.js.map +1 -1
  31. package/dist/types/FIX.5.0SP2/repo/set/hop_grp.js.map +1 -1
  32. package/dist/types/FIX4.0/repo/enum/all-enum.js.map +1 -1
  33. package/dist/types/FIX4.1/repo/enum/all-enum.js.map +1 -1
  34. package/dist/types/FIX4.2/quickfix/enum/all-enum.js.map +1 -1
  35. package/dist/types/FIX4.2/repo/enum/all-enum.js.map +1 -1
  36. package/dist/types/FIX4.3/quickfix/enum/all-enum.js.map +1 -1
  37. package/dist/types/FIX4.3/repo/allocation.js.map +1 -1
  38. package/dist/types/FIX4.3/repo/allocation_ack.js.map +1 -1
  39. package/dist/types/FIX4.3/repo/cross_order_cancel_replace_request.js.map +1 -1
  40. package/dist/types/FIX4.3/repo/cross_order_cancel_request.js.map +1 -1
  41. package/dist/types/FIX4.3/repo/derivative_security_list.js.map +1 -1
  42. package/dist/types/FIX4.3/repo/enum/all-enum.js.map +1 -1
  43. package/dist/types/FIX4.3/repo/execution_report.js.map +1 -1
  44. package/dist/types/FIX4.3/repo/mass_quote.js.map +1 -1
  45. package/dist/types/FIX4.3/repo/mass_quote_acknowledgement.js.map +1 -1
  46. package/dist/types/FIX4.3/repo/multileg_order_cancel_replace_request.js.map +1 -1
  47. package/dist/types/FIX4.3/repo/new_order_cross.js.map +1 -1
  48. package/dist/types/FIX4.3/repo/new_order_list.js.map +1 -1
  49. package/dist/types/FIX4.3/repo/new_order_multileg.js.map +1 -1
  50. package/dist/types/FIX4.3/repo/new_order_single.js.map +1 -1
  51. package/dist/types/FIX4.3/repo/order_cancel_replace_request.js.map +1 -1
  52. package/dist/types/FIX4.3/repo/order_cancel_request.js.map +1 -1
  53. package/dist/types/FIX4.3/repo/order_mass_status_request.js.map +1 -1
  54. package/dist/types/FIX4.3/repo/order_status_request.js.map +1 -1
  55. package/dist/types/FIX4.3/repo/quote.js.map +1 -1
  56. package/dist/types/FIX4.3/repo/quote_cancel.js.map +1 -1
  57. package/dist/types/FIX4.3/repo/quote_request.js.map +1 -1
  58. package/dist/types/FIX4.3/repo/quote_request_reject.js.map +1 -1
  59. package/dist/types/FIX4.3/repo/quote_status_report.js.map +1 -1
  60. package/dist/types/FIX4.3/repo/quote_status_request.js.map +1 -1
  61. package/dist/types/FIX4.3/repo/registration_instructions.js.map +1 -1
  62. package/dist/types/FIX4.3/repo/registration_instructions_response.js.map +1 -1
  63. package/dist/types/FIX4.3/repo/security_definition.js.map +1 -1
  64. package/dist/types/FIX4.3/repo/security_definition_request.js.map +1 -1
  65. package/dist/types/FIX4.3/repo/security_list.js.map +1 -1
  66. package/dist/types/FIX4.3/repo/settlement_instructions.js.map +1 -1
  67. package/dist/types/FIX4.3/repo/trade_capture_report.js.map +1 -1
  68. package/dist/types/FIX4.3/repo/trade_capture_report_request.js.map +1 -1
  69. package/dist/types/FIX4.4/quickfix/enum/all-enum.js.map +1 -1
  70. package/dist/types/FIX4.4/repo/enum/all-enum.js.map +1 -1
  71. package/dist/types/FIX4.4/repo/set/hop.js.map +1 -1
  72. package/dist/types/FIXML50SP2/adjusted_position_report.js.map +1 -1
  73. package/dist/types/FIXML50SP2/derivative_security_list.js.map +1 -1
  74. package/dist/types/FIXML50SP2/derivative_security_list_update_report.js.map +1 -1
  75. package/dist/types/FIXML50SP2/email.js.map +1 -1
  76. package/dist/types/FIXML50SP2/enum/all-enum.js.map +1 -1
  77. package/dist/types/FIXML50SP2/news.js.map +1 -1
  78. package/dist/types/FIXML50SP2/quote_cancel.js.map +1 -1
  79. package/dist/types/FIXML50SP2/set/market_segment_grp.js.map +1 -1
  80. package/dist/types/FIXML50SP2/set/party_risk_limits_ack_grp.js.map +1 -1
  81. package/dist/types/FIXML50SP2/set/party_risk_limits_grp.js.map +1 -1
  82. package/dist/types/FIXML50SP2/set/party_risk_limits_update_grp.js.map +1 -1
  83. package/dist/types/FIXML50SP2/set/sec_list_grp.js.map +1 -1
  84. package/dist/types/FIXML50SP2/set/sec_lst_upd_rel_sym_grp.js.map +1 -1
  85. package/dist/types/FIXML50SP2/set/trading_session_rules_grp.js.map +1 -1
  86. package/dist/types/FIXML50SP2/set/trd_instrmt_leg_grp.js.map +1 -1
  87. package/dist/types/FIXML50SP2/set/trd_sess_lst_grp.js.map +1 -1
  88. package/dist/types/FIXML50SP2/stream_assignment_report.js.map +1 -1
  89. package/dist/types/FIXML50SP2/stream_assignment_request.js.map +1 -1
  90. package/eslint.config.mjs +119 -0
  91. package/jsfix.test_client.txt +66 -66
  92. package/jsfix.test_server.txt +63 -63
  93. package/package.json +4 -5
  94. package/src/buffer/ascii/ascii-parser-state.ts +4 -7
  95. package/src/buffer/ascii/ascii-parser.ts +14 -3
  96. package/src/buffer/ascii/ascii-segment-parser.ts +37 -0
  97. package/src/buffer/ascii/tag-index.ts +101 -0
  98. package/src/buffer/msg-view.ts +18 -6
  99. package/src/buffer/segment/segment-description.ts +10 -0
  100. package/src/buffer/segment/segment-view.ts +28 -0
  101. package/src/collections/collection.ts +0 -1
  102. package/src/config/js-fix-logger.ts +0 -1
  103. package/src/store/fix-msg-ascii-store-resend.ts +1 -1
  104. package/src/transport/session/fix-session.ts +7 -0
  105. package/src/transport/tcp/tcp-initiator-connector.ts +1 -1
  106. package/src/types/FIX.5.0SP2/quickfix/enum/all-enum.ts +0 -1
  107. package/src/types/FIX.5.0SP2/repo/enum/all-enum.ts +0 -1
  108. package/src/types/FIX.5.0SP2/repo/set/hop_grp.ts +1 -1
  109. package/src/types/FIX4.0/repo/enum/all-enum.ts +0 -1
  110. package/src/types/FIX4.1/repo/enum/all-enum.ts +0 -1
  111. package/src/types/FIX4.2/quickfix/enum/all-enum.ts +0 -1
  112. package/src/types/FIX4.2/repo/enum/all-enum.ts +0 -1
  113. package/src/types/FIX4.3/quickfix/enum/all-enum.ts +0 -1
  114. package/src/types/FIX4.3/repo/allocation.ts +2 -2
  115. package/src/types/FIX4.3/repo/allocation_ack.ts +1 -1
  116. package/src/types/FIX4.3/repo/cross_order_cancel_replace_request.ts +4 -4
  117. package/src/types/FIX4.3/repo/cross_order_cancel_request.ts +1 -1
  118. package/src/types/FIX4.3/repo/derivative_security_list.ts +1 -1
  119. package/src/types/FIX4.3/repo/enum/all-enum.ts +0 -1
  120. package/src/types/FIX4.3/repo/execution_report.ts +5 -5
  121. package/src/types/FIX4.3/repo/mass_quote.ts +1 -1
  122. package/src/types/FIX4.3/repo/mass_quote_acknowledgement.ts +1 -1
  123. package/src/types/FIX4.3/repo/multileg_order_cancel_replace_request.ts +3 -3
  124. package/src/types/FIX4.3/repo/new_order_cross.ts +4 -4
  125. package/src/types/FIX4.3/repo/new_order_list.ts +4 -4
  126. package/src/types/FIX4.3/repo/new_order_multileg.ts +3 -3
  127. package/src/types/FIX4.3/repo/new_order_single.ts +4 -4
  128. package/src/types/FIX4.3/repo/order_cancel_replace_request.ts +3 -3
  129. package/src/types/FIX4.3/repo/order_cancel_request.ts +1 -1
  130. package/src/types/FIX4.3/repo/order_mass_status_request.ts +1 -1
  131. package/src/types/FIX4.3/repo/order_status_request.ts +1 -1
  132. package/src/types/FIX4.3/repo/quote.ts +1 -1
  133. package/src/types/FIX4.3/repo/quote_cancel.ts +1 -1
  134. package/src/types/FIX4.3/repo/quote_request.ts +2 -2
  135. package/src/types/FIX4.3/repo/quote_request_reject.ts +2 -2
  136. package/src/types/FIX4.3/repo/quote_status_report.ts +1 -1
  137. package/src/types/FIX4.3/repo/quote_status_request.ts +1 -1
  138. package/src/types/FIX4.3/repo/registration_instructions.ts +2 -2
  139. package/src/types/FIX4.3/repo/registration_instructions_response.ts +1 -1
  140. package/src/types/FIX4.3/repo/security_definition.ts +1 -1
  141. package/src/types/FIX4.3/repo/security_definition_request.ts +1 -1
  142. package/src/types/FIX4.3/repo/security_list.ts +1 -1
  143. package/src/types/FIX4.3/repo/settlement_instructions.ts +1 -1
  144. package/src/types/FIX4.3/repo/trade_capture_report.ts +1 -1
  145. package/src/types/FIX4.3/repo/trade_capture_report_request.ts +1 -1
  146. package/src/types/FIX4.4/quickfix/enum/all-enum.ts +0 -1
  147. package/src/types/FIX4.4/repo/enum/all-enum.ts +0 -1
  148. package/src/types/FIX4.4/repo/set/hop.ts +1 -1
  149. package/src/types/FIXML50SP2/adjusted_position_report.ts +1 -1
  150. package/src/types/FIXML50SP2/derivative_security_list.ts +1 -1
  151. package/src/types/FIXML50SP2/derivative_security_list_update_report.ts +1 -1
  152. package/src/types/FIXML50SP2/email.ts +1 -1
  153. package/src/types/FIXML50SP2/enum/all-enum.ts +0 -1
  154. package/src/types/FIXML50SP2/news.ts +1 -1
  155. package/src/types/FIXML50SP2/quote_cancel.ts +1 -1
  156. package/src/types/FIXML50SP2/set/market_segment_grp.ts +1 -1
  157. package/src/types/FIXML50SP2/set/party_risk_limits_ack_grp.ts +1 -1
  158. package/src/types/FIXML50SP2/set/party_risk_limits_grp.ts +1 -1
  159. package/src/types/FIXML50SP2/set/party_risk_limits_update_grp.ts +1 -1
  160. package/src/types/FIXML50SP2/set/sec_list_grp.ts +2 -2
  161. package/src/types/FIXML50SP2/set/sec_lst_upd_rel_sym_grp.ts +1 -1
  162. package/src/types/FIXML50SP2/set/trading_session_rules_grp.ts +1 -1
  163. package/src/types/FIXML50SP2/set/trd_instrmt_leg_grp.ts +1 -1
  164. package/src/types/FIXML50SP2/set/trd_sess_lst_grp.ts +1 -1
  165. package/src/types/FIXML50SP2/stream_assignment_report.ts +1 -1
  166. package/src/types/FIXML50SP2/stream_assignment_request.ts +1 -1
  167. package/src/util/unzip.js +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jspurefix",
3
- "version": "4.1.2",
3
+ "version": "5.1.0",
4
4
  "description": "pure node js fix engine",
5
5
  "keywords": [
6
6
  "typescript",
@@ -92,6 +92,7 @@
92
92
  "yauzl": "^3.2.0"
93
93
  },
94
94
  "devDependencies": {
95
+ "@stylistic/eslint-plugin": "^5.10.0",
95
96
  "@types/express": "^5.0.6",
96
97
  "@types/express-serve-static-core": "^5.1.1",
97
98
  "@types/jest": "^30.0.0",
@@ -103,10 +104,8 @@
103
104
  "@types/sax": "^1.2.7",
104
105
  "@types/uuid": "^9.0.7",
105
106
  "@types/winston": "^2.4.4",
106
- "@typescript-eslint/eslint-plugin": "^6.19.0",
107
- "@typescript-eslint/parser": "^6.19.0",
108
- "eslint": "^8.56.0",
109
- "eslint-config-standard-with-typescript": "^43.0.1",
107
+ "eslint": "^9.39.4",
108
+ "eslint-config-love": "^151.0.0",
110
109
  "jest": "^30.2.0",
111
110
  "madge": "^8.0.0",
112
111
  "standard": "^17.1.2",
@@ -83,7 +83,6 @@ export class AsciiParserState {
83
83
  const tag: number = this.currentTag
84
84
  const locations: Tags = this.locations
85
85
  const buffer = this.elasticBuffer
86
- const terminates = this.checksumExpectedPos
87
86
 
88
87
  switch (this.parseState) {
89
88
  case ParseState.ParsingValue:
@@ -132,17 +131,15 @@ export class AsciiParserState {
132
131
  }
133
132
 
134
133
  case Tags.CheckSumTag: {
135
- if (valueEndPos < this.bodyLen) {
136
- throw new Error(`CheckSumTag: [${valueEndPos}] expected after ${this.bodyLen}`)
137
- }
134
+ // do not enforce body length constraint - manually edited messages
135
+ // may have mismatched body length but are otherwise valid
138
136
  this.parseState = ParseState.MsgComplete
139
137
  break
140
138
  }
141
139
 
142
140
  default: {
143
- if (terminates && valueEndPos > terminates) {
144
- throw new Error(`Tag: [${tag}] cant be after ${terminates}`)
145
- }
141
+ // do not enforce body length boundary - manually edited messages
142
+ // may have fields added or modified beyond the original body length
146
143
  break
147
144
  }
148
145
  }
@@ -77,6 +77,10 @@ export class AsciiParser extends MsgParser {
77
77
  })
78
78
  }
79
79
 
80
+ public reset (): void {
81
+ this.state.beginMessage()
82
+ }
83
+
80
84
  private msg (ptr: number): void {
81
85
  const receivingBuffer: ElasticBuffer = this.receivingBuffer
82
86
  const state = this.state
@@ -136,15 +140,22 @@ export class AsciiParser extends MsgParser {
136
140
  case ParseState.ParsingRawData: {
137
141
  // keep skipping until length read, regardless of delimiter or not
138
142
  if (state.incRaw()) {
139
- // having consumed the raw field expecting delimiter
143
+ // having consumed the declared raw data length, expect delimiter.
144
+ // if not found (e.g. replayed messages with pretty-printed XML where
145
+ // the stored length undershoots), keep scanning forward to the actual
146
+ // delimiter rather than throwing.
140
147
  if (charAtPos === delimiter) {
141
148
  if (switchDelimiter) {
142
149
  receivingBuffer.switchChar(writeDelimiter)
143
150
  }
144
151
  state.store()
145
- } else {
146
- throw new Error(`delimiter (${delimiter}) expected at position ${readPtr} when value is ${charAtPos}`)
147
152
  }
153
+ } else if (charAtPos === delimiter) {
154
+ // hit delimiter before declared length exhausted — store what we have
155
+ if (switchDelimiter) {
156
+ receivingBuffer.switchChar(writeDelimiter)
157
+ }
158
+ state.store()
148
159
  }
149
160
  break
150
161
  }
@@ -16,6 +16,7 @@ import { DITokens } from '../../runtime/di-tokens'
16
16
  import { SegmentType } from '../segment/segment-type'
17
17
  import { AsciiParserError } from './ascii-segment-parser-error'
18
18
  import { AsciiSegmentParserSummary } from './ascii-segment-parser-summary'
19
+ import { TagIndex } from './tag-index'
19
20
 
20
21
  // this takes linear time i.e. it constantly makes forward progress
21
22
  // one tag at a time
@@ -39,6 +40,10 @@ export class AsciiSegmentParser {
39
40
  let currentTagPosition: number = 0
40
41
  let peek: SegmentDescription
41
42
 
43
+ // track depth-1 components that have been exited for fragmentation detection
44
+ const exitedDepth1Components: Set<string> = new Set()
45
+ const fragmentedComponents: Set<string> = new Set()
46
+
42
47
  // having finished one segments keep unwinding until tag matches further up stack
43
48
  function unwind (tag: number): void {
44
49
  while (structureStack.length > 1) {
@@ -46,6 +51,12 @@ export class AsciiSegmentParser {
46
51
  if (!done) continue
47
52
  done.end(segments.length, currentTagPosition - 1, tags.tagPos[currentTagPosition - 1].tag)
48
53
  segments.push(done)
54
+
55
+ // track when we exit depth-1 components for fragmentation detection
56
+ if (done.depth === 1 && done.name) {
57
+ exitedDepth1Components.add(done.name)
58
+ }
59
+
49
60
  peek = structureStack[structureStack.length - 1]
50
61
  if (peek.set?.containedTag[tag]) {
51
62
  // unwound to point this tag lives in this set.
@@ -86,6 +97,10 @@ export class AsciiSegmentParser {
86
97
  // moving deeper into structure, start a new context
87
98
  case ContainedFieldType.Component: {
88
99
  const cf: ContainedComponentField = currentField as ContainedComponentField
100
+ // detect fragmentation: re-entering a depth-1 component we already exited
101
+ if (structureStack.length === 1 && exitedDepth1Components.has(cf.name)) {
102
+ fragmentedComponents.add(cf.name)
103
+ }
89
104
  structure = new SegmentDescription(cf.name, tag, cf.definition,
90
105
  currentTagPosition, structureStack.length, SegmentType.Component)
91
106
  break
@@ -170,11 +185,33 @@ export class AsciiSegmentParser {
170
185
  segments[m2] = tmp
171
186
  }
172
187
 
188
+ function fragments (): void {
189
+ // only build SegmentViews for components detected as fragmented during discover
190
+ // non-fragmented components use their position ranges directly (zero overhead)
191
+ if (fragmentedComponents.size === 0) return
192
+ const ti = new TagIndex(msgDefinition!, tags, last + 1)
193
+ const seen: Set<string> = new Set()
194
+ for (let i = 1; i < segments.length - 1; i++) {
195
+ const seg = segments[i]
196
+ if (seg.depth !== 1) continue
197
+ if (!seg.name) continue
198
+ if (seg.segmentView) continue
199
+ if (seen.has(seg.name)) continue
200
+ if (!fragmentedComponents.has(seg.name)) continue
201
+ if (ti.isComponentGroupWrapper(seg.name)) continue
202
+ const v = ti.getInstance(seg.name)
203
+ if (!v) continue
204
+ seen.add(seg.name)
205
+ seg.addSegmentView(v)
206
+ }
207
+ }
208
+
173
209
  const msgStructure = new SegmentDescription(msgDefinition.name, tags.tagPos[0].tag, msgDefinition,
174
210
  currentTagPosition, structureStack.length, SegmentType.Msg)
175
211
  structureStack.push(msgStructure)
176
212
  discover()
177
213
  clean()
214
+ fragments()
178
215
 
179
216
  // now know where all components and groups are positioned within message
180
217
  return new Structure(tags, segments)
@@ -0,0 +1,101 @@
1
+ import { IContainedSet } from '../../dictionary/contained'
2
+ import { SegmentView } from '../segment/segment-view'
3
+ import { TagPos } from '../tag/tag-pos'
4
+ import { Tags } from '../tag/tags'
5
+
6
+ export interface TagSpan {
7
+ start: number
8
+ end: number
9
+ }
10
+
11
+ export class TagIndex {
12
+ private readonly sortedTagPos: TagPos[]
13
+ private readonly tagSpans: Map<number, TagSpan> = new Map()
14
+ private readonly componentGroupWrappers: Set<string> = new Set()
15
+ private readonly cache: Map<string, SegmentView | null> = new Map()
16
+
17
+ constructor (
18
+ public readonly set: IContainedSet,
19
+ tags: Tags,
20
+ count: number) {
21
+ this.sortedTagPos = new Array(count)
22
+ for (let i = 0; i < count; i++) {
23
+ this.sortedTagPos[i] = tags.tagPos[i]
24
+ }
25
+ this.sortedTagPos.sort(TagPos.compare)
26
+ this.populateTagSpans()
27
+ this.calcGroups(tags, count)
28
+ }
29
+
30
+ private populateTagSpans (): void {
31
+ for (let i = 0; i < this.sortedTagPos.length; i++) {
32
+ const t = this.sortedTagPos[i]
33
+ const existing = this.tagSpans.get(t.tag)
34
+ if (existing) {
35
+ existing.end = i
36
+ } else {
37
+ this.tagSpans.set(t.tag, { start: i, end: i })
38
+ }
39
+ }
40
+ }
41
+
42
+ private calcGroups (tags: Tags, count: number): void {
43
+ for (let i = 0; i < count; i++) {
44
+ const tag = tags.tagPos[i]
45
+ const field = this.set.tagToField[tag.tag]
46
+ if (!field) continue
47
+ // detect component group wrappers (single-field components containing a group)
48
+ const component = this.findParentComponent(tag.tag)
49
+ if (component) {
50
+ const componentSet = this.set.components.get(component)
51
+ if (componentSet && componentSet.fields.length === 1) {
52
+ this.componentGroupWrappers.add(component)
53
+ }
54
+ }
55
+ }
56
+ }
57
+
58
+ private findParentComponent (tag: number): string | null {
59
+ for (const [name, componentSet] of this.set.components) {
60
+ if (componentSet.containedTag[tag]) {
61
+ return name
62
+ }
63
+ }
64
+ return null
65
+ }
66
+
67
+ public getInstance (name: string): SegmentView | null {
68
+ const cached = this.cache.get(name)
69
+ if (cached !== undefined) {
70
+ return cached
71
+ }
72
+
73
+ const componentSet = this.set.components.get(name)
74
+ if (!componentSet) {
75
+ this.cache.set(name, null)
76
+ return null
77
+ }
78
+
79
+ const view = new SegmentView(name, componentSet)
80
+ const flatTags = componentSet.flattenedTag
81
+ for (let x = 0; x < flatTags.length; x++) {
82
+ const t = flatTags[x]
83
+ const span = this.tagSpans.get(t)
84
+ if (!span) continue
85
+ if (span.start === span.end) {
86
+ view.add(this.sortedTagPos[span.start])
87
+ } else {
88
+ for (let j = span.start; j <= span.end; j++) {
89
+ view.add(this.sortedTagPos[j])
90
+ }
91
+ }
92
+ }
93
+
94
+ this.cache.set(name, view)
95
+ return view
96
+ }
97
+
98
+ public isComponentGroupWrapper (name: string): boolean {
99
+ return this.componentGroupWrappers.has(name)
100
+ }
101
+ }
@@ -276,7 +276,7 @@ export abstract class MsgView {
276
276
  }
277
277
  // is this a full name where abbreviation exists
278
278
  const component: ContainedField | null = a.segment.set?.localNameToField.get(current) ?? null
279
- // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
279
+
280
280
  if (component) {
281
281
  const abbreviated: SegmentDescription | null = structure?.firstContainedWithin(component.name, a.segment) ?? null
282
282
  if (abbreviated) {
@@ -362,8 +362,14 @@ export abstract class MsgView {
362
362
  private allStrings (): Array<(string | null)> {
363
363
  const segment: SegmentDescription = this.segment
364
364
  const range: number[] = []
365
- for (let i: number = segment.startPosition; i <= segment.endPosition; ++i) {
366
- range[range.length] = i
365
+ if (segment.segmentView) {
366
+ for (const tp of segment.segmentView.tags) {
367
+ range[range.length] = tp.position
368
+ }
369
+ } else {
370
+ for (let i: number = segment.startPosition; i <= segment.endPosition; ++i) {
371
+ range[range.length] = i
372
+ }
367
373
  }
368
374
  return range.map((i: number) => this.stringAtPosition(i))
369
375
  }
@@ -383,7 +389,7 @@ export abstract class MsgView {
383
389
  }
384
390
 
385
391
  private asLoose (def: IContainedSet): ILooseObject {
386
- // eslint-disable-next-line
392
+
387
393
  return this.reducer.reduce(def, {
388
394
  group: (a: ILooseObject, field: ContainedGroupField) => { this.asLooseGroup(a, field) },
389
395
  simple: (a: ILooseObject, field: ContainedSimpleField) => { this.asLooseSimple(a, field) },
@@ -469,8 +475,14 @@ export abstract class MsgView {
469
475
  let forwards = this.sortedTagPosForwards
470
476
  if (!forwards) {
471
477
  const segment = this.segment
472
- forwards = this.sortedTagPosForwards = this.structure.tags.tagPos.slice(segment.startPosition, segment.endPosition + 1)
473
- forwards.sort(TagPos.compare)
478
+ if (segment.segmentView) {
479
+ // fragmented component: use the collected tags from SegmentView
480
+ forwards = this.sortedTagPosForwards = segment.segmentView.tags.slice()
481
+ forwards.sort(TagPos.compare)
482
+ } else {
483
+ forwards = this.sortedTagPosForwards = this.structure.tags.tagPos.slice(segment.startPosition, segment.endPosition + 1)
484
+ forwards.sort(TagPos.compare)
485
+ }
474
486
  this.sortedTagPosBackwards = forwards.slice().reverse()
475
487
  }
476
488
  return TagPos.binarySearch(forwards, tag)
@@ -2,6 +2,7 @@ import { INumericKeyed } from '../../collections/collection'
2
2
  import { ContainedField, IContainedSet } from '../../dictionary/contained'
3
3
  import { GroupFieldDefinition } from '../../dictionary/definition'
4
4
  import { SegmentType } from './segment-type'
5
+ import { SegmentView } from './segment-view'
5
6
  import { ElasticBuffer } from '../elastic-buffer'
6
7
 
7
8
  export class SegmentDescription {
@@ -12,6 +13,7 @@ export class SegmentDescription {
12
13
  public delimiterPositions: number[]
13
14
  public currentField: ContainedField | null
14
15
  public containedDelimiterPositions: INumericKeyed<boolean>
16
+ public segmentView: SegmentView | null = null
15
17
 
16
18
  constructor (
17
19
  public name: string,
@@ -91,6 +93,14 @@ export class SegmentDescription {
91
93
  return delimiter
92
94
  }
93
95
 
96
+ public addSegmentView (view: SegmentView): void {
97
+ this.segmentView = view
98
+ this.endPosition = view.endPosition
99
+ this.startPosition = view.startPosition
100
+ this.endTag = view.endTag
101
+ this.startTag = view.startTag
102
+ }
103
+
94
104
  public end (i: number, pos: number, endTag: number): void {
95
105
  this.index = i
96
106
  this.currentField = null
@@ -0,0 +1,28 @@
1
+ import { IContainedSet } from '../../dictionary/contained'
2
+ import { TagPos } from '../tag/tag-pos'
3
+
4
+ export class SegmentView {
5
+ public endTag: number = 0
6
+ public endPosition: number = Number.MIN_SAFE_INTEGER
7
+ public startPosition: number = Number.MAX_SAFE_INTEGER
8
+ public startTag: number = 0
9
+ public readonly tags: TagPos[] = []
10
+
11
+ constructor (
12
+ public readonly name: string,
13
+ public readonly set: IContainedSet) {
14
+ }
15
+
16
+ public add (tag: TagPos): void {
17
+ if (tag.position < this.startPosition) {
18
+ this.startPosition = tag.position
19
+ this.startTag = tag.tag
20
+ }
21
+ if (tag.position > this.endPosition) {
22
+ this.endPosition = tag.position
23
+ this.endTag = tag.tag
24
+ }
25
+ // clone the TagPos to avoid mutation when Tags array is reused
26
+ this.tags.push(tag.clone())
27
+ }
28
+ }
@@ -1,4 +1,3 @@
1
1
  export type INumericKeyed<T> = Record<number, T>
2
2
 
3
3
  export type ILooseObject = Record<string, any>
4
-
@@ -21,7 +21,6 @@ export class EmptyLogger implements IJsFixLogger {
21
21
  // nothing
22
22
  }
23
23
 
24
- // eslint-disable-next-line n/handle-callback-err
25
24
  public error (_: Error): void {
26
25
  // nothing
27
26
  }
@@ -95,7 +95,7 @@ export class FixMsgAsciiStoreResend {
95
95
  new Date(),
96
96
  startGap,
97
97
  gapFill,
98
- null,
98
+ null
99
99
  )
100
100
  }
101
101
 
@@ -379,6 +379,13 @@ export abstract class FixSession extends events.EventEmitter {
379
379
 
380
380
  public reset (resetSeqNum?: number | null): void {
381
381
  this.stopTimer()
382
+ // unsubscribe from old transport before clearing reference
383
+ this.unsubscribe()
384
+ // reset parser to clear any partial message state from dropped connection
385
+ const receiver: any = this.transport?.receiver
386
+ if (receiver?.reset) {
387
+ receiver.reset()
388
+ }
382
389
  this.transport = null
383
390
  const resetFlag = this.config.description.ResetSeqNumFlag
384
391
  const seqNum = resetFlag ? 0 : resetSeqNum ?? this.sessionState.lastPeerMsgSeqNum
@@ -14,7 +14,7 @@ export class TcpInitiatorConnector extends FixEntity {
14
14
  }
15
15
 
16
16
  async start (reconnectTimeout: number = 0): Promise<any> {
17
- // eslint-disable-next-line no-async-promise-executor
17
+
18
18
  return await new Promise<any>(async (resolve, reject) => {
19
19
  const logger = this.config.logFactory.logger('initiator')
20
20
  const sessionContainer = this.config.sessionContainer
@@ -7566,4 +7566,3 @@ export enum BatchProcessMode {
7566
7566
  Update = 0,
7567
7567
  Snapshot = 1
7568
7568
  }
7569
-
@@ -6085,4 +6085,3 @@ export enum StreamAsgnType {
6085
6085
  Rejected = 2,
6086
6086
  Terminate = 3
6087
6087
  }
6088
-
@@ -1,5 +1,5 @@
1
1
  export interface IHopGrp {
2
- HopCompID?: string // 628
2
+ HopCompID?: string // 628
3
3
  HopSendingTime?: Date// 629
4
4
  HopRefID?: number// 630
5
5
  }
@@ -571,4 +571,3 @@ export enum MiscFeeType {
571
571
  Levy = '6',
572
572
  Other = '7'
573
573
  }
574
-
@@ -801,4 +801,3 @@ export enum AllocHandlInst {
801
801
  Forward = 2,
802
802
  ForwardAndMatch = 3
803
803
  }
804
-
@@ -943,4 +943,3 @@ export enum MultiLegReportingType {
943
943
  IndividualLegOfAMultiLegSecurity = '2',
944
944
  MultiLegSecurity = '3'
945
945
  }
946
-
@@ -1512,4 +1512,3 @@ export enum MultiLegReportingType {
1512
1512
  IndividualLegOfAMultiLegSecurity = '2',
1513
1513
  MultiLegSecurity = '3'
1514
1514
  }
1515
-
@@ -1672,4 +1672,3 @@ export enum QuoteRequestRejectReason {
1672
1672
  InvalidPrice = 5,
1673
1673
  NotAuthorizedToRequestQuote = 6
1674
1674
  }
1675
-
@@ -38,7 +38,7 @@ export interface IAllocation {
38
38
  AvgPx: number// [23] 6 (Float)
39
39
  Currency?: string// [24] 15 (String)
40
40
  AvgPrxPrecision?: number// [25] 74 (Int)
41
- Parties?: IParties[]// [26]
41
+ Parties?: IParties[]// [26]
42
42
  TradeDate: Date// [27] 75 (LocalDate)
43
43
  TransactTime?: Date// [28] 60 (UtcTimestamp)
44
44
  SettlmntTyp?: string// [29] 63 (String)
@@ -56,7 +56,7 @@ export interface IAllocation {
56
56
  AllocPrice?: number// [41] 366 (Float)
57
57
  AllocShares?: number// [42] 80 (Float)
58
58
  ProcessCode?: string// [43] 81 (String)
59
- NestedParties?: INestedParties[]// [44]
59
+ NestedParties?: INestedParties[]// [44]
60
60
  NotifyBrokerOfCredit?: boolean// [45] 208 (Boolean)
61
61
  AllocHandlInst?: number// [46] 209 (Int)
62
62
  AllocText?: string// [47] 161 (String)
@@ -10,7 +10,7 @@ import { IStandardTrailer } from './set/standard_trailer'
10
10
  */
11
11
  export interface IAllocationAck {
12
12
  StandardHeader: IStandardHeader// [1] BeginString.8, BodyLength.9 .. OnBehalfOfSendingTime.370
13
- Parties?: IParties[]// [2]
13
+ Parties?: IParties[]// [2]
14
14
  AllocID: string// [3] 70 (String)
15
15
  TradeDate: Date// [4] 75 (LocalDate)
16
16
  TransactTime?: Date// [5] 60 (UtcTimestamp)
@@ -21,11 +21,11 @@ export interface ICrossOrderCancelReplaceRequest {
21
21
  Side: string// [3] 54 (String)
22
22
  OrigClOrdID: string// [4] 41 (String)
23
23
  ClOrdID: string// [5] 11 (String)
24
- Parties?: IParties[]// [6]
24
+ Parties?: IParties[]// [6]
25
25
  Account?: string// [7] 1 (String)
26
26
  NoAllocs?: number// [8] 78 (Int)
27
27
  AllocAccount?: string// [9] 79 (String)
28
- NestedParties?: INestedParties[]// [10]
28
+ NestedParties?: INestedParties[]// [10]
29
29
  AllocShares?: number// [11] 80 (Float)
30
30
  OrderQtyData: IOrderQtyData// [12] OrderQty.38, CashOrderQty.152
31
31
  CommissionData?: ICommissionData// [13] Commission.12, CommType.13
@@ -51,13 +51,13 @@ export interface ICrossOrderCancelReplaceRequest {
51
51
  PrevClosePx?: number// [33] 140 (Float)
52
52
  LocateReqd?: boolean// [34] 114 (Boolean)
53
53
  TransactTime: Date// [35] 60 (UtcTimestamp)
54
- Stipulations?: IStipulations[]// [36]
54
+ Stipulations?: IStipulations[]// [36]
55
55
  OrdType: string// [37] 40 (String)
56
56
  PriceType?: number// [38] 423 (Int)
57
57
  Price?: number// [39] 44 (Float)
58
58
  StopPx?: number// [40] 99 (Float)
59
59
  SpreadOrBenchmarkCurveData?: ISpreadOrBenchmarkCurveData// [41] SpreadToBenchmark.218
60
- YieldData?: IYieldData// [42]
60
+ YieldData?: IYieldData// [42]
61
61
  Currency?: string// [43] 15 (String)
62
62
  ComplianceID?: string// [44] 376 (String)
63
63
  IOIid?: string// [45] 23 (String)
@@ -16,7 +16,7 @@ export interface ICrossOrderCancelRequest {
16
16
  Side: string// [3] 54 (String)
17
17
  OrigClOrdID: string// [4] 41 (String)
18
18
  ClOrdID: string// [5] 11 (String)
19
- Parties?: IParties[]// [6]
19
+ Parties?: IParties[]// [6]
20
20
  OrderQtyData: IOrderQtyData// [7] OrderQty.38, CashOrderQty.152
21
21
  ComplianceID?: string// [8] 376 (String)
22
22
  Text?: string// [9] 58 (String)
@@ -20,7 +20,7 @@ export interface IDerivativeSecurityList {
20
20
  NoRelatedSym?: number// [6] 146 (Int)
21
21
  Instrument?: IInstrument// [7] Symbol.55, SymbolSfx.65 .. EncodedSecurityDesc.351
22
22
  Currency?: string// [8] 15 (String)
23
- InstrumentLeg?: IInstrumentLeg// [9]
23
+ InstrumentLeg?: IInstrumentLeg// [9]
24
24
  TradingSessionID?: string// [10] 336 (String)
25
25
  Text?: string// [11] 58 (String)
26
26
  EncodedTextLen?: number// [12] 354 (Int)
@@ -1731,4 +1731,3 @@ export enum MultiLegReportingType {
1731
1731
  IndividualLegOfAMultiLegSecurity = '2',
1732
1732
  MultiLegSecurity = '3'
1733
1733
  }
1734
-
@@ -31,7 +31,7 @@ export interface IExecutionReport {
31
31
  SecondaryOrderID?: string// [3] 198 (String)
32
32
  ClOrdID?: string// [4] 11 (String)
33
33
  OrigClOrdID?: string// [5] 41 (String)
34
- Parties?: IParties[]// [6]
34
+ Parties?: IParties[]// [6]
35
35
  NoContraBrokers?: number// [7] 382 (Int)
36
36
  ContraBroker?: string// [8] 375 (String)
37
37
  ContraTrader?: string// [9] 337 (String)
@@ -49,7 +49,7 @@ export interface IExecutionReport {
49
49
  FutSettDate?: Date// [21] 64 (LocalDate)
50
50
  Instrument: IInstrument// [22] Symbol.55, SymbolSfx.65 .. EncodedSecurityDesc.351
51
51
  Side: string// [23] 54 (String)
52
- Stipulations?: IStipulations[]// [24]
52
+ Stipulations?: IStipulations[]// [24]
53
53
  OrderQtyData: IOrderQtyData// [25] OrderQty.38, CashOrderQty.152
54
54
  OrdType?: string// [26] 40 (String)
55
55
  PriceType?: number// [27] 423 (Int)
@@ -86,7 +86,7 @@ export interface IExecutionReport {
86
86
  ReportToExch?: boolean// [58] 113 (Boolean)
87
87
  CommissionData?: ICommissionData// [59] Commission.12, CommType.13
88
88
  SpreadOrBenchmarkCurveData?: ISpreadOrBenchmarkCurveData// [60] SpreadToBenchmark.218
89
- YieldData?: IYieldData// [61]
89
+ YieldData?: IYieldData// [61]
90
90
  GrossTradeAmt?: number// [62] 381 (Float)
91
91
  NumDaysInterest?: number// [63] 157 (Int)
92
92
  AccruedInterestRate?: number// [64] 158 (Float)
@@ -107,7 +107,7 @@ export interface IExecutionReport {
107
107
  FutSettDate2?: Date// [79] 193 (LocalDate)
108
108
  OrderQty2?: number// [80] 192 (Float)
109
109
  MultiLegReportingType?: string// [81] 442 (String)
110
- InstrumentLeg?: IInstrumentLeg// [82]
111
- NestedParties?: INestedParties[]// [83]
110
+ InstrumentLeg?: IInstrumentLeg// [82]
111
+ NestedParties?: INestedParties[]// [83]
112
112
  StandardTrailer: IStandardTrailer// [84] SignatureLength.93, Signature.89, CheckSum.10
113
113
  }
@@ -16,7 +16,7 @@ export interface IMassQuote {
16
16
  QuoteReqID?: string// [2] 131 (String)
17
17
  QuoteID: string// [3] 117 (String)
18
18
  QuoteResponseLevel?: number// [4] 301 (Int)
19
- Parties?: IParties[]// [5]
19
+ Parties?: IParties[]// [5]
20
20
  Account?: string// [6] 1 (String)
21
21
  DefBidSize?: number// [7] 293 (Float)
22
22
  DefOfferSize?: number// [8] 294 (Float)
@@ -17,7 +17,7 @@ export interface IMassQuoteAcknowledgement {
17
17
  QuoteAckStatus: number// [4] 297 (Int)
18
18
  QuoteRejectReason?: number// [5] 300 (Int)
19
19
  QuoteResponseLevel?: number// [6] 301 (Int)
20
- Parties?: IParties[]// [7]
20
+ Parties?: IParties[]// [7]
21
21
  Account?: string// [8] 1 (String)
22
22
  Text?: string// [9] 58 (String)
23
23
  NoQuoteSets?: number// [10] 296 (Int)
@@ -18,7 +18,7 @@ export interface IMultilegOrderCancelReplaceRequest {
18
18
  OrderID?: string// [2] 37 (String)
19
19
  OrigClOrdID: string// [3] 41 (String)
20
20
  ClOrdID: string// [4] 11 (String)
21
- Parties?: IParties[]// [5]
21
+ Parties?: IParties[]// [5]
22
22
  Account?: string// [6] 1 (String)
23
23
  NoAllocs?: number// [7] 78 (Int)
24
24
  AllocAccount?: string// [8] 79 (String)
@@ -36,8 +36,8 @@ export interface IMultilegOrderCancelReplaceRequest {
36
36
  Side: string// [20] 54 (String)
37
37
  Instrument: IInstrument// [21] Symbol.55, SymbolSfx.65 .. EncodedSecurityDesc.351
38
38
  PrevClosePx?: number// [22] 140 (Float)
39
- InstrumentLeg?: IInstrumentLeg// [23]
40
- NestedParties?: INestedParties[]// [24]
39
+ InstrumentLeg?: IInstrumentLeg// [23]
40
+ NestedParties?: INestedParties[]// [24]
41
41
  LocateReqd?: boolean// [25] 114 (Boolean)
42
42
  TransactTime: Date// [26] 60 (UtcTimestamp)
43
43
  OrderQtyData: IOrderQtyData// [27] OrderQty.38, CashOrderQty.152