@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
|
@@ -37,16 +37,358 @@ function createErrorResult(code, message, statusCode, extra = {}) {
|
|
|
37
37
|
message: redactSecrets(message),
|
|
38
38
|
statusCode,
|
|
39
39
|
...extra.type ? { type: extra.type } : {},
|
|
40
|
+
...extra.title !== void 0 ? { title: extra.title } : {},
|
|
40
41
|
...extra.detail !== void 0 ? { detail: extra.detail } : {},
|
|
42
|
+
...extra.instance !== void 0 ? { instance: extra.instance } : {},
|
|
41
43
|
...extra.param !== void 0 ? { param: extra.param } : {},
|
|
42
44
|
...extra.currentRevision !== void 0 ? { currentRevision: extra.currentRevision } : {},
|
|
43
45
|
...extra.requestId !== void 0 ? { requestId: extra.requestId } : {},
|
|
44
46
|
...extra.suggestion !== void 0 ? { suggestion: extra.suggestion } : {},
|
|
45
|
-
...extra.retryable !== void 0 ? { retryable: extra.retryable } : {}
|
|
47
|
+
...extra.retryable !== void 0 ? { retryable: extra.retryable } : {},
|
|
48
|
+
...extra.body !== void 0 ? { body: extra.body } : {}
|
|
46
49
|
},
|
|
47
50
|
headers: extra.headers ?? {}
|
|
48
51
|
};
|
|
49
52
|
}
|
|
53
|
+
var PublishInputError = class extends Error {
|
|
54
|
+
constructor(code, message, param, suggestion) {
|
|
55
|
+
super(message);
|
|
56
|
+
this.code = code;
|
|
57
|
+
this.param = param;
|
|
58
|
+
this.suggestion = suggestion;
|
|
59
|
+
this.name = "PublishInputError";
|
|
60
|
+
}
|
|
61
|
+
code;
|
|
62
|
+
param;
|
|
63
|
+
suggestion;
|
|
64
|
+
};
|
|
65
|
+
function toErrorResult(err) {
|
|
66
|
+
if (err instanceof PublishInputError)
|
|
67
|
+
return createErrorResult(err.code, err.message, null, {
|
|
68
|
+
param: err.param ?? null,
|
|
69
|
+
suggestion: err.suggestion ?? null
|
|
70
|
+
});
|
|
71
|
+
return createErrorResult(
|
|
72
|
+
"sdk_error",
|
|
73
|
+
err instanceof Error ? err.message : "Unknown SDK error",
|
|
74
|
+
null
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/publish/detect.ts
|
|
79
|
+
function isHttpUrl(value) {
|
|
80
|
+
try {
|
|
81
|
+
const url = new URL(value);
|
|
82
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
83
|
+
} catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function containsHtmlTag(value) {
|
|
88
|
+
return /<[a-z!/][^>]*>/i.test(value);
|
|
89
|
+
}
|
|
90
|
+
function contentTypeForString(value, override) {
|
|
91
|
+
if (override) return override;
|
|
92
|
+
return containsHtmlTag(value) ? "text/html" : "text/plain; charset=utf-8";
|
|
93
|
+
}
|
|
94
|
+
function detectBytesContentType(bytes) {
|
|
95
|
+
const text = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
|
|
96
|
+
if (text.includes("\uFFFD")) return "application/octet-stream";
|
|
97
|
+
return containsHtmlTag(text) ? "text/html" : "text/plain; charset=utf-8";
|
|
98
|
+
}
|
|
99
|
+
function entryForContentType(contentType) {
|
|
100
|
+
const m = contentType.toLowerCase().split(";", 1)[0]?.trim() ?? "";
|
|
101
|
+
if (m === "text/html" || m === "application/xhtml+xml") return "index.html";
|
|
102
|
+
if (m === "application/json") return "index.json";
|
|
103
|
+
if (m === "text/css") return "index.css";
|
|
104
|
+
if (m === "text/javascript" || m === "application/javascript")
|
|
105
|
+
return "index.js";
|
|
106
|
+
if (m.startsWith("text/")) return "index.txt";
|
|
107
|
+
return "index";
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// src/publish/paths.ts
|
|
111
|
+
function normalizeManifestPath(path) {
|
|
112
|
+
return path.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
113
|
+
}
|
|
114
|
+
function assertValidManifestPath(path) {
|
|
115
|
+
if (path.includes("\\"))
|
|
116
|
+
throw new PublishInputError(
|
|
117
|
+
"invalid_path",
|
|
118
|
+
`Manifest path must use "/": "${path}".`,
|
|
119
|
+
path
|
|
120
|
+
);
|
|
121
|
+
const p = normalizeManifestPath(path);
|
|
122
|
+
if (p === "")
|
|
123
|
+
throw new PublishInputError(
|
|
124
|
+
"invalid_path",
|
|
125
|
+
"Manifest path must not be empty.",
|
|
126
|
+
path
|
|
127
|
+
);
|
|
128
|
+
if (p.startsWith("/"))
|
|
129
|
+
throw new PublishInputError(
|
|
130
|
+
"invalid_path",
|
|
131
|
+
`Manifest path must be relative: "${path}".`,
|
|
132
|
+
path
|
|
133
|
+
);
|
|
134
|
+
if (p.startsWith("~"))
|
|
135
|
+
throw new PublishInputError(
|
|
136
|
+
"invalid_path",
|
|
137
|
+
`Manifest path must not start with "~": "${path}".`,
|
|
138
|
+
path
|
|
139
|
+
);
|
|
140
|
+
if (p.split("/").includes(".."))
|
|
141
|
+
throw new PublishInputError(
|
|
142
|
+
"invalid_path",
|
|
143
|
+
`Manifest path must not contain "..": "${path}".`,
|
|
144
|
+
path
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
function assertNoDuplicatePaths(paths) {
|
|
148
|
+
const seen = /* @__PURE__ */ new Set();
|
|
149
|
+
for (const raw of paths) {
|
|
150
|
+
const p = normalizeManifestPath(raw);
|
|
151
|
+
if (seen.has(p))
|
|
152
|
+
throw new PublishInputError(
|
|
153
|
+
"duplicate_path",
|
|
154
|
+
`Duplicate file path after normalization: "${p}".`,
|
|
155
|
+
p,
|
|
156
|
+
"Give each file a unique path; basename mapping for string[] can collide."
|
|
157
|
+
);
|
|
158
|
+
seen.add(p);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function assertNonEmptyBundle(paths) {
|
|
162
|
+
if (paths.length === 0)
|
|
163
|
+
throw new PublishInputError(
|
|
164
|
+
"empty_bundle",
|
|
165
|
+
"Nothing to publish: the bundle has no files.",
|
|
166
|
+
void 0,
|
|
167
|
+
"Pass at least one file, or a directory that contains files."
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/publish/core.ts
|
|
172
|
+
var VALID_VISIBILITIES = ["public", "unlisted"];
|
|
173
|
+
var MAX_TITLE_LENGTH = 500;
|
|
174
|
+
function validatePublishOptions(options) {
|
|
175
|
+
if (options.title !== void 0 && options.title !== null && options.title.length > MAX_TITLE_LENGTH) {
|
|
176
|
+
throw new Error(`Title must be ${MAX_TITLE_LENGTH} characters or less.`);
|
|
177
|
+
}
|
|
178
|
+
if (options.visibility !== void 0 && options.visibility !== null) {
|
|
179
|
+
if (!VALID_VISIBILITIES.includes(options.visibility)) {
|
|
180
|
+
throw new Error(
|
|
181
|
+
`Invalid visibility "${options.visibility}". Use "public" or "unlisted".`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (options.expiresAt !== void 0 && options.expiresAt !== null) {
|
|
186
|
+
if (options.expiresAt instanceof Date) {
|
|
187
|
+
if (Number.isNaN(options.expiresAt.getTime())) {
|
|
188
|
+
throw new Error("Invalid expiresAt date.");
|
|
189
|
+
}
|
|
190
|
+
} else if (typeof options.expiresAt === "string") {
|
|
191
|
+
const parsed = new Date(options.expiresAt);
|
|
192
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
193
|
+
throw new Error("Invalid expiresAt date.");
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function optionsBody(options) {
|
|
199
|
+
validatePublishOptions(options);
|
|
200
|
+
return {
|
|
201
|
+
title: options.title,
|
|
202
|
+
visibility: options.visibility,
|
|
203
|
+
password: options.password,
|
|
204
|
+
noindex: options.noindex,
|
|
205
|
+
expiresAt: options.expiresAt instanceof Date ? options.expiresAt.toISOString() : options.expiresAt
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function buildStagedRequest(files, options, entry) {
|
|
209
|
+
assertNonEmptyBundle(files.map((f) => f.path));
|
|
210
|
+
const normalizedPaths = files.map((f) => normalizeManifestPath(f.path));
|
|
211
|
+
assertNoDuplicatePaths(normalizedPaths);
|
|
212
|
+
const manifestFiles = files.map((file) => ({
|
|
213
|
+
path: normalizeManifestPath(file.path),
|
|
214
|
+
contentType: file.contentType,
|
|
215
|
+
sizeBytes: file.sizeBytes,
|
|
216
|
+
...file.checksumSha256 ? { checksumSha256: file.checksumSha256 } : {}
|
|
217
|
+
}));
|
|
218
|
+
const resolvedEntry = entry ?? options.entry;
|
|
219
|
+
if (resolvedEntry !== void 0) assertValidManifestPath(resolvedEntry);
|
|
220
|
+
const manifest = {
|
|
221
|
+
schemaVersion: 1,
|
|
222
|
+
files: manifestFiles,
|
|
223
|
+
...resolvedEntry ? { entry: resolvedEntry } : {}
|
|
224
|
+
};
|
|
225
|
+
const prepared = {
|
|
226
|
+
kind: "staged",
|
|
227
|
+
manifest,
|
|
228
|
+
files,
|
|
229
|
+
options: optionsBody(options)
|
|
230
|
+
};
|
|
231
|
+
if (options.metadata) prepared.metadata = options.metadata;
|
|
232
|
+
return prepared;
|
|
233
|
+
}
|
|
234
|
+
function buildSourceRequest(sourceUrl, options) {
|
|
235
|
+
if (!isHttpUrl(sourceUrl)) {
|
|
236
|
+
throw new PublishInputError(
|
|
237
|
+
"invalid_source_url",
|
|
238
|
+
"source_url must be an http(s) URL.",
|
|
239
|
+
sourceUrl,
|
|
240
|
+
"Fetch the content first, then pass it as content/files."
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
const prepared = {
|
|
244
|
+
kind: "source",
|
|
245
|
+
sourceUrl,
|
|
246
|
+
options: optionsBody(options)
|
|
247
|
+
};
|
|
248
|
+
if (options.metadata) prepared.metadata = options.metadata;
|
|
249
|
+
return prepared;
|
|
250
|
+
}
|
|
251
|
+
function decodeBase64(value) {
|
|
252
|
+
const binary = atob(value);
|
|
253
|
+
return Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
254
|
+
}
|
|
255
|
+
function fileBytes(file) {
|
|
256
|
+
if (file.content !== void 0) return new TextEncoder().encode(file.content);
|
|
257
|
+
if (file.contentBase64 !== void 0) return decodeBase64(file.contentBase64);
|
|
258
|
+
if (file.bytes !== void 0) return file.bytes;
|
|
259
|
+
throw new PublishInputError(
|
|
260
|
+
"missing_file_bytes",
|
|
261
|
+
`File "${file.path}" is missing content bytes.`,
|
|
262
|
+
file.path,
|
|
263
|
+
"Provide one of content, contentBase64, or bytes."
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
function singleStagedFile(path, contentType, bytes, options) {
|
|
267
|
+
return buildStagedRequest(
|
|
268
|
+
[
|
|
269
|
+
{
|
|
270
|
+
path,
|
|
271
|
+
contentType,
|
|
272
|
+
sizeBytes: bytes.byteLength,
|
|
273
|
+
getBody: async () => bytes
|
|
274
|
+
}
|
|
275
|
+
],
|
|
276
|
+
options
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
async function resolveInMemory(input, options = {}) {
|
|
280
|
+
if (input instanceof URL) {
|
|
281
|
+
return buildSourceRequest(input.toString(), options);
|
|
282
|
+
}
|
|
283
|
+
if (typeof input === "string") {
|
|
284
|
+
if (isHttpUrl(input)) return buildSourceRequest(input, options);
|
|
285
|
+
const contentType = contentTypeForString(input, options.contentType);
|
|
286
|
+
if (options.path !== void 0) assertValidManifestPath(options.path);
|
|
287
|
+
const path = options.path ?? entryForContentType(contentType);
|
|
288
|
+
const bytes = new TextEncoder().encode(input);
|
|
289
|
+
return singleStagedFile(path, contentType, bytes, options);
|
|
290
|
+
}
|
|
291
|
+
if (input instanceof Uint8Array) {
|
|
292
|
+
const contentType = options.contentType ?? detectBytesContentType(input);
|
|
293
|
+
if (options.path !== void 0) assertValidManifestPath(options.path);
|
|
294
|
+
const path = options.path ?? entryForContentType(contentType);
|
|
295
|
+
return singleStagedFile(path, contentType, input, options);
|
|
296
|
+
}
|
|
297
|
+
if (input.kind === "content") {
|
|
298
|
+
const contentType = input.contentType ?? options.contentType ?? "text/html";
|
|
299
|
+
if (input.path !== void 0) assertValidManifestPath(input.path);
|
|
300
|
+
if (options.path !== void 0) assertValidManifestPath(options.path);
|
|
301
|
+
const path = input.path ?? options.path ?? entryForContentType(contentType);
|
|
302
|
+
const bytes = new TextEncoder().encode(input.content);
|
|
303
|
+
return singleStagedFile(path, contentType, bytes, options);
|
|
304
|
+
}
|
|
305
|
+
if (input.kind === "source_url") {
|
|
306
|
+
return buildSourceRequest(input.sourceUrl, options);
|
|
307
|
+
}
|
|
308
|
+
const files = input.files.map((file) => {
|
|
309
|
+
assertValidManifestPath(file.path);
|
|
310
|
+
const bytes = fileBytes(file);
|
|
311
|
+
const contentType = file.contentType ?? detectBytesContentType(bytes);
|
|
312
|
+
return {
|
|
313
|
+
path: file.path,
|
|
314
|
+
contentType,
|
|
315
|
+
sizeBytes: bytes.byteLength,
|
|
316
|
+
getBody: async () => bytes
|
|
317
|
+
};
|
|
318
|
+
});
|
|
319
|
+
return buildStagedRequest(files, options, input.entry);
|
|
320
|
+
}
|
|
321
|
+
function errorResult(result) {
|
|
322
|
+
if (!result.error) throw new Error("Expected an error result");
|
|
323
|
+
return { data: null, error: result.error, headers: result.headers };
|
|
324
|
+
}
|
|
325
|
+
async function publishStaged(transport, prepared, finalPath, options = {}) {
|
|
326
|
+
const baseKey = options.idempotencyKey ?? `sdk-${crypto.randomUUID()}`;
|
|
327
|
+
const createResult = await transport.request(
|
|
328
|
+
"POST",
|
|
329
|
+
"/uploads",
|
|
330
|
+
{
|
|
331
|
+
body: prepared.manifest,
|
|
332
|
+
idempotencyKey: `${baseKey}:create-upload`
|
|
333
|
+
}
|
|
334
|
+
);
|
|
335
|
+
if (createResult.error) return errorResult(createResult);
|
|
336
|
+
for (const file of prepared.files) {
|
|
337
|
+
const target = createResult.data.files.find((f) => f.path === file.path);
|
|
338
|
+
if (!target) {
|
|
339
|
+
return createErrorResult(
|
|
340
|
+
"upload_target_missing",
|
|
341
|
+
`Upload target missing for ${file.path}`,
|
|
342
|
+
null
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
if (target.upload.strategy !== "single_put") {
|
|
346
|
+
return createErrorResult(
|
|
347
|
+
"unsupported_upload_strategy",
|
|
348
|
+
`Unsupported upload strategy "${target.upload.strategy}" for ${file.path}. This SDK uploads via single PUT only.`,
|
|
349
|
+
null
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
const put = await transport.putSignedUrl(
|
|
353
|
+
target.upload.url,
|
|
354
|
+
await file.getBody(),
|
|
355
|
+
target.upload.headers
|
|
356
|
+
);
|
|
357
|
+
if (put.error) return errorResult(put);
|
|
358
|
+
}
|
|
359
|
+
const completeResult = await transport.request(
|
|
360
|
+
"POST",
|
|
361
|
+
`/uploads/${encodeURIComponent(createResult.data.uploadId)}/complete`,
|
|
362
|
+
{
|
|
363
|
+
body: { files: {} },
|
|
364
|
+
idempotencyKey: `${baseKey}:complete-upload`
|
|
365
|
+
}
|
|
366
|
+
);
|
|
367
|
+
if (completeResult.error) return errorResult(completeResult);
|
|
368
|
+
const finalOptions = {
|
|
369
|
+
body: {
|
|
370
|
+
uploadId: createResult.data.uploadId,
|
|
371
|
+
options: prepared.options,
|
|
372
|
+
...prepared.metadata ? { metadata: prepared.metadata } : {}
|
|
373
|
+
},
|
|
374
|
+
idempotencyKey: `${baseKey}:publish`
|
|
375
|
+
};
|
|
376
|
+
if (options.ifRevision !== void 0)
|
|
377
|
+
finalOptions.ifRevision = options.ifRevision;
|
|
378
|
+
return transport.request("POST", finalPath, finalOptions);
|
|
379
|
+
}
|
|
380
|
+
async function publishSource(transport, prepared, finalPath, options = {}) {
|
|
381
|
+
const baseKey = options.idempotencyKey ?? `sdk-${crypto.randomUUID()}`;
|
|
382
|
+
return transport.request("POST", finalPath, {
|
|
383
|
+
body: {
|
|
384
|
+
sourceUrl: prepared.sourceUrl,
|
|
385
|
+
options: prepared.options,
|
|
386
|
+
...prepared.metadata ? { metadata: prepared.metadata } : {}
|
|
387
|
+
},
|
|
388
|
+
idempotencyKey: `${baseKey}:publish`,
|
|
389
|
+
...options.ifRevision !== void 0 ? { ifRevision: options.ifRevision } : {}
|
|
390
|
+
});
|
|
391
|
+
}
|
|
50
392
|
|
|
51
393
|
// src/resources/account.ts
|
|
52
394
|
var AccountResource = class {
|
|
@@ -91,33 +433,6 @@ var DeploymentsResource = class {
|
|
|
91
433
|
this.transport = transport;
|
|
92
434
|
}
|
|
93
435
|
transport;
|
|
94
|
-
create(dropId, body, options = {}) {
|
|
95
|
-
const requestOptions = { body };
|
|
96
|
-
if (options.idempotencyKey)
|
|
97
|
-
requestOptions.idempotencyKey = options.idempotencyKey;
|
|
98
|
-
if (options.ifRevision !== void 0)
|
|
99
|
-
requestOptions.ifRevision = options.ifRevision;
|
|
100
|
-
return this.transport.request(
|
|
101
|
-
"POST",
|
|
102
|
-
`/drops/${encodeURIComponent(dropId)}/deployments`,
|
|
103
|
-
requestOptions
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
createRaw(dropId, body, options = {}) {
|
|
107
|
-
const requestOptions = {
|
|
108
|
-
body,
|
|
109
|
-
bodyCase: "raw"
|
|
110
|
-
};
|
|
111
|
-
if (options.idempotencyKey)
|
|
112
|
-
requestOptions.idempotencyKey = options.idempotencyKey;
|
|
113
|
-
if (options.ifRevision !== void 0)
|
|
114
|
-
requestOptions.ifRevision = options.ifRevision;
|
|
115
|
-
return this.transport.request(
|
|
116
|
-
"POST",
|
|
117
|
-
`/drops/${encodeURIComponent(dropId)}/deployments`,
|
|
118
|
-
requestOptions
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
436
|
list(dropId, params = {}) {
|
|
122
437
|
return this.transport.request(
|
|
123
438
|
"GET",
|
|
@@ -172,21 +487,6 @@ var DropsResource = class {
|
|
|
172
487
|
this.transport = transport;
|
|
173
488
|
}
|
|
174
489
|
transport;
|
|
175
|
-
create(body, options = {}) {
|
|
176
|
-
const requestOptions = { body };
|
|
177
|
-
if (options.idempotencyKey)
|
|
178
|
-
requestOptions.idempotencyKey = options.idempotencyKey;
|
|
179
|
-
return this.transport.request("POST", "/drops", requestOptions);
|
|
180
|
-
}
|
|
181
|
-
createRaw(body, options = {}) {
|
|
182
|
-
const requestOptions = {
|
|
183
|
-
body,
|
|
184
|
-
bodyCase: "raw"
|
|
185
|
-
};
|
|
186
|
-
if (options.idempotencyKey)
|
|
187
|
-
requestOptions.idempotencyKey = options.idempotencyKey;
|
|
188
|
-
return this.transport.request("POST", "/drops", requestOptions);
|
|
189
|
-
}
|
|
190
490
|
async list(params = {}) {
|
|
191
491
|
const result = await this.transport.request("GET", "/drops", {
|
|
192
492
|
params
|
|
@@ -227,15 +527,22 @@ var DropsResource = class {
|
|
|
227
527
|
);
|
|
228
528
|
}
|
|
229
529
|
};
|
|
530
|
+
var SETTINGS = [
|
|
531
|
+
"title",
|
|
532
|
+
"visibility",
|
|
533
|
+
"password",
|
|
534
|
+
"noindex",
|
|
535
|
+
"expiresAt",
|
|
536
|
+
"slug"
|
|
537
|
+
];
|
|
230
538
|
function updateBody(options) {
|
|
231
|
-
const {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
...
|
|
237
|
-
}
|
|
238
|
-
return { options: dropOptions2, metadata };
|
|
539
|
+
const out = {};
|
|
540
|
+
for (const key of SETTINGS)
|
|
541
|
+
if (options[key] !== void 0) out[key] = options[key];
|
|
542
|
+
return {
|
|
543
|
+
options: out,
|
|
544
|
+
...options.metadata !== void 0 ? { metadata: options.metadata } : {}
|
|
545
|
+
};
|
|
239
546
|
}
|
|
240
547
|
|
|
241
548
|
// src/case.ts
|
|
@@ -273,25 +580,24 @@ function toSnakeCase(value) {
|
|
|
273
580
|
|
|
274
581
|
// src/transport.ts
|
|
275
582
|
var DEFAULT_BASE_URL = "https://api.dropthis.app";
|
|
276
|
-
var SDK_VERSION = "0.
|
|
583
|
+
var SDK_VERSION = "0.6.0";
|
|
277
584
|
var Transport = class {
|
|
278
585
|
apiKey;
|
|
279
586
|
baseUrl;
|
|
280
587
|
timeoutMs;
|
|
588
|
+
uploadTimeoutMs;
|
|
281
589
|
fetchImpl;
|
|
282
590
|
constructor(options = {}) {
|
|
283
591
|
const resolved = typeof options === "string" ? { apiKey: options } : options;
|
|
284
592
|
this.apiKey = resolved.apiKey ?? (typeof process !== "undefined" ? process.env?.DROPTHIS_API_KEY : void 0);
|
|
285
593
|
this.baseUrl = (resolved.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
286
594
|
this.timeoutMs = resolved.timeoutMs ?? 3e4;
|
|
595
|
+
this.uploadTimeoutMs = resolved.uploadTimeoutMs ?? 12e4;
|
|
287
596
|
this.fetchImpl = resolved.fetch ?? globalThis.fetch;
|
|
288
597
|
}
|
|
289
|
-
multipart(method, path, form, options = {}) {
|
|
290
|
-
return this.request(method, path, { ...options, body: form });
|
|
291
|
-
}
|
|
292
598
|
async putSignedUrl(url, body, headers) {
|
|
293
599
|
const controller = new AbortController();
|
|
294
|
-
const timeout = setTimeout(() => controller.abort(), this.
|
|
600
|
+
const timeout = setTimeout(() => controller.abort(), this.uploadTimeoutMs);
|
|
295
601
|
try {
|
|
296
602
|
const request = {
|
|
297
603
|
method: "PUT",
|
|
@@ -346,7 +652,7 @@ var Transport = class {
|
|
|
346
652
|
if (options.idempotencyKey)
|
|
347
653
|
headers["idempotency-key"] = options.idempotencyKey;
|
|
348
654
|
if (options.ifRevision !== void 0)
|
|
349
|
-
headers["
|
|
655
|
+
headers["if-revision"] = String(options.ifRevision);
|
|
350
656
|
if (options.body !== void 0 && !(options.body instanceof FormData))
|
|
351
657
|
headers["content-type"] = "application/json";
|
|
352
658
|
const url = new URL(`${this.baseUrl}${path}`);
|
|
@@ -378,11 +684,18 @@ var Transport = class {
|
|
|
378
684
|
const parsed = parseJson(text);
|
|
379
685
|
if (!response.ok) {
|
|
380
686
|
const body = parsed && typeof parsed === "object" ? parsed : {};
|
|
381
|
-
const message = stringValue(body.detail) ?? stringValue(body.
|
|
687
|
+
const message = stringValue(body.detail) ?? stringValue(body.title) ?? response.statusText;
|
|
382
688
|
const code = stringValue(body.code) ?? stringValue(body.error_code) ?? `http_${response.status}`;
|
|
383
689
|
const currentRevision = numberValue(body.current_revision);
|
|
690
|
+
const type = stringValue(body.type);
|
|
691
|
+
const title = stringValue(body.title);
|
|
692
|
+
const instance = stringValue(body.instance);
|
|
384
693
|
return createErrorResult(code, message, response.status, {
|
|
385
|
-
|
|
694
|
+
body,
|
|
695
|
+
...type !== null ? { type } : {},
|
|
696
|
+
...title !== null ? { title } : {},
|
|
697
|
+
...instance !== null ? { instance } : {},
|
|
698
|
+
detail: stringValue(body.detail),
|
|
386
699
|
param: stringValue(body.param),
|
|
387
700
|
...currentRevision !== void 0 ? { currentRevision } : {},
|
|
388
701
|
requestId: stringValue(body.request_id) ?? responseHeaders["x-request-id"] ?? null,
|
|
@@ -434,106 +747,6 @@ function booleanValue(value) {
|
|
|
434
747
|
}
|
|
435
748
|
|
|
436
749
|
// src/edge.ts
|
|
437
|
-
var DROP_OPTION_KEYS = [
|
|
438
|
-
"title",
|
|
439
|
-
"visibility",
|
|
440
|
-
"password",
|
|
441
|
-
"noindex",
|
|
442
|
-
"expiresAt"
|
|
443
|
-
];
|
|
444
|
-
function detectStringContentType(value) {
|
|
445
|
-
const trimmed = value.trimStart().toLowerCase();
|
|
446
|
-
if (trimmed.startsWith("<!doctype html") || trimmed.startsWith("<html") || /^<[a-z][a-z0-9:-]*(\s|>)/.test(trimmed)) {
|
|
447
|
-
return "text/html";
|
|
448
|
-
}
|
|
449
|
-
return "text/plain; charset=utf-8";
|
|
450
|
-
}
|
|
451
|
-
function defaultEntry(contentType) {
|
|
452
|
-
const media = contentType.split(";", 1)[0]?.trim().toLowerCase() ?? "";
|
|
453
|
-
return media === "text/html" ? "index.html" : "index.txt";
|
|
454
|
-
}
|
|
455
|
-
function normalizeInput(input, options) {
|
|
456
|
-
if (typeof input === "string") {
|
|
457
|
-
const contentType = options.contentType ?? detectStringContentType(input);
|
|
458
|
-
return {
|
|
459
|
-
bytes: new TextEncoder().encode(input),
|
|
460
|
-
path: options.path ?? defaultEntry(contentType),
|
|
461
|
-
contentType
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
return {
|
|
465
|
-
bytes: input.bytes,
|
|
466
|
-
path: input.filename ?? options.path ?? "index.html",
|
|
467
|
-
contentType: input.contentType ?? options.contentType ?? "application/octet-stream"
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
function dropOptions(options) {
|
|
471
|
-
const out = {};
|
|
472
|
-
for (const key of DROP_OPTION_KEYS) {
|
|
473
|
-
if (options[key] !== void 0) out[key] = options[key];
|
|
474
|
-
}
|
|
475
|
-
return out;
|
|
476
|
-
}
|
|
477
|
-
function errorResult(result) {
|
|
478
|
-
if (!result.error) throw new Error("Expected an error result");
|
|
479
|
-
return { data: null, error: result.error, headers: result.headers };
|
|
480
|
-
}
|
|
481
|
-
async function stagedInline(transport, input, options, finalPath) {
|
|
482
|
-
const { bytes, path, contentType } = normalizeInput(input, options);
|
|
483
|
-
const baseKey = options.idempotencyKey ?? `mcp-${crypto.randomUUID()}`;
|
|
484
|
-
const manifest = {
|
|
485
|
-
schemaVersion: 1,
|
|
486
|
-
files: [{ path, contentType, sizeBytes: bytes.byteLength }],
|
|
487
|
-
entry: path
|
|
488
|
-
};
|
|
489
|
-
const created = await transport.request(
|
|
490
|
-
"POST",
|
|
491
|
-
"/uploads",
|
|
492
|
-
{
|
|
493
|
-
body: manifest,
|
|
494
|
-
idempotencyKey: `${baseKey}:create-upload`
|
|
495
|
-
}
|
|
496
|
-
);
|
|
497
|
-
if (created.error) return errorResult(created);
|
|
498
|
-
const target = created.data.files[0];
|
|
499
|
-
if (!target) {
|
|
500
|
-
return createErrorResult(
|
|
501
|
-
"upload_target_missing",
|
|
502
|
-
"No upload target returned for the file.",
|
|
503
|
-
null
|
|
504
|
-
);
|
|
505
|
-
}
|
|
506
|
-
if (target.upload.strategy !== "single_put") {
|
|
507
|
-
return createErrorResult(
|
|
508
|
-
"unsupported_upload_strategy",
|
|
509
|
-
"Edge publishInline supports single_put uploads only; the content is too large for inline publishing.",
|
|
510
|
-
null
|
|
511
|
-
);
|
|
512
|
-
}
|
|
513
|
-
const put = await transport.putSignedUrl(
|
|
514
|
-
target.upload.url,
|
|
515
|
-
bytes,
|
|
516
|
-
target.upload.headers
|
|
517
|
-
);
|
|
518
|
-
if (put.error) return errorResult(put);
|
|
519
|
-
const completed = await transport.request(
|
|
520
|
-
"POST",
|
|
521
|
-
`/uploads/${encodeURIComponent(created.data.uploadId)}/complete`,
|
|
522
|
-
{ body: { files: {} }, idempotencyKey: `${baseKey}:complete-upload` }
|
|
523
|
-
);
|
|
524
|
-
if (completed.error) return errorResult(completed);
|
|
525
|
-
const finalOptions = {
|
|
526
|
-
body: {
|
|
527
|
-
uploadId: created.data.uploadId,
|
|
528
|
-
options: dropOptions(options),
|
|
529
|
-
...options.metadata ? { metadata: options.metadata } : {}
|
|
530
|
-
},
|
|
531
|
-
idempotencyKey: `${baseKey}:publish`
|
|
532
|
-
};
|
|
533
|
-
if (options.ifRevision !== void 0)
|
|
534
|
-
finalOptions.ifRevision = options.ifRevision;
|
|
535
|
-
return transport.request("POST", finalPath, finalOptions);
|
|
536
|
-
}
|
|
537
750
|
var DropthisEdge = class {
|
|
538
751
|
transport;
|
|
539
752
|
dropsResource;
|
|
@@ -564,17 +777,25 @@ var DropthisEdge = class {
|
|
|
564
777
|
return this.deploymentsResource;
|
|
565
778
|
}
|
|
566
779
|
/** Publish a NEW drop from in-memory content. */
|
|
567
|
-
|
|
568
|
-
|
|
780
|
+
async publish(input, options = {}) {
|
|
781
|
+
let prepared;
|
|
782
|
+
try {
|
|
783
|
+
prepared = await resolveInMemory(input, options);
|
|
784
|
+
} catch (e) {
|
|
785
|
+
return toErrorResult(e);
|
|
786
|
+
}
|
|
787
|
+
return prepared.kind === "source" ? publishSource(this.transport, prepared, "/drops", options) : publishStaged(this.transport, prepared, "/drops", options);
|
|
569
788
|
}
|
|
570
789
|
/** Publish a NEW content version to an existing drop, keeping its URL. */
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
input,
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
790
|
+
async deploy(dropId, input, options = {}) {
|
|
791
|
+
let prepared;
|
|
792
|
+
try {
|
|
793
|
+
prepared = await resolveInMemory(input, options);
|
|
794
|
+
} catch (e) {
|
|
795
|
+
return toErrorResult(e);
|
|
796
|
+
}
|
|
797
|
+
const path = `/drops/${encodeURIComponent(dropId)}/deployments`;
|
|
798
|
+
return prepared.kind === "source" ? publishSource(this.transport, prepared, path, options) : publishStaged(this.transport, prepared, path, options);
|
|
578
799
|
}
|
|
579
800
|
};
|
|
580
801
|
// Annotate the CommonJS export names for ESM import in node:
|