@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,16 +11,358 @@ 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
|
}
|
|
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
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/publish/detect.ts
|
|
53
|
+
function isHttpUrl(value) {
|
|
54
|
+
try {
|
|
55
|
+
const url = new URL(value);
|
|
56
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function containsHtmlTag(value) {
|
|
62
|
+
return /<[a-z!/][^>]*>/i.test(value);
|
|
63
|
+
}
|
|
64
|
+
function contentTypeForString(value, override) {
|
|
65
|
+
if (override) return override;
|
|
66
|
+
return containsHtmlTag(value) ? "text/html" : "text/plain; charset=utf-8";
|
|
67
|
+
}
|
|
68
|
+
function detectBytesContentType(bytes) {
|
|
69
|
+
const text = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
|
|
70
|
+
if (text.includes("\uFFFD")) return "application/octet-stream";
|
|
71
|
+
return containsHtmlTag(text) ? "text/html" : "text/plain; charset=utf-8";
|
|
72
|
+
}
|
|
73
|
+
function entryForContentType(contentType) {
|
|
74
|
+
const m = contentType.toLowerCase().split(";", 1)[0]?.trim() ?? "";
|
|
75
|
+
if (m === "text/html" || m === "application/xhtml+xml") return "index.html";
|
|
76
|
+
if (m === "application/json") return "index.json";
|
|
77
|
+
if (m === "text/css") return "index.css";
|
|
78
|
+
if (m === "text/javascript" || m === "application/javascript")
|
|
79
|
+
return "index.js";
|
|
80
|
+
if (m.startsWith("text/")) return "index.txt";
|
|
81
|
+
return "index";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/publish/paths.ts
|
|
85
|
+
function normalizeManifestPath(path) {
|
|
86
|
+
return path.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
87
|
+
}
|
|
88
|
+
function assertValidManifestPath(path) {
|
|
89
|
+
if (path.includes("\\"))
|
|
90
|
+
throw new PublishInputError(
|
|
91
|
+
"invalid_path",
|
|
92
|
+
`Manifest path must use "/": "${path}".`,
|
|
93
|
+
path
|
|
94
|
+
);
|
|
95
|
+
const p = normalizeManifestPath(path);
|
|
96
|
+
if (p === "")
|
|
97
|
+
throw new PublishInputError(
|
|
98
|
+
"invalid_path",
|
|
99
|
+
"Manifest path must not be empty.",
|
|
100
|
+
path
|
|
101
|
+
);
|
|
102
|
+
if (p.startsWith("/"))
|
|
103
|
+
throw new PublishInputError(
|
|
104
|
+
"invalid_path",
|
|
105
|
+
`Manifest path must be relative: "${path}".`,
|
|
106
|
+
path
|
|
107
|
+
);
|
|
108
|
+
if (p.startsWith("~"))
|
|
109
|
+
throw new PublishInputError(
|
|
110
|
+
"invalid_path",
|
|
111
|
+
`Manifest path must not start with "~": "${path}".`,
|
|
112
|
+
path
|
|
113
|
+
);
|
|
114
|
+
if (p.split("/").includes(".."))
|
|
115
|
+
throw new PublishInputError(
|
|
116
|
+
"invalid_path",
|
|
117
|
+
`Manifest path must not contain "..": "${path}".`,
|
|
118
|
+
path
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
function assertNoDuplicatePaths(paths) {
|
|
122
|
+
const seen = /* @__PURE__ */ new Set();
|
|
123
|
+
for (const raw of paths) {
|
|
124
|
+
const p = normalizeManifestPath(raw);
|
|
125
|
+
if (seen.has(p))
|
|
126
|
+
throw new PublishInputError(
|
|
127
|
+
"duplicate_path",
|
|
128
|
+
`Duplicate file path after normalization: "${p}".`,
|
|
129
|
+
p,
|
|
130
|
+
"Give each file a unique path; basename mapping for string[] can collide."
|
|
131
|
+
);
|
|
132
|
+
seen.add(p);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function assertNonEmptyBundle(paths) {
|
|
136
|
+
if (paths.length === 0)
|
|
137
|
+
throw new PublishInputError(
|
|
138
|
+
"empty_bundle",
|
|
139
|
+
"Nothing to publish: the bundle has no files.",
|
|
140
|
+
void 0,
|
|
141
|
+
"Pass at least one file, or a directory that contains files."
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/publish/core.ts
|
|
146
|
+
var VALID_VISIBILITIES = ["public", "unlisted"];
|
|
147
|
+
var MAX_TITLE_LENGTH = 500;
|
|
148
|
+
function validatePublishOptions(options) {
|
|
149
|
+
if (options.title !== void 0 && options.title !== null && options.title.length > MAX_TITLE_LENGTH) {
|
|
150
|
+
throw new Error(`Title must be ${MAX_TITLE_LENGTH} characters or less.`);
|
|
151
|
+
}
|
|
152
|
+
if (options.visibility !== void 0 && options.visibility !== null) {
|
|
153
|
+
if (!VALID_VISIBILITIES.includes(options.visibility)) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`Invalid visibility "${options.visibility}". Use "public" or "unlisted".`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (options.expiresAt !== void 0 && options.expiresAt !== null) {
|
|
160
|
+
if (options.expiresAt instanceof Date) {
|
|
161
|
+
if (Number.isNaN(options.expiresAt.getTime())) {
|
|
162
|
+
throw new Error("Invalid expiresAt date.");
|
|
163
|
+
}
|
|
164
|
+
} else if (typeof options.expiresAt === "string") {
|
|
165
|
+
const parsed = new Date(options.expiresAt);
|
|
166
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
167
|
+
throw new Error("Invalid expiresAt date.");
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function optionsBody(options) {
|
|
173
|
+
validatePublishOptions(options);
|
|
174
|
+
return {
|
|
175
|
+
title: options.title,
|
|
176
|
+
visibility: options.visibility,
|
|
177
|
+
password: options.password,
|
|
178
|
+
noindex: options.noindex,
|
|
179
|
+
expiresAt: options.expiresAt instanceof Date ? options.expiresAt.toISOString() : options.expiresAt
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function buildStagedRequest(files, options, entry) {
|
|
183
|
+
assertNonEmptyBundle(files.map((f) => f.path));
|
|
184
|
+
const normalizedPaths = files.map((f) => normalizeManifestPath(f.path));
|
|
185
|
+
assertNoDuplicatePaths(normalizedPaths);
|
|
186
|
+
const manifestFiles = files.map((file) => ({
|
|
187
|
+
path: normalizeManifestPath(file.path),
|
|
188
|
+
contentType: file.contentType,
|
|
189
|
+
sizeBytes: file.sizeBytes,
|
|
190
|
+
...file.checksumSha256 ? { checksumSha256: file.checksumSha256 } : {}
|
|
191
|
+
}));
|
|
192
|
+
const resolvedEntry = entry ?? options.entry;
|
|
193
|
+
if (resolvedEntry !== void 0) assertValidManifestPath(resolvedEntry);
|
|
194
|
+
const manifest = {
|
|
195
|
+
schemaVersion: 1,
|
|
196
|
+
files: manifestFiles,
|
|
197
|
+
...resolvedEntry ? { entry: resolvedEntry } : {}
|
|
198
|
+
};
|
|
199
|
+
const prepared = {
|
|
200
|
+
kind: "staged",
|
|
201
|
+
manifest,
|
|
202
|
+
files,
|
|
203
|
+
options: optionsBody(options)
|
|
204
|
+
};
|
|
205
|
+
if (options.metadata) prepared.metadata = options.metadata;
|
|
206
|
+
return prepared;
|
|
207
|
+
}
|
|
208
|
+
function buildSourceRequest(sourceUrl, options) {
|
|
209
|
+
if (!isHttpUrl(sourceUrl)) {
|
|
210
|
+
throw new PublishInputError(
|
|
211
|
+
"invalid_source_url",
|
|
212
|
+
"source_url must be an http(s) URL.",
|
|
213
|
+
sourceUrl,
|
|
214
|
+
"Fetch the content first, then pass it as content/files."
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
const prepared = {
|
|
218
|
+
kind: "source",
|
|
219
|
+
sourceUrl,
|
|
220
|
+
options: optionsBody(options)
|
|
221
|
+
};
|
|
222
|
+
if (options.metadata) prepared.metadata = options.metadata;
|
|
223
|
+
return prepared;
|
|
224
|
+
}
|
|
225
|
+
function decodeBase64(value) {
|
|
226
|
+
const binary = atob(value);
|
|
227
|
+
return Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
228
|
+
}
|
|
229
|
+
function fileBytes(file) {
|
|
230
|
+
if (file.content !== void 0) return new TextEncoder().encode(file.content);
|
|
231
|
+
if (file.contentBase64 !== void 0) return decodeBase64(file.contentBase64);
|
|
232
|
+
if (file.bytes !== void 0) return file.bytes;
|
|
233
|
+
throw new PublishInputError(
|
|
234
|
+
"missing_file_bytes",
|
|
235
|
+
`File "${file.path}" is missing content bytes.`,
|
|
236
|
+
file.path,
|
|
237
|
+
"Provide one of content, contentBase64, or bytes."
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
function singleStagedFile(path, contentType, bytes, options) {
|
|
241
|
+
return buildStagedRequest(
|
|
242
|
+
[
|
|
243
|
+
{
|
|
244
|
+
path,
|
|
245
|
+
contentType,
|
|
246
|
+
sizeBytes: bytes.byteLength,
|
|
247
|
+
getBody: async () => bytes
|
|
248
|
+
}
|
|
249
|
+
],
|
|
250
|
+
options
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
async function resolveInMemory(input, options = {}) {
|
|
254
|
+
if (input instanceof URL) {
|
|
255
|
+
return buildSourceRequest(input.toString(), options);
|
|
256
|
+
}
|
|
257
|
+
if (typeof input === "string") {
|
|
258
|
+
if (isHttpUrl(input)) return buildSourceRequest(input, options);
|
|
259
|
+
const contentType = contentTypeForString(input, options.contentType);
|
|
260
|
+
if (options.path !== void 0) assertValidManifestPath(options.path);
|
|
261
|
+
const path = options.path ?? entryForContentType(contentType);
|
|
262
|
+
const bytes = new TextEncoder().encode(input);
|
|
263
|
+
return singleStagedFile(path, contentType, bytes, options);
|
|
264
|
+
}
|
|
265
|
+
if (input instanceof Uint8Array) {
|
|
266
|
+
const contentType = options.contentType ?? detectBytesContentType(input);
|
|
267
|
+
if (options.path !== void 0) assertValidManifestPath(options.path);
|
|
268
|
+
const path = options.path ?? entryForContentType(contentType);
|
|
269
|
+
return singleStagedFile(path, contentType, input, options);
|
|
270
|
+
}
|
|
271
|
+
if (input.kind === "content") {
|
|
272
|
+
const contentType = input.contentType ?? options.contentType ?? "text/html";
|
|
273
|
+
if (input.path !== void 0) assertValidManifestPath(input.path);
|
|
274
|
+
if (options.path !== void 0) assertValidManifestPath(options.path);
|
|
275
|
+
const path = input.path ?? options.path ?? entryForContentType(contentType);
|
|
276
|
+
const bytes = new TextEncoder().encode(input.content);
|
|
277
|
+
return singleStagedFile(path, contentType, bytes, options);
|
|
278
|
+
}
|
|
279
|
+
if (input.kind === "source_url") {
|
|
280
|
+
return buildSourceRequest(input.sourceUrl, options);
|
|
281
|
+
}
|
|
282
|
+
const files = input.files.map((file) => {
|
|
283
|
+
assertValidManifestPath(file.path);
|
|
284
|
+
const bytes = fileBytes(file);
|
|
285
|
+
const contentType = file.contentType ?? detectBytesContentType(bytes);
|
|
286
|
+
return {
|
|
287
|
+
path: file.path,
|
|
288
|
+
contentType,
|
|
289
|
+
sizeBytes: bytes.byteLength,
|
|
290
|
+
getBody: async () => bytes
|
|
291
|
+
};
|
|
292
|
+
});
|
|
293
|
+
return buildStagedRequest(files, options, input.entry);
|
|
294
|
+
}
|
|
295
|
+
function errorResult(result) {
|
|
296
|
+
if (!result.error) throw new Error("Expected an error result");
|
|
297
|
+
return { data: null, error: result.error, headers: result.headers };
|
|
298
|
+
}
|
|
299
|
+
async function publishStaged(transport, prepared, finalPath, options = {}) {
|
|
300
|
+
const baseKey = options.idempotencyKey ?? `sdk-${crypto.randomUUID()}`;
|
|
301
|
+
const createResult = await transport.request(
|
|
302
|
+
"POST",
|
|
303
|
+
"/uploads",
|
|
304
|
+
{
|
|
305
|
+
body: prepared.manifest,
|
|
306
|
+
idempotencyKey: `${baseKey}:create-upload`
|
|
307
|
+
}
|
|
308
|
+
);
|
|
309
|
+
if (createResult.error) return errorResult(createResult);
|
|
310
|
+
for (const file of prepared.files) {
|
|
311
|
+
const target = createResult.data.files.find((f) => f.path === file.path);
|
|
312
|
+
if (!target) {
|
|
313
|
+
return createErrorResult(
|
|
314
|
+
"upload_target_missing",
|
|
315
|
+
`Upload target missing for ${file.path}`,
|
|
316
|
+
null
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
if (target.upload.strategy !== "single_put") {
|
|
320
|
+
return createErrorResult(
|
|
321
|
+
"unsupported_upload_strategy",
|
|
322
|
+
`Unsupported upload strategy "${target.upload.strategy}" for ${file.path}. This SDK uploads via single PUT only.`,
|
|
323
|
+
null
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
const put = await transport.putSignedUrl(
|
|
327
|
+
target.upload.url,
|
|
328
|
+
await file.getBody(),
|
|
329
|
+
target.upload.headers
|
|
330
|
+
);
|
|
331
|
+
if (put.error) return errorResult(put);
|
|
332
|
+
}
|
|
333
|
+
const completeResult = await transport.request(
|
|
334
|
+
"POST",
|
|
335
|
+
`/uploads/${encodeURIComponent(createResult.data.uploadId)}/complete`,
|
|
336
|
+
{
|
|
337
|
+
body: { files: {} },
|
|
338
|
+
idempotencyKey: `${baseKey}:complete-upload`
|
|
339
|
+
}
|
|
340
|
+
);
|
|
341
|
+
if (completeResult.error) return errorResult(completeResult);
|
|
342
|
+
const finalOptions = {
|
|
343
|
+
body: {
|
|
344
|
+
uploadId: createResult.data.uploadId,
|
|
345
|
+
options: prepared.options,
|
|
346
|
+
...prepared.metadata ? { metadata: prepared.metadata } : {}
|
|
347
|
+
},
|
|
348
|
+
idempotencyKey: `${baseKey}:publish`
|
|
349
|
+
};
|
|
350
|
+
if (options.ifRevision !== void 0)
|
|
351
|
+
finalOptions.ifRevision = options.ifRevision;
|
|
352
|
+
return transport.request("POST", finalPath, finalOptions);
|
|
353
|
+
}
|
|
354
|
+
async function publishSource(transport, prepared, finalPath, options = {}) {
|
|
355
|
+
const baseKey = options.idempotencyKey ?? `sdk-${crypto.randomUUID()}`;
|
|
356
|
+
return transport.request("POST", finalPath, {
|
|
357
|
+
body: {
|
|
358
|
+
sourceUrl: prepared.sourceUrl,
|
|
359
|
+
options: prepared.options,
|
|
360
|
+
...prepared.metadata ? { metadata: prepared.metadata } : {}
|
|
361
|
+
},
|
|
362
|
+
idempotencyKey: `${baseKey}:publish`,
|
|
363
|
+
...options.ifRevision !== void 0 ? { ifRevision: options.ifRevision } : {}
|
|
364
|
+
});
|
|
365
|
+
}
|
|
24
366
|
|
|
25
367
|
// src/resources/account.ts
|
|
26
368
|
var AccountResource = class {
|
|
@@ -65,33 +407,6 @@ var DeploymentsResource = class {
|
|
|
65
407
|
this.transport = transport;
|
|
66
408
|
}
|
|
67
409
|
transport;
|
|
68
|
-
create(dropId, body, options = {}) {
|
|
69
|
-
const requestOptions = { body };
|
|
70
|
-
if (options.idempotencyKey)
|
|
71
|
-
requestOptions.idempotencyKey = options.idempotencyKey;
|
|
72
|
-
if (options.ifRevision !== void 0)
|
|
73
|
-
requestOptions.ifRevision = options.ifRevision;
|
|
74
|
-
return this.transport.request(
|
|
75
|
-
"POST",
|
|
76
|
-
`/drops/${encodeURIComponent(dropId)}/deployments`,
|
|
77
|
-
requestOptions
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
createRaw(dropId, body, options = {}) {
|
|
81
|
-
const requestOptions = {
|
|
82
|
-
body,
|
|
83
|
-
bodyCase: "raw"
|
|
84
|
-
};
|
|
85
|
-
if (options.idempotencyKey)
|
|
86
|
-
requestOptions.idempotencyKey = options.idempotencyKey;
|
|
87
|
-
if (options.ifRevision !== void 0)
|
|
88
|
-
requestOptions.ifRevision = options.ifRevision;
|
|
89
|
-
return this.transport.request(
|
|
90
|
-
"POST",
|
|
91
|
-
`/drops/${encodeURIComponent(dropId)}/deployments`,
|
|
92
|
-
requestOptions
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
410
|
list(dropId, params = {}) {
|
|
96
411
|
return this.transport.request(
|
|
97
412
|
"GET",
|
|
@@ -146,21 +461,6 @@ var DropsResource = class {
|
|
|
146
461
|
this.transport = transport;
|
|
147
462
|
}
|
|
148
463
|
transport;
|
|
149
|
-
create(body, options = {}) {
|
|
150
|
-
const requestOptions = { body };
|
|
151
|
-
if (options.idempotencyKey)
|
|
152
|
-
requestOptions.idempotencyKey = options.idempotencyKey;
|
|
153
|
-
return this.transport.request("POST", "/drops", requestOptions);
|
|
154
|
-
}
|
|
155
|
-
createRaw(body, options = {}) {
|
|
156
|
-
const requestOptions = {
|
|
157
|
-
body,
|
|
158
|
-
bodyCase: "raw"
|
|
159
|
-
};
|
|
160
|
-
if (options.idempotencyKey)
|
|
161
|
-
requestOptions.idempotencyKey = options.idempotencyKey;
|
|
162
|
-
return this.transport.request("POST", "/drops", requestOptions);
|
|
163
|
-
}
|
|
164
464
|
async list(params = {}) {
|
|
165
465
|
const result = await this.transport.request("GET", "/drops", {
|
|
166
466
|
params
|
|
@@ -201,15 +501,22 @@ var DropsResource = class {
|
|
|
201
501
|
);
|
|
202
502
|
}
|
|
203
503
|
};
|
|
504
|
+
var SETTINGS = [
|
|
505
|
+
"title",
|
|
506
|
+
"visibility",
|
|
507
|
+
"password",
|
|
508
|
+
"noindex",
|
|
509
|
+
"expiresAt",
|
|
510
|
+
"slug"
|
|
511
|
+
];
|
|
204
512
|
function updateBody(options) {
|
|
205
|
-
const {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
...
|
|
211
|
-
}
|
|
212
|
-
return { options: dropOptions2, metadata };
|
|
513
|
+
const out = {};
|
|
514
|
+
for (const key of SETTINGS)
|
|
515
|
+
if (options[key] !== void 0) out[key] = options[key];
|
|
516
|
+
return {
|
|
517
|
+
options: out,
|
|
518
|
+
...options.metadata !== void 0 ? { metadata: options.metadata } : {}
|
|
519
|
+
};
|
|
213
520
|
}
|
|
214
521
|
|
|
215
522
|
// src/case.ts
|
|
@@ -247,25 +554,24 @@ function toSnakeCase(value) {
|
|
|
247
554
|
|
|
248
555
|
// src/transport.ts
|
|
249
556
|
var DEFAULT_BASE_URL = "https://api.dropthis.app";
|
|
250
|
-
var SDK_VERSION = "0.
|
|
557
|
+
var SDK_VERSION = "0.6.0";
|
|
251
558
|
var Transport = class {
|
|
252
559
|
apiKey;
|
|
253
560
|
baseUrl;
|
|
254
561
|
timeoutMs;
|
|
562
|
+
uploadTimeoutMs;
|
|
255
563
|
fetchImpl;
|
|
256
564
|
constructor(options = {}) {
|
|
257
565
|
const resolved = typeof options === "string" ? { apiKey: options } : options;
|
|
258
566
|
this.apiKey = resolved.apiKey ?? (typeof process !== "undefined" ? process.env?.DROPTHIS_API_KEY : void 0);
|
|
259
567
|
this.baseUrl = (resolved.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
260
568
|
this.timeoutMs = resolved.timeoutMs ?? 3e4;
|
|
569
|
+
this.uploadTimeoutMs = resolved.uploadTimeoutMs ?? 12e4;
|
|
261
570
|
this.fetchImpl = resolved.fetch ?? globalThis.fetch;
|
|
262
571
|
}
|
|
263
|
-
multipart(method, path, form, options = {}) {
|
|
264
|
-
return this.request(method, path, { ...options, body: form });
|
|
265
|
-
}
|
|
266
572
|
async putSignedUrl(url, body, headers) {
|
|
267
573
|
const controller = new AbortController();
|
|
268
|
-
const timeout = setTimeout(() => controller.abort(), this.
|
|
574
|
+
const timeout = setTimeout(() => controller.abort(), this.uploadTimeoutMs);
|
|
269
575
|
try {
|
|
270
576
|
const request = {
|
|
271
577
|
method: "PUT",
|
|
@@ -320,7 +626,7 @@ var Transport = class {
|
|
|
320
626
|
if (options.idempotencyKey)
|
|
321
627
|
headers["idempotency-key"] = options.idempotencyKey;
|
|
322
628
|
if (options.ifRevision !== void 0)
|
|
323
|
-
headers["
|
|
629
|
+
headers["if-revision"] = String(options.ifRevision);
|
|
324
630
|
if (options.body !== void 0 && !(options.body instanceof FormData))
|
|
325
631
|
headers["content-type"] = "application/json";
|
|
326
632
|
const url = new URL(`${this.baseUrl}${path}`);
|
|
@@ -352,11 +658,18 @@ var Transport = class {
|
|
|
352
658
|
const parsed = parseJson(text);
|
|
353
659
|
if (!response.ok) {
|
|
354
660
|
const body = parsed && typeof parsed === "object" ? parsed : {};
|
|
355
|
-
const message = stringValue(body.detail) ?? stringValue(body.
|
|
661
|
+
const message = stringValue(body.detail) ?? stringValue(body.title) ?? response.statusText;
|
|
356
662
|
const code = stringValue(body.code) ?? stringValue(body.error_code) ?? `http_${response.status}`;
|
|
357
663
|
const currentRevision = numberValue(body.current_revision);
|
|
664
|
+
const type = stringValue(body.type);
|
|
665
|
+
const title = stringValue(body.title);
|
|
666
|
+
const instance = stringValue(body.instance);
|
|
358
667
|
return createErrorResult(code, message, response.status, {
|
|
359
|
-
|
|
668
|
+
body,
|
|
669
|
+
...type !== null ? { type } : {},
|
|
670
|
+
...title !== null ? { title } : {},
|
|
671
|
+
...instance !== null ? { instance } : {},
|
|
672
|
+
detail: stringValue(body.detail),
|
|
360
673
|
param: stringValue(body.param),
|
|
361
674
|
...currentRevision !== void 0 ? { currentRevision } : {},
|
|
362
675
|
requestId: stringValue(body.request_id) ?? responseHeaders["x-request-id"] ?? null,
|
|
@@ -408,106 +721,6 @@ function booleanValue(value) {
|
|
|
408
721
|
}
|
|
409
722
|
|
|
410
723
|
// src/edge.ts
|
|
411
|
-
var DROP_OPTION_KEYS = [
|
|
412
|
-
"title",
|
|
413
|
-
"visibility",
|
|
414
|
-
"password",
|
|
415
|
-
"noindex",
|
|
416
|
-
"expiresAt"
|
|
417
|
-
];
|
|
418
|
-
function detectStringContentType(value) {
|
|
419
|
-
const trimmed = value.trimStart().toLowerCase();
|
|
420
|
-
if (trimmed.startsWith("<!doctype html") || trimmed.startsWith("<html") || /^<[a-z][a-z0-9:-]*(\s|>)/.test(trimmed)) {
|
|
421
|
-
return "text/html";
|
|
422
|
-
}
|
|
423
|
-
return "text/plain; charset=utf-8";
|
|
424
|
-
}
|
|
425
|
-
function defaultEntry(contentType) {
|
|
426
|
-
const media = contentType.split(";", 1)[0]?.trim().toLowerCase() ?? "";
|
|
427
|
-
return media === "text/html" ? "index.html" : "index.txt";
|
|
428
|
-
}
|
|
429
|
-
function normalizeInput(input, options) {
|
|
430
|
-
if (typeof input === "string") {
|
|
431
|
-
const contentType = options.contentType ?? detectStringContentType(input);
|
|
432
|
-
return {
|
|
433
|
-
bytes: new TextEncoder().encode(input),
|
|
434
|
-
path: options.path ?? defaultEntry(contentType),
|
|
435
|
-
contentType
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
return {
|
|
439
|
-
bytes: input.bytes,
|
|
440
|
-
path: input.filename ?? options.path ?? "index.html",
|
|
441
|
-
contentType: input.contentType ?? options.contentType ?? "application/octet-stream"
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
function dropOptions(options) {
|
|
445
|
-
const out = {};
|
|
446
|
-
for (const key of DROP_OPTION_KEYS) {
|
|
447
|
-
if (options[key] !== void 0) out[key] = options[key];
|
|
448
|
-
}
|
|
449
|
-
return out;
|
|
450
|
-
}
|
|
451
|
-
function errorResult(result) {
|
|
452
|
-
if (!result.error) throw new Error("Expected an error result");
|
|
453
|
-
return { data: null, error: result.error, headers: result.headers };
|
|
454
|
-
}
|
|
455
|
-
async function stagedInline(transport, input, options, finalPath) {
|
|
456
|
-
const { bytes, path, contentType } = normalizeInput(input, options);
|
|
457
|
-
const baseKey = options.idempotencyKey ?? `mcp-${crypto.randomUUID()}`;
|
|
458
|
-
const manifest = {
|
|
459
|
-
schemaVersion: 1,
|
|
460
|
-
files: [{ path, contentType, sizeBytes: bytes.byteLength }],
|
|
461
|
-
entry: path
|
|
462
|
-
};
|
|
463
|
-
const created = await transport.request(
|
|
464
|
-
"POST",
|
|
465
|
-
"/uploads",
|
|
466
|
-
{
|
|
467
|
-
body: manifest,
|
|
468
|
-
idempotencyKey: `${baseKey}:create-upload`
|
|
469
|
-
}
|
|
470
|
-
);
|
|
471
|
-
if (created.error) return errorResult(created);
|
|
472
|
-
const target = created.data.files[0];
|
|
473
|
-
if (!target) {
|
|
474
|
-
return createErrorResult(
|
|
475
|
-
"upload_target_missing",
|
|
476
|
-
"No upload target returned for the file.",
|
|
477
|
-
null
|
|
478
|
-
);
|
|
479
|
-
}
|
|
480
|
-
if (target.upload.strategy !== "single_put") {
|
|
481
|
-
return createErrorResult(
|
|
482
|
-
"unsupported_upload_strategy",
|
|
483
|
-
"Edge publishInline supports single_put uploads only; the content is too large for inline publishing.",
|
|
484
|
-
null
|
|
485
|
-
);
|
|
486
|
-
}
|
|
487
|
-
const put = await transport.putSignedUrl(
|
|
488
|
-
target.upload.url,
|
|
489
|
-
bytes,
|
|
490
|
-
target.upload.headers
|
|
491
|
-
);
|
|
492
|
-
if (put.error) return errorResult(put);
|
|
493
|
-
const completed = await transport.request(
|
|
494
|
-
"POST",
|
|
495
|
-
`/uploads/${encodeURIComponent(created.data.uploadId)}/complete`,
|
|
496
|
-
{ body: { files: {} }, idempotencyKey: `${baseKey}:complete-upload` }
|
|
497
|
-
);
|
|
498
|
-
if (completed.error) return errorResult(completed);
|
|
499
|
-
const finalOptions = {
|
|
500
|
-
body: {
|
|
501
|
-
uploadId: created.data.uploadId,
|
|
502
|
-
options: dropOptions(options),
|
|
503
|
-
...options.metadata ? { metadata: options.metadata } : {}
|
|
504
|
-
},
|
|
505
|
-
idempotencyKey: `${baseKey}:publish`
|
|
506
|
-
};
|
|
507
|
-
if (options.ifRevision !== void 0)
|
|
508
|
-
finalOptions.ifRevision = options.ifRevision;
|
|
509
|
-
return transport.request("POST", finalPath, finalOptions);
|
|
510
|
-
}
|
|
511
724
|
var DropthisEdge = class {
|
|
512
725
|
transport;
|
|
513
726
|
dropsResource;
|
|
@@ -538,17 +751,25 @@ var DropthisEdge = class {
|
|
|
538
751
|
return this.deploymentsResource;
|
|
539
752
|
}
|
|
540
753
|
/** Publish a NEW drop from in-memory content. */
|
|
541
|
-
|
|
542
|
-
|
|
754
|
+
async publish(input, options = {}) {
|
|
755
|
+
let prepared;
|
|
756
|
+
try {
|
|
757
|
+
prepared = await resolveInMemory(input, options);
|
|
758
|
+
} catch (e) {
|
|
759
|
+
return toErrorResult(e);
|
|
760
|
+
}
|
|
761
|
+
return prepared.kind === "source" ? publishSource(this.transport, prepared, "/drops", options) : publishStaged(this.transport, prepared, "/drops", options);
|
|
543
762
|
}
|
|
544
763
|
/** Publish a NEW content version to an existing drop, keeping its URL. */
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
input,
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
764
|
+
async deploy(dropId, input, options = {}) {
|
|
765
|
+
let prepared;
|
|
766
|
+
try {
|
|
767
|
+
prepared = await resolveInMemory(input, options);
|
|
768
|
+
} catch (e) {
|
|
769
|
+
return toErrorResult(e);
|
|
770
|
+
}
|
|
771
|
+
const path = `/drops/${encodeURIComponent(dropId)}/deployments`;
|
|
772
|
+
return prepared.kind === "source" ? publishSource(this.transport, prepared, path, options) : publishStaged(this.transport, prepared, path, options);
|
|
552
773
|
}
|
|
553
774
|
};
|
|
554
775
|
export {
|