fixtureqa 0.8.2__tar.gz → 0.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. {fixtureqa-0.8.2/fixtureqa.egg-info → fixtureqa-0.9.0}/PKG-INFO +2 -2
  2. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/api/routers/sessions.py +12 -1
  3. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/api/schemas.py +3 -0
  4. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/models.py +6 -0
  5. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/session.py +1 -0
  6. fixtureqa-0.8.2/fixture/static/assets/index-Cu3H3T9B.js → fixtureqa-0.9.0/fixture/static/assets/index-D2BF3qaF.js +1 -1
  7. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/static/index.html +1 -1
  8. {fixtureqa-0.8.2 → fixtureqa-0.9.0/fixtureqa.egg-info}/PKG-INFO +2 -2
  9. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixtureqa.egg-info/SOURCES.txt +1 -1
  10. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixtureqa.egg-info/requires.txt +1 -1
  11. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/pyproject.toml +2 -2
  12. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_config_store.py +1 -0
  13. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_non_stop_session.py +14 -0
  14. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_sessions.py +17 -0
  15. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/LICENSE +0 -0
  16. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/README.md +0 -0
  17. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/__init__.py +0 -0
  18. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/__main__.py +0 -0
  19. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/api/__init__.py +0 -0
  20. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/api/app.py +0 -0
  21. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/api/connection_manager.py +0 -0
  22. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/api/deps.py +0 -0
  23. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/api/routers/__init__.py +0 -0
  24. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/api/routers/admin.py +0 -0
  25. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/api/routers/auth.py +0 -0
  26. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/api/routers/branding.py +0 -0
  27. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/api/routers/custom_tags.py +0 -0
  28. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/api/routers/fix_spec.py +0 -0
  29. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/api/routers/messages.py +0 -0
  30. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/api/routers/perf.py +0 -0
  31. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/api/routers/scenarios.py +0 -0
  32. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/api/routers/setup.py +0 -0
  33. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/api/routers/spec_overlay.py +0 -0
  34. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/api/routers/templates.py +0 -0
  35. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/api/routers/ws.py +0 -0
  36. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/config/__init__.py +0 -0
  37. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/__init__.py +0 -0
  38. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/atomic_io.py +0 -0
  39. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/auth.py +0 -0
  40. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/config_store.py +0 -0
  41. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/custom_tag_store.py +0 -0
  42. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/db_migrations.py +0 -0
  43. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/events.py +0 -0
  44. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/fix_application.py +0 -0
  45. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/fix_builder.py +0 -0
  46. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/fix_parser.py +0 -0
  47. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/fix_spec_parser.py +0 -0
  48. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/fix_tags.py +0 -0
  49. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/fix_time.py +0 -0
  50. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/housekeeping.py +0 -0
  51. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/inbound.py +0 -0
  52. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/json_store.py +0 -0
  53. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/latency_csv_writer.py +0 -0
  54. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/message_log.py +0 -0
  55. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/message_store.py +0 -0
  56. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/perf_charts.py +0 -0
  57. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/perf_engine.py +0 -0
  58. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/perf_models.py +0 -0
  59. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/perf_payload.py +0 -0
  60. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/perf_stats.py +0 -0
  61. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/perf_store.py +0 -0
  62. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/perf_writer.py +0 -0
  63. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/scenario_runner.py +0 -0
  64. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/scenario_store.py +0 -0
  65. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/session_manager.py +0 -0
  66. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/spec_overlay_store.py +0 -0
  67. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/template_store.py +0 -0
  68. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/user_store.py +0 -0
  69. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/value_gen.py +0 -0
  70. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/core/venue_responses.py +0 -0
  71. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/fix_specs/FIX42.xml +0 -0
  72. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/fix_specs/FIX44.xml +0 -0
  73. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/server.py +0 -0
  74. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/static/assets/ag-grid-_QKprVdm.js +0 -0
  75. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/static/assets/index-B-hpSZWZ.css +0 -0
  76. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/static/assets/index-CyNOPa0n.js +0 -0
  77. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/static/assets/react-vendor-2eF0YfZT.js +0 -0
  78. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/static/favicon.svg +0 -0
  79. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixture/ui/__init__.py +0 -0
  80. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixtureqa.egg-info/dependency_links.txt +0 -0
  81. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixtureqa.egg-info/entry_points.txt +0 -0
  82. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/fixtureqa.egg-info/top_level.txt +0 -0
  83. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/setup.cfg +0 -0
  84. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_atomic_io.py +0 -0
  85. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_auth.py +0 -0
  86. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_connection_manager.py +0 -0
  87. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_db_migrations.py +0 -0
  88. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_fix_builder.py +0 -0
  89. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_health.py +0 -0
  90. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_inbound.py +0 -0
  91. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_inbound_validation.py +0 -0
  92. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_message_store.py +0 -0
  93. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_perf_api.py +0 -0
  94. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_perf_engine.py +0 -0
  95. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_perf_models.py +0 -0
  96. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_perf_payload.py +0 -0
  97. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_perf_rehydrate.py +0 -0
  98. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_scenarios.py +0 -0
  99. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_session_auto_response.py +0 -0
  100. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_session_lifecycle.py +0 -0
  101. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_session_manager_concurrency.py +0 -0
  102. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_templates.py +0 -0
  103. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_timezone_data.py +0 -0
  104. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_value_gen.py +0 -0
  105. {fixtureqa-0.8.2 → fixtureqa-0.9.0}/tests/test_ws.py +0 -0
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fixtureqa
3
- Version: 0.8.2
3
+ Version: 0.9.0
4
4
  Summary: FIXture — FIX Protocol Testing Tool
5
5
  Requires-Python: >=3.10
6
6
  License-File: LICENSE
7
- Requires-Dist: fixcore-engine==0.7.1
7
+ Requires-Dist: fixcore-engine==0.8.1
8
8
  Requires-Dist: fastapi>=0.111.0
9
9
  Requires-Dist: uvicorn[standard]>=0.29.0
10
10
  Requires-Dist: websockets>=12.0
@@ -68,6 +68,7 @@ def _session_response(sm: SessionManager, session_id: str, us: Optional[UserStor
68
68
  requires_orig_sending_time=cfg.requires_orig_sending_time,
69
69
  resend_request_chunk_size=cfg.resend_request_chunk_size,
70
70
  send_redundant_resend_requests=cfg.send_redundant_resend_requests,
71
+ enable_last_msg_seq_num_processed=cfg.enable_last_msg_seq_num_processed,
71
72
  use_file_log=cfg.use_file_log,
72
73
  start_time=cfg.start_time,
73
74
  end_time=cfg.end_time,
@@ -127,7 +128,17 @@ async def create_session(body: SessionConfigRequest, sm: SM, us: US, user: Curre
127
128
  try:
128
129
  await sm.start_session(session_id)
129
130
  except Exception as e:
130
- raise HTTPException(status_code=500, detail=f"Session added but failed to start: {e}")
131
+ # Creation is atomic: roll back the just-added session so a failed
132
+ # auto-start doesn't leave an orphan persisted. (Pre-0.8.2 a missing
133
+ # tzdata raised a TimeZone error here while the session stayed
134
+ # registered; the UI surfaced a failure, users retried, and the
135
+ # orphans piled up as duplicates.) The config is reported in the
136
+ # error so the user can fix and re-add.
137
+ try:
138
+ await sm.remove_session(session_id)
139
+ except Exception:
140
+ pass # best-effort rollback; surface the original start failure
141
+ raise HTTPException(status_code=500, detail=f"Failed to start session: {e}")
131
142
 
132
143
  return _session_response(sm, session_id, _owner_store(us, user))
133
144
 
@@ -21,6 +21,7 @@ class SessionConfigRequest(BaseModel):
21
21
  requires_orig_sending_time: bool = True # RequiresOrigSendingTime
22
22
  resend_request_chunk_size: int = Field(default=0, ge=0) # 0 = unlimited
23
23
  send_redundant_resend_requests: bool = False
24
+ enable_last_msg_seq_num_processed: bool = False # stamp LastMsgSeqNumProcessed(369)
24
25
  data_dictionary_path: Optional[str] = None
25
26
  use_file_log: bool = True
26
27
  start_time: str = "00:00:00"
@@ -67,6 +68,7 @@ class SessionResponse(BaseModel):
67
68
  requires_orig_sending_time: bool
68
69
  resend_request_chunk_size: int
69
70
  send_redundant_resend_requests: bool
71
+ enable_last_msg_seq_num_processed: bool
70
72
  use_file_log: bool
71
73
  start_time: str
72
74
  end_time: str
@@ -118,6 +120,7 @@ class UpdateSessionRequest(BaseModel):
118
120
  requires_orig_sending_time: Optional[bool] = None
119
121
  resend_request_chunk_size: Optional[int] = Field(default=None, ge=0)
120
122
  send_redundant_resend_requests: Optional[bool] = None
123
+ enable_last_msg_seq_num_processed: Optional[bool] = None
121
124
  data_dictionary_path: Optional[str] = None
122
125
  use_file_log: Optional[bool] = None
123
126
  start_time: Optional[str] = None
@@ -108,6 +108,12 @@ class SessionConfig:
108
108
  # outstanding (default N = re-request only when the gap advances).
109
109
  send_redundant_resend_requests: bool = False
110
110
 
111
+ # Message header (fixcore)
112
+ # Stamp LastMsgSeqNumProcessed(369) on every outbound message header, set to
113
+ # the seqnum of the last inbound message processed, so the peer can gauge how
114
+ # far behind we are (fixcore EnableLastMsgSeqNumProcessed). Off by default.
115
+ enable_last_msg_seq_num_processed: bool = False
116
+
111
117
  # Seqnum/message store durability. True (default) = fixcore FileStore, which
112
118
  # fsyncs per message (durable resend/recovery, but caps throughput at disk
113
119
  # speed). False = in-memory store: no per-message fsync, far higher
@@ -382,6 +382,7 @@ class Session:
382
382
  f"RequiresOrigSendingTime={yn(self.config.requires_orig_sending_time)}",
383
383
  f"ResendRequestChunkSize={self.config.resend_request_chunk_size}",
384
384
  f"SendRedundantResendRequests={yn(self.config.send_redundant_resend_requests)}",
385
+ f"EnableLastMsgSeqNumProcessed={yn(self.config.enable_last_msg_seq_num_processed)}",
385
386
  *schedule_lines,
386
387
  f"DataDictionary={dd_path}",
387
388
  # Always load the dictionary so inbound repeating groups decode into
@@ -33,7 +33,7 @@ Please change the parent <Route path="${b}"> to <Route path="${b==="/"?"*":`${b}
33
33
  *
34
34
  * This source code is licensed under the MIT license found in the
35
35
  * LICENSE file in the root directory of this source tree.
36
- */var eo=h,iS=aS;function sS(e,t){return e===t&&(e!==0||1/e===1/t)||e!==e&&t!==t}var oS=typeof Object.is=="function"?Object.is:sS,lS=iS.useSyncExternalStore,cS=eo.useRef,uS=eo.useEffect,dS=eo.useMemo,fS=eo.useDebugValue;Wv.useSyncExternalStoreWithSelector=function(e,t,n,r,a){var i=cS(null);if(i.current===null){var s={hasValue:!1,value:null};i.current=s}else s=i.current;i=dS(function(){function c(m){if(!u){if(u=!0,f=m,m=r(m),a!==void 0&&s.hasValue){var v=s.value;if(a(v,m))return d=v}return d=m}if(v=d,oS(f,m))return v;var y=r(m);return a!==void 0&&a(v,y)?(f=m,v):(f=m,d=y)}var u=!1,f,d,p=n===void 0?null:n;return[function(){return c(t())},p===null?void 0:function(){return c(p())}]},[t,n,r,a]);var l=lS(e,i[0],i[1]);return uS(function(){s.hasValue=!0,s.value=l},[l]),fS(l),l};Bv.exports=Wv;var Gv=Bv.exports;const hS=Fn(Gv),Hv={},{useDebugValue:mS}=xv,{useSyncExternalStoreWithSelector:pS}=hS;let ff=!1;const vS=e=>e;function gS(e,t=vS,n){(Hv?"production":void 0)!=="production"&&n&&!ff&&(console.warn("[DEPRECATED] Use `createWithEqualityFn` instead of `create` or use `useStoreWithEqualityFn` instead of `useStore`. They can be imported from 'zustand/traditional'. https://github.com/pmndrs/zustand/discussions/1937"),ff=!0);const r=pS(e.subscribe,e.getState,e.getServerState||e.getInitialState,t,n);return mS(r),r}const hf=e=>{(Hv?"production":void 0)!=="production"&&typeof e!="function"&&console.warn("[DEPRECATED] Passing a vanilla store will be unsupported in a future version. Instead use `import { useStore } from 'zustand'`.");const t=typeof e=="function"?K_(e):e,n=(r,a)=>gS(t,r,a);return Object.assign(n,t),n},yS=e=>e?hf(e):hf,Xe={get(e){try{return localStorage.getItem(e)}catch{return null}},set(e,t){try{localStorage.setItem(e,t)}catch{}},remove(e){try{localStorage.removeItem(e)}catch{}}};let vl=0;const bS=Xe.get("theme")??"dark",xS=Xe.get("auth_token"),wS=(()=>{try{const e=Xe.get("auth_user");return e?JSON.parse(e):null}catch{return null}})(),V=yS(e=>({sessions:{},messages:{},openTabs:[],activeTabId:null,theme:bS,_pendingStatus:{},scenarioRuns:{},scenarioGroupRuns:{},messageRates:{},branding:null,setBranding:t=>e({branding:t}),token:xS,user:wS,login:(t,n)=>{Xe.set("auth_token",t),Xe.set("auth_user",JSON.stringify(n)),e({token:t,user:n})},logout:()=>{Xe.remove("auth_token"),Xe.remove("auth_user"),e({token:null,user:null,sessions:{},messages:{},openTabs:[],activeTabId:null})},setSessions:t=>e(n=>{const r=Object.fromEntries(t.map(a=>[a.session_id,a]));for(const[a,i]of Object.entries(n._pendingStatus))r[a]&&(r[a]={...r[a],...i});return{sessions:r,_pendingStatus:{}}}),upsertSession:t=>e(n=>{const r=n.sessions[t.session_id];return(r==null?void 0:r.status_changed_at)!=null&&t.status_changed_at!=null&&r.status_changed_at>t.status_changed_at?{sessions:{...n.sessions,[t.session_id]:{...t,status:r.status,status_changed_at:r.status_changed_at,last_heartbeat_at:r.last_heartbeat_at}}}:{sessions:{...n.sessions,[t.session_id]:t}}}),removeSession:t=>e(n=>{const{[t]:r,...a}=n.sessions,{[t]:i,...s}=n.messages,l=n.openTabs.filter(u=>u!==t);let c=n.activeTabId;if(c===t){const u=n.openTabs.indexOf(t);c=l[u]??l[u-1]??null}return{sessions:a,messages:s,openTabs:l,activeTabId:c}}),updateSessionStatus:(t,n,r={})=>e(a=>{const i=a.sessions[t];return i?{sessions:{...a.sessions,[t]:{...i,status:n,...r}}}:{_pendingStatus:{...a._pendingStatus,[t]:{status:n,...r}}}}),updateLastHeartbeat:(t,n)=>e(r=>{const a=r.sessions[t];return a?{sessions:{...r.sessions,[t]:{...a,last_heartbeat_at:n}}}:{}}),openTab:t=>e(n=>({openTabs:n.openTabs.includes(t)?n.openTabs:[...n.openTabs,t],activeTabId:t})),closeTab:t=>e(n=>{const r=n.openTabs.filter(i=>i!==t);let a=n.activeTabId;if(a===t){const i=n.openTabs.indexOf(t);a=r[i]??r[i-1]??null}return{openTabs:r,activeTabId:a}}),setActiveTab:t=>e({activeTabId:t}),setMessages:(t,n)=>e(r=>({messages:{...r.messages,[t]:n.map(a=>({...a,_id:++vl}))}})),prependMessages:(t,n)=>e(r=>{const a=r.messages[t]??[],i=n.map(s=>({...s,_id:++vl}));return{messages:{...r.messages,[t]:[...i,...a]}}}),appendMessage:(t,n)=>e(r=>{const a=r.messages[t]??[],i={...n,_id:++vl},s=[...a,i],l=2e3;return{messages:{...r.messages,[t]:s.length>l?s.slice(-l):s}}}),clearMessages:t=>e(n=>({messages:{...n.messages,[t]:[]}})),toggleTheme:()=>e(t=>{const n=t.theme==="dark"?"light":"dark";return Xe.set("theme",n),{theme:n}}),setMessageRate:(t,n)=>e(r=>({messageRates:{...r.messageRates,[t]:{...n,at:Date.now()}}})),upsertScenarioRun:t=>e(n=>({scenarioRuns:{...n.scenarioRuns,[t.run_id]:t}})),updateScenarioStep:(t,n)=>e(r=>{const a=r.scenarioRuns[t];if(!a)return{};const i=[...a.steps],s=i.findIndex(l=>l.step_index===n.step_index);return s>=0?i[s]=n:i.push(n),{scenarioRuns:{...r.scenarioRuns,[t]:{...a,steps:i}}}}),completeScenarioRun:(t,n,r)=>e(a=>{const i=a.scenarioRuns[t];return i?{scenarioRuns:{...a.scenarioRuns,[t]:{...i,status:n,duration_ms:r}}}:{}}),upsertScenarioGroupRun:t=>e(n=>({scenarioGroupRuns:{...n.scenarioGroupRuns,[t.group_run_id]:t}})),completeScenarioGroupRun:(t,n,r)=>e(a=>{const i=a.scenarioGroupRuns[t];return i?{scenarioGroupRuns:{...a.scenarioGroupRuns,[t]:{...i,status:n,duration_ms:r}}}:{}})})),iu="/api";async function X(e,t){const n=V.getState().token,r={"Content-Type":"application/json"};n&&(r.Authorization=`Bearer ${n}`);const a=await fetch(`${iu}${e}`,{headers:r,...t});if(a.status===401)throw V.getState().logout(),window.location.href="/login",new Error("Session expired");if(!a.ok){const s=(await a.json().catch(()=>({detail:a.statusText}))).detail,l=Array.isArray(s)?s.map(c=>{const u=Array.isArray(c.loc)?c.loc.filter(f=>f!=="body").join("."):"";return u?`${u}: ${c.msg}`:c.msg??JSON.stringify(c)}).join("; "):s??a.statusText;throw new Error(l)}if(a.status!==204)return a.json()}const H={sessions:{list:()=>X("/sessions/"),create:e=>X("/sessions/",{method:"POST",body:JSON.stringify(e)}),get:e=>X(`/sessions/${e}`),update:(e,t)=>X(`/sessions/${e}`,{method:"PATCH",body:JSON.stringify(t)}),remove:e=>X(`/sessions/${e}`,{method:"DELETE"}),start:e=>X(`/sessions/${e}/start`,{method:"POST"}),stop:(e,t=!1)=>X(`/sessions/${e}/stop?force=${t}`,{method:"POST"}),send:(e,t,n)=>X(`/sessions/${e}/send`,{method:"POST",body:JSON.stringify({raw:t,no_auto_ts:(n==null?void 0:n.noAutoTs)??!1})}),import:e=>X("/sessions/import",{method:"POST",body:JSON.stringify(e)}),getSeqnums:e=>X(`/sessions/${e}/seqnums`),setSeqnums:(e,t)=>X(`/sessions/${e}/seqnums`,{method:"PATCH",body:JSON.stringify(t)})},messages:{list:(e,t)=>{const n=new URLSearchParams;return t!=null&&t.direction&&n.set("direction",t.direction),t!=null&&t.limit&&n.set("limit",String(t.limit)),(t==null?void 0:t.beforeId)!=null&&n.set("before_id",String(t.beforeId)),X(`/sessions/${e}/messages?${n}`)},search:(e,t)=>{const n=new URLSearchParams;return n.set("q",t.q),t.regex&&n.set("regex","true"),t.limit!=null&&n.set("limit",String(t.limit)),X(`/sessions/${e}/messages/search?${n}`)},clear:e=>X(`/sessions/${e}/messages`,{method:"DELETE"}),export:(e,t)=>mf(`/sessions/${e}/messages/export?fmt=${t}`,`${e}.${t}`)},admin:{users:{list:()=>X("/admin/users"),create:e=>X("/admin/users",{method:"POST",body:JSON.stringify(e)}),update:(e,t)=>X(`/admin/users/${e}`,{method:"PATCH",body:JSON.stringify(t)}),delete:e=>X(`/admin/users/${e}`,{method:"DELETE"})},settings:{get:()=>X("/admin/settings"),update:e=>X("/admin/settings",{method:"PATCH",body:JSON.stringify(e)})},housekeeping:{get:()=>X("/admin/housekeeping"),update:e=>X("/admin/housekeeping",{method:"PATCH",body:JSON.stringify(e)}),runNow:()=>X("/admin/housekeeping/run",{method:"POST"})},branding:{get:()=>X("/admin/branding"),update:e=>X("/admin/branding",{method:"PATCH",body:JSON.stringify(e)})}},fixSpec:{messages:e=>X(`/fix-spec/messages?begin_string=${encodeURIComponent(e)}`),message:(e,t)=>X(`/fix-spec/messages/${encodeURIComponent(t)}?begin_string=${encodeURIComponent(e)}`)},templates:{list:()=>X("/templates"),create:e=>X("/templates",{method:"POST",body:JSON.stringify(e)}),update:(e,t)=>X(`/templates/${e}`,{method:"PUT",body:JSON.stringify(t)}),delete:e=>X(`/templates/${e}`,{method:"DELETE"})},customTags:{list:()=>X("/custom-tags"),upsert:e=>X("/custom-tags",{method:"POST",body:JSON.stringify(e)}),delete:e=>X(`/custom-tags/${e}`,{method:"DELETE"})},specOverlay:{get:()=>X("/spec-overlay"),set:e=>X("/spec-overlay",{method:"PUT",body:JSON.stringify(e)})},scenarios:{list:()=>X("/scenarios"),create:e=>X("/scenarios",{method:"POST",body:JSON.stringify(e)}),update:(e,t)=>X(`/scenarios/${e}`,{method:"PUT",body:JSON.stringify(t)}),delete:e=>X(`/scenarios/${e}`,{method:"DELETE"}),run:e=>X(`/scenarios/${e}/run`,{method:"POST"}),abort:e=>X(`/scenarios/runs/${e}`,{method:"DELETE"}),groups:{list:()=>X("/scenarios/groups"),create:e=>X("/scenarios/groups",{method:"POST",body:JSON.stringify(e)}),update:(e,t)=>X(`/scenarios/groups/${e}`,{method:"PUT",body:JSON.stringify(t)}),delete:e=>X(`/scenarios/groups/${e}`,{method:"DELETE"}),runSaved:e=>X(`/scenarios/groups/${e}/run`,{method:"POST"})},runGroup:e=>X("/scenarios/groups/run",{method:"POST",body:JSON.stringify({scenario_ids:e})}),abortGroup:e=>X(`/scenarios/groups/runs/${e}`,{method:"DELETE"})},perf:{runs:{create:e=>X("/perf/runs",{method:"POST",body:JSON.stringify(e)}),list:()=>X("/perf/runs"),get:e=>X(`/perf/runs/${e}`),stop:e=>X(`/perf/runs/${e}`,{method:"DELETE"}),messages:(e,t)=>{const n=new URLSearchParams;return(t==null?void 0:t.limit)!=null&&n.set("limit",String(t.limit)),(t==null?void 0:t.offset)!=null&&n.set("offset",String(t.offset)),X(`/perf/runs/${e}/messages?${n}`)},scenarios:(e,t)=>{const n=new URLSearchParams;return(t==null?void 0:t.limit)!=null&&n.set("limit",String(t.limit)),(t==null?void 0:t.offset)!=null&&n.set("offset",String(t.offset)),X(`/perf/runs/${e}/scenarios?${n}`)},ticket:e=>X(`/perf/runs/${e}/ticket`,{method:"POST"}),export:(e,t)=>mf(`/perf/runs/${e}/export?kind=${t}`,`${e}_${t}.${t==="both"?"zip":t==="latency"?"csv.gz":"csv"}`),chart:(e,t)=>{const n=new URLSearchParams;return t!=null&&t.metric&&n.set("metric",t.metric),t!=null&&t.agg&&n.set("agg",t.agg),t!=null&&t.scale&&n.set("scale",t.scale),jS(`/perf/runs/${e}/chart?${n}`)}},configs:{list:()=>X("/perf/configs"),get:e=>X(`/perf/configs/${e}`),save:e=>X("/perf/configs",{method:"POST",body:JSON.stringify(e)}),delete:e=>X(`/perf/configs/${e}`,{method:"DELETE"})}}};async function mf(e,t){const n=V.getState().token,r={};n&&(r.Authorization=`Bearer ${n}`);const a=await fetch(`${iu}${e}`,{headers:r});if(a.status===401)throw V.getState().logout(),window.location.href="/login",new Error("Session expired");if(!a.ok)throw new Error(await a.text());const i=await a.blob(),s=URL.createObjectURL(i),l=document.createElement("a");l.href=s,l.download=t,l.click(),URL.revokeObjectURL(s)}async function jS(e){const t=V.getState().token,n={};t&&(n.Authorization=`Bearer ${t}`);const r=await fetch(`${iu}${e}`,{headers:n});if(r.status===401)throw V.getState().logout(),window.location.href="/login",new Error("Session expired");if(!r.ok)throw new Error(await r.text());return URL.createObjectURL(await r.blob())}const _S=Object.freeze(Object.defineProperty({__proto__:null,api:H},Symbol.toStringTag,{value:"Module"})),SS={1:"Account",6:"AvgPx",7:"BeginSeqNo",8:"BeginString",9:"BodyLength",10:"CheckSum",11:"ClOrdID",14:"CumQty",15:"Currency",17:"ExecID",18:"ExecInst",20:"ExecTransType",21:"HandlInst",22:"IDSource",29:"LastCapacity",30:"LastMkt",31:"LastPx",32:"LastShares",34:"MsgSeqNum",35:"MsgType",37:"OrderID",38:"OrderQty",39:"OrdStatus",40:"OrdType",41:"OrigClOrdID",44:"Price",48:"SecurityID",49:"SenderCompID",50:"SenderSubID",52:"SendingTime",54:"Side",55:"Symbol",56:"TargetCompID",57:"TargetSubID",58:"Text",59:"TimeInForce",60:"TransactTime",63:"SettlmntTyp",64:"FutSettDate",76:"ExecBroker",77:"OpenClose",98:"EncryptMethod",99:"StopPx",100:"ExDestination",102:"CxlRejReason",103:"OrdRejReason",108:"HeartBtInt",109:"ClientID",110:"MinQty",111:"MaxFloor",112:"TestReqID",115:"OnBehalfOfCompID",117:"QuoteID",122:"OrigSendingTime",126:"ExpireTime",131:"QuoteReqID",132:"BidPx",133:"OfferPx",134:"BidSize",135:"OfferSize",140:"PrevClosePx",141:"ResetSeqNumFlag",150:"ExecType",151:"LeavesQty",167:"SecurityType",200:"MaturityMonthYear",201:"PutOrCall",202:"StrikePrice",207:"SecurityExchange",210:"MaxShow",369:"LastMsgSeqNumProcessed",371:"RefTagID",372:"RefMsgType",373:"SessionRejectReason",553:"Username",554:"Password"},Ji={};function Zv(e){for(const t of e)Ji[t.number]=t.name}function OS(){for(const e of Object.keys(Ji))delete Ji[Number(e)]}function rr(e){const t=typeof e=="string"?parseInt(e,10):e;return Ji[t]??SS[t]??`Tag${t}`}const pf=3e3,NS=6e4;function su(){const e=h.useRef(null),{updateSessionStatus:t,appendMessage:n,upsertSession:r,updateLastHeartbeat:a,setMessageRate:i,upsertScenarioRun:s,updateScenarioStep:l,completeScenarioRun:c,upsertScenarioGroupRun:u,completeScenarioGroupRun:f}=V();h.useEffect(()=>{let d=!1,p,m=pf;function v(){d||(p=setTimeout(y,m),m=Math.min(m*2,NS))}function y(){const g=V.getState().token;if(!g){p=setTimeout(y,1e3);return}const w=`${window.location.protocol==="https:"?"wss:":"ws:"}//${window.location.host}/ws`,_=new WebSocket(w,["bearer",g]);e.current=_,_.onopen=()=>{m=pf},_.onmessage=x=>{let j;try{j=JSON.parse(x.data)}catch{return}if(j.type!=="ping")if(j.type==="session_status")t(j.session_id,j.status,{last_sent_seq_num:j.last_sent_seq_num,last_recv_seq_num:j.last_recv_seq_num,status_changed_at:j.status_changed_at??void 0,last_heartbeat_at:j.last_heartbeat_at??void 0});else if(j.type==="message")n(j.session_id,{session_id:j.session_id,direction:j.direction,admin:j.admin,seq_num:j.seq_num,msg_type:j.msg_type,msg_type_name:j.msg_type_name,ts:j.ts,fields:j.fields,raw:j.raw}),j.msg_type==="0"&&a(j.session_id,Date.now()/1e3);else if(j.type==="message_agg")i(j.session_id,{rate:j.rate,in:j.in,out:j.out,last_seq:j.last_seq});else if(j.type==="scenario_start")s({run_id:j.run_id,scenario_id:j.scenario_id,scenario_name:j.scenario_name,session_id:j.session_id,group_run_id:j.group_run_id??void 0,status:"running",step_count:j.step_count,steps:[],started_at:Date.now()});else if(j.type==="scenario_step")l(j.run_id,{step_index:j.step_index,label:j.label,status:j.status,error:j.error??void 0,duration_ms:j.duration_ms});else if(j.type==="scenario_complete")c(j.run_id,j.status,j.duration_ms);else if(j.type==="scenario_group_start"){u({group_run_id:j.group_run_id,run_ids:j.runs.map(S=>S.run_id),status:"running",started_at:Date.now()});for(const S of j.runs)s({run_id:S.run_id,scenario_id:S.scenario_id,scenario_name:S.scenario_name,session_id:S.session_id,group_run_id:j.group_run_id,status:"running",step_count:S.step_count,steps:[],started_at:Date.now()})}else j.type==="scenario_group_complete"&&f(j.group_run_id,j.status,j.duration_ms)},_.onclose=()=>{v()},_.onerror=()=>{_.close()}}return y(),()=>{var g;d=!0,clearTimeout(p),(g=e.current)==null||g.close()}},[])}const PS=20,Kv=e=>`host_history_${e}`;function Yv(e){try{const t=Xe.get(Kv(e)),n=t?JSON.parse(t):[];return Array.isArray(n)?n.filter(r=>typeof r=="string"):[]}catch{return[]}}function Vv(e){var a;const t=e.trim(),n=(a=V.getState().user)==null?void 0:a.uid;if(!t||!n)return;const r=[t,...Yv(n).filter(i=>i!==t)].slice(0,PS);Xe.set(Kv(n),JSON.stringify(r))}function Xv(){const e=V.getState(),t=e.user?Yv(e.user.uid):[],n=Object.values(e.sessions).map(r=>r.host).filter(Boolean);return[...new Set([...t,...n])]}const CS={display_name:"",begin_string:"FIX.4.2",sender_comp_id:"",target_comp_id:"",connection_type:"initiator",host:"localhost",port:9876,heartbeat_interval:30,session_role:"generic",auto_start:!0,non_stop_session:!1,durable_seqnums:!0,log_messages:!0,validate_inbound:!1,session_qualifier:"",socket_accept_host:"",logon_timeout:10,logout_timeout:10,check_latency:!1,max_latency:120,requires_orig_sending_time:!0,resend_request_chunk_size:0,send_redundant_resend_requests:!1,start_time:"00:00:00",end_time:"00:00:00",start_day:"",end_day:"",weekdays:"",time_zone:"",reset_on_logon:!1,reset_on_logout:!1,reset_on_disconnect:!1,validate_fields_have_values:!0,validate_user_defined_fields:!1,validate_fields_out_of_range:!1};function kS({onClose:e,onSubmit:t}){const[n,r]=h.useState(CS),[a,i]=h.useState(""),[s,l]=h.useState(!1),[c,u]=h.useState(!1),f=h.useMemo(()=>Xv(),[]),d=(m,v)=>r(y=>({...y,[m]:v}));async function p(m){if(m.preventDefault(),!n.sender_comp_id||!n.target_comp_id){i("SenderCompID and TargetCompID are required");return}i(""),l(!0);try{await t(n),n.connection_type==="initiator"&&Vv(n.host),e()}catch(v){i(v instanceof Error?v.message:String(v))}finally{l(!1)}}return o.jsx("div",{className:"modal-overlay",onClick:e,children:o.jsxs("div",{className:"modal",onClick:m=>m.stopPropagation(),children:[o.jsxs("div",{className:"modal-header",children:[o.jsx("span",{children:"Add Session"}),o.jsx("button",{className:"btn-icon",onClick:e,children:"✕"})]}),o.jsxs("form",{onSubmit:p,children:[o.jsxs("div",{className:"form-grid",children:[o.jsx("label",{children:"Display Name"}),o.jsx("input",{value:n.display_name,onChange:m=>d("display_name",m.target.value),placeholder:"My Session"}),o.jsx("label",{children:"FIX Version"}),o.jsxs("select",{value:n.begin_string,onChange:m=>d("begin_string",m.target.value),children:[o.jsx("option",{value:"FIX.4.2",children:"FIX 4.2"}),o.jsx("option",{value:"FIX.4.4",children:"FIX 4.4"})]}),o.jsx("label",{children:"Connection Type"}),o.jsxs("select",{value:n.connection_type,onChange:m=>d("connection_type",m.target.value),children:[o.jsx("option",{value:"initiator",children:"Initiator"}),o.jsx("option",{value:"acceptor",children:"Acceptor"})]}),o.jsx("label",{children:"SenderCompID *"}),o.jsx("input",{value:n.sender_comp_id,onChange:m=>d("sender_comp_id",m.target.value),placeholder:"CLIENT",required:!0}),o.jsx("label",{children:"TargetCompID *"}),o.jsx("input",{value:n.target_comp_id,onChange:m=>d("target_comp_id",m.target.value),placeholder:"SERVER",required:!0}),n.connection_type==="initiator"&&o.jsxs(o.Fragment,{children:[o.jsx("label",{children:"Host"}),o.jsx("input",{list:"host-history-add",value:n.host,onChange:m=>d("host",m.target.value)}),o.jsx("datalist",{id:"host-history-add",children:f.map(m=>o.jsx("option",{value:m},m))})]}),o.jsx("label",{children:"Port"}),o.jsx("input",{type:"number",value:n.port,onChange:m=>d("port",Number(m.target.value)),min:1,max:65535}),o.jsx("label",{children:"HeartBtInt (s)"}),o.jsx("input",{type:"number",value:n.heartbeat_interval,onChange:m=>d("heartbeat_interval",Number(m.target.value)),min:1}),o.jsx("label",{children:"Role"}),o.jsxs("select",{value:n.session_role,onChange:m=>d("session_role",m.target.value),children:[o.jsx("option",{value:"client",children:"Client — sends orders"}),o.jsx("option",{value:"venue",children:"Venue — receives orders, sends fills"}),o.jsx("option",{value:"generic",children:"Generic — post-trade, IOIs, etc."})]}),o.jsx("label",{children:"Auto-start"}),o.jsx("input",{type:"checkbox",checked:n.auto_start,onChange:m=>d("auto_start",m.target.checked)}),o.jsx("label",{title:"Disables per-message fsync (durable_seqnums) and the message log — required for high-rate perf runs.",children:"Performance session"}),o.jsx("input",{type:"checkbox",checked:!n.durable_seqnums&&!n.log_messages,onChange:m=>{const v=m.target.checked;r(y=>({...y,durable_seqnums:!v,log_messages:!v}))}}),o.jsx("label",{title:"Validate inbound messages against the FIX spec and reject non-conforming ones (missing required fields, unknown MsgType), like a real counterparty. Off = permissive: repeating groups still decode, but nothing is auto-rejected.",children:"Validate inbound (conformant venue)"}),o.jsx("input",{type:"checkbox",checked:!!n.validate_inbound,onChange:m=>d("validate_inbound",m.target.checked)})]}),n.validate_inbound&&o.jsx("div",{className:"form-hint",children:"Conformant-venue mode: inbound app messages are validated against the FIX spec and non-conforming ones are rejected (Reject, 35=3). Leave off for permissive testing — repeating groups still decode correctly either way."}),!n.durable_seqnums&&!n.log_messages&&o.jsx("div",{className:"form-hint",children:"Performance mode: in-memory seqnums (no fsync) and no message log — fast, but messages won't appear in the session log and sequence numbers reset on restart."}),o.jsx("div",{style:{margin:"8px 0 0"},children:o.jsx("button",{type:"button",className:"btn btn-sm btn-ghost",onClick:()=>u(m=>!m),children:c?"▲ Hide advanced":"▼ Show advanced"})}),c&&o.jsxs("div",{className:"form-grid",style:{marginTop:8},children:[o.jsx("label",{title:"SessionQualifier: disambiguates two sessions sharing the same BeginString/Sender/Target triple.",children:"SessionQualifier"}),o.jsx("input",{value:n.session_qualifier,onChange:m=>d("session_qualifier",m.target.value),placeholder:"(none)"}),n.connection_type==="acceptor"&&o.jsxs(o.Fragment,{children:[o.jsx("label",{title:"SocketAcceptHost: bind address for the acceptor. Empty = 0.0.0.0 (all interfaces).",children:"SocketAcceptHost"}),o.jsx("input",{value:n.socket_accept_host,onChange:m=>d("socket_accept_host",m.target.value),placeholder:"0.0.0.0"})]}),o.jsx("label",{title:"LogonTimeout: seconds to wait for a Logon to complete before disconnecting.",children:"LogonTimeout (s)"}),o.jsx("input",{type:"number",value:n.logon_timeout,onChange:m=>d("logon_timeout",Number(m.target.value)),min:1}),o.jsx("label",{title:"LogoutTimeout: seconds to wait for a Logout confirmation before disconnecting.",children:"LogoutTimeout (s)"}),o.jsx("input",{type:"number",value:n.logout_timeout,onChange:m=>d("logout_timeout",Number(m.target.value)),min:1}),o.jsx("label",{title:"CheckLatency: reject inbound messages whose SendingTime(52) skews more than MaxLatency seconds from the local clock. PossDup resends are exempt.",children:"CheckLatency"}),o.jsx("input",{type:"checkbox",checked:n.check_latency,onChange:m=>d("check_latency",m.target.checked)}),o.jsx("label",{title:"MaxLatency: maximum allowed SendingTime skew in seconds (only when CheckLatency is on).",children:"MaxLatency (s)"}),o.jsx("input",{type:"number",value:n.max_latency,onChange:m=>d("max_latency",Number(m.target.value)),min:1,disabled:!n.check_latency,title:n.check_latency?void 0:"Only applies when CheckLatency is on"}),o.jsx("label",{title:"RequiresOrigSendingTime: reject a resent application message (PossDupFlag=Y) that omits OrigSendingTime(122). Admin gap-fills are never blocked.",children:"RequiresOrigSendingTime"}),o.jsx("input",{type:"checkbox",checked:n.requires_orig_sending_time,onChange:m=>d("requires_orig_sending_time",m.target.checked)}),o.jsx("label",{title:"ResendRequestChunkSize: cap the messages requested per outbound ResendRequest. 0 = unlimited (one open-ended request).",children:"ResendRequestChunkSize"}),o.jsx("input",{type:"number",value:n.resend_request_chunk_size,onChange:m=>d("resend_request_chunk_size",Number(m.target.value)),min:0}),o.jsx("label",{title:"SendRedundantResendRequests: re-send a ResendRequest for every out-of-order message while one is already outstanding.",children:"SendRedundantResendRequests"}),o.jsx("input",{type:"checkbox",checked:n.send_redundant_resend_requests,onChange:m=>d("send_redundant_resend_requests",m.target.checked)}),o.jsx("label",{title:"NonStopSession=Y: run as a single never-ending session and never reset sequence numbers on a day boundary. Off (default) uses the schedule below, which resets seqnums at each new period (e.g. midnight). Mutually exclusive with the schedule keys.",children:"Non-stop session"}),o.jsx("input",{type:"checkbox",checked:!!n.non_stop_session,onChange:m=>d("non_stop_session",m.target.checked)}),o.jsx("label",{children:"StartTime"}),o.jsx("input",{value:n.start_time,onChange:m=>d("start_time",m.target.value),placeholder:"00:00:00",disabled:n.non_stop_session,title:n.non_stop_session?"Ignored while Non-stop session is on":void 0}),o.jsx("label",{children:"EndTime"}),o.jsx("input",{value:n.end_time,onChange:m=>d("end_time",m.target.value),placeholder:"00:00:00",disabled:n.non_stop_session,title:n.non_stop_session?"Ignored while Non-stop session is on":void 0}),o.jsx("label",{title:"TimeZone: IANA name (e.g. America/New_York) interpreting the schedule times/days. Empty = UTC.",children:"TimeZone"}),o.jsx("input",{value:n.time_zone,onChange:m=>d("time_zone",m.target.value),placeholder:"UTC",disabled:n.non_stop_session}),o.jsx("label",{title:"Weekdays: comma list (e.g. Mon,Tue,Wed,Thu,Fri) restricting the daily window to those days. Mutually exclusive with StartDay/EndDay.",children:"Weekdays"}),o.jsx("input",{value:n.weekdays,onChange:m=>d("weekdays",m.target.value),placeholder:"(every day)",disabled:n.non_stop_session||!!n.start_day||!!n.end_day,title:n.start_day||n.end_day?"Clear StartDay/EndDay to use Weekdays":void 0}),o.jsx("label",{title:"StartDay + EndDay (both required) make this a weekly window, e.g. Sunday → Friday. Mutually exclusive with Weekdays.",children:"StartDay"}),o.jsx("input",{value:n.start_day,onChange:m=>d("start_day",m.target.value),placeholder:"(none)",disabled:n.non_stop_session||!!n.weekdays,title:n.weekdays?"Clear Weekdays to use StartDay/EndDay":void 0}),o.jsx("label",{children:"EndDay"}),o.jsx("input",{value:n.end_day,onChange:m=>d("end_day",m.target.value),placeholder:"(none)",disabled:n.non_stop_session||!!n.weekdays,title:n.weekdays?"Clear Weekdays to use StartDay/EndDay":void 0}),o.jsx("label",{children:"ResetOnLogon"}),o.jsx("input",{type:"checkbox",checked:!!n.reset_on_logon,onChange:m=>d("reset_on_logon",m.target.checked),title:"Reset sequence numbers to 1 on each Logon"}),o.jsx("label",{children:"ResetOnLogout"}),o.jsx("input",{type:"checkbox",checked:!!n.reset_on_logout,onChange:m=>d("reset_on_logout",m.target.checked),title:"Reset sequence numbers on Logout"}),o.jsx("label",{title:"ResetOnDisconnect: reset sequence numbers to 1 whenever the connection drops.",children:"ResetOnDisconnect"}),o.jsx("input",{type:"checkbox",checked:!!n.reset_on_disconnect,onChange:m=>d("reset_on_disconnect",m.target.checked)}),o.jsx("label",{title:"ValidateFieldsHaveValues: reject a present field with an empty value (e.g. 55=). Only bites while Validate inbound is on.",children:"↳ Fields have values"}),o.jsx("input",{type:"checkbox",checked:n.validate_fields_have_values,onChange:m=>d("validate_fields_have_values",m.target.checked),disabled:!n.validate_inbound,title:n.validate_inbound?void 0:"Only applies while Validate inbound is on"}),o.jsx("label",{title:"ValidateUserDefinedFields: reject a user-defined field (tag ≥ 5000) not in the loaded dictionary. Only bites while Validate inbound is on.",children:"↳ User-defined fields"}),o.jsx("input",{type:"checkbox",checked:n.validate_user_defined_fields,onChange:m=>d("validate_user_defined_fields",m.target.checked),disabled:!n.validate_inbound,title:n.validate_inbound?void 0:"Only applies while Validate inbound is on"}),o.jsx("label",{title:"ValidateFieldsOutOfRange: reject out-of-range enum values (e.g. Side=Z). Off = unknown enums allowed (QuickFIX/n AllowUnknownEnumValues=Y). Only bites while Validate inbound is on.",children:"↳ Enums in range"}),o.jsx("input",{type:"checkbox",checked:n.validate_fields_out_of_range,onChange:m=>d("validate_fields_out_of_range",m.target.checked),disabled:!n.validate_inbound,title:n.validate_inbound?void 0:"Only applies while Validate inbound is on"})]}),a&&o.jsx("div",{className:"error-msg",children:a}),o.jsxs("div",{className:"modal-footer",children:[o.jsx("button",{type:"button",className:"btn btn-secondary",onClick:e,children:"Cancel"}),o.jsx("button",{type:"submit",className:"btn btn-primary",disabled:s,children:s?"Adding…":"Add Session"})]})]})]})})}const ES="modulepreload",AS=function(e){return"/"+e},vf={},Jv=function(t,n,r){let a=Promise.resolve();if(n&&n.length>0){document.getElementsByTagName("link");const s=document.querySelector("meta[property=csp-nonce]"),l=(s==null?void 0:s.nonce)||(s==null?void 0:s.getAttribute("nonce"));a=Promise.allSettled(n.map(c=>{if(c=AS(c),c in vf)return;vf[c]=!0;const u=c.endsWith(".css"),f=u?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${c}"]${f}`))return;const d=document.createElement("link");if(d.rel=u?"stylesheet":ES,u||(d.as="script"),d.crossOrigin="",d.href=c,l&&d.setAttribute("nonce",l),document.head.appendChild(d),u)return new Promise((p,m)=>{d.addEventListener("load",p),d.addEventListener("error",()=>m(new Error(`Unable to preload CSS for ${c}`)))})}))}function i(s){const l=new Event("vite:preloadError",{cancelable:!0});if(l.payload=s,window.dispatchEvent(l),!l.defaultPrevented)throw s}return a.then(s=>{for(const l of s||[])l.status==="rejected"&&i(l.reason);return t().catch(i)})};function IS({session:e,onClose:t,onSaved:n}){const[r,a]=h.useState({display_name:e.display_name,begin_string:e.begin_string,sender_comp_id:e.sender_comp_id,target_comp_id:e.target_comp_id,connection_type:e.connection_type,host:e.host,port:e.port,heartbeat_interval:e.heartbeat_interval,reconnect_interval:e.reconnect_interval,session_qualifier:e.session_qualifier,socket_accept_host:e.socket_accept_host,logon_timeout:e.logon_timeout,logout_timeout:e.logout_timeout,check_latency:e.check_latency,max_latency:e.max_latency,requires_orig_sending_time:e.requires_orig_sending_time,resend_request_chunk_size:e.resend_request_chunk_size,send_redundant_resend_requests:e.send_redundant_resend_requests,use_file_log:e.use_file_log,start_time:e.start_time,end_time:e.end_time,start_day:e.start_day,end_day:e.end_day,weekdays:e.weekdays,time_zone:e.time_zone,non_stop_session:e.non_stop_session,reset_on_logon:e.reset_on_logon,reset_on_logout:e.reset_on_logout,reset_on_disconnect:e.reset_on_disconnect,session_role:e.session_role,auto_ack:e.auto_ack,auto_ack_delay_ms:e.auto_ack_delay_ms,auto_fill:e.auto_fill,auto_fill_delay_ms:e.auto_fill_delay_ms,validate_inbound:e.validate_inbound,validate_fields_have_values:e.validate_fields_have_values,validate_user_defined_fields:e.validate_user_defined_fields,validate_fields_out_of_range:e.validate_fields_out_of_range,durable_seqnums:e.durable_seqnums,log_messages:e.log_messages,data_dictionary_path:e.data_dictionary_path??""}),[i,s]=h.useState(""),[l,c]=h.useState(!1),[u,f]=h.useState(!1),d=V(x=>x.user),p=h.useMemo(()=>Xv(),[]),m=(x,j)=>a(S=>({...S,[x]:j})),v=!r.durable_seqnums&&!r.log_messages,y=r.durable_seqnums!==!1&&r.log_messages!==!1,g=e.durable_seqnums===!1&&e.log_messages===!1,b=v!==g,w=x=>x?"on":"off";async function _(x){x.preventDefault(),s(""),c(!0);try{const{api:j}=await Jv(async()=>{const{api:N}=await Promise.resolve().then(()=>_S);return{api:N}},[]),S={...r,data_dictionary_path:r.data_dictionary_path||null},P=await j.sessions.update(e.session_id,S);r.connection_type==="initiator"&&Vv(r.host),n(P),t()}catch(j){s(j instanceof Error?j.message:String(j))}finally{c(!1)}}return o.jsx("div",{className:"modal-overlay",onClick:t,children:o.jsxs("div",{className:"modal",onClick:x=>x.stopPropagation(),children:[o.jsxs("div",{className:"modal-header",children:[o.jsx("span",{children:"Edit Session"}),o.jsx("button",{className:"btn-icon",onClick:t,children:"✕"})]}),o.jsxs("div",{className:`session-type-banner ${v?"perf":""}`,children:[o.jsx("span",{className:"session-type-tag",children:v?"⚡ Performance session":y?"Standard session":"Custom session"}),o.jsxs("span",{className:"session-type-detail",children:["durable seqnums: ",w(r.durable_seqnums)," · message log: ",w(r.log_messages)," · validate inbound: ",w(r.validate_inbound)]})]}),o.jsxs("form",{onSubmit:_,children:[o.jsxs("div",{className:"form-grid",children:[(d==null?void 0:d.role)==="platform_admin"&&e.owner_username&&o.jsxs(o.Fragment,{children:[o.jsx("label",{children:"Owner"}),o.jsx("span",{className:"owner-badge",style:{justifySelf:"start"},children:e.owner_username})]}),o.jsx("label",{children:"Display Name"}),o.jsx("input",{value:r.display_name,onChange:x=>m("display_name",x.target.value),placeholder:"My Session"}),o.jsx("label",{children:"FIX Version"}),o.jsxs("select",{value:r.begin_string,onChange:x=>m("begin_string",x.target.value),children:[o.jsx("option",{value:"FIX.4.2",children:"FIX 4.2"}),o.jsx("option",{value:"FIX.4.4",children:"FIX 4.4"})]}),o.jsx("label",{children:"Connection Type"}),o.jsxs("select",{value:r.connection_type,onChange:x=>m("connection_type",x.target.value),children:[o.jsx("option",{value:"initiator",children:"Initiator"}),o.jsx("option",{value:"acceptor",children:"Acceptor"})]}),o.jsx("label",{children:"SenderCompID *"}),o.jsx("input",{value:r.sender_comp_id,onChange:x=>m("sender_comp_id",x.target.value),required:!0}),o.jsx("label",{children:"TargetCompID *"}),o.jsx("input",{value:r.target_comp_id,onChange:x=>m("target_comp_id",x.target.value),required:!0}),r.connection_type==="initiator"&&o.jsxs(o.Fragment,{children:[o.jsx("label",{children:"Host"}),o.jsx("input",{list:"host-history-edit",value:r.host,onChange:x=>m("host",x.target.value)}),o.jsx("datalist",{id:"host-history-edit",children:p.map(x=>o.jsx("option",{value:x},x))})]}),o.jsx("label",{children:"Port"}),o.jsx("input",{type:"number",value:r.port,onChange:x=>m("port",Number(x.target.value)),min:1,max:65535}),o.jsx("label",{children:"HeartBtInt (s)"}),o.jsx("input",{type:"number",value:r.heartbeat_interval,onChange:x=>m("heartbeat_interval",Number(x.target.value)),min:1}),o.jsx("label",{children:"Role"}),o.jsxs("select",{value:r.session_role,onChange:x=>m("session_role",x.target.value),children:[o.jsx("option",{value:"client",children:"Client — sends orders"}),o.jsx("option",{value:"venue",children:"Venue — receives orders, sends fills"}),o.jsx("option",{value:"generic",children:"Generic — post-trade, IOIs, etc."})]}),o.jsx("label",{title:"Disables per-message fsync (durable_seqnums) and the message log — required for high-rate perf runs. Toggling converts the session in place.",children:"Performance session"}),o.jsx("input",{type:"checkbox",checked:v,onChange:x=>{const j=x.target.checked;a(S=>({...S,durable_seqnums:!j,log_messages:!j,...j?{auto_ack:!1,auto_fill:!1}:{}}))}})]}),b&&o.jsx("div",{className:"form-hint",children:v?"Converting to a performance session: messages stay in memory only (last 10,000 viewable; nothing persisted) and sequence numbers reset — they no longer survive a restart.":"Converting to a standard session: messages persist to the database again (earlier persisted history reappears; the in-memory log from perf mode is not carried over) and sequence numbers reset once on conversion."}),r.session_role==="venue"&&o.jsxs("div",{className:"venue-auto-section",children:[o.jsx("div",{className:"venue-auto-title",children:"Auto-responses (venue)"}),v&&o.jsx("div",{className:"form-hint",children:"Auto-responses are disabled for performance sessions — the perf engine generates its own acks/fills."}),o.jsxs("div",{className:"form-grid",children:[o.jsx("label",{children:"Auto-ack"}),o.jsxs("div",{className:"venue-auto-row",children:[o.jsx("input",{type:"checkbox",checked:r.auto_ack,onChange:x=>m("auto_ack",x.target.checked),disabled:v,title:"Send Execution Report (OrdStatus=New) on each inbound New Order Single"}),o.jsx("span",{className:"venue-auto-label",children:"delay"}),o.jsx("input",{type:"number",className:"venue-delay-input",value:r.auto_ack_delay_ms,onChange:x=>m("auto_ack_delay_ms",Number(x.target.value)),min:0,disabled:v||!r.auto_ack}),o.jsx("span",{className:"venue-auto-label",children:"ms"})]}),o.jsx("label",{children:"Auto-fill"}),o.jsxs("div",{className:"venue-auto-row",children:[o.jsx("input",{type:"checkbox",checked:r.auto_fill,onChange:x=>m("auto_fill",x.target.checked),disabled:v,title:"Send Execution Report (OrdStatus=Filled) on each inbound New Order Single"}),o.jsx("span",{className:"venue-auto-label",children:"delay"}),o.jsx("input",{type:"number",className:"venue-delay-input",value:r.auto_fill_delay_ms,onChange:x=>m("auto_fill_delay_ms",Number(x.target.value)),min:0,disabled:v||!r.auto_fill}),o.jsx("span",{className:"venue-auto-label",children:"ms"})]})]})]}),o.jsx("div",{style:{margin:"8px 0 0"},children:o.jsx("button",{type:"button",className:"btn btn-sm btn-ghost",onClick:()=>f(x=>!x),children:u?"▲ Hide advanced":"▼ Show advanced"})}),u&&o.jsxs("div",{className:"form-grid",style:{marginTop:8},children:[o.jsx("label",{title:"SessionQualifier: disambiguates two sessions sharing the same BeginString/Sender/Target triple.",children:"SessionQualifier"}),o.jsx("input",{value:r.session_qualifier,onChange:x=>m("session_qualifier",x.target.value),placeholder:"(none)"}),o.jsx("label",{children:"ReconnectInterval (s)"}),o.jsx("input",{type:"number",value:r.reconnect_interval,onChange:x=>m("reconnect_interval",Number(x.target.value)),min:1}),r.connection_type==="acceptor"&&o.jsxs(o.Fragment,{children:[o.jsx("label",{title:"SocketAcceptHost: bind address for the acceptor. Empty = 0.0.0.0 (all interfaces).",children:"SocketAcceptHost"}),o.jsx("input",{value:r.socket_accept_host,onChange:x=>m("socket_accept_host",x.target.value),placeholder:"0.0.0.0"})]}),o.jsx("label",{title:"LogonTimeout: seconds to wait for a Logon to complete before disconnecting.",children:"LogonTimeout (s)"}),o.jsx("input",{type:"number",value:r.logon_timeout,onChange:x=>m("logon_timeout",Number(x.target.value)),min:1}),o.jsx("label",{title:"LogoutTimeout: seconds to wait for a Logout confirmation before disconnecting.",children:"LogoutTimeout (s)"}),o.jsx("input",{type:"number",value:r.logout_timeout,onChange:x=>m("logout_timeout",Number(x.target.value)),min:1}),o.jsx("label",{title:"CheckLatency: reject inbound messages whose SendingTime(52) skews more than MaxLatency seconds from the local clock. PossDup resends are exempt.",children:"CheckLatency"}),o.jsx("input",{type:"checkbox",checked:r.check_latency,onChange:x=>m("check_latency",x.target.checked)}),o.jsx("label",{title:"MaxLatency: maximum allowed SendingTime skew in seconds (only when CheckLatency is on).",children:"MaxLatency (s)"}),o.jsx("input",{type:"number",value:r.max_latency,onChange:x=>m("max_latency",Number(x.target.value)),min:1,disabled:!r.check_latency,title:r.check_latency?void 0:"Only applies when CheckLatency is on"}),o.jsx("label",{title:"RequiresOrigSendingTime: reject a resent application message (PossDupFlag=Y) that omits OrigSendingTime(122). Admin gap-fills are never blocked.",children:"RequiresOrigSendingTime"}),o.jsx("input",{type:"checkbox",checked:r.requires_orig_sending_time,onChange:x=>m("requires_orig_sending_time",x.target.checked)}),o.jsx("label",{title:"ResendRequestChunkSize: cap the messages requested per outbound ResendRequest. 0 = unlimited (one open-ended request).",children:"ResendRequestChunkSize"}),o.jsx("input",{type:"number",value:r.resend_request_chunk_size,onChange:x=>m("resend_request_chunk_size",Number(x.target.value)),min:0}),o.jsx("label",{title:"SendRedundantResendRequests: re-send a ResendRequest for every out-of-order message while one is already outstanding. Off = re-request only when the gap advances.",children:"SendRedundantResendRequests"}),o.jsx("input",{type:"checkbox",checked:r.send_redundant_resend_requests,onChange:x=>m("send_redundant_resend_requests",x.target.checked)}),o.jsx("label",{title:"NonStopSession=Y: run as a single never-ending session and never reset sequence numbers on a day boundary. Off (default) uses the schedule below, which resets seqnums at each new period (e.g. midnight). Mutually exclusive with the schedule keys.",children:"Non-stop session"}),o.jsx("input",{type:"checkbox",checked:!!r.non_stop_session,onChange:x=>m("non_stop_session",x.target.checked)}),o.jsx("label",{children:"StartTime"}),o.jsx("input",{value:r.start_time,onChange:x=>m("start_time",x.target.value),placeholder:"00:00:00",disabled:r.non_stop_session,title:r.non_stop_session?"Ignored while Non-stop session is on":void 0}),o.jsx("label",{children:"EndTime"}),o.jsx("input",{value:r.end_time,onChange:x=>m("end_time",x.target.value),placeholder:"00:00:00",disabled:r.non_stop_session,title:r.non_stop_session?"Ignored while Non-stop session is on":void 0}),o.jsx("label",{title:"TimeZone: IANA name (e.g. America/New_York) interpreting the schedule times/days. Empty = UTC.",children:"TimeZone"}),o.jsx("input",{value:r.time_zone,onChange:x=>m("time_zone",x.target.value),placeholder:"UTC",disabled:r.non_stop_session}),o.jsx("label",{title:"Weekdays: comma list (e.g. Mon,Tue,Wed,Thu,Fri) restricting the daily window to those days. Mutually exclusive with StartDay/EndDay.",children:"Weekdays"}),o.jsx("input",{value:r.weekdays,onChange:x=>m("weekdays",x.target.value),placeholder:"(every day)",disabled:r.non_stop_session||!!r.start_day||!!r.end_day,title:r.start_day||r.end_day?"Clear StartDay/EndDay to use Weekdays":void 0}),o.jsx("label",{title:"StartDay + EndDay (both required) make this a weekly window, e.g. Sunday → Friday. Mutually exclusive with Weekdays.",children:"StartDay"}),o.jsx("input",{value:r.start_day,onChange:x=>m("start_day",x.target.value),placeholder:"(none)",disabled:r.non_stop_session||!!r.weekdays,title:r.weekdays?"Clear Weekdays to use StartDay/EndDay":void 0}),o.jsx("label",{children:"EndDay"}),o.jsx("input",{value:r.end_day,onChange:x=>m("end_day",x.target.value),placeholder:"(none)",disabled:r.non_stop_session||!!r.weekdays,title:r.weekdays?"Clear Weekdays to use StartDay/EndDay":void 0}),o.jsx("label",{children:"ResetOnLogon"}),o.jsx("input",{type:"checkbox",checked:r.reset_on_logon,onChange:x=>m("reset_on_logon",x.target.checked),title:"Reset sequence numbers to 1 on each Logon"}),o.jsx("label",{children:"ResetOnLogout"}),o.jsx("input",{type:"checkbox",checked:r.reset_on_logout,onChange:x=>m("reset_on_logout",x.target.checked),title:"Reset sequence numbers on Logout"}),o.jsx("label",{title:"ResetOnDisconnect: reset sequence numbers to 1 whenever the connection drops.",children:"ResetOnDisconnect"}),o.jsx("input",{type:"checkbox",checked:r.reset_on_disconnect,onChange:x=>m("reset_on_disconnect",x.target.checked)}),o.jsx("label",{children:"Data Dictionary"}),o.jsx("input",{value:r.data_dictionary_path,onChange:x=>m("data_dictionary_path",x.target.value),placeholder:"(use bundled)"}),o.jsx("label",{children:"File Log"}),o.jsx("input",{type:"checkbox",checked:r.use_file_log,onChange:x=>m("use_file_log",x.target.checked)}),o.jsx("label",{title:"Validate inbound messages against the FIX spec and reject non-conforming ones (missing required fields, unknown MsgType), like a real counterparty. Off = permissive: repeating groups still decode, but nothing is auto-rejected. Takes effect on next start.",children:"Validate inbound"}),o.jsx("input",{type:"checkbox",checked:r.validate_inbound,onChange:x=>m("validate_inbound",x.target.checked),title:"Conformant-venue mode — applies on next session start"}),o.jsx("label",{title:"ValidateFieldsHaveValues: reject a present field with an empty value (e.g. 55=). Only bites while Validate inbound is on.",children:"↳ Fields have values"}),o.jsx("input",{type:"checkbox",checked:r.validate_fields_have_values,onChange:x=>m("validate_fields_have_values",x.target.checked),disabled:!r.validate_inbound,title:r.validate_inbound?void 0:"Only applies while Validate inbound is on"}),o.jsx("label",{title:"ValidateUserDefinedFields: reject a user-defined field (tag ≥ 5000) not in the loaded dictionary. Only bites while Validate inbound is on.",children:"↳ User-defined fields"}),o.jsx("input",{type:"checkbox",checked:r.validate_user_defined_fields,onChange:x=>m("validate_user_defined_fields",x.target.checked),disabled:!r.validate_inbound,title:r.validate_inbound?void 0:"Only applies while Validate inbound is on"}),o.jsx("label",{title:"ValidateFieldsOutOfRange: reject out-of-range enum values (e.g. Side=Z). Off = unknown enums allowed (QuickFIX/n AllowUnknownEnumValues=Y). Only bites while Validate inbound is on.",children:"↳ Enums in range"}),o.jsx("input",{type:"checkbox",checked:r.validate_fields_out_of_range,onChange:x=>m("validate_fields_out_of_range",x.target.checked),disabled:!r.validate_inbound,title:r.validate_inbound?void 0:"Only applies while Validate inbound is on"})]}),i&&o.jsx("div",{className:"error-msg",children:i}),o.jsxs("div",{className:"modal-footer",children:[o.jsx("button",{type:"button",className:"btn btn-secondary",onClick:t,children:"Cancel"}),o.jsx("button",{type:"submit",className:"btn btn-primary",disabled:l,children:l?"Saving…":"Save"})]})]})]})})}function TS({session:e,onClose:t,onSaved:n}){const[r,a]=h.useState(""),[i,s]=h.useState(""),[l,c]=h.useState(!0),[u,f]=h.useState(!1),[d,p]=h.useState(null),m=!["STOPPED","ERROR"].includes(e.status),v=e.display_name||`${e.sender_comp_id}→${e.target_comp_id}`;h.useEffect(()=>{H.sessions.getSeqnums(e.session_id).then(b=>{a(String(b.sender)),s(String(b.target)),c(!1)}).catch(b=>{p(b.message),c(!1)})},[e.session_id]);async function y(){const b=parseInt(r,10),w=parseInt(i,10);if(isNaN(b)||isNaN(w)||b<1||w<1){p("Sequence numbers must be ≥ 1");return}f(!0),p(null);try{await H.sessions.setSeqnums(e.session_id,{sender:b,target:w}),n(),t()}catch(_){p(_.message??"Failed to set seqnums")}finally{f(!1)}}function g(){a("1"),s("1")}return o.jsx("div",{className:"modal-overlay",onClick:t,children:o.jsxs("div",{className:"modal",style:{width:340},onClick:b=>b.stopPropagation(),children:[o.jsxs("div",{className:"modal-header",children:[o.jsxs("span",{children:["Sequence Numbers — ",v]}),o.jsx("button",{className:"btn btn-ghost btn-sm",onClick:t,children:"✕"})]}),o.jsxs("div",{className:"modal-body",children:[m&&o.jsx("div",{className:"seqnum-warning",children:"⚠ Session is running — changing seqnums will trigger a sequence reset with the counterparty."}),l?o.jsx("div",{style:{color:"var(--text2)",padding:"12px 0"},children:"Loading…"}):o.jsxs("div",{className:"form-grid",style:{"--label-w":"140px"},children:[o.jsx("label",{children:"Next TX (outgoing)"}),o.jsx("input",{type:"number",min:1,value:r,onChange:b=>a(b.target.value),disabled:u}),o.jsx("label",{children:"Next RX (incoming)"}),o.jsx("input",{type:"number",min:1,value:i,onChange:b=>s(b.target.value),disabled:u})]}),d&&o.jsx("div",{className:"error-msg",children:d})]}),o.jsxs("div",{className:"modal-footer",children:[o.jsx("button",{className:"btn btn-sm btn-ghost",onClick:g,disabled:l||u,children:"Reset 1/1"}),o.jsx("div",{style:{flex:1}}),o.jsx("button",{className:"btn btn-sm btn-secondary",onClick:t,children:"Cancel"}),o.jsx("button",{className:"btn btn-sm btn-primary",onClick:y,disabled:l||u,children:u?"Applying…":"Apply"})]})]})})}const DS=["display_name","begin_string","sender_comp_id","target_comp_id","connection_type","host","port","heartbeat_interval","reconnect_interval","use_file_log","start_time","end_time","reset_on_logon","reset_on_logout","session_role","data_dictionary_path"];function Qv(e){return Object.fromEntries(DS.map(t=>[t,e[t]]))}function eg(e,t){const n=new Blob([JSON.stringify(e,null,2)],{type:"application/json"}),r=URL.createObjectURL(n),a=document.createElement("a");a.href=r,a.download=t,a.click(),URL.revokeObjectURL(r)}const MS={LOGGED_ON:"#4ade80",CONNECTING:"#facc15",LISTENING:"#facc15",LOGGED_OUT:"#94a3b8",STOPPED:"#94a3b8",ERROR:"#f87171"};function RS({status:e}){return o.jsxs("span",{className:"status-badge",style:{color:MS[e]??"#94a3b8"},children:["● ",e]})}function gf(e){const t=Math.floor(e/60),n=e%60;return t>0?`${t}m ${n}s`:`${n}s`}function LS({session:e}){const[,t]=h.useState(0),n=e.status==="LOGGED_ON",r=e.connection_type==="initiator"&&["CONNECTING","LOGGED_OUT"].includes(e.status);h.useEffect(()=>{if(!n&&!r)return;const i=setInterval(()=>t(s=>s+1),1e3);return()=>clearInterval(i)},[n,r]);const a=Date.now()/1e3;if(n&&e.status_changed_at){const i=Math.floor(a-e.status_changed_at);let s=null;if(e.last_heartbeat_at){const l=Math.floor(a-e.last_heartbeat_at),c=l<2*e.heartbeat_interval?"var(--green)":l<3*e.heartbeat_interval?"var(--yellow)":"var(--red)";s=o.jsxs("span",{style:{color:c},children:["· HB ",gf(l)," ago"]})}return o.jsxs("div",{className:"session-status-details",children:[o.jsxs("span",{children:["Connected ",gf(i)]}),s]})}if(r&&e.status_changed_at){const i=a-e.status_changed_at,s=Math.ceil(e.reconnect_interval-i%e.reconnect_interval);return o.jsx("div",{className:"session-status-details",children:o.jsxs("span",{style:{color:"var(--text2)"},children:["Reconnecting in ",s,"s"]})})}return null}function $S({session:e,active:t,onClick:n,showOwner:r}){const{upsertSession:a,removeSession:i}=V(),[s,l]=h.useState(!1),[c,u]=h.useState(!1),[f,d]=h.useState(!1),p=!["STOPPED","ERROR"].includes(e.status);async function m(){l(!0);try{const g=p?await H.sessions.stop(e.session_id):await H.sessions.start(e.session_id);a(g)}catch(g){console.error(g)}finally{l(!1)}}async function v(){confirm(`Remove session "${e.display_name||e.sender_comp_id}"?`)&&(await H.sessions.remove(e.session_id),i(e.session_id))}const y=e.display_name||`${e.sender_comp_id}→${e.target_comp_id}`;return o.jsxs("div",{className:`session-card${t?" active":""}`,onClick:n,children:[o.jsx("div",{className:"session-card-name",children:y}),o.jsxs("div",{className:"session-card-meta",children:[e.begin_string," · ",e.connection_type,o.jsx("span",{className:`role-badge role-${e.session_role}`,children:e.session_role}),r&&e.owner_username&&o.jsx("span",{className:"owner-badge",children:e.owner_username})]}),o.jsx(RS,{status:e.status}),o.jsx(LS,{session:e}),o.jsxs("div",{className:"session-card-seqnums",children:[o.jsxs("span",{children:["↑",e.last_sent_seq_num]}),o.jsxs("span",{children:["↓",e.last_recv_seq_num]}),o.jsxs("span",{children:[e.message_count," msgs"]})]}),o.jsxs("div",{className:"session-card-actions",onClick:g=>g.stopPropagation(),children:[o.jsx("button",{className:`btn btn-sm ${p?"btn-stop":"btn-start"}`,onClick:m,disabled:s,children:p?"Stop":"Start"}),o.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>u(!0),disabled:p,title:p?"Stop session to edit":"Edit config",children:"Edit"}),o.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>d(!0),title:"Set sequence numbers",children:"Seq"}),o.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>eg([Qv(e)],`${y}.json`),title:"Export this session",children:"⬇"}),o.jsx("button",{className:"btn btn-sm btn-danger",onClick:v,children:"✕"})]}),c&&o.jsx(IS,{session:e,onClose:()=>u(!1),onSaved:g=>{a(g),u(!1)}}),f&&o.jsx(TS,{session:e,onClose:()=>d(!1),onSaved:async()=>{const g=await H.sessions.get(e.session_id);a(g)}})]})}const yf="sessions_mine_only";function FS(){const{sessions:e,activeTabId:t,openTab:n,upsertSession:r,user:a}=V(),[i,s]=h.useState(!1),[l,c]=h.useState(""),[u,f]=h.useState(""),[d,p]=h.useState(()=>Xe.get(yf)==="1"),m=h.useRef(null),v=(a==null?void 0:a.role)==="platform_admin";function y(j){Xe.set(yf,j?"1":"0"),p(j)}async function g(j){const S=await H.sessions.create({...j,auto_start:j.auto_start});r(S),n(S.session_id)}function b(){const j=Object.values(e);if(j.length===0)return;const S=new Date().toISOString().slice(0,10);eg(j.map(Qv),`fixture-sessions-${S}.json`)}async function w(j){var P;const S=(P=j.target.files)==null?void 0:P[0];if(m.current&&(m.current.value="",!!S)){c(""),f("");try{const N=await S.text();let k=JSON.parse(N);Array.isArray(k)||(k=[k]);const A=await H.sessions.import(k);A.length===0?c("No sessions imported — file may be empty or all entries conflicted."):(A.forEach(T=>{r(T)}),f(`Imported ${A.length} of ${k.length} session${k.length!==1?"s":""}.`),setTimeout(()=>f(""),4e3))}catch(N){c(N instanceof Error?N.message:"Import failed")}}}const _=Object.values(e),x=v&&d?_.filter(j=>j.owner_username===(a==null?void 0:a.username)):_;return o.jsxs("div",{className:"sessions-panel",children:[o.jsxs("div",{className:"sessions-panel-header",children:[o.jsx("span",{children:"Sessions"}),o.jsxs("div",{className:"sessions-panel-header-actions",children:[o.jsx("button",{className:"btn btn-ghost btn-sm",onClick:b,disabled:_.length===0,title:"Export all sessions to JSON",children:"⬇ Export"}),o.jsx("button",{className:"btn btn-ghost btn-sm",onClick:()=>{var j;return(j=m.current)==null?void 0:j.click()},title:"Import sessions from JSON",children:"⬆ Import"}),o.jsx("input",{ref:m,type:"file",accept:".json,application/json",style:{display:"none"},onChange:w}),o.jsx("button",{className:"btn btn-primary btn-sm",onClick:()=>s(!0),children:"+ Add"})]})]}),v&&o.jsxs("div",{className:"sessions-filter-row",children:[o.jsx("button",{className:`btn btn-sm ${d?"btn-ghost":"btn-active"}`,onClick:()=>y(!1),title:"Show all users’ sessions",children:"All"}),o.jsx("button",{className:`btn btn-sm ${d?"btn-active":"btn-ghost"}`,onClick:()=>y(!0),title:"Show only your sessions",children:"Mine"})]}),l&&o.jsx("div",{className:"import-feedback import-error",children:l}),u&&o.jsx("div",{className:"import-feedback import-ok",children:u}),o.jsxs("div",{className:"sessions-list",children:[x.length===0&&o.jsx("div",{className:"sessions-empty",children:_.length>0?'No sessions of yours. Switch to "All" to see other users’ sessions.':"No sessions. Add one to get started."}),x.map(j=>o.jsx($S,{session:j,active:j.session_id===t,onClick:()=>n(j.session_id),showOwner:(a==null?void 0:a.role)==="platform_admin"},j.session_id))]}),i&&o.jsx(kS,{onClose:()=>s(!1),onSubmit:g})]})}const ou={D:{tag:"11",name:"ClOrdID"},F:{tag:"11",name:"ClOrdID"},G:{tag:"11",name:"ClOrdID"},AB:{tag:"11",name:"ClOrdID"},V:{tag:"262",name:"MDReqID"},R:{tag:"131",name:"QuoteReqID"},S:{tag:"117",name:"QuoteID"},x:{tag:"320",name:"SecurityReqID"}},Pa={autoTs:!0,autoId:!0,idPrefix:"",idCounter:1,autoExpiry:!0,expiryOffset:1,expiryUnit:"days",evalInline:!0,genRules:[]};function tg(e){return`fixture_send_opts_${e}`}function ng(e){try{const t=JSON.parse(Xe.get(tg(e))??"null");return t?{...Pa,...t}:{...Pa}}catch{return{...Pa}}}function rg(e,t){Xe.set(tg(e),JSON.stringify(t))}function bf(e){return e.replace(/\x01/g,"|").replace(/\|$/,"")}function Aa(e,t,n){const r=new RegExp(`(^|\\|)(${t}=[^|]*)`);return r.test(e)?e.replace(r,`$1${t}=${n}`):e?`${e}|${t}=${n}`:`${t}=${n}`}function xf(e,t,n){const r=new RegExp(`(^|\\|)(${t}=[^|]*)`);return r.test(e)?e.replace(r,`$1${t}=${n}`):e}function to(e=new Date){const t=e.toISOString();return t.slice(0,10).replace(/-/g,"")+"-"+t.slice(11,19)}function ag(e=new Date){return e.toISOString().slice(0,10).replace(/-/g,"")}const zS={minutes:6e4,hours:36e5,days:864e5};function BS(e,t){return new Date(Date.now()+e*zS[t])}function ig(e,t){if(!t.autoExpiry)return e;const n=BS(t.expiryOffset,t.expiryUnit);return e=xf(e,"126",to(n)),e=xf(e,"432",ag(n)),e}const Vl="uuid() · random_str(n) · random_int(a,b) · seq(width[,start]) · timestamp() · date() — mix with text as {gen()}, e.g. ORD-{seq(8)}",wf="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";function sg(e){const t=new Uint8Array(e);return crypto.getRandomValues(t),t}function WS(){const e=sg(16);e[6]=e[6]&15|64,e[8]=e[8]&63|128;const t=[...e].map(n=>n.toString(16).padStart(2,"0"));return`${t.slice(0,4).join("")}-${t.slice(4,6).join("")}-${t.slice(6,8).join("")}-${t.slice(8,10).join("")}-${t.slice(10,16).join("")}`}function US(e){const t=Number.isFinite(e)&&e>0?Math.min(Math.floor(e),256):8;return[...sg(t)].map(n=>wf[n%wf.length]).join("")}function qS(e,t){const n=Math.ceil(Math.min(e,t)),r=Math.floor(Math.max(e,t));return n+Math.floor(Math.random()*(r-n+1))}const GS=/^(\w+)\(([^)]*)\)$/,HS=/\{(\w+)\(([^)]*)\)\}/g,jf=new Map;function _f(e,t,n){const r=t.trim()?t.split(",").map(a=>a.trim()):[];switch(e){case"uuid":return WS();case"random_str":return US(r[0]!==void 0?Number(r[0]):8);case"random_int":{const a=Number(r[0]),i=Number(r[1]);return!Number.isFinite(a)||!Number.isFinite(i)?null:String(qS(a,i))}case"seq":{const a=r[0]!==void 0?Math.floor(Number(r[0])):0,i=r[1]!==void 0?Math.floor(Number(r[1])):1,s=jf.get(n)??(Number.isFinite(i)?i:1);jf.set(n,s+1);const l=String(s);return Number.isFinite(a)&&a>0?l.padStart(a,"0"):l}case"timestamp":return to();case"date":return ag();default:return null}}function og(e,t=""){const n=e.trim().match(GS);return n?_f(n[1],n[2],t)??e:e.replace(HS,(r,a,i)=>_f(a,i,t)??r)}function lg(e){const t=e.includes("")?"":"|";return e.split(t).map(n=>{const r=n.indexOf("=");if(r<0)return n;const a=n.slice(0,r),i=n.slice(r+1),s=og(i,a);return s===i?n:`${a}=${s}`}).join(t)}function cg(e,t){for(const n of t.genRules){const r=n.tag.trim();r&&(e=Aa(e,r,og(n.value,r)))}return e}function ug({opts:e,onChange:t,expanded:n,onToggle:r,msgType:a}){const i=a?ou[a]:void 0;function s(d){t({...e,...d})}const l=`${e.idPrefix}${e.idCounter}`;function c(d,p){s({genRules:e.genRules.map((m,v)=>v===d?{...m,...p}:m)})}function u(){s({genRules:[...e.genRules,{tag:"",value:""}]})}function f(d){s({genRules:e.genRules.filter((p,m)=>m!==d)})}return o.jsxs("div",{className:"send-opts",children:[o.jsxs("button",{type:"button",className:"btn btn-sm btn-ghost send-opts-toggle",onClick:r,children:[n?"▲":"▶"," Send Options"]}),n&&o.jsxs("div",{className:"send-opts-body",children:[o.jsxs("label",{className:"checkbox-label",children:[o.jsx("input",{type:"checkbox",checked:e.autoTs,onChange:d=>s({autoTs:d.target.checked})}),"Auto TransactTime (tag 60)"]}),o.jsxs("label",{className:"checkbox-label",children:[o.jsx("input",{type:"checkbox",checked:e.autoId,onChange:d=>s({autoId:d.target.checked})}),i?`Auto-generate ${i.name} (tag ${i.tag})`:a?`Auto-generate ID — no mapping for type ${a}`:"Auto-generate ID tag"]}),e.autoId&&o.jsxs("div",{className:"send-opts-id-row",children:[o.jsx("span",{className:"send-opts-label",children:"Prefix"}),o.jsx("input",{className:"send-opts-prefix",value:e.idPrefix,onChange:d=>s({idPrefix:d.target.value}),placeholder:"e.g. ORD",maxLength:20}),o.jsxs("span",{className:"send-opts-next",children:["Next: ",o.jsx("code",{children:l||e.idCounter})]})]}),o.jsxs("label",{className:"checkbox-label",children:[o.jsx("input",{type:"checkbox",checked:e.autoExpiry,onChange:d=>s({autoExpiry:d.target.checked})}),"Auto expiry (tags 126 / 432, only if present)"]}),e.autoExpiry&&o.jsxs("div",{className:"send-opts-id-row",children:[o.jsx("span",{className:"send-opts-label",children:"Future offset"}),o.jsx("input",{className:"send-opts-prefix",type:"number",min:0,style:{width:64},value:e.expiryOffset,onChange:d=>s({expiryOffset:Number(d.target.value)})}),o.jsxs("select",{value:e.expiryUnit,onChange:d=>s({expiryUnit:d.target.value}),children:[o.jsx("option",{value:"minutes",children:"minutes"}),o.jsx("option",{value:"hours",children:"hours"}),o.jsx("option",{value:"days",children:"days"})]}),o.jsx("span",{className:"send-opts-next",children:"126 = future time · 432 = that date"})]}),o.jsxs("label",{className:"checkbox-label",children:[o.jsx("input",{type:"checkbox",checked:e.evalInline,onChange:d=>s({evalInline:d.target.checked})}),"Evaluate value generators in the message (uuid(), random_str(), …)"]}),o.jsxs("div",{className:"send-opts-gen",children:[o.jsx("div",{className:"send-opts-label",children:"Custom tag generators"}),e.genRules.map((d,p)=>o.jsxs("div",{className:"send-opts-id-row",children:[o.jsx("input",{className:"send-opts-prefix",style:{width:64},placeholder:"tag",value:d.tag,onChange:m=>c(p,{tag:m.target.value.replace(/[^0-9]/g,"")})}),o.jsx("span",{children:"="}),o.jsx("input",{className:"send-opts-prefix",style:{flex:1},placeholder:"uuid() / seq(8) / ORD-{seq(8)} / literal",value:d.value,onChange:m=>c(p,{value:m.target.value})}),o.jsx("button",{type:"button",className:"btn btn-sm btn-ghost btn-danger-ghost",onClick:()=>f(p),title:"Remove rule",children:"✕"})]},p)),o.jsx("button",{type:"button",className:"btn btn-sm btn-ghost",onClick:u,children:"+ Add tag generator"}),o.jsxs("div",{className:"send-opts-next",style:{marginTop:4},children:["Generators: ",Vl,". Rules set the tag on every send (insert or replace); literal values are sent as-is."]})]})]})]})}const ZS=["FIX.4.2","FIX.4.4"];function KS(e){const t={};for(const n of e.split("|")){const r=n.indexOf("=");if(r<1)continue;const a=n.slice(0,r).trim(),i=n.slice(r+1).trim();a&&!isNaN(Number(a))&&(t[a]=i)}return t}function Sf(e){return Object.entries(e).filter(([,t])=>t!=="").map(([t,n])=>`${t}=${n}`).join("|")}function YS({fieldRef:e,value:t,onChange:n}){const r=String(e.tag||""),a=Object.keys(e.values).length>0;return o.jsxs("tr",{className:`tpl-field-row${e.required?" required":""}`,children:[o.jsx("td",{className:"tpl-field-tag",children:e.tag||"?"}),o.jsxs("td",{className:"tpl-field-name",children:[e.name,e.required&&o.jsx("span",{className:"tpl-req-badge",children:"req"})]}),o.jsx("td",{className:"tpl-field-value",children:a?o.jsxs("select",{className:"tpl-select",value:t,onChange:i=>n(r,i.target.value),children:[o.jsx("option",{value:"",children:"— pick —"}),Object.entries(e.values).map(([i,s])=>o.jsxs("option",{value:i,children:[i," – ",s.replace(/_/g," ")]},i))]}):o.jsx("input",{className:"tpl-input",type:"text",value:t,placeholder:e.type,onChange:i=>n(r,i.target.value)})})]})}function lu({template:e,defaultBeginString:t="FIX.4.2",onSaved:n,onClose:r}){const[a,i]=h.useState("fields"),[s,l]=h.useState((e==null?void 0:e.name)??""),[c,u]=h.useState((e==null?void 0:e.begin_string)??t),[f,d]=h.useState((e==null?void 0:e.msg_type)??""),[p,m]=h.useState((e==null?void 0:e.msg_type_name)??""),[v,y]=h.useState((e==null?void 0:e.fields)??{}),[g,b]=h.useState(()=>Sf((e==null?void 0:e.fields)??{})),[w,_]=h.useState([]),[x,j]=h.useState(null),[S,P]=h.useState(!1),[N,k]=h.useState(!1),[A,T]=h.useState("");h.useEffect(()=>{H.fixSpec.messages(c).then(_).catch(()=>_([]))},[c]),h.useEffect(()=>{if(!f){j(null);return}P(!0),H.fixSpec.message(c,f).then(W=>{j(W),P(!1)}).catch(()=>{j(null),P(!1)})},[c,f]);function C(W,re){y(Q=>({...Q,[W]:re}))}function I(W){const re=w.find(Q=>Q.msg_type===W);d(W),m((re==null?void 0:re.name)??W),W&&y(Q=>({...Q,35:W}))}function L(W){b(W);const re=KS(W);if(y(re),re[35]&&re[35]!==f){const Q=re[35],B=w.find(z=>z.msg_type===Q);d(Q),m((B==null?void 0:B.name)??Q)}}function G(W){W==="raw"&&b(Sf(v)),i(W)}const D=new Set((x==null?void 0:x.fields.map(W=>String(W.tag)).filter(Boolean))??[]),F=Object.entries(v).filter(([W])=>!D.has(W)&&W!=="35"),[U,M]=h.useState(""),[q,ne]=h.useState("");function ee(){!U||isNaN(Number(U))||(C(U,q),M(""),ne(""))}async function pe(){if(!s.trim()){T("Template name is required");return}if(!f){T("Message type is required");return}k(!0),T("");try{const W=Object.fromEntries(Object.entries(v).filter(([,B])=>B!=="")),re={name:s.trim(),msg_type:f,msg_type_name:p,begin_string:c,fields:W},Q=e!=null&&e.id?await H.templates.update(e.id,re):await H.templates.create(re);n(Q)}catch(W){T(W instanceof Error?W.message:"Save failed")}finally{k(!1)}}const ve=w.filter(W=>W.category==="app"),qe=w.filter(W=>W.category==="admin");return o.jsx("div",{className:"modal-overlay",onClick:r,children:o.jsxs("div",{className:"modal tpl-form-modal",onClick:W=>W.stopPropagation(),children:[o.jsxs("div",{className:"modal-header",children:[o.jsx("span",{children:e!=null&&e.id?"Edit Template":"New Template"}),o.jsx("button",{className:"btn-icon",onClick:r,children:"✕"})]}),o.jsxs("div",{className:"tpl-form-top",children:[o.jsxs("div",{className:"tpl-form-row",children:[o.jsx("label",{className:"tpl-label",children:"Name"}),o.jsx("input",{className:"tpl-input tpl-name-input",value:s,onChange:W=>l(W.target.value),placeholder:"e.g. Buy 100 AAPL",autoFocus:!0})]}),o.jsxs("div",{className:"tpl-form-row",children:[o.jsx("label",{className:"tpl-label",children:"FIX version"}),o.jsx("select",{className:"tpl-select",value:c,onChange:W=>u(W.target.value),children:ZS.map(W=>o.jsx("option",{value:W,children:W},W))}),o.jsx("label",{className:"tpl-label",style:{marginLeft:12},children:"Message type"}),o.jsxs("select",{className:"tpl-select tpl-msgtype-select",value:f,onChange:W=>I(W.target.value),children:[o.jsx("option",{value:"",children:"— select —"}),ve.length>0&&o.jsx("optgroup",{label:"Application",children:ve.map(W=>o.jsxs("option",{value:W.msg_type,children:[W.name," (",W.msg_type,")"]},W.msg_type))}),qe.length>0&&o.jsx("optgroup",{label:"Admin",children:qe.map(W=>o.jsxs("option",{value:W.msg_type,children:[W.name," (",W.msg_type,")"]},W.msg_type))})]})]})]}),o.jsxs("div",{className:"analysis-tabs",style:{marginBottom:0},children:[o.jsx("button",{className:`analysis-tab-btn${a==="fields"?" active":""}`,onClick:()=>G("fields"),children:"Fields"}),o.jsx("button",{className:`analysis-tab-btn${a==="raw"?" active":""}`,onClick:()=>G("raw"),children:"Raw FIX"})]}),o.jsxs("div",{className:"tpl-form-body",children:[a==="fields"&&o.jsxs("div",{className:"tpl-fields-tab",children:[!f&&o.jsx("div",{className:"tpl-no-msgtype",children:"Select a message type above to see its field definitions."}),f&&S&&o.jsx("div",{className:"tpl-loading",children:"Loading fields…"}),f&&!S&&o.jsxs("table",{className:"tpl-field-table",children:[o.jsx("thead",{children:o.jsxs("tr",{children:[o.jsx("th",{children:"Tag"}),o.jsx("th",{children:"Field"}),o.jsx("th",{children:"Value"})]})}),o.jsxs("tbody",{children:[x==null?void 0:x.fields.map(W=>o.jsx(YS,{fieldRef:W,value:v[String(W.tag)]??"",onChange:C},`${W.tag}-${W.name}`)),F.length>0&&o.jsxs(o.Fragment,{children:[o.jsx("tr",{className:"tpl-section-divider",children:o.jsx("td",{colSpan:3,children:"Custom / extra fields"})}),F.map(([W,re])=>o.jsxs("tr",{className:"tpl-field-row",children:[o.jsx("td",{className:"tpl-field-tag",children:W}),o.jsx("td",{className:"tpl-field-name",children:rr(Number(W))}),o.jsx("td",{className:"tpl-field-value",children:o.jsx("input",{className:"tpl-input",value:re,onChange:Q=>C(W,Q.target.value)})})]},W))]}),o.jsxs("tr",{className:"tpl-add-field-row",children:[o.jsx("td",{children:o.jsx("input",{className:"tpl-input tpl-add-tag",type:"number",placeholder:"tag",value:U,onChange:W=>M(W.target.value)})}),o.jsx("td",{className:"tpl-field-name",style:{color:"var(--text2)",fontSize:11},children:U&&!isNaN(Number(U))?rr(Number(U)):"add field"}),o.jsxs("td",{className:"tpl-field-value",style:{display:"flex",gap:4},children:[o.jsx("input",{className:"tpl-input",placeholder:"value",value:q,onChange:W=>ne(W.target.value),onKeyDown:W=>{W.key==="Enter"&&ee()}}),o.jsx("button",{className:"btn btn-sm btn-ghost",onClick:ee,children:"+"})]})]})]})]})]}),a==="raw"&&o.jsxs("div",{className:"tpl-raw-tab",children:[o.jsx("textarea",{className:"tpl-raw-textarea",value:g,onChange:W=>L(W.target.value),placeholder:"35=D|49=CLIENT|56=VENUE|55=AAPL|54=1|38=100|40=2|44=100.00",spellCheck:!1}),Object.keys(v).length>0&&o.jsxs("div",{className:"tpl-raw-preview",children:[o.jsx("div",{className:"tpl-raw-preview-title",children:"Parsed fields"}),o.jsx("table",{className:"tpl-field-table tpl-preview-table",children:o.jsx("tbody",{children:Object.entries(v).map(([W,re])=>o.jsxs("tr",{children:[o.jsx("td",{className:"tpl-field-tag",children:W}),o.jsx("td",{className:"tpl-field-name",children:rr(Number(W))}),o.jsx("td",{className:"tpl-field-value",style:{color:"var(--text)"},children:re})]},W))})})]})]})]}),A&&o.jsx("div",{className:"tpl-error",children:A}),o.jsxs("div",{className:"modal-footer",children:[o.jsx("button",{className:"btn btn-ghost",onClick:r,children:"Cancel"}),o.jsx("button",{className:"btn btn-primary",onClick:pe,disabled:N,children:N?"Saving…":"Save template"})]})]})})}function dg({onSelect:e,onClose:t,defaultBeginString:n="FIX.4.2"}){const[r,a]=h.useState([]),[i,s]=h.useState(!0),[l,c]=h.useState(!1),[u,f]=h.useState(null);async function d(){s(!0);try{a(await H.templates.list())}finally{s(!1)}}h.useEffect(()=>{d()},[]);async function p(g,b){b.stopPropagation(),confirm(`Delete template "${g.name}"?`)&&(await H.templates.delete(g.id),a(w=>w.filter(_=>_.id!==g.id)))}function m(g,b){b.stopPropagation(),f(g),c(!0)}function v(g){c(!1),f(null),d()}const y=r.reduce((g,b)=>{const w=`${b.msg_type_name} (${b.msg_type})`;return(g[w]??(g[w]=[])).push(b),g},{});return l?o.jsx(lu,{template:u??void 0,defaultBeginString:n,onSaved:v,onClose:()=>{c(!1),f(null)}}):o.jsx("div",{className:"modal-overlay",onClick:t,children:o.jsxs("div",{className:"modal template-picker-modal",onClick:g=>g.stopPropagation(),children:[o.jsxs("div",{className:"modal-header",children:[o.jsx("span",{children:"Templates"}),o.jsx("button",{className:"btn-icon",onClick:t,children:"✕"})]}),o.jsx("div",{className:"template-picker-toolbar",children:o.jsx("button",{className:"btn btn-sm btn-primary",onClick:()=>{f(null),c(!0)},children:"+ New template"})}),o.jsxs("div",{className:"template-picker-body",children:[i&&o.jsx("div",{className:"template-picker-empty",children:"Loading…"}),!i&&r.length===0&&o.jsx("div",{className:"template-picker-empty",children:"No templates yet. Create one to reuse pre-populated FIX messages."}),!i&&Object.entries(y).sort(([g],[b])=>g.localeCompare(b)).map(([g,b])=>o.jsxs("div",{className:"template-group",children:[o.jsx("div",{className:"template-group-title",children:g}),b.map(w=>o.jsxs("div",{className:"template-row",onClick:()=>{e(w),t()},children:[o.jsx("div",{className:"template-row-name",children:w.name}),o.jsxs("div",{className:"template-row-meta",children:[o.jsx("span",{className:"template-begin-string",children:w.begin_string}),o.jsxs("span",{className:"template-field-count",children:[Object.keys(w.fields).length," fields"]})]}),o.jsxs("div",{className:"template-row-actions",onClick:_=>_.stopPropagation(),children:[o.jsx("button",{className:"btn btn-sm btn-ghost",onClick:_=>m(w,_),title:"Edit",children:"✎"}),o.jsx("button",{className:"btn btn-sm btn-danger",onClick:_=>p(w,_),title:"Delete",children:"✕"})]})]},w.id))]},g))]})]})})}function Va(e){try{const t=new Date(e),n=(r,a=2)=>String(r).padStart(a,"0");return`${n(t.getHours())}:${n(t.getMinutes())}:${n(t.getSeconds())}.${n(t.getMilliseconds(),3)}`}catch{return e}}function cu(e){const t=e.includes("")?"":"|";return e.split(t).filter(Boolean).map(n=>{const r=n.indexOf("=");if(r===-1)return null;const a=parseInt(n.slice(0,r),10);return isNaN(a)?null:{tag:a,value:n.slice(r+1)}}).filter(n=>n!==null)}function Of(e){return`fixture_compose_${e}`}const VS=new Set(["8","9","10","34","49","56"]);function Nf(e){return cu(e).map(({tag:t,value:n})=>({tag:String(t),value:n}))}function XS({session:e,raw:t,onRawChange:n,onClose:r}){var T;const a=V(C=>C.user),[i,s]=h.useState(!1),[l,c]=h.useState(null),[u,f]=h.useState(()=>a?ng(a.uid):{...Pa}),[d,p]=h.useState(!1),[m,v]=h.useState(!1),[y,g]=h.useState(!1);h.useEffect(()=>{if(a&&!t){const C=Xe.get(Of(a.uid));C&&n(C)}},[]);function b(C){const I=Object.entries(C.fields).filter(([,L])=>L!=="").map(([L,G])=>`${L}=${G}`).join("|");_(I)}function w(){return Object.fromEntries(Nf(t).map(C=>[C.tag,C.value]))}function _(C){n(C),a&&Xe.set(Of(a.uid),C)}function x(C){f(C),a&&rg(a.uid,C)}const j=t.trim()?Nf(t):[],S=j.filter(C=>!VS.has(C.tag)),P=((T=j.find(C=>C.tag==="35"))==null?void 0:T.value)??"",N=!!P,k=N&&e.status==="LOGGED_ON";async function A(){if(k){s(!0),c(null);try{let C=t;if(u.autoId&&P){const I=ou[P];if(I){const L=`${u.idPrefix}${u.idCounter}`;C=Aa(C,I.tag,L),x({...u,idCounter:u.idCounter+1})}}u.autoTs&&(C=Aa(C,"60",to())),C=ig(C,u),u.evalInline&&(C=lg(C)),C=cg(C,u),await H.sessions.send(e.session_id,C,{noAutoTs:!u.autoTs})}catch(C){c(C.message??"Send failed")}finally{s(!1)}}}return o.jsxs("div",{className:"compose-pane",children:[o.jsx("textarea",{className:"compose-textarea",placeholder:`Paste raw FIX (pipe- or SOH-delimited)
36
+ */var eo=h,iS=aS;function sS(e,t){return e===t&&(e!==0||1/e===1/t)||e!==e&&t!==t}var oS=typeof Object.is=="function"?Object.is:sS,lS=iS.useSyncExternalStore,cS=eo.useRef,uS=eo.useEffect,dS=eo.useMemo,fS=eo.useDebugValue;Wv.useSyncExternalStoreWithSelector=function(e,t,n,r,a){var i=cS(null);if(i.current===null){var s={hasValue:!1,value:null};i.current=s}else s=i.current;i=dS(function(){function c(m){if(!u){if(u=!0,f=m,m=r(m),a!==void 0&&s.hasValue){var v=s.value;if(a(v,m))return d=v}return d=m}if(v=d,oS(f,m))return v;var y=r(m);return a!==void 0&&a(v,y)?(f=m,v):(f=m,d=y)}var u=!1,f,d,p=n===void 0?null:n;return[function(){return c(t())},p===null?void 0:function(){return c(p())}]},[t,n,r,a]);var l=lS(e,i[0],i[1]);return uS(function(){s.hasValue=!0,s.value=l},[l]),fS(l),l};Bv.exports=Wv;var Gv=Bv.exports;const hS=Fn(Gv),Hv={},{useDebugValue:mS}=xv,{useSyncExternalStoreWithSelector:pS}=hS;let ff=!1;const vS=e=>e;function gS(e,t=vS,n){(Hv?"production":void 0)!=="production"&&n&&!ff&&(console.warn("[DEPRECATED] Use `createWithEqualityFn` instead of `create` or use `useStoreWithEqualityFn` instead of `useStore`. They can be imported from 'zustand/traditional'. https://github.com/pmndrs/zustand/discussions/1937"),ff=!0);const r=pS(e.subscribe,e.getState,e.getServerState||e.getInitialState,t,n);return mS(r),r}const hf=e=>{(Hv?"production":void 0)!=="production"&&typeof e!="function"&&console.warn("[DEPRECATED] Passing a vanilla store will be unsupported in a future version. Instead use `import { useStore } from 'zustand'`.");const t=typeof e=="function"?K_(e):e,n=(r,a)=>gS(t,r,a);return Object.assign(n,t),n},yS=e=>e?hf(e):hf,Xe={get(e){try{return localStorage.getItem(e)}catch{return null}},set(e,t){try{localStorage.setItem(e,t)}catch{}},remove(e){try{localStorage.removeItem(e)}catch{}}};let vl=0;const bS=Xe.get("theme")??"dark",xS=Xe.get("auth_token"),wS=(()=>{try{const e=Xe.get("auth_user");return e?JSON.parse(e):null}catch{return null}})(),V=yS(e=>({sessions:{},messages:{},openTabs:[],activeTabId:null,theme:bS,_pendingStatus:{},scenarioRuns:{},scenarioGroupRuns:{},messageRates:{},branding:null,setBranding:t=>e({branding:t}),token:xS,user:wS,login:(t,n)=>{Xe.set("auth_token",t),Xe.set("auth_user",JSON.stringify(n)),e({token:t,user:n})},logout:()=>{Xe.remove("auth_token"),Xe.remove("auth_user"),e({token:null,user:null,sessions:{},messages:{},openTabs:[],activeTabId:null})},setSessions:t=>e(n=>{const r=Object.fromEntries(t.map(a=>[a.session_id,a]));for(const[a,i]of Object.entries(n._pendingStatus))r[a]&&(r[a]={...r[a],...i});return{sessions:r,_pendingStatus:{}}}),upsertSession:t=>e(n=>{const r=n.sessions[t.session_id];return(r==null?void 0:r.status_changed_at)!=null&&t.status_changed_at!=null&&r.status_changed_at>t.status_changed_at?{sessions:{...n.sessions,[t.session_id]:{...t,status:r.status,status_changed_at:r.status_changed_at,last_heartbeat_at:r.last_heartbeat_at}}}:{sessions:{...n.sessions,[t.session_id]:t}}}),removeSession:t=>e(n=>{const{[t]:r,...a}=n.sessions,{[t]:i,...s}=n.messages,l=n.openTabs.filter(u=>u!==t);let c=n.activeTabId;if(c===t){const u=n.openTabs.indexOf(t);c=l[u]??l[u-1]??null}return{sessions:a,messages:s,openTabs:l,activeTabId:c}}),updateSessionStatus:(t,n,r={})=>e(a=>{const i=a.sessions[t];return i?{sessions:{...a.sessions,[t]:{...i,status:n,...r}}}:{_pendingStatus:{...a._pendingStatus,[t]:{status:n,...r}}}}),updateLastHeartbeat:(t,n)=>e(r=>{const a=r.sessions[t];return a?{sessions:{...r.sessions,[t]:{...a,last_heartbeat_at:n}}}:{}}),openTab:t=>e(n=>({openTabs:n.openTabs.includes(t)?n.openTabs:[...n.openTabs,t],activeTabId:t})),closeTab:t=>e(n=>{const r=n.openTabs.filter(i=>i!==t);let a=n.activeTabId;if(a===t){const i=n.openTabs.indexOf(t);a=r[i]??r[i-1]??null}return{openTabs:r,activeTabId:a}}),setActiveTab:t=>e({activeTabId:t}),setMessages:(t,n)=>e(r=>({messages:{...r.messages,[t]:n.map(a=>({...a,_id:++vl}))}})),prependMessages:(t,n)=>e(r=>{const a=r.messages[t]??[],i=n.map(s=>({...s,_id:++vl}));return{messages:{...r.messages,[t]:[...i,...a]}}}),appendMessage:(t,n)=>e(r=>{const a=r.messages[t]??[],i={...n,_id:++vl},s=[...a,i],l=2e3;return{messages:{...r.messages,[t]:s.length>l?s.slice(-l):s}}}),clearMessages:t=>e(n=>({messages:{...n.messages,[t]:[]}})),toggleTheme:()=>e(t=>{const n=t.theme==="dark"?"light":"dark";return Xe.set("theme",n),{theme:n}}),setMessageRate:(t,n)=>e(r=>({messageRates:{...r.messageRates,[t]:{...n,at:Date.now()}}})),upsertScenarioRun:t=>e(n=>({scenarioRuns:{...n.scenarioRuns,[t.run_id]:t}})),updateScenarioStep:(t,n)=>e(r=>{const a=r.scenarioRuns[t];if(!a)return{};const i=[...a.steps],s=i.findIndex(l=>l.step_index===n.step_index);return s>=0?i[s]=n:i.push(n),{scenarioRuns:{...r.scenarioRuns,[t]:{...a,steps:i}}}}),completeScenarioRun:(t,n,r)=>e(a=>{const i=a.scenarioRuns[t];return i?{scenarioRuns:{...a.scenarioRuns,[t]:{...i,status:n,duration_ms:r}}}:{}}),upsertScenarioGroupRun:t=>e(n=>({scenarioGroupRuns:{...n.scenarioGroupRuns,[t.group_run_id]:t}})),completeScenarioGroupRun:(t,n,r)=>e(a=>{const i=a.scenarioGroupRuns[t];return i?{scenarioGroupRuns:{...a.scenarioGroupRuns,[t]:{...i,status:n,duration_ms:r}}}:{}})})),iu="/api";async function X(e,t){const n=V.getState().token,r={"Content-Type":"application/json"};n&&(r.Authorization=`Bearer ${n}`);const a=await fetch(`${iu}${e}`,{headers:r,...t});if(a.status===401)throw V.getState().logout(),window.location.href="/login",new Error("Session expired");if(!a.ok){const s=(await a.json().catch(()=>({detail:a.statusText}))).detail,l=Array.isArray(s)?s.map(c=>{const u=Array.isArray(c.loc)?c.loc.filter(f=>f!=="body").join("."):"";return u?`${u}: ${c.msg}`:c.msg??JSON.stringify(c)}).join("; "):s??a.statusText;throw new Error(l)}if(a.status!==204)return a.json()}const H={sessions:{list:()=>X("/sessions/"),create:e=>X("/sessions/",{method:"POST",body:JSON.stringify(e)}),get:e=>X(`/sessions/${e}`),update:(e,t)=>X(`/sessions/${e}`,{method:"PATCH",body:JSON.stringify(t)}),remove:e=>X(`/sessions/${e}`,{method:"DELETE"}),start:e=>X(`/sessions/${e}/start`,{method:"POST"}),stop:(e,t=!1)=>X(`/sessions/${e}/stop?force=${t}`,{method:"POST"}),send:(e,t,n)=>X(`/sessions/${e}/send`,{method:"POST",body:JSON.stringify({raw:t,no_auto_ts:(n==null?void 0:n.noAutoTs)??!1})}),import:e=>X("/sessions/import",{method:"POST",body:JSON.stringify(e)}),getSeqnums:e=>X(`/sessions/${e}/seqnums`),setSeqnums:(e,t)=>X(`/sessions/${e}/seqnums`,{method:"PATCH",body:JSON.stringify(t)})},messages:{list:(e,t)=>{const n=new URLSearchParams;return t!=null&&t.direction&&n.set("direction",t.direction),t!=null&&t.limit&&n.set("limit",String(t.limit)),(t==null?void 0:t.beforeId)!=null&&n.set("before_id",String(t.beforeId)),X(`/sessions/${e}/messages?${n}`)},search:(e,t)=>{const n=new URLSearchParams;return n.set("q",t.q),t.regex&&n.set("regex","true"),t.limit!=null&&n.set("limit",String(t.limit)),X(`/sessions/${e}/messages/search?${n}`)},clear:e=>X(`/sessions/${e}/messages`,{method:"DELETE"}),export:(e,t)=>mf(`/sessions/${e}/messages/export?fmt=${t}`,`${e}.${t}`)},admin:{users:{list:()=>X("/admin/users"),create:e=>X("/admin/users",{method:"POST",body:JSON.stringify(e)}),update:(e,t)=>X(`/admin/users/${e}`,{method:"PATCH",body:JSON.stringify(t)}),delete:e=>X(`/admin/users/${e}`,{method:"DELETE"})},settings:{get:()=>X("/admin/settings"),update:e=>X("/admin/settings",{method:"PATCH",body:JSON.stringify(e)})},housekeeping:{get:()=>X("/admin/housekeeping"),update:e=>X("/admin/housekeeping",{method:"PATCH",body:JSON.stringify(e)}),runNow:()=>X("/admin/housekeeping/run",{method:"POST"})},branding:{get:()=>X("/admin/branding"),update:e=>X("/admin/branding",{method:"PATCH",body:JSON.stringify(e)})}},fixSpec:{messages:e=>X(`/fix-spec/messages?begin_string=${encodeURIComponent(e)}`),message:(e,t)=>X(`/fix-spec/messages/${encodeURIComponent(t)}?begin_string=${encodeURIComponent(e)}`)},templates:{list:()=>X("/templates"),create:e=>X("/templates",{method:"POST",body:JSON.stringify(e)}),update:(e,t)=>X(`/templates/${e}`,{method:"PUT",body:JSON.stringify(t)}),delete:e=>X(`/templates/${e}`,{method:"DELETE"})},customTags:{list:()=>X("/custom-tags"),upsert:e=>X("/custom-tags",{method:"POST",body:JSON.stringify(e)}),delete:e=>X(`/custom-tags/${e}`,{method:"DELETE"})},specOverlay:{get:()=>X("/spec-overlay"),set:e=>X("/spec-overlay",{method:"PUT",body:JSON.stringify(e)})},scenarios:{list:()=>X("/scenarios"),create:e=>X("/scenarios",{method:"POST",body:JSON.stringify(e)}),update:(e,t)=>X(`/scenarios/${e}`,{method:"PUT",body:JSON.stringify(t)}),delete:e=>X(`/scenarios/${e}`,{method:"DELETE"}),run:e=>X(`/scenarios/${e}/run`,{method:"POST"}),abort:e=>X(`/scenarios/runs/${e}`,{method:"DELETE"}),groups:{list:()=>X("/scenarios/groups"),create:e=>X("/scenarios/groups",{method:"POST",body:JSON.stringify(e)}),update:(e,t)=>X(`/scenarios/groups/${e}`,{method:"PUT",body:JSON.stringify(t)}),delete:e=>X(`/scenarios/groups/${e}`,{method:"DELETE"}),runSaved:e=>X(`/scenarios/groups/${e}/run`,{method:"POST"})},runGroup:e=>X("/scenarios/groups/run",{method:"POST",body:JSON.stringify({scenario_ids:e})}),abortGroup:e=>X(`/scenarios/groups/runs/${e}`,{method:"DELETE"})},perf:{runs:{create:e=>X("/perf/runs",{method:"POST",body:JSON.stringify(e)}),list:()=>X("/perf/runs"),get:e=>X(`/perf/runs/${e}`),stop:e=>X(`/perf/runs/${e}`,{method:"DELETE"}),messages:(e,t)=>{const n=new URLSearchParams;return(t==null?void 0:t.limit)!=null&&n.set("limit",String(t.limit)),(t==null?void 0:t.offset)!=null&&n.set("offset",String(t.offset)),X(`/perf/runs/${e}/messages?${n}`)},scenarios:(e,t)=>{const n=new URLSearchParams;return(t==null?void 0:t.limit)!=null&&n.set("limit",String(t.limit)),(t==null?void 0:t.offset)!=null&&n.set("offset",String(t.offset)),X(`/perf/runs/${e}/scenarios?${n}`)},ticket:e=>X(`/perf/runs/${e}/ticket`,{method:"POST"}),export:(e,t)=>mf(`/perf/runs/${e}/export?kind=${t}`,`${e}_${t}.${t==="both"?"zip":t==="latency"?"csv.gz":"csv"}`),chart:(e,t)=>{const n=new URLSearchParams;return t!=null&&t.metric&&n.set("metric",t.metric),t!=null&&t.agg&&n.set("agg",t.agg),t!=null&&t.scale&&n.set("scale",t.scale),jS(`/perf/runs/${e}/chart?${n}`)}},configs:{list:()=>X("/perf/configs"),get:e=>X(`/perf/configs/${e}`),save:e=>X("/perf/configs",{method:"POST",body:JSON.stringify(e)}),delete:e=>X(`/perf/configs/${e}`,{method:"DELETE"})}}};async function mf(e,t){const n=V.getState().token,r={};n&&(r.Authorization=`Bearer ${n}`);const a=await fetch(`${iu}${e}`,{headers:r});if(a.status===401)throw V.getState().logout(),window.location.href="/login",new Error("Session expired");if(!a.ok)throw new Error(await a.text());const i=await a.blob(),s=URL.createObjectURL(i),l=document.createElement("a");l.href=s,l.download=t,l.click(),URL.revokeObjectURL(s)}async function jS(e){const t=V.getState().token,n={};t&&(n.Authorization=`Bearer ${t}`);const r=await fetch(`${iu}${e}`,{headers:n});if(r.status===401)throw V.getState().logout(),window.location.href="/login",new Error("Session expired");if(!r.ok)throw new Error(await r.text());return URL.createObjectURL(await r.blob())}const _S=Object.freeze(Object.defineProperty({__proto__:null,api:H},Symbol.toStringTag,{value:"Module"})),SS={1:"Account",6:"AvgPx",7:"BeginSeqNo",8:"BeginString",9:"BodyLength",10:"CheckSum",11:"ClOrdID",14:"CumQty",15:"Currency",17:"ExecID",18:"ExecInst",20:"ExecTransType",21:"HandlInst",22:"IDSource",29:"LastCapacity",30:"LastMkt",31:"LastPx",32:"LastShares",34:"MsgSeqNum",35:"MsgType",37:"OrderID",38:"OrderQty",39:"OrdStatus",40:"OrdType",41:"OrigClOrdID",44:"Price",48:"SecurityID",49:"SenderCompID",50:"SenderSubID",52:"SendingTime",54:"Side",55:"Symbol",56:"TargetCompID",57:"TargetSubID",58:"Text",59:"TimeInForce",60:"TransactTime",63:"SettlmntTyp",64:"FutSettDate",76:"ExecBroker",77:"OpenClose",98:"EncryptMethod",99:"StopPx",100:"ExDestination",102:"CxlRejReason",103:"OrdRejReason",108:"HeartBtInt",109:"ClientID",110:"MinQty",111:"MaxFloor",112:"TestReqID",115:"OnBehalfOfCompID",117:"QuoteID",122:"OrigSendingTime",126:"ExpireTime",131:"QuoteReqID",132:"BidPx",133:"OfferPx",134:"BidSize",135:"OfferSize",140:"PrevClosePx",141:"ResetSeqNumFlag",150:"ExecType",151:"LeavesQty",167:"SecurityType",200:"MaturityMonthYear",201:"PutOrCall",202:"StrikePrice",207:"SecurityExchange",210:"MaxShow",369:"LastMsgSeqNumProcessed",371:"RefTagID",372:"RefMsgType",373:"SessionRejectReason",553:"Username",554:"Password"},Ji={};function Zv(e){for(const t of e)Ji[t.number]=t.name}function OS(){for(const e of Object.keys(Ji))delete Ji[Number(e)]}function rr(e){const t=typeof e=="string"?parseInt(e,10):e;return Ji[t]??SS[t]??`Tag${t}`}const pf=3e3,NS=6e4;function su(){const e=h.useRef(null),{updateSessionStatus:t,appendMessage:n,upsertSession:r,updateLastHeartbeat:a,setMessageRate:i,upsertScenarioRun:s,updateScenarioStep:l,completeScenarioRun:c,upsertScenarioGroupRun:u,completeScenarioGroupRun:f}=V();h.useEffect(()=>{let d=!1,p,m=pf;function v(){d||(p=setTimeout(y,m),m=Math.min(m*2,NS))}function y(){const g=V.getState().token;if(!g){p=setTimeout(y,1e3);return}const w=`${window.location.protocol==="https:"?"wss:":"ws:"}//${window.location.host}/ws`,_=new WebSocket(w,["bearer",g]);e.current=_,_.onopen=()=>{m=pf},_.onmessage=x=>{let j;try{j=JSON.parse(x.data)}catch{return}if(j.type!=="ping")if(j.type==="session_status")t(j.session_id,j.status,{last_sent_seq_num:j.last_sent_seq_num,last_recv_seq_num:j.last_recv_seq_num,status_changed_at:j.status_changed_at??void 0,last_heartbeat_at:j.last_heartbeat_at??void 0});else if(j.type==="message")n(j.session_id,{session_id:j.session_id,direction:j.direction,admin:j.admin,seq_num:j.seq_num,msg_type:j.msg_type,msg_type_name:j.msg_type_name,ts:j.ts,fields:j.fields,raw:j.raw}),j.msg_type==="0"&&a(j.session_id,Date.now()/1e3);else if(j.type==="message_agg")i(j.session_id,{rate:j.rate,in:j.in,out:j.out,last_seq:j.last_seq});else if(j.type==="scenario_start")s({run_id:j.run_id,scenario_id:j.scenario_id,scenario_name:j.scenario_name,session_id:j.session_id,group_run_id:j.group_run_id??void 0,status:"running",step_count:j.step_count,steps:[],started_at:Date.now()});else if(j.type==="scenario_step")l(j.run_id,{step_index:j.step_index,label:j.label,status:j.status,error:j.error??void 0,duration_ms:j.duration_ms});else if(j.type==="scenario_complete")c(j.run_id,j.status,j.duration_ms);else if(j.type==="scenario_group_start"){u({group_run_id:j.group_run_id,run_ids:j.runs.map(S=>S.run_id),status:"running",started_at:Date.now()});for(const S of j.runs)s({run_id:S.run_id,scenario_id:S.scenario_id,scenario_name:S.scenario_name,session_id:S.session_id,group_run_id:j.group_run_id,status:"running",step_count:S.step_count,steps:[],started_at:Date.now()})}else j.type==="scenario_group_complete"&&f(j.group_run_id,j.status,j.duration_ms)},_.onclose=()=>{v()},_.onerror=()=>{_.close()}}return y(),()=>{var g;d=!0,clearTimeout(p),(g=e.current)==null||g.close()}},[])}const PS=20,Kv=e=>`host_history_${e}`;function Yv(e){try{const t=Xe.get(Kv(e)),n=t?JSON.parse(t):[];return Array.isArray(n)?n.filter(r=>typeof r=="string"):[]}catch{return[]}}function Vv(e){var a;const t=e.trim(),n=(a=V.getState().user)==null?void 0:a.uid;if(!t||!n)return;const r=[t,...Yv(n).filter(i=>i!==t)].slice(0,PS);Xe.set(Kv(n),JSON.stringify(r))}function Xv(){const e=V.getState(),t=e.user?Yv(e.user.uid):[],n=Object.values(e.sessions).map(r=>r.host).filter(Boolean);return[...new Set([...t,...n])]}const CS={display_name:"",begin_string:"FIX.4.2",sender_comp_id:"",target_comp_id:"",connection_type:"initiator",host:"localhost",port:9876,heartbeat_interval:30,session_role:"generic",auto_start:!0,non_stop_session:!1,durable_seqnums:!0,log_messages:!0,validate_inbound:!1,session_qualifier:"",socket_accept_host:"",logon_timeout:10,logout_timeout:10,check_latency:!1,max_latency:120,requires_orig_sending_time:!0,resend_request_chunk_size:0,send_redundant_resend_requests:!1,enable_last_msg_seq_num_processed:!1,start_time:"00:00:00",end_time:"00:00:00",start_day:"",end_day:"",weekdays:"",time_zone:"",reset_on_logon:!1,reset_on_logout:!1,reset_on_disconnect:!1,validate_fields_have_values:!0,validate_user_defined_fields:!1,validate_fields_out_of_range:!1};function kS({onClose:e,onSubmit:t}){const[n,r]=h.useState(CS),[a,i]=h.useState(""),[s,l]=h.useState(!1),[c,u]=h.useState(!1),f=h.useMemo(()=>Xv(),[]),d=(m,v)=>r(y=>({...y,[m]:v}));async function p(m){if(m.preventDefault(),!n.sender_comp_id||!n.target_comp_id){i("SenderCompID and TargetCompID are required");return}i(""),l(!0);try{await t(n),n.connection_type==="initiator"&&Vv(n.host),e()}catch(v){i(v instanceof Error?v.message:String(v))}finally{l(!1)}}return o.jsx("div",{className:"modal-overlay",onClick:e,children:o.jsxs("div",{className:"modal",onClick:m=>m.stopPropagation(),children:[o.jsxs("div",{className:"modal-header",children:[o.jsx("span",{children:"Add Session"}),o.jsx("button",{className:"btn-icon",onClick:e,children:"✕"})]}),o.jsxs("form",{onSubmit:p,children:[o.jsxs("div",{className:"form-grid",children:[o.jsx("label",{children:"Display Name"}),o.jsx("input",{value:n.display_name,onChange:m=>d("display_name",m.target.value),placeholder:"My Session"}),o.jsx("label",{children:"FIX Version"}),o.jsxs("select",{value:n.begin_string,onChange:m=>d("begin_string",m.target.value),children:[o.jsx("option",{value:"FIX.4.2",children:"FIX 4.2"}),o.jsx("option",{value:"FIX.4.4",children:"FIX 4.4"})]}),o.jsx("label",{children:"Connection Type"}),o.jsxs("select",{value:n.connection_type,onChange:m=>d("connection_type",m.target.value),children:[o.jsx("option",{value:"initiator",children:"Initiator"}),o.jsx("option",{value:"acceptor",children:"Acceptor"})]}),o.jsx("label",{children:"SenderCompID *"}),o.jsx("input",{value:n.sender_comp_id,onChange:m=>d("sender_comp_id",m.target.value),placeholder:"CLIENT",required:!0}),o.jsx("label",{children:"TargetCompID *"}),o.jsx("input",{value:n.target_comp_id,onChange:m=>d("target_comp_id",m.target.value),placeholder:"SERVER",required:!0}),n.connection_type==="initiator"&&o.jsxs(o.Fragment,{children:[o.jsx("label",{children:"Host"}),o.jsx("input",{list:"host-history-add",value:n.host,onChange:m=>d("host",m.target.value)}),o.jsx("datalist",{id:"host-history-add",children:f.map(m=>o.jsx("option",{value:m},m))})]}),o.jsx("label",{children:"Port"}),o.jsx("input",{type:"number",value:n.port,onChange:m=>d("port",Number(m.target.value)),min:1,max:65535}),o.jsx("label",{children:"HeartBtInt (s)"}),o.jsx("input",{type:"number",value:n.heartbeat_interval,onChange:m=>d("heartbeat_interval",Number(m.target.value)),min:1}),o.jsx("label",{children:"Role"}),o.jsxs("select",{value:n.session_role,onChange:m=>d("session_role",m.target.value),children:[o.jsx("option",{value:"client",children:"Client — sends orders"}),o.jsx("option",{value:"venue",children:"Venue — receives orders, sends fills"}),o.jsx("option",{value:"generic",children:"Generic — post-trade, IOIs, etc."})]}),o.jsx("label",{children:"Auto-start"}),o.jsx("input",{type:"checkbox",checked:n.auto_start,onChange:m=>d("auto_start",m.target.checked)}),o.jsx("label",{title:"Disables per-message fsync (durable_seqnums) and the message log — required for high-rate perf runs.",children:"Performance session"}),o.jsx("input",{type:"checkbox",checked:!n.durable_seqnums&&!n.log_messages,onChange:m=>{const v=m.target.checked;r(y=>({...y,durable_seqnums:!v,log_messages:!v}))}}),o.jsx("label",{title:"Validate inbound messages against the FIX spec and reject non-conforming ones (missing required fields, unknown MsgType), like a real counterparty. Off = permissive: repeating groups still decode, but nothing is auto-rejected.",children:"Validate inbound (conformant venue)"}),o.jsx("input",{type:"checkbox",checked:!!n.validate_inbound,onChange:m=>d("validate_inbound",m.target.checked)})]}),n.validate_inbound&&o.jsx("div",{className:"form-hint",children:"Conformant-venue mode: inbound app messages are validated against the FIX spec and non-conforming ones are rejected (Reject, 35=3). Leave off for permissive testing — repeating groups still decode correctly either way."}),!n.durable_seqnums&&!n.log_messages&&o.jsx("div",{className:"form-hint",children:"Performance mode: in-memory seqnums (no fsync) and no message log — fast, but messages won't appear in the session log and sequence numbers reset on restart."}),o.jsx("div",{style:{margin:"8px 0 0"},children:o.jsx("button",{type:"button",className:"btn btn-sm btn-ghost",onClick:()=>u(m=>!m),children:c?"▲ Hide advanced":"▼ Show advanced"})}),c&&o.jsxs("div",{className:"form-grid",style:{marginTop:8},children:[o.jsx("label",{title:"SessionQualifier: disambiguates two sessions sharing the same BeginString/Sender/Target triple.",children:"SessionQualifier"}),o.jsx("input",{value:n.session_qualifier,onChange:m=>d("session_qualifier",m.target.value),placeholder:"(none)"}),n.connection_type==="acceptor"&&o.jsxs(o.Fragment,{children:[o.jsx("label",{title:"SocketAcceptHost: bind address for the acceptor. Empty = 0.0.0.0 (all interfaces).",children:"SocketAcceptHost"}),o.jsx("input",{value:n.socket_accept_host,onChange:m=>d("socket_accept_host",m.target.value),placeholder:"0.0.0.0"})]}),o.jsx("label",{title:"LogonTimeout: seconds to wait for a Logon to complete before disconnecting.",children:"LogonTimeout (s)"}),o.jsx("input",{type:"number",value:n.logon_timeout,onChange:m=>d("logon_timeout",Number(m.target.value)),min:1}),o.jsx("label",{title:"LogoutTimeout: seconds to wait for a Logout confirmation before disconnecting.",children:"LogoutTimeout (s)"}),o.jsx("input",{type:"number",value:n.logout_timeout,onChange:m=>d("logout_timeout",Number(m.target.value)),min:1}),o.jsx("label",{title:"CheckLatency: reject inbound messages whose SendingTime(52) skews more than MaxLatency seconds from the local clock. PossDup resends are exempt.",children:"CheckLatency"}),o.jsx("input",{type:"checkbox",checked:n.check_latency,onChange:m=>d("check_latency",m.target.checked)}),o.jsx("label",{title:"MaxLatency: maximum allowed SendingTime skew in seconds (only when CheckLatency is on).",children:"MaxLatency (s)"}),o.jsx("input",{type:"number",value:n.max_latency,onChange:m=>d("max_latency",Number(m.target.value)),min:1,disabled:!n.check_latency,title:n.check_latency?void 0:"Only applies when CheckLatency is on"}),o.jsx("label",{title:"RequiresOrigSendingTime: reject a resent application message (PossDupFlag=Y) that omits OrigSendingTime(122). Admin gap-fills are never blocked.",children:"RequiresOrigSendingTime"}),o.jsx("input",{type:"checkbox",checked:n.requires_orig_sending_time,onChange:m=>d("requires_orig_sending_time",m.target.checked)}),o.jsx("label",{title:"ResendRequestChunkSize: cap the messages requested per outbound ResendRequest. 0 = unlimited (one open-ended request).",children:"ResendRequestChunkSize"}),o.jsx("input",{type:"number",value:n.resend_request_chunk_size,onChange:m=>d("resend_request_chunk_size",Number(m.target.value)),min:0}),o.jsx("label",{title:"SendRedundantResendRequests: re-send a ResendRequest for every out-of-order message while one is already outstanding.",children:"SendRedundantResendRequests"}),o.jsx("input",{type:"checkbox",checked:n.send_redundant_resend_requests,onChange:m=>d("send_redundant_resend_requests",m.target.checked)}),o.jsx("label",{title:"EnableLastMsgSeqNumProcessed: stamp LastMsgSeqNumProcessed(369) on every outbound message header (the seqnum of the last inbound message processed), so the peer can gauge how far behind we are.",children:"EnableLastMsgSeqNumProcessed"}),o.jsx("input",{type:"checkbox",checked:!!n.enable_last_msg_seq_num_processed,onChange:m=>d("enable_last_msg_seq_num_processed",m.target.checked)}),o.jsx("label",{title:"NonStopSession=Y: run as a single never-ending session and never reset sequence numbers on a day boundary. Off (default) uses the schedule below, which resets seqnums at each new period (e.g. midnight). Mutually exclusive with the schedule keys.",children:"Non-stop session"}),o.jsx("input",{type:"checkbox",checked:!!n.non_stop_session,onChange:m=>d("non_stop_session",m.target.checked)}),o.jsx("label",{children:"StartTime"}),o.jsx("input",{value:n.start_time,onChange:m=>d("start_time",m.target.value),placeholder:"00:00:00",disabled:n.non_stop_session,title:n.non_stop_session?"Ignored while Non-stop session is on":void 0}),o.jsx("label",{children:"EndTime"}),o.jsx("input",{value:n.end_time,onChange:m=>d("end_time",m.target.value),placeholder:"00:00:00",disabled:n.non_stop_session,title:n.non_stop_session?"Ignored while Non-stop session is on":void 0}),o.jsx("label",{title:"TimeZone: IANA name (e.g. America/New_York) interpreting the schedule times/days. Empty = UTC.",children:"TimeZone"}),o.jsx("input",{value:n.time_zone,onChange:m=>d("time_zone",m.target.value),placeholder:"UTC",disabled:n.non_stop_session}),o.jsx("label",{title:"Weekdays: comma list (e.g. Mon,Tue,Wed,Thu,Fri) restricting the daily window to those days. Mutually exclusive with StartDay/EndDay.",children:"Weekdays"}),o.jsx("input",{value:n.weekdays,onChange:m=>d("weekdays",m.target.value),placeholder:"(every day)",disabled:n.non_stop_session||!!n.start_day||!!n.end_day,title:n.start_day||n.end_day?"Clear StartDay/EndDay to use Weekdays":void 0}),o.jsx("label",{title:"StartDay + EndDay (both required) make this a weekly window, e.g. Sunday → Friday. Mutually exclusive with Weekdays.",children:"StartDay"}),o.jsx("input",{value:n.start_day,onChange:m=>d("start_day",m.target.value),placeholder:"(none)",disabled:n.non_stop_session||!!n.weekdays,title:n.weekdays?"Clear Weekdays to use StartDay/EndDay":void 0}),o.jsx("label",{children:"EndDay"}),o.jsx("input",{value:n.end_day,onChange:m=>d("end_day",m.target.value),placeholder:"(none)",disabled:n.non_stop_session||!!n.weekdays,title:n.weekdays?"Clear Weekdays to use StartDay/EndDay":void 0}),o.jsx("label",{children:"ResetOnLogon"}),o.jsx("input",{type:"checkbox",checked:!!n.reset_on_logon,onChange:m=>d("reset_on_logon",m.target.checked),title:"Reset sequence numbers to 1 on each Logon"}),o.jsx("label",{children:"ResetOnLogout"}),o.jsx("input",{type:"checkbox",checked:!!n.reset_on_logout,onChange:m=>d("reset_on_logout",m.target.checked),title:"Reset sequence numbers on Logout"}),o.jsx("label",{title:"ResetOnDisconnect: reset sequence numbers to 1 whenever the connection drops.",children:"ResetOnDisconnect"}),o.jsx("input",{type:"checkbox",checked:!!n.reset_on_disconnect,onChange:m=>d("reset_on_disconnect",m.target.checked)}),o.jsx("label",{title:"ValidateFieldsHaveValues: reject a present field with an empty value (e.g. 55=). Only bites while Validate inbound is on.",children:"↳ Fields have values"}),o.jsx("input",{type:"checkbox",checked:n.validate_fields_have_values,onChange:m=>d("validate_fields_have_values",m.target.checked),disabled:!n.validate_inbound,title:n.validate_inbound?void 0:"Only applies while Validate inbound is on"}),o.jsx("label",{title:"ValidateUserDefinedFields: reject a user-defined field (tag ≥ 5000) not in the loaded dictionary. Only bites while Validate inbound is on.",children:"↳ User-defined fields"}),o.jsx("input",{type:"checkbox",checked:n.validate_user_defined_fields,onChange:m=>d("validate_user_defined_fields",m.target.checked),disabled:!n.validate_inbound,title:n.validate_inbound?void 0:"Only applies while Validate inbound is on"}),o.jsx("label",{title:"ValidateFieldsOutOfRange: reject out-of-range enum values (e.g. Side=Z). Off = unknown enums allowed (QuickFIX/n AllowUnknownEnumValues=Y). Only bites while Validate inbound is on.",children:"↳ Enums in range"}),o.jsx("input",{type:"checkbox",checked:n.validate_fields_out_of_range,onChange:m=>d("validate_fields_out_of_range",m.target.checked),disabled:!n.validate_inbound,title:n.validate_inbound?void 0:"Only applies while Validate inbound is on"})]}),a&&o.jsx("div",{className:"error-msg",children:a}),o.jsxs("div",{className:"modal-footer",children:[o.jsx("button",{type:"button",className:"btn btn-secondary",onClick:e,children:"Cancel"}),o.jsx("button",{type:"submit",className:"btn btn-primary",disabled:s,children:s?"Adding…":"Add Session"})]})]})]})})}const ES="modulepreload",AS=function(e){return"/"+e},vf={},Jv=function(t,n,r){let a=Promise.resolve();if(n&&n.length>0){document.getElementsByTagName("link");const s=document.querySelector("meta[property=csp-nonce]"),l=(s==null?void 0:s.nonce)||(s==null?void 0:s.getAttribute("nonce"));a=Promise.allSettled(n.map(c=>{if(c=AS(c),c in vf)return;vf[c]=!0;const u=c.endsWith(".css"),f=u?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${c}"]${f}`))return;const d=document.createElement("link");if(d.rel=u?"stylesheet":ES,u||(d.as="script"),d.crossOrigin="",d.href=c,l&&d.setAttribute("nonce",l),document.head.appendChild(d),u)return new Promise((p,m)=>{d.addEventListener("load",p),d.addEventListener("error",()=>m(new Error(`Unable to preload CSS for ${c}`)))})}))}function i(s){const l=new Event("vite:preloadError",{cancelable:!0});if(l.payload=s,window.dispatchEvent(l),!l.defaultPrevented)throw s}return a.then(s=>{for(const l of s||[])l.status==="rejected"&&i(l.reason);return t().catch(i)})};function IS({session:e,onClose:t,onSaved:n}){const[r,a]=h.useState({display_name:e.display_name,begin_string:e.begin_string,sender_comp_id:e.sender_comp_id,target_comp_id:e.target_comp_id,connection_type:e.connection_type,host:e.host,port:e.port,heartbeat_interval:e.heartbeat_interval,reconnect_interval:e.reconnect_interval,session_qualifier:e.session_qualifier,socket_accept_host:e.socket_accept_host,logon_timeout:e.logon_timeout,logout_timeout:e.logout_timeout,check_latency:e.check_latency,max_latency:e.max_latency,requires_orig_sending_time:e.requires_orig_sending_time,resend_request_chunk_size:e.resend_request_chunk_size,send_redundant_resend_requests:e.send_redundant_resend_requests,enable_last_msg_seq_num_processed:e.enable_last_msg_seq_num_processed,use_file_log:e.use_file_log,start_time:e.start_time,end_time:e.end_time,start_day:e.start_day,end_day:e.end_day,weekdays:e.weekdays,time_zone:e.time_zone,non_stop_session:e.non_stop_session,reset_on_logon:e.reset_on_logon,reset_on_logout:e.reset_on_logout,reset_on_disconnect:e.reset_on_disconnect,session_role:e.session_role,auto_ack:e.auto_ack,auto_ack_delay_ms:e.auto_ack_delay_ms,auto_fill:e.auto_fill,auto_fill_delay_ms:e.auto_fill_delay_ms,validate_inbound:e.validate_inbound,validate_fields_have_values:e.validate_fields_have_values,validate_user_defined_fields:e.validate_user_defined_fields,validate_fields_out_of_range:e.validate_fields_out_of_range,durable_seqnums:e.durable_seqnums,log_messages:e.log_messages,data_dictionary_path:e.data_dictionary_path??""}),[i,s]=h.useState(""),[l,c]=h.useState(!1),[u,f]=h.useState(!1),d=V(x=>x.user),p=h.useMemo(()=>Xv(),[]),m=(x,j)=>a(S=>({...S,[x]:j})),v=!r.durable_seqnums&&!r.log_messages,y=r.durable_seqnums!==!1&&r.log_messages!==!1,g=e.durable_seqnums===!1&&e.log_messages===!1,b=v!==g,w=x=>x?"on":"off";async function _(x){x.preventDefault(),s(""),c(!0);try{const{api:j}=await Jv(async()=>{const{api:N}=await Promise.resolve().then(()=>_S);return{api:N}},[]),S={...r,data_dictionary_path:r.data_dictionary_path||null},P=await j.sessions.update(e.session_id,S);r.connection_type==="initiator"&&Vv(r.host),n(P),t()}catch(j){s(j instanceof Error?j.message:String(j))}finally{c(!1)}}return o.jsx("div",{className:"modal-overlay",onClick:t,children:o.jsxs("div",{className:"modal",onClick:x=>x.stopPropagation(),children:[o.jsxs("div",{className:"modal-header",children:[o.jsx("span",{children:"Edit Session"}),o.jsx("button",{className:"btn-icon",onClick:t,children:"✕"})]}),o.jsxs("div",{className:`session-type-banner ${v?"perf":""}`,children:[o.jsx("span",{className:"session-type-tag",children:v?"⚡ Performance session":y?"Standard session":"Custom session"}),o.jsxs("span",{className:"session-type-detail",children:["durable seqnums: ",w(r.durable_seqnums)," · message log: ",w(r.log_messages)," · validate inbound: ",w(r.validate_inbound)]})]}),o.jsxs("form",{onSubmit:_,children:[o.jsxs("div",{className:"form-grid",children:[(d==null?void 0:d.role)==="platform_admin"&&e.owner_username&&o.jsxs(o.Fragment,{children:[o.jsx("label",{children:"Owner"}),o.jsx("span",{className:"owner-badge",style:{justifySelf:"start"},children:e.owner_username})]}),o.jsx("label",{children:"Display Name"}),o.jsx("input",{value:r.display_name,onChange:x=>m("display_name",x.target.value),placeholder:"My Session"}),o.jsx("label",{children:"FIX Version"}),o.jsxs("select",{value:r.begin_string,onChange:x=>m("begin_string",x.target.value),children:[o.jsx("option",{value:"FIX.4.2",children:"FIX 4.2"}),o.jsx("option",{value:"FIX.4.4",children:"FIX 4.4"})]}),o.jsx("label",{children:"Connection Type"}),o.jsxs("select",{value:r.connection_type,onChange:x=>m("connection_type",x.target.value),children:[o.jsx("option",{value:"initiator",children:"Initiator"}),o.jsx("option",{value:"acceptor",children:"Acceptor"})]}),o.jsx("label",{children:"SenderCompID *"}),o.jsx("input",{value:r.sender_comp_id,onChange:x=>m("sender_comp_id",x.target.value),required:!0}),o.jsx("label",{children:"TargetCompID *"}),o.jsx("input",{value:r.target_comp_id,onChange:x=>m("target_comp_id",x.target.value),required:!0}),r.connection_type==="initiator"&&o.jsxs(o.Fragment,{children:[o.jsx("label",{children:"Host"}),o.jsx("input",{list:"host-history-edit",value:r.host,onChange:x=>m("host",x.target.value)}),o.jsx("datalist",{id:"host-history-edit",children:p.map(x=>o.jsx("option",{value:x},x))})]}),o.jsx("label",{children:"Port"}),o.jsx("input",{type:"number",value:r.port,onChange:x=>m("port",Number(x.target.value)),min:1,max:65535}),o.jsx("label",{children:"HeartBtInt (s)"}),o.jsx("input",{type:"number",value:r.heartbeat_interval,onChange:x=>m("heartbeat_interval",Number(x.target.value)),min:1}),o.jsx("label",{children:"Role"}),o.jsxs("select",{value:r.session_role,onChange:x=>m("session_role",x.target.value),children:[o.jsx("option",{value:"client",children:"Client — sends orders"}),o.jsx("option",{value:"venue",children:"Venue — receives orders, sends fills"}),o.jsx("option",{value:"generic",children:"Generic — post-trade, IOIs, etc."})]}),o.jsx("label",{title:"Disables per-message fsync (durable_seqnums) and the message log — required for high-rate perf runs. Toggling converts the session in place.",children:"Performance session"}),o.jsx("input",{type:"checkbox",checked:v,onChange:x=>{const j=x.target.checked;a(S=>({...S,durable_seqnums:!j,log_messages:!j,...j?{auto_ack:!1,auto_fill:!1}:{}}))}})]}),b&&o.jsx("div",{className:"form-hint",children:v?"Converting to a performance session: messages stay in memory only (last 10,000 viewable; nothing persisted) and sequence numbers reset — they no longer survive a restart.":"Converting to a standard session: messages persist to the database again (earlier persisted history reappears; the in-memory log from perf mode is not carried over) and sequence numbers reset once on conversion."}),r.session_role==="venue"&&o.jsxs("div",{className:"venue-auto-section",children:[o.jsx("div",{className:"venue-auto-title",children:"Auto-responses (venue)"}),v&&o.jsx("div",{className:"form-hint",children:"Auto-responses are disabled for performance sessions — the perf engine generates its own acks/fills."}),o.jsxs("div",{className:"form-grid",children:[o.jsx("label",{children:"Auto-ack"}),o.jsxs("div",{className:"venue-auto-row",children:[o.jsx("input",{type:"checkbox",checked:r.auto_ack,onChange:x=>m("auto_ack",x.target.checked),disabled:v,title:"Send Execution Report (OrdStatus=New) on each inbound New Order Single"}),o.jsx("span",{className:"venue-auto-label",children:"delay"}),o.jsx("input",{type:"number",className:"venue-delay-input",value:r.auto_ack_delay_ms,onChange:x=>m("auto_ack_delay_ms",Number(x.target.value)),min:0,disabled:v||!r.auto_ack}),o.jsx("span",{className:"venue-auto-label",children:"ms"})]}),o.jsx("label",{children:"Auto-fill"}),o.jsxs("div",{className:"venue-auto-row",children:[o.jsx("input",{type:"checkbox",checked:r.auto_fill,onChange:x=>m("auto_fill",x.target.checked),disabled:v,title:"Send Execution Report (OrdStatus=Filled) on each inbound New Order Single"}),o.jsx("span",{className:"venue-auto-label",children:"delay"}),o.jsx("input",{type:"number",className:"venue-delay-input",value:r.auto_fill_delay_ms,onChange:x=>m("auto_fill_delay_ms",Number(x.target.value)),min:0,disabled:v||!r.auto_fill}),o.jsx("span",{className:"venue-auto-label",children:"ms"})]})]})]}),o.jsx("div",{style:{margin:"8px 0 0"},children:o.jsx("button",{type:"button",className:"btn btn-sm btn-ghost",onClick:()=>f(x=>!x),children:u?"▲ Hide advanced":"▼ Show advanced"})}),u&&o.jsxs("div",{className:"form-grid",style:{marginTop:8},children:[o.jsx("label",{title:"SessionQualifier: disambiguates two sessions sharing the same BeginString/Sender/Target triple.",children:"SessionQualifier"}),o.jsx("input",{value:r.session_qualifier,onChange:x=>m("session_qualifier",x.target.value),placeholder:"(none)"}),o.jsx("label",{children:"ReconnectInterval (s)"}),o.jsx("input",{type:"number",value:r.reconnect_interval,onChange:x=>m("reconnect_interval",Number(x.target.value)),min:1}),r.connection_type==="acceptor"&&o.jsxs(o.Fragment,{children:[o.jsx("label",{title:"SocketAcceptHost: bind address for the acceptor. Empty = 0.0.0.0 (all interfaces).",children:"SocketAcceptHost"}),o.jsx("input",{value:r.socket_accept_host,onChange:x=>m("socket_accept_host",x.target.value),placeholder:"0.0.0.0"})]}),o.jsx("label",{title:"LogonTimeout: seconds to wait for a Logon to complete before disconnecting.",children:"LogonTimeout (s)"}),o.jsx("input",{type:"number",value:r.logon_timeout,onChange:x=>m("logon_timeout",Number(x.target.value)),min:1}),o.jsx("label",{title:"LogoutTimeout: seconds to wait for a Logout confirmation before disconnecting.",children:"LogoutTimeout (s)"}),o.jsx("input",{type:"number",value:r.logout_timeout,onChange:x=>m("logout_timeout",Number(x.target.value)),min:1}),o.jsx("label",{title:"CheckLatency: reject inbound messages whose SendingTime(52) skews more than MaxLatency seconds from the local clock. PossDup resends are exempt.",children:"CheckLatency"}),o.jsx("input",{type:"checkbox",checked:r.check_latency,onChange:x=>m("check_latency",x.target.checked)}),o.jsx("label",{title:"MaxLatency: maximum allowed SendingTime skew in seconds (only when CheckLatency is on).",children:"MaxLatency (s)"}),o.jsx("input",{type:"number",value:r.max_latency,onChange:x=>m("max_latency",Number(x.target.value)),min:1,disabled:!r.check_latency,title:r.check_latency?void 0:"Only applies when CheckLatency is on"}),o.jsx("label",{title:"RequiresOrigSendingTime: reject a resent application message (PossDupFlag=Y) that omits OrigSendingTime(122). Admin gap-fills are never blocked.",children:"RequiresOrigSendingTime"}),o.jsx("input",{type:"checkbox",checked:r.requires_orig_sending_time,onChange:x=>m("requires_orig_sending_time",x.target.checked)}),o.jsx("label",{title:"ResendRequestChunkSize: cap the messages requested per outbound ResendRequest. 0 = unlimited (one open-ended request).",children:"ResendRequestChunkSize"}),o.jsx("input",{type:"number",value:r.resend_request_chunk_size,onChange:x=>m("resend_request_chunk_size",Number(x.target.value)),min:0}),o.jsx("label",{title:"SendRedundantResendRequests: re-send a ResendRequest for every out-of-order message while one is already outstanding. Off = re-request only when the gap advances.",children:"SendRedundantResendRequests"}),o.jsx("input",{type:"checkbox",checked:r.send_redundant_resend_requests,onChange:x=>m("send_redundant_resend_requests",x.target.checked)}),o.jsx("label",{title:"EnableLastMsgSeqNumProcessed: stamp LastMsgSeqNumProcessed(369) on every outbound message header (the seqnum of the last inbound message processed), so the peer can gauge how far behind we are.",children:"EnableLastMsgSeqNumProcessed"}),o.jsx("input",{type:"checkbox",checked:!!r.enable_last_msg_seq_num_processed,onChange:x=>m("enable_last_msg_seq_num_processed",x.target.checked)}),o.jsx("label",{title:"NonStopSession=Y: run as a single never-ending session and never reset sequence numbers on a day boundary. Off (default) uses the schedule below, which resets seqnums at each new period (e.g. midnight). Mutually exclusive with the schedule keys.",children:"Non-stop session"}),o.jsx("input",{type:"checkbox",checked:!!r.non_stop_session,onChange:x=>m("non_stop_session",x.target.checked)}),o.jsx("label",{children:"StartTime"}),o.jsx("input",{value:r.start_time,onChange:x=>m("start_time",x.target.value),placeholder:"00:00:00",disabled:r.non_stop_session,title:r.non_stop_session?"Ignored while Non-stop session is on":void 0}),o.jsx("label",{children:"EndTime"}),o.jsx("input",{value:r.end_time,onChange:x=>m("end_time",x.target.value),placeholder:"00:00:00",disabled:r.non_stop_session,title:r.non_stop_session?"Ignored while Non-stop session is on":void 0}),o.jsx("label",{title:"TimeZone: IANA name (e.g. America/New_York) interpreting the schedule times/days. Empty = UTC.",children:"TimeZone"}),o.jsx("input",{value:r.time_zone,onChange:x=>m("time_zone",x.target.value),placeholder:"UTC",disabled:r.non_stop_session}),o.jsx("label",{title:"Weekdays: comma list (e.g. Mon,Tue,Wed,Thu,Fri) restricting the daily window to those days. Mutually exclusive with StartDay/EndDay.",children:"Weekdays"}),o.jsx("input",{value:r.weekdays,onChange:x=>m("weekdays",x.target.value),placeholder:"(every day)",disabled:r.non_stop_session||!!r.start_day||!!r.end_day,title:r.start_day||r.end_day?"Clear StartDay/EndDay to use Weekdays":void 0}),o.jsx("label",{title:"StartDay + EndDay (both required) make this a weekly window, e.g. Sunday → Friday. Mutually exclusive with Weekdays.",children:"StartDay"}),o.jsx("input",{value:r.start_day,onChange:x=>m("start_day",x.target.value),placeholder:"(none)",disabled:r.non_stop_session||!!r.weekdays,title:r.weekdays?"Clear Weekdays to use StartDay/EndDay":void 0}),o.jsx("label",{children:"EndDay"}),o.jsx("input",{value:r.end_day,onChange:x=>m("end_day",x.target.value),placeholder:"(none)",disabled:r.non_stop_session||!!r.weekdays,title:r.weekdays?"Clear Weekdays to use StartDay/EndDay":void 0}),o.jsx("label",{children:"ResetOnLogon"}),o.jsx("input",{type:"checkbox",checked:r.reset_on_logon,onChange:x=>m("reset_on_logon",x.target.checked),title:"Reset sequence numbers to 1 on each Logon"}),o.jsx("label",{children:"ResetOnLogout"}),o.jsx("input",{type:"checkbox",checked:r.reset_on_logout,onChange:x=>m("reset_on_logout",x.target.checked),title:"Reset sequence numbers on Logout"}),o.jsx("label",{title:"ResetOnDisconnect: reset sequence numbers to 1 whenever the connection drops.",children:"ResetOnDisconnect"}),o.jsx("input",{type:"checkbox",checked:r.reset_on_disconnect,onChange:x=>m("reset_on_disconnect",x.target.checked)}),o.jsx("label",{children:"Data Dictionary"}),o.jsx("input",{value:r.data_dictionary_path,onChange:x=>m("data_dictionary_path",x.target.value),placeholder:"(use bundled)"}),o.jsx("label",{children:"File Log"}),o.jsx("input",{type:"checkbox",checked:r.use_file_log,onChange:x=>m("use_file_log",x.target.checked)}),o.jsx("label",{title:"Validate inbound messages against the FIX spec and reject non-conforming ones (missing required fields, unknown MsgType), like a real counterparty. Off = permissive: repeating groups still decode, but nothing is auto-rejected. Takes effect on next start.",children:"Validate inbound"}),o.jsx("input",{type:"checkbox",checked:r.validate_inbound,onChange:x=>m("validate_inbound",x.target.checked),title:"Conformant-venue mode — applies on next session start"}),o.jsx("label",{title:"ValidateFieldsHaveValues: reject a present field with an empty value (e.g. 55=). Only bites while Validate inbound is on.",children:"↳ Fields have values"}),o.jsx("input",{type:"checkbox",checked:r.validate_fields_have_values,onChange:x=>m("validate_fields_have_values",x.target.checked),disabled:!r.validate_inbound,title:r.validate_inbound?void 0:"Only applies while Validate inbound is on"}),o.jsx("label",{title:"ValidateUserDefinedFields: reject a user-defined field (tag ≥ 5000) not in the loaded dictionary. Only bites while Validate inbound is on.",children:"↳ User-defined fields"}),o.jsx("input",{type:"checkbox",checked:r.validate_user_defined_fields,onChange:x=>m("validate_user_defined_fields",x.target.checked),disabled:!r.validate_inbound,title:r.validate_inbound?void 0:"Only applies while Validate inbound is on"}),o.jsx("label",{title:"ValidateFieldsOutOfRange: reject out-of-range enum values (e.g. Side=Z). Off = unknown enums allowed (QuickFIX/n AllowUnknownEnumValues=Y). Only bites while Validate inbound is on.",children:"↳ Enums in range"}),o.jsx("input",{type:"checkbox",checked:r.validate_fields_out_of_range,onChange:x=>m("validate_fields_out_of_range",x.target.checked),disabled:!r.validate_inbound,title:r.validate_inbound?void 0:"Only applies while Validate inbound is on"})]}),i&&o.jsx("div",{className:"error-msg",children:i}),o.jsxs("div",{className:"modal-footer",children:[o.jsx("button",{type:"button",className:"btn btn-secondary",onClick:t,children:"Cancel"}),o.jsx("button",{type:"submit",className:"btn btn-primary",disabled:l,children:l?"Saving…":"Save"})]})]})]})})}function TS({session:e,onClose:t,onSaved:n}){const[r,a]=h.useState(""),[i,s]=h.useState(""),[l,c]=h.useState(!0),[u,f]=h.useState(!1),[d,p]=h.useState(null),m=!["STOPPED","ERROR"].includes(e.status),v=e.display_name||`${e.sender_comp_id}→${e.target_comp_id}`;h.useEffect(()=>{H.sessions.getSeqnums(e.session_id).then(b=>{a(String(b.sender)),s(String(b.target)),c(!1)}).catch(b=>{p(b.message),c(!1)})},[e.session_id]);async function y(){const b=parseInt(r,10),w=parseInt(i,10);if(isNaN(b)||isNaN(w)||b<1||w<1){p("Sequence numbers must be ≥ 1");return}f(!0),p(null);try{await H.sessions.setSeqnums(e.session_id,{sender:b,target:w}),n(),t()}catch(_){p(_.message??"Failed to set seqnums")}finally{f(!1)}}function g(){a("1"),s("1")}return o.jsx("div",{className:"modal-overlay",onClick:t,children:o.jsxs("div",{className:"modal",style:{width:340},onClick:b=>b.stopPropagation(),children:[o.jsxs("div",{className:"modal-header",children:[o.jsxs("span",{children:["Sequence Numbers — ",v]}),o.jsx("button",{className:"btn btn-ghost btn-sm",onClick:t,children:"✕"})]}),o.jsxs("div",{className:"modal-body",children:[m&&o.jsx("div",{className:"seqnum-warning",children:"⚠ Session is running — changing seqnums will trigger a sequence reset with the counterparty."}),l?o.jsx("div",{style:{color:"var(--text2)",padding:"12px 0"},children:"Loading…"}):o.jsxs("div",{className:"form-grid",style:{"--label-w":"140px"},children:[o.jsx("label",{children:"Next TX (outgoing)"}),o.jsx("input",{type:"number",min:1,value:r,onChange:b=>a(b.target.value),disabled:u}),o.jsx("label",{children:"Next RX (incoming)"}),o.jsx("input",{type:"number",min:1,value:i,onChange:b=>s(b.target.value),disabled:u})]}),d&&o.jsx("div",{className:"error-msg",children:d})]}),o.jsxs("div",{className:"modal-footer",children:[o.jsx("button",{className:"btn btn-sm btn-ghost",onClick:g,disabled:l||u,children:"Reset 1/1"}),o.jsx("div",{style:{flex:1}}),o.jsx("button",{className:"btn btn-sm btn-secondary",onClick:t,children:"Cancel"}),o.jsx("button",{className:"btn btn-sm btn-primary",onClick:y,disabled:l||u,children:u?"Applying…":"Apply"})]})]})})}const DS=["display_name","begin_string","sender_comp_id","target_comp_id","connection_type","host","port","heartbeat_interval","reconnect_interval","use_file_log","start_time","end_time","reset_on_logon","reset_on_logout","session_role","data_dictionary_path"];function Qv(e){return Object.fromEntries(DS.map(t=>[t,e[t]]))}function eg(e,t){const n=new Blob([JSON.stringify(e,null,2)],{type:"application/json"}),r=URL.createObjectURL(n),a=document.createElement("a");a.href=r,a.download=t,a.click(),URL.revokeObjectURL(r)}const MS={LOGGED_ON:"#4ade80",CONNECTING:"#facc15",LISTENING:"#facc15",LOGGED_OUT:"#94a3b8",STOPPED:"#94a3b8",ERROR:"#f87171"};function RS({status:e}){return o.jsxs("span",{className:"status-badge",style:{color:MS[e]??"#94a3b8"},children:["● ",e]})}function gf(e){const t=Math.floor(e/60),n=e%60;return t>0?`${t}m ${n}s`:`${n}s`}function LS({session:e}){const[,t]=h.useState(0),n=e.status==="LOGGED_ON",r=e.connection_type==="initiator"&&["CONNECTING","LOGGED_OUT"].includes(e.status);h.useEffect(()=>{if(!n&&!r)return;const i=setInterval(()=>t(s=>s+1),1e3);return()=>clearInterval(i)},[n,r]);const a=Date.now()/1e3;if(n&&e.status_changed_at){const i=Math.floor(a-e.status_changed_at);let s=null;if(e.last_heartbeat_at){const l=Math.floor(a-e.last_heartbeat_at),c=l<2*e.heartbeat_interval?"var(--green)":l<3*e.heartbeat_interval?"var(--yellow)":"var(--red)";s=o.jsxs("span",{style:{color:c},children:["· HB ",gf(l)," ago"]})}return o.jsxs("div",{className:"session-status-details",children:[o.jsxs("span",{children:["Connected ",gf(i)]}),s]})}if(r&&e.status_changed_at){const i=a-e.status_changed_at,s=Math.ceil(e.reconnect_interval-i%e.reconnect_interval);return o.jsx("div",{className:"session-status-details",children:o.jsxs("span",{style:{color:"var(--text2)"},children:["Reconnecting in ",s,"s"]})})}return null}function $S({session:e,active:t,onClick:n,showOwner:r}){const{upsertSession:a,removeSession:i}=V(),[s,l]=h.useState(!1),[c,u]=h.useState(!1),[f,d]=h.useState(!1),p=!["STOPPED","ERROR"].includes(e.status);async function m(){l(!0);try{const g=p?await H.sessions.stop(e.session_id):await H.sessions.start(e.session_id);a(g)}catch(g){console.error(g)}finally{l(!1)}}async function v(){confirm(`Remove session "${e.display_name||e.sender_comp_id}"?`)&&(await H.sessions.remove(e.session_id),i(e.session_id))}const y=e.display_name||`${e.sender_comp_id}→${e.target_comp_id}`;return o.jsxs("div",{className:`session-card${t?" active":""}`,onClick:n,children:[o.jsx("div",{className:"session-card-name",children:y}),o.jsxs("div",{className:"session-card-meta",children:[e.begin_string," · ",e.connection_type,o.jsx("span",{className:`role-badge role-${e.session_role}`,children:e.session_role}),r&&e.owner_username&&o.jsx("span",{className:"owner-badge",children:e.owner_username})]}),o.jsx(RS,{status:e.status}),o.jsx(LS,{session:e}),o.jsxs("div",{className:"session-card-seqnums",children:[o.jsxs("span",{children:["↑",e.last_sent_seq_num]}),o.jsxs("span",{children:["↓",e.last_recv_seq_num]}),o.jsxs("span",{children:[e.message_count," msgs"]})]}),o.jsxs("div",{className:"session-card-actions",onClick:g=>g.stopPropagation(),children:[o.jsx("button",{className:`btn btn-sm ${p?"btn-stop":"btn-start"}`,onClick:m,disabled:s,children:p?"Stop":"Start"}),o.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>u(!0),disabled:p,title:p?"Stop session to edit":"Edit config",children:"Edit"}),o.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>d(!0),title:"Set sequence numbers",children:"Seq"}),o.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>eg([Qv(e)],`${y}.json`),title:"Export this session",children:"⬇"}),o.jsx("button",{className:"btn btn-sm btn-danger",onClick:v,children:"✕"})]}),c&&o.jsx(IS,{session:e,onClose:()=>u(!1),onSaved:g=>{a(g),u(!1)}}),f&&o.jsx(TS,{session:e,onClose:()=>d(!1),onSaved:async()=>{const g=await H.sessions.get(e.session_id);a(g)}})]})}const yf="sessions_mine_only";function FS(){const{sessions:e,activeTabId:t,openTab:n,upsertSession:r,user:a}=V(),[i,s]=h.useState(!1),[l,c]=h.useState(""),[u,f]=h.useState(""),[d,p]=h.useState(()=>Xe.get(yf)==="1"),m=h.useRef(null),v=(a==null?void 0:a.role)==="platform_admin";function y(j){Xe.set(yf,j?"1":"0"),p(j)}async function g(j){const S=await H.sessions.create({...j,auto_start:j.auto_start});r(S),n(S.session_id)}function b(){const j=Object.values(e);if(j.length===0)return;const S=new Date().toISOString().slice(0,10);eg(j.map(Qv),`fixture-sessions-${S}.json`)}async function w(j){var P;const S=(P=j.target.files)==null?void 0:P[0];if(m.current&&(m.current.value="",!!S)){c(""),f("");try{const N=await S.text();let k=JSON.parse(N);Array.isArray(k)||(k=[k]);const A=await H.sessions.import(k);A.length===0?c("No sessions imported — file may be empty or all entries conflicted."):(A.forEach(T=>{r(T)}),f(`Imported ${A.length} of ${k.length} session${k.length!==1?"s":""}.`),setTimeout(()=>f(""),4e3))}catch(N){c(N instanceof Error?N.message:"Import failed")}}}const _=Object.values(e),x=v&&d?_.filter(j=>j.owner_username===(a==null?void 0:a.username)):_;return o.jsxs("div",{className:"sessions-panel",children:[o.jsxs("div",{className:"sessions-panel-header",children:[o.jsx("span",{children:"Sessions"}),o.jsxs("div",{className:"sessions-panel-header-actions",children:[o.jsx("button",{className:"btn btn-ghost btn-sm",onClick:b,disabled:_.length===0,title:"Export all sessions to JSON",children:"⬇ Export"}),o.jsx("button",{className:"btn btn-ghost btn-sm",onClick:()=>{var j;return(j=m.current)==null?void 0:j.click()},title:"Import sessions from JSON",children:"⬆ Import"}),o.jsx("input",{ref:m,type:"file",accept:".json,application/json",style:{display:"none"},onChange:w}),o.jsx("button",{className:"btn btn-primary btn-sm",onClick:()=>s(!0),children:"+ Add"})]})]}),v&&o.jsxs("div",{className:"sessions-filter-row",children:[o.jsx("button",{className:`btn btn-sm ${d?"btn-ghost":"btn-active"}`,onClick:()=>y(!1),title:"Show all users’ sessions",children:"All"}),o.jsx("button",{className:`btn btn-sm ${d?"btn-active":"btn-ghost"}`,onClick:()=>y(!0),title:"Show only your sessions",children:"Mine"})]}),l&&o.jsx("div",{className:"import-feedback import-error",children:l}),u&&o.jsx("div",{className:"import-feedback import-ok",children:u}),o.jsxs("div",{className:"sessions-list",children:[x.length===0&&o.jsx("div",{className:"sessions-empty",children:_.length>0?'No sessions of yours. Switch to "All" to see other users’ sessions.':"No sessions. Add one to get started."}),x.map(j=>o.jsx($S,{session:j,active:j.session_id===t,onClick:()=>n(j.session_id),showOwner:(a==null?void 0:a.role)==="platform_admin"},j.session_id))]}),i&&o.jsx(kS,{onClose:()=>s(!1),onSubmit:g})]})}const ou={D:{tag:"11",name:"ClOrdID"},F:{tag:"11",name:"ClOrdID"},G:{tag:"11",name:"ClOrdID"},AB:{tag:"11",name:"ClOrdID"},V:{tag:"262",name:"MDReqID"},R:{tag:"131",name:"QuoteReqID"},S:{tag:"117",name:"QuoteID"},x:{tag:"320",name:"SecurityReqID"}},Pa={autoTs:!0,autoId:!0,idPrefix:"",idCounter:1,autoExpiry:!0,expiryOffset:1,expiryUnit:"days",evalInline:!0,genRules:[]};function tg(e){return`fixture_send_opts_${e}`}function ng(e){try{const t=JSON.parse(Xe.get(tg(e))??"null");return t?{...Pa,...t}:{...Pa}}catch{return{...Pa}}}function rg(e,t){Xe.set(tg(e),JSON.stringify(t))}function bf(e){return e.replace(/\x01/g,"|").replace(/\|$/,"")}function Aa(e,t,n){const r=new RegExp(`(^|\\|)(${t}=[^|]*)`);return r.test(e)?e.replace(r,`$1${t}=${n}`):e?`${e}|${t}=${n}`:`${t}=${n}`}function xf(e,t,n){const r=new RegExp(`(^|\\|)(${t}=[^|]*)`);return r.test(e)?e.replace(r,`$1${t}=${n}`):e}function to(e=new Date){const t=e.toISOString();return t.slice(0,10).replace(/-/g,"")+"-"+t.slice(11,19)}function ag(e=new Date){return e.toISOString().slice(0,10).replace(/-/g,"")}const zS={minutes:6e4,hours:36e5,days:864e5};function BS(e,t){return new Date(Date.now()+e*zS[t])}function ig(e,t){if(!t.autoExpiry)return e;const n=BS(t.expiryOffset,t.expiryUnit);return e=xf(e,"126",to(n)),e=xf(e,"432",ag(n)),e}const Vl="uuid() · random_str(n) · random_int(a,b) · seq(width[,start]) · timestamp() · date() — mix with text as {gen()}, e.g. ORD-{seq(8)}",wf="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";function sg(e){const t=new Uint8Array(e);return crypto.getRandomValues(t),t}function WS(){const e=sg(16);e[6]=e[6]&15|64,e[8]=e[8]&63|128;const t=[...e].map(n=>n.toString(16).padStart(2,"0"));return`${t.slice(0,4).join("")}-${t.slice(4,6).join("")}-${t.slice(6,8).join("")}-${t.slice(8,10).join("")}-${t.slice(10,16).join("")}`}function US(e){const t=Number.isFinite(e)&&e>0?Math.min(Math.floor(e),256):8;return[...sg(t)].map(n=>wf[n%wf.length]).join("")}function qS(e,t){const n=Math.ceil(Math.min(e,t)),r=Math.floor(Math.max(e,t));return n+Math.floor(Math.random()*(r-n+1))}const GS=/^(\w+)\(([^)]*)\)$/,HS=/\{(\w+)\(([^)]*)\)\}/g,jf=new Map;function _f(e,t,n){const r=t.trim()?t.split(",").map(a=>a.trim()):[];switch(e){case"uuid":return WS();case"random_str":return US(r[0]!==void 0?Number(r[0]):8);case"random_int":{const a=Number(r[0]),i=Number(r[1]);return!Number.isFinite(a)||!Number.isFinite(i)?null:String(qS(a,i))}case"seq":{const a=r[0]!==void 0?Math.floor(Number(r[0])):0,i=r[1]!==void 0?Math.floor(Number(r[1])):1,s=jf.get(n)??(Number.isFinite(i)?i:1);jf.set(n,s+1);const l=String(s);return Number.isFinite(a)&&a>0?l.padStart(a,"0"):l}case"timestamp":return to();case"date":return ag();default:return null}}function og(e,t=""){const n=e.trim().match(GS);return n?_f(n[1],n[2],t)??e:e.replace(HS,(r,a,i)=>_f(a,i,t)??r)}function lg(e){const t=e.includes("")?"":"|";return e.split(t).map(n=>{const r=n.indexOf("=");if(r<0)return n;const a=n.slice(0,r),i=n.slice(r+1),s=og(i,a);return s===i?n:`${a}=${s}`}).join(t)}function cg(e,t){for(const n of t.genRules){const r=n.tag.trim();r&&(e=Aa(e,r,og(n.value,r)))}return e}function ug({opts:e,onChange:t,expanded:n,onToggle:r,msgType:a}){const i=a?ou[a]:void 0;function s(d){t({...e,...d})}const l=`${e.idPrefix}${e.idCounter}`;function c(d,p){s({genRules:e.genRules.map((m,v)=>v===d?{...m,...p}:m)})}function u(){s({genRules:[...e.genRules,{tag:"",value:""}]})}function f(d){s({genRules:e.genRules.filter((p,m)=>m!==d)})}return o.jsxs("div",{className:"send-opts",children:[o.jsxs("button",{type:"button",className:"btn btn-sm btn-ghost send-opts-toggle",onClick:r,children:[n?"▲":"▶"," Send Options"]}),n&&o.jsxs("div",{className:"send-opts-body",children:[o.jsxs("label",{className:"checkbox-label",children:[o.jsx("input",{type:"checkbox",checked:e.autoTs,onChange:d=>s({autoTs:d.target.checked})}),"Auto TransactTime (tag 60)"]}),o.jsxs("label",{className:"checkbox-label",children:[o.jsx("input",{type:"checkbox",checked:e.autoId,onChange:d=>s({autoId:d.target.checked})}),i?`Auto-generate ${i.name} (tag ${i.tag})`:a?`Auto-generate ID — no mapping for type ${a}`:"Auto-generate ID tag"]}),e.autoId&&o.jsxs("div",{className:"send-opts-id-row",children:[o.jsx("span",{className:"send-opts-label",children:"Prefix"}),o.jsx("input",{className:"send-opts-prefix",value:e.idPrefix,onChange:d=>s({idPrefix:d.target.value}),placeholder:"e.g. ORD",maxLength:20}),o.jsxs("span",{className:"send-opts-next",children:["Next: ",o.jsx("code",{children:l||e.idCounter})]})]}),o.jsxs("label",{className:"checkbox-label",children:[o.jsx("input",{type:"checkbox",checked:e.autoExpiry,onChange:d=>s({autoExpiry:d.target.checked})}),"Auto expiry (tags 126 / 432, only if present)"]}),e.autoExpiry&&o.jsxs("div",{className:"send-opts-id-row",children:[o.jsx("span",{className:"send-opts-label",children:"Future offset"}),o.jsx("input",{className:"send-opts-prefix",type:"number",min:0,style:{width:64},value:e.expiryOffset,onChange:d=>s({expiryOffset:Number(d.target.value)})}),o.jsxs("select",{value:e.expiryUnit,onChange:d=>s({expiryUnit:d.target.value}),children:[o.jsx("option",{value:"minutes",children:"minutes"}),o.jsx("option",{value:"hours",children:"hours"}),o.jsx("option",{value:"days",children:"days"})]}),o.jsx("span",{className:"send-opts-next",children:"126 = future time · 432 = that date"})]}),o.jsxs("label",{className:"checkbox-label",children:[o.jsx("input",{type:"checkbox",checked:e.evalInline,onChange:d=>s({evalInline:d.target.checked})}),"Evaluate value generators in the message (uuid(), random_str(), …)"]}),o.jsxs("div",{className:"send-opts-gen",children:[o.jsx("div",{className:"send-opts-label",children:"Custom tag generators"}),e.genRules.map((d,p)=>o.jsxs("div",{className:"send-opts-id-row",children:[o.jsx("input",{className:"send-opts-prefix",style:{width:64},placeholder:"tag",value:d.tag,onChange:m=>c(p,{tag:m.target.value.replace(/[^0-9]/g,"")})}),o.jsx("span",{children:"="}),o.jsx("input",{className:"send-opts-prefix",style:{flex:1},placeholder:"uuid() / seq(8) / ORD-{seq(8)} / literal",value:d.value,onChange:m=>c(p,{value:m.target.value})}),o.jsx("button",{type:"button",className:"btn btn-sm btn-ghost btn-danger-ghost",onClick:()=>f(p),title:"Remove rule",children:"✕"})]},p)),o.jsx("button",{type:"button",className:"btn btn-sm btn-ghost",onClick:u,children:"+ Add tag generator"}),o.jsxs("div",{className:"send-opts-next",style:{marginTop:4},children:["Generators: ",Vl,". Rules set the tag on every send (insert or replace); literal values are sent as-is."]})]})]})]})}const ZS=["FIX.4.2","FIX.4.4"];function KS(e){const t={};for(const n of e.split("|")){const r=n.indexOf("=");if(r<1)continue;const a=n.slice(0,r).trim(),i=n.slice(r+1).trim();a&&!isNaN(Number(a))&&(t[a]=i)}return t}function Sf(e){return Object.entries(e).filter(([,t])=>t!=="").map(([t,n])=>`${t}=${n}`).join("|")}function YS({fieldRef:e,value:t,onChange:n}){const r=String(e.tag||""),a=Object.keys(e.values).length>0;return o.jsxs("tr",{className:`tpl-field-row${e.required?" required":""}`,children:[o.jsx("td",{className:"tpl-field-tag",children:e.tag||"?"}),o.jsxs("td",{className:"tpl-field-name",children:[e.name,e.required&&o.jsx("span",{className:"tpl-req-badge",children:"req"})]}),o.jsx("td",{className:"tpl-field-value",children:a?o.jsxs("select",{className:"tpl-select",value:t,onChange:i=>n(r,i.target.value),children:[o.jsx("option",{value:"",children:"— pick —"}),Object.entries(e.values).map(([i,s])=>o.jsxs("option",{value:i,children:[i," – ",s.replace(/_/g," ")]},i))]}):o.jsx("input",{className:"tpl-input",type:"text",value:t,placeholder:e.type,onChange:i=>n(r,i.target.value)})})]})}function lu({template:e,defaultBeginString:t="FIX.4.2",onSaved:n,onClose:r}){const[a,i]=h.useState("fields"),[s,l]=h.useState((e==null?void 0:e.name)??""),[c,u]=h.useState((e==null?void 0:e.begin_string)??t),[f,d]=h.useState((e==null?void 0:e.msg_type)??""),[p,m]=h.useState((e==null?void 0:e.msg_type_name)??""),[v,y]=h.useState((e==null?void 0:e.fields)??{}),[g,b]=h.useState(()=>Sf((e==null?void 0:e.fields)??{})),[w,_]=h.useState([]),[x,j]=h.useState(null),[S,P]=h.useState(!1),[N,k]=h.useState(!1),[A,T]=h.useState("");h.useEffect(()=>{H.fixSpec.messages(c).then(_).catch(()=>_([]))},[c]),h.useEffect(()=>{if(!f){j(null);return}P(!0),H.fixSpec.message(c,f).then(W=>{j(W),P(!1)}).catch(()=>{j(null),P(!1)})},[c,f]);function C(W,re){y(Q=>({...Q,[W]:re}))}function I(W){const re=w.find(Q=>Q.msg_type===W);d(W),m((re==null?void 0:re.name)??W),W&&y(Q=>({...Q,35:W}))}function L(W){b(W);const re=KS(W);if(y(re),re[35]&&re[35]!==f){const Q=re[35],B=w.find(z=>z.msg_type===Q);d(Q),m((B==null?void 0:B.name)??Q)}}function G(W){W==="raw"&&b(Sf(v)),i(W)}const D=new Set((x==null?void 0:x.fields.map(W=>String(W.tag)).filter(Boolean))??[]),F=Object.entries(v).filter(([W])=>!D.has(W)&&W!=="35"),[U,M]=h.useState(""),[q,ne]=h.useState("");function ee(){!U||isNaN(Number(U))||(C(U,q),M(""),ne(""))}async function pe(){if(!s.trim()){T("Template name is required");return}if(!f){T("Message type is required");return}k(!0),T("");try{const W=Object.fromEntries(Object.entries(v).filter(([,B])=>B!=="")),re={name:s.trim(),msg_type:f,msg_type_name:p,begin_string:c,fields:W},Q=e!=null&&e.id?await H.templates.update(e.id,re):await H.templates.create(re);n(Q)}catch(W){T(W instanceof Error?W.message:"Save failed")}finally{k(!1)}}const ve=w.filter(W=>W.category==="app"),qe=w.filter(W=>W.category==="admin");return o.jsx("div",{className:"modal-overlay",onClick:r,children:o.jsxs("div",{className:"modal tpl-form-modal",onClick:W=>W.stopPropagation(),children:[o.jsxs("div",{className:"modal-header",children:[o.jsx("span",{children:e!=null&&e.id?"Edit Template":"New Template"}),o.jsx("button",{className:"btn-icon",onClick:r,children:"✕"})]}),o.jsxs("div",{className:"tpl-form-top",children:[o.jsxs("div",{className:"tpl-form-row",children:[o.jsx("label",{className:"tpl-label",children:"Name"}),o.jsx("input",{className:"tpl-input tpl-name-input",value:s,onChange:W=>l(W.target.value),placeholder:"e.g. Buy 100 AAPL",autoFocus:!0})]}),o.jsxs("div",{className:"tpl-form-row",children:[o.jsx("label",{className:"tpl-label",children:"FIX version"}),o.jsx("select",{className:"tpl-select",value:c,onChange:W=>u(W.target.value),children:ZS.map(W=>o.jsx("option",{value:W,children:W},W))}),o.jsx("label",{className:"tpl-label",style:{marginLeft:12},children:"Message type"}),o.jsxs("select",{className:"tpl-select tpl-msgtype-select",value:f,onChange:W=>I(W.target.value),children:[o.jsx("option",{value:"",children:"— select —"}),ve.length>0&&o.jsx("optgroup",{label:"Application",children:ve.map(W=>o.jsxs("option",{value:W.msg_type,children:[W.name," (",W.msg_type,")"]},W.msg_type))}),qe.length>0&&o.jsx("optgroup",{label:"Admin",children:qe.map(W=>o.jsxs("option",{value:W.msg_type,children:[W.name," (",W.msg_type,")"]},W.msg_type))})]})]})]}),o.jsxs("div",{className:"analysis-tabs",style:{marginBottom:0},children:[o.jsx("button",{className:`analysis-tab-btn${a==="fields"?" active":""}`,onClick:()=>G("fields"),children:"Fields"}),o.jsx("button",{className:`analysis-tab-btn${a==="raw"?" active":""}`,onClick:()=>G("raw"),children:"Raw FIX"})]}),o.jsxs("div",{className:"tpl-form-body",children:[a==="fields"&&o.jsxs("div",{className:"tpl-fields-tab",children:[!f&&o.jsx("div",{className:"tpl-no-msgtype",children:"Select a message type above to see its field definitions."}),f&&S&&o.jsx("div",{className:"tpl-loading",children:"Loading fields…"}),f&&!S&&o.jsxs("table",{className:"tpl-field-table",children:[o.jsx("thead",{children:o.jsxs("tr",{children:[o.jsx("th",{children:"Tag"}),o.jsx("th",{children:"Field"}),o.jsx("th",{children:"Value"})]})}),o.jsxs("tbody",{children:[x==null?void 0:x.fields.map(W=>o.jsx(YS,{fieldRef:W,value:v[String(W.tag)]??"",onChange:C},`${W.tag}-${W.name}`)),F.length>0&&o.jsxs(o.Fragment,{children:[o.jsx("tr",{className:"tpl-section-divider",children:o.jsx("td",{colSpan:3,children:"Custom / extra fields"})}),F.map(([W,re])=>o.jsxs("tr",{className:"tpl-field-row",children:[o.jsx("td",{className:"tpl-field-tag",children:W}),o.jsx("td",{className:"tpl-field-name",children:rr(Number(W))}),o.jsx("td",{className:"tpl-field-value",children:o.jsx("input",{className:"tpl-input",value:re,onChange:Q=>C(W,Q.target.value)})})]},W))]}),o.jsxs("tr",{className:"tpl-add-field-row",children:[o.jsx("td",{children:o.jsx("input",{className:"tpl-input tpl-add-tag",type:"number",placeholder:"tag",value:U,onChange:W=>M(W.target.value)})}),o.jsx("td",{className:"tpl-field-name",style:{color:"var(--text2)",fontSize:11},children:U&&!isNaN(Number(U))?rr(Number(U)):"add field"}),o.jsxs("td",{className:"tpl-field-value",style:{display:"flex",gap:4},children:[o.jsx("input",{className:"tpl-input",placeholder:"value",value:q,onChange:W=>ne(W.target.value),onKeyDown:W=>{W.key==="Enter"&&ee()}}),o.jsx("button",{className:"btn btn-sm btn-ghost",onClick:ee,children:"+"})]})]})]})]})]}),a==="raw"&&o.jsxs("div",{className:"tpl-raw-tab",children:[o.jsx("textarea",{className:"tpl-raw-textarea",value:g,onChange:W=>L(W.target.value),placeholder:"35=D|49=CLIENT|56=VENUE|55=AAPL|54=1|38=100|40=2|44=100.00",spellCheck:!1}),Object.keys(v).length>0&&o.jsxs("div",{className:"tpl-raw-preview",children:[o.jsx("div",{className:"tpl-raw-preview-title",children:"Parsed fields"}),o.jsx("table",{className:"tpl-field-table tpl-preview-table",children:o.jsx("tbody",{children:Object.entries(v).map(([W,re])=>o.jsxs("tr",{children:[o.jsx("td",{className:"tpl-field-tag",children:W}),o.jsx("td",{className:"tpl-field-name",children:rr(Number(W))}),o.jsx("td",{className:"tpl-field-value",style:{color:"var(--text)"},children:re})]},W))})})]})]})]}),A&&o.jsx("div",{className:"tpl-error",children:A}),o.jsxs("div",{className:"modal-footer",children:[o.jsx("button",{className:"btn btn-ghost",onClick:r,children:"Cancel"}),o.jsx("button",{className:"btn btn-primary",onClick:pe,disabled:N,children:N?"Saving…":"Save template"})]})]})})}function dg({onSelect:e,onClose:t,defaultBeginString:n="FIX.4.2"}){const[r,a]=h.useState([]),[i,s]=h.useState(!0),[l,c]=h.useState(!1),[u,f]=h.useState(null);async function d(){s(!0);try{a(await H.templates.list())}finally{s(!1)}}h.useEffect(()=>{d()},[]);async function p(g,b){b.stopPropagation(),confirm(`Delete template "${g.name}"?`)&&(await H.templates.delete(g.id),a(w=>w.filter(_=>_.id!==g.id)))}function m(g,b){b.stopPropagation(),f(g),c(!0)}function v(g){c(!1),f(null),d()}const y=r.reduce((g,b)=>{const w=`${b.msg_type_name} (${b.msg_type})`;return(g[w]??(g[w]=[])).push(b),g},{});return l?o.jsx(lu,{template:u??void 0,defaultBeginString:n,onSaved:v,onClose:()=>{c(!1),f(null)}}):o.jsx("div",{className:"modal-overlay",onClick:t,children:o.jsxs("div",{className:"modal template-picker-modal",onClick:g=>g.stopPropagation(),children:[o.jsxs("div",{className:"modal-header",children:[o.jsx("span",{children:"Templates"}),o.jsx("button",{className:"btn-icon",onClick:t,children:"✕"})]}),o.jsx("div",{className:"template-picker-toolbar",children:o.jsx("button",{className:"btn btn-sm btn-primary",onClick:()=>{f(null),c(!0)},children:"+ New template"})}),o.jsxs("div",{className:"template-picker-body",children:[i&&o.jsx("div",{className:"template-picker-empty",children:"Loading…"}),!i&&r.length===0&&o.jsx("div",{className:"template-picker-empty",children:"No templates yet. Create one to reuse pre-populated FIX messages."}),!i&&Object.entries(y).sort(([g],[b])=>g.localeCompare(b)).map(([g,b])=>o.jsxs("div",{className:"template-group",children:[o.jsx("div",{className:"template-group-title",children:g}),b.map(w=>o.jsxs("div",{className:"template-row",onClick:()=>{e(w),t()},children:[o.jsx("div",{className:"template-row-name",children:w.name}),o.jsxs("div",{className:"template-row-meta",children:[o.jsx("span",{className:"template-begin-string",children:w.begin_string}),o.jsxs("span",{className:"template-field-count",children:[Object.keys(w.fields).length," fields"]})]}),o.jsxs("div",{className:"template-row-actions",onClick:_=>_.stopPropagation(),children:[o.jsx("button",{className:"btn btn-sm btn-ghost",onClick:_=>m(w,_),title:"Edit",children:"✎"}),o.jsx("button",{className:"btn btn-sm btn-danger",onClick:_=>p(w,_),title:"Delete",children:"✕"})]})]},w.id))]},g))]})]})})}function Va(e){try{const t=new Date(e),n=(r,a=2)=>String(r).padStart(a,"0");return`${n(t.getHours())}:${n(t.getMinutes())}:${n(t.getSeconds())}.${n(t.getMilliseconds(),3)}`}catch{return e}}function cu(e){const t=e.includes("")?"":"|";return e.split(t).filter(Boolean).map(n=>{const r=n.indexOf("=");if(r===-1)return null;const a=parseInt(n.slice(0,r),10);return isNaN(a)?null:{tag:a,value:n.slice(r+1)}}).filter(n=>n!==null)}function Of(e){return`fixture_compose_${e}`}const VS=new Set(["8","9","10","34","49","56"]);function Nf(e){return cu(e).map(({tag:t,value:n})=>({tag:String(t),value:n}))}function XS({session:e,raw:t,onRawChange:n,onClose:r}){var T;const a=V(C=>C.user),[i,s]=h.useState(!1),[l,c]=h.useState(null),[u,f]=h.useState(()=>a?ng(a.uid):{...Pa}),[d,p]=h.useState(!1),[m,v]=h.useState(!1),[y,g]=h.useState(!1);h.useEffect(()=>{if(a&&!t){const C=Xe.get(Of(a.uid));C&&n(C)}},[]);function b(C){const I=Object.entries(C.fields).filter(([,L])=>L!=="").map(([L,G])=>`${L}=${G}`).join("|");_(I)}function w(){return Object.fromEntries(Nf(t).map(C=>[C.tag,C.value]))}function _(C){n(C),a&&Xe.set(Of(a.uid),C)}function x(C){f(C),a&&rg(a.uid,C)}const j=t.trim()?Nf(t):[],S=j.filter(C=>!VS.has(C.tag)),P=((T=j.find(C=>C.tag==="35"))==null?void 0:T.value)??"",N=!!P,k=N&&e.status==="LOGGED_ON";async function A(){if(k){s(!0),c(null);try{let C=t;if(u.autoId&&P){const I=ou[P];if(I){const L=`${u.idPrefix}${u.idCounter}`;C=Aa(C,I.tag,L),x({...u,idCounter:u.idCounter+1})}}u.autoTs&&(C=Aa(C,"60",to())),C=ig(C,u),u.evalInline&&(C=lg(C)),C=cg(C,u),await H.sessions.send(e.session_id,C,{noAutoTs:!u.autoTs})}catch(C){c(C.message??"Send failed")}finally{s(!1)}}}return o.jsxs("div",{className:"compose-pane",children:[o.jsx("textarea",{className:"compose-textarea",placeholder:`Paste raw FIX (pipe- or SOH-delimited)
37
37
  8=FIX.4.2|35=D|49=SENDER|56=TARGET|11=ORD1|55=AAPL|54=1|38=100|40=2|44=150.00`,value:t,onChange:C=>{_(C.target.value),c(null)},rows:4,spellCheck:!1}),S.length>0&&o.jsx("div",{className:"compose-fields",children:o.jsxs("table",{className:"compose-fields-table",children:[o.jsx("thead",{children:o.jsxs("tr",{children:[o.jsx("th",{children:"Tag"}),o.jsx("th",{children:"Value"})]})}),o.jsx("tbody",{children:S.map((C,I)=>o.jsxs("tr",{children:[o.jsx("td",{children:C.tag}),o.jsx("td",{children:C.value})]},I))})]})}),S.length>0&&o.jsxs("div",{className:"compose-managed-note",children:["FIXture sets: 49=",e.sender_comp_id," · 56=",e.target_comp_id," · 8, 9, 10, 34"]}),o.jsx(ug,{opts:u,onChange:x,expanded:d,onToggle:()=>p(C=>!C),msgType:P||void 0}),o.jsxs("div",{className:"compose-footer",children:[o.jsx("button",{className:"btn btn-sm btn-primary",onClick:A,disabled:!k||i,title:N?e.status!=="LOGGED_ON"?"Session must be logged on":void 0:"Message needs tag 35 (MsgType)",children:i?"Sending…":`Send on ${e.sender_comp_id}→${e.target_comp_id}`}),o.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>v(!0),title:"Load a template",children:"Templates"}),t.trim()&&o.jsx("button",{className:"btn btn-sm btn-ghost",onClick:()=>g(!0),title:"Save current message as a template",children:"Save as template"}),l&&o.jsx("span",{className:"compose-error",children:l}),o.jsx("button",{className:"btn btn-sm btn-ghost compose-close",onClick:r,children:"✕"})]}),m&&o.jsx(dg,{defaultBeginString:e.begin_string,onSelect:b,onClose:()=>v(!1)}),y&&o.jsx(lu,{defaultBeginString:e.begin_string,template:{id:"",name:"",msg_type:P,msg_type_name:"",begin_string:e.begin_string,fields:w()},onSaved:()=>g(!1),onClose:()=>g(!1)})]})}function JS({row:e,sessionId:t,onClose:n}){const r=V(v=>v.user),[a,i]=h.useState(()=>r?ng(r.uid):{...Pa}),[s,l]=h.useState(!1),[c,u]=h.useState(""),[f,d]=h.useState(!1);function p(v){i(v),r&&rg(r.uid,v)}async function m(){u(""),d(!0);try{let v=bf(e.raw);if(a.autoId){const y=ou[e.msg_type];if(y){const g=`${a.idPrefix}${a.idCounter}`;v=Aa(v,y.tag,g),p({...a,idCounter:a.idCounter+1})}}a.autoTs&&(v=Aa(v,"60",to())),v=ig(v,a),a.evalInline&&(v=lg(v)),v=cg(v,a),await H.sessions.send(t,v,{noAutoTs:!a.autoTs}),n()}catch(v){u(v instanceof Error?v.message:String(v))}finally{d(!1)}}return o.jsx("div",{className:"modal-overlay",onClick:n,children:o.jsxs("div",{className:"modal",onClick:v=>v.stopPropagation(),children:[o.jsxs("div",{className:"modal-header",children:[o.jsxs("span",{children:["Resend — ",e.msg_type_name," (seq ",e.seq_num,")"]}),o.jsx("button",{className:"btn-icon",onClick:n,children:"✕"})]}),o.jsxs("div",{style:{padding:"12px 16px",display:"flex",flexDirection:"column",gap:10},children:[o.jsx("div",{className:"resend-raw",children:bf(e.raw)}),o.jsx(ug,{opts:a,onChange:p,expanded:s,onToggle:()=>l(v=>!v),msgType:e.msg_type})]}),c&&o.jsx("div",{className:"error-msg",style:{margin:"0 16px 8px"},children:c}),o.jsxs("div",{className:"modal-footer",children:[o.jsx("button",{className:"btn btn-secondary",onClick:n,children:"Cancel"}),o.jsx("button",{className:"btn btn-primary",onClick:m,disabled:f,children:f?"Sending…":"Send"})]})]})})}function fg(e){var t,n,r="";if(typeof e=="string"||typeof e=="number")r+=e;else if(typeof e=="object")if(Array.isArray(e)){var a=e.length;for(t=0;t<a;t++)e[t]&&(n=fg(e[t]))&&(r&&(r+=" "),r+=n)}else for(n in e)e[n]&&(r&&(r+=" "),r+=n);return r}function ce(){for(var e,t,n=0,r="",a=arguments.length;n<a;n++)(e=arguments[n])&&(t=fg(e))&&(r&&(r+=" "),r+=t);return r}var QS=["dangerouslySetInnerHTML","onCopy","onCopyCapture","onCut","onCutCapture","onPaste","onPasteCapture","onCompositionEnd","onCompositionEndCapture","onCompositionStart","onCompositionStartCapture","onCompositionUpdate","onCompositionUpdateCapture","onFocus","onFocusCapture","onBlur","onBlurCapture","onChange","onChangeCapture","onBeforeInput","onBeforeInputCapture","onInput","onInputCapture","onReset","onResetCapture","onSubmit","onSubmitCapture","onInvalid","onInvalidCapture","onLoad","onLoadCapture","onError","onErrorCapture","onKeyDown","onKeyDownCapture","onKeyPress","onKeyPressCapture","onKeyUp","onKeyUpCapture","onAbort","onAbortCapture","onCanPlay","onCanPlayCapture","onCanPlayThrough","onCanPlayThroughCapture","onDurationChange","onDurationChangeCapture","onEmptied","onEmptiedCapture","onEncrypted","onEncryptedCapture","onEnded","onEndedCapture","onLoadedData","onLoadedDataCapture","onLoadedMetadata","onLoadedMetadataCapture","onLoadStart","onLoadStartCapture","onPause","onPauseCapture","onPlay","onPlayCapture","onPlaying","onPlayingCapture","onProgress","onProgressCapture","onRateChange","onRateChangeCapture","onSeeked","onSeekedCapture","onSeeking","onSeekingCapture","onStalled","onStalledCapture","onSuspend","onSuspendCapture","onTimeUpdate","onTimeUpdateCapture","onVolumeChange","onVolumeChangeCapture","onWaiting","onWaitingCapture","onAuxClick","onAuxClickCapture","onClick","onClickCapture","onContextMenu","onContextMenuCapture","onDoubleClick","onDoubleClickCapture","onDrag","onDragCapture","onDragEnd","onDragEndCapture","onDragEnter","onDragEnterCapture","onDragExit","onDragExitCapture","onDragLeave","onDragLeaveCapture","onDragOver","onDragOverCapture","onDragStart","onDragStartCapture","onDrop","onDropCapture","onMouseDown","onMouseDownCapture","onMouseEnter","onMouseLeave","onMouseMove","onMouseMoveCapture","onMouseOut","onMouseOutCapture","onMouseOver","onMouseOverCapture","onMouseUp","onMouseUpCapture","onSelect","onSelectCapture","onTouchCancel","onTouchCancelCapture","onTouchEnd","onTouchEndCapture","onTouchMove","onTouchMoveCapture","onTouchStart","onTouchStartCapture","onPointerDown","onPointerDownCapture","onPointerMove","onPointerMoveCapture","onPointerUp","onPointerUpCapture","onPointerCancel","onPointerCancelCapture","onPointerEnter","onPointerEnterCapture","onPointerLeave","onPointerLeaveCapture","onPointerOver","onPointerOverCapture","onPointerOut","onPointerOutCapture","onGotPointerCapture","onGotPointerCaptureCapture","onLostPointerCapture","onLostPointerCaptureCapture","onScroll","onScrollCapture","onWheel","onWheelCapture","onAnimationStart","onAnimationStartCapture","onAnimationEnd","onAnimationEndCapture","onAnimationIteration","onAnimationIterationCapture","onTransitionEnd","onTransitionEndCapture"];function uu(e){if(typeof e!="string")return!1;var t=QS;return t.includes(e)}var e1=["aria-activedescendant","aria-atomic","aria-autocomplete","aria-busy","aria-checked","aria-colcount","aria-colindex","aria-colspan","aria-controls","aria-current","aria-describedby","aria-details","aria-disabled","aria-errormessage","aria-expanded","aria-flowto","aria-haspopup","aria-hidden","aria-invalid","aria-keyshortcuts","aria-label","aria-labelledby","aria-level","aria-live","aria-modal","aria-multiline","aria-multiselectable","aria-orientation","aria-owns","aria-placeholder","aria-posinset","aria-pressed","aria-readonly","aria-relevant","aria-required","aria-roledescription","aria-rowcount","aria-rowindex","aria-rowspan","aria-selected","aria-setsize","aria-sort","aria-valuemax","aria-valuemin","aria-valuenow","aria-valuetext","className","color","height","id","lang","max","media","method","min","name","style","target","width","role","tabIndex","accentHeight","accumulate","additive","alignmentBaseline","allowReorder","alphabetic","amplitude","arabicForm","ascent","attributeName","attributeType","autoReverse","azimuth","baseFrequency","baselineShift","baseProfile","bbox","begin","bias","by","calcMode","capHeight","clip","clipPath","clipPathUnits","clipRule","colorInterpolation","colorInterpolationFilters","colorProfile","colorRendering","contentScriptType","contentStyleType","cursor","cx","cy","d","decelerate","descent","diffuseConstant","direction","display","divisor","dominantBaseline","dur","dx","dy","edgeMode","elevation","enableBackground","end","exponent","externalResourcesRequired","fill","fillOpacity","fillRule","filter","filterRes","filterUnits","floodColor","floodOpacity","focusable","fontFamily","fontSize","fontSizeAdjust","fontStretch","fontStyle","fontVariant","fontWeight","format","from","fx","fy","g1","g2","glyphName","glyphOrientationHorizontal","glyphOrientationVertical","glyphRef","gradientTransform","gradientUnits","hanging","horizAdvX","horizOriginX","href","ideographic","imageRendering","in2","in","intercept","k1","k2","k3","k4","k","kernelMatrix","kernelUnitLength","kerning","keyPoints","keySplines","keyTimes","lengthAdjust","letterSpacing","lightingColor","limitingConeAngle","local","markerEnd","markerHeight","markerMid","markerStart","markerUnits","markerWidth","mask","maskContentUnits","maskUnits","mathematical","mode","numOctaves","offset","opacity","operator","order","orient","orientation","origin","overflow","overlinePosition","overlineThickness","paintOrder","panose1","pathLength","patternContentUnits","patternTransform","patternUnits","pointerEvents","pointsAtX","pointsAtY","pointsAtZ","preserveAlpha","preserveAspectRatio","primitiveUnits","r","radius","refX","refY","renderingIntent","repeatCount","repeatDur","requiredExtensions","requiredFeatures","restart","result","rotate","rx","ry","seed","shapeRendering","slope","spacing","specularConstant","specularExponent","speed","spreadMethod","startOffset","stdDeviation","stemh","stemv","stitchTiles","stopColor","stopOpacity","strikethroughPosition","strikethroughThickness","string","stroke","strokeDasharray","strokeDashoffset","strokeLinecap","strokeLinejoin","strokeMiterlimit","strokeOpacity","strokeWidth","surfaceScale","systemLanguage","tableValues","targetX","targetY","textAnchor","textDecoration","textLength","textRendering","to","transform","u1","u2","underlinePosition","underlineThickness","unicode","unicodeBidi","unicodeRange","unitsPerEm","vAlphabetic","values","vectorEffect","version","vertAdvY","vertOriginX","vertOriginY","vHanging","vIdeographic","viewTarget","visibility","vMathematical","widths","wordSpacing","writingMode","x1","x2","x","xChannelSelector","xHeight","xlinkActuate","xlinkArcrole","xlinkHref","xlinkRole","xlinkShow","xlinkTitle","xlinkType","xmlBase","xmlLang","xmlns","xmlnsXlink","xmlSpace","y1","y2","y","yChannelSelector","z","zoomAndPan","ref","key","angle"],t1=new Set(e1);function hg(e){return typeof e!="string"?!1:t1.has(e)}function mg(e){return typeof e=="string"&&e.startsWith("data-")}function Tt(e){if(typeof e!="object"||e===null)return{};var t={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(hg(n)||mg(n))&&(t[n]=e[n]);return t}function Xa(e){if(e==null)return null;if(h.isValidElement(e)&&typeof e.props=="object"&&e.props!==null){var t=e.props;return Tt(t)}return typeof e=="object"&&!Array.isArray(e)?Tt(e):null}function nt(e){var t={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(hg(n)||mg(n)||uu(n))&&(t[n]=e[n]);return t}function n1(e){return e==null?null:h.isValidElement(e)?nt(e.props):typeof e=="object"&&!Array.isArray(e)?nt(e):null}var r1=["children","width","height","viewBox","className","style","title","desc"];function Xl(){return Xl=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)({}).hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},Xl.apply(null,arguments)}function a1(e,t){if(e==null)return{};var n,r,a=i1(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)===-1&&{}.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}function i1(e,t){if(e==null)return{};var n={};for(var r in e)if({}.hasOwnProperty.call(e,r)){if(t.indexOf(r)!==-1)continue;n[r]=e[r]}return n}var du=h.forwardRef((e,t)=>{var{children:n,width:r,height:a,viewBox:i,className:s,style:l,title:c,desc:u}=e,f=a1(e,r1),d=i||{width:r,height:a,x:0,y:0},p=ce("recharts-surface",s);return h.createElement("svg",Xl({},nt(f),{className:p,width:r,height:a,style:l,viewBox:"".concat(d.x," ").concat(d.y," ").concat(d.width," ").concat(d.height),ref:t}),h.createElement("title",null,c),h.createElement("desc",null,u),n)}),s1=["children","className"];function Jl(){return Jl=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)({}).hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},Jl.apply(null,arguments)}function o1(e,t){if(e==null)return{};var n,r,a=l1(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)===-1&&{}.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}function l1(e,t){if(e==null)return{};var n={};for(var r in e)if({}.hasOwnProperty.call(e,r)){if(t.indexOf(r)!==-1)continue;n[r]=e[r]}return n}var ht=h.forwardRef((e,t)=>{var{children:n,className:r}=e,a=o1(e,s1),i=ce("recharts-layer",r);return h.createElement("g",Jl({className:i},nt(a),{ref:t}),n)}),pg=h.createContext(null),c1=()=>h.useContext(pg);function me(e){return function(){return e}}const vg=Math.cos,Qi=Math.sin,Lt=Math.sqrt,es=Math.PI,no=2*es,Ql=Math.PI,ec=2*Ql,Zn=1e-6,u1=ec-Zn;function gg(e){this._+=e[0];for(let t=1,n=e.length;t<n;++t)this._+=arguments[t]+e[t]}function d1(e){let t=Math.floor(e);if(!(t>=0))throw new Error(`invalid digits: ${e}`);if(t>15)return gg;const n=10**t;return function(r){this._+=r[0];for(let a=1,i=r.length;a<i;++a)this._+=Math.round(arguments[a]*n)/n+r[a]}}class f1{constructor(t){this._x0=this._y0=this._x1=this._y1=null,this._="",this._append=t==null?gg:d1(t)}moveTo(t,n){this._append`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}`}closePath(){this._x1!==null&&(this._x1=this._x0,this._y1=this._y0,this._append`Z`)}lineTo(t,n){this._append`L${this._x1=+t},${this._y1=+n}`}quadraticCurveTo(t,n,r,a){this._append`Q${+t},${+n},${this._x1=+r},${this._y1=+a}`}bezierCurveTo(t,n,r,a,i,s){this._append`C${+t},${+n},${+r},${+a},${this._x1=+i},${this._y1=+s}`}arcTo(t,n,r,a,i){if(t=+t,n=+n,r=+r,a=+a,i=+i,i<0)throw new Error(`negative radius: ${i}`);let s=this._x1,l=this._y1,c=r-t,u=a-n,f=s-t,d=l-n,p=f*f+d*d;if(this._x1===null)this._append`M${this._x1=t},${this._y1=n}`;else if(p>Zn)if(!(Math.abs(d*c-u*f)>Zn)||!i)this._append`L${this._x1=t},${this._y1=n}`;else{let m=r-s,v=a-l,y=c*c+u*u,g=m*m+v*v,b=Math.sqrt(y),w=Math.sqrt(p),_=i*Math.tan((Ql-Math.acos((y+p-g)/(2*b*w)))/2),x=_/w,j=_/b;Math.abs(x-1)>Zn&&this._append`L${t+x*f},${n+x*d}`,this._append`A${i},${i},0,0,${+(d*m>f*v)},${this._x1=t+j*c},${this._y1=n+j*u}`}}arc(t,n,r,a,i,s){if(t=+t,n=+n,r=+r,s=!!s,r<0)throw new Error(`negative radius: ${r}`);let l=r*Math.cos(a),c=r*Math.sin(a),u=t+l,f=n+c,d=1^s,p=s?a-i:i-a;this._x1===null?this._append`M${u},${f}`:(Math.abs(this._x1-u)>Zn||Math.abs(this._y1-f)>Zn)&&this._append`L${u},${f}`,r&&(p<0&&(p=p%ec+ec),p>u1?this._append`A${r},${r},0,1,${d},${t-l},${n-c}A${r},${r},0,1,${d},${this._x1=u},${this._y1=f}`:p>Zn&&this._append`A${r},${r},0,${+(p>=Ql)},${d},${this._x1=t+r*Math.cos(i)},${this._y1=n+r*Math.sin(i)}`)}rect(t,n,r,a){this._append`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}h${r=+r}v${+a}h${-r}Z`}toString(){return this._}}function fu(e){let t=3;return e.digits=function(n){if(!arguments.length)return t;if(n==null)t=null;else{const r=Math.floor(n);if(!(r>=0))throw new RangeError(`invalid digits: ${n}`);t=r}return e},()=>new f1(t)}function hu(e){return typeof e=="object"&&"length"in e?e:Array.from(e)}function yg(e){this._context=e}yg.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;default:this._context.lineTo(e,t);break}}};function ro(e){return new yg(e)}function bg(e){return e[0]}function xg(e){return e[1]}function wg(e,t){var n=me(!0),r=null,a=ro,i=null,s=fu(l);e=typeof e=="function"?e:e===void 0?bg:me(e),t=typeof t=="function"?t:t===void 0?xg:me(t);function l(c){var u,f=(c=hu(c)).length,d,p=!1,m;for(r==null&&(i=a(m=s())),u=0;u<=f;++u)!(u<f&&n(d=c[u],u,c))===p&&((p=!p)?i.lineStart():i.lineEnd()),p&&i.point(+e(d,u,c),+t(d,u,c));if(m)return i=null,m+""||null}return l.x=function(c){return arguments.length?(e=typeof c=="function"?c:me(+c),l):e},l.y=function(c){return arguments.length?(t=typeof c=="function"?c:me(+c),l):t},l.defined=function(c){return arguments.length?(n=typeof c=="function"?c:me(!!c),l):n},l.curve=function(c){return arguments.length?(a=c,r!=null&&(i=a(r)),l):a},l.context=function(c){return arguments.length?(c==null?r=i=null:i=a(r=c),l):r},l}function _i(e,t,n){var r=null,a=me(!0),i=null,s=ro,l=null,c=fu(u);e=typeof e=="function"?e:e===void 0?bg:me(+e),t=typeof t=="function"?t:me(t===void 0?0:+t),n=typeof n=="function"?n:n===void 0?xg:me(+n);function u(d){var p,m,v,y=(d=hu(d)).length,g,b=!1,w,_=new Array(y),x=new Array(y);for(i==null&&(l=s(w=c())),p=0;p<=y;++p){if(!(p<y&&a(g=d[p],p,d))===b)if(b=!b)m=p,l.areaStart(),l.lineStart();else{for(l.lineEnd(),l.lineStart(),v=p-1;v>=m;--v)l.point(_[v],x[v]);l.lineEnd(),l.areaEnd()}b&&(_[p]=+e(g,p,d),x[p]=+t(g,p,d),l.point(r?+r(g,p,d):_[p],n?+n(g,p,d):x[p]))}if(w)return l=null,w+""||null}function f(){return wg().defined(a).curve(s).context(i)}return u.x=function(d){return arguments.length?(e=typeof d=="function"?d:me(+d),r=null,u):e},u.x0=function(d){return arguments.length?(e=typeof d=="function"?d:me(+d),u):e},u.x1=function(d){return arguments.length?(r=d==null?null:typeof d=="function"?d:me(+d),u):r},u.y=function(d){return arguments.length?(t=typeof d=="function"?d:me(+d),n=null,u):t},u.y0=function(d){return arguments.length?(t=typeof d=="function"?d:me(+d),u):t},u.y1=function(d){return arguments.length?(n=d==null?null:typeof d=="function"?d:me(+d),u):n},u.lineX0=u.lineY0=function(){return f().x(e).y(t)},u.lineY1=function(){return f().x(e).y(n)},u.lineX1=function(){return f().x(r).y(t)},u.defined=function(d){return arguments.length?(a=typeof d=="function"?d:me(!!d),u):a},u.curve=function(d){return arguments.length?(s=d,i!=null&&(l=s(i)),u):s},u.context=function(d){return arguments.length?(d==null?i=l=null:l=s(i=d),u):i},u}class jg{constructor(t,n){this._context=t,this._x=n}areaStart(){this._line=0}areaEnd(){this._line=NaN}lineStart(){this._point=0}lineEnd(){(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line}point(t,n){switch(t=+t,n=+n,this._point){case 0:{this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break}case 1:this._point=2;default:{this._x?this._context.bezierCurveTo(this._x0=(this._x0+t)/2,this._y0,this._x0,n,t,n):this._context.bezierCurveTo(this._x0,this._y0=(this._y0+n)/2,t,this._y0,t,n);break}}this._x0=t,this._y0=n}}function h1(e){return new jg(e,!0)}function m1(e){return new jg(e,!1)}const mu={draw(e,t){const n=Lt(t/es);e.moveTo(n,0),e.arc(0,0,n,0,no)}},p1={draw(e,t){const n=Lt(t/5)/2;e.moveTo(-3*n,-n),e.lineTo(-n,-n),e.lineTo(-n,-3*n),e.lineTo(n,-3*n),e.lineTo(n,-n),e.lineTo(3*n,-n),e.lineTo(3*n,n),e.lineTo(n,n),e.lineTo(n,3*n),e.lineTo(-n,3*n),e.lineTo(-n,n),e.lineTo(-3*n,n),e.closePath()}},_g=Lt(1/3),v1=_g*2,g1={draw(e,t){const n=Lt(t/v1),r=n*_g;e.moveTo(0,-n),e.lineTo(r,0),e.lineTo(0,n),e.lineTo(-r,0),e.closePath()}},y1={draw(e,t){const n=Lt(t),r=-n/2;e.rect(r,r,n,n)}},b1=.8908130915292852,Sg=Qi(es/10)/Qi(7*es/10),x1=Qi(no/10)*Sg,w1=-vg(no/10)*Sg,j1={draw(e,t){const n=Lt(t*b1),r=x1*n,a=w1*n;e.moveTo(0,-n),e.lineTo(r,a);for(let i=1;i<5;++i){const s=no*i/5,l=vg(s),c=Qi(s);e.lineTo(c*n,-l*n),e.lineTo(l*r-c*a,c*r+l*a)}e.closePath()}},gl=Lt(3),_1={draw(e,t){const n=-Lt(t/(gl*3));e.moveTo(0,n*2),e.lineTo(-gl*n,-n),e.lineTo(gl*n,-n),e.closePath()}},bt=-.5,xt=Lt(3)/2,tc=1/Lt(12),S1=(tc/2+1)*3,O1={draw(e,t){const n=Lt(t/S1),r=n/2,a=n*tc,i=r,s=n*tc+n,l=-i,c=s;e.moveTo(r,a),e.lineTo(i,s),e.lineTo(l,c),e.lineTo(bt*r-xt*a,xt*r+bt*a),e.lineTo(bt*i-xt*s,xt*i+bt*s),e.lineTo(bt*l-xt*c,xt*l+bt*c),e.lineTo(bt*r+xt*a,bt*a-xt*r),e.lineTo(bt*i+xt*s,bt*s-xt*i),e.lineTo(bt*l+xt*c,bt*c-xt*l),e.closePath()}};function N1(e,t){let n=null,r=fu(a);e=typeof e=="function"?e:me(e||mu),t=typeof t=="function"?t:me(t===void 0?64:+t);function a(){let i;if(n||(n=i=r()),e.apply(this,arguments).draw(n,+t.apply(this,arguments)),i)return n=null,i+""||null}return a.type=function(i){return arguments.length?(e=typeof i=="function"?i:me(i),a):e},a.size=function(i){return arguments.length?(t=typeof i=="function"?i:me(+i),a):t},a.context=function(i){return arguments.length?(n=i??null,a):n},a}function ts(){}function ns(e,t,n){e._context.bezierCurveTo((2*e._x0+e._x1)/3,(2*e._y0+e._y1)/3,(e._x0+2*e._x1)/3,(e._y0+2*e._y1)/3,(e._x0+4*e._x1+t)/6,(e._y0+4*e._y1+n)/6)}function Og(e){this._context=e}Og.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:ns(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:ns(this,e,t);break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t}};function P1(e){return new Og(e)}function Ng(e){this._context=e}Ng.prototype={areaStart:ts,areaEnd:ts,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:{this._context.moveTo(this._x2,this._y2),this._context.closePath();break}case 2:{this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break}case 3:{this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4);break}}},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._x2=e,this._y2=t;break;case 1:this._point=2,this._x3=e,this._y3=t;break;case 2:this._point=3,this._x4=e,this._y4=t,this._context.moveTo((this._x0+4*this._x1+e)/6,(this._y0+4*this._y1+t)/6);break;default:ns(this,e,t);break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t}};function C1(e){return new Ng(e)}function Pg(e){this._context=e}Pg.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var n=(this._x0+4*this._x1+e)/6,r=(this._y0+4*this._y1+t)/6;this._line?this._context.lineTo(n,r):this._context.moveTo(n,r);break;case 3:this._point=4;default:ns(this,e,t);break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t}};function k1(e){return new Pg(e)}function Cg(e){this._context=e}Cg.prototype={areaStart:ts,areaEnd:ts,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(e,t){e=+e,t=+t,this._point?this._context.lineTo(e,t):(this._point=1,this._context.moveTo(e,t))}};function E1(e){return new Cg(e)}function Pf(e){return e<0?-1:1}function Cf(e,t,n){var r=e._x1-e._x0,a=t-e._x1,i=(e._y1-e._y0)/(r||a<0&&-0),s=(n-e._y1)/(a||r<0&&-0),l=(i*a+s*r)/(r+a);return(Pf(i)+Pf(s))*Math.min(Math.abs(i),Math.abs(s),.5*Math.abs(l))||0}function kf(e,t){var n=e._x1-e._x0;return n?(3*(e._y1-e._y0)/n-t)/2:t}function yl(e,t,n){var r=e._x0,a=e._y0,i=e._x1,s=e._y1,l=(i-r)/3;e._context.bezierCurveTo(r+l,a+l*t,i-l,s-l*n,i,s)}function rs(e){this._context=e}rs.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:yl(this,this._t0,kf(this,this._t0));break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(e,t){var n=NaN;if(e=+e,t=+t,!(e===this._x1&&t===this._y1)){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;break;case 2:this._point=3,yl(this,kf(this,n=Cf(this,e,t)),n);break;default:yl(this,this._t0,n=Cf(this,e,t));break}this._x0=this._x1,this._x1=e,this._y0=this._y1,this._y1=t,this._t0=n}}};function kg(e){this._context=new Eg(e)}(kg.prototype=Object.create(rs.prototype)).point=function(e,t){rs.prototype.point.call(this,t,e)};function Eg(e){this._context=e}Eg.prototype={moveTo:function(e,t){this._context.moveTo(t,e)},closePath:function(){this._context.closePath()},lineTo:function(e,t){this._context.lineTo(t,e)},bezierCurveTo:function(e,t,n,r,a,i){this._context.bezierCurveTo(t,e,r,n,i,a)}};function A1(e){return new rs(e)}function I1(e){return new kg(e)}function Ag(e){this._context=e}Ag.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=[],this._y=[]},lineEnd:function(){var e=this._x,t=this._y,n=e.length;if(n)if(this._line?this._context.lineTo(e[0],t[0]):this._context.moveTo(e[0],t[0]),n===2)this._context.lineTo(e[1],t[1]);else for(var r=Ef(e),a=Ef(t),i=0,s=1;s<n;++i,++s)this._context.bezierCurveTo(r[0][i],a[0][i],r[1][i],a[1][i],e[s],t[s]);(this._line||this._line!==0&&n===1)&&this._context.closePath(),this._line=1-this._line,this._x=this._y=null},point:function(e,t){this._x.push(+e),this._y.push(+t)}};function Ef(e){var t,n=e.length-1,r,a=new Array(n),i=new Array(n),s=new Array(n);for(a[0]=0,i[0]=2,s[0]=e[0]+2*e[1],t=1;t<n-1;++t)a[t]=1,i[t]=4,s[t]=4*e[t]+2*e[t+1];for(a[n-1]=2,i[n-1]=7,s[n-1]=8*e[n-1]+e[n],t=1;t<n;++t)r=a[t]/i[t-1],i[t]-=r,s[t]-=r*s[t-1];for(a[n-1]=s[n-1]/i[n-1],t=n-2;t>=0;--t)a[t]=(s[t]-a[t+1])/i[t];for(i[n-1]=(e[n]+a[n-1])/2,t=0;t<n-1;++t)i[t]=2*e[t+1]-a[t+1];return[a,i]}function T1(e){return new Ag(e)}function ao(e,t){this._context=e,this._t=t}ao.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=this._y=NaN,this._point=0},lineEnd:function(){0<this._t&&this._t<1&&this._point===2&&this._context.lineTo(this._x,this._y),(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line>=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(e,t){switch(e=+e,t=+t,this._point){case 0:this._point=1,this._line?this._context.lineTo(e,t):this._context.moveTo(e,t);break;case 1:this._point=2;default:{if(this._t<=0)this._context.lineTo(this._x,t),this._context.lineTo(e,t);else{var n=this._x*(1-this._t)+e*this._t;this._context.lineTo(n,this._y),this._context.lineTo(n,t)}break}}this._x=e,this._y=t}};function D1(e){return new ao(e,.5)}function M1(e){return new ao(e,0)}function R1(e){return new ao(e,1)}function or(e,t){if((s=e.length)>1)for(var n=1,r,a,i=e[t[0]],s,l=i.length;n<s;++n)for(a=i,i=e[t[n]],r=0;r<l;++r)i[r][1]+=i[r][0]=isNaN(a[r][1])?a[r][0]:a[r][1]}function nc(e){for(var t=e.length,n=new Array(t);--t>=0;)n[t]=t;return n}function L1(e,t){return e[t]}function $1(e){const t=[];return t.key=e,t}function F1(){var e=me([]),t=nc,n=or,r=L1;function a(i){var s=Array.from(e.apply(this,arguments),$1),l,c=s.length,u=-1,f;for(const d of i)for(l=0,++u;l<c;++l)(s[l][u]=[0,+r(d,s[l].key,u,i)]).data=d;for(l=0,f=hu(t(s));l<c;++l)s[f[l]].index=l;return n(s,f),s}return a.keys=function(i){return arguments.length?(e=typeof i=="function"?i:me(Array.from(i)),a):e},a.value=function(i){return arguments.length?(r=typeof i=="function"?i:me(+i),a):r},a.order=function(i){return arguments.length?(t=i==null?nc:typeof i=="function"?i:me(Array.from(i)),a):t},a.offset=function(i){return arguments.length?(n=i??or,a):n},a}function z1(e,t){if((r=e.length)>0){for(var n,r,a=0,i=e[0].length,s;a<i;++a){for(s=n=0;n<r;++n)s+=e[n][a][1]||0;if(s)for(n=0;n<r;++n)e[n][a][1]/=s}or(e,t)}}function B1(e,t){if((a=e.length)>0){for(var n=0,r=e[t[0]],a,i=r.length;n<i;++n){for(var s=0,l=0;s<a;++s)l+=e[s][n][1]||0;r[n][1]+=r[n][0]=-l/2}or(e,t)}}function W1(e,t){if(!(!((s=e.length)>0)||!((i=(a=e[t[0]]).length)>0))){for(var n=0,r=1,a,i,s;r<i;++r){for(var l=0,c=0,u=0;l<s;++l){for(var f=e[t[l]],d=f[r][1]||0,p=f[r-1][1]||0,m=(d-p)/2,v=0;v<l;++v){var y=e[t[v]],g=y[r][1]||0,b=y[r-1][1]||0;m+=g-b}c+=d,u+=m*d}a[r-1][1]+=a[r-1][0]=n,c&&(n-=u/c)}a[r-1][1]+=a[r-1][0]=n,or(e,t)}}var io={},Ig={};(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function t(n){return n==="__proto__"}e.isUnsafeProperty=t})(Ig);var pu={};(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function t(n){switch(typeof n){case"number":case"symbol":return!1;case"string":return n.includes(".")||n.includes("[")||n.includes("]")}}e.isDeepKey=t})(pu);var so={};(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function t(n){var r;return typeof n=="string"||typeof n=="symbol"?n:Object.is((r=n==null?void 0:n.valueOf)==null?void 0:r.call(n),-0)?"-0":String(n)}e.toKey=t})(so);var oo={},Tg={};(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function t(n){if(n==null)return"";if(typeof n=="string")return n;if(Array.isArray(n))return n.map(t).join(",");const r=String(n);return r==="0"&&Object.is(Number(n),-0)?"-0":r}e.toString=t})(Tg);(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});const t=Tg,n=so;function r(a){if(Array.isArray(a))return a.map(n.toKey);if(typeof a=="symbol")return[a];a=t.toString(a);const i=[],s=a.length;if(s===0)return i;let l=0,c="",u="",f=!1;for(a.charCodeAt(0)===46&&(i.push(""),l++);l<s;){const d=a[l];u?d==="\\"&&l+1<s?(l++,c+=a[l]):d===u?u="":c+=d:f?d==='"'||d==="'"?u=d:d==="]"?(f=!1,i.push(c),c=""):c+=d:d==="["?(f=!0,c&&(i.push(c),c="")):d==="."?c&&(i.push(c),c=""):c+=d,l++}return c&&i.push(c),i}e.toPath=r})(oo);(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});const t=Ig,n=pu,r=so,a=oo;function i(l,c,u){if(l==null)return u;switch(typeof c){case"string":{if(t.isUnsafeProperty(c))return u;const f=l[c];return f===void 0?n.isDeepKey(c)?i(l,a.toPath(c),u):u:f}case"number":case"symbol":{typeof c=="number"&&(c=r.toKey(c));const f=l[c];return f===void 0?u:f}default:{if(Array.isArray(c))return s(l,c,u);if(Object.is(c==null?void 0:c.valueOf(),-0)?c="-0":c=String(c),t.isUnsafeProperty(c))return u;const f=l[c];return f===void 0?u:f}}}function s(l,c,u){if(c.length===0)return u;let f=l;for(let d=0;d<c.length;d++){if(f==null||t.isUnsafeProperty(c[d]))return u;f=f[c[d]]}return f===void 0?u:f}e.get=i})(io);var U1=io.get;const Br=Fn(U1);var q1=4;function En(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:q1,n=10**t,r=Math.round(e*n)/n;return Object.is(r,-0)?0:r}function Ne(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r<t;r++)n[r-1]=arguments[r];return e.reduce((a,i,s)=>{var l=n[s-1];return typeof l=="string"?a+l+i:l!==void 0?a+En(l)+i:a+i},"")}var it=e=>e===0?0:e>0?1:-1,Xt=e=>typeof e=="number"&&e!=+e,lr=e=>typeof e=="string"&&e.indexOf("%")===e.length-1,Y=e=>(typeof e=="number"||e instanceof Number)&&!Xt(e),Jt=e=>Y(e)||typeof e=="string",G1=0,Ia=e=>{var t=++G1;return"".concat(e||"").concat(t)},Dt=function(t,n){var r=arguments.length>2&&arguments[2]!==void 0?arguments[2]:0,a=arguments.length>3&&arguments[3]!==void 0?arguments[3]:!1;if(!Y(t)&&typeof t!="string")return r;var i;if(lr(t)){if(n==null)return r;var s=t.indexOf("%");i=n*parseFloat(t.slice(0,s))/100}else i=+t;return Xt(i)&&(i=r),a&&n!=null&&i>n&&(i=n),i},Dg=e=>{if(!Array.isArray(e))return!1;for(var t=e.length,n={},r=0;r<t;r++)if(!n[String(e[r])])n[String(e[r])]=!0;else return!0;return!1};function Pe(e,t,n){return Y(e)&&Y(t)?En(e+n*(t-e)):t}function Mg(e,t,n){if(!(!e||!e.length))return e.find(r=>r&&(typeof t=="function"?t(r):Br(r,t))===n)}var je=e=>e===null||typeof e>"u",Ja=e=>je(e)?e:"".concat(e.charAt(0).toUpperCase()).concat(e.slice(1));function st(e){return e!=null}function gr(){}var H1=["type","size","sizeType"];function rc(){return rc=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)({}).hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},rc.apply(null,arguments)}function Af(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),n.push.apply(n,r)}return n}function If(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]!=null?arguments[t]:{};t%2?Af(Object(n),!0).forEach(function(r){Z1(e,r,n[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):Af(Object(n)).forEach(function(r){Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(n,r))})}return e}function Z1(e,t,n){return(t=K1(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function K1(e){var t=Y1(e,"string");return typeof t=="symbol"?t:t+""}function Y1(e,t){if(typeof e!="object"||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t);if(typeof r!="object")return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(e)}function V1(e,t){if(e==null)return{};var n,r,a=X1(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)===-1&&{}.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}function X1(e,t){if(e==null)return{};var n={};for(var r in e)if({}.hasOwnProperty.call(e,r)){if(t.indexOf(r)!==-1)continue;n[r]=e[r]}return n}var Rg={symbolCircle:mu,symbolCross:p1,symbolDiamond:g1,symbolSquare:y1,symbolStar:j1,symbolTriangle:_1,symbolWye:O1},J1=Math.PI/180,Q1=e=>{var t="symbol".concat(Ja(e));return Rg[t]||mu},eO=(e,t,n)=>{if(t==="area")return e;switch(n){case"cross":return 5*e*e/9;case"diamond":return .5*e*e/Math.sqrt(3);case"square":return e*e;case"star":{var r=18*J1;return 1.25*e*e*(Math.tan(r)-Math.tan(r*2)*Math.tan(r)**2)}case"triangle":return Math.sqrt(3)*e*e/4;case"wye":return(21-10*Math.sqrt(3))*e*e/8;default:return Math.PI*e*e/4}},tO=(e,t)=>{Rg["symbol".concat(Ja(e))]=t},vu=e=>{var{type:t="circle",size:n=64,sizeType:r="area"}=e,a=V1(e,H1),i=If(If({},a),{},{type:t,size:n,sizeType:r}),s="circle";typeof t=="string"&&(s=t);var l=()=>{var p=Q1(s),m=N1().type(p).size(eO(n,r,s)),v=m();if(v!==null)return v},{className:c,cx:u,cy:f}=i,d=nt(i);return Y(u)&&Y(f)&&Y(n)?h.createElement("path",rc({},d,{className:ce("recharts-symbols",c),transform:"translate(".concat(u,", ").concat(f,")"),d:l()})):null};vu.registerSymbol=tO;var Lg=e=>"radius"in e&&"startAngle"in e&&"endAngle"in e,gu=(e,t)=>{if(!e||typeof e=="function"||typeof e=="boolean")return null;var n=e;if(h.isValidElement(e)&&(n=e.props),typeof n!="object"&&typeof n!="function")return null;var r={};return Object.keys(n).forEach(a=>{uu(a)&&typeof n[a]=="function"&&(r[a]=i=>n[a](n,i))}),r},nO=(e,t,n)=>r=>(e(t,n,r),null),lo=(e,t,n)=>{if(e===null||typeof e!="object"&&typeof e!="function")return null;var r=null;return Object.keys(e).forEach(a=>{var i=e[a];uu(a)&&typeof i=="function"&&(r||(r={}),r[a]=nO(i,t,n))}),r};function Tf(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),n.push.apply(n,r)}return n}function rO(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]!=null?arguments[t]:{};t%2?Tf(Object(n),!0).forEach(function(r){aO(e,r,n[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):Tf(Object(n)).forEach(function(r){Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(n,r))})}return e}function aO(e,t,n){return(t=iO(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function iO(e){var t=sO(e,"string");return typeof t=="symbol"?t:t+""}function sO(e,t){if(typeof e!="object"||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t);if(typeof r!="object")return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(e)}function Ke(e,t){var n=rO({},e),r=t,a=Object.keys(t),i=a.reduce((s,l)=>(s[l]===void 0&&r[l]!==void 0&&(s[l]=r[l]),s),n);return i}function as(){return as=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)({}).hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},as.apply(null,arguments)}function Df(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable})),n.push.apply(n,r)}return n}function $g(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]!=null?arguments[t]:{};t%2?Df(Object(n),!0).forEach(function(r){oO(e,r,n[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):Df(Object(n)).forEach(function(r){Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(n,r))})}return e}function oO(e,t,n){return(t=lO(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function lO(e){var t=cO(e,"string");return typeof t=="symbol"?t:t+""}function cO(e,t){if(typeof e!="object"||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t);if(typeof r!="object")return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(e)}var jt=32,uO={align:"center",iconSize:14,inactiveColor:"#ccc",layout:"horizontal",verticalAlign:"middle",labelStyle:{}};function dO(e){if(typeof e=="object"&&e!==null&&"strokeDasharray"in e)return String(e.strokeDasharray)}function fO(e){var{data:t,iconType:n,inactiveColor:r}=e,a=jt/2,i=jt/6,s=jt/3,l=t.inactive?r:t.color,c=n??t.type;if(c==="none")return null;if(c==="plainline")return h.createElement("line",{strokeWidth:4,fill:"none",stroke:l,strokeDasharray:dO(t.payload),x1:0,y1:a,x2:jt,y2:a,className:"recharts-legend-icon"});if(c==="line")return h.createElement("path",{strokeWidth:4,fill:"none",stroke:l,d:"M0,".concat(a,"h").concat(s,`
38
38
  A`).concat(i,",").concat(i,",0,1,1,").concat(2*s,",").concat(a,`
39
39
  H`).concat(jt,"M").concat(2*s,",").concat(a,`
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
7
7
  <title>FIXture</title>
8
- <script type="module" crossorigin src="/assets/index-Cu3H3T9B.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-D2BF3qaF.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/ag-grid-_QKprVdm.js">
10
10
  <link rel="stylesheet" crossorigin href="/assets/index-B-hpSZWZ.css">
11
11
  </head>
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fixtureqa
3
- Version: 0.8.2
3
+ Version: 0.9.0
4
4
  Summary: FIXture — FIX Protocol Testing Tool
5
5
  Requires-Python: >=3.10
6
6
  License-File: LICENSE
7
- Requires-Dist: fixcore-engine==0.7.1
7
+ Requires-Dist: fixcore-engine==0.8.1
8
8
  Requires-Dist: fastapi>=0.111.0
9
9
  Requires-Dist: uvicorn[standard]>=0.29.0
10
10
  Requires-Dist: websockets>=12.0
@@ -66,8 +66,8 @@ fixture/static/favicon.svg
66
66
  fixture/static/index.html
67
67
  fixture/static/assets/ag-grid-_QKprVdm.js
68
68
  fixture/static/assets/index-B-hpSZWZ.css
69
- fixture/static/assets/index-Cu3H3T9B.js
70
69
  fixture/static/assets/index-CyNOPa0n.js
70
+ fixture/static/assets/index-D2BF3qaF.js
71
71
  fixture/static/assets/react-vendor-2eF0YfZT.js
72
72
  fixture/ui/__init__.py
73
73
  fixtureqa.egg-info/PKG-INFO
@@ -1,4 +1,4 @@
1
- fixcore-engine==0.7.1
1
+ fixcore-engine==0.8.1
2
2
  fastapi>=0.111.0
3
3
  uvicorn[standard]>=0.29.0
4
4
  websockets>=12.0
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "fixtureqa"
7
- version = "0.8.2"
7
+ version = "0.9.0"
8
8
  description = "FIXture — FIX Protocol Testing Tool"
9
9
  requires-python = ">=3.10"
10
10
  dependencies = [
11
11
  # 0.7.1 owns the tzdata dependency (zoneinfo for the TimeZone schedule key),
12
12
  # so it's no longer declared here — fixcore is the only code using ZoneInfo.
13
- "fixcore-engine==0.7.1",
13
+ "fixcore-engine==0.8.1",
14
14
  "fastapi>=0.111.0",
15
15
  "uvicorn[standard]>=0.29.0",
16
16
  "websockets>=12.0",
@@ -31,6 +31,7 @@ def _non_default_config() -> SessionConfig:
31
31
  requires_orig_sending_time=False,
32
32
  resend_request_chunk_size=5,
33
33
  send_redundant_resend_requests=True,
34
+ enable_last_msg_seq_num_processed=True,
34
35
  data_dictionary_path="/tmp/dd.xml",
35
36
  file_store_path="./store2",
36
37
  file_log_path="./log2",
@@ -87,6 +87,20 @@ def test_timers_and_resend_keys_emitted(tmp_path):
87
87
  assert settings.get_bool(sid, "SendRedundantResendRequests", False) is True
88
88
 
89
89
 
90
+ def test_enable_last_msg_seq_num_processed_emitted(tmp_path):
91
+ # Default off; opt-in flips EnableLastMsgSeqNumProcessed=Y (tag 369 stamping).
92
+ off = Session(_cfg(tmp_path), event_handler=lambda e: None)._build_settings()
93
+ sid = list(off.session_ids)[0]
94
+ assert off.get_bool(sid, "EnableLastMsgSeqNumProcessed", False) is False
95
+
96
+ on = Session(
97
+ _cfg(tmp_path, enable_last_msg_seq_num_processed=True),
98
+ event_handler=lambda e: None,
99
+ )._build_settings()
100
+ sid = list(on.session_ids)[0]
101
+ assert on.get_bool(sid, "EnableLastMsgSeqNumProcessed", False) is True
102
+
103
+
90
104
  def test_validation_toggles_emitted(tmp_path):
91
105
  sess = Session(
92
106
  _cfg(
@@ -53,6 +53,23 @@ def test_create_session(authed):
53
53
  assert "session_id" in body
54
54
 
55
55
 
56
+ def test_failed_autostart_rolls_back(authed):
57
+ """A failed auto-start must not leave an orphan persisted session.
58
+
59
+ Regression: pre-0.8.2 a missing tzdata raised a TimeZone error while the
60
+ session stayed registered; retries piled up duplicates. An invalid TimeZone
61
+ passes config validation (we don't pre-check the zone) but fails when the
62
+ engine builds the schedule on start — so it exercises the rollback path.
63
+ """
64
+ client, _ = authed
65
+ cfg = _ini(9876)
66
+ cfg["auto_start"] = True
67
+ cfg["time_zone"] = "Not/AZone" # invalid IANA zone -> schedule build fails on start
68
+ resp = client.post("/api/sessions/", json=cfg)
69
+ assert resp.status_code == 500
70
+ assert client.get("/api/sessions/").json() == [] # rolled back — no orphan
71
+
72
+
56
73
  def _wait_persisted(sm, sid: str, n: int, timeout: float = 5.0) -> None:
57
74
  from fixture.core.message_store import MessageStore
58
75
  store = MessageStore(sid, sm.message_writer)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes