jspurefix 4.1.2 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BACKPORT_PLAN.md +214 -0
- 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-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/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-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/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/BACKPORT_PLAN.md
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Backport Plan: C# cspurefix → TypeScript jspurefix
|
|
2
|
+
|
|
3
|
+
## Background
|
|
4
|
+
|
|
5
|
+
The C# dotnet core port (`~/dev/cs/cspurefix`) has accumulated significant improvements over the TypeScript version which has had no real improvements for some time. The two codebases are structurally very similar, making incremental backporting feasible. The goal is to bring the TS version up to the same quality level through careful, non-disruptive changes.
|
|
6
|
+
|
|
7
|
+
A standalone demo at `~/dev/cs/purefix-standalone-demo` demonstrates the C# store/recovery working end-to-end including restart scenarios and extended soak testing.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Phase 1: Non-Contiguous Tag Parsing (Correctness Fix)
|
|
12
|
+
|
|
13
|
+
**Priority: Highest | Risk: Medium | Scope: Contained to `src/buffer/`**
|
|
14
|
+
|
|
15
|
+
### Problem
|
|
16
|
+
|
|
17
|
+
The TS segment parser assumes component tags appear in a contiguous block within the message body. Some brokers (discovered when using the C# engine) send messages where root-level component tags are scattered. For example, an Instrument component's tags (Symbol=55, SecurityIDSource=22, SecurityID=48) can be interleaved with non-Instrument tags (HandlInst=21, OrderQty=38, OrdType=40, Price=44, Side=54).
|
|
18
|
+
|
|
19
|
+
This is valid FIX — the spec does not require component tags to be contiguous at the root level. The TS parser fails to correctly resolve these messages.
|
|
20
|
+
|
|
21
|
+
### Example (from fixsim-examples.txt NewOrderSingle)
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
8=FIX.4.4|9=193|35=D|34=5|49=FIXSIMDEMO|52=20241013-14:09:45.126|56=sjames8888@gmail_com|
|
|
25
|
+
11=567638644253361428000|15=GBP|21=2|22=5|38=100|40=2|44=100|48=VOD.L|54=1|55=VOD.L|
|
|
26
|
+
59=0|60=20241013-14:09:45.126|388=1|10=091|
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Instrument tags (15, 22, 48, 55) are scattered among order tags (21, 38, 40, 44, 54, 59, 60).
|
|
30
|
+
|
|
31
|
+
### C# Solution
|
|
32
|
+
|
|
33
|
+
| File | Purpose |
|
|
34
|
+
|------|---------|
|
|
35
|
+
| `PureFix.Buffer/Ascii/AsciiSegmentParser.cs` | Stack-based parser with fragment detection |
|
|
36
|
+
| `PureFix.Buffer/Ascii/AsciiSegmentParser.Context.cs` | Parse state with `ExitedDepth1Components` and `FragmentedComponents` tracking |
|
|
37
|
+
| `PureFix.Buffer/Ascii/TagIndex.cs` | Indexes tags for non-contiguous access via `_tagSpans` dictionary |
|
|
38
|
+
| `PureFix.Buffer/Segment/SegmentView.cs` | Transparent access to scattered tags as if contiguous |
|
|
39
|
+
| `PureFix.Buffer/Segment/SegmentDescription.cs` | Segment metadata with position tracking |
|
|
40
|
+
|
|
41
|
+
**Algorithm:**
|
|
42
|
+
|
|
43
|
+
1. **Discovery**: Stack-based recursive descent through tags. When a depth-1 component is popped from the stack, it's added to `ExitedDepth1Components`.
|
|
44
|
+
2. **Fragment Detection**: If a tag belonging to an already-exited depth-1 component is encountered, that component is added to `FragmentedComponents`.
|
|
45
|
+
3. **Optimised Access**: Only fragmented components get expensive `SegmentView` construction via `TagIndex`. Non-fragmented components use simple position ranges (no overhead).
|
|
46
|
+
|
|
47
|
+
### TS Files to Modify
|
|
48
|
+
|
|
49
|
+
| File | Change |
|
|
50
|
+
|------|--------|
|
|
51
|
+
| `src/buffer/ascii/ascii-segment-parser.ts` | Add fragment detection (ExitedDepth1Components, FragmentedComponents tracking) |
|
|
52
|
+
| `src/buffer/segment/segment-description.ts` | May need SegmentView reference for fragmented components |
|
|
53
|
+
| New: `src/buffer/segment/segment-view.ts` | Port SegmentView for non-contiguous tag access |
|
|
54
|
+
| New: `src/buffer/ascii/tag-index.ts` | Port TagIndex for tag span indexing |
|
|
55
|
+
|
|
56
|
+
### Test Data
|
|
57
|
+
|
|
58
|
+
Port from `~/dev/cs/cspurefix/PureFix.Test.ModularTypes/Data/examples/FIX.4.4/fixsim-examples.txt` — 46 real FIX messages including fragmented Instrument components. Write a failing test first to prove the problem, then fix.
|
|
59
|
+
|
|
60
|
+
### C# Test Reference
|
|
61
|
+
|
|
62
|
+
`~/dev/cs/cspurefix/PureFix.Test.ModularTypes/FixSimTest.cs` — parses all 46 messages, verifies type counts, deserialises ExecutionReport to strongly-typed object.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Phase 2: Parser Reset on Disconnect (Quick Win)
|
|
67
|
+
|
|
68
|
+
**Priority: High | Risk: Low | Scope: Small**
|
|
69
|
+
|
|
70
|
+
### Problem
|
|
71
|
+
|
|
72
|
+
When a TCP connection drops mid-message, the ASCII parser retains partial buffer state. On reconnect, the stale buffer can corrupt the next message parse. The C# version explicitly resets the parser during reconnection preparation.
|
|
73
|
+
|
|
74
|
+
### C# Solution
|
|
75
|
+
|
|
76
|
+
In `FixSession.cs` → `PrepareForReconnect()`:
|
|
77
|
+
```csharp
|
|
78
|
+
m_parser.Reset();
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Also resets transient coordinator state (logon retry count, timeout recovery attempts) while preserving sequence numbers.
|
|
82
|
+
|
|
83
|
+
### TS Files to Modify
|
|
84
|
+
|
|
85
|
+
| File | Change |
|
|
86
|
+
|------|--------|
|
|
87
|
+
| `src/transport/session/fix-session.ts` | Add parser reset in reconnection path |
|
|
88
|
+
| `src/buffer/ascii/ascii-parser.ts` | Ensure `reset()` method clears all partial state |
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Phase 3: SessionSequenceCoordinator (Foundation)
|
|
93
|
+
|
|
94
|
+
**Priority: High | Risk: Medium | Scope: Medium**
|
|
95
|
+
|
|
96
|
+
### Problem
|
|
97
|
+
|
|
98
|
+
The TS version has sequence state scattered across `fix-session-state.ts` and `ascii-session.ts` with no single source of truth. This makes it hard to reason about recovery, reset, and reconnect behaviour. The C# version centralises all of this.
|
|
99
|
+
|
|
100
|
+
### C# Solution
|
|
101
|
+
|
|
102
|
+
| File | Purpose |
|
|
103
|
+
|------|---------|
|
|
104
|
+
| `PureFix.Transport/Session/SessionSequenceCoordinator.cs` | Single source of truth for sender/target sequences |
|
|
105
|
+
|
|
106
|
+
**Key capabilities missing from TS:**
|
|
107
|
+
|
|
108
|
+
- `PrepareForReconnect()` — resets transient state, preserves sequences
|
|
109
|
+
- `HandlePeerReset()` — coordinates ResetSeqNumFlag=Y from peer
|
|
110
|
+
- `ResetAsAcceptor()` — acceptor-initiated reset
|
|
111
|
+
- `PossDupFlag` handling — messages with PossDupFlag=Y bypass normal sequence checks
|
|
112
|
+
- Logon retry with `MaxLogonRetries=100`
|
|
113
|
+
- Timeout recovery with `MaxTimeoutRecoveryAttempts=3` (handles sleep/wake scenarios)
|
|
114
|
+
- `ResendRequestManager` integration — storm protection, pending request tracking
|
|
115
|
+
- Delayed message acceptance — out-of-order messages that fill pending gap ranges
|
|
116
|
+
|
|
117
|
+
### TS Files to Modify
|
|
118
|
+
|
|
119
|
+
| File | Change |
|
|
120
|
+
|------|--------|
|
|
121
|
+
| New: `src/transport/session/session-sequence-coordinator.ts` | Port coordinator |
|
|
122
|
+
| `src/transport/session/fix-session.ts` | Integrate coordinator as sequence authority |
|
|
123
|
+
| `src/transport/ascii/ascii-session.ts` | Replace inline sequence logic with coordinator calls |
|
|
124
|
+
| `src/transport/session/fix-session-state.ts` | May reduce scope — coordinator takes over sequence tracking |
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Phase 4: Persistent Message Store
|
|
129
|
+
|
|
130
|
+
**Priority: Medium-High | Risk: Medium | Scope: Medium**
|
|
131
|
+
|
|
132
|
+
### Problem
|
|
133
|
+
|
|
134
|
+
The TS version only has `FixMsgMemoryStore` — all message history and sequence state is lost on process restart. The C# version has a QuickFix-compatible file store that persists across restarts.
|
|
135
|
+
|
|
136
|
+
### C# Solution
|
|
137
|
+
|
|
138
|
+
| File | Purpose |
|
|
139
|
+
|------|---------|
|
|
140
|
+
| `PureFix.Transport/Store/IFixSessionStore.cs` | Unified persistence interface |
|
|
141
|
+
| `PureFix.Transport/Store/FileSessionStore.cs` | QuickFix-compatible file storage |
|
|
142
|
+
| `PureFix.Transport/Store/MemorySessionStore.cs` | In-memory implementation |
|
|
143
|
+
| `PureFix.Transport/Store/FileSessionStoreFactory.cs` | Factory pattern for store creation |
|
|
144
|
+
| `PureFix.Transport/Store/FixMsgStoreRecord.cs` | Persisted message record |
|
|
145
|
+
|
|
146
|
+
**QuickFix file format:**
|
|
147
|
+
- `.seqnums` — 20-char padded sender:target sequence numbers
|
|
148
|
+
- `.session` — creation timestamp
|
|
149
|
+
- `.header` — seqnum,offset,length index
|
|
150
|
+
- `.body` — raw FIX message bytes
|
|
151
|
+
|
|
152
|
+
### Key Integration Points
|
|
153
|
+
|
|
154
|
+
- `StoreEncodedMessage()` — persist every sent message
|
|
155
|
+
- `InitializeSessionStore()` — load state on startup
|
|
156
|
+
- Store factory in config — deferred creation
|
|
157
|
+
- Store reset on `ResetSeqNumFlag=Y`
|
|
158
|
+
- Resender recreation after reset
|
|
159
|
+
|
|
160
|
+
### TS Files to Modify
|
|
161
|
+
|
|
162
|
+
| File | Change |
|
|
163
|
+
|------|--------|
|
|
164
|
+
| New: `src/store/fix-session-store.ts` | Port IFixSessionStore interface |
|
|
165
|
+
| New: `src/store/file-session-store.ts` | Port FileSessionStore |
|
|
166
|
+
| New: `src/store/file-session-store-factory.ts` | Port factory |
|
|
167
|
+
| `src/store/fix-msg-memory-store.ts` | Align with IFixSessionStore interface |
|
|
168
|
+
| `src/transport/ascii/ascii-session.ts` | Integrate store lifecycle |
|
|
169
|
+
| `src/config/js-fix-config.ts` | Add SessionStoreFactory to config |
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Phase 5: Resend Request Improvements
|
|
174
|
+
|
|
175
|
+
**Priority: Medium | Risk: Low-Medium | Scope: Small**
|
|
176
|
+
|
|
177
|
+
### Problem
|
|
178
|
+
|
|
179
|
+
The TS version sends a simple ResendRequest on gap detection with no protection against request storms or tracking of pending requests.
|
|
180
|
+
|
|
181
|
+
### C# Solution
|
|
182
|
+
|
|
183
|
+
`ResendRequestManager` in the coordinator provides:
|
|
184
|
+
- Pending request tracking (`PendingResendRequests` collection)
|
|
185
|
+
- Storm protection — after N requests without response, accepts the gap
|
|
186
|
+
- Delayed message detection — clears pending requests when gap-filling messages arrive
|
|
187
|
+
- `ResendGapFillOnly` mode — sends GapFill instead of replaying stored messages (prevents duplicate executions)
|
|
188
|
+
|
|
189
|
+
### TS Files to Modify
|
|
190
|
+
|
|
191
|
+
| File | Change |
|
|
192
|
+
|------|--------|
|
|
193
|
+
| New: `src/transport/session/resend-request-manager.ts` | Port manager |
|
|
194
|
+
| `src/transport/ascii/ascii-session.ts` | Integrate with gap detection |
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Phase 6: Message Generator / XML Parser (Future)
|
|
199
|
+
|
|
200
|
+
**Priority: Low | Risk: Medium | Scope: Large**
|
|
201
|
+
|
|
202
|
+
- C# uses nested types for groups (sub-types) in generated code
|
|
203
|
+
- QuickFix XML parser has improved design with better error tracking
|
|
204
|
+
- Nice to align for maintenance but not critical
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## General Principles
|
|
209
|
+
|
|
210
|
+
1. **Incremental PRs** — one phase at a time, smallest safe changes
|
|
211
|
+
2. **Test first** — port test data and write failing tests before fixing
|
|
212
|
+
3. **Maintain compatibility** — existing tests must continue to pass
|
|
213
|
+
4. **Align code structure** — where reasonable, keep TS looking similar to C# for easier future maintenance
|
|
214
|
+
5. **No big bang** — each phase should be independently mergeable and useful
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
8=FIX.4.4|9=0000095|35=A|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=1|57=|52=20241013-14:03:55.006|98=0|108=30|141=Y|10=000|
|
|
2
|
+
8=FIX.4.4|9=95|35=0|34=1|49=FIXSIMDEMO|52=20241013-14:04:31.878|56=sjames8888@gmail_com|112=0.824042533442398|10=088|
|
|
3
|
+
8=FIX.4.4|9=0000111|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=2|57=|52=20241013-14:04:32.430|112=heartbeat-10/13/2024 14:04:32|10=248|
|
|
4
|
+
8=FIX.4.4|9=0000111|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=3|57=|52=20241013-14:05:02.038|112=heartbeat-10/13/2024 14:05:02|10=249|
|
|
5
|
+
8=FIX.4.4|9=0000111|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=4|57=|52=20241013-14:05:32.041|112=heartbeat-10/13/2024 14:05:32|10=250|
|
|
6
|
+
8=FIX.4.4|9=0000111|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=5|57=|52=20241013-14:06:02.041|112=heartbeat-10/13/2024 14:06:02|10=247|
|
|
7
|
+
8=FIX.4.4|9=151|35=6|34=2|49=FIXSIMDEMO|52=20241013-14:06:17.412|56=sjames8888@gmail_com|15=GBP|22=5|23=566638644251319700000|27=100|28=N|44=40|48=VOD.L|54=1|55=VOD.L|10=156|
|
|
8
|
+
8=FIX.4.4|9=0000111|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=6|57=|52=20241013-14:06:32.037|112=heartbeat-10/13/2024 14:06:32|10=003|
|
|
9
|
+
8=FIX.4.4|9=0000111|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=7|57=|52=20241013-14:07:02.042|112=heartbeat-10/13/2024 14:07:02|10=252|
|
|
10
|
+
8=FIX.4.4|9=0000111|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=8|57=|52=20241013-14:07:32.029|112=heartbeat-10/13/2024 14:07:32|10=008|
|
|
11
|
+
8=FIX.4.4|9=95|35=1|34=3|49=FIXSIMDEMO|52=20241013-14:07:35.444|56=sjames8888@gmail_com|112=0.349266975815067|10=104|
|
|
12
|
+
8=FIX.4.4|9=0000099|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=9|57=|52=20241013-14:07:35.930|112=0.349266975815067|10=011|
|
|
13
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=10|57=|52=20241013-14:08:05.043|112=heartbeat-10/13/2024 14:08:05|10=048|
|
|
14
|
+
8=FIX.4.4|9=141|35=V|34=4|49=FIXSIMDEMO|52=20241013-14:08:12.728|56=sjames8888@gmail_com|262=VOD.L|263=1|264=1|146=1|55=VOD.L|167=CS|267=3|269=0|269=1|269=2|10=128|
|
|
15
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=11|57=|52=20241013-14:08:35.030|112=heartbeat-10/13/2024 14:08:35|10=051|
|
|
16
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=12|57=|52=20241013-14:09:05.043|112=heartbeat-10/13/2024 14:09:05|10=052|
|
|
17
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=13|57=|52=20241013-14:09:35.037|112=heartbeat-10/13/2024 14:09:35|10=062|
|
|
18
|
+
8=FIX.4.4|9=193|35=D|34=5|49=FIXSIMDEMO|52=20241013-14:09:45.126|56=sjames8888@gmail_com|11=567638644253361428000|15=GBP|21=2|22=5|38=100|40=2|44=100|48=VOD.L|54=1|55=VOD.L|59=0|60=20241013-14:09:45.126|388=1|10=091|
|
|
19
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=14|57=|52=20241013-14:10:05.041|112=heartbeat-10/13/2024 14:10:05|10=036|
|
|
20
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=15|57=|52=20241013-14:10:35.043|112=heartbeat-10/13/2024 14:10:35|10=045|
|
|
21
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=16|57=|52=20241013-14:11:05.038|112=heartbeat-10/13/2024 14:11:05|10=046|
|
|
22
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=17|57=|52=20241013-14:11:35.031|112=heartbeat-10/13/2024 14:11:35|10=046|
|
|
23
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=18|57=|52=20241013-14:12:05.041|112=heartbeat-10/13/2024 14:12:05|10=044|
|
|
24
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=19|57=|52=20241013-14:12:35.043|112=heartbeat-10/13/2024 14:12:35|10=053|
|
|
25
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=20|57=|52=20241013-14:13:05.028|112=heartbeat-10/13/2024 14:13:05|10=044|
|
|
26
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=21|57=|52=20241013-14:13:35.029|112=heartbeat-10/13/2024 14:13:35|10=052|
|
|
27
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=22|57=|52=20241013-14:14:05.041|112=heartbeat-10/13/2024 14:14:05|10=043|
|
|
28
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=23|57=|52=20241013-14:14:35.028|112=heartbeat-10/13/2024 14:14:35|10=055|
|
|
29
|
+
8=FIX.4.4|9=261|35=8|34=6|49=FIXSIMDEMO|52=20241013-14:15:00.746|56=sjames8888@gmail_com|6=100|14=100|15=GBP|17=41638644256253512000|18=VOD.L|21=2|22=5|31=100|32=100|37=638644257007460000|38=100|39=2|40=2|44=100|48=VOD.L|54=1|55=VOD.L|59=0|60=20241013-14:15:00.746|150=2|151=0|10=207|
|
|
30
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=24|57=|52=20241013-14:15:05.030|112=heartbeat-10/13/2024 14:15:05|10=045|
|
|
31
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=25|57=|52=20241013-14:15:35.034|112=heartbeat-10/13/2024 14:15:35|10=056|
|
|
32
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=26|57=|52=20241013-14:16:05.033|112=heartbeat-10/13/2024 14:16:05|10=052|
|
|
33
|
+
8=FIX.4.4|9=119|35=R|34=7|49=FIXSIMDEMO|52=20241013-14:16:23.036|56=sjames8888@gmail_com|131=569638644257643472000|146=1|55=VOD.L|54=1|10=113|
|
|
34
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=27|57=|52=20241013-14:16:35.037|112=heartbeat-10/13/2024 14:16:35|10=063|
|
|
35
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=28|57=|52=20241013-14:17:05.033|112=heartbeat-10/13/2024 14:17:05|10=056|
|
|
36
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=29|57=|52=20241013-14:17:35.038|112=heartbeat-10/13/2024 14:17:35|10=068|
|
|
37
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=30|57=|52=20241013-14:18:05.037|112=heartbeat-10/13/2024 14:18:05|10=055|
|
|
38
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=31|57=|52=20241013-14:18:35.029|112=heartbeat-10/13/2024 14:18:35|10=063|
|
|
39
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=32|57=|52=20241013-14:19:05.040|112=heartbeat-10/13/2024 14:19:05|10=053|
|
|
40
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=33|57=|52=20241013-14:19:35.029|112=heartbeat-10/13/2024 14:19:35|10=067|
|
|
41
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=34|57=|52=20241013-14:20:05.031|112=heartbeat-10/13/2024 14:20:05|10=039|
|
|
42
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=35|57=|52=20241013-14:20:35.029|112=heartbeat-10/13/2024 14:20:35|10=053|
|
|
43
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=36|57=|52=20241013-14:21:05.036|112=heartbeat-10/13/2024 14:21:05|10=048|
|
|
44
|
+
8=FIX.4.4|9=0000112|35=0|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=37|57=|52=20241013-14:21:35.043|112=heartbeat-10/13/2024 14:21:35|10=053|
|
|
45
|
+
8=FIX.4.4|9=0000095|35=A|49=sjames8888@gmail_com|56=FIXSIMDEMO|34=1|57=|52=20241013-14:31:31.938|98=0|108=30|141=Y|10=009|
|
|
46
|
+
8=FIX.4.4|9=96|35=0|34=8|49=FIXSIMDEMO|52=20241013-14:31:36.182|56=sjames8888@gmail_com|112=0.0534090264949058|10=144|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"application": {
|
|
3
|
+
"type": "initiator",
|
|
4
|
+
"name": "test_client",
|
|
5
|
+
"tcp": {
|
|
6
|
+
"host": "localhost",
|
|
7
|
+
"port": 2344
|
|
8
|
+
},
|
|
9
|
+
"protocol": "ascii",
|
|
10
|
+
"dictionary": "data/FIX44.xml"
|
|
11
|
+
},
|
|
12
|
+
"Username": "",
|
|
13
|
+
"Password": "",
|
|
14
|
+
"EncryptMethod": 0,
|
|
15
|
+
"ResetSeqNumFlag": true,
|
|
16
|
+
"HeartBtInt": 30,
|
|
17
|
+
"SenderCompId": "sjames8888@gmail_com",
|
|
18
|
+
"TargetCompID": "FIXSIMDEMO",
|
|
19
|
+
"TargetSubID": "",
|
|
20
|
+
"BeginString": "FIX.4.4"
|
|
21
|
+
}
|
|
@@ -21,6 +21,7 @@ const tsyringe_1 = require("tsyringe");
|
|
|
21
21
|
const di_tokens_1 = require("../../runtime/di-tokens");
|
|
22
22
|
const segment_type_1 = require("../segment/segment-type");
|
|
23
23
|
const ascii_segment_parser_error_1 = require("./ascii-segment-parser-error");
|
|
24
|
+
const tag_index_1 = require("./tag-index");
|
|
24
25
|
let AsciiSegmentParser = class AsciiSegmentParser {
|
|
25
26
|
constructor(definitions) {
|
|
26
27
|
this.definitions = definitions;
|
|
@@ -34,6 +35,8 @@ let AsciiSegmentParser = class AsciiSegmentParser {
|
|
|
34
35
|
const structureStack = [];
|
|
35
36
|
let currentTagPosition = 0;
|
|
36
37
|
let peek;
|
|
38
|
+
const exitedDepth1Components = new Set();
|
|
39
|
+
const fragmentedComponents = new Set();
|
|
37
40
|
function unwind(tag) {
|
|
38
41
|
var _a;
|
|
39
42
|
while (structureStack.length > 1) {
|
|
@@ -42,6 +45,9 @@ let AsciiSegmentParser = class AsciiSegmentParser {
|
|
|
42
45
|
continue;
|
|
43
46
|
done.end(segments.length, currentTagPosition - 1, tags.tagPos[currentTagPosition - 1].tag);
|
|
44
47
|
segments.push(done);
|
|
48
|
+
if (done.depth === 1 && done.name) {
|
|
49
|
+
exitedDepth1Components.add(done.name);
|
|
50
|
+
}
|
|
45
51
|
peek = structureStack[structureStack.length - 1];
|
|
46
52
|
if ((_a = peek.set) === null || _a === void 0 ? void 0 : _a.containedTag[tag]) {
|
|
47
53
|
break;
|
|
@@ -78,6 +84,9 @@ let AsciiSegmentParser = class AsciiSegmentParser {
|
|
|
78
84
|
}
|
|
79
85
|
case contained_1.ContainedFieldType.Component: {
|
|
80
86
|
const cf = currentField;
|
|
87
|
+
if (structureStack.length === 1 && exitedDepth1Components.has(cf.name)) {
|
|
88
|
+
fragmentedComponents.add(cf.name);
|
|
89
|
+
}
|
|
81
90
|
structure = new segment_description_1.SegmentDescription(cf.name, tag, cf.definition, currentTagPosition, structureStack.length, segment_type_1.SegmentType.Component);
|
|
82
91
|
break;
|
|
83
92
|
}
|
|
@@ -150,10 +159,37 @@ let AsciiSegmentParser = class AsciiSegmentParser {
|
|
|
150
159
|
segments[m1] = segments[m2];
|
|
151
160
|
segments[m2] = tmp;
|
|
152
161
|
}
|
|
162
|
+
function fragments() {
|
|
163
|
+
if (fragmentedComponents.size === 0)
|
|
164
|
+
return;
|
|
165
|
+
const ti = new tag_index_1.TagIndex(msgDefinition, tags, last + 1);
|
|
166
|
+
const seen = new Set();
|
|
167
|
+
for (let i = 1; i < segments.length - 1; i++) {
|
|
168
|
+
const seg = segments[i];
|
|
169
|
+
if (seg.depth !== 1)
|
|
170
|
+
continue;
|
|
171
|
+
if (!seg.name)
|
|
172
|
+
continue;
|
|
173
|
+
if (seg.segmentView)
|
|
174
|
+
continue;
|
|
175
|
+
if (seen.has(seg.name))
|
|
176
|
+
continue;
|
|
177
|
+
if (!fragmentedComponents.has(seg.name))
|
|
178
|
+
continue;
|
|
179
|
+
if (ti.isComponentGroupWrapper(seg.name))
|
|
180
|
+
continue;
|
|
181
|
+
const v = ti.getInstance(seg.name);
|
|
182
|
+
if (!v)
|
|
183
|
+
continue;
|
|
184
|
+
seen.add(seg.name);
|
|
185
|
+
seg.addSegmentView(v);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
153
188
|
const msgStructure = new segment_description_1.SegmentDescription(msgDefinition.name, tags.tagPos[0].tag, msgDefinition, currentTagPosition, structureStack.length, segment_type_1.SegmentType.Msg);
|
|
154
189
|
structureStack.push(msgStructure);
|
|
155
190
|
discover();
|
|
156
191
|
clean();
|
|
192
|
+
fragments();
|
|
157
193
|
return new structure_1.Structure(tags, segments);
|
|
158
194
|
}
|
|
159
195
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ascii-segment-parser.js","sourceRoot":"","sources":["../../../src/buffer/ascii/ascii-segment-parser.ts"],"names":[],"mappings":"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","sourcesContent":["import { SegmentDescription } from '../segment/segment-description'\nimport { Structure } from '../structure'\nimport { Tags } from '../tag/tags'\nimport {\n FixDefinitions,\n MessageDefinition\n} from '../../dictionary/definition'\nimport {\n ContainedComponentField,\n ContainedGroupField,\n ContainedSimpleField,\n ContainedFieldType\n} from '../../dictionary/contained'\nimport { inject, injectable } from 'tsyringe'\nimport { DITokens } from '../../runtime/di-tokens'\nimport { SegmentType } from '../segment/segment-type'\nimport { AsciiParserError } from './ascii-segment-parser-error'\nimport { AsciiSegmentParserSummary } from './ascii-segment-parser-summary'\n\n// this takes linear time i.e. it constantly makes forward progress\n// one tag at a time\n\n@injectable()\nexport class AsciiSegmentParser {\n constructor (@inject(DITokens.Definitions) public readonly definitions: FixDefinitions) {\n }\n\n public parse (msgType: string, tags: Tags, last: number): Structure | null {\n // completed segments in that they are fully parsed\n const segments: SegmentDescription[] = []\n const msgDefinition: MessageDefinition | undefined = this.definitions.message.get(msgType)\n if (!msgDefinition) {\n return null\n }\n // in process of being discovered and may have any amount of depth\n // i.e. a component containing a repeated group of components\n // with sub-groups of components\n const structureStack: SegmentDescription[] = []\n let currentTagPosition: number = 0\n let peek: SegmentDescription\n\n // having finished one segments keep unwinding until tag matches further up stack\n function unwind (tag: number): void {\n while (structureStack.length > 1) {\n const done = structureStack.pop()\n if (!done) continue\n done.end(segments.length, currentTagPosition - 1, tags.tagPos[currentTagPosition - 1].tag)\n segments.push(done)\n peek = structureStack[structureStack.length - 1]\n if (peek.set?.containedTag[tag]) {\n // unwound to point this tag lives in this set.\n break\n }\n if (peek.type === SegmentType.Msg) {\n // this is unknown tag and it is not part of trailer so raise unknown\n break\n }\n }\n }\n\n function summarise (): AsciiSegmentParserSummary {\n return <AsciiSegmentParserSummary>{\n msgType,\n tags: tags.clone().tagPos,\n last,\n msgDefinition: msgDefinition?.toString(),\n currentTagPosition,\n peek: peek.toString(),\n segments: segments.map(s => s.toString()),\n structureStack: structureStack.map(s => s.toString())\n }\n }\n\n function examine (tag: number): SegmentDescription | null {\n let structure: SegmentDescription | null = null\n const currentField = peek.currentField\n if (!currentField) return null\n switch (currentField.type) {\n case ContainedFieldType.Simple: {\n const sf: ContainedSimpleField = currentField as ContainedSimpleField\n if (sf.definition.tag === tag) {\n currentTagPosition = currentTagPosition + 1\n }\n break\n }\n // moving deeper into structure, start a new context\n case ContainedFieldType.Component: {\n const cf: ContainedComponentField = currentField as ContainedComponentField\n structure = new SegmentDescription(cf.name, tag, cf.definition,\n currentTagPosition, structureStack.length, SegmentType.Component)\n break\n }\n // for a group also need to know where all delimiters are positioned\n case ContainedFieldType.Group: {\n const gf: ContainedComponentField = currentField as ContainedGroupField\n structure = new SegmentDescription(gf.name, tag, gf.definition,\n currentTagPosition, structureStack.length, SegmentType.Group)\n currentTagPosition = currentTagPosition + 1\n structure.startGroup(tags.tagPos[currentTagPosition].tag)\n break\n }\n\n default: {\n const c = summarise()\n throw new AsciiParserError(`examine unknown type for tag = ${tag}`, c)\n }\n }\n return structure\n }\n\n function groupDelimiter (tag: number): boolean {\n let delimiter: boolean = false\n if (tag === peek.delimiterTag) {\n peek.addDelimiterPosition(currentTagPosition)\n } else if (structureStack.length > 1) {\n // if a group is represented by a repeated component, then the tag representing delimiter\n // needs to be added further up stack to group itself.\n delimiter = structureStack[structureStack.length - 2].groupAddDelimiter(tag, currentTagPosition)\n }\n return delimiter\n }\n\n function gap (tag: number): void {\n const gap = new SegmentDescription('.undefined', tag, peek.set,\n currentTagPosition, structureStack.length, SegmentType.Gap)\n gap.end(segments.length, currentTagPosition, tag)\n segments.push(gap)\n currentTagPosition++\n }\n\n function discover (): void {\n while (currentTagPosition <= last) {\n const tag: number = tags.tagPos[currentTagPosition].tag\n peek = structureStack[structureStack.length - 1]\n peek.setCurrentField(tag)\n if (!peek.set?.containedTag[tag] || groupDelimiter(tag)) {\n // unravelled all way back to root hence this is not recognised\n const unknown = peek.type === SegmentType.Msg\n if (unknown) {\n gap(tag)\n } else if (structureStack.length > 1) {\n // move back up the segments and save the finished group / component\n unwind(tag)\n }\n continue\n }\n if (!peek.currentField || !peek.set) {\n throw new AsciiParserError(`discover no currentField or set for tag = ${tag}`, summarise())\n }\n const structure = examine(tag)\n if (structure) {\n structureStack.push(structure)\n }\n }\n }\n\n function clean (): void {\n // any remainder components can be closed.\n while (structureStack.length > 0) {\n const done = structureStack.pop()\n if (!done) continue\n done.end(segments.length, currentTagPosition - 1, tags.tagPos[currentTagPosition - 1].tag)\n segments[segments.length] = done\n }\n // logically reverse the trailer and message so trailer is last in list.\n const m1 = segments.length - 1\n const m2 = segments.length - 2\n const tmp = segments[m1]\n segments[m1] = segments[m2]\n segments[m2] = tmp\n }\n\n const msgStructure = new SegmentDescription(msgDefinition.name, tags.tagPos[0].tag, msgDefinition,\n currentTagPosition, structureStack.length, SegmentType.Msg)\n structureStack.push(msgStructure)\n discover()\n clean()\n\n // now know where all components and groups are positioned within message\n return new Structure(tags, segments)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ascii-segment-parser.js","sourceRoot":"","sources":["../../../src/buffer/ascii/ascii-segment-parser.ts"],"names":[],"mappings":"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","sourcesContent":["import { SegmentDescription } from '../segment/segment-description'\nimport { Structure } from '../structure'\nimport { Tags } from '../tag/tags'\nimport {\n FixDefinitions,\n MessageDefinition\n} from '../../dictionary/definition'\nimport {\n ContainedComponentField,\n ContainedGroupField,\n ContainedSimpleField,\n ContainedFieldType\n} from '../../dictionary/contained'\nimport { inject, injectable } from 'tsyringe'\nimport { DITokens } from '../../runtime/di-tokens'\nimport { SegmentType } from '../segment/segment-type'\nimport { AsciiParserError } from './ascii-segment-parser-error'\nimport { AsciiSegmentParserSummary } from './ascii-segment-parser-summary'\nimport { TagIndex } from './tag-index'\n\n// this takes linear time i.e. it constantly makes forward progress\n// one tag at a time\n\n@injectable()\nexport class AsciiSegmentParser {\n constructor (@inject(DITokens.Definitions) public readonly definitions: FixDefinitions) {\n }\n\n public parse (msgType: string, tags: Tags, last: number): Structure | null {\n // completed segments in that they are fully parsed\n const segments: SegmentDescription[] = []\n const msgDefinition: MessageDefinition | undefined = this.definitions.message.get(msgType)\n if (!msgDefinition) {\n return null\n }\n // in process of being discovered and may have any amount of depth\n // i.e. a component containing a repeated group of components\n // with sub-groups of components\n const structureStack: SegmentDescription[] = []\n let currentTagPosition: number = 0\n let peek: SegmentDescription\n\n // track depth-1 components that have been exited for fragmentation detection\n const exitedDepth1Components: Set<string> = new Set()\n const fragmentedComponents: Set<string> = new Set()\n\n // having finished one segments keep unwinding until tag matches further up stack\n function unwind (tag: number): void {\n while (structureStack.length > 1) {\n const done = structureStack.pop()\n if (!done) continue\n done.end(segments.length, currentTagPosition - 1, tags.tagPos[currentTagPosition - 1].tag)\n segments.push(done)\n\n // track when we exit depth-1 components for fragmentation detection\n if (done.depth === 1 && done.name) {\n exitedDepth1Components.add(done.name)\n }\n\n peek = structureStack[structureStack.length - 1]\n if (peek.set?.containedTag[tag]) {\n // unwound to point this tag lives in this set.\n break\n }\n if (peek.type === SegmentType.Msg) {\n // this is unknown tag and it is not part of trailer so raise unknown\n break\n }\n }\n }\n\n function summarise (): AsciiSegmentParserSummary {\n return <AsciiSegmentParserSummary>{\n msgType,\n tags: tags.clone().tagPos,\n last,\n msgDefinition: msgDefinition?.toString(),\n currentTagPosition,\n peek: peek.toString(),\n segments: segments.map(s => s.toString()),\n structureStack: structureStack.map(s => s.toString())\n }\n }\n\n function examine (tag: number): SegmentDescription | null {\n let structure: SegmentDescription | null = null\n const currentField = peek.currentField\n if (!currentField) return null\n switch (currentField.type) {\n case ContainedFieldType.Simple: {\n const sf: ContainedSimpleField = currentField as ContainedSimpleField\n if (sf.definition.tag === tag) {\n currentTagPosition = currentTagPosition + 1\n }\n break\n }\n // moving deeper into structure, start a new context\n case ContainedFieldType.Component: {\n const cf: ContainedComponentField = currentField as ContainedComponentField\n // detect fragmentation: re-entering a depth-1 component we already exited\n if (structureStack.length === 1 && exitedDepth1Components.has(cf.name)) {\n fragmentedComponents.add(cf.name)\n }\n structure = new SegmentDescription(cf.name, tag, cf.definition,\n currentTagPosition, structureStack.length, SegmentType.Component)\n break\n }\n // for a group also need to know where all delimiters are positioned\n case ContainedFieldType.Group: {\n const gf: ContainedComponentField = currentField as ContainedGroupField\n structure = new SegmentDescription(gf.name, tag, gf.definition,\n currentTagPosition, structureStack.length, SegmentType.Group)\n currentTagPosition = currentTagPosition + 1\n structure.startGroup(tags.tagPos[currentTagPosition].tag)\n break\n }\n\n default: {\n const c = summarise()\n throw new AsciiParserError(`examine unknown type for tag = ${tag}`, c)\n }\n }\n return structure\n }\n\n function groupDelimiter (tag: number): boolean {\n let delimiter: boolean = false\n if (tag === peek.delimiterTag) {\n peek.addDelimiterPosition(currentTagPosition)\n } else if (structureStack.length > 1) {\n // if a group is represented by a repeated component, then the tag representing delimiter\n // needs to be added further up stack to group itself.\n delimiter = structureStack[structureStack.length - 2].groupAddDelimiter(tag, currentTagPosition)\n }\n return delimiter\n }\n\n function gap (tag: number): void {\n const gap = new SegmentDescription('.undefined', tag, peek.set,\n currentTagPosition, structureStack.length, SegmentType.Gap)\n gap.end(segments.length, currentTagPosition, tag)\n segments.push(gap)\n currentTagPosition++\n }\n\n function discover (): void {\n while (currentTagPosition <= last) {\n const tag: number = tags.tagPos[currentTagPosition].tag\n peek = structureStack[structureStack.length - 1]\n peek.setCurrentField(tag)\n if (!peek.set?.containedTag[tag] || groupDelimiter(tag)) {\n // unravelled all way back to root hence this is not recognised\n const unknown = peek.type === SegmentType.Msg\n if (unknown) {\n gap(tag)\n } else if (structureStack.length > 1) {\n // move back up the segments and save the finished group / component\n unwind(tag)\n }\n continue\n }\n if (!peek.currentField || !peek.set) {\n throw new AsciiParserError(`discover no currentField or set for tag = ${tag}`, summarise())\n }\n const structure = examine(tag)\n if (structure) {\n structureStack.push(structure)\n }\n }\n }\n\n function clean (): void {\n // any remainder components can be closed.\n while (structureStack.length > 0) {\n const done = structureStack.pop()\n if (!done) continue\n done.end(segments.length, currentTagPosition - 1, tags.tagPos[currentTagPosition - 1].tag)\n segments[segments.length] = done\n }\n // logically reverse the trailer and message so trailer is last in list.\n const m1 = segments.length - 1\n const m2 = segments.length - 2\n const tmp = segments[m1]\n segments[m1] = segments[m2]\n segments[m2] = tmp\n }\n\n function fragments (): void {\n // only build SegmentViews for components detected as fragmented during discover\n // non-fragmented components use their position ranges directly (zero overhead)\n if (fragmentedComponents.size === 0) return\n const ti = new TagIndex(msgDefinition!, tags, last + 1)\n const seen: Set<string> = new Set()\n for (let i = 1; i < segments.length - 1; i++) {\n const seg = segments[i]\n if (seg.depth !== 1) continue\n if (!seg.name) continue\n if (seg.segmentView) continue\n if (seen.has(seg.name)) continue\n if (!fragmentedComponents.has(seg.name)) continue\n if (ti.isComponentGroupWrapper(seg.name)) continue\n const v = ti.getInstance(seg.name)\n if (!v) continue\n seen.add(seg.name)\n seg.addSegmentView(v)\n }\n }\n\n const msgStructure = new SegmentDescription(msgDefinition.name, tags.tagPos[0].tag, msgDefinition,\n currentTagPosition, structureStack.length, SegmentType.Msg)\n structureStack.push(msgStructure)\n discover()\n clean()\n fragments()\n\n // now know where all components and groups are positioned within message\n return new Structure(tags, segments)\n }\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { IContainedSet } from '../../dictionary/contained';
|
|
2
|
+
import { SegmentView } from '../segment/segment-view';
|
|
3
|
+
import { Tags } from '../tag/tags';
|
|
4
|
+
export interface TagSpan {
|
|
5
|
+
start: number;
|
|
6
|
+
end: number;
|
|
7
|
+
}
|
|
8
|
+
export declare class TagIndex {
|
|
9
|
+
readonly set: IContainedSet;
|
|
10
|
+
private readonly sortedTagPos;
|
|
11
|
+
private readonly tagSpans;
|
|
12
|
+
private readonly componentGroupWrappers;
|
|
13
|
+
private readonly cache;
|
|
14
|
+
constructor(set: IContainedSet, tags: Tags, count: number);
|
|
15
|
+
private populateTagSpans;
|
|
16
|
+
private calcGroups;
|
|
17
|
+
private findParentComponent;
|
|
18
|
+
getInstance(name: string): SegmentView | null;
|
|
19
|
+
isComponentGroupWrapper(name: string): boolean;
|
|
20
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TagIndex = void 0;
|
|
4
|
+
const segment_view_1 = require("../segment/segment-view");
|
|
5
|
+
const tag_pos_1 = require("../tag/tag-pos");
|
|
6
|
+
class TagIndex {
|
|
7
|
+
constructor(set, tags, count) {
|
|
8
|
+
this.set = set;
|
|
9
|
+
this.tagSpans = new Map();
|
|
10
|
+
this.componentGroupWrappers = new Set();
|
|
11
|
+
this.cache = new Map();
|
|
12
|
+
this.sortedTagPos = new Array(count);
|
|
13
|
+
for (let i = 0; i < count; i++) {
|
|
14
|
+
this.sortedTagPos[i] = tags.tagPos[i];
|
|
15
|
+
}
|
|
16
|
+
this.sortedTagPos.sort(tag_pos_1.TagPos.compare);
|
|
17
|
+
this.populateTagSpans();
|
|
18
|
+
this.calcGroups(tags, count);
|
|
19
|
+
}
|
|
20
|
+
populateTagSpans() {
|
|
21
|
+
for (let i = 0; i < this.sortedTagPos.length; i++) {
|
|
22
|
+
const t = this.sortedTagPos[i];
|
|
23
|
+
const existing = this.tagSpans.get(t.tag);
|
|
24
|
+
if (existing) {
|
|
25
|
+
existing.end = i;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
this.tagSpans.set(t.tag, { start: i, end: i });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
calcGroups(tags, count) {
|
|
33
|
+
for (let i = 0; i < count; i++) {
|
|
34
|
+
const tag = tags.tagPos[i];
|
|
35
|
+
const field = this.set.tagToField[tag.tag];
|
|
36
|
+
if (!field)
|
|
37
|
+
continue;
|
|
38
|
+
const component = this.findParentComponent(tag.tag);
|
|
39
|
+
if (component) {
|
|
40
|
+
const componentSet = this.set.components.get(component);
|
|
41
|
+
if (componentSet && componentSet.fields.length === 1) {
|
|
42
|
+
this.componentGroupWrappers.add(component);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
findParentComponent(tag) {
|
|
48
|
+
for (const [name, componentSet] of this.set.components) {
|
|
49
|
+
if (componentSet.containedTag[tag]) {
|
|
50
|
+
return name;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
getInstance(name) {
|
|
56
|
+
const cached = this.cache.get(name);
|
|
57
|
+
if (cached !== undefined) {
|
|
58
|
+
return cached;
|
|
59
|
+
}
|
|
60
|
+
const componentSet = this.set.components.get(name);
|
|
61
|
+
if (!componentSet) {
|
|
62
|
+
this.cache.set(name, null);
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
const view = new segment_view_1.SegmentView(name, componentSet);
|
|
66
|
+
const flatTags = componentSet.flattenedTag;
|
|
67
|
+
for (let x = 0; x < flatTags.length; x++) {
|
|
68
|
+
const t = flatTags[x];
|
|
69
|
+
const span = this.tagSpans.get(t);
|
|
70
|
+
if (!span)
|
|
71
|
+
continue;
|
|
72
|
+
if (span.start === span.end) {
|
|
73
|
+
view.add(this.sortedTagPos[span.start]);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
for (let j = span.start; j <= span.end; j++) {
|
|
77
|
+
view.add(this.sortedTagPos[j]);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
this.cache.set(name, view);
|
|
82
|
+
return view;
|
|
83
|
+
}
|
|
84
|
+
isComponentGroupWrapper(name) {
|
|
85
|
+
return this.componentGroupWrappers.has(name);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
exports.TagIndex = TagIndex;
|
|
89
|
+
//# sourceMappingURL=tag-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tag-index.js","sourceRoot":"","sources":["../../../src/buffer/ascii/tag-index.ts"],"names":[],"mappings":"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","sourcesContent":["import { IContainedSet } from '../../dictionary/contained'\nimport { SegmentView } from '../segment/segment-view'\nimport { TagPos } from '../tag/tag-pos'\nimport { Tags } from '../tag/tags'\n\nexport interface TagSpan {\n start: number\n end: number\n}\n\nexport class TagIndex {\n private readonly sortedTagPos: TagPos[]\n private readonly tagSpans: Map<number, TagSpan> = new Map()\n private readonly componentGroupWrappers: Set<string> = new Set()\n private readonly cache: Map<string, SegmentView | null> = new Map()\n\n constructor (\n public readonly set: IContainedSet,\n tags: Tags,\n count: number) {\n this.sortedTagPos = new Array(count)\n for (let i = 0; i < count; i++) {\n this.sortedTagPos[i] = tags.tagPos[i]\n }\n this.sortedTagPos.sort(TagPos.compare)\n this.populateTagSpans()\n this.calcGroups(tags, count)\n }\n\n private populateTagSpans (): void {\n for (let i = 0; i < this.sortedTagPos.length; i++) {\n const t = this.sortedTagPos[i]\n const existing = this.tagSpans.get(t.tag)\n if (existing) {\n existing.end = i\n } else {\n this.tagSpans.set(t.tag, { start: i, end: i })\n }\n }\n }\n\n private calcGroups (tags: Tags, count: number): void {\n for (let i = 0; i < count; i++) {\n const tag = tags.tagPos[i]\n const field = this.set.tagToField[tag.tag]\n if (!field) continue\n // detect component group wrappers (single-field components containing a group)\n const component = this.findParentComponent(tag.tag)\n if (component) {\n const componentSet = this.set.components.get(component)\n if (componentSet && componentSet.fields.length === 1) {\n this.componentGroupWrappers.add(component)\n }\n }\n }\n }\n\n private findParentComponent (tag: number): string | null {\n for (const [name, componentSet] of this.set.components) {\n if (componentSet.containedTag[tag]) {\n return name\n }\n }\n return null\n }\n\n public getInstance (name: string): SegmentView | null {\n const cached = this.cache.get(name)\n if (cached !== undefined) {\n return cached\n }\n\n const componentSet = this.set.components.get(name)\n if (!componentSet) {\n this.cache.set(name, null)\n return null\n }\n\n const view = new SegmentView(name, componentSet)\n const flatTags = componentSet.flattenedTag\n for (let x = 0; x < flatTags.length; x++) {\n const t = flatTags[x]\n const span = this.tagSpans.get(t)\n if (!span) continue\n if (span.start === span.end) {\n view.add(this.sortedTagPos[span.start])\n } else {\n for (let j = span.start; j <= span.end; j++) {\n view.add(this.sortedTagPos[j])\n }\n }\n }\n\n this.cache.set(name, view)\n return view\n }\n\n public isComponentGroupWrapper (name: string): boolean {\n return this.componentGroupWrappers.has(name)\n }\n}\n"]}
|
package/dist/buffer/msg-view.js
CHANGED
|
@@ -234,8 +234,15 @@ class MsgView {
|
|
|
234
234
|
allStrings() {
|
|
235
235
|
const segment = this.segment;
|
|
236
236
|
const range = [];
|
|
237
|
-
|
|
238
|
-
|
|
237
|
+
if (segment.segmentView) {
|
|
238
|
+
for (const tp of segment.segmentView.tags) {
|
|
239
|
+
range[range.length] = tp.position;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
for (let i = segment.startPosition; i <= segment.endPosition; ++i) {
|
|
244
|
+
range[range.length] = i;
|
|
245
|
+
}
|
|
239
246
|
}
|
|
240
247
|
return range.map((i) => this.stringAtPosition(i));
|
|
241
248
|
}
|
|
@@ -334,8 +341,14 @@ class MsgView {
|
|
|
334
341
|
let forwards = this.sortedTagPosForwards;
|
|
335
342
|
if (!forwards) {
|
|
336
343
|
const segment = this.segment;
|
|
337
|
-
|
|
338
|
-
|
|
344
|
+
if (segment.segmentView) {
|
|
345
|
+
forwards = this.sortedTagPosForwards = segment.segmentView.tags.slice();
|
|
346
|
+
forwards.sort(tag_pos_1.TagPos.compare);
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
forwards = this.sortedTagPosForwards = this.structure.tags.tagPos.slice(segment.startPosition, segment.endPosition + 1);
|
|
350
|
+
forwards.sort(tag_pos_1.TagPos.compare);
|
|
351
|
+
}
|
|
339
352
|
this.sortedTagPosBackwards = forwards.slice().reverse();
|
|
340
353
|
}
|
|
341
354
|
return tag_pos_1.TagPos.binarySearch(forwards, tag);
|