@neeloong/form 0.27.0 → 0.28.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/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @neeloong/form v0.27.0
2
+ * @neeloong/form v0.28.0
3
3
  * (c) 2024-2026 Fierflame
4
4
  * @license Apache-2.0
5
5
  */
@@ -410,6 +410,31 @@ type Ref = {
410
410
  [k: string]: Ref | undefined;
411
411
  };
412
412
 
413
+ /** @import { Schema } from '../Schema.types.mjs' */
414
+ /**
415
+ * @template [T=any]
416
+ * @template [M=any]
417
+ * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
418
+ * @extends {Store<T, M, S>}
419
+ */
420
+ declare class BindObjectStore<T = any, M = any, S extends {
421
+ [x: string]: Schema.State;
422
+ } = {
423
+ [x: string]: Schema.State;
424
+ }> extends Store<T, M, S> {
425
+ /**
426
+ * @param {Schema<any, Object.<string, Schema.State>>} schema 数据结构模式
427
+ * @param {Store<T, M, S>} store
428
+ */
429
+ constructor(schema: Schema<any, {
430
+ [x: string]: Schema.State;
431
+ }>, store: Store<T, M, S>);
432
+ [Symbol.iterator](): Generator<[string, Store<any, any, {
433
+ [x: string]: Schema.State;
434
+ }>], void, unknown>;
435
+ #private;
436
+ }
437
+
413
438
  /** @import { Ref } from './ref.mjs' */
414
439
  /** @import { Schema } from '../Schema.types.mjs' */
415
440
  /** @import { StoreLayout } from '../StoreLayout.types.mjs' */
@@ -450,7 +475,7 @@ declare class Store<T = any, M = any, S extends {
450
475
  new (...p: ConstructorParameters<typeof Store>): Store;
451
476
  }): void;
452
477
  /**
453
- * @param {Schema.Field<M, S>} schema 字段的 Schema 定义
478
+ * @param {Schema.Field<M, S> | Store<T,M,S>} schema 字段的 Schema 定义
454
479
  * @param {object} [options] 可选配置
455
480
  * @param {Store?} [options.parent]
456
481
  * @param {Partial<S>?} [options.states]
@@ -486,7 +511,7 @@ declare class Store<T = any, M = any, S extends {
486
511
  *
487
512
  * @param {((value: T?, index: any, store: Store) => void)?} [options.onUpdate]
488
513
  */
