A simple Data Table Editor that generates Markdown, CSV or JSON. It can also be used to interchange data between the formats, supporting editing in the grid.
- Grid interface
- drag and drop rows to re-order
- drag and drop columns to re-order
- import csv from file
- import csv, markdown, and json from editor
- generate csv, markdown, json and javascript from grid
- export csv, markdown and json files
- add and delete columns
- add and delete rows
- randomly fill data with Test Data
- configure export options
The application is live at AnyWayData.com
- Or clone and run locally with Vite:
pnpm installpnpm run dev:web- open
http://127.0.0.1:4173/app.html,http://127.0.0.1:4173/generator.html, orhttp://127.0.0.1:4173/webmcp.html
Use pnpm run build:web to create a production build and pnpm run preview:web to preview the built output.
The old static-server flow (e.g. python3 -m http.server) is no longer the recommended local runtime path.
Storybook is available for isolated frontend development and UI review:
pnpm run storybook- open
http://127.0.0.1:6006
Current story groups:
Test Data / Embedded Panelfor the in-app schema editor inside the main table editorTest Data / Generatorfor the standalone generator schema editor statesExport Formats / Previewsfor export-preview flows such as Markdown, CSV, DSV, JSON, JSONL, XML, SQL, HTML, Gherkin, ASCII table, and code-oriented outputs
Useful workflows:
- use Storybook when iterating on schema-editor rendering, validation states, and text/schema mode transitions
- use Storybook when adjusting export-preview UI without needing the full app boot flow
- use
pnpm run test:storybookto run Storybook smoke and interaction tests in CI-style mode - use
pnpm run build-storybookto create a static Storybook build instorybook-static
The editor can run with AG Grid or Tabulator using the same import/export and toolbar processing layer.
- Query string:
?grid=ag-gridor?grid=tabulator - Global override before app load:
window.ANYWAYDATA_GRID_ENGINE = "ag-grid" - Persisted setting key:
localStorage["anywaydata:grid-engine"]
Default is Tabulator.
Use the Generator to create test data rules using a nicer UI.
You can preview the data prior to generating.
You can copy the schema into the editor UI and use it to populate existing tables.
You can add data to an existing data table e.g. you import a CSV file and want to a new column of dates, or you want to amend an entire column (perhaps to obfuscate PII Data)
Expand the "> Test Data" section.
Choose the Data fields to add or amend in the table or enter a spec.
You can then create a new table, or amend the existing table or selected rows.
The spec is a paragraph of text where each line is either a 'name' or a 'rule':
- Comments: lines starting with
#(optionally prefixed by whitespace) are treated as comments. - Blank lines: blank lines are allowed and ignored, so you can separate column groups for readability.
- Column definitions: each column can be defined either as
namefollowed byruleon the next logical content line, or inline asname: rule. - Constraints: optional
IF ... THEN ...statements may appear in text mode after the field definitions, terminated by either;orENDIF.
# optional comment
name
rule
# another comment
name
rule
# compact pict-style alternative
status: enum(active,inactive)
IF [name] = "Bob" THEN [status] = "active" ENDIF
namewill be used as a column namerulewill be used to generate the data- constraint parameter references must use bracketed column names, e.g.
[Status] - constraints are currently text-mode only in the editor UI
- pairwise / n-wise generation with constraints supports only constraints that reference enum columns
A rule can be a regex string e.g.
(connie|bob)which would generate 'connie' or 'bob'[1-9][0-9]{0,4}which would generate number between 1 and 99999
A rule can also be a domain mapped faker API call.
Faker API can be found here: https://fakerjs.dev/api/
e.g.
First Name
awd.domain.person.firstName
Noun
domain.hacker.noun
The awd and domain prefixes are optional:
e.g.
First Name
person.firstName
Noun
hacker.noun
The fake method on faker is also supported, which takes a mustache template style string combining api methods e.g.
helpers.fake("{{name.lastName}}, {{name.firstName}}")
So a sample test data spec might look like:
# person details
name
helpers.fake("{{name.lastName}}, {{name.firstName}}")
# profile text
desc
lorem.paragraph
# preference data
collects
hacker.noun
# regex example
prefers
(Connie|Bob)
When you have 2 or more enum fields (comma-separated values), you can generate pairwise combinatorial test data using a greedy approximation approach. This provides 100% pairwise coverage and typically reduces test cases substantially (often around 90-99% fewer) compared with full factorial testing.
For enum data, use comma-separated values in your spec:
# pairwise parameters
browser
chrome,firefox,safari,edge
# viewport class
device
desktop,tablet,mobile
# style variant
theme
light,dark
The "Generate Pairwise" button will appear automatically, creating a compact near-minimal set of combinations that tests every pair of values across all parameters.
Looking for similar apps to compare features sets and functionality?
There is a maintained list in the documentation:
- Faker.js for backing the domain api
- RandExp.js for regular expression based random data generation
- Tabulator for light weight tables
- PapaParse for csv processing
This tool falls in to the categories:
- online markdown editor
- markdown table generator
- markdown table editor
- Online Test Data Generation
- Online CSV Editor
Test - pnpm test
Coverage - pnpm run testcoverage
Preview Docs - pnpm run previewdocs
Build for release - pnpm run anywaydata:win
This repository now uses pnpm workspaces:
packages/core-> shared generation engine (@anywaydata/core)packages/core-ui-> browser UI modules (@anywaydata/core-ui)apps/cli-> npm CLI package (@anywaydata/cli)apps/api-> REST API (@anywaydata/api)apps/mcp-> MCP server (@anywaydata/mcp)
Imports now resolve directly from package sources.
Use packages/core/js/* and packages/core-ui/js/* paths in local runtime/test code.
Install all dependencies from the repo root:
pnpm install
Run workspace build/test orchestration from root:
pnpm run build:workspacespnpm run test:workspacespnpm run test:workspaces:all
Notes:
pnpm run test:workspacesruns only the non-duplicating workspace tests that are not already covered by the root Jest suite (@anywaydata/api,@anywaydata/cli, and@anywaydata/mcp).pnpm run test:workspaces:allpreserves the old “run every workspace test script” behavior.
Run an individual workspace command:
pnpm --filter @anywaydata/core run testpnpm --filter @anywaydata/core-ui run testpnpm --filter @anywaydata/api run startpnpm --filter @anywaydata/mcp run start
The npm CLI package is the workspace app apps/cli (@anywaydata/cli), and Bun executable builds also use apps/cli as the source.
Install globally from npm:
npm install -g @anywaydata/cli
Or run without installing:
npx @anywaydata/cli --help
Run a one-off generation command with npx:
npx @anywaydata/cli generate -i input.txt -n 10 -f csv
Show CLI help:
anywaydata --help
Generate output to console:
anywaydata generate -i input.txt -n 10 -f csv
Generate output to a file:
anywaydata generate -i input.txt -n 10 -f json -o output.json
Run in test mode (forces a single generated row and prints diagnostics):
anywaydata generate -i input.txt -n 10 -f markdown -t
Supported options:
-i, --inputfilepath to the input text spec (required)-n, --numberOfLinesnumber of rows to generate (default1)--pairwisegenerate pairwise combinations for enum fields (requires at least 2 enum rules)-f, --formatoutput format (defaultcsv)-o, --outputfilewrite output to file instead of stdout-t, --testModeenable diagnostics mode and generate one row--unsafe-faker-expressionsallow expression-style faker args (disabled by default)--trim-inputtrim whitespace from every imported field value before amend processing--trim-input-fieldscomma-separated imported field names to trim before amend processing
Amend existing data with a schema:
anywaydata amend --schema-file schema.txt --data-file input.csv --input-format csv -f json -o amended.json
Trim imported input values during amend:
anywaydata amend --schema-file schema.txt --data-file input.csv --input-format csv --trim-input-fields Name,Email -f json
Pairwise note:
- when
--pairwiseis enabled,--numberOfLinesis accepted but ignored (the pairwise engine determines row count)
Start the API:
pnpm --filter @anywaydata/api run start
Run the published API package directly with npx:
npx -y @anywaydata/api
Note: If npx fails on Windows, use direct execution instead:
npm install -g @anywaydata/api && anywaydata-api
For interactive local development (foreground process with Ctrl+C stop), prefer:
pnpm --filter @anywaydata/api run start -- --port 3001node apps/api/src/index.js --port 3001
Use npx primarily for consumer-style package execution/testing.
Choose a specific port:
pnpm --filter @anywaydata/api run start -- --port 3001- PowerShell:
$env:PORT=3001; pnpm --filter @anywaydata/api run start
Port behavior:
--portoverridesPORT- if a port is explicitly provided (
--portorPORT) and is in use, startup fails with a short message - if no explicit port is provided, API starts at
3000and auto-tries3001..3020when needed
Health check:
GET http://localhost:3000/v1/health
Generate data:
POST http://localhost:3000/v1/generate
/v1/generate request supports optional responseFormat mode:
responseFormat: "rows"(default) returnsheaders+rowsresponseFormat: "rendered"returns only rendered text in JSON payloadresponseFormat: "all"returnsheaders+rows+renderedresponseFormat: "raw"returns rendered payload directly with content-type matching output formatoutputFormatis optional and defaults tocsvoutputFormatsupports:csv,dsv,markdown,json,jsonl,javascript,python,java,typescript,xml,sql,gherkin,html,asciitable
Raw multiline schema/spec endpoint:
POST http://localhost:3000/v1/generate/fromschema?rowCount=10&outputFormat=csv&responseFormat=rows
Content-Typemust betext/plain- request body is the full multiline spec
- query params:
rowCount(required),outputFormat(optional),seed(optional),pairwise(optional),responseFormat(optional:rows|rendered|all|raw) - pairwise note: when
pairwise=true,rowCountis accepted for compatibility but ignored (pairwise output size is computed automatically)
Amend imported data endpoint:
POST http://localhost:3000/v1/generate/amend
- JSON body fields:
textSpec(required),inputData(required raw text),inputFormat(required),rowCount(optional, defaults to input row count),outputFormat(optional),responseFormat(optional),stream(optional and ignored) - amend trim controls:
trimInputtrims all imported field values;trimInputFieldsCsvtrims only the listed imported field names rowCountmust be<=imported row count- response returns the full resulting dataset after amendment
Set format default options:
POST http://localhost:3000/v1/generate/options/<format>
Get current format default options:
GET http://localhost:3000/v1/generate/options/<format>
Reset format default options:
POST http://localhost:3000/v1/generate/options/<format>/default
Notes:
- option defaults set for a format are used when
optionsare not provided to/v1/generateor/v1/generate/fromschema - payload for
/v1/generate/options/<format>is the same options object normally sent asoptionsin generation requests
Notes:
- for JSON requests, newline characters inside string literals must be escaped as
\\n - use
/v1/generate/fromschemawhen you want to paste raw multiline text directly
Streaming notes for CLI/core generation:
- stream mode supports:
csv,jsonl,dsv,json,xml jsonstream mode emits a valid JSON array payload- incompatible format options in stream mode are warned and ignored where needed
- REST API generation endpoints currently run in buffered mode
OpenAPI spec:
GET http://localhost:3000/v1/openapi.json
Swagger UI:
GET http://localhost:3000/v1/docs
Example request body:
{
"textSpec": "# literal example\\n\\nName\\nBob",
"rowCount": 3,
"outputFormat": "json"
}For security, complex faker expressions that use eval-like functionality are disabled by default. The API uses a hybrid approach that tries safe method calls first, then falls back to secure execution when needed.
Start the API with unsafe expressions enabled for all requests:
# Via command line flag
node apps/api/src/index.js --unsafe-faker
# Or via npm workspace
pnpm --filter @anywaydata/api run start -- --unsafe-faker
# Or via npx
npx -y @anywaydata/api --unsafe-fakerAdd unsafeFakerExpressions: true to individual requests:
{
"textSpec": "# faker + numeric range\\n\\nName\\nperson.firstName\\n\\nScore\\nnumber.int({\"min\": 18, \"max\": 65})",
"rowCount": 5,
"outputFormat": "json",
"unsafeFakerExpressions": true
}For /fromschema endpoint, use the query parameter:
POST /v1/generate/fromschema?rowCount=10&unsafeFakerExpressions=true
- Safe by default: Most faker expressions use direct method calls without eval
- Hybrid approach: Tries safe
.apply()method first, falls back toFunction()constructor with security filtering - Expression patterns blocked:
process,require,import,eval,__,constructor,prototype - Granular control: Enable globally via CLI or per-request via parameter
Examples of expressions that work safely without the flag:
person.firstNameorperson.firstName()number.int({"min": 10, "max": 100})helpers.arrayElement(["red", "green", "blue"])
Examples that require the unsafe flag:
() => this.person.firstName()(arrow functions withthis){count: () => \${this.number.int()}`}` (template literals)
Start the MCP server:
pnpm --filter @anywaydata/mcp run start
Note: MCP runs over stdio in this version and does not bind to a TCP port. OpenAPI/Swagger routes are available on the REST API only.
The server exposes tools:
generate_data_from_specamend_data_from_specget_output_format_options_schema
Inputs:
textSpec(required string)rowCount(required integer, >= 0)pairwise(optional boolean, defaultfalse; whentrue,rowCountis ignored)outputFormat(required string e.g.csv,json,jsonl,xml,sql)options(optional object)seed(optional number)trimInput(optional boolean foramend_data_from_spec)trimInputFieldsCsv(optional string foramend_data_from_spec)
Discoverability support:
tools/listpublishes typedinputSchemaandoutputSchemaget_output_format_options_schemareturns per-format defaults and typed option schemaresources/listandresources/readexpose:anywaydata://schemas/output-format-optionsanywaydata://install/config-examples
Security note:
- MCP input validation restricts faker rule arguments to literals only (string/number/boolean/null) or no args.
- Expression-style faker arguments (e.g. arrow functions,
this, template expressions) are rejected in MCP mode. - Node and Bun CLIs are also safe-by-default with the same rule; use
--unsafe-faker-expressionsto opt in.
Error conventions:
- Tool-level validation/safety failures return
result.isError=true - Tool payload shape for failures is:
{ "ok": false, "error": { "code": "...", "message": "...", "details": ... } }
- Common
error.codevalues:invalid_output_formatunsafe_faker_ruletext_spec_too_largerow_count_too_largegeneration_failed
Codex connects to this server as an MCP tool provider over stdio (not HTTP).
- Ensure dependencies are installed from repo root:
pnpm install
- Start MCP server (or let your MCP host launch it):
pnpm --filter @anywaydata/mcp run start
- Configure your MCP host/Codex to launch the server command.
Example MCP server config (published package via npx):
{
"mcpServers": {
"anywaydata": {
"command": "npx",
"args": ["-y", "@anywaydata/mcp"]
}
}
}Example MCP server config (Claude Desktop style):
{
"mcpServers": {
"anywaydata": {
"command": "npx",
"args": ["-y", "@anywaydata/mcp"]
}
}
}Example MCP server config (Cursor/Cline-style local repo command):
{
"mcpServers": {
"anywaydata-local": {
"command": "node",
"args": ["apps/mcp/src/index.js"],
"cwd": "/path/to/grid-table-editor"
}
}
}Windows-safe alternative (avoids npx bin resolution issues in some shells):
{
"mcpServers": {
"anywaydata": {
"command": "node",
"args": ["-e", "import('@anywaydata/mcp')"]
}
}
}Example MCP server config (local repo):
{
"mcpServers": {
"anywaydata": {
"command": "node",
"args": ["apps/mcp/src/index.js"],
"cwd": "/path/to/grid-table-editor"
}
}
}For MCP hosts, prefer direct node execution or npx as shown above. pnpm run can emit non-JSON stdout lines, which may interfere with stdio MCP message parsing in some clients.
Notes:
- Transport is
stdio, soPORT/--portdo not apply to MCP. - Use REST API (
@anywaydata/api) for HTTP/OpenAPI/Swagger use cases. - MCP tool name exposed is
generate_data_from_spec.
Packages are configured with publishConfig.access=public.
npm loginpnpm installpnpm run verify:local
Versioning and release notes are managed with Changesets:
- Create new changeset
pnpm changeset- Commit the generated changeset file
- Bump version
pnpm changeset version- publish package
pnpm changeset publish
- Get/create a new token from npm (or your registry UI).
- Update your user .npmrc entry (Windows):
notepad $HOME\.npmrc
- Replace the auth line, e.g.:
//registry.npmjs.org/:_authToken=YOUR_NEW_TOKEN
If you use a project-level .npmrc, update/remove token there too.
Verify:
npm whoami
For manual one-off publish, set the version in that package’s package.json first, then publish.
Bump version:
pnpm --filter @anywaydata/cli version patch
or minor / major / exact like 1.2.3
Publish a single workspace package manually:
pnpm --filter @anywaydata/core publishpnpm --filter @anywaydata/core-ui publishpnpm --filter @anywaydata/cli publish
Build images from repo root:
docker build -f apps/api/Dockerfile -t anywaydata-api .
Run the API container:
docker run --rm -p 8082:3000 anywaydata-api
Then sent requests to:
localhost:8082
e.g.
GET http://localhost:8082/v1/healthGET http://localhost:8082/v1/generate/options/csv
Notes:
docker run: starts a new container from an image.--rm: automatically removes the container when it stops.-p 8082:3000: maps host port8082to container port3000.anywaydata-api: the image name to run.- Connect from your machine at
http://localhost:8082. - Inside the container, the app still listens on port
3000.
Build the browser app image from repo root:
docker build -f apps/web/Dockerfile -t anywaydata-web .
Run the browser app container:
docker run --rm -p 8080:80 anywaydata-web
Then open:
http://localhost:8080/http://localhost:8080/app.htmlhttp://localhost:8080/generator.htmlhttp://localhost:8080/webmcp.html
Notes:
- This image serves the static browser app assets only.
- It does not build or include Docusaurus (
docs-srcpipeline).
Build images from repo root:
docker build -f apps/mcp/Dockerfile -t anywaydata-mcp .
Configure MCP with stdio for Docker
Example MCP config:
{
"mcpServers": {
"anywaydata-docker": {
"command": "docker",
"args": ["run", "--rm", "-i", "anywaydata-mcp"]
}
}
}Notes:
- Build first:
docker build -f apps/mcp/Dockerfile -t anywaydata-mcp . -iis required for stdio transport.- No
-pmapping is needed for MCP.
If you need local files available inside the container, add a bind mount, e.g. -v /host/path:/workspace.
Start both services with Compose:
docker compose up --build
GitHub Actions runs linting and tests for pushes and pull requests to master.
The workflow also publishes coverage output as build artifacts on each run:
coverage-report-node-20.xcontains the fullcoverage/directorycoverage-html-report-node-20.xcontains the HTML report fromcoverage/lcov-report/
Open the workflow run in the Actions tab and download the artifact from the run summary page.
- TODO: convert all JS to TypeScript
- TODO: improve and expand the test data generation - written in TypeScript, don't use faker directly - create a full semantic abstraction
- TODO: create a DSL for the test data generation... probably just JSON initially
- TODO: convert UI to use React?
Large sources of data: