@stonecrop/stonecrop 0.10.12 → 0.10.14
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/dist/composables/stonecrop.js +19 -144
- package/dist/index.js +9 -6
- package/dist/plugins/index.js +4 -1
- package/dist/schema-validator.js +1 -13
- package/dist/src/composables/stonecrop.d.ts +2 -74
- package/dist/src/composables/stonecrop.d.ts.map +1 -1
- package/dist/src/doctype.d.ts +1 -27
- package/dist/src/doctype.d.ts.map +1 -1
- package/dist/src/index.d.ts +6 -9
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/plugins/index.d.ts.map +1 -1
- package/dist/src/schema-validator.d.ts +1 -62
- package/dist/src/schema-validator.d.ts.map +1 -1
- package/dist/src/stonecrop.d.ts +38 -17
- package/dist/src/stonecrop.d.ts.map +1 -1
- package/dist/src/stores/operation-log.d.ts +1 -1
- package/dist/src/types/composable.d.ts +230 -0
- package/dist/src/types/composable.d.ts.map +1 -0
- package/dist/src/types/doctype.d.ts +57 -0
- package/dist/src/types/doctype.d.ts.map +1 -0
- package/dist/src/types/index.d.ts +6 -67
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/plugin.d.ts +37 -0
- package/dist/src/types/plugin.d.ts.map +1 -0
- package/dist/src/types/schema-validator.d.ts +64 -0
- package/dist/src/types/schema-validator.d.ts.map +1 -0
- package/dist/src/types/stonecrop.d.ts +17 -0
- package/dist/src/types/stonecrop.d.ts.map +1 -0
- package/dist/stonecrop.d.ts +192 -3
- package/dist/stonecrop.js +1509 -1517
- package/dist/stonecrop.js.map +1 -1
- package/dist/types/composable.js +0 -0
- package/dist/types/doctype.js +0 -0
- package/dist/types/index.js +7 -2
- package/dist/types/plugin.js +0 -0
- package/dist/types/schema-validator.js +13 -0
- package/dist/types/stonecrop.js +0 -0
- package/package.json +5 -5
- package/src/composables/stonecrop.ts +26 -266
- package/src/doctype.ts +2 -29
- package/src/index.ts +12 -19
- package/src/plugins/index.ts +4 -1
- package/src/schema-validator.ts +3 -66
- package/src/stonecrop.ts +147 -16
- package/src/types/composable.ts +245 -0
- package/src/types/doctype.ts +60 -0
- package/src/types/index.ts +7 -74
- package/src/types/plugin.ts +38 -0
- package/src/types/schema-validator.ts +67 -0
- package/src/types/stonecrop.ts +17 -0
|
File without changes
|
|
File without changes
|
package/dist/types/index.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
// Re-export
|
|
1
|
+
// Re-export all type files
|
|
2
|
+
export * from './composable';
|
|
3
|
+
export * from './doctype';
|
|
2
4
|
export * from './field-triggers';
|
|
3
|
-
export * from './registry';
|
|
4
5
|
export * from './operation-log';
|
|
6
|
+
export * from './plugin';
|
|
7
|
+
export * from './registry';
|
|
8
|
+
export * from './schema-validator';
|
|
9
|
+
export * from './stonecrop';
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation severity levels
|
|
3
|
+
* @public
|
|
4
|
+
*/
|
|
5
|
+
export var ValidationSeverity;
|
|
6
|
+
(function (ValidationSeverity) {
|
|
7
|
+
/** Blocking error that prevents save */
|
|
8
|
+
ValidationSeverity["ERROR"] = "error";
|
|
9
|
+
/** Advisory warning that allows save */
|
|
10
|
+
ValidationSeverity["WARNING"] = "warning";
|
|
11
|
+
/** Informational message */
|
|
12
|
+
ValidationSeverity["INFO"] = "info";
|
|
13
|
+
})(ValidationSeverity || (ValidationSeverity = {}));
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stonecrop/stonecrop",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.14",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"pinia-shared-state": "^1.0.1",
|
|
35
35
|
"pinia-xstate": "^3.0.0",
|
|
36
36
|
"xstate": "^5.25.0",
|
|
37
|
-
"@stonecrop/schema": "0.10.
|
|
37
|
+
"@stonecrop/schema": "0.10.14"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"pinia": "^3.0.4",
|
|
@@ -60,9 +60,9 @@
|
|
|
60
60
|
"vue-router": "^5.0.2",
|
|
61
61
|
"vite": "^7.3.1",
|
|
62
62
|
"vitest": "^4.0.18",
|
|
63
|
-
"@stonecrop/
|
|
64
|
-
"
|
|
65
|
-
"stonecrop
|
|
63
|
+
"@stonecrop/atable": "0.10.14",
|
|
64
|
+
"stonecrop-rig": "0.7.0",
|
|
65
|
+
"@stonecrop/aform": "0.10.14"
|
|
66
66
|
},
|
|
67
67
|
"description": "Schema-driven framework with XState workflows and HST state management",
|
|
68
68
|
"publishConfig": {
|
|
@@ -1,100 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type DoctypeManySchema,
|
|
3
|
-
type DoctypeOneSchema,
|
|
4
|
-
type DoctypeSchema,
|
|
5
|
-
type SchemaTypes,
|
|
6
|
-
isDoctypeMany,
|
|
7
|
-
} from '@stonecrop/aform'
|
|
1
|
+
import { type SchemaTypes } from '@stonecrop/aform'
|
|
8
2
|
import { storeToRefs } from 'pinia'
|
|
9
|
-
import { inject, onMounted, Ref, ref, watch, provide, computed
|
|
3
|
+
import { inject, onMounted, Ref, ref, watch, provide, computed } from 'vue'
|
|
10
4
|
|
|
5
|
+
import Doctype from '../doctype'
|
|
11
6
|
import Registry from '../registry'
|
|
12
7
|
import { Stonecrop } from '../stonecrop'
|
|
13
|
-
import Doctype from '../doctype'
|
|
14
8
|
import type { HSTNode } from '../stores/hst'
|
|
9
|
+
import type { BaseStonecropReturn, HSTStonecropReturn, HSTChangeData, OperationLogAPI } from '../types/composable'
|
|
10
|
+
import type { HSTOperation, OperationLogConfig } from '../types/operation-log'
|
|
15
11
|
import type { RouteContext } from '../types/registry'
|
|
16
|
-
import type { HSTOperation, OperationLogConfig, OperationLogSnapshot } from '../types/operation-log'
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Operation Log API - nested object containing all operation log functionality
|
|
20
|
-
* @public
|
|
21
|
-
*/
|
|
22
|
-
export type OperationLogAPI = {
|
|
23
|
-
operations: Ref<HSTOperation[]>
|
|
24
|
-
currentIndex: Ref<number>
|
|
25
|
-
undoRedoState: ComputedRef<{
|
|
26
|
-
canUndo: boolean
|
|
27
|
-
canRedo: boolean
|
|
28
|
-
undoCount: number
|
|
29
|
-
redoCount: number
|
|
30
|
-
currentIndex: number
|
|
31
|
-
}>
|
|
32
|
-
canUndo: ComputedRef<boolean>
|
|
33
|
-
canRedo: ComputedRef<boolean>
|
|
34
|
-
undoCount: ComputedRef<number>
|
|
35
|
-
redoCount: ComputedRef<number>
|
|
36
|
-
undo: (hstStore: HSTNode) => boolean
|
|
37
|
-
redo: (hstStore: HSTNode) => boolean
|
|
38
|
-
startBatch: () => void
|
|
39
|
-
commitBatch: (description?: string) => string | null
|
|
40
|
-
cancelBatch: () => void
|
|
41
|
-
clear: () => void
|
|
42
|
-
getOperationsFor: (doctype: string, recordId?: string) => HSTOperation[]
|
|
43
|
-
getSnapshot: () => OperationLogSnapshot
|
|
44
|
-
markIrreversible: (operationId: string, reason: string) => void
|
|
45
|
-
logAction: (
|
|
46
|
-
doctype: string,
|
|
47
|
-
actionName: string,
|
|
48
|
-
recordIds?: string[],
|
|
49
|
-
result?: 'success' | 'failure' | 'pending',
|
|
50
|
-
error?: string
|
|
51
|
-
) => string
|
|
52
|
-
configure: (options: Partial<OperationLogConfig>) => void
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Base Stonecrop composable return type - includes operation log functionality
|
|
57
|
-
* @public
|
|
58
|
-
*/
|
|
59
|
-
export type BaseStonecropReturn = {
|
|
60
|
-
stonecrop: Ref<Stonecrop | undefined>
|
|
61
|
-
operationLog: OperationLogAPI
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* HST-enabled Stonecrop composable return type
|
|
66
|
-
* @public
|
|
67
|
-
*/
|
|
68
|
-
export type HSTStonecropReturn = BaseStonecropReturn & {
|
|
69
|
-
provideHSTPath: (fieldname: string, recordId?: string) => string
|
|
70
|
-
handleHSTChange: (changeData: HSTChangeData) => void
|
|
71
|
-
hstStore: Ref<HSTNode | undefined>
|
|
72
|
-
formData: Ref<Record<string, any>>
|
|
73
|
-
resolvedSchema: Ref<SchemaTypes[]>
|
|
74
|
-
loadNestedData: (parentPath: string, childDoctype: Doctype, recordId?: string) => Record<string, any>
|
|
75
|
-
collectRecordPayload: (doctype: Doctype, recordId: string) => Record<string, any>
|
|
76
|
-
createNestedContext: (
|
|
77
|
-
basePath: string,
|
|
78
|
-
childDoctype: Doctype
|
|
79
|
-
) => {
|
|
80
|
-
provideHSTPath: (fieldname: string) => string
|
|
81
|
-
handleHSTChange: (changeData: HSTChangeData) => void
|
|
82
|
-
}
|
|
83
|
-
isLoading: Ref<boolean>
|
|
84
|
-
error: Ref<Error | null>
|
|
85
|
-
resolvedDoctype: Ref<Doctype | undefined>
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* HST Change data structure
|
|
90
|
-
* @public
|
|
91
|
-
*/
|
|
92
|
-
export type HSTChangeData = {
|
|
93
|
-
path: string
|
|
94
|
-
value: any
|
|
95
|
-
fieldname: string
|
|
96
|
-
recordId?: string
|
|
97
|
-
}
|
|
98
12
|
|
|
99
13
|
/**
|
|
100
14
|
* Unified Stonecrop composable - handles both general operations and HST reactive integration
|
|
@@ -128,7 +42,7 @@ export function useStonecrop(options?: {
|
|
|
128
42
|
|
|
129
43
|
const registry = options.registry || inject<Registry>('$registry')
|
|
130
44
|
const providedStonecrop = inject<Stonecrop>('$stonecrop')
|
|
131
|
-
const stonecrop = ref<Stonecrop>()
|
|
45
|
+
const stonecrop = ref<Stonecrop | undefined>()
|
|
132
46
|
const hstStore = ref<HSTNode>()
|
|
133
47
|
const formData = ref<Record<string, any>>({})
|
|
134
48
|
|
|
@@ -144,6 +58,13 @@ export function useStonecrop(options?: {
|
|
|
144
58
|
const error = ref<Error | null>(null)
|
|
145
59
|
const resolvedDoctype = ref<Doctype | undefined>()
|
|
146
60
|
|
|
61
|
+
// Initialize stonecrop instance synchronously using singleton pattern
|
|
62
|
+
// Use injected instance if available, otherwise fall back to the singleton root
|
|
63
|
+
const stonecropInstance = providedStonecrop || Stonecrop._root
|
|
64
|
+
if (stonecropInstance) {
|
|
65
|
+
stonecrop.value = stonecropInstance
|
|
66
|
+
}
|
|
67
|
+
|
|
147
68
|
// If doctype is a Doctype instance (not string), set resolved immediately
|
|
148
69
|
if (options?.doctype && typeof options.doctype !== 'string') {
|
|
149
70
|
resolvedDoctype.value = options.doctype
|
|
@@ -228,12 +149,10 @@ export function useStonecrop(options?: {
|
|
|
228
149
|
|
|
229
150
|
// Initialize Stonecrop instance
|
|
230
151
|
onMounted(async () => {
|
|
231
|
-
if (!registry) {
|
|
152
|
+
if (!registry || !stonecrop.value) {
|
|
232
153
|
return
|
|
233
154
|
}
|
|
234
155
|
|
|
235
|
-
stonecrop.value = providedStonecrop || new Stonecrop(registry)
|
|
236
|
-
|
|
237
156
|
// Set up reactive refs from operation log store - only if Pinia is available
|
|
238
157
|
try {
|
|
239
158
|
const opLogStore = stonecrop.value.getOperationLogStore()
|
|
@@ -308,11 +227,11 @@ export function useStonecrop(options?: {
|
|
|
308
227
|
formData.value = loadedRecord.get('') || {}
|
|
309
228
|
}
|
|
310
229
|
} catch {
|
|
311
|
-
formData.value =
|
|
230
|
+
formData.value = registry.initializeRecord(resolvedSchema.value)
|
|
312
231
|
}
|
|
313
232
|
}
|
|
314
233
|
} else {
|
|
315
|
-
formData.value =
|
|
234
|
+
formData.value = registry.initializeRecord(resolvedSchema.value)
|
|
316
235
|
}
|
|
317
236
|
|
|
318
237
|
if (hstStore.value) {
|
|
@@ -395,11 +314,11 @@ export function useStonecrop(options?: {
|
|
|
395
314
|
formData.value = loadedRecord.get('') || {}
|
|
396
315
|
}
|
|
397
316
|
} catch {
|
|
398
|
-
formData.value =
|
|
317
|
+
formData.value = registry.initializeRecord(resolvedSchema.value)
|
|
399
318
|
}
|
|
400
319
|
}
|
|
401
320
|
} else {
|
|
402
|
-
formData.value =
|
|
321
|
+
formData.value = registry.initializeRecord(resolvedSchema.value)
|
|
403
322
|
}
|
|
404
323
|
|
|
405
324
|
if (hstStore.value) {
|
|
@@ -475,96 +394,31 @@ export function useStonecrop(options?: {
|
|
|
475
394
|
|
|
476
395
|
/**
|
|
477
396
|
* Load nested doctype data from API or initialize empty structure
|
|
397
|
+
* Delegates to Stonecrop.loadNestedData method
|
|
478
398
|
* @param parentPath - The parent path (e.g., "customer.123.address")
|
|
479
399
|
* @param childDoctype - The child doctype metadata
|
|
480
400
|
* @param recordId - Optional record ID to load
|
|
481
|
-
* @returns
|
|
401
|
+
* @returns The loaded or initialized data
|
|
482
402
|
*/
|
|
483
403
|
const loadNestedData = (parentPath: string, childDoctype: Doctype, recordId?: string): Record<string, any> => {
|
|
484
404
|
if (!stonecrop.value) {
|
|
485
|
-
|
|
405
|
+
throw new Error('Stonecrop instance not available')
|
|
486
406
|
}
|
|
487
|
-
|
|
488
|
-
// If recordId provided, try to load existing data
|
|
489
|
-
if (recordId) {
|
|
490
|
-
try {
|
|
491
|
-
// Check if data already exists in HST
|
|
492
|
-
const existingData = hstStore.value?.get(parentPath)
|
|
493
|
-
if (existingData && typeof existingData === 'object') {
|
|
494
|
-
return existingData as Record<string, any>
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
// TODO: Add API fetch logic here if needed
|
|
498
|
-
// For now, initialize new record
|
|
499
|
-
return initializeNewRecord(childDoctype)
|
|
500
|
-
} catch {
|
|
501
|
-
return initializeNewRecord(childDoctype)
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
// Initialize new record
|
|
506
|
-
return initializeNewRecord(childDoctype)
|
|
407
|
+
return stonecrop.value.loadNestedData(parentPath, childDoctype, recordId)
|
|
507
408
|
}
|
|
508
409
|
|
|
509
410
|
/**
|
|
510
411
|
* Collect a record payload with all nested doctype fields from HST
|
|
412
|
+
* Delegates to Stonecrop.collectRecordPayload method
|
|
511
413
|
* @param doctype - The doctype metadata
|
|
512
414
|
* @param recordId - The record ID to collect
|
|
513
415
|
* @returns The complete record payload ready for API submission
|
|
514
416
|
*/
|
|
515
417
|
const collectRecordPayload = (doctype: Doctype, recordId: string): Record<string, any> => {
|
|
516
|
-
if (!
|
|
517
|
-
throw new Error('
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
const recordPath = `${doctype.slug}.${recordId}`
|
|
521
|
-
const recordData = hstStore.value.get(recordPath) || {}
|
|
522
|
-
|
|
523
|
-
// Build the save payload using resolved schema
|
|
524
|
-
const payload: Record<string, any> = { ...recordData }
|
|
525
|
-
|
|
526
|
-
// Use resolveSchema to get the full resolved tree, then walk Doctype fields
|
|
527
|
-
const schemaArray = doctype.schema
|
|
528
|
-
? Array.isArray(doctype.schema)
|
|
529
|
-
? doctype.schema
|
|
530
|
-
: Array.from(doctype.schema)
|
|
531
|
-
: []
|
|
532
|
-
const resolved = registry ? registry.resolveSchema(schemaArray) : schemaArray
|
|
533
|
-
|
|
534
|
-
// 1:1 nested Doctype fields (cardinality: 'one' or undefined)
|
|
535
|
-
const doctypeFields = resolved.filter(
|
|
536
|
-
field =>
|
|
537
|
-
'fieldtype' in field &&
|
|
538
|
-
field.fieldtype === 'Doctype' &&
|
|
539
|
-
!isDoctypeMany(field as DoctypeSchema) &&
|
|
540
|
-
'schema' in field &&
|
|
541
|
-
Array.isArray(field.schema)
|
|
542
|
-
)
|
|
543
|
-
|
|
544
|
-
// Recursively collect nested data from HST using resolved schemas
|
|
545
|
-
for (const field of doctypeFields) {
|
|
546
|
-
const doctypeField = field as DoctypeOneSchema
|
|
547
|
-
const fieldPath = `${recordPath}.${doctypeField.fieldname}`
|
|
548
|
-
const nestedData = collectNestedData(doctypeField.schema!, fieldPath, hstStore.value)
|
|
549
|
-
payload[doctypeField.fieldname] = nestedData
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
// 1:many child tables (cardinality: 'many')
|
|
553
|
-
const doctypeManyFields = resolved.filter(
|
|
554
|
-
field => 'fieldtype' in field && field.fieldtype === 'Doctype' && isDoctypeMany(field as DoctypeSchema)
|
|
555
|
-
)
|
|
556
|
-
|
|
557
|
-
// Read array data from HST for cardinality: 'many' fields
|
|
558
|
-
for (const field of doctypeManyFields) {
|
|
559
|
-
const doctypeField = field as DoctypeManySchema
|
|
560
|
-
const fieldPath = `${recordPath}.${doctypeField.fieldname}`
|
|
561
|
-
const arrayData = hstStore.value.get(fieldPath)
|
|
562
|
-
if (Array.isArray(arrayData)) {
|
|
563
|
-
payload[doctypeField.fieldname] = arrayData
|
|
564
|
-
}
|
|
418
|
+
if (!stonecrop.value) {
|
|
419
|
+
throw new Error('Stonecrop instance not available')
|
|
565
420
|
}
|
|
566
|
-
|
|
567
|
-
return payload
|
|
421
|
+
return stonecrop.value.collectRecordPayload(doctype, recordId)
|
|
568
422
|
}
|
|
569
423
|
|
|
570
424
|
/**
|
|
@@ -659,54 +513,6 @@ export function useStonecrop(options?: {
|
|
|
659
513
|
} as BaseStonecropReturn
|
|
660
514
|
}
|
|
661
515
|
|
|
662
|
-
/**
|
|
663
|
-
* Initialize new record structure based on doctype schema
|
|
664
|
-
*/
|
|
665
|
-
function initializeNewRecord(doctype: Doctype): Record<string, any> {
|
|
666
|
-
const initialData: Record<string, any> = {}
|
|
667
|
-
|
|
668
|
-
if (!doctype.schema) {
|
|
669
|
-
return initialData
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
doctype.schema.forEach(field => {
|
|
673
|
-
const fieldtype = 'fieldtype' in field ? field.fieldtype : 'Data'
|
|
674
|
-
|
|
675
|
-
switch (fieldtype) {
|
|
676
|
-
case 'Data':
|
|
677
|
-
case 'Text':
|
|
678
|
-
initialData[field.fieldname] = ''
|
|
679
|
-
break
|
|
680
|
-
case 'Check':
|
|
681
|
-
initialData[field.fieldname] = false
|
|
682
|
-
break
|
|
683
|
-
case 'Int':
|
|
684
|
-
case 'Float':
|
|
685
|
-
initialData[field.fieldname] = 0
|
|
686
|
-
break
|
|
687
|
-
case 'JSON':
|
|
688
|
-
initialData[field.fieldname] = {}
|
|
689
|
-
break
|
|
690
|
-
case 'Doctype': {
|
|
691
|
-
// Check cardinality to determine initial value
|
|
692
|
-
const cardinality = 'cardinality' in field ? field.cardinality : undefined
|
|
693
|
-
if (cardinality === 'many') {
|
|
694
|
-
// 1:many child table - initialize as empty array
|
|
695
|
-
initialData[field.fieldname] = []
|
|
696
|
-
} else {
|
|
697
|
-
// 1:1 nested form - initialize as empty object
|
|
698
|
-
initialData[field.fieldname] = {}
|
|
699
|
-
}
|
|
700
|
-
break
|
|
701
|
-
}
|
|
702
|
-
default:
|
|
703
|
-
initialData[field.fieldname] = null
|
|
704
|
-
}
|
|
705
|
-
})
|
|
706
|
-
|
|
707
|
-
return initialData
|
|
708
|
-
}
|
|
709
|
-
|
|
710
516
|
/**
|
|
711
517
|
* Setup deep reactivity between form data and HST store
|
|
712
518
|
*/
|
|
@@ -753,49 +559,3 @@ function updateNestedObject(obj: any, path: string[], value: any): void {
|
|
|
753
559
|
const finalKey = path[path.length - 1]
|
|
754
560
|
current[finalKey] = value
|
|
755
561
|
}
|
|
756
|
-
|
|
757
|
-
/**
|
|
758
|
-
* Recursively collect nested data from HST using pre-resolved schemas
|
|
759
|
-
* @param resolvedSchema - The already-resolved schema (with nested schemas embedded)
|
|
760
|
-
* @param basePath - The base path in HST (e.g., "customer.123.address")
|
|
761
|
-
* @param hstStore - The HST store instance
|
|
762
|
-
* @returns The collected data object
|
|
763
|
-
*/
|
|
764
|
-
function collectNestedData(resolvedSchema: SchemaTypes[], basePath: string, hstStore: HSTNode): Record<string, any> {
|
|
765
|
-
const data = hstStore.get(basePath) || {}
|
|
766
|
-
const payload: Record<string, any> = { ...data }
|
|
767
|
-
|
|
768
|
-
// Find Doctype fields that have resolved child schemas (1:1 only, not cardinality: 'many')
|
|
769
|
-
const doctypeFields = resolvedSchema.filter(
|
|
770
|
-
field =>
|
|
771
|
-
'fieldtype' in field &&
|
|
772
|
-
field.fieldtype === 'Doctype' &&
|
|
773
|
-
!isDoctypeMany(field as DoctypeSchema) &&
|
|
774
|
-
'schema' in field &&
|
|
775
|
-
Array.isArray(field.schema)
|
|
776
|
-
)
|
|
777
|
-
|
|
778
|
-
// Recursively collect nested data
|
|
779
|
-
for (const field of doctypeFields) {
|
|
780
|
-
const doctypeField = field as DoctypeOneSchema
|
|
781
|
-
const fieldPath = `${basePath}.${doctypeField.fieldname}`
|
|
782
|
-
const nestedData = collectNestedData(doctypeField.schema!, fieldPath, hstStore)
|
|
783
|
-
payload[doctypeField.fieldname] = nestedData
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
// Also collect array data for cardinality: 'many' fields
|
|
787
|
-
const doctypeManyFields = resolvedSchema.filter(
|
|
788
|
-
field => 'fieldtype' in field && field.fieldtype === 'Doctype' && isDoctypeMany(field as DoctypeSchema)
|
|
789
|
-
)
|
|
790
|
-
|
|
791
|
-
for (const field of doctypeManyFields) {
|
|
792
|
-
const doctypeField = field as DoctypeManySchema
|
|
793
|
-
const fieldPath = `${basePath}.${doctypeField.fieldname}`
|
|
794
|
-
const arrayData = hstStore.get(fieldPath)
|
|
795
|
-
if (Array.isArray(arrayData)) {
|
|
796
|
-
payload[doctypeField.fieldname] = arrayData
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
return payload
|
|
801
|
-
}
|
package/src/doctype.ts
CHANGED
|
@@ -5,32 +5,7 @@ import { Component } from 'vue'
|
|
|
5
5
|
import type { UnknownMachineConfig } from 'xstate'
|
|
6
6
|
|
|
7
7
|
import type { ImmutableDoctype } from './types'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Plain object representation of doctype configuration for serialization/API responses.
|
|
11
|
-
* Compatible with the DoctypeMeta type from \@stonecrop/schema.
|
|
12
|
-
* @public
|
|
13
|
-
*/
|
|
14
|
-
export type DoctypeConfig = {
|
|
15
|
-
/** Display name of the doctype */
|
|
16
|
-
name: string
|
|
17
|
-
/** URL-friendly slug (kebab-case) */
|
|
18
|
-
slug?: string
|
|
19
|
-
/** Database table name */
|
|
20
|
-
tableName?: string
|
|
21
|
-
/** Field definitions */
|
|
22
|
-
fields?: SchemaTypes[]
|
|
23
|
-
/** Workflow configuration (XState format or simple WorkflowMeta) */
|
|
24
|
-
workflow?: UnknownMachineConfig | WorkflowMeta
|
|
25
|
-
/** Actions and their field triggers */
|
|
26
|
-
actions?: Record<string, string[]>
|
|
27
|
-
/** Parent doctype for inheritance */
|
|
28
|
-
inherits?: string
|
|
29
|
-
/** Doctype to use for list views */
|
|
30
|
-
listDoctype?: string
|
|
31
|
-
/** Parent doctype for child tables */
|
|
32
|
-
parentDoctype?: string
|
|
33
|
-
}
|
|
8
|
+
import type { DoctypeConfig } from './types/doctype'
|
|
34
9
|
|
|
35
10
|
/**
|
|
36
11
|
* Doctype runtime class with Immutable.js collections for HST change tracking.
|
|
@@ -249,9 +224,7 @@ export default class Doctype {
|
|
|
249
224
|
*
|
|
250
225
|
* @public
|
|
251
226
|
*/
|
|
252
|
-
getActionMeta(
|
|
253
|
-
actionName: string
|
|
254
|
-
):
|
|
227
|
+
getActionMeta(actionName: string):
|
|
255
228
|
| {
|
|
256
229
|
label: string
|
|
257
230
|
handler: string
|
package/src/index.ts
CHANGED
|
@@ -3,8 +3,9 @@ export type * from '@stonecrop/atable/types'
|
|
|
3
3
|
|
|
4
4
|
import { useStonecrop } from './composables/stonecrop'
|
|
5
5
|
import { useOperationLog, useUndoRedoShortcuts, withBatch } from './composables/operation-log'
|
|
6
|
-
import Doctype
|
|
6
|
+
import Doctype from './doctype'
|
|
7
7
|
import {
|
|
8
|
+
FieldTriggerEngine,
|
|
8
9
|
getGlobalTriggerEngine,
|
|
9
10
|
markOperationIrreversible,
|
|
10
11
|
registerGlobalAction,
|
|
@@ -14,39 +15,29 @@ import {
|
|
|
14
15
|
} from './field-triggers'
|
|
15
16
|
import plugin from './plugins'
|
|
16
17
|
import Registry from './registry'
|
|
17
|
-
import {
|
|
18
|
+
import { Stonecrop, collectNestedData } from './stonecrop'
|
|
18
19
|
import { HST, createHST, type HSTNode } from './stores/hst'
|
|
19
20
|
import { useOperationLogStore } from './stores/operation-log'
|
|
20
|
-
// Export schema validator
|
|
21
21
|
import { SchemaValidator, createValidator, validateSchema } from './schema-validator'
|
|
22
|
+
import { ValidationSeverity } from './types/schema-validator'
|
|
23
|
+
|
|
24
|
+
// Export all types from ./types
|
|
22
25
|
export type * from './types'
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
export
|
|
26
|
-
FieldChangeContext,
|
|
27
|
-
TransitionChangeContext,
|
|
28
|
-
FieldTriggerExecutionResult,
|
|
29
|
-
ActionExecutionResult,
|
|
30
|
-
TransitionExecutionResult,
|
|
31
|
-
FieldActionFunction,
|
|
32
|
-
TransitionActionFunction,
|
|
33
|
-
} from './types/field-triggers'
|
|
34
|
-
// Export schema validator types
|
|
35
|
-
export type { ValidationIssue, ValidationResult, ValidatorOptions } from './schema-validator'
|
|
36
|
-
export { ValidationSeverity } from './schema-validator'
|
|
26
|
+
|
|
27
|
+
// Export enum as value (enums need runtime export, not just type)
|
|
28
|
+
export { ValidationSeverity }
|
|
37
29
|
|
|
38
30
|
export {
|
|
39
31
|
Doctype,
|
|
40
|
-
DoctypeConfig,
|
|
41
32
|
Registry,
|
|
42
33
|
Stonecrop,
|
|
43
|
-
StonecropOptions,
|
|
44
34
|
useStonecrop,
|
|
45
35
|
// HST exports for advanced usage
|
|
46
36
|
HST,
|
|
47
37
|
createHST,
|
|
48
38
|
HSTNode,
|
|
49
39
|
// Field trigger system exports
|
|
40
|
+
FieldTriggerEngine,
|
|
50
41
|
getGlobalTriggerEngine,
|
|
51
42
|
registerGlobalAction,
|
|
52
43
|
registerTransitionAction,
|
|
@@ -62,6 +53,8 @@ export {
|
|
|
62
53
|
useOperationLogStore,
|
|
63
54
|
useUndoRedoShortcuts,
|
|
64
55
|
withBatch,
|
|
56
|
+
// Utility functions
|
|
57
|
+
collectNestedData,
|
|
65
58
|
}
|
|
66
59
|
|
|
67
60
|
// Default export is the Vue plugin
|
package/src/plugins/index.ts
CHANGED
|
@@ -72,7 +72,10 @@ const plugin: Plugin = {
|
|
|
72
72
|
app.config.globalProperties.$registry = registry
|
|
73
73
|
|
|
74
74
|
// Create and provide a global Stonecrop instance
|
|
75
|
-
const stonecrop = new Stonecrop(registry
|
|
75
|
+
const stonecrop = new Stonecrop(registry)
|
|
76
|
+
if (options?.client) {
|
|
77
|
+
stonecrop.setClient(options.client)
|
|
78
|
+
}
|
|
76
79
|
app.provide('$stonecrop', stonecrop)
|
|
77
80
|
app.config.globalProperties.$stonecrop = stonecrop
|
|
78
81
|
|
package/src/schema-validator.ts
CHANGED
|
@@ -7,74 +7,11 @@
|
|
|
7
7
|
import type { SchemaTypes } from '@stonecrop/aform'
|
|
8
8
|
import type { List, Map as ImmutableMap } from 'immutable'
|
|
9
9
|
import type { AnyStateNodeConfig } from 'xstate'
|
|
10
|
+
|
|
10
11
|
import { getGlobalTriggerEngine } from './field-triggers'
|
|
11
12
|
import type Registry from './registry'
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* Validation severity levels
|
|
15
|
-
* @public
|
|
16
|
-
*/
|
|
17
|
-
export enum ValidationSeverity {
|
|
18
|
-
/** Blocking error that prevents save */
|
|
19
|
-
ERROR = 'error',
|
|
20
|
-
/** Advisory warning that allows save */
|
|
21
|
-
WARNING = 'warning',
|
|
22
|
-
/** Informational message */
|
|
23
|
-
INFO = 'info',
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Validation issue
|
|
28
|
-
* @public
|
|
29
|
-
*/
|
|
30
|
-
export interface ValidationIssue {
|
|
31
|
-
/** Severity level */
|
|
32
|
-
severity: ValidationSeverity
|
|
33
|
-
/** Validation rule that failed */
|
|
34
|
-
rule: string
|
|
35
|
-
/** Human-readable message */
|
|
36
|
-
message: string
|
|
37
|
-
/** Doctype name */
|
|
38
|
-
doctype?: string
|
|
39
|
-
/** Field name if applicable */
|
|
40
|
-
fieldname?: string
|
|
41
|
-
/** Additional context */
|
|
42
|
-
context?: Record<string, unknown>
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Validation result
|
|
47
|
-
* @public
|
|
48
|
-
*/
|
|
49
|
-
export interface ValidationResult {
|
|
50
|
-
/** Whether validation passed (no blocking errors) */
|
|
51
|
-
valid: boolean
|
|
52
|
-
/** List of validation issues */
|
|
53
|
-
issues: ValidationIssue[]
|
|
54
|
-
/** Count of errors */
|
|
55
|
-
errorCount: number
|
|
56
|
-
/** Count of warnings */
|
|
57
|
-
warningCount: number
|
|
58
|
-
/** Count of info messages */
|
|
59
|
-
infoCount: number
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Schema validator options
|
|
64
|
-
* @public
|
|
65
|
-
*/
|
|
66
|
-
export interface ValidatorOptions {
|
|
67
|
-
/** Registry instance for doctype lookups */
|
|
68
|
-
registry?: Registry
|
|
69
|
-
/** Whether to validate Link field targets */
|
|
70
|
-
validateLinkTargets?: boolean
|
|
71
|
-
/** Whether to validate workflow reachability */
|
|
72
|
-
validateWorkflows?: boolean
|
|
73
|
-
/** Whether to validate action registration */
|
|
74
|
-
validateActions?: boolean
|
|
75
|
-
/** Whether to validate required schema properties */
|
|
76
|
-
validateRequiredProperties?: boolean
|
|
77
|
-
}
|
|
13
|
+
import { ValidationSeverity } from './types/schema-validator'
|
|
14
|
+
import type { ValidationIssue, ValidationResult, ValidatorOptions } from './types/schema-validator'
|
|
78
15
|
|
|
79
16
|
/**
|
|
80
17
|
* Schema validator class
|