Skip to content

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

Terminal window
npm install @fobo-tools/datamaker

Submit

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 · money

Then 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 })Promise of a form descriptor (formId, name, schemaVersion, submitPolicy, envelopeVersion, recipientUserId, recipientPublicKey, signer, fields, verified). Throws DmfError on tampering.
  • submit({ form | dmf, values, ...opts }){ submissionId, editToken, formId }. Options: apiBaseUrl, submitterId (default null), validate (default true), 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

Terminal window
datamaker inspect contact.dmf
datamaker submit contact.dmf --field email=ada@example.com --field full_name=Ada
datamaker submit contact.dmf --data-file answers.json --dry-run

Browser

A bundled build (dist/datamaker.browser.js, global DataMaker) powers the web embed:

  • createSubmitHandler(config) — adapts the lightweight JS renderer’s onSubmit to 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_HOST is 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.