@mcp-abap-adt/adt-clients 7.5.0 → 7.6.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.
@@ -30,6 +30,9 @@ export declare class AdtClass implements IAdtObject<IClassConfig, IClassState> {
30
30
  protected readonly contentTypes?: IAdtContentTypes;
31
31
  private readonly lockTracker;
32
32
  readonly objectType: string;
33
+ private readonly capCtx;
34
+ private readonly lockCap;
35
+ private readonly versionsCap;
33
36
  constructor(connection: IAbapConnection, logger?: ILogger, systemContext?: IAdtSystemContext, contentTypes?: IAdtContentTypes, lockRegistry?: LockRegistry);
34
37
  /**
35
38
  * Validate class configuration before creation
@@ -1 +1 @@
1
- {"version":3,"file":"AdtClass.d.ts","sourceRoot":"","sources":["../../../src/core/class/AdtClass.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AdtClass.d.ts","sourceRoot":"","sources":["../../../src/core/class/AdtClass.ts"],"names":[],"mappings":"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"}
@@ -54,6 +54,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
54
54
  exports.AdtClass = void 0;
55
55
  const interfaces_1 = require("@mcp-abap-adt/interfaces");
56
56
  const internalUtils_1 = require("../../utils/internalUtils");
57
+ const capabilities_1 = require("../shared/capabilities");
57
58
  const LockRegistry_1 = require("../shared/LockRegistry");
58
59
  const activation_1 = require("./activation");
59
60
  const check_1 = require("./check");
@@ -73,6 +74,36 @@ class AdtClass {
73
74
  contentTypes;
74
75
  lockTracker;
75
76
  objectType = 'Class';
77
+ // LAZY thunk (not a getter that snapshots): captures `this` but reads
78
+ // this.connection/this.logger only when invoked, after the constructor has
79
+ // run — so building the capabilities below as class fields is safe.
80
+ capCtx = () => ({
81
+ connection: this.connection,
82
+ logger: this.logger,
83
+ });
84
+ lockCap = new capabilities_1.LockCapability(this.capCtx, {
85
+ nameOf: (c) => {
86
+ if (!c.className)
87
+ throw new Error('Class name is required');
88
+ return c.className;
89
+ },
90
+ acquire: async (ctx, name) => ({
91
+ lockHandle: await (0, lock_1.lockClass)(ctx.connection, name),
92
+ }),
93
+ release: async (ctx, name, handle) => {
94
+ const result = await (0, unlock_1.unlockClass)(ctx.connection, name, handle);
95
+ return { unlockResult: result, errors: [] };
96
+ },
97
+ });
98
+ versionsCap = new capabilities_1.VersionsCapability(this.capCtx, {
99
+ nameOf: (c) => {
100
+ if (!c.className)
101
+ throw new Error('className is required');
102
+ return c.className;
103
+ },
104
+ list: (ctx, name) => (0, versions_1.getClassIncludeVersions)(ctx.connection, name, 'main'),
105
+ source: (ctx, uri) => (0, versions_1.getClassVersionSource)(ctx.connection, uri),
106
+ });
76
107
  constructor(connection, logger, systemContext, contentTypes, lockRegistry) {
77
108
  this.connection = connection;
78
109
  this.logger = logger;
@@ -537,33 +568,17 @@ class AdtClass {
537
568
  * Lock class
538
569
  */
539
570
  async lock(config) {
540
- if (!config.className) {
541
- throw new Error('Class name is required');
542
- }
543
- // Stay stateful while the lock is held — the caller must release it via
544
- // unlock(), which restores stateless. On older BASIS (#106) the lock handle
545
- // is only valid inside stateful requests, so a stateless write between lock
546
- // and unlock fails with 423.
547
- this.connection.setSessionType('stateful');
548
- const lockHandle = await (0, lock_1.lockClass)(this.connection, config.className);
549
- this.lockTracker.track(config.className, lockHandle);
550
- return lockHandle;
571
+ const handle = await this.lockCap.lock(config);
572
+ this.lockTracker.track(config.className, handle);
573
+ return handle;
551
574
  }
552
575
  /**
553
576
  * Unlock class
554
577
  */
555
578
  async unlock(config, lockHandle) {
556
- if (!config.className) {
557
- throw new Error('Class name is required');
558
- }
559
- this.connection.setSessionType('stateful');
560
- const result = await (0, unlock_1.unlockClass)(this.connection, config.className, lockHandle);
561
- this.connection.setSessionType('stateless');
579
+ const state = await this.lockCap.unlock(config, lockHandle);
562
580
  this.lockTracker.untrack(config.className);
563
- return {
564
- unlockResult: result,
565
- errors: [],
566
- };
581
+ return state;
567
582
  }
568
583
  /**
569
584
  * Lock test classes (local classes) for modification
@@ -698,12 +713,10 @@ class AdtClass {
698
713
  }
699
714
  }
700
715
  getVersions(config) {
701
- if (!config.className)
702
- throw new Error('className is required');
703
- return (0, versions_1.getClassIncludeVersions)(this.connection, config.className, 'main');
716
+ return this.versionsCap.getVersions(config);
704
717
  }
705
718
  getVersionSource(contentUri) {
706
- return (0, versions_1.getClassVersionSource)(this.connection, contentUri);
719
+ return this.versionsCap.getVersionSource(contentUri);
707
720
  }
708
721
  /** Shared by local-include subclasses to target their own include. */
709
722
  getIncludeVersions(className, includeType) {
@@ -28,6 +28,8 @@ export declare class AdtDomain implements IAdtObject<IDomainConfig, IDomainState
28
28
  private readonly systemContext;
29
29
  private readonly lockTracker;
30
30
  readonly objectType: string;
31
+ private readonly capCtx;
32
+ private readonly lockCap;
31
33
  constructor(connection: IAbapConnection, logger?: ILogger, systemContext?: IAdtSystemContext, lockRegistry?: LockRegistry);
32
34
  /**
33
35
  * Validate domain configuration before creation
@@ -1 +1 @@
1
- {"version":3,"file":"AdtDomain.d.ts","sourceRoot":"","sources":["../../../src/core/domain/AdtDomain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAEV,eAAe,EACf,UAAU,EACV,oBAAoB,EACpB,OAAO,EACP,cAAc,EACf,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,OAAO,EAEL,KAAK,YAAY,EAElB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAQpD,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAI3D,qBAAa,SAAU,YAAW,UAAU,CAAC,aAAa,EAAE,YAAY,CAAC;IACvE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoB;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,SAAgB,UAAU,EAAE,MAAM,CAAY;gBAG5C,UAAU,EAAE,eAAe,EAC3B,MAAM,CAAC,EAAE,OAAO,EAChB,aAAa,CAAC,EAAE,iBAAiB,EACjC,YAAY,CAAC,EAAE,YAAY;IAa7B;;OAEG;IACG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC;IAkBrE;;OAEG;IACG,MAAM,CACV,MAAM,EAAE,aAAa,EACrB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,YAAY,CAAC;IAmExB;;OAEG;IACG,IAAI,CACR,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,EAC9B,QAAQ,CAAC,EAAE,QAAQ,GAAG,UAAU,EAChC,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;IAyBpC;;;OAGG;IACG,YAAY,CAChB,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,EAC9B,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,YAAY,CAAC;IA4CxB;;;OAGG;IACG,MAAM,CACV,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,EAC9B,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,YAAY,CAAC;IA8NxB;;OAEG;IACG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC;IAmCnE;;;OAGG;IACG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC;IAsBrE;;OAEG;IACG,KAAK,CACT,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,EAC9B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC;IAuBxB;;OAEG;IACG,aAAa,CACjB,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,EAC9B,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GACtC,OAAO,CAAC,YAAY,CAAC;IAoCxB;;OAEG;IACG,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAW3D;;OAEG;IACG,MAAM,CACV,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,EAC9B,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,CAAC;IAmBlB,WAAW,CACf,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,GAC9B,OAAO,CAAC,cAAc,EAAE,CAAC;IAItB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAG7D"}
1
+ {"version":3,"file":"AdtDomain.d.ts","sourceRoot":"","sources":["../../../src/core/domain/AdtDomain.ts"],"names":[],"mappings":"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"}
@@ -21,6 +21,7 @@
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
22
  exports.AdtDomain = void 0;
23
23
  const internalUtils_1 = require("../../utils/internalUtils");
24
+ const capabilities_1 = require("../shared/capabilities");
24
25
  const LockRegistry_1 = require("../shared/LockRegistry");
25
26
  const versions_1 = require("../shared/versions");
26
27
  const activation_1 = require("./activation");
@@ -38,6 +39,27 @@ class AdtDomain {
38
39
  systemContext;
39
40
  lockTracker;
40
41
  objectType = 'Domain';
42
+ // LAZY thunk (not a getter that snapshots): captures `this` but reads
43
+ // this.connection/this.logger only when invoked, after the constructor has
44
+ // run — so building the capability below as a class field is safe.
45
+ capCtx = () => ({
46
+ connection: this.connection,
47
+ logger: this.logger,
48
+ });
49
+ lockCap = new capabilities_1.LockCapability(this.capCtx, {
50
+ nameOf: (c) => {
51
+ if (!c.domainName)
52
+ throw new Error('Domain name is required');
53
+ return c.domainName;
54
+ },
55
+ acquire: async (ctx, name) => ({
56
+ lockHandle: await (0, lock_1.lockDomain)(ctx.connection, name),
57
+ }),
58
+ release: async (ctx, name, handle) => {
59
+ const result = await (0, unlock_1.unlockDomain)(ctx.connection, name, handle);
60
+ return { unlockResult: result, errors: [] };
61
+ },
62
+ });
41
63
  constructor(connection, logger, systemContext, lockRegistry) {
42
64
  this.connection = connection;
43
65
  this.logger = logger;
@@ -465,29 +487,17 @@ class AdtDomain {
465
487
  * Lock domain for modification
466
488
  */
467
489
  async lock(config) {
468
- if (!config.domainName) {
469
- throw new Error('Domain name is required');
470
- }
471
- this.connection.setSessionType('stateful');
472
- const lockHandle = await (0, lock_1.lockDomain)(this.connection, config.domainName);
473
- this.lockTracker.track(config.domainName, lockHandle);
474
- return lockHandle;
490
+ const handle = await this.lockCap.lock(config);
491
+ this.lockTracker.track(config.domainName, handle);
492
+ return handle;
475
493
  }
476
494
  /**
477
495
  * Unlock domain
478
496
  */
479
497
  async unlock(config, lockHandle) {
480
- if (!config.domainName) {
481
- throw new Error('Domain name is required');
482
- }
483
- this.connection.setSessionType('stateful');
484
- const result = await (0, unlock_1.unlockDomain)(this.connection, config.domainName, lockHandle);
485
- this.connection.setSessionType('stateless');
498
+ const state = await this.lockCap.unlock(config, lockHandle);
486
499
  this.lockTracker.untrack(config.domainName);
487
- return {
488
- unlockResult: result,
489
- errors: [],
490
- };
500
+ return state;
491
501
  }
492
502
  async getVersions(_config) {
493
503
  (0, versions_1.throwUnsupportedVersions)('domain');
@@ -29,6 +29,9 @@ export declare class AdtServiceDefinition implements IAdtObject<IServiceDefiniti
29
29
  private readonly systemContext;
30
30
  private readonly lockTracker;
31
31
  readonly objectType: string;
32
+ private readonly capCtx;
33
+ private readonly lockCap;
34
+ private readonly versionsCap;
32
35
  constructor(connection: IAbapConnection, logger?: ILogger, systemContext?: IAdtSystemContext, lockRegistry?: LockRegistry);
33
36
  /**
34
37
  * Validate service definition configuration before creation
@@ -1 +1 @@
1
- {"version":3,"file":"AdtServiceDefinition.d.ts","sourceRoot":"","sources":["../../../src/core/serviceDefinition/AdtServiceDefinition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAEV,eAAe,EACf,UAAU,EACV,oBAAoB,EACpB,OAAO,EACR,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,OAAO,EAEL,KAAK,YAAY,EAElB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAWpD,OAAO,KAAK,EACV,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,SAAS,CAAC;AASjB,qBAAa,oBACX,YAAW,UAAU,CAAC,wBAAwB,EAAE,uBAAuB,CAAC;IAExE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoB;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,SAAgB,UAAU,EAAE,MAAM,CAAuB;gBAGvD,UAAU,EAAE,eAAe,EAC3B,MAAM,CAAC,EAAE,OAAO,EAChB,aAAa,CAAC,EAAE,iBAAiB,EACjC,YAAY,CAAC,EAAE,YAAY;IAa7B;;OAEG;IACG,QAAQ,CACZ,MAAM,EAAE,OAAO,CAAC,wBAAwB,CAAC,GACxC,OAAO,CAAC,uBAAuB,CAAC;IA8BnC;;OAEG;IACG,MAAM,CACV,MAAM,EAAE,wBAAwB,EAChC,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,OAAO,CAAC,uBAAuB,CAAC;IAqCnC;;OAEG;IACG,IAAI,CACR,MAAM,EAAE,OAAO,CAAC,wBAAwB,CAAC,EACzC,OAAO,CAAC,EAAE,QAAQ,GAAG,UAAU,EAC/B,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC;IA2B/C;;OAEG;IACG,YAAY,CAChB,MAAM,EAAE,OAAO,CAAC,wBAAwB,CAAC,EACzC,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,uBAAuB,CAAC;IAkCnC;;OAEG;IACG,aAAa,CACjB,MAAM,EAAE,OAAO,CAAC,wBAAwB,CAAC,EACzC,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GACtC,OAAO,CAAC,uBAAuB,CAAC;IAoCnC;;;;OAIG;IACG,MAAM,CACV,MAAM,EAAE,OAAO,CAAC,wBAAwB,CAAC,EACzC,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,uBAAuB,CAAC;IAuNnC;;OAEG;IACG,MAAM,CACV,MAAM,EAAE,OAAO,CAAC,wBAAwB,CAAC,GACxC,OAAO,CAAC,uBAAuB,CAAC;IAmCnC;;;OAGG;IACG,QAAQ,CACZ,MAAM,EAAE,OAAO,CAAC,wBAAwB,CAAC,GACxC,OAAO,CAAC,uBAAuB,CAAC;IAqBnC;;OAEG;IACG,KAAK,CACT,MAAM,EAAE,OAAO,CAAC,wBAAwB,CAAC,EACzC,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,uBAAuB,CAAC;IAkBnC;;OAEG;IACG,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,wBAAwB,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IActE;;OAEG;IACG,MAAM,CACV,MAAM,EAAE,OAAO,CAAC,wBAAwB,CAAC,EACzC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,uBAAuB,CAAC;IAmBnC,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,wBAAwB,CAAC;IAIrD,gBAAgB,CAAC,UAAU,EAAE,MAAM;CAGpC"}
1
+ {"version":3,"file":"AdtServiceDefinition.d.ts","sourceRoot":"","sources":["../../../src/core/serviceDefinition/AdtServiceDefinition.ts"],"names":[],"mappings":"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"}
@@ -22,6 +22,7 @@
22
22
  Object.defineProperty(exports, "__esModule", { value: true });
23
23
  exports.AdtServiceDefinition = void 0;
24
24
  const internalUtils_1 = require("../../utils/internalUtils");
25
+ const capabilities_1 = require("../shared/capabilities");
25
26
  const LockRegistry_1 = require("../shared/LockRegistry");
26
27
  const activation_1 = require("./activation");
27
28
  const check_1 = require("./check");
@@ -39,6 +40,41 @@ class AdtServiceDefinition {
39
40
  systemContext;
40
41
  lockTracker;
41
42
  objectType = 'ServiceDefinition';
43
+ // LAZY thunk (not a getter that snapshots): captures `this` but reads
44
+ // this.connection/this.logger only when invoked, after the constructor has
45
+ // run — so building the capabilities below as class fields is safe.
46
+ capCtx = () => ({
47
+ connection: this.connection,
48
+ logger: this.logger,
49
+ });
50
+ lockCap = new capabilities_1.LockCapability(this.capCtx, {
51
+ nameOf: (c) => {
52
+ if (!c.serviceDefinitionName)
53
+ throw new Error('Service definition name is required');
54
+ return c.serviceDefinitionName;
55
+ },
56
+ acquire: async (ctx, name) => ({
57
+ lockHandle: await (0, lock_1.lockServiceDefinition)(ctx.connection, name),
58
+ }),
59
+ release: async (ctx, name, handle) => {
60
+ const result = await (0, unlock_1.unlockServiceDefinition)(ctx.connection, name, handle);
61
+ return { unlockResult: result, errors: [] };
62
+ },
63
+ });
64
+ versionsCap = new capabilities_1.VersionsCapability(this.capCtx, {
65
+ nameOf: (c) => {
66
+ if (!c.serviceDefinitionName)
67
+ throw new Error('serviceDefinitionName is required');
68
+ return c.serviceDefinitionName;
69
+ },
70
+ // NOTE: getServiceDefinitionVersions takes a config, not a bare name —
71
+ // the strategy re-wraps. (getClassIncludeVersions, by contrast, takes a
72
+ // name; the two low-level shapes differ and the strategy absorbs that.)
73
+ list: (ctx, name) => (0, versions_1.getServiceDefinitionVersions)(ctx.connection, {
74
+ serviceDefinitionName: name,
75
+ }),
76
+ source: (ctx, uri) => (0, versions_1.getServiceDefinitionVersionSource)(ctx.connection, uri),
77
+ });
42
78
  constructor(connection, logger, systemContext, lockRegistry) {
43
79
  this.connection = connection;
44
80
  this.logger = logger;
@@ -415,11 +451,7 @@ class AdtServiceDefinition {
415
451
  * Lock service definition for modification
416
452
  */
417
453
  async lock(config) {
418
- if (!config.serviceDefinitionName) {
419
- throw new Error('Service definition name is required');
420
- }
421
- this.connection.setSessionType('stateful');
422
- const lockHandle = await (0, lock_1.lockServiceDefinition)(this.connection, config.serviceDefinitionName);
454
+ const lockHandle = await this.lockCap.lock(config);
423
455
  this.lockTracker.track(config.serviceDefinitionName, lockHandle);
424
456
  return lockHandle;
425
457
  }
@@ -427,23 +459,15 @@ class AdtServiceDefinition {
427
459
  * Unlock service definition
428
460
  */
429
461
  async unlock(config, lockHandle) {
430
- if (!config.serviceDefinitionName) {
431
- throw new Error('Service definition name is required');
432
- }
433
- this.connection.setSessionType('stateful');
434
- const result = await (0, unlock_1.unlockServiceDefinition)(this.connection, config.serviceDefinitionName, lockHandle);
435
- this.connection.setSessionType('stateless');
462
+ const state = await this.lockCap.unlock(config, lockHandle);
436
463
  this.lockTracker.untrack(config.serviceDefinitionName);
437
- return {
438
- unlockResult: result,
439
- errors: [],
440
- };
464
+ return state;
441
465
  }
442
466
  getVersions(config) {
443
- return (0, versions_1.getServiceDefinitionVersions)(this.connection, config);
467
+ return this.versionsCap.getVersions(config);
444
468
  }
445
469
  getVersionSource(contentUri) {
446
- return (0, versions_1.getServiceDefinitionVersionSource)(this.connection, contentUri);
470
+ return this.versionsCap.getVersionSource(contentUri);
447
471
  }
448
472
  }
449
473
  exports.AdtServiceDefinition = AdtServiceDefinition;
@@ -0,0 +1,37 @@
1
+ import type { IAdtLockable } from '@mcp-abap-adt/interfaces';
2
+ import type { ICapabilityContext, ILockStrategy } from './types';
3
+ /**
4
+ * Shared lock/unlock for handlers whose lock differs only by endpoint and
5
+ * name field. `lock` leaves the session stateful (the caller must unlock).
6
+ * `unlock` runs the release inside a stateful request — older BASIS (#106)
7
+ * only accepts UNLOCK while stateful — then restores stateless. The handler's
8
+ * state shape (including the ADT `unlockResult`) is built by `strategy.release`
9
+ * and returned unchanged. See the spec's IAdtLockable obligations — idempotent
10
+ * unlock is a TARGET, not implemented here, and is left to the per-endpoint
11
+ * probe + adaptation rule of the full-migration plan.
12
+ *
13
+ * DELIBERATELY byte-identical to the current handlers, including the failure
14
+ * path: if `acquire`/`release` throws, the session is left as-is (stateful) and
15
+ * the error propagates — exactly as today. Failure/abandonment handling is
16
+ * DISTRIBUTED and this capability owns none of it:
17
+ * - the consumer largely owns lock/unlock atomicity (it decides when to lock
18
+ * and unlock);
19
+ * - adt-clients' `LockRegistry.unlockAll()` is a disposal safety net that
20
+ * raw-releases abandoned locks — deliberately WITHOUT toggling the session,
21
+ * because `unlockAll()` manages the session once for the whole batch;
22
+ * - the operation chain's create/update catch blocks also call
23
+ * setSessionType('stateless').
24
+ * Adding a try/finally here would change behaviour and relocate responsibility
25
+ * across those layers, so the atom-level "restore stateless on failure"
26
+ * contract is deferred to the behavioural-conformance work (same bucket as
27
+ * activate/check error unification), where all three layers are reconciled
28
+ * rather than double-cleaned.
29
+ */
30
+ export declare class LockCapability<TConfig, TReadResult> implements IAdtLockable<TConfig, TReadResult> {
31
+ private readonly getCtx;
32
+ private readonly strategy;
33
+ constructor(getCtx: () => ICapabilityContext, strategy: ILockStrategy<TConfig, TReadResult>);
34
+ lock(config: Partial<TConfig>): Promise<string>;
35
+ unlock(config: Partial<TConfig>, lockHandle: string): Promise<TReadResult>;
36
+ }
37
+ //# sourceMappingURL=LockCapability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LockCapability.d.ts","sourceRoot":"","sources":["../../../../src/core/shared/capabilities/LockCapability.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,cAAc,CAAC,OAAO,EAAE,WAAW,CAC9C,YAAW,YAAY,CAAC,OAAO,EAAE,WAAW,CAAC;IAK3C,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBADR,MAAM,EAAE,MAAM,kBAAkB,EAChC,QAAQ,EAAE,aAAa,CAAC,OAAO,EAAE,WAAW,CAAC;IAG1D,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAS/C,MAAM,CACV,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,EACxB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,WAAW,CAAC;CASxB"}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LockCapability = void 0;
4
+ /**
5
+ * Shared lock/unlock for handlers whose lock differs only by endpoint and
6
+ * name field. `lock` leaves the session stateful (the caller must unlock).
7
+ * `unlock` runs the release inside a stateful request — older BASIS (#106)
8
+ * only accepts UNLOCK while stateful — then restores stateless. The handler's
9
+ * state shape (including the ADT `unlockResult`) is built by `strategy.release`
10
+ * and returned unchanged. See the spec's IAdtLockable obligations — idempotent
11
+ * unlock is a TARGET, not implemented here, and is left to the per-endpoint
12
+ * probe + adaptation rule of the full-migration plan.
13
+ *
14
+ * DELIBERATELY byte-identical to the current handlers, including the failure
15
+ * path: if `acquire`/`release` throws, the session is left as-is (stateful) and
16
+ * the error propagates — exactly as today. Failure/abandonment handling is
17
+ * DISTRIBUTED and this capability owns none of it:
18
+ * - the consumer largely owns lock/unlock atomicity (it decides when to lock
19
+ * and unlock);
20
+ * - adt-clients' `LockRegistry.unlockAll()` is a disposal safety net that
21
+ * raw-releases abandoned locks — deliberately WITHOUT toggling the session,
22
+ * because `unlockAll()` manages the session once for the whole batch;
23
+ * - the operation chain's create/update catch blocks also call
24
+ * setSessionType('stateless').
25
+ * Adding a try/finally here would change behaviour and relocate responsibility
26
+ * across those layers, so the atom-level "restore stateless on failure"
27
+ * contract is deferred to the behavioural-conformance work (same bucket as
28
+ * activate/check error unification), where all three layers are reconciled
29
+ * rather than double-cleaned.
30
+ */
31
+ class LockCapability {
32
+ getCtx;
33
+ strategy;
34
+ constructor(
35
+ // LAZY: read at method-call time, so the handler can build this capability
36
+ // as a class field before its constructor assigns this.connection.
37
+ getCtx, strategy) {
38
+ this.getCtx = getCtx;
39
+ this.strategy = strategy;
40
+ }
41
+ async lock(config) {
42
+ const ctx = this.getCtx();
43
+ const name = this.strategy.nameOf(config);
44
+ // Stay stateful while the lock is held; the caller releases via unlock().
45
+ ctx.connection.setSessionType('stateful');
46
+ const { lockHandle } = await this.strategy.acquire(ctx, name);
47
+ return lockHandle;
48
+ }
49
+ async unlock(config, lockHandle) {
50
+ const ctx = this.getCtx();
51
+ const name = this.strategy.nameOf(config);
52
+ // UNLOCK must run stateful (older BASIS #106); restore stateless after.
53
+ ctx.connection.setSessionType('stateful');
54
+ const state = await this.strategy.release(ctx, name, lockHandle);
55
+ ctx.connection.setSessionType('stateless');
56
+ return state;
57
+ }
58
+ }
59
+ exports.LockCapability = LockCapability;
@@ -0,0 +1,15 @@
1
+ import type { IAdtVersionable, IObjectVersion } from '@mcp-abap-adt/interfaces';
2
+ import type { ICapabilityContext, IVersionsStrategy } from './types';
3
+ /**
4
+ * Shared version history for source-backed objects. Types without a
5
+ * /source/main resource do NOT compose this capability and do not implement
6
+ * IAdtVersionable — absence is expressed structurally, not by throwing.
7
+ */
8
+ export declare class VersionsCapability<TConfig> implements IAdtVersionable<TConfig> {
9
+ private readonly getCtx;
10
+ private readonly strategy;
11
+ constructor(getCtx: () => ICapabilityContext, strategy: IVersionsStrategy<TConfig>);
12
+ getVersions(config: Partial<TConfig>): Promise<IObjectVersion[]>;
13
+ getVersionSource(contentUri: string): Promise<string>;
14
+ }
15
+ //# sourceMappingURL=VersionsCapability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VersionsCapability.d.ts","sourceRoot":"","sources":["../../../../src/core/shared/capabilities/VersionsCapability.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAChF,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAErE;;;;GAIG;AACH,qBAAa,kBAAkB,CAAC,OAAO,CAAE,YAAW,eAAe,CAAC,OAAO,CAAC;IAGxE,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBADR,MAAM,EAAE,MAAM,kBAAkB,EAChC,QAAQ,EAAE,iBAAiB,CAAC,OAAO,CAAC;IAGvD,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAKhE,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAGtD"}
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VersionsCapability = void 0;
4
+ /**
5
+ * Shared version history for source-backed objects. Types without a
6
+ * /source/main resource do NOT compose this capability and do not implement
7
+ * IAdtVersionable — absence is expressed structurally, not by throwing.
8
+ */
9
+ class VersionsCapability {
10
+ getCtx;
11
+ strategy;
12
+ constructor(
13
+ // LAZY: see LockCapability — read at call time so it can be a class field.
14
+ getCtx, strategy) {
15
+ this.getCtx = getCtx;
16
+ this.strategy = strategy;
17
+ }
18
+ getVersions(config) {
19
+ const name = this.strategy.nameOf(config);
20
+ return this.strategy.list(this.getCtx(), name);
21
+ }
22
+ getVersionSource(contentUri) {
23
+ return this.strategy.source(this.getCtx(), contentUri);
24
+ }
25
+ }
26
+ exports.VersionsCapability = VersionsCapability;
@@ -0,0 +1,4 @@
1
+ export { LockCapability } from './LockCapability';
2
+ export * from './types';
3
+ export { VersionsCapability } from './VersionsCapability';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/shared/capabilities/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.VersionsCapability = exports.LockCapability = void 0;
18
+ var LockCapability_1 = require("./LockCapability");
19
+ Object.defineProperty(exports, "LockCapability", { enumerable: true, get: function () { return LockCapability_1.LockCapability; } });
20
+ __exportStar(require("./types"), exports);
21
+ var VersionsCapability_1 = require("./VersionsCapability");
22
+ Object.defineProperty(exports, "VersionsCapability", { enumerable: true, get: function () { return VersionsCapability_1.VersionsCapability; } });
@@ -0,0 +1,41 @@
1
+ import type { IAbapConnection, ILogger, IObjectVersion } from '@mcp-abap-adt/interfaces';
2
+ /** The connection + logger every capability implementation needs. */
3
+ export interface ICapabilityContext {
4
+ readonly connection: IAbapConnection;
5
+ readonly logger?: ILogger;
6
+ }
7
+ /**
8
+ * Normalized lock result. The per-type helpers return different shapes
9
+ * (bare string vs { lockHandle, corrNr }); the capability normalizes up to
10
+ * this superset. See the spec's "lock normalization contract".
11
+ */
12
+ export interface INormalizedLock {
13
+ lockHandle: string;
14
+ corrNr?: string;
15
+ }
16
+ /**
17
+ * Per-handler strategy for LockCapability. The handler supplies its own
18
+ * endpoint knowledge — there is no centralized lifecycle-URI resolver
19
+ * (buildObjectUri is for group operations only).
20
+ *
21
+ * `release` returns the handler's OWN state shape (e.g. `{ unlockResult,
22
+ * errors: [] }`), not void — the current handlers put the ADT unlock response
23
+ * into `state.unlockResult`, and dropping it would change observable behaviour.
24
+ * The capability owns only the session toggling around it.
25
+ */
26
+ export interface ILockStrategy<TConfig, TReadResult> {
27
+ /** Extract the object name from config, or throw if missing. */
28
+ nameOf(config: Partial<TConfig>): string;
29
+ /** POST _action=LOCK, return the normalized handle. */
30
+ acquire(ctx: ICapabilityContext, name: string): Promise<INormalizedLock>;
31
+ /** POST _action=UNLOCK with the handle; build and return the handler state. */
32
+ release(ctx: ICapabilityContext, name: string, lockHandle: string): Promise<TReadResult>;
33
+ }
34
+ /** Per-handler strategy for VersionsCapability. */
35
+ export interface IVersionsStrategy<TConfig> {
36
+ nameOf(config: Partial<TConfig>): string;
37
+ list(ctx: ICapabilityContext, name: string): Promise<IObjectVersion[]>;
38
+ /** GET the content URI, return the source text. */
39
+ source(ctx: ICapabilityContext, contentUri: string): Promise<string>;
40
+ }
41
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/core/shared/capabilities/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,OAAO,EACP,cAAc,EACf,MAAM,0BAA0B,CAAC;AAElC,qEAAqE;AACrE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAa,CAAC,OAAO,EAAE,WAAW;IACjD,gEAAgE;IAChE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC;IACzC,uDAAuD;IACvD,OAAO,CAAC,GAAG,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IACzE,+EAA+E;IAC/E,OAAO,CACL,GAAG,EAAE,kBAAkB,EACvB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,WAAW,CAAC,CAAC;CACzB;AAED,mDAAmD;AACnD,MAAM,WAAW,iBAAiB,CAAC,OAAO;IACxC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC;IACzC,IAAI,CAAC,GAAG,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IACvE,mDAAmD;IACnD,MAAM,CAAC,GAAG,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACtE"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1 +1 @@
1
- {"version":3,"file":"activationUtils.d.ts","sourceRoot":"","sources":["../../src/utils/activationUtils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,YAAY,IAAI,aAAa,EAC7B,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAKlC;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CAyGR;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,eAAe,EAC3B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,QAAQ,GAAE,OAAc,GACvB,OAAO,CAAC,aAAa,CAAC,CAoBxB"}
1
+ {"version":3,"file":"activationUtils.d.ts","sourceRoot":"","sources":["../../src/utils/activationUtils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,YAAY,IAAI,aAAa,EAC7B,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAmGlC;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CAyGR;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,eAAe,EAC3B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,QAAQ,GAAE,OAAc,GACvB,OAAO,CAAC,aAAa,CAAC,CA8BxB"}
@@ -9,9 +9,82 @@
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.buildObjectUri = buildObjectUri;
11
11
  exports.activateObjectInSession = activateObjectInSession;
12
+ const fast_xml_parser_1 = require("fast-xml-parser");
12
13
  const contentTypes_1 = require("../constants/contentTypes");
13
14
  const internalUtils_1 = require("./internalUtils");
14
15
  const timeouts_1 = require("./timeouts");
16
+ /**
17
+ * Extract a human-readable text from a single ADT `<msg>` node.
18
+ */
19
+ function extractActivationMsgText(msg) {
20
+ const shortText = msg?.shortText;
21
+ if (shortText && typeof shortText === 'object' && 'txt' in shortText) {
22
+ const txt = shortText.txt;
23
+ if (typeof txt === 'string' && txt.trim()) {
24
+ return txt.trim();
25
+ }
26
+ }
27
+ if (typeof shortText === 'string' && shortText.trim()) {
28
+ return shortText.trim();
29
+ }
30
+ if (typeof msg?.objDescr === 'string' && msg.objDescr.trim()) {
31
+ return msg.objDescr.trim();
32
+ }
33
+ return 'activation error';
34
+ }
35
+ /**
36
+ * Inspect an ADT activation response body for an **explicit failure signal**.
37
+ *
38
+ * ADT's `/sap/bc/adt/activation` endpoint returns HTTP 200 even when activation
39
+ * fails (object locked by another session, syntax errors), carrying a
40
+ * `<chkl:messages>` body with `chkl:properties activationExecuted="false"` and/or
41
+ * `<msg type="E">` entries. Treating "no HTTP error" as success masks these
42
+ * failures (issue #78).
43
+ *
44
+ * Conservative by design: returns a failure detail string ONLY on a positive
45
+ * error signal. Empty, unparseable, or unrecognized bodies return `null`
46
+ * (success) so the many object types whose success-body shape differs are never
47
+ * regressed into false failures.
48
+ *
49
+ * @returns failure detail text, or `null` when no failure signal is present
50
+ */
51
+ function detectActivationFailure(responseData) {
52
+ if (typeof responseData !== 'string' || responseData.trim() === '') {
53
+ return null;
54
+ }
55
+ let parsed;
56
+ try {
57
+ const parser = new fast_xml_parser_1.XMLParser({
58
+ ignoreAttributes: false,
59
+ attributeNamePrefix: '',
60
+ });
61
+ parsed = parser.parse(responseData);
62
+ }
63
+ catch {
64
+ return null;
65
+ }
66
+ const messages = parsed?.['chkl:messages'];
67
+ if (!messages) {
68
+ return null;
69
+ }
70
+ const activationExecuted = messages['chkl:properties']?.activationExecuted;
71
+ const executedFalse = activationExecuted === 'false' || activationExecuted === false;
72
+ const rawMsg = messages.msg;
73
+ const msgList = Array.isArray(rawMsg) ? rawMsg : rawMsg ? [rawMsg] : [];
74
+ const errorTexts = msgList
75
+ .filter((m) => {
76
+ const severity = m?.type ??
77
+ m?.severity;
78
+ return typeof severity === 'string' && severity.toUpperCase() === 'E';
79
+ })
80
+ .map((m) => extractActivationMsgText(m));
81
+ if (executedFalse || errorTexts.length > 0) {
82
+ return errorTexts.length > 0
83
+ ? errorTexts.join('; ')
84
+ : 'activationExecuted=false';
85
+ }
86
+ return null;
87
+ }
15
88
  /**
16
89
  * Build object URI from name and type
17
90
  * Used by both individual and group activation
@@ -127,11 +200,19 @@ async function activateObjectInSession(connection, objectUri, objectName, preaud
127
200
  'Content-Type': contentTypes_1.CT_ACTIVATION,
128
201
  Accept: 'application/xml',
129
202
  };
130
- return await connection.makeAdtRequest({
203
+ const response = await connection.makeAdtRequest({
131
204
  url,
132
205
  method: 'POST',
133
206
  timeout: (0, timeouts_1.getTimeout)('default'),
134
207
  data: activationXml,
135
208
  headers,
136
209
  });
210
+ // ADT returns HTTP 200 even on failed activation (locked object, syntax
211
+ // errors). Surface an explicit failure signal as a thrown error instead of
212
+ // letting callers report a false success (issue #78).
213
+ const failure = detectActivationFailure(response.data);
214
+ if (failure) {
215
+ throw new Error(`Activation of ${objectName} failed: ${failure}`);
216
+ }
217
+ return response;
137
218
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-abap-adt/adt-clients",
3
- "version": "7.5.0",
3
+ "version": "7.6.0",
4
4
  "description": "ADT clients for SAP ABAP systems - AdtClient and AdtRuntimeClient",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -113,7 +113,7 @@
113
113
  "node": ">=18.0.0"
114
114
  },
115
115
  "dependencies": {
116
- "@mcp-abap-adt/interfaces": "^11.0.0",
116
+ "@mcp-abap-adt/interfaces": "^11.2.0",
117
117
  "@mcp-abap-adt/logger": "^0.1.3",
118
118
  "axios": "^1.18.1",
119
119
  "fast-xml-parser": "^5.9.3"