@mcp-abap-adt/adt-clients 32.0.0 → 32.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.
Files changed (63) hide show
  1. package/README.md +29 -1
  2. package/dist/batch/AdtBatchConnection.d.ts +29 -0
  3. package/dist/batch/AdtBatchConnection.d.ts.map +1 -0
  4. package/dist/batch/AdtBatchConnection.js +155 -0
  5. package/dist/batch/index.d.ts +2 -0
  6. package/dist/batch/index.d.ts.map +1 -0
  7. package/dist/batch/index.js +5 -0
  8. package/dist/batch/wire.d.ts +10 -0
  9. package/dist/batch/wire.d.ts.map +1 -0
  10. package/dist/batch/wire.js +88 -0
  11. package/dist/core/accessControl/versions.d.ts +1 -1
  12. package/dist/core/accessControl/versions.js +1 -1
  13. package/dist/core/appendStructure/versions.d.ts +1 -1
  14. package/dist/core/appendStructure/versions.js +1 -1
  15. package/dist/core/behaviorDefinition/versions.d.ts +1 -1
  16. package/dist/core/behaviorDefinition/versions.js +1 -1
  17. package/dist/core/behaviorImplementation/versions.d.ts +1 -1
  18. package/dist/core/behaviorImplementation/versions.js +1 -1
  19. package/dist/core/enhancement/versions.js +1 -1
  20. package/dist/core/functionGroup/create.js +1 -1
  21. package/dist/core/functionInclude/versions.js +1 -1
  22. package/dist/core/functionModule/versions.js +1 -1
  23. package/dist/core/scalarFunction/versions.d.ts +1 -1
  24. package/dist/core/scalarFunction/versions.js +1 -1
  25. package/dist/core/scalarFunctionImplementation/versions.d.ts +1 -1
  26. package/dist/core/scalarFunctionImplementation/versions.js +1 -1
  27. package/dist/core/service/AdtService.js +2 -2
  28. package/dist/core/service/lock.d.ts +1 -1
  29. package/dist/core/service/lock.js +1 -1
  30. package/dist/core/service/types.d.ts +2 -2
  31. package/dist/core/serviceDefinition/versions.d.ts +1 -1
  32. package/dist/core/serviceDefinition/versions.js +1 -1
  33. package/dist/core/structure/versions.d.ts +1 -1
  34. package/dist/core/structure/versions.js +1 -1
  35. package/dist/core/tabletype/versions.d.ts +1 -1
  36. package/dist/core/tabletype/versions.js +1 -1
  37. package/dist/core/transformation/versions.d.ts +1 -1
  38. package/dist/core/transformation/versions.js +1 -1
  39. package/dist/core/transport/AdtRequest.d.ts +2 -2
  40. package/dist/core/transport/AdtRequest.js +2 -2
  41. package/dist/core/transport/objects.d.ts +1 -1
  42. package/dist/core/transport/objects.js +1 -1
  43. package/dist/endpoints/objects.d.ts +5 -0
  44. package/dist/endpoints/objects.d.ts.map +1 -1
  45. package/dist/endpoints/objects.js +6 -1
  46. package/dist/index.d.ts +1 -0
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +3 -1
  49. package/dist/runtime/debugger/abap.d.ts.map +1 -1
  50. package/dist/runtime/debugger/abap.js +28 -40
  51. package/dist/runtime/debugger/batchPayload.d.ts +2 -16
  52. package/dist/runtime/debugger/batchPayload.d.ts.map +1 -1
  53. package/dist/runtime/debugger/batchPayload.js +5 -28
  54. package/dist/runtime/dumps/read.d.ts +1 -1
  55. package/dist/runtime/dumps/read.js +1 -1
  56. package/dist/runtime/index.d.ts +1 -0
  57. package/dist/runtime/index.d.ts.map +1 -1
  58. package/dist/runtime/index.js +3 -0
  59. package/docs/usage/BATCH.md +160 -0
  60. package/docs/usage/CLIENT_API_REFERENCE.md +6 -6
  61. package/docs/usage/ERRATA.md +1 -1
  62. package/docs/usage/STATEFUL_SESSION_GUIDE.md +2 -2
  63. package/package.json +1 -1
