@abapify/adt-locks 0.4.17 → 0.4.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +0 -8
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/types.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* ADT Lock Types
|
|
4
3
|
*
|
|
@@ -36,8 +35,6 @@ interface LockEntry {
|
|
|
36
35
|
/** ISO timestamp when lock was acquired */
|
|
37
36
|
lockedAt: string;
|
|
38
37
|
}
|
|
39
|
-
//#endregion
|
|
40
|
-
//#region src/store.d.ts
|
|
41
38
|
/**
|
|
42
39
|
* Lock store interface — opt-in persistence for ADT lock handles.
|
|
43
40
|
*
|
|
@@ -70,8 +67,6 @@ declare class FileLockStore implements LockStore {
|
|
|
70
67
|
private load;
|
|
71
68
|
private save;
|
|
72
69
|
}
|
|
73
|
-
//#endregion
|
|
74
|
-
//#region src/service.d.ts
|
|
75
70
|
/** Minimal client interface — anything with an authenticated fetch() */
|
|
76
71
|
interface LockClient {
|
|
77
72
|
fetch(url: string, options?: {
|
|
@@ -148,8 +143,6 @@ interface LockService {
|
|
|
148
143
|
clear(): void;
|
|
149
144
|
}
|
|
150
145
|
declare function createLockService(client: LockClient, serviceOptions?: LockServiceOptions): LockService;
|
|
151
|
-
//#endregion
|
|
152
|
-
//#region src/batch/batch-lock-session.d.ts
|
|
153
146
|
interface BatchLockTarget {
|
|
154
147
|
/** ADT object URI — e.g. `/sap/bc/adt/oo/classes/zcl_foo` */
|
|
155
148
|
objectUri: string;
|
|
@@ -194,6 +187,5 @@ interface BatchLockSession {
|
|
|
194
187
|
* { release(); }`).
|
|
195
188
|
*/
|
|
196
189
|
declare function createBatchLockSession(lockService: Pick<LockService, 'lock' | 'unlock'>, targets: BatchLockTarget[], options?: BatchLockSessionOptions): BatchLockSession;
|
|
197
|
-
//#endregion
|
|
198
190
|
export { AdtLockHandleUnavailableError, type BatchLockAcquisition, type BatchLockSession, type BatchLockSessionOptions, type BatchLockTarget, type BatchReleaseResult, FileLockStore, type LockClient, type LockEntry, type LockHandle, type LockOptions, type LockService, type LockStore, type UnlockOptions, createBatchLockSession, createLockService, parseLockResponse, resolveLockCorrelation };
|
|
199
191
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/store.ts","../src/service.ts","../src/batch/batch-lock-session.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/store.ts","../src/service.ts","../src/batch/batch-lock-session.ts"],"mappings":";;;;;;;;;;UAWiB;;EAEf;;EAEA;;EAEA;;;;;UAMe;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;;;;;;UCpBe;;EAEf,SAAS,OAAO;;EAEhB,WAAW;;EAEX,QAAQ;;EAER;;;;;;;;;cAUW,yBAAyB;mBACnB;EAEL,YAAA;EAKZ,SAAS,OAAO;EAQhB,WAAW;EAQX,QAAQ;EAIR;UAIQ;UASA;;;UC1DO;EACf,MACE,aACA;IACE;IACA,UAAU;IACV;MAED;;UAGY;;EAEf,QAAQ;;UAGO;;EAEf;;EAEA;;EAEA;;UAGe;;EAEf;;;;;;;iBAQc,uBACd,QAAQ,KAAK,kCACb;;;;;;cAUW,sCAAsC;WAI5B;WAHZ;WACS;EAEG,YAAA;;;;;;;;;;;;;;iBAoBP,kBAAkB,cAAc;UAiB/B;;EAEf,KAAK,mBAAmB,UAAU,cAAc,QAAQ;;EAExD,OAAO,mBAAmB,SAAS,gBAAgB;;;;;;EAMnD,YAAY,oBAAoB;;EAEhC,QAAQ;;EAER,WAAW;IAAU;IAAY;;;EAEjC;;iBAGc,kBACd,QAAQ,YACR,iBAAiB,qBAChB;UCjGc;;EAEf;;EAEA,UAAU;;UAGK;EACf,QAAQ;EACR,QAAQ;;UAGO;EACf;EACA,QAAQ;IAAQ;IAAmB;;;UAGpB;;;;;;;EAOf;;UAGe;;EAEf,SAAS,QAAQ;;EAEjB,WAAW,QAAQ;;EAEnB,WAAW,cAAc;;WAEhB;;;;;;;;;iBAUK,uBACd,aAAa,KAAK,iCAClB,SAAS,mBACT,UAAU,0BACT"}
|
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// src/store.ts
|
|
6
6
|
/**
|
|
7
7
|
* Lock Store — persistence layer for ADT lock handles
|
|
8
8
|
*
|
|
@@ -55,8 +55,7 @@ var FileLockStore = class {
|
|
|
55
55
|
}
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
//#region src/service.ts
|
|
58
|
+
// src/service.ts
|
|
60
59
|
/**
|
|
61
60
|
* Resolve the CTS correlation for a write performed under a SAP editor lock.
|
|
62
61
|
* SAP's LOCK response is authoritative because it may normalize a child task
|
|
@@ -179,8 +178,7 @@ function createLockService(client, serviceOptions) {
|
|
|
179
178
|
};
|
|
180
179
|
}
|
|
181
180
|
|
|
182
|
-
|
|
183
|
-
//#region src/batch/batch-lock-session.ts
|
|
181
|
+
// src/batch/batch-lock-session.ts
|
|
184
182
|
/**
|
|
185
183
|
* Create a batch lock session over a set of targets.
|
|
186
184
|
*
|
|
@@ -245,6 +243,5 @@ function createBatchLockSession(lockService, targets, options) {
|
|
|
245
243
|
};
|
|
246
244
|
}
|
|
247
245
|
|
|
248
|
-
//#endregion
|
|
249
246
|
export { AdtLockHandleUnavailableError, FileLockStore, createBatchLockSession, createLockService, parseLockResponse, resolveLockCorrelation };
|
|
250
247
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/store.ts","../src/service.ts","../src/batch/batch-lock-session.ts"],"sourcesContent":["/**\n * Lock Store — persistence layer for ADT lock handles\n *\n * Provides an interface for registering/deregistering lock entries\n * and a file-based implementation that persists to `~/.adt/locks.json`.\n */\n\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { homedir } from 'node:os';\nimport type { LockEntry } from './types';\n\n/**\n * Lock store interface — opt-in persistence for ADT lock handles.\n *\n * Implementations can be file-based, in-memory, or backed by any storage.\n */\nexport interface LockStore {\n /** Persist a lock entry after successful LOCK */\n register(entry: LockEntry): void;\n /** Remove a lock entry after successful UNLOCK */\n deregister(objectUri: string): void;\n /** List all persisted lock entries */\n list(): LockEntry[];\n /** Remove all entries */\n clear(): void;\n}\n\n/**\n * File-based lock store.\n *\n * Persists lock entries to a JSON file (default: `~/.adt/locks.json`).\n * Each entry is keyed by objectUri — re-locking the same URI overwrites.\n * File permissions are 0o600 (owner read/write only).\n */\nexport class FileLockStore implements LockStore {\n private readonly filePath: string;\n\n constructor(baseDir?: string) {\n const dir = baseDir ?? join(homedir(), '.adt');\n this.filePath = join(dir, 'locks.json');\n }\n\n register(entry: LockEntry): void {\n const entries = this.load();\n const idx = entries.findIndex((e) => e.objectUri === entry.objectUri);\n if (idx >= 0) entries[idx] = entry;\n else entries.push(entry);\n this.save(entries);\n }\n\n deregister(objectUri: string): void {\n const entries = this.load();\n const filtered = entries.filter((e) => e.objectUri !== objectUri);\n if (filtered.length !== entries.length) {\n this.save(filtered);\n }\n }\n\n list(): LockEntry[] {\n return this.load();\n }\n\n clear(): void {\n this.save([]);\n }\n\n private load(): LockEntry[] {\n if (!existsSync(this.filePath)) return [];\n try {\n return JSON.parse(readFileSync(this.filePath, 'utf8')) as LockEntry[];\n } catch {\n return [];\n }\n }\n\n private save(entries: LockEntry[]): void {\n const dir = dirname(this.filePath);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n writeFileSync(this.filePath, JSON.stringify(entries, null, 2), {\n mode: 0o600,\n });\n }\n}\n","/**\n * Lock Service — lock/unlock ADT objects via REST API\n *\n * Provides standalone lock/unlock operations that work with any\n * object URI. Optionally integrates with a LockStore for persistence.\n *\n * Usage:\n * import { createLockService, FileLockStore } from '@abapify/adt-locks';\n *\n * const locks = createLockService(client, { store: new FileLockStore() });\n * const handle = await locks.lock('/sap/bc/adt/oo/classes/zcl_test');\n * await locks.unlock('/sap/bc/adt/oo/classes/zcl_test', handle);\n */\n\nimport type { LockHandle, LockEntry } from './types';\nimport type { LockStore } from './store';\n\n/** Minimal client interface — anything with an authenticated fetch() */\nexport interface LockClient {\n fetch(\n url: string,\n options?: {\n method?: string;\n headers?: Record<string, string>;\n body?: string;\n },\n ): Promise<unknown>;\n}\n\nexport interface LockServiceOptions {\n /** Optional lock store for persistence */\n store?: LockStore;\n}\n\nexport interface LockOptions {\n /** Transport request */\n transport?: string;\n /** Object name (for store metadata) */\n objectName?: string;\n /** Object type (for store metadata) */\n objectType?: string;\n}\n\nexport interface UnlockOptions {\n /** Lock handle — REQUIRED. SAP always requires an explicit handle for unlock. */\n lockHandle: string;\n}\n\n/**\n * Resolve the CTS correlation for a write performed under a SAP editor lock.\n * SAP's LOCK response is authoritative because it may normalize a child task\n * to the request that actually owns the object lock.\n */\nexport function resolveLockCorrelation(\n handle: Pick<LockHandle, 'correlationNumber'>,\n requestedTransport?: string,\n): string | undefined {\n return handle.correlationNumber || requestedTransport;\n}\n\n/**\n * SAP does not expose an ADT operation that returns the handle of an\n * existing CTS/editor lock. A caller can only unlock a handle it persisted\n * when it acquired that lock.\n */\nexport class AdtLockHandleUnavailableError extends Error {\n readonly code = 'ADT_LOCK_HANDLE_UNAVAILABLE' as const;\n override readonly name = 'AdtLockHandleUnavailableError';\n\n constructor(readonly objectUri: string) {\n super(\n `No persisted lock handle is available for ${objectUri}. ` +\n 'SAP cannot force-unlock this object by URI; wait for the server lock to expire or ask the SAP administrator to clear it.',\n );\n }\n}\n\n/**\n * Parse SAP lock response XML to extract lock handle and correlation info.\n *\n * Response format:\n * ```xml\n * <asx:abap>...<DATA>\n * <LOCK_HANDLE>xxx</LOCK_HANDLE>\n * <CORRNR>yyy</CORRNR>\n * <CORRUSER>zzz</CORRUSER>\n * </DATA>...</asx:abap>\n * ```\n */\nexport function parseLockResponse(xml: string): LockHandle {\n const handleMatch = xml.match(/<LOCK_HANDLE>([^<]+)<\\/LOCK_HANDLE>/);\n if (!handleMatch) {\n throw new Error(\n 'Failed to parse lock handle from SAP response. The response format may have changed.',\n );\n }\n const corrNrMatch = xml.match(/<CORRNR>([^<]+)<\\/CORRNR>/);\n const corrUserMatch = xml.match(/<CORRUSER>([^<]+)<\\/CORRUSER>/);\n\n return {\n handle: handleMatch[1],\n correlationNumber: corrNrMatch?.[1],\n correlationUser: corrUserMatch?.[1],\n };\n}\n\nexport interface LockService {\n /** Acquire a lock on an ADT object */\n lock(objectUri: string, options?: LockOptions): Promise<LockHandle>;\n /** Release a lock on an ADT object (lock handle required) */\n unlock(objectUri: string, options: UnlockOptions): Promise<void>;\n /**\n * Unlock an object using the handle persisted when this client previously\n * acquired the lock. The historical name is retained for compatibility;\n * SAP has no server-side force-unlock operation and this never issues LOCK.\n */\n forceUnlock(objectUri: string): Promise<void>;\n /** List all persisted lock entries (from store) */\n list(): LockEntry[];\n /** Try to UNLOCK every persisted entry, removing successful ones from store */\n cleanup(): Promise<{ ok: number; failed: number }>;\n /** Clear the store without sending any UNLOCK requests */\n clear(): void;\n}\n\nexport function createLockService(\n client: LockClient,\n serviceOptions?: LockServiceOptions,\n): LockService {\n const store = serviceOptions?.store;\n\n /** Headers for lock operations — stateful session with security session reuse */\n const lockHeaders = {\n 'X-sap-adt-sessiontype': 'stateful',\n 'x-sap-security-session': 'use',\n Accept:\n 'application/*,application/vnd.sap.as+xml;charset=UTF-8;dataname=com.sap.adt.lock.result',\n };\n\n /** Headers for unlock operations */\n const unlockHeaders = {\n 'X-sap-adt-sessiontype': 'stateful',\n 'x-sap-security-session': 'use',\n };\n\n return {\n async lock(objectUri, options) {\n const query = options?.transport\n ? `?_action=LOCK&accessMode=MODIFY&corrNr=${options.transport}`\n : '?_action=LOCK&accessMode=MODIFY';\n\n const response = await client.fetch(`${objectUri}${query}`, {\n method: 'POST',\n headers: lockHeaders,\n });\n\n // The adapter may return a string (text/xml) or a parsed object;\n // parseLockResponse needs the raw XML string.\n const body =\n typeof response === 'string'\n ? response\n : (response as Response)?.text\n ? await (response as Response).text()\n : JSON.stringify(response);\n const handle = parseLockResponse(body);\n\n store?.register({\n objectUri,\n objectName:\n options?.objectName ?? objectUri.split('/').pop() ?? objectUri,\n objectType: options?.objectType,\n lockHandle: handle.handle,\n transport: handle.correlationNumber,\n lockedAt: new Date().toISOString(),\n });\n\n return handle;\n },\n\n async unlock(objectUri, options) {\n const query = `?_action=UNLOCK&accessMode=MODIFY&lockHandle=${encodeURIComponent(options.lockHandle)}`;\n\n await client.fetch(`${objectUri}${query}`, {\n method: 'POST',\n headers: unlockHeaders,\n });\n\n store?.deregister(objectUri);\n },\n\n async forceUnlock(objectUri) {\n // A second LOCK cannot recover a lost handle: real SAP systems such as\n // TRL reject it with an \"already locked in request\" conflict. Re-locking\n // also risks creating a new CTS lock on systems that permit it. Only a\n // handle registered by a previous successful LOCK is safe to use.\n const entry = store?.list()?.find((item) => item.objectUri === objectUri);\n if (!entry) {\n throw new AdtLockHandleUnavailableError(objectUri);\n }\n await this.unlock(objectUri, { lockHandle: entry.lockHandle });\n },\n\n list() {\n return store?.list() ?? [];\n },\n\n async cleanup() {\n const entries = store?.list() ?? [];\n let ok = 0;\n let failed = 0;\n\n for (const entry of entries) {\n try {\n const query = `?_action=UNLOCK&accessMode=MODIFY&lockHandle=${encodeURIComponent(entry.lockHandle)}`;\n await client.fetch(`${entry.objectUri}${query}`, {\n method: 'POST',\n headers: unlockHeaders,\n });\n store?.deregister(entry.objectUri);\n ok++;\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n if (msg.includes('not locked') || msg.includes('not enqueued')) {\n store?.deregister(entry.objectUri);\n ok++;\n } else {\n failed++;\n }\n }\n }\n\n return { ok, failed };\n },\n\n clear() {\n store?.clear();\n },\n };\n}\n","/**\n * BatchLockSession — acquire and release N locks with best-effort rollback.\n *\n * SAP ADT has no transactional multi-object save. A \"batch lock\" is therefore\n * a **best-effort** construct:\n *\n * - `begin()` sequentially acquires a lock on every target. If any lock\n * acquisition fails, every previously acquired lock in this session is\n * released before the error is rethrown — leaving the backend in the\n * same state it was before `begin()` was called.\n *\n * - `release()` unlocks every successfully acquired handle, swallowing\n * per-object unlock failures (they are surfaced through the returned\n * result for the caller to inspect).\n *\n * - `handles()` exposes the acquired handles so callers can thread them\n * into downstream save/PUT flows without re-locking.\n *\n * The session is **single-use**: once released (explicitly or via rollback),\n * it cannot be reused. Tests exercise acquire/release/partial-failure paths.\n *\n * Consumer rationale: ADK's per-object `save({mode:'update'})` already owns\n * its own lock/unlock cycle. `BatchLockSession` is therefore positioned as an\n * outer pre-flight primitive — acquire across a whole ChangePlan to catch\n * conflicts BEFORE we start mutating SAP, release before handing off to ADK.\n * When ADK's save cycle later re-locks per object it simply re-uses the same\n * security session (the CSRF token survives), so the double lock is cheap.\n */\nimport type { LockHandle } from '../types';\nimport type { LockService, LockOptions } from '../service';\n\nexport interface BatchLockTarget {\n /** ADT object URI — e.g. `/sap/bc/adt/oo/classes/zcl_foo` */\n objectUri: string;\n /** Per-target lock options (transport, object name/type for the store). */\n options?: LockOptions;\n}\n\nexport interface BatchLockAcquisition {\n target: BatchLockTarget;\n handle: LockHandle;\n}\n\nexport interface BatchReleaseResult {\n released: number;\n failed: Array<{ objectUri: string; error: string }>;\n}\n\nexport interface BatchLockSessionOptions {\n /**\n * When true, `begin()` stops on the first failure and rolls back locks\n * already acquired in this session. When false (rare), errors are\n * collected and returned but already-acquired locks are kept — use only\n * for diagnostic tooling. Default: true.\n */\n rollbackOnError?: boolean;\n}\n\nexport interface BatchLockSession {\n /** Acquire all configured locks. Rollback on failure (by default). */\n begin(): Promise<BatchLockAcquisition[]>;\n /** Release every currently held handle. Safe to call multiple times. */\n release(): Promise<BatchReleaseResult>;\n /** Snapshot of currently held handles. */\n handles(): ReadonlyArray<BatchLockAcquisition>;\n /** Has `begin()` completed successfully? */\n readonly active: boolean;\n}\n\n/**\n * Create a batch lock session over a set of targets.\n *\n * The session does NOT auto-begin; callers drive the lifecycle explicitly so\n * error handling stays visible at the call-site (`try { begin(); … } finally\n * { release(); }`).\n */\nexport function createBatchLockSession(\n lockService: Pick<LockService, 'lock' | 'unlock'>,\n targets: BatchLockTarget[],\n options?: BatchLockSessionOptions,\n): BatchLockSession {\n const rollback = options?.rollbackOnError ?? true;\n const acquired: BatchLockAcquisition[] = [];\n let beganOnce = false;\n let released = false;\n\n return {\n async begin() {\n if (beganOnce) {\n throw new Error('BatchLockSession.begin() called more than once');\n }\n beganOnce = true;\n\n for (const target of targets) {\n try {\n const handle = await lockService.lock(\n target.objectUri,\n target.options,\n );\n acquired.push({ target, handle });\n } catch (err) {\n if (rollback) {\n // Best-effort: release what we already have, then propagate.\n for (const a of acquired.splice(0).reverse()) {\n try {\n await lockService.unlock(a.target.objectUri, {\n lockHandle: a.handle.handle,\n });\n } catch {\n /* swallow — primary failure is the real signal */\n }\n }\n // Session is done — no further release() work possible.\n released = true;\n }\n throw err instanceof Error\n ? err\n : new Error(`Batch lock failed: ${String(err)}`);\n }\n }\n return [...acquired];\n },\n\n async release() {\n if (released) return { released: 0, failed: [] };\n released = true;\n\n const failed: BatchReleaseResult['failed'] = [];\n let ok = 0;\n // Release in reverse acquisition order — symmetric with begin()'s\n // rollback path and avoids surprising the SAP enqueue server.\n for (const a of acquired.splice(0).reverse()) {\n try {\n await lockService.unlock(a.target.objectUri, {\n lockHandle: a.handle.handle,\n });\n ok++;\n } catch (err) {\n failed.push({\n objectUri: a.target.objectUri,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n return { released: ok, failed };\n },\n\n handles() {\n return [...acquired];\n },\n\n get active() {\n return beganOnce && !released;\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/store.ts","../src/service.ts","../src/batch/batch-lock-session.ts"],"sourcesContent":["/**\n * Lock Store — persistence layer for ADT lock handles\n *\n * Provides an interface for registering/deregistering lock entries\n * and a file-based implementation that persists to `~/.adt/locks.json`.\n */\n\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { homedir } from 'node:os';\nimport type { LockEntry } from './types';\n\n/**\n * Lock store interface — opt-in persistence for ADT lock handles.\n *\n * Implementations can be file-based, in-memory, or backed by any storage.\n */\nexport interface LockStore {\n /** Persist a lock entry after successful LOCK */\n register(entry: LockEntry): void;\n /** Remove a lock entry after successful UNLOCK */\n deregister(objectUri: string): void;\n /** List all persisted lock entries */\n list(): LockEntry[];\n /** Remove all entries */\n clear(): void;\n}\n\n/**\n * File-based lock store.\n *\n * Persists lock entries to a JSON file (default: `~/.adt/locks.json`).\n * Each entry is keyed by objectUri — re-locking the same URI overwrites.\n * File permissions are 0o600 (owner read/write only).\n */\nexport class FileLockStore implements LockStore {\n private readonly filePath: string;\n\n constructor(baseDir?: string) {\n const dir = baseDir ?? join(homedir(), '.adt');\n this.filePath = join(dir, 'locks.json');\n }\n\n register(entry: LockEntry): void {\n const entries = this.load();\n const idx = entries.findIndex((e) => e.objectUri === entry.objectUri);\n if (idx >= 0) entries[idx] = entry;\n else entries.push(entry);\n this.save(entries);\n }\n\n deregister(objectUri: string): void {\n const entries = this.load();\n const filtered = entries.filter((e) => e.objectUri !== objectUri);\n if (filtered.length !== entries.length) {\n this.save(filtered);\n }\n }\n\n list(): LockEntry[] {\n return this.load();\n }\n\n clear(): void {\n this.save([]);\n }\n\n private load(): LockEntry[] {\n if (!existsSync(this.filePath)) return [];\n try {\n return JSON.parse(readFileSync(this.filePath, 'utf8')) as LockEntry[];\n } catch {\n return [];\n }\n }\n\n private save(entries: LockEntry[]): void {\n const dir = dirname(this.filePath);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n writeFileSync(this.filePath, JSON.stringify(entries, null, 2), {\n mode: 0o600,\n });\n }\n}\n","/**\n * Lock Service — lock/unlock ADT objects via REST API\n *\n * Provides standalone lock/unlock operations that work with any\n * object URI. Optionally integrates with a LockStore for persistence.\n *\n * Usage:\n * import { createLockService, FileLockStore } from '@abapify/adt-locks';\n *\n * const locks = createLockService(client, { store: new FileLockStore() });\n * const handle = await locks.lock('/sap/bc/adt/oo/classes/zcl_test');\n * await locks.unlock('/sap/bc/adt/oo/classes/zcl_test', handle);\n */\n\nimport type { LockHandle, LockEntry } from './types';\nimport type { LockStore } from './store';\n\n/** Minimal client interface — anything with an authenticated fetch() */\nexport interface LockClient {\n fetch(\n url: string,\n options?: {\n method?: string;\n headers?: Record<string, string>;\n body?: string;\n },\n ): Promise<unknown>;\n}\n\nexport interface LockServiceOptions {\n /** Optional lock store for persistence */\n store?: LockStore;\n}\n\nexport interface LockOptions {\n /** Transport request */\n transport?: string;\n /** Object name (for store metadata) */\n objectName?: string;\n /** Object type (for store metadata) */\n objectType?: string;\n}\n\nexport interface UnlockOptions {\n /** Lock handle — REQUIRED. SAP always requires an explicit handle for unlock. */\n lockHandle: string;\n}\n\n/**\n * Resolve the CTS correlation for a write performed under a SAP editor lock.\n * SAP's LOCK response is authoritative because it may normalize a child task\n * to the request that actually owns the object lock.\n */\nexport function resolveLockCorrelation(\n handle: Pick<LockHandle, 'correlationNumber'>,\n requestedTransport?: string,\n): string | undefined {\n return handle.correlationNumber || requestedTransport;\n}\n\n/**\n * SAP does not expose an ADT operation that returns the handle of an\n * existing CTS/editor lock. A caller can only unlock a handle it persisted\n * when it acquired that lock.\n */\nexport class AdtLockHandleUnavailableError extends Error {\n readonly code = 'ADT_LOCK_HANDLE_UNAVAILABLE' as const;\n override readonly name = 'AdtLockHandleUnavailableError';\n\n constructor(readonly objectUri: string) {\n super(\n `No persisted lock handle is available for ${objectUri}. ` +\n 'SAP cannot force-unlock this object by URI; wait for the server lock to expire or ask the SAP administrator to clear it.',\n );\n }\n}\n\n/**\n * Parse SAP lock response XML to extract lock handle and correlation info.\n *\n * Response format:\n * ```xml\n * <asx:abap>...<DATA>\n * <LOCK_HANDLE>xxx</LOCK_HANDLE>\n * <CORRNR>yyy</CORRNR>\n * <CORRUSER>zzz</CORRUSER>\n * </DATA>...</asx:abap>\n * ```\n */\nexport function parseLockResponse(xml: string): LockHandle {\n const handleMatch = xml.match(/<LOCK_HANDLE>([^<]+)<\\/LOCK_HANDLE>/);\n if (!handleMatch) {\n throw new Error(\n 'Failed to parse lock handle from SAP response. The response format may have changed.',\n );\n }\n const corrNrMatch = xml.match(/<CORRNR>([^<]+)<\\/CORRNR>/);\n const corrUserMatch = xml.match(/<CORRUSER>([^<]+)<\\/CORRUSER>/);\n\n return {\n handle: handleMatch[1],\n correlationNumber: corrNrMatch?.[1],\n correlationUser: corrUserMatch?.[1],\n };\n}\n\nexport interface LockService {\n /** Acquire a lock on an ADT object */\n lock(objectUri: string, options?: LockOptions): Promise<LockHandle>;\n /** Release a lock on an ADT object (lock handle required) */\n unlock(objectUri: string, options: UnlockOptions): Promise<void>;\n /**\n * Unlock an object using the handle persisted when this client previously\n * acquired the lock. The historical name is retained for compatibility;\n * SAP has no server-side force-unlock operation and this never issues LOCK.\n */\n forceUnlock(objectUri: string): Promise<void>;\n /** List all persisted lock entries (from store) */\n list(): LockEntry[];\n /** Try to UNLOCK every persisted entry, removing successful ones from store */\n cleanup(): Promise<{ ok: number; failed: number }>;\n /** Clear the store without sending any UNLOCK requests */\n clear(): void;\n}\n\nexport function createLockService(\n client: LockClient,\n serviceOptions?: LockServiceOptions,\n): LockService {\n const store = serviceOptions?.store;\n\n /** Headers for lock operations — stateful session with security session reuse */\n const lockHeaders = {\n 'X-sap-adt-sessiontype': 'stateful',\n 'x-sap-security-session': 'use',\n Accept:\n 'application/*,application/vnd.sap.as+xml;charset=UTF-8;dataname=com.sap.adt.lock.result',\n };\n\n /** Headers for unlock operations */\n const unlockHeaders = {\n 'X-sap-adt-sessiontype': 'stateful',\n 'x-sap-security-session': 'use',\n };\n\n return {\n async lock(objectUri, options) {\n const query = options?.transport\n ? `?_action=LOCK&accessMode=MODIFY&corrNr=${options.transport}`\n : '?_action=LOCK&accessMode=MODIFY';\n\n const response = await client.fetch(`${objectUri}${query}`, {\n method: 'POST',\n headers: lockHeaders,\n });\n\n // The adapter may return a string (text/xml) or a parsed object;\n // parseLockResponse needs the raw XML string.\n const body =\n typeof response === 'string'\n ? response\n : (response as Response)?.text\n ? await (response as Response).text()\n : JSON.stringify(response);\n const handle = parseLockResponse(body);\n\n store?.register({\n objectUri,\n objectName:\n options?.objectName ?? objectUri.split('/').pop() ?? objectUri,\n objectType: options?.objectType,\n lockHandle: handle.handle,\n transport: handle.correlationNumber,\n lockedAt: new Date().toISOString(),\n });\n\n return handle;\n },\n\n async unlock(objectUri, options) {\n const query = `?_action=UNLOCK&accessMode=MODIFY&lockHandle=${encodeURIComponent(options.lockHandle)}`;\n\n await client.fetch(`${objectUri}${query}`, {\n method: 'POST',\n headers: unlockHeaders,\n });\n\n store?.deregister(objectUri);\n },\n\n async forceUnlock(objectUri) {\n // A second LOCK cannot recover a lost handle: real SAP systems such as\n // TRL reject it with an \"already locked in request\" conflict. Re-locking\n // also risks creating a new CTS lock on systems that permit it. Only a\n // handle registered by a previous successful LOCK is safe to use.\n const entry = store?.list()?.find((item) => item.objectUri === objectUri);\n if (!entry) {\n throw new AdtLockHandleUnavailableError(objectUri);\n }\n await this.unlock(objectUri, { lockHandle: entry.lockHandle });\n },\n\n list() {\n return store?.list() ?? [];\n },\n\n async cleanup() {\n const entries = store?.list() ?? [];\n let ok = 0;\n let failed = 0;\n\n for (const entry of entries) {\n try {\n const query = `?_action=UNLOCK&accessMode=MODIFY&lockHandle=${encodeURIComponent(entry.lockHandle)}`;\n await client.fetch(`${entry.objectUri}${query}`, {\n method: 'POST',\n headers: unlockHeaders,\n });\n store?.deregister(entry.objectUri);\n ok++;\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n if (msg.includes('not locked') || msg.includes('not enqueued')) {\n store?.deregister(entry.objectUri);\n ok++;\n } else {\n failed++;\n }\n }\n }\n\n return { ok, failed };\n },\n\n clear() {\n store?.clear();\n },\n };\n}\n","/**\n * BatchLockSession — acquire and release N locks with best-effort rollback.\n *\n * SAP ADT has no transactional multi-object save. A \"batch lock\" is therefore\n * a **best-effort** construct:\n *\n * - `begin()` sequentially acquires a lock on every target. If any lock\n * acquisition fails, every previously acquired lock in this session is\n * released before the error is rethrown — leaving the backend in the\n * same state it was before `begin()` was called.\n *\n * - `release()` unlocks every successfully acquired handle, swallowing\n * per-object unlock failures (they are surfaced through the returned\n * result for the caller to inspect).\n *\n * - `handles()` exposes the acquired handles so callers can thread them\n * into downstream save/PUT flows without re-locking.\n *\n * The session is **single-use**: once released (explicitly or via rollback),\n * it cannot be reused. Tests exercise acquire/release/partial-failure paths.\n *\n * Consumer rationale: ADK's per-object `save({mode:'update'})` already owns\n * its own lock/unlock cycle. `BatchLockSession` is therefore positioned as an\n * outer pre-flight primitive — acquire across a whole ChangePlan to catch\n * conflicts BEFORE we start mutating SAP, release before handing off to ADK.\n * When ADK's save cycle later re-locks per object it simply re-uses the same\n * security session (the CSRF token survives), so the double lock is cheap.\n */\nimport type { LockHandle } from '../types';\nimport type { LockService, LockOptions } from '../service';\n\nexport interface BatchLockTarget {\n /** ADT object URI — e.g. `/sap/bc/adt/oo/classes/zcl_foo` */\n objectUri: string;\n /** Per-target lock options (transport, object name/type for the store). */\n options?: LockOptions;\n}\n\nexport interface BatchLockAcquisition {\n target: BatchLockTarget;\n handle: LockHandle;\n}\n\nexport interface BatchReleaseResult {\n released: number;\n failed: Array<{ objectUri: string; error: string }>;\n}\n\nexport interface BatchLockSessionOptions {\n /**\n * When true, `begin()` stops on the first failure and rolls back locks\n * already acquired in this session. When false (rare), errors are\n * collected and returned but already-acquired locks are kept — use only\n * for diagnostic tooling. Default: true.\n */\n rollbackOnError?: boolean;\n}\n\nexport interface BatchLockSession {\n /** Acquire all configured locks. Rollback on failure (by default). */\n begin(): Promise<BatchLockAcquisition[]>;\n /** Release every currently held handle. Safe to call multiple times. */\n release(): Promise<BatchReleaseResult>;\n /** Snapshot of currently held handles. */\n handles(): ReadonlyArray<BatchLockAcquisition>;\n /** Has `begin()` completed successfully? */\n readonly active: boolean;\n}\n\n/**\n * Create a batch lock session over a set of targets.\n *\n * The session does NOT auto-begin; callers drive the lifecycle explicitly so\n * error handling stays visible at the call-site (`try { begin(); … } finally\n * { release(); }`).\n */\nexport function createBatchLockSession(\n lockService: Pick<LockService, 'lock' | 'unlock'>,\n targets: BatchLockTarget[],\n options?: BatchLockSessionOptions,\n): BatchLockSession {\n const rollback = options?.rollbackOnError ?? true;\n const acquired: BatchLockAcquisition[] = [];\n let beganOnce = false;\n let released = false;\n\n return {\n async begin() {\n if (beganOnce) {\n throw new Error('BatchLockSession.begin() called more than once');\n }\n beganOnce = true;\n\n for (const target of targets) {\n try {\n const handle = await lockService.lock(\n target.objectUri,\n target.options,\n );\n acquired.push({ target, handle });\n } catch (err) {\n if (rollback) {\n // Best-effort: release what we already have, then propagate.\n for (const a of acquired.splice(0).reverse()) {\n try {\n await lockService.unlock(a.target.objectUri, {\n lockHandle: a.handle.handle,\n });\n } catch {\n /* swallow — primary failure is the real signal */\n }\n }\n // Session is done — no further release() work possible.\n released = true;\n }\n throw err instanceof Error\n ? err\n : new Error(`Batch lock failed: ${String(err)}`);\n }\n }\n return [...acquired];\n },\n\n async release() {\n if (released) return { released: 0, failed: [] };\n released = true;\n\n const failed: BatchReleaseResult['failed'] = [];\n let ok = 0;\n // Release in reverse acquisition order — symmetric with begin()'s\n // rollback path and avoids surprising the SAP enqueue server.\n for (const a of acquired.splice(0).reverse()) {\n try {\n await lockService.unlock(a.target.objectUri, {\n lockHandle: a.handle.handle,\n });\n ok++;\n } catch (err) {\n failed.push({\n objectUri: a.target.objectUri,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n return { released: ok, failed };\n },\n\n handles() {\n return [...acquired];\n },\n\n get active() {\n return beganOnce && !released;\n },\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abapify/adt-locks",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.18",
|
|
4
4
|
"description": "ADT lock/unlock operations and lock store management",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"./package.json": "./package.json"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@abapify/adt-client": "0.4.
|
|
13
|
+
"@abapify/adt-client": "0.4.17"
|
|
14
14
|
},
|
|
15
15
|
"publishConfig": {
|
|
16
16
|
"access": "public"
|