@honua/app-platform 0.2.3 → 0.2.4
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/control-plane/dataset-import/client.d.ts +32 -0
- package/control-plane/dataset-import/client.d.ts.map +1 -0
- package/control-plane/dataset-import/client.js +112 -0
- package/control-plane/dataset-import/errors.d.ts +5 -0
- package/control-plane/dataset-import/errors.d.ts.map +1 -0
- package/control-plane/dataset-import/errors.js +8 -0
- package/control-plane/dataset-import/file-hash.d.ts +14 -0
- package/control-plane/dataset-import/file-hash.d.ts.map +1 -0
- package/control-plane/dataset-import/file-hash.js +117 -0
- package/control-plane/dataset-import/index.d.ts +10 -0
- package/control-plane/dataset-import/index.d.ts.map +1 -0
- package/control-plane/dataset-import/index.js +4 -0
- package/control-plane/dataset-import/submission-client.d.ts +18 -0
- package/control-plane/dataset-import/submission-client.d.ts.map +1 -0
- package/control-plane/dataset-import/submission-client.js +197 -0
- package/control-plane/dataset-import/transport.d.ts +14 -0
- package/control-plane/dataset-import/transport.d.ts.map +1 -0
- package/control-plane/dataset-import/transport.js +53 -0
- package/control-plane/dataset-import/types.d.ts +150 -0
- package/control-plane/dataset-import/types.d.ts.map +1 -0
- package/control-plane/dataset-import/types.js +1 -0
- package/control-plane/dataset-import/upload-client.d.ts +23 -0
- package/control-plane/dataset-import/upload-client.d.ts.map +1 -0
- package/control-plane/dataset-import/upload-client.js +187 -0
- package/control-plane/index.d.ts +1 -0
- package/control-plane/index.d.ts.map +1 -1
- package/control-plane/index.js +1 -0
- package/core/job-run-lifecycle.d.ts.map +1 -1
- package/core/job-run-lifecycle.js +4 -0
- package/package.json +2 -2
- package/runtime/generated/map-package-schema-meta.d.ts +1 -1
- package/runtime/generated/map-package-schema-meta.js +1 -1
- package/runtime/generated/map-package-schema-validator.d.ts +5 -5
- package/runtime/generated/map-package-schema-validator.d.ts.map +1 -1
- package/runtime/generated/map-package-schema-validator.js +746 -619
- package/runtime/index.d.ts +1 -1
- package/runtime/index.d.ts.map +1 -1
- package/runtime/map-package-fetch.d.ts +1 -0
- package/runtime/map-package-fetch.d.ts.map +1 -1
- package/runtime/map-package-fetch.js +24 -39
- package/runtime/map-package-validation.d.ts +1 -1
- package/runtime/map-package-validation.d.ts.map +1 -1
- package/runtime/map-package-validation.js +27 -1
- package/runtime/map-package.d.ts +16 -0
- package/runtime/map-package.d.ts.map +1 -1
- package/runtime/ogc-styles.d.ts +2 -2
- package/runtime/ogc-styles.d.ts.map +1 -1
- package/runtime/ogc-styles.js +17 -3
- package/runtime/style-compose.d.ts +2 -2
- package/runtime/style-compose.d.ts.map +1 -1
- package/runtime/style-compose.js +26 -3
- package/runtime/style-ref-bindings.d.ts +11 -0
- package/runtime/style-ref-bindings.d.ts.map +1 -0
- package/runtime/style-ref-bindings.js +82 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export { DatasetImportTaskApiError } from "./errors.js";
|
|
2
|
+
import type { CreateDatasetImportTask, DatasetImportAuthorityDescriptor, DatasetImportAuthorityRenewal, DatasetImportTask, DatasetImportTaskReference, RenewDatasetImportAuthority } from "./types.js";
|
|
3
|
+
export interface DatasetImportTaskClientOptions {
|
|
4
|
+
/** Deployment origin, optionally with its reverse-proxy path prefix. */
|
|
5
|
+
readonly baseUrl: string;
|
|
6
|
+
readonly workspaceId: string;
|
|
7
|
+
readonly targetId: string;
|
|
8
|
+
/** Framework-retained turn, never a moving current-view value. Omit for human actions. */
|
|
9
|
+
readonly originatingTurnId?: string;
|
|
10
|
+
readonly fetch?: typeof globalThis.fetch;
|
|
11
|
+
/** Reads fresh authentication for each request; credentials are never persisted here. */
|
|
12
|
+
readonly headers?: () => HeadersInit | Promise<HeadersInit>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Canonical task/authority APIs only. Never dispatches an import, retries a POST,
|
|
16
|
+
* invents an authority revision, or treats preparation as an accepted job.
|
|
17
|
+
* Persist original intent/renewal IDs before calling mutation methods. On lost
|
|
18
|
+
* replies use readIntent or replay the SAME renewal ID, never mint a replacement.
|
|
19
|
+
*/
|
|
20
|
+
export declare class DatasetImportTaskClient {
|
|
21
|
+
private readonly workspaceId;
|
|
22
|
+
private readonly targetId;
|
|
23
|
+
private readonly transport;
|
|
24
|
+
constructor(options: DatasetImportTaskClientOptions);
|
|
25
|
+
createTask(request: CreateDatasetImportTask, signal?: AbortSignal): Promise<DatasetImportTask>;
|
|
26
|
+
readIntent(intentId: string, signal?: AbortSignal): Promise<DatasetImportTask | null>;
|
|
27
|
+
readTask(reference: DatasetImportTaskReference, signal?: AbortSignal): Promise<DatasetImportTask | null>;
|
|
28
|
+
readAuthority(reference: DatasetImportTaskReference, signal?: AbortSignal): Promise<DatasetImportAuthorityDescriptor | null>;
|
|
29
|
+
renewAuthority(reference: DatasetImportTaskReference, request: RenewDatasetImportAuthority, signal?: AbortSignal): Promise<DatasetImportAuthorityRenewal>;
|
|
30
|
+
private assertTask;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../../src/control-plane/dataset-import/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,KAAK,EACV,uBAAuB,EACvB,gCAAgC,EAChC,6BAA6B,EAC7B,iBAAiB,EACjB,0BAA0B,EAC1B,2BAA2B,EAC5B,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,8BAA8B;IAC7C,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,0FAA0F;IAC1F,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IACzC,yFAAyF;IACzF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CAC7D;AAED;;;;;GAKG;AACH,qBAAa,uBAAuB;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkC;gBAEhD,OAAO,EAAE,8BAA8B;IAM7C,UAAU,CAAC,OAAO,EAAE,uBAAuB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAQ9F,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAarF,QAAQ,CAAC,SAAS,EAAE,0BAA0B,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAWxG,aAAa,CACjB,SAAS,EAAE,0BAA0B,EACrC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,gCAAgC,GAAG,IAAI,CAAC;IA+B7C,cAAc,CAClB,SAAS,EAAE,0BAA0B,EACrC,OAAO,EAAE,2BAA2B,EACpC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,6BAA6B,CAAC;IA6BzC,OAAO,CAAC,UAAU;CASnB"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { DatasetImportWorkspaceTransport, validId } from "./transport.js";
|
|
2
|
+
export { DatasetImportTaskApiError } from "./errors.js";
|
|
3
|
+
/**
|
|
4
|
+
* Canonical task/authority APIs only. Never dispatches an import, retries a POST,
|
|
5
|
+
* invents an authority revision, or treats preparation as an accepted job.
|
|
6
|
+
* Persist original intent/renewal IDs before calling mutation methods. On lost
|
|
7
|
+
* replies use readIntent or replay the SAME renewal ID, never mint a replacement.
|
|
8
|
+
*/
|
|
9
|
+
export class DatasetImportTaskClient {
|
|
10
|
+
workspaceId;
|
|
11
|
+
targetId;
|
|
12
|
+
transport;
|
|
13
|
+
constructor(options) {
|
|
14
|
+
this.transport = new DatasetImportWorkspaceTransport(options);
|
|
15
|
+
this.workspaceId = options.workspaceId;
|
|
16
|
+
this.targetId = options.targetId;
|
|
17
|
+
}
|
|
18
|
+
async createTask(request, signal) {
|
|
19
|
+
// Serialize before the first await so later form edits cannot change the request.
|
|
20
|
+
const body = JSON.stringify(request);
|
|
21
|
+
const task = await this.transport.request("/dataset-import-tasks", "POST", body, signal);
|
|
22
|
+
this.assertTask(task);
|
|
23
|
+
return task;
|
|
24
|
+
}
|
|
25
|
+
async readIntent(intentId, signal) {
|
|
26
|
+
validId(intentId);
|
|
27
|
+
const task = await this.transport.request(`/dataset-import-task-intents/${encodeURIComponent(intentId)}`, "GET", undefined, signal, true);
|
|
28
|
+
if (task !== null)
|
|
29
|
+
this.assertTask(task);
|
|
30
|
+
return task;
|
|
31
|
+
}
|
|
32
|
+
async readTask(reference, signal) {
|
|
33
|
+
const pinned = pin(reference);
|
|
34
|
+
const task = await this.transport.request(path(pinned), "GET", undefined, signal, true);
|
|
35
|
+
if (task !== null) {
|
|
36
|
+
this.assertTask(task);
|
|
37
|
+
if (task.taskId !== pinned.taskId || task.revision !== pinned.revision || task.contentHash !== pinned.contentHash)
|
|
38
|
+
throw new Error("Import task response changed the immutable task binding.");
|
|
39
|
+
}
|
|
40
|
+
return task;
|
|
41
|
+
}
|
|
42
|
+
async readAuthority(reference, signal) {
|
|
43
|
+
const pinned = pin(reference);
|
|
44
|
+
const descriptor = await this.transport.request(`${path(pinned)}/authority?contentHash=${pinned.contentHash}`, "GET", undefined, signal, true);
|
|
45
|
+
if (descriptor !== null &&
|
|
46
|
+
(descriptor.taskId !== pinned.taskId ||
|
|
47
|
+
descriptor.taskRevision !== pinned.revision ||
|
|
48
|
+
descriptor.taskContentHash !== pinned.contentHash ||
|
|
49
|
+
!Number.isSafeInteger(descriptor.authorityRevision) ||
|
|
50
|
+
descriptor.authorityRevision < 1 ||
|
|
51
|
+
!validDigest(descriptor.authorityFingerprint) ||
|
|
52
|
+
!validDigest(descriptor.policyContextFingerprint) ||
|
|
53
|
+
!validDigest(descriptor.originalTurnHash) ||
|
|
54
|
+
typeof descriptor.executorActorId !== "string" ||
|
|
55
|
+
descriptor.executorActorId.length < 1 ||
|
|
56
|
+
typeof descriptor.originatingTurnId !== "string" ||
|
|
57
|
+
descriptor.originatingTurnId.length < 1 ||
|
|
58
|
+
!Number.isFinite(Date.parse(descriptor.uploadAdmissionDeadline)) ||
|
|
59
|
+
(descriptor.preparedOperationId != null &&
|
|
60
|
+
(descriptor.preparedOperationId.length !== 32 || !/^[a-f0-9]{32}$/.test(descriptor.preparedOperationId)))))
|
|
61
|
+
throw new Error("Import authority response changed the immutable task binding.");
|
|
62
|
+
return descriptor;
|
|
63
|
+
}
|
|
64
|
+
async renewAuthority(reference, request, signal) {
|
|
65
|
+
const pinned = pin(reference);
|
|
66
|
+
validId(request.renewalId);
|
|
67
|
+
if (request.taskContentHash !== pinned.contentHash ||
|
|
68
|
+
!Number.isSafeInteger(request.expectedAuthorityRevision) ||
|
|
69
|
+
request.expectedAuthorityRevision < 1)
|
|
70
|
+
throw new TypeError("An exact task hash and positive authority revision are required.");
|
|
71
|
+
const body = JSON.stringify(request);
|
|
72
|
+
const renewalId = request.renewalId;
|
|
73
|
+
const previousRevision = request.expectedAuthorityRevision;
|
|
74
|
+
const receipt = await this.transport.request(`${path(pinned)}/authority-renewals`, "POST", body, signal);
|
|
75
|
+
if (receipt.renewalId !== renewalId ||
|
|
76
|
+
receipt.previousAuthorityRevision !== previousRevision ||
|
|
77
|
+
receipt.authorityRevision !== previousRevision + 1 ||
|
|
78
|
+
!validDigest(receipt.authorityFingerprint) ||
|
|
79
|
+
!Number.isFinite(Date.parse(receipt.renewedAt)))
|
|
80
|
+
throw new Error("Import renewal response changed the original renewal binding.");
|
|
81
|
+
return receipt;
|
|
82
|
+
}
|
|
83
|
+
assertTask(task) {
|
|
84
|
+
if (!task || task.workspaceId !== this.workspaceId || task.targetId !== this.targetId)
|
|
85
|
+
throw new Error("Import task response changed workspace or deployment target.");
|
|
86
|
+
pin({
|
|
87
|
+
taskId: task.taskId,
|
|
88
|
+
revision: task.revision,
|
|
89
|
+
contentHash: task.contentHash,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function pin(reference) {
|
|
94
|
+
validId(reference.taskId);
|
|
95
|
+
if (!Number.isSafeInteger(reference.revision) ||
|
|
96
|
+
reference.revision < 1 ||
|
|
97
|
+
typeof reference.contentHash !== "string" ||
|
|
98
|
+
reference.contentHash.length !== 64 ||
|
|
99
|
+
!/^[a-f0-9]{64}$/.test(reference.contentHash))
|
|
100
|
+
throw new TypeError("A positive revision and lowercase whole SHA-256 task hash are required.");
|
|
101
|
+
return {
|
|
102
|
+
taskId: reference.taskId,
|
|
103
|
+
revision: reference.revision,
|
|
104
|
+
contentHash: reference.contentHash,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function path(reference) {
|
|
108
|
+
return `/dataset-import-tasks/${encodeURIComponent(reference.taskId)}/versions/${reference.revision}`;
|
|
109
|
+
}
|
|
110
|
+
function validDigest(value) {
|
|
111
|
+
return typeof value === "string" && value.length === 64 && /^[a-f0-9]{64}$/.test(value);
|
|
112
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../../src/control-plane/dataset-import/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,yBAA0B,SAAQ,KAAK;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM;CAIpC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Whole-file SHA-256 with bounded reads; no upload or task is created by hashing. */
|
|
2
|
+
export interface DatasetImportFileHashOptions {
|
|
3
|
+
readonly signal?: AbortSignal;
|
|
4
|
+
/** Defaults to 1 MiB; bounded to 64 KiB–8 MiB. */
|
|
5
|
+
readonly chunkBytes?: number;
|
|
6
|
+
readonly onProgress?: (hashedBytes: number, totalBytes: number) => void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Hash every byte before creating a resumable intent, including when resuming a
|
|
10
|
+
* reselected file. Names, timestamps and accepted-prefix hashes are insufficient.
|
|
11
|
+
* Reads slices sequentially so browser memory does not grow with file size.
|
|
12
|
+
*/
|
|
13
|
+
export declare function hashDatasetImportFile(file: Blob, options?: DatasetImportFileHashOptions): Promise<string>;
|
|
14
|
+
//# sourceMappingURL=file-hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-hash.d.ts","sourceRoot":"","sources":["../../../../src/control-plane/dataset-import/file-hash.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,kDAAkD;IAClD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;CACzE;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,GAAE,4BAAiC,GAAG,OAAO,CAAC,MAAM,CAAC,CAoDnH"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hash every byte before creating a resumable intent, including when resuming a
|
|
3
|
+
* reselected file. Names, timestamps and accepted-prefix hashes are insufficient.
|
|
4
|
+
* Reads slices sequentially so browser memory does not grow with file size.
|
|
5
|
+
*/
|
|
6
|
+
export async function hashDatasetImportFile(file, options = {}) {
|
|
7
|
+
const chunkBytes = options.chunkBytes ?? 1024 * 1024;
|
|
8
|
+
if (!Number.isSafeInteger(chunkBytes) || chunkBytes < 65536 || chunkBytes > 8 * 1024 * 1024)
|
|
9
|
+
throw new RangeError("chunkBytes must be an integer between 64 KiB and 8 MiB.");
|
|
10
|
+
if (!Number.isSafeInteger(file.size) || file.size < 0)
|
|
11
|
+
throw new RangeError("File size must be a nonnegative safe integer.");
|
|
12
|
+
const state = new Uint32Array([
|
|
13
|
+
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
|
14
|
+
]);
|
|
15
|
+
const words = new Uint32Array(64);
|
|
16
|
+
const block = new Uint8Array(64);
|
|
17
|
+
let buffered = 0;
|
|
18
|
+
const consume = (bytes) => {
|
|
19
|
+
let offset = 0;
|
|
20
|
+
while (offset < bytes.length) {
|
|
21
|
+
if (buffered === 0 && bytes.length - offset >= 64) {
|
|
22
|
+
compress(state, words, bytes.subarray(offset, offset + 64));
|
|
23
|
+
offset += 64;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const count = Math.min(64 - buffered, bytes.length - offset);
|
|
27
|
+
block.set(bytes.subarray(offset, offset + count), buffered);
|
|
28
|
+
offset += count;
|
|
29
|
+
buffered += count;
|
|
30
|
+
if (buffered === 64) {
|
|
31
|
+
compress(state, words, block);
|
|
32
|
+
buffered = 0;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
options.signal?.throwIfAborted();
|
|
37
|
+
options.onProgress?.(0, file.size);
|
|
38
|
+
for (let offset = 0; offset < file.size; offset += chunkBytes) {
|
|
39
|
+
options.signal?.throwIfAborted();
|
|
40
|
+
const expected = Math.min(chunkBytes, file.size - offset);
|
|
41
|
+
const slice = file.slice(offset, offset + expected);
|
|
42
|
+
if (slice.size !== expected)
|
|
43
|
+
throw new Error("File slice length changed during hashing.");
|
|
44
|
+
const bytes = new Uint8Array(await slice.arrayBuffer());
|
|
45
|
+
options.signal?.throwIfAborted();
|
|
46
|
+
if (bytes.length !== expected)
|
|
47
|
+
throw new Error("File read was truncated or exceeded its declared length.");
|
|
48
|
+
consume(bytes);
|
|
49
|
+
options.onProgress?.(offset + bytes.length, file.size);
|
|
50
|
+
}
|
|
51
|
+
options.signal?.throwIfAborted();
|
|
52
|
+
// SHA-256 encodes the full 64-bit bit length, including files over 512 MiB.
|
|
53
|
+
const padding = new Uint8Array(buffered < 56 ? 64 - buffered : 128 - buffered);
|
|
54
|
+
padding[0] = 0x80;
|
|
55
|
+
const length = new DataView(padding.buffer);
|
|
56
|
+
length.setUint32(padding.length - 8, Math.floor(file.size / 0x20000000), false);
|
|
57
|
+
length.setUint32(padding.length - 4, (file.size % 0x20000000) * 8, false);
|
|
58
|
+
consume(padding);
|
|
59
|
+
return Array.from(state, (word) => word.toString(16).padStart(8, "0")).join("");
|
|
60
|
+
}
|
|
61
|
+
function compress(state, words, block) {
|
|
62
|
+
const view = new DataView(block.buffer, block.byteOffset, block.byteLength);
|
|
63
|
+
for (let index = 0; index < 16; index++)
|
|
64
|
+
words[index] = view.getUint32(index * 4, false);
|
|
65
|
+
for (let index = 16; index < 64; index++) {
|
|
66
|
+
const before15 = words[index - 15] ?? 0;
|
|
67
|
+
const before2 = words[index - 2] ?? 0;
|
|
68
|
+
const s0 = rotateRight(before15, 7) ^ rotateRight(before15, 18) ^ (before15 >>> 3);
|
|
69
|
+
const s1 = rotateRight(before2, 17) ^ rotateRight(before2, 19) ^ (before2 >>> 10);
|
|
70
|
+
words[index] = ((words[index - 16] ?? 0) + s0 + (words[index - 7] ?? 0) + s1) >>> 0;
|
|
71
|
+
}
|
|
72
|
+
let a = state[0] ?? 0;
|
|
73
|
+
let b = state[1] ?? 0;
|
|
74
|
+
let c = state[2] ?? 0;
|
|
75
|
+
let d = state[3] ?? 0;
|
|
76
|
+
let e = state[4] ?? 0;
|
|
77
|
+
let f = state[5] ?? 0;
|
|
78
|
+
let g = state[6] ?? 0;
|
|
79
|
+
let h = state[7] ?? 0;
|
|
80
|
+
for (let index = 0; index < 64; index++) {
|
|
81
|
+
const upper1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25);
|
|
82
|
+
const choose = (e & f) ^ (~e & g);
|
|
83
|
+
const temp1 = (h + upper1 + choose + (SHA256_CONSTANTS[index] ?? 0) + (words[index] ?? 0)) >>> 0;
|
|
84
|
+
const upper0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22);
|
|
85
|
+
const majority = (a & b) ^ (a & c) ^ (b & c);
|
|
86
|
+
const temp2 = (upper0 + majority) >>> 0;
|
|
87
|
+
h = g;
|
|
88
|
+
g = f;
|
|
89
|
+
f = e;
|
|
90
|
+
e = (d + temp1) >>> 0;
|
|
91
|
+
d = c;
|
|
92
|
+
c = b;
|
|
93
|
+
b = a;
|
|
94
|
+
a = (temp1 + temp2) >>> 0;
|
|
95
|
+
}
|
|
96
|
+
state[0] = ((state[0] ?? 0) + a) >>> 0;
|
|
97
|
+
state[1] = ((state[1] ?? 0) + b) >>> 0;
|
|
98
|
+
state[2] = ((state[2] ?? 0) + c) >>> 0;
|
|
99
|
+
state[3] = ((state[3] ?? 0) + d) >>> 0;
|
|
100
|
+
state[4] = ((state[4] ?? 0) + e) >>> 0;
|
|
101
|
+
state[5] = ((state[5] ?? 0) + f) >>> 0;
|
|
102
|
+
state[6] = ((state[6] ?? 0) + g) >>> 0;
|
|
103
|
+
state[7] = ((state[7] ?? 0) + h) >>> 0;
|
|
104
|
+
}
|
|
105
|
+
function rotateRight(value, amount) {
|
|
106
|
+
return (value >>> amount) | (value << (32 - amount));
|
|
107
|
+
}
|
|
108
|
+
const SHA256_CONSTANTS = new Uint32Array([
|
|
109
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98,
|
|
110
|
+
0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
|
|
111
|
+
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8,
|
|
112
|
+
0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
|
113
|
+
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
|
|
114
|
+
0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
|
|
115
|
+
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
|
|
116
|
+
0xc67178f2,
|
|
117
|
+
]);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { hashDatasetImportFile } from "./file-hash.js";
|
|
2
|
+
export type { DatasetImportFileHashOptions } from "./file-hash.js";
|
|
3
|
+
export type { CreateDatasetImportTask, DatasetImportArtifactSource, DatasetImportAuthorityDescriptor, DatasetImportAuthorityRenewal, DatasetImportDestination, DatasetImportSource, DatasetImportTask, DatasetImportTaskOptions, DatasetImportTaskReference, RenewDatasetImportAuthority, } from "./types.js";
|
|
4
|
+
export { DatasetImportTaskApiError, DatasetImportTaskClient, } from "./client.js";
|
|
5
|
+
export type { DatasetImportTaskClientOptions } from "./client.js";
|
|
6
|
+
export { DatasetImportUploadClient } from "./upload-client.js";
|
|
7
|
+
export type { DatasetImportSourceRetentionReceipt, DatasetImportUploadCapabilities, DatasetImportUploadReceipt, } from "./types.js";
|
|
8
|
+
export { DatasetImportSubmissionClient } from "./submission-client.js";
|
|
9
|
+
export type { DatasetImportJobReceipt, DatasetImportOutputReceipt, DatasetImportSubmissionReceipt, SubmitDatasetImport, } from "./types.js";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/control-plane/dataset-import/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,YAAY,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAC;AACnE,YAAY,EACV,uBAAuB,EACvB,2BAA2B,EAC3B,gCAAgC,EAChC,6BAA6B,EAC7B,wBAAwB,EACxB,mBAAmB,EACnB,iBAAiB,EACjB,wBAAwB,EACxB,0BAA0B,EAC1B,2BAA2B,GAC5B,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,yBAAyB,EACzB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,YAAY,EACV,mCAAmC,EACnC,+BAA+B,EAC/B,0BAA0B,GAC3B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AACvE,YAAY,EACV,uBAAuB,EACvB,0BAA0B,EAC1B,8BAA8B,EAC9B,mBAAmB,GACpB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { hashDatasetImportFile } from "./file-hash.js";
|
|
2
|
+
export { DatasetImportTaskApiError, DatasetImportTaskClient, } from "./client.js";
|
|
3
|
+
export { DatasetImportUploadClient } from "./upload-client.js";
|
|
4
|
+
export { DatasetImportSubmissionClient } from "./submission-client.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { DatasetImportTaskClientOptions } from "./client.js";
|
|
2
|
+
import type { DatasetImportAuthorityDescriptor, DatasetImportSubmissionReceipt, DatasetImportTaskReference } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Shared canonical submission/status transport. Human POST prepares the original
|
|
5
|
+
* task and awaits its actual agent; only an authenticated executor can dispatch.
|
|
6
|
+
* Persist the task and reviewed authority BEFORE POST. After an unknown reply,
|
|
7
|
+
* read/reconcile the SAME task; GET never dispatches and there is no POST retry.
|
|
8
|
+
* Explicit original POST replay is a deliberate caller action, never automatic.
|
|
9
|
+
*/
|
|
10
|
+
export declare class DatasetImportSubmissionClient {
|
|
11
|
+
private readonly transport;
|
|
12
|
+
constructor(options: DatasetImportTaskClientOptions);
|
|
13
|
+
read(reference: DatasetImportTaskReference, signal?: AbortSignal): Promise<DatasetImportSubmissionReceipt | null>;
|
|
14
|
+
/** Snapshot and validate the original known receipt before the authentication await. */
|
|
15
|
+
reconcile(reference: DatasetImportTaskReference, original: DatasetImportSubmissionReceipt, signal?: AbortSignal): Promise<DatasetImportSubmissionReceipt | null>;
|
|
16
|
+
submit(reference: DatasetImportTaskReference, reviewed: DatasetImportAuthorityDescriptor, signal?: AbortSignal): Promise<DatasetImportSubmissionReceipt>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=submission-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"submission-client.d.ts","sourceRoot":"","sources":["../../../../src/control-plane/dataset-import/submission-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAElE,OAAO,KAAK,EACV,gCAAgC,EAEhC,8BAA8B,EAC9B,0BAA0B,EAC3B,MAAM,YAAY,CAAC;AAEpB;;;;;;GAMG;AACH,qBAAa,6BAA6B;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkC;gBAChD,OAAO,EAAE,8BAA8B;IAI7C,IAAI,CACR,SAAS,EAAE,0BAA0B,EACrC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,8BAA8B,GAAG,IAAI,CAAC;IAajD,wFAAwF;IAClF,SAAS,CACb,SAAS,EAAE,0BAA0B,EACrC,QAAQ,EAAE,8BAA8B,EACxC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,8BAA8B,GAAG,IAAI,CAAC;IA4B3C,MAAM,CACV,SAAS,EAAE,0BAA0B,EACrC,QAAQ,EAAE,gCAAgC,EAC1C,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,8BAA8B,CAAC;CAwC3C"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { DatasetImportWorkspaceTransport, validId } from "./transport.js";
|
|
2
|
+
/**
|
|
3
|
+
* Shared canonical submission/status transport. Human POST prepares the original
|
|
4
|
+
* task and awaits its actual agent; only an authenticated executor can dispatch.
|
|
5
|
+
* Persist the task and reviewed authority BEFORE POST. After an unknown reply,
|
|
6
|
+
* read/reconcile the SAME task; GET never dispatches and there is no POST retry.
|
|
7
|
+
* Explicit original POST replay is a deliberate caller action, never automatic.
|
|
8
|
+
*/
|
|
9
|
+
export class DatasetImportSubmissionClient {
|
|
10
|
+
transport;
|
|
11
|
+
constructor(options) {
|
|
12
|
+
this.transport = new DatasetImportWorkspaceTransport(options);
|
|
13
|
+
}
|
|
14
|
+
async read(reference, signal) {
|
|
15
|
+
const pinned = pin(reference);
|
|
16
|
+
const value = await this.transport.request(`${path(pinned)}?contentHash=${pinned.contentHash}`, "GET", undefined, signal, true);
|
|
17
|
+
if (value !== null)
|
|
18
|
+
validate(value, pinned);
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
/** Snapshot and validate the original known receipt before the authentication await. */
|
|
22
|
+
async reconcile(reference, original, signal) {
|
|
23
|
+
const pinned = pin(reference);
|
|
24
|
+
const before = structuredClone(original);
|
|
25
|
+
validate(before, pinned);
|
|
26
|
+
const value = await this.read(pinned, signal);
|
|
27
|
+
if (value !== null) {
|
|
28
|
+
if (before.state !== "not-prepared")
|
|
29
|
+
requireOriginal(before, value);
|
|
30
|
+
if (before.state === "accepted") {
|
|
31
|
+
if (value.state !== "accepted" ||
|
|
32
|
+
value.job.jobId !== before.job.jobId ||
|
|
33
|
+
value.job.leaseId !== before.job.leaseId ||
|
|
34
|
+
value.job.createdAt !== before.job.createdAt)
|
|
35
|
+
fail();
|
|
36
|
+
if (terminal(before.job.status) &&
|
|
37
|
+
(value.job.status !== before.job.status ||
|
|
38
|
+
outputKey(value.job.output) !== outputKey(before.job.output) ||
|
|
39
|
+
value.job.completedAt !== before.job.completedAt ||
|
|
40
|
+
value.job.errorCode !== before.job.errorCode))
|
|
41
|
+
fail();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
async submit(reference, reviewed, signal) {
|
|
47
|
+
const pinned = pin(reference);
|
|
48
|
+
if (!reviewed ||
|
|
49
|
+
reviewed.taskId !== pinned.taskId ||
|
|
50
|
+
reviewed.taskRevision !== pinned.revision ||
|
|
51
|
+
reviewed.taskContentHash !== pinned.contentHash ||
|
|
52
|
+
!positive(reviewed.authorityRevision) ||
|
|
53
|
+
!digest(reviewed.authorityFingerprint) ||
|
|
54
|
+
!digest(reviewed.policyContextFingerprint) ||
|
|
55
|
+
!date(reviewed.uploadAdmissionDeadline) ||
|
|
56
|
+
(reviewed.preparedOperationId != null && !operation(reviewed.preparedOperationId)))
|
|
57
|
+
throw new TypeError("An exact reviewed task authority and original admission deadline are required.");
|
|
58
|
+
const expected = {
|
|
59
|
+
authorityRevision: reviewed.authorityRevision,
|
|
60
|
+
authorityFingerprint: reviewed.authorityFingerprint,
|
|
61
|
+
policyContextFingerprint: reviewed.policyContextFingerprint,
|
|
62
|
+
acceptBefore: reviewed.uploadAdmissionDeadline,
|
|
63
|
+
operationId: reviewed.preparedOperationId,
|
|
64
|
+
};
|
|
65
|
+
const body = JSON.stringify({
|
|
66
|
+
taskContentHash: pinned.contentHash,
|
|
67
|
+
authorityRevision: expected.authorityRevision,
|
|
68
|
+
authorityFingerprint: expected.authorityFingerprint,
|
|
69
|
+
policyContextFingerprint: expected.policyContextFingerprint,
|
|
70
|
+
});
|
|
71
|
+
const value = await this.transport.request(path(pinned), "POST", body, signal);
|
|
72
|
+
validate(value, pinned);
|
|
73
|
+
if (value.state === "not-prepared" ||
|
|
74
|
+
value.authorityRevision !== expected.authorityRevision ||
|
|
75
|
+
value.authorityFingerprint !== expected.authorityFingerprint ||
|
|
76
|
+
value.policyContextFingerprint !== expected.policyContextFingerprint ||
|
|
77
|
+
Date.parse(value.acceptBefore) !== Date.parse(expected.acceptBefore) ||
|
|
78
|
+
(expected.operationId != null && value.operationId !== expected.operationId))
|
|
79
|
+
fail();
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function pin(reference) {
|
|
84
|
+
if (!reference)
|
|
85
|
+
throw new TypeError("An immutable task reference is required.");
|
|
86
|
+
validId(reference.taskId);
|
|
87
|
+
if (!positive(reference.revision) || !digest(reference.contentHash))
|
|
88
|
+
throw new TypeError("An immutable task reference is required.");
|
|
89
|
+
return { taskId: reference.taskId, revision: reference.revision, contentHash: reference.contentHash };
|
|
90
|
+
}
|
|
91
|
+
function path(reference) {
|
|
92
|
+
return `/dataset-import-tasks/${encodeURIComponent(reference.taskId)}/versions/${reference.revision}/submission`;
|
|
93
|
+
}
|
|
94
|
+
function digest(value) {
|
|
95
|
+
return typeof value === "string" && value.length === 64 && /^[a-f0-9]{64}$/.test(value);
|
|
96
|
+
}
|
|
97
|
+
function operation(value) {
|
|
98
|
+
return typeof value === "string" && value.length === 32 && /^[a-f0-9]{32}$/.test(value);
|
|
99
|
+
}
|
|
100
|
+
function positive(value) {
|
|
101
|
+
return Number.isSafeInteger(value) && value > 0;
|
|
102
|
+
}
|
|
103
|
+
function date(value) {
|
|
104
|
+
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
105
|
+
}
|
|
106
|
+
function text(value) {
|
|
107
|
+
return (typeof value === "string" &&
|
|
108
|
+
value.length > 0 &&
|
|
109
|
+
value.length <= 256 &&
|
|
110
|
+
value.trim() === value &&
|
|
111
|
+
!Array.from(value).some((character) => {
|
|
112
|
+
const code = character.charCodeAt(0);
|
|
113
|
+
return code <= 31 || (code >= 127 && code <= 159);
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
function outputKey(output) {
|
|
117
|
+
return output == null
|
|
118
|
+
? "null"
|
|
119
|
+
: JSON.stringify([output.schema, output.physicalTableName, output.featureCount, output.detectedSrid ?? null]);
|
|
120
|
+
}
|
|
121
|
+
function terminal(value) {
|
|
122
|
+
return ["succeeded", "failed", "cancelled"].includes(value);
|
|
123
|
+
}
|
|
124
|
+
function fail() {
|
|
125
|
+
throw new Error("Submission response changed the original task, authority, operation or accepted job receipt.");
|
|
126
|
+
}
|
|
127
|
+
function requireOriginal(before, value) {
|
|
128
|
+
if (value.state === "not-prepared" ||
|
|
129
|
+
value.operationId !== before.operationId ||
|
|
130
|
+
value.authorityRevision !== before.authorityRevision ||
|
|
131
|
+
value.authorityFingerprint !== before.authorityFingerprint ||
|
|
132
|
+
value.policyContextFingerprint !== before.policyContextFingerprint ||
|
|
133
|
+
value.acceptBefore !== before.acceptBefore)
|
|
134
|
+
fail();
|
|
135
|
+
}
|
|
136
|
+
function validate(value, reference) {
|
|
137
|
+
if (!value ||
|
|
138
|
+
value.taskId !== reference.taskId ||
|
|
139
|
+
value.taskRevision !== reference.revision ||
|
|
140
|
+
value.taskContentHash !== reference.contentHash ||
|
|
141
|
+
!["not-prepared", "prepared", "accepted"].includes(value.state))
|
|
142
|
+
fail();
|
|
143
|
+
if (value.state === "not-prepared") {
|
|
144
|
+
if ([
|
|
145
|
+
value.operationId,
|
|
146
|
+
value.authorityRevision,
|
|
147
|
+
value.authorityFingerprint,
|
|
148
|
+
value.policyContextFingerprint,
|
|
149
|
+
value.acceptBefore,
|
|
150
|
+
value.job,
|
|
151
|
+
].some((item) => item != null))
|
|
152
|
+
fail();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (!operation(value.operationId) ||
|
|
156
|
+
!positive(value.authorityRevision) ||
|
|
157
|
+
!digest(value.authorityFingerprint) ||
|
|
158
|
+
!digest(value.policyContextFingerprint) ||
|
|
159
|
+
!date(value.acceptBefore))
|
|
160
|
+
fail();
|
|
161
|
+
if (value.state === "prepared") {
|
|
162
|
+
if (value.job != null)
|
|
163
|
+
fail();
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const job = value.job;
|
|
167
|
+
if (!job ||
|
|
168
|
+
job.jobId !== value.operationId ||
|
|
169
|
+
!text(job.leaseId) ||
|
|
170
|
+
!["queued", "provisioning", "running", "succeeded", "failed", "cancelled"].includes(job.status) ||
|
|
171
|
+
!date(job.createdAt) ||
|
|
172
|
+
!date(job.updatedAt) ||
|
|
173
|
+
Date.parse(job.updatedAt) < Date.parse(job.createdAt) ||
|
|
174
|
+
(job.completedAt != null && (!date(job.completedAt) || !terminal(job.status))) ||
|
|
175
|
+
(job.percentComplete != null &&
|
|
176
|
+
(!Number.isFinite(job.percentComplete) || job.percentComplete < 0 || job.percentComplete > 100)) ||
|
|
177
|
+
(job.errorCode != null && (job.status !== "failed" || job.errorCode !== "import-failed")))
|
|
178
|
+
fail();
|
|
179
|
+
if (job.outcomePending !== undefined &&
|
|
180
|
+
(typeof job.outcomePending !== "boolean" || (job.outcomePending && terminal(job.status))))
|
|
181
|
+
fail();
|
|
182
|
+
const output = job.output;
|
|
183
|
+
if (job.status === "succeeded" && output == null)
|
|
184
|
+
fail();
|
|
185
|
+
if (output != null &&
|
|
186
|
+
(job.status !== "succeeded" ||
|
|
187
|
+
!text(output.schema) ||
|
|
188
|
+
!text(output.physicalTableName) ||
|
|
189
|
+
!Number.isSafeInteger(output.featureCount) ||
|
|
190
|
+
output.featureCount < 0 ||
|
|
191
|
+
output.featureCount > 2147483647 ||
|
|
192
|
+
(output.detectedSrid != null &&
|
|
193
|
+
(!Number.isInteger(output.detectedSrid) ||
|
|
194
|
+
output.detectedSrid < -2147483648 ||
|
|
195
|
+
output.detectedSrid > 2147483647))))
|
|
196
|
+
fail();
|
|
197
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { DatasetImportTaskClientOptions } from "./client.js";
|
|
2
|
+
/** Internal common transport: fixed deployment/workspace/turn, fresh authentication only. */
|
|
3
|
+
export declare class DatasetImportWorkspaceTransport {
|
|
4
|
+
private readonly base;
|
|
5
|
+
private readonly workspaceId;
|
|
6
|
+
private readonly originatingTurnId?;
|
|
7
|
+
private readonly fetcher;
|
|
8
|
+
private readonly headers?;
|
|
9
|
+
constructor(options: DatasetImportTaskClientOptions);
|
|
10
|
+
request<T>(suffix: string, method: "GET" | "POST" | "PUT" | "DELETE", body: BodyInit | undefined, signal?: AbortSignal, allowMissing?: false, contentType?: string): Promise<T>;
|
|
11
|
+
request<T>(suffix: string, method: "GET" | "POST" | "PUT" | "DELETE", body: BodyInit | undefined, signal: AbortSignal | undefined, allowMissing: true, contentType?: string): Promise<T | null>;
|
|
12
|
+
}
|
|
13
|
+
export declare function validId(value: string): void;
|
|
14
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../../../src/control-plane/dataset-import/transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,aAAa,CAAC;AAGlE,6FAA6F;AAC7F,qBAAa,+BAA+B;IAC1C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;IAClD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAA4C;gBAEzD,OAAO,EAAE,8BAA8B;IAe7C,OAAO,CAAC,CAAC,EACb,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,EACzC,IAAI,EAAE,QAAQ,GAAG,SAAS,EAC1B,MAAM,CAAC,EAAE,WAAW,EACpB,YAAY,CAAC,EAAE,KAAK,EACpB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,CAAC,CAAC;IACP,OAAO,CAAC,CAAC,EACb,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,EACzC,IAAI,EAAE,QAAQ,GAAG,SAAS,EAC1B,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,YAAY,EAAE,IAAI,EAClB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;CAuBrB;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAY3C"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { DatasetImportTaskApiError } from "./errors.js";
|
|
2
|
+
/** Internal common transport: fixed deployment/workspace/turn, fresh authentication only. */
|
|
3
|
+
export class DatasetImportWorkspaceTransport {
|
|
4
|
+
base;
|
|
5
|
+
workspaceId;
|
|
6
|
+
originatingTurnId;
|
|
7
|
+
fetcher;
|
|
8
|
+
headers;
|
|
9
|
+
constructor(options) {
|
|
10
|
+
const base = new URL(options.baseUrl);
|
|
11
|
+
if (!/^https?:$/.test(base.protocol) || base.username || base.password || base.search || base.hash)
|
|
12
|
+
throw new TypeError("A deployment HTTP(S) URL without credentials, query or fragment is required.");
|
|
13
|
+
validId(options.workspaceId);
|
|
14
|
+
validId(options.targetId);
|
|
15
|
+
if (options.originatingTurnId !== undefined)
|
|
16
|
+
validId(options.originatingTurnId);
|
|
17
|
+
this.base = `${base.href.replace(/\/$/, "")}/api/v1/agent/workspaces/${encodeURIComponent(options.workspaceId)}`;
|
|
18
|
+
this.workspaceId = options.workspaceId;
|
|
19
|
+
this.originatingTurnId = options.originatingTurnId;
|
|
20
|
+
// Native browser fetch rejects a transport instance as its receiver.
|
|
21
|
+
this.fetcher = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
22
|
+
this.headers = options.headers;
|
|
23
|
+
}
|
|
24
|
+
async request(suffix, method, body, signal, allowMissing = false, contentType = "application/json") {
|
|
25
|
+
const headers = new Headers(await this.headers?.());
|
|
26
|
+
headers.set("Accept", "application/json");
|
|
27
|
+
headers.delete("X-Honua-Workspace");
|
|
28
|
+
headers.delete("X-Honua-Turn");
|
|
29
|
+
if (this.originatingTurnId !== undefined) {
|
|
30
|
+
headers.set("X-Honua-Workspace", this.workspaceId);
|
|
31
|
+
headers.set("X-Honua-Turn", this.originatingTurnId);
|
|
32
|
+
}
|
|
33
|
+
if (body !== undefined)
|
|
34
|
+
headers.set("Content-Type", contentType);
|
|
35
|
+
const response = await this.fetcher(`${this.base}${suffix}`, { method, headers, body, signal, redirect: "error" });
|
|
36
|
+
if (allowMissing && response.status === 404)
|
|
37
|
+
return null;
|
|
38
|
+
if (!response.ok)
|
|
39
|
+
throw new DatasetImportTaskApiError(response.status);
|
|
40
|
+
return (await response.json());
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function validId(value) {
|
|
44
|
+
if (typeof value !== "string" ||
|
|
45
|
+
value.length < 1 ||
|
|
46
|
+
value.length > 256 ||
|
|
47
|
+
value.trim() !== value ||
|
|
48
|
+
Array.from(value).some((character) => {
|
|
49
|
+
const code = character.charCodeAt(0);
|
|
50
|
+
return code <= 31 || (code >= 127 && code <= 159);
|
|
51
|
+
}))
|
|
52
|
+
throw new TypeError("A bounded canonical identity is required.");
|
|
53
|
+
}
|