@stonecrop/schema 0.13.12 → 0.14.0
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/README.md +38 -92
- package/dist/cli.js +6 -6
- package/dist/index-BzhZpuzA.js +556 -0
- package/dist/index-BzhZpuzA.js.map +1 -0
- package/dist/index-C1oC7Xwz.js +552 -0
- package/dist/index-C1oC7Xwz.js.map +1 -0
- package/dist/index-C8F23K8c.js +555 -0
- package/dist/index-C8F23K8c.js.map +1 -0
- package/dist/index-CD5wQNH-.js +516 -0
- package/dist/index-CD5wQNH-.js.map +1 -0
- package/dist/index-CMeexvC6.js +459 -0
- package/dist/index-CMeexvC6.js.map +1 -0
- package/dist/index-CX662Zb2.js +551 -0
- package/dist/index-CX662Zb2.js.map +1 -0
- package/dist/index-Cq_yfoqI.js +553 -0
- package/dist/index-Cq_yfoqI.js.map +1 -0
- package/dist/index-D68mWfHm.js +510 -0
- package/dist/index-D68mWfHm.js.map +1 -0
- package/dist/index-D8R5ppAg.js +529 -0
- package/dist/index-D8R5ppAg.js.map +1 -0
- package/dist/index-DCjAX4l-.js +516 -0
- package/dist/index-DCjAX4l-.js.map +1 -0
- package/dist/index-DE0b6fs6.js +515 -0
- package/dist/index-DE0b6fs6.js.map +1 -0
- package/dist/index-DIY_FLR2.js +516 -0
- package/dist/index-DIY_FLR2.js.map +1 -0
- package/dist/index-DT-NJBvW.js +510 -0
- package/dist/index-DT-NJBvW.js.map +1 -0
- package/dist/index-DYy5E1cU.js +508 -0
- package/dist/index-DYy5E1cU.js.map +1 -0
- package/dist/index-Dk593rMz.js +512 -0
- package/dist/index-Dk593rMz.js.map +1 -0
- package/dist/index-DpCFgELB.js +514 -0
- package/dist/index-DpCFgELB.js.map +1 -0
- package/dist/index-DyK5-0kh.js +514 -0
- package/dist/index-DyK5-0kh.js.map +1 -0
- package/dist/index-Vd0_gZjr.js +451 -0
- package/dist/index-Vd0_gZjr.js.map +1 -0
- package/dist/index-YP0m26Y5.js +555 -0
- package/dist/index-YP0m26Y5.js.map +1 -0
- package/dist/index-_3q9Bzka.js +512 -0
- package/dist/index-_3q9Bzka.js.map +1 -0
- package/dist/index-glU_Uw7z.js +543 -0
- package/dist/index-glU_Uw7z.js.map +1 -0
- package/dist/index-i0Uz8G2K.js +554 -0
- package/dist/index-i0Uz8G2K.js.map +1 -0
- package/dist/index-rozX0Luh.js +458 -0
- package/dist/index-rozX0Luh.js.map +1 -0
- package/dist/index.js +70 -31
- package/dist/index.js.map +1 -1
- package/dist/schema.d.ts +324 -88
- package/dist/src/column-schema.d.ts +17 -8
- package/dist/src/column-schema.d.ts.map +1 -1
- package/dist/src/component-meta.d.ts +96 -0
- package/dist/src/component-meta.d.ts.map +1 -0
- package/dist/src/component-meta.js +85 -0
- package/dist/src/converter/heuristics.d.ts.map +1 -1
- package/dist/src/converter/heuristics.js +6 -13
- package/dist/src/converter/index.d.ts +1 -1
- package/dist/src/converter/index.d.ts.map +1 -1
- package/dist/src/converter/index.js +9 -8
- package/dist/src/converter/scalars.d.ts +1 -1
- package/dist/src/converter/scalars.d.ts.map +1 -1
- package/dist/src/converter/scalars.js +23 -26
- package/dist/src/converter/types.d.ts +17 -8
- package/dist/src/converter/types.d.ts.map +1 -1
- package/dist/src/doctype.d.ts +127 -9
- package/dist/src/doctype.d.ts.map +1 -1
- package/dist/src/doctype.js +80 -7
- package/dist/src/field.d.ts +69 -12
- package/dist/src/field.d.ts.map +1 -1
- package/dist/src/field.js +45 -7
- package/dist/src/fieldtype.d.ts.map +1 -1
- package/dist/src/fieldtype.js +14 -13
- package/dist/src/index.d.ts +4 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Schema definitions and validation for Stonecrop doctypes, fields, and workflows.
|
|
|
10
10
|
- **Doctype definitions** (`DoctypeMeta`) - Complete document type schemas
|
|
11
11
|
- **Workflows** (`WorkflowMeta`) - State machines and action definitions
|
|
12
12
|
- **Validation** - Runtime schema validation with detailed error reporting
|
|
13
|
-
- **
|
|
13
|
+
- **GraphQL Conversion** - GraphQL schema (SDL, introspection, or live endpoint) to Stonecrop doctype generation
|
|
14
14
|
|
|
15
15
|
This package is schema-only and has no UI dependencies - it can be used in both frontend and backend contexts.
|
|
16
16
|
|
|
@@ -26,36 +26,40 @@ pnpm add @stonecrop/schema
|
|
|
26
26
|
|
|
27
27
|
## Core Concepts
|
|
28
28
|
|
|
29
|
-
###
|
|
29
|
+
### Fields and Components
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
Every field declares a `component` — the Vue widget that renders it. `component` is the primary axis: it decides how the field looks and, for links, how it behaves. `componentCategory()` maps a component to a semantic category that drives table formatting, filtering, and record-initialization defaults; `CANONICAL_COMPONENTS` is the set the framework ships:
|
|
32
32
|
|
|
33
33
|
```typescript
|
|
34
|
-
import {
|
|
35
|
-
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
34
|
+
import { CANONICAL_COMPONENTS, componentCategory } from '@stonecrop/schema'
|
|
35
|
+
|
|
36
|
+
// Components by value category (componentCategory):
|
|
37
|
+
// text: ATextInput, ATextarea, ADuration
|
|
38
|
+
// number: ANumericInput
|
|
39
|
+
// boolean: ACheckbox
|
|
40
|
+
// date: ADate, ADatePicker, ADateSelection, ADateRange
|
|
41
|
+
// datetime: ADateTime
|
|
42
|
+
// code: ACodeEditor (pair with `language: 'json' | 'javascript' | …`)
|
|
43
|
+
// select: ADropdown
|
|
44
|
+
// link: AFormLink (inline picker)
|
|
45
|
+
// attach: AFileAttach
|
|
46
|
+
// Link containers (no value category): AForm (1:1 expand), ATable (1:many expand)
|
|
47
|
+
|
|
48
|
+
componentCategory('ANumericInput') // 'number'
|
|
45
49
|
```
|
|
46
50
|
|
|
47
51
|
### Field Definitions
|
|
48
52
|
|
|
49
53
|
`DoctypeField` is a discriminated union of three structural variants:
|
|
50
54
|
|
|
51
|
-
- **`ValueField`** — a scalar or link field; has `
|
|
55
|
+
- **`ValueField`** — a scalar or link field; has `component`
|
|
52
56
|
- **`FieldsetField`** — a layout container grouping other fields; has `schema: DoctypeField[]`
|
|
53
57
|
- **`TableField`** — an inline table with column definitions; has `columns: ColumnSchema[]`
|
|
54
58
|
|
|
55
59
|
**In authored JSON** (doctype files), `kind` is inferred from structure automatically — you only write the properties that define what the field is:
|
|
56
60
|
|
|
57
61
|
```json
|
|
58
|
-
{ "fieldname": "customer_name", "
|
|
62
|
+
{ "fieldname": "customer_name", "component": "ATextInput", "label": "Customer Name", "required": true }
|
|
59
63
|
{ "fieldname": "details", "label": "Details", "schema": [...] }
|
|
60
64
|
{ "fieldname": "line_items", "label": "Line Items", "columns": [...] }
|
|
61
65
|
```
|
|
@@ -68,7 +72,7 @@ import type { ValueField, FieldsetField, DoctypeField } from '@stonecrop/schema'
|
|
|
68
72
|
const field: ValueField = {
|
|
69
73
|
kind: 'field',
|
|
70
74
|
fieldname: 'customer_name',
|
|
71
|
-
|
|
75
|
+
component: 'ATextInput',
|
|
72
76
|
label: 'Customer Name',
|
|
73
77
|
required: true,
|
|
74
78
|
readOnly: false,
|
|
@@ -80,15 +84,15 @@ const field: ValueField = {
|
|
|
80
84
|
const linkField: ValueField = {
|
|
81
85
|
kind: 'field',
|
|
82
86
|
fieldname: 'customer',
|
|
83
|
-
|
|
87
|
+
component: 'AFormLink',
|
|
84
88
|
label: 'Customer',
|
|
85
|
-
|
|
89
|
+
doctype: 'customer', // Target doctype slug
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
const selectField: ValueField = {
|
|
89
93
|
kind: 'field',
|
|
90
94
|
fieldname: 'status',
|
|
91
|
-
|
|
95
|
+
component: 'ADropdown',
|
|
92
96
|
label: 'Status',
|
|
93
97
|
options: ['Draft', 'Submitted', 'Cancelled'], // Choices array
|
|
94
98
|
}
|
|
@@ -96,7 +100,7 @@ const selectField: ValueField = {
|
|
|
96
100
|
const decimalField: ValueField = {
|
|
97
101
|
kind: 'field',
|
|
98
102
|
fieldname: 'price',
|
|
99
|
-
|
|
103
|
+
component: 'ANumericInput',
|
|
100
104
|
label: 'Price',
|
|
101
105
|
options: { precision: 10, scale: 2 }, // Config object
|
|
102
106
|
}
|
|
@@ -116,17 +120,17 @@ const doctype: DoctypeMeta = {
|
|
|
116
120
|
{
|
|
117
121
|
kind: 'field',
|
|
118
122
|
fieldname: 'customer',
|
|
119
|
-
|
|
123
|
+
component: 'AFormLink',
|
|
120
124
|
label: 'Customer',
|
|
121
|
-
|
|
125
|
+
doctype: 'customer',
|
|
122
126
|
required: true,
|
|
123
127
|
},
|
|
124
128
|
{
|
|
125
129
|
kind: 'field',
|
|
126
130
|
fieldname: 'items',
|
|
127
|
-
|
|
131
|
+
component: 'ATable',
|
|
128
132
|
label: 'Items',
|
|
129
|
-
|
|
133
|
+
doctype: 'sales-order-item',
|
|
130
134
|
},
|
|
131
135
|
],
|
|
132
136
|
links: {
|
|
@@ -142,7 +146,7 @@ const doctype: DoctypeMeta = {
|
|
|
142
146
|
actions: {
|
|
143
147
|
submit: {
|
|
144
148
|
label: 'Submit',
|
|
145
|
-
|
|
149
|
+
nextState: 'Submitted',
|
|
146
150
|
requiredFields: ['customer', 'items'],
|
|
147
151
|
allowedStates: ['Draft'],
|
|
148
152
|
},
|
|
@@ -192,16 +196,14 @@ const workflow: WorkflowMeta = {
|
|
|
192
196
|
actions: {
|
|
193
197
|
submit: {
|
|
194
198
|
label: 'Submit for Approval',
|
|
195
|
-
|
|
199
|
+
nextState: 'Pending Approval',
|
|
196
200
|
requiredFields: ['title', 'description'],
|
|
197
201
|
allowedStates: ['Draft'],
|
|
198
|
-
confirm: true,
|
|
199
202
|
},
|
|
200
203
|
approve: {
|
|
201
204
|
label: 'Approve',
|
|
202
|
-
|
|
205
|
+
nextState: 'Approved',
|
|
203
206
|
allowedStates: ['Pending Approval'],
|
|
204
|
-
args: { notifyUser: true },
|
|
205
207
|
},
|
|
206
208
|
},
|
|
207
209
|
}
|
|
@@ -247,7 +249,7 @@ import { validateField, validateDoctype } from '@stonecrop/schema'
|
|
|
247
249
|
// Validate a field definition
|
|
248
250
|
const fieldResult = validateField({
|
|
249
251
|
fieldname: 'email',
|
|
250
|
-
|
|
252
|
+
component: 'ATextInput',
|
|
251
253
|
label: 'Email',
|
|
252
254
|
})
|
|
253
255
|
|
|
@@ -343,8 +345,8 @@ a JSON mapping file:
|
|
|
343
345
|
|
|
344
346
|
```json
|
|
345
347
|
{
|
|
346
|
-
"BigFloat": { "component": "
|
|
347
|
-
"Datetime": { "component": "
|
|
348
|
+
"BigFloat": { "component": "ANumericInput" },
|
|
349
|
+
"Datetime": { "component": "ADateTime" }
|
|
348
350
|
}
|
|
349
351
|
```
|
|
350
352
|
|
|
@@ -360,7 +362,7 @@ Override the generated field definition for specific types and fields:
|
|
|
360
362
|
```json
|
|
361
363
|
{
|
|
362
364
|
"SalesOrder": {
|
|
363
|
-
"totalAmount": { "
|
|
365
|
+
"totalAmount": { "component": "ANumericInput" }
|
|
364
366
|
}
|
|
365
367
|
}
|
|
366
368
|
```
|
|
@@ -370,49 +372,7 @@ stonecrop-schema generate -e http://localhost:3000/graphql -o ./app/doctypes \
|
|
|
370
372
|
--overrides overrides.json
|
|
371
373
|
```
|
|
372
374
|
|
|
373
|
-
##
|
|
374
|
-
|
|
375
|
-
Convert PostgreSQL DDL statements to Stonecrop doctype schemas:
|
|
376
|
-
|
|
377
|
-
```typescript
|
|
378
|
-
import { convertSchema, type ConversionOptions } from '@stonecrop/schema'
|
|
379
|
-
|
|
380
|
-
const ddl = `
|
|
381
|
-
CREATE TABLE customers (
|
|
382
|
-
id SERIAL PRIMARY KEY,
|
|
383
|
-
name VARCHAR(255) NOT NULL,
|
|
384
|
-
email VARCHAR(255) UNIQUE,
|
|
385
|
-
created_at TIMESTAMP DEFAULT NOW()
|
|
386
|
-
);
|
|
387
|
-
|
|
388
|
-
CREATE TABLE sales_orders (
|
|
389
|
-
id SERIAL PRIMARY KEY,
|
|
390
|
-
customer_id INTEGER REFERENCES customers(id),
|
|
391
|
-
status VARCHAR(20) DEFAULT 'Draft',
|
|
392
|
-
total_amount DECIMAL(10, 2)
|
|
393
|
-
);
|
|
394
|
-
`
|
|
395
|
-
|
|
396
|
-
const options: ConversionOptions = {
|
|
397
|
-
inheritanceMode: 'flatten', // or 'reference'
|
|
398
|
-
useCamelCase: true, // Convert snake_case to camelCase
|
|
399
|
-
includeUnmappedMeta: false, // Include unmapped metadata
|
|
400
|
-
schema: 'public', // Filter by schema
|
|
401
|
-
exclude: ['migrations'], // Exclude tables
|
|
402
|
-
typeOverrides: {
|
|
403
|
-
status: { fieldtype: 'Select', options: ['Draft', 'Submitted'] },
|
|
404
|
-
},
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
const doctypes = convertSchema(ddl, options)
|
|
408
|
-
|
|
409
|
-
doctypes.forEach(doctype => {
|
|
410
|
-
console.log(`Doctype: ${doctype.name}`)
|
|
411
|
-
console.log(`Fields: ${doctype.fields.length}`)
|
|
412
|
-
})
|
|
413
|
-
```
|
|
414
|
-
|
|
415
|
-
### Naming Utilities
|
|
375
|
+
## Naming Utilities
|
|
416
376
|
|
|
417
377
|
Convert between different naming conventions:
|
|
418
378
|
|
|
@@ -427,20 +387,6 @@ toPascalCase('customer_name') // 'CustomerName'
|
|
|
427
387
|
toSlug('Customer Name') // 'customer-name'
|
|
428
388
|
```
|
|
429
389
|
|
|
430
|
-
## API
|
|
431
|
-
|
|
432
|
-
### Field Type Mapping
|
|
433
|
-
|
|
434
|
-
```typescript
|
|
435
|
-
import { TYPE_MAP, getDefaultComponent } from '@stonecrop/schema'
|
|
436
|
-
|
|
437
|
-
// Get default component for a field type
|
|
438
|
-
const component = getDefaultComponent('Data') // 'ATextInput'
|
|
439
|
-
|
|
440
|
-
// Access full type map
|
|
441
|
-
console.log(TYPE_MAP['Link']) // { component: 'ALink', fieldtype: 'Link' }
|
|
442
|
-
```
|
|
443
|
-
|
|
444
390
|
## Usage in Stonecrop
|
|
445
391
|
|
|
446
392
|
This package provides the type system used throughout Stonecrop:
|
|
@@ -481,7 +427,7 @@ import type { ValueField, DoctypeField, DoctypeMeta } from '@stonecrop/schema'
|
|
|
481
427
|
const field: ValueField = {
|
|
482
428
|
kind: 'field',
|
|
483
429
|
fieldname: 'title',
|
|
484
|
-
|
|
430
|
+
component: 'ATextInput',
|
|
485
431
|
// TypeScript will catch typos and missing required fields
|
|
486
432
|
}
|
|
487
433
|
|
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import { readFileSync as u, existsSync as S, mkdirSync as w, writeFileSync as x
|
|
|
3
3
|
import { resolve as c, join as O } from "node:path";
|
|
4
4
|
import { parseArgs as $ } from "node:util";
|
|
5
5
|
import { getIntrospectionQuery as N } from "graphql";
|
|
6
|
-
import {
|
|
6
|
+
import { i as P, v as j } from "./index-rozX0Luh.js";
|
|
7
7
|
async function C(e, m) {
|
|
8
8
|
const t = await fetch(e, {
|
|
9
9
|
method: "POST",
|
|
@@ -69,15 +69,15 @@ async function E() {
|
|
|
69
69
|
const s = `${o.slug}.json`, d = O(r, s), v = JSON.stringify(o, null, " ");
|
|
70
70
|
x(d, v + `
|
|
71
71
|
`, "utf-8");
|
|
72
|
-
const
|
|
73
|
-
if (
|
|
74
|
-
const n = o.fields.filter((
|
|
72
|
+
const g = j(o);
|
|
73
|
+
if (g.success) {
|
|
74
|
+
const n = o.fields.filter((y) => y._unmapped);
|
|
75
75
|
n.length > 0 && (h++, console.warn(
|
|
76
|
-
` WARN: ${s} has ${n.length} unmapped field(s): ${n.map((
|
|
76
|
+
` WARN: ${s} has ${n.length} unmapped field(s): ${n.map((y) => y.fieldname).join(", ")}`
|
|
77
77
|
));
|
|
78
78
|
} else {
|
|
79
79
|
p++, console.error(` ERROR: ${s} failed validation:`);
|
|
80
|
-
for (const n of
|
|
80
|
+
for (const n of g.errors)
|
|
81
81
|
console.error(` ${n.path.join(".")}: ${n.message}`);
|
|
82
82
|
}
|
|
83
83
|
}
|