Skip to content

Commit e6aa6c2

Browse files
authored
add auto increment timestamp generator (#201)
* add auto increment timestamp generator * Fix auto increment timestamp parity across surfaces * Prevent domain execution context overrides * Clarify schema definition examples * Restore CI API test parallelism * Allow Vitest browser port fallback * Accept zero row index for timestamp increments
1 parent 6b120f7 commit e6aa6c2

30 files changed

Lines changed: 683 additions & 28 deletions

apps/mcp/src/mcp.test.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,26 @@ test('MCP server handles generate_data_from_spec tool call', () => {
5454
expect(response?.result?.structuredContent?.ok).toBe(true);
5555
});
5656

57+
test('MCP server allows registered domain commands in safe mode', () => {
58+
const response = requestServer({
59+
jsonrpc: '2.0',
60+
id: 202,
61+
method: 'tools/call',
62+
params: {
63+
name: 'generate_data_from_spec',
64+
arguments: {
65+
textSpec: 't2\nautoIncrement.timestamp(start="12th June 2026 at 4pm", step=60, type="minutes")',
66+
rowCount: 3,
67+
outputFormat: 'json',
68+
},
69+
},
70+
});
71+
const payload = JSON.parse(response?.result?.content?.[0]?.text || '{}');
72+
expect(payload.ok).toBe(true);
73+
expect(payload.rows).toEqual([['2026-06-12T16:00:00Z'], ['2026-06-12T17:00:00Z'], ['2026-06-12T18:00:00Z']]);
74+
expect(response?.result?.isError).toBe(false);
75+
});
76+
5777
test('MCP server accepts comments and blank lines in textSpec', () => {
5878
const response = requestServer({
5979
jsonrpc: '2.0',

docs-src/blog/2026-06-12-release-prep-combinatorial-grid-workflows.md

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
slug: release-prep-combinatorial-grid-workflows
3-
title: "Release Prep: Stronger Combinatorial Generation and Faster Grid Workflows"
3+
title: 'Release Prep: Stronger Combinatorial Generation and Faster Grid Workflows'
44
authors: [alan]
55
tags: [release, feature, combinatorial, schema, import, export, ux]
66
date: 2026-06-12T10:00
@@ -12,7 +12,30 @@ This release adds broader combinatorial generation, schema authoring improvement
1212

1313
<!-- truncate -->
1414

15-
## 1. N-wise combinatorial generation, not just pairwise
15+
## 1. Auto-increment timestamps for deterministic event streams
16+
17+
You can now generate timestamps that move forward one row at a time instead of relying on purely random dates.
18+
19+
Example:
20+
21+
```text
22+
CreatedAt
23+
autoIncrement.timestamp(start="2026-06-12T12:39:23Z", step=1, type="seconds")
24+
```
25+
26+
That produces:
27+
28+
- row 1: `2026-06-12T12:39:23Z`
29+
- row 2: `2026-06-12T12:39:24Z`
30+
- row 3: `2026-06-12T12:39:25Z`
31+
32+
This is useful for audit logs, event streams, ordered API records, and any test data where time should progress predictably across generated rows.
33+
34+
Docs:
35+
36+
- [autoIncrement Domain](/docs/test-data/domain/autoIncrement)
37+
38+
## 2. N-wise combinatorial generation, not just pairwise
1639

1740
The biggest addition is that combinatorial generation now goes beyond pairwise.
1841

@@ -42,7 +65,7 @@ Docs:
4265

4366
![N-wise combinations dialog](/img/release-198/n-wise-generation.png)
4467

45-
## 2. Schema constraints with PICT-style `IF ... THEN ...`
68+
## 3. Schema constraints with PICT-style `IF ... THEN ...`
4669

4770
Schema constraints make generated combinations more realistic by filtering out invalid rows.
4871

@@ -71,7 +94,7 @@ Docs:
7194

7295
- [Schema Definition](/docs/test-data/Schema-Definition)
7396

74-
## 3. Grid to Enum Schema for turning existing tables into generators
97+
## 4. Grid to Enum Schema for turning existing tables into generators
7598

7699
If you already have representative data in the main grid, you can now turn that grid into an enum schema automatically.
77100

@@ -103,7 +126,7 @@ Docs:
103126

104127
![Grid to enum schema in the app](/img/release-198/grid-to-enum-schema.png)
105128

106-
## 4. Constraint-aware auto-increment sequences for generated identifiers
129+
## 5. Constraint-aware auto-increment sequences for generated identifiers
107130

108131
Schemas can now generate sequential IDs through the domain model with `autoIncrement.sequence`.
109132

@@ -128,7 +151,7 @@ Docs:
128151

129152
- [Auto Increment Sequences](/docs/test-data/auto-increment-sequences)
130153

131-
## 5. PICT-style inline enum definitions such as `Name: values`
154+
## 6. PICT-style inline enum definitions such as `Name: values`
132155

133156
Schema text now fits more naturally with compact PICT-style authoring.
134157

@@ -153,7 +176,7 @@ Docs:
153176

154177
- [Schema Definition](/docs/test-data/Schema-Definition)
155178

156-
## 6. Import trimming controls for cleaner amend and import workflows
179+
## 7. Import trimming controls for cleaner amend and import workflows
157180

158181
Imported files and clipboard data can now be normalized during import.
159182

@@ -176,7 +199,7 @@ Docs:
176199

177200
![Import trim settings](/img/release-198/import-trim-settings.png)
178201

179-
## 7. File export settings for line endings and BOM
202+
## 8. File export settings for line endings and BOM
180203

181204
Downloads now support file transport settings without changing the preview text shown in the browser.
182205

@@ -193,7 +216,7 @@ Docs:
193216

194217
![Download encoding settings](/img/release-198/export-encoding-settings.png)
195218

196-
## 8. Right-click context menu in the main data grid
219+
## 9. Right-click context menu in the main data grid
197220

198221
The editable grid now has a right-click context menu for common grid actions.
199222

@@ -203,7 +226,7 @@ Docs:
203226

204227
- [Data Grid Editable](/docs/test-data/data-grid-editable)
205228

206-
## 9. Always-visible total row counts in the data grid
229+
## 10. Always-visible total row counts in the data grid
207230

208231
The main grid now shows total row counts, and filtered views also show how many rows remain visible.
209232

docs-src/docs/040-test-data/018-Schema-Definition.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,12 @@ Customer Name
9191
person.fullName
9292
```
9393

94-
This generates names such as `Alice Smith`.
94+
```text
95+
CreatedAt
96+
autoIncrement.timestamp(start="2026-06-12T12:39:23Z", step=1, type="seconds")
97+
```
98+
99+
The `Customer Name` example generates names such as `Alice Smith`. The `CreatedAt` example generates deterministic timestamps for time-ordered rows.
95100

96101
## Comments and blank lines
97102

docs-src/docs/040-test-data/domain/000-domain-test-data.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ Date
3939
date.between(from=1577836800000, to=1659312000000)
4040
```
4141

42+
```txt
43+
CreatedAt
44+
autoIncrement.timestamp(start="2026-06-12T12:39:23Z", step=1, type="seconds")
45+
```
46+
4247
```txt
4348
IBAN
4449
finance.iban(formatted=true, countryCode="GB")
@@ -62,9 +67,9 @@ For faker helper templates and utility functions, use faker helpers:
6267

6368
## Domains
6469

70+
- [autoIncrement](/docs/test-data/domain/autoIncrement)
6571
- [airline](/docs/test-data/domain/airline)
6672
- [animal](/docs/test-data/domain/animal)
67-
- [autoIncrement](/docs/test-data/domain/autoIncrement)
6873
- [book](/docs/test-data/domain/book)
6974
- [color](/docs/test-data/domain/color)
7075
- [commerce](/docs/test-data/domain/commerce)
@@ -89,4 +94,4 @@ For faker helper templates and utility functions, use faker helpers:
8994
- [string](/docs/test-data/domain/string)
9095
- [system](/docs/test-data/domain/system)
9196
- [vehicle](/docs/test-data/domain/vehicle)
92-
- [word](/docs/test-data/domain/word)
97+
- [word](/docs/test-data/domain/word)

docs-src/docs/040-test-data/domain/040-autoIncrement.md

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ description: "Domain keyword reference for autoIncrement."
66

77
# autoIncrement Domain
88

9-
The `autoIncrement` domain provides stateful sequence helpers for accepted generated rows.
9+
The `autoIncrement` domain provides deterministic values that move forward for each generated row.
1010

1111
## Methods
1212

@@ -43,3 +43,40 @@ Example return values:
4343
- `1`
4444
- `15`
4545
- `filename001.txt`
46+
47+
### `autoIncrement.timestamp`
48+
49+
Generates a timestamp that starts from a fixed point and increments by the configured amount for each generated row.
50+
51+
- Canonical: `awd.domain.autoIncrement.timestamp`
52+
53+
| Arg | Type | Required | Description |
54+
| --- | --- | --- | --- |
55+
| `start` | `string\|number` | no | Starting timestamp. Defaults to the generation run start time. Valid examples include `2026-06-12T12:39:23Z`, `20/03/1969`, `12-06-2026 12:39:23`, or a Unix timestamp such as `1718195963000`. |
56+
| `step` | `number` | no | Amount added for each generated row. Defaults to `1`. |
57+
| `type` | `string` | no | Unit applied to `step` for each row. Supports `milliseconds`, `seconds`, `minutes`, `hours`, `days`, `weeks`, `months`, or `years`. Defaults to `seconds`. |
58+
| `outputFormat` | `string` | no | Output format. Defaults to ISO-8601 without milliseconds. Use `iso8601` for the default behaviour or a custom pattern such as `yyyy-MM-dd HH:mm:ss`. |
59+
| `inputFormat` | `string` | no | Optional parse pattern used only for `start` when you want to match a specific text shape such as `dd/MM/yyyy` or `dd-MM-yyyy HH:mm:ss`. |
60+
61+
Examples:
62+
63+
```txt
64+
autoIncrement.timestamp()
65+
```
66+
67+
```txt
68+
autoIncrement.timestamp(start="2026-06-12T12:39:23Z", step=1, type="seconds")
69+
```
70+
71+
```txt
72+
autoIncrement.timestamp(start="20/03/1969", step=1, type="days", outputFormat="yyyy-MM-dd")
73+
```
74+
75+
```txt
76+
autoIncrement.timestamp(start="12-06-2026 12:39:23", step=15, type="minutes", outputFormat="yyyy-MM-dd HH:mm:ss", inputFormat="dd-MM-yyyy HH:mm:ss")
77+
```
78+
79+
Example return values:
80+
- `2026-06-12T12:39:23Z`
81+
- `2026-06-12T12:39:24Z`
82+
- `2026-06-12T12:39:25Z`

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"test:browser": "node ./node_modules/@playwright/test/cli.js test",
2626
"test:browser:headed": "node ./node_modules/@playwright/test/cli.js test --headed",
2727
"test:browser:install": "node ./node_modules/@playwright/test/cli.js install chromium",
28-
"test:api": "node ./node_modules/@playwright/test/cli.js test --config=playwright-api.config.js",
28+
"test:api": "node ./scripts/run-playwright-api-tests.mjs",
2929
"test:api:headed": "node ./node_modules/@playwright/test/cli.js test --config=playwright-api.config.js --headed",
3030
"test:api:verbose": "node ./node_modules/@playwright/test/cli.js test --config=playwright-api.config.js --reporter=verbose",
3131
"lint": "eslint \"apps/**/*.js\" \"packages/**/*.js\"",
@@ -103,6 +103,9 @@
103103
"@anywaydata/core": "workspace:^",
104104
"@mcp-b/webmcp-polyfill": "3.0.0",
105105
"@popperjs/core": "2.11.8",
106+
"chrono-node": "^2.9.1",
107+
"date-fns": "^4.4.0",
108+
"date-fns-tz": "^3.2.0",
106109
"papaparse": "5.5.3",
107110
"randexp": "0.5.3",
108111
"tippy.js": "6.3.7"

packages/core-ui/src/tests/grid/schema/test-data-command-catalog.test.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,18 @@ describe('Test Data Command Catalog', () => {
7171
it('should also populate domain commands for the domain dropdown section', () => {
7272
const domainCommands = getDomainCommands();
7373
expect(domainCommands.length).toBeGreaterThan(0);
74+
expect(domainCommands).toContain('autoIncrement.timestamp');
7475
expect(domainCommands).toContain('number.int');
7576
expect(domainCommands).toContain('string.counterString');
7677
expect(domainCommands.some((command) => command.startsWith('helpers.'))).toBe(false);
7778
});
7879

7980
it('should provide picker options with schema help metadata', () => {
8081
const values = getMethodPickerOptions('');
82+
const autoIncrementEntry = values.find((entry) => entry.command === 'autoIncrement.timestamp');
8183
const domainEntry = values.find((entry) => entry.command === 'number.int');
8284
const fakerEntry = values.find((entry) => entry.command === 'helpers.arrayElement');
85+
expect(autoIncrementEntry?.sourceType).toBe('domain');
8386
expect(domainEntry?.sourceType).toBe('domain');
8487
expect(fakerEntry?.sourceType).toBe('faker');
8588
expect(Array.isArray(domainEntry?.helpModel?.params)).toBe(true);

packages/core-ui/src/tests/shared/help-model-builder.test.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ describe('help-model-builder', () => {
6666
expect(renderSchemaHelpHtml(model)).toContain('delimiter');
6767
});
6868

69+
test('builds domain help for auto-increment timestamps with step metadata', () => {
70+
const model = buildSchemaHelpModel('domain', 'autoIncrement.timestamp');
71+
72+
expect(model.show).toBe(true);
73+
expect(model.heading).toContain('autoIncrement.timestamp');
74+
expect(renderSchemaHelpHtml(model)).toContain('run start time');
75+
expect(renderSchemaHelpHtml(model)).toContain('outputFormat');
76+
});
77+
6978
test('preserves custom domain docs links and parameters for auto increment help', () => {
7079
const model = buildSchemaHelpModel('domain', 'autoIncrement.sequence');
7180

packages/core-ui/src/tests/utils/domain-command-help-metadata.test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ describe('domain command help metadata docs links', () => {
1313
expect(enumHelp.docsUrl).toBe('https://anywaydata.com/docs/test-data/domain/datatype');
1414
});
1515

16+
test('maps autoIncrement timestamp help to the autoIncrement domain page', () => {
17+
const autoIncrementHelp = getDomainCommandHelp('autoIncrement.timestamp');
18+
expect(autoIncrementHelp).toBeTruthy();
19+
expect(autoIncrementHelp.docsUrl).toBe('https://anywaydata.com/docs/test-data/domain/autoIncrement');
20+
});
21+
1622
test('keeps explicit anywaydata docs pages for custom domain commands', () => {
1723
const sequenceHelp = getDomainCommandHelp('autoIncrement.sequence');
1824
expect(sequenceHelp).toBeTruthy();

packages/core/js/data_generation/domain/domainTestDataGenerator.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class DomainTestDataGenerator {
1010
this.nextAutoIncrementRuleId = 1;
1111
}
1212

13-
generateFrom(aRule) {
13+
generateFrom(aRule, executionContext = {}) {
1414
const ruleSpec = String(aRule?.ruleSpec || '').trim();
1515
const parsed = parseKeywordInvocation(ruleSpec);
1616
if (Array.isArray(parsed?.errors) && parsed.errors.length > 0) {
@@ -19,6 +19,7 @@ class DomainTestDataGenerator {
1919

2020
try {
2121
const result = executeDomainKeyword(parsed.keyword, {
22+
...executionContext,
2223
faker: this.faker,
2324
args: Array.isArray(parsed.args) ? parsed.args : [],
2425
autoIncrementState: this.#getAutoIncrementStateForRule(aRule),

0 commit comments

Comments
 (0)