@dropthis/cli 0.5.0 → 0.6.1
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 +0 -1
- package/dist/cli.cjs +330 -346
- package/dist/cli.cjs.map +1 -1
- package/node_modules/@dropthis/node/README.md +88 -20
- package/node_modules/@dropthis/node/dist/{drops-fK63OCk6.d.cts → drops-BPdmUPCT.d.cts} +65 -56
- package/node_modules/@dropthis/node/dist/{drops-fK63OCk6.d.ts → drops-BPdmUPCT.d.ts} +65 -56
- package/node_modules/@dropthis/node/dist/edge.cjs +389 -168
- package/node_modules/@dropthis/node/dist/edge.cjs.map +1 -1
- package/node_modules/@dropthis/node/dist/edge.d.cts +4 -32
- package/node_modules/@dropthis/node/dist/edge.d.ts +4 -32
- package/node_modules/@dropthis/node/dist/edge.mjs +389 -168
- package/node_modules/@dropthis/node/dist/edge.mjs.map +1 -1
- package/node_modules/@dropthis/node/dist/index.cjs +449 -429
- package/node_modules/@dropthis/node/dist/index.cjs.map +1 -1
- package/node_modules/@dropthis/node/dist/index.d.cts +13 -39
- package/node_modules/@dropthis/node/dist/index.d.ts +13 -39
- package/node_modules/@dropthis/node/dist/index.mjs +448 -429
- package/node_modules/@dropthis/node/dist/index.mjs.map +1 -1
- package/node_modules/@dropthis/node/package.json +1 -1
- package/package.json +2 -2
|
@@ -11,33 +11,42 @@ function createErrorResult(code, message, statusCode, extra = {}) {
|
|
|
11
11
|
message: redactSecrets(message),
|
|
12
12
|
statusCode,
|
|
13
13
|
...extra.type ? { type: extra.type } : {},
|
|
14
|
+
...extra.title !== void 0 ? { title: extra.title } : {},
|
|
14
15
|
...extra.detail !== void 0 ? { detail: extra.detail } : {},
|
|
16
|
+
...extra.instance !== void 0 ? { instance: extra.instance } : {},
|
|
15
17
|
...extra.param !== void 0 ? { param: extra.param } : {},
|
|
16
18
|
...extra.currentRevision !== void 0 ? { currentRevision: extra.currentRevision } : {},
|
|
17
19
|
...extra.requestId !== void 0 ? { requestId: extra.requestId } : {},
|
|
18
20
|
...extra.suggestion !== void 0 ? { suggestion: extra.suggestion } : {},
|
|
19
|
-
...extra.retryable !== void 0 ? { retryable: extra.retryable } : {}
|
|
21
|
+
...extra.retryable !== void 0 ? { retryable: extra.retryable } : {},
|
|
22
|
+
...extra.body !== void 0 ? { body: extra.body } : {}
|
|
20
23
|
},
|
|
21
24
|
headers: extra.headers ?? {}
|
|
22
25
|
};
|
|
23
26
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
27
|
+
var PublishInputError = class extends Error {
|
|
28
|
+
constructor(code, message, param, suggestion) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.code = code;
|
|
31
|
+
this.param = param;
|
|
32
|
+
this.suggestion = suggestion;
|
|
33
|
+
this.name = "PublishInputError";
|
|
34
|
+
}
|
|
35
|
+
code;
|
|
36
|
+
param;
|
|
37
|
+
suggestion;
|
|
38
|
+
};
|
|
39
|
+
function toErrorResult(err) {
|
|
40
|
+
if (err instanceof PublishInputError)
|
|
41
|
+
return createErrorResult(err.code, err.message, null, {
|
|
42
|
+
param: err.param ?? null,
|
|
43
|
+
suggestion: err.suggestion ?? null
|
|
44
|
+
});
|
|
45
|
+
return createErrorResult(
|
|
46
|
+
"sdk_error",
|
|
47
|
+
err instanceof Error ? err.message : "Unknown SDK error",
|
|
48
|
+
null
|
|
49
|
+
);
|
|
41
50
|
}
|
|
42
51
|
|
|
43
52
|
// src/publish/detect.ts
|
|
@@ -49,253 +58,100 @@ function isHttpUrl(value) {
|
|
|
49
58
|
return false;
|
|
50
59
|
}
|
|
51
60
|
}
|
|
52
|
-
function
|
|
53
|
-
|
|
54
|
-
return trimmed.startsWith("<!doctype html") || trimmed.startsWith("<html") || /^<[a-z][a-z0-9:-]*(\s|>)/.test(trimmed);
|
|
61
|
+
function containsHtmlTag(value) {
|
|
62
|
+
return /<[a-z!/][^>]*>/i.test(value);
|
|
55
63
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
var FILE_EXT = /\.[A-Za-z0-9]{1,8}([?#].*)?$/;
|
|
65
|
+
function isPathShaped(value) {
|
|
66
|
+
if (value.length === 0 || value.length > 4096 || value.includes("\n"))
|
|
67
|
+
return false;
|
|
68
|
+
if (containsHtmlTag(value)) return false;
|
|
69
|
+
if (value.includes("/") || value.includes("\\")) return true;
|
|
70
|
+
if (/^[A-Za-z]:[\\/]/.test(value)) return true;
|
|
71
|
+
return FILE_EXT.test(value);
|
|
72
|
+
}
|
|
73
|
+
function contentTypeForString(value, override) {
|
|
74
|
+
if (override) return override;
|
|
75
|
+
return containsHtmlTag(value) ? "text/html" : "text/plain; charset=utf-8";
|
|
62
76
|
}
|
|
63
77
|
function detectBytesContentType(bytes) {
|
|
64
|
-
const text =
|
|
78
|
+
const text = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
|
|
65
79
|
if (text.includes("\uFFFD")) return "application/octet-stream";
|
|
66
|
-
return
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// src/publish/files.ts
|
|
70
|
-
import { readFile, stat } from "fs/promises";
|
|
71
|
-
import { basename, sep } from "path";
|
|
72
|
-
import fg from "fast-glob";
|
|
73
|
-
import ignore from "ignore";
|
|
74
|
-
import { lookup } from "mime-types";
|
|
75
|
-
var DEFAULT_IGNORES = [
|
|
76
|
-
".git",
|
|
77
|
-
".git/**",
|
|
78
|
-
"node_modules",
|
|
79
|
-
"node_modules/**",
|
|
80
|
-
".DS_Store",
|
|
81
|
-
".env",
|
|
82
|
-
".env.*",
|
|
83
|
-
".cache",
|
|
84
|
-
".cache/**",
|
|
85
|
-
".tmp",
|
|
86
|
-
".tmp/**",
|
|
87
|
-
"dist/.cache",
|
|
88
|
-
"dist/.cache/**"
|
|
89
|
-
];
|
|
90
|
-
async function collectPublishFiles(inputPath, options = {}) {
|
|
91
|
-
const inputStat = await stat(inputPath);
|
|
92
|
-
if (inputStat.isFile()) {
|
|
93
|
-
return [
|
|
94
|
-
{
|
|
95
|
-
absolutePath: inputPath,
|
|
96
|
-
path: basename(inputPath),
|
|
97
|
-
contentType: await detectPathContentType(inputPath),
|
|
98
|
-
sizeBytes: inputStat.size
|
|
99
|
-
}
|
|
100
|
-
];
|
|
101
|
-
}
|
|
102
|
-
if (!inputStat.isDirectory()) {
|
|
103
|
-
throw new Error(`Input is not a file or directory: ${inputPath}`);
|
|
104
|
-
}
|
|
105
|
-
const matcher = ignore().add([
|
|
106
|
-
...options.ignoreDefaults === false ? [] : DEFAULT_IGNORES,
|
|
107
|
-
...options.ignore ?? []
|
|
108
|
-
]);
|
|
109
|
-
const entries = await fg("**/*", {
|
|
110
|
-
cwd: inputPath,
|
|
111
|
-
onlyFiles: true,
|
|
112
|
-
followSymbolicLinks: false,
|
|
113
|
-
dot: true,
|
|
114
|
-
unique: true
|
|
115
|
-
});
|
|
116
|
-
const paths = entries.map((entry) => entry.split(sep).join("/")).filter((entry) => !matcher.ignores(entry)).sort((a, b) => a.localeCompare(b));
|
|
117
|
-
return Promise.all(
|
|
118
|
-
paths.map(async (path) => {
|
|
119
|
-
const absolutePath = `${inputPath}/${path}`;
|
|
120
|
-
const fileStat = await stat(absolutePath);
|
|
121
|
-
return {
|
|
122
|
-
absolutePath,
|
|
123
|
-
path,
|
|
124
|
-
contentType: await detectPathContentType(absolutePath),
|
|
125
|
-
sizeBytes: fileStat.size
|
|
126
|
-
};
|
|
127
|
-
})
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
async function detectPathContentType(path) {
|
|
131
|
-
const detected = lookup(path);
|
|
132
|
-
if (detected) return detected;
|
|
133
|
-
return detectBytesContentType(await readFile(path));
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// src/publish/prepare.ts
|
|
137
|
-
var SINGLE_PUT_CHECKSUM_THRESHOLD_BYTES = 10 * 1024 * 1024;
|
|
138
|
-
async function preparePublishRequest(input, options = {}) {
|
|
139
|
-
if (input instanceof URL) {
|
|
140
|
-
return sourceRequest(input.toString(), options);
|
|
141
|
-
}
|
|
142
|
-
if (typeof input === "string") {
|
|
143
|
-
const local = await localKind(input);
|
|
144
|
-
if (local === "file") {
|
|
145
|
-
return fileStaged(input, options);
|
|
146
|
-
}
|
|
147
|
-
if (local === "directory") {
|
|
148
|
-
return folderStaged(input, options);
|
|
149
|
-
}
|
|
150
|
-
const detection = detectStringInput(input);
|
|
151
|
-
if (detection.mode === "sourceUrl") return sourceRequest(input, options);
|
|
152
|
-
return stringContent(input, options, detection.contentType);
|
|
153
|
-
}
|
|
154
|
-
if (input instanceof Uint8Array || Buffer.isBuffer(input)) {
|
|
155
|
-
return bytesStaged(input, options);
|
|
156
|
-
}
|
|
157
|
-
return explicitInput(input, options);
|
|
158
|
-
}
|
|
159
|
-
async function stringContent(content, options, detectedContentType) {
|
|
160
|
-
const contentType = options.contentType ?? detectedContentType;
|
|
161
|
-
return bytesStaged(Buffer.from(content), {
|
|
162
|
-
...options,
|
|
163
|
-
path: options.path ?? entryForContentType(contentType),
|
|
164
|
-
contentType
|
|
165
|
-
});
|
|
80
|
+
return containsHtmlTag(text) ? "text/html" : "text/plain; charset=utf-8";
|
|
166
81
|
}
|
|
167
82
|
function entryForContentType(contentType) {
|
|
168
|
-
const
|
|
169
|
-
if (
|
|
170
|
-
|
|
171
|
-
if (
|
|
172
|
-
if (
|
|
173
|
-
if (normalized === "text/javascript" || normalized === "application/javascript")
|
|
83
|
+
const m = contentType.toLowerCase().split(";", 1)[0]?.trim() ?? "";
|
|
84
|
+
if (m === "text/html" || m === "application/xhtml+xml") return "index.html";
|
|
85
|
+
if (m === "application/json") return "index.json";
|
|
86
|
+
if (m === "text/css") return "index.css";
|
|
87
|
+
if (m === "text/javascript" || m === "application/javascript")
|
|
174
88
|
return "index.js";
|
|
175
|
-
if (
|
|
89
|
+
if (m.startsWith("text/")) return "index.txt";
|
|
176
90
|
return "index";
|
|
177
91
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if (info.isDirectory()) return "directory";
|
|
183
|
-
return "none";
|
|
184
|
-
} catch {
|
|
185
|
-
return "none";
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
async function fileStaged(path, options) {
|
|
189
|
-
const fileStat = await stat2(path);
|
|
190
|
-
const manifestPath = options.path ?? basename2(path);
|
|
191
|
-
return stagedRequest(
|
|
192
|
-
[
|
|
193
|
-
{
|
|
194
|
-
path: manifestPath,
|
|
195
|
-
contentType: options.contentType ?? await detectPathContentType(path),
|
|
196
|
-
sizeBytes: fileStat.size,
|
|
197
|
-
source: { kind: "path", path }
|
|
198
|
-
}
|
|
199
|
-
],
|
|
200
|
-
{ ...options, entry: options.entry ?? manifestPath }
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
async function folderStaged(path, options) {
|
|
204
|
-
const files = (await collectPublishFiles(path, options)).map((file) => ({
|
|
205
|
-
path: file.path,
|
|
206
|
-
contentType: file.contentType,
|
|
207
|
-
sizeBytes: file.sizeBytes,
|
|
208
|
-
source: { kind: "path", path: file.absolutePath }
|
|
209
|
-
}));
|
|
210
|
-
return stagedRequest(files, options);
|
|
211
|
-
}
|
|
212
|
-
async function bytesStaged(bytes, options) {
|
|
213
|
-
return stagedRequest(
|
|
214
|
-
[
|
|
215
|
-
{
|
|
216
|
-
path: options.path ?? "index",
|
|
217
|
-
contentType: options.contentType ?? detectBytesContentType(bytes),
|
|
218
|
-
sizeBytes: bytes.byteLength,
|
|
219
|
-
source: { kind: "bytes", bytes }
|
|
220
|
-
}
|
|
221
|
-
],
|
|
222
|
-
options
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
async function stagedRequest(files, options) {
|
|
226
|
-
const preparedFiles = await Promise.all(
|
|
227
|
-
files.map(async (file) => {
|
|
228
|
-
if (file.sizeBytes <= SINGLE_PUT_CHECKSUM_THRESHOLD_BYTES) return file;
|
|
229
|
-
const checksumSha256 = file.source.kind === "bytes" ? sha256Bytes(file.source.bytes) : await sha256File(file.source.path);
|
|
230
|
-
return { ...file, checksumSha256 };
|
|
231
|
-
})
|
|
232
|
-
);
|
|
233
|
-
const manifestFiles = preparedFiles.map((file) => ({
|
|
234
|
-
path: file.path,
|
|
235
|
-
contentType: file.contentType,
|
|
236
|
-
sizeBytes: file.sizeBytes,
|
|
237
|
-
...file.checksumSha256 ? { checksumSha256: file.checksumSha256 } : {}
|
|
238
|
-
}));
|
|
239
|
-
const prepared = {
|
|
240
|
-
kind: "staged",
|
|
241
|
-
manifest: {
|
|
242
|
-
schemaVersion: 1,
|
|
243
|
-
files: manifestFiles,
|
|
244
|
-
...options.entry ? { entry: options.entry } : {}
|
|
245
|
-
},
|
|
246
|
-
files: preparedFiles,
|
|
247
|
-
options: optionsBody(options)
|
|
248
|
-
};
|
|
249
|
-
if (options.metadata) prepared.metadata = options.metadata;
|
|
250
|
-
return prepared;
|
|
92
|
+
|
|
93
|
+
// src/publish/paths.ts
|
|
94
|
+
function normalizeManifestPath(path) {
|
|
95
|
+
return path.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
251
96
|
}
|
|
252
|
-
function
|
|
253
|
-
if (
|
|
254
|
-
throw new
|
|
255
|
-
"
|
|
97
|
+
function assertValidManifestPath(path) {
|
|
98
|
+
if (path.includes("\\"))
|
|
99
|
+
throw new PublishInputError(
|
|
100
|
+
"invalid_path",
|
|
101
|
+
`Manifest path must use "/": "${path}".`,
|
|
102
|
+
path
|
|
256
103
|
);
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if (options.metadata) prepared.metadata = options.metadata;
|
|
264
|
-
return prepared;
|
|
265
|
-
}
|
|
266
|
-
async function explicitInput(input, options) {
|
|
267
|
-
if ("content" in input)
|
|
268
|
-
return stringContent(
|
|
269
|
-
input.content,
|
|
270
|
-
{ ...options, ...input },
|
|
271
|
-
input.contentType ?? options.contentType ?? "text/html"
|
|
104
|
+
const p = normalizeManifestPath(path);
|
|
105
|
+
if (p === "")
|
|
106
|
+
throw new PublishInputError(
|
|
107
|
+
"invalid_path",
|
|
108
|
+
"Manifest path must not be empty.",
|
|
109
|
+
path
|
|
272
110
|
);
|
|
273
|
-
if ("
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
111
|
+
if (p.startsWith("/"))
|
|
112
|
+
throw new PublishInputError(
|
|
113
|
+
"invalid_path",
|
|
114
|
+
`Manifest path must be relative: "${path}".`,
|
|
115
|
+
path
|
|
116
|
+
);
|
|
117
|
+
if (p.startsWith("~"))
|
|
118
|
+
throw new PublishInputError(
|
|
119
|
+
"invalid_path",
|
|
120
|
+
`Manifest path must not start with "~": "${path}".`,
|
|
121
|
+
path
|
|
122
|
+
);
|
|
123
|
+
if (p.split("/").includes(".."))
|
|
124
|
+
throw new PublishInputError(
|
|
125
|
+
"invalid_path",
|
|
126
|
+
`Manifest path must not contain "..": "${path}".`,
|
|
127
|
+
path
|
|
278
128
|
);
|
|
279
|
-
return stagedRequest(
|
|
280
|
-
input.files.map((file) => {
|
|
281
|
-
const bytes = explicitFileBytes(file);
|
|
282
|
-
return {
|
|
283
|
-
path: file.path,
|
|
284
|
-
contentType: file.contentType,
|
|
285
|
-
sizeBytes: bytes.byteLength,
|
|
286
|
-
source: { kind: "bytes", bytes }
|
|
287
|
-
};
|
|
288
|
-
}),
|
|
289
|
-
{ ...options, ...input }
|
|
290
|
-
);
|
|
291
129
|
}
|
|
292
|
-
function
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
130
|
+
function assertNoDuplicatePaths(paths) {
|
|
131
|
+
const seen = /* @__PURE__ */ new Set();
|
|
132
|
+
for (const raw of paths) {
|
|
133
|
+
const p = normalizeManifestPath(raw);
|
|
134
|
+
if (seen.has(p))
|
|
135
|
+
throw new PublishInputError(
|
|
136
|
+
"duplicate_path",
|
|
137
|
+
`Duplicate file path after normalization: "${p}".`,
|
|
138
|
+
p,
|
|
139
|
+
"Give each file a unique path; basename mapping for string[] can collide."
|
|
140
|
+
);
|
|
141
|
+
seen.add(p);
|
|
142
|
+
}
|
|
298
143
|
}
|
|
144
|
+
function assertNonEmptyBundle(paths) {
|
|
145
|
+
if (paths.length === 0)
|
|
146
|
+
throw new PublishInputError(
|
|
147
|
+
"empty_bundle",
|
|
148
|
+
"Nothing to publish: the bundle has no files.",
|
|
149
|
+
void 0,
|
|
150
|
+
"Pass at least one file, or a directory that contains files."
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/publish/core.ts
|
|
299
155
|
var VALID_VISIBILITIES = ["public", "unlisted"];
|
|
300
156
|
var MAX_TITLE_LENGTH = 500;
|
|
301
157
|
function validatePublishOptions(options) {
|
|
@@ -332,69 +188,125 @@ function optionsBody(options) {
|
|
|
332
188
|
expiresAt: options.expiresAt instanceof Date ? options.expiresAt.toISOString() : options.expiresAt
|
|
333
189
|
};
|
|
334
190
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
191
|
+
function buildStagedRequest(files, options, entry) {
|
|
192
|
+
assertNonEmptyBundle(files.map((f) => f.path));
|
|
193
|
+
const normalizedPaths = files.map((f) => normalizeManifestPath(f.path));
|
|
194
|
+
assertNoDuplicatePaths(normalizedPaths);
|
|
195
|
+
const manifestFiles = files.map((file) => ({
|
|
196
|
+
path: normalizeManifestPath(file.path),
|
|
197
|
+
contentType: file.contentType,
|
|
198
|
+
sizeBytes: file.sizeBytes,
|
|
199
|
+
...file.checksumSha256 ? { checksumSha256: file.checksumSha256 } : {}
|
|
200
|
+
}));
|
|
201
|
+
const resolvedEntry = entry ?? options.entry;
|
|
202
|
+
if (resolvedEntry !== void 0) assertValidManifestPath(resolvedEntry);
|
|
203
|
+
const manifest = {
|
|
204
|
+
schemaVersion: 1,
|
|
205
|
+
files: manifestFiles,
|
|
206
|
+
...resolvedEntry ? { entry: resolvedEntry } : {}
|
|
207
|
+
};
|
|
208
|
+
const prepared = {
|
|
209
|
+
kind: "staged",
|
|
210
|
+
manifest,
|
|
211
|
+
files,
|
|
212
|
+
options: optionsBody(options)
|
|
213
|
+
};
|
|
214
|
+
if (options.metadata) prepared.metadata = options.metadata;
|
|
215
|
+
return prepared;
|
|
216
|
+
}
|
|
217
|
+
function buildSourceRequest(sourceUrl, options) {
|
|
218
|
+
if (!isHttpUrl(sourceUrl)) {
|
|
219
|
+
throw new PublishInputError(
|
|
220
|
+
"invalid_source_url",
|
|
221
|
+
"source_url must be an http(s) URL.",
|
|
222
|
+
sourceUrl,
|
|
223
|
+
"Fetch the content first, then pass it as content/files."
|
|
348
224
|
);
|
|
349
225
|
}
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
226
|
+
const prepared = {
|
|
227
|
+
kind: "source",
|
|
228
|
+
sourceUrl,
|
|
229
|
+
options: optionsBody(options)
|
|
230
|
+
};
|
|
231
|
+
if (options.metadata) prepared.metadata = options.metadata;
|
|
232
|
+
return prepared;
|
|
233
|
+
}
|
|
234
|
+
function decodeBase64(value) {
|
|
235
|
+
const binary = atob(value);
|
|
236
|
+
return Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
237
|
+
}
|
|
238
|
+
function fileBytes(file) {
|
|
239
|
+
if (file.content !== void 0) return new TextEncoder().encode(file.content);
|
|
240
|
+
if (file.contentBase64 !== void 0) return decodeBase64(file.contentBase64);
|
|
241
|
+
if (file.bytes !== void 0) return file.bytes;
|
|
242
|
+
throw new PublishInputError(
|
|
243
|
+
"missing_file_bytes",
|
|
244
|
+
`File "${file.path}" is missing content bytes.`,
|
|
245
|
+
file.path,
|
|
246
|
+
"Provide one of content, contentBase64, or bytes."
|
|
353
247
|
);
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
{ body: { fileId, partNumbers: partNumberBatch } }
|
|
364
|
-
);
|
|
365
|
-
if (targetsResult.error) return errorResult(targetsResult);
|
|
366
|
-
for (const target of targetsResult.data.parts) {
|
|
367
|
-
const start = (target.partNumber - 1) * partSize;
|
|
368
|
-
const end = Math.min(start + partSize, bytes.byteLength);
|
|
369
|
-
const uploadResult = await transport.putSignedUrl(
|
|
370
|
-
target.url,
|
|
371
|
-
bytes.slice(start, end),
|
|
372
|
-
target.headers
|
|
373
|
-
);
|
|
374
|
-
if (uploadResult.error) return errorResult(uploadResult);
|
|
375
|
-
if (!uploadResult.data.etag) {
|
|
376
|
-
return createErrorResult(
|
|
377
|
-
"missing_upload_etag",
|
|
378
|
-
"Signed upload response did not include an ETag.",
|
|
379
|
-
null
|
|
380
|
-
);
|
|
248
|
+
}
|
|
249
|
+
function singleStagedFile(path, contentType, bytes, options) {
|
|
250
|
+
return buildStagedRequest(
|
|
251
|
+
[
|
|
252
|
+
{
|
|
253
|
+
path,
|
|
254
|
+
contentType,
|
|
255
|
+
sizeBytes: bytes.byteLength,
|
|
256
|
+
getBody: async () => bytes
|
|
381
257
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
258
|
+
],
|
|
259
|
+
options
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
async function resolveInMemory(input, options = {}) {
|
|
263
|
+
if (input instanceof URL) {
|
|
264
|
+
return buildSourceRequest(input.toString(), options);
|
|
387
265
|
}
|
|
388
|
-
|
|
266
|
+
if (typeof input === "string") {
|
|
267
|
+
if (isHttpUrl(input)) return buildSourceRequest(input, options);
|
|
268
|
+
const contentType = contentTypeForString(input, options.contentType);
|
|
269
|
+
if (options.path !== void 0) assertValidManifestPath(options.path);
|
|
270
|
+
const path = options.path ?? entryForContentType(contentType);
|
|
271
|
+
const bytes = new TextEncoder().encode(input);
|
|
272
|
+
return singleStagedFile(path, contentType, bytes, options);
|
|
273
|
+
}
|
|
274
|
+
if (input instanceof Uint8Array) {
|
|
275
|
+
const contentType = options.contentType ?? detectBytesContentType(input);
|
|
276
|
+
if (options.path !== void 0) assertValidManifestPath(options.path);
|
|
277
|
+
const path = options.path ?? entryForContentType(contentType);
|
|
278
|
+
return singleStagedFile(path, contentType, input, options);
|
|
279
|
+
}
|
|
280
|
+
if (input.kind === "content") {
|
|
281
|
+
const contentType = input.contentType ?? options.contentType ?? "text/html";
|
|
282
|
+
if (input.path !== void 0) assertValidManifestPath(input.path);
|
|
283
|
+
if (options.path !== void 0) assertValidManifestPath(options.path);
|
|
284
|
+
const path = input.path ?? options.path ?? entryForContentType(contentType);
|
|
285
|
+
const bytes = new TextEncoder().encode(input.content);
|
|
286
|
+
return singleStagedFile(path, contentType, bytes, options);
|
|
287
|
+
}
|
|
288
|
+
if (input.kind === "source_url") {
|
|
289
|
+
return buildSourceRequest(input.sourceUrl, options);
|
|
290
|
+
}
|
|
291
|
+
const files = input.files.map((file) => {
|
|
292
|
+
assertValidManifestPath(file.path);
|
|
293
|
+
const bytes = fileBytes(file);
|
|
294
|
+
const contentType = file.contentType ?? detectBytesContentType(bytes);
|
|
295
|
+
return {
|
|
296
|
+
path: file.path,
|
|
297
|
+
contentType,
|
|
298
|
+
sizeBytes: bytes.byteLength,
|
|
299
|
+
getBody: async () => bytes
|
|
300
|
+
};
|
|
301
|
+
});
|
|
302
|
+
return buildStagedRequest(files, options, input.entry);
|
|
389
303
|
}
|
|
390
304
|
function errorResult(result) {
|
|
391
305
|
if (!result.error) throw new Error("Expected an error result");
|
|
392
306
|
return { data: null, error: result.error, headers: result.headers };
|
|
393
307
|
}
|
|
394
|
-
|
|
395
|
-
// src/publish/upload.ts
|
|
396
308
|
async function publishStaged(transport, prepared, finalPath, options = {}) {
|
|
397
|
-
const baseKey = options.idempotencyKey ?? `sdk-${randomUUID()}`;
|
|
309
|
+
const baseKey = options.idempotencyKey ?? `sdk-${crypto.randomUUID()}`;
|
|
398
310
|
const createResult = await transport.request(
|
|
399
311
|
"POST",
|
|
400
312
|
"/uploads",
|
|
@@ -403,12 +315,9 @@ async function publishStaged(transport, prepared, finalPath, options = {}) {
|
|
|
403
315
|
idempotencyKey: `${baseKey}:create-upload`
|
|
404
316
|
}
|
|
405
317
|
);
|
|
406
|
-
if (createResult.error) return
|
|
407
|
-
const completedFiles = {};
|
|
318
|
+
if (createResult.error) return errorResult(createResult);
|
|
408
319
|
for (const file of prepared.files) {
|
|
409
|
-
const target = createResult.data.files.find(
|
|
410
|
-
(candidate) => candidate.path === file.path
|
|
411
|
-
);
|
|
320
|
+
const target = createResult.data.files.find((f) => f.path === file.path);
|
|
412
321
|
if (!target) {
|
|
413
322
|
return createErrorResult(
|
|
414
323
|
"upload_target_missing",
|
|
@@ -416,36 +325,29 @@ async function publishStaged(transport, prepared, finalPath, options = {}) {
|
|
|
416
325
|
null
|
|
417
326
|
);
|
|
418
327
|
}
|
|
419
|
-
if (target.upload.strategy
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
target.fileId,
|
|
425
|
-
bytes2,
|
|
426
|
-
target.upload.partSize
|
|
328
|
+
if (target.upload.strategy !== "single_put") {
|
|
329
|
+
return createErrorResult(
|
|
330
|
+
"unsupported_upload_strategy",
|
|
331
|
+
`Unsupported upload strategy "${target.upload.strategy}" for ${file.path}. This SDK uploads via single PUT only.`,
|
|
332
|
+
null
|
|
427
333
|
);
|
|
428
|
-
if (partsResult.error) return errorResult2(partsResult);
|
|
429
|
-
completedFiles[target.fileId] = { parts: partsResult.data };
|
|
430
|
-
continue;
|
|
431
334
|
}
|
|
432
|
-
const
|
|
433
|
-
const uploadResult = await transport.putSignedUrl(
|
|
335
|
+
const put = await transport.putSignedUrl(
|
|
434
336
|
target.upload.url,
|
|
435
|
-
|
|
337
|
+
await file.getBody(),
|
|
436
338
|
target.upload.headers
|
|
437
339
|
);
|
|
438
|
-
if (
|
|
340
|
+
if (put.error) return errorResult(put);
|
|
439
341
|
}
|
|
440
342
|
const completeResult = await transport.request(
|
|
441
343
|
"POST",
|
|
442
344
|
`/uploads/${encodeURIComponent(createResult.data.uploadId)}/complete`,
|
|
443
345
|
{
|
|
444
|
-
body: { files:
|
|
346
|
+
body: { files: {} },
|
|
445
347
|
idempotencyKey: `${baseKey}:complete-upload`
|
|
446
348
|
}
|
|
447
349
|
);
|
|
448
|
-
if (completeResult.error) return
|
|
350
|
+
if (completeResult.error) return errorResult(completeResult);
|
|
449
351
|
const finalOptions = {
|
|
450
352
|
body: {
|
|
451
353
|
uploadId: createResult.data.uploadId,
|
|
@@ -458,13 +360,184 @@ async function publishStaged(transport, prepared, finalPath, options = {}) {
|
|
|
458
360
|
finalOptions.ifRevision = options.ifRevision;
|
|
459
361
|
return transport.request("POST", finalPath, finalOptions);
|
|
460
362
|
}
|
|
461
|
-
async function
|
|
462
|
-
|
|
463
|
-
return
|
|
363
|
+
async function publishSource(transport, prepared, finalPath, options = {}) {
|
|
364
|
+
const baseKey = options.idempotencyKey ?? `sdk-${crypto.randomUUID()}`;
|
|
365
|
+
return transport.request("POST", finalPath, {
|
|
366
|
+
body: {
|
|
367
|
+
sourceUrl: prepared.sourceUrl,
|
|
368
|
+
options: prepared.options,
|
|
369
|
+
...prepared.metadata ? { metadata: prepared.metadata } : {}
|
|
370
|
+
},
|
|
371
|
+
idempotencyKey: `${baseKey}:publish`,
|
|
372
|
+
...options.ifRevision !== void 0 ? { ifRevision: options.ifRevision } : {}
|
|
373
|
+
});
|
|
464
374
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
375
|
+
|
|
376
|
+
// src/publish/node.ts
|
|
377
|
+
import { readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
378
|
+
import { basename as basename2 } from "path";
|
|
379
|
+
|
|
380
|
+
// src/publish/checksum.ts
|
|
381
|
+
import { createHash } from "crypto";
|
|
382
|
+
import { createReadStream } from "fs";
|
|
383
|
+
async function sha256File(path) {
|
|
384
|
+
const hash = createHash("sha256");
|
|
385
|
+
for await (const chunk of createReadStream(path)) {
|
|
386
|
+
hash.update(chunk);
|
|
387
|
+
}
|
|
388
|
+
return hash.digest("hex");
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// src/publish/files.ts
|
|
392
|
+
import { readFile, stat } from "fs/promises";
|
|
393
|
+
import { basename, sep } from "path";
|
|
394
|
+
import fg from "fast-glob";
|
|
395
|
+
import ignore from "ignore";
|
|
396
|
+
import { lookup } from "mime-types";
|
|
397
|
+
var DEFAULT_IGNORES = [
|
|
398
|
+
".git",
|
|
399
|
+
".git/**",
|
|
400
|
+
"node_modules",
|
|
401
|
+
"node_modules/**",
|
|
402
|
+
".DS_Store",
|
|
403
|
+
".env",
|
|
404
|
+
".env.*",
|
|
405
|
+
".cache",
|
|
406
|
+
".cache/**",
|
|
407
|
+
".tmp",
|
|
408
|
+
".tmp/**",
|
|
409
|
+
"dist/.cache",
|
|
410
|
+
"dist/.cache/**"
|
|
411
|
+
];
|
|
412
|
+
async function collectPublishFiles(inputPath, options = {}) {
|
|
413
|
+
const inputStat = await stat(inputPath);
|
|
414
|
+
if (inputStat.isFile()) {
|
|
415
|
+
return [
|
|
416
|
+
{
|
|
417
|
+
absolutePath: inputPath,
|
|
418
|
+
path: basename(inputPath),
|
|
419
|
+
contentType: await detectPathContentType(inputPath),
|
|
420
|
+
sizeBytes: inputStat.size
|
|
421
|
+
}
|
|
422
|
+
];
|
|
423
|
+
}
|
|
424
|
+
if (!inputStat.isDirectory()) {
|
|
425
|
+
throw new Error(`Input is not a file or directory: ${inputPath}`);
|
|
426
|
+
}
|
|
427
|
+
const matcher = ignore().add([
|
|
428
|
+
...options.ignoreDefaults === false ? [] : DEFAULT_IGNORES,
|
|
429
|
+
...options.ignore ?? []
|
|
430
|
+
]);
|
|
431
|
+
const entries = await fg("**/*", {
|
|
432
|
+
cwd: inputPath,
|
|
433
|
+
onlyFiles: true,
|
|
434
|
+
followSymbolicLinks: false,
|
|
435
|
+
dot: true,
|
|
436
|
+
unique: true
|
|
437
|
+
});
|
|
438
|
+
const paths = entries.map((entry) => entry.split(sep).join("/")).filter((entry) => !matcher.ignores(entry)).sort((a, b) => a.localeCompare(b));
|
|
439
|
+
return Promise.all(
|
|
440
|
+
paths.map(async (path) => {
|
|
441
|
+
const absolutePath = `${inputPath}/${path}`;
|
|
442
|
+
const fileStat = await stat(absolutePath);
|
|
443
|
+
return {
|
|
444
|
+
absolutePath,
|
|
445
|
+
path,
|
|
446
|
+
contentType: await detectPathContentType(absolutePath),
|
|
447
|
+
sizeBytes: fileStat.size
|
|
448
|
+
};
|
|
449
|
+
})
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
async function detectPathContentType(path) {
|
|
453
|
+
const detected = lookup(path);
|
|
454
|
+
if (detected) return detected;
|
|
455
|
+
return detectBytesContentType(await readFile(path));
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// src/publish/node.ts
|
|
459
|
+
var SINGLE_PUT_CHECKSUM_THRESHOLD = 10 * 1024 * 1024;
|
|
460
|
+
async function resolveInput(input, options = {}) {
|
|
461
|
+
if (input instanceof URL || input instanceof Uint8Array || typeof input === "object" && input !== null && "kind" in input) {
|
|
462
|
+
return resolveInMemory(input, options);
|
|
463
|
+
}
|
|
464
|
+
if (Array.isArray(input)) {
|
|
465
|
+
return resolveBundle(input, options);
|
|
466
|
+
}
|
|
467
|
+
if (typeof input === "string") {
|
|
468
|
+
return resolveString(input, options);
|
|
469
|
+
}
|
|
470
|
+
return resolveInMemory(input, options);
|
|
471
|
+
}
|
|
472
|
+
async function resolveString(value, options) {
|
|
473
|
+
if (isHttpUrl(value)) return resolveInMemory(value, options);
|
|
474
|
+
if (value.includes("\n") || value.length > 4096) {
|
|
475
|
+
return resolveInMemory(value, options);
|
|
476
|
+
}
|
|
477
|
+
let info;
|
|
478
|
+
try {
|
|
479
|
+
info = await stat2(value);
|
|
480
|
+
} catch {
|
|
481
|
+
info = void 0;
|
|
482
|
+
}
|
|
483
|
+
if (info?.isFile()) return fileStaged(value, options);
|
|
484
|
+
if (info?.isDirectory()) return folderStaged(value, options);
|
|
485
|
+
if (isPathShaped(value)) {
|
|
486
|
+
throw new PublishInputError(
|
|
487
|
+
"file_not_found",
|
|
488
|
+
`No file or directory at "${value}".`,
|
|
489
|
+
value,
|
|
490
|
+
'To publish this as inline text, pass { kind: "content", content }.'
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
return resolveInMemory(value, options);
|
|
494
|
+
}
|
|
495
|
+
async function fileStaged(path, options) {
|
|
496
|
+
const fileStat = await stat2(path);
|
|
497
|
+
if (options.path !== void 0) assertValidManifestPath(options.path);
|
|
498
|
+
const manifestPath = options.path ?? basename2(path);
|
|
499
|
+
const contentType = options.contentType ?? await detectPathContentType(path);
|
|
500
|
+
const checksumSha256 = fileStat.size > SINGLE_PUT_CHECKSUM_THRESHOLD ? await sha256File(path) : void 0;
|
|
501
|
+
const file = {
|
|
502
|
+
path: manifestPath,
|
|
503
|
+
contentType,
|
|
504
|
+
sizeBytes: fileStat.size,
|
|
505
|
+
...checksumSha256 ? { checksumSha256 } : {},
|
|
506
|
+
getBody: () => readFile2(path)
|
|
507
|
+
};
|
|
508
|
+
return buildStagedRequest([file], options);
|
|
509
|
+
}
|
|
510
|
+
async function folderStaged(path, options) {
|
|
511
|
+
const files = await collectPublishFiles(path, options);
|
|
512
|
+
const preparedFiles = await Promise.all(files.map(toPreparedFile));
|
|
513
|
+
return buildStagedRequest(preparedFiles, options);
|
|
514
|
+
}
|
|
515
|
+
async function resolveBundle(paths, options) {
|
|
516
|
+
const collected = [];
|
|
517
|
+
for (const element of paths) {
|
|
518
|
+
try {
|
|
519
|
+
await stat2(element);
|
|
520
|
+
} catch {
|
|
521
|
+
throw new PublishInputError(
|
|
522
|
+
"file_not_found",
|
|
523
|
+
`No file or directory at "${element}".`,
|
|
524
|
+
element
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
collected.push(...await collectPublishFiles(element, options));
|
|
528
|
+
}
|
|
529
|
+
const preparedFiles = await Promise.all(collected.map(toPreparedFile));
|
|
530
|
+
return buildStagedRequest(preparedFiles, options);
|
|
531
|
+
}
|
|
532
|
+
async function toPreparedFile(pf) {
|
|
533
|
+
const checksumSha256 = pf.sizeBytes > SINGLE_PUT_CHECKSUM_THRESHOLD ? await sha256File(pf.absolutePath) : void 0;
|
|
534
|
+
return {
|
|
535
|
+
path: pf.path,
|
|
536
|
+
contentType: pf.contentType,
|
|
537
|
+
sizeBytes: pf.sizeBytes,
|
|
538
|
+
...checksumSha256 ? { checksumSha256 } : {},
|
|
539
|
+
getBody: () => readFile2(pf.absolutePath)
|
|
540
|
+
};
|
|
468
541
|
}
|
|
469
542
|
|
|
470
543
|
// src/resources/account.ts
|
|
@@ -533,33 +606,6 @@ var DeploymentsResource = class {
|
|
|
533
606
|
this.transport = transport;
|
|
534
607
|
}
|
|
535
608
|
transport;
|
|
536
|
-
create(dropId, body, options = {}) {
|
|
537
|
-
const requestOptions = { body };
|
|
538
|
-
if (options.idempotencyKey)
|
|
539
|
-
requestOptions.idempotencyKey = options.idempotencyKey;
|
|
540
|
-
if (options.ifRevision !== void 0)
|
|
541
|
-
requestOptions.ifRevision = options.ifRevision;
|
|
542
|
-
return this.transport.request(
|
|
543
|
-
"POST",
|
|
544
|
-
`/drops/${encodeURIComponent(dropId)}/deployments`,
|
|
545
|
-
requestOptions
|
|
546
|
-
);
|
|
547
|
-
}
|
|
548
|
-
createRaw(dropId, body, options = {}) {
|
|
549
|
-
const requestOptions = {
|
|
550
|
-
body,
|
|
551
|
-
bodyCase: "raw"
|
|
552
|
-
};
|
|
553
|
-
if (options.idempotencyKey)
|
|
554
|
-
requestOptions.idempotencyKey = options.idempotencyKey;
|
|
555
|
-
if (options.ifRevision !== void 0)
|
|
556
|
-
requestOptions.ifRevision = options.ifRevision;
|
|
557
|
-
return this.transport.request(
|
|
558
|
-
"POST",
|
|
559
|
-
`/drops/${encodeURIComponent(dropId)}/deployments`,
|
|
560
|
-
requestOptions
|
|
561
|
-
);
|
|
562
|
-
}
|
|
563
609
|
list(dropId, params = {}) {
|
|
564
610
|
return this.transport.request(
|
|
565
611
|
"GET",
|
|
@@ -614,21 +660,6 @@ var DropsResource = class {
|
|
|
614
660
|
this.transport = transport;
|
|
615
661
|
}
|
|
616
662
|
transport;
|
|
617
|
-
create(body, options = {}) {
|
|
618
|
-
const requestOptions = { body };
|
|
619
|
-
if (options.idempotencyKey)
|
|
620
|
-
requestOptions.idempotencyKey = options.idempotencyKey;
|
|
621
|
-
return this.transport.request("POST", "/drops", requestOptions);
|
|
622
|
-
}
|
|
623
|
-
createRaw(body, options = {}) {
|
|
624
|
-
const requestOptions = {
|
|
625
|
-
body,
|
|
626
|
-
bodyCase: "raw"
|
|
627
|
-
};
|
|
628
|
-
if (options.idempotencyKey)
|
|
629
|
-
requestOptions.idempotencyKey = options.idempotencyKey;
|
|
630
|
-
return this.transport.request("POST", "/drops", requestOptions);
|
|
631
|
-
}
|
|
632
663
|
async list(params = {}) {
|
|
633
664
|
const result = await this.transport.request("GET", "/drops", {
|
|
634
665
|
params
|
|
@@ -669,15 +700,22 @@ var DropsResource = class {
|
|
|
669
700
|
);
|
|
670
701
|
}
|
|
671
702
|
};
|
|
703
|
+
var SETTINGS = [
|
|
704
|
+
"title",
|
|
705
|
+
"visibility",
|
|
706
|
+
"password",
|
|
707
|
+
"noindex",
|
|
708
|
+
"expiresAt",
|
|
709
|
+
"slug"
|
|
710
|
+
];
|
|
672
711
|
function updateBody(options) {
|
|
673
|
-
const {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
...
|
|
679
|
-
}
|
|
680
|
-
return { options: dropOptions, metadata };
|
|
712
|
+
const out = {};
|
|
713
|
+
for (const key of SETTINGS)
|
|
714
|
+
if (options[key] !== void 0) out[key] = options[key];
|
|
715
|
+
return {
|
|
716
|
+
options: out,
|
|
717
|
+
...options.metadata !== void 0 ? { metadata: options.metadata } : {}
|
|
718
|
+
};
|
|
681
719
|
}
|
|
682
720
|
|
|
683
721
|
// src/resources/uploads.ts
|
|
@@ -698,13 +736,6 @@ var UploadsResource = class {
|
|
|
698
736
|
`/uploads/${encodeURIComponent(uploadId)}`
|
|
699
737
|
);
|
|
700
738
|
}
|
|
701
|
-
createPartTargets(uploadId, body) {
|
|
702
|
-
return this.transport.request(
|
|
703
|
-
"POST",
|
|
704
|
-
`/uploads/${encodeURIComponent(uploadId)}/parts`,
|
|
705
|
-
{ body }
|
|
706
|
-
);
|
|
707
|
-
}
|
|
708
739
|
complete(uploadId, body, options = {}) {
|
|
709
740
|
const requestOptions = { body };
|
|
710
741
|
if (options.idempotencyKey)
|
|
@@ -758,25 +789,24 @@ function toSnakeCase(value) {
|
|
|
758
789
|
|
|
759
790
|
// src/transport.ts
|
|
760
791
|
var DEFAULT_BASE_URL = "https://api.dropthis.app";
|
|
761
|
-
var SDK_VERSION = "0.
|
|
792
|
+
var SDK_VERSION = "0.6.0";
|
|
762
793
|
var Transport = class {
|
|
763
794
|
apiKey;
|
|
764
795
|
baseUrl;
|
|
765
796
|
timeoutMs;
|
|
797
|
+
uploadTimeoutMs;
|
|
766
798
|
fetchImpl;
|
|
767
799
|
constructor(options = {}) {
|
|
768
800
|
const resolved = typeof options === "string" ? { apiKey: options } : options;
|
|
769
801
|
this.apiKey = resolved.apiKey ?? (typeof process !== "undefined" ? process.env?.DROPTHIS_API_KEY : void 0);
|
|
770
802
|
this.baseUrl = (resolved.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
771
803
|
this.timeoutMs = resolved.timeoutMs ?? 3e4;
|
|
804
|
+
this.uploadTimeoutMs = resolved.uploadTimeoutMs ?? 12e4;
|
|
772
805
|
this.fetchImpl = resolved.fetch ?? globalThis.fetch;
|
|
773
806
|
}
|
|
774
|
-
multipart(method, path, form, options = {}) {
|
|
775
|
-
return this.request(method, path, { ...options, body: form });
|
|
776
|
-
}
|
|
777
807
|
async putSignedUrl(url, body, headers) {
|
|
778
808
|
const controller = new AbortController();
|
|
779
|
-
const timeout = setTimeout(() => controller.abort(), this.
|
|
809
|
+
const timeout = setTimeout(() => controller.abort(), this.uploadTimeoutMs);
|
|
780
810
|
try {
|
|
781
811
|
const request = {
|
|
782
812
|
method: "PUT",
|
|
@@ -831,7 +861,7 @@ var Transport = class {
|
|
|
831
861
|
if (options.idempotencyKey)
|
|
832
862
|
headers["idempotency-key"] = options.idempotencyKey;
|
|
833
863
|
if (options.ifRevision !== void 0)
|
|
834
|
-
headers["
|
|
864
|
+
headers["if-revision"] = String(options.ifRevision);
|
|
835
865
|
if (options.body !== void 0 && !(options.body instanceof FormData))
|
|
836
866
|
headers["content-type"] = "application/json";
|
|
837
867
|
const url = new URL(`${this.baseUrl}${path}`);
|
|
@@ -863,11 +893,18 @@ var Transport = class {
|
|
|
863
893
|
const parsed = parseJson(text);
|
|
864
894
|
if (!response.ok) {
|
|
865
895
|
const body = parsed && typeof parsed === "object" ? parsed : {};
|
|
866
|
-
const message = stringValue(body.detail) ?? stringValue(body.
|
|
896
|
+
const message = stringValue(body.detail) ?? stringValue(body.title) ?? response.statusText;
|
|
867
897
|
const code = stringValue(body.code) ?? stringValue(body.error_code) ?? `http_${response.status}`;
|
|
868
898
|
const currentRevision = numberValue(body.current_revision);
|
|
899
|
+
const type = stringValue(body.type);
|
|
900
|
+
const title = stringValue(body.title);
|
|
901
|
+
const instance = stringValue(body.instance);
|
|
869
902
|
return createErrorResult(code, message, response.status, {
|
|
870
|
-
|
|
903
|
+
body,
|
|
904
|
+
...type !== null ? { type } : {},
|
|
905
|
+
...title !== null ? { title } : {},
|
|
906
|
+
...instance !== null ? { instance } : {},
|
|
907
|
+
detail: stringValue(body.detail),
|
|
871
908
|
param: stringValue(body.param),
|
|
872
909
|
...currentRevision !== void 0 ? { currentRevision } : {},
|
|
873
910
|
requestId: stringValue(body.request_id) ?? responseHeaders["x-request-id"] ?? null,
|
|
@@ -961,54 +998,36 @@ var Dropthis = class {
|
|
|
961
998
|
return this.uploadsResource;
|
|
962
999
|
}
|
|
963
1000
|
async prepare(input, options = {}) {
|
|
964
|
-
return
|
|
1001
|
+
return resolveInput(input, options);
|
|
965
1002
|
}
|
|
966
1003
|
async publish(input, options = {}) {
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
1004
|
+
let prepared;
|
|
1005
|
+
try {
|
|
1006
|
+
prepared = await resolveInput(input, options);
|
|
1007
|
+
} catch (e) {
|
|
1008
|
+
return toErrorResult(e);
|
|
1009
|
+
}
|
|
1010
|
+
return prepared.kind === "source" ? publishSource(this.transport, prepared, "/drops", options) : publishStaged(this.transport, prepared, "/drops", options);
|
|
971
1011
|
}
|
|
972
1012
|
async deploy(dropId, input, options = {}) {
|
|
973
|
-
|
|
1013
|
+
let prepared;
|
|
1014
|
+
try {
|
|
1015
|
+
prepared = await resolveInput(input, options);
|
|
1016
|
+
} catch (e) {
|
|
1017
|
+
return toErrorResult(e);
|
|
1018
|
+
}
|
|
974
1019
|
const path = `/drops/${encodeURIComponent(dropId)}/deployments`;
|
|
975
|
-
|
|
976
|
-
return createErrorResult(
|
|
977
|
-
"source_url_unsupported_for_deployment",
|
|
978
|
-
"Publishing a deployment from a source_url is not supported yet. Fetch the content first, then pass it as a string or file.",
|
|
979
|
-
null
|
|
980
|
-
);
|
|
981
|
-
return publishStaged(this.transport, prepared, path, options);
|
|
982
|
-
}
|
|
983
|
-
publishSource(prepared, path, options) {
|
|
984
|
-
const body = {
|
|
985
|
-
sourceUrl: prepared.sourceUrl,
|
|
986
|
-
options: prepared.options
|
|
987
|
-
};
|
|
988
|
-
if (prepared.metadata) body.metadata = prepared.metadata;
|
|
989
|
-
const baseKey = options.idempotencyKey ?? `sdk-${crypto.randomUUID()}`;
|
|
990
|
-
return this.transport.request("POST", path, {
|
|
991
|
-
body,
|
|
992
|
-
idempotencyKey: `${baseKey}:publish`
|
|
993
|
-
});
|
|
1020
|
+
return prepared.kind === "source" ? publishSource(this.transport, prepared, path, options) : publishStaged(this.transport, prepared, path, options);
|
|
994
1021
|
}
|
|
995
1022
|
async update(dropId, options = {}) {
|
|
996
1023
|
return this.drops.update(dropId, options);
|
|
997
1024
|
}
|
|
998
|
-
request(method, path, options = {}) {
|
|
999
|
-
return this.transport.request(method, path, {
|
|
1000
|
-
...options,
|
|
1001
|
-
bodyCase: "raw"
|
|
1002
|
-
});
|
|
1003
|
-
}
|
|
1004
|
-
requestSignedUpload(url, bytes, headers) {
|
|
1005
|
-
return this.transport.putSignedUrl(url, bytes, headers);
|
|
1006
|
-
}
|
|
1007
1025
|
};
|
|
1008
1026
|
export {
|
|
1009
1027
|
CursorPage,
|
|
1010
1028
|
DeploymentsResource,
|
|
1011
1029
|
Dropthis,
|
|
1030
|
+
PublishInputError,
|
|
1012
1031
|
UploadsResource,
|
|
1013
1032
|
createErrorResult,
|
|
1014
1033
|
redactSecrets
|