@stonecrop/stonecrop 0.10.11 → 0.10.13
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 +26 -110
- package/dist/field-triggers.js +5 -6
- package/dist/index.js +9 -6
- package/dist/plugins/index.js +4 -1
- package/dist/registry.js +62 -61
- 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/field-triggers.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/registry.d.ts +13 -10
- package/dist/src/registry.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.css +1 -0
- package/dist/stonecrop.d.ts +206 -14
- package/dist/stonecrop.js +1751 -1631
- 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 +4 -4
- package/src/composables/stonecrop.ts +34 -218
- package/src/doctype.ts +2 -29
- package/src/field-triggers.ts +5 -6
- package/src/index.ts +12 -19
- package/src/plugins/index.ts +4 -1
- package/src/registry.ts +61 -66
- package/src/schema-validator.ts +3 -66
- package/src/stonecrop.ts +148 -17
- 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
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { storeToRefs } from 'pinia';
|
|
1
2
|
import { inject, onMounted, ref, watch, provide, computed } from 'vue';
|
|
2
3
|
import { Stonecrop } from '../stonecrop';
|
|
3
|
-
import { storeToRefs } from 'pinia';
|
|
4
4
|
/**
|
|
5
5
|
* @public
|
|
6
6
|
*/
|
|
@@ -21,6 +21,12 @@ export function useStonecrop(options) {
|
|
|
21
21
|
const isLoading = ref(false);
|
|
22
22
|
const error = ref(null);
|
|
23
23
|
const resolvedDoctype = ref();
|
|
24
|
+
// Initialize stonecrop instance synchronously using singleton pattern
|
|
25
|
+
// Use injected instance if available, otherwise fall back to the singleton root
|
|
26
|
+
const stonecropInstance = providedStonecrop || Stonecrop._root;
|
|
27
|
+
if (stonecropInstance) {
|
|
28
|
+
stonecrop.value = stonecropInstance;
|
|
29
|
+
}
|
|
24
30
|
// If doctype is a Doctype instance (not string), set resolved immediately
|
|
25
31
|
if (options?.doctype && typeof options.doctype !== 'string') {
|
|
26
32
|
resolvedDoctype.value = options.doctype;
|
|
@@ -81,10 +87,9 @@ export function useStonecrop(options) {
|
|
|
81
87
|
};
|
|
82
88
|
// Initialize Stonecrop instance
|
|
83
89
|
onMounted(async () => {
|
|
84
|
-
if (!registry) {
|
|
90
|
+
if (!registry || !stonecrop.value) {
|
|
85
91
|
return;
|
|
86
92
|
}
|
|
87
|
-
stonecrop.value = providedStonecrop || new Stonecrop(registry);
|
|
88
93
|
// Set up reactive refs from operation log store - only if Pinia is available
|
|
89
94
|
try {
|
|
90
95
|
const opLogStore = stonecrop.value.getOperationLogStore();
|
|
@@ -148,12 +153,12 @@ export function useStonecrop(options) {
|
|
|
148
153
|
}
|
|
149
154
|
}
|
|
150
155
|
catch {
|
|
151
|
-
formData.value =
|
|
156
|
+
formData.value = registry.initializeRecord(resolvedSchema.value);
|
|
152
157
|
}
|
|
153
158
|
}
|
|
154
159
|
}
|
|
155
160
|
else {
|
|
156
|
-
formData.value =
|
|
161
|
+
formData.value = registry.initializeRecord(resolvedSchema.value);
|
|
157
162
|
}
|
|
158
163
|
if (hstStore.value) {
|
|
159
164
|
setupDeepReactivity(doctype, recordId || 'new', formData, hstStore.value);
|
|
@@ -229,12 +234,12 @@ export function useStonecrop(options) {
|
|
|
229
234
|
}
|
|
230
235
|
}
|
|
231
236
|
catch {
|
|
232
|
-
formData.value =
|
|
237
|
+
formData.value = registry.initializeRecord(resolvedSchema.value);
|
|
233
238
|
}
|
|
234
239
|
}
|
|
235
240
|
}
|
|
236
241
|
else {
|
|
237
|
-
formData.value =
|
|
242
|
+
formData.value = registry.initializeRecord(resolvedSchema.value);
|
|
238
243
|
}
|
|
239
244
|
if (hstStore.value) {
|
|
240
245
|
setupDeepReactivity(doctype, recordId || 'new', formData, hstStore.value);
|
|
@@ -298,64 +303,30 @@ export function useStonecrop(options) {
|
|
|
298
303
|
}
|
|
299
304
|
/**
|
|
300
305
|
* Load nested doctype data from API or initialize empty structure
|
|
306
|
+
* Delegates to Stonecrop.loadNestedData method
|
|
301
307
|
* @param parentPath - The parent path (e.g., "customer.123.address")
|
|
302
308
|
* @param childDoctype - The child doctype metadata
|
|
303
309
|
* @param recordId - Optional record ID to load
|
|
304
|
-
* @returns
|
|
310
|
+
* @returns The loaded or initialized data
|
|
305
311
|
*/
|
|
306
312
|
const loadNestedData = (parentPath, childDoctype, recordId) => {
|
|
307
313
|
if (!stonecrop.value) {
|
|
308
|
-
|
|
314
|
+
throw new Error('Stonecrop instance not available');
|
|
309
315
|
}
|
|
310
|
-
|
|
311
|
-
if (recordId) {
|
|
312
|
-
try {
|
|
313
|
-
// Check if data already exists in HST
|
|
314
|
-
const existingData = hstStore.value?.get(parentPath);
|
|
315
|
-
if (existingData && typeof existingData === 'object') {
|
|
316
|
-
return existingData;
|
|
317
|
-
}
|
|
318
|
-
// TODO: Add API fetch logic here if needed
|
|
319
|
-
// For now, initialize new record
|
|
320
|
-
return initializeNewRecord(childDoctype);
|
|
321
|
-
}
|
|
322
|
-
catch {
|
|
323
|
-
return initializeNewRecord(childDoctype);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
// Initialize new record
|
|
327
|
-
return initializeNewRecord(childDoctype);
|
|
316
|
+
return stonecrop.value.loadNestedData(parentPath, childDoctype, recordId);
|
|
328
317
|
};
|
|
329
318
|
/**
|
|
330
|
-
*
|
|
319
|
+
* Collect a record payload with all nested doctype fields from HST
|
|
320
|
+
* Delegates to Stonecrop.collectRecordPayload method
|
|
331
321
|
* @param doctype - The doctype metadata
|
|
332
|
-
* @param recordId - The record ID to
|
|
333
|
-
* @returns The complete
|
|
322
|
+
* @param recordId - The record ID to collect
|
|
323
|
+
* @returns The complete record payload ready for API submission
|
|
334
324
|
*/
|
|
335
|
-
const
|
|
336
|
-
if (!
|
|
337
|
-
throw new Error('
|
|
338
|
-
}
|
|
339
|
-
const recordPath = `${doctype.slug}.${recordId}`;
|
|
340
|
-
const recordData = hstStore.value.get(recordPath) || {};
|
|
341
|
-
// Build the save payload using resolved schema
|
|
342
|
-
const payload = { ...recordData };
|
|
343
|
-
// Use resolveSchema to get the full resolved tree, then walk Doctype fields
|
|
344
|
-
const schemaArray = doctype.schema
|
|
345
|
-
? Array.isArray(doctype.schema)
|
|
346
|
-
? doctype.schema
|
|
347
|
-
: Array.from(doctype.schema)
|
|
348
|
-
: [];
|
|
349
|
-
const resolved = registry ? registry.resolveSchema(schemaArray) : schemaArray;
|
|
350
|
-
const doctypeFields = resolved.filter(field => 'fieldtype' in field && field.fieldtype === 'Doctype' && 'schema' in field && Array.isArray(field.schema));
|
|
351
|
-
// Recursively collect nested data from HST using resolved schemas
|
|
352
|
-
for (const field of doctypeFields) {
|
|
353
|
-
const doctypeField = field;
|
|
354
|
-
const fieldPath = `${recordPath}.${doctypeField.fieldname}`;
|
|
355
|
-
const nestedData = collectNestedData(doctypeField.schema, fieldPath, hstStore.value);
|
|
356
|
-
payload[doctypeField.fieldname] = nestedData;
|
|
325
|
+
const collectRecordPayload = (doctype, recordId) => {
|
|
326
|
+
if (!stonecrop.value) {
|
|
327
|
+
throw new Error('Stonecrop instance not available');
|
|
357
328
|
}
|
|
358
|
-
return
|
|
329
|
+
return stonecrop.value.collectRecordPayload(doctype, recordId);
|
|
359
330
|
};
|
|
360
331
|
/**
|
|
361
332
|
* Create a nested context for child forms
|
|
@@ -413,7 +384,7 @@ export function useStonecrop(options) {
|
|
|
413
384
|
formData,
|
|
414
385
|
resolvedSchema,
|
|
415
386
|
loadNestedData,
|
|
416
|
-
|
|
387
|
+
collectRecordPayload,
|
|
417
388
|
createNestedContext,
|
|
418
389
|
isLoading,
|
|
419
390
|
error,
|
|
@@ -431,7 +402,7 @@ export function useStonecrop(options) {
|
|
|
431
402
|
formData,
|
|
432
403
|
resolvedSchema,
|
|
433
404
|
loadNestedData,
|
|
434
|
-
|
|
405
|
+
collectRecordPayload,
|
|
435
406
|
createNestedContext,
|
|
436
407
|
isLoading,
|
|
437
408
|
error,
|
|
@@ -444,40 +415,6 @@ export function useStonecrop(options) {
|
|
|
444
415
|
operationLog,
|
|
445
416
|
};
|
|
446
417
|
}
|
|
447
|
-
/**
|
|
448
|
-
* Initialize new record structure based on doctype schema
|
|
449
|
-
*/
|
|
450
|
-
function initializeNewRecord(doctype) {
|
|
451
|
-
const initialData = {};
|
|
452
|
-
if (!doctype.schema) {
|
|
453
|
-
return initialData;
|
|
454
|
-
}
|
|
455
|
-
doctype.schema.forEach(field => {
|
|
456
|
-
const fieldtype = 'fieldtype' in field ? field.fieldtype : 'Data';
|
|
457
|
-
switch (fieldtype) {
|
|
458
|
-
case 'Data':
|
|
459
|
-
case 'Text':
|
|
460
|
-
initialData[field.fieldname] = '';
|
|
461
|
-
break;
|
|
462
|
-
case 'Check':
|
|
463
|
-
initialData[field.fieldname] = false;
|
|
464
|
-
break;
|
|
465
|
-
case 'Int':
|
|
466
|
-
case 'Float':
|
|
467
|
-
initialData[field.fieldname] = 0;
|
|
468
|
-
break;
|
|
469
|
-
case 'Table':
|
|
470
|
-
initialData[field.fieldname] = [];
|
|
471
|
-
break;
|
|
472
|
-
case 'JSON':
|
|
473
|
-
initialData[field.fieldname] = {};
|
|
474
|
-
break;
|
|
475
|
-
default:
|
|
476
|
-
initialData[field.fieldname] = null;
|
|
477
|
-
}
|
|
478
|
-
});
|
|
479
|
-
return initialData;
|
|
480
|
-
}
|
|
481
418
|
/**
|
|
482
419
|
* Setup deep reactivity between form data and HST store
|
|
483
420
|
*/
|
|
@@ -510,24 +447,3 @@ function updateNestedObject(obj, path, value) {
|
|
|
510
447
|
const finalKey = path[path.length - 1];
|
|
511
448
|
current[finalKey] = value;
|
|
512
449
|
}
|
|
513
|
-
/**
|
|
514
|
-
* Recursively collect nested data from HST using pre-resolved schemas
|
|
515
|
-
* @param resolvedSchema - The already-resolved schema (with nested schemas embedded)
|
|
516
|
-
* @param basePath - The base path in HST (e.g., "customer.123.address")
|
|
517
|
-
* @param hstStore - The HST store instance
|
|
518
|
-
* @returns The collected data object
|
|
519
|
-
*/
|
|
520
|
-
function collectNestedData(resolvedSchema, basePath, hstStore) {
|
|
521
|
-
const data = hstStore.get(basePath) || {};
|
|
522
|
-
const payload = { ...data };
|
|
523
|
-
// Find Doctype fields that have resolved child schemas
|
|
524
|
-
const doctypeFields = resolvedSchema.filter(field => 'fieldtype' in field && field.fieldtype === 'Doctype' && 'schema' in field && Array.isArray(field.schema));
|
|
525
|
-
// Recursively collect nested data
|
|
526
|
-
for (const field of doctypeFields) {
|
|
527
|
-
const doctypeField = field;
|
|
528
|
-
const fieldPath = `${basePath}.${doctypeField.fieldname}`;
|
|
529
|
-
const nestedData = collectNestedData(doctypeField.schema, fieldPath, hstStore);
|
|
530
|
-
payload[doctypeField.fieldname] = nestedData;
|
|
531
|
-
}
|
|
532
|
-
return payload;
|
|
533
|
-
}
|
package/dist/field-triggers.js
CHANGED
|
@@ -77,12 +77,11 @@ export class FieldTriggerEngine {
|
|
|
77
77
|
const actionMap = new Map();
|
|
78
78
|
const transitionMap = new Map();
|
|
79
79
|
// Convert from different Map types to regular Map
|
|
80
|
-
//
|
|
81
|
-
|
|
80
|
+
// Check for Immutable.js Map first (has entrySeq method)
|
|
81
|
+
const immutableActions = actions;
|
|
82
|
+
if (typeof immutableActions.entrySeq === 'function') {
|
|
82
83
|
// Immutable Map
|
|
83
|
-
|
|
84
|
-
;
|
|
85
|
-
actions.entrySeq().forEach(([key, value]) => {
|
|
84
|
+
immutableActions.entrySeq().forEach(([key, value]) => {
|
|
86
85
|
this.categorizeAction(key, value, actionMap, transitionMap);
|
|
87
86
|
});
|
|
88
87
|
}
|
|
@@ -423,7 +422,7 @@ export class FieldTriggerEngine {
|
|
|
423
422
|
})
|
|
424
423
|
.catch(error => {
|
|
425
424
|
clearTimeout(timeoutId);
|
|
426
|
-
reject(error);
|
|
425
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
427
426
|
});
|
|
428
427
|
});
|
|
429
428
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
import { useStonecrop } from './composables/stonecrop';
|
|
2
2
|
import { useOperationLog, useUndoRedoShortcuts, withBatch } from './composables/operation-log';
|
|
3
3
|
import Doctype from './doctype';
|
|
4
|
-
import { getGlobalTriggerEngine, markOperationIrreversible, registerGlobalAction, registerTransitionAction, setFieldRollback, triggerTransition, } from './field-triggers';
|
|
4
|
+
import { FieldTriggerEngine, getGlobalTriggerEngine, markOperationIrreversible, registerGlobalAction, registerTransitionAction, setFieldRollback, triggerTransition, } from './field-triggers';
|
|
5
5
|
import plugin from './plugins';
|
|
6
6
|
import Registry from './registry';
|
|
7
|
-
import { Stonecrop } from './stonecrop';
|
|
7
|
+
import { Stonecrop, collectNestedData } from './stonecrop';
|
|
8
8
|
import { HST, createHST } from './stores/hst';
|
|
9
9
|
import { useOperationLogStore } from './stores/operation-log';
|
|
10
|
-
// Export schema validator
|
|
11
10
|
import { SchemaValidator, createValidator, validateSchema } from './schema-validator';
|
|
12
|
-
|
|
11
|
+
import { ValidationSeverity } from './types/schema-validator';
|
|
12
|
+
// Export enum as value (enums need runtime export, not just type)
|
|
13
|
+
export { ValidationSeverity };
|
|
13
14
|
export { Doctype, Registry, Stonecrop, useStonecrop,
|
|
14
15
|
// HST exports for advanced usage
|
|
15
16
|
HST, createHST,
|
|
16
17
|
// Field trigger system exports
|
|
17
|
-
getGlobalTriggerEngine, registerGlobalAction, registerTransitionAction, setFieldRollback, triggerTransition, markOperationIrreversible,
|
|
18
|
+
FieldTriggerEngine, getGlobalTriggerEngine, registerGlobalAction, registerTransitionAction, setFieldRollback, triggerTransition, markOperationIrreversible,
|
|
18
19
|
// Schema validator exports
|
|
19
20
|
SchemaValidator, createValidator, validateSchema,
|
|
20
21
|
// Operation log exports
|
|
21
|
-
useOperationLog, useOperationLogStore, useUndoRedoShortcuts, withBatch,
|
|
22
|
+
useOperationLog, useOperationLogStore, useUndoRedoShortcuts, withBatch,
|
|
23
|
+
// Utility functions
|
|
24
|
+
collectNestedData, };
|
|
22
25
|
// Default export is the Vue plugin
|
|
23
26
|
export default plugin;
|
package/dist/plugins/index.js
CHANGED
|
@@ -61,7 +61,10 @@ const plugin = {
|
|
|
61
61
|
app.provide('$registry', registry);
|
|
62
62
|
app.config.globalProperties.$registry = registry;
|
|
63
63
|
// Create and provide a global Stonecrop instance
|
|
64
|
-
const stonecrop = new Stonecrop(registry
|
|
64
|
+
const stonecrop = new Stonecrop(registry);
|
|
65
|
+
if (options?.client) {
|
|
66
|
+
stonecrop.setClient(options.client);
|
|
67
|
+
}
|
|
65
68
|
app.provide('$stonecrop', stonecrop);
|
|
66
69
|
app.config.globalProperties.$stonecrop = stonecrop;
|
|
67
70
|
// Initialize operation log store if Pinia is available
|
package/dist/registry.js
CHANGED
|
@@ -70,17 +70,18 @@ export default class Registry {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
/**
|
|
73
|
-
* Resolve nested Doctype
|
|
73
|
+
* Resolve nested Doctype fields in a schema by embedding child schemas inline.
|
|
74
74
|
*
|
|
75
75
|
* @remarks
|
|
76
76
|
* Walks the schema array and for each field with `fieldtype: 'Doctype'` and a string
|
|
77
|
-
* `options` value, looks up the referenced doctype in the registry and
|
|
78
|
-
* as the field's `schema` property. Recurses for deeply nested doctypes.
|
|
77
|
+
* `options` value, looks up the referenced doctype in the registry and:
|
|
79
78
|
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
79
|
+
* - If `cardinality: 'many'`: auto-derives `columns` from the child doctype's schema,
|
|
80
|
+
* sets `component: 'ATable'`, `config: { view: 'list' }`, and initializes `rows: []`.
|
|
81
|
+
* - Otherwise (default/`cardinality: 'one'`): embeds the child schema as the field's
|
|
82
|
+
* `schema` property for 1:1 nested forms.
|
|
83
|
+
*
|
|
84
|
+
* Recurses for deeply nested doctypes. Circular references are protected against.
|
|
84
85
|
*
|
|
85
86
|
* Returns a new array — does not mutate the original schema.
|
|
86
87
|
*
|
|
@@ -116,53 +117,46 @@ export default class Registry {
|
|
|
116
117
|
if (doctype && doctype.schema) {
|
|
117
118
|
// Convert Immutable.List to plain array if needed
|
|
118
119
|
const childSchema = Array.isArray(doctype.schema) ? doctype.schema : Array.from(doctype.schema);
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}));
|
|
151
|
-
}
|
|
152
|
-
// Set default component if not already specified
|
|
153
|
-
if (!resolved.component) {
|
|
154
|
-
resolved.component = 'ATable';
|
|
120
|
+
// Check cardinality to determine handling
|
|
121
|
+
const cardinality = 'cardinality' in field ? field.cardinality : undefined;
|
|
122
|
+
if (cardinality === 'many') {
|
|
123
|
+
// 1:many child table - derive columns, set component, config, rows
|
|
124
|
+
const resolved = { ...field };
|
|
125
|
+
// Auto-derive columns from child schema fields if not already provided
|
|
126
|
+
if (!('columns' in field) || !field.columns) {
|
|
127
|
+
resolved.columns = childSchema.map(childField => ({
|
|
128
|
+
name: childField.fieldname,
|
|
129
|
+
fieldname: childField.fieldname,
|
|
130
|
+
label: ('label' in childField && childField.label) || childField.fieldname,
|
|
131
|
+
fieldtype: 'fieldtype' in childField ? childField.fieldtype : 'Data',
|
|
132
|
+
align: ('align' in childField && childField.align) || 'left',
|
|
133
|
+
edit: 'edit' in childField ? childField.edit : true,
|
|
134
|
+
width: ('width' in childField && childField.width) || '20ch',
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
// Set default component if not already specified
|
|
138
|
+
if (!resolved.component) {
|
|
139
|
+
resolved.component = 'ATable';
|
|
140
|
+
}
|
|
141
|
+
// Set default config if not already specified
|
|
142
|
+
if (!('config' in field) || !field.config) {
|
|
143
|
+
resolved.config = { view: 'list' };
|
|
144
|
+
}
|
|
145
|
+
// Initialize rows to empty array so componentProps fallback
|
|
146
|
+
// routes data from the form's dataModel[fieldname]
|
|
147
|
+
if (!('rows' in field) || !field.rows) {
|
|
148
|
+
resolved.rows = [];
|
|
149
|
+
}
|
|
150
|
+
return resolved;
|
|
155
151
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
resolved.rows = [];
|
|
152
|
+
else {
|
|
153
|
+
// 1:1 nested form (default cardinality: 'one')
|
|
154
|
+
// Recurse into child schema to resolve deeply nested doctypes
|
|
155
|
+
seen.add(doctypeSlug);
|
|
156
|
+
const resolvedChild = this.resolveSchema(childSchema, seen);
|
|
157
|
+
seen.delete(doctypeSlug);
|
|
158
|
+
return { ...field, schema: resolvedChild };
|
|
164
159
|
}
|
|
165
|
-
return resolved;
|
|
166
160
|
}
|
|
167
161
|
}
|
|
168
162
|
return { ...field };
|
|
@@ -177,11 +171,13 @@ export default class Registry {
|
|
|
177
171
|
* - Data, Text → `''`
|
|
178
172
|
* - Check → `false`
|
|
179
173
|
* - Int, Float, Decimal, Currency, Quantity → `0`
|
|
180
|
-
* -
|
|
181
|
-
* -
|
|
174
|
+
* - JSON → `{}`
|
|
175
|
+
* - Doctype with `cardinality: 'many'` → `[]`
|
|
176
|
+
* - Doctype without `cardinality` or `cardinality: 'one'` → recursively initializes nested record
|
|
182
177
|
* - All others → `null`
|
|
183
178
|
*
|
|
184
|
-
* For Doctype fields with a resolved `schema` array, recursively
|
|
179
|
+
* For Doctype fields with a resolved `schema` array (cardinality: 'one'), recursively
|
|
180
|
+
* initializes the nested record.
|
|
185
181
|
*
|
|
186
182
|
* @param schema - The schema array to derive defaults from
|
|
187
183
|
* @returns A plain object with default values for each field
|
|
@@ -214,21 +210,26 @@ export default class Registry {
|
|
|
214
210
|
case 'Quantity':
|
|
215
211
|
record[field.fieldname] = 0;
|
|
216
212
|
break;
|
|
217
|
-
case 'Table':
|
|
218
|
-
record[field.fieldname] = [];
|
|
219
|
-
break;
|
|
220
213
|
case 'JSON':
|
|
221
214
|
record[field.fieldname] = {};
|
|
222
215
|
break;
|
|
223
|
-
case 'Doctype':
|
|
224
|
-
//
|
|
225
|
-
|
|
216
|
+
case 'Doctype': {
|
|
217
|
+
// Check cardinality to determine initial value
|
|
218
|
+
const cardinality = 'cardinality' in field ? field.cardinality : undefined;
|
|
219
|
+
if (cardinality === 'many') {
|
|
220
|
+
// 1:many child table - initialize as empty array
|
|
221
|
+
record[field.fieldname] = [];
|
|
222
|
+
}
|
|
223
|
+
else if ('schema' in field && Array.isArray(field.schema)) {
|
|
224
|
+
// 1:1 nested form with resolved schema - recursively initialize
|
|
226
225
|
record[field.fieldname] = this.initializeRecord(field.schema);
|
|
227
226
|
}
|
|
228
227
|
else {
|
|
228
|
+
// 1:1 without resolved schema - empty object
|
|
229
229
|
record[field.fieldname] = {};
|
|
230
230
|
}
|
|
231
231
|
break;
|
|
232
|
+
}
|
|
232
233
|
default:
|
|
233
234
|
record[field.fieldname] = null;
|
|
234
235
|
}
|
package/dist/schema-validator.js
CHANGED
|
@@ -4,19 +4,7 @@
|
|
|
4
4
|
* @packageDocumentation
|
|
5
5
|
*/
|
|
6
6
|
import { getGlobalTriggerEngine } from './field-triggers';
|
|
7
|
-
|
|
8
|
-
* Validation severity levels
|
|
9
|
-
* @public
|
|
10
|
-
*/
|
|
11
|
-
export var ValidationSeverity;
|
|
12
|
-
(function (ValidationSeverity) {
|
|
13
|
-
/** Blocking error that prevents save */
|
|
14
|
-
ValidationSeverity["ERROR"] = "error";
|
|
15
|
-
/** Advisory warning that allows save */
|
|
16
|
-
ValidationSeverity["WARNING"] = "warning";
|
|
17
|
-
/** Informational message */
|
|
18
|
-
ValidationSeverity["INFO"] = "info";
|
|
19
|
-
})(ValidationSeverity || (ValidationSeverity = {}));
|
|
7
|
+
import { ValidationSeverity } from './types/schema-validator';
|
|
20
8
|
/**
|
|
21
9
|
* Schema validator class
|
|
22
10
|
* @public
|
|
@@ -1,78 +1,6 @@
|
|
|
1
|
-
import { Ref, ComputedRef } from 'vue';
|
|
2
|
-
import Registry from '../registry';
|
|
3
|
-
import { Stonecrop } from '../stonecrop';
|
|
4
1
|
import Doctype from '../doctype';
|
|
5
|
-
import
|
|
6
|
-
import type {
|
|
7
|
-
import { SchemaTypes } from '@stonecrop/aform';
|
|
8
|
-
/**
|
|
9
|
-
* Operation Log API - nested object containing all operation log functionality
|
|
10
|
-
* @public
|
|
11
|
-
*/
|
|
12
|
-
export type OperationLogAPI = {
|
|
13
|
-
operations: Ref<HSTOperation[]>;
|
|
14
|
-
currentIndex: Ref<number>;
|
|
15
|
-
undoRedoState: ComputedRef<{
|
|
16
|
-
canUndo: boolean;
|
|
17
|
-
canRedo: boolean;
|
|
18
|
-
undoCount: number;
|
|
19
|
-
redoCount: number;
|
|
20
|
-
currentIndex: number;
|
|
21
|
-
}>;
|
|
22
|
-
canUndo: ComputedRef<boolean>;
|
|
23
|
-
canRedo: ComputedRef<boolean>;
|
|
24
|
-
undoCount: ComputedRef<number>;
|
|
25
|
-
redoCount: ComputedRef<number>;
|
|
26
|
-
undo: (hstStore: HSTNode) => boolean;
|
|
27
|
-
redo: (hstStore: HSTNode) => boolean;
|
|
28
|
-
startBatch: () => void;
|
|
29
|
-
commitBatch: (description?: string) => string | null;
|
|
30
|
-
cancelBatch: () => void;
|
|
31
|
-
clear: () => void;
|
|
32
|
-
getOperationsFor: (doctype: string, recordId?: string) => HSTOperation[];
|
|
33
|
-
getSnapshot: () => OperationLogSnapshot;
|
|
34
|
-
markIrreversible: (operationId: string, reason: string) => void;
|
|
35
|
-
logAction: (doctype: string, actionName: string, recordIds?: string[], result?: 'success' | 'failure' | 'pending', error?: string) => string;
|
|
36
|
-
configure: (options: Partial<OperationLogConfig>) => void;
|
|
37
|
-
};
|
|
38
|
-
/**
|
|
39
|
-
* Base Stonecrop composable return type - includes operation log functionality
|
|
40
|
-
* @public
|
|
41
|
-
*/
|
|
42
|
-
export type BaseStonecropReturn = {
|
|
43
|
-
stonecrop: Ref<Stonecrop | undefined>;
|
|
44
|
-
operationLog: OperationLogAPI;
|
|
45
|
-
};
|
|
46
|
-
/**
|
|
47
|
-
* HST-enabled Stonecrop composable return type
|
|
48
|
-
* @public
|
|
49
|
-
*/
|
|
50
|
-
export type HSTStonecropReturn = BaseStonecropReturn & {
|
|
51
|
-
provideHSTPath: (fieldname: string, recordId?: string) => string;
|
|
52
|
-
handleHSTChange: (changeData: HSTChangeData) => void;
|
|
53
|
-
hstStore: Ref<HSTNode | undefined>;
|
|
54
|
-
formData: Ref<Record<string, any>>;
|
|
55
|
-
resolvedSchema: Ref<SchemaTypes[]>;
|
|
56
|
-
loadNestedData: (parentPath: string, childDoctype: Doctype, recordId?: string) => Record<string, any>;
|
|
57
|
-
saveRecursive: (doctype: Doctype, recordId: string) => Promise<Record<string, any>>;
|
|
58
|
-
createNestedContext: (basePath: string, childDoctype: Doctype) => {
|
|
59
|
-
provideHSTPath: (fieldname: string) => string;
|
|
60
|
-
handleHSTChange: (changeData: HSTChangeData) => void;
|
|
61
|
-
};
|
|
62
|
-
isLoading: Ref<boolean>;
|
|
63
|
-
error: Ref<Error | null>;
|
|
64
|
-
resolvedDoctype: Ref<Doctype | undefined>;
|
|
65
|
-
};
|
|
66
|
-
/**
|
|
67
|
-
* HST Change data structure
|
|
68
|
-
* @public
|
|
69
|
-
*/
|
|
70
|
-
export type HSTChangeData = {
|
|
71
|
-
path: string;
|
|
72
|
-
value: any;
|
|
73
|
-
fieldname: string;
|
|
74
|
-
recordId?: string;
|
|
75
|
-
};
|
|
2
|
+
import Registry from '../registry';
|
|
3
|
+
import type { BaseStonecropReturn, HSTStonecropReturn } from '../types/composable';
|
|
76
4
|
/**
|
|
77
5
|
* Unified Stonecrop composable - handles both general operations and HST reactive integration
|
|
78
6
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stonecrop.d.ts","sourceRoot":"","sources":["../../../src/composables/stonecrop.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"stonecrop.d.ts","sourceRoot":"","sources":["../../../src/composables/stonecrop.ts"],"names":[],"mappings":"AAIA,OAAO,OAAO,MAAM,YAAY,CAAA;AAChC,OAAO,QAAQ,MAAM,aAAa,CAAA;AAGlC,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAkC,MAAM,qBAAqB,CAAA;AAIlH;;;;;;GAMG;AACH,wBAAgB,YAAY,IAAI,mBAAmB,GAAG,kBAAkB,CAAA;AACxE;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE;IACrC,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,OAAO,EAAE,OAAO,GAAG,MAAM,CAAA;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAA;CACjB,GAAG,kBAAkB,CAAA"}
|
package/dist/src/doctype.d.ts
CHANGED
|
@@ -1,33 +1,7 @@
|
|
|
1
1
|
import type { SchemaTypes } from '@stonecrop/aform';
|
|
2
|
-
import type { WorkflowMeta } from '@stonecrop/schema';
|
|
3
2
|
import { Component } from 'vue';
|
|
4
|
-
import type { UnknownMachineConfig } from 'xstate';
|
|
5
3
|
import type { ImmutableDoctype } from './types';
|
|
6
|
-
|
|
7
|
-
* Plain object representation of doctype configuration for serialization/API responses.
|
|
8
|
-
* Compatible with the DoctypeMeta type from \@stonecrop/schema.
|
|
9
|
-
* @public
|
|
10
|
-
*/
|
|
11
|
-
export type DoctypeConfig = {
|
|
12
|
-
/** Display name of the doctype */
|
|
13
|
-
name: string;
|
|
14
|
-
/** URL-friendly slug (kebab-case) */
|
|
15
|
-
slug?: string;
|
|
16
|
-
/** Database table name */
|
|
17
|
-
tableName?: string;
|
|
18
|
-
/** Field definitions */
|
|
19
|
-
fields?: SchemaTypes[];
|
|
20
|
-
/** Workflow configuration (XState format or simple WorkflowMeta) */
|
|
21
|
-
workflow?: UnknownMachineConfig | WorkflowMeta;
|
|
22
|
-
/** Actions and their field triggers */
|
|
23
|
-
actions?: Record<string, string[]>;
|
|
24
|
-
/** Parent doctype for inheritance */
|
|
25
|
-
inherits?: string;
|
|
26
|
-
/** Doctype to use for list views */
|
|
27
|
-
listDoctype?: string;
|
|
28
|
-
/** Parent doctype for child tables */
|
|
29
|
-
parentDoctype?: string;
|
|
30
|
-
};
|
|
4
|
+
import type { DoctypeConfig } from './types/doctype';
|
|
31
5
|
/**
|
|
32
6
|
* Doctype runtime class with Immutable.js collections for HST change tracking.
|
|
33
7
|
* @public
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctype.d.ts","sourceRoot":"","sources":["../../src/doctype.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"doctype.d.ts","sourceRoot":"","sources":["../../src/doctype.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAGnD,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,CAAA;AAG/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,OAAO;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IAExB;;;;OAIG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IAE3C;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAA;IAE/C;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC,SAAS,CAAC,CAAA;IAE7C;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAA;IAE9B;;;;;;;OAOG;gBAEF,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EAClC,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,EACtC,OAAO,EAAE,gBAAgB,CAAC,SAAS,CAAC,EACpC,SAAS,CAAC,EAAE,SAAS;IAStB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqCG;IACH,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO;IAOjD;;;;;;;;;;;;;;OAcG;IACH,cAAc,IAAI,WAAW,EAAE;IAK/B;;;;;;;OAOG;IACH,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAK5C;;;;;;;;;;;;;;OAcG;IACH,uBAAuB,CAAC,YAAY,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAsC3F;;;;;;;;;;;;;;OAcG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAC7B;QACA,KAAK,EAAE,MAAM,CAAA;QACb,OAAO,EAAE,MAAM,CAAA;QACf,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;QACzB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;QACxB,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAC7B,GACD,SAAS;IAQZ;;;;;;;;;;;;;;;OAeG;IACH,IAAI,IAAI,WAKP;CACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-triggers.d.ts","sourceRoot":"","sources":["../../src/field-triggers.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"field-triggers.d.ts","sourceRoot":"","sources":["../../src/field-triggers.ts"],"names":[],"mappings":"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"}
|