xrootd 0.1.12 → 0.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.
package/README.md CHANGED
@@ -1,23 +1,144 @@
1
- # XRootD Typescript Support
1
+ # 🚀 XRootD TypeScript Support (`xrootd`)
2
2
 
3
- > [!WARNING]
4
- > **Working in process**. This package is currently under active development and not ready for production use. Stay tuned!
3
+ A high-performance, enterprise-grade Node.js binding for the [XRootD](https://github.com/xrootd/xrootd) client library. Designed to bring seamless, extreme-throughput data access to the TypeScript ecosystem.
5
4
 
6
- - now complete: FileSystem/Url/Env/File
7
- - in progress: CopyProcess/ErrorProcess/And others
5
+ > [!NOTE]
6
+ > **Work in Progress**: This package is currently under active development. While core features are highly functional, the API may undergo minor refinements.
7
+
8
+
9
+ ## Supported Capabilities
10
+
11
+ * **`FileSystem`**: Cluster-level operations (`stat`, `dirList`, `rm`, `mkdir`, `locate`).
12
+ * **`File`**: High-performance I/O (`open`, `read`, `write`, `vectorRead`, server-side `clone`).
13
+ * **`CopyProcess`**: Asynchronous, highly-parallel data transfers with real-time progress callbacks.
14
+ * **`Env`**: Type-safe configuration management and OS-level auth protocol routing.
15
+ * **`Url`**: XRootD-specific URI scheme parsing and validation.
16
+
17
+
18
+ ## Prerequisites
19
+
20
+ To ensure compatibility with the pre-compiled native bindings, your environment must meet the following requirements:
21
+
22
+ * **Node.js**: Version 20.x or higher.
23
+ * **System Dependencies (Linux)**: Requires `libstdc++` providing `GLIBCXX_3.4.26` or newer (typically GCC 9+, e.g., CentOS 9 / Ubuntu 20.04+).
24
+ * **System Dependencies (Macos)**: Requires macos 14 or later for arm64 architecture, macos 15 or later for intel architecture.
25
+ * **Authentication**: Ensure your host machine has valid tickets (e.g., `kinit` for Kerberos) if accessing a secured cluster.
26
+
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ npm install xrootd
32
+ # or
33
+ yarn add xrootd
34
+
35
+ ```
8
36
 
9
37
  ---
10
38
 
11
- minimium gcc version for linux: GLIBCXX_3.4.26 (GCC 9 or newer) <br> minimium node version: node20
39
+ ## Usage Examples
40
+
41
+ ### 1. Configuration & Basic FileSystem
42
+
43
+ ```typescript
44
+ import { Env, FileSystem } from 'xrootd';
45
+
46
+ // Safely configure underlying XRootD environment
47
+ Env.configure({
48
+ RequestTimeout: 30, // Prevent 30-min infinite hangs (Node.js friendly)
49
+ WorkerThreads: 4, // Boost underlying multiplexing
50
+ SecProtocol: 'krb5,unix' // Force Kerberos or Unix socket auth
51
+ });
52
+
53
+ const fs = new FileSystem('root://eos01.ihep.ac.cn/');
54
+
55
+ async function checkCluster() {
56
+ try {
57
+ const stat = await fs.stat('/eos/lhaaso/data');
58
+ console.log(`Directory size: ${stat.size} bytes`);
59
+ console.log(`Modified at: ${stat.modTimeAsString}`);
60
+ } catch (err: any) {
61
+ if (err.code === 'ENOENT') {
62
+ console.error('File not found in cluster.');
63
+ } else {
64
+ console.error(err.message);
65
+ }
66
+ }
67
+ }
68
+
69
+ ```
70
+
71
+ ### 2. Zero-Copy File Reading
72
+
73
+ ```typescript
74
+ import { File, OpenFlags } from 'xrootd';
75
+
76
+ async function readHeader() {
77
+ const file = new File();
78
+
79
+ // Auto-translates and handles C++ lifecycle
80
+ await file.open('root://eos01.ihep.ac.cn//eos/test.dat', OpenFlags.Read);
81
+
82
+ // The returned buffer is a zero-copy mapping of C++ allocated memory
83
+ const buffer = await file.read(0n, 1024);
84
+ console.log(buffer.toString('utf-8'));
85
+
86
+ await file.close();
87
+ }
88
+
89
+ ```
90
+
91
+ ### 3. High-Performance CopyProcess with Progress
92
+
93
+ ```typescript
94
+ import { CopyProcess } from 'xrootd';
95
+
96
+ async function syncData() {
97
+ const cp = new CopyProcess();
98
+
99
+ cp.addJob({
100
+ source: 'root://eos01.ihep.ac.cn//eos/data.raw',
101
+ target: '/local/disk/data.raw',
102
+ force: true,
103
+ parallelChunks: 4
104
+ });
105
+
106
+ await cp.prepare();
107
+
108
+ const results = await cp.run((jobNum, processed, total) => {
109
+ const percent = ((processed / total) * 100).toFixed(2);
110
+ console.log(`Job ${jobNum} Progress: ${percent}%`);
111
+ });
112
+
113
+ console.log('Copy completed:', results);
114
+ }
115
+
116
+ ```
117
+
118
+ ## ✨ Why `xrootd`? (Core Architecture)
119
+
120
+ Unlike traditional Node.js C++ addons, `xrootd` is architected for **High Energy Physics (HEP)** and **Big Data** workloads:
121
+
122
+ * **True Native Async**: Bypasses the notoriously limited Node.js `libuv` thread pool (default 4 threads). It hooks directly into XRootD's underlying C++ event loop via N-API `ThreadSafeFunction`, allowing thousands of concurrent requests without blocking the V8 engine.
123
+ * **Absolute Zero-Copy I/O**: Implements direct memory handoffs. Data read from the EOS cluster is mounted directly as V8 `Buffer` objects without a single byte of internal memory copying, completely eliminating GC (Garbage Collection) pauses during heavy I/O.
124
+ * **Idiomatic Node.js Experience**: "Thin C++, Thick TS". Complex XRootD protocol errors (e.g., `3011`) are smartly translated into standard Node.js exceptions (e.g., `ENOENT`, `EACCES`), complete with system call contexts and retryable flags.
125
+ * **Zero-Config Authentication**: Bundles essential security plugins (Kerberos, SSS, Unix, Token) natively. Path injection is handled automatically under the hood—no more `[FATAL] Auth failed` or missing `.so` nightmares.
12
126
 
13
127
  ---
14
128
 
15
- License
129
+ ## Licensing
130
+
131
+ This project is released under a **Dual License** strategy to balance open-source compatibility and developer freedom:
16
132
 
17
- This project follows the dual license: [GNU GPLv3](LICENSE-GPLv3) and [MIT](LICENSE-MIT).
133
+ 1. **[GNU GPLv3](https://www.google.com/search?q=https://www.gnu.org/licenses/gpl-3.0.en.html)**: The native binding codebase adheres to the GPLv3 license to remain fully compatible with the upstream C++ XRootD project.
134
+ 2. **[MIT](https://www.google.com/search?q=https://opensource.org/licenses/MIT)**: The core TypeScript APIs, interface definitions, and glue layers are provided under the MIT license, allowing you to integrate the TS components into your own software architectures without viral restrictive requirements.
18
135
 
19
- The XRootD project is GPL, so this project is also GPL. However, the API of this project is provided under MIT license, so you can use this project in your project without any restriction.
136
+ *Disclaimer: This project is a third-party community initiative built for modern web ecosystems and is not affiliated with, officially endorsed by, or sponsored by the core XRootD project.*
137
+
138
+ ---
20
139
 
140
+ ## Contributing
21
141
 
142
+ We welcome contributions from the high-energy physics, astrophysics, and Node.js communities! Whether it's reporting bugs, improving documentation, or adding support for advanced XRootD features.
22
143
 
23
- This project is not affiliated with the XRootD project. It is a third-party project that provides a Node.js interface to the XRootD library.
144
+ *Contribution guidelines coming soon.*
package/dist/index.cjs CHANGED
@@ -179,7 +179,9 @@ var File = class {
179
179
  return this._internal.IsOpen();
180
180
  }
181
181
  async getProperty(name) {
182
- return this._internal.GetProperty(name);
182
+ const ret = this._internal.GetProperty(name);
183
+ if (ret.success) return ret.value;
184
+ throw new Error("TODO");
183
185
  }
184
186
  async setProperty(name, value) {
185
187
  return this._internal.SetProperty(name, value);
@@ -211,25 +213,37 @@ var File = class {
211
213
  /**
212
214
  * 设置文件的扩展属性
213
215
  */
214
- async setXAttr(name, value) {
215
- return this._internal.SetXAttr(name);
216
+ async setXAttrs(attrs) {
217
+ return this._internal.SetXAttr(attrs);
218
+ }
219
+ /**
220
+ * 设置文件的扩展属性
221
+ */
222
+ async setXAttr(key, value) {
223
+ return (await this._internal.SetXAttr({ [key]: value }))[0].ok;
216
224
  }
217
225
  /**
218
226
  * 获取文件的扩展属性
219
227
  */
220
- async getXAttr(name) {
221
- return this._internal.GetXAttr(name);
228
+ async getXAttrs(keys) {
229
+ return this._internal.GetXAttr(keys);
230
+ }
231
+ async getXAttr(key) {
232
+ return (await this._internal.GetXAttr([key]))[key];
222
233
  }
223
234
  /**
224
235
  * 删除文件的扩展属性
225
236
  */
226
- async delXAttr(name) {
227
- return this._internal.DelXAttr(name);
237
+ async delXAttrs(keys) {
238
+ return this._internal.DelXAttr(keys);
239
+ }
240
+ async delXAttr(key) {
241
+ return (await this._internal.DelXAttr([key]))[0].ok;
228
242
  }
229
243
  /**
230
- * 列出文件所有的扩展属性名称
244
+ * 列出文件所有的扩展属性和内容
231
245
  */
232
- async listXAttr() {
246
+ async listXAttrs() {
233
247
  return this._internal.ListXAttr();
234
248
  }
235
249
  /**
@@ -535,25 +549,34 @@ var FileSystem = class {
535
549
  * @param targetPath 目标路径
536
550
  * @param attrs 扩展属性键值对记录
537
551
  */
538
- async setXAttr(targetPath, attrs) {
552
+ async setXAttrs(targetPath, attrs) {
539
553
  return this._internal.SetXAttr(this._normalize(targetPath), attrs);
540
554
  }
555
+ async setXAttr(targetPath, key, value) {
556
+ return (await this.setXAttrs(targetPath, { [key]: value }))[0].ok;
557
+ }
541
558
  /**
542
559
  * 获取扩展属性
543
560
  * @param targetPath 目标路径
544
561
  * @param keys 需要获取的属性名数组
545
562
  */
546
- async getXAttr(targetPath, keys) {
563
+ async getXAttrs(targetPath, keys) {
547
564
  return this._internal.GetXAttr(this._normalize(targetPath), keys);
548
565
  }
566
+ async getXAttr(targetPath, key) {
567
+ return (await this.getXAttrs(targetPath, [key]))[key];
568
+ }
549
569
  /**
550
570
  * 删除指定的扩展属性
551
571
  * @param targetPath 目标路径
552
572
  * @param keys 需要删除的属性名数组
553
573
  */
554
- async delXAttr(targetPath, keys) {
574
+ async delXAttrs(targetPath, keys) {
555
575
  return this._internal.DelXAttr(this._normalize(targetPath), keys);
556
576
  }
577
+ async delXAttr(targetPath, key) {
578
+ return (await this.delXAttrs(targetPath, [key]))[0].ok;
579
+ }
557
580
  /**
558
581
  * 列出目标文件或目录的所有扩展属性
559
582
  */
@@ -562,6 +585,47 @@ var FileSystem = class {
562
585
  }
563
586
  };
564
587
  //#endregion
588
+ //#region lib/copy.ts
589
+ var CopyProcess = class {
590
+ nativeCp;
591
+ constructor() {
592
+ this.nativeCp = new nativeAddon.CopyProcess();
593
+ }
594
+ /**
595
+ * Add a job to the copy process synchronously
596
+ */
597
+ addJob(config) {
598
+ this.nativeCp.AddJob(config);
599
+ }
600
+ /**
601
+ * Asynchronously prepare the jobs (resolve DNS, check endpoints, etc.)
602
+ */
603
+ async prepare() {
604
+ return this.nativeCp.Prepare();
605
+ }
606
+ /**
607
+ * Run the copy process.
608
+ * @param onProgress Optional progress callback
609
+ * @returns Array of results corresponding to the jobs added
610
+ */
611
+ async run(onProgress) {
612
+ if (onProgress) this.nativeCp.SetEventListener("progress", onProgress);
613
+ return this.nativeCp.Run();
614
+ }
615
+ /**
616
+ * Abort the running copy process.
617
+ */
618
+ abort() {
619
+ this.nativeCp.CancelJob();
620
+ }
621
+ /**
622
+ * Abort the running copy process. (alias for abort)
623
+ */
624
+ cancelJob() {
625
+ this.nativeCp.CancelJob();
626
+ }
627
+ };
628
+ //#endregion
565
629
  //#region lib/url.ts
566
630
  /**
567
631
  * 纯 TypeScript 实现的 XRootD URL 解析器
@@ -747,6 +811,7 @@ Env.configure({
747
811
  });
748
812
  //#endregion
749
813
  exports.AccessMode = AccessMode;
814
+ exports.CopyProcess = CopyProcess;
750
815
  exports.Env = Env;
751
816
  exports.File = File;
752
817
  exports.FileSystem = FileSystem;
package/dist/index.d.cts CHANGED
@@ -8,6 +8,18 @@ interface IXRootDError extends Error {
8
8
  xrdErrNo: number;
9
9
  xrdErrMsg: string;
10
10
  }
11
+ type XRootDOkError = {
12
+ ok: true;
13
+ } | ({
14
+ ok: false;
15
+ } & IXRootDError);
16
+ type XAttrStatusResult = {
17
+ ok: true;
18
+ name: string;
19
+ } | ({
20
+ ok: false;
21
+ name: string;
22
+ } & IXRootDError);
11
23
  interface StatInfo {
12
24
  id: string;
13
25
  size: bigint;
@@ -56,16 +68,10 @@ interface DirListEntry {
56
68
  hostAddress: string;
57
69
  stat: StatInfo | null;
58
70
  }
59
- interface XAttrStatusResult {
60
- name: string;
61
- isOk: boolean;
62
- code: number;
63
- message: string;
64
- }
65
71
  declare class XRootDError extends Error {
66
72
  code: number;
67
73
  status: number;
68
- constructor(status: IXRootDError);
74
+ constructor(status: XRootDOkError);
69
75
  }
70
76
  /**
71
77
  * 对应 src/core/XrdNodeFile.cc 中 Init 暴露的类
@@ -93,6 +99,65 @@ interface INativeFile {
93
99
  ListXAttr(): Promise<Record<string, string>>;
94
100
  Clone(list: CloneLocationRequest[]): Promise<void>;
95
101
  }
102
+ interface CopyJobConfig {
103
+ /** original source URL */
104
+ source: string;
105
+ /** target directory or file */
106
+ target: string;
107
+ /** maximum number sources */
108
+ sourceLimit?: number;
109
+ /** overwrite target if exists */
110
+ force?: boolean;
111
+ /** persistify only on successful close */
112
+ posc?: boolean;
113
+ /** ignore locking semantics on destination */
114
+ coerce?: boolean;
115
+ /** create path to the file if it doesn't exist */
116
+ makeDir?: boolean;
117
+ /** "first" try third party copy, if it fails try normal copy; "only" only try third party copy */
118
+ thirdParty?: string;
119
+ /** "none" - no checksumming
120
+ "end2end" - end to end checksumming
121
+ "source" - calculate checksum at source
122
+ "target" - calculate checksum at target */
123
+ checkSumMode?: "none" | "end2end" | "source" | "target";
124
+ /** type of the checksum to be used */
125
+ checkSumType?: string;
126
+ /** checksum preset */
127
+ checkSumPreset?: string;
128
+ /** size of a copy chunks in bytes */
129
+ chunkSize?: number;
130
+ /** number of chunks that should be requested in parallel
131
+ *
132
+ * [uint8_t]
133
+ */
134
+ parallelChunks?: number;
135
+ /** time limit for successfull initialization of the copy job
136
+ * [time_t]
137
+ */
138
+ initTimeout?: number;
139
+ /** time limit for the actual copy to finish
140
+ * [time_t]
141
+ */
142
+ tpcTimeout?: number;
143
+ /** support for the case where the size source file may change during reading process */
144
+ dynamicSource?: boolean;
145
+ }
146
+ interface CopyJobResult {
147
+ /** checksum at source, if requested */
148
+ sourceCheckSum?: string;
149
+ /** checksum at target, if requested */
150
+ targetCheckSum?: string;
151
+ /** file size */
152
+ size: bigint;
153
+ /** status of the copy operation */
154
+ status?: XRootDOkError;
155
+ /** all sources used */
156
+ sources?: string[];
157
+ /** the actual disk server target */
158
+ realTarget?: string;
159
+ }
160
+ type ProgressCallback = (jobNum: number, processed: number, total: number) => void;
96
161
  interface ReadStreamOptions {
97
162
  start?: bigint;
98
163
  end?: bigint;
@@ -232,7 +297,7 @@ declare class File$1 {
232
297
  */
233
298
  isOpen(): boolean;
234
299
  getProperty(name: string): Promise<string>;
235
- setProperty(name: string, value: string): Promise<void>;
300
+ setProperty(name: string, value: string): Promise<boolean>;
236
301
  /**
237
302
  * 向量化读取 (Vector Read)
238
303
  * 在单个请求中从文件的多个非连续区域读取数据,极大地减少网络往返开销。
@@ -248,19 +313,25 @@ declare class File$1 {
248
313
  /**
249
314
  * 设置文件的扩展属性
250
315
  */
251
- setXAttr(name: string, value: string): Promise<void>;
316
+ setXAttrs(attrs: Record<string, string>): Promise<XAttrStatusResult[]>;
317
+ /**
318
+ * 设置文件的扩展属性
319
+ */
320
+ setXAttr(key: string, value: string): Promise<boolean>;
252
321
  /**
253
322
  * 获取文件的扩展属性
254
323
  */
255
- getXAttr(name: string): Promise<string>;
324
+ getXAttrs(keys: string[]): Promise<Record<string, string>>;
325
+ getXAttr(key: string): Promise<string>;
256
326
  /**
257
327
  * 删除文件的扩展属性
258
328
  */
259
- delXAttr(name: string): Promise<void>;
329
+ delXAttrs(keys: string[]): Promise<XAttrStatusResult[]>;
330
+ delXAttr(key: string): Promise<boolean>;
260
331
  /**
261
- * 列出文件所有的扩展属性名称
332
+ * 列出文件所有的扩展属性和内容
262
333
  */
263
- listXAttr(): Promise<string[]>;
334
+ listXAttrs(): Promise<Record<string, string>>;
264
335
  /**
265
336
  * 将其他文件的指定区间在服务器端克隆到当前文件
266
337
  * 当前文件必须以写入/更新模式打开
@@ -408,25 +479,56 @@ declare class FileSystem {
408
479
  * @param targetPath 目标路径
409
480
  * @param attrs 扩展属性键值对记录
410
481
  */
411
- setXAttr(targetPath: string, attrs: Record<string, string>): Promise<XAttrStatusResult[]>;
482
+ setXAttrs(targetPath: string, attrs: Record<string, string>): Promise<XAttrStatusResult[]>;
483
+ setXAttr(targetPath: string, key: string, value: string): Promise<boolean>;
412
484
  /**
413
485
  * 获取扩展属性
414
486
  * @param targetPath 目标路径
415
487
  * @param keys 需要获取的属性名数组
416
488
  */
417
- getXAttr(targetPath: string, keys: string[]): Promise<Record<string, string>>;
489
+ getXAttrs(targetPath: string, keys: string[]): Promise<Record<string, string>>;
490
+ getXAttr(targetPath: string, key: string): Promise<string>;
418
491
  /**
419
492
  * 删除指定的扩展属性
420
493
  * @param targetPath 目标路径
421
494
  * @param keys 需要删除的属性名数组
422
495
  */
423
- delXAttr(targetPath: string, keys: string[]): Promise<XAttrStatusResult[]>;
496
+ delXAttrs(targetPath: string, keys: string[]): Promise<XAttrStatusResult[]>;
497
+ delXAttr(targetPath: string, key: string): Promise<boolean>;
424
498
  /**
425
499
  * 列出目标文件或目录的所有扩展属性
426
500
  */
427
501
  listXAttr(targetPath: string): Promise<Record<string, string>>;
428
502
  }
429
503
  //#endregion
504
+ //#region lib/copy.d.ts
505
+ declare class CopyProcess {
506
+ private nativeCp;
507
+ constructor();
508
+ /**
509
+ * Add a job to the copy process synchronously
510
+ */
511
+ addJob(config: CopyJobConfig): void;
512
+ /**
513
+ * Asynchronously prepare the jobs (resolve DNS, check endpoints, etc.)
514
+ */
515
+ prepare(): Promise<void>;
516
+ /**
517
+ * Run the copy process.
518
+ * @param onProgress Optional progress callback
519
+ * @returns Array of results corresponding to the jobs added
520
+ */
521
+ run(onProgress?: ProgressCallback): Promise<CopyJobResult[]>;
522
+ /**
523
+ * Abort the running copy process.
524
+ */
525
+ abort(): void;
526
+ /**
527
+ * Abort the running copy process. (alias for abort)
528
+ */
529
+ cancelJob(): void;
530
+ }
531
+ //#endregion
430
532
  //#region lib/url.d.ts
431
533
  /**
432
534
  * 纯 TypeScript 实现的 XRootD URL 解析器
@@ -758,4 +860,4 @@ declare class XRootDEnvironment {
758
860
  }
759
861
  declare const Env: XRootDEnvironment;
760
862
  //#endregion
761
- export { AccessMode, Env, File$1 as File, FileSystem, type LocationInfo, MkDirFlags, OpenFlags, type PropertyList, type ReadChunkRequest, type ReadStreamOptions, type StatInfo, type StatVFSInfo, XRootDUrl as URL, type WriteStreamOptions, type XRootDError };
863
+ export { AccessMode, CopyProcess, Env, File$1 as File, FileSystem, type LocationInfo, MkDirFlags, OpenFlags, type PropertyList, type ReadChunkRequest, type ReadStreamOptions, type StatInfo, type StatVFSInfo, XRootDUrl as URL, type WriteStreamOptions, type XRootDError };
package/dist/index.d.mts CHANGED
@@ -8,6 +8,18 @@ interface IXRootDError extends Error {
8
8
  xrdErrNo: number;
9
9
  xrdErrMsg: string;
10
10
  }
11
+ type XRootDOkError = {
12
+ ok: true;
13
+ } | ({
14
+ ok: false;
15
+ } & IXRootDError);
16
+ type XAttrStatusResult = {
17
+ ok: true;
18
+ name: string;
19
+ } | ({
20
+ ok: false;
21
+ name: string;
22
+ } & IXRootDError);
11
23
  interface StatInfo {
12
24
  id: string;
13
25
  size: bigint;
@@ -56,16 +68,10 @@ interface DirListEntry {
56
68
  hostAddress: string;
57
69
  stat: StatInfo | null;
58
70
  }
59
- interface XAttrStatusResult {
60
- name: string;
61
- isOk: boolean;
62
- code: number;
63
- message: string;
64
- }
65
71
  declare class XRootDError extends Error {
66
72
  code: number;
67
73
  status: number;
68
- constructor(status: IXRootDError);
74
+ constructor(status: XRootDOkError);
69
75
  }
70
76
  /**
71
77
  * 对应 src/core/XrdNodeFile.cc 中 Init 暴露的类
@@ -93,6 +99,65 @@ interface INativeFile {
93
99
  ListXAttr(): Promise<Record<string, string>>;
94
100
  Clone(list: CloneLocationRequest[]): Promise<void>;
95
101
  }
102
+ interface CopyJobConfig {
103
+ /** original source URL */
104
+ source: string;
105
+ /** target directory or file */
106
+ target: string;
107
+ /** maximum number sources */
108
+ sourceLimit?: number;
109
+ /** overwrite target if exists */
110
+ force?: boolean;
111
+ /** persistify only on successful close */
112
+ posc?: boolean;
113
+ /** ignore locking semantics on destination */
114
+ coerce?: boolean;
115
+ /** create path to the file if it doesn't exist */
116
+ makeDir?: boolean;
117
+ /** "first" try third party copy, if it fails try normal copy; "only" only try third party copy */
118
+ thirdParty?: string;
119
+ /** "none" - no checksumming
120
+ "end2end" - end to end checksumming
121
+ "source" - calculate checksum at source
122
+ "target" - calculate checksum at target */
123
+ checkSumMode?: "none" | "end2end" | "source" | "target";
124
+ /** type of the checksum to be used */
125
+ checkSumType?: string;
126
+ /** checksum preset */
127
+ checkSumPreset?: string;
128
+ /** size of a copy chunks in bytes */
129
+ chunkSize?: number;
130
+ /** number of chunks that should be requested in parallel
131
+ *
132
+ * [uint8_t]
133
+ */
134
+ parallelChunks?: number;
135
+ /** time limit for successfull initialization of the copy job
136
+ * [time_t]
137
+ */
138
+ initTimeout?: number;
139
+ /** time limit for the actual copy to finish
140
+ * [time_t]
141
+ */
142
+ tpcTimeout?: number;
143
+ /** support for the case where the size source file may change during reading process */
144
+ dynamicSource?: boolean;
145
+ }
146
+ interface CopyJobResult {
147
+ /** checksum at source, if requested */
148
+ sourceCheckSum?: string;
149
+ /** checksum at target, if requested */
150
+ targetCheckSum?: string;
151
+ /** file size */
152
+ size: bigint;
153
+ /** status of the copy operation */
154
+ status?: XRootDOkError;
155
+ /** all sources used */
156
+ sources?: string[];
157
+ /** the actual disk server target */
158
+ realTarget?: string;
159
+ }
160
+ type ProgressCallback = (jobNum: number, processed: number, total: number) => void;
96
161
  interface ReadStreamOptions {
97
162
  start?: bigint;
98
163
  end?: bigint;
@@ -232,7 +297,7 @@ declare class File$1 {
232
297
  */
233
298
  isOpen(): boolean;
234
299
  getProperty(name: string): Promise<string>;
235
- setProperty(name: string, value: string): Promise<void>;
300
+ setProperty(name: string, value: string): Promise<boolean>;
236
301
  /**
237
302
  * 向量化读取 (Vector Read)
238
303
  * 在单个请求中从文件的多个非连续区域读取数据,极大地减少网络往返开销。
@@ -248,19 +313,25 @@ declare class File$1 {
248
313
  /**
249
314
  * 设置文件的扩展属性
250
315
  */
251
- setXAttr(name: string, value: string): Promise<void>;
316
+ setXAttrs(attrs: Record<string, string>): Promise<XAttrStatusResult[]>;
317
+ /**
318
+ * 设置文件的扩展属性
319
+ */
320
+ setXAttr(key: string, value: string): Promise<boolean>;
252
321
  /**
253
322
  * 获取文件的扩展属性
254
323
  */
255
- getXAttr(name: string): Promise<string>;
324
+ getXAttrs(keys: string[]): Promise<Record<string, string>>;
325
+ getXAttr(key: string): Promise<string>;
256
326
  /**
257
327
  * 删除文件的扩展属性
258
328
  */
259
- delXAttr(name: string): Promise<void>;
329
+ delXAttrs(keys: string[]): Promise<XAttrStatusResult[]>;
330
+ delXAttr(key: string): Promise<boolean>;
260
331
  /**
261
- * 列出文件所有的扩展属性名称
332
+ * 列出文件所有的扩展属性和内容
262
333
  */
263
- listXAttr(): Promise<string[]>;
334
+ listXAttrs(): Promise<Record<string, string>>;
264
335
  /**
265
336
  * 将其他文件的指定区间在服务器端克隆到当前文件
266
337
  * 当前文件必须以写入/更新模式打开
@@ -408,25 +479,56 @@ declare class FileSystem {
408
479
  * @param targetPath 目标路径
409
480
  * @param attrs 扩展属性键值对记录
410
481
  */
411
- setXAttr(targetPath: string, attrs: Record<string, string>): Promise<XAttrStatusResult[]>;
482
+ setXAttrs(targetPath: string, attrs: Record<string, string>): Promise<XAttrStatusResult[]>;
483
+ setXAttr(targetPath: string, key: string, value: string): Promise<boolean>;
412
484
  /**
413
485
  * 获取扩展属性
414
486
  * @param targetPath 目标路径
415
487
  * @param keys 需要获取的属性名数组
416
488
  */
417
- getXAttr(targetPath: string, keys: string[]): Promise<Record<string, string>>;
489
+ getXAttrs(targetPath: string, keys: string[]): Promise<Record<string, string>>;
490
+ getXAttr(targetPath: string, key: string): Promise<string>;
418
491
  /**
419
492
  * 删除指定的扩展属性
420
493
  * @param targetPath 目标路径
421
494
  * @param keys 需要删除的属性名数组
422
495
  */
423
- delXAttr(targetPath: string, keys: string[]): Promise<XAttrStatusResult[]>;
496
+ delXAttrs(targetPath: string, keys: string[]): Promise<XAttrStatusResult[]>;
497
+ delXAttr(targetPath: string, key: string): Promise<boolean>;
424
498
  /**
425
499
  * 列出目标文件或目录的所有扩展属性
426
500
  */
427
501
  listXAttr(targetPath: string): Promise<Record<string, string>>;
428
502
  }
429
503
  //#endregion
504
+ //#region lib/copy.d.ts
505
+ declare class CopyProcess {
506
+ private nativeCp;
507
+ constructor();
508
+ /**
509
+ * Add a job to the copy process synchronously
510
+ */
511
+ addJob(config: CopyJobConfig): void;
512
+ /**
513
+ * Asynchronously prepare the jobs (resolve DNS, check endpoints, etc.)
514
+ */
515
+ prepare(): Promise<void>;
516
+ /**
517
+ * Run the copy process.
518
+ * @param onProgress Optional progress callback
519
+ * @returns Array of results corresponding to the jobs added
520
+ */
521
+ run(onProgress?: ProgressCallback): Promise<CopyJobResult[]>;
522
+ /**
523
+ * Abort the running copy process.
524
+ */
525
+ abort(): void;
526
+ /**
527
+ * Abort the running copy process. (alias for abort)
528
+ */
529
+ cancelJob(): void;
530
+ }
531
+ //#endregion
430
532
  //#region lib/url.d.ts
431
533
  /**
432
534
  * 纯 TypeScript 实现的 XRootD URL 解析器
@@ -758,4 +860,4 @@ declare class XRootDEnvironment {
758
860
  }
759
861
  declare const Env: XRootDEnvironment;
760
862
  //#endregion
761
- export { AccessMode, Env, File$1 as File, FileSystem, type LocationInfo, MkDirFlags, OpenFlags, type PropertyList, type ReadChunkRequest, type ReadStreamOptions, type StatInfo, type StatVFSInfo, XRootDUrl as URL, type WriteStreamOptions, type XRootDError };
863
+ export { AccessMode, CopyProcess, Env, File$1 as File, FileSystem, type LocationInfo, MkDirFlags, OpenFlags, type PropertyList, type ReadChunkRequest, type ReadStreamOptions, type StatInfo, type StatVFSInfo, XRootDUrl as URL, type WriteStreamOptions, type XRootDError };
package/dist/index.mjs CHANGED
@@ -154,7 +154,9 @@ var File = class {
154
154
  return this._internal.IsOpen();
155
155
  }
156
156
  async getProperty(name) {
157
- return this._internal.GetProperty(name);
157
+ const ret = this._internal.GetProperty(name);
158
+ if (ret.success) return ret.value;
159
+ throw new Error("TODO");
158
160
  }
159
161
  async setProperty(name, value) {
160
162
  return this._internal.SetProperty(name, value);
@@ -186,25 +188,37 @@ var File = class {
186
188
  /**
187
189
  * 设置文件的扩展属性
188
190
  */
189
- async setXAttr(name, value) {
190
- return this._internal.SetXAttr(name);
191
+ async setXAttrs(attrs) {
192
+ return this._internal.SetXAttr(attrs);
193
+ }
194
+ /**
195
+ * 设置文件的扩展属性
196
+ */
197
+ async setXAttr(key, value) {
198
+ return (await this._internal.SetXAttr({ [key]: value }))[0].ok;
191
199
  }
192
200
  /**
193
201
  * 获取文件的扩展属性
194
202
  */
195
- async getXAttr(name) {
196
- return this._internal.GetXAttr(name);
203
+ async getXAttrs(keys) {
204
+ return this._internal.GetXAttr(keys);
205
+ }
206
+ async getXAttr(key) {
207
+ return (await this._internal.GetXAttr([key]))[key];
197
208
  }
198
209
  /**
199
210
  * 删除文件的扩展属性
200
211
  */
201
- async delXAttr(name) {
202
- return this._internal.DelXAttr(name);
212
+ async delXAttrs(keys) {
213
+ return this._internal.DelXAttr(keys);
214
+ }
215
+ async delXAttr(key) {
216
+ return (await this._internal.DelXAttr([key]))[0].ok;
203
217
  }
204
218
  /**
205
- * 列出文件所有的扩展属性名称
219
+ * 列出文件所有的扩展属性和内容
206
220
  */
207
- async listXAttr() {
221
+ async listXAttrs() {
208
222
  return this._internal.ListXAttr();
209
223
  }
210
224
  /**
@@ -510,25 +524,34 @@ var FileSystem = class {
510
524
  * @param targetPath 目标路径
511
525
  * @param attrs 扩展属性键值对记录
512
526
  */
513
- async setXAttr(targetPath, attrs) {
527
+ async setXAttrs(targetPath, attrs) {
514
528
  return this._internal.SetXAttr(this._normalize(targetPath), attrs);
515
529
  }
530
+ async setXAttr(targetPath, key, value) {
531
+ return (await this.setXAttrs(targetPath, { [key]: value }))[0].ok;
532
+ }
516
533
  /**
517
534
  * 获取扩展属性
518
535
  * @param targetPath 目标路径
519
536
  * @param keys 需要获取的属性名数组
520
537
  */
521
- async getXAttr(targetPath, keys) {
538
+ async getXAttrs(targetPath, keys) {
522
539
  return this._internal.GetXAttr(this._normalize(targetPath), keys);
523
540
  }
541
+ async getXAttr(targetPath, key) {
542
+ return (await this.getXAttrs(targetPath, [key]))[key];
543
+ }
524
544
  /**
525
545
  * 删除指定的扩展属性
526
546
  * @param targetPath 目标路径
527
547
  * @param keys 需要删除的属性名数组
528
548
  */
529
- async delXAttr(targetPath, keys) {
549
+ async delXAttrs(targetPath, keys) {
530
550
  return this._internal.DelXAttr(this._normalize(targetPath), keys);
531
551
  }
552
+ async delXAttr(targetPath, key) {
553
+ return (await this.delXAttrs(targetPath, [key]))[0].ok;
554
+ }
532
555
  /**
533
556
  * 列出目标文件或目录的所有扩展属性
534
557
  */
@@ -537,6 +560,47 @@ var FileSystem = class {
537
560
  }
538
561
  };
539
562
  //#endregion
563
+ //#region lib/copy.ts
564
+ var CopyProcess = class {
565
+ nativeCp;
566
+ constructor() {
567
+ this.nativeCp = new nativeAddon.CopyProcess();
568
+ }
569
+ /**
570
+ * Add a job to the copy process synchronously
571
+ */
572
+ addJob(config) {
573
+ this.nativeCp.AddJob(config);
574
+ }
575
+ /**
576
+ * Asynchronously prepare the jobs (resolve DNS, check endpoints, etc.)
577
+ */
578
+ async prepare() {
579
+ return this.nativeCp.Prepare();
580
+ }
581
+ /**
582
+ * Run the copy process.
583
+ * @param onProgress Optional progress callback
584
+ * @returns Array of results corresponding to the jobs added
585
+ */
586
+ async run(onProgress) {
587
+ if (onProgress) this.nativeCp.SetEventListener("progress", onProgress);
588
+ return this.nativeCp.Run();
589
+ }
590
+ /**
591
+ * Abort the running copy process.
592
+ */
593
+ abort() {
594
+ this.nativeCp.CancelJob();
595
+ }
596
+ /**
597
+ * Abort the running copy process. (alias for abort)
598
+ */
599
+ cancelJob() {
600
+ this.nativeCp.CancelJob();
601
+ }
602
+ };
603
+ //#endregion
540
604
  //#region lib/url.ts
541
605
  /**
542
606
  * 纯 TypeScript 实现的 XRootD URL 解析器
@@ -721,4 +785,4 @@ Env.configure({
721
785
  WorkerThreads: 4
722
786
  });
723
787
  //#endregion
724
- export { AccessMode, Env, File, FileSystem, MkDirFlags, OpenFlags, XRootDUrl as URL };
788
+ export { AccessMode, CopyProcess, Env, File, FileSystem, MkDirFlags, OpenFlags, XRootDUrl as URL };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xrootd",
3
- "version": "0.1.12",
3
+ "version": "0.2.0",
4
4
  "description": "Prebuilt Node.js Native Addon for XRootD, allowing JS access to EOS file systems",
5
5
  "type": "module",
6
6
  "exports": {
@@ -77,7 +77,7 @@
77
77
  "build": "pnpm run build:addon && pnpm run build:binaries && pnpm run build:tsc && pnpm run build:ts",
78
78
  "build:clean": "rm -r ./build && pnpm run build",
79
79
  "pack": "mkdir -p packages && pnpm pack --pack-destination ./packages",
80
- "test:prep": "unrun ./test/prepare.ts",
81
- "test": "node --test test/*.mjs"
80
+ "test:prep": "unrun ./tests/prepare.ts",
81
+ "test": "node --test tests/*.mjs"
82
82
  }
83
83
  }
Binary file
Binary file
Binary file
Binary file