@abapify/adt-rfc 0.4.0 → 0.4.3

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,125 @@
1
+ //#region src/lib/transport/types.d.ts
2
+ /**
3
+ * RFC transport primitives.
4
+ *
5
+ * Flat value types kept deliberately loose — SAP RFC function modules
6
+ * accept scalars, structures (objects), and tables (arrays of objects).
7
+ */
8
+ /** A scalar RFC value — string, number, boolean, or bytes (base64) — or null. */
9
+ type RfcScalar = string | number | boolean | null;
10
+ /** A structure — flat field->scalar mapping. Nested structures allowed. */
11
+ interface RfcStructure {
12
+ [field: string]: RfcScalar | RfcStructure | RfcTable;
13
+ }
14
+ /** A table — array of structures. */
15
+ type RfcTable = RfcStructure[];
16
+ /** Top-level RFC parameter map. */
17
+ type RfcParams = Record<string, RfcScalar | RfcStructure | RfcTable>;
18
+ /** Top-level RFC response map (same shape). */
19
+ type RfcResponse = Record<string, RfcScalar | RfcStructure | RfcTable>;
20
+ /**
21
+ * Options for one RFC call.
22
+ */
23
+ interface RfcCallOptions {
24
+ /** Explicit sap-client override (otherwise uses the client's default). */
25
+ client?: string;
26
+ /** Abort signal. */
27
+ signal?: AbortSignal;
28
+ }
29
+ /**
30
+ * SOAP fault raised when the server returns a `soap:Fault`.
31
+ */
32
+ declare class RfcSoapFault extends Error {
33
+ readonly faultcode: string;
34
+ readonly faultstring: string;
35
+ readonly raw: string;
36
+ constructor(faultcode: string, faultstring: string, raw: string);
37
+ }
38
+ /**
39
+ * Raised when the HTTP endpoint /sap/bc/soap/rfc is unavailable
40
+ * (401/403/404) — useful for gracefully skipping real-e2e tests
41
+ * against systems where SOAP-RFC is disabled.
42
+ */
43
+ declare class RfcTransportUnavailable extends Error {
44
+ readonly status: number;
45
+ readonly url: string;
46
+ readonly body: string;
47
+ constructor(status: number, url: string, body: string);
48
+ }
49
+ //#endregion
50
+ //#region src/lib/client/rfc-client.d.ts
51
+ /**
52
+ * Raw fetcher signature the RFC client depends on.
53
+ * Matches `AdtClient.fetch()` — callers pass
54
+ * `(url, options) => client.fetch(url, options)`.
55
+ */
56
+ type RawFetcher = (url: string, options: {
57
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
58
+ headers?: Record<string, string>;
59
+ body?: string;
60
+ }) => Promise<unknown>;
61
+ interface RfcClientConfig {
62
+ /**
63
+ * A fetcher that performs authenticated HTTP requests against the SAP
64
+ * system. Must already handle CSRF / session cookies.
65
+ *
66
+ * Signature matches `AdtClient.fetch()`.
67
+ */
68
+ fetch: RawFetcher;
69
+ /**
70
+ * Default `sap-client` (e.g. "100") appended as query parameter.
71
+ * Optional — most SAP deployments can infer it from the cookie.
72
+ */
73
+ client?: string;
74
+ }
75
+ interface RfcClient {
76
+ /**
77
+ * Invoke an RFC function module synchronously.
78
+ *
79
+ * @param fmName - Function module name (case-insensitive).
80
+ * @param params - Importing / changing / tables parameters (flat map).
81
+ * @param options - Per-call overrides.
82
+ */
83
+ call(fmName: string, params?: RfcParams, options?: RfcCallOptions): Promise<RfcResponse>;
84
+ }
85
+ /**
86
+ * Create an RFC client backed by the SOAP-over-HTTP transport.
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * import { createAdtClient } from '@abapify/adt-client';
91
+ * import { createRfcClient } from '@abapify/adt-rfc';
92
+ *
93
+ * const adt = createAdtClient({ …auth });
94
+ * const rfc = createRfcClient({ fetch: (url, opts) => adt.fetch(url, opts) });
95
+ *
96
+ * const resp = await rfc.call('STFC_CONNECTION', { REQUTEXT: 'hello' });
97
+ * console.log(resp.ECHOTEXT); // "hello"
98
+ * ```
99
+ */
100
+ declare function createRfcClient(config: RfcClientConfig): RfcClient;
101
+ //#endregion
102
+ //#region src/lib/transport/soap-rfc.d.ts
103
+ /** SAP RFC SOAP namespace — fixed. */
104
+ declare const RFC_SOAP_NS = "urn:sap-com:document:sap:rfc:functions";
105
+ /** SOAP 1.1 envelope namespace. */
106
+ declare const SOAP_ENV_NS = "http://schemas.xmlsoap.org/soap/envelope/";
107
+ /**
108
+ * Build the SOAP request envelope for an RFC function module call.
109
+ *
110
+ * @param fmName - The RFC function module name (will be upper-cased).
111
+ * @param params - Flat map of importing / changing / tables parameters.
112
+ * @returns XML string suitable for `POST /sap/bc/soap/rfc` with
113
+ * `Content-Type: text/xml; charset=utf-8`.
114
+ */
115
+ declare function buildRfcSoapEnvelope(fmName: string, params?: RfcParams): string;
116
+ /**
117
+ * Parse a SOAP-RFC response envelope and return the RFC response params.
118
+ *
119
+ * @param xml - Raw response body as returned by `/sap/bc/soap/rfc`.
120
+ * @throws {@link RfcSoapFault} if the envelope contains a `soap:Fault`.
121
+ */
122
+ declare function parseRfcSoapResponse(xml: string): RfcResponse;
123
+ //#endregion
124
+ export { RFC_SOAP_NS, type RawFetcher, type RfcCallOptions, type RfcClient, type RfcClientConfig, type RfcParams, type RfcResponse, type RfcScalar, RfcSoapFault, type RfcStructure, type RfcTable, RfcTransportUnavailable, SOAP_ENV_NS, buildRfcSoapEnvelope, createRfcClient, parseRfcSoapResponse };
125
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/lib/transport/types.ts","../src/lib/client/rfc-client.ts","../src/lib/transport/soap-rfc.ts"],"mappings":";;;;;;;;KAQY;;UAGK;GACd,gBAAgB,YAAY,eAAe;;;KAIlC,WAAW;;KAGX,YAAY,eAAe,YAAY,eAAe;;KAGtD,cAAc,eAAe,YAAY,eAAe;;;;UAKnD;;EAEf;;EAEA,SAAS;;;;;cAME,qBAAqB;WAEd;WACA;WACA;EAFA,YAAA,mBACA,qBACA;;;;;;;cAYP,gCAAgC;WAEzB;WACA;WACA;EAFA,YAAA,gBACA,aACA;;;;;;;;;KC/BR,cACV,aACA;EACE;EACA,UAAU;EACV;MAEC;UAEY;;;;;;;EAOf,OAAO;;;;;EAMP;;UAGe;;;;;;;;EAQf,KACE,gBACA,SAAS,WACT,UAAU,iBACT,QAAQ;;;;;;;;;;;;;;;;;iBAwBG,gBAAgB,QAAQ,kBAAkB;;;;cC7D7C;;cAGA;;;;;;;;;iBAsDG,qBACd,gBACA,SAAQ;;;;;;;iBAmLM,qBAAqB,cAAc"}
package/dist/index.mjs ADDED
@@ -0,0 +1,302 @@
1
+ //#region src/lib/transport/types.ts
2
+ /**
3
+ * SOAP fault raised when the server returns a `soap:Fault`.
4
+ */
5
+ var RfcSoapFault = class extends Error {
6
+ faultcode;
7
+ faultstring;
8
+ raw;
9
+ constructor(faultcode, faultstring, raw) {
10
+ super(`SOAP Fault: ${faultcode}: ${faultstring}`);
11
+ this.faultcode = faultcode;
12
+ this.faultstring = faultstring;
13
+ this.raw = raw;
14
+ this.name = "RfcSoapFault";
15
+ }
16
+ };
17
+ /**
18
+ * Raised when the HTTP endpoint /sap/bc/soap/rfc is unavailable
19
+ * (401/403/404) — useful for gracefully skipping real-e2e tests
20
+ * against systems where SOAP-RFC is disabled.
21
+ */
22
+ var RfcTransportUnavailable = class extends Error {
23
+ status;
24
+ url;
25
+ body;
26
+ constructor(status, url, body) {
27
+ super(`SOAP-RFC transport unavailable at ${url}: HTTP ${status}. The endpoint /sap/bc/soap/rfc may be disabled on this SAP system.`);
28
+ this.status = status;
29
+ this.url = url;
30
+ this.body = body;
31
+ this.name = "RfcTransportUnavailable";
32
+ }
33
+ };
34
+
35
+ //#endregion
36
+ //#region src/lib/transport/soap-rfc.ts
37
+ /**
38
+ * SOAP-over-HTTP RFC transport for SAP.
39
+ *
40
+ * Wraps an RFC call in a SOAP 1.1 envelope targeted at
41
+ * `/sap/bc/soap/rfc?sap-client=<n>` — the SAP NetWeaver built-in
42
+ * wrapper around the NW RFC library. Avoids the native `libsapnwrfc`
43
+ * dependency of node-rfc at the cost of a slightly restricted
44
+ * parameter type space (scalars, structures, tables).
45
+ *
46
+ * No third-party XML parser is used — SAP's SOAP output for RFC is
47
+ * well-formed, stable, and simple enough to handle with a minimal
48
+ * hand-rolled parser (see {@link parseRfcSoapResponse}).
49
+ *
50
+ * Reference: https://wiki.scn.sap.com/wiki/display/Snippets/Calling+RFC+enabled+Function+Modules+via+SOAP
51
+ */
52
+ /** SAP RFC SOAP namespace — fixed. */
53
+ const RFC_SOAP_NS = "urn:sap-com:document:sap:rfc:functions";
54
+ /** SOAP 1.1 envelope namespace. */
55
+ const SOAP_ENV_NS = "http://schemas.xmlsoap.org/soap/envelope/";
56
+ function escapeXml(value) {
57
+ return value.replaceAll(/&/g, "&amp;").replaceAll(/</g, "&lt;").replaceAll(/>/g, "&gt;");
58
+ }
59
+ function renderScalar(value) {
60
+ if (value === null || value === void 0) return "";
61
+ if (typeof value === "boolean") return value ? "X" : " ";
62
+ return escapeXml(String(value));
63
+ }
64
+ function renderStructure(obj) {
65
+ let out = "";
66
+ for (const [field, v] of Object.entries(obj)) out += renderField(field, v);
67
+ return out;
68
+ }
69
+ function renderTable(name, rows) {
70
+ return `<${name}>${rows.map((row) => `<item>${renderStructure(row)}</item>`).join("")}</${name}>`;
71
+ }
72
+ function renderField(name, value) {
73
+ if (Array.isArray(value)) return renderTable(name, value);
74
+ if (value !== null && typeof value === "object") return `<${name}>${renderStructure(value)}</${name}>`;
75
+ return `<${name}>${renderScalar(value)}</${name}>`;
76
+ }
77
+ /**
78
+ * Build the SOAP request envelope for an RFC function module call.
79
+ *
80
+ * @param fmName - The RFC function module name (will be upper-cased).
81
+ * @param params - Flat map of importing / changing / tables parameters.
82
+ * @returns XML string suitable for `POST /sap/bc/soap/rfc` with
83
+ * `Content-Type: text/xml; charset=utf-8`.
84
+ */
85
+ function buildRfcSoapEnvelope(fmName, params = {}) {
86
+ const fm = fmName.toUpperCase();
87
+ const body = renderStructure(params);
88
+ return `<?xml version="1.0" encoding="utf-8"?><soap-env:Envelope xmlns:soap-env="${SOAP_ENV_NS}"><soap-env:Body><urn:${fm} xmlns:urn="${RFC_SOAP_NS}">` + body + `</urn:${fm}></soap-env:Body></soap-env:Envelope>`;
89
+ }
90
+ const TAG_NAME_RE = /^\/?([A-Za-z_][\w.:-]*)/;
91
+ function tagNameOf(inside) {
92
+ return TAG_NAME_RE.exec(inside)?.[1];
93
+ }
94
+ function tokenize(xml) {
95
+ const tokens = [];
96
+ const parts = xml.split("<");
97
+ if (parts[0] && parts[0].trim().length > 0) tokens.push({
98
+ kind: "text",
99
+ text: parts[0]
100
+ });
101
+ for (let i = 1; i < parts.length; i++) {
102
+ const part = parts[i];
103
+ const gt = part.indexOf(">");
104
+ if (gt < 0) {
105
+ if (part.trim().length > 0) tokens.push({
106
+ kind: "text",
107
+ text: "<" + part
108
+ });
109
+ continue;
110
+ }
111
+ const inside = part.slice(0, gt);
112
+ const trailing = part.slice(gt + 1);
113
+ if (inside.startsWith("?") || inside.startsWith("!--") || inside.startsWith("!DOCTYPE") || inside.startsWith("!")) {
114
+ if (trailing && trailing.trim().length > 0) tokens.push({
115
+ kind: "text",
116
+ text: trailing
117
+ });
118
+ continue;
119
+ }
120
+ const name = tagNameOf(inside);
121
+ if (!name) tokens.push({
122
+ kind: "text",
123
+ text: "<" + inside + ">"
124
+ });
125
+ else if (inside.startsWith("/")) tokens.push({
126
+ kind: "close",
127
+ name
128
+ });
129
+ else if (inside.endsWith("/")) tokens.push({
130
+ kind: "self",
131
+ name
132
+ });
133
+ else tokens.push({
134
+ kind: "open",
135
+ name
136
+ });
137
+ if (trailing && trailing.trim().length > 0) tokens.push({
138
+ kind: "text",
139
+ text: trailing
140
+ });
141
+ }
142
+ return tokens;
143
+ }
144
+ function localName(qname) {
145
+ const i = qname.indexOf(":");
146
+ return i < 0 ? qname : qname.slice(i + 1);
147
+ }
148
+ function unescapeXml(s) {
149
+ return s.replaceAll(/&lt;/g, "<").replaceAll(/&gt;/g, ">").replaceAll(/&quot;/g, "\"").replaceAll(/&apos;/g, "'").replaceAll(/&amp;/g, "&");
150
+ }
151
+ function newNode() {
152
+ return {
153
+ text: "",
154
+ children: []
155
+ };
156
+ }
157
+ /**
158
+ * Parse an XML fragment (no `<?xml …?>` prolog required) into a tree of
159
+ * `ParsedNode`s keyed by local element name. Repeated element names are
160
+ * preserved as separate `children` entries (so `<item>` tables parse as
161
+ * multiple children with the same name).
162
+ */
163
+ function parseXmlFragment(xml) {
164
+ const tokens = tokenize(xml);
165
+ const root = newNode();
166
+ const stack = [root];
167
+ for (const tok of tokens) {
168
+ const top = stack.at(-1);
169
+ if (tok.kind === "open") {
170
+ const child = newNode();
171
+ top.children.push({
172
+ name: localName(tok.name),
173
+ node: child
174
+ });
175
+ stack.push(child);
176
+ } else if (tok.kind === "close") stack.pop();
177
+ else if (tok.kind === "self") {
178
+ const child = newNode();
179
+ top.children.push({
180
+ name: localName(tok.name),
181
+ node: child
182
+ });
183
+ } else if (tok.kind === "text") top.text += tok.text ?? "";
184
+ }
185
+ return root;
186
+ }
187
+ /**
188
+ * Convert a `ParsedNode` into a JS value:
189
+ * - leaf → string (unescaped)
190
+ * - mixed children with all name="item" → array of objects (table)
191
+ * - object otherwise (structure)
192
+ */
193
+ function nodeToValue(node) {
194
+ if (node.children.length === 0) return unescapeXml(node.text.trim());
195
+ if (node.children.length > 0 && node.children.every((c) => c.name.toLowerCase() === "item")) return node.children.map((c) => nodeToValue(c.node));
196
+ const obj = {};
197
+ for (const { name, node: child } of node.children) {
198
+ const val = nodeToValue(child);
199
+ if (name in obj) {
200
+ const existing = obj[name];
201
+ if (Array.isArray(existing)) existing.push(val);
202
+ else obj[name] = [existing, val];
203
+ } else obj[name] = val;
204
+ }
205
+ return obj;
206
+ }
207
+ /**
208
+ * Parse a SOAP-RFC response envelope and return the RFC response params.
209
+ *
210
+ * @param xml - Raw response body as returned by `/sap/bc/soap/rfc`.
211
+ * @throws {@link RfcSoapFault} if the envelope contains a `soap:Fault`.
212
+ */
213
+ function parseRfcSoapResponse(xml) {
214
+ const envelope = parseXmlFragment(xml).children.find((c) => c.name.toLowerCase() === "envelope");
215
+ if (!envelope) throw new Error("Invalid SOAP response: no <Envelope> element found. Got: " + xml.slice(0, 200));
216
+ const body = envelope.node.children.find((c) => c.name.toLowerCase() === "body");
217
+ if (!body) throw new Error("Invalid SOAP response: no <Body> element inside envelope");
218
+ const fault = body.node.children.find((c) => c.name.toLowerCase() === "fault");
219
+ if (fault) {
220
+ const faultObj = nodeToValue(fault.node);
221
+ const faultcode = String(faultObj.faultcode ?? "Server");
222
+ const faultstring = String(faultObj.faultstring ?? "Unknown SOAP fault");
223
+ throw new RfcSoapFault(faultcode, faultstring, xml);
224
+ }
225
+ const rfcRoot = body.node.children[0];
226
+ if (!rfcRoot) return {};
227
+ const result = nodeToValue(rfcRoot.node);
228
+ if (result === null || typeof result !== "object") return {};
229
+ if (Array.isArray(result)) return { items: result };
230
+ return result;
231
+ }
232
+
233
+ //#endregion
234
+ //#region src/lib/client/rfc-client.ts
235
+ /**
236
+ * High-level RFC client — SOAP-over-HTTP transport by default.
237
+ *
238
+ * The client is deliberately decoupled from `@abapify/adt-client` at the
239
+ * type level: callers pass a *fetcher* function matching the minimal
240
+ * shape of `AdtClient.fetch()`. This keeps `adt-rfc` a leaf package
241
+ * with no workspace dependencies (and avoids a circular graph once the
242
+ * CLI wires the two together).
243
+ */
244
+ function buildSoapRfcUrl(client) {
245
+ const base = "/sap/bc/soap/rfc";
246
+ if (!client) return base;
247
+ return `${base}?sap-client=${encodeURIComponent(client)}`;
248
+ }
249
+ /**
250
+ * Create an RFC client backed by the SOAP-over-HTTP transport.
251
+ *
252
+ * @example
253
+ * ```ts
254
+ * import { createAdtClient } from '@abapify/adt-client';
255
+ * import { createRfcClient } from '@abapify/adt-rfc';
256
+ *
257
+ * const adt = createAdtClient({ …auth });
258
+ * const rfc = createRfcClient({ fetch: (url, opts) => adt.fetch(url, opts) });
259
+ *
260
+ * const resp = await rfc.call('STFC_CONNECTION', { REQUTEXT: 'hello' });
261
+ * console.log(resp.ECHOTEXT); // "hello"
262
+ * ```
263
+ */
264
+ function createRfcClient(config) {
265
+ return { async call(fmName, params = {}, options = {}) {
266
+ const envelope = buildRfcSoapEnvelope(fmName, params);
267
+ const url = buildSoapRfcUrl(options.client ?? config.client);
268
+ let response;
269
+ try {
270
+ response = await config.fetch(url, {
271
+ method: "POST",
272
+ headers: {
273
+ "Content-Type": "text/xml; charset=utf-8",
274
+ SOAPAction: "\"\"",
275
+ Accept: "text/xml"
276
+ },
277
+ body: envelope
278
+ });
279
+ } catch (err) {
280
+ const msg = err instanceof Error ? err.message : String(err);
281
+ const statusMatch = /\b(401|403|404|501)\b/.exec(msg);
282
+ if (statusMatch) throw new RfcTransportUnavailable(Number(statusMatch[1]), url, msg);
283
+ throw err;
284
+ }
285
+ const coerceToXml = (value) => {
286
+ if (typeof value === "string") return value;
287
+ if (value === null || value === void 0) return "";
288
+ if (typeof value === "object") try {
289
+ return JSON.stringify(value);
290
+ } catch {
291
+ return String(value);
292
+ }
293
+ return String(value);
294
+ };
295
+ const xml = coerceToXml(response);
296
+ return parseRfcSoapResponse(xml);
297
+ } };
298
+ }
299
+
300
+ //#endregion
301
+ export { RFC_SOAP_NS, RfcSoapFault, RfcTransportUnavailable, SOAP_ENV_NS, buildRfcSoapEnvelope, createRfcClient, parseRfcSoapResponse };
302
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/lib/transport/types.ts","../src/lib/transport/soap-rfc.ts","../src/lib/client/rfc-client.ts"],"sourcesContent":["/**\n * RFC transport primitives.\n *\n * Flat value types kept deliberately loose — SAP RFC function modules\n * accept scalars, structures (objects), and tables (arrays of objects).\n */\n\n/** A scalar RFC value — string, number, boolean, or bytes (base64) — or null. */\nexport type RfcScalar = string | number | boolean | null;\n\n/** A structure — flat field->scalar mapping. Nested structures allowed. */\nexport interface RfcStructure {\n [field: string]: RfcScalar | RfcStructure | RfcTable;\n}\n\n/** A table — array of structures. */\nexport type RfcTable = RfcStructure[];\n\n/** Top-level RFC parameter map. */\nexport type RfcParams = Record<string, RfcScalar | RfcStructure | RfcTable>;\n\n/** Top-level RFC response map (same shape). */\nexport type RfcResponse = Record<string, RfcScalar | RfcStructure | RfcTable>;\n\n/**\n * Options for one RFC call.\n */\nexport interface RfcCallOptions {\n /** Explicit sap-client override (otherwise uses the client's default). */\n client?: string;\n /** Abort signal. */\n signal?: AbortSignal;\n}\n\n/**\n * SOAP fault raised when the server returns a `soap:Fault`.\n */\nexport class RfcSoapFault extends Error {\n constructor(\n public readonly faultcode: string,\n public readonly faultstring: string,\n public readonly raw: string,\n ) {\n super(`SOAP Fault: ${faultcode}: ${faultstring}`);\n this.name = 'RfcSoapFault';\n }\n}\n\n/**\n * Raised when the HTTP endpoint /sap/bc/soap/rfc is unavailable\n * (401/403/404) — useful for gracefully skipping real-e2e tests\n * against systems where SOAP-RFC is disabled.\n */\nexport class RfcTransportUnavailable extends Error {\n constructor(\n public readonly status: number,\n public readonly url: string,\n public readonly body: string,\n ) {\n super(\n `SOAP-RFC transport unavailable at ${url}: HTTP ${status}. ` +\n `The endpoint /sap/bc/soap/rfc may be disabled on this SAP system.`,\n );\n this.name = 'RfcTransportUnavailable';\n }\n}\n","/**\n * SOAP-over-HTTP RFC transport for SAP.\n *\n * Wraps an RFC call in a SOAP 1.1 envelope targeted at\n * `/sap/bc/soap/rfc?sap-client=<n>` — the SAP NetWeaver built-in\n * wrapper around the NW RFC library. Avoids the native `libsapnwrfc`\n * dependency of node-rfc at the cost of a slightly restricted\n * parameter type space (scalars, structures, tables).\n *\n * No third-party XML parser is used — SAP's SOAP output for RFC is\n * well-formed, stable, and simple enough to handle with a minimal\n * hand-rolled parser (see {@link parseRfcSoapResponse}).\n *\n * Reference: https://wiki.scn.sap.com/wiki/display/Snippets/Calling+RFC+enabled+Function+Modules+via+SOAP\n */\n\nimport {\n RfcSoapFault,\n type RfcParams,\n type RfcResponse,\n type RfcScalar,\n type RfcStructure,\n type RfcTable,\n} from './types';\n\n/** SAP RFC SOAP namespace — fixed. */\nexport const RFC_SOAP_NS = 'urn:sap-com:document:sap:rfc:functions';\n\n/** SOAP 1.1 envelope namespace. */\nexport const SOAP_ENV_NS = 'http://schemas.xmlsoap.org/soap/envelope/';\n\n// ────────────────────────────────────────────────────────────────────────────\n// Envelope builder\n// ────────────────────────────────────────────────────────────────────────────\n\nfunction escapeXml(value: string): string {\n return value\n .replaceAll(/&/g, '&amp;')\n .replaceAll(/</g, '&lt;')\n .replaceAll(/>/g, '&gt;');\n}\n\nfunction renderScalar(value: RfcScalar): string {\n if (value === null || value === undefined) return '';\n if (typeof value === 'boolean') return value ? 'X' : ' ';\n return escapeXml(String(value));\n}\n\nfunction renderStructure(obj: RfcStructure): string {\n let out = '';\n for (const [field, v] of Object.entries(obj)) {\n out += renderField(field, v);\n }\n return out;\n}\n\nfunction renderTable(name: string, rows: RfcTable): string {\n // SAP RFC SOAP convention: <TABLE_NAME><item>…</item><item>…</item></TABLE_NAME>\n const items = rows\n .map((row) => `<item>${renderStructure(row)}</item>`)\n .join('');\n return `<${name}>${items}</${name}>`;\n}\n\nfunction renderField(\n name: string,\n value: RfcScalar | RfcStructure | RfcTable,\n): string {\n if (Array.isArray(value)) return renderTable(name, value);\n if (value !== null && typeof value === 'object') {\n return `<${name}>${renderStructure(value)}</${name}>`;\n }\n return `<${name}>${renderScalar(value)}</${name}>`;\n}\n\n/**\n * Build the SOAP request envelope for an RFC function module call.\n *\n * @param fmName - The RFC function module name (will be upper-cased).\n * @param params - Flat map of importing / changing / tables parameters.\n * @returns XML string suitable for `POST /sap/bc/soap/rfc` with\n * `Content-Type: text/xml; charset=utf-8`.\n */\nexport function buildRfcSoapEnvelope(\n fmName: string,\n params: RfcParams = {},\n): string {\n const fm = fmName.toUpperCase();\n const body = renderStructure(params as RfcStructure);\n\n return (\n `<?xml version=\"1.0\" encoding=\"utf-8\"?>` +\n `<soap-env:Envelope xmlns:soap-env=\"${SOAP_ENV_NS}\">` +\n `<soap-env:Body>` +\n `<urn:${fm} xmlns:urn=\"${RFC_SOAP_NS}\">` +\n body +\n `</urn:${fm}>` +\n `</soap-env:Body>` +\n `</soap-env:Envelope>`\n );\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Envelope parser — minimal, regex + stack based\n// ────────────────────────────────────────────────────────────────────────────\n\ninterface Token {\n kind: 'open' | 'close' | 'self' | 'text';\n name?: string;\n text?: string;\n}\n\n// Linear tokenizer via split on '<' + indexOf('>'). Zero regex on the\n// hot path means CodeQL cannot flag it as polynomial-redos and the\n// runtime is guaranteed O(N).\nconst TAG_NAME_RE = /^\\/?([A-Za-z_][\\w.:-]*)/;\n\nfunction tagNameOf(inside: string): string | undefined {\n const m = TAG_NAME_RE.exec(inside);\n return m?.[1];\n}\n\nfunction tokenize(xml: string): Token[] {\n const tokens: Token[] = [];\n const parts = xml.split('<');\n // parts[0] is whatever text precedes the first '<'.\n if (parts[0] && parts[0].trim().length > 0) {\n tokens.push({ kind: 'text', text: parts[0] });\n }\n for (let i = 1; i < parts.length; i++) {\n const part = parts[i];\n const gt = part.indexOf('>');\n if (gt < 0) {\n // malformed (no '>'): treat everything as literal text\n if (part.trim().length > 0) {\n tokens.push({ kind: 'text', text: '<' + part });\n }\n continue;\n }\n const inside = part.slice(0, gt);\n const trailing = part.slice(gt + 1);\n // Skip XML prolog (<?...?>), DOCTYPE, comments (<!-- -->), and any\n // directive (<!...) — non-structural for RFC parsing.\n if (\n inside.startsWith('?') ||\n inside.startsWith('!--') ||\n inside.startsWith('!DOCTYPE') ||\n inside.startsWith('!')\n ) {\n if (trailing && trailing.trim().length > 0) {\n tokens.push({ kind: 'text', text: trailing });\n }\n continue;\n }\n const name = tagNameOf(inside);\n if (!name) {\n tokens.push({ kind: 'text', text: '<' + inside + '>' });\n } else if (inside.startsWith('/')) {\n tokens.push({ kind: 'close', name });\n } else if (inside.endsWith('/')) {\n tokens.push({ kind: 'self', name });\n } else {\n tokens.push({ kind: 'open', name });\n }\n if (trailing && trailing.trim().length > 0) {\n tokens.push({ kind: 'text', text: trailing });\n }\n }\n return tokens;\n}\n\nfunction localName(qname: string): string {\n const i = qname.indexOf(':');\n return i < 0 ? qname : qname.slice(i + 1);\n}\n\nfunction unescapeXml(s: string): string {\n return s\n .replaceAll(/&lt;/g, '<')\n .replaceAll(/&gt;/g, '>')\n .replaceAll(/&quot;/g, '\"')\n .replaceAll(/&apos;/g, \"'\")\n .replaceAll(/&amp;/g, '&');\n}\n\ninterface ParsedNode {\n text: string;\n children: Array<{ name: string; node: ParsedNode }>;\n}\n\nfunction newNode(): ParsedNode {\n return { text: '', children: [] };\n}\n\n/**\n * Parse an XML fragment (no `<?xml …?>` prolog required) into a tree of\n * `ParsedNode`s keyed by local element name. Repeated element names are\n * preserved as separate `children` entries (so `<item>` tables parse as\n * multiple children with the same name).\n */\nfunction parseXmlFragment(xml: string): ParsedNode {\n const tokens = tokenize(xml);\n const root = newNode();\n const stack: ParsedNode[] = [root];\n\n for (const tok of tokens) {\n const top = stack.at(-1)!;\n if (tok.kind === 'open') {\n const child = newNode();\n top.children.push({ name: localName(tok.name!), node: child });\n stack.push(child);\n } else if (tok.kind === 'close') {\n stack.pop();\n } else if (tok.kind === 'self') {\n const child = newNode();\n top.children.push({ name: localName(tok.name!), node: child });\n } else if (tok.kind === 'text') {\n top.text += tok.text ?? '';\n }\n }\n return root;\n}\n\n/**\n * Convert a `ParsedNode` into a JS value:\n * - leaf → string (unescaped)\n * - mixed children with all name=\"item\" → array of objects (table)\n * - object otherwise (structure)\n */\nfunction nodeToValue(node: ParsedNode): RfcScalar | RfcStructure | RfcTable {\n if (node.children.length === 0) {\n return unescapeXml(node.text.trim());\n }\n // table detection: all children named \"item\" (case-insensitive)\n const allItems =\n node.children.length > 0 &&\n node.children.every((c) => c.name.toLowerCase() === 'item');\n if (allItems) {\n return node.children.map((c) => nodeToValue(c.node) as RfcStructure);\n }\n // structure: collapse children into an object; duplicate names → arrays\n const obj: RfcStructure = {};\n for (const { name, node: child } of node.children) {\n const val = nodeToValue(child);\n if (name in obj) {\n const existing = obj[name];\n if (Array.isArray(existing)) {\n existing.push(val as RfcStructure);\n } else {\n obj[name] = [existing as unknown as RfcStructure, val as RfcStructure];\n }\n } else {\n obj[name] = val;\n }\n }\n return obj;\n}\n\n/**\n * Parse a SOAP-RFC response envelope and return the RFC response params.\n *\n * @param xml - Raw response body as returned by `/sap/bc/soap/rfc`.\n * @throws {@link RfcSoapFault} if the envelope contains a `soap:Fault`.\n */\nexport function parseRfcSoapResponse(xml: string): RfcResponse {\n const root = parseXmlFragment(xml);\n\n // Find soap:Envelope/soap:Body\n const envelope = root.children.find(\n (c) => c.name.toLowerCase() === 'envelope',\n );\n if (!envelope) {\n throw new Error(\n 'Invalid SOAP response: no <Envelope> element found. Got: ' +\n xml.slice(0, 200),\n );\n }\n const body = envelope.node.children.find(\n (c) => c.name.toLowerCase() === 'body',\n );\n if (!body) {\n throw new Error('Invalid SOAP response: no <Body> element inside envelope');\n }\n\n // Check for Fault first\n const fault = body.node.children.find(\n (c) => c.name.toLowerCase() === 'fault',\n );\n if (fault) {\n const faultObj = nodeToValue(fault.node) as RfcStructure;\n const faultcode = String(\n (faultObj.faultcode as string | number | undefined) ?? 'Server',\n );\n const faultstring = String(\n (faultObj.faultstring as string | number | undefined) ??\n 'Unknown SOAP fault',\n );\n throw new RfcSoapFault(faultcode, faultstring, xml);\n }\n\n // The RFC response is the single non-fault child of <Body>.\n // SAP names it either `<FMNAME.Response>` or `<FMNAMEResponse>` depending\n // on system version.\n const rfcRoot = body.node.children[0];\n if (!rfcRoot) return {};\n\n const result = nodeToValue(rfcRoot.node);\n if (result === null || typeof result !== 'object') {\n // degenerate scalar response (string, number, boolean, null)\n return {};\n }\n if (Array.isArray(result)) {\n // should not happen for an RFC response root\n return { items: result };\n }\n return result;\n}\n","/**\n * High-level RFC client — SOAP-over-HTTP transport by default.\n *\n * The client is deliberately decoupled from `@abapify/adt-client` at the\n * type level: callers pass a *fetcher* function matching the minimal\n * shape of `AdtClient.fetch()`. This keeps `adt-rfc` a leaf package\n * with no workspace dependencies (and avoids a circular graph once the\n * CLI wires the two together).\n */\n\nimport {\n buildRfcSoapEnvelope,\n parseRfcSoapResponse,\n} from '../transport/soap-rfc';\nimport {\n RfcTransportUnavailable,\n type RfcCallOptions,\n type RfcParams,\n type RfcResponse,\n} from '../transport/types';\n\n/**\n * Raw fetcher signature the RFC client depends on.\n * Matches `AdtClient.fetch()` — callers pass\n * `(url, options) => client.fetch(url, options)`.\n */\nexport type RawFetcher = (\n url: string,\n options: {\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n headers?: Record<string, string>;\n body?: string;\n },\n) => Promise<unknown>;\n\nexport interface RfcClientConfig {\n /**\n * A fetcher that performs authenticated HTTP requests against the SAP\n * system. Must already handle CSRF / session cookies.\n *\n * Signature matches `AdtClient.fetch()`.\n */\n fetch: RawFetcher;\n\n /**\n * Default `sap-client` (e.g. \"100\") appended as query parameter.\n * Optional — most SAP deployments can infer it from the cookie.\n */\n client?: string;\n}\n\nexport interface RfcClient {\n /**\n * Invoke an RFC function module synchronously.\n *\n * @param fmName - Function module name (case-insensitive).\n * @param params - Importing / changing / tables parameters (flat map).\n * @param options - Per-call overrides.\n */\n call(\n fmName: string,\n params?: RfcParams,\n options?: RfcCallOptions,\n ): Promise<RfcResponse>;\n}\n\nfunction buildSoapRfcUrl(client?: string): string {\n const base = '/sap/bc/soap/rfc';\n if (!client) return base;\n return `${base}?sap-client=${encodeURIComponent(client)}`;\n}\n\n/**\n * Create an RFC client backed by the SOAP-over-HTTP transport.\n *\n * @example\n * ```ts\n * import { createAdtClient } from '@abapify/adt-client';\n * import { createRfcClient } from '@abapify/adt-rfc';\n *\n * const adt = createAdtClient({ …auth });\n * const rfc = createRfcClient({ fetch: (url, opts) => adt.fetch(url, opts) });\n *\n * const resp = await rfc.call('STFC_CONNECTION', { REQUTEXT: 'hello' });\n * console.log(resp.ECHOTEXT); // \"hello\"\n * ```\n */\nexport function createRfcClient(config: RfcClientConfig): RfcClient {\n return {\n async call(\n fmName: string,\n params: RfcParams = {},\n options: RfcCallOptions = {},\n ): Promise<RfcResponse> {\n const envelope = buildRfcSoapEnvelope(fmName, params);\n const url = buildSoapRfcUrl(options.client ?? config.client);\n\n let response: unknown;\n try {\n response = await config.fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'text/xml; charset=utf-8',\n // SAP expects SOAPAction — empty is fine for RFC SOAP wrapper.\n SOAPAction: '\"\"',\n Accept: 'text/xml',\n },\n body: envelope,\n });\n } catch (err) {\n // Detect HTTP-level \"endpoint not available\" errors raised by the\n // underlying fetcher. `AdtClient.fetch()` throws errors whose\n // `message` usually contains the status code.\n const msg = err instanceof Error ? err.message : String(err);\n const statusMatch = /\\b(401|403|404|501)\\b/.exec(msg);\n if (statusMatch) {\n throw new RfcTransportUnavailable(Number(statusMatch[1]), url, msg);\n }\n throw err;\n }\n\n // Fallback coercion of the fetch result to XML text. JSON.stringify\n // can throw on circular refs or BigInt; guard against that so an\n // unrelated serialization error doesn't mask the real SOAP failure\n // downstream in parseRfcSoapResponse.\n const coerceToXml = (value: unknown): string => {\n if (typeof value === 'string') return value;\n if (value === null || value === undefined) return '';\n if (typeof value === 'object') {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n }\n return String(value);\n };\n const xml = coerceToXml(response);\n return parseRfcSoapResponse(xml);\n },\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.4.0",
6
+ "version": "0.4.3",
7
7
  "description": "SAP RFC transport for @abapify — SOAP-over-HTTP RFC implementation (/sap/bc/soap/rfc)",
8
8
  "type": "module",
9
9
  "types": "./dist/index.d.mts",
@@ -23,7 +23,7 @@
23
23
  "abap",
24
24
  "startrfc"
25
25
  ],
26
- "author": "Booking.com",
26
+ "author": "abapify",
27
27
  "license": "MIT",
28
28
  "repository": {
29
29
  "type": "git",