form0-core 0.1.6 → 0.1.7
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/package.json +1 -1
- package/src/index.js +1 -0
- package/src/utilities/calculation-editor.js +153 -48
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -296,6 +296,33 @@ function sanitizeCollectedWarnings(warnings) {
|
|
|
296
296
|
}));
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
+
function clearRecord(record) {
|
|
300
|
+
if (!record || typeof record !== 'object') {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
Object.keys(record).forEach((key) => {
|
|
305
|
+
delete record[key];
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function resetEngineValues(targetValues, sourceValues) {
|
|
310
|
+
clearRecord(targetValues);
|
|
311
|
+
Object.assign(targetValues, deepClone(sourceValues));
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function normalizeSimulationValues(values) {
|
|
315
|
+
return values && typeof values === 'object' && !Array.isArray(values) ? values : {};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function createSimulationErrorResult(runtimeError, warnings = []) {
|
|
319
|
+
return {
|
|
320
|
+
result: null,
|
|
321
|
+
runtimeError,
|
|
322
|
+
warnings,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
299
326
|
/**
|
|
300
327
|
* @typedef {'stable' | 'advanced' | 'unavailable'} CalculationBuiltinStatus
|
|
301
328
|
*
|
|
@@ -615,30 +642,34 @@ export function analyzeCalculationExpression({
|
|
|
615
642
|
}
|
|
616
643
|
|
|
617
644
|
/**
|
|
618
|
-
*
|
|
645
|
+
* Create a reusable preview session for CalculatedField authoring.
|
|
619
646
|
*
|
|
620
647
|
* @param {Object} options
|
|
621
648
|
* @param {Object} options.schema
|
|
622
649
|
* @param {string} options.fieldDataName
|
|
623
650
|
* @param {string} options.expression
|
|
624
|
-
* @param {Object} [options.values]
|
|
625
651
|
* @param {Object} [options.security]
|
|
626
652
|
* @returns {{
|
|
627
|
-
*
|
|
628
|
-
*
|
|
629
|
-
*
|
|
630
|
-
*
|
|
631
|
-
*
|
|
632
|
-
*
|
|
633
|
-
*
|
|
634
|
-
*
|
|
653
|
+
* run: (options?: {
|
|
654
|
+
* expression?: string,
|
|
655
|
+
* values?: Object,
|
|
656
|
+
* }) => {
|
|
657
|
+
* result: unknown,
|
|
658
|
+
* runtimeError: string | null,
|
|
659
|
+
* warnings: Array<{
|
|
660
|
+
* message: string,
|
|
661
|
+
* suggestion: string | null,
|
|
662
|
+
* context: Object | null,
|
|
663
|
+
* fieldContext: Object | null,
|
|
664
|
+
* }>,
|
|
665
|
+
* },
|
|
666
|
+
* dispose: () => void,
|
|
635
667
|
* }}
|
|
636
668
|
*/
|
|
637
|
-
export function
|
|
669
|
+
export function createCalculationPreviewSession({
|
|
638
670
|
schema,
|
|
639
671
|
fieldDataName,
|
|
640
672
|
expression,
|
|
641
|
-
values = {},
|
|
642
673
|
security = DEFAULT_SECURITY_CONFIG,
|
|
643
674
|
}) {
|
|
644
675
|
const simulationSchema = cloneSimulationSchema(schema);
|
|
@@ -647,17 +678,19 @@ export function simulateCalculationExpression({
|
|
|
647
678
|
|
|
648
679
|
if (!targetField) {
|
|
649
680
|
return {
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
681
|
+
run: () =>
|
|
682
|
+
createSimulationErrorResult(
|
|
683
|
+
`Field '${fieldDataName}' does not exist in the form schema.`
|
|
684
|
+
),
|
|
685
|
+
dispose: () => {},
|
|
653
686
|
};
|
|
654
687
|
}
|
|
655
688
|
|
|
656
689
|
if (targetField.type !== 'CalculatedField') {
|
|
657
690
|
return {
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
691
|
+
run: () =>
|
|
692
|
+
createSimulationErrorResult(`Field '${fieldDataName}' is not a CalculatedField.`),
|
|
693
|
+
dispose: () => {},
|
|
661
694
|
};
|
|
662
695
|
}
|
|
663
696
|
|
|
@@ -666,41 +699,113 @@ export function simulateCalculationExpression({
|
|
|
666
699
|
const warningSystem = new WarningSystem({
|
|
667
700
|
enableCollection: true,
|
|
668
701
|
enableConsoleWarnings: false,
|
|
702
|
+
throttleMs: 0,
|
|
669
703
|
});
|
|
670
704
|
const runtimeDiagnostics = [];
|
|
705
|
+
const engine = createFormEngine({
|
|
706
|
+
schema: simulationSchema,
|
|
707
|
+
initialValues: {},
|
|
708
|
+
security,
|
|
709
|
+
warningSystem,
|
|
710
|
+
runtimeDiagnostics,
|
|
711
|
+
});
|
|
671
712
|
|
|
672
|
-
|
|
673
|
-
const engine = createFormEngine({
|
|
674
|
-
schema: simulationSchema,
|
|
675
|
-
initialValues:
|
|
676
|
-
values && typeof values === 'object' && !Array.isArray(values) ? values : {},
|
|
677
|
-
security,
|
|
678
|
-
warningSystem,
|
|
679
|
-
runtimeDiagnostics,
|
|
680
|
-
});
|
|
713
|
+
engine.eval();
|
|
681
714
|
|
|
682
|
-
|
|
715
|
+
const engineState = engine.getState();
|
|
716
|
+
const baselineValues = deepClone(engineState.values);
|
|
717
|
+
let currentExpression = targetField.calculate;
|
|
718
|
+
let disposed = false;
|
|
683
719
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
null;
|
|
720
|
+
return {
|
|
721
|
+
run: ({ expression: nextExpression, values = {} } = {}) => {
|
|
722
|
+
if (disposed) {
|
|
723
|
+
return createSimulationErrorResult('Calculation preview session has been disposed.');
|
|
724
|
+
}
|
|
690
725
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
726
|
+
warningSystem.clearCollectedWarnings();
|
|
727
|
+
runtimeDiagnostics.length = 0;
|
|
728
|
+
|
|
729
|
+
if (typeof nextExpression === 'string' && nextExpression !== currentExpression) {
|
|
730
|
+
targetField.calculate = nextExpression;
|
|
731
|
+
currentExpression = nextExpression;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
resetEngineValues(engineState.values, baselineValues);
|
|
735
|
+
Object.assign(engineState.values, normalizeSimulationValues(values));
|
|
736
|
+
|
|
737
|
+
try {
|
|
738
|
+
engine.eval();
|
|
739
|
+
|
|
740
|
+
const simulatedResult = engineState.values[fieldDataName];
|
|
741
|
+
const runtimeError =
|
|
742
|
+
runtimeDiagnostics.find((diagnostic) => diagnostic.fieldName === fieldDataName)
|
|
743
|
+
?.message ||
|
|
744
|
+
runtimeDiagnostics[0]?.message ||
|
|
745
|
+
null;
|
|
746
|
+
|
|
747
|
+
return {
|
|
748
|
+
result: simulatedResult === undefined ? null : simulatedResult,
|
|
749
|
+
runtimeError,
|
|
750
|
+
warnings: sanitizeCollectedWarnings(warningSystem.getCollectedWarnings()),
|
|
751
|
+
};
|
|
752
|
+
} catch (error) {
|
|
753
|
+
return createSimulationErrorResult(
|
|
754
|
+
error instanceof Error && error.message
|
|
755
|
+
? error.message
|
|
756
|
+
: 'Unknown calculation simulation error.',
|
|
757
|
+
sanitizeCollectedWarnings(warningSystem.getCollectedWarnings())
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
},
|
|
761
|
+
dispose: () => {
|
|
762
|
+
disposed = true;
|
|
763
|
+
warningSystem.clearCollectedWarnings();
|
|
764
|
+
runtimeDiagnostics.length = 0;
|
|
765
|
+
clearRecord(engineState.values);
|
|
766
|
+
},
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Simulate a CalculatedField expression against form0-core's real evaluation engine.
|
|
772
|
+
*
|
|
773
|
+
* @param {Object} options
|
|
774
|
+
* @param {Object} options.schema
|
|
775
|
+
* @param {string} options.fieldDataName
|
|
776
|
+
* @param {string} options.expression
|
|
777
|
+
* @param {Object} [options.values]
|
|
778
|
+
* @param {Object} [options.security]
|
|
779
|
+
* @returns {{
|
|
780
|
+
* result: unknown,
|
|
781
|
+
* runtimeError: string | null,
|
|
782
|
+
* warnings: Array<{
|
|
783
|
+
* message: string,
|
|
784
|
+
* suggestion: string | null,
|
|
785
|
+
* context: Object | null,
|
|
786
|
+
* fieldContext: Object | null,
|
|
787
|
+
* }>,
|
|
788
|
+
* }}
|
|
789
|
+
*/
|
|
790
|
+
export function simulateCalculationExpression({
|
|
791
|
+
schema,
|
|
792
|
+
fieldDataName,
|
|
793
|
+
expression,
|
|
794
|
+
values = {},
|
|
795
|
+
security = DEFAULT_SECURITY_CONFIG,
|
|
796
|
+
}) {
|
|
797
|
+
const session = createCalculationPreviewSession({
|
|
798
|
+
schema,
|
|
799
|
+
fieldDataName,
|
|
800
|
+
expression,
|
|
801
|
+
security,
|
|
802
|
+
});
|
|
803
|
+
try {
|
|
804
|
+
return session.run({
|
|
805
|
+
expression,
|
|
806
|
+
values,
|
|
807
|
+
});
|
|
808
|
+
} finally {
|
|
809
|
+
session.dispose();
|
|
705
810
|
}
|
|
706
811
|
}
|