xrootd 0.2.3 → 1.0.0-beta.1
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/CHANGELOG.md +24 -0
- package/LICENSE +189 -0
- package/README.md +334 -101
- package/dist/index.d.mts +620 -821
- package/dist/index.mjs +1554 -723
- package/package.json +65 -83
- package/LICENSE-GPLv3 +0 -674
- package/LICENSE-MIT +0 -7
- package/dist/index.cjs +0 -817
- package/dist/index.d.cts +0 -864
- package/libs/darwin-arm64/libXrdCl.6.dylib +0 -0
- package/libs/darwin-arm64/libXrdCrypto.6.dylib +0 -0
- package/libs/darwin-arm64/libXrdCryptossl-6.so +0 -0
- package/libs/darwin-arm64/libXrdSec-6.so +0 -0
- package/libs/darwin-arm64/libXrdSecProt-6.so +0 -0
- package/libs/darwin-arm64/libXrdSeckrb5-6.so +0 -0
- package/libs/darwin-arm64/libXrdSecpwd-6.so +0 -0
- package/libs/darwin-arm64/libXrdSecsss-6.so +0 -0
- package/libs/darwin-arm64/libXrdSecunix-6.so +0 -0
- package/libs/darwin-arm64/libXrdSecztn-6.so +0 -0
- package/libs/darwin-arm64/libXrdUtils.6.dylib +0 -0
- package/libs/darwin-arm64/libXrdXml.6.dylib +0 -0
- package/libs/darwin-x64/libXrdCl.6.dylib +0 -0
- package/libs/darwin-x64/libXrdCrypto.6.dylib +0 -0
- package/libs/darwin-x64/libXrdCryptossl-6.so +0 -0
- package/libs/darwin-x64/libXrdSec-6.so +0 -0
- package/libs/darwin-x64/libXrdSecProt-6.so +0 -0
- package/libs/darwin-x64/libXrdSeckrb5-6.so +0 -0
- package/libs/darwin-x64/libXrdSecpwd-6.so +0 -0
- package/libs/darwin-x64/libXrdSecsss-6.so +0 -0
- package/libs/darwin-x64/libXrdSecunix-6.so +0 -0
- package/libs/darwin-x64/libXrdSecztn-6.so +0 -0
- package/libs/darwin-x64/libXrdUtils.6.dylib +0 -0
- package/libs/darwin-x64/libXrdXml.6.dylib +0 -0
- package/libs/linux-arm64/libXrdCl.so.6 +0 -0
- package/libs/linux-arm64/libXrdCrypto.so.6 +0 -0
- package/libs/linux-arm64/libXrdCryptossl-6.so +0 -0
- package/libs/linux-arm64/libXrdSec-6.so +0 -0
- package/libs/linux-arm64/libXrdSecProt-6.so +0 -0
- package/libs/linux-arm64/libXrdSeckrb5-6.so +0 -0
- package/libs/linux-arm64/libXrdSecpwd-6.so +0 -0
- package/libs/linux-arm64/libXrdSecsss-6.so +0 -0
- package/libs/linux-arm64/libXrdSecunix-6.so +0 -0
- package/libs/linux-arm64/libXrdSecztn-6.so +0 -0
- package/libs/linux-arm64/libXrdUtils.so.6 +0 -0
- package/libs/linux-arm64/libXrdXml.so.6 +0 -0
- package/libs/linux-x64/libXrdCl.so.6 +0 -0
- package/libs/linux-x64/libXrdCrypto.so.6 +0 -0
- package/libs/linux-x64/libXrdCryptossl-6.so +0 -0
- package/libs/linux-x64/libXrdSec-6.so +0 -0
- package/libs/linux-x64/libXrdSecProt-6.so +0 -0
- package/libs/linux-x64/libXrdSeckrb5-6.so +0 -0
- package/libs/linux-x64/libXrdSecpwd-6.so +0 -0
- package/libs/linux-x64/libXrdSecsss-6.so +0 -0
- package/libs/linux-x64/libXrdSecunix-6.so +0 -0
- package/libs/linux-x64/libXrdSecztn-6.so +0 -0
- package/libs/linux-x64/libXrdUtils.so.6 +0 -0
- package/libs/linux-x64/libXrdXml.so.6 +0 -0
- package/prebuilds/darwin-arm64/xrootd.node +0 -0
- package/prebuilds/darwin-x64/xrootd.node +0 -0
- package/prebuilds/linux-arm64/xrootd.node +0 -0
- package/prebuilds/linux-x64/xrootd.node +0 -0
package/README.md
CHANGED
|
@@ -1,150 +1,383 @@
|
|
|
1
|
-
#
|
|
1
|
+
# xrootd
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A TypeScript client library for the [XRootD](https://xrootd.org) protocol.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
XRootD (eXtended ROOT Daemon) is a high-performance, fault-tolerant protocol for accessing and managing large-scale distributed storage systems. It is widely used in High Energy Physics (HEP) for data access at facilities like CERN's LHC experiments.
|
|
6
6
|
|
|
7
|
-
>
|
|
8
|
-
>
|
|
9
|
-
>
|
|
10
|
-
> ```bash
|
|
11
|
-
> npm install xrootd@next
|
|
12
|
-
> ```
|
|
13
|
-
> Feedback is highly appreciated!
|
|
7
|
+
> **🚀 v2.0.0 is Here: A Modern, Native Rewrite!**
|
|
8
|
+
> ⚠️ **Breaking Changes**: Because of the fundamental architecture shift, **v2 is not API-compatible with v1**.
|
|
9
|
+
> Please read our [Migration Guide (MIGRATING.md)](MIGRATING.md) to upgrade.
|
|
14
10
|
|
|
15
|
-
##
|
|
11
|
+
## Installation
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
* **`Env`**: Type-safe configuration management and OS-level auth protocol routing.
|
|
21
|
-
* **`Url`**: XRootD-specific URI scheme parsing and validation.
|
|
13
|
+
```sh
|
|
14
|
+
npm install xrootd
|
|
15
|
+
```
|
|
22
16
|
|
|
17
|
+
## Quick Start
|
|
23
18
|
|
|
24
|
-
|
|
19
|
+
```ts
|
|
20
|
+
import { XRootDClient, OpenFlags } from 'xrootd'
|
|
25
21
|
|
|
26
|
-
|
|
22
|
+
const client = new XRootDClient('root://server.example.com')
|
|
23
|
+
await client.connect()
|
|
27
24
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
// List directory contents
|
|
26
|
+
const list = await client.readdir('/data')
|
|
27
|
+
for (const entry of list.entries) {
|
|
28
|
+
console.log(entry.name, entry.size)
|
|
29
|
+
}
|
|
32
30
|
|
|
31
|
+
// Read a file
|
|
32
|
+
const file = await client.open('/data/file.dat')
|
|
33
|
+
const data = await file.read(0, 1024)
|
|
34
|
+
await file.close()
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
// Write a file
|
|
37
|
+
const file2 = await client.open('/data/output.dat', { flags: OpenFlags.Write | OpenFlags.New })
|
|
38
|
+
await file2.write(0, new Uint8Array([72, 101, 108, 108, 111]))
|
|
39
|
+
await file2.close()
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
await client.close()
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## API
|
|
45
|
+
|
|
46
|
+
### XRootDClient
|
|
40
47
|
|
|
48
|
+
High-level client that manages connection, authentication, and automatic redirect handling.
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { XRootDClient, OpenFlags } from 'xrootd'
|
|
52
|
+
|
|
53
|
+
const client = new XRootDClient('root://server.example.com', {
|
|
54
|
+
credentials: { username: 'user', password: 'pass' },
|
|
55
|
+
timeout: 30000,
|
|
56
|
+
maxRedirects: 16,
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
await client.connect()
|
|
60
|
+
|
|
61
|
+
// Connection state
|
|
62
|
+
console.log(client.isConnected) // boolean
|
|
63
|
+
console.log(client.location) // "root://server.example.com:1094/"
|
|
64
|
+
|
|
65
|
+
// File operations
|
|
66
|
+
const file = await client.open('/data/file.dat', { flags: OpenFlags.Read })
|
|
67
|
+
const data = await file.read(0, 1024)
|
|
68
|
+
await file.close()
|
|
69
|
+
|
|
70
|
+
// Filesystem operations
|
|
71
|
+
await client.mkdir('/new/dir')
|
|
72
|
+
const list = await client.readdir('/data')
|
|
73
|
+
await client.mv('/old/path', '/new/path')
|
|
74
|
+
await client.rm('/old/file')
|
|
75
|
+
await client.rmdir('/old/dir')
|
|
76
|
+
|
|
77
|
+
// Metadata — two approaches
|
|
78
|
+
const info1 = await client.stat('/data/file.dat') // opens + stats + closes
|
|
79
|
+
const info2 = await client.statFilesystem('/data/file.dat') // filesystem protocol, no file open
|
|
80
|
+
|
|
81
|
+
await client.close()
|
|
41
82
|
```
|
|
42
83
|
|
|
43
|
-
|
|
84
|
+
### File
|
|
44
85
|
|
|
45
|
-
|
|
86
|
+
File operations for reading, writing, and managing files on XRootD servers.
|
|
46
87
|
|
|
47
|
-
|
|
88
|
+
Obtained via `client.open()` — do not construct directly.
|
|
48
89
|
|
|
49
|
-
```
|
|
50
|
-
|
|
90
|
+
```ts
|
|
91
|
+
// Open a file
|
|
92
|
+
const file = await client.open('/data/file.dat', { flags: OpenFlags.Read })
|
|
51
93
|
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
94
|
+
// Core operations
|
|
95
|
+
const data: Uint8Array = await file.read(offset, size)
|
|
96
|
+
const written: number = await file.write(offset, data)
|
|
97
|
+
const info: StatInfo = await file.stat()
|
|
98
|
+
await file.sync()
|
|
99
|
+
await file.truncate(size)
|
|
100
|
+
await file.close()
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### FileSystem
|
|
104
|
+
|
|
105
|
+
Stateless filesystem metadata operations:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { FileSystem } from 'xrootd'
|
|
109
|
+
|
|
110
|
+
// Obtained internally by XRootDClient; shown here for reference
|
|
111
|
+
const fs = new FileSystem(mux)
|
|
112
|
+
|
|
113
|
+
// Metadata
|
|
114
|
+
const info = await fs.stat('/data/file.dat')
|
|
115
|
+
console.log(info.size, info.mtime, info.isDirectory)
|
|
116
|
+
|
|
117
|
+
// Directory operations
|
|
118
|
+
await fs.mkdir('/new/dir')
|
|
119
|
+
const list = await fs.readdir('/data')
|
|
120
|
+
await fs.mv('/old/path', '/new/path')
|
|
121
|
+
await fs.rm('/old/file')
|
|
122
|
+
await fs.rmdir('/old/dir')
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Authentication
|
|
126
|
+
|
|
127
|
+
XRootD supports multiple authentication mechanisms. The library automatically negotiates the best available protocol during handshake.
|
|
128
|
+
|
|
129
|
+
#### Supported Protocols
|
|
130
|
+
|
|
131
|
+
| Protocol | Description |
|
|
132
|
+
|----------|-------------|
|
|
133
|
+
| `host` | Host-based trust authentication (IP whitelist) |
|
|
134
|
+
| `sss` | Simple Shared Secret (Blowfish-ECB encrypted password) |
|
|
135
|
+
|
|
136
|
+
#### Configuration
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { XRootDClient } from 'xrootd'
|
|
140
|
+
|
|
141
|
+
// host authentication (server trusts client by IP)
|
|
142
|
+
const client = new XRootDClient('root://server.example.com', {
|
|
143
|
+
credentials: { username: 'user' },
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
// SSS authentication (shared secret)
|
|
147
|
+
const client = new XRootDClient('root://server.example.com', {
|
|
148
|
+
credentials: {
|
|
149
|
+
username: 'user',
|
|
150
|
+
password: 'shared-secret',
|
|
151
|
+
},
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
await client.connect()
|
|
155
|
+
```
|
|
58
156
|
|
|
59
|
-
|
|
157
|
+
When connecting to a server that requires authentication, the client will automatically:
|
|
60
158
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
159
|
+
1. Receive the server's supported authentication protocols (`secReqs`)
|
|
160
|
+
2. Select the first matching protocol from the list
|
|
161
|
+
3. Execute the authentication handshake (including multi-round `kXR_authmore` challenges)
|
|
162
|
+
|
|
163
|
+
### Error Handling
|
|
164
|
+
|
|
165
|
+
All errors are thrown as `XRootDError` instances:
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import { XRootDClient, OpenFlags, XRootDError } from 'xrootd'
|
|
169
|
+
|
|
170
|
+
const client = new XRootDClient('root://server.example.com')
|
|
171
|
+
await client.connect()
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
const file = await client.open('/nonexistent', { flags: OpenFlags.Read })
|
|
175
|
+
await file.read(0, 1024)
|
|
176
|
+
await file.close()
|
|
177
|
+
} catch (err) {
|
|
178
|
+
if (err instanceof XRootDError) {
|
|
179
|
+
console.log(err.code) // 3011 (NotFound)
|
|
180
|
+
console.log(err.message) // "File not found"
|
|
181
|
+
console.log(err.errno) // POSIX errno (if applicable)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
#### Common Error Codes
|
|
187
|
+
|
|
188
|
+
| Code | Constant | Description |
|
|
189
|
+
|------|----------|-------------|
|
|
190
|
+
| 3010 | `NotAuthorized` | Permission denied |
|
|
191
|
+
| 3011 | `NotFound` | File or directory not found |
|
|
192
|
+
| 3016 | `IsDirectory` | Expected file, got directory |
|
|
193
|
+
| 3018 | `ItExists` | File already exists (e.g. mkdir on existing path) |
|
|
194
|
+
| 3030 | `AuthFailed` | Authentication failed |
|
|
195
|
+
|
|
196
|
+
#### Client-side Error Codes
|
|
197
|
+
|
|
198
|
+
| Code | Constant | Description |
|
|
199
|
+
|------|----------|-------------|
|
|
200
|
+
| 309 | `Timeout` | Request timed out |
|
|
201
|
+
| 312 | `Disconnected` | Connection closed unexpectedly |
|
|
202
|
+
| 315 | `TooManyRedirs` | Exceeded maximum redirect count |
|
|
203
|
+
|
|
204
|
+
### Types
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
interface StatInfo {
|
|
208
|
+
id: string // opaque device id (string to avoid precision loss)
|
|
209
|
+
size: bigint // file size in bytes (bigint for >4GB files)
|
|
210
|
+
flags: number // XRootD server flags
|
|
211
|
+
mtime: number // modification time (epoch seconds)
|
|
212
|
+
ctime: number // change time (epoch seconds)
|
|
213
|
+
atime: number // access time (epoch seconds)
|
|
214
|
+
mode: number // POSIX mode (e.g. 0o100644)
|
|
215
|
+
owner: string // file owner
|
|
216
|
+
group: string // file group
|
|
217
|
+
get isDirectory(): boolean
|
|
218
|
+
get isLink(): boolean
|
|
219
|
+
get isOffline(): boolean
|
|
220
|
+
get isCached(): boolean
|
|
73
221
|
}
|
|
74
222
|
|
|
223
|
+
interface DirectoryEntry {
|
|
224
|
+
name: string
|
|
225
|
+
size: number
|
|
226
|
+
flags: number
|
|
227
|
+
mtime: number
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
interface DirectoryList {
|
|
231
|
+
name: string
|
|
232
|
+
entries: DirectoryEntry[]
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const enum OpenFlags {
|
|
236
|
+
Compress = 0x0001,
|
|
237
|
+
Delete = 0x0002,
|
|
238
|
+
Force = 0x0004,
|
|
239
|
+
New = 0x0008,
|
|
240
|
+
Read = 0x0010,
|
|
241
|
+
Write = 0x0020,
|
|
242
|
+
Async = 0x0040,
|
|
243
|
+
Refresh = 0x0080,
|
|
244
|
+
Mkpath = 0x0100,
|
|
245
|
+
Append = 0x0200,
|
|
246
|
+
Retstat = 0x0400,
|
|
247
|
+
Replica = 0x0800,
|
|
248
|
+
Posc = 0x1000,
|
|
249
|
+
Nowait = 0x2000,
|
|
250
|
+
Seqio = 0x4000,
|
|
251
|
+
Wrto = 0x8000,
|
|
252
|
+
}
|
|
75
253
|
```
|
|
76
254
|
|
|
77
|
-
|
|
255
|
+
#### OpenOptions
|
|
78
256
|
|
|
79
|
-
```
|
|
80
|
-
|
|
257
|
+
```ts
|
|
258
|
+
interface OpenOptions {
|
|
259
|
+
flags?: OpenFlags // file open flags (default: OpenFlags.Read)
|
|
260
|
+
mode?: number // POSIX mode (default: 0)
|
|
261
|
+
signal?: AbortSignal // cancellation signal
|
|
262
|
+
}
|
|
263
|
+
```
|
|
81
264
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
265
|
+
#### StatFlags
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
const StatFlags = {
|
|
269
|
+
XBitSet: 1,
|
|
270
|
+
IsDir: 2,
|
|
271
|
+
Other: 4,
|
|
272
|
+
Offline: 8,
|
|
273
|
+
Readable: 16,
|
|
274
|
+
Writable: 32,
|
|
275
|
+
POSCPending: 64,
|
|
276
|
+
BackUpExists: 128,
|
|
277
|
+
CacheResp: 512,
|
|
93
278
|
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Redirect Handling
|
|
94
282
|
|
|
283
|
+
The client automatically handles server redirects. When a server responds with `kXR_redirect`, the client:
|
|
284
|
+
|
|
285
|
+
1. Closes the current connection
|
|
286
|
+
2. Connects to the new target server
|
|
287
|
+
3. Re-executes the handshake and authentication
|
|
288
|
+
4. Retries the original request
|
|
289
|
+
|
|
290
|
+
```ts
|
|
291
|
+
const client = new XRootDClient('root://server.example.com', {
|
|
292
|
+
maxRedirects: 16, // default
|
|
293
|
+
})
|
|
95
294
|
```
|
|
96
295
|
|
|
97
|
-
|
|
296
|
+
## Architecture
|
|
98
297
|
|
|
99
|
-
|
|
100
|
-
import { CopyProcess } from 'xrootd';
|
|
298
|
+
The library uses a three-layer architecture, simplified from the original C++ XrdCl's five-layer design:
|
|
101
299
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
300
|
+
```
|
|
301
|
+
┌──────────────────────────────────────────────────┐
|
|
302
|
+
│ Layer 3: Multiplexer │
|
|
303
|
+
│ streamId → Promise mapping, timeout management │
|
|
304
|
+
│ + automatic redirect interception │
|
|
305
|
+
├──────────────────────────────────────────────────┤
|
|
306
|
+
│ Layer 2: Framer │
|
|
307
|
+
│ TCP packet reassembly, Header+Body framing │
|
|
308
|
+
├──────────────────────────────────────────────────┤
|
|
309
|
+
│ Layer 1: Transport │
|
|
310
|
+
│ net.Socket wrapper, binary data I/O │
|
|
311
|
+
└──────────────────────────────────────────────────┘
|
|
312
|
+
```
|
|
111
313
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
314
|
+
| C++ Original | TypeScript |
|
|
315
|
+
|--------------|-----------|
|
|
316
|
+
| 138 files, ~15,000 lines | ~25 files, ~3,000 lines |
|
|
317
|
+
| Callbacks + state machines | async/await |
|
|
318
|
+
| Error code checking | throw XRootDError |
|
|
319
|
+
| 5-layer abstraction | 3-layer streamlined architecture |
|
|
320
|
+
| Multiple auth plugins | Pluggable SecurityProtocol interface |
|
|
118
321
|
|
|
119
|
-
|
|
120
|
-
}
|
|
322
|
+
## Development
|
|
121
323
|
|
|
324
|
+
### Prerequisites
|
|
325
|
+
|
|
326
|
+
- Node.js >= 22
|
|
327
|
+
- pnpm
|
|
328
|
+
|
|
329
|
+
### Setup
|
|
330
|
+
|
|
331
|
+
```sh
|
|
332
|
+
pnpm install
|
|
122
333
|
```
|
|
123
334
|
|
|
124
|
-
|
|
335
|
+
### Mock Server
|
|
125
336
|
|
|
126
|
-
|
|
337
|
+
A Docker-based XRootD mock server is available for integration testing:
|
|
127
338
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
* **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.
|
|
339
|
+
```sh
|
|
340
|
+
# Start mock server
|
|
341
|
+
pnpm mock-server:up
|
|
132
342
|
|
|
133
|
-
|
|
343
|
+
# Verify it's running
|
|
344
|
+
pnpm mock-server:verify
|
|
134
345
|
|
|
135
|
-
|
|
346
|
+
# View logs
|
|
347
|
+
pnpm mock-server:logs
|
|
136
348
|
|
|
137
|
-
|
|
349
|
+
# Stop
|
|
350
|
+
pnpm mock-server:down
|
|
351
|
+
```
|
|
138
352
|
|
|
139
|
-
|
|
140
|
-
|
|
353
|
+
### Build
|
|
354
|
+
|
|
355
|
+
```sh
|
|
356
|
+
pnpm build
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Type Check
|
|
360
|
+
|
|
361
|
+
```sh
|
|
362
|
+
pnpm typecheck
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Test
|
|
366
|
+
|
|
367
|
+
```sh
|
|
368
|
+
pnpm test
|
|
369
|
+
```
|
|
141
370
|
|
|
142
|
-
|
|
371
|
+
## Documentation
|
|
143
372
|
|
|
144
|
-
|
|
373
|
+
- [Migration Design](docs/migration.md) — Full protocol analysis and architecture design
|
|
374
|
+
- [TypeScript API Design](docs/typescript-design.md) — Detailed API specifications
|
|
375
|
+
- [Work Plan](docs/work-plan.md) — Implementation roadmap
|
|
376
|
+
- [Phase 2 Plan](docs/phase2.md) — Complete API, fault tolerance, and authentication
|
|
377
|
+
- [Rust Future Optimizations](docs/rust-future-optimizations.md) — Performance optimizations deferred to Rust version
|
|
145
378
|
|
|
146
|
-
##
|
|
379
|
+
## License
|
|
147
380
|
|
|
148
|
-
|
|
381
|
+
This project is licensed under the [GNU Lesser General Public License v3.0 or later](LICENSE).
|
|
149
382
|
|
|
150
|
-
|
|
383
|
+
This is an independent TypeScript implementation of the XRootD protocol. The original XRootD project is developed by the Board of Trustees of the Leland Stanford, Jr. University and is licensed under LGPL-3.0-or-later.
|