package/README.md CHANGED
@@ -450,7 +450,7 @@ await utils.readObjectSource('view' satisfies AdtSourceObjectType, 'ZOK_I_CDS_TE
450
450
  The `withLongPolling` parameter asks ADT to hold a read until the object is
451
451
  available, instead of answering from whatever is there right now:
452
452
 
453
- > **Measured caveat — do not rely on it for readiness.** On an SAP BTP trial the
453
+ > **Measured caveat — do not rely on it for readiness.** On the ABAP Cloud system the
454
454
  > parameter changes nothing on an object read. Reading a message class straight
455
455
  > after creating it returned `404` in ~650ms **with** the flag — the same
456
456
  > latency as without it — and the server did not hold the request. The system's
@@ -1115,3 +1115,31 @@ users can replace it with their own build.
1115
1115
  ## Author
1116
1116
 
1117
1117
  Oleksii Kyslytsia <oleksij.kyslytsja@gmail.com>
1118
+
1119
+ ### Recording batches
1120
+
1121
+ `AdtBatchConnection` records calls from the existing clients. Start the member
1122
+ calls, execute the queue, then await their results. Group creates before locks,
1123
+ then queue writes with the returned lock handles before unlocks;
1124
+ activate all objects with one plain `activateObjectsGroup` call. At a debugger
1125
+ stop, record step + stack + variables on the attached stateful connection.
1126
+ See [domain and debugger examples](docs/usage/BATCH.md).
1127
+
1128
+ `execute()` also sends follow-up envelopes for requests recorded during settlement,
1129
+ including automatic 406/415 negotiation retries (up to 100 envelopes, then all
1130
+ remaining requests reject). Explicit session switches apply to the next recording;
1131
+ LOCK/UNLOCK run in stateful envelopes; other ordinary members run stateless.
1132
+ Consecutive LOCKs, consecutive UNLOCKs, and consecutive stateless requests each
1133
+ form separate envelopes sent in queue order.
1134
+ Execution returns the last outer response. An envelope failure stops sending and
1135
+ rejects unsent requests with a clear library error; inner HTTP failures remain
1136
+ per-part failures. The wrapped session mode is restored after execution.
1137
+
1138
+ ```typescript
1139
+ import { AdtBatchConnection, AdtClient } from '@mcp-abap-adt/adt-clients';
1140
+
1141
+ const batch = new AdtBatchConnection(connection, { endpoint: 'communication' });
1142
+ const pending = new AdtClient(batch).getDomain().readMetadata({ domainName: 'Z_DOMAIN' });
1143
+ await batch.execute();
1144
+ const answer = await pending;
1145
+ ```
@@ -0,0 +1,29 @@
1
+ import type { IAbapConnection, IAbapRequestOptions, IAdtWireResponse } from '@mcp-abap-adt/interfaces-adt-connection';
2
+ import type { ILogger } from '@mcp-abap-adt/interfaces-utils';
3
+ type Session = 'stateful' | 'stateless';
4
+ /** Records member requests; execute also drains requests queued during settlement. */
5
+ export declare class AdtBatchConnection implements IAbapConnection {
6
+ private readonly connection;
7
+ private readonly options;
8
+ private queue;
9
+ private session;
10
+ private readonly defaultSession;
11
+ private settlementFailure?;
12
+ private executing;
13
+ constructor(connection: IAbapConnection, options: {
14
+ endpoint: 'communication' | 'debugger';
15
+ logger?: ILogger;
16
+ });
17
+ private wrappedSession;
18
+ connect(): Promise<void>;
19
+ getBaseUrl(): Promise<string>;
20
+ getSessionId(): string | null;
21
+ setSessionType(type: Session): void;
22
+ getSessionType(): Session;
23
+ makeAdtRequest<T = unknown, D = unknown>(request: IAbapRequestOptions): Promise<IAdtWireResponse<T, D>>;
24
+ private envelopeKey;
25
+ /** Empty queues return a synthetic 204, data '', without network traffic. */
26
+ execute(): Promise<IAdtWireResponse>;
27
+ }
28
+ export {};
29
+ //# sourceMappingURL=AdtBatchConnection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdtBatchConnection.d.ts","sourceRoot":"","sources":["../../src/batch/AdtBatchConnection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EACjB,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAI9D,KAAK,OAAO,GAAG,UAAU,GAAG,WAAW,CAAC;AAQxC,sFAAsF;AACtF,qBAAa,kBAAmB,YAAW,eAAe;IAOtD,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAP1B,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,iBAAiB,CAAC,CAAqB;IAC/C,OAAO,CAAC,SAAS,CAAS;gBAEP,UAAU,EAAE,eAAe,EAC3B,OAAO,EAAE;QACxB,QAAQ,EAAE,eAAe,GAAG,UAAU,CAAC;QACvC,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB;IAQH,OAAO,CAAC,cAAc;IAWtB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAGxB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAG7B,YAAY,IAAI,MAAM,GAAG,IAAI;IAG7B,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAGnC,cAAc,IAAI,OAAO;IAGzB,cAAc,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EACrC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAuBlC,OAAO,CAAC,WAAW;IAkBnB,6EAA6E;IACvE,OAAO,IAAI,OAAO,CAAC,gBAAgB,CAAC;CAuF3C"}
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AdtBatchConnection = void 0;
4
+ const objects_1 = require("../endpoints/objects");
5
+ const wire_1 = require("./wire");
6
+ /** Records member requests; execute also drains requests queued during settlement. */
7
+ class AdtBatchConnection {
8
+ connection;
9
+ options;
10
+ queue = [];
11
+ session;
12
+ defaultSession;
13
+ settlementFailure;
14
+ executing = false;
15
+ constructor(connection, options) {
16
+ this.connection = connection;
17
+ this.options = options;
18
+ // Debugger requests use the attached stateful session; communication
19
+ // members opt into stateful mode only for their own LOCK or UNLOCK.
20
+ this.defaultSession =
21
+ options.endpoint === 'debugger' ? 'stateful' : 'stateless';
22
+ this.session = this.defaultSession;
23
+ }
24
+ wrappedSession() {
25
+ const connection = this.connection;
26
+ return (connection.getSessionType?.() ??
27
+ connection.getSessionMode?.() ??
28
+ 'stateless');
29
+ }
30
+ connect() {
31
+ return this.connection.connect();
32
+ }
33
+ getBaseUrl() {
34
+ return this.connection.getBaseUrl();
35
+ }
36
+ getSessionId() {
37
+ return this.connection.getSessionId();
38
+ }
39
+ setSessionType(type) {
40
+ this.session = type;
41
+ }
42
+ getSessionType() {
43
+ return this.session;
44
+ }
45
+ makeAdtRequest(request) {
46
+ if (this.settlementFailure)
47
+ return Promise.reject(this.settlementFailure.error);
48
+ const headers = { ...request.headers };
49
+ const stateful = this.session === 'stateful' ||
50
+ Object.entries(headers).some(([key, value]) => key.toLowerCase() === 'x-sap-adt-sessiontype' && value === 'stateful');
51
+ // Members restore their mode only after the deferred answer. Scope an
52
+ // explicit switch to this recording so neighbouring members cannot inherit it.
53
+ this.session = this.defaultSession;
54
+ if (stateful)
55
+ headers['X-sap-adt-sessiontype'] = 'stateful';
56
+ return new Promise((resolve, reject) => {
57
+ this.queue.push({
58
+ request: { ...request, headers },
59
+ stateful,
60
+ resolve,
61
+ reject,
62
+ });
63
+ });
64
+ }
65
+ envelopeKey(part) {
66
+ if (!part.stateful)
67
+ return 'stateless';
68
+ if (this.options.endpoint === 'debugger')
69
+ return 'stateful';
70
+ const action = new URL(part.request.url, 'https://batch.invalid').searchParams.get('_action') ??
71
+ new URLSearchParams(part.request.params).get('_action');
72
+ if (action === 'LOCK')
73
+ return 'lock';
74
+ if (action === 'UNLOCK')
75
+ return 'unlock';
76
+ // Communication members currently use stateful mode only for LOCK/UNLOCK.
77
+ // Keep any future other stateful requests in their own consecutive group.
78
+ return 'stateful';
79
+ }
80
+ /** Empty queues return a synthetic 204, data '', without network traffic. */
81
+ async execute() {
82
+ if (this.executing)
83
+ throw new Error('An ADT batch execute is already in progress');
84
+ const pending = this.queue;
85
+ this.queue = [];
86
+ if (!pending.length)
87
+ return { status: 204, statusText: 'Empty batch', headers: {}, data: '' };
88
+ this.executing = true;
89
+ const previous = this.wrappedSession();
90
+ let active = [];
91
+ try {
92
+ let response;
93
+ for (let envelope = 0;; envelope++) {
94
+ if (envelope === 100)
95
+ throw new Error('ADT batch exceeded 100 settlement envelopes');
96
+ const key = this.envelopeKey(pending[0]);
97
+ const nextMode = pending.findIndex((part) => this.envelopeKey(part) !== key);
98
+ active = pending.splice(0, nextMode < 0 ? pending.length : nextMode);
99
+ const payload = (0, wire_1.buildBatchPayload)(active.map((part) => (0, wire_1.innerRequest)(part.request)));
100
+ this.connection.setSessionType(active[0].stateful ? 'stateful' : 'stateless');
101
+ this.options.logger?.debug('Executing ADT batch', {
102
+ endpoint: this.options.endpoint,
103
+ parts: active.length,
104
+ });
105
+ response = await this.connection.makeAdtRequest({
106
+ url: objects_1.BATCH[this.options.endpoint],
107
+ method: 'POST',
108
+ timeout: 0,
109
+ data: payload.body,
110
+ headers: {
111
+ 'Content-Type': `multipart/mixed; boundary=${payload.boundary}`,
112
+ Accept: 'multipart/mixed',
113
+ },
114
+ });
115
+ const parts = (0, wire_1.parseBatchResponse)(response);
116
+ for (const [index, part] of active.entries()) {
117
+ const answer = parts[index];
118
+ if (!answer)
119
+ part.reject(new Error(`ADT batch response part count mismatch: expected ${active.length}, received ${parts.length}; missing part ${index + 1}`));
120
+ else if (answer.status >= 200 && answer.status < 300)
121
+ part.resolve(answer);
122
+ else
123
+ part.reject(Object.assign(new Error(`Request failed with status code ${answer.status}`), { response: answer }));
124
+ }
125
+ if (parts.length !== active.length)
126
+ throw new Error(`ADT batch response part count mismatch: expected ${active.length}, received ${parts.length}`);
127
+ active = [];
128
+ // Let negotiation and member continuations record their follow-up calls.
129
+ await new Promise((resolve) => setImmediate(resolve));
130
+ pending.push(...this.queue.splice(0));
131
+ if (!pending.length)
132
+ return response;
133
+ }
134
+ }
135
+ catch (error) {
136
+ const unsentError = new Error('ADT batch request not sent because an earlier batch envelope failed', { cause: error });
137
+ this.settlementFailure = { error: unsentError };
138
+ for (const part of active)
139
+ part.reject(error);
140
+ for (const part of pending)
141
+ part.reject(unsentError);
142
+ await new Promise((resolve) => setImmediate(resolve));
143
+ for (const part of this.queue.splice(0))
144
+ part.reject(unsentError);
145
+ await new Promise((resolve) => setImmediate(resolve));
146
+ throw error;
147
+ }
148
+ finally {
149
+ this.connection.setSessionType(previous);
150
+ this.executing = false;
151
+ this.settlementFailure = undefined;
152
+ }
153
+ }
154
+ }
155
+ exports.AdtBatchConnection = AdtBatchConnection;
@@ -0,0 +1,2 @@
1
+ export { AdtBatchConnection } from './AdtBatchConnection';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/batch/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AdtBatchConnection = void 0;
4
+ var AdtBatchConnection_1 = require("./AdtBatchConnection");
5
+ Object.defineProperty(exports, "AdtBatchConnection", { enumerable: true, get: function () { return AdtBatchConnection_1.AdtBatchConnection; } });
@@ -0,0 +1,10 @@
1
+ import type { IAbapRequestOptions, IAdtWireResponse } from '@mcp-abap-adt/interfaces-adt-connection';
2
+ export declare const createBatchBoundary: () => string;
3
+ export declare const createRequestId: () => string;
4
+ export declare function buildBatchPayload(requests: readonly string[], boundary?: string): {
5
+ boundary: string;
6
+ body: string;
7
+ };
8
+ export declare function innerRequest(request: IAbapRequestOptions): string;
9
+ export declare function parseBatchResponse(response: IAdtWireResponse): IAdtWireResponse[];
10
+ //# sourceMappingURL=wire.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../src/batch/wire.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,mBAAmB,EACnB,gBAAgB,EACjB,MAAM,yCAAyC,CAAC;AAEjD,eAAO,MAAM,mBAAmB,QAAO,MAAiC,CAAC;AACzE,eAAO,MAAM,eAAe,QAAO,MAAwC,CAAC;AAE5E,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,QAAQ,SAAwB,GAC/B;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAWpC;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAqBjE;AAED,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,gBAAgB,GACzB,gBAAgB,EAAE,CAyDpB"}
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createRequestId = exports.createBatchBoundary = void 0;
4
+ exports.buildBatchPayload = buildBatchPayload;
5
+ exports.innerRequest = innerRequest;
6
+ exports.parseBatchResponse = parseBatchResponse;
7
+ const node_crypto_1 = require("node:crypto");
8
+ const createBatchBoundary = () => `batch_${(0, node_crypto_1.randomUUID)()}`;
9
+ exports.createBatchBoundary = createBatchBoundary;
10
+ const createRequestId = () => (0, node_crypto_1.randomUUID)().replace(/-/g, '');
11
+ exports.createRequestId = createRequestId;
12
+ function buildBatchPayload(requests, boundary = (0, exports.createBatchBoundary)()) {
13
+ return {
14
+ boundary,
15
+ body: requests
16
+ .map((request) => `--${boundary}\r\nContent-Type: application/http\r\ncontent-transfer-encoding: binary\r\n\r\n${request}`)
17
+ .join('') + `\r\n--${boundary}--\r\n`,
18
+ };
19
+ }
20
+ function innerRequest(request) {
21
+ const headers = { ...request.headers };
22
+ // `data: null` is how a member says "no body" (lock, unlock): it must not
23
+ // become the four characters "null". Sent that way, every PUT under those
24
+ // locks was refused 423 "invalid lock handle" (on an on-premise system, 2026-10-16).
25
+ const hasBody = request.data !== undefined && request.data !== null;
26
+ const body = hasBody ? String(request.data) : '';
27
+ if (hasBody) {
28
+ for (const key of Object.keys(headers))
29
+ if (key.toLowerCase() === 'content-length')
30
+ delete headers[key];
31
+ headers['Content-Length'] = String(Buffer.byteLength(body));
32
+ }
33
+ const params = request.params
34
+ ? new URLSearchParams(request.params).toString()
35
+ : '';
36
+ const url = request.url +
37
+ (params ? `${request.url.includes('?') ? '&' : '?'}${params}` : '');
38
+ return `${request.method} ${url} HTTP/1.1\r\n${Object.entries(headers)
39
+ .map(([key, value]) => `${key}: ${value}\r\n`)
40
+ .join('')}\r\n${body}\r\n`;
41
+ }
42
+ function parseBatchResponse(response) {
43
+ const contentType = Object.entries(response.headers).find(([key]) => key.toLowerCase() === 'content-type')?.[1];
44
+ const boundary = /boundary=(?:"([^"]+)"|([^;\s]+))/i.exec(String(contentType));
45
+ if (!boundary)
46
+ throw new Error('Invalid ADT batch response: missing multipart boundary');
47
+ const escaped = (boundary[1] ?? boundary[2]).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
48
+ // Only complete delimiter lines count; boundary-like body text is payload.
49
+ const delimiter = new RegExp(`(?:^|\\r?\\n)--${escaped}(--)?[ \t]*(?:\\r?\\n|$)`, 'g');
50
+ const body = String(response.data);
51
+ const chunks = [];
52
+ let start;
53
+ for (const match of body.matchAll(delimiter)) {
54
+ if (start !== undefined)
55
+ chunks.push(body.slice(start, match.index));
56
+ if (match[1])
57
+ break;
58
+ start = match.index + match[0].length;
59
+ }
60
+ return chunks.map((part) => {
61
+ const match = /(?:^|\r?\n)HTTP\/1\.\d\s+(\d{3})\s*([^\r\n]*)\r?\n/.exec(part);
62
+ if (!match || match.index === undefined)
63
+ throw new Error('Invalid ADT batch response: missing inner HTTP status');
64
+ const rest = part.slice(match.index + match[0].length);
65
+ // A bodyless answer (an UNLOCK's 200) ends right after its headers: the
66
+ // empty line's last CRLF belongs to the next delimiter (RFC 2046), so the
67
+ // part holds one line break, not two. Headers then run to the end.
68
+ // Immediately after the status line, one blank line means no headers.
69
+ const found = /^(?:\r?\n)|\r?\n\r?\n/.exec(rest);
70
+ const split = found && found.index !== undefined
71
+ ? { index: found.index, length: found[0].length }
72
+ : { index: rest.length, length: 0 };
73
+ const headers = {};
74
+ for (const line of rest.slice(0, split.index).split(/\r?\n/)) {
75
+ const colon = line.indexOf(':');
76
+ if (colon > 0)
77
+ headers[line.slice(0, colon).toLowerCase()] = line
78
+ .slice(colon + 1)
79
+ .trim();
80
+ }
81
+ return {
82
+ status: Number(match[1]),
83
+ statusText: match[2].trim(),
84
+ headers,
85
+ data: rest.slice(split.index + split.length),
86
+ };
87
+ });
88
+ }
@@ -7,7 +7,7 @@ import type { IAccessControlConfig } from './types';
7
7
  * system without the resource answers 404 or 406, which comes back as the
