JavaScript / Node SDK
@fobo-tools/datamaker reads a .dmf, validates, sealed-box encrypts, and
posts. Node ≥ 18.20 (uses global fetch + Web Crypto). Ships TypeScript types.
Install
npm install @fobo-tools/datamakerSubmit
import fs from 'node:fs';import * as dm from '@fobo-tools/datamaker';
const dmf = fs.readFileSync('contact.dmf');const form = await dm.readForm(dmf); // verifies signature + form hash
const result = await dm.submit({ form, values: { email: 'ada@example.com', full_name: 'Ada' },});// → { submissionId, editToken, formId }Or pass the bundle straight in: await dm.submit({ dmf, values }).
Building the values object
values is keyed by each field’s name (its storage key — not the
label). Inspect the form to see the names + kinds you need to fill:
for (const f of form.fields) { console.log(f.key, '·', f.kind, f.required ? '(required)' : '');}// email · email (required)// full_name · text// age · number// subscribed · boolean// plan · choice// interests · multi-choice// signup_date · date// budget · moneyThen map your data to those names, using the value type each kind expects:
const values = { email: 'ada@example.com', // email → string full_name: 'Ada Lovelace', // text → string age: 37, // number → number (37, not "37") subscribed: true, // boolean → boolean plan: 'pro', // choice → string (one of the choice values) interests: ['news', 'beta'], // multi-choice → string[] signup_date: '2026-05-29', // date → "YYYY-MM-DD" budget: 1999.99, // money → number};
await dm.submit({ form, values });For image / attachment fields, upload the bytes first and pass the returned
ref as the value — see Files & attachments.
The full value type for every kind is in the field kinds reference.
try { await dm.submit({ form, values });} catch (err) { if (err.code === 'VALIDATION_FAILED') { for (const i of err.issues) console.error(`${i.field}: ${i.message}`); } else throw err;}API
readForm(dmfBytes, { verify = true })→Promiseof a form descriptor (formId,name,schemaVersion,submitPolicy,envelopeVersion,recipientUserId,recipientPublicKey,signer,fields,verified). ThrowsDmfErroron tampering.submit({ form | dmf, values, ...opts })→{ submissionId, editToken, formId }. Options:apiBaseUrl,submitterId(defaultnull),validate(defaulttrue),allowUnknown,verify,fetch.buildSubmission(form, values, opts)→ seal without sending ({ submissionId, envelope, payload, values }).postSubmission(envelope, opts)→ post a pre-built envelope.validateValues(fields, input, opts)→{ values, issues }.
Validation throws ValidationError (with .issues) for missing required
fields, unknown keys, read-only kinds, and bad choices. SubmissionError
carries a non-2xx .status.
CLI
datamaker inspect contact.dmfdatamaker submit contact.dmf --field email=ada@example.com --field full_name=Adadatamaker submit contact.dmf --data-file answers.json --dry-runBrowser
A bundled build (dist/datamaker.browser.js, global DataMaker) powers the
web embed:
createSubmitHandler(config)— adapts the lightweight JS renderer’sonSubmitto a sealed POST.mountWasm(config, opts?)— frames the high-fidelity Wasm renderer (pixel-parity with the desktop designer) into#form-root; returns an unmount fn.DEFAULT_WASM_HOSTis the default bundle host.
Both read config.theme: 'auto' (default) follows the visitor’s
prefers-color-scheme and re-themes live; 'light' / 'dark' force one.
Other common config: recipientPublicKey (required), apiBaseUrl,
applyFormStyle. Full list in web embed.