
Security News
vlt 1.5.0 Adds Agent Skills Management
vlt 1.5.0 links skills from JavaScript dependencies to coding agents, with lockfile versioning and Socket data to filter out known malicious packages.
@mapled/client
Advanced tools
Mapled's JavaScript client — read published content from any JavaScript runtime: a browser, Node, an edge function. Preview, publish notifications and forms included; no framework, no dependencies.
The JavaScript client for Mapled, the hosted headless CMS for sites built with AI. It reads published content from any JavaScript runtime — a browser, Node, an edge function — and has no framework in it and no dependencies.
@mapled/react, which is built on this package.@mapled/next instead: the same reads, plus ISR cache tags, draft mode and the webhook handlers.npm install @mapled/client
// src/content.ts
import { createClient } from "@mapled/client";
const mapled = createClient({ key: "mk_live_…" });
const home = await mapled.getSingle("homepage");
const { records, total } = await mapled.getRecords("posts", { sort: "-date", limit: 10, expand: ["author"] });
const post = await mapled.getRecordBySlug("posts", "hello-world");
The key is the project's delivery key (Mapled: Integrations → Your site). It reads published content of collections with access Public and nothing else, so it is the one key that is safe in a browser — in a Vite app keep it in .env as VITE_MAPLED_KEY. The server key and the webhook signing secret are never safe there.
getSingle(key) — the record of a single, or null while it is empty.getRecords(collection, options) — { records, total, release }. Options: filter ({ featured: true, price: { gte: 10, lt: 100 }, tag: { in: ["a", "b"] }, title: { contains: "an" } }), sort ("date" / "-date"), limit (1–100), offset, fields, expand (relation fields, dotted for a second level — linked records arrive under record.expanded).getRecord(collection, id) / getRecordBySlug(collection, slug) — one record, or null when it isn't published.getRelease() — { release, publishedAt } of what is being served now, null before the first publish.release: N to read a published release by its number, and signal to abandon it.A record is { id, data }. Rich text values are Markdown — render them with a Markdown component, never as raw HTML. Image and file values are asset ids:
import { assetUrl } from "@mapled/client";
const src = assetUrl(imageId, { width: 1200, format: "webp" });
A failed read throws a MapledError with the API's status, code ("INVALID_QUERY", "RELEASE_NOT_FOUND", "RATE_LIMITED", …) and message. A key gets 1,200 requests a minute.
npx @mapled/cli types generate
// src/mapled.ts
import { createClient } from "@mapled/client";
import type { MapledSchema } from "./mapled-types";
export const mapled = createClient<MapledSchema>({ key: import.meta.env.VITE_MAPLED_KEY });
// src/home.ts
import { mapled } from "./mapled";
const home = await mapled.getSingle("homepage");
document.querySelector("h1")!.textContent = home?.data.headline ?? ""; // typed: an unknown key or field is a compile error
Content changes when someone presses Publish in Mapled. Answers for the latest release may be reused by the browser for a minute; an answer pinned with release: N never changes and is cached for good. So a page that was loaded before a publish shows it when it is loaded again — or sooner, if it watches for it.
Mapled's Preview shows drafts on your real site. For a site rendered in the browser there is nothing to set up in code: Mapled opens the page with a one-time token — https://your-site/blog/hello?mapled-preview=mpv_… — and the client, before its first read, takes the token out of the address bar, trades it for a draft session and keeps the session for the tab. Reads made in that tab show drafts until the session ends.
Mapled has to know where the site lives and that it renders in the browser:
framework is "react-spa" or "plain-html" (npx @mapled/cli scan --write and bindings push, or your AI agent's push_site_manifest) — or the project was created with that stack;set_site_url).// src/preview-bar.ts
import { endPreview } from "@mapled/client";
import { mapled } from "./mapled";
if (await mapled.isPreview()) {
// show "You are looking at a preview" with a way out
document.querySelector("#exit-preview")?.addEventListener("click", () => {
endPreview();
location.reload();
});
}
resolvePreview() does the trade on its own, previewSession() answers the tab's session. On a server, keep the draft session wherever your preview route put it and name it per request:
// server/mapled.ts
import { createClient } from "@mapled/client";
export function mapledFor(request: Request) {
const session = /(?:^|;\s*)mapled_draft=([^;]+)/.exec(request.headers.get("cookie") ?? "")?.[1] ?? null;
return createClient({ key: process.env.MAPLED_KEY!, draft: session });
}
draft: null never reads drafts.
A page that stays open can hear about a publish:
// src/live.ts
import { mapled } from "./mapled";
export const stop = mapled.watch(async (next) => {
// read again, pinned to the new release: the browser's cache can't answer with the old one
const home = await mapled.getSingle("homepage", { release: next.release });
document.querySelector("h1")!.textContent = home?.data.headline ?? "";
});
The tab asks Mapled for the release pointer — once a minute by default ({ interval: 30 } for thirty seconds, never under fifteen), only while it is visible, at once when it is shown again. Every check is a request of the delivery key's budget (a quiet one is a 304), so keep the interval honest: a thousand open tabs at a minute is most of the budget.
A site with a server can spare Mapled the traffic: relay GET /v1/delivery/release from a route of your own, cache it there for a few seconds, and point the watch at it.
// src/live-relay.ts
import { watchRelease } from "@mapled/client";
export const stop = watchRelease({ endpoint: "/api/mapled/release", interval: 10, onPublish: () => location.reload() });
A form is created once — npx @mapled/cli forms create "Contact", or by your AI agent — and gets a key. Submissions show up in Mapled under Forms right away.
// src/contact.ts
import { submitForm } from "@mapled/client";
const form = document.querySelector<HTMLFormElement>("#contact")!;
const status = document.querySelector("#contact-status")!;
form.addEventListener("submit", async (event) => {
event.preventDefault();
const consent = form.querySelector<HTMLInputElement>("[name=consent]");
const answer = await submitForm("contact", form, {
key: "mk_live_…",
consent: consent?.checked ? "I agree to be contacted about my request." : undefined,
});
status.textContent = answer.ok ? "Thank you — we'll be in touch." : "That didn't go through. Try again in a minute.";
});
submitForm takes a <form>, a FormData or a plain object of text, numbers and booleans, and never throws: it answers { ok: true } or { ok: false, status, code, message }. Keep a hidden _gotcha input in the form — people leave it empty, what bots fill in is filed as spam. consent is the exact label of the checkbox the visitor ticked, kept with the submission as evidence; page defaults to the page's path; test: true sends a test submission.
Operational collections skip the publish cycle. Your server reads and writes them with the project's server key — the client refuses to be made in a browser.
// server/orders.ts
import { createAppClient } from "@mapled/client";
const db = createAppClient({ serverKey: process.env.MAPLED_SERVER_KEY! });
await db.create("orders", { customer: "Ada Lovelace", total: 120 });
const { records } = await db.list("orders", { filter: { status: "new" }, sort: "-total" });
npx @mapled/cli doctor
doctor knows a site rendered in the browser: it looks for VITE_MAPLED_KEY, reads ○ «Not needed» for the webhook and the routes such a site doesn't have, and fails any env file that puts a server key or a signing secret under a public prefix.
| Export | |
|---|---|
createClient({ key, apiUrl?, release?, draft?, appUrl?, fetch? }) | The delivery client: getSingle, getRecords, getRecord, getRecordBySlug, getRelease, isPreview, watch |
createAppClient({ serverKey, apiUrl?, fetch? }) | Live data, server-side only: list, get, create, update, remove |
assetUrl(id, { width?, height?, fit?, format?, quality? }) | The URL of an image or file value |
watchRelease({ key | endpoint, interval?, onPublish }) | Publish notifications without a client |
resolvePreview(options?), previewSession(), endPreview(), PREVIEW_PARAM | Preview in a browser |
submitForm(form, fields, { key, consent?, page?, test? }) | A visitor's form submission |
MapledError | status, code, message of a failed read |
The HTTP API underneath — endpoints, the query grammar, caching, the webhook's signature — is described in «Without Next.js: the HTTP API» of the @mapled/next README.
FAQs
Mapled's JavaScript client — read published content from any JavaScript runtime: a browser, Node, an edge function. Preview, publish notifications and forms included; no framework, no dependencies.
The npm package @mapled/client receives a total of 177 weekly downloads. As such, @mapled/client popularity was classified as not popular.
We found that @mapled/client 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
vlt 1.5.0 links skills from JavaScript dependencies to coding agents, with lockfile versioning and Socket data to filter out known malicious packages.

Security News
Rust 1.100 will add opt-in dependency cooldowns to Cargo, giving security scanners and maintainers time to catch malicious crate releases.

Security News
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.