489
- constructor(schema: Schema.Field<M, S>, { null: isNull, ref, default: defaultValue, setValue, convert, onUpdate, states, validator, validators, index, size, new: isNew, parent: parentNode, hidden, clearable, required, disabled, readonly, removable, label, description, placeholder, min, max, step, minLength, maxLength, pattern, values }?: {
514
+ constructor(schema: Schema.Field<M, S> | Store<T, M, S>, { null: isNull, ref, default: defaultValue, setValue, convert, onUpdate, states, validator, validators, index, size, new: isNew, parent: parentNode, hidden, clearable, required, disabled, readonly, removable, label, description, placeholder, min, max, step, minLength, maxLength, pattern, values }?: {
490
515
  parent?: Store<any, any, {
491
516
  [x: string]: Schema.State;
492
517
  }> | null | undefined;
@@ -540,11 +565,13 @@ declare class Store<T = any, M = any, S extends {
540
565
  convert?: ((value: any) => any) | null | undefined;
541
566
  onUpdate?: ((value: T | null, index: any, store: Store) => void) | null | undefined;
542
567
  });
568
+ get schema(): Schema.Field<M, S>;
543
569
  /**
544
570
  * 触发事件并通知监听器
545
571
  * @template {keyof Schema.Events} K
546
572
  * @param {K} event
547
573
  * @param {Schema.Events[K]} value
574
+ * @returns {boolean}
548
575
  */
549
576
  emit<K extends keyof Schema.Events>(event: K, value: Schema.Events[K]): boolean;
550
577
  /**
@@ -560,7 +587,6 @@ declare class Store<T = any, M = any, S extends {
560
587
  /** 存储类类别,继承的自定义类需要设置自定义的此只读属性 */
561
588
  get kind(): string;
562
589
  get ref(): Ref;
563
- schema: Schema.Field<M, S>;
564
590
  get states(): S | null;
565
591
  get layout(): StoreLayout.Field<any>;
566
592
  /** @param {any} [value] @returns {any} */
@@ -700,17 +726,19 @@ declare class Store<T = any, M = any, S extends {
700
726
  * @template [M=any]
701
727
  * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
702
728
  * @param {Schema<M, S>} schema 数据结构模式
729
+ * @param {AbortSignal} [signal]
730
+ * @returns {BindObjectStore}
703
731
  */
704
732
  bindObject<M_1 = any, S_1 extends {
705
733
  [x: string]: Schema.State;
706
734
  } = {
707
735
  [x: string]: Schema.State;
708
- }>(schema: Schema<M_1, S_1>): () => void;
736
+ }>(schema: Schema<M_1, S_1>, signal?: AbortSignal): BindObjectStore;
737
+ /** 内容是否已改变 */
738
+ get changed(): boolean;
709
739
  set value(v: T | null);
710
740
  /** 字段当前值 */
711
741
  get value(): T | null;
712
- /** 内容是否已改变 */
713
- get changed(): boolean;
714
742
  /** 重置数据 */
715
743
  reset(value?: unknown, isNew?: boolean): void;
716
744
  /**
package/index.full.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @neeloong/form v0.27.0
2
+ * @neeloong/form v0.28.0
3
3
  * (c) 2024-2026 Fierflame
4
4
  * @license Apache-2.0
5
5
  */
@@ -716,7 +716,7 @@
716
716
 
717
717
 
718
718
  /** @type {{new(...p: ConstructorParameters<typeof Store>): Store}?} */
719
- let ObjectStore$1 = null;
719
+ let ObjectStore$2 = null;
720
720
  /** @type {{new(...p: ConstructorParameters<typeof Store>): ArrayStore}?} */
721
721
  let ArrayStoreClass = null;
722
722
  /** @type {Record<string, {new(...p: ConstructorParameters<typeof Store>): Store}?>} */
@@ -741,14 +741,14 @@
741
741
  const C = TypeStores[type];
742
742
  if (C) { Class = C; }
743
743
  } else if (type && typeof type === 'object') {
744
- if (ObjectStore$1) { Class = ObjectStore$1; }
744
+ if (ObjectStore$2) { Class = ObjectStore$2; }
745
745
  }
746
746
  return new Class(schema, options);
747
747
  }
748
748
 
749
749
  /** @param {{new(...p: ConstructorParameters<typeof Store>): Store}} Class */
750
750
  function setObjectStore(Class) {
751
- ObjectStore$1 = Class;
751
+ ObjectStore$2 = Class;
752
752
  }
753
753
 
754
754
  /** @param {{new(...p: ConstructorParameters<typeof Store>): ArrayStore}} Class */
@@ -870,6 +870,52 @@
870
870
  return (value) => structuredClone(def(store, value));
871
871
  }
872
872
 
