@dropthis/cli 0.9.3 → 0.11.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.
@@ -17,8 +17,14 @@ const dropthis = new Dropthis({ apiKey: "sk_..." });
17
17
  const { data, error } = await dropthis.drops.publish("<h1>Hello</h1>");
18
18
 
19
19
  console.log(data.url); // https://abc123.dropthis.app
20
+ console.log(data.id); // drop_… — keep this; it's how you update or delete later
20
21
  ```
21
22
 
23
+ > **Keep the `drop_…` id.** `publish()` never takes an id — every call creates a NEW drop.
24
+ > To change something already published, pass the id from the publish response to
25
+ > `drops.updateContent()` (the files at the URL) or `drops.updateSettings()` (title,
26
+ > visibility, expiry, …). Lost the id? Recover it from the URL with `drops.resolve()`.
27
+
22
28
  ## Usage
23
29
 
24
30
  ### Publish an HTML string
@@ -45,7 +51,7 @@ const { data } = await dropthis.drops.publish("./dist");
45
51
  const { data } = await dropthis.drops.publish("./dist", {
46
52
  title: "Q4 Report",
47
53
  visibility: "unlisted",
48
- password: "s3cret",
54
+ noindex: true,
49
55
  expiresAt: "2026-12-31T00:00:00Z",
50
56
  });
51
57
  ```