8
8
  * transport's failure; until 23.0.0 it was thrown as a library error.
9
9
  *
10
- * Candidate URI — probe-verify on trial.
10
+ * Candidate URI — probe-verify on the ABAP Cloud system.
11
11
  */
12
12
  export declare function getAccessControlVersions(connection: IAbapConnection, config: Partial<IAccessControlConfig>): Promise<IAdtWireResponse>;
13
13
  /** The source of one version, by the `contentUri` its entry carried. */
@@ -11,7 +11,7 @@ const ACCEPT_VERSION_FEED = 'application/atom+xml;type=feed';
11
11
  * system without the resource answers 404 or 406, which comes back as the
12
12
  * transport's failure; until 23.0.0 it was thrown as a library error.
13
13
  *
14
- * Candidate URI — probe-verify on trial.
14
+ * Candidate URI — probe-verify on the ABAP Cloud system.
15
15
  */
16
16
  async function getAccessControlVersions(connection, config) {
17
17
  return connection.makeAdtRequest({
@@ -7,7 +7,7 @@ import type { IAppendStructureConfig } from './types';
7
7
  * system without the resource answers 404 or 406, which comes back as the
8
8
  * transport's failure; until 23.0.0 it was thrown as a library error.
9
9
  *
10
- * Candidate URI — probe-verify on trial.
10
+ * Candidate URI — probe-verify on the ABAP Cloud system.
11
11
  */
12
12
  export declare function getAppendStructureVersions(connection: IAbapConnection, config: Partial<IAppendStructureConfig>): Promise<IAdtWireResponse>;
13
13
  /** The source of one version, by the `contentUri` its entry carried. */
@@ -11,7 +11,7 @@ const ACCEPT_VERSION_FEED = 'application/atom+xml;type=feed';
11
11
  * system without the resource answers 404 or 406, which comes back as the
12
12
  * transport's failure; until 23.0.0 it was thrown as a library error.
13
13
  *
14
- * Candidate URI — probe-verify on trial.
14
+ * Candidate URI — probe-verify on the ABAP Cloud system.
15
15
  */
16
16
  async function getAppendStructureVersions(connection, config) {
17
17
  return connection.makeAdtRequest({
@@ -7,7 +7,7 @@ import type { IBehaviorDefinitionConfig } from './types';
7
7
  * system without the resource answers 404 or 406, which comes back as the
8
8
  * transport's failure; until 23.0.0 it was thrown as a library error.
9
9
  *
10
- * Candidate URI — probe-verify on trial.
10
+ * Candidate URI — probe-verify on the ABAP Cloud system.
11
11
  */
12
12
  export declare function getBehaviorDefinitionVersions(connection: IAbapConnection, config: Partial<IBehaviorDefinitionConfig>): Promise<IAdtWireResponse>;
13
13
  /** The source of one version, by the `contentUri` its entry carried. */
@@ -11,7 +11,7 @@ const ACCEPT_VERSION_FEED = 'application/atom+xml;type=feed';
11
11
  * system without the resource answers 404 or 406, which comes back as the
12
12
  * transport's failure; until 23.0.0 it was thrown as a library error.
13
13
  *
14
- * Candidate URI — probe-verify on trial.
14
+ * Candidate URI — probe-verify on the ABAP Cloud system.
15
15
  */
16
16
  async function getBehaviorDefinitionVersions(connection, config) {
17
17
  return connection.makeAdtRequest({
@@ -7,7 +7,7 @@ import type { IBehaviorImplementationConfig } from './types';
7
7
  * system without the resource answers 404 or 406, which comes back as the
8
8
  * transport's failure; until 23.0.0 it was thrown as a library error.
9
9
  *
10
- * Candidate URI — probe-verify on trial.
10
+ * Candidate URI — probe-verify on the ABAP Cloud system.
11
11
  */
12
12
  export declare function getBehaviorImplementationVersions(connection: IAbapConnection, config: Partial<IBehaviorImplementationConfig>): Promise<IAdtWireResponse>;
13
13
  /** The source of one version, by the `contentUri` its entry carried. */
@@ -11,7 +11,7 @@ const ACCEPT_VERSION_FEED = 'application/atom+xml;type=feed';
11
11
  * system without the resource answers 404 or 406, which comes back as the
12
12
  * transport's failure; until 23.0.0 it was thrown as a library error.
13
13
  *
14
- * Candidate URI — probe-verify on trial.
14
+ * Candidate URI — probe-verify on the ABAP Cloud system.
15
15
  */
16
16
  async function getBehaviorImplementationVersions(connection, config) {
17
17
  return connection.makeAdtRequest({
@@ -12,7 +12,7 @@ const ACCEPT_VERSION_FEED = 'application/atom+xml;type=feed';
12
12
  * system without the resource answers 404 or 406, which comes back as the
13
13
  * transport's failure; until 23.0.0 it was thrown as a library error.
14
14
  */
15
- // candidate URI — probe-verify on trial
15
+ // candidate URI — probe-verify on the ABAP Cloud system
16
16
  async function getEnhancementVersions(connection, config) {
17
17
  return connection.makeAdtRequest({
18
18
  url: `${(0, types_1.getEnhancementUri)(config.enhancementType, config.enhancementName)}/source/main/versions`,
@@ -20,7 +20,7 @@ async function create(connection, params, logger, contentTypes) {
20
20
  if (finalResponsible && finalResponsible.trim() === '') {
21
21
  finalResponsible = undefined;
22
22
  }
23
- // Don't escape masterSystem and responsible - they are identifiers (like "TRL", "CB9980002377")
23
+ // Don't escape masterSystem and responsible - they are identifiers (like "SID", "DEVELOPER")
24
24
  // Escaping them may cause SAP to fail parsing and trigger Kerberos errors
25
25
  const masterSystemAttr = finalMasterSystem
26
26
  ? ` adtcore:masterSystem="${finalMasterSystem}"`
@@ -11,7 +11,7 @@ const ACCEPT_VERSION_FEED = 'application/atom+xml;type=feed';
11
11
  * system without the resource answers 404 or 406, which comes back as the
12
12
  * transport's failure; until 23.0.0 it was thrown as a library error.
13
13
  */
14
- // candidate URI — probe-verify on trial
14
+ // candidate URI — probe-verify on the ABAP Cloud system
15
15
  async function getFunctionIncludeVersions(connection, config) {
16
16
  return connection.makeAdtRequest({
17
17
  url: `${(0, objects_1.versionsUri)((0, objects_1.sourceUri)(objects_1.FUNCTION_INCLUDE.uri(config.functionGroupName, config.includeName.toUpperCase())))}`,
@@ -11,7 +11,7 @@ const ACCEPT_VERSION_FEED = 'application/atom+xml;type=feed';
11
11
  * system without the resource answers 404 or 406, which comes back as the
12
12
  * transport's failure; until 23.0.0 it was thrown as a library error.
13
13
  */
14
- // candidate URI — probe-verify on trial
14
+ // candidate URI — probe-verify on the ABAP Cloud system
15
15
  async function getFunctionModuleVersions(connection, config) {
16
16
  return connection.makeAdtRequest({
17
17
  url: `${(0, objects_1.versionsUri)((0, objects_1.sourceUri)(objects_1.FUNCTION_MODULE.uri(config.functionGroupName, config.functionModuleName)))}`,
@@ -2,7 +2,7 @@ import type { IAbapConnection, IAdtWireResponse } from '@mcp-abap-adt/interfaces
2
2
  import type { IScalarFunctionConfig } from './types';
3
3
  /**
4
4
  * The version history — the Atom feed, as it arrived (candidate URI,
5
- * probe-verify on trial).
5
+ * probe-verify on the ABAP Cloud system).
6
6
  *
7
7
  * `objectVersions` in @mcp-abap-adt/adt-strategies reads it into entries. A
8
8
  * system without the resource answers 404 or 406, which comes back as the
@@ -6,7 +6,7 @@ const objects_1 = require("../../endpoints/objects");
6
6
  const ACCEPT_VERSION_FEED = 'application/atom+xml;type=feed';
7
7
  /**
8
8
  * The version history — the Atom feed, as it arrived (candidate URI,
9
- * probe-verify on trial).
9
+ * probe-verify on the ABAP Cloud system).
10
10
  *
11
11
  * `objectVersions` in @mcp-abap-adt/adt-strategies reads it into entries. A
12
12
  * system without the resource answers 404 or 406, which comes back as the
@@ -2,7 +2,7 @@ import type { IAbapConnection, IAdtWireResponse } from '@mcp-abap-adt/interfaces
2
2
  import type { IScalarFunctionImplementationConfig } from './types';
3
3
  /**
4
4
  * The version history — the Atom feed, as it arrived (candidate URI,
5
- * probe-verify on trial).
5
+ * probe-verify on the ABAP Cloud system).
6
6
  *
7
7
  * `objectVersions` in @mcp-abap-adt/adt-strategies reads it into entries. A
8
8
  * system without the resource answers 404 or 406, which comes back as the
@@ -6,7 +6,7 @@ const objects_1 = require("../../endpoints/objects");
6
6
  const ACCEPT_VERSION_FEED = 'application/atom+xml;type=feed';
7
7
  /**
8
8
  * The version history — the Atom feed, as it arrived (candidate URI,
9
- * probe-verify on trial).
9
+ * probe-verify on the ABAP Cloud system).
10
10
  *
11
11
  * `objectVersions` in @mcp-abap-adt/adt-strategies reads it into entries. A
12
12
  * system without the resource answers 404 or 406, which comes back as the
@@ -74,7 +74,7 @@ class AdtServiceBinding {
74
74
  }
75
75
  async publishByServiceType(serviceType, bindingName,
76
76
  // **The caller's, when they give one — else none.** A publication job is
77
- // the slowest thing this library asks for — measured at ~135s on a trial,
77
+ // the slowest thing this library asks for — measured at ~135s on the ABAP Cloud system,
78
78
  // and an unpublish once not settled after eleven minutes. The library sets
79
79
  // no deadline of its own; `IAdtOperationOptions.timeout` is how a caller
80
80
  // asks for one.
@@ -125,7 +125,7 @@ class AdtServiceBinding {
125
125
  }
126
126
  async unpublishByServiceType(serviceType, bindingName,
127
127
  // **The caller's, when they give one — else none.** A publication job is
128
- // the slowest thing this library asks for — measured at ~135s on a trial,
128
+ // the slowest thing this library asks for — measured at ~135s on the ABAP Cloud system,
129
129
  // and an unpublish once not settled after eleven minutes. The library sets
130
130
  // no deadline of its own; `IAdtOperationOptions.timeout` is how a caller
131
131
  // asks for one.
@@ -2,7 +2,7 @@
2
2
  * Locking a service binding — which is what publishing one is.
3
3
  *
4
4
  * A binding is not edited the way a class is: it is created, and then published
5
- * and unpublished. Measured from Eclipse (ADT 3.60.3) on the BTP trial,
5
+ * and unpublished. Measured from Eclipse (ADT 3.60.3) on the ABAP Cloud system,
6
6
  * 2026-09-05, the lock is what those two take:
7
7
  *
8
8
  * ```
@@ -3,7 +3,7 @@
3
3
  * Locking a service binding — which is what publishing one is.
4
4
  *
5
5
  * A binding is not edited the way a class is: it is created, and then published
6
- * and unpublished. Measured from Eclipse (ADT 3.60.3) on the BTP trial,
6
+ * and unpublished. Measured from Eclipse (ADT 3.60.3) on the ABAP Cloud system,
7
7
  * 2026-09-05, the lock is what those two take:
8
8
  *
9
9
  * ```
@@ -105,7 +105,7 @@ export type IServiceBindingPublicationConfig = Partial<ISBC> & {
105
105
  /**
106
106
  * What a publication job needs, which is not the same for the two protocols.
107
107
  *
108
- * **V2 requires the service name and version; V4 does not.** Measured on a trial,
108
+ * **V2 requires the service name and version; V4 does not.** Measured on an ABAP Cloud system,
109
109
  * 2026-09-29, on one binding per protocol with a known publication state and a
110
110
  * single job each — the only difference being the query string:
111
111
  *
@@ -141,7 +141,7 @@ interface IServiceBindingPublicationCommon {
141
141
  * How long to wait for the publication job, in milliseconds.
142
142
  *
143
143
  * A publication is the slowest request this library makes — ~135s measured on
144
- * a trial, and one unpublish still unsettled after eleven minutes. Absent, the
144
+ * the ABAP Cloud system, and one unpublish still unsettled after eleven minutes. Absent, the
145
145
  * request carries no deadline: it waits for SAP's answer.
146
146
  */
147
147
  timeout?: number;
@@ -2,7 +2,7 @@ import type { IAbapConnection, IAdtWireResponse } from '@mcp-abap-adt/interfaces
2
2
  import type { IServiceDefinitionConfig } from './types';
3
3
  /**
4
4
  * The version history — the Atom feed, as it arrived (candidate URI,
5
- * probe-verify on trial).
5
+ * probe-verify on the ABAP Cloud system).
6
6
  *
7
7
  * `objectVersions` in @mcp-abap-adt/adt-strategies reads it into entries. A
8
8
  * system without the resource answers 404 or 406, which comes back as the
@@ -6,7 +6,7 @@ const objects_1 = require("../../endpoints/objects");
6
6
  const ACCEPT_VERSION_FEED = 'application/atom+xml;type=feed';
7
7
  /**
8
8
  * The version history — the Atom feed, as it arrived (candidate URI,
9
- * probe-verify on trial).
9
+ * probe-verify on the ABAP Cloud system).
10
10
  *
11
11
  * `objectVersions` in @mcp-abap-adt/adt-strategies reads it into entries. A
12
12
  * system without the resource answers 404 or 406, which comes back as the
@@ -2,7 +2,7 @@ import type { IAbapConnection, IAdtWireResponse } from '@mcp-abap-adt/interfaces
2
2
  import type { IStructureConfig } from './types';
3
3
  /**
4
4
  * The version history — the Atom feed, as it arrived (candidate URI,
5
- * probe-verify on trial).
5
+ * probe-verify on the ABAP Cloud system).
6
6
  *
7
7
  * `objectVersions` in @mcp-abap-adt/adt-strategies reads it into entries. A
8
8
  * system without the resource answers 404 or 406, which comes back as the
@@ -6,7 +6,7 @@ const objects_1 = require("../../endpoints/objects");
6
6
  const ACCEPT_VERSION_FEED = 'application/atom+xml;type=feed';
7
7
  /**
8
8
  * The version history — the Atom feed, as it arrived (candidate URI,
9
- * probe-verify on trial).
9
+ * probe-verify on the ABAP Cloud system).
10
10
  *
11
11
  * `objectVersions` in @mcp-abap-adt/adt-strategies reads it into entries. A
12
12
  * system without the resource answers 404 or 406, which comes back as the
@@ -2,7 +2,7 @@ import type { IAbapConnection, IAdtWireResponse } from '@mcp-abap-adt/interfaces
2
2
  import type { ITableTypeConfig } from './types';
3
3
  /**
4
4
  * The version history — the Atom feed, as it arrived (candidate URI,
5
- * probe-verify on trial).
5
+ * probe-verify on the ABAP Cloud system).
6
6
  *
7
7
  * `objectVersions` in @mcp-abap-adt/adt-strategies reads it into entries. A
8
8
  * system without the resource answers 404 or 406, which comes back as the
@@ -6,7 +6,7 @@ const objects_1 = require("../../endpoints/objects");
6
6
  const ACCEPT_VERSION_FEED = 'application/atom+xml;type=feed';
7
7
  /**
8
8
  * The version history — the Atom feed, as it arrived (candidate URI,
9
- * probe-verify on trial).
9
+ * probe-verify on the ABAP Cloud system).
10
10
  *
11
11
  * `objectVersions` in @mcp-abap-adt/adt-strategies reads it into entries. A
12
12
  * system without the resource answers 404 or 406, which comes back as the