873
+ /** @import { Schema } from '../Schema.types.mjs' */
874
+
875
+ /**
876
+ * @template [T=any]
877
+ * @template [M=any]
878
+ * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
879
+ * @extends {Store<T, M, S>}
880
+ */
881
+ class BindObjectStore extends Store {
882
+
883
+ get kind() { return 'object'; }
884
+ /** @type {Record<string, Store>} */
885
+ #children = Object.create(null);
886
+ *[Symbol.iterator]() { yield* Object.entries(this.#children); }
887
+ /**
888
+ *
889
+ * @param {string | number} key
890
+ * @returns {Store?}
891
+ */
892
+ child(key) { return this.#children[key] || null; }
893
+ /**
894
+ * @param {Schema<any, Object.<string, Schema.State>>} schema 数据结构模式
895
+ * @param {Store<T, M, S>} store
896
+ */
897
+ constructor(schema, store) {
898
+ super(store);
899
+ const children = this.#children;
900
+ for (const [index, field] of Object.entries(schema)) {
901
+ const bindStore = create(field, {
902
+ index, parent: this,
903
+ /** @param {*} value @param {*} index @param {Store} store */
904
+ onUpdate: (value, index, store) => {
905
+ if (store !== children[index]) { return; }
906
+ const val = this.value ?? null;
907
+ if (typeof val !== 'object' || Array.isArray(val)) { return; }
908
+ // @ts-ignore
909
+ this.value = { ...val, [index]: value };
910
+ },
911
+ });
912
+ children[index] = bindStore;
913
+ }
914
+ }
915
+ }
916
+ // @ts-ignore
917
+ setObjectStore(ObjectStore);
918
+
873
919
  /** @import { Ref } from './ref.mjs' */
874
920
  /** @import { Schema } from '../Schema.types.mjs' */
875
921
  /** @import { StoreLayout } from '../StoreLayout.types.mjs' */
@@ -881,6 +927,12 @@
881
927
  * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
882
928
  */
883
929
  class Store {
930
+ /** @type {Store<T, M, S>?} */
931
+ #originStore = null;
932
+
933
+ /** @type {Schema.Field<M, S>} 字段的 Schema 定义 */
934
+ #schema;
935
+ get schema() { return this.#schema; }
884
936
  /** @type {Map<string, Set<(value: any, store: any) => void | boolean | null>>} */
885
937
  #events = new Map();
886
938
  /**
@@ -888,8 +940,11 @@
888
940
  * @template {keyof Schema.Events} K
889
941
  * @param {K} event
890
942
  * @param {Schema.Events[K]} value
943
+ * @returns {boolean}
891
944
  */
892
945
  emit(event, value) {
946
+ const originStore = this.#originStore;
947
+ if (originStore) { return originStore.emit(event, value); }
893
948
  const key = typeof event === 'number' ? String(event) : event;
894
949
  const events = this.#events;
895
950
  let canceled = false;
@@ -906,6 +961,8 @@
906
961
  * @returns {() => void}
907
962
  */
908
963
  listen(event, listener) {
964
+ const originStore = this.#originStore;
965
+ if (originStore) { return originStore.listen(event, p => listener.call(this, p, this)); }
909
966
  const fn = listener.bind(this);
910
967
  const events = this.#events;
911
968
  const key = typeof event === 'number' ? String(event) : event;
@@ -946,7 +1003,7 @@
946
1003
  #ref = null;
947
1004
  get ref() { return this.#ref || createRef(this); }
948
1005
  /**
949
- * @param {Schema.Field<M, S>} schema 字段的 Schema 定义
1006
+ * @param {Schema.Field<M, S> | Store<T,M,S>} schema 字段的 Schema 定义
950
1007
  * @param {object} [options] 可选配置
951
1008
  * @param {Store?} [options.parent]
952
1009
  * @param {Partial<S>?} [options.states]
@@ -990,7 +1047,74 @@
990
1047
  hidden, clearable, required, disabled, readonly, removable,
991
1048
  label, description, placeholder, min, max, step, minLength, maxLength, pattern, values: values$1
992
1049
  } = {}) {
993
- this.schema = schema;
1050
+ if (schema instanceof Store) {
1051
+ this.#originStore = schema;
1052
+ this.#schema = schema.#schema;
1053
+ this.#null = schema.#null;
1054
+ this.#ref = schema.#ref;
1055
+ this.#states = schema.#states;
1056
+ this.#layout = schema.#layout;
1057
+ this.#createDefault = schema.#createDefault;
1058
+ this.#setValue = schema.#setValue;
1059
+ this.#convert = schema.#convert;
1060
+ this.#onUpdate = schema.#onUpdate;
1061
+ this.#parent = schema.#parent;
1062
+ this.#root = schema.#root;
1063
+ this.#type = schema.#type;
1064
+ this.#meta = schema.#meta;
1065
+ this.#component = schema.#component;
1066
+ this.#selfLoading = schema.#selfLoading;
1067
+ this.#loading = schema.#loading;
1068
+ this.#size = schema.#size;
1069
+ this.#index = schema.#index;
1070
+ this.#creatable = schema.#creatable;
1071
+ this.#immutable = schema.#immutable;
1072
+ this.#new = schema.#new;
1073
+ this.#selfNew = schema.#selfNew;
1074
+ this.#selfHidden = schema.#selfHidden;
1075
+ this.#hidden = schema.#hidden;
1076
+ this.#selfClearable = schema.#selfClearable;
1077
+ this.#clearable = schema.#clearable;
1078
+ this.#selfRequired = schema.#selfRequired;
1079
+ this.#required = schema.#required;
1080
+ this.#selfDisabled = schema.#selfDisabled;
1081
+ this.#disabled = schema.#disabled;
1082
+ this.#selfReadonly = schema.#selfReadonly;
1083
+ this.#readonly = schema.#readonly;
1084
+ this.#selfRemovable = schema.#selfRemovable;
1085
+ this.#removable = schema.#removable;
1086
+ this.#selfLabel = schema.#selfLabel;
1087
+ this.#label = schema.#label;
1088
+ this.#selfDescription = schema.#selfDescription;
1089
+ this.#description = schema.#description;
1090
+ this.#selfPlaceholder = schema.#selfPlaceholder;
1091
+ this.#placeholder = schema.#placeholder;
1092
+ this.#selfMin = schema.#selfMin;
1093
+ this.#min = schema.#min;
1094
+ this.#selfMax = schema.#selfMax;
1095
+ this.#max = schema.#max;
1096
+ this.#selfStep = schema.#selfStep;
1097
+ this.#step = schema.#step;
1098
+ this.#selfMinLength = schema.#selfMinLength;
1099
+ this.#minLength = schema.#minLength;
1100
+ this.#selfMaxLength = schema.#selfMaxLength;
1101
+ this.#maxLength = schema.#maxLength;
1102
+ this.#selfPattern = schema.#selfPattern;
1103
+ this.#pattern = schema.#pattern;
1104
+ this.#selfValues = schema.#selfValues;
1105
+ this.#values = schema.#values;
1106
+ this.#errors = schema.#errors;
1107
+ this.#validatorResult = schema.#validatorResult;
1108
+ this.#changed = schema.#changed;
1109
+ this.#blurred = schema.#blurred;
1110
+ this.#cancelChange = schema.#cancelChange;
1111
+ this.#cancelBlur = schema.#cancelBlur;
1112
+ this.#set = schema.#set;
1113
+ this.#initValue = schema.#initValue;
1114
+ this.#value = schema.#value;
1115
+ return;
1116
+ }
1117
+ this.#schema = schema;
994
1118
  const parent = parentNode instanceof Store ? parentNode : null;
995
1119
  if (parent) {
996
1120
  this.#parent = parent;
@@ -1118,6 +1242,7 @@
1118
1242
  /** @type {StoreLayout.Field<any>} */
1119
1243
  #layout;
1120
1244
  get layout() { return this.#layout; }
1245
+ /** @type {(value?: any) => unknown} */
1121
1246
  #createDefault;
1122
1247
  /** @param {any} [value] @returns {any} */
1123
1248
  createDefault(value) { return this.#createDefault(value); }
@@ -1399,40 +1524,26 @@
1399
1524
  #initValue = new exports.Signal.State(/** @type {T?} */(null));
1400
1525
  #value = new exports.Signal.State(this.#initValue.get());
1401
1526
 
1527
+ /** @type {Set<Store>} */
1528
+ #subBindStores = new Set();
1402
1529
  /**
1403
1530
  * @template [M=any]
1404
1531
  * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
1405
1532
  * @param {Schema<M, S>} schema 数据结构模式
1533
+ * @param {AbortSignal} [signal]
1534
+ * @returns {BindObjectStore}
1406
1535
  */
1407
- bindObject(schema) {
1408
- const bindStores = this.#bindStores;
1409
- /** @type {Store[]} */
1410
- const list = [];
1411
- for (const [index, field] of Object.entries(schema)) {
1412
- const bindStore = create(field, {
1413
- index, parent: this,
1414
- /** @param {*} value @param {*} currentIndex @param {Store} store */
1415
- onUpdate: (value, currentIndex, store) => {
1416
- if (index !== currentIndex) { return; }
1417
- if (bindStores.has(store)) { return; }
1418
- const val = this.#value ?? null;
1419
- if (typeof val !== 'object' || Array.isArray(val)) { return }
1420
- // @ts-ignore
1421
- this.value = { ...val, [currentIndex]: value };
1422
- },
1423
- });
1424
- list.push(bindStore);
1425
- bindStores.set(bindStore, index);
1426
- }
1536
+ bindObject(schema, signal) {
1537
+ const originStore = this.#originStore;
1538
+ if (originStore) { return originStore.bindObject(schema, signal); }
1539
+ const store = new BindObjectStore(schema, this);
1540
+ if (signal?.aborted) { return store; }
1541
+ const subBindStores = this.#subBindStores;
1542
+ subBindStores.add(store);
1543
+ signal?.addEventListener('abort', () => subBindStores.delete(store));
1427
1544
  this.#requestUpdate();
1428
- return () => {
1429
- for (const bindStore of list) {
1430
- bindStores.delete(bindStore);
1431
- }
1432
- };
1545
+ return store;
1433
1546
  }
1434
- /** @type {Map<Store, string>} */
1435
- #bindStores = new Map();
1436
1547
 
1437
1548
  /** 内容是否已改变 */
1438
1549
  get changed() { return !Object.is(this.#value.get(), this.#initValue.get()); }
@@ -1440,6 +1551,8 @@
1440
1551
  /** 字段当前值 */
1441
1552
  get value() { return this.#value.get(); }
1442
1553
  set value(v) {
1554
+ const originStore = this.#originStore;
1555
+ if (originStore) { originStore.value = v; return; }
1443
1556
  const newValue = this.#setValue?.(v);
1444
1557
  const val = newValue === undefined ? v : newValue;
1445
1558
  this.#value.set(val);
@@ -1460,6 +1573,8 @@
1460
1573
  }
1461
1574
  /** 重置数据 */
1462
1575
  reset(value = this.#set ? this.#initValue.get() : this.#createDefault(), isNew = this.#selfNew.get()) {
1576
+ const originStore = this.#originStore;
1577
+ if (originStore) { originStore.reset(value, isNew); return; }
1463
1578
  this.#reset(value, Boolean(isNew));
1464
1579
  }
1465
1580
  /**
@@ -1476,11 +1591,10 @@
1476
1591
  this.#cancelBlur();
1477
1592
  this.#set = true;
1478
1593
  if (!value || typeof value !== 'object') {
1479
- for (const [, field] of this) {
1480
- field.#reset(null, false);
1481
- }
1482
- for (const [field] of this.#bindStores) {
1483
- field.#reset(null, false);
1594
+ for (const bind of [this, ...this.#subBindStores]) {
1595
+ for (const [, field] of bind) {
1596
+ field.#reset(null, false);
1597
+ }
1484
1598
  }
1485
1599
  this.#value.set(value);
1486
1600
  this.#initValue.set(value);
@@ -1489,11 +1603,10 @@
1489
1603
  }
1490
1604
  /** @type {*} */
1491
1605
  const newValues = Array.isArray(value) ? [...value] : { ...value };
1492
- for (const [key, field] of this) {
1493
- newValues[key] = field.#reset(Object.hasOwn(newValues, key) ? newValues[key] : undefined, false);
1494
- }
1495
- for (const [field, key] of this.#bindStores) {
1496
- newValues[key] = field.#reset(Object.hasOwn(newValues, key) ? newValues[key] : undefined, false);
1606
+ for (const bind of [this, ...this.#subBindStores]) {
1607
+ for (const [key, field] of bind) {
1608
+ newValues[key] = field.#reset(Object.hasOwn(newValues, key) ? newValues[key] : undefined, false);
1609
+ }
1497
1610
  }
1498
1611
  this.#value.set(newValues);
1499
1612
  this.#initValue.set(newValues);
@@ -1527,23 +1640,16 @@
1527
1640
  // @ts-ignore
1528
1641
  let newValues = Array.isArray(val) ? [...val] : { ...val };
1529
1642
  let updated = false;
1530
- for (const [key, field] of this) {
1531
- // @ts-ignore
1532
- const data = Object.hasOwn(val, key) ? val[key] : undefined;
1533
- const newData = field.#toUpdate(data);
1534
- if (Object.is(data, newData)) { continue; }
1535
- // @ts-ignore
1536
- newValues[key] = newData;
1537
- updated = true;
1538
- }
1539
- for (const [field, key] of this.#bindStores) {
1540
- // @ts-ignore
1541
- const data = Object.hasOwn(val, key) ? val[key] : undefined;
1542
- const newData = field.#toUpdate(data);
1543
- if (Object.is(data, newData)) { continue; }
1544
- // @ts-ignore
1545
- newValues[key] = newData;
1546
- updated = true;
1643
+ for (const bind of [this,...this.#subBindStores]) {
1644
+ for (const [key, field] of bind) {
1645
+ // @ts-ignore
1646
+ const data = Object.hasOwn(val, key) ? val[key] : undefined;
1647
+ const newData = field.#toUpdate(data);
1648
+ if (Object.is(data, newData)) { continue; }
1649
+ // @ts-ignore
1650
+ newValues[key] = newData;
1651
+ updated = true;
1652
+ }
1547
1653
  }
1548
1654
  if (updated) {
1549
1655
  val = newValues;
@@ -1577,6 +1683,7 @@
1577
1683
  */
1578
1684
  validate(path) {
1579
1685
  if (path === true) {
1686
+ if (this.#originStore) { return Promise.resolve(null); }
1580
1687
  return Promise.all([this.#validatorResult.get(), this.#changed(), this.#blurred()])
1581
1688
  .then(v => {
1582
1689
  const errors = v.flat();
@@ -1584,7 +1691,7 @@
1584
1691
  });
1585
1692
  }
1586
1693
  const selfPath = Array.isArray(path) ? path : [];
1587
- if (this.#hidden.get()) { return Promise.resolve([]); }
1694
+ if (!this.#originStore && this.#hidden.get()) { return Promise.resolve([]); }
1588
1695
  const list = [this.validate(true).then(errors => {
1589
1696
  if (!errors?.length) { return []; }
1590
1697
  return [{ path: [...selfPath], store: /** @type {Store} */(this), errors }];
@@ -1592,8 +1699,8 @@
1592
1699
  for (const [key, field] of this) {
1593
1700
  list.push(field.validate([...selfPath, key]));
1594
1701
  }
1595
- for (const [field, key] of this.#bindStores) {
1596
- list.push(field.validate([...selfPath, key]));
1702
+ for (const sub of this.#subBindStores) {
1703
+ list.push(sub.validate(selfPath));
1597
1704
  }
1598
1705
  return Promise.all(list).then(v => v.flat());
1599
1706
  }
@@ -1607,7 +1714,7 @@
1607
1714
  * @template {Object.<string, Schema.State>} [S=Object.<string, Schema.State>]
1608
1715
  * @extends {Store<T, M, S>}
1609
1716
  */
1610
- class ObjectStore extends Store {
1717
+ let ObjectStore$1 = class ObjectStore extends Store {
1611
1718
  get kind() { return 'object'; }
1612
1719
  /** @type {Record<string, Store>} */
1613
1720
  #children;
@@ -1660,9 +1767,9 @@
1660
1767
  }
1661
1768
  this.#children = children;
1662
1769
  }
1663
- }
1770
+ };
1664
1771
  // @ts-ignore
1665
- setObjectStore(ObjectStore);
1772
+ setObjectStore(ObjectStore$1);
1666
1773
 
1667
1774
  /** @import { Schema } from '../Schema.types.mjs' */
1668
1775
 
@@ -5332,7 +5439,7 @@
5332
5439
  if (list instanceof ArrayStore) {
5333
5440
  return renderArray(parent, next, list, env, r, layout.sort);
5334
5441
  }
5335
- if (list instanceof ObjectStore) {
5442
+ if (list instanceof ObjectStore$1) {
5336
5443
  return renderObject(parent, next, list, env, r, layout.sort);
5337
5444
  }
5338
5445
  if (typeof list === 'function') {
@@ -7066,7 +7173,7 @@
7066
7173
 
7067
7174
  exports.ArrayStore = ArrayStore;
7068
7175
  exports.Layout = index;
7069
- exports.ObjectStore = ObjectStore;
7176
+ exports.ObjectStore = ObjectStore$1;
7070
7177
  exports.Store = Store;
7071
7178
  exports.effect = effect;
7072
7179
  exports.render = render;