@mcp-abap-adt/sap-rfc-lite 0.1.2 → 0.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/CHANGELOG.md ADDED
@@ -0,0 +1,31 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@mcp-abap-adt/sap-rfc-lite` are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ Releases before 0.2.0 were not recorded here.
9
+
10
+ ## [0.2.0] - 2026-09-27
11
+
12
+ ### Added
13
+
14
+ - **`client.resetServerContext(): Promise<void>`** wraps the SDK's
15
+ `RfcResetServerContext`. It ends the ABAP session context the connection
16
+ holds and keeps the connection open, so the next call runs in a fresh
17
+ context without a new logon.
18
+
19
+ What the context held is gone after the reset, including enqueue locks and
20
+ program buffers. That is how a stateless call gets a clean session on a
21
+ connection that stays open.
22
+
23
+ Measured on E19 (SAP_BASIS 816) through ADT's `SADT_REST_RFC_ENDPOINT`:
24
+ - Without a reset, a package created or written on a kept connection could
25
+ not be written there again (400 PAK/058, `CL_PACKAGE`'s session buffer).
26
+ - With a reset after each call, create, read, two writes and a delete all
27
+ passed on one connection.
28
+ - A reset costs about 0.1 s. Opening a new connection for each call costs
29
+ about 0.5 s.
30
+
31
+ [0.2.0]: https://github.com/fr0ster/sap-rfc-lite/releases/tag/v0.2.0
package/README.md CHANGED
@@ -123,6 +123,16 @@ Invokes a remote function module.
123
123
  - `rfmName` — name of the function module
124
124
  - `rfmParams` — optional input parameters
125
125
 
126
+ ### `client.resetServerContext(): Promise<void>`
127
+
128
+ Resets the ABAP session context the connection holds and keeps the connection
129
+ open (`RfcResetServerContext`). The next call runs in a fresh context, without
130
+ a new logon.
131
+
132
+ Everything the context held goes with it, including enqueue locks and program
133
+ buffers. This is how a stateless call gets a clean session on a connection
134
+ that is kept open.
135
+
126
136
  ### `client.close(): Promise<void>`
127
137
 
128
138
  Closes the RFC connection.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-abap-adt/sap-rfc-lite",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Lightweight Node.js bindings for SAP NW RFC SDK",
5
5
  "license": "Apache-2.0",
6
6
  "main": "./lib/index.js",
@@ -9,7 +9,8 @@
9
9
  "binding.gyp",
10
10
  "src",
11
11
  "lib",
12
- "NOTICE"
12
+ "NOTICE",
13
+ "CHANGELOG.md"
13
14
  ],
14
15
  "scripts": {
15
16
  "install": "node-gyp-build",
package/src/cpp/Client.cc CHANGED
@@ -48,6 +48,7 @@ Napi::Object Client::Init(Napi::Env env, Napi::Object exports) {
48
48
  InstanceAccessor("_alive", &Client::AliveGetter, nullptr),
49
49
  InstanceMethod("open", &Client::Open),
50
50
  InstanceMethod("close", &Client::Close),
51
+ InstanceMethod("resetServerContext", &Client::ResetServerContext),
51
52
  InstanceMethod("invoke", &Client::Invoke),
52
53
  });
53
54
 
@@ -186,6 +187,42 @@ class CloseAsync : public Napi::AsyncWorker {
186
187
  bool conn_closed = false;
187
188
  };
188
189
 
190
+ // RfcResetServerContext: ends the ABAP session context ("user context") the
191
+ // connection holds and keeps the connection open, so the next call runs in a
192
+ // fresh context without a new logon.
193
+ class ResetServerContextAsync : public Napi::AsyncWorker {
194
+ public:
195
+ ResetServerContextAsync(Napi::Function& callback, Client* client)
196
+ : Napi::AsyncWorker(callback), client(client) {}
197
+ ~ResetServerContextAsync() {}
198
+
199
+ void Execute() {
200
+ client->LockMutex();
201
+ conn_closed = (client->connectionHandle == nullptr);
202
+ if (!conn_closed) {
203
+ RfcResetServerContext(client->connectionHandle, &errorInfo);
204
+ }
205
+ client->UnlockMutex();
206
+ }
207
+
208
+ void OnOK() {
209
+ Napi::HandleScope scope(Env());
210
+ if (conn_closed) {
211
+ Callback().Call({client->connectionClosedError("resetServerContext()")});
212
+ } else if (errorInfo.code != RFC_OK) {
213
+ Callback().Call({rfcSdkError(&errorInfo)});
214
+ } else {
215
+ Callback().Call({});
216
+ }
217
+ Callback().Reset();
218
+ }
219
+
220
+ private:
221
+ Client* client;
222
+ RFC_ERROR_INFO errorInfo;
223
+ bool conn_closed = false;
224
+ };
225
+
189
226
  class InvokeAsync : public Napi::AsyncWorker {
190
227
  public:
191
228
  InvokeAsync(Napi::Function& callback,
@@ -412,6 +449,18 @@ Napi::Value Client::Close(const Napi::CallbackInfo& info) {
412
449
  return info.Env().Undefined();
413
450
  }
414
451
 
452
+ Napi::Value Client::ResetServerContext(const Napi::CallbackInfo& info) {
453
+ if (!info[0].IsFunction()) {
454
+ Napi::TypeError::New(
455
+ info.Env(), "Client resetServerContext() requires a callback function")
456
+ .ThrowAsJavaScriptException();
457
+ return info.Env().Undefined();
458
+ }
459
+ Napi::Function callback = info[0].As<Napi::Function>();
460
+ (new ResetServerContextAsync(callback, this))->Queue();
461
+ return info.Env().Undefined();
462
+ }
463
+
415
464
  Napi::Value Client::Invoke(const Napi::CallbackInfo& info) {
416
465
  Napi::Array notRequested = Napi::Array::New(info.Env());
417
466
  Napi::Value bcd;
package/src/cpp/Client.h CHANGED
@@ -16,6 +16,7 @@ class Client : public Napi::ObjectWrap<Client> {
16
16
  public:
17
17
  friend class OpenAsync;
18
18
  friend class CloseAsync;
19
+ friend class ResetServerContextAsync;
19
20
  friend class PrepareAsync;
20
21
  friend class InvokeAsync;
21
22
  static Napi::Object Init(Napi::Env env, Napi::Object exports);
@@ -39,6 +40,7 @@ class Client : public Napi::ObjectWrap<Client> {
39
40
 
40
41
  Napi::Value Open(const Napi::CallbackInfo& info);
41
42
  Napi::Value Close(const Napi::CallbackInfo& info);
43
+ Napi::Value ResetServerContext(const Napi::CallbackInfo& info);
42
44
  Napi::Value Invoke(const Napi::CallbackInfo& info);
43
45
 
44
46
  RfmErrorPath errorPath;
package/src/ts/binding.ts CHANGED
@@ -15,6 +15,7 @@ export interface RfcClientBinding {
15
15
  _alive: boolean;
16
16
  open(callback: (err?: unknown) => void): void;
17
17
  close(callback: (err?: unknown) => void): void;
18
+ resetServerContext(callback: (err?: unknown) => void): void;
18
19
  invoke(
19
20
  rfmName: string,
20
21
  rfmParams: RfcObject,
package/src/ts/client.ts CHANGED
@@ -56,6 +56,25 @@ export class Client {
56
56
  });
57
57
  }
58
58
 
59
+ /**
60
+ * Resets the ABAP session context this connection holds and keeps the
61
+ * connection open (`RfcResetServerContext`). The next call runs in a fresh
62
+ * context without a new logon. Whatever the context held is gone, including
63
+ * enqueue locks and program buffers.
64
+ */
65
+ resetServerContext(): Promise<void> {
66
+ return new Promise((resolve, reject) => {
67
+ try {
68
+ this.__client.resetServerContext((err: unknown) => {
69
+ if (err === undefined) resolve();
70
+ else reject(err);
71
+ });
72
+ } catch (ex) {
73
+ reject(ex);
74
+ }
75
+ });
76
+ }
77
+
59
78
  call(rfmName: string, rfmParams: RfcObject = {}): Promise<RfcObject> {
60
79
  return new Promise((resolve, reject) => {
61
80
  if (typeof rfmName !== 'string' || rfmName.length === 0) {