jspurefix 5.10.0 → 5.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ATDL_PLAN.md ADDED
@@ -0,0 +1,109 @@
1
+ # FIXatdl renderer and end-to-end test harness
2
+
3
+ Design notes from 2026-08-08. Nothing is built yet. This exists so the next session
4
+ can start from the thinking rather than redo it.
5
+
6
+ **First decision to settle:** this is not jspurefix work and should almost certainly
7
+ live in its own repository (`jspf-atdl`, or similar). The plan is parked here because
8
+ it is where the conversation happened and because the back-end half does use the
9
+ engine. Move it when the code starts.
10
+
11
+ ---
12
+
13
+ ## The problem
14
+
15
+ A broker publishes a FIXatdl document per algorithm: an XML definition of its
16
+ parameters and the order ticket used to fill them in. An EMS — Bloomberg EMSX,
17
+ Flextrade, Portware, Fidessa, ITG Triton — renders the ticket from that document, and
18
+ the values map onto FIX tags, usually the `NoStrategyParameters` group.
19
+
20
+ The observed pain, in more than one shop: getting a definition rendered is cobbled
21
+ together, and **you cannot tell whether a definition is sound until a vendor renders
22
+ it**. The loop is write it, send it, wait, discover it renders differently in EMSX
23
+ than in Triton, repeat. Nothing good and open exists to shorten that.
24
+
25
+ The goal is to close the loop locally, and then go one step further than any renderer
26
+ does — send the resulting order to a back end that answers, so a definition can be
27
+ proved end to end before it goes to clients.
28
+
29
+ ## What it actually is
30
+
31
+ **An interpreter, not an application.** The UI is entirely data driven: you never
32
+ hand-write a component per algorithm, you walk the layout tree. That single
33
+ observation drives every technology choice below.
34
+
35
+ The state is a flat map of control id to value. There is no routing, no server state,
36
+ no virtualised list, no async rendering. Almost nothing a UI framework is good at is
37
+ in play.
38
+
39
+ ## Technology
40
+
41
+ Deliberately small. FIXatdl is *already* a declarative UI description — wrapping it in
42
+ React means writing a compiler from one declarative tree into another and inheriting a
43
+ second lifecycle model, build chain and version churn, for a form with thirty
44
+ controls.
45
+
46
+ - **`DOMParser` for the XML.** ATDL is split across several namespaces — core,
47
+ layout, validation, flow control — and `getElementsByTagNameNS` is exactly the tool
48
+ for that. Built into every browser, zero dependencies.
49
+ - **Plain DOM for rendering.** Walk the layout tree, emit a label and an input per
50
+ control. On any change, re-evaluate every state rule and edit, then toggle
51
+ `disabled` / `hidden`. Tens of rules, not thousands — a full re-evaluation is
52
+ microseconds, so the machinery that exists to avoid one is dead weight.
53
+ - **TypeScript for the model and interpreter**, built to a single ESM module. A build
54
+ step for the author, not for anyone using it.
55
+ - **Static hosting, dynamic page.** One HTML file plus one module, opening from
56
+ `file://` if need be. Drag an ATDL file on, see it render, edit the XML, drop it
57
+ again.
58
+
59
+ Note the distinction that matters: static **hosting**, not static **generation**.
60
+ Generating HTML at build time would need a rebuild per edit, and the tight edit loop
61
+ is the entire product.
62
+
63
+ ## Build order
64
+
65
+ **The expression engine first, headless, with tests.** `StateRule` and `StrategyEdit`
66
+ form a small expression language — operators, logical combinators, references to other
67
+ controls — and that is where the time will go and where correctness actually matters.
68
+ Unit-test it against real broker ATDL documents before drawing a single widget. If
69
+ that engine is right the tool is good; if it is approximate, it is one more
70
+ cobbled-together thing.
71
+
72
+ Controls are the easy half and should come second.
73
+
74
+ ## Pluggable skins
75
+
76
+ Part of the value is answering "roughly how will EMSX lay this out, versus Fidessa" —
77
+ the question people genuinely cannot answer today. Keep layout strategy and CSS behind
78
+ an adapter so the same definition can render two ways side by side. CSS custom
79
+ properties cover it; still no framework.
80
+
81
+ ## The back end
82
+
83
+ A browser cannot open a TCP FIX session, so:
84
+
85
+ ```
86
+ page --(WebSocket, composed order as JSON)--> bridge --(FIX)--> test acceptor
87
+ <--(ExecutionReports)---------------------
88
+ ```
89
+
90
+ The bridge owns a jspurefix initiator; the acceptor validates the strategy parameters
91
+ against the same ATDL definition and answers. This keeps the page genuinely static and
92
+ the FIX where it belongs.
93
+
94
+ A thin WebSocket server is the right shape here — the engine's existing HTTP acceptor
95
+ is FIXML-shaped and is not a good fit for this.
96
+
97
+ ## Rough size
98
+
99
+ ~1,500 lines of interpreter, zero runtime dependencies.
100
+
101
+ ## Open questions for next session
102
+
103
+ 1. Own repository or a folder here? (Recommendation: own repository.)
104
+ 2. Which real ATDL documents can be used as test fixtures? The expression engine is
105
+ only as good as what it is tested against, and broker-published files vary a lot.
106
+ 3. Which FIXatdl version to target. 1.1 is the one in widespread use; confirm before
107
+ writing the parser rather than after.
108
+ 4. How strategy parameters map onto the outbound order — the `NoStrategyParameters`
109
+ group versus direct custom tags — likely needs to be configurable per broker.
package/WORKLOG.md CHANGED
@@ -2,7 +2,40 @@
2
2
 
3
3
  Running notes on what has been done and what is worth doing next, so a session can
4
4
  be resumed cold. The long-form plans live in `BACKPORT_PLAN.md` (engine, cspurefix →
5
- jspurefix) and `DEMO_PORT_PLAN.md` (the jspf-demo reference app).
5
+ jspurefix), `DEMO_PORT_PLAN.md` (the jspf-demo reference app) and `ATDL_PLAN.md` (a
6
+ FIXatdl renderer and test harness — a prospective sibling project, not engine work).
7
+
8
+ ---
9
+
10
+ ## 2026-08-08 — closing the acceptor's listener
11
+
12
+ Reported from jspf-demo: `npm run custom-logon` never exits. The logs say everything
13
+ shut down — sessions logged out, transports harvested, registry empty, `.. done` —
14
+ and the node process sits there.
15
+
16
+ Diagnosed by dumping `process._getActiveHandles()` after the run. Session and
17
+ transport teardown is complete; the only handle left is the listening `net.Server`.
18
+ A listening socket keeps node alive by itself, and nothing ever closed it.
19
+
20
+ `TcpAcceptorListener.stop()` has always existed and does the right thing. What was
21
+ missing was any way to *call* it: `SessionLauncher.stopAcceptor()` was private and
22
+ reached only from the branch where one launcher holds both roles. An application that
23
+ gives its acceptor a launcher of its own — which jspf-demo started doing on 2026-08-01
24
+ so the acceptor would survive any one client — could not close its listener at all.
25
+
26
+ **Changed:** `SessionLauncher.stop()`, public. Remembers a stop that arrives before
27
+ the acceptor has started (`makeSystem` is async, so that window is real) and never
28
+ opens the listener in that case. `run()` resolves once the listener has closed.
29
+ Pinned by `src/test/session/launcher-stop.test.ts`.
30
+
31
+ Worth checking against
32
+ [#72](https://github.com/TimelordUK/jspurefix/issues/72) ("`session-launcher.run()`
33
+ never resolves when connection established") before that issue is next looked at —
34
+ for an acceptor, run() not resolving was the designed behaviour with no way out, and
35
+ now there is one.
36
+
37
+ `docs/acceptor.md` gained a "Closing the listener" section, because from the outside
38
+ this reads as a hang rather than as a decision.
6
39
 
7
40
  ---
8
41
 
@@ -37,6 +70,62 @@ confirmation — see below).
37
70
 
