@pipelex/sdk 0.49.0 → 0.50.0
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 +20 -12
- package/dist/cli/script.d.ts +1 -1
- package/dist/client.d.ts +253 -14
- package/dist/client.js +484 -21
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +9 -6
- package/dist/errors.js +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/product-models.d.ts +458 -20
- package/dist/upload-grant.d.ts +134 -40
- package/dist/upload-grant.js +328 -136
- package/dist/upload-grant.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/architecture.md +5 -5
- package/docs/errors.md +6 -2
- package/docs/files.md +173 -3
- package/docs/input-preparation.md +43 -19
- package/docs/method-examples.md +36 -3
- package/package.json +1 -1
package/dist/upload-grant.d.ts
CHANGED
|
@@ -1,21 +1,93 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `uploadWithGrant` —
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* `describeUpload` and `uploadWithGrant` — the two halves of an upload grant
|
|
3
|
+
* (`PipelexApiClient.requestUploadGrant`, `POST /v1/upload/grant`) that the side
|
|
4
|
+
* holding the bytes runs, typically a browser page. That side describes the file,
|
|
5
|
+
* its SHA-256 included, with `describeUpload`; the side that holds the credential
|
|
6
|
+
* requests the grant for that description; and the bytes go to storage with
|
|
7
|
+
* `uploadWithGrant`, unless the grant says the organization already holds them. See
|
|
6
8
|
* `docs/input-preparation.md`.
|
|
7
9
|
*
|
|
8
10
|
* **This module is the browser-safe entry, `@pipelex/sdk/upload`.** Its runtime
|
|
9
11
|
* import graph must never reach a Node builtin, `undici` or the client: a page
|
|
10
12
|
* bundles it as is, with nothing marked external. It imports the error classes,
|
|
11
|
-
* the timer bound in `timers.ts` and the wire types only,
|
|
12
|
-
* `tests/upload-grant.test.ts` bundles it for the browser with esbuild to hold
|
|
13
|
-
* to that.
|
|
13
|
+
* the timer bound in `timers.ts` and the wire types only, hashes with Web Crypto,
|
|
14
|
+
* and `tests/upload-grant.test.ts` bundles it for the browser with esbuild to hold
|
|
15
|
+
* it to that.
|
|
14
16
|
*/
|
|
15
17
|
import type { UploadGrant } from "./product-models.js";
|
|
16
|
-
export type { UploadGrant, UploadGrantInput } from "./product-models.js";
|
|
18
|
+
export type { UploadGrant, UploadGrantInput, UploadKind } from "./product-models.js";
|
|
17
19
|
export { InputPreparationError, RejectedAssetError, UploadTransportError } from "./errors.js";
|
|
18
20
|
export type { RejectedAssetCode, UploadTransportCode } from "./errors.js";
|
|
21
|
+
/** Per-call options for {@link describeUpload}. */
|
|
22
|
+
export interface DescribeUploadOptions {
|
|
23
|
+
/** The file's name, replacing a `File`'s own; required for a nameless `Blob`. */
|
|
24
|
+
filename?: string;
|
|
25
|
+
/**
|
|
26
|
+
* The largest file to hash, in bytes, 52428800 (50 MiB, the platform's default limit on
|
|
27
|
+
* one upload) when omitted. Hashing reads the whole file into memory, so a larger file
|
|
28
|
+
* is described without its SHA-256 and never read. Where the deployment's limit is no
|
|
29
|
+
* larger, its grant request then meets the grant route's own `413`. On a deployment
|
|
30
|
+
* whose grants report a larger `max_bytes`, a file between the two is granted without
|
|
31
|
+
* a hash: it is uploaded under a fresh name, with no deduplication and no checksum for
|
|
32
|
+
* storage to hold the body to, and the description's absent `sha256` is how a caller
|
|
33
|
+
* sees it. Pass that `max_bytes` to hash up to it. It must be a positive number.
|
|
34
|
+
*/
|
|
35
|
+
maxBytes?: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A file described for `requestUploadGrant`, as {@link describeUpload} reads it: every
|
|
39
|
+
* field the grant request takes to describe a file, ready to send as is.
|
|
40
|
+
*/
|
|
41
|
+
export interface UploadDescription {
|
|
42
|
+
/** The file's name: the `File`'s own, or the `filename` option for a nameless `Blob`. */
|
|
43
|
+
filename: string;
|
|
44
|
+
/** The type the runtime gives the file, `Blob.type`: empty when it knows none. */
|
|
45
|
+
content_type: string;
|
|
46
|
+
/** The file's exact size in bytes. */
|
|
47
|
+
size: number;
|
|
48
|
+
/**
|
|
49
|
+
* The SHA-256 of the file's bytes, 64 lowercase hexadecimal digits. Absent in two
|
|
50
|
+
* cases, where the grant then names a new object and asks for the bytes, as it does
|
|
51
|
+
* for every request with no hash: where the runtime offers no Web Crypto digest, which
|
|
52
|
+
* a browser withholds from a page served over plain `http` from any host but the local
|
|
53
|
+
* one, and for a file larger than `describeUpload`'s `maxBytes`, which is never read.
|
|
54
|
+
* The grant route refuses such a file's size with its own `413` only where the
|
|
55
|
+
* deployment's limit is no larger than `maxBytes`; under a larger limit the file is
|
|
56
|
+
* granted and uploaded without its hash, so without deduplication.
|
|
57
|
+
*/
|
|
58
|
+
sha256?: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Describe a file for `requestUploadGrant`: its name, its type, its size and the
|
|
62
|
+
* SHA-256 of its bytes, computed with Web Crypto. A grant asked for with the hash
|
|
63
|
+
* names the object after the bytes, so a file the organization already holds is
|
|
64
|
+
* answered `stored: true` and never sent again, and storage refuses a body whose
|
|
65
|
+
* bytes differ from the ones described.
|
|
66
|
+
*
|
|
67
|
+
* The side holding the bytes runs this, so a page describes the file and hands the
|
|
68
|
+
* description to the server that holds the key, which passes it to
|
|
69
|
+
* `requestUploadGrant` unchanged; the bytes never cross that server. A nameless
|
|
70
|
+
* `Blob` takes its name from `options.filename`, which also replaces a `File`'s own.
|
|
71
|
+
*
|
|
72
|
+
* The whole file is read into memory once to hash it, so only a file no larger than
|
|
73
|
+
* `options.maxBytes` is, 50 MiB by default: a larger one, such as a video of several
|
|
74
|
+
* gigabytes picked by mistake, would freeze or crash a browser tab before the grant
|
|
75
|
+
* route could refuse its size. It is described without `sha256` and never read. Where
|
|
76
|
+
* the deployment's limit is no larger than `maxBytes`, as with the default on a
|
|
77
|
+
* deployment at the platform's default limit, the grant route's `413` refuses it with its
|
|
78
|
+
* own message. On a deployment whose grants report a larger `max_bytes`, a file between
|
|
79
|
+
* the two is granted without the hash and uploaded under a fresh name, with no
|
|
80
|
+
* deduplication and no checksum for storage to hold the body to: the absent `sha256` on
|
|
81
|
+
* the description is how the caller sees it, and passing that `max_bytes` as `maxBytes`
|
|
82
|
+
* hashes up to it. Where the runtime offers no Web
|
|
83
|
+
* Crypto digest, the file is described without `sha256` too, and the upload goes on
|
|
84
|
+
* without the hash.
|
|
85
|
+
*
|
|
86
|
+
* A file that cannot be read, such as one changed on disk since it was picked, throws an
|
|
87
|
+
* `InputPreparationError` whose `cause` is the runtime's error; a file with no name and a
|
|
88
|
+
* `maxBytes` that is not a positive number throw one before anything is read.
|
|
89
|
+
*/
|
|
90
|
+
export declare function describeUpload(file: Blob, options?: DescribeUploadOptions): Promise<UploadDescription>;
|
|
19
91
|
/** Per-call options for {@link uploadWithGrant}. */
|
|
20
92
|
export interface UploadWithGrantOptions {
|
|
21
93
|
/**
|
|
@@ -33,56 +105,78 @@ export interface UploadWithGrantOptions {
|
|
|
33
105
|
*/
|
|
34
106
|
timeoutMs?: number;
|
|
35
107
|
}
|
|
36
|
-
/** What a successful {@link uploadWithGrant}
|
|
108
|
+
/** What a successful {@link uploadWithGrant} leaves in storage. */
|
|
37
109
|
export interface GrantedUpload {
|
|
38
|
-
/** The grant's `pipelex-storage://` reference, which now names the stored
|
|
110
|
+
/** The grant's `pipelex-storage://` reference, which now names the stored file. */
|
|
39
111
|
uri: string;
|
|
112
|
+
/**
|
|
113
|
+
* The file's record, the grant's `file_id`. Absent when the grant carried none, as from a
|
|
114
|
+
* deployment that predates file records.
|
|
115
|
+
*/
|
|
116
|
+
file_id?: string;
|
|
40
117
|
}
|
|
41
118
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
119
|
+
* Store a file with an upload grant, and return the reference it now carries.
|
|
120
|
+
*
|
|
121
|
+
* **A grant that says `stored: true` sends nothing**: the organization already holds
|
|
122
|
+
* these bytes, so the call returns the grant's `uri` and `file_id` at once, without
|
|
123
|
+
* reading the file or reaching storage. Otherwise the file goes to the grant's `url` as
|
|
124
|
+
* the raw body of a `PUT`, with the grant's signed headers unchanged; the body sets
|
|
125
|
+
* `Content-Length`, which the grant signed, so the file must be exactly the size the
|
|
126
|
+
* grant was requested for, and when the grant was requested with the file's SHA-256,
|
|
127
|
+
* storage holds the body to it.
|
|
128
|
+
*
|
|
129
|
+
* Two answers from storage are no failure. A `412` refuses the create-only `PUT`
|
|
130
|
+
* because the object already exists: when the grant names the file by its bytes (it
|
|
131
|
+
* signs `x-amz-checksum-sha256`), that object can only hold those bytes, stored
|
|
132
|
+
* meanwhile by another upload of them or by the platform's own copy of a run's file
|
|
133
|
+
* that holds them, so the call succeeds with the grant's `uri` and `file_id`, the answer
|
|
134
|
+
* a `stored: true` grant gives. A `409 ConditionalRequestConflict`
|
|
135
|
+
* says another write of the object was in flight, so the `PUT` is sent again after a
|
|
136
|
+
* short pause, a few times at most.
|
|
46
137
|
*
|
|
47
138
|
* Storage's refusals map onto the input-preparation errors, so a caller that
|
|
48
139
|
* already handles `uploadFile` needs no new case:
|
|
49
140
|
*
|
|
50
|
-
* - `RejectedAssetError` (`status` is storage's, `code` says why) — a `412`
|
|
51
|
-
* the grant was already used (`grant_used`:
|
|
52
|
-
* `403` when the file's size, type or metadata
|
|
53
|
-
* (`signature_mismatch`), when the request carried
|
|
54
|
-
* (`unsigned_header`), or when the grant has expired
|
|
55
|
-
* `4xx` but a `408` or a `429` (`store_refused`)
|
|
56
|
-
*
|
|
141
|
+
* - `RejectedAssetError` (`status` is storage's, `code` says why) — a `412` for a
|
|
142
|
+
* grant that names no bytes, when the grant was already used (`grant_used`: such a
|
|
143
|
+
* grant writes one object, once); a `403` when the file's size, type or metadata
|
|
144
|
+
* differ from what the grant signed (`signature_mismatch`), when the request carried
|
|
145
|
+
* a header the grant did not sign (`unsigned_header`), or when the grant has expired
|
|
146
|
+
* (`grant_expired`); any other `4xx` but a `408` or a `429` (`store_refused`), a
|
|
147
|
+
* `400 BadDigest` among them, for bytes other than the ones whose SHA-256 the grant
|
|
148
|
+
* was requested with. Each asks for a new grant, or for the file the grant was
|
|
149
|
+
* requested for.
|
|
57
150
|
* - `UploadTransportError` (`status` is storage's when it answered, `code` says
|
|
58
151
|
* which) — the time limit running out (`timeout`), storage unreachable
|
|
59
152
|
* (`unreachable`), a `5xx` (`server_error`), storage timing out on the body
|
|
60
|
-
* (`storage_timeout`, a `400 RequestTimeout`, which wrote nothing), another
|
|
61
|
-
*
|
|
153
|
+
* (`storage_timeout`, a `400 RequestTimeout`, which wrote nothing), another write of
|
|
154
|
+
* the object still in progress after every attempt (`conflict`, a `409
|
|
62
155
|
* ConditionalRequestConflict`), storage timing out or throttling the request
|
|
63
156
|
* (`unexpected`, a `408` or a `429`, refused for its timing rather than for the
|
|
64
157
|
* file), or a redirect, which is refused rather than followed (`redirected`). In a
|
|
65
158
|
* browser a refused cross-origin request looks like an unreachable host, so the
|
|
66
|
-
* message names that too. A grant whose `url` is not an
|
|
67
|
-
* of user info is refused the same way, before anything
|
|
68
|
-
* (`invalid_grant_url`).
|
|
159
|
+
* message names that too. A grant that is not stored and whose `url` is not an
|
|
160
|
+
* absolute `http(s)` URL free of user info is refused the same way, before anything
|
|
161
|
+
* is sent (`invalid_grant_url`).
|
|
69
162
|
*
|
|
70
|
-
* The whole exchange runs under a time limit
|
|
71
|
-
* 1 s for every started 128 KiB of the file. A
|
|
72
|
-
* and its abort propagates as the signal's own
|
|
73
|
-
* limit ran out too. Only the first 16 KiB of
|
|
74
|
-
* the limit runs out while that body is still
|
|
75
|
-
*
|
|
76
|
-
* body
|
|
163
|
+
* The whole exchange runs under a time limit, the pauses after a conflict included:
|
|
164
|
+
* `timeoutMs` when given, else 60 s plus 1 s for every started 128 KiB of the file. A
|
|
165
|
+
* caller's `signal` can end it sooner, and its abort propagates as the signal's own
|
|
166
|
+
* reason, unwrapped, even when the time limit ran out too. Only the first 16 KiB of
|
|
167
|
+
* storage's error body are read, and when the limit runs out while that body is still
|
|
168
|
+
* arriving, or during the pause after a conflict, storage has already answered: the
|
|
169
|
+
* call settles as that answer, classified from its status and whatever part of the body
|
|
170
|
+
* arrived.
|
|
77
171
|
*
|
|
78
172
|
* A timeout, a `5xx`, a conflict and a connection lost after the file went out leave
|
|
79
173
|
* it unknown whether the object was written: retrying with the same grant before it
|
|
80
|
-
* expires either stores it or
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
174
|
+
* expires either stores it or finds it stored, and then the grant's `uri` names the
|
|
175
|
+
* file. After a `408` or a `429` the same retry applies, later, since storage refused
|
|
176
|
+
* the request for its timing. A `501` is the exception: storage does not implement the
|
|
177
|
+
* request it was sent, stored nothing, and answers a retry the same way, so its error
|
|
178
|
+
* is not retryable. The grant is a bearer capability: nothing here logs it, and no
|
|
179
|
+
* error this throws carries its URL, storage's error body or a runtime error that
|
|
180
|
+
* could hold either.
|
|
87
181
|
*/
|
|
88
182
|
export declare function uploadWithGrant(grant: UploadGrant, file: Blob, options?: UploadWithGrantOptions): Promise<GrantedUpload>;
|