@elaraai/e3-api-server 1.0.76 → 1.0.77

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.
Files changed (36) hide show
  1. package/README.md +21 -0
  2. package/dist/src/errors.d.ts.map +1 -1
  3. package/dist/src/errors.js +13 -1
  4. package/dist/src/errors.js.map +1 -1
  5. package/dist/src/handlers/datasets.d.ts.map +1 -1
  6. package/dist/src/handlers/datasets.js +3 -1
  7. package/dist/src/handlers/datasets.js.map +1 -1
  8. package/dist/src/handlers/datasets.spec.js +1 -1
  9. package/dist/src/handlers/datasets.spec.js.map +1 -1
  10. package/dist/src/handlers/functions.spec.js +1 -0
  11. package/dist/src/handlers/functions.spec.js.map +1 -1
  12. package/dist/src/handlers/records.spec.js +1 -0
  13. package/dist/src/handlers/records.spec.js.map +1 -1
  14. package/dist/src/handlers/workspaces.d.ts.map +1 -1
  15. package/dist/src/handlers/workspaces.js +11 -1
  16. package/dist/src/handlers/workspaces.js.map +1 -1
  17. package/dist/src/routes/data.d.ts +2 -1
  18. package/dist/src/routes/data.d.ts.map +1 -1
  19. package/dist/src/routes/data.js +86 -7
  20. package/dist/src/routes/data.js.map +1 -1
  21. package/dist/src/routes/index.d.ts +1 -1
  22. package/dist/src/routes/index.d.ts.map +1 -1
  23. package/dist/src/routes/index.js.map +1 -1
  24. package/dist/src/routes/transfer.d.ts +20 -3
  25. package/dist/src/routes/transfer.d.ts.map +1 -1
  26. package/dist/src/routes/transfer.js +191 -54
  27. package/dist/src/routes/transfer.js.map +1 -1
  28. package/dist/src/server.d.ts +9 -0
  29. package/dist/src/server.d.ts.map +1 -1
  30. package/dist/src/server.js +5 -2
  31. package/dist/src/server.js.map +1 -1
  32. package/dist/src/types.d.ts +23 -2
  33. package/dist/src/types.d.ts.map +1 -1
  34. package/dist/src/types.js +3 -2
  35. package/dist/src/types.js.map +1 -1
  36. package/package.json +4 -4
@@ -194,7 +194,17 @@ export async function deployWorkspace(storage, repoPath, workspace, packageRef)
194
194
  if (!pkgVersion) {
195
195
  return sendError(NullType, errorToVariant(new Error(`Package not found: ${pkgName}`)));
196
196
  }
197
- await workspaceDeploy(storage, repoPath, workspace, pkgName, pkgVersion);
197
+ // A path-initialised input names a path on the machine that EXPORTED the
198
+ // package, never this server's. The server must not open it — even a path
199
+ // it can read — or a package could adopt any server-readable file of the
200
+ // declared type into the caller's repository. Every `file` source is left
201
+ // unassigned with a warning: `e3 workspace deploy <url> --from-source`
202
+ // completes those inputs over the dataset transfer protocol immediately
203
+ // afterwards, and the server's commit runs the same validation.
204
+ await workspaceDeploy(storage, repoPath, workspace, pkgName, pkgVersion, {
205
+ resolveFileSources: false,
206
+ sourceWarning: (message) => console.warn(`[deploy ${workspace}] ${message}`),
207
+ });
198
208
  return sendSuccess(NullType, null);
199
209
  }
200
210
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"workspaces.js","sourceRoot":"","sources":["../../../src/handlers/workspaces.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workspaces.js","sourceRoot":"","sources":["../../../src/handlers/workspaces.ts"],"names":[],"mappings":"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"}
@@ -8,7 +8,8 @@ import { type StorageBackend, type TransferBackend } from '@elaraai/e3-core';
8
8
  * Create generic upload/download data endpoints.
9
9
  *
10
10
  * Returns two Hono apps:
11
- * - `uploads`: PUT /:id — upload data (dataset BEAST2 or package zip)
11
+ * - `uploads`: PUT /:id — upload data (dataset BEAST2 or package zip);
12
+ * PUT /:id/parts/:part — one part of a dataset upload planned as parts
12
13
  * - `downloads`: GET /:id — download data (dataset BEAST2 or package zip)
13
14
  *
14
15
  * These are unauthenticated — the UUID in the URL is the sole capability.
@@ -1 +1 @@
1
- {"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../../src/routes/data.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAK5B,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,kBAAkB,CAAC;AAmB1B;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,cAAc,EACvB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM;;;EAoGtC"}
1
+ {"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../../src/routes/data.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAS5B,OAAO,EAKL,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,kBAAkB,CAAC;AAmB1B;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,cAAc,EACvB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM;;;EAoLtC"}
@@ -3,11 +3,15 @@
3
3
  * Licensed under BSL 1.1. See LICENSE for details.