@@ -66,6 +72,71 @@ const updated = await dropthis.drops.updateContent(created.data.id, "./dist-v2",
66
72
  await dropthis.drops.updateSettings("drop_abc123", { title: "New title" });
67
73
  ```
68
74
 
75
+ ### Resolve a URL back to its drop
76
+
77
+ Lost the `drop_…` id? Resolve the drop's URL (or bare slug) back to the drop. The slug is
78
+ parsed client-side from the first hostname label of a dropthis hostname
79
+ (`<slug>.dropthis.app`, or `<slug>.` + your configured `baseUrl` host), then matched against
80
+ your own drops via `GET /drops?slug=`. Custom-domain URLs are not resolvable yet — they are
81
+ rejected client-side with an `invalid_drop_url` error, without hitting the API.
82
+
83
+ ```typescript
84
+ const { data } = await dropthis.drops.resolve("https://my-report.dropthis.app/");
85
+
86
+ if (data) {
87
+ console.log(data.id); // drop_… — use this for updateContent/updateSettings/delete
88
+ } else {
89
+ // none of your drops has that slug
90
+ }
91
+ ```
92
+
93
+ ### Read back what a drop is serving
94
+
95
+ `drops.getContent()` is the owner-side read-back (it works regardless of any viewer
96
+ password). By default it returns a JSON manifest of the current deployment's files;
97
+ pass `path` to download one file's exact stored bytes.
98
+
99
+ ```typescript
100
+ // Manifest of the current deployment
101
+ const manifest = await dropthis.drops.getContent("drop_abc123");
102
+ console.log(manifest.data.files); // [{ path, contentType, sizeBytes }, …]
103
+
104
+ // One file's bytes (and a text() helper)
105
+ const file = await dropthis.drops.getContent("drop_abc123", { path: "index.html" });
106
+ console.log(file.data.contentType); // "text/html"
107
+ console.log(file.data.text()); // "<h1>…"
108
+
109
+ // A historical (even superseded) deployment — this is also the rollback path:
110
+ // download the old version's files and republish them with updateContent().
111
+ const old = await dropthis.drops.getContent("drop_abc123", {
112
+ deploymentId: "dep_xyz789",
113
+ path: "index.html",
114
+ });
115
+ ```
116
+
117
+ ### Safe concurrent edits with ifRevision
118
+
119
+ Every drop response carries a `revision`. Pass it back as `ifRevision` on
120
+ `updateContent()` / `updateSettings()` to make the update conditional: if someone else
121
+ changed the drop in between, the API answers 409 instead of clobbering, and the error
122
+ exposes the server's `currentRevision` so you can re-read and retry.
123
+
124
+ ```typescript
125
+ // 1. Read
126
+ const drop = await dropthis.drops.get("drop_abc123");
127
+
128
+ // 2. Edit locally (e.g. via getContent read-back), then
129
+ // 3. Update conditionally
130
+ const result = await dropthis.drops.updateContent(drop.data.id, "./dist-v2", {
131
+ ifRevision: drop.data.revision,
132
+ });
133
+
134
+ if (result.error?.statusCode === 409) {
135
+ console.log("Drop changed underneath us; server is at revision", result.error.currentRevision);
136
+ // re-read with drops.get(), merge, retry with the fresh revision
137
+ }
138
+ ```
139
+
69
140
  ## Supported inputs
70
141
 
71
142
  The `drops.publish()` and `drops.updateContent()` methods accept:
@@ -82,7 +153,7 @@ The `drops.publish()` and `drops.updateContent()` methods accept:
82
153
 
83
154
  Drop **settings** (title, visibility, password, noindex, expiresAt, metadata) go in the second `options` argument, not in the input object.
84
155
 
85
- All inputs are uploaded through staged presigned URLs. The SDK handles this transparently.
156
+ All inputs are uploaded through staged presigned URLs — one signed PUT per file, up to 5 files in parallel. The SDK handles this transparently.
86
157
 
87
158
  ### Explicit input examples
88
159
 
@@ -174,6 +245,9 @@ const dropthis = new Dropthis("sk_...");
174
245
  ```typescript
175
246
  await dropthis.drops.list({ limit: 20 });
176
247
  await dropthis.drops.get("drop_abc123");
248
+ await dropthis.drops.resolve("https://my-report.dropthis.app/"); // URL/slug → drop (or null)
249
+ await dropthis.drops.getContent("drop_abc123"); // manifest of served files
250
+ await dropthis.drops.getContent("drop_abc123", { path: "index.html" }); // one file's bytes
177
251
  await dropthis.drops.updateSettings("drop_abc123", { title: "Updated" });
178
252
  await dropthis.drops.delete("drop_abc123");
179
253
  ```
@@ -209,7 +283,7 @@ await dropthis.uploads.create({
209
283
  files: [{ path: "index.html", contentType: "text/html", sizeBytes: 1024 }],
210
284
  });
211
285
  await dropthis.uploads.get("upl_abc123");
212
- await dropthis.uploads.complete("upl_abc123", { files: {} });
286
+ await dropthis.uploads.complete("upl_abc123"); // no body — server verifies against the manifest
213
287
  await dropthis.uploads.cancel("upl_abc123");
214
288
  ```
215
289
 
@@ -218,7 +292,7 @@ await dropthis.uploads.cancel("upl_abc123");
218
292
  ```typescript
219
293
  await dropthis.auth.requestEmailOtp({ email: "you@example.com" });
220
294
  await dropthis.auth.verifyEmailOtp({ email: "you@example.com", code: "123456" });
221
- await dropthis.auth.logout();
295
+ await dropthis.auth.logout(); // 204 No Content — data is null
222
296
  ```
223
297
 
224
298
  ### apiKeys
@@ -226,17 +300,76 @@ await dropthis.auth.logout();
226
300
  ```typescript
227
301
  await dropthis.apiKeys.create({ label: "CI" });
228
302
  await dropthis.apiKeys.list();
229
- await dropthis.apiKeys.delete("key_abc123");
303
+ await dropthis.apiKeys.delete("key_abc123"); // 204 No Content — data is null
230
304
  ```
231
305
 
232
306
  ### account
233
307
 
234
308
  ```typescript
235
- await dropthis.account.get();
309
+ const { data } = await dropthis.account.get();
310
+ // data.limits carries your plan's limits — use them to size a publish first:
311
+ // { name, maxSizeBytes, defaultTtlSeconds, maxStorageBytes }
312
+
236
313
  await dropthis.account.update({ displayName: "Jane Doe" });
237
314
  await dropthis.account.delete();
238
315
  ```
239
316
 
317
+ ### domains
318
+
319
+ Custom domains let you serve drops on your own hostname instead of the shared pool. There are two
320
+ modes: **path** (many drops at `hostname/{slug}/`) and **dedicated** (one drop at the hostname root).
321
+
322
+ ```typescript
323
+ // 1. Connect the domain — returns DNS instructions (status: "pending_dns")
324
+ const { data: domain } = await dropthis.domains.connect({
325
+ hostname: "drops.example.com",
326
+ mode: "path",
327
+ });
328
+
329
+ // 2. Create the CNAME at your DNS provider:
330
+ // drops.example.com CNAME edge.dropthis.app
331
+ const dnsRecord = domain.dns[0];
332
+ // dnsRecord.name → "drops.example.com"
333
+ // dnsRecord.value → "edge.dropthis.app"
334
+
335
+ // 3. Verify — call repeatedly until status is "live"
336
+ const { data: verified } = await dropthis.domains.verify("drops.example.com");
337
+ // Use verified.dns[0].retryAfter (seconds) as the polling interval while status !== "live"
338
+ // verified.status → "live"
339
+
340
+ // 4. Publish to the custom domain (path mode: specify a vanity slug, or omit for a random one)
341
+ const { data: drop } = await dropthis.drops.publish("<h1>Hello</h1>", {
342
+ domain: "drops.example.com",
343
+ slug: "summer-sale",
344
+ });
345
+ // drop.url → "https://drops.example.com/summer-sale/"
346
+ ```
347
+
348
+ Other domain operations:
349
+
350
+ ```typescript
351
+ await dropthis.domains.list();
352
+ await dropthis.domains.get("drops.example.com");
353
+
354
+ // Repoint a dedicated domain to a different drop
355
+ await dropthis.domains.update("bio.example.com", { dropId: "drop_abc123" });
356
+
357
+ // Set a path-mode domain as the account's publish default
358
+ await dropthis.domains.update("drops.example.com", { default: true });
359
+
360
+ // Delete (remove your DNS CNAME after this to prevent re-claim by another account)
361
+ await dropthis.domains.delete("drops.example.com");
362
+ ```
363
+
364
+ ## Pricing tiers
365
+
366
+ - **Free ($0)** — drops expire after 7 days, 5 MB per drop, dropthis badge.
367
+ - **Personal ($5/mo)** — drops stay live while subscribed (no expiry), no badge, 100 MB per drop, 2 GB account storage.
368
+ - **Pro ($19/mo)** — everything in Personal plus password protection, custom domains, and analytics.
369
+
370
+ `account.get().data.limits` reflects your active tier. Note: password protection ships
371
+ with Pro and is not enabled on any tier yet — the API rejects the `password` option until then.
372
+
240
373
  ## Cloudflare Workers (edge)
241
374
 
242
375
  Use the fs-free entry point for Cloudflare Workers and other edge runtimes. It does not import `node:fs`, `node:path`, or `node:crypto`.
@@ -250,7 +383,7 @@ const { data, error } = await dropthis.drops.publish("<h1>Hello from the edge</h
250
383
 
251
384
  `DropthisEdge` accepts the in-memory subset of `PublishInput`: inline strings, `Uint8Array`, `URL`, and the explicit `{ kind: "content" }`, `{ kind: "source_url" }`, and `{ kind: "files" }` forms. Local file paths and `string[]` path arrays are not supported (no filesystem on the edge).
252
385
 
253
- `DropthisEdge` exposes the drop lifecycle through `drops.publish(input, options?)`, `drops.updateContent(dropId, input, options?)`, `drops.updateSettings`, `drops.get`, `drops.list`, and `drops.delete`, plus the `deployments`, `account`, and `apiKeys` resource accessors — the same surface as the Node client.
386
+ `DropthisEdge` exposes the drop lifecycle through `drops.publish(input, options?)`, `drops.updateContent(dropId, input, options?)`, `drops.updateSettings`, `drops.get`, `drops.list`, `drops.resolve`, `drops.getContent`, and `drops.delete`, plus the `deployments`, `account`, `apiKeys`, and `domains` resource accessors — the same surface as the Node client.
254
387
 
255
388
  ## Types
256
389
 
@@ -258,12 +391,18 @@ Key types exported from the package:
258
391
 
259
392
  ```typescript
260
393
  import type {
394
+ AccountLimits,
395
+ AccountResponse,
261
396
  DropthisClientOptions,
262
397
  DropthisResult,
263
398
  DropthisErrorResponse,
264
399
  DropResponse,
265
400
  DropDeploymentResponse,
266
401
  DropOptions,
402
+ DeploymentContentManifest,
403
+ DeploymentContentFile,
404
+ DropContentFile,
405
+ GetContentOptions,
267
406
  PrepareOptions,
268
407
  RequestControls,
269
408
  PublishOptions,
@@ -153,6 +153,17 @@ type ApiKeyCreatedResponse = ApiKeyResponse & {
153
153
  accountId?: string | null;
154
154
  isNewAccount?: boolean;
155
155
  };
156
+ /** Active plan tier limits — use these to size a publish before uploading. */
157
+ type AccountLimits = {
158
+ /** Plan tier the limits apply to. */
159
+ name: string;
160
+ /** Maximum size of a single drop in bytes. */
161
+ maxSizeBytes: number;
162
+ /** Drop lifetime in seconds before expiry; null means drops are permanent. */
163
+ defaultTtlSeconds: number | null;
164
+ /** Total account storage cap in bytes; null means no account-level cap. */
165
+ maxStorageBytes: number | null;
166
+ };
156
167
  type AccountResponse = {
157
168
  id: string;
158
169
  email: string;
@@ -160,6 +171,7 @@ type AccountResponse = {
160
171
  plan: string;
161
172
  status: string;
162
173
  createdAt: string;
174
+ limits: AccountLimits;
163
175
  };
164
176
  type ListDropsParams = {
165
177
  cursor?: string | null;
@@ -177,7 +189,8 @@ type CreateUploadSessionRequest = {
177
189
  entry?: string | null;
178
190
  };
179
191
  type UploadTarget = {
180
- strategy: "single_put" | "multipart";
192
+ /** Always `single_put` one signed PUT per file is the upload contract. */
193
+ strategy: "single_put";
181
194
  url: string;
182
195
  headers: Record<string, string>;
183
196
  expiresAt: string;
@@ -210,14 +223,6 @@ type CreateUploadSessionResponse = {
210
223
  files: CreateUploadSessionFileResponse[];
211
224
  nextAction?: Action | null;
212
225
  };
213
- type CompleteUploadSessionRequest = {
214
- files?: Record<string, {
215
- parts?: Array<{
216
- partNumber: number;
217
- etag: string;
218
- }> | null;
219
- }>;
220
- };
221
226
  type DropOptions = {
222
227
  /** Drop title. */
223
228
  title?: string;
@@ -233,6 +238,20 @@ type DropOptions = {
233
238
  entry?: string;
234
239
  /** Attach JSON key-value pairs, e.g. `{ source: "ci" }`. */
235
240
  metadata?: Record<string, unknown>;
241
+ /**
242
+ * Hostname of a custom domain connected to this account (must be live — see
243
+ * `client.domains`). Path-mode domains serve the drop at `https://{domain}/{slug}/`;
244
+ * dedicated domains serve it at the root and conflict (409) once occupied. Omit to
245
+ * use the account's default path domain if one exists, else the shared pool.
246
+ */
247
+ domain?: string | null;
248
+ /**
249
+ * Vanity slug — only valid when the target is a path-mode custom domain. 1–63
250
+ * lowercase letters/digits/hyphens (no leading/trailing/double hyphen). Taken slugs
251
+ * are auto-suffixed; omit for a random slug. Setting this on the shared pool returns
252
+ * 422.
253
+ */
254
+ slug?: string | null;
236
255
  };
237
256
  type PrepareOptions = {
238
257
  /** Glob patterns to ignore when publishing directories. */
@@ -281,6 +300,119 @@ type PublishInput = string | string[] | URL | Uint8Array | {
281
300
  files: PublishFileInput[];
282
301
  entry?: string;
283
302
  };
303
+ /** Structured next-step hint returned by domain operations (and publish). */
304
+ type NextHint = {
305
+ action: string;
306
+ message: string;
307
+ };
308
+ /** One DNS record instruction or diagnostic for a custom domain. */
309
+ type DnsRecord = {
310
+ /** Purpose of the record, e.g. "routing". */
311
+ purpose: string;
312
+ /** DNS record type, e.g. "CNAME". */
313
+ type: string;
314
+ /** The DNS name to create the record for. */
315
+ name: string;
316
+ /** The value the record must point at. */
317
+ value: string;
318
+ /** Current DNS status: "missing" | "ok" | "mismatch". */
319
+ status: "missing" | "ok" | "mismatch";
320
+ /** What DoH currently resolves for this record (verify only). */
321
+ observed?: string | null;
322
+ /** Specific guidance for fixing this record. */
323
+ hint?: string | null;
324
+ /** Seconds to wait before retrying verify while DNS/cert propagates. */
325
+ retryAfter?: number | null;
326
+ };
327
+ /** Full domain resource representation. */
328
+ type DomainResponse = {
329
+ object: "domain";
330
+ /** Stable domain identifier. */
331
+ id: string;
332
+ /** Canonical hostname registered with dropthis. */
333
+ hostname: string;
334
+ /** Mount mode: "path" (many drops at hostname/{slug}/) or "dedicated" (one drop at hostname/). */
335
+ mode: "path" | "dedicated";
336
+ /** Lifecycle status: "pending_dns" | "verifying" | "live" | "failed". */
337
+ status: "pending_dns" | "verifying" | "live" | "failed";
338
+ /** Reason for failure status. */
339
+ failureReason?: string | null;
340
+ /** Whether this is the account's default publish domain. */
341
+ default: boolean;
342
+ /** Mounted drop id (dedicated mode only). */
343
+ dropId?: string | null;
344
+ /** DNS records required for this domain. */
345
+ dns: DnsRecord[];
346
+ /** Creation timestamp. */
347
+ createdAt: string;
348
+ /** When the domain first reached "live" status. */
349
+ verifiedAt?: string | null;
350
+ /** Structured next-step hints for the agent. */
351
+ next: NextHint[];
352
+ };
353
+ /** List of domains for the account. */
354
+ type DomainListResponse = {
355
+ object: "domain.list";
356
+ /** Domains connected to this account. */
357
+ domains: DomainResponse[];
358
+ };
359
+ /** Response body for a successful domain deletion. */
360
+ type DomainDeletedResponse = {
361
+ object: "domain.deleted";
362
+ /** Id of the deleted domain. */
363
+ id: string;
364
+ /** Hostname of the deleted domain. */
365
+ hostname: string;
366
+ /** Dangling-CNAME risk warning (always present; instructs you to remove the DNS record). */
367
+ warning: string;
368
+ };
369
+ /** One readable file in a deployment's content manifest. */
370
+ type DeploymentContentFile = {
371
+ /** File path within the deployment, relative to its root. */
372
+ path: string;
373
+ /** Stored MIME type the file is served with. */
374
+ contentType: string;
375
+ /** Stored file size in bytes. */
376
+ sizeBytes: number;
377
+ };
378
+ /**
379
+ * Manifest of one deployment's readable files (content read-back).
380
+ * Fetch a single file's bytes with `drops.getContent(dropId, { path })`.
381
+ */
382
+ type DeploymentContentManifest = {
383
+ /** Parent drop identifier. */
384
+ dropId: string;
385
+ /** Deployment identifier. */
386
+ deploymentId: string;
387
+ /** Content revision of this deployment. */
388
+ revision: number;
389
+ /** Deployment lifecycle status. */
390
+ status: string;
391
+ /** Total deployment size in bytes. */
392
+ sizeBytes: number;
393
+ /** Entry path served at the drop root. */
394
+ entry?: string | null;
395
+ /** Readable files in this deployment; pass files[].path as `path` to download one. */
396
+ files: DeploymentContentFile[];
397
+ };
398
+ /** Options for `drops.getContent()`. */
399
+ type GetContentOptions = {
400
+ /** Read a historical deployment instead of the current one. */
401
+ deploymentId?: string;
402
+ /** Download this single file's raw stored bytes instead of the JSON manifest. */
403
+ path?: string;
404
+ };
405
+ /** A single file downloaded via `drops.getContent(dropId, { path })`. */
406
+ type DropContentFile = {
407
+ /** The requested file path. */
408
+ path: string;
409
+ /** Stored MIME type the file is served with (from the response Content-Type). */
410
+ contentType: string | null;
411
+ /** Exact stored bytes. */
412
+ bytes: Uint8Array;
413
+ /** Decode the bytes as UTF-8 text. */
414
+ text(): string;
415
+ };
284
416
 
285
417
  declare class Transport {
286
418
  readonly apiKey: string | undefined;
@@ -292,6 +424,17 @@ declare class Transport {
292
424
  putSignedUrl(url: string, body: Uint8Array | Blob | ReadableStream, headers: Record<string, string>): Promise<DropthisResult<{
293
425
  etag: string | null;
294
426
  }>>;
427
+ /**
428
+ * Authenticated GET that returns the raw response bytes untouched (no JSON parsing,
429
+ * no case conversion). Error responses are still parsed as problem+json. Used for
430
+ * content read-back (`drops.getContent` with a file path).
431
+ */
432
+ requestBytes(path: string, options?: RequestOptions & {
433
+ params?: Record<string, string | number | boolean | null | undefined>;
434
+ }): Promise<DropthisResult<{
435
+ bytes: Uint8Array;
436
+ contentType: string | null;
437
+ }>>;
295
438
  request<T>(method: string, path: string, options?: RequestOptions & {
296
439
  body?: unknown;
297
440
  bodyCase?: "snake" | "raw";
@@ -372,9 +515,8 @@ declare class ApiKeysResource {
372
515
  create(input: {
373
516
  label: string;
374
517
  }): Promise<DropthisResult<ApiKeyCreatedResponse>>;
375
- delete(keyId: string): Promise<DropthisResult<{
376
- ok: true;
377
- }>>;
518
+ /** Revoke an API key. 204 No Content — data is null on success. */
519
+ delete(keyId: string): Promise<DropthisResult<null>>;
378
520
  }
379
521
 
380
522
  declare class DeploymentsResource {
@@ -384,6 +526,45 @@ declare class DeploymentsResource {
384
526
  get(dropId: string, deploymentId: string): Promise<DropthisResult<DropDeploymentResponse>>;
385
527
  }
386
528
 
529
+ declare class DomainsResource {
530
+ private readonly transport;
531
+ constructor(transport: Transport);
532
+ /**
533
+ * Connect a custom domain to the account. Returns the domain in `pending_dns` status with
534
+ * DNS instructions. Idempotent on (account, hostname) — re-connecting an already-connected
535
+ * domain returns the existing row. POST /domains.
536
+ */
537
+ connect(input: {
538
+ hostname: string;
539
+ mode: "path" | "dedicated";
540
+ }): Promise<DropthisResult<DomainResponse>>;
541
+ /** List all custom domains connected to this account. GET /domains. */
542
+ list(): Promise<DropthisResult<DomainListResponse>>;
543
+ /** Get a domain by its stable id or hostname. GET /domains/{id_or_hostname}. */
544
+ get(idOrHostname: string): Promise<DropthisResult<DomainResponse>>;
545
+ /**
546
+ * Trigger a DNS + Cloudflare verification check. Returns the domain with updated status and
547
+ * per-record diagnostics. If DNS is still propagating, `dns[].retryAfter` tells you when to
548
+ * re-call. POST /domains/{id_or_hostname}/verify.
549
+ */
550
+ verify(idOrHostname: string): Promise<DropthisResult<DomainResponse>>;
551
+ /**
552
+ * Update a domain's `dropId` (dedicated mode: repoint to a different drop) or `default`
553
+ * flag (path mode only: set/clear the account's default publish domain). Mode is immutable
554
+ * — delete and reconnect to change it. PATCH /domains/{id_or_hostname}.
555
+ */
556
+ update(idOrHostname: string, input: {
557
+ dropId?: string | null;
558
+ default?: boolean | null;
559
+ }): Promise<DropthisResult<DomainResponse>>;
560
+ /**
561
+ * Delete a custom domain and remove all its routes. The response includes a dangling-CNAME
562
+ * warning — remove the DNS record after deleting so another account cannot re-claim the
563
+ * hostname. DELETE /domains/{id_or_hostname}.
564
+ */
565
+ delete(idOrHostname: string): Promise<DropthisResult<DomainDeletedResponse>>;
566
+ }
567
+
387
568
  declare class CursorPage<T> implements ListPage<T> {
388
569
  readonly object: "list";
389
570
  readonly data: T[];
@@ -410,8 +591,15 @@ declare class CursorPage<T> implements ListPage<T> {
410
591
  */
411
592
  declare class DropsResource<TInput = PublishInput> {
412
593
  private readonly transport;
413
- private readonly resolve;
414
- constructor(transport: Transport, resolve: PublishInputResolver<TInput>);
594
+ private readonly resolveInput;
595
+ private parentHosts?;
596
+ constructor(transport: Transport, resolveInput: PublishInputResolver<TInput>);
597
+ /**
598
+ * Hostnames the SDK can attribute to dropthis for slug parsing: the canonical
599
+ * viewer domain plus the configured baseUrl's host (covers staging/self-hosted
600
+ * setups where drops are served under the API's own domain).
601
+ */
602
+ private allowedParentHosts;
415
603
  /**
416
604
  * Publish content to a NEW permanent public URL; returns the created drop (with its `drop_…` id).
417
605
  * Use to publish / share / post / put online / make public a report, dashboard, site, or file.
@@ -432,14 +620,44 @@ declare class DropsResource<TInput = PublishInput> {
432
620
  list(params?: ListDropsParams): Promise<DropthisResult<CursorPage<DropResponse>>>;
433
621
  /** Fetch one drop by its `drop_…` id (not the slug/URL). GET /drops/{id}. */
434
622
  get(dropId: string): Promise<DropthisResult<DropResponse>>;
623
+ /**
624
+ * Resolve a drop URL (or bare slug) back to the drop — the way to recover a lost
625
+ * `drop_…` id. The slug is parsed client-side from the first hostname label of a
626
+ * dropthis-attributable hostname (`https://<slug>.dropthis.app/…`, or `<slug>.` +
627
+ * the configured baseUrl's host), then matched owner-scoped via GET /drops?slug=.
628
+ * Returns the drop, or `data: null` when no drop of yours has that slug.
629
+ * Custom-domain URLs are rejected client-side with `invalid_drop_url` — they are
630
+ * not resolvable yet.
631
+ */
632
+ resolve(urlOrSlug: string): Promise<DropthisResult<DropResponse | null>>;
633
+ /**
634
+ * Read back what a drop is serving (owner-only; works regardless of any viewer
635
+ * password). By default returns the JSON manifest of the CURRENT deployment's files;
636
+ * pass `deploymentId` to read a historical (even superseded) deployment — downloading
637
+ * an old version's files and republishing them via {@link updateContent} is the
638
+ * rollback path. Pass `path` (one of the manifest's `files[].path` values) to download
639
+ * that file's exact stored bytes instead. GET /drops/{id}/content.
640
+ */
641
+ getContent(dropId: string, options: GetContentOptions & {
642
+ path: string;
643
+ }): Promise<DropthisResult<DropContentFile>>;
644
+ getContent(dropId: string, options?: Omit<GetContentOptions, "path">): Promise<DropthisResult<DeploymentContentManifest>>;
435
645
  /**
436
646
  * Change an EXISTING drop's settings — title, visibility, password, noindex, expiry,
437
- * metadata — by its `drop_…` id. Does not touch content; replace that with {@link updateContent}.
438
- * Idempotent. PATCH /drops/{id}.
647
+ * metadata, domain, or slug — by its `drop_…` id. Does not touch content; replace that
648
+ * with {@link updateContent}. Idempotent. PATCH /drops/{id}.
649
+ *
650
+ * **`domain`** — move the drop to a different custom domain (must be live). Pass `null` to
651
+ * move the drop back to the shared pool (unmount from its current domain).
652
+ *
653
+ * **`slug`** — rename the vanity slug on a path-mode custom domain. Only valid when the drop
654
+ * lives on a path-mode domain. Unlike {@link publish} (which auto-suffixes taken slugs),
655
+ * `updateSettings` returns 409 on a slug conflict and never auto-suffixes — your code must
656
+ * catch 409 and retry with a different slug. Passing `slug` on the shared pool returns 422.
439
657
  */
440
658
  updateSettings(dropId: string, options?: DropOptions & RequestControls): Promise<DropthisResult<DropResponse>>;
441
659
  /** Permanently delete a drop and its public URL by its `drop_…` id. DELETE /drops/{id}. */
442
660
  delete(dropId: string): Promise<DropthisResult<null>>;
443
661
  }
444
662
 
445
- export { AccountResource as A, type UploadTarget as B, type CompleteUploadSessionRequest as C, DeploymentsResource as D, type EmailOtpResponse as E, type InMemoryPublishInput as I, type Limitations as L, type PrepareOptions as P, type RequestControls as R, type SessionResponse as S, type TierInfo as T, type UpdateContentOptions as U, type ActionResolve as a, ApiKeysResource as b, type CreateUploadSessionRequest as c, type CreateUploadSessionResponse as d, CursorPage as e, type DropAction as f, type DropDeploymentResponse as g, type DropOptions as h, type DropResponse as i, DropsResource as j, type DropthisClientOptions as k, type DropthisErrorResponse as l, type DropthisResult as m, type ListDeploymentsParams as n, type ListDeploymentsResponse as o, type ListPage as p, type PreparedPublishRequest as q, type PreparedUploadFile as r, type PublishFileInput as s, type PublishInput as t, type PublishOptions as u, type RequestOptions as v, Transport as w, type UploadManifestFile as x, type UploadSessionFileResponse as y, type UploadSessionResponse as z };
663
+ export { type AccountLimits as A, type PreparedUploadFile as B, type CreateUploadSessionRequest as C, type DeploymentContentFile as D, type EmailOtpResponse as E, type PublishFileInput as F, type GetContentOptions as G, type PublishInput as H, type InMemoryPublishInput as I, type PublishOptions as J, type RequestOptions as K, type Limitations as L, Transport as M, type NextHint as N, type UploadManifestFile as O, type PrepareOptions as P, type UploadSessionFileResponse as Q, type RequestControls as R, type SessionResponse as S, type TierInfo as T, type UpdateContentOptions as U, type UploadSessionResponse as V, type UploadTarget as W, AccountResource as a, type AccountResponse as b, type ActionResolve as c, ApiKeysResource as d, type CreateUploadSessionResponse as e, CursorPage as f, type DeploymentContentManifest as g, DeploymentsResource as h, type DnsRecord as i, type DomainDeletedResponse as j, type DomainListResponse as k, type DomainResponse as l, DomainsResource as m, type DropAction as n, type DropContentFile as o, type DropDeploymentResponse as p, type DropOptions as q, type DropResponse as r, DropsResource as s, type DropthisClientOptions as t, type DropthisErrorResponse as u, type DropthisResult as v, type ListDeploymentsParams as w, type ListDeploymentsResponse as x, type ListPage as y, type PreparedPublishRequest as z };