
Security News
GPT-6 Astra Attempts Supply Chain Attacks Against Open Source Maintainers in Testing
GPT-6 Astra hits 100% on ExploitBench and finds zero-days autonomously, while independent tests reveal scope violations and monitoring gaps.
@dropthis/node
Advanced tools
Official Node.js SDK for dropthis -- the publish layer between AI and the internet. One API call in, one URL out.
npm install @dropthis/node
import { Dropthis } from "@dropthis/node";
const dropthis = new Dropthis({ apiKey: "sk_..." });
const { data, error } = await dropthis.drops.publish("<h1>Hello</h1>");
console.log(data.url); // https://abc123.dropthis.app
console.log(data.id); // drop_… — keep this; it's how you update or delete later
Keep the
drop_…id.publish()never takes an id — every call creates a NEW drop. To change something already published, pass the id from the publish response todrops.updateContent()(the files at the URL) ordrops.updateSettings()(title, visibility, expiry, …). Lost the id? Recover it from the URL withdrops.resolve().
const { data } = await dropthis.drops.publish("<h1>Launch page</h1>");
const { data } = await dropthis.drops.publish("./report.html");
const { data } = await dropthis.drops.publish("./dist");
const { data } = await dropthis.drops.publish("./dist", {
title: "Q4 Report",
visibility: "unlisted",
noindex: true,
expiresAt: "2026-12-31T00:00:00Z",
});
const created = await dropthis.drops.publish("./dist", { title: "v1" });
const updated = await dropthis.drops.updateContent(created.data.id, "./dist-v2", {
ifRevision: created.data.revision,
});
await dropthis.drops.updateSettings("drop_abc123", { title: "New title" });
Lost the drop_… id? Resolve the drop's URL (or bare slug) back to the drop. The slug is
parsed client-side from the first hostname label of a dropthis hostname
(<slug>.dropthis.app, or <slug>. + your configured baseUrl host), then matched against
your own drops via GET /drops?slug=. Custom-domain URLs are not resolvable yet — they are
rejected client-side with an invalid_drop_url error, without hitting the API.
const { data } = await dropthis.drops.resolve("https://my-report.dropthis.app/");
if (data) {
console.log(data.id); // drop_… — use this for updateContent/updateSettings/delete
} else {
// none of your drops has that slug
}
drops.getContent() is the owner-side read-back (it works regardless of any viewer
password). By default it returns a JSON manifest of the current deployment's files;
pass path to download one file's exact stored bytes.
// Manifest of the current deployment
const manifest = await dropthis.drops.getContent("drop_abc123");
console.log(manifest.data.files); // [{ path, contentType, sizeBytes }, …]
// One file's bytes (and a text() helper)
const file = await dropthis.drops.getContent("drop_abc123", { path: "index.html" });
console.log(file.data.contentType); // "text/html"
console.log(file.data.text()); // "<h1>…"
// A historical (even superseded) deployment — this is also the rollback path:
// download the old version's files and republish them with updateContent().
const old = await dropthis.drops.getContent("drop_abc123", {
deploymentId: "dep_xyz789",
path: "index.html",
});
Every drop response carries a revision. Pass it back as ifRevision on
updateContent() / updateSettings() to make the update conditional: if someone else
changed the drop in between, the API answers 409 instead of clobbering, and the error
exposes the server's currentRevision so you can re-read and retry.
// 1. Read
const drop = await dropthis.drops.get("drop_abc123");
// 2. Edit locally (e.g. via getContent read-back), then
// 3. Update conditionally
const result = await dropthis.drops.updateContent(drop.data.id, "./dist-v2", {
ifRevision: drop.data.revision,
});
if (result.error?.statusCode === 409) {
console.log("Drop changed underneath us; server is at revision", result.error.currentRevision);
// re-read with drops.get(), merge, retry with the fresh revision
}
The drops.publish() and drops.updateContent() methods accept:
"<h1>Hello</h1>" (auto-detected as inline content)"./report.html" (local file)"./dist" (local directory, bundled)["./dist", "./extra.css"] (multi-path bundle)new URL("https://example.com/page") (source fetch)new Uint8Array(...) (raw bytes){ kind: "content", content: "...", contentType?: "text/html", path?: "page.html" }{ kind: "source_url", sourceUrl: "https://example.com/page" }{ kind: "files", files: [{ path, content?, contentBase64?, bytes?, contentType? }], entry? }Drop settings (title, visibility, password, noindex, expiresAt, metadata) go in the second options argument, not in the input object.
All inputs are uploaded through staged presigned URLs — one signed PUT per file, up to 5 files in parallel. The SDK handles this transparently.
// Inline content with explicit MIME type
await dropthis.drops.publish({
kind: "content",
content: "<h1>Hello</h1>",
contentType: "text/html",
});
// Fetch and re-publish a remote URL
await dropthis.drops.publish({
kind: "source_url",
sourceUrl: "https://example.com/report",
});
// Multi-file bundle with explicit entry point
await dropthis.drops.publish(
{
kind: "files",
files: [
{ path: "index.html", content: "<h1>Hello</h1>" },
{ path: "style.css", content: "body { margin: 0; }" },
],
entry: "index.html",
},
{ title: "My Site" },
);
prepare() resolves and validates the input locally, returning the prepared request object without making any API calls. It throws PublishInputError on invalid input (e.g. missing file).
import { Dropthis, PublishInputError } from "@dropthis/node";
try {
const prepared = await dropthis.prepare("./dist");
console.log("Ready to publish:", prepared.kind);
} catch (e) {
if (e instanceof PublishInputError) {
console.error("Bad input:", e.message);
}
}
All methods return DropthisResult<T> -- either { data: T, error: null, headers } or { data: null, error, headers }. API errors never throw; check error before using data.
const result = await dropthis.drops.get("drop_abc123");
if (result.error) {
console.error(result.error.code, result.error.message);
// Also available: error.statusCode, error.requestId, error.suggestion,
// error.retryable, error.param, error.currentRevision
} else {
console.log(result.data);
}
Local input validation errors (e.g. file_not_found) are also returned as { error: { code: "file_not_found", ... } } rather than thrown -- except for prepare(), which throws PublishInputError.
const dropthis = new Dropthis({
apiKey: "sk_...", // Required. Defaults to DROPTHIS_API_KEY env var.
baseUrl: "https://...", // Override API base URL.
timeoutMs: 30_000, // Request timeout in milliseconds (default: 30s).
uploadTimeoutMs: 120_000, // Timeout for signed-PUT file uploads (default: 120s).
fetch: customFetch, // Custom fetch implementation.
});
You can also pass just the API key as a string:
const dropthis = new Dropthis("sk_...");
await dropthis.drops.list({ limit: 20 });
await dropthis.drops.get("drop_abc123");
await dropthis.drops.resolve("https://my-report.dropthis.app/"); // URL/slug → drop (or null)
await dropthis.drops.getContent("drop_abc123"); // manifest of served files
await dropthis.drops.getContent("drop_abc123", { path: "index.html" }); // one file's bytes
await dropthis.drops.updateSettings("drop_abc123", { title: "Updated" });
await dropthis.drops.delete("drop_abc123");
List results support auto-pagination:
const page = await dropthis.drops.list();
const allDrops = await page.data.autoPagingToArray({ limit: 100 });
// Or iterate
for await (const drop of page.data) {
console.log(drop.url);
}
To change a drop's content, use
client.drops.updateContent(dropId, newInput).drops.updateSettings()is for settings only (title, visibility, password, noindex, expiresAt, metadata).
await dropthis.deployments.list("drop_abc123");
await dropthis.deployments.get("drop_abc123", "dep_xyz789");
Low-level upload session management. Most users should use publish() instead.
await dropthis.uploads.create({
schemaVersion: 1,
files: [{ path: "index.html", contentType: "text/html", sizeBytes: 1024 }],
});
await dropthis.uploads.get("upl_abc123");
await dropthis.uploads.complete("upl_abc123"); // no body — server verifies against the manifest
await dropthis.uploads.cancel("upl_abc123");
await dropthis.auth.requestEmailOtp({ email: "you@example.com" });
await dropthis.auth.verifyEmailOtp({ email: "you@example.com", code: "123456" });
await dropthis.auth.logout(); // 204 No Content — data is null
await dropthis.apiKeys.create({ label: "CI" });
await dropthis.apiKeys.list();
await dropthis.apiKeys.delete("key_abc123"); // 204 No Content — data is null
const { data } = await dropthis.account.get();
// data.limits carries your plan's limits — use them to size a publish first:
// { name, maxSizeBytes, defaultTtlSeconds, maxStorageBytes }
await dropthis.account.update({ displayName: "Jane Doe" });
await dropthis.account.delete();
Custom domains let you serve drops on your own hostname instead of the shared pool. There are two
modes: path (many drops at hostname/{slug}/) and dedicated (one drop at the hostname root).
// 1. Connect the domain — returns DNS instructions (status: "pending_dns")
const { data: domain } = await dropthis.domains.connect({
hostname: "drops.example.com",
mode: "path",
});
// 2. Create the CNAME at your DNS provider:
// drops.example.com CNAME edge.dropthis.app
const dnsRecord = domain.dns[0];
// dnsRecord.name → "drops.example.com"
// dnsRecord.value → "edge.dropthis.app"
// 3. Verify — call repeatedly until status is "live"
const { data: verified } = await dropthis.domains.verify("drops.example.com");
// Use verified.dns[0].retryAfter (seconds) as the polling interval while status !== "live"
// verified.status → "live"
// 4. Publish to the custom domain (path mode: specify a vanity slug, or omit for a random one)
const { data: drop } = await dropthis.drops.publish("<h1>Hello</h1>", {
domain: "drops.example.com",
slug: "summer-sale",
});
// drop.url → "https://drops.example.com/summer-sale/"
Other domain operations:
await dropthis.domains.list();
await dropthis.domains.get("drops.example.com");
// Repoint a dedicated domain to a different drop
await dropthis.domains.update("bio.example.com", { dropId: "drop_abc123" });
// Set a path-mode domain as the account's publish default
await dropthis.domains.update("drops.example.com", { default: true });
// Delete (remove your DNS CNAME after this to prevent re-claim by another account)
await dropthis.domains.delete("drops.example.com");
account.get().data.limits reflects your active tier. Note: password protection ships
with Pro and is not enabled on any tier yet — the API rejects the password option until then.
Use the fs-free entry point for Cloudflare Workers and other edge runtimes. It does not import node:fs, node:path, or node:crypto.
import { DropthisEdge } from "@dropthis/node/edge";
const dropthis = new DropthisEdge({ apiKey: env.DROPTHIS_API_KEY });
const { data, error } = await dropthis.drops.publish("<h1>Hello from the edge</h1>");
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).
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.
Key types exported from the package:
import type {
AccountLimits,
AccountResponse,
DropthisClientOptions,
DropthisResult,
DropthisErrorResponse,
DropResponse,
DropDeploymentResponse,
DropOptions,
DeploymentContentManifest,
DeploymentContentFile,
DropContentFile,
GetContentOptions,
PrepareOptions,
RequestControls,
PublishOptions,
PublishInput,
PublishFileInput,
ListPage,
CreateUploadSessionRequest,
CreateUploadSessionResponse,
} from "@dropthis/node";
For AI coding agents (Cursor, Claude Code, Windsurf, etc.), install the dropthis-skills package:
npx skills add dropthis-dev/dropthis-skills
FAQs
Official Node.js SDK for dropthis — the publish layer between AI and the internet. One call in, one URL out.
The npm package @dropthis/node receives a total of 51 weekly downloads. As such, @dropthis/node popularity was classified as not popular.
We found that @dropthis/node demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
GPT-6 Astra hits 100% on ExploitBench and finds zero-days autonomously, while independent tests reveal scope violations and monitoring gaps.

Product
Socket can now send alerts and supply chain attack notifications to Microsoft Teams, with filters that route the right updates to each channel.

Security News
pnpm 12 rewrites the package manager in Rust, cutting install times by up to 90% while preserving pnpm 11 workflows and lockfiles.