@centia-io/sdk 0.2.14 → 0.2.15
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 +34 -0
- package/dist/centia-io-sdk.cjs +207 -16
- package/dist/centia-io-sdk.d.cts +184 -1
- package/dist/centia-io-sdk.d.cts.map +1 -1
- package/dist/centia-io-sdk.d.ts +184 -1
- package/dist/centia-io-sdk.d.ts.map +1 -1
- package/dist/centia-io-sdk.js +207 -17
- package/dist/centia-io-sdk.js.map +1 -1
- package/dist/centia-io-sdk.umd.js +207 -16
- package/package.json +1 -1
|
@@ -712,6 +712,52 @@
|
|
|
712
712
|
getHeader: (name) => response.headers.get(name)
|
|
713
713
|
};
|
|
714
714
|
}
|
|
715
|
+
/**
|
|
716
|
+
* Execute a request and return the raw Response without consuming the body.
|
|
717
|
+
* For binary endpoints (file downloads): follows redirects (a presigned
|
|
718
|
+
* storage URL — the fetch spec strips Authorization on cross-origin
|
|
719
|
+
* redirects), sends no Content-Type, and supports extra request headers
|
|
720
|
+
* such as Range. Throws CentiaApiError on a status not in expectedStatus.
|
|
721
|
+
*/
|
|
722
|
+
async requestRaw(opts) {
|
|
723
|
+
var _this3 = this;
|
|
724
|
+
var _opts$accept, _opts$expectedStatus2;
|
|
725
|
+
const url = _this3.buildUrl(opts.path, opts.query);
|
|
726
|
+
const headers = { "Accept": (_opts$accept = opts.accept) !== null && _opts$accept !== void 0 ? _opts$accept : "*/*" };
|
|
727
|
+
if (_this3.auth.getAccessToken) {
|
|
728
|
+
const token = await _this3.auth.getAccessToken();
|
|
729
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
730
|
+
}
|
|
731
|
+
if (_this3.auth.getHeaders) Object.assign(headers, await _this3.auth.getHeaders());
|
|
732
|
+
if (_this3.userAgent && typeof navigator === "undefined") headers["User-Agent"] = _this3.userAgent;
|
|
733
|
+
Object.assign(headers, opts.headers);
|
|
734
|
+
const response = await _this3.fetchFn(url, {
|
|
735
|
+
method: opts.method,
|
|
736
|
+
headers,
|
|
737
|
+
redirect: "follow"
|
|
738
|
+
});
|
|
739
|
+
if (!((_opts$expectedStatus2 = opts.expectedStatus) !== null && _opts$expectedStatus2 !== void 0 ? _opts$expectedStatus2 : [200]).includes(response.status)) {
|
|
740
|
+
var _ref, _parsed$message, _response$headers$get;
|
|
741
|
+
let bodyText = "";
|
|
742
|
+
try {
|
|
743
|
+
bodyText = await response.text();
|
|
744
|
+
} catch (_unused) {}
|
|
745
|
+
let parsed = null;
|
|
746
|
+
if (bodyText) try {
|
|
747
|
+
parsed = JSON.parse(bodyText);
|
|
748
|
+
} catch (_unused2) {}
|
|
749
|
+
throw new CentiaApiError({
|
|
750
|
+
message: ((_ref = (_parsed$message = parsed === null || parsed === void 0 ? void 0 : parsed.message) !== null && _parsed$message !== void 0 ? _parsed$message : parsed === null || parsed === void 0 ? void 0 : parsed.error) !== null && _ref !== void 0 ? _ref : bodyText) || `Unexpected status ${response.status}`,
|
|
751
|
+
status: response.status,
|
|
752
|
+
code: parsed === null || parsed === void 0 ? void 0 : parsed.code,
|
|
753
|
+
details: parsed,
|
|
754
|
+
requestId: (_response$headers$get = response.headers.get("x-request-id")) !== null && _response$headers$get !== void 0 ? _response$headers$get : void 0,
|
|
755
|
+
method: opts.method,
|
|
756
|
+
url
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
return response;
|
|
760
|
+
}
|
|
715
761
|
buildUrl(path, query) {
|
|
716
762
|
const cleanPath = path.replace(/^\/+/, "");
|
|
717
763
|
let url = `${this.baseUrl}/${cleanPath}`;
|
|
@@ -722,19 +768,19 @@
|
|
|
722
768
|
return url;
|
|
723
769
|
}
|
|
724
770
|
async buildHeaders(opts) {
|
|
725
|
-
var
|
|
726
|
-
var _opts$
|
|
727
|
-
const headers = { "Accept": (_opts$
|
|
728
|
-
if (
|
|
729
|
-
const token = await
|
|
771
|
+
var _this4 = this;
|
|
772
|
+
var _opts$accept2;
|
|
773
|
+
const headers = { "Accept": (_opts$accept2 = opts.accept) !== null && _opts$accept2 !== void 0 ? _opts$accept2 : "application/json" };
|
|
774
|
+
if (_this4.auth.getAccessToken) {
|
|
775
|
+
const token = await _this4.auth.getAccessToken();
|
|
730
776
|
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
731
777
|
}
|
|
732
|
-
if (
|
|
733
|
-
const authHeaders = await
|
|
778
|
+
if (_this4.auth.getHeaders) {
|
|
779
|
+
const authHeaders = await _this4.auth.getHeaders();
|
|
734
780
|
Object.assign(headers, authHeaders);
|
|
735
781
|
}
|
|
736
|
-
if (
|
|
737
|
-
const ct =
|
|
782
|
+
if (_this4.userAgent && typeof navigator === "undefined") headers["User-Agent"] = _this4.userAgent;
|
|
783
|
+
const ct = _this4.resolveContentType(opts.contentType);
|
|
738
784
|
if (ct) headers["Content-Type"] = ct;
|
|
739
785
|
return headers;
|
|
740
786
|
}
|
|
@@ -743,24 +789,24 @@
|
|
|
743
789
|
return contentType !== null && contentType !== void 0 ? contentType : "application/json";
|
|
744
790
|
}
|
|
745
791
|
async handleResponse(response, opts, url) {
|
|
746
|
-
var _opts$
|
|
747
|
-
const expectedStatus = (_opts$
|
|
792
|
+
var _opts$expectedStatus3, _parsed;
|
|
793
|
+
const expectedStatus = (_opts$expectedStatus3 = opts.expectedStatus) !== null && _opts$expectedStatus3 !== void 0 ? _opts$expectedStatus3 : 200;
|
|
748
794
|
let bodyText = "";
|
|
749
795
|
try {
|
|
750
796
|
bodyText = await response.text();
|
|
751
|
-
} catch (
|
|
797
|
+
} catch (_unused3) {}
|
|
752
798
|
let parsed = null;
|
|
753
799
|
if (bodyText) try {
|
|
754
800
|
parsed = JSON.parse(bodyText);
|
|
755
|
-
} catch (
|
|
801
|
+
} catch (_unused4) {}
|
|
756
802
|
if (response.status !== expectedStatus) {
|
|
757
|
-
var
|
|
803
|
+
var _ref2, _parsed$message2, _response$headers$get2;
|
|
758
804
|
throw new CentiaApiError({
|
|
759
|
-
message: ((
|
|
805
|
+
message: ((_ref2 = (_parsed$message2 = parsed === null || parsed === void 0 ? void 0 : parsed.message) !== null && _parsed$message2 !== void 0 ? _parsed$message2 : parsed === null || parsed === void 0 ? void 0 : parsed.error) !== null && _ref2 !== void 0 ? _ref2 : bodyText) || `Unexpected status ${response.status}`,
|
|
760
806
|
status: response.status,
|
|
761
807
|
code: parsed === null || parsed === void 0 ? void 0 : parsed.code,
|
|
762
808
|
details: parsed,
|
|
763
|
-
requestId: (_response$headers$
|
|
809
|
+
requestId: (_response$headers$get2 = response.headers.get("x-request-id")) !== null && _response$headers$get2 !== void 0 ? _response$headers$get2 : void 0,
|
|
764
810
|
method: opts.method,
|
|
765
811
|
url
|
|
766
812
|
});
|
|
@@ -3391,6 +3437,150 @@
|
|
|
3391
3437
|
}
|
|
3392
3438
|
};
|
|
3393
3439
|
|
|
3440
|
+
//#endregion
|
|
3441
|
+
//#region src/snapshots/Snapshots.ts
|
|
3442
|
+
/**
|
|
3443
|
+
* Client for the snapshot API: GeoParquet exports of a relation.
|
|
3444
|
+
*
|
|
3445
|
+
* The job API (`/api/v4/snapshots`) queues and inspects exports and is
|
|
3446
|
+
* super-user only; exports run asynchronously — poll with `getSnapshot` or
|
|
3447
|
+
* use `waitForSnapshot`. The read API
|
|
3448
|
+
* (`/api/v4/schemas/{schema}/relations/{relation}/snapshots`) serves the
|
|
3449
|
+
* published snapshots to any user with read access to the relation, with
|
|
3450
|
+
* HEAD + byte-range support so Parquet readers can fetch footers and row
|
|
3451
|
+
* groups selectively.
|
|
3452
|
+
*
|
|
3453
|
+
* ```ts
|
|
3454
|
+
* const snapshots = new Snapshots(createCentiaClient({ baseUrl, auth }));
|
|
3455
|
+
* const { id } = await snapshots.postSnapshot({ schema: 'geodanmark', relation: 'bygning' });
|
|
3456
|
+
* const done = await snapshots.waitForSnapshot(id);
|
|
3457
|
+
* ```
|
|
3458
|
+
*/
|
|
3459
|
+
var Snapshots = class {
|
|
3460
|
+
constructor(client) {
|
|
3461
|
+
this.client = client;
|
|
3462
|
+
}
|
|
3463
|
+
relationPath(schema, relation, rest = "") {
|
|
3464
|
+
return `api/v4/schemas/${encodeURIComponent(schema)}/relations/${encodeURIComponent(relation)}/snapshots${rest}`;
|
|
3465
|
+
}
|
|
3466
|
+
/**
|
|
3467
|
+
* Queue a Parquet snapshot of a table or view. Returns 202 with the job id.
|
|
3468
|
+
* Throws `CentiaApiError`: 403 (super-user only), 404 (relation not found),
|
|
3469
|
+
* 409 (a snapshot of the relation is already pending or running),
|
|
3470
|
+
* 501 (snapshot storage not configured).
|
|
3471
|
+
*/
|
|
3472
|
+
async postSnapshot(body) {
|
|
3473
|
+
return this.client.request({
|
|
3474
|
+
path: "api/v4/snapshots",
|
|
3475
|
+
method: "POST",
|
|
3476
|
+
body,
|
|
3477
|
+
expectedStatus: 202
|
|
3478
|
+
});
|
|
3479
|
+
}
|
|
3480
|
+
/** Get a snapshot job by id. Super-user only. */
|
|
3481
|
+
async getSnapshot(id) {
|
|
3482
|
+
return this.client.request({
|
|
3483
|
+
path: `api/v4/snapshots/${encodeURIComponent(id)}`,
|
|
3484
|
+
method: "GET"
|
|
3485
|
+
});
|
|
3486
|
+
}
|
|
3487
|
+
/** List the newest snapshot jobs (50), optionally filtered by schema/relation. Super-user only. */
|
|
3488
|
+
async getSnapshots(options) {
|
|
3489
|
+
var _this3 = this;
|
|
3490
|
+
const query = {};
|
|
3491
|
+
if ((options === null || options === void 0 ? void 0 : options.schema) !== void 0) query.schema = options.schema;
|
|
3492
|
+
if ((options === null || options === void 0 ? void 0 : options.relation) !== void 0) query.relation = options.relation;
|
|
3493
|
+
return _this3.client.request({
|
|
3494
|
+
path: "api/v4/snapshots",
|
|
3495
|
+
method: "GET",
|
|
3496
|
+
query: Object.keys(query).length > 0 ? query : void 0
|
|
3497
|
+
});
|
|
3498
|
+
}
|
|
3499
|
+
/**
|
|
3500
|
+
* Poll a snapshot job until it reaches a terminal status (succeeded, failed
|
|
3501
|
+
* or superseded) and return it — check `status`/`error` on the result.
|
|
3502
|
+
* Throws an `Error` when `timeoutMs` elapses first.
|
|
3503
|
+
*/
|
|
3504
|
+
async waitForSnapshot(id, options) {
|
|
3505
|
+
var _this4 = this;
|
|
3506
|
+
var _options$intervalMs, _options$timeoutMs;
|
|
3507
|
+
const intervalMs = (_options$intervalMs = options === null || options === void 0 ? void 0 : options.intervalMs) !== null && _options$intervalMs !== void 0 ? _options$intervalMs : 2e3;
|
|
3508
|
+
const timeoutMs = (_options$timeoutMs = options === null || options === void 0 ? void 0 : options.timeoutMs) !== null && _options$timeoutMs !== void 0 ? _options$timeoutMs : 3e5;
|
|
3509
|
+
const deadline = Date.now() + timeoutMs;
|
|
3510
|
+
for (;;) {
|
|
3511
|
+
const job = await _this4.getSnapshot(id);
|
|
3512
|
+
if (job.status === "succeeded" || job.status === "failed" || job.status === "superseded") return job;
|
|
3513
|
+
if (Date.now() + intervalMs > deadline) throw new Error(`Timed out after ${timeoutMs} ms waiting for snapshot ${id} (status: ${job.status})`);
|
|
3514
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
/** List the published snapshots of a relation, newest first. */
|
|
3518
|
+
async getRelationSnapshots(schema, relation) {
|
|
3519
|
+
var _this5 = this;
|
|
3520
|
+
return _this5.client.request({
|
|
3521
|
+
path: _this5.relationPath(schema, relation),
|
|
3522
|
+
method: "GET"
|
|
3523
|
+
});
|
|
3524
|
+
}
|
|
3525
|
+
/** Get one published snapshot (metadata) by date (`YYYY-MM-DD`). */
|
|
3526
|
+
async getRelationSnapshot(schema, relation, date) {
|
|
3527
|
+
var _this6 = this;
|
|
3528
|
+
return _this6.client.request({
|
|
3529
|
+
path: _this6.relationPath(schema, relation, `/${encodeURIComponent(date)}`),
|
|
3530
|
+
method: "GET"
|
|
3531
|
+
});
|
|
3532
|
+
}
|
|
3533
|
+
/**
|
|
3534
|
+
* Absolute URL of the snapshot's Parquet file, without fetching it — for
|
|
3535
|
+
* DuckDB/GDAL or a plain fetch. Authorization travels in the request
|
|
3536
|
+
* header, so protected relations need the caller to attach the token.
|
|
3537
|
+
*/
|
|
3538
|
+
getRelationSnapshotDataUrl(schema, relation, date) {
|
|
3539
|
+
return `${this.client.baseUrl}/${this.relationPath(schema, relation, `/${encodeURIComponent(date)}/data`)}`;
|
|
3540
|
+
}
|
|
3541
|
+
/**
|
|
3542
|
+
* Fetch the snapshot's Parquet file and return the raw `Response` (stream
|
|
3543
|
+
* or buffer it yourself). Follows the 302 redirect the server answers in
|
|
3544
|
+
* redirect mode. Pass `range` for a single byte range (206). Throws
|
|
3545
|
+
* `CentiaApiError`: 403, 404, 409 (several files; use
|
|
3546
|
+
* getRelationSnapshotFile), 416, 502.
|
|
3547
|
+
*/
|
|
3548
|
+
async getRelationSnapshotData(schema, relation, date, options) {
|
|
3549
|
+
var _this7 = this;
|
|
3550
|
+
return _this7.rawGet(_this7.relationPath(schema, relation, `/${encodeURIComponent(date)}/data`), "GET", options);
|
|
3551
|
+
}
|
|
3552
|
+
/** HEAD request for the snapshot's Parquet file — Content-Length, Accept-Ranges and ETag without the body. */
|
|
3553
|
+
async headRelationSnapshotData(schema, relation, date) {
|
|
3554
|
+
var _this8 = this;
|
|
3555
|
+
return _this8.rawGet(_this8.relationPath(schema, relation, `/${encodeURIComponent(date)}/data`), "HEAD");
|
|
3556
|
+
}
|
|
3557
|
+
/**
|
|
3558
|
+
* Fetch one file of the snapshot by catalog name (`data-<id>.parquet`,
|
|
3559
|
+
* `metadata-<id>.json`). Same redirect/range semantics as
|
|
3560
|
+
* getRelationSnapshotData.
|
|
3561
|
+
*/
|
|
3562
|
+
async getRelationSnapshotFile(schema, relation, date, file, options) {
|
|
3563
|
+
var _this9 = this;
|
|
3564
|
+
return _this9.rawGet(_this9.relationPath(schema, relation, `/${encodeURIComponent(date)}/files/${encodeURIComponent(file)}`), "GET", options);
|
|
3565
|
+
}
|
|
3566
|
+
/** HEAD request for one file of the snapshot. */
|
|
3567
|
+
async headRelationSnapshotFile(schema, relation, date, file) {
|
|
3568
|
+
var _this10 = this;
|
|
3569
|
+
return _this10.rawGet(_this10.relationPath(schema, relation, `/${encodeURIComponent(date)}/files/${encodeURIComponent(file)}`), "HEAD");
|
|
3570
|
+
}
|
|
3571
|
+
async rawGet(path, method, options) {
|
|
3572
|
+
var _this11 = this;
|
|
3573
|
+
const headers = {};
|
|
3574
|
+
if (options === null || options === void 0 ? void 0 : options.range) headers["Range"] = `bytes=${options.range[0]}-${options.range[1]}`;
|
|
3575
|
+
return _this11.client.requestRaw({
|
|
3576
|
+
path,
|
|
3577
|
+
method,
|
|
3578
|
+
headers: Object.keys(headers).length > 0 ? headers : void 0,
|
|
3579
|
+
expectedStatus: [200, 206]
|
|
3580
|
+
});
|
|
3581
|
+
}
|
|
3582
|
+
};
|
|
3583
|
+
|
|
3394
3584
|
//#endregion
|
|
3395
3585
|
//#region src/auth/errors.ts
|
|
3396
3586
|
/**
|
|
@@ -3530,6 +3720,7 @@ exports.PasswordFlow = PasswordFlow;
|
|
|
3530
3720
|
exports.Rpc = Rpc;
|
|
3531
3721
|
exports.SessionExpiredError = SessionExpiredError;
|
|
3532
3722
|
exports.SignUp = SignUp;
|
|
3723
|
+
exports.Snapshots = Snapshots;
|
|
3533
3724
|
exports.Sql = Sql;
|
|
3534
3725
|
exports.SqlNoToken = SqlNoToken;
|
|
3535
3726
|
exports.Stats = Stats;
|