phecda-core 5.2.0 → 5.3.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/dist/index.d.mts CHANGED
@@ -144,6 +144,15 @@ declare abstract class Base {
144
144
  }
145
145
 
146
146
  declare const _createErrorMessage: (type: string, { property, meta }: RuleArgs) => string;
147
- declare function validate(model: Construct, data: any, collectErrors?: boolean, createErrMsg?: (type: string, { property, meta }: RuleArgs) => string, equalFn?: (a: any, b: any) => boolean): Promise<any[]>;
147
+ interface ExtractedRule {
148
+ property: string;
149
+ designType: any;
150
+ mergedMeta: any;
151
+ validate: (value: any) => Promise<string | undefined>;
152
+ }
153
+ declare function extractRules(model: Construct, createErrMsg?: (type: string, { property, meta }: RuleArgs) => string, equalFn?: (a: any, b: any) => boolean, parentPath?: string): ExtractedRule[];
154
+ declare function extractDataByRules(data: any, rules: ExtractedRule[]): any;
155
+ declare function validateWithRules(data: any, rules: ExtractedRule[], collectErrors?: boolean): Promise<string[]>;
156
+ declare function validate(model: Construct, data: any, collectErrors?: boolean, createErrMsg?: (type: string, { property, meta }: RuleArgs) => string, equalFn?: (a: any, b: any) => boolean): Promise<string[]>;
148
157
 
149
- export { type AbConstruct, Assign, Base, CLEAR_KEY, type ClassValue, Clear, Const, type Construct, DataMap, Doc, Effect, Empty, Enum, Err, type Events, Expose, Global, If, Init, Inject, type InjectData, Injectable, Isolate, Max, Min, type NameSpace, Nested, OneOf, Optional, PHECDA_KEY, type Phecda, Pipeline, Provide, Required, Rule, type RuleArgs, SHARE_KEY, Storage, type StorageParam, Tag, Unique, Unmount, Watcher, type WatcherParam, _createErrorMessage, activeInstance, addDecoToClass, functionToClass, get, getInject, getMergedMeta, getMeta, getMetaKey, getMetaParams, getOwnMeta, getOwnMetaKey, getOwnMetaParams, getPhecdaFromTarget, getTag, init, invoke, invokeInit, invokeUnmount, isAsyncFunc, isPhecda, objectToClass, omit, override, partial, pick, set, setInject, setMeta, validate, wait };
158
+ export { type AbConstruct, Assign, Base, CLEAR_KEY, type ClassValue, Clear, Const, type Construct, DataMap, Doc, Effect, Empty, Enum, Err, type Events, Expose, type ExtractedRule, Global, If, Init, Inject, type InjectData, Injectable, Isolate, Max, Min, type NameSpace, Nested, OneOf, Optional, PHECDA_KEY, type Phecda, Pipeline, Provide, Required, Rule, type RuleArgs, SHARE_KEY, Storage, type StorageParam, Tag, Unique, Unmount, Watcher, type WatcherParam, _createErrorMessage, activeInstance, addDecoToClass, extractDataByRules, extractRules, functionToClass, get, getInject, getMergedMeta, getMeta, getMetaKey, getMetaParams, getOwnMeta, getOwnMetaKey, getOwnMetaParams, getPhecdaFromTarget, getTag, init, invoke, invokeInit, invokeUnmount, isAsyncFunc, isPhecda, objectToClass, omit, override, partial, pick, set, setInject, setMeta, validate, validateWithRules, wait };
package/dist/index.d.ts CHANGED
@@ -144,6 +144,15 @@ declare abstract class Base {
144
144
  }
145
145
 
146
146
  declare const _createErrorMessage: (type: string, { property, meta }: RuleArgs) => string;
147
- declare function validate(model: Construct, data: any, collectErrors?: boolean, createErrMsg?: (type: string, { property, meta }: RuleArgs) => string, equalFn?: (a: any, b: any) => boolean): Promise<any[]>;
147
+ interface ExtractedRule {
148
+ property: string;
149
+ designType: any;
150
+ mergedMeta: any;
151
+ validate: (value: any) => Promise<string | undefined>;
152
+ }
153
+ declare function extractRules(model: Construct, createErrMsg?: (type: string, { property, meta }: RuleArgs) => string, equalFn?: (a: any, b: any) => boolean, parentPath?: string): ExtractedRule[];
154
+ declare function extractDataByRules(data: any, rules: ExtractedRule[]): any;
155
+ declare function validateWithRules(data: any, rules: ExtractedRule[], collectErrors?: boolean): Promise<string[]>;
156
+ declare function validate(model: Construct, data: any, collectErrors?: boolean, createErrMsg?: (type: string, { property, meta }: RuleArgs) => string, equalFn?: (a: any, b: any) => boolean): Promise<string[]>;
148
157
 
