@mcp-abap-adt/sap-rfc-lite 0.1.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.
@@ -0,0 +1,308 @@
1
+ // SPDX-FileCopyrightText: 2014 SAP SE Srdjan Boskovic <srdjan.boskovic@sap.com>
2
+ // SPDX-FileCopyrightText: 2026 sap-rfc-lite contributors
3
+ //
4
+ // SPDX-License-Identifier: Apache-2.0
5
+
6
+ #ifndef NodeRfc_SDK_H_
7
+ #define NodeRfc_SDK_H_
8
+
9
+ #include <sstream>
10
+ #include "noderfc.h"
11
+
12
+ using namespace Napi;
13
+
14
+ namespace node_rfc {
15
+
16
+ extern Napi::Env __env;
17
+
18
+ Napi::Value wrapString(const SAP_UC* uc, int length = -1);
19
+
20
+ //
21
+ // Client connection parameters internal representation
22
+ //
23
+ typedef struct _ConnectionParamsStruct {
24
+ uint_t paramSize = 0;
25
+ RFC_CONNECTION_PARAMETER* connectionParams = nullptr;
26
+ _ConnectionParamsStruct(uint_t paramSize,
27
+ RFC_CONNECTION_PARAMETER* connectionParams)
28
+ : paramSize(paramSize), connectionParams(connectionParams) {
29
+ // DEBUG("ConnectionParamsStruct %u", paramSize);
30
+ }
31
+
32
+ ~_ConnectionParamsStruct() {
33
+ // DEBUG("~ConnectionParamsStruct ", paramSize);
34
+ if (connectionParams != nullptr) {
35
+ for (uint_t i = 0; i < this->paramSize; i++) {
36
+ delete[] connectionParams[i].name;
37
+ delete[] connectionParams[i].value;
38
+ }
39
+ connectionParams = nullptr;
40
+ delete[] connectionParams;
41
+ }
42
+ }
43
+ } ConnectionParamsStruct;
44
+
45
+ //
46
+ // Client options internal representation
47
+ //
48
+ typedef struct _ClientOptionsStruct {
49
+ uint_t bcd = CLIENT_OPTION_BCD_STRING;
50
+ bool stateless = false;
51
+ uint_t timeout = 0;
52
+ RFC_DIRECTION filter_param_type = (RFC_DIRECTION)0;
53
+ Napi::FunctionReference bcdFunction;
54
+ Napi::FunctionReference dateToABAP;
55
+ Napi::FunctionReference dateFromABAP;
56
+ Napi::FunctionReference timeToABAP;
57
+ Napi::FunctionReference timeFromABAP;
58
+
59
+ Napi::Value _Value(Napi::Env env) {
60
+ Napi::Object options = Napi::Object::New(env);
61
+
62
+ // timeout
63
+ options.Set(CLIENT_OPTION_TIMEOUT, Napi::Number::New(env, timeout));
64
+
65
+ // stateless
66
+ options.Set(CLIENT_OPTION_STATELESS,
67
+ Napi::Boolean::New(env, stateless));
68
+
69
+ // filter
70
+ options.Set(CLIENT_OPTION_FILTER,
71
+ Napi::Number::New(env, filter_param_type));
72
+
73
+ // bcd
74
+ if (bcd == CLIENT_OPTION_BCD_STRING) {
75
+ options.Set(CLIENT_OPTION_BCD, "string");
76
+ } else if (bcd == CLIENT_OPTION_BCD_NUMBER) {
77
+ options.Set(CLIENT_OPTION_BCD, "number");
78
+ } else if (bcd == CLIENT_OPTION_BCD_FUNCTION) {
79
+ options.Set(CLIENT_OPTION_BCD, bcdFunction.Value());
80
+ } else {
81
+ options.Set(CLIENT_OPTION_BCD, "?");
82
+ }
83
+
84
+ // date
85
+ Napi::Object odate = Napi::Object::New(env);
86
+ if (!dateToABAP.IsEmpty()) {
87
+ odate.Set("toABAP", dateToABAP.Value());
88
+ } else {
89
+ odate.Set("toABAP", "string");
90
+ }
91
+ if (!dateFromABAP.IsEmpty()) {
92
+ odate.Set("fromABAP", dateFromABAP.Value());
93
+ } else {
94
+ odate.Set("fromABAP", "string");
95
+ }
96
+ options.Set(CLIENT_OPTION_DATE, odate);
97
+
98
+ // time
99
+ Napi::Object otime = Napi::Object::New(env);
100
+ if (!timeToABAP.IsEmpty()) {
101
+ otime.Set("toABAP", timeToABAP.Value());
102
+ } else {
103
+ otime.Set("toABAP", "string");
104
+ }
105
+ if (!timeFromABAP.IsEmpty()) {
106
+ otime.Set("fromABAP", timeFromABAP.Value());
107
+ } else {
108
+ otime.Set("fromABAP", "string");
109
+ }
110
+ options.Set(CLIENT_OPTION_TIME, otime);
111
+
112
+ Napi::EscapableHandleScope scope(env);
113
+ return scope.Escape(options);
114
+ }
115
+
116
+ _ClientOptionsStruct& operator=(
117
+ _ClientOptionsStruct& pool_client_options) // note: passed by copy
118
+ {
119
+ bcd = pool_client_options.bcd;
120
+ stateless = pool_client_options.stateless;
121
+ filter_param_type = pool_client_options.filter_param_type;
122
+ timeout = pool_client_options.timeout;
123
+ // bcd
124
+ if (!pool_client_options.bcdFunction) {
125
+ bcdFunction = Napi::Persistent(pool_client_options.bcdFunction.Value());
126
+ }
127
+ // date
128
+ if (!pool_client_options.dateToABAP.IsEmpty()) {
129
+ dateToABAP = Napi::Persistent(pool_client_options.dateToABAP.Value());
130
+ }
131
+ if (!pool_client_options.dateFromABAP.IsEmpty()) {
132
+ dateFromABAP = Napi::Persistent(pool_client_options.dateFromABAP.Value());
133
+ }
134
+ // time
135
+ if (!pool_client_options.timeToABAP.IsEmpty()) {
136
+ timeToABAP = Napi::Persistent(pool_client_options.timeToABAP.Value());
137
+ }
138
+ if (!pool_client_options.timeFromABAP.IsEmpty()) {
139
+ timeFromABAP = Napi::Persistent(pool_client_options.timeFromABAP.Value());
140
+ }
141
+ return *this;
142
+ };
143
+
144
+ ~_ClientOptionsStruct() {
145
+ // DEBUG("~ClientOptionsStruct");
146
+ // bcd
147
+ if (!bcdFunction.IsEmpty()) {
148
+ bcdFunction.Unref();
149
+ }
150
+ // date
151
+ if (!dateToABAP.IsEmpty()) {
152
+ dateToABAP.Unref();
153
+ }
154
+ if (!dateFromABAP.IsEmpty()) {
155
+ dateFromABAP.Unref();
156
+ }
157
+ // time
158
+ if (!timeToABAP.IsEmpty()) {
159
+ timeToABAP.Unref();
160
+ }
161
+ if (!timeFromABAP.IsEmpty()) {
162
+ timeFromABAP.Unref();
163
+ }
164
+ }
165
+ } ClientOptionsStruct;
166
+
167
+ typedef struct _RfmErrorPath {
168
+ RFC_ABAP_NAME functionName;
169
+ RFC_ABAP_NAME parameterName;
170
+ RFC_ABAP_NAME tableName;
171
+ int64_t table_line = -1;
172
+ RFC_ABAP_NAME structureName;
173
+ RFC_ABAP_NAME fieldName;
174
+
175
+ void clear() {
176
+ functionName[0] = 0;
177
+ parameterName[0] = 0;
178
+ resetPath();
179
+ }
180
+
181
+ void resetPath() {
182
+ table_line = -1;
183
+ tableName[0] = 0;
184
+ structureName[0] = 0;
185
+ fieldName[0] = 0;
186
+ }
187
+
188
+ void setName(RFCTYPE typ, SAP_UC* cName) {
189
+ if (typ == RFCTYPE_STRUCTURE) {
190
+ strcpyU(structureName, cName);
191
+ } else if (typ == RFCTYPE_TABLE) {
192
+ strcpyU(tableName, cName);
193
+ } else {
194
+ strcpyU(fieldName, cName);
195
+ }
196
+ }
197
+
198
+ void setFunctionName(SAP_UC* funcName) {
199
+ clear();
200
+ strcpyU(functionName, funcName);
201
+ }
202
+
203
+ void setParameterName(SAP_UC* pName) {
204
+ resetPath();
205
+ strcpyU(parameterName, pName);
206
+ }
207
+
208
+ void setFieldName(SAP_UC* fName) { strcpyU(fieldName, fName); }
209
+
210
+ Napi::Object getpath() {
211
+ Napi::Object path = Napi::Object::New(node_rfc::__env);
212
+ path.Set("rfm", wrapString(functionName));
213
+ path.Set("parameter", wrapString(parameterName));
214
+ if (*tableName) {
215
+ path.Set("table", wrapString(tableName));
216
+ path.Set("table_line", table_line);
217
+ }
218
+ if (*structureName) {
219
+ path.Set("structure", wrapString(structureName));
220
+ }
221
+ if (*fieldName) {
222
+ path.Set("field", wrapString(fieldName));
223
+ }
224
+ return path;
225
+ }
226
+
227
+ std::string pathstr() {
228
+ std::ostringstream ss;
229
+ Napi::Object obj = this->getpath();
230
+ Napi::Array objKeys = obj.GetPropertyNames();
231
+ ss << "rfmPath: {\n";
232
+ for (uint_t ii = 0; ii < objKeys.Length(); ii++) {
233
+ std::string key = objKeys.Get(ii).As<Napi::String>().Utf8Value();
234
+ std::string val = obj.Get(key).ToString().Utf8Value();
235
+ ss << " " << key << ": " << val << ", \n";
236
+ }
237
+ ss << "}\n";
238
+ return ss.str();
239
+ }
240
+
241
+ } RfmErrorPath;
242
+
243
+ typedef std::pair<Napi::Value, Napi::Value> ValuePair;
244
+ typedef std::pair<RFC_ERROR_INFO, std::string> ErrorPair;
245
+
246
+ // Write parameters (to SDK)
247
+ SAP_UC* setString(const Napi::String napistr);
248
+ SAP_UC* setString(std::string str);
249
+ Napi::Value setRfmParameter(RFC_FUNCTION_DESC_HANDLE functionDescHandle,
250
+ RFC_FUNCTION_HANDLE functionHandle,
251
+ Napi::String name,
252
+ Napi::Value value,
253
+ RfmErrorPath* errorPath,
254
+ ClientOptionsStruct* client_options);
255
+ Napi::Value setStructure(RFC_STRUCTURE_HANDLE structHandle,
256
+ RFC_TYPE_DESC_HANDLE functionDescHandle,
257
+ SAP_UC* cName,
258
+ Napi::Value value,
259
+ RfmErrorPath* errorPath,
260
+ ClientOptionsStruct* client_options);
261
+ Napi::Value setVariable(RFCTYPE typ,
262
+ RFC_FUNCTION_HANDLE functionHandle,
263
+ SAP_UC* cName,
264
+ Napi::Value value,
265
+ RFC_TYPE_DESC_HANDLE functionDescHandle,
266
+ RfmErrorPath* errorPath,
267
+ ClientOptionsStruct* client_options);
268
+
269
+ // Read parameters (from SDK)
270
+ ValuePair getStructure(RFC_TYPE_DESC_HANDLE typeDesc,
271
+ RFC_STRUCTURE_HANDLE structHandle,
272
+ RfmErrorPath* errorPath,
273
+ ClientOptionsStruct* client_options);
274
+ ValuePair getVariable(RFCTYPE typ,
275
+ RFC_FUNCTION_HANDLE functionHandle,
276
+ SAP_UC* cName,
277
+ uint_t cLen,
278
+ RFC_TYPE_DESC_HANDLE typeDesc,
279
+ RfmErrorPath* errorPath,
280
+ ClientOptionsStruct* client_options);
281
+ ValuePair getRfmParameters(RFC_FUNCTION_DESC_HANDLE functionDescHandle,
282
+ RFC_FUNCTION_HANDLE functionHandle,
283
+ RfmErrorPath* errorPath,
284
+ ClientOptionsStruct* client_options);
285
+ Napi::Value getConnectionAttributes(Napi::Env env,
286
+ RFC_CONNECTION_HANDLE connectionHandle);
287
+
288
+ // RFC ERRORS
289
+ Napi::Object RfcLibError(RFC_ERROR_INFO* errorInfo);
290
+ Napi::Object AbapError(RFC_ERROR_INFO* errorInfo);
291
+ Napi::Value rfcSdkError(RFC_ERROR_INFO* errorInfo,
292
+ RfmErrorPath* errorPath = nullptr);
293
+ Napi::Value nodeRfcError(std::string message,
294
+ RfmErrorPath* errorPath = nullptr);
295
+
296
+ // Connection parameters and client options parsers
297
+ void getConnectionParams(Napi::Object clientParamsObject,
298
+ ConnectionParamsStruct* clientParams);
299
+ void checkClientOptions(Napi::Object clientOptionsObject,
300
+ ClientOptionsStruct* clientOptions);
301
+
302
+ } // namespace node_rfc
303
+
304
+ #define CONNECTION_INFO_SET(property) \
305
+ infoObj.Set(#property, \
306
+ wrapString(connInfo.property, strlenU(connInfo.property)));
307
+
308
+ #endif
@@ -0,0 +1,50 @@
1
+ // SPDX-FileCopyrightText: 2026 sap-rfc-lite contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import path from 'node:path';
5
+ import type {
6
+ RfcConnectionParameters,
7
+ RfcObject,
8
+ RfcSdkVersions,
9
+ } from './types';
10
+
11
+ export interface RfcClientBinding {
12
+ // biome-ignore lint/suspicious/noMisleadingInstantiator: mirrors native C++ addon constructor
13
+ new (connectionParameters: RfcConnectionParameters): RfcClientBinding;
14
+ _id: number;
15
+ _alive: boolean;
16
+ open(callback: (err?: unknown) => void): void;
17
+ close(callback: (err?: unknown) => void): void;
18
+ invoke(
19
+ rfmName: string,
20
+ rfmParams: RfcObject,
21
+ callback: (err: unknown, res: RfcObject) => void,
22
+ callOptions?: object,
23
+ ): void;
24
+ }
25
+
26
+ export interface NativeBinding {
27
+ Client: RfcClientBinding;
28
+ bindingVersions: RfcSdkVersions;
29
+ }
30
+
31
+ let binding: NativeBinding;
32
+
33
+ try {
34
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
35
+ binding = require('node-gyp-build')(
36
+ path.resolve(__dirname, '..'),
37
+ ) as NativeBinding;
38
+ } catch (ex) {
39
+ const err = ex as Error;
40
+ const env = {
41
+ SAPNWRFC_HOME: process.env.SAPNWRFC_HOME || '(not set)',
42
+ platform: process.platform,
43
+ arch: process.arch,
44
+ node: process.version,
45
+ };
46
+ err.message += `\nEnvironment: ${JSON.stringify(env, null, 2)}`;
47
+ throw err;
48
+ }
49
+
50
+ export { binding };
@@ -0,0 +1,84 @@
1
+ // SPDX-FileCopyrightText: 2026 sap-rfc-lite contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { binding, type RfcClientBinding } from './binding';
5
+ import type { RfcConnectionParameters, RfcObject } from './types';
6
+
7
+ export class Client {
8
+ private __client: RfcClientBinding;
9
+
10
+ constructor(connectionParameters: RfcConnectionParameters) {
11
+ if (!connectionParameters || typeof connectionParameters !== 'object') {
12
+ throw new TypeError(
13
+ 'Client constructor requires connection parameters object',
14
+ );
15
+ }
16
+ this.__client = new binding.Client(connectionParameters);
17
+ }
18
+
19
+ get id(): number {
20
+ return this.__client._id;
21
+ }
22
+
23
+ get alive(): boolean {
24
+ return this.__client._alive;
25
+ }
26
+
27
+ open(): Promise<this> {
28
+ return new Promise((resolve, reject) => {
29
+ try {
30
+ this.__client.open((err: unknown) => {
31
+ if (err !== undefined) {
32
+ reject(err);
33
+ } else {
34
+ resolve(this);
35
+ }
36
+ });
37
+ } catch (ex) {
38
+ reject(ex);
39
+ }
40
+ });
41
+ }
42
+
43
+ close(): Promise<void> {
44
+ return new Promise((resolve, reject) => {
45
+ try {
46
+ this.__client.close((err: unknown) => {
47
+ if (err === undefined) {
48
+ resolve();
49
+ } else {
50
+ reject(err);
51
+ }
52
+ });
53
+ } catch (ex) {
54
+ reject(ex);
55
+ }
56
+ });
57
+ }
58
+
59
+ call(rfmName: string, rfmParams: RfcObject = {}): Promise<RfcObject> {
60
+ return new Promise((resolve, reject) => {
61
+ if (typeof rfmName !== 'string' || rfmName.length === 0) {
62
+ reject(
63
+ new TypeError('Function module name must be a non-empty string'),
64
+ );
65
+ return;
66
+ }
67
+ try {
68
+ this.__client.invoke(
69
+ rfmName,
70
+ rfmParams,
71
+ (err: unknown, res: RfcObject) => {
72
+ if (err !== undefined && err !== null) {
73
+ reject(err);
74
+ } else {
75
+ resolve(res);
76
+ }
77
+ },
78
+ );
79
+ } catch (ex) {
80
+ reject(ex);
81
+ }
82
+ });
83
+ }
84
+ }
@@ -0,0 +1,6 @@
1
+ // SPDX-FileCopyrightText: 2026 sap-rfc-lite contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ export { binding } from './binding';
5
+ export { Client } from './client';
6
+ export * from './types';
@@ -0,0 +1,53 @@
1
+ // SPDX-FileCopyrightText: 2026 sap-rfc-lite contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // Connection parameters — same keys as SAP NW RFC SDK
5
+ export type RfcConnectionParameters = Record<string, string>;
6
+
7
+ // RFC data types
8
+ export type RfcVariable = string | number | Buffer;
9
+ export type RfcStructure = {
10
+ [key: string]: RfcVariable | RfcStructure | RfcTable;
11
+ };
12
+ export type RfcTable = Array<RfcVariable | RfcStructure>;
13
+ export type RfcParameterValue = RfcVariable | RfcStructure | RfcTable;
14
+ export type RfcObject = { [key: string]: RfcParameterValue };
15
+
16
+ // Binding versions
17
+ export interface RfcSdkVersions {
18
+ version: string;
19
+ nwrfcsdk: { major: number; minor: number; patchLevel: number };
20
+ }
21
+
22
+ // Error types
23
+ export interface RfcLibError {
24
+ name: 'RfcLibError';
25
+ group: number;
26
+ code: number;
27
+ codeString: string;
28
+ key: string;
29
+ message: string;
30
+ }
31
+
32
+ export interface AbapError {
33
+ name: 'ABAPError';
34
+ group: number;
35
+ code: number;
36
+ codeString: string;
37
+ key: string;
38
+ message: string;
39
+ abapMsgClass: string;
40
+ abapMsgType: string;
41
+ abapMsgNumber: string;
42
+ abapMsgV1: string;
43
+ abapMsgV2: string;
44
+ abapMsgV3: string;
45
+ abapMsgV4: string;
46
+ }
47
+
48
+ export interface NodeRfcError {
49
+ name: 'nodeRfcError';
50
+ message: string;
51
+ }
52
+
53
+ export type RfcError = RfcLibError | AbapError | NodeRfcError;