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.
- package/BACKPORT_PLAN.md +214 -0
- package/README.md +1 -2
- package/data/examples/FIX.4.4/fixsim/fix.txt +46 -0
- package/data/session/fixsim-qf44-initiator.json +21 -0
- package/dist/buffer/ascii/ascii-parser-state.js +0 -7
- package/dist/buffer/ascii/ascii-parser-state.js.map +1 -1
- package/dist/buffer/ascii/ascii-parser.d.ts +1 -0
- package/dist/buffer/ascii/ascii-parser.js +8 -2
- package/dist/buffer/ascii/ascii-parser.js.map +1 -1
- package/dist/buffer/ascii/ascii-segment-parser.js +36 -0
- package/dist/buffer/ascii/ascii-segment-parser.js.map +1 -1
- package/dist/buffer/ascii/tag-index.d.ts +20 -0
- package/dist/buffer/ascii/tag-index.js +89 -0
- package/dist/buffer/ascii/tag-index.js.map +1 -0
- package/dist/buffer/msg-view.js +17 -4
- package/dist/buffer/msg-view.js.map +1 -1
- package/dist/buffer/segment/segment-description.d.ts +3 -0
- package/dist/buffer/segment/segment-description.js +8 -0
- package/dist/buffer/segment/segment-description.js.map +1 -1
- package/dist/buffer/segment/segment-view.d.ts +13 -0
- package/dist/buffer/segment/segment-view.js +27 -0
- package/dist/buffer/segment/segment-view.js.map +1 -0
- package/dist/collections/collection.js.map +1 -1
- package/dist/config/js-fix-logger.js.map +1 -1
- package/dist/store/fix-msg-ascii-store-resend.js.map +1 -1
- package/dist/transport/session/fix-session.js +6 -0
- package/dist/transport/session/fix-session.js.map +1 -1
- package/dist/transport/tcp/tcp-initiator-connector.js.map +1 -1
- package/dist/types/FIX.5.0SP2/quickfix/enum/all-enum.js.map +1 -1
- package/dist/types/FIX.5.0SP2/repo/enum/all-enum.js.map +1 -1
- package/dist/types/FIX.5.0SP2/repo/set/hop_grp.js.map +1 -1
- package/dist/types/FIX4.0/repo/enum/all-enum.js.map +1 -1
- package/dist/types/FIX4.1/repo/enum/all-enum.js.map +1 -1
- package/dist/types/FIX4.2/quickfix/enum/all-enum.js.map +1 -1
- package/dist/types/FIX4.2/repo/enum/all-enum.js.map +1 -1
- package/dist/types/FIX4.3/quickfix/enum/all-enum.js.map +1 -1
- package/dist/types/FIX4.3/repo/allocation.js.map +1 -1
- package/dist/types/FIX4.3/repo/allocation_ack.js.map +1 -1
- package/dist/types/FIX4.3/repo/cross_order_cancel_replace_request.js.map +1 -1
- package/dist/types/FIX4.3/repo/cross_order_cancel_request.js.map +1 -1
- package/dist/types/FIX4.3/repo/derivative_security_list.js.map +1 -1
- package/dist/types/FIX4.3/repo/enum/all-enum.js.map +1 -1
- package/dist/types/FIX4.3/repo/execution_report.js.map +1 -1
- package/dist/types/FIX4.3/repo/mass_quote.js.map +1 -1
- package/dist/types/FIX4.3/repo/mass_quote_acknowledgement.js.map +1 -1
- package/dist/types/FIX4.3/repo/multileg_order_cancel_replace_request.js.map +1 -1
- package/dist/types/FIX4.3/repo/new_order_cross.js.map +1 -1
- package/dist/types/FIX4.3/repo/new_order_list.js.map +1 -1
- package/dist/types/FIX4.3/repo/new_order_multileg.js.map +1 -1
- package/dist/types/FIX4.3/repo/new_order_single.js.map +1 -1
- package/dist/types/FIX4.3/repo/order_cancel_replace_request.js.map +1 -1
- package/dist/types/FIX4.3/repo/order_cancel_request.js.map +1 -1
- package/dist/types/FIX4.3/repo/order_mass_status_request.js.map +1 -1
- package/dist/types/FIX4.3/repo/order_status_request.js.map +1 -1
- package/dist/types/FIX4.3/repo/quote.js.map +1 -1
- package/dist/types/FIX4.3/repo/quote_cancel.js.map +1 -1
- package/dist/types/FIX4.3/repo/quote_request.js.map +1 -1
- package/dist/types/FIX4.3/repo/quote_request_reject.js.map +1 -1
- package/dist/types/FIX4.3/repo/quote_status_report.js.map +1 -1
- package/dist/types/FIX4.3/repo/quote_status_request.js.map +1 -1
- package/dist/types/FIX4.3/repo/registration_instructions.js.map +1 -1
- package/dist/types/FIX4.3/repo/registration_instructions_response.js.map +1 -1
- package/dist/types/FIX4.3/repo/security_definition.js.map +1 -1
- package/dist/types/FIX4.3/repo/security_definition_request.js.map +1 -1
- package/dist/types/FIX4.3/repo/security_list.js.map +1 -1
- package/dist/types/FIX4.3/repo/settlement_instructions.js.map +1 -1
- package/dist/types/FIX4.3/repo/trade_capture_report.js.map +1 -1
- package/dist/types/FIX4.3/repo/trade_capture_report_request.js.map +1 -1
- package/dist/types/FIX4.4/quickfix/enum/all-enum.js.map +1 -1
- package/dist/types/FIX4.4/repo/enum/all-enum.js.map +1 -1
- package/dist/types/FIX4.4/repo/set/hop.js.map +1 -1
- package/dist/types/FIXML50SP2/adjusted_position_report.js.map +1 -1
- package/dist/types/FIXML50SP2/derivative_security_list.js.map +1 -1
- package/dist/types/FIXML50SP2/derivative_security_list_update_report.js.map +1 -1
- package/dist/types/FIXML50SP2/email.js.map +1 -1
- package/dist/types/FIXML50SP2/enum/all-enum.js.map +1 -1
- package/dist/types/FIXML50SP2/news.js.map +1 -1
- package/dist/types/FIXML50SP2/quote_cancel.js.map +1 -1
- package/dist/types/FIXML50SP2/set/market_segment_grp.js.map +1 -1
- package/dist/types/FIXML50SP2/set/party_risk_limits_ack_grp.js.map +1 -1
- package/dist/types/FIXML50SP2/set/party_risk_limits_grp.js.map +1 -1
- package/dist/types/FIXML50SP2/set/party_risk_limits_update_grp.js.map +1 -1
- package/dist/types/FIXML50SP2/set/sec_list_grp.js.map +1 -1
- package/dist/types/FIXML50SP2/set/sec_lst_upd_rel_sym_grp.js.map +1 -1
- package/dist/types/FIXML50SP2/set/trading_session_rules_grp.js.map +1 -1
- package/dist/types/FIXML50SP2/set/trd_instrmt_leg_grp.js.map +1 -1
- package/dist/types/FIXML50SP2/set/trd_sess_lst_grp.js.map +1 -1
- package/dist/types/FIXML50SP2/stream_assignment_report.js.map +1 -1
- package/dist/types/FIXML50SP2/stream_assignment_request.js.map +1 -1
- package/eslint.config.mjs +119 -0
- package/jsfix.test_client.txt +66 -66
- package/jsfix.test_server.txt +63 -63
- package/package.json +4 -5
- package/src/buffer/ascii/ascii-parser-state.ts +4 -7
- package/src/buffer/ascii/ascii-parser.ts +14 -3
- package/src/buffer/ascii/ascii-segment-parser.ts +37 -0
- package/src/buffer/ascii/tag-index.ts +101 -0
- package/src/buffer/msg-view.ts +18 -6
- package/src/buffer/segment/segment-description.ts +10 -0
- package/src/buffer/segment/segment-view.ts +28 -0
- package/src/collections/collection.ts +0 -1
- package/src/config/js-fix-logger.ts +0 -1
- package/src/store/fix-msg-ascii-store-resend.ts +1 -1
- package/src/transport/session/fix-session.ts +7 -0
- package/src/transport/tcp/tcp-initiator-connector.ts +1 -1
- package/src/types/FIX.5.0SP2/quickfix/enum/all-enum.ts +0 -1
- package/src/types/FIX.5.0SP2/repo/enum/all-enum.ts +0 -1
- package/src/types/FIX.5.0SP2/repo/set/hop_grp.ts +1 -1
- package/src/types/FIX4.0/repo/enum/all-enum.ts +0 -1
- package/src/types/FIX4.1/repo/enum/all-enum.ts +0 -1
- package/src/types/FIX4.2/quickfix/enum/all-enum.ts +0 -1
- package/src/types/FIX4.2/repo/enum/all-enum.ts +0 -1
- package/src/types/FIX4.3/quickfix/enum/all-enum.ts +0 -1
- package/src/types/FIX4.3/repo/allocation.ts +2 -2
- package/src/types/FIX4.3/repo/allocation_ack.ts +1 -1
- package/src/types/FIX4.3/repo/cross_order_cancel_replace_request.ts +4 -4
- package/src/types/FIX4.3/repo/cross_order_cancel_request.ts +1 -1
- package/src/types/FIX4.3/repo/derivative_security_list.ts +1 -1
- package/src/types/FIX4.3/repo/enum/all-enum.ts +0 -1
- package/src/types/FIX4.3/repo/execution_report.ts +5 -5
- package/src/types/FIX4.3/repo/mass_quote.ts +1 -1
- package/src/types/FIX4.3/repo/mass_quote_acknowledgement.ts +1 -1
- package/src/types/FIX4.3/repo/multileg_order_cancel_replace_request.ts +3 -3
- package/src/types/FIX4.3/repo/new_order_cross.ts +4 -4
- package/src/types/FIX4.3/repo/new_order_list.ts +4 -4
- package/src/types/FIX4.3/repo/new_order_multileg.ts +3 -3
- package/src/types/FIX4.3/repo/new_order_single.ts +4 -4
- package/src/types/FIX4.3/repo/order_cancel_replace_request.ts +3 -3
- package/src/types/FIX4.3/repo/order_cancel_request.ts +1 -1
- package/src/types/FIX4.3/repo/order_mass_status_request.ts +1 -1
- package/src/types/FIX4.3/repo/order_status_request.ts +1 -1
- package/src/types/FIX4.3/repo/quote.ts +1 -1
- package/src/types/FIX4.3/repo/quote_cancel.ts +1 -1
- package/src/types/FIX4.3/repo/quote_request.ts +2 -2
- package/src/types/FIX4.3/repo/quote_request_reject.ts +2 -2
- package/src/types/FIX4.3/repo/quote_status_report.ts +1 -1
- package/src/types/FIX4.3/repo/quote_status_request.ts +1 -1
- package/src/types/FIX4.3/repo/registration_instructions.ts +2 -2
- package/src/types/FIX4.3/repo/registration_instructions_response.ts +1 -1
- package/src/types/FIX4.3/repo/security_definition.ts +1 -1
- package/src/types/FIX4.3/repo/security_definition_request.ts +1 -1
- package/src/types/FIX4.3/repo/security_list.ts +1 -1
- package/src/types/FIX4.3/repo/settlement_instructions.ts +1 -1
- package/src/types/FIX4.3/repo/trade_capture_report.ts +1 -1
- package/src/types/FIX4.3/repo/trade_capture_report_request.ts +1 -1
- package/src/types/FIX4.4/quickfix/enum/all-enum.ts +0 -1
- package/src/types/FIX4.4/repo/enum/all-enum.ts +0 -1
- package/src/types/FIX4.4/repo/set/hop.ts +1 -1
- package/src/types/FIXML50SP2/adjusted_position_report.ts +1 -1
- package/src/types/FIXML50SP2/derivative_security_list.ts +1 -1
- package/src/types/FIXML50SP2/derivative_security_list_update_report.ts +1 -1
- package/src/types/FIXML50SP2/email.ts +1 -1
- package/src/types/FIXML50SP2/enum/all-enum.ts +0 -1
- package/src/types/FIXML50SP2/news.ts +1 -1
- package/src/types/FIXML50SP2/quote_cancel.ts +1 -1
- package/src/types/FIXML50SP2/set/market_segment_grp.ts +1 -1
- package/src/types/FIXML50SP2/set/party_risk_limits_ack_grp.ts +1 -1
- package/src/types/FIXML50SP2/set/party_risk_limits_grp.ts +1 -1
- package/src/types/FIXML50SP2/set/party_risk_limits_update_grp.ts +1 -1
- package/src/types/FIXML50SP2/set/sec_list_grp.ts +2 -2
- package/src/types/FIXML50SP2/set/sec_lst_upd_rel_sym_grp.ts +1 -1
- package/src/types/FIXML50SP2/set/trading_session_rules_grp.ts +1 -1
- package/src/types/FIXML50SP2/set/trd_instrmt_leg_grp.ts +1 -1
- package/src/types/FIXML50SP2/set/trd_sess_lst_grp.ts +1 -1
- package/src/types/FIXML50SP2/stream_assignment_report.ts +1 -1
- package/src/types/FIXML50SP2/stream_assignment_request.ts +1 -1
- package/src/util/unzip.js +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jspurefix",
|
|
3
|
-
"version": "
|
|
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
|
-
"
|
|
107
|
-
"
|
|
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
|
-
|
|
136
|
-
|
|
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
|
-
|
|
144
|
-
|
|
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
|
|
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
|
+
}
|
package/src/buffer/msg-view.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
366
|
-
|
|
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
|
-
|
|
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
|
-
|
|
473
|
-
|
|
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
|
+
}
|
|
@@ -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
|
-
|
|
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
|
|
@@ -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)
|
|
@@ -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
|