create-restforge-skills 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/index.js +250 -250
- package/cli/mcp.js +103 -94
- package/package.json +2 -1
- package/skills/restforge/SKILL.md +230 -773
- package/skills/restforge/agents/openai.yaml +4 -4
- package/skills/restforge/references/auth.md +185 -145
- package/skills/restforge/references/backend-pipeline.md +388 -0
- package/skills/restforge/references/data-seeding.md +27 -0
- package/skills/restforge/references/dbschema-catalog.md +2 -2
- package/skills/restforge/references/field-validation.md +6 -4
- package/skills/restforge/references/frontend-pipeline.md +178 -0
- package/skills/restforge/references/rdf-advanced.md +1 -1
- package/skills/restforge/references/troubleshooting.md +144 -0
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
# Reference: Backend Pipeline and Decisions
|
|
2
|
+
|
|
3
|
+
Read this file when the intent router in SKILL.md points to the backend track
|
|
4
|
+
(schema, payload, endpoint, dashboard, processor, consumer, SDK, launcher) and
|
|
5
|
+
the next step is not obvious from the router row alone.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Pipeline (canonical)](#pipeline-canonical)
|
|
10
|
+
2. [Schema decisions](#schema-decisions)
|
|
11
|
+
3. [RDF payload decisions](#rdf-payload-decisions)
|
|
12
|
+
4. [Backend module type](#backend-module-type)
|
|
13
|
+
5. [Soft-delete vs hard-delete](#soft-delete-vs-hard-delete)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Pipeline (canonical)
|
|
18
|
+
|
|
19
|
+
This is the canonical (golden) path. For state-dependent choices see the decision
|
|
20
|
+
sections below; for failure handling see SKILL.md § Guardrails and
|
|
21
|
+
troubleshooting.md.
|
|
22
|
+
|
|
23
|
+
The sequence below applies to a **new project from scratch**. For an existing
|
|
24
|
+
project, start from the step that matches the current state — do not re-run
|
|
25
|
+
earlier steps that already succeeded.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
1. npx create-restforge-app <name> ── PRIMARY ── human-run scaffolder
|
|
29
|
+
One shot: creates the project folder, runs
|
|
30
|
+
npm install @restforgejs/platform (local), and bundles the designer
|
|
31
|
+
binary. This is the dominant way to start a new project.
|
|
32
|
+
Granular alternative (agent scaffolds step by step):
|
|
33
|
+
setup_create_folder → create the project folder.
|
|
34
|
+
|
|
35
|
+
2. setup_install_package (granular path only)
|
|
36
|
+
Install @restforgejs/platform into the folder. SKIP when the project
|
|
37
|
+
was created with create-restforge-app (already installed). Plain
|
|
38
|
+
'npm install @restforgejs/platform' stays valid but is not the
|
|
39
|
+
primary entry point.
|
|
40
|
+
|
|
41
|
+
3. setup_init_config
|
|
42
|
+
Write config/db-connection.env from the default template.
|
|
43
|
+
setup_get_init_template returns that same template WITHOUT writing a
|
|
44
|
+
file — use it to compare an edited config against the defaults.
|
|
45
|
+
|
|
46
|
+
4. setup_write_env / setup_update_env
|
|
47
|
+
Set values: LICENSE, SERVER_ADDRESS, SERVER_PORT, DB_TYPE,
|
|
48
|
+
DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME (9 required parameters).
|
|
49
|
+
Use setup_read_env to read existing values before overwriting.
|
|
50
|
+
Grounding for the full parameter set: setup_get_config_schema.
|
|
51
|
+
|
|
52
|
+
5. setup_validate_config
|
|
53
|
+
── GATE ── Must pass before the first tool that takes a 'config'
|
|
54
|
+
parameter (it reads the .env or connects to the database). Validates
|
|
55
|
+
database connection and license. Running such a tool before this gate
|
|
56
|
+
passes produces uninformative errors. Steps 6-9 are file-only and may
|
|
57
|
+
run before the gate.
|
|
58
|
+
Read-only by default. Set autoCreateDb=true only when the target database
|
|
59
|
+
itself does not exist yet AND the user agreed to have it created: it runs
|
|
60
|
+
CREATE DATABASE on the server (postgres/mysql only, ignored for sqlite and
|
|
61
|
+
oracle), and validation has to be repeated afterwards.
|
|
62
|
+
|
|
63
|
+
6. Decide where the table structure comes from (§ Schema decisions):
|
|
64
|
+
New table → the CLARIFY gate: the user names the fields and types, or
|
|
65
|
+
explicitly hands the design to the agent.
|
|
66
|
+
Existing DB → step 7b.
|
|
67
|
+
|
|
68
|
+
7. codegen_get_dbschema_catalog
|
|
69
|
+
── GROUNDING ── Source of truth for SDF syntax: field types,
|
|
70
|
+
constraints, shorthand syntax, relations, referential actions,
|
|
71
|
+
check operations, and the soft-delete contract. Ask only for the
|
|
72
|
+
sections the task needs ('section': fieldTypes, shorthandSyntax,
|
|
73
|
+
auditColumns, relationTypes, ...), once per session.
|
|
74
|
+
→ references/dbschema-catalog.md
|
|
75
|
+
|
|
76
|
+
7a. [author schema/<table>.js] (new table — PRIMARY path)
|
|
77
|
+
Write the complete SDF with the Write tool: every agreed field, the
|
|
78
|
+
RESTForge conventions (§ Schema decisions), indexes, uniques,
|
|
79
|
+
checks, and relations. One file per table.
|
|
80
|
+
codegen_dbschema_template (only when the user asks for a ready-made
|
|
81
|
+
template / reference example)
|
|
82
|
+
codegen_dbschema_init (only when the user explicitly asks for a
|
|
83
|
+
draft / initial / skeleton file)
|
|
84
|
+
7b. codegen_list_tables (existing DB — what is actually in there)
|
|
85
|
+
→ codegen_describe_table (columns, PK, FKs, indexes of one table)
|
|
86
|
+
→ codegen_dbschema_introspect (generate SDF from the actual schema)
|
|
87
|
+
list/describe are read-only catalog reads: they answer "what does this
|
|
88
|
+
database already hold?" without writing an SDF file. Use them before
|
|
89
|
+
introspecting a subset, and whenever a question about an existing table
|
|
90
|
+
would otherwise be answered from memory.
|
|
91
|
+
|
|
92
|
+
8. codegen_dbschema_validate
|
|
93
|
+
Validate SDF before any DDL is generated. Catch errors here, not at migrate.
|
|
94
|
+
File-only by default. With 'config' it also compares every model with the
|
|
95
|
+
database and gives one verdict per table: [OK], [DRIFT], or [ERROR] with
|
|
96
|
+
category table-missing or sdf-invalid. Exit code 1 in that mode is a
|
|
97
|
+
result (drift or error found), not a tool failure. SQLite is not
|
|
98
|
+
supported by the database mode.
|
|
99
|
+
codegen_dbschema_models (optional) lists the models already defined in the
|
|
100
|
+
SDF files with field count, primary key kind, indexes, uniques, relations.
|
|
101
|
+
|
|
102
|
+
9. codegen_dbschema_generate_ddl (optional — preview DDL without executing)
|
|
103
|
+
|
|
104
|
+
10a. codegen_dbschema_migrate (empty DB — apply DDL directly)
|
|
105
|
+
10b. codegen_dbschema_diff (existing DB — review the differences first)
|
|
106
|
+
→ codegen_dbschema_apply (apply only after confirming the drift)
|
|
107
|
+
|
|
108
|
+
11. codegen_get_field_validation_catalog
|
|
109
|
+
── GROUNDING ── before defining fieldValidation in a payload.
|
|
110
|
+
→ references/field-validation.md
|
|
111
|
+
|
|
112
|
+
12. codegen_get_query_declarative_catalog
|
|
113
|
+
── GROUNDING ── when the payload contains datatablesQuery, viewQuery,
|
|
114
|
+
viewName, exportQuery, or detailQuery.
|
|
115
|
+
codegen_validate_sql
|
|
116
|
+
Check the SELECT / WITH statement against the live database (EXPLAIN, no
|
|
117
|
+
rows executed) BEFORE pasting it into the payload: syntax, column
|
|
118
|
+
references, function existence, type compatibility, JOIN resolution.
|
|
119
|
+
|
|
120
|
+
13. codegen_generate_payload
|
|
121
|
+
Generate payload JSON from a table. Foundation for all subsequent
|
|
122
|
+
codegen operations. 'detail' (a detail table name) also writes the
|
|
123
|
+
masterDetail block, the detail query file, and the composite actions.
|
|
124
|
+
Re-running it on an existing payload keeps the customisations made to
|
|
125
|
+
generator-owned keys; commit payload/.meta/<name>.json together with the
|
|
126
|
+
payload (see § RDF payload decisions).
|
|
127
|
+
|
|
128
|
+
14. codegen_validate_payload
|
|
129
|
+
Validate the payload before codegen. Catch errors here.
|
|
130
|
+
|
|
131
|
+
15. codegen_diff_payload (when a payload exists and the DB schema has changed)
|
|
132
|
+
→ codegen_sync_payload (apply the schema drift to the payload)
|
|
133
|
+
'expandFk' (with 'table') also writes query/<table>-join.sql so
|
|
134
|
+
datatablesQuery (and viewQuery) show columns of referenced tables.
|
|
135
|
+
|
|
136
|
+
16a. codegen_create_endpoint (standard CRUD module)
|
|
137
|
+
Leave 'database' UNSET unless the user named a database: the CLI then
|
|
138
|
+
auto-detects DB_TYPE from the active config (fallback postgres), so a
|
|
139
|
+
MySQL/Oracle/SQLite project generates for its own dialect. 'config'
|
|
140
|
+
selects that .env explicitly. createDemo (default true) writes the
|
|
141
|
+
curl / Postman / Insomnia examples. force defaults to true — an existing
|
|
142
|
+
module is overwritten and the previous files are moved to
|
|
143
|
+
.restforge/archive/<run>/<original relative path> (the 5 most recent runs
|
|
144
|
+
are kept); force=false stops without writing anything when the module
|
|
145
|
+
exists, which is the closest thing to a conflict dry run.
|
|
146
|
+
16b. codegen_get_dashboard_catalog
|
|
147
|
+
→ codegen_validate_dashboard_payload
|
|
148
|
+
── GATE ── structural check of a dashboard payload; writes nothing.
|
|
149
|
+
On a platform without 'dashboard create --validate-only' the tool
|
|
150
|
+
answers with an upgrade suggestion instead of a validation result — then
|
|
151
|
+
let the generator itself validate, since it runs the same validator
|
|
152
|
+
before it writes.
|
|
153
|
+
→ codegen_create_dashboard (analytic dashboard with SQL widgets)
|
|
154
|
+
No database auto-detection here: the dashboard command uses 'database'
|
|
155
|
+
when given and plain postgres otherwise, so pass it whenever the project
|
|
156
|
+
is not postgres. force defaults to true and then re-registers the project
|
|
157
|
+
under the database type carried by this call; force=false refuses cleanly
|
|
158
|
+
instead of writing.
|
|
159
|
+
16c. codegen_create_processor (background processing)
|
|
160
|
+
16d. codegen_create_kafka_consumer (Kafka event streaming)
|
|
161
|
+
→ runtime_generate_consumer_launcher
|
|
162
|
+
Prepare a way to RUN that consumer: mode=host writes the fixed
|
|
163
|
+
consumer-start/consumer-stop pair in the project root, mode=pm2 produces
|
|
164
|
+
ecosystem.config.js + consumer-manager.sh in ./deploy/. 'config' is
|
|
165
|
+
required and must end with .env.
|
|
166
|
+
→ (user runs the consumer) — the agent never starts it, same rule as the
|
|
167
|
+
server launcher below.
|
|
168
|
+
|
|
169
|
+
17. codegen_generate_test (optional)
|
|
170
|
+
Jest + Supertest integration test for an endpoint that ALREADY exists.
|
|
171
|
+
Natural follow-up once the module is generated.
|
|
172
|
+
|
|
173
|
+
18. project_sdk_generate (optional)
|
|
174
|
+
Write the JavaScript SDK source for the project (one resource file per
|
|
175
|
+
registered endpoint, plus client.auth when the backend auth extension is
|
|
176
|
+
installed) so a frontend calls client.<resource>.<verb>(payload). Source
|
|
177
|
+
only: the user runs install / build / deploy. Without force it refuses
|
|
178
|
+
when an SDK exists; with force=true it overwrites IN PLACE with no
|
|
179
|
+
archive, so local edits in the SDK folder are lost.
|
|
180
|
+
|
|
181
|
+
19. runtime_check_launcher_exists → runtime_validate_preflight
|
|
182
|
+
→ runtime_generate_launcher
|
|
183
|
+
Check what is already there (read-only), validate the runtime
|
|
184
|
+
prerequisites, then write the launcher script. The agent STOPS here. The
|
|
185
|
+
user executes the launcher — the server runs independently of the agent
|
|
186
|
+
session.
|
|
187
|
+
|
|
188
|
+
20. (user executes the launcher)
|
|
189
|
+
|
|
190
|
+
21. runtime_check_status
|
|
191
|
+
Verify the server is running and endpoints are reachable.
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Three hard checkpoints (SKILL.md § Guardrails): **step 5** is a gate — no tool that
|
|
195
|
+
takes a `config` parameter before `setup_validate_config` passes; **step 6** is
|
|
196
|
+
the clarify gate — no new table is written before its structure is settled;
|
|
197
|
+
**step 19** is where the agent stops (it generates the launcher, never runs the
|
|
198
|
+
server).
|
|
199
|
+
|
|
200
|
+
**Payload naming differs per generator** — the value is handed to the CLI and
|
|
201
|
+
each verb resolves it differently:
|
|
202
|
+
|
|
203
|
+
| Tool | Accepted form |
|
|
204
|
+
|---|---|
|
|
205
|
+
| `codegen_create_endpoint`, `codegen_create_processor` | bare name, with or without `.json`; lowercased by the CLI; path forms are rejected |
|
|
206
|
+
| `codegen_create_dashboard`, `codegen_validate_dashboard_payload` | name or relative path **with** the extension, used verbatim (`dash-sales.json`, `payload/dash-sales.json`) — nothing is appended |
|
|
207
|
+
| `codegen_create_kafka_consumer` | name or path |
|
|
208
|
+
|
|
209
|
+
**Config selection across calls.** Most backend tools take an optional `config`
|
|
210
|
+
and otherwise fall back to the default recorded per working directory in
|
|
211
|
+
`.restforge/defaults.json`. Manage that default instead of repeating the file
|
|
212
|
+
name on every call: `setup_list_configs` (which `.env` files exist),
|
|
213
|
+
`setup_set_default_config` (record one), `setup_get_default_config` (which one is
|
|
214
|
+
active), `setup_clear_default_config` (remove it — afterwards every call must
|
|
215
|
+
name its config explicitly).
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Schema decisions
|
|
220
|
+
|
|
221
|
+
Pick the branch from **what the user asked for**, not from whether the database
|
|
222
|
+
is empty. "Create a product table" is a request for a real table, never a request
|
|
223
|
+
for a skeleton.
|
|
224
|
+
|
|
225
|
+
- **New table, fields not stated** (e.g. "buatkan tabel product") → **CLARIFY
|
|
226
|
+
gate.** Ask once, in one short message, for the fields and their types
|
|
227
|
+
(optionally: required, unique, relations to other tables), and say the user may
|
|
228
|
+
leave the design to the agent. Write nothing and call no schema tool while
|
|
229
|
+
waiting: no `codegen_dbschema_init`, no `codegen_dbschema_template`, no file.
|
|
230
|
+
- **New table, fields stated** (with or without types) → author the SDF from
|
|
231
|
+
them. Missing types are chosen from the field names and the conventions below;
|
|
232
|
+
ask only when a type is genuinely ambiguous. Do not add business fields the user
|
|
233
|
+
did not list, apart from the conventional PK and audit columns.
|
|
234
|
+
- **New table, design handed to the agent** ("terserah", "tentukan sendiri",
|
|
235
|
+
"you decide", or no structure after the clarify question) → design the fields
|
|
236
|
+
from domain knowledge of the entity, then author the SDF with the conventions
|
|
237
|
+
below. Report the chosen structure briefly after the file is validated.
|
|
238
|
+
- **RESTForge table conventions** (apply to every authored table; syntax from the
|
|
239
|
+
catalog):
|
|
240
|
+
- table name snake_case, singular (`product`, `stock_inbound_item`);
|
|
241
|
+
- primary key `<table>_id` as `string:36 pk`, or the PK style the other SDF
|
|
242
|
+
files of the project already use;
|
|
243
|
+
- foreign key named after the target PK (`category_id` →
|
|
244
|
+
`fk:category.category_id`), only to a table that exists in the schema folder
|
|
245
|
+
(`codegen_dbschema_models`) or that the user asked for — otherwise ask;
|
|
246
|
+
- business code `string:<n> unique notnull`, display name `notnull`,
|
|
247
|
+
money/quantity `decimal:15,2 default:0` with a `gte: 0` check, active flag
|
|
248
|
+
`is_active` as `boolean default:true`, an index on columns used for search;
|
|
249
|
+
- the 4 audit columns exactly as the catalog `auditColumns` section gives
|
|
250
|
+
them (`created_at`, `created_by`, `updated_at`, `updated_by`) — the RDF
|
|
251
|
+
generator assumes they exist.
|
|
252
|
+
- **Explicit draft / initial / skeleton request** ("draft table", "inisial
|
|
253
|
+
table", "skeleton schema", "file awal untuk diisi sendiri") →
|
|
254
|
+
`codegen_dbschema_init`. It writes the generic `dummy` template (a sample
|
|
255
|
+
column per field type plus audit columns) for the user to edit. Never use it as
|
|
256
|
+
a first step before authoring a real table.
|
|
257
|
+
- **Explicit template request** ("pakai template", "contoh schema invoice",
|
|
258
|
+
"template apa saja untuk ERP") → `codegen_dbschema_template` (list, show, or
|
|
259
|
+
generate).
|
|
260
|
+
- **Table already in the database** → `codegen_dbschema_introspect` to generate SDF from the
|
|
261
|
+
actual schema. Do not hand-write a schema that a real DB can describe.
|
|
262
|
+
- **Question about what the DB already contains** → `codegen_list_tables` (tables
|
|
263
|
+
and views) and `codegen_describe_table` (columns, primary key, foreign keys,
|
|
264
|
+
indexes of one table). Both are read-only live introspection; they are the
|
|
265
|
+
safe way to answer such a question without generating anything.
|
|
266
|
+
- **Starting from a UI design** (HTML mockup, screenshot, image, Figma export)
|
|
267
|
+
→ first confirm the design is an entity to model, not a dashboard/analytics
|
|
268
|
+
screen (those map to the Dashboard RDF branch below, not to a new SDF table).
|
|
269
|
+
Then classify visible elements into stored / derived / relation / audit, draft
|
|
270
|
+
the SDF, and run `codegen_dbschema_validate`. The design shows presentation,
|
|
271
|
+
not storage — do not turn every visible label into a column.
|
|
272
|
+
→ references/design-to-sdf.md
|
|
273
|
+
- **"Is my schema valid and in sync with the database?"** →
|
|
274
|
+
`codegen_dbschema_validate` with `config` (optionally `table`): one verdict per
|
|
275
|
+
table, `[OK]`, `[DRIFT]`, or `[ERROR]` (`table-missing` → migrate/apply fixes
|
|
276
|
+
it; `sdf-invalid` → fix the file). Exit code 1 means drift or an error was
|
|
277
|
+
found, not that the tool failed. Not available for SQLite.
|
|
278
|
+
- **DB exists with drift** → `codegen_dbschema_diff` to review the per-column
|
|
279
|
+
differences, then `codegen_dbschema_apply`. Not `codegen_dbschema_migrate` —
|
|
280
|
+
that is for empty DBs only.
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## RDF payload decisions
|
|
285
|
+
|
|
286
|
+
- **No payload yet** → `codegen_generate_payload`.
|
|
287
|
+
- **Payload exists, edit is API-layer only** (e.g. add/adjust `fieldValidation`,
|
|
288
|
+
messages, or a query — no column added or dropped) → ground via the matching
|
|
289
|
+
catalog, edit `payload/<name>.json`, run `codegen_validate_payload`, then regenerate
|
|
290
|
+
with `codegen_create_endpoint`. No DB migration: the schema (SDF) is unchanged.
|
|
291
|
+
- **Payload exists, DB schema changed** → `codegen_diff_payload` first, then
|
|
292
|
+
`codegen_sync_payload`. Turning the RDF into a frontend UDF is a separate
|
|
293
|
+
frontend step (Frontend Pipeline step 2a), not part of this branch.
|
|
294
|
+
- **Customisations survive regeneration.** `codegen_generate_payload` and
|
|
295
|
+
`codegen_sync_payload` keep edits made to generator-owned keys (`action`,
|
|
296
|
+
`fieldValidation`, inline SQL, `auditColumns`, the datatables query file).
|
|
297
|
+
A column deliberately removed from `fieldName` stays removed because generate
|
|
298
|
+
records the known columns in `payload/.meta/<name>.json`; commit that snapshot
|
|
299
|
+
with the payload and never edit it.
|
|
300
|
+
- **Show columns of a referenced table in the list** (e.g. `supplier_name` next
|
|
301
|
+
to `supplier_id`) → `codegen_sync_payload` with `table` and `expandFk`
|
|
302
|
+
(`"both"`, or `"datatables-only"` to leave a custom `viewQuery` alone). It
|
|
303
|
+
writes `query/<table>-join.sql` and points `datatablesQuery` (and `viewQuery`)
|
|
304
|
+
at it; `FK_AUTO_JOIN` picks LEFT or INNER JOIN. The display column per FK is
|
|
305
|
+
chosen automatically and is never the primary key. Use `fkColumns`
|
|
306
|
+
(`ref_table.column`, or `local_fk:ref_table.column`) to override it and
|
|
307
|
+
`expandFkSkip` to leave a relation out. When the CLI reports "No natural
|
|
308
|
+
display column found", **ask the user** which column to show or whether to
|
|
309
|
+
skip that relation — do not pick one yourself.
|
|
310
|
+
- **Custom query needed** → ground via `codegen_get_query_declarative_catalog`,
|
|
311
|
+
check the statement with `codegen_validate_sql` against the live database, then
|
|
312
|
+
set `datatablesQuery`, `viewQuery`, or `exportQuery` in the payload. External
|
|
313
|
+
SQL files use the `file:` prefix
|
|
314
|
+
(e.g. `"datatablesQuery": "file:sql/orders.sql"`). Validating first turns a
|
|
315
|
+
runtime 500 into an error message before the payload is even written.
|
|
316
|
+
A joined column must not reuse the name of a main table column
|
|
317
|
+
(`s.status_name AS status` when `status` is the FK column is rejected by
|
|
318
|
+
`codegen_validate_payload`, and `codegen_migrate_payload` then skips that
|
|
319
|
+
field). Select the main table column itself and give the joined column its
|
|
320
|
+
own name, e.g. `s.status_name`.
|
|
321
|
+
|
|
322
|
+
> **"Add an uppercase / case constraint" is ambiguous — disambiguate first.**
|
|
323
|
+
> In RESTForge `uppercase` is an RDF `fieldValidation` *normalization transform*
|
|
324
|
+
> (forces the stored value to upper case), not a reject-if-not-uppercase
|
|
325
|
+
> validator. If the user wants rejection, use `pattern`. If the user wants the
|
|
326
|
+
> database to enforce it, that is an SDF check constraint, not RDF. Confirm which
|
|
327
|
+
> one is meant before editing — see SKILL.md § Grounding-First Rules.
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Backend module type
|
|
332
|
+
|
|
333
|
+
- **Standard CRUD** → `codegen_create_endpoint`. One payload = one module.
|
|
334
|
+
- **Analytic dashboard** → `codegen_validate_dashboard_payload`, then
|
|
335
|
+
`codegen_create_dashboard`. Payload must have `widgets` (not `tableName`); page
|
|
336
|
+
name must be prefixed `dash-`; the payload argument keeps its `.json`
|
|
337
|
+
extension.
|
|
338
|
+
- **Background job** → `codegen_create_processor`.
|
|
339
|
+
- **Kafka event consumer** → `codegen_create_kafka_consumer`; requires
|
|
340
|
+
`KAFKA_ENABLED=true` in config. The consumer runtime is a separate process:
|
|
341
|
+
`runtime_generate_consumer_launcher` prepares it (host scripts or PM2 deploy
|
|
342
|
+
files) and the user starts it.
|
|
343
|
+
- **JavaScript client for a generated project** → `project_sdk_generate`. Run it
|
|
344
|
+
after the endpoints exist, and after `project_auth` when the project needs
|
|
345
|
+
auth, so `client.auth` is included.
|
|
346
|
+
- **Integration test for an existing endpoint** → `codegen_generate_test`.
|
|
347
|
+
- **Every feature endpoint needs its `action` flag.** A feature block without
|
|
348
|
+
the matching flag in `action` produces no endpoint.
|
|
349
|
+
→ references/rdf-advanced.md § The `action` Block
|
|
350
|
+
- **Workflow (status transitions)** → set `action.workflow: true` and add a
|
|
351
|
+
`workflow` block (`statusField`, `transitions` as a map `status → [targets]`,
|
|
352
|
+
optional `hooks` keyed by target status) to the RDF; generates
|
|
353
|
+
`/change-status`. The buttons are UDF `workflowActions` — a frontend key that
|
|
354
|
+
never goes into the RDF.
|
|
355
|
+
→ references/rdf-advanced.md § Workflow
|
|
356
|
+
- **Master-detail (composite CRUD)** → run `codegen_generate_payload` with
|
|
357
|
+
`detail: "<detail table>"`. It writes the `masterDetail` block, the detail
|
|
358
|
+
query file, and `action.createComposite` / `updateComposite` /
|
|
359
|
+
`readComposite`; then fill `headerCalculations` and `calculated` formulas by
|
|
360
|
+
hand. Generates `/create-composite`, `/update-composite`, `/read-composite`.
|
|
361
|
+
→ references/rdf-advanced.md § Master-Detail
|
|
362
|
+
- **Summary numbers per resource** (count, sum, avg, min, max, optionally
|
|
363
|
+
grouped) → `action.aggregate: true`; the request body carries the operations,
|
|
364
|
+
and `aggregateConfig.joins` only whitelists JOINs. For charts or KPIs across
|
|
365
|
+
several tables, prefer a backend dashboard (`codegen_create_dashboard`).
|
|
366
|
+
→ references/rdf-advanced.md § Aggregate Config
|
|
367
|
+
- **Excel export** → `/export` works by default (falls back to
|
|
368
|
+
`SELECT {fields} FROM tableName`); customise the columns/filter with
|
|
369
|
+
`exportQuery` in the RDF payload. Tune `EXPORT_FILE_EXPIRY` / `EXPORT_CHUNK_SIZE`
|
|
370
|
+
in config. Ground `exportQuery` via `codegen_get_query_declarative_catalog`.
|
|
371
|
+
→ references/rdf-advanced.md § Data Source Resolution
|
|
372
|
+
- **Excel import (.xlsx)** → set `action.import: true` and add `importConfig`
|
|
373
|
+
with `enabled: true` (`upsertKeys`, `upsertStrategy`, `requiredFields`,
|
|
374
|
+
optional `lookupFields`) to the RDF payload; generates `/import-upload`,
|
|
375
|
+
`/import-preview`, `/import-commit`, and `/import-status`.
|
|
376
|
+
→ references/rdf-advanced.md § Import Config
|
|
377
|
+
- Activate on an existing project: edit the payload to add `importConfig`
|
|
378
|
+
(and/or `exportQuery`) → `codegen_validate_payload` → `codegen_create_endpoint`.
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## Soft-delete vs hard-delete
|
|
383
|
+
|
|
384
|
+
- Use soft-delete when deleted rows must be audited or recoverable. Declare
|
|
385
|
+
`softDelete: { enabled: true }` in SDF and add the three contract columns
|
|
386
|
+
(`is_deleted`, `deleted_at`, `deleted_by`).
|
|
387
|
+
- Soft-delete is supported on PostgreSQL only (Phase 1).
|
|
388
|
+
- Tables with composite UNIQUE constraints are incompatible with soft-delete.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Reference: Data Seeding and Migration (rows)
|
|
2
|
+
|
|
3
|
+
Read this file when the user wants to export, import, seed, back up, or move
|
|
4
|
+
table rows. Structure changes go through the dbschema tools, not through these.
|
|
5
|
+
|
|
6
|
+
Move table **rows** through SDF-driven envelope files. This is for data, never
|
|
7
|
+
for schema — use the dbschema tools for structure.
|
|
8
|
+
|
|
9
|
+
**Default output location:** `data-storage/<schema>/<table>.json`, relative to the
|
|
10
|
+
project cwd. The `data-storage` folder is the default of the `storagePath` param
|
|
11
|
+
(CLI `--storage-path <folder>`); override it to write elsewhere. The SDF read from
|
|
12
|
+
is `schemaPath` (CLI `--schema-path`, default `schema`).
|
|
13
|
+
|
|
14
|
+
- **Export / dump / snapshot / back up rows** → `data_pull`. Scope is exactly one
|
|
15
|
+
of `table`, `schema`, or `allSchemas`. Only tables registered in the SDF can be
|
|
16
|
+
pulled. `force: true` overwrites existing envelope files. Optional `limit`,
|
|
17
|
+
`batchSize`, `config` (falls back to the default set via `config set-default`,
|
|
18
|
+
i.e. `.restforge/defaults.json`), `schemaPath`, `storagePath`.
|
|
19
|
+
- **Import / load / seed / restore rows** → `data_push`. Same file names as
|
|
20
|
+
`data_pull`, so pulled files push back directly. Scope is exactly one of `table`,
|
|
21
|
+
`schema`, or `allSchemas`; for `schema`/`allSchemas` tables load in FK
|
|
22
|
+
parent→child order.
|
|
23
|
+
- **Move data between databases** → `data_pull` from the source, then `data_push`
|
|
24
|
+
into the target (`config` selects the env per side).
|
|
25
|
+
- ⚠ `data_push` is **APPEND-ONLY** (batch INSERT, no upsert/replace). Running it
|
|
26
|
+
twice inserts the rows twice. Confirm with the user before pushing into a
|
|
27
|
+
database that may already hold those rows.
|
|
@@ -158,8 +158,8 @@ Oracle enforces the same way. `codegen_dbschema_diff` treats `noAction` and
|
|
|
158
158
|
|
|
159
159
|
## Audit Columns
|
|
160
160
|
|
|
161
|
-
4 standard columns for tables managed by RESTForge.
|
|
162
|
-
|
|
161
|
+
4 standard columns for tables managed by RESTForge. Every authored table
|
|
162
|
+
declares them with the shorthand below; lookup/system tables may leave them out.
|
|
163
163
|
|
|
164
164
|
| Column | SDF shorthand | Notes |
|
|
165
165
|
|---|---|---|
|
|
@@ -60,10 +60,10 @@ Constraints not listed for a given type will be rejected.
|
|
|
60
60
|
|
|
61
61
|
| Type | Database Types | Applicable Constraints |
|
|
62
62
|
|---|---|---|
|
|
63
|
-
| `string` | VARCHAR, TEXT, CHAR | required, unique, default, primaryKey, autoGenerate, nullable, minLength, maxLength, pattern, patternMessage, format, enum, trim, lowercase, uppercase |
|
|
64
|
-
| `integer` | INTEGER, INT, BIGINT | required, unique, default, primaryKey, nullable, min, max, precision, scale, positive, negative, integer, format |
|
|
65
|
-
| `decimal` | DECIMAL, NUMERIC | required, unique, default, primaryKey, nullable, min, max, precision, scale, positive, negative, integer, format |
|
|
66
|
-
| `number` | NUMERIC | required, unique, default, primaryKey, nullable, min, max, precision, scale, positive, negative, integer, format |
|
|
63
|
+
| `string` | VARCHAR, TEXT, CHAR | required, unique, default, primaryKey, autoGenerate, nullable, minLength, maxLength, pattern, patternMessage, format, enum, notEqual, trim, lowercase, uppercase |
|
|
64
|
+
| `integer` | INTEGER, INT, BIGINT | required, unique, default, primaryKey, nullable, min, max, precision, scale, positive, negative, integer, notEqual, format |
|
|
65
|
+
| `decimal` | DECIMAL, NUMERIC | required, unique, default, primaryKey, nullable, min, max, precision, scale, positive, negative, integer, notEqual, format |
|
|
66
|
+
| `number` | NUMERIC | required, unique, default, primaryKey, nullable, min, max, precision, scale, positive, negative, integer, notEqual, format |
|
|
67
67
|
| `boolean` | BOOLEAN | required, unique, default, primaryKey, nullable, strict |
|
|
68
68
|
| `date` | DATE | required, unique, default, primaryKey, autoGenerate, nullable, format, min, max, before, after |
|
|
69
69
|
| `datetime` | TIMESTAMP | required, unique, default, primaryKey, autoGenerate, nullable, format, min, max, before, after |
|
|
@@ -105,6 +105,7 @@ Constraints not listed for a given type will be rejected.
|
|
|
105
105
|
| `patternMessage` | string | — | `"patternMessage": "Invalid format"` |
|
|
106
106
|
| `format` | string | `formatMessage` | `"format": "email"` (see format presets) |
|
|
107
107
|
| `enum` | array | `enumMessage` | `"enum": ["active", "inactive"]` |
|
|
108
|
+
| `notEqual` | string | `notEqualMessage` | `"notEqual": "none"` (derived from an SDF CHECK `neq`) |
|
|
108
109
|
| `trim` | boolean | — | `"trim": true` |
|
|
109
110
|
| `lowercase` | boolean | — | `"lowercase": true` |
|
|
110
111
|
| `uppercase` | boolean | — | `"uppercase": true` |
|
|
@@ -126,6 +127,7 @@ Constraints not listed for a given type will be rejected.
|
|
|
126
127
|
| `positive` | boolean | `positiveMessage` | `"positive": true` |
|
|
127
128
|
| `negative` | boolean | `negativeMessage` | `"negative": true` |
|
|
128
129
|
| `integer` | boolean | `integerMessage` | `"integer": true` |
|
|
130
|
+
| `notEqual` | number | `notEqualMessage` | `"notEqual": 0` (derived from an SDF CHECK `neq`) |
|
|
129
131
|
| `format` | string | — | `"format": "currency"` (the only valid value) |
|
|
130
132
|
|
|
131
133
|
`format: "currency"` is a display hint, not a validator. `codegen_migrate_payload`
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Reference: Frontend Pipeline and Decisions
|
|
2
|
+
|
|
3
|
+
Read this file when the intent router in SKILL.md points to the frontend track
|
|
4
|
+
(UDF, frontend page, plugin, designer generate).
|
|
5
|
+
|
|
6
|
+
## Table of Contents
|
|
7
|
+
|
|
8
|
+
1. [Pipeline (canonical)](#pipeline-canonical)
|
|
9
|
+
2. [UDF decisions](#udf-decisions)
|
|
10
|
+
3. [Regenerating an existing app](#regenerating-an-existing-app)
|
|
11
|
+
4. [Page type](#page-type)
|
|
12
|
+
5. [Plugin choice](#plugin-choice)
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Pipeline (canonical)
|
|
17
|
+
|
|
18
|
+
This is the canonical (golden) path for the frontend track. It runs
|
|
19
|
+
**independently** from the backend pipeline. The backend API must be running and
|
|
20
|
+
reachable at `apiBaseUrl` before the generated frontend is useful, but the
|
|
21
|
+
frontend can be defined and generated without the backend live.
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
1. designer_list_plugins
|
|
25
|
+
── GROUNDING ── list available output plugins before creating the UDF.
|
|
26
|
+
Built-in: vanilla-js-basic (no auth), vanilla-js-auth and
|
|
27
|
+
vanilla-js-custom (JWT auth + RBAC).
|
|
28
|
+
→ references/udf-catalog.md § Plugins
|
|
29
|
+
|
|
30
|
+
2a. codegen_migrate_payload ── PRIMARY ── when a backend RDF payload exists
|
|
31
|
+
Convert the RDF into a split UDF set in the output folder (default
|
|
32
|
+
frontend/payload/): app-config.json, pages/<pageId>.json, the aggregator
|
|
33
|
+
<appCode>.json, and snapshots in .meta/pages/. Run it once per RDF with
|
|
34
|
+
the same output folder to add pages to one app. It derives fields, types,
|
|
35
|
+
lookups, details[], status filters, and the date patterns from the
|
|
36
|
+
backend, so start here instead of writing pages by hand.
|
|
37
|
+
Needs a license and the backend config (it takes `config`, so the
|
|
38
|
+
SKILL.md Guardrail 3 gate applies).
|
|
39
|
+
2b. [hand-write the UDF] only when there is no RDF to migrate from
|
|
40
|
+
Ground every key with designer_get_udf_catalog first.
|
|
41
|
+
designer_init_project (optional) scaffold a project folder with the
|
|
42
|
+
assets of an auth-capable plugin (vanilla-js-auth / vanilla-js-custom).
|
|
43
|
+
It writes no UDF payload file.
|
|
44
|
+
|
|
45
|
+
3. designer_get_udf_catalog
|
|
46
|
+
── GROUNDING ── call before editing any UDF page.
|
|
47
|
+
Returns valid field types, enums, limits, and validation constants for
|
|
48
|
+
the installed designer version.
|
|
49
|
+
→ references/udf-catalog.md
|
|
50
|
+
|
|
51
|
+
4. [edit the UDF pages]
|
|
52
|
+
Edit pages/<pageId>.json (labels, layout, features, workflowActions) and
|
|
53
|
+
the aggregator (navigation, homepage). One page entry = one CRUD page or
|
|
54
|
+
one dashboard page. Re-running migrate later merges RDF changes into
|
|
55
|
+
these files without losing the edits (§ UDF decisions).
|
|
56
|
+
|
|
57
|
+
5. designer_validate_payload
|
|
58
|
+
── GATE ── validate the UDF (the aggregator file). Catches structural
|
|
59
|
+
errors before generation. Run before preview or generate, every time.
|
|
60
|
+
|
|
61
|
+
6. designer_preview_files
|
|
62
|
+
Dry-run: list the files the generator produces, without writing to disk.
|
|
63
|
+
It does not read the output folder, so it cannot show which files a
|
|
64
|
+
re-generate will merge, keep, or skip.
|
|
65
|
+
|
|
66
|
+
7. designer_generate
|
|
67
|
+
Generate frontend HTML/JS/CSS from the aggregator. On an existing app it
|
|
68
|
+
merges into the files on disk (§ Regenerating an existing app).
|
|
69
|
+
The agent STOPS here — the user opens the output in a browser.
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
For plugin development (custom output plugins):
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
designer_scaffold_plugin → [develop plugin templates]
|
|
76
|
+
→ designer_inspect_plugin (verify plugin metadata and capabilities)
|
|
77
|
+
→ designer_generate (test generation with the custom plugin)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Two checkpoints (SKILL.md § Guardrails): **step 5** is a gate — never `designer_generate`
|
|
81
|
+
an unvalidated payload; **step 7** is where the agent stops (generates files, does
|
|
82
|
+
not serve or deploy).
|
|
83
|
+
|
|
84
|
+
Licensing note: the Designer tools (`designer_preview_files`, `designer_generate`,
|
|
85
|
+
etc.) do **not** require a RESTForge license, unlike the `codegen_*`, `runtime_*`,
|
|
86
|
+
and `setup_validate_config` tools. `codegen_migrate_payload` is a `codegen_*`
|
|
87
|
+
tool, so step 2a runs in the backend project folder.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## UDF decisions
|
|
92
|
+
|
|
93
|
+
- **A backend RDF exists** → `codegen_migrate_payload`; do not write the page by
|
|
94
|
+
hand. Run it once per RDF into the same output folder to build one app.
|
|
95
|
+
- **Re-running migrate** (RDF changed, or a page must pick up new columns) →
|
|
96
|
+
run it again **without** `overwrite`. Existing pages are merged: user edits
|
|
97
|
+
(labels, layout, removed fields, blocks) are kept and RDF changes to untouched
|
|
98
|
+
values are applied. Backend contract values (`apiPath`, `primaryKey`, `type`,
|
|
99
|
+
`required`, `maxlength`, `decimalPlaces`, `tableField`, lookup source, option
|
|
100
|
+
values) follow the RDF when both sides changed, with a warning. The merge uses
|
|
101
|
+
the snapshots in `<output>/.meta/pages/<pageId>.json`; commit them with the
|
|
102
|
+
pages and never edit them. The aggregator (`navigation`, `homepage`) and
|
|
103
|
+
`app-config.json` are merged too.
|
|
104
|
+
- **`overwrite: true`** recreates the page from the RDF and discards every
|
|
105
|
+
customisation (the old file goes to `.restforge/archive/`). Confirm with the
|
|
106
|
+
user before using it.
|
|
107
|
+
- **Date patterns** → `appConfig.dateFormat` / `dateTimeFormat` are rewritten
|
|
108
|
+
from the backend `DATEFORMAT` / `DATETIMEFORMAT` on every migrate. After the
|
|
109
|
+
backend changes them, re-run migrate and `designer_generate`; never edit the
|
|
110
|
+
frontend values to differ from the backend.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Regenerating an existing app
|
|
115
|
+
|
|
116
|
+
`designer_generate` without `overwrite` is the normal way to bring UDF changes
|
|
117
|
+
into an app that already exists, for scope `app` and `form` alike. The user may
|
|
118
|
+
edit the generated app files (`<page>.html`, `js/<page>.js`, `js/common.js`,
|
|
119
|
+
`js/config.js`, `sidebar.html`, assets); the edits survive.
|
|
120
|
+
|
|
121
|
+
- **Merge** — each text file is merged three-way against the snapshot of the
|
|
122
|
+
last generate in `<output parent>/.meta/<output folder>/` (for
|
|
123
|
+
`frontend/apps/<project>` that is `frontend/apps/.meta/<project>/`). User
|
|
124
|
+
edits are kept and UDF or template changes on untouched lines are applied.
|
|
125
|
+
The result lists a status per file: `written`, `merged`, `unchanged`, `kept`,
|
|
126
|
+
`conflict`, or `skipped`. Commit the `.meta` folder with the app, except
|
|
127
|
+
`conflicts/` (it has its own `.gitignore`); never edit it.
|
|
128
|
+
- **Conflict** — a file whose edits clash with the new output is left untouched;
|
|
129
|
+
the version with conflict markers is in `.meta/<project>/conflicts/<path>`.
|
|
130
|
+
All other files are still processed and the command exits 1. That is a result
|
|
131
|
+
to relay, not a tool failure: resolve the file (apply the conflict version and
|
|
132
|
+
keep the user's intent), then run generate again. Do not use `overwrite` as
|
|
133
|
+
the fix.
|
|
134
|
+
- **Managed lines** — `BACKEND_DATE_FORMAT` and `BACKEND_DATETIME_FORMAT` in
|
|
135
|
+
`js/common.js` (marked `restforge:managed`) always take the new value.
|
|
136
|
+
- **Assets** — a modified asset (CSS, vendor bundle, image) is kept with a
|
|
137
|
+
warning; the new version is in `conflicts/<path>`.
|
|
138
|
+
- **App generated before snapshots existed** — files that differ from the new
|
|
139
|
+
output are kept with a warning and the new version goes to `conflicts/<path>`.
|
|
140
|
+
The user merges by hand, or recreates one page with `scope: "form"`, `page`,
|
|
141
|
+
and `overwrite`.
|
|
142
|
+
- **`index.html`** — merged like the shared files when it carries the
|
|
143
|
+
`RESTForge-Designer:LandingGenerated` marker. Without the marker it is never
|
|
144
|
+
replaced, even with `overwrite`, and is reported as `skipped`; a new page then
|
|
145
|
+
does not appear on the homepage. Remove the file or add the marker line back,
|
|
146
|
+
then generate with scope `app`.
|
|
147
|
+
- **`overwrite: true`** recreates every file from scratch and discards the
|
|
148
|
+
customisations in all of them (each old file that differs is archived to
|
|
149
|
+
`.restforge/archive/`). Confirm with the user before using it.
|
|
150
|
+
- **Not handled** — files of a page removed from the UDF are not deleted, and a
|
|
151
|
+
clean text merge is not proof that the logic still works; test the result in
|
|
152
|
+
the browser.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Page type
|
|
157
|
+
|
|
158
|
+
- **Standard CRUD page** → `pageType: "crud"` (default) with `apiPath`,
|
|
159
|
+
`primaryKey`, `displayField`, and `fields[]`.
|
|
160
|
+
- **Dashboard page** → `pageType: "dashboard"` with a `dataSources` object
|
|
161
|
+
(`name → { url, method, body }`) and `rows[]` → `columns[]` → `widgets[]`.
|
|
162
|
+
→ references/udf-catalog.md § Dashboard Page
|
|
163
|
+
- **Page with approval workflow** → add `workflow` (`statusField` and
|
|
164
|
+
`transitions`, identical to the RDF) and `workflowActions[]` whose `actionId`
|
|
165
|
+
equals the target status. Add `fieldStates` to lock rows in final statuses.
|
|
166
|
+
→ references/udf-catalog.md § Workflow Actions
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Plugin choice
|
|
171
|
+
|
|
172
|
+
- **No auth** → `vanilla-js-basic`.
|
|
173
|
+
- **Auth WITH RBAC, built into the app** → `vanilla-js-auth` or
|
|
174
|
+
`vanilla-js-custom` at `designer_init_project`. Use `noAuth: true` (`--no-auth`)
|
|
175
|
+
to get the plugin's UI without its auth.
|
|
176
|
+
- **Custom branding / new plugin** → `designer_scaffold_plugin`.
|
|
177
|
+
- **Bolt-on auth WITHOUT RBAC** → not a plugin; see auth.md § Choosing the mechanism.
|
|
178
|
+
→ references/udf-catalog.md § Plugins
|
|
@@ -26,7 +26,7 @@ before `codegen_create_endpoint` — a processor payload through
|
|
|
26
26
|
`dateTimeFields`, `deleteReferences`, `softDelete`, and (for a table with an
|
|
27
27
|
`is_active` column) `defaultScope`. Edit that file; do not write those keys by
|
|
28
28
|
hand. Later `codegen_generate_payload` / `codegen_sync_payload` runs keep the
|
|
29
|
-
customisations made to those keys (see
|
|
29
|
+
customisations made to those keys (see backend-pipeline.md § RDF payload decisions).
|
|
30
30
|
|
|
31
31
|
---
|
|
32
32
|
|