cabloy 5.1.209 → 5.1.210

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/.cabloy-version CHANGED
@@ -1 +1 @@
1
- 5.1.209
1
+ 5.1.210
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.1.210
4
+
5
+ ### Features
6
+
7
+ - Add draft recovery.
8
+
3
9
  ## 5.1.209
4
10
 
5
11
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cabloy",
3
- "version": "5.1.209",
3
+ "version": "5.1.210",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "A Node.js fullstack framework",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova",
3
- "version": "5.1.168",
3
+ "version": "5.1.169",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "A vue3 framework with ioc",
6
6
  "keywords": [
@@ -46,7 +46,7 @@
46
46
  },
47
47
  "dependencies": {
48
48
  "zova-core": "^5.1.92",
49
- "zova-suite-a-zova": "^5.1.167"
49
+ "zova-suite-a-zova": "^5.1.168"
50
50
  },
51
51
  "devDependencies": {
52
52
  "clean-package": "^2.2.0",
@@ -5,7 +5,7 @@ import type { IResourceFormFieldOptionsBase } from 'zova-module-a-openapi';
5
5
  import { classes } from 'typestyle';
6
6
  import { BeanControllerBase } from 'zova';
7
7
  import { Controller } from 'zova-module-a-bean';
8
- import { ZFormField } from 'zova-module-a-form';
8
+ import { constFieldBindingSlot, ZFormField } from 'zova-module-a-form';
9
9
 
10
10
  declare module 'zova-module-a-openapi' {
11
11
  export interface IResourceFormFieldRecord {
@@ -37,6 +37,7 @@ export class ControllerFormFieldInput extends BeanControllerBase {
37
37
  return (
38
38
  <ZFormField
39
39
  {...this.$props}
40
+ {...{ [constFieldBindingSlot]: true }}
40
41
  slotDefault={({ propsBucket, props }, $$formField) => {
41
42
  const className = !propsBucket.needHandleBorder
42
43
  ? props.class
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova-module-a-form",
3
- "version": "5.1.52",
3
+ "version": "5.1.53",
4
4
  "gitHead": "09d901d17140a80ee0764211b441cda72fd94663",
5
5
  "description": "",
6
6
  "keywords": [
@@ -12,6 +12,7 @@ import type { ControllerForm } from '../form/controller.jsx';
12
12
  import { schemaRefreshFingerprint } from '../../lib/schemaRefresh.js';
13
13
  import { TypeFormField } from '../../types/form.js';
14
14
  import {
15
+ constFieldBindingSlot,
15
16
  constFieldProps,
16
17
  IFormFieldComponentOptions,
17
18
  IFormFieldOptions,
@@ -105,6 +106,7 @@ export class ControllerFormField<TParentData extends {} = {}> extends BeanContro
105
106
  props.behaviors,
106
107
  !!props.onEffect,
107
108
  !!props.slotDefault,
109
+ props[constFieldBindingSlot] === true,
108
110
  ]);
109
111
  }
110
112
 
@@ -118,12 +120,9 @@ export class ControllerFormField<TParentData extends {} = {}> extends BeanContro
118
120
  return;
119
121
  }
120
122
  const props = this.$props;
121
- // Basic Input owns its binding slot; direct custom slots remain opaque.
123
+ // Only explicitly renderer-owned binding slots qualify; custom slots remain opaque.
122
124
  const inputBindingSlot =
123
- props[constFieldProps] === true &&
124
- (props.render
125
- ? this.$$form.getRenderProvider(props.render)
126
- : this.formProvider.components?.Input) === 'basic-input:formFieldInput';
125
+ props[constFieldProps] === true && props[constFieldBindingSlot] === true;
127
126
  const eligible =
128
127
  !props.sys &&
129
128
  !props.behaviors &&
@@ -28,6 +28,8 @@ export type HTMLInputElementType =
28
28
  | 'tel'
29
29
  | 'email';
30
30
  export const constFieldProps = '$$FieldProps';
31
+ // Internal: the renderer supplies its own form-value binding slot.
32
+ export const constFieldBindingSlot = '$$FieldBindingSlot';
31
33
 
32
34
  export interface IFormFieldScope<TParentData extends {} = {}> {
33
35
  name: string;
@@ -22,7 +22,12 @@ function deferred<T = void>() {
22
22
 
23
23
  // Like basic-pageentry's draftModel harness, execute the current source rather than a
24
24
  // copied refresh algorithm. TSX requires transpileModule instead of native stripping.
25
- function evaluateClass(path: string, name: string, bindings: Record<string, unknown>) {
25
+ function evaluateClass(
26
+ path: string,
27
+ name: string,
28
+ bindings: Record<string, unknown>,
29
+ jsx = ts.JsxEmit.Preserve,
30
+ ) {
26
31
  const source = readFileSync(new URL(path, import.meta.url), 'utf8')
27
32
  .replace(/^import[\s\S]*?;\n/gm, '')
28
33
  .replace(/^\s*@(Controller|UseScope|Use)\([^\n]*\)\s*\n/gm, '');
@@ -32,7 +37,8 @@ function evaluateClass(path: string, name: string, bindings: Record<string, unkn
32
37
  compilerOptions: {
33
38
  target: ts.ScriptTarget.ES2022,
34
39
  module: ts.ModuleKind.ESNext,
35
- jsx: ts.JsxEmit.Preserve,
40
+ jsx,
41
+ jsxFactory: 'createElement',
36
42
  },
37
43
  });
38
44
  assert.deepEqual(
@@ -85,7 +91,14 @@ class ControllerBase {
85
91
  }
86
92
  }
87
93
 
88
- const constFieldProps = Symbol('constFieldProps');
94
+ const fieldTypes = ts.transpileModule(
95
+ readFileSync(new URL('../../src/types/formField.ts', import.meta.url), 'utf8'),
96
+ { compilerOptions: { module: ts.ModuleKind.CommonJS } },
97
+ ).outputText;
98
+ const { constFieldProps, constFieldBindingSlot } = new Function(
99
+ 'exports',
100
+ `${fieldTypes}\nreturn exports;`,
101
+ )({});
89
102
  const commonBindings = {
90
103
  markRaw: (value: unknown) => value,
91
104
  useSelector: (store: any, selector: (state: any) => any) => ({
@@ -95,8 +108,32 @@ const commonBindings = {
95
108
  isNil: (value: unknown) => value === undefined || value === null,
96
109
  schemaRefreshFingerprint,
97
110
  constFieldProps,
111
+ constFieldBindingSlot,
98
112
  };
99
113
 
114
+ const ZFormField = {};
115
+ const ControllerFormFieldInput = evaluateClass(
116
+ '../../../../../../suite/cabloy-basic/modules/basic-input/src/component/formFieldInput/controller.tsx',
117
+ 'ControllerFormFieldInput',
118
+ {
119
+ BeanControllerBase: ControllerBase,
120
+ constFieldBindingSlot,
121
+ ZFormField,
122
+ classes: (...values: unknown[]) => values.filter(Boolean).join(' '),
123
+ createElement: (type: unknown, props: any) => ({ type, props }),
124
+ },
125
+ ts.JsxEmit.React,
126
+ );
127
+
128
+ function inputBindingProps(props: any = {}) {
129
+ const input = new ControllerFormFieldInput();
130
+ input.$props = props;
131
+ const rendered = input.render();
132
+ assert.equal(rendered.type, ZFormField);
133
+ assert.equal(rendered.props[constFieldBindingSlot], true);
134
+ return rendered.props;
135
+ }
136
+
100
137
  const ControllerForm = evaluateClass('../../src/component/form/controller.tsx', 'ControllerForm', {
101
138
  ...commonBindings,
102
139
  BeanControllerFormBase: ControllerBase,
@@ -732,6 +769,7 @@ test(
732
769
  const before: unknown[] = [];
733
770
  const after: unknown[] = [];
734
771
  const owner = await initializedField(runtime, {
772
+ [constFieldBindingSlot]: true,
735
773
  slotDefault: () => 'initial slot',
736
774
  onFieldStateReady: (field: any) => before.push(field.form.getFieldValue(field.name)),
737
775
  });
@@ -755,31 +793,30 @@ test(
755
793
  );
756
794
 
757
795
  test(
758
- 'canonical Basic input binding slots preserve live names across locale refresh',
796
+ 'explicit renderer-owned binding slots preserve live names independently of component selectors',
759
797
  { concurrency: false },
760
798
  async () => {
761
- for (const render of [
762
- undefined,
763
- 'Input',
764
- 'basic-input:formFieldInput',
765
- { $$typeof: 'zova-jsx:component', type: 'basic-input:formFieldInput' },
766
- ]) {
767
- await withRuntime({}, async runtime => {
768
- runtime.form.$$scopeOpenapi.config.formProvider = {
769
- components: { Input: 'basic-input:formFieldInput' },
770
- };
771
- const owner = await initializedField(runtime, {
772
- render,
773
- slotDefault: () => 'binding slot',
799
+ for (const selector of ['basic-input:formFieldInput', 'start-input:formFieldInput']) {
800
+ for (const render of [
801
+ undefined,
802
+ 'Input',
803
+ selector,
804
+ { $$typeof: 'zova-jsx:component', type: selector },
805
+ ]) {
806
+ await withRuntime({}, async runtime => {
807
+ runtime.form.$$scopeOpenapi.config.formProvider = {
808
+ components: { Input: selector },
809
+ };
810
+ const owner = await initializedField(runtime, inputBindingProps({ render }));
811
+ dirty(runtime, 'typed name');
812
+ runtime.select('zh-cn');
813
+ owner.refresh();
814
+ assert.deepEqual(lifecycle(runtime.events), ['update', 'reset', 'validate', 'ready']);
815
+ assert.equal(lastEvent(runtime.events).value, 'typed name');
816
+ assertResetMeta(lastEvent(runtime.events).meta);
817
+ assert.deepEqual(runtime.changed, []);
774
818
  });
775
- dirty(runtime, 'typed name');
776
- runtime.select('zh-cn');
777
- owner.refresh();
778
- assert.deepEqual(lifecycle(runtime.events), ['update', 'reset', 'validate', 'ready']);
779
- assert.equal(lastEvent(runtime.events).value, 'typed name');
780
- assertResetMeta(lastEvent(runtime.events).meta);
781
- assert.deepEqual(runtime.changed, []);
782
- });
819
+ }
783
820
  }
784
821
  },
785
822
  );
@@ -789,9 +826,13 @@ test(
789
826
  { concurrency: false },
790
827
  async () => {
791
828
  for (const props of [
792
- { [constFieldProps]: false, render: 'basic-input:formFieldInput' },
829
+ inputBindingProps({ [constFieldProps]: false, render: 'basic-input:formFieldInput' }),
830
+ { render: 'basic-input:formFieldInput' },
793
831
  { render: 'custom:formFieldInput' },
794
832
  { render: 'Input' },
833
+ {},
834
+ { [constFieldBindingSlot]: false },
835
+ { [constFieldBindingSlot]: 'true' },
795
836
  ]) {
796
837
  await withRuntime({}, async runtime => {
797
838
  runtime.form.$$scopeOpenapi.config.formProvider = {
@@ -812,6 +853,69 @@ test(
812
853
  },
813
854
  );
814
855
 
856
+ test('the input wrapper owns its marker and value/blur binding slot', () => {
857
+ const externalSlot = () => 'external slot';
858
+ const props = inputBindingProps({
859
+ name: 'name',
860
+ [constFieldBindingSlot]: false,
861
+ slotDefault: externalSlot,
862
+ });
863
+ assert.equal(props.name, 'name');
864
+ assert.notEqual(props.slotDefault, externalSlot);
865
+ const writes: unknown[] = [];
866
+ let blurred = false;
867
+ const input = props.slotDefault(
868
+ { propsBucket: { value: 'current', disableNotifyChanged: true }, props: {} },
869
+ {
870
+ setValue: (...args: unknown[]) => writes.push(args),
871
+ handleBlur: () => {
872
+ blurred = true;
873
+ },
874
+ },
875
+ );
876
+ assert.equal(input.type, 'input');
877
+ assert.equal(input.props.value, 'current');
878
+ input.props.onInput({ target: { value: 'edited' } });
879
+ input.props.onBlur();
880
+ assert.deepEqual(writes, [['edited', true]]);
881
+ assert.equal(blurred, true);
882
+ });
883
+
884
+ test(
885
+ 'a missing Input provider does not authorize an unmarked slot',
886
+ { concurrency: false },
887
+ async () => {
888
+ await withRuntime({}, async runtime => {
889
+ const owner = await initializedField(runtime, { slotDefault: () => 'custom slot' });
890
+ dirty(runtime, 'typed name');
891
+ runtime.select('zh-cn');
892
+ owner.refresh();
893
+ assert.deepEqual(lifecycle(runtime.events), ['update', 'reset', 'ready']);
894
+ assert.equal(lastEvent(runtime.events).value, 'baseline');
895
+ });
896
+ },
897
+ );
898
+
899
+ test(
900
+ 'binding-slot ownership changes invalidate the prior refresh configuration',
901
+ { concurrency: false },
902
+ async () => {
903
+ for (const initiallyOwned of [false, true]) {
904
+ await withRuntime({}, async runtime => {
905
+ const owner = await initializedField(runtime, {
906
+ [constFieldBindingSlot]: initiallyOwned,
907
+ slotDefault: () => 'slot',
908
+ });
909
+ dirty(runtime, 'typed name');
910
+ owner.field.$props[constFieldBindingSlot] = !initiallyOwned;
911
+ owner.refresh();
912
+ assert.deepEqual(lifecycle(runtime.events), ['update', 'reset', 'ready']);
913
+ assert.equal(lastEvent(runtime.events).value, 'baseline');
914
+ });
915
+ }
916
+ },
917
+ );
918
+
815
919
  test(
816
920
  'disposal during awaited behavior initialization suppresses refresh and ready callbacks',
817
921
  { concurrency: false },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zova-suite-a-zova",
3
- "version": "5.1.167",
3
+ "version": "5.1.168",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "zova",
6
6
  "license": "MIT",
@@ -15,7 +15,7 @@
15
15
  "zova-module-a-behaviors": "^5.1.22",
16
16
  "zova-module-a-command": "^5.1.35",
17
17
  "zova-module-a-fetch": "^5.1.25",
18
- "zova-module-a-form": "^5.1.52",
18
+ "zova-module-a-form": "^5.1.53",
19
19
  "zova-module-a-icon": "^5.1.27",
20
20
  "zova-module-a-interceptor": "^5.1.31",
21
21
  "zova-module-a-logger": "^5.1.28",