disk 0.8.12 → 0.8.13
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 +74 -0
- package/dist/cli.js +390 -22
- package/dist/index.cjs +357 -20
- package/dist/index.d.cts +218 -4
- package/dist/index.d.ts +218 -4
- package/dist/index.js +353 -19
- package/package.json +7 -4
package/dist/index.js
CHANGED
|
@@ -1,17 +1,77 @@
|
|
|
1
1
|
// src/client.ts
|
|
2
|
-
import
|
|
2
|
+
import createClientDefault from "openapi-fetch";
|
|
3
|
+
|
|
4
|
+
// src/s3xml.ts
|
|
5
|
+
import { XMLParser } from "fast-xml-parser";
|
|
6
|
+
var parser = new XMLParser({
|
|
7
|
+
isArray: (name) => name === "Contents" || name === "CommonPrefixes",
|
|
8
|
+
parseTagValue: false,
|
|
9
|
+
trimValues: true,
|
|
10
|
+
ignoreAttributes: true
|
|
11
|
+
});
|
|
12
|
+
function parseXml(xml) {
|
|
13
|
+
if (!xml.trim()) return {};
|
|
14
|
+
return parser.parse(xml);
|
|
15
|
+
}
|
|
3
16
|
|
|
4
17
|
// src/errors.ts
|
|
5
|
-
var
|
|
18
|
+
var ArchilError = class extends Error {
|
|
19
|
+
/** HTTP status code associated with the failure. */
|
|
6
20
|
status;
|
|
21
|
+
/** Machine-readable error code (e.g. an S3 code like "NoSuchKey"), if known. */
|
|
7
22
|
code;
|
|
8
23
|
constructor(message, status, code) {
|
|
9
24
|
super(message);
|
|
10
|
-
this.name = "
|
|
25
|
+
this.name = "ArchilError";
|
|
11
26
|
this.status = status;
|
|
12
27
|
this.code = code;
|
|
13
28
|
}
|
|
14
29
|
};
|
|
30
|
+
var ArchilApiError = class extends ArchilError {
|
|
31
|
+
constructor(message, status, code) {
|
|
32
|
+
super(message, status, code);
|
|
33
|
+
this.name = "ArchilApiError";
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var ArchilS3Error = class extends ArchilError {
|
|
37
|
+
/** S3 request id, if the gateway returned one. */
|
|
38
|
+
requestId;
|
|
39
|
+
/** Raw response body (the XML document), for debugging. */
|
|
40
|
+
raw;
|
|
41
|
+
constructor(opts) {
|
|
42
|
+
const detail = opts.message ?? opts.statusText ?? "";
|
|
43
|
+
const codePart = opts.code ? ` ${opts.code}` : "";
|
|
44
|
+
super(
|
|
45
|
+
`S3 ${opts.operation} failed: ${opts.statusCode}${codePart}${detail ? ` \u2014 ${detail}` : ""}`,
|
|
46
|
+
opts.statusCode,
|
|
47
|
+
opts.code
|
|
48
|
+
);
|
|
49
|
+
this.name = "ArchilS3Error";
|
|
50
|
+
this.requestId = opts.requestId;
|
|
51
|
+
this.raw = opts.raw;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
function tagString(obj, tag) {
|
|
55
|
+
const value = obj[tag];
|
|
56
|
+
return value === void 0 || value === null ? void 0 : String(value);
|
|
57
|
+
}
|
|
58
|
+
function parseS3Error(operation, statusCode, statusText, body) {
|
|
59
|
+
let err = {};
|
|
60
|
+
try {
|
|
61
|
+
err = parseXml(body).Error ?? {};
|
|
62
|
+
} catch {
|
|
63
|
+
err = {};
|
|
64
|
+
}
|
|
65
|
+
return new ArchilS3Error({
|
|
66
|
+
operation,
|
|
67
|
+
statusCode,
|
|
68
|
+
statusText,
|
|
69
|
+
code: tagString(err, "Code"),
|
|
70
|
+
message: tagString(err, "Message"),
|
|
71
|
+
requestId: tagString(err, "RequestId"),
|
|
72
|
+
raw: body
|
|
73
|
+
});
|
|
74
|
+
}
|
|
15
75
|
|
|
16
76
|
// src/regions.ts
|
|
17
77
|
var REGION_URLS = {
|
|
@@ -29,14 +89,32 @@ function resolveBaseUrl(region) {
|
|
|
29
89
|
}
|
|
30
90
|
return url;
|
|
31
91
|
}
|
|
92
|
+
function deriveS3BaseUrl(controlBaseUrl) {
|
|
93
|
+
try {
|
|
94
|
+
const u = new URL(controlBaseUrl);
|
|
95
|
+
u.hostname = u.hostname.replace(/^control\./, "s3.");
|
|
96
|
+
return u.toString().replace(/\/$/, "");
|
|
97
|
+
} catch {
|
|
98
|
+
return void 0;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/version.ts
|
|
103
|
+
var VERSION = true ? "0.8.13" : "0.0.0-dev";
|
|
104
|
+
var USER_AGENT = `archil-js/${VERSION}`;
|
|
32
105
|
|
|
33
106
|
// src/client.ts
|
|
107
|
+
var createClient = typeof createClientDefault === "function" ? createClientDefault : createClientDefault.default;
|
|
34
108
|
function createApiClient(opts) {
|
|
35
109
|
const baseUrl = opts.baseUrl ?? resolveBaseUrl(opts.region);
|
|
36
110
|
return createClient({
|
|
37
111
|
baseUrl,
|
|
38
112
|
headers: {
|
|
39
|
-
Authorization: `key-${opts.apiKey.replace(/^key-/, "")}
|
|
113
|
+
Authorization: `key-${opts.apiKey.replace(/^key-/, "")}`,
|
|
114
|
+
// Identifies the JS SDK (and its version) to the control plane. Honored
|
|
115
|
+
// by Node's fetch; browsers treat User-Agent as a forbidden header and
|
|
116
|
+
// drop it, which is fine — the SDK's primary use is server-side.
|
|
117
|
+
"User-Agent": USER_AGENT
|
|
40
118
|
}
|
|
41
119
|
});
|
|
42
120
|
}
|
|
@@ -75,8 +153,6 @@ async function unwrapEmpty(promise) {
|
|
|
75
153
|
}
|
|
76
154
|
|
|
77
155
|
// src/disk.ts
|
|
78
|
-
import { createRequire } from "module";
|
|
79
|
-
import { pathToFileURL } from "url";
|
|
80
156
|
var Disk = class {
|
|
81
157
|
id;
|
|
82
158
|
name;
|
|
@@ -98,8 +174,10 @@ var Disk = class {
|
|
|
98
174
|
_client;
|
|
99
175
|
/** @internal */
|
|
100
176
|
_archilRegion;
|
|
177
|
+
/** Base URL for the S3-compatible API (port 9000 ingress). Empty if unset. */
|
|
178
|
+
_s3BaseUrl;
|
|
101
179
|
/** @internal */
|
|
102
|
-
constructor(data, client, archilRegion) {
|
|
180
|
+
constructor(data, client, archilRegion, s3BaseUrl) {
|
|
103
181
|
this.id = data.id;
|
|
104
182
|
this.name = data.name;
|
|
105
183
|
this.organization = data.organization;
|
|
@@ -118,6 +196,7 @@ var Disk = class {
|
|
|
118
196
|
this.allowedIps = data.allowedIps;
|
|
119
197
|
this._client = client;
|
|
120
198
|
this._archilRegion = archilRegion;
|
|
199
|
+
this._s3BaseUrl = s3BaseUrl ?? "";
|
|
121
200
|
}
|
|
122
201
|
toJSON() {
|
|
123
202
|
return {
|
|
@@ -242,6 +321,219 @@ var Disk = class {
|
|
|
242
321
|
})
|
|
243
322
|
);
|
|
244
323
|
}
|
|
324
|
+
/**
|
|
325
|
+
* Create a signed, time-limited URL that lets anyone download a single file
|
|
326
|
+
* from this disk without authentication. The returned URL embeds a
|
|
327
|
+
* cryptographically signed token carrying the disk, the file's key, and an
|
|
328
|
+
* expiry — share it directly; no API key is needed to redeem it.
|
|
329
|
+
*
|
|
330
|
+
* @param key Path to the file on the disk (e.g. "reports/2026-01/data.pdf").
|
|
331
|
+
* @param opts `expiresIn` sets the URL lifetime in seconds (any positive
|
|
332
|
+
* integer, max 604800 = 7 days). Defaults to 24h.
|
|
333
|
+
*/
|
|
334
|
+
async share(key, opts = {}) {
|
|
335
|
+
const body = { key };
|
|
336
|
+
if (opts.expiresIn !== void 0) body.expiresIn = opts.expiresIn;
|
|
337
|
+
const call = this._client.POST;
|
|
338
|
+
return unwrap(call(`/api/disks/${this.id}/share`, { body }));
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Read an object from the disk via the S3-compatible GetObject API and return
|
|
342
|
+
* its full contents as bytes. Throws `ArchilS3Error` if the object does not
|
|
343
|
+
* exist (status 404, code "NoSuchKey") or the request is rejected; use
|
|
344
|
+
* `headObject`/`objectExists` to check existence without throwing.
|
|
345
|
+
*
|
|
346
|
+
* @param key Path on the disk (e.g. "reports/2026-01/data.json")
|
|
347
|
+
*/
|
|
348
|
+
async getObject(key) {
|
|
349
|
+
const resp = await this._s3Request("GET", key);
|
|
350
|
+
if (!resp.ok) {
|
|
351
|
+
throw parseS3Error("GetObject", resp.status, resp.statusText, decodeText(resp.body));
|
|
352
|
+
}
|
|
353
|
+
return resp.body;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Fetch an object's metadata (size, etag, content type, last-modified) without
|
|
357
|
+
* downloading its contents, via the S3-compatible HeadObject API. Returns
|
|
358
|
+
* `null` if the object does not exist.
|
|
359
|
+
*
|
|
360
|
+
* @param key Path on the disk (e.g. "reports/2026-01/data.json")
|
|
361
|
+
*/
|
|
362
|
+
async headObject(key) {
|
|
363
|
+
const resp = await this._s3Request("HEAD", key);
|
|
364
|
+
if (resp.status === 404) return null;
|
|
365
|
+
if (!resp.ok) {
|
|
366
|
+
throw parseS3Error("HeadObject", resp.status, resp.statusText, decodeText(resp.body));
|
|
367
|
+
}
|
|
368
|
+
const lastModified = resp.headers.get("last-modified");
|
|
369
|
+
return {
|
|
370
|
+
size: Number(resp.headers.get("content-length") ?? 0),
|
|
371
|
+
etag: resp.headers.get("etag") ?? void 0,
|
|
372
|
+
contentType: resp.headers.get("content-type") ?? void 0,
|
|
373
|
+
lastModified: lastModified ? new Date(lastModified) : void 0
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
/** Whether an object exists on the disk (a HeadObject that maps 404 → false). */
|
|
377
|
+
async objectExists(key) {
|
|
378
|
+
return await this.headObject(key) !== null;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Write an object to the disk using the S3-compatible PutObject API. Faster
|
|
382
|
+
* than exec for large files — no container overhead, no command-length limits.
|
|
383
|
+
* Returns the entity tag the server assigned.
|
|
384
|
+
*
|
|
385
|
+
* @param key Path on the disk (e.g. "reports/2026-01/data.json")
|
|
386
|
+
* @param body Contents as a string, Uint8Array/Buffer, or ArrayBuffer
|
|
387
|
+
* @param contentType MIME type (default: "application/octet-stream")
|
|
388
|
+
*/
|
|
389
|
+
async putObject(key, body, contentType = "application/octet-stream") {
|
|
390
|
+
const resp = await this._s3Request("PUT", key, { body, contentType });
|
|
391
|
+
if (!resp.ok) {
|
|
392
|
+
throw parseS3Error("PutObject", resp.status, resp.statusText, decodeText(resp.body));
|
|
393
|
+
}
|
|
394
|
+
return { etag: resp.headers.get("etag") ?? void 0 };
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Delete an object from the disk via the S3-compatible DeleteObject API.
|
|
398
|
+
* Idempotent: deleting a key that doesn't exist resolves successfully, per
|
|
399
|
+
* S3 semantics.
|
|
400
|
+
*
|
|
401
|
+
* @param key Path on the disk (e.g. "project/dist/server.cjs")
|
|
402
|
+
*/
|
|
403
|
+
async deleteObject(key) {
|
|
404
|
+
const resp = await this._s3Request("DELETE", key);
|
|
405
|
+
if (!resp.ok && resp.status !== 404) {
|
|
406
|
+
throw parseS3Error("DeleteObject", resp.status, resp.statusText, decodeText(resp.body));
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* List objects on the disk via the S3-compatible ListObjectsV2 API. By
|
|
411
|
+
* default this follows continuation tokens until the listing is exhausted and
|
|
412
|
+
* returns every matching key. Use `limit` to cap the total, `singlePage` for a
|
|
413
|
+
* single request, or {@link listObjectsPages} to stream pages without loading
|
|
414
|
+
* everything into memory.
|
|
415
|
+
*
|
|
416
|
+
* @param prefix Only return keys beginning with this prefix (omit for all).
|
|
417
|
+
* @param opts Listing and pagination options.
|
|
418
|
+
*/
|
|
419
|
+
async listObjects(prefix, opts = {}) {
|
|
420
|
+
if (opts.singlePage) {
|
|
421
|
+
return this._listObjectsPage(prefix, opts);
|
|
422
|
+
}
|
|
423
|
+
const objects = [];
|
|
424
|
+
const commonPrefixes = [];
|
|
425
|
+
const seenPrefixes = /* @__PURE__ */ new Set();
|
|
426
|
+
let echoedPrefix;
|
|
427
|
+
let truncated = false;
|
|
428
|
+
outer: for await (const page of this.listObjectsPages(prefix, opts)) {
|
|
429
|
+
echoedPrefix = page.prefix;
|
|
430
|
+
for (const cp of page.commonPrefixes) {
|
|
431
|
+
if (!seenPrefixes.has(cp)) {
|
|
432
|
+
seenPrefixes.add(cp);
|
|
433
|
+
commonPrefixes.push(cp);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
for (const obj of page.objects) {
|
|
437
|
+
if (opts.limit !== void 0 && objects.length >= opts.limit) {
|
|
438
|
+
truncated = true;
|
|
439
|
+
break outer;
|
|
440
|
+
}
|
|
441
|
+
objects.push(obj);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return { objects, commonPrefixes, isTruncated: truncated, keyCount: objects.length, prefix: echoedPrefix };
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Yield ListObjectsV2 pages lazily, following continuation tokens. A
|
|
448
|
+
* memory-friendly way to process a large listing without materializing it:
|
|
449
|
+
*
|
|
450
|
+
* ```ts
|
|
451
|
+
* for await (const page of disk.listObjectsPages("logs/")) {
|
|
452
|
+
* for (const obj of page.objects) process(obj);
|
|
453
|
+
* }
|
|
454
|
+
* ```
|
|
455
|
+
*
|
|
456
|
+
* @param prefix Only return keys beginning with this prefix (omit for all).
|
|
457
|
+
* @param opts Listing options (`limit` / `singlePage` are ignored here —
|
|
458
|
+
* control your own loop).
|
|
459
|
+
*/
|
|
460
|
+
async *listObjectsPages(prefix, opts = {}) {
|
|
461
|
+
const seenTokens = /* @__PURE__ */ new Set();
|
|
462
|
+
let continuationToken = opts.continuationToken;
|
|
463
|
+
for (; ; ) {
|
|
464
|
+
const page = await this._listObjectsPage(prefix, { ...opts, continuationToken });
|
|
465
|
+
yield page;
|
|
466
|
+
const next = page.isTruncated ? page.nextContinuationToken : void 0;
|
|
467
|
+
if (!next || seenTokens.has(next)) break;
|
|
468
|
+
seenTokens.add(next);
|
|
469
|
+
continuationToken = next;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
/** Fetch a single ListObjectsV2 page. @internal */
|
|
473
|
+
async _listObjectsPage(prefix, opts) {
|
|
474
|
+
const query = { "list-type": 2 };
|
|
475
|
+
if (prefix !== void 0) query.prefix = prefix;
|
|
476
|
+
if (!opts.recursive) query.delimiter = "/";
|
|
477
|
+
if (opts.continuationToken !== void 0) query["continuation-token"] = opts.continuationToken;
|
|
478
|
+
if (opts.startAfter !== void 0) query["start-after"] = opts.startAfter;
|
|
479
|
+
const resp = await this._s3Request("GET", "", { query });
|
|
480
|
+
if (!resp.ok) {
|
|
481
|
+
throw parseS3Error("ListObjectsV2", resp.status, resp.statusText, decodeText(resp.body));
|
|
482
|
+
}
|
|
483
|
+
return parseListObjectsResult(decodeText(resp.body));
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Send a single request to the disk's S3-compatible endpoint. This reuses the
|
|
487
|
+
* control-plane client purely for its credential and transport — the same
|
|
488
|
+
* `Authorization` header is sent and verified by the same code server-side —
|
|
489
|
+
* pointed at the S3 host. Returns the response status and fully-buffered body
|
|
490
|
+
* so callers can inspect both regardless of the verb used.
|
|
491
|
+
*
|
|
492
|
+
* The S3 routes (`/{diskId}/{key}` for objects, `/{diskId}` for the bucket)
|
|
493
|
+
* are not part of the typed control-plane API, so the path and per-request
|
|
494
|
+
* options are passed untyped. An empty `key` targets the bucket itself (used
|
|
495
|
+
* by listObjects).
|
|
496
|
+
*
|
|
497
|
+
* @internal
|
|
498
|
+
*/
|
|
499
|
+
async _s3Request(method, key, opts = {}) {
|
|
500
|
+
if (!this._s3BaseUrl) {
|
|
501
|
+
throw new Error(
|
|
502
|
+
"S3 base URL not configured. Pass s3BaseUrl to new Archil({...}) or set ARCHIL_S3_BASE_URL."
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
const call = this._client[method];
|
|
506
|
+
const trimmedKey = key.replace(/^\//, "");
|
|
507
|
+
const encodedKey = trimmedKey.split("/").map(encodeURIComponent).join("/");
|
|
508
|
+
const path = encodedKey ? `/${this.id}/${encodedKey}` : `/${this.id}`;
|
|
509
|
+
const { error, response } = await call(path, {
|
|
510
|
+
baseUrl: this._s3BaseUrl,
|
|
511
|
+
parseAs: "stream",
|
|
512
|
+
...opts.query ? { params: { query: opts.query } } : {},
|
|
513
|
+
// fetch accepts string / Uint8Array / ArrayBuffer bodies directly; pass it
|
|
514
|
+
// through unchanged (no Node Buffer in the data path).
|
|
515
|
+
...opts.body !== void 0 ? { body: opts.body, bodySerializer: (b) => b } : {},
|
|
516
|
+
...opts.contentType ? { headers: { "Content-Type": opts.contentType } } : {}
|
|
517
|
+
});
|
|
518
|
+
if (!response.ok) {
|
|
519
|
+
const message = typeof error === "string" ? error : error ? JSON.stringify(error) : "";
|
|
520
|
+
return {
|
|
521
|
+
ok: false,
|
|
522
|
+
status: response.status,
|
|
523
|
+
statusText: response.statusText,
|
|
524
|
+
headers: response.headers,
|
|
525
|
+
body: new TextEncoder().encode(message)
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
const bytes = method === "GET" ? new Uint8Array(await response.arrayBuffer()) : new Uint8Array(0);
|
|
529
|
+
return {
|
|
530
|
+
ok: true,
|
|
531
|
+
status: response.status,
|
|
532
|
+
statusText: response.statusText,
|
|
533
|
+
headers: response.headers,
|
|
534
|
+
body: bytes
|
|
535
|
+
};
|
|
536
|
+
}
|
|
245
537
|
/**
|
|
246
538
|
* Connect to this disk's data plane via the native ArchilClient.
|
|
247
539
|
*
|
|
@@ -250,13 +542,15 @@ var Disk = class {
|
|
|
250
542
|
async mount(opts) {
|
|
251
543
|
let ArchilClient;
|
|
252
544
|
try {
|
|
253
|
-
const
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
ArchilClient
|
|
545
|
+
const nativeSpecifier = "@archildata/native";
|
|
546
|
+
const native = await import(nativeSpecifier);
|
|
547
|
+
ArchilClient = native.ArchilClient ?? native.default?.ArchilClient;
|
|
548
|
+
if (!ArchilClient) {
|
|
549
|
+
throw new Error("@archildata/native did not export ArchilClient");
|
|
550
|
+
}
|
|
257
551
|
} catch {
|
|
258
552
|
throw new Error(
|
|
259
|
-
"Native client not available. Install @archildata/native (platform-specific binary) or use ArchilClient.connect() directly."
|
|
553
|
+
"Native client not available. Install @archildata/native (platform-specific binary) or use ArchilClient.connect() directly. mount() is not supported in browsers."
|
|
260
554
|
);
|
|
261
555
|
}
|
|
262
556
|
return ArchilClient.connect({
|
|
@@ -269,6 +563,34 @@ var Disk = class {
|
|
|
269
563
|
});
|
|
270
564
|
}
|
|
271
565
|
};
|
|
566
|
+
function optionalString(value) {
|
|
567
|
+
return value === void 0 || value === null ? void 0 : String(value);
|
|
568
|
+
}
|
|
569
|
+
function optionalDate(value) {
|
|
570
|
+
return value === void 0 || value === null ? void 0 : new Date(String(value));
|
|
571
|
+
}
|
|
572
|
+
function decodeText(bytes) {
|
|
573
|
+
return new TextDecoder().decode(bytes);
|
|
574
|
+
}
|
|
575
|
+
function parseListObjectsResult(xml) {
|
|
576
|
+
const root = parseXml(xml).ListBucketResult ?? {};
|
|
577
|
+
const contents = root.Contents ?? [];
|
|
578
|
+
const objects = contents.map((c) => ({
|
|
579
|
+
key: String(c.Key ?? ""),
|
|
580
|
+
size: Number(c.Size ?? 0),
|
|
581
|
+
etag: optionalString(c.ETag),
|
|
582
|
+
lastModified: optionalDate(c.LastModified)
|
|
583
|
+
}));
|
|
584
|
+
const commonPrefixes = (root.CommonPrefixes ?? []).map((cp) => optionalString(cp.Prefix)).filter((p) => p !== void 0);
|
|
585
|
+
return {
|
|
586
|
+
objects,
|
|
587
|
+
commonPrefixes,
|
|
588
|
+
isTruncated: root.IsTruncated === "true" || root.IsTruncated === true,
|
|
589
|
+
nextContinuationToken: optionalString(root.NextContinuationToken),
|
|
590
|
+
keyCount: root.KeyCount !== void 0 ? Number(root.KeyCount) : objects.length,
|
|
591
|
+
prefix: optionalString(root.Prefix)
|
|
592
|
+
};
|
|
593
|
+
}
|
|
272
594
|
|
|
273
595
|
// src/disks.ts
|
|
274
596
|
var Disks = class {
|
|
@@ -277,9 +599,12 @@ var Disks = class {
|
|
|
277
599
|
/** @internal */
|
|
278
600
|
_region;
|
|
279
601
|
/** @internal */
|
|
280
|
-
|
|
602
|
+
_s3BaseUrl;
|
|
603
|
+
/** @internal */
|
|
604
|
+
constructor(client, region, s3BaseUrl) {
|
|
281
605
|
this._client = client;
|
|
282
606
|
this._region = region;
|
|
607
|
+
this._s3BaseUrl = s3BaseUrl;
|
|
283
608
|
}
|
|
284
609
|
async list(opts) {
|
|
285
610
|
const data = await unwrap(
|
|
@@ -288,7 +613,7 @@ var Disks = class {
|
|
|
288
613
|
})
|
|
289
614
|
);
|
|
290
615
|
return data.map(
|
|
291
|
-
(d) => new Disk(d, this._client, this._region)
|
|
616
|
+
(d) => new Disk(d, this._client, this._region, this._s3BaseUrl)
|
|
292
617
|
);
|
|
293
618
|
}
|
|
294
619
|
async get(id) {
|
|
@@ -297,7 +622,7 @@ var Disks = class {
|
|
|
297
622
|
params: { path: { id } }
|
|
298
623
|
})
|
|
299
624
|
);
|
|
300
|
-
return new Disk(data, this._client, this._region);
|
|
625
|
+
return new Disk(data, this._client, this._region, this._s3BaseUrl);
|
|
301
626
|
}
|
|
302
627
|
/**
|
|
303
628
|
* Create a new disk with an auto-generated mount token.
|
|
@@ -357,6 +682,9 @@ var Tokens = class {
|
|
|
357
682
|
};
|
|
358
683
|
|
|
359
684
|
// src/archil.ts
|
|
685
|
+
function envVar(name) {
|
|
686
|
+
return typeof process !== "undefined" && process.env ? process.env[name] : void 0;
|
|
687
|
+
}
|
|
360
688
|
function isExecMountSpec(m) {
|
|
361
689
|
return typeof m === "object" && m !== null && "disk" in m;
|
|
362
690
|
}
|
|
@@ -369,21 +697,23 @@ var Archil = class {
|
|
|
369
697
|
/** @internal */
|
|
370
698
|
_client;
|
|
371
699
|
constructor(opts = {}) {
|
|
372
|
-
const apiKey = opts.apiKey ??
|
|
373
|
-
const region = opts.region ??
|
|
700
|
+
const apiKey = opts.apiKey ?? envVar("ARCHIL_API_KEY");
|
|
701
|
+
const region = opts.region ?? envVar("ARCHIL_REGION");
|
|
374
702
|
if (!apiKey) {
|
|
375
703
|
throw new Error("Missing API key: pass apiKey in options or set ARCHIL_API_KEY environment variable");
|
|
376
704
|
}
|
|
377
705
|
if (!region) {
|
|
378
706
|
throw new Error("Missing region: pass region in options or set ARCHIL_REGION environment variable");
|
|
379
707
|
}
|
|
708
|
+
const controlBaseUrl = opts.baseUrl ?? resolveBaseUrl(region);
|
|
380
709
|
const client = createApiClient({
|
|
381
710
|
apiKey,
|
|
382
711
|
region,
|
|
383
|
-
baseUrl:
|
|
712
|
+
baseUrl: controlBaseUrl
|
|
384
713
|
});
|
|
714
|
+
const s3BaseUrl = opts.s3BaseUrl ?? envVar("ARCHIL_S3_BASE_URL") ?? deriveS3BaseUrl(controlBaseUrl);
|
|
385
715
|
this._client = client;
|
|
386
|
-
this.disks = new Disks(client, region);
|
|
716
|
+
this.disks = new Disks(client, region, s3BaseUrl);
|
|
387
717
|
this.tokens = new Tokens(client);
|
|
388
718
|
}
|
|
389
719
|
/**
|
|
@@ -450,9 +780,13 @@ function exec(opts) {
|
|
|
450
780
|
export {
|
|
451
781
|
Archil,
|
|
452
782
|
ArchilApiError,
|
|
783
|
+
ArchilError,
|
|
784
|
+
ArchilS3Error,
|
|
453
785
|
Disk,
|
|
454
786
|
Disks,
|
|
455
787
|
Tokens,
|
|
788
|
+
USER_AGENT,
|
|
789
|
+
VERSION,
|
|
456
790
|
configure,
|
|
457
791
|
createApiKey,
|
|
458
792
|
createDisk,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "disk",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.13",
|
|
4
4
|
"description": "Pure-JS client and CLI for Archil disks",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -23,16 +23,19 @@
|
|
|
23
23
|
],
|
|
24
24
|
"scripts": {
|
|
25
25
|
"generate": "openapi-typescript ../../api/controlplane/openapi.yaml -o src/generated/openapi.d.ts",
|
|
26
|
-
"build": "npm run generate && tsup
|
|
27
|
-
"dev": "tsup
|
|
28
|
-
"typecheck": "npm run generate && tsc --noEmit"
|
|
26
|
+
"build": "npm run generate && tsup",
|
|
27
|
+
"dev": "tsup --watch",
|
|
28
|
+
"typecheck": "npm run generate && tsc --noEmit",
|
|
29
|
+
"test": "node --test"
|
|
29
30
|
},
|
|
30
31
|
"dependencies": {
|
|
31
32
|
"commander": "^14.0.3",
|
|
33
|
+
"fast-xml-parser": "^4.5.1",
|
|
32
34
|
"openapi-fetch": "^0.13.5"
|
|
33
35
|
},
|
|
34
36
|
"devDependencies": {
|
|
35
37
|
"@types/node": "^20.19.33",
|
|
38
|
+
"esbuild": "^0.27.7",
|
|
36
39
|
"openapi-typescript": "^7.6.1",
|
|
37
40
|
"tsup": "^8.4.0",
|
|
38
41
|
"typescript": "^5.9.3"
|