@dropthis/cli 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,805 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/edge.ts
21
+ var edge_exports = {};
22
+ __export(edge_exports, {
23
+ DropthisEdge: () => DropthisEdge
24
+ });
25
+ module.exports = __toCommonJS(edge_exports);
26
+
27
+ // src/errors.ts
28
+ var API_KEY_RE = /sk_[A-Za-z0-9_-]+/g;
29
+ function redactSecrets(message) {
30
+ return message.replace(API_KEY_RE, "sk_[redacted]");
31
+ }
32
+ function createErrorResult(code, message, statusCode, extra = {}) {
33
+ return {
34
+ data: null,
35
+ error: {
36
+ code,
37
+ message: redactSecrets(message),
38
+ statusCode,
39
+ ...extra.type ? { type: extra.type } : {},
40
+ ...extra.title !== void 0 ? { title: extra.title } : {},
41
+ ...extra.detail !== void 0 ? { detail: extra.detail } : {},
42
+ ...extra.instance !== void 0 ? { instance: extra.instance } : {},
43
+ ...extra.param !== void 0 ? { param: extra.param } : {},
44
+ ...extra.currentRevision !== void 0 ? { currentRevision: extra.currentRevision } : {},
45
+ ...extra.requestId !== void 0 ? { requestId: extra.requestId } : {},
46
+ ...extra.suggestion !== void 0 ? { suggestion: extra.suggestion } : {},
47
+ ...extra.retryable !== void 0 ? { retryable: extra.retryable } : {},
48
+ ...extra.body !== void 0 ? { body: extra.body } : {}
49
+ },
50
+ headers: extra.headers ?? {}
51
+ };
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
+ }
392
+
393
+ // src/resources/account.ts
394
+ var AccountResource = class {
395
+ constructor(transport) {
396
+ this.transport = transport;
397
+ }
398
+ transport;
399
+ get() {
400
+ return this.transport.request("GET", "/account");
401
+ }
402
+ update(input) {
403
+ return this.transport.request("PATCH", "/account", { body: input });
404
+ }
405
+ delete() {
406
+ return this.transport.request("DELETE", "/account");
407
+ }
408
+ };
409
+
410
+ // src/resources/api-keys.ts
411
+ var ApiKeysResource = class {
412
+ constructor(transport) {
413
+ this.transport = transport;
414
+ }
415
+ transport;
416
+ list() {
417
+ return this.transport.request("GET", "/api-keys");
418
+ }
419
+ create(input) {
420
+ return this.transport.request("POST", "/api-keys", { body: input });
421
+ }
422
+ delete(keyId) {
423
+ return this.transport.request(
424
+ "DELETE",
425
+ `/api-keys/${encodeURIComponent(keyId)}`
426
+ );
427
+ }
428
+ };
429
+
430
+ // src/resources/deployments.ts
431
+ var DeploymentsResource = class {
432
+ constructor(transport) {
433
+ this.transport = transport;
434
+ }
435
+ transport;
436
+ list(dropId, params = {}) {
437
+ return this.transport.request(
438
+ "GET",
439
+ `/drops/${encodeURIComponent(dropId)}/deployments`,
440
+ { params }
441
+ );
442
+ }
443
+ get(dropId, deploymentId) {
444
+ return this.transport.request(
445
+ "GET",
446
+ `/drops/${encodeURIComponent(dropId)}/deployments/${encodeURIComponent(deploymentId)}`
447
+ );
448
+ }
449
+ };
450
+
451
+ // src/pagination.ts
452
+ var CursorPage = class {
453
+ object = "list";
454
+ data;
455
+ hasMore;
456
+ nextCursor;
457
+ fetchNextPage;
458
+ constructor(input) {
459
+ this.data = input.data;
460
+ this.hasMore = input.hasMore;
461
+ this.nextCursor = input.nextCursor;
462
+ this.fetchNextPage = input.fetchNextPage;
463
+ }
464
+ async autoPagingToArray(options = {}) {
465
+ const items = [];
466
+ for await (const item of this) {
467
+ items.push(item);
468
+ if (options.limit !== void 0 && items.length >= options.limit) break;
469
+ }
470
+ return items;
471
+ }
472
+ async *[Symbol.asyncIterator]() {
473
+ for (const item of this.data) yield item;
474
+ let current = this;
475
+ while (current.hasMore && current.fetchNextPage) {
476
+ const next = await current.fetchNextPage();
477
+ if (next.error) break;
478
+ current = next.data;
479
+ for (const item of current.data) yield item;
480
+ }
481
+ }
482
+ };
483
+
484
+ // src/resources/drops.ts
485
+ var DropsResource = class {
486
+ constructor(transport) {
487
+ this.transport = transport;
488
+ }
489
+ transport;
490
+ async list(params = {}) {
491
+ const result = await this.transport.request("GET", "/drops", {
492
+ params
493
+ });
494
+ if (result.error) return result;
495
+ const page = new CursorPage({
496
+ data: result.data.drops,
497
+ nextCursor: result.data.nextCursor,
498
+ hasMore: result.data.nextCursor !== null,
499
+ fetchNextPage: result.data.nextCursor ? () => this.list({ ...params, cursor: result.data.nextCursor }) : void 0
500
+ });
501
+ return { data: page, error: null, headers: result.headers };
502
+ }
503
+ get(dropId) {
504
+ return this.transport.request(
505
+ "GET",
506
+ `/drops/${encodeURIComponent(dropId)}`
507
+ );
508
+ }
509
+ update(dropId, options = {}) {
510
+ const requestOptions = {
511
+ body: updateBody(options)
512
+ };
513
+ if (options.idempotencyKey)
514
+ requestOptions.idempotencyKey = options.idempotencyKey;
515
+ if (options.ifRevision !== void 0)
516
+ requestOptions.ifRevision = options.ifRevision;
517
+ return this.transport.request(
518
+ "PATCH",
519
+ `/drops/${encodeURIComponent(dropId)}`,
520
+ requestOptions
521
+ );
522
+ }
523
+ delete(dropId) {
524
+ return this.transport.request(
525
+ "DELETE",
526
+ `/drops/${encodeURIComponent(dropId)}`
527
+ );
528
+ }
529
+ };
530
+ var SETTINGS = [
531
+ "title",
532
+ "visibility",
533
+ "password",
534
+ "noindex",
535
+ "expiresAt",
536
+ "slug"
537
+ ];
538
+ function updateBody(options) {
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
+ };
546
+ }
547
+
548
+ // src/case.ts
549
+ function toCamelKey(key) {
550
+ return key.replace(/_([a-z])/g, (_, char) => char.toUpperCase());
551
+ }
552
+ function toSnakeKey(key) {
553
+ return key.replace(/[A-Z]/g, (char) => `_${char.toLowerCase()}`);
554
+ }
555
+ var PASSTHROUGH_KEYS = /* @__PURE__ */ new Set(["metadata"]);
556
+ function toCamelCase(value) {
557
+ if (Array.isArray(value)) return value.map(toCamelCase);
558
+ if (value && typeof value === "object") {
559
+ return Object.fromEntries(
560
+ Object.entries(value).map(([key, item]) => [
561
+ toCamelKey(key),
562
+ PASSTHROUGH_KEYS.has(key) ? item : toCamelCase(item)
563
+ ])
564
+ );
565
+ }
566
+ return value;
567
+ }
568
+ function toSnakeCase(value) {
569
+ if (Array.isArray(value)) return value.map(toSnakeCase);
570
+ if (value && typeof value === "object") {
571
+ return Object.fromEntries(
572
+ Object.entries(value).filter(([, item]) => item !== void 0).map(([key, item]) => [
573
+ toSnakeKey(key),
574
+ PASSTHROUGH_KEYS.has(key) ? item : toSnakeCase(item)
575
+ ])
576
+ );
577
+ }
578
+ return value;
579
+ }
580
+
581
+ // src/transport.ts
582
+ var DEFAULT_BASE_URL = "https://api.dropthis.app";
583
+ var SDK_VERSION = "0.6.0";
584
+ var Transport = class {
585
+ apiKey;
586
+ baseUrl;
587
+ timeoutMs;
588
+ uploadTimeoutMs;
589
+ fetchImpl;
590
+ constructor(options = {}) {
591
+ const resolved = typeof options === "string" ? { apiKey: options } : options;
592
+ this.apiKey = resolved.apiKey ?? (typeof process !== "undefined" ? process.env?.DROPTHIS_API_KEY : void 0);
593
+ this.baseUrl = (resolved.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
594
+ this.timeoutMs = resolved.timeoutMs ?? 3e4;
595
+ this.uploadTimeoutMs = resolved.uploadTimeoutMs ?? 12e4;
596
+ this.fetchImpl = resolved.fetch ?? globalThis.fetch;
597
+ }
598
+ async putSignedUrl(url, body, headers) {
599
+ const controller = new AbortController();
600
+ const timeout = setTimeout(() => controller.abort(), this.uploadTimeoutMs);
601
+ try {
602
+ const request = {
603
+ method: "PUT",
604
+ headers,
605
+ body,
606
+ signal: controller.signal
607
+ };
608
+ if (body instanceof ReadableStream) request.duplex = "half";
609
+ const response = await this.fetchImpl(url, request);
610
+ const responseHeaders = headersToObject(response.headers);
611
+ if (!response.ok) {
612
+ const text = await response.text();
613
+ return createErrorResult(
614
+ `signed_upload_${response.status}`,
615
+ text || response.statusText,
616
+ response.status,
617
+ { headers: responseHeaders }
618
+ );
619
+ }
620
+ return {
621
+ data: {
622
+ etag: response.headers.get("ETag") ?? responseHeaders.etag ?? null
623
+ },
624
+ error: null,
625
+ headers: responseHeaders
626
+ };
627
+ } catch (error) {
628
+ const message = error instanceof Error && error.name === "AbortError" ? "Request timed out" : error instanceof Error ? error.message : "Network error";
629
+ return createErrorResult(
630
+ error instanceof Error && error.name === "AbortError" ? "timeout" : "network_error",
631
+ message,
632
+ null
633
+ );
634
+ } finally {
635
+ clearTimeout(timeout);
636
+ }
637
+ }
638
+ async request(method, path, options = {}) {
639
+ const authenticated = options.authenticated ?? true;
640
+ if (authenticated && !this.apiKey) {
641
+ return createErrorResult(
642
+ "missing_api_key",
643
+ "No API key provided. Pass apiKey or set DROPTHIS_API_KEY.",
644
+ null
645
+ );
646
+ }
647
+ const headers = {
648
+ "user-agent": `dropthis-node/${SDK_VERSION}`
649
+ };
650
+ if (authenticated && this.apiKey)
651
+ headers.authorization = `Bearer ${this.apiKey}`;
652
+ if (options.idempotencyKey)
653
+ headers["idempotency-key"] = options.idempotencyKey;
654
+ if (options.ifRevision !== void 0)
655
+ headers["if-revision"] = String(options.ifRevision);
656
+ if (options.body !== void 0 && !(options.body instanceof FormData))
657
+ headers["content-type"] = "application/json";
658
+ const url = new URL(`${this.baseUrl}${path}`);
659
+ const wireParams = options.bodyCase === "raw" ? options.params : toSnakeCase(options.params ?? {});
660
+ for (const [key, value] of Object.entries(wireParams ?? {})) {
661
+ if (value !== void 0 && value !== null)
662
+ url.searchParams.set(key, String(value));
663
+ }
664
+ const controller = new AbortController();
665
+ const timeout = setTimeout(
666
+ () => controller.abort(),
667
+ options.timeoutMs ?? this.timeoutMs
668
+ );
669
+ try {
670
+ const request = {
671
+ method,
672
+ headers,
673
+ signal: controller.signal
674
+ };
675
+ if (options.body instanceof FormData) {
676
+ request.body = options.body;
677
+ } else if (options.body !== void 0) {
678
+ const wireBody = options.bodyCase === "raw" ? options.body : toSnakeCase(options.body);
679
+ request.body = JSON.stringify(wireBody);
680
+ }
681
+ const response = await this.fetchImpl(url.toString(), request);
682
+ const responseHeaders = headersToObject(response.headers);
683
+ const text = await response.text();
684
+ const parsed = parseJson(text);
685
+ if (!response.ok) {
686
+ const body = parsed && typeof parsed === "object" ? parsed : {};
687
+ const message = stringValue(body.detail) ?? stringValue(body.title) ?? response.statusText;
688
+ const code = stringValue(body.code) ?? stringValue(body.error_code) ?? `http_${response.status}`;
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);
693
+ return createErrorResult(code, message, response.status, {
694
+ body,
695
+ ...type !== null ? { type } : {},
696
+ ...title !== null ? { title } : {},
697
+ ...instance !== null ? { instance } : {},
698
+ detail: stringValue(body.detail),
699
+ param: stringValue(body.param),
700
+ ...currentRevision !== void 0 ? { currentRevision } : {},
701
+ requestId: stringValue(body.request_id) ?? responseHeaders["x-request-id"] ?? null,
702
+ suggestion: stringValue(body.suggestion),
703
+ retryable: booleanValue(body.retryable),
704
+ headers: responseHeaders
705
+ });
706
+ }
707
+ return {
708
+ data: toCamelCase(parsed),
709
+ error: null,
710
+ headers: responseHeaders
711
+ };
712
+ } catch (error) {
713
+ const message = error instanceof Error && error.name === "AbortError" ? "Request timed out" : error instanceof Error ? error.message : "Network error";
714
+ return createErrorResult(
715
+ error instanceof Error && error.name === "AbortError" ? "timeout" : "network_error",
716
+ message,
717
+ null
718
+ );
719
+ } finally {
720
+ clearTimeout(timeout);
721
+ }
722
+ }
723
+ };
724
+ function headersToObject(headers) {
725
+ const result = {};
726
+ headers.forEach((value, key) => {
727
+ result[key.toLowerCase()] = value;
728
+ });
729
+ return result;
730
+ }
731
+ function parseJson(text) {
732
+ if (!text) return null;
733
+ try {
734
+ return JSON.parse(text);
735
+ } catch {
736
+ return text;
737
+ }
738
+ }
739
+ function stringValue(value) {
740
+ return typeof value === "string" ? value : null;
741
+ }
742
+ function numberValue(value) {
743
+ return typeof value === "number" ? value : void 0;
744
+ }
745
+ function booleanValue(value) {
746
+ return typeof value === "boolean" ? value : null;
747
+ }
748
+
749
+ // src/edge.ts
750
+ var DropthisEdge = class {
751
+ transport;
752
+ dropsResource;
753
+ accountResource;
754
+ apiKeysResource;
755
+ deploymentsResource;
756
+ constructor(options = {}) {
757
+ this.transport = new Transport(options);
758
+ }
759
+ get drops() {
760
+ if (!this.dropsResource)
761
+ this.dropsResource = new DropsResource(this.transport);
762
+ return this.dropsResource;
763
+ }
764
+ get account() {
765
+ if (!this.accountResource)
766
+ this.accountResource = new AccountResource(this.transport);
767
+ return this.accountResource;
768
+ }
769
+ get apiKeys() {
770
+ if (!this.apiKeysResource)
771
+ this.apiKeysResource = new ApiKeysResource(this.transport);
772
+ return this.apiKeysResource;
773
+ }
774
+ get deployments() {
775
+ if (!this.deploymentsResource)
776
+ this.deploymentsResource = new DeploymentsResource(this.transport);
777
+ return this.deploymentsResource;
778
+ }
779
+ /** Publish a NEW drop from in-memory content. */
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);
788
+ }
789
+ /** Publish a NEW content version to an existing drop, keeping its URL. */
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);
799
+ }
800
+ };
801
+ // Annotate the CommonJS export names for ESM import in node:
802
+ 0 && (module.exports = {
803
+ DropthisEdge
804
+ });
805
+ //# sourceMappingURL=edge.cjs.map