149
- export { type AbConstruct, Assign, Base, CLEAR_KEY, type ClassValue, Clear, Const, type Construct, DataMap, Doc, Effect, Empty, Enum, Err, type Events, Expose, Global, If, Init, Inject, type InjectData, Injectable, Isolate, Max, Min, type NameSpace, Nested, OneOf, Optional, PHECDA_KEY, type Phecda, Pipeline, Provide, Required, Rule, type RuleArgs, SHARE_KEY, Storage, type StorageParam, Tag, Unique, Unmount, Watcher, type WatcherParam, _createErrorMessage, activeInstance, addDecoToClass, functionToClass, get, getInject, getMergedMeta, getMeta, getMetaKey, getMetaParams, getOwnMeta, getOwnMetaKey, getOwnMetaParams, getPhecdaFromTarget, getTag, init, invoke, invokeInit, invokeUnmount, isAsyncFunc, isPhecda, objectToClass, omit, override, partial, pick, set, setInject, setMeta, validate, wait };
158
+ export { type AbConstruct, Assign, Base, CLEAR_KEY, type ClassValue, Clear, Const, type Construct, DataMap, Doc, Effect, Empty, Enum, Err, type Events, Expose, type ExtractedRule, Global, If, Init, Inject, type InjectData, Injectable, Isolate, Max, Min, type NameSpace, Nested, OneOf, Optional, PHECDA_KEY, type Phecda, Pipeline, Provide, Required, Rule, type RuleArgs, SHARE_KEY, Storage, type StorageParam, Tag, Unique, Unmount, Watcher, type WatcherParam, _createErrorMessage, activeInstance, addDecoToClass, extractDataByRules, extractRules, functionToClass, get, getInject, getMergedMeta, getMeta, getMetaKey, getMetaParams, getOwnMeta, getOwnMetaKey, getOwnMetaParams, getPhecdaFromTarget, getTag, init, invoke, invokeInit, invokeUnmount, isAsyncFunc, isPhecda, objectToClass, omit, override, partial, pick, set, setInject, setMeta, validate, validateWithRules, wait };
package/dist/index.js CHANGED
@@ -59,6 +59,8 @@ __export(index_exports, {
59
59
  _createErrorMessage: () => _createErrorMessage,
60
60
  activeInstance: () => activeInstance,
61
61
  addDecoToClass: () => addDecoToClass,
62
+ extractDataByRules: () => extractDataByRules,
63
+ extractRules: () => extractRules,
62
64
  functionToClass: () => functionToClass,
63
65
  get: () => get,
64
66
  getInject: () => getInject,
@@ -86,6 +88,7 @@ __export(index_exports, {
86
88
  setInject: () => setInject,
87
89
  setMeta: () => setMeta,
88
90
  validate: () => validate,
91
+ validateWithRules: () => validateWithRules,
89
92
  wait: () => wait
90
93
  });
91
94
  module.exports = __toCommonJS(index_exports);
@@ -288,23 +291,21 @@ function isAsyncFunc(fn) {
288
291
  __name(isAsyncFunc, "isAsyncFunc");
289
292
  function invoke(instance, key, ...params) {
290
293
  const metaKeys = getMetaKey(instance);
291
- const names = [];
292
294
  return Promise.allSettled(metaKeys.map((k) => {
293
- return getMeta(instance, k);
294
- }).flat().filter((item) => {
295
- if (typeof item[key] === "function") {
295
+ const names = [];
296
+ const meta = getMeta(instance, k);
297
+ return meta.filter((item) => {
296
298
  if (item.__NAME__) {
297
- if (names.includes(item.__NAME__)) {
298
- return false;
299
- }
299
+ if (names.includes(item.__NAME__)) return false;
300
300
  names.push(item.__NAME__);
301
301
  return true;
302
302
  } else {
303
303
  return true;
304
304
  }
305
- } else {
306
- return false;
307
- }
305
+ });
306
+ }).flat().filter((item) => {
307
+ if (typeof item[key] === "function") return true;
308
+ else return false;
308
309
  }).map((item) => item[key](instance, ...params))).then((res) => {
309
310
  res.filter((item) => item.status === "rejected").forEach((item) => {
310
311
  console.error(item.reason);
@@ -752,6 +753,70 @@ Base = _ts_decorate([
752
753
  ], Base);
753
754
 
754
755
  // src/validate.ts
756
+ function parsePath(path) {
757
+ if (!path) return [];
758
+ return path.split(".").flatMap((token) => {
759
+ if (!token) return [];
760
+ if (token.endsWith("[]")) return [
761
+ token.slice(0, -2),
762
+ "[]"
763
+ ];
764
+ return [
765
+ token
766
+ ];
767
+ });
768
+ }
769
+ __name(parsePath, "parsePath");
770
+ function collectConcretePathsByTokens(data, tokens) {
771
+ if (!tokens.length) return [
772
+ {
773
+ keys: [],
774
+ value: data
775
+ }
776
+ ];
777
+ const walk = /* @__PURE__ */ __name((node, tokenIndex, keys) => {
778
+ if (tokenIndex >= tokens.length) return [
779
+ {
780
+ keys,
781
+ value: node
782
+ }
783
+ ];
784
+ const token = tokens[tokenIndex];
785
+ if (token === "[]") {
786
+ if (!Array.isArray(node)) return [];
787
+ return node.flatMap((item, idx) => walk(item, tokenIndex + 1, [
788
+ ...keys,
789
+ idx
790
+ ]));
791
+ }
792
+ if (node === null || node === void 0) return [];
793
+ const nodeType = typeof node;
794
+ if (nodeType !== "object" && nodeType !== "function") return [];
795
+ return walk(node[token], tokenIndex + 1, [
796
+ ...keys,
797
+ token
798
+ ]);
799
+ }, "walk");
800
+ return walk(data, 0, []);
801
+ }
802
+ __name(collectConcretePathsByTokens, "collectConcretePathsByTokens");
803
+ function setByConcreteKeys(target, keys, value) {
804
+ if (!keys.length) return value;
805
+ let node = target;
806
+ for (let i = 0; i < keys.length; i++) {
807
+ const key = keys[i];
808
+ const isLast = i === keys.length - 1;
809
+ if (isLast) {
810
+ node[key] = value;
811
+ break;
812
+ }
813
+ const nextKey = keys[i + 1];
814
+ if (node[key] === void 0) node[key] = typeof nextKey === "number" ? [] : {};
815
+ node = node[key];
816
+ }
817
+ return target;
818
+ }
819
+ __name(setByConcreteKeys, "setByConcreteKeys");
755
820
  var _createErrorMessage = /* @__PURE__ */ __name((type, { property, meta }) => {
756
821
  switch (type) {
757
822
  case "const":
@@ -789,128 +854,197 @@ function isObject(value) {
789
854
  return Object.prototype.toString.call(value) === "[object Object]";
790
855
  }
791
856
  __name(isObject, "isObject");
792
- async function validate(model, data, collectErrors = false, createErrMsg = _createErrorMessage, equalFn = (a, b) => a === b) {
793
- async function parse(model2, data2) {
794
- const errors = [];
795
- if (!isObject(data2)) {
796
- errors.push("data must be an object");
797
- return errors;
798
- }
799
- for (const key of getMetaKey(model2)) {
800
- const meta = getMergedMeta(model2, key);
801
- const property = key === SHARE_KEY ? "" : key;
802
- const type = property === "" ? model2 : Reflect.getMetadata("design:type", model2.prototype, key);
803
- const { rules = [], nested, oneOf, min, max, enum: enumMap, required } = meta;
804
- const value = property === "" ? data2 : data2?.[key];
805
- const allRules = [
806
- async (args2) => {
807
- const { value: value2 } = args2;
808
- if ("const" in meta) {
809
- if (!equalFn(value2, meta.const)) return createErrMsg("const", args2);
810
- }
811
- if (required === false && value2 === void 0) return true;
812
- if (required !== false && value2 === void 0) return createErrMsg("required", args2);
813
- if (type === String && typeof value2 !== "string") return createErrMsg("string", args2);
814
- if (type === Number && typeof value2 !== "number") return createErrMsg("number", args2);
815
- if (type === Boolean && typeof value2 !== "boolean") return createErrMsg("boolean", args2);
816
- if (type === Array) {
817
- if (!Array.isArray(value2)) return createErrMsg("array", args2);
818
- if (!nested) return;
819
- for (const i in value2) {
820
- if (nested === String && typeof value2[i] !== "string") return createErrMsg("stringArray", args2);
821
- if (nested === Number && typeof value2[i] !== "number") return createErrMsg("numberArray", args2);
822
- if (nested === Boolean && typeof value2[i] !== "boolean") return createErrMsg("booleanArray", args2);
823
- if (isPhecda(nested)) {
824
- if (!isObject(value2[i])) return createErrMsg("object", {
825
- ...args2,
826
- arrayIndex: i
827
- });
828
- const errs = await parse(nested, value2[i]);
829
- if (errs.length) {
830
- errors.push(errs[0]);
831
- break;
857
+ function extractRules(model, createErrMsg = _createErrorMessage, equalFn = (a, b) => a === b, parentPath = "") {
858
+ const mergedRuleMap = /* @__PURE__ */ new Map();
859
+ const collect = /* @__PURE__ */ __name((currentModel, currentPath) => {
860
+ for (const key of getMetaKey(currentModel)) {
861
+ const mergedMeta = getMergedMeta(currentModel, key);
862
+ const baseProperty = key === SHARE_KEY ? "" : key;
863
+ const property = baseProperty && currentPath ? `${currentPath}.${baseProperty}` : baseProperty || currentPath;
864
+ const designType = baseProperty === "" ? currentModel : Reflect.getMetadata("design:type", currentModel.prototype, key);
865
+ const ruleRunner = /* @__PURE__ */ __name(async (value) => {
866
+ const { rules = [], nested, oneOf, min, max, enum: enumMap, required } = mergedMeta;
867
+ const args = {
868
+ value,
869
+ property,
870
+ meta: mergedMeta,
871
+ model: currentModel
872
+ };
873
+ const allRules = [
874
+ async (localArgs) => {
875
+ const { value: value2 } = localArgs;
876
+ if ("const" in mergedMeta) {
877
+ if (!equalFn(value2, mergedMeta.const)) return createErrMsg("const", localArgs);
878
+ }
879
+ if (required === false && value2 === void 0) return true;
880
+ if (required !== false && value2 === void 0) return createErrMsg("required", localArgs);
881
+ if (designType === String && typeof value2 !== "string") return createErrMsg("string", localArgs);
882
+ if (designType === Number && typeof value2 !== "number") return createErrMsg("number", localArgs);
883
+ if (designType === Boolean && typeof value2 !== "boolean") return createErrMsg("boolean", localArgs);
884
+ if (designType === Array) {
885
+ if (!Array.isArray(value2)) return createErrMsg("array", localArgs);
886
+ if (!nested) return;
887
+ for (const i in value2) {
888
+ if (nested === String && typeof value2[i] !== "string") return createErrMsg("stringArray", localArgs);
889
+ if (nested === Number && typeof value2[i] !== "number") return createErrMsg("numberArray", localArgs);
890
+ if (nested === Boolean && typeof value2[i] !== "boolean") return createErrMsg("booleanArray", localArgs);
891
+ if (isPhecda(nested)) {
892
+ if (!isObject(value2[i])) return createErrMsg("object", {
893
+ ...localArgs,
894
+ arrayIndex: i
895
+ });
832
896
  }
833
897
  }
898
+ } else if (nested && isPhecda(nested)) {
899
+ if (!isObject(value2)) return createErrMsg("object", localArgs);
834
900
  }
835
- } else {
836
- if (nested && isPhecda(nested)) {
837
- if (!isObject(value2)) return createErrMsg("object", args2);
838
- const errs = await parse(nested, value2);
839
- if (errs.length) errors.push(errs[0]);
901
+ if (min) {
902
+ if (typeof value2 === "number" && value2 < min) return createErrMsg("min", localArgs);
903
+ if (typeof value2 === "string" && value2.length < min) return createErrMsg("min", localArgs);
904
+ if (Array.isArray(value2) && value2.length < min) return createErrMsg("min", localArgs);
840
905
  }
841
- }
842
- if (min) {
843
- if (typeof value2 === "number" && value2 < min) return createErrMsg("min", args2);
844
- if (typeof value2 === "string" && value2.length < min) return createErrMsg("min", args2);
845
- if (Array.isArray(value2) && value2.length < min) return createErrMsg("min", args2);
846
- }
847
- if (max) {
848
- if (typeof value2 === "number" && value2 > max) return createErrMsg("max", args2);
849
- if (typeof value2 === "string" && value2.length > max) return createErrMsg("max", args2);
850
- if (Array.isArray(value2) && value2.length > max) return createErrMsg("max", args2);
851
- }
852
- if (enumMap) {
853
- if (!Object.values(enumMap).includes(value2)) return createErrMsg("enum", args2);
854
- }
855
- if (oneOf) {
856
- let isCorrect = false;
857
- for (const item of oneOf) {
858
- switch (item) {
859
- case String:
860
- if (typeof value2 === "string") isCorrect = true;
861
- break;
862
- case Number:
863
- if (typeof value2 === "number") isCorrect = true;
864
- break;
865
- case Boolean:
866
- if (typeof value2 === "boolean") isCorrect = true;
867
- break;
868
- default:
869
- if (isPhecda(item)) {
870
- const errs = await validate(item, value2);
871
- if (!errs.length) {
872
- isCorrect = true;
873
- break;
874
- }
875
- } else if (typeof item === "function") {
876
- const ret = await item(args2);
877
- if (ret) {
878
- isCorrect = true;
879
- break;
880
- }
881
- } else {
882
- if (equalFn(value2, item)) {
906
+ if (max) {
907
+ if (typeof value2 === "number" && value2 > max) return createErrMsg("max", localArgs);
908
+ if (typeof value2 === "string" && value2.length > max) return createErrMsg("max", localArgs);
909
+ if (Array.isArray(value2) && value2.length > max) return createErrMsg("max", localArgs);
910
+ }
911
+ if (enumMap) {
912
+ if (!Object.values(enumMap).includes(value2)) return createErrMsg("enum", localArgs);
913
+ }
914
+ if (oneOf) {
915
+ let isCorrect = false;
916
+ for (const item of oneOf) {
917
+ switch (item) {
918
+ case String:
919
+ if (typeof value2 === "string") isCorrect = true;
920
+ break;
921
+ case Number:
922
+ if (typeof value2 === "number") isCorrect = true;
923
+ break;
924
+ case Boolean:
925
+ if (typeof value2 === "boolean") isCorrect = true;
926
+ break;
927
+ default:
928
+ if (isPhecda(item)) {
929
+ const errs = await validate(item, value2);
930
+ if (!errs.length) {
931
+ isCorrect = true;
932
+ break;
933
+ }
934
+ } else if (typeof item === "function") {
935
+ const ret = await item(localArgs);
936
+ if (ret) {
937
+ isCorrect = true;
938
+ break;
939
+ }
940
+ } else if (equalFn(value2, item)) {
883
941
  isCorrect = true;
884
942
  break;
885
943
  }
886
- }
944
+ }
887
945
  }
946
+ if (!isCorrect) return createErrMsg("oneOf", localArgs);
888
947
  }
889
- if (!isCorrect) return createErrMsg("oneOf", args2);
890
- }
891
- },
892
- ...rules
893
- ];
894
- const args = {
895
- value,
896
- property,
897
- meta,
898
- model: model2
899
- };
900
- for (const rule of allRules) {
901
- const errMsg = await rule(args);
902
- if (errMsg === true) break;
903
- if (errMsg) {
904
- errors.push(errMsg);
905
- break;
948
+ },
949
+ ...rules
950
+ ];
951
+ for (const rule of allRules) {
952
+ const errMsg = await rule(args);
953
+ if (errMsg === true) return;
954
+ if (errMsg) return errMsg;
906
955
  }
956
+ }, "ruleRunner");
957
+ const current = mergedRuleMap.get(property);
958
+ if (!current) {
959
+ mergedRuleMap.set(property, {
960
+ property,
961
+ designType,
962
+ mergedMeta,
963
+ validators: [
964
+ ruleRunner
965
+ ]
966
+ });
967
+ } else {
968
+ current.validators.push(ruleRunner);
969
+ }
970
+ if (mergedMeta.nested && isPhecda(mergedMeta.nested)) {
971
+ const nestedPath = designType === Array ? `${property}[]` : property;
972
+ collect(mergedMeta.nested, nestedPath);
907
973
  }
908
- if (errors.length !== 0 && !collectErrors) break;
909
974
  }
975
+ }, "collect");
976
+ collect(model, parentPath);
977
+ return [
978
+ ...mergedRuleMap.values()
979
+ ].map((item) => ({
980
+ property: item.property,
981
+ designType: item.designType,
982
+ mergedMeta: item.mergedMeta,
983
+ validate: /* @__PURE__ */ __name(async (value) => {
984
+ for (const validator of item.validators) {
985
+ const err = await validator(value);
986
+ if (err) return err;
987
+ }
988
+ }, "validate")
989
+ }));
990
+ }
991
+ __name(extractRules, "extractRules");
992
+ function extractDataByRules(data, rules) {
993
+ let transformed = {};
994
+ const projectContainerOnly = /* @__PURE__ */ __name((value, rule) => {
995
+ const nested = rule.mergedMeta?.nested;
996
+ if (!nested || !isPhecda(nested)) return value;
997
+ if (rule.designType === Array) return Array.isArray(value) ? [] : value;
998
+ return isObject(value) ? {} : value;
999
+ }, "projectContainerOnly");
1000
+ for (const rule of rules) {
1001
+ if (!rule.property) continue;
1002
+ const tokens = parsePath(rule.property);
1003
+ const concreteValues = collectConcretePathsByTokens(data, tokens);
1004
+ for (const concrete of concreteValues) {
1005
+ const projectedValue = projectContainerOnly(concrete.value, rule);
1006
+ transformed = setByConcreteKeys(transformed, concrete.keys, projectedValue);
1007
+ }
1008
+ }
1009
+ return transformed;
1010
+ }
1011
+ __name(extractDataByRules, "extractDataByRules");
1012
+ async function validateWithRules(data, rules, collectErrors = false) {
1013
+ const errors = [];
1014
+ for (const rule of rules) {
1015
+ if (!rule.property) {
1016
+ const err = await rule.validate(data);
1017
+ if (err) {
1018
+ errors.push(err);
1019
+ if (!collectErrors) break;
1020
+ }
1021
+ continue;
1022
+ }
1023
+ const tokens = parsePath(rule.property);
1024
+ const values = collectConcretePathsByTokens(data, tokens).map((v) => v.value);
1025
+ const targetValues = values.length ? values : tokens.length === 1 ? [
1026
+ void 0
1027
+ ] : [];
1028
+ for (const value of targetValues) {
1029
+ const err = await rule.validate(value);
1030
+ if (err) {
1031
+ errors.push(err);
1032
+ break;
1033
+ }
1034
+ }
1035
+ if (errors.length && !collectErrors) break;
1036
+ }
1037
+ return errors;
1038
+ }
1039
+ __name(validateWithRules, "validateWithRules");
1040
+ async function validate(model, data, collectErrors = false, createErrMsg = _createErrorMessage, equalFn = (a, b) => a === b) {
1041
+ const errors = [];
1042
+ if (!isObject(data)) {
1043
+ errors.push("data must be an object");
910
1044
  return errors;
911
1045
  }
912
- __name(parse, "parse");
913
- return await parse(model, data);
1046
+ const rules = extractRules(model, createErrMsg, equalFn);
1047
+ return await validateWithRules(data, rules, collectErrors);
914
1048
  }
915
1049
  __name(validate, "validate");
916
1050
  // Annotate the CommonJS export names for ESM import in node:
@@ -952,6 +1086,8 @@ __name(validate, "validate");
952
1086
  _createErrorMessage,
953
1087
  activeInstance,
954
1088
  addDecoToClass,
1089
+ extractDataByRules,
1090
+ extractRules,
955
1091
  functionToClass,
956
1092
  get,
957
1093
  getInject,
@@ -979,6 +1115,7 @@ __name(validate, "validate");
979
1115
  setInject,
980
1116
  setMeta,
981
1117
  validate,
1118
+ validateWithRules,
982
1119
  wait,
983
1120
  ...require("reflect-metadata")
984
1121
  });
package/dist/index.mjs CHANGED
@@ -201,23 +201,21 @@ function isAsyncFunc(fn) {
201
201
  __name(isAsyncFunc, "isAsyncFunc");
202
202
  function invoke(instance, key, ...params) {
203
203
  const metaKeys = getMetaKey(instance);
204
- const names = [];
205
204
  return Promise.allSettled(metaKeys.map((k) => {
206
- return getMeta(instance, k);
207
- }).flat().filter((item) => {
208
- if (typeof item[key] === "function") {
205
+ const names = [];
206
+ const meta = getMeta(instance, k);
207
+ return meta.filter((item) => {
209
208
  if (item.__NAME__) {
210
- if (names.includes(item.__NAME__)) {
211
- return false;
212
- }
209
+ if (names.includes(item.__NAME__)) return false;
213
210
  names.push(item.__NAME__);
214
211
  return true;
215
212
  } else {
216
213
  return true;
217
214
  }
218
- } else {
219
- return false;
220
- }
215
+ });
216
+ }).flat().filter((item) => {
217
+ if (typeof item[key] === "function") return true;
218
+ else return false;
221
219
  }).map((item) => item[key](instance, ...params))).then((res) => {
222
220
  res.filter((item) => item.status === "rejected").forEach((item) => {
223
221
  console.error(item.reason);
@@ -665,6 +663,70 @@ Base = _ts_decorate([
665
663
  ], Base);
666
664
 
667
665
  // src/validate.ts
666
+ function parsePath(path) {
667
+ if (!path) return [];
668
+ return path.split(".").flatMap((token) => {
669
+ if (!token) return [];
670
+ if (token.endsWith("[]")) return [
671
+ token.slice(0, -2),
672
+ "[]"
673
+ ];
674
+ return [
675
+ token
676
+ ];
677
+ });
678
+ }
679
+ __name(parsePath, "parsePath");
680
+ function collectConcretePathsByTokens(data, tokens) {
681
+ if (!tokens.length) return [
682
+ {
683
+ keys: [],
684
+ value: data
685
+ }
686
+ ];
687
+ const walk = /* @__PURE__ */ __name((node, tokenIndex, keys) => {
688
+ if (tokenIndex >= tokens.length) return [
689
+ {
690
+ keys,
691
+ value: node
692
+ }
693
+ ];
694
+ const token = tokens[tokenIndex];
695
+ if (token === "[]") {
696
+ if (!Array.isArray(node)) return [];
697
+ return node.flatMap((item, idx) => walk(item, tokenIndex + 1, [
698
+ ...keys,
699
+ idx
700
+ ]));
701
+ }
702
+ if (node === null || node === void 0) return [];
703
+ const nodeType = typeof node;
704
+ if (nodeType !== "object" && nodeType !== "function") return [];
705
+ return walk(node[token], tokenIndex + 1, [
706
+ ...keys,
707
+ token
708
+ ]);
709
+ }, "walk");
710
+ return walk(data, 0, []);
711
+ }
712
+ __name(collectConcretePathsByTokens, "collectConcretePathsByTokens");
713
+ function setByConcreteKeys(target, keys, value) {
714
+ if (!keys.length) return value;
715
+ let node = target;
716
+ for (let i = 0; i < keys.length; i++) {
717
+ const key = keys[i];
718
+ const isLast = i === keys.length - 1;
719
+ if (isLast) {
720
+ node[key] = value;
721
+ break;
722
+ }
723
+ const nextKey = keys[i + 1];
724
+ if (node[key] === void 0) node[key] = typeof nextKey === "number" ? [] : {};
725
+ node = node[key];
726
+ }
727
+ return target;
728
+ }
729
+ __name(setByConcreteKeys, "setByConcreteKeys");
668
730
  var _createErrorMessage = /* @__PURE__ */ __name((type, { property, meta }) => {
669
731
  switch (type) {
670
732
  case "const":
@@ -702,128 +764,197 @@ function isObject(value) {
702
764
  return Object.prototype.toString.call(value) === "[object Object]";
703
765
  }
704
766
  __name(isObject, "isObject");
705
- async function validate(model, data, collectErrors = false, createErrMsg = _createErrorMessage, equalFn = (a, b) => a === b) {
706
- async function parse(model2, data2) {
707
- const errors = [];
708
- if (!isObject(data2)) {
709
- errors.push("data must be an object");
710
- return errors;
711
- }
712
- for (const key of getMetaKey(model2)) {
713
- const meta = getMergedMeta(model2, key);
714
- const property = key === SHARE_KEY ? "" : key;
715
- const type = property === "" ? model2 : Reflect.getMetadata("design:type", model2.prototype, key);
716
- const { rules = [], nested, oneOf, min, max, enum: enumMap, required } = meta;
717
- const value = property === "" ? data2 : data2?.[key];
718
- const allRules = [
719
- async (args2) => {
720
- const { value: value2 } = args2;
721
- if ("const" in meta) {
722
- if (!equalFn(value2, meta.const)) return createErrMsg("const", args2);
723
- }
724
- if (required === false && value2 === void 0) return true;
725
- if (required !== false && value2 === void 0) return createErrMsg("required", args2);
726
- if (type === String && typeof value2 !== "string") return createErrMsg("string", args2);
727
- if (type === Number && typeof value2 !== "number") return createErrMsg("number", args2);
728
- if (type === Boolean && typeof value2 !== "boolean") return createErrMsg("boolean", args2);
729
- if (type === Array) {
730
- if (!Array.isArray(value2)) return createErrMsg("array", args2);
731
- if (!nested) return;
732
- for (const i in value2) {
733
- if (nested === String && typeof value2[i] !== "string") return createErrMsg("stringArray", args2);
734
- if (nested === Number && typeof value2[i] !== "number") return createErrMsg("numberArray", args2);
735
- if (nested === Boolean && typeof value2[i] !== "boolean") return createErrMsg("booleanArray", args2);
736
- if (isPhecda(nested)) {
737
- if (!isObject(value2[i])) return createErrMsg("object", {
738
- ...args2,
739
- arrayIndex: i
740
- });
741
- const errs = await parse(nested, value2[i]);
742
- if (errs.length) {
743
- errors.push(errs[0]);
744
- break;
767
+ function extractRules(model, createErrMsg = _createErrorMessage, equalFn = (a, b) => a === b, parentPath = "") {
768
+ const mergedRuleMap = /* @__PURE__ */ new Map();
769
+ const collect = /* @__PURE__ */ __name((currentModel, currentPath) => {
770
+ for (const key of getMetaKey(currentModel)) {
771
+ const mergedMeta = getMergedMeta(currentModel, key);
772
+ const baseProperty = key === SHARE_KEY ? "" : key;
773
+ const property = baseProperty && currentPath ? `${currentPath}.${baseProperty}` : baseProperty || currentPath;
774
+ const designType = baseProperty === "" ? currentModel : Reflect.getMetadata("design:type", currentModel.prototype, key);
775
+ const ruleRunner = /* @__PURE__ */ __name(async (value) => {
776
+ const { rules = [], nested, oneOf, min, max, enum: enumMap, required } = mergedMeta;
777
+ const args = {
778
+ value,
779
+ property,
780
+ meta: mergedMeta,
781
+ model: currentModel
782
+ };
783
+ const allRules = [
784
+ async (localArgs) => {
785
+ const { value: value2 } = localArgs;
786
+ if ("const" in mergedMeta) {
787
+ if (!equalFn(value2, mergedMeta.const)) return createErrMsg("const", localArgs);
788
+ }
789
+ if (required === false && value2 === void 0) return true;
790
+ if (required !== false && value2 === void 0) return createErrMsg("required", localArgs);
791
+ if (designType === String && typeof value2 !== "string") return createErrMsg("string", localArgs);
792
+ if (designType === Number && typeof value2 !== "number") return createErrMsg("number", localArgs);
793
+ if (designType === Boolean && typeof value2 !== "boolean") return createErrMsg("boolean", localArgs);
794
+ if (designType === Array) {
795
+ if (!Array.isArray(value2)) return createErrMsg("array", localArgs);
796
+ if (!nested) return;
797
+ for (const i in value2) {
798
+ if (nested === String && typeof value2[i] !== "string") return createErrMsg("stringArray", localArgs);
799
+ if (nested === Number && typeof value2[i] !== "number") return createErrMsg("numberArray", localArgs);
800
+ if (nested === Boolean && typeof value2[i] !== "boolean") return createErrMsg("booleanArray", localArgs);
801
+ if (isPhecda(nested)) {
802
+ if (!isObject(value2[i])) return createErrMsg("object", {
803
+ ...localArgs,
804
+ arrayIndex: i
805
+ });
745
806
  }
746
807
  }
808
+ } else if (nested && isPhecda(nested)) {
809
+ if (!isObject(value2)) return createErrMsg("object", localArgs);
747
810
  }
748
- } else {
749
- if (nested && isPhecda(nested)) {
750
- if (!isObject(value2)) return createErrMsg("object", args2);
751
- const errs = await parse(nested, value2);
752
- if (errs.length) errors.push(errs[0]);
811
+ if (min) {
812
+ if (typeof value2 === "number" && value2 < min) return createErrMsg("min", localArgs);
813
+ if (typeof value2 === "string" && value2.length < min) return createErrMsg("min", localArgs);
814
+ if (Array.isArray(value2) && value2.length < min) return createErrMsg("min", localArgs);
753
815
  }
754
- }
755
- if (min) {
756
- if (typeof value2 === "number" && value2 < min) return createErrMsg("min", args2);
757
- if (typeof value2 === "string" && value2.length < min) return createErrMsg("min", args2);
758
- if (Array.isArray(value2) && value2.length < min) return createErrMsg("min", args2);
759
- }
760
- if (max) {
761
- if (typeof value2 === "number" && value2 > max) return createErrMsg("max", args2);
762
- if (typeof value2 === "string" && value2.length > max) return createErrMsg("max", args2);
763
- if (Array.isArray(value2) && value2.length > max) return createErrMsg("max", args2);
764
- }
765
- if (enumMap) {
766
- if (!Object.values(enumMap).includes(value2)) return createErrMsg("enum", args2);
767
- }
768
- if (oneOf) {
769
- let isCorrect = false;
770
- for (const item of oneOf) {
771
- switch (item) {
772
- case String:
773
- if (typeof value2 === "string") isCorrect = true;
774
- break;
775
- case Number:
776
- if (typeof value2 === "number") isCorrect = true;
777
- break;
778
- case Boolean:
779
- if (typeof value2 === "boolean") isCorrect = true;
780
- break;
781
- default:
782
- if (isPhecda(item)) {
783
- const errs = await validate(item, value2);
784
- if (!errs.length) {
785
- isCorrect = true;
786
- break;
787
- }
788
- } else if (typeof item === "function") {
789
- const ret = await item(args2);
790
- if (ret) {
791
- isCorrect = true;
792
- break;
793
- }
794
- } else {
795
- if (equalFn(value2, item)) {
816
+ if (max) {
817
+ if (typeof value2 === "number" && value2 > max) return createErrMsg("max", localArgs);
818
+ if (typeof value2 === "string" && value2.length > max) return createErrMsg("max", localArgs);
819
+ if (Array.isArray(value2) && value2.length > max) return createErrMsg("max", localArgs);
820
+ }
821
+ if (enumMap) {
822
+ if (!Object.values(enumMap).includes(value2)) return createErrMsg("enum", localArgs);
823
+ }
824
+ if (oneOf) {
825
+ let isCorrect = false;
826
+ for (const item of oneOf) {
827
+ switch (item) {
828
+ case String:
829
+ if (typeof value2 === "string") isCorrect = true;
830
+ break;
831
+ case Number:
832
+ if (typeof value2 === "number") isCorrect = true;
833
+ break;
834
+ case Boolean:
835
+ if (typeof value2 === "boolean") isCorrect = true;
836
+ break;
837
+ default:
838
+ if (isPhecda(item)) {
839
+ const errs = await validate(item, value2);
840
+ if (!errs.length) {
841
+ isCorrect = true;
842
+ break;
843
+ }
844
+ } else if (typeof item === "function") {
845
+ const ret = await item(localArgs);
846
+ if (ret) {
847
+ isCorrect = true;
848
+ break;
849
+ }
850
+ } else if (equalFn(value2, item)) {
796
851
  isCorrect = true;
797
852
  break;
798
853
  }
799
- }
854
+ }
800
855
  }
856
+ if (!isCorrect) return createErrMsg("oneOf", localArgs);
801
857
  }
802
- if (!isCorrect) return createErrMsg("oneOf", args2);
803
- }
804
- },
805
- ...rules
806
- ];
807
- const args = {
808
- value,
809
- property,
810
- meta,
811
- model: model2
812
- };
813
- for (const rule of allRules) {
814
- const errMsg = await rule(args);
815
- if (errMsg === true) break;
816
- if (errMsg) {
817
- errors.push(errMsg);
818
- break;
858
+ },
859
+ ...rules
860
+ ];
861
+ for (const rule of allRules) {
862
+ const errMsg = await rule(args);
863
+ if (errMsg === true) return;
864
+ if (errMsg) return errMsg;
819
865
  }
866
+ }, "ruleRunner");
867
+ const current = mergedRuleMap.get(property);
868
+ if (!current) {
869
+ mergedRuleMap.set(property, {
870
+ property,
871
+ designType,
872
+ mergedMeta,
873
+ validators: [
874
+ ruleRunner
875
+ ]
876
+ });
877
+ } else {
878
+ current.validators.push(ruleRunner);
879
+ }
880
+ if (mergedMeta.nested && isPhecda(mergedMeta.nested)) {
881
+ const nestedPath = designType === Array ? `${property}[]` : property;
882
+ collect(mergedMeta.nested, nestedPath);
820
883
  }
821
- if (errors.length !== 0 && !collectErrors) break;
822
884
  }
885
+ }, "collect");
886
+ collect(model, parentPath);
887
+ return [
888
+ ...mergedRuleMap.values()
889
+ ].map((item) => ({
890
+ property: item.property,
891
+ designType: item.designType,
892
+ mergedMeta: item.mergedMeta,
893
+ validate: /* @__PURE__ */ __name(async (value) => {
894
+ for (const validator of item.validators) {
895
+ const err = await validator(value);
896
+ if (err) return err;
897
+ }
898
+ }, "validate")
899
+ }));
900
+ }
901
+ __name(extractRules, "extractRules");
902
+ function extractDataByRules(data, rules) {
903
+ let transformed = {};
904
+ const projectContainerOnly = /* @__PURE__ */ __name((value, rule) => {
905
+ const nested = rule.mergedMeta?.nested;
906
+ if (!nested || !isPhecda(nested)) return value;
907
+ if (rule.designType === Array) return Array.isArray(value) ? [] : value;
908
+ return isObject(value) ? {} : value;
909
+ }, "projectContainerOnly");
910
+ for (const rule of rules) {
911
+ if (!rule.property) continue;
912
+ const tokens = parsePath(rule.property);
913
+ const concreteValues = collectConcretePathsByTokens(data, tokens);
914
+ for (const concrete of concreteValues) {
915
+ const projectedValue = projectContainerOnly(concrete.value, rule);
916
+ transformed = setByConcreteKeys(transformed, concrete.keys, projectedValue);
917
+ }
918
+ }
919
+ return transformed;
920
+ }
921
+ __name(extractDataByRules, "extractDataByRules");
922
+ async function validateWithRules(data, rules, collectErrors = false) {
923
+ const errors = [];
924
+ for (const rule of rules) {
925
+ if (!rule.property) {
926
+ const err = await rule.validate(data);
927
+ if (err) {
928
+ errors.push(err);
929
+ if (!collectErrors) break;
930
+ }
931
+ continue;
932
+ }
933
+ const tokens = parsePath(rule.property);
934
+ const values = collectConcretePathsByTokens(data, tokens).map((v) => v.value);
935
+ const targetValues = values.length ? values : tokens.length === 1 ? [
936
+ void 0
937
+ ] : [];
938
+ for (const value of targetValues) {
939
+ const err = await rule.validate(value);
940
+ if (err) {
941
+ errors.push(err);
942
+ break;
943
+ }
944
+ }
945
+ if (errors.length && !collectErrors) break;
946
+ }
947
+ return errors;
948
+ }
949
+ __name(validateWithRules, "validateWithRules");
950
+ async function validate(model, data, collectErrors = false, createErrMsg = _createErrorMessage, equalFn = (a, b) => a === b) {
951
+ const errors = [];
952
+ if (!isObject(data)) {
953
+ errors.push("data must be an object");
823
954
  return errors;
824
955
  }
825
- __name(parse, "parse");
826
- return await parse(model, data);
956
+ const rules = extractRules(model, createErrMsg, equalFn);
957
+ return await validateWithRules(data, rules, collectErrors);
827
958
  }
828
959
  __name(validate, "validate");
829
960
  export {
@@ -864,6 +995,8 @@ export {
864
995
  _createErrorMessage,
865
996
  activeInstance,
866
997
  addDecoToClass,
998
+ extractDataByRules,
999
+ extractRules,
867
1000
  functionToClass,
868
1001
  get,
869
1002
  getInject,
@@ -891,5 +1024,6 @@ export {
891
1024
  setInject,
892
1025
  setMeta,
893
1026
  validate,
1027
+ validateWithRules,
894
1028
  wait
895
1029
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phecda-core",
3
- "version": "5.2.0",
3
+ "version": "5.3.0",
4
4
  "description": "provide base function and abstract limit to other phecda module ",
5
5
  "author": "fgsreally",
6
6
  "license": "MIT",