38
71
  ---
39
72
 
73
+ ## 2026-08-08 — customising the Logon, and making silent drops visible, released as 5.10.0
74
+
75
+ Started from [#93](https://github.com/TimelordUK/jspurefix/issues/93), open since
76
+ December 2024, and ended up somewhere broader. Two issues that look unrelated turned
77
+ out to share a theme: **the engine failed quietly in several places, and nobody could
78
+ see it.**
79
+
80
+ **Merged (PR #164):**
81
+
82
+ - a `Logon` block in the session description, merged over the fields the engine
83
+ derives. `AsciiSessionMsgFactory.logon()` had read exactly five values off the
84
+ description and ignored everything else, so an `"Account"` in the config was never
85
+ looked at. A null in the block suppresses a field.
86
+ - `AsciiEncoder` now reports a key it cannot resolve, via
87
+ `MsgEncoder.onUnknownField`. This was the real cause of the confusion: `Account` is
88
+ tag 1 but is not a field of FIX 4.4 `Logon`, so it was discarded with no error and
89
+ no log line. Only runs on the miss path — a key that resolves costs nothing.
90
+ - `SessionContainer(provider)` and `SessionLauncher.makeSessionMsgFactory()`, so
91
+ supplying a factory no longer needs a `SessionContainer` subclass
92
+ - `AsciiSessionMsgFactory` / `FixmlSessionMsgFactory` exported from the package root.
93
+ Extending the ascii factory — the thing every previous answer to these issues told
94
+ people to do — used to require importing through `dist/transport/ascii/...`.
95
+ - an optional callback on `send`, giving back the stamped `StandardHeader` (and so
96
+ the sequence number) or an error. Writing it exposed three drops that told nobody:
97
+ `encodeMessage` returning null for an unknown msgType, a session in state
98
+ `Stopped`, and no transport at all. All three still reach the error channel; the
99
+ callback is additive.
100
+
101
+ **Issues resolved:** #93, #86, #39, #96 (all commented with the full explanation).
102
+ #69 verified fixed — `SenderSubID` has been in the header since `dd9a540` — and left
103
+ open pending the reporter, since their actual complaint was "no response from the
104
+ server".
105
+
106
+ **Docs:** `docs/custom-logon.md`, plus README sections on the `Logon` block and on
107
+ send callbacks.
108
+
109
+ **Perf:** flat. Parse benchmarks moved inside the noise floor; Logon encode 2.26
110
+ µs/msg against the 2.3 in the README table. The encoder change is genuinely off the
111
+ hot path.
112
+
113
+ **Demos:** `jspf-demo` gained a `custom-logon` mode (config block + generated
114
+ dictionary + run-time factory, and one field deliberately left undeclared so the new
115
+ warning is visible). `jspf-md-demo`'s `Msg44Fact` had a `logon()` that was a verbatim
116
+ copy of the stock one — it showed the hook and never the point — and now sends a real
117
+ bespoke tag; its generated types were regenerated in a separate commit, having
118
+ drifted a long way behind the generator.
119
+
120
+ **Found along the way:** jspf-demo had been sending every `TradeCaptureReportRequest`
121
+ without its dates for as long as the demo has existed. `TrdCapDtGrp` was built as a
122
+ flat array, which is how the FIX repository models it, but those sessions load the
123
+ QuickFIX dictionary where it is a component wrapping `NoDates`. The encoder dropped
124
+ it silently. The new warning is what caught it, about an hour after it existed — a
125
+ fair advertisement for the feature.
126
+
127
+ ---
128
+
40
129
  ## Next up
41
130
 
42
131
  ### 1. Toolchain major bumps
@@ -49,10 +138,9 @@ acceptor work:
49
138
  - `eslint-config-love` 151 → 154
50
139
  - `@types/node` 25 → 26
51
140
 
52
- Expect lint churn. There are 17 pre-existing lint errors on master (`fix-clock`,
53
- `resend-request-manager`, `session-sequence-*`, `tcp-initiator`) — worth clearing in
54
- the same pass, since they make it hard to tell new problems from old. `npm audit` is
55
- currently clean.
141
+ Expect lint churn. The 17 pre-existing lint errors noted here were cleared in
142
+ `1d2f267` — `npx eslint src/` is clean on master as of 5.10.0, so any churn from the
143
+ bump is genuinely new and worth reading. `npm audit` is clean.
56
144
 
57
145
  ### 2. #77 — confirm or close
58
146
 
@@ -76,14 +164,21 @@ reproduced.
76
164
 
77
165
  ### 3. Other open issues worth a look
78
166
 
79
- Several are old and may already be fixed, in the way #140 turned out to be — worth
80
- verifying against 5.9.1 rather than reading the thread:
81
-
82
- - #96 / #69 — `DefaultApplVerID` / `SenderSubID` missing during login
83
- - #93 — customising the Logon message
84
- - #86 — make `encoderStream` protected
85
- - #85 — `ASessionMsgFactory` types patch (may be superseded by `cloneFor`)
86
- - #72 — `session-launcher.run()` never resolves when connection established
167
+ Most of this list was cleared in 5.10.0. What remains:
168
+
169
+ - **#69** — verified fixed and commented; awaiting the reporter before closing. Their
170
+ literal question (`SenderSubID`) works; their real complaint was silence from
171
+ cServer, which is a different problem.
172
+ - **#85** — `ASessionMsgFactory` types patch. Possibly superseded twice over now, by
173
+ `cloneFor` and by the root exports added in 5.10.0. Worth reading the patch against
174
+ current master before doing anything.
175
+ - **#72** — `session-launcher.run()` never resolves when the connection is
176
+ established. Not looked at. Given how #140 and #77 went, verify against 5.10.0
177
+ before trusting the thread.
178
+
179
+ The lesson from this batch, worth repeating: **several of these were already fixed,
180
+ or were one line plus a docs gap.** Reading the thread is a poor substitute for
181
+ running the reporter's config.
87
182
 
88
183
  ### 4. Small things noticed, none urgent
89
184
 
@@ -14,9 +14,11 @@ export declare abstract class SessionLauncher {
14
14
  protected makeSessionMsgFactory(_description: ISessionDescription): ISessionMsgFactory | null;
15
15
  private empty;
16
16
  private acceptorEntity;
17
+ private stopRequested;
17
18
  protected getAcceptor(sessionContainer: DependencyContainer): Promise<any>;
18
19
  protected getInitiator(sessionContainer: DependencyContainer): Promise<any>;
19
20
  protected makeFactory(config: IJsFixConfig): EngineFactory | null;
21
+ stop(): void;
20
22
  run(): Promise<boolean>;
21
23
  exec(): void;
22
24
  isAscii(description: ISessionDescription): boolean;
@@ -54,6 +54,7 @@ class SessionLauncher {
54
54
  this.root = '../../';
55
55
  this.sessionContainer = new session_container_1.SessionContainer((d) => this.makeSessionMsgFactory(d));
56
56
  this.acceptorEntity = null;
57
+ this.stopRequested = false;
57
58
  this.logger = this.loggerFactory.logger('launcher');
58
59
  this.initiatorConfig = initiatorConfig ? this.loadConfig(initiatorConfig) : null;
59
60
  this.acceptorConfig = acceptorConfig ? this.loadConfig(acceptorConfig) : null;
@@ -81,6 +82,10 @@ class SessionLauncher {
81
82
  if (sessionContainer.isRegistered(di_tokens_1.DITokens.FixEntity)) {
82
83
  const entity = sessionContainer.resolve(di_tokens_1.DITokens.FixEntity);
83
84
  this.acceptorEntity = entity;
85
+ if (this.stopRequested) {
86
+ this.logger.info('stop requested before the acceptor started - not listening');
87
+ return this.empty();
88
+ }
84
89
  return entity.start();
85
90
  }
86
91
  else {
@@ -102,6 +107,10 @@ class SessionLauncher {
102
107
  makeFactory(config) {
103
108
  return null;
104
109
  }
110
+ stop() {
111
+ this.stopRequested = true;
112
+ this.stopAcceptor();
113
+ }
105
114
  run() {
106
115
  return __awaiter(this, void 0, void 0, function* () {
107
116
  return yield new Promise((resolve, reject) => {
@@ -195,7 +204,7 @@ class SessionLauncher {
195
204
  if (this.acceptorConfig && this.initiatorConfig) {
196
205
  yield client;
197
206
  this.logger.info('client finished, stopping acceptor');
198
- this.stopAcceptor();
207
+ this.stop();
199
208
  return true;
200
209
  }
201
210
  return yield Promise.all([server, client]);
@@ -1 +1 @@
1
- {"version":3,"file":"session-launcher.js","sourceRoot":"","sources":["../../src/runtime/session-launcher.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path'\nimport { IJsFixConfig, IJsFixLogger, JsFixLoggerFactory, JsFixWinstonLogFactory, WinstonLogger } from '../config'\nimport { FixEntity, FixSession, ISessionDescription, ISessionMsgFactory } from '../transport'\nimport { DependencyContainer } from 'tsyringe'\nimport { EngineFactory } from './engine-factory'\nimport { SessionContainer } from './session-container'\nimport { DITokens } from './di-tokens'\n\nconst defaultLoggerFactory = new JsFixWinstonLogFactory(WinstonLogger.consoleOptions('info'))\n\nexport abstract class SessionLauncher {\n public root: string = '../../'\n protected readonly logger: IJsFixLogger\n public readonly initiatorConfig: ISessionDescription | null\n public readonly acceptorConfig: ISessionDescription | null\n\n protected constructor (\n initiatorConfig: string | ISessionDescription | null = null,\n acceptorConfig: string | ISessionDescription | null = null,\n private readonly loggerFactory: JsFixLoggerFactory = defaultLoggerFactory\n ) {\n this.logger = this.loggerFactory.logger('launcher')\n this.initiatorConfig = initiatorConfig ? this.loadConfig(initiatorConfig) : null\n this.acceptorConfig = acceptorConfig ? this.loadConfig(acceptorConfig) : null\n }\n\n // the arrow defers to makeSessionMsgFactory at resolve time, so a subclass need\n // only override that one method - no SessionContainer subclass required\n protected sessionContainer: SessionContainer =\n new SessionContainer((d: ISessionDescription) => this.makeSessionMsgFactory(d))\n\n /**\n * Override to supply this application's own session message factory - the place to\n * put a Logon carrying tags the standard message does not, or a header a\n * counterparty stamps differently. Return null (the default) for the stock\n * factory.\n *\n * For a Logon that only needs extra fields, prefer the \"Logon\" block in the session\n * description - it needs no code at all. Reach for a factory when the values are\n * computed at run time (a signature, a nonce, a token fetched at start up).\n */\n protected makeSessionMsgFactory (_description: ISessionDescription): ISessionMsgFactory | null {\n return null\n }\n\n private async empty (): Promise<any> {\n return await new Promise((resolve, reject) => {\n try {\n setImmediate(() => {\n this.logger.info('resolving an empty promise')\n resolve(null)\n })\n } catch (e) {\n reject(e)\n }\n })\n }\n\n private acceptorEntity: FixEntity | null = null\n\n protected async getAcceptor (sessionContainer: DependencyContainer): Promise<any> {\n if (sessionContainer.isRegistered<FixEntity>(DITokens.FixEntity)) {\n const entity = sessionContainer.resolve<FixEntity>(DITokens.FixEntity)\n this.acceptorEntity = entity\n return entity.start()\n } else {\n return this.empty()\n }\n }\n\n protected async getInitiator (sessionContainer: DependencyContainer): Promise<any> {\n if (sessionContainer.isRegistered<FixEntity>(DITokens.FixEntity)) {\n const entity = sessionContainer.resolve<FixEntity>(DITokens.FixEntity)\n return entity.start()\n } else {\n return this.empty()\n }\n }\n\n /**\n * provide a factory which will be invoked with a config where an instance of the application\n * should be constructed and returned.\n * @param config to be provided to the constucted application representing this session\n * @protected\n */\n protected makeFactory (config: IJsFixConfig): EngineFactory | null {\n return null\n }\n\n public async run (): Promise<boolean> {\n return await new Promise<any>((resolve, reject) => {\n const logger = this.logger\n logger.info('launching ..')\n this.setup().then(() => {\n logger.info('.. done')\n resolve(true)\n }).catch((e: Error) => {\n logger.error(e)\n reject(e)\n })\n })\n }\n\n public exec (): void {\n this.run().then(() => {\n console.log('finished.')\n }).catch(e => {\n console.error(e)\n })\n }\n\n /**\n * is this session config representing an ascii based session\n * @param description config to be tested.\n */\n public isAscii (description: ISessionDescription): boolean {\n return this.sessionContainer.isAscii(description)\n }\n\n /**\n * is this session config representing an initiator based session\n * @param description config to be tested.\n */\n public isInitiator (description: ISessionDescription): boolean {\n return this.sessionContainer.isInitiator(description)\n }\n\n protected registerApplication (_: DependencyContainer): void {\n this.logger.info('bypass register via DI')\n }\n\n private async makeSystem (description: ISessionDescription): Promise<DependencyContainer> {\n const name = description.application?.name ?? 'na'\n const protocol = description.application?.protocol ?? 'ascii'\n this.logger.info(`creating app ${name} [protocol ${protocol}]`)\n return await this.sessionContainer.makeSystem(description)\n }\n\n private register (container: DependencyContainer): void {\n const config = container.resolve<IJsFixConfig>(DITokens.IJsFixConfig)\n const factory = this.makeFactory(config)\n if (!factory) {\n this.registerApplication(container)\n } else {\n if (factory.makeSession) {\n // resolve the config from the container doing the resolving, not the one\n // captured here. An acceptor resolves each session from a per-connection\n // child scope which re-registers IJsFixConfig, so this is how an application\n // receives that session's own description, store and message factory.\n container.register<FixSession>(DITokens.FixSession, {\n useFactory: (c) => factory.makeSession(c.resolve<IJsFixConfig>(DITokens.IJsFixConfig))\n })\n }\n }\n }\n\n private async makeClient (): Promise<any> {\n if (!this.initiatorConfig) return\n const sessionContainer = await this.makeSystem(this.initiatorConfig)\n this.register(sessionContainer)\n this.logger.info('create initiator')\n return await this.getInitiator(sessionContainer)\n }\n\n private async makeServer (): Promise<any> {\n if (!this.acceptorConfig) return\n const sessionContainer = await this.makeSystem(this.acceptorConfig)\n this.register(sessionContainer)\n this.logger.info('create acceptor')\n return await this.getAcceptor(sessionContainer)\n }\n\n async serverOrEmpty (): Promise<any> {\n return this.acceptorConfig ? this.makeServer() : this.empty()\n }\n\n async clientOrEmpty (): Promise<any> {\n return this.initiatorConfig ? this.makeClient() : this.empty()\n }\n\n private async setup (): Promise<any> {\n this.sessionContainer.registerGlobal(this.loggerFactory)\n const server = this.serverOrEmpty()\n const client = this.clientOrEmpty()\n this.logger.info('launching ....')\n if (this.acceptorConfig && this.initiatorConfig) {\n // Both mode: client drives the lifecycle. When client finishes,\n // stop the acceptor so the process can exit cleanly.\n await client\n this.logger.info('client finished, stopping acceptor')\n this.stopAcceptor()\n return true\n }\n return await Promise.all([server, client])\n }\n\n private stopAcceptor (): void {\n const entity = this.acceptorEntity as any\n if (entity && typeof entity.stop === 'function') {\n entity.stop()\n }\n }\n\n private loadConfig (config: string | ISessionDescription): ISessionDescription {\n if (typeof config === 'string') {\n return require(path.join(this.root, config))\n }\n return config\n }\n}\n"]}
1
+ {"version":3,"file":"session-launcher.js","sourceRoot":"","sources":["../../src/runtime/session-launcher.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path'\nimport { IJsFixConfig, IJsFixLogger, JsFixLoggerFactory, JsFixWinstonLogFactory, WinstonLogger } from '../config'\nimport { FixEntity, FixSession, ISessionDescription, ISessionMsgFactory } from '../transport'\nimport { DependencyContainer } from 'tsyringe'\nimport { EngineFactory } from './engine-factory'\nimport { SessionContainer } from './session-container'\nimport { DITokens } from './di-tokens'\n\nconst defaultLoggerFactory = new JsFixWinstonLogFactory(WinstonLogger.consoleOptions('info'))\n\nexport abstract class SessionLauncher {\n public root: string = '../../'\n protected readonly logger: IJsFixLogger\n public readonly initiatorConfig: ISessionDescription | null\n public readonly acceptorConfig: ISessionDescription | null\n\n protected constructor (\n initiatorConfig: string | ISessionDescription | null = null,\n acceptorConfig: string | ISessionDescription | null = null,\n private readonly loggerFactory: JsFixLoggerFactory = defaultLoggerFactory\n ) {\n this.logger = this.loggerFactory.logger('launcher')\n this.initiatorConfig = initiatorConfig ? this.loadConfig(initiatorConfig) : null\n this.acceptorConfig = acceptorConfig ? this.loadConfig(acceptorConfig) : null\n }\n\n // the arrow defers to makeSessionMsgFactory at resolve time, so a subclass need\n // only override that one method - no SessionContainer subclass required\n protected sessionContainer: SessionContainer =\n new SessionContainer((d: ISessionDescription) => this.makeSessionMsgFactory(d))\n\n /**\n * Override to supply this application's own session message factory - the place to\n * put a Logon carrying tags the standard message does not, or a header a\n * counterparty stamps differently. Return null (the default) for the stock\n * factory.\n *\n * For a Logon that only needs extra fields, prefer the \"Logon\" block in the session\n * description - it needs no code at all. Reach for a factory when the values are\n * computed at run time (a signature, a nonce, a token fetched at start up).\n */\n protected makeSessionMsgFactory (_description: ISessionDescription): ISessionMsgFactory | null {\n return null\n }\n\n private async empty (): Promise<any> {\n return await new Promise((resolve, reject) => {\n try {\n setImmediate(() => {\n this.logger.info('resolving an empty promise')\n resolve(null)\n })\n } catch (e) {\n reject(e)\n }\n })\n }\n\n private acceptorEntity: FixEntity | null = null\n private stopRequested: boolean = false\n\n protected async getAcceptor (sessionContainer: DependencyContainer): Promise<any> {\n if (sessionContainer.isRegistered<FixEntity>(DITokens.FixEntity)) {\n const entity = sessionContainer.resolve<FixEntity>(DITokens.FixEntity)\n this.acceptorEntity = entity\n // a stop can arrive while the container is still being built - the listener has\n // no existence yet to close, so honour the request by never opening it\n if (this.stopRequested) {\n this.logger.info('stop requested before the acceptor started - not listening')\n return this.empty()\n }\n return entity.start()\n } else {\n return this.empty()\n }\n }\n\n protected async getInitiator (sessionContainer: DependencyContainer): Promise<any> {\n if (sessionContainer.isRegistered<FixEntity>(DITokens.FixEntity)) {\n const entity = sessionContainer.resolve<FixEntity>(DITokens.FixEntity)\n return entity.start()\n } else {\n return this.empty()\n }\n }\n\n /**\n * provide a factory which will be invoked with a config where an instance of the application\n * should be constructed and returned.\n * @param config to be provided to the constucted application representing this session\n * @protected\n */\n protected makeFactory (config: IJsFixConfig): EngineFactory | null {\n return null\n }\n\n /**\n * Stop what this launcher started - in practice, close the acceptor's listener.\n *\n * An acceptor is meant to outlive any one counterparty, so nothing closes its\n * listener on its own. That is right for a venue and wrong for an application\n * which knows it is finished: the listening socket alone keeps node alive, long\n * after every session has logged out and every transport has been harvested.\n *\n * Until now the only caller was the both-roles branch of setup() below, which an\n * application cannot reach once it gives the acceptor a launcher of its own - the\n * usual arrangement as soon as more than one client is involved. There was then\n * no way to shut a listener down short of killing the process.\n *\n * Safe before start (the request is remembered and the listener never opens), and\n * safe to call twice. run() resolves once the listener has closed.\n */\n public stop (): void {\n this.stopRequested = true\n this.stopAcceptor()\n }\n\n public async run (): Promise<boolean> {\n return await new Promise<any>((resolve, reject) => {\n const logger = this.logger\n logger.info('launching ..')\n this.setup().then(() => {\n logger.info('.. done')\n resolve(true)\n }).catch((e: Error) => {\n logger.error(e)\n reject(e)\n })\n })\n }\n\n public exec (): void {\n this.run().then(() => {\n console.log('finished.')\n }).catch(e => {\n console.error(e)\n })\n }\n\n /**\n * is this session config representing an ascii based session\n * @param description config to be tested.\n */\n public isAscii (description: ISessionDescription): boolean {\n return this.sessionContainer.isAscii(description)\n }\n\n /**\n * is this session config representing an initiator based session\n * @param description config to be tested.\n */\n public isInitiator (description: ISessionDescription): boolean {\n return this.sessionContainer.isInitiator(description)\n }\n\n protected registerApplication (_: DependencyContainer): void {\n this.logger.info('bypass register via DI')\n }\n\n private async makeSystem (description: ISessionDescription): Promise<DependencyContainer> {\n const name = description.application?.name ?? 'na'\n const protocol = description.application?.protocol ?? 'ascii'\n this.logger.info(`creating app ${name} [protocol ${protocol}]`)\n return await this.sessionContainer.makeSystem(description)\n }\n\n private register (container: DependencyContainer): void {\n const config = container.resolve<IJsFixConfig>(DITokens.IJsFixConfig)\n const factory = this.makeFactory(config)\n if (!factory) {\n this.registerApplication(container)\n } else {\n if (factory.makeSession) {\n // resolve the config from the container doing the resolving, not the one\n // captured here. An acceptor resolves each session from a per-connection\n // child scope which re-registers IJsFixConfig, so this is how an application\n // receives that session's own description, store and message factory.\n container.register<FixSession>(DITokens.FixSession, {\n useFactory: (c) => factory.makeSession(c.resolve<IJsFixConfig>(DITokens.IJsFixConfig))\n })\n }\n }\n }\n\n private async makeClient (): Promise<any> {\n if (!this.initiatorConfig) return\n const sessionContainer = await this.makeSystem(this.initiatorConfig)\n this.register(sessionContainer)\n this.logger.info('create initiator')\n return await this.getInitiator(sessionContainer)\n }\n\n private async makeServer (): Promise<any> {\n if (!this.acceptorConfig) return\n const sessionContainer = await this.makeSystem(this.acceptorConfig)\n this.register(sessionContainer)\n this.logger.info('create acceptor')\n return await this.getAcceptor(sessionContainer)\n }\n\n async serverOrEmpty (): Promise<any> {\n return this.acceptorConfig ? this.makeServer() : this.empty()\n }\n\n async clientOrEmpty (): Promise<any> {\n return this.initiatorConfig ? this.makeClient() : this.empty()\n }\n\n private async setup (): Promise<any> {\n this.sessionContainer.registerGlobal(this.loggerFactory)\n const server = this.serverOrEmpty()\n const client = this.clientOrEmpty()\n this.logger.info('launching ....')\n if (this.acceptorConfig && this.initiatorConfig) {\n // Both mode: client drives the lifecycle. When client finishes,\n // stop the acceptor so the process can exit cleanly.\n await client\n this.logger.info('client finished, stopping acceptor')\n this.stop()\n return true\n }\n return await Promise.all([server, client])\n }\n\n private stopAcceptor (): void {\n const entity = this.acceptorEntity as any\n if (entity && typeof entity.stop === 'function') {\n entity.stop()\n }\n }\n\n private loadConfig (config: string | ISessionDescription): ISessionDescription {\n if (typeof config === 'string') {\n return require(path.join(this.root, config))\n }\n return config\n }\n}\n"]}
package/docs/acceptor.md CHANGED
@@ -199,6 +199,38 @@ asks for a resend, the stored bodies are replayed with `PossDupFlag=Y`.
199
199
  Because the store is keyed on `SessionId`, a wildcard acceptor recovers each
200
200
  counterparty independently.
201
201
 
202
+ ## Closing the listener
203
+
204
+ An acceptor outlives its counterparties. A session ending closes nothing: the
205
+ listener stays up, waiting for the next connection, which is the whole point of a
206
+ venue and why `TcpAcceptorListener` never stops itself.
207
+
208
+ The consequence is worth stating plainly, because it does not look like a decision
209
+ from the outside — **a listening socket keeps node alive on its own**. Every session
210
+ can log out, every transport can be harvested, the census can read `transports=0
211
+ sessions=0`, and the process still will not exit. Nothing is stuck; there is simply
212
+ one handle left, and only the application knows when it no longer wants it.
213
+
214
+ `SessionLauncher.stop()` closes it:
215
+
216
+ ```ts
217
+ const venue = new MyLauncher(null, acceptorDescription)
218
+ const run = venue.run() // resolves when the listener closes, not before
219
+
220
+ process.once('SIGINT', () => { venue.stop() })
221
+ await run // ... and now the process can end
222
+ ```
223
+
224
+ `stop()` releases any live transports rather than waiting on them — `net.Server.close`
225
+ alone would block on whichever counterparty happened to be connected, and forever on
226
+ a half-open one. It is safe before the listener has opened (the request is remembered
227
+ and it never opens) and safe to call twice.
228
+
229
+ A launcher given *both* roles calls this for you when its client finishes, which is
230
+ why a single-launcher sample exits by itself. An application that gives its acceptor a
231
+ launcher of its own — the usual arrangement once more than one client is involved —
232
+ owns that decision, and must say when.
233
+
202
234
  ## See also
203
235
 
204
236
  - [`src/transport/tcp/tcp-acceptor.ts`](../src/transport/tcp/tcp-acceptor.ts) — listener, transport lifecycle
@@ -1,62 +1,62 @@
1
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-13:59:37.235|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=111|
2
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-13:59:37.240|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=155|
3
- 8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-13:59:37.241|7=1|16=1|10=243|
4
- 8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=20260808-13:59:37.245|122=20260808-13:59:37.245|123=Y|36=2|10=125|
5
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-13:59:39.246|58=5|10=226|
6
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-13:59:42.038|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=108|
7
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-13:59:42.040|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=149|
8
- 8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-13:59:42.041|7=1|16=0|10=236|
9
- 8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=20260808-13:59:42.043|122=20260808-13:59:42.043|123=Y|36=2|10=109|
10
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-13:59:44.043|58=5|10=217|
11
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-13:59:46.524|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=112|
12
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-13:59:46.526|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=162|
13
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-13:59:46.526|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=115|
14
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-13:59:47.064|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=112|
15
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-13:59:47.066|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=162|
16
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-13:59:48.068|58=5|10=228|
17
- 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260808-13:59:48.527|58=5|10=229|
18
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-13:59:50.829|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=115|
19
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-13:59:50.830|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=155|
20
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-13:59:51.832|58=5|10=221|
21
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-13:59:54.326|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=111|
22
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-13:59:54.327|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=160|
23
- 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=0|57=fix|52=20260808-13:59:55.328|58=5|10=223|
24
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-13:59:57.802|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=113|
25
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=5|57=fix|52=20260808-13:59:57.803|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=166|
26
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-13:59:58.805|58=5|10=228|
27
- 8=FIX.4.4|9=0000110|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-14:00:01.434|98=0|141=Y|553=js-client|554=pwd-client|10=158|
28
- 8=FIX.4.4|9=0000125|35=3|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-14:00:01.435|45=1|372=A|373=1|58=msgType A missing required tag 108|10=248|
29
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-14:00:06.211|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=088|
30
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-14:00:06.213|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=138|
31
- 8=FIX.4.4|9=0000111|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-14:00:06.214|45=2|372=ZZ|373=11|58=msgType ZZ unknown|10=140|
32
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260808-14:00:08.215|58=5|10=206|
33
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-14:00:10.703|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=089|
34
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-14:00:10.704|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=138|
35
- 8=FIX.4.4|9=0000146|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-14:00:10.705|45=2|372=0|373=5|58=msgType 0 checksum failed. received = 95 computed = 143|10=061|
36
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260808-14:00:12.705|58=5|10=205|
37
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-14:00:14.705|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=095|
38
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-14:00:19.323|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=096|
39
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-14:00:19.324|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=145|
40
- 8=FIX.4.4|9=0000145|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-14:00:19.326|45=2|372=0|373=5|58=msgType 0 checksum failed. received = 95 computed = 59|10=026|
41
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260808-14:00:21.326|58=5|10=204|
42
- 8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-14:00:24.088|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=050|
43
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-14:00:24.089|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=149|
44
- 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-14:00:26.095|112=Sat, 08 Aug 2026 14:00:26 GMT|10=133|
45
- 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260808-14:00:28.099|112=Sat, 08 Aug 2026 14:00:28 GMT|10=142|
46
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-14:00:30.091|58=5|10=202|
47
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-14:00:32.593|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=100|
48
- 8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-14:00:32.594|98=0|108=2|141=Y|553=js-server|554=pwd-server|10=099|
49
- 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-14:00:34.598|112=Sat, 08 Aug 2026 14:00:34 GMT|10=139|
50
- 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=3|57=fix|52=20260808-14:00:36.602|112=Sat, 08 Aug 2026 14:00:36 GMT|10=130|
51
- 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=4|57=fix|52=20260808-14:00:38.606|112=Sat, 08 Aug 2026 14:00:38 GMT|10=139|
52
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=5|57=fix|52=20260808-14:00:40.596|58=5|10=216|
53
- 8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-14:00:43.075|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=047|
54
- 8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-14:00:43.077|98=0|108=5|141=Y|553=js-server|554=pwd-server|10=100|
55
- 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-14:00:45.081|112=Sat, 08 Aug 2026 14:00:45 GMT|10=130|
56
- 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260808-14:00:47.086|112=Sat, 08 Aug 2026 14:00:47 GMT|10=140|
57
- 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-14:00:48.087|112=Sat, 08 Aug 2026 14:00:48 GMT|10=142|
58
- 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=4|57=fix|52=20260808-14:00:49.089|112=Sat, 08 Aug 2026 14:00:49 GMT|10=148|
59
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260808-14:00:51.078|58=5|10=211|
60
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-14:03:01.489|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=103|
61
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-14:03:01.499|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=152|
62
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-14:03:02.504|58=5|10=203|
1
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:10.991|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=101|
2
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:10.996|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=154|
3
+ 8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-21:05:10.997|7=1|16=1|10=242|
4
+ 8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=20260808-21:05:11.002|122=20260808-21:05:11.001|123=Y|36=2|10=070|
5
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:05:13.000|58=5|10=196|
6
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:15.791|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=104|
7
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:15.793|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=154|
8
+ 8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-21:05:15.793|7=1|16=0|10=240|
9
+ 8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=20260808-21:05:15.796|122=20260808-21:05:15.795|123=Y|36=2|10=118|
10
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:05:17.795|58=5|10=221|
11
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:20.270|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=092|
12
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:20.272|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=142|
13
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-21:05:20.272|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=095|
14
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:20.802|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=093|
15
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:20.804|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=143|
16
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:05:21.806|58=5|10=209|
17
+ 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260808-21:05:22.274|58=5|10=210|
18
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:24.510|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=093|
19
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:24.511|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=142|
20
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:05:25.513|58=5|10=208|
21
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:28.267|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=106|
22
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:28.268|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=155|
23
+ 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=0|57=fix|52=20260808-21:05:29.269|58=5|10=218|
24
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:31.756|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=103|
25
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=5|57=fix|52=20260808-21:05:31.758|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=157|
26
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:05:32.760|58=5|10=210|
27
+ 8=FIX.4.4|9=0000110|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:35.230|98=0|141=Y|553=js-client|554=pwd-client|10=162|
28
+ 8=FIX.4.4|9=0000125|35=3|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:35.232|45=1|372=A|373=1|58=msgType A missing required tag 108|10=253|
29
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:39.805|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=106|
30
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:39.807|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=156|
31
+ 8=FIX.4.4|9=0000111|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:05:39.809|45=2|372=ZZ|373=11|58=msgType ZZ unknown|10=159|
32
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260808-21:05:41.809|58=5|10=215|
33
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:44.541|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=099|
34
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:44.542|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=148|
35
+ 8=FIX.4.4|9=0000146|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:05:44.544|45=2|372=0|373=5|58=msgType 0 checksum failed. received = 95 computed = 143|10=072|
36
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260808-21:05:46.545|58=5|10=217|
37
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:48.542|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=104|
38
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:53.038|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=100|
39
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:53.040|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=141|
40
+ 8=FIX.4.4|9=0000145|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:05:53.041|45=2|372=0|373=5|58=msgType 0 checksum failed. received = 95 computed = 59|10=021|
41
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260808-21:05:55.041|58=5|10=208|
42
+ 8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:57.513|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=052|
43
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:57.514|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=151|
44
+ 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-21:05:59.518|112=Sat, 08 Aug 2026 21:05:59 GMT|10=151|
45
+ 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260808-21:06:01.522|112=Sat, 08 Aug 2026 21:06:01 GMT|10=123|
46
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:06:03.517|58=5|10=209|
47
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:06:05.983|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=107|
48
+ 8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:06:05.984|98=0|108=2|141=Y|553=js-server|554=pwd-server|10=106|
49
+ 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:06:07.988|112=Sat, 08 Aug 2026 21:06:07 GMT|10=150|
50
+ 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=3|57=fix|52=20260808-21:06:09.990|112=Sat, 08 Aug 2026 21:06:09 GMT|10=148|
51
+ 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=4|57=fix|52=20260808-21:06:11.994|112=Sat, 08 Aug 2026 21:06:11 GMT|10=139|
52
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=5|57=fix|52=20260808-21:06:13.987|58=5|10=224|
53
+ 8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:06:16.697|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=061|
54
+ 8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:06:16.698|98=0|108=5|141=Y|553=js-server|554=pwd-server|10=113|
55
+ 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-21:06:18.703|112=Sat, 08 Aug 2026 21:06:18 GMT|10=139|
56
+ 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260808-21:06:20.707|112=Sat, 08 Aug 2026 21:06:20 GMT|10=130|
57
+ 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:06:21.711|112=Sat, 08 Aug 2026 21:06:21 GMT|10=126|
58
+ 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=4|57=fix|52=20260808-21:06:22.713|112=Sat, 08 Aug 2026 21:06:22 GMT|10=132|
59
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260808-21:06:24.700|58=5|10=207|
60
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:08:32.932|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=103|
61
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:08:32.941|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=151|
62
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:08:33.946|58=5|10=220|
@@ -1,75 +1,75 @@
1
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-13:59:37.235|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=111|
2
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-13:59:37.240|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=155|
3
- 8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-13:59:37.241|7=1|16=1|10=243|
4
- 8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=20260808-13:59:37.245|122=20260808-13:59:37.245|123=Y|36=2|10=125|
5
- 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260808-13:59:39.244|58=5|10=225|
6
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-13:59:39.246|58=5|10=226|
7
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-13:59:42.038|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=108|
8
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-13:59:42.040|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=149|
9
- 8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-13:59:42.041|7=1|16=0|10=236|
10
- 8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=20260808-13:59:42.043|122=20260808-13:59:42.043|123=Y|36=2|10=109|
11
- 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260808-13:59:44.042|58=5|10=217|
12
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-13:59:44.043|58=5|10=217|
13
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-13:59:46.524|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=112|
14
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-13:59:46.526|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=162|
15
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-13:59:46.526|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=115|
16
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-13:59:47.064|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=112|
17
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-13:59:47.066|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=162|
18
- 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-13:59:48.067|58=5|10=227|
19
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-13:59:48.068|58=5|10=228|
20
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-13:59:50.829|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=115|
21
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-13:59:50.830|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=155|
22
- 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-13:59:51.831|58=5|10=220|
23
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-13:59:51.832|58=5|10=221|
24
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-13:59:54.326|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=111|
25
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-13:59:54.327|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=160|
26
- 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=0|57=fix|52=20260808-13:59:55.328|58=5|10=223|
27
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-13:59:57.802|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=113|
28
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=5|57=fix|52=20260808-13:59:57.803|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=166|
29
- 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-13:59:58.804|58=5|10=227|
30
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-13:59:58.805|58=5|10=228|
31
- 8=FIX.4.4|9=0000110|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-14:00:01.434|98=0|141=Y|553=js-client|554=pwd-client|10=158|
32
- 8=FIX.4.4|9=0000125|35=3|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-14:00:01.435|45=1|372=A|373=1|58=msgType A missing required tag 108|10=248|
33
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-14:00:06.211|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=088|
34
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-14:00:06.213|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=138|
1
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:10.991|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=101|
2
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:10.996|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=154|
3
+ 8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-21:05:10.997|7=1|16=1|10=242|
4
+ 8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=20260808-21:05:11.002|122=20260808-21:05:11.001|123=Y|36=2|10=070|
5
+ 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260808-21:05:12.999|58=5|10=223|
6
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:05:13.000|58=5|10=196|
7
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:15.791|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=104|
8
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:15.793|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=154|
9
+ 8=FIX.4.4|9=0000079|35=2|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-21:05:15.793|7=1|16=0|10=240|
10
+ 8=FIX.4.4|9=0000112|35=4|49=accept-comp|56=init-comp|34=1|57=fix|43=Y|52=20260808-21:05:15.796|122=20260808-21:05:15.795|123=Y|36=2|10=118|
11
+ 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260808-21:05:17.794|58=5|10=221|
12
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:05:17.795|58=5|10=221|
13
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:20.270|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=092|
14
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:20.272|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=142|
15
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-21:05:20.272|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=095|
16
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:20.802|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=093|
17
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:20.804|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=143|
18
+ 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-21:05:21.805|58=5|10=208|
19
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:05:21.806|58=5|10=209|
20
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:24.510|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=093|
21
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:24.511|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=142|
22
+ 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-21:05:25.512|58=5|10=207|
23
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:05:25.513|58=5|10=208|
24
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:28.267|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=106|
25
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:28.268|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=155|
26
+ 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=0|57=fix|52=20260808-21:05:29.269|58=5|10=218|
27
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:31.756|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=103|
28
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=5|57=fix|52=20260808-21:05:31.758|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=157|
29
+ 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-21:05:32.759|58=5|10=218|
30
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:05:32.760|58=5|10=210|
31
+ 8=FIX.4.4|9=0000110|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:35.230|98=0|141=Y|553=js-client|554=pwd-client|10=162|
32
+ 8=FIX.4.4|9=0000125|35=3|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:35.232|45=1|372=A|373=1|58=msgType A missing required tag 108|10=253|
33
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:39.805|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=106|
34
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:39.807|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=156|
35
35
  8=FIX4.4|9=0000136|35=ZZ|49=init-comp|56=accept-comp|34=2|57=fix|52=20180902-12:25:28.980|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=177|
36
- 8=FIX.4.4|9=0000111|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-14:00:06.214|45=2|372=ZZ|373=11|58=msgType ZZ unknown|10=140|
37
- 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260808-14:00:08.214|58=5|10=205|
38
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260808-14:00:08.215|58=5|10=206|
39
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-14:00:10.703|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=089|
40
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-14:00:10.704|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=138|
36
+ 8=FIX.4.4|9=0000111|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:05:39.809|45=2|372=ZZ|373=11|58=msgType ZZ unknown|10=159|
37
+ 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260808-21:05:41.808|58=5|10=214|
38
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260808-21:05:41.809|58=5|10=215|
39
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:44.541|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=099|
40
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:44.542|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=148|
41
41
  8=FIX4.4|9=0000123|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20180902-12:25:59.161|999=Sun, 02 Sep 2018 12:25:59 GMT|10=95|
42
- 8=FIX.4.4|9=0000146|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-14:00:10.705|45=2|372=0|373=5|58=msgType 0 checksum failed. received = 95 computed = 143|10=061|
43
- 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260808-14:00:12.704|58=5|10=204|
44
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260808-14:00:12.705|58=5|10=205|
45
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-14:00:19.323|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=096|
46
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-14:00:19.324|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=145|
42
+ 8=FIX.4.4|9=0000146|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:05:44.544|45=2|372=0|373=5|58=msgType 0 checksum failed. received = 95 computed = 143|10=072|
43
+ 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260808-21:05:46.544|58=5|10=216|
44
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260808-21:05:46.545|58=5|10=217|
45
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:53.038|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=100|
46
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:53.040|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=141|
47
47
  8=FIX4.4|9=0000123|35=0|49=init-not!|56=accept-comp|34=2|57=fix|52=20180902-12:25:59.161|112=Sun, 02 Sep 2018 12:25:59 GMT|10=95|
48
- 8=FIX.4.4|9=0000145|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-14:00:19.326|45=2|372=0|373=5|58=msgType 0 checksum failed. received = 95 computed = 59|10=026|
49
- 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260808-14:00:21.325|58=5|10=203|
50
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260808-14:00:21.326|58=5|10=204|
51
- 8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-14:00:24.088|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=050|
52
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-14:00:24.089|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=149|
53
- 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-14:00:26.095|112=Sat, 08 Aug 2026 14:00:26 GMT|10=133|
54
- 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260808-14:00:28.099|112=Sat, 08 Aug 2026 14:00:28 GMT|10=142|
55
- 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=4|57=fix|52=20260808-14:00:30.090|58=5|10=203|
56
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-14:00:30.091|58=5|10=202|
57
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-14:00:32.593|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=100|
58
- 8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-14:00:32.594|98=0|108=2|141=Y|553=js-server|554=pwd-server|10=099|
59
- 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-14:00:34.598|112=Sat, 08 Aug 2026 14:00:34 GMT|10=139|
60
- 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=3|57=fix|52=20260808-14:00:36.602|112=Sat, 08 Aug 2026 14:00:36 GMT|10=130|
61
- 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=4|57=fix|52=20260808-14:00:38.606|112=Sat, 08 Aug 2026 14:00:38 GMT|10=139|
62
- 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-14:00:40.595|58=5|10=212|
63
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=5|57=fix|52=20260808-14:00:40.596|58=5|10=216|
64
- 8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-14:00:43.075|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=047|
65
- 8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-14:00:43.077|98=0|108=5|141=Y|553=js-server|554=pwd-server|10=100|
66
- 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-14:00:45.081|112=Sat, 08 Aug 2026 14:00:45 GMT|10=130|
67
- 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260808-14:00:47.086|112=Sat, 08 Aug 2026 14:00:47 GMT|10=140|
68
- 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-14:00:48.087|112=Sat, 08 Aug 2026 14:00:48 GMT|10=142|
69
- 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=4|57=fix|52=20260808-14:00:49.089|112=Sat, 08 Aug 2026 14:00:49 GMT|10=148|
70
- 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=5|57=fix|52=20260808-14:00:51.077|58=5|10=212|
71
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260808-14:00:51.078|58=5|10=211|
72
- 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-14:03:01.489|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=103|
73
- 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-14:03:01.499|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=152|
74
- 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-14:03:02.502|58=5|10=201|
75
- 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-14:03:02.504|58=5|10=203|
48
+ 8=FIX.4.4|9=0000145|35=3|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:05:53.041|45=2|372=0|373=5|58=msgType 0 checksum failed. received = 95 computed = 59|10=021|
49
+ 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=3|57=fix|52=20260808-21:05:55.040|58=5|10=207|
50
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260808-21:05:55.041|58=5|10=208|
51
+ 8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:05:57.513|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=052|
52
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:05:57.514|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=151|
53
+ 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-21:05:59.518|112=Sat, 08 Aug 2026 21:05:59 GMT|10=151|
54
+ 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260808-21:06:01.522|112=Sat, 08 Aug 2026 21:06:01 GMT|10=123|
55
+ 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=4|57=fix|52=20260808-21:06:03.516|58=5|10=210|
56
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:06:03.517|58=5|10=209|
57
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:06:05.983|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=107|
58
+ 8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:06:05.984|98=0|108=2|141=Y|553=js-server|554=pwd-server|10=106|
59
+ 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:06:07.988|112=Sat, 08 Aug 2026 21:06:07 GMT|10=150|
60
+ 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=3|57=fix|52=20260808-21:06:09.990|112=Sat, 08 Aug 2026 21:06:09 GMT|10=148|
61
+ 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=4|57=fix|52=20260808-21:06:11.994|112=Sat, 08 Aug 2026 21:06:11 GMT|10=139|
62
+ 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-21:06:13.986|58=5|10=220|
63
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=5|57=fix|52=20260808-21:06:13.987|58=5|10=224|
64
+ 8=FIX.4.4|9=0000116|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:06:16.697|98=0|108=2|141=Y|553=js-client|554=pwd-client|10=061|
65
+ 8=FIX.4.4|9=0000116|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:06:16.698|98=0|108=5|141=Y|553=js-server|554=pwd-server|10=113|
66
+ 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-21:06:18.703|112=Sat, 08 Aug 2026 21:06:18 GMT|10=139|
67
+ 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=3|57=fix|52=20260808-21:06:20.707|112=Sat, 08 Aug 2026 21:06:20 GMT|10=130|
68
+ 8=FIX.4.4|9=0000104|35=0|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:06:21.711|112=Sat, 08 Aug 2026 21:06:21 GMT|10=126|
69
+ 8=FIX.4.4|9=0000104|35=0|49=init-comp|56=accept-comp|34=4|57=fix|52=20260808-21:06:22.713|112=Sat, 08 Aug 2026 21:06:22 GMT|10=132|
70
+ 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=5|57=fix|52=20260808-21:06:24.699|58=5|10=226|
71
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=3|57=fix|52=20260808-21:06:24.700|58=5|10=207|
72
+ 8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20260808-21:08:32.932|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=103|
73
+ 8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20260808-21:08:32.941|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=151|
74
+ 8=FIX.4.4|9=0000075|35=5|49=init-comp|56=accept-comp|34=2|57=fix|52=20260808-21:08:33.944|58=5|10=218|
75
+ 8=FIX.4.4|9=0000075|35=5|49=accept-comp|56=init-comp|34=2|57=fix|52=20260808-21:08:33.946|58=5|10=220|
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jspurefix",
3
- "version": "5.10.0",
3
+ "version": "5.10.2",
4
4
  "description": "pure node js fix engine",
5
5
  "keywords": [
6
6
  "typescript",
@@ -57,11 +57,18 @@ export abstract class SessionLauncher {
57
57
  }
58
58
 
59
59
  private acceptorEntity: FixEntity | null = null
60
+ private stopRequested: boolean = false
60
61
 
61
62
  protected async getAcceptor (sessionContainer: DependencyContainer): Promise<any> {
62
63
  if (sessionContainer.isRegistered<FixEntity>(DITokens.FixEntity)) {
63
64
  const entity = sessionContainer.resolve<FixEntity>(DITokens.FixEntity)
64
65
  this.acceptorEntity = entity
66
+ // a stop can arrive while the container is still being built - the listener has
67
+ // no existence yet to close, so honour the request by never opening it
68
+ if (this.stopRequested) {
69
+ this.logger.info('stop requested before the acceptor started - not listening')
70
+ return this.empty()
71
+ }
65
72
  return entity.start()
66
73
  } else {
67
74
  return this.empty()
@@ -87,6 +94,27 @@ export abstract class SessionLauncher {
87
94
  return null
88
95
  }
89
96
 
97
+ /**
98
+ * Stop what this launcher started - in practice, close the acceptor's listener.
99
+ *
100
+ * An acceptor is meant to outlive any one counterparty, so nothing closes its
101
+ * listener on its own. That is right for a venue and wrong for an application
102
+ * which knows it is finished: the listening socket alone keeps node alive, long
103
+ * after every session has logged out and every transport has been harvested.
104
+ *
105
+ * Until now the only caller was the both-roles branch of setup() below, which an
106
+ * application cannot reach once it gives the acceptor a launcher of its own - the
107
+ * usual arrangement as soon as more than one client is involved. There was then
108
+ * no way to shut a listener down short of killing the process.
109
+ *
110
+ * Safe before start (the request is remembered and the listener never opens), and
111
+ * safe to call twice. run() resolves once the listener has closed.
112
+ */
113
+ public stop (): void {
114
+ this.stopRequested = true
115
+ this.stopAcceptor()
116
+ }
117
+
90
118
  public async run (): Promise<boolean> {
91
119
  return await new Promise<any>((resolve, reject) => {
92
120
  const logger = this.logger
@@ -188,7 +216,7 @@ export abstract class SessionLauncher {
188
216
  // stop the acceptor so the process can exit cleanly.
189
217
  await client
190
218
  this.logger.info('client finished, stopping acceptor')
191
- this.stopAcceptor()
219
+ this.stop()
192
220
  return true
193
221
  }
194
222
  return await Promise.all([server, client])