green-screen-proxy 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -8
- package/dist/cli.js +0 -6
- package/dist/cli.js.map +1 -1
- package/dist/controller.d.ts +3 -0
- package/dist/controller.d.ts.map +1 -1
- package/dist/controller.js +14 -5
- package/dist/controller.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -16
- package/dist/index.js.map +1 -1
- package/dist/protocols/tn3270-handler.d.ts +2 -1
- package/dist/protocols/tn3270-handler.d.ts.map +1 -1
- package/dist/protocols/tn3270-handler.js +10 -2
- package/dist/protocols/tn3270-handler.js.map +1 -1
- package/dist/protocols/tn5250-handler.d.ts +3 -2
- package/dist/protocols/tn5250-handler.d.ts.map +1 -1
- package/dist/protocols/tn5250-handler.js +85 -26
- package/dist/protocols/tn5250-handler.js.map +1 -1
- package/dist/protocols/types.d.ts +30 -2
- package/dist/protocols/types.d.ts.map +1 -1
- package/dist/protocols/types.js +32 -0
- package/dist/protocols/types.js.map +1 -1
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +261 -30
- package/dist/routes.js.map +1 -1
- package/dist/server.js +1 -5
- package/dist/server.js.map +1 -1
- package/dist/session-store.d.ts +68 -0
- package/dist/session-store.d.ts.map +1 -0
- package/dist/session-store.js +40 -0
- package/dist/session-store.js.map +1 -0
- package/dist/session.d.ts +32 -2
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +105 -9
- package/dist/session.js.map +1 -1
- package/dist/standalone.d.ts +3 -0
- package/dist/standalone.d.ts.map +1 -0
- package/dist/standalone.js +6 -0
- package/dist/standalone.js.map +1 -0
- package/dist/tn3270/connection.d.ts +1 -1
- package/dist/tn3270/connection.d.ts.map +1 -1
- package/dist/tn3270/connection.js +2 -2
- package/dist/tn3270/connection.js.map +1 -1
- package/dist/tn3270/screen.d.ts +1 -0
- package/dist/tn3270/screen.d.ts.map +1 -1
- package/dist/tn3270/screen.js +1 -0
- package/dist/tn3270/screen.js.map +1 -1
- package/dist/tn5250/connection.d.ts +6 -1
- package/dist/tn5250/connection.d.ts.map +1 -1
- package/dist/tn5250/connection.js +27 -3
- package/dist/tn5250/connection.js.map +1 -1
- package/dist/tn5250/constants.d.ts +39 -3
- package/dist/tn5250/constants.d.ts.map +1 -1
- package/dist/tn5250/constants.js +51 -3
- package/dist/tn5250/constants.js.map +1 -1
- package/dist/tn5250/ebcdic-jp-builtin.d.ts +45 -0
- package/dist/tn5250/ebcdic-jp-builtin.d.ts.map +1 -0
- package/dist/tn5250/ebcdic-jp-builtin.js +124 -0
- package/dist/tn5250/ebcdic-jp-builtin.js.map +1 -0
- package/dist/tn5250/ebcdic-jp.d.ts +61 -0
- package/dist/tn5250/ebcdic-jp.d.ts.map +1 -0
- package/dist/tn5250/ebcdic-jp.js +188 -0
- package/dist/tn5250/ebcdic-jp.js.map +1 -0
- package/dist/tn5250/ebcdic.d.ts +13 -4
- package/dist/tn5250/ebcdic.d.ts.map +1 -1
- package/dist/tn5250/ebcdic.js +30 -8
- package/dist/tn5250/ebcdic.js.map +1 -1
- package/dist/tn5250/encoder.d.ts +41 -9
- package/dist/tn5250/encoder.d.ts.map +1 -1
- package/dist/tn5250/encoder.js +228 -41
- package/dist/tn5250/encoder.js.map +1 -1
- package/dist/tn5250/parser.d.ts +14 -0
- package/dist/tn5250/parser.d.ts.map +1 -1
- package/dist/tn5250/parser.js +428 -53
- package/dist/tn5250/parser.js.map +1 -1
- package/dist/tn5250/screen.d.ts +144 -24
- package/dist/tn5250/screen.d.ts.map +1 -1
- package/dist/tn5250/screen.js +245 -13
- package/dist/tn5250/screen.js.map +1 -1
- package/dist/ui/assets/index-B51sr7HL.js +56 -0
- package/dist/ui/assets/index-B9wpEWAh.css +1 -0
- package/dist/ui/assets/index-BrUnECmE.css +1 -0
- package/dist/ui/assets/index-CDBbEXbH.js +56 -0
- package/dist/ui/index.html +16 -0
- package/dist/websocket.d.ts +3 -0
- package/dist/websocket.d.ts.map +1 -1
- package/dist/websocket.js +90 -1
- package/dist/websocket.js.map +1 -1
- package/dist/worker/index.js +5573 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,18 +12,13 @@ npm install green-screen-proxy
|
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
14
|
npx green-screen-proxy # Start on port 3001
|
|
15
|
-
npx green-screen-proxy --mock # Mock mode (no real host needed)
|
|
16
15
|
npx green-screen-proxy --port 8080 # Custom port
|
|
17
16
|
npx green-screen-terminal # Proxy + web terminal UI (separate package)
|
|
18
17
|
```
|
|
19
18
|
|
|
20
|
-
###
|
|
19
|
+
### Connecting to a host
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
### Connecting to a real host
|
|
25
|
-
|
|
26
|
-
Without `--mock`, the proxy opens real TCP connections. The sign-in form in the React component collects host, port, protocol, and credentials — the proxy handles the rest.
|
|
21
|
+
The proxy opens real TCP connections. The sign-in form in the React component collects host, port, protocol, and credentials — the proxy handles the rest.
|
|
27
22
|
|
|
28
23
|
## Cloudflare Worker Deployment
|
|
29
24
|
|
|
@@ -68,7 +63,6 @@ await proxy.close();
|
|
|
68
63
|
| Option | Type | Default | Description |
|
|
69
64
|
|--------|------|---------|-------------|
|
|
70
65
|
| `port` | `number` | `3001` | Port to listen on |
|
|
71
|
-
| `mock` | `boolean` | `false` | Use mock screens |
|
|
72
66
|
|
|
73
67
|
### Returns
|
|
74
68
|
|
|
@@ -79,6 +73,97 @@ await proxy.close();
|
|
|
79
73
|
| `port` | `number` | The actual port (may differ if original was in use) |
|
|
80
74
|
| `close()` | `Promise<void>` | Stop the server |
|
|
81
75
|
|
|
76
|
+
## HTTP endpoints
|
|
77
|
+
|
|
78
|
+
All routes accept an `X-Session-Id` header (or `?sessionId=` query) to target a specific session; omit it when there's exactly one session and the proxy will use it by default.
|
|
79
|
+
|
|
80
|
+
| Method | Path | Purpose |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| `POST` | `/connect` | Open a session to a host. Optionally includes `username`/`password` for auto sign-in. |
|
|
83
|
+
| `POST` | `/disconnect` | Close the current session. |
|
|
84
|
+
| `POST` | `/reconnect` | Reconnect the current session's TCP socket. |
|
|
85
|
+
| `GET` | `/screen` | Read the latest `ScreenData` snapshot. |
|
|
86
|
+
| `GET` | `/status` | Read `ConnectionStatus`. |
|
|
87
|
+
| `POST` | `/send-text` | Type text at the current cursor. |
|
|
88
|
+
| `POST` | `/send-key` | Send a key (`Enter`, `F1`–`F24`, `Tab`, arrows, etc.). |
|
|
89
|
+
| `POST` | `/set-cursor` | Move cursor to `{row, col}`. |
|
|
90
|
+
| `POST` | `/batch` | Atomic batch of `{type: 'key'|'text'|'setCursor', ...}` operations. |
|
|
91
|
+
| `GET` | `/read-mdt` | **v1.2.0** — return input fields whose MDT bit is set. `?includeUnmodified=1` returns all input fields. |
|
|
92
|
+
| `POST` | `/session/resume` | **v1.2.0** — probe whether a session still exists; returns current status + screen. Use on page reload for REST-only clients. |
|
|
93
|
+
| `POST` | `/session/authenticated` | **v1.2.0** — flip the session status to `authenticated`. For integrators running their own sign-on cascade. |
|
|
94
|
+
| `POST` | `/wait-for-fields` | **v1.2.0** — wait until the current screen has at least `minFields` input fields (or `timeoutMs`). Short-circuits if already satisfied. |
|
|
95
|
+
|
|
96
|
+
## WebSocket protocol
|
|
97
|
+
|
|
98
|
+
Single endpoint at `ws(s)://host/ws`. Clients send JSON commands, receive JSON events.
|
|
99
|
+
|
|
100
|
+
Commands the client sends:
|
|
101
|
+
|
|
102
|
+
| `type` | Purpose |
|
|
103
|
+
|---|---|
|
|
104
|
+
| `connect` | Open a session (same body as `POST /connect`). |
|
|
105
|
+
| `reattach` | Re-bind to an existing session by `sessionId`. |
|
|
106
|
+
| `text` | Send text input. |
|
|
107
|
+
| `key` | Send a key. |
|
|
108
|
+
| `setCursor` | Move cursor. |
|
|
109
|
+
| `readMdt` | **v1.2.0** — request modified field values; response is `{type: 'mdt', data: {fields, modifiedOnly}}`. |
|
|
110
|
+
| `markAuthenticated` | **v1.2.0** — flip status to `authenticated`. |
|
|
111
|
+
| `waitForFields` | **v1.2.0** — wait for a screen with at least N input fields. |
|
|
112
|
+
| `disconnect` | Close the session. |
|
|
113
|
+
|
|
114
|
+
Events the proxy pushes:
|
|
115
|
+
|
|
116
|
+
| `type` | Meaning |
|
|
117
|
+
|---|---|
|
|
118
|
+
| `screen` | New `ScreenData` snapshot. |
|
|
119
|
+
| `status` | `ConnectionStatus` change. |
|
|
120
|
+
| `connected` | Session established after `connect`/`reattach`. |
|
|
121
|
+
| `cursor` | Lightweight cursor-only update (local ops like Tab/arrows). |
|
|
122
|
+
| `mdt` | Response to a `readMdt` command. |
|
|
123
|
+
| `session.lost` | **v1.2.0** — session died (TCP drop, idle timeout, destroy). |
|
|
124
|
+
| `session.resumed` | **v1.2.0** — a client successfully reattached to this session. |
|
|
125
|
+
| `error` | Generic error with a `message`. |
|
|
126
|
+
|
|
127
|
+
## Pluggable session store
|
|
128
|
+
|
|
129
|
+
Sessions live in an in-memory Map by default. Integrators can plug their own store (e.g. for multi-process routing via Redis) before the server accepts connections:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { createProxy, setSessionStore, type SessionStore } from 'green-screen-proxy';
|
|
133
|
+
|
|
134
|
+
class MyStore implements SessionStore {
|
|
135
|
+
set(id, session) { /* ... */ }
|
|
136
|
+
get(id) { /* ... */ }
|
|
137
|
+
delete(id) { /* ... */ }
|
|
138
|
+
has(id) { /* ... */ }
|
|
139
|
+
values() { /* ... */ }
|
|
140
|
+
size() { /* ... */ }
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
setSessionStore(new MyStore());
|
|
144
|
+
const proxy = await createProxy({ port: 3001 });
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The store holds live `Session` instances (each owns a TCP socket + parser state), so a cross-process store needs to additionally implement request routing to the owning process — that's out of scope for the interface itself.
|
|
148
|
+
|
|
149
|
+
### Session lifecycle events
|
|
150
|
+
|
|
151
|
+
Subscribe to the global lifecycle bus to observe session transitions at the server:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import { sessionLifecycle } from 'green-screen-proxy';
|
|
155
|
+
|
|
156
|
+
sessionLifecycle.on('session.lost', (sessionId, status) => {
|
|
157
|
+
console.log('session died:', sessionId, status.status);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
sessionLifecycle.on('session.resumed', (sessionId) => {
|
|
161
|
+
console.log('client reattached:', sessionId);
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
These same events are forwarded to WebSocket clients watching the affected session — clients subscribe on the adapter side via `WebSocketAdapter.onSessionLost()` / `onSessionResumed()`.
|
|
166
|
+
|
|
82
167
|
## How It Works
|
|
83
168
|
|
|
84
169
|
```
|
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,6 @@ if (subcommand === 'deploy') {
|
|
|
9
9
|
import { parseArgs } from 'node:util';
|
|
10
10
|
const { values } = parseArgs({
|
|
11
11
|
options: {
|
|
12
|
-
mock: { type: 'boolean', default: false },
|
|
13
12
|
port: { type: 'string', default: '' },
|
|
14
13
|
help: { type: 'boolean', short: 'h', default: false },
|
|
15
14
|
},
|
|
@@ -24,13 +23,11 @@ Commands:
|
|
|
24
23
|
deploy Deploy as a Cloudflare Worker (run "deploy --help" for options)
|
|
25
24
|
|
|
26
25
|
Options:
|
|
27
|
-
--mock Run with mock data (no real host connection needed)
|
|
28
26
|
--port NUM Port to listen on (default: 3001, or PORT env var)
|
|
29
27
|
-h, --help Show this help message
|
|
30
28
|
|
|
31
29
|
Examples:
|
|
32
30
|
npx green-screen-proxy # Start proxy on port 3001
|
|
33
|
-
npx green-screen-proxy --mock # Start with mock screens
|
|
34
31
|
npx green-screen-proxy --port 8080 # Start on port 8080
|
|
35
32
|
npx green-screen-proxy deploy # Deploy to Cloudflare Workers`);
|
|
36
33
|
process.exit(0);
|
|
@@ -38,8 +35,5 @@ Examples:
|
|
|
38
35
|
if (values.port) {
|
|
39
36
|
process.env.PORT = values.port;
|
|
40
37
|
}
|
|
41
|
-
if (values.mock) {
|
|
42
|
-
process.argv.push('--mock');
|
|
43
|
-
}
|
|
44
38
|
await import('./server.js');
|
|
45
39
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,uDAAuD;AACvD,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACnC,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;IAC5B,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;IAC/C,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE;QACP,IAAI,EAAE,EAAE,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,uDAAuD;AACvD,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACnC,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;IAC5B,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;IAC/C,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE;QACP,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE;QACrC,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE;KACtD;CACF,CAAC,CAAC;AAEH,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;yEAe2D,CAAC,CAAC;IACzE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;AACjC,CAAC;AAED,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC"}
|
package/dist/controller.d.ts
CHANGED
|
@@ -25,12 +25,15 @@ export declare class SessionController {
|
|
|
25
25
|
password?: string;
|
|
26
26
|
sessionId: string;
|
|
27
27
|
terminalType?: string;
|
|
28
|
+
/** EBCDIC code page override (e.g. 'cp290' for Japanese Katakana). */
|
|
29
|
+
codePage?: 'cp37' | 'cp290';
|
|
28
30
|
}): Promise<ProtocolHandler>;
|
|
29
31
|
handleText(text: string): void;
|
|
30
32
|
handleKey(key: string): Promise<void>;
|
|
31
33
|
handleSetCursor(row: number, col: number): void;
|
|
32
34
|
handleDisconnect(): void;
|
|
33
35
|
getScreenData(): ScreenData | null;
|
|
36
|
+
handleReadMdt(modifiedOnly: boolean): void;
|
|
34
37
|
private waitForScreen;
|
|
35
38
|
}
|
|
36
39
|
//# sourceMappingURL=controller.d.ts.map
|
package/dist/controller.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,eAAe,EAAiB,MAAM,sBAAsB,CAAC;AAC7F,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAErE;;;;;;;GAOG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,EAAE,eAAe,GAAG,IAAI,CAAQ;IACvC,SAAS,EAAE,OAAO,CAAS;IAC3B,OAAO,CAAC,IAAI,CAAwB;gBAExB,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI;IAIvC;;;OAGG;IACG,aAAa,CAAC,IAAI,EAAE;QACxB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,YAAY,CAAC;QACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,eAAe,EAAiB,MAAM,sBAAsB,CAAC;AAC7F,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAErE;;;;;;;GAOG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,EAAE,eAAe,GAAG,IAAI,CAAQ;IACvC,SAAS,EAAE,OAAO,CAAS;IAC3B,OAAO,CAAC,IAAI,CAAwB;gBAExB,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI;IAIvC;;;OAGG;IACG,aAAa,CAAC,IAAI,EAAE;QACxB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,YAAY,CAAC;QACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,sEAAsE;QACtE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;KAC7B,GAAG,OAAO,CAAC,eAAe,CAAC;IA+C5B,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IASxB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0C3C,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAU/C,gBAAgB,IAAI,IAAI;IASxB,aAAa,IAAI,UAAU,GAAG,IAAI;IAIlC,aAAa,CAAC,YAAY,EAAE,OAAO,GAAG,IAAI;IAS1C,OAAO,CAAC,aAAa;CAUtB"}
|
package/dist/controller.js
CHANGED
|
@@ -24,7 +24,7 @@ export class SessionController {
|
|
|
24
24
|
this.handler = null;
|
|
25
25
|
this.connected = false;
|
|
26
26
|
}
|
|
27
|
-
const { host, port = 23, protocol = 'tn5250', username, password, sessionId, terminalType } = opts;
|
|
27
|
+
const { host, port = 23, protocol = 'tn5250', username, password, sessionId, terminalType, codePage } = opts;
|
|
28
28
|
this.handler = createProtocolHandler(protocol);
|
|
29
29
|
this.send({ type: 'status', data: { connected: false, status: 'connecting', protocol, host } });
|
|
30
30
|
// Bind protocol events → WebSocket messages
|
|
@@ -39,7 +39,10 @@ export class SessionController {
|
|
|
39
39
|
this.send({ type: 'error', message: err.message });
|
|
40
40
|
this.send({ type: 'status', data: { connected: false, status: 'error', protocol, host, error: err.message } });
|
|
41
41
|
});
|
|
42
|
-
|
|
42
|
+
const connectOpts = (terminalType || codePage)
|
|
43
|
+
? { ...(terminalType ? { terminalType } : {}), ...(codePage ? { codePage } : {}) }
|
|
44
|
+
: undefined;
|
|
45
|
+
await this.handler.connect(host, port, connectOpts);
|
|
43
46
|
this.connected = true;
|
|
44
47
|
this.send({ type: 'status', data: { connected: true, status: 'connected', protocol, host } });
|
|
45
48
|
// Auto-sign-in if credentials provided and handler supports it
|
|
@@ -110,9 +113,7 @@ export class SessionController {
|
|
|
110
113
|
this.send({ type: 'error', message: 'Not connected' });
|
|
111
114
|
return;
|
|
112
115
|
}
|
|
113
|
-
|
|
114
|
-
this.handler.setCursor(row, col);
|
|
115
|
-
}
|
|
116
|
+
this.handler.setCursor(row, col);
|
|
116
117
|
const sd = this.handler.getScreenData();
|
|
117
118
|
this.send({ type: 'cursor', data: { cursor_row: sd.cursor_row, cursor_col: sd.cursor_col } });
|
|
118
119
|
}
|
|
@@ -127,6 +128,14 @@ export class SessionController {
|
|
|
127
128
|
getScreenData() {
|
|
128
129
|
return this.handler?.getScreenData() ?? null;
|
|
129
130
|
}
|
|
131
|
+
handleReadMdt(modifiedOnly) {
|
|
132
|
+
if (!this.handler || !this.connected) {
|
|
133
|
+
this.send({ type: 'error', message: 'Not connected' });
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const fields = this.handler.readFieldValues(modifiedOnly);
|
|
137
|
+
this.send({ type: 'mdt', data: { modifiedOnly, fields } });
|
|
138
|
+
}
|
|
130
139
|
waitForScreen(timeoutMs) {
|
|
131
140
|
return new Promise((resolve) => {
|
|
132
141
|
if (!this.handler) {
|
package/dist/controller.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.js","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAmB,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAG7F;;;;;;;GAOG;AACH,MAAM,OAAO,iBAAiB;IAC5B,OAAO,GAA2B,IAAI,CAAC;IACvC,SAAS,GAAY,KAAK,CAAC;IACnB,IAAI,CAAwB;IAEpC,YAAY,IAA2B;QACrC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"controller.js","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import { Server as HttpServer } from 'http';
|
|
3
|
+
export { type SessionStore, InMemorySessionStore, setSessionStore, getSessionStore, sessionLifecycle, } from './session-store.js';
|
|
3
4
|
export interface ProxyOptions {
|
|
4
5
|
/** Port to listen on (default: 3001) */
|
|
5
6
|
port?: number;
|
|
6
|
-
/** Use mock screens instead of real connections */
|
|
7
|
-
mock?: boolean;
|
|
8
7
|
}
|
|
9
8
|
export interface ProxyServer {
|
|
10
9
|
/** The underlying HTTP server */
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAgB,MAAM,IAAI,UAAU,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAgB,MAAM,IAAI,UAAU,EAAE,MAAM,MAAM,CAAC;AAK1D,OAAO,EACL,KAAK,YAAY,EACjB,oBAAoB,EACpB,eAAe,EACf,eAAe,EACf,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,WAAW,YAAY;IAC3B,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,iCAAiC;IACjC,MAAM,EAAE,UAAU,CAAC;IACnB,sBAAsB;IACtB,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC;IACrB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,sBAAsB;IACtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,WAAW,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,CAkDlF"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import cors from 'cors';
|
|
3
3
|
import { createServer } from 'http';
|
|
4
|
+
// Re-export session store primitives so integrators can swap the default
|
|
5
|
+
// in-memory store for a custom implementation (e.g. Redis routing for
|
|
6
|
+
// multi-process deployments) before createProxy() is called.
|
|
7
|
+
export { InMemorySessionStore, setSessionStore, getSessionStore, sessionLifecycle, } from './session-store.js';
|
|
4
8
|
/**
|
|
5
9
|
* Create and start a green-screen proxy server.
|
|
6
10
|
*
|
|
@@ -16,31 +20,26 @@ import { createServer } from 'http';
|
|
|
16
20
|
* ```
|
|
17
21
|
*/
|
|
18
22
|
export async function createProxy(options = {}) {
|
|
19
|
-
const { port = 3001
|
|
23
|
+
const { port = 3001 } = options;
|
|
20
24
|
const app = express();
|
|
21
25
|
app.use(cors());
|
|
22
26
|
app.use(express.json());
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
else {
|
|
29
|
-
const [{ default: routes }, { setupWebSocket: setupWs }] = await Promise.all([
|
|
30
|
-
import('./routes.js'),
|
|
31
|
-
import('./websocket.js'),
|
|
32
|
-
]);
|
|
33
|
-
app.use('/', routes);
|
|
34
|
-
setupWebSocket = setupWs;
|
|
35
|
-
}
|
|
27
|
+
const [{ default: routes }, { setupWebSocket }] = await Promise.all([
|
|
28
|
+
import('./routes.js'),
|
|
29
|
+
import('./websocket.js'),
|
|
30
|
+
]);
|
|
31
|
+
app.use('/', routes);
|
|
36
32
|
const server = createServer(app);
|
|
37
|
-
if (setupWebSocket)
|
|
38
|
-
setupWebSocket(server);
|
|
39
33
|
let resolvedPort = port;
|
|
34
|
+
const maxPort = port + 20;
|
|
40
35
|
return new Promise((resolve, reject) => {
|
|
41
36
|
server.on('error', (err) => {
|
|
42
37
|
if (err.code === 'EADDRINUSE') {
|
|
43
38
|
resolvedPort++;
|
|
39
|
+
if (resolvedPort > maxPort) {
|
|
40
|
+
reject(new Error(`All ports ${port}–${maxPort} are in use`));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
44
43
|
server.listen(resolvedPort);
|
|
45
44
|
}
|
|
46
45
|
else {
|
|
@@ -48,6 +47,9 @@ export async function createProxy(options = {}) {
|
|
|
48
47
|
}
|
|
49
48
|
});
|
|
50
49
|
server.listen(resolvedPort, () => {
|
|
50
|
+
// Attach WebSocket after successful listen to avoid EADDRINUSE
|
|
51
|
+
// being re-emitted as an unhandled error on the WebSocketServer
|
|
52
|
+
setupWebSocket(server);
|
|
51
53
|
resolve({
|
|
52
54
|
server,
|
|
53
55
|
app,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,YAAY,EAAwB,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,YAAY,EAAwB,MAAM,MAAM,CAAC;AAE1D,yEAAyE;AACzE,sEAAsE;AACtE,6DAA6D;AAC7D,OAAO,EAEL,oBAAoB,EACpB,eAAe,EACf,eAAe,EACf,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAkB5B;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,UAAwB,EAAE;IAC1D,MAAM,EAAE,IAAI,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAEhC,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IACtB,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAChB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAExB,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAClE,MAAM,CAAC,aAAa,CAAC;QACrB,MAAM,CAAC,gBAAgB,CAAC;KACzB,CAAC,CAAC;IACH,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAErB,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IAEjC,IAAI,YAAY,GAAG,IAAI,CAAC;IACxB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAE,CAAC;IAE1B,OAAO,IAAI,OAAO,CAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAClD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAA0B,EAAE,EAAE;YAChD,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBAC9B,YAAY,EAAE,CAAC;gBACf,IAAI,YAAY,GAAG,OAAO,EAAE,CAAC;oBAC3B,MAAM,CAAC,IAAI,KAAK,CAAC,aAAa,IAAI,IAAI,OAAO,aAAa,CAAC,CAAC,CAAC;oBAC7D,OAAO;gBACT,CAAC;gBACD,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;YAC9B,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,GAAG,EAAE;YAC/B,+DAA+D;YAC/D,gEAAgE;YAChE,cAAc,CAAC,MAAM,CAAC,CAAC;YAEvB,OAAO,CAAC;gBACN,MAAM;gBACN,GAAG;gBACH,IAAI,EAAE,YAAY;gBAClB,KAAK;oBACH,OAAO,IAAI,OAAO,CAAO,CAAC,GAAG,EAAE,EAAE;wBAC/B,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;wBAC1B,UAAU,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;oBAChC,CAAC,CAAC,CAAC;gBACL,CAAC;aACF,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -16,11 +16,12 @@ export declare class TN3270Handler extends ProtocolHandler {
|
|
|
16
16
|
readonly encoder: TN3270Encoder;
|
|
17
17
|
constructor();
|
|
18
18
|
get isConnected(): boolean;
|
|
19
|
-
connect(host: string, port: number,
|
|
19
|
+
connect(host: string, port: number, options?: ProtocolOptions): Promise<void>;
|
|
20
20
|
disconnect(): void;
|
|
21
21
|
getScreenData(): ScreenData;
|
|
22
22
|
sendText(text: string): boolean;
|
|
23
23
|
sendKey(keyName: string): boolean;
|
|
24
|
+
setCursor(row: number, col: number): boolean;
|
|
24
25
|
sendRaw(data: Buffer): void;
|
|
25
26
|
destroy(): void;
|
|
26
27
|
private onRecord;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tn3270-handler.d.ts","sourceRoot":"","sources":["../../src/protocols/tn3270-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD;;;GAGG;AACH,qBAAa,aAAc,SAAQ,eAAe;IAChD,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAY;IAE3C,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;;IAchC,IAAI,WAAW,IAAI,OAAO,CAEzB;IAEK,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"tn3270-handler.d.ts","sourceRoot":"","sources":["../../src/protocols/tn3270-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD;;;GAGG;AACH,qBAAa,aAAc,SAAQ,eAAe;IAChD,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAY;IAE3C,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;;IAchC,IAAI,WAAW,IAAI,OAAO,CAEzB;IAEK,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnF,UAAU,IAAI,IAAI;IAIlB,aAAa,IAAI,UAAU;IAI3B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI/B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAOjC,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAQ5C,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI3B,OAAO,IAAI,IAAI;IAKf,OAAO,CAAC,QAAQ;CAMjB"}
|
|
@@ -26,8 +26,9 @@ export class TN3270Handler extends ProtocolHandler {
|
|
|
26
26
|
get isConnected() {
|
|
27
27
|
return this.connection.isConnected;
|
|
28
28
|
}
|
|
29
|
-
async connect(host, port,
|
|
30
|
-
|
|
29
|
+
async connect(host, port, options) {
|
|
30
|
+
const connectTimeout = options?.connectTimeout;
|
|
31
|
+
await this.connection.connect(host, port, connectTimeout);
|
|
31
32
|
}
|
|
32
33
|
disconnect() {
|
|
33
34
|
this.connection.disconnect();
|
|
@@ -45,6 +46,13 @@ export class TN3270Handler extends ProtocolHandler {
|
|
|
45
46
|
this.connection.sendRaw(response);
|
|
46
47
|
return true;
|
|
47
48
|
}
|
|
49
|
+
setCursor(row, col) {
|
|
50
|
+
if (row < 0 || row >= this.screen.rows || col < 0 || col >= this.screen.cols) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
this.screen.cursorAddr = row * this.screen.cols + col;
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
48
56
|
sendRaw(data) {
|
|
49
57
|
this.connection.sendRaw(data);
|
|
50
58
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tn3270-handler.js","sourceRoot":"","sources":["../../src/protocols/tn3270-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD;;;GAGG;AACH,MAAM,OAAO,aAAc,SAAQ,eAAe;IACvC,QAAQ,GAAiB,QAAQ,CAAC;IAElC,UAAU,CAAmB;IAC7B,MAAM,CAAmB;IACzB,MAAM,CAAe;IACrB,OAAO,CAAgB;IAEhC;QACE,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,MAAM,GAAG,IAAI,gBAAgB,EAAE,CAAC;QACrC,IAAI,CAAC,UAAU,GAAG,IAAI,gBAAgB,EAAE,CAAC;QACzC,IAAI,CAAC,MAAM,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,OAAO,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAE9C,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,MAAc,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QACtE,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;QACpE,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;IACvE,CAAC;IAED,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAY,EAAE,IAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"tn3270-handler.js","sourceRoot":"","sources":["../../src/protocols/tn3270-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ProtocolHandler } from './types.js';
|
|
2
|
-
import type { ScreenData, ProtocolOptions, ProtocolType } from './types.js';
|
|
2
|
+
import type { ScreenData, ProtocolOptions, ProtocolType, FieldValue } from './types.js';
|
|
3
3
|
import { TN5250Connection } from '../tn5250/connection.js';
|
|
4
4
|
import { ScreenBuffer } from '../tn5250/screen.js';
|
|
5
5
|
import { TN5250Parser } from '../tn5250/parser.js';
|
|
@@ -19,6 +19,7 @@ export declare class TN5250Handler extends ProtocolHandler {
|
|
|
19
19
|
connect(host: string, port: number, options?: ProtocolOptions): Promise<void>;
|
|
20
20
|
disconnect(): void;
|
|
21
21
|
getScreenData(): ScreenData;
|
|
22
|
+
readFieldValues(modifiedOnly?: boolean): FieldValue[];
|
|
22
23
|
sendText(text: string): boolean;
|
|
23
24
|
sendKey(keyName: string): boolean;
|
|
24
25
|
/**
|
|
@@ -59,7 +60,7 @@ export declare class TN5250Handler extends ProtocolHandler {
|
|
|
59
60
|
/** Wait for the next screenChange event (or timeout with current screen). */
|
|
60
61
|
private waitForScreen;
|
|
61
62
|
/** Wait until the screen has at least `minFields` input fields, or timeout. */
|
|
62
|
-
|
|
63
|
+
waitForScreenWithFields(minFields: number, timeoutMs: number): Promise<ScreenData>;
|
|
63
64
|
/**
|
|
64
65
|
* Set cursor position (for click-to-position). Validates the target is
|
|
65
66
|
* inside an input field; if not, finds the nearest input field.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tn5250-handler.d.ts","sourceRoot":"","sources":["../../src/protocols/tn5250-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"tn5250-handler.d.ts","sourceRoot":"","sources":["../../src/protocols/tn5250-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxF,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAY,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD;;;GAGG;AACH,qBAAa,aAAc,SAAQ,eAAe;IAChD,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAY;IAE3C,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;;IAchC,IAAI,WAAW,IAAI,OAAO,CAEzB;IAEK,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAoCnF,UAAU,IAAI,IAAI;IAIlB,aAAa,IAAI,UAAU;IAI3B,eAAe,CAAC,YAAY,GAAE,OAAc,GAAG,UAAU,EAAE;IAI3D,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI/B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IA2LjC;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,gBAAgB;IAcxB;;;;;OAKG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO;IAmDvD,iEAAiE;IACjE,OAAO,CAAC,WAAW,CAA6E;IAEhG;;;OAGG;IACH,OAAO,CAAC,eAAe;IAsBvB;;;;OAIG;IACH,aAAa,IAAI,IAAI;IAerB,OAAO,CAAC,YAAY;IAQpB;;;;OAIG;IACG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IASvF,6EAA6E;IAC7E,OAAO,CAAC,aAAa;IAUrB,+EAA+E;IAC/E,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAuBlF;;;;OAIG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAyC5C,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI3B,OAAO,IAAI,IAAI;IAKf,OAAO,CAAC,QAAQ;CAuBjB"}
|
|
@@ -38,7 +38,27 @@ export class TN5250Handler extends ProtocolHandler {
|
|
|
38
38
|
if (dims.rows !== this.screen.rows || dims.cols !== this.screen.cols) {
|
|
39
39
|
this.screen.resize(dims.rows, dims.cols);
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
// Resolve EBCDIC code page. Explicit option wins; otherwise derive from
|
|
42
|
+
// the terminal type string (IBM-5555-* is the standard Japanese DBCS
|
|
43
|
+
// terminal family) and fall back to CP37 for everything else.
|
|
44
|
+
if (options?.codePage) {
|
|
45
|
+
this.screen.codePage = options.codePage;
|
|
46
|
+
}
|
|
47
|
+
else if (/^IBM-5555/i.test(termType) || /KATAKANA/i.test(termType)) {
|
|
48
|
+
this.screen.codePage = 'cp290';
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
this.screen.codePage = 'cp37';
|
|
52
|
+
}
|
|
53
|
+
// For Japanese sessions, register the built-in DBCS table so
|
|
54
|
+
// hiragana/katakana/symbols render without further setup. A full
|
|
55
|
+
// Kanji table can be layered on top via `registerDbcsTable(...)`.
|
|
56
|
+
if (this.screen.codePage === 'cp290') {
|
|
57
|
+
const { registerBuiltinDbcsTable } = await import('../tn5250/ebcdic-jp-builtin.js');
|
|
58
|
+
registerBuiltinDbcsTable();
|
|
59
|
+
}
|
|
60
|
+
const connectTimeout = options?.connectTimeout;
|
|
61
|
+
await this.connection.connect(host, port, termType, connectTimeout);
|
|
42
62
|
}
|
|
43
63
|
disconnect() {
|
|
44
64
|
this.connection.disconnect();
|
|
@@ -46,6 +66,9 @@ export class TN5250Handler extends ProtocolHandler {
|
|
|
46
66
|
getScreenData() {
|
|
47
67
|
return this.screen.toScreenData();
|
|
48
68
|
}
|
|
69
|
+
readFieldValues(modifiedOnly = true) {
|
|
70
|
+
return this.screen.readFieldValues(modifiedOnly);
|
|
71
|
+
}
|
|
49
72
|
sendText(text) {
|
|
50
73
|
return this.encoder.insertText(text);
|
|
51
74
|
}
|
|
@@ -132,39 +155,68 @@ export class TN5250Handler extends ProtocolHandler {
|
|
|
132
155
|
return true;
|
|
133
156
|
}
|
|
134
157
|
// Tab/Backtab: move cursor to next/previous input field.
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
//
|
|
158
|
+
//
|
|
159
|
+
// Keep only fields with a native interactive attribute byte — either
|
|
160
|
+
// underscored (regular text input) or non-display (password input).
|
|
161
|
+
// This matches `isVisibleInput()` in screen.ts and excludes UIM framework
|
|
162
|
+
// artifact fields that are technically non-bypass but carry no visible
|
|
163
|
+
// interactive attribute (e.g. the selection field at (1,2) on the main
|
|
164
|
+
// menu produces "Type option number or command" error when navigated
|
|
165
|
+
// into). Falls back to the last input field if nothing matches.
|
|
166
|
+
//
|
|
167
|
+
// IMPORTANT: password fields on IBM i sign-on screens are non-display
|
|
168
|
+
// input fields by design (attribute byte lower bits = 0x07, so chars
|
|
169
|
+
// don't echo). Any filter that broadly excludes non-display fields
|
|
170
|
+
// will skip them on Tab and make sign-on impossible.
|
|
171
|
+
//
|
|
172
|
+
// Tab order: if ANY field has a non-zero `resequence` FCW (0x80xx), order
|
|
173
|
+
// fields by resequence ascending (resequence=0 → spatial), matching the
|
|
174
|
+
// IBM 5250 Functions Reference cursor progression rules. Otherwise fall
|
|
175
|
+
// back to pure spatial order. Per lib5250 session.c:1577-1579 (which
|
|
176
|
+
// stores the FCW but leaves sequencing as a FIXME).
|
|
140
177
|
if (normalizedKey === 'Tab' || normalizedKey === 'Backtab') {
|
|
141
|
-
const allInputs = this.screen.fields
|
|
142
|
-
.filter(f => this.screen.isInputField(f))
|
|
143
|
-
.sort((a, b) => this.screen.offset(a.row, a.col) - this.screen.offset(b.row, b.col));
|
|
178
|
+
const allInputs = this.screen.fields.filter(f => this.screen.isInputField(f));
|
|
144
179
|
if (allInputs.length === 0)
|
|
145
180
|
return false;
|
|
146
|
-
//
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
181
|
+
// Determine ordering: resequence-aware if any field declares it.
|
|
182
|
+
const hasResequence = allInputs.some(f => f.resequence && f.resequence > 0);
|
|
183
|
+
const orderOf = (f) => {
|
|
184
|
+
if (hasResequence) {
|
|
185
|
+
// Resequenced fields come first in FCW order; non-resequenced
|
|
186
|
+
// fields sort after them in spatial order. Spatial offset is
|
|
187
|
+
// added as a tiebreaker within the same resequence value.
|
|
188
|
+
const base = f.resequence && f.resequence > 0 ? f.resequence : 10000;
|
|
189
|
+
return base * 1_000_000 + this.screen.offset(f.row, f.col);
|
|
190
|
+
}
|
|
191
|
+
return this.screen.offset(f.row, f.col);
|
|
192
|
+
};
|
|
193
|
+
allInputs.sort((a, b) => orderOf(a) - orderOf(b));
|
|
194
|
+
// Keep fields that have a native underscore OR native non-display raw
|
|
195
|
+
// attribute byte — both are legitimate interactive targets. Drops
|
|
196
|
+
// UIM artifacts that have neither attribute set.
|
|
197
|
+
const functional = allInputs.filter(f => this.screen.hasNativeUnderscore(f) || this.screen.hasNativeNonDisplay(f));
|
|
153
198
|
const inputFields = functional.length > 0 ? functional
|
|
154
199
|
: [allInputs[allInputs.length - 1]];
|
|
200
|
+
// Find current field's index in the ordered list so we walk the
|
|
201
|
+
// resequenced chain even if the cursor isn't at an exact field start.
|
|
155
202
|
const cursorPos = this.screen.offset(this.screen.cursorRow, this.screen.cursorCol);
|
|
203
|
+
const curIdx = inputFields.findIndex(f => {
|
|
204
|
+
const start = this.screen.offset(f.row, f.col);
|
|
205
|
+
return cursorPos >= start && cursorPos < start + f.length;
|
|
206
|
+
});
|
|
207
|
+
let target;
|
|
156
208
|
if (normalizedKey === 'Tab') {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
this.screen.cursorCol = target.col;
|
|
209
|
+
target = curIdx >= 0 && curIdx + 1 < inputFields.length
|
|
210
|
+
? inputFields[curIdx + 1]
|
|
211
|
+
: inputFields[0];
|
|
161
212
|
}
|
|
162
213
|
else {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
this.screen.cursorCol = target.col;
|
|
214
|
+
target = curIdx > 0
|
|
215
|
+
? inputFields[curIdx - 1]
|
|
216
|
+
: inputFields[inputFields.length - 1];
|
|
167
217
|
}
|
|
218
|
+
this.screen.cursorRow = target.row;
|
|
219
|
+
this.screen.cursorCol = target.col;
|
|
168
220
|
return true;
|
|
169
221
|
}
|
|
170
222
|
// Field Exit: right-adjust field value, mark modified, advance to next field
|
|
@@ -172,10 +224,17 @@ export class TN5250Handler extends ProtocolHandler {
|
|
|
172
224
|
this.encoder.fieldExit();
|
|
173
225
|
return this.sendKey('Tab');
|
|
174
226
|
}
|
|
175
|
-
// Reset: clear keyboard lock and
|
|
227
|
+
// Reset: clear keyboard lock and restore message line (per lib5250
|
|
228
|
+
// display.c:1861-1876: clearing the INHIBIT indicator restores
|
|
229
|
+
// saved_msg_line). Client-side only — nothing sent to host.
|
|
176
230
|
if (normalizedKey === 'Reset') {
|
|
177
231
|
this.screen.keyboardLocked = false;
|
|
178
|
-
this.screen.
|
|
232
|
+
if (this.screen.savedMsgLine) {
|
|
233
|
+
this.screen.restoreMsgLine();
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
this.screen.clearErrorLine();
|
|
237
|
+
}
|
|
179
238
|
if (this.screen.savedCursorBeforeError) {
|
|
180
239
|
this.screen.cursorRow = this.screen.savedCursorBeforeError.row;
|
|
181
240
|
this.screen.cursorCol = this.screen.savedCursorBeforeError.col;
|