4
4
  */
5
5
  import { Hono } from 'hono';
6
- import { mkdir, writeFile, readFile, unlink } from 'node:fs/promises';
6
+ import { mkdir, open, writeFile, readFile, unlink } from 'node:fs/promises';
7
+ import { createWriteStream } from 'node:fs';
8
+ import { Readable, Transform } from 'node:stream';
9
+ import { pipeline } from 'node:stream/promises';
7
10
  import { join } from 'node:path';
8
11
  import { tmpdir } from 'node:os';
9
12
  import { variant } from '@elaraai/east';
10
- import { BEAST2_CONTENT_TYPE, ObjectNotFoundError, } from '@elaraai/e3-core';
13
+ import { transferPartCount, transferPartRange } from '@elaraai/e3-types';
14
+ import { BEAST2_CONTENT_TYPE, ObjectNotFoundError, transferStagingDir, transferStagingPath, } from '@elaraai/e3-core';
11
15
  const STAGING_DIR = join(tmpdir(), 'e3-transfers');
12
16
  /**
13
17
  * Reject requests that carry an Authorization header.
@@ -27,7 +31,8 @@ function rejectAuthHeader(c) {
27
31
  * Create generic upload/download data endpoints.
28
32
  *
29
33
  * Returns two Hono apps:
30
- * - `uploads`: PUT /:id — upload data (dataset BEAST2 or package zip)
34
+ * - `uploads`: PUT /:id — upload data (dataset BEAST2 or package zip);
35
+ * PUT /:id/parts/:part — one part of a dataset upload planned as parts
31
36
  * - `downloads`: GET /:id — download data (dataset BEAST2 or package zip)
32
37
  *
33
38
  * These are unauthenticated — the UUID in the URL is the sole capability.
@@ -45,10 +50,28 @@ export function createDataEndpoints(transferBackend, storage, getRepoPath) {
45
50
  // Try dataset upload first
46
51
  const dsRecord = await transferBackend.datasetUpload.get(id);
47
52
  if (dsRecord) {
48
- const stagingPath = join(STAGING_DIR, `${id}.beast2.partial`);
49
- const body = new Uint8Array(await c.req.arrayBuffer());
50
- await mkdir(STAGING_DIR, { recursive: true });
51
- await writeFile(stagingPath, body);
53
+ // Streamed to the repo's own staging area: a delivery can be far larger
54
+ // than this process's heap, and the commit turns the staged file into an
55
+ // object by link or rename — which only works on the repo's device.
56
+ const repoPath = getRepoPath(dsRecord.repo);
57
+ const stagingPath = transferStagingPath(repoPath, id);
58
+ await mkdir(transferStagingDir(repoPath), { recursive: true });
59
+ const stream = c.req.raw.body;
60
+ try {
61
+ if (stream) {
62
+ await pipeline(Readable.fromWeb(stream), createWriteStream(stagingPath));
63
+ }
64
+ else {
65
+ await writeFile(stagingPath, new Uint8Array(await c.req.arrayBuffer()));
66
+ }
67
+ }
68
+ catch (err) {
69
+ // A broken upload leaves no partial file behind; gc sweeps the staging
70
+ // directory only as the backstop for a server that crashed mid-upload.
71
+ // The transfer record stays, so the client may retry the PUT.
72
+ await unlink(stagingPath).catch(() => { });
73
+ throw err;
74
+ }
52
75
  return new Response(null, { status: 200 });
53
76
  }
54
77
  // Try package import — stage file for later processing by trigger endpoint
@@ -67,6 +90,62 @@ export function createDataEndpoints(transferBackend, storage, getRepoPath) {
67
90
  }
68
91
  return new Response('Not found', { status: 404 });
69
92
  });
93
+ // PUT /api/uploads/:id/parts/:part — One part of a dataset upload planned as
94
+ // parts (protocol 2)
95
+ uploads.put('/:id/parts/:part', async (c) => {
96
+ const rejected = rejectAuthHeader(c);
97
+ if (rejected)
98
+ return rejected;
99
+ const id = c.req.param('id');
100
+ const record = await transferBackend.datasetUpload.get(id);
101
+ const partBytes = record ? await transferBackend.datasetUpload.getPartBytes(id) : null;
102
+ if (!record || partBytes === null) {
103
+ return new Response('Not found', { status: 404 });
104
+ }
105
+ const part = Number(c.req.param('part'));
106
+ const range = transferPartRange(record.size, partBytes, part);
107
+ if (!range) {
108
+ return new Response(`No part ${c.req.param('part')}: the upload has ${transferPartCount(record.size, partBytes)} parts`, { status: 404 });
109
+ }
110
+ // Every part streams to its own offset in the one staged file — in any
111
+ // order, concurrently, and a re-sent part over the old one — so the commit
112
+ // adopts the file exactly as it does a single PUT's, with nothing to
113
+ // assemble. Opening with 'a' creates the file without truncating the parts
114
+ // already there; the write itself needs a positioned handle.
115
+ const repoPath = getRepoPath(record.repo);
116
+ const stagingPath = transferStagingPath(repoPath, id);
117
+ await mkdir(transferStagingDir(repoPath), { recursive: true });
118
+ await (await open(stagingPath, 'a')).close();
119
+ // A part longer than its range would overwrite the next part, so the
120
+ // stream stops at the range's end instead of trusting Content-Length.
121
+ const expected = range.end - range.start;
122
+ let received = 0;
123
+ const bounded = new Transform({
124
+ transform(chunk, _encoding, callback) {
125
+ received += chunk.length;
126
+ if (received > expected) {
127
+ callback(new Error(`part ${part} is ${expected} bytes, and more were sent`));
128
+ }
129
+ else {
130
+ callback(null, chunk);
131
+ }
132
+ },
133
+ });
134
+ const body = c.req.raw.body;
135
+ try {
136
+ await pipeline(body ? Readable.fromWeb(body) : Readable.from([]), bounded, createWriteStream(stagingPath, { flags: 'r+', start: range.start }));
137
+ }
138
+ catch (err) {
139
+ if (received > expected) {
140
+ return new Response(`part ${part} is ${expected} bytes, and more were sent`, { status: 400 });
141
+ }
142
+ throw err;
143
+ }
144
+ if (received !== expected) {
145
+ return new Response(`part ${part} is ${expected} bytes, got ${received}`, { status: 400 });
146
+ }
147
+ return new Response(null, { status: 200 });
148
+ });
70
149
  // GET /api/downloads/:id — Download data (dataset BEAST2 or package zip)
71
150
  downloads.get('/:id', async (c) => {
72
151
  const rejected = rejectAuthHeader(c);
@@ -1 +1 @@
1
- {"version":3,"file":"data.js","sourceRoot":"","sources":["../../../src/routes/data.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"data.js","sourceRoot":"","sources":["../../../src/routes/data.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,GAGpB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,cAAc,CAAC,CAAC;AAEnD;;;;;;;GAOG;AACH,SAAS,gBAAgB,CAAC,CAAwD;IAChF,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC;QAClC,OAAO,IAAI,QAAQ,CAAC,yDAAyD,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAClG,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CACjC,eAAgC,EAChC,OAAuB,EACvB,WAAqC;IAErC,MAAM,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC;IAC3B,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;IAE7B,qEAAqE;IACrE,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC9B,MAAM,QAAQ,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC;QACrC,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAE9B,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAE,CAAC;QAE9B,2BAA2B;QAC3B,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC7D,IAAI,QAAQ,EAAE,CAAC;YACb,wEAAwE;YACxE,yEAAyE;YACzE,oEAAoE;YACpE,MAAM,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAC5C,MAAM,WAAW,GAAG,mBAAmB,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YACtD,MAAM,KAAK,CAAC,kBAAkB,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/D,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC;YAC9B,IAAI,CAAC;gBACH,IAAI,MAAM,EAAE,CAAC;oBACX,MAAM,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAgD,CAAC,EAAE,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;gBACrH,CAAC;qBAAM,CAAC;oBACN,MAAM,SAAS,CAAC,WAAW,EAAE,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;gBAC1E,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,uEAAuE;gBACvE,uEAAuE;gBACvE,8DAA8D;gBAC9D,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;gBAC1C,MAAM,GAAG,CAAC;YACZ,CAAC;YACD,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAC7C,CAAC;QAED,2EAA2E;QAC3E,MAAM,SAAS,GAAG,MAAM,eAAe,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC9D,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;YACvD,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;gBAC/C,MAAM,eAAe,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAC/C,OAAO,IAAI,QAAQ,CACjB,2BAA2B,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC,UAAU,EAAE,EACnE,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAC;YACJ,CAAC;YAED,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;YAC3D,MAAM,KAAK,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9C,MAAM,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;YACnC,MAAM,eAAe,CAAC,aAAa,CAAC,YAAY,CAAC,EAAE,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC;YAEhF,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAC7C,CAAC;QAED,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,qBAAqB;IACrB,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC1C,MAAM,QAAQ,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC;QACrC,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAE9B,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC3D,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,eAAe,CAAC,aAAa,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACvF,IAAI,CAAC,MAAM,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YAClC,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QACpD,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,iBAAiB,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;QAC9D,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,QAAQ,CACjB,WAAW,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,oBAAoB,iBAAiB,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,QAAQ,EACnG,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAC;QACJ,CAAC;QAED,uEAAuE;QACvE,2EAA2E;QAC3E,qEAAqE;QACrE,2EAA2E;QAC3E,6DAA6D;QAC7D,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,WAAW,GAAG,mBAAmB,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACtD,MAAM,KAAK,CAAC,kBAAkB,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/D,MAAM,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;QAE7C,qEAAqE;QACrE,sEAAsE;QACtE,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC;QACzC,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,MAAM,OAAO,GAAG,IAAI,SAAS,CAAC;YAC5B,SAAS,CAAC,KAAa,EAAE,SAAS,EAAE,QAAQ;gBAC1C,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC;gBACzB,IAAI,QAAQ,GAAG,QAAQ,EAAE,CAAC;oBACxB,QAAQ,CAAC,IAAI,KAAK,CAAC,QAAQ,IAAI,OAAO,QAAQ,4BAA4B,CAAC,CAAC,CAAC;gBAC/E,CAAC;qBAAM,CAAC;oBACN,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;gBACxB,CAAC;YACH,CAAC;SACF,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,QAAQ,CACZ,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAA8C,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,EAC3F,OAAO,EACP,iBAAiB,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CACpE,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,QAAQ,GAAG,QAAQ,EAAE,CAAC;gBACxB,OAAO,IAAI,QAAQ,CAAC,QAAQ,IAAI,OAAO,QAAQ,4BAA4B,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YAChG,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC1B,OAAO,IAAI,QAAQ,CAAC,QAAQ,IAAI,OAAO,QAAQ,eAAe,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAC7F,CAAC;QACD,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,yEAAyE;IACzE,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAChC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC;QACrC,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAE9B,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAE,CAAC;QAE9B,uBAAuB;QACvB,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC/D,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAC5C,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACjE,MAAM,eAAe,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACjD,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;oBACxB,OAAO,EAAE;wBACP,cAAc,EAAE,mBAAmB;wBACnC,gBAAgB,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;wBACzC,kBAAkB,EAAE,QAAQ,CAAC,IAAI;qBAClC;iBACF,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,eAAe,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACjD,IAAI,GAAG,YAAY,mBAAmB,EAAE,CAAC;oBACvC,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;gBACpD,CAAC;gBACD,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC;QAED,qBAAqB;QACrB,MAAM,SAAS,GAAG,MAAM,eAAe,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC9D,IAAI,SAAS,IAAI,SAAS,CAAC,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YACvD,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;YACnD,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,WAAW,CAAC,CAAC;gBAC7C,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;gBAC1C,MAAM,eAAe,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAE/C,OAAO,IAAI,QAAQ,CAAC,QAAQ,EAAE;oBAC5B,MAAM,EAAE,GAAG;oBACX,OAAO,EAAE;wBACP,cAAc,EAAE,iBAAiB;wBACjC,gBAAgB,EAAE,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;qBAC9C;iBACF,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YACpD,CAAC;QACH,CAAC;QAED,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AAChC,CAAC"}
@@ -9,7 +9,7 @@ export { createDatasetRoutes } from './datasets.js';
9
9
  export { createTaskRoutes } from './tasks.js';
10
10
  export { createExecutionRoutes } from './executions.js';
11
11
  export { createObjectRoutes } from './objects.js';
12
- export { createTransferRoutes } from './transfer.js';
12
+ export { createTransferRoutes, type TransferRouteOptions } from './transfer.js';
13
13
  export { createPackageTransferRoutes } from './package-transfer.js';
14
14
  export { createDataEndpoints } from './data.js';
15
15
  export { createPackageFunctionRoutes, createWorkspaceFunctionRoutes, createOneShotRoutes, type GetRunner } from './functions.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,2BAA2B,EAAE,6BAA6B,EAAE,mBAAmB,EAAE,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACjI,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,KAAK,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,2BAA2B,EAAE,6BAA6B,EAAE,mBAAmB,EAAE,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACjI,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/routes/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,2BAA2B,EAAE,6BAA6B,EAAE,mBAAmB,EAAkB,MAAM,gBAAgB,CAAC;AACjI,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/routes/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAA6B,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EAAE,2BAA2B,EAAE,6BAA6B,EAAE,mBAAmB,EAAkB,MAAM,gBAAgB,CAAC;AACjI,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC"}
@@ -4,16 +4,33 @@
4
4
  */
5
5
  import { Hono } from 'hono';
6
6
  import { type StorageBackend, type TransferBackend } from '@elaraai/e3-core';
7
+ /** Options for {@link createTransferRoutes}. */
8
+ export interface TransferRouteOptions {
9
+ /**
10
+ * How long a protocol-2 commit waits for the staged bytes to be verified
11
+ * before it answers `processing` for the client to poll (default 5000 ms; 0
12
+ * answers `processing` at once). A protocol-1 commit always waits.
13
+ */
14
+ commitWaitMs?: number;
15
+ }
7
16
  /**
8
17
  * Create dataset transfer routes.
9
18
  *
10
- * Returns an `api` Hono app with authenticated routes (init upload, commit)
11
- * mounted at /api/repos/:repo/workspaces/:ws/datasets.
19
+ * Returns an `api` Hono app with authenticated routes (init upload, part
20
+ * targets, commit, commit poll) mounted at /api/repos/:repo/workspaces/:ws/datasets.
12
21
  *
13
22
  * Unauthenticated data routes (upload/download bytes) are handled by the
14
23
  * generic data endpoints in `data.ts`.
24
+ *
25
+ * @remarks
26
+ * A protocol-2 client (`?protocol=2` on the init and the commit) is sent its
27
+ * bytes' plan as parts, and its commit runs in the background: the request
28
+ * waits up to `commitWaitMs` for it and otherwise answers `processing`, which
29
+ * the client polls — so verifying a delivery of many gigabytes never holds one
30
+ * request open for as long as its SHA-256 takes. A protocol-1 client gets the
31
+ * single upload URL and a commit that answers only when it is done.
15
32
  */
16
- export declare function createTransferRoutes(storage: StorageBackend, getRepoPath: (repo: string) => string, transferBackend: TransferBackend): {
33
+ export declare function createTransferRoutes(storage: StorageBackend, getRepoPath: (repo: string) => string, transferBackend: TransferBackend, options?: TransferRouteOptions): {
17
34
  api: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
18
35
  };
19
36
  //# sourceMappingURL=transfer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"transfer.d.ts","sourceRoot":"","sources":["../../../src/routes/transfer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAQ5B,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,kBAAkB,CAAC;AAM1B;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,cAAc,EACvB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EACrC,eAAe,EAAE,eAAe;;EAuIjC"}
1
+ {"version":3,"file":"transfer.d.ts","sourceRoot":"","sources":["../../../src/routes/transfer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAM5B,OAAO,EAOL,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,kBAAkB,CAAC;AAW1B,gDAAgD;AAChD,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAqDD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,cAAc,EACvB,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EACrC,eAAe,EAAE,eAAe,EAChC,OAAO,GAAE,oBAAyB;;EA0OnC"}
@@ -3,27 +3,66 @@
3
3
  * Licensed under BSL 1.1. See LICENSE for details.
4
4
  */
5
5
  import { Hono } from 'hono';
6
- import { mkdir, readFile, unlink } from 'node:fs/promises';
7
- import { join } from 'node:path';
8
- import { tmpdir } from 'node:os';
6
+ import { mkdir, stat, unlink } from 'node:fs/promises';
9
7
  import { randomUUID } from 'node:crypto';
10
- import { variant, NullType, beast2HasIndex, isVariant, toEastTypeValue } from '@elaraai/east';
11
- import { urlPathToTreePath } from '@elaraai/e3-types';
12
- import { computeHash, workspaceGetDatasetStatus, workspaceSetDatasetByHash, } from '@elaraai/e3-core';
8
+ import { variant } from '@elaraai/east';
9
+ import { transferPartCount, urlPathToTreePath } from '@elaraai/e3-types';
10
+ import { DatasetTypeMismatchError, datasetAdoptFile, datasetAdoptObject, transferStagingDir, transferStagingPath, } from '@elaraai/e3-core';
13
11
  import { decodeBody, sendSuccess, sendError } from '../beast2.js';
14
- import { TransferUploadRequestType, TransferUploadResponseType, TransferDoneResponseType } from '../types.js';
15
- const STAGING_DIR = join(tmpdir(), 'e3-transfers');
12
+ import { TransferUploadRequestType, TransferUploadResponseType, TransferPartResponseType, TransferDoneResponseType, } from '../types.js';
13
+ const DEFAULT_COMMIT_WAIT_MS = 5_000;
14
+ /** How long a finished commit's answer stays pollable. */
15
+ const COMMIT_RESULT_TTL_MS = 10 * 60 * 1000;
16
+ /** The transfer protocol version a request speaks: `?protocol=N`, else 1. */
17
+ function requestProtocol(c) {
18
+ const version = Number(c.req.query('protocol'));
19
+ return Number.isInteger(version) && version >= 1 ? version : 1;
20
+ }
21
+ /** A backend's URL as the client can use it: a relative one resolves against the request's origin. */
22
+ function resolveUrl(c, url) {
23
+ return url.startsWith('/') ? `${new URL(c.req.url).origin}${url}` : url;
24
+ }
25
+ /** The promise's value if it settles within `ms`, else `null` — at once when `ms` is 0. */
26
+ async function within(promise, ms) {
27
+ if (ms <= 0)
28
+ return null;
29
+ let timer;
30
+ const timeout = new Promise((resolve) => {
31
+ timer = setTimeout(() => resolve(null), ms);
32
+ });
33
+ try {
34
+ return await Promise.race([promise, timeout]);
35
+ }
36
+ finally {
37
+ clearTimeout(timer);
38
+ }
39
+ }
40
+ function sendOutcome(outcome) {
41
+ return outcome.type === 'answer'
42
+ ? sendSuccess(TransferDoneResponseType, outcome.value)
43
+ : sendError(TransferDoneResponseType, outcome.error);
44
+ }
16
45
  /**
17
46
  * Create dataset transfer routes.
18
47
  *
19
- * Returns an `api` Hono app with authenticated routes (init upload, commit)
20
- * mounted at /api/repos/:repo/workspaces/:ws/datasets.
48
+ * Returns an `api` Hono app with authenticated routes (init upload, part
49
+ * targets, commit, commit poll) mounted at /api/repos/:repo/workspaces/:ws/datasets.
21
50
  *
22
51
  * Unauthenticated data routes (upload/download bytes) are handled by the
23
52
  * generic data endpoints in `data.ts`.
53
+ *
54
+ * @remarks
55
+ * A protocol-2 client (`?protocol=2` on the init and the commit) is sent its
56
+ * bytes' plan as parts, and its commit runs in the background: the request
57
+ * waits up to `commitWaitMs` for it and otherwise answers `processing`, which
58
+ * the client polls — so verifying a delivery of many gigabytes never holds one
59
+ * request open for as long as its SHA-256 takes. A protocol-1 client gets the
60
+ * single upload URL and a commit that answers only when it is done.
24
61
  */
25
- export function createTransferRoutes(storage, getRepoPath, transferBackend) {
62
+ export function createTransferRoutes(storage, getRepoPath, transferBackend, options = {}) {
26
63
  const api = new Hono();
64
+ const commitWaitMs = options.commitWaitMs ?? DEFAULT_COMMIT_WAIT_MS;
65
+ const commits = new Map();
27
66
  /**
28
67
  * Extract dataset path from the request URL wildcard.
29
68
  * The route is mounted at /api/repos/:repo/workspaces/:ws/datasets
@@ -45,6 +84,17 @@ export function createTransferRoutes(storage, getRepoPath, transferBackend) {
45
84
  }
46
85
  return pathStr;
47
86
  }
87
+ /** Whether an upload was created for the repository, workspace and dataset this request addresses. */
88
+ function addresses(c, transfer, suffix) {
89
+ return transfer.repo === c.req.param('repo')
90
+ && transfer.workspace === c.req.param('ws')
91
+ && transfer.path === extractDatasetPath(c, suffix);
92
+ }
93
+ /** The upload `id`, if this request addresses it. */
94
+ async function uploadAt(c, id, suffix) {
95
+ const transfer = await transferBackend.datasetUpload.get(id);
96
+ return transfer && addresses(c, transfer, suffix) ? transfer : null;
97
+ }
48
98
  // =========================================================================
49
99
  // Authenticated API routes (mounted at /api/repos/:repo/workspaces/:ws/datasets)
50
100
  // =========================================================================
@@ -56,7 +106,7 @@ export function createTransferRoutes(storage, getRepoPath, transferBackend) {
56
106
  // Check for commit: .../upload/<uuid>
57
107
  const commitMatch = fullPath.match(/\/upload\/([0-9a-f-]{36})$/);
58
108
  if (commitMatch) {
59
- return handleCommit(c, commitMatch[1]);
109
+ return handleCommit(c, commitMatch[1], commitMatch[0]);
60
110
  }
61
111
  // Check for init: .../upload
62
112
  if (fullPath.endsWith('/upload')) {
@@ -65,70 +115,157 @@ export function createTransferRoutes(storage, getRepoPath, transferBackend) {
65
115
  // Not a transfer route — return 404
66
116
  return new Response('Not found', { status: 404 });
67
117
  });
118
+ // GET routes dispatch the same way, and leave every other read to the
119
+ // dataset routes mounted after these:
120
+ // - .../datasets/<path>/upload/<id>/parts/<n> → where to send part n
121
+ // - .../datasets/<path>/upload/<id> → poll a commit
122
+ api.get('/*', async (c, next) => {
123
+ const fullPath = c.req.path;
124
+ const partMatch = fullPath.match(/\/upload\/([0-9a-f-]{36})\/parts\/([0-9]+)$/);
125
+ if (partMatch) {
126
+ return handlePart(c, partMatch[1], Number(partMatch[2]), partMatch[0]);
127
+ }
128
+ const pollMatch = fullPath.match(/\/upload\/([0-9a-f-]{36})$/);
129
+ if (pollMatch) {
130
+ return handlePoll(c, pollMatch[1], pollMatch[0]);
131
+ }
132
+ await next();
133
+ });
68
134
  async function handleInit(c) {
69
135
  const repo = c.req.param('repo');
70
136
  const ws = c.req.param('ws');
71
137
  const repoPath = getRepoPath(repo);
72
138
  const pathStr = extractDatasetPath(c, '/upload');
73
139
  const { hash, size } = await decodeBody(c, TransferUploadRequestType);
74
- // Dedup check — object already verified when originally stored
140
+ // Dedup — the bytes are already an object, so no upload is needed. The
141
+ // object is not necessarily THIS dataset's type though (it may have been
142
+ // stored for another one), so the pairing is checked here: it is the only
143
+ // door that skips the commit.
75
144
  if (await storage.objects.exists(repoPath, hash)) {
76
145
  const treePath = urlPathToTreePath(pathStr);
77
- await workspaceSetDatasetByHash(storage, repoPath, ws, treePath, hash, new Map());
146
+ await datasetAdoptObject(storage, repoPath, ws, treePath, hash);
78
147
  return sendSuccess(TransferUploadResponseType, variant('completed', null));
79
148
  }
80
149
  // Create transfer record in backend
81
150
  const transferId = randomUUID();
82
- await transferBackend.datasetUpload.create(transferId, { repo, workspace: ws, path: pathStr, hash, size });
83
- // Create staging slot in OS temp dir
84
- await mkdir(STAGING_DIR, { recursive: true });
151
+ const transfer = { repo, workspace: ws, path: pathStr, hash, size };
152
+ await transferBackend.datasetUpload.create(transferId, transfer);
153
+ // Create the staging slot under the repo, so the commit's adopt is a
154
+ // same-device link or rename rather than a whole-file copy.
155
+ await mkdir(transferStagingDir(repoPath), { recursive: true });
156
+ if (requestProtocol(c) >= 2) {
157
+ const partBytes = await transferBackend.datasetUpload.createParts(transferId, transfer);
158
+ return sendSuccess(TransferUploadResponseType, variant('upload_parts', { id: transferId, partBytes }));
159
+ }
85
160
  const uploadUrl = await transferBackend.datasetUpload.getUploadUrl(transferId, repo, hash);
86
- // Resolve relative URL against the request origin
87
- const origin = new URL(c.req.url).origin;
88
- const resolvedUrl = uploadUrl.startsWith('/') ? `${origin}${uploadUrl}` : uploadUrl;
89
- return sendSuccess(TransferUploadResponseType, variant('upload', { id: transferId, uploadUrl: resolvedUrl }));
161
+ return sendSuccess(TransferUploadResponseType, variant('upload', { id: transferId, uploadUrl: resolveUrl(c, uploadUrl) }));
90
162
  }
91
- async function handleCommit(c, id) {
92
- const transfer = await transferBackend.datasetUpload.get(id);
163
+ async function handlePart(c, id, part, suffix) {
164
+ const transfer = await uploadAt(c, id, suffix);
93
165
  if (!transfer) {
94
- return sendError(NullType, variant('internal', { message: 'transfer not found' }));
166
+ return sendError(TransferPartResponseType, variant('internal', { message: 'transfer not found' }));
95
167
  }
96
- const repoPath = getRepoPath(transfer.repo);
97
- const stagingPath = join(STAGING_DIR, `${id}.beast2.partial`);
98
- try {
99
- // Read from staging to verify size and hash
100
- const data = await readFile(stagingPath);
101
- if (BigInt(data.byteLength) !== transfer.size) {
102
- await unlink(stagingPath).catch(() => { });
103
- return sendSuccess(TransferDoneResponseType, variant('error', { message: `size mismatch: expected ${transfer.size}, got ${data.byteLength}` }));
168
+ const partBytes = await transferBackend.datasetUpload.getPartBytes(id);
169
+ if (partBytes === null) {
170
+ return sendError(TransferPartResponseType, variant('internal', { message: 'transfer was not planned as parts' }));
171
+ }
172
+ const count = transferPartCount(transfer.size, partBytes);
173
+ if (part < 1 || part > count) {
174
+ return sendError(TransferPartResponseType, variant('internal', {
175
+ message: `no part ${part}: the upload has ${count} part${count === 1 ? '' : 's'}`,
176
+ }));
177
+ }
178
+ const target = await transferBackend.datasetUpload.getPartUpload(id, transfer, part);
179
+ return sendSuccess(TransferPartResponseType, {
180
+ url: resolveUrl(c, target.url),
181
+ headers: new Map(Object.entries(target.headers)),
182
+ });
183
+ }
184
+ async function handleCommit(c, id, suffix) {
185
+ let commit = commits.get(id);
186
+ if (!commit) {
187
+ const transfer = await uploadAt(c, id, suffix);
188
+ if (!transfer) {
189
+ return sendError(TransferDoneResponseType, variant('internal', { message: 'transfer not found' }));
104
190
  }
105
- const actualHash = computeHash(data);
106
- if (actualHash !== transfer.hash) {
107
- await unlink(stagingPath).catch(() => { });
108
- return sendSuccess(TransferDoneResponseType, variant('error', { message: `hash mismatch: expected ${transfer.hash}, got ${actualHash}` }));
191
+ // A retried commit may have started it while this one looked it up.
192
+ commit = commits.get(id) ?? startCommit(id, transfer);
193
+ }
194
+ if (!addresses(c, commit.transfer, suffix)) {
195
+ return sendError(TransferDoneResponseType, variant('internal', { message: 'transfer not found' }));
196
+ }
197
+ const outcome = requestProtocol(c) >= 2
198
+ ? commit.outcome ?? await within(commit.settled, commitWaitMs)
199
+ : await commit.settled;
200
+ return outcome ? sendOutcome(outcome) : sendSuccess(TransferDoneResponseType, variant('processing', null));
201
+ }
202
+ async function handlePoll(c, id, suffix) {
203
+ const commit = commits.get(id);
204
+ if (commit && addresses(c, commit.transfer, suffix)) {
205
+ return commit.outcome
206
+ ? sendOutcome(commit.outcome)
207
+ : sendSuccess(TransferDoneResponseType, variant('processing', null));
208
+ }
209
+ const transfer = await uploadAt(c, id, suffix);
210
+ return sendError(TransferDoneResponseType, variant('internal', {
211
+ message: transfer ? 'transfer not committed' : 'transfer not found',
212
+ }));
213
+ }
214
+ function startCommit(id, transfer) {
215
+ const settled = verifyAndAdopt(id, transfer).then((outcome) => {
216
+ commit.outcome = outcome;
217
+ // The answer stays pollable for a while — a client whose response was
218
+ // lost asks again — and then goes, as the upload record already has.
219
+ setTimeout(() => commits.delete(id), COMMIT_RESULT_TTL_MS).unref();
220
+ return outcome;
221
+ });
222
+ const commit = { transfer, outcome: null, settled };
223
+ commits.set(id, commit);
224
+ return commit;
225
+ }
226
+ /**
227
+ * Verify a staged upload and point its dataset at it; never rejects.
228
+ */
229
+ async function verifyAndAdopt(id, transfer) {
230
+ // Nothing may escape: a commit answering `processing` is awaited by no one.
231
+ let stagingPath = null;
232
+ try {
233
+ const repoPath = getRepoPath(transfer.repo);
234
+ stagingPath = transferStagingPath(repoPath, id);
235
+ // The staged file is never read whole: its size comes from `stat`, its
236
+ // digest from a streamed hash, its declared type and paging index from
237
+ // two ranged reads, and it becomes an object by link or rename. A
238
+ // multi-gigabyte delivery therefore commits for the cost of its SHA-256.
239
+ const stats = await stat(stagingPath);
240
+ if (BigInt(stats.size) !== transfer.size) {
241
+ return {
242
+ type: 'answer',
243
+ value: variant('error', { message: `size mismatch: expected ${transfer.size}, got ${stats.size}` }),
244
+ };
109
245
  }
110
- // The transfer path stores the client's bytes verbatim — the one place
111
- // un-indexed bytes could enter at rest. Collection datasets must be
112
- // segmented + indexed (the uniform at-rest contract), so validate here.
113
246
  const treePath = urlPathToTreePath(transfer.path);
114
- const status = await workspaceGetDatasetStatus(storage, repoPath, transfer.workspace, treePath);
115
- const typeValue = isVariant(status.datasetType)
116
- ? status.datasetType
117
- : toEastTypeValue(status.datasetType);
118
- const collection = typeValue.type === 'Array' || typeValue.type === 'Set' || typeValue.type === 'Dict';
119
- if (collection && !beast2HasIndex(data)) {
120
- await unlink(stagingPath).catch(() => { });
121
- return sendSuccess(TransferDoneResponseType, variant('error', { message: 'collection dataset blob carries no paging index — encode it with encodeBeast2PagedFor (collection datasets are stored segmented + indexed)' }));
247
+ await datasetAdoptFile(storage, repoPath, transfer.workspace, treePath, stagingPath, {
248
+ expectHash: transfer.hash,
249
+ });
250
+ return { type: 'answer', value: variant('completed', null) };
251
+ }
252
+ catch (err) {
253
+ if (err instanceof DatasetTypeMismatchError) {
254
+ return {
255
+ type: 'refused',
256
+ error: variant('dataset_type_mismatch', {
257
+ workspace: transfer.workspace,
258
+ path: err.path,
259
+ message: err.message,
260
+ }),
261
+ };
122
262
  }
123
- // Write through storage abstraction (re-hashes internally, that's fine)
124
- await storage.objects.write(repoPath, data);
125
- await unlink(stagingPath).catch(() => { });
126
- // Update dataset ref
127
- await workspaceSetDatasetByHash(storage, repoPath, transfer.workspace, treePath, actualHash, new Map());
128
- return sendSuccess(TransferDoneResponseType, variant('completed', null));
263
+ return { type: 'answer', value: variant('error', { message: err instanceof Error ? err.message : String(err) }) };
129
264
  }
130
265
  finally {
131
- await transferBackend.datasetUpload.delete(id);
266
+ if (stagingPath !== null)
267
+ await unlink(stagingPath).catch(() => { });
268
+ await transferBackend.datasetUpload.delete(id).catch(() => { });
132
269
  }
133
270
  }
134
271
  return { api };