phecda-core 5.2.1 → 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);
@@ -293,9 +296,7 @@ function invoke(instance, key, ...params) {
293
296
  const meta = getMeta(instance, k);
294
297
  return meta.filter((item) => {
295
298
  if (item.__NAME__) {
296
- if (names.includes(item.__NAME__)) {
297
- return false;
298
- }
299
+ if (names.includes(item.__NAME__)) return false;
299
300
  names.push(item.__NAME__);
300
301
  return true;
301
302
  } else {
@@ -303,11 +304,8 @@ function invoke(instance, key, ...params) {
303
304
  }
304
305
  });
305
306
  }).flat().filter((item) => {
306
- if (typeof item[key] === "function") {
307
- return true;
308
- } else {
309
- return false;
310
- }
307
+ if (typeof item[key] === "function") return true;
308
+ else return false;
311
309
  }).map((item) => item[key](instance, ...params))).then((res) => {
312
310
  res.filter((item) => item.status === "rejected").forEach((item) => {
313
311
  console.error(item.reason);
@@ -755,6 +753,70 @@ Base = _ts_decorate([
755
753
  ], Base);
756
754
 
757
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");
758
820
  var _createErrorMessage = /* @__PURE__ */ __name((type, { property, meta }) => {
759
821
  switch (type) {
760
822
  case "const":
@@ -792,128 +854,197 @@ function isObject(value) {
792
854
  return Object.prototype.toString.call(value) === "[object Object]";
793
855
  }
794
856
  __name(isObject, "isObject");
795
- async function validate(model, data, collectErrors = false, createErrMsg = _createErrorMessage, equalFn = (a, b) => a === b) {
796
- async function parse(model2, data2) {
797
- const errors = [];
798
- if (!isObject(data2)) {
799
- errors.push("data must be an object");
800
- return errors;
801
- }
802
- for (const key of getMetaKey(model2)) {
803
- const meta = getMergedMeta(model2, key);
804
- const property = key === SHARE_KEY ? "" : key;
805
- const type = property === "" ? model2 : Reflect.getMetadata("design:type", model2.prototype, key);
806
- const { rules = [], nested, oneOf, min, max, enum: enumMap, required } = meta;
807
- const value = property === "" ? data2 : data2?.[key];
808
- const allRules = [
809
- async (args2) => {
810
- const { value: value2 } = args2;
811
- if ("const" in meta) {
812
- if (!equalFn(value2, meta.const)) return createErrMsg("const", args2);
813
- }
814
- if (required === false && value2 === void 0) return true;
815
- if (required !== false && value2 === void 0) return createErrMsg("required", args2);
816
- if (type === String && typeof value2 !== "string") return createErrMsg("string", args2);
817
- if (type === Number && typeof value2 !== "number") return createErrMsg("number", args2);
818
- if (type === Boolean && typeof value2 !== "boolean") return createErrMsg("boolean", args2);
819
- if (type === Array) {
820
- if (!Array.isArray(value2)) return createErrMsg("array", args2);
821
- if (!nested) return;
822
- for (const i in value2) {
823
- if (nested === String && typeof value2[i] !== "string") return createErrMsg("stringArray", args2);
824
- if (nested === Number && typeof value2[i] !== "number") return createErrMsg("numberArray", args2);
825
- if (nested === Boolean && typeof value2[i] !== "boolean") return createErrMsg("booleanArray", args2);
826
- if (isPhecda(nested)) {
827
- if (!isObject(value2[i])) return createErrMsg("object", {
828
- ...args2,
829
- arrayIndex: i
830
- });
831
- const errs = await parse(nested, value2[i]);
832
- if (errs.length) {
833
- errors.push(errs[0]);
834
- 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
+ });
835
896
  }
836
897
  }
898
+ } else if (nested && isPhecda(nested)) {
899
+ if (!isObject(value2)) return createErrMsg("object", localArgs);
837
900
  }
838
- } else {
839
- if (nested && isPhecda(nested)) {
840
- if (!isObject(value2)) return createErrMsg("object", args2);
841
- const errs = await parse(nested, value2);
842
- 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);
843
905
  }
844
- }
845
- if (min) {
846
- if (typeof value2 === "number" && value2 < min) return createErrMsg("min", args2);
847
- if (typeof value2 === "string" && value2.length < min) return createErrMsg("min", args2);
848
- if (Array.isArray(value2) && value2.length < min) return createErrMsg("min", args2);
849
- }
850
- if (max) {
851
- if (typeof value2 === "number" && value2 > max) return createErrMsg("max", args2);
852
- if (typeof value2 === "string" && value2.length > max) return createErrMsg("max", args2);
853
- if (Array.isArray(value2) && value2.length > max) return createErrMsg("max", args2);
854
- }
855
- if (enumMap) {
856
- if (!Object.values(enumMap).includes(value2)) return createErrMsg("enum", args2);
857
- }
858
- if (oneOf) {
859
- let isCorrect = false;
860
- for (const item of oneOf) {
861
- switch (item) {
862
- case String:
863
- if (typeof value2 === "string") isCorrect = true;
864
- break;
865
- case Number:
866
- if (typeof value2 === "number") isCorrect = true;
867
- break;
868
- case Boolean:
869
- if (typeof value2 === "boolean") isCorrect = true;
870
- break;
871
- default:
872
- if (isPhecda(item)) {
873
- const errs = await validate(item, value2);
874
- if (!errs.length) {
875
- isCorrect = true;
876
- break;
877
- }
878
- } else if (typeof item === "function") {
879
- const ret = await item(args2);
880
- if (ret) {
881
- isCorrect = true;
882
- break;
883
- }
884
- } else {
885
- 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)) {
886
941
  isCorrect = true;
887
942
  break;
888
943
  }
889
- }
944
+ }
890
945
  }
946
+ if (!isCorrect) return createErrMsg("oneOf", localArgs);
891
947
  }
892
- if (!isCorrect) return createErrMsg("oneOf", args2);
893
- }
894
- },
895
- ...rules
896
- ];
897
- const args = {
898
- value,
899
- property,
900
- meta,
901
- model: model2
902
- };
903
- for (const rule of allRules) {
904
- const errMsg = await rule(args);
905
- if (errMsg === true) break;
906
- if (errMsg) {
907
- errors.push(errMsg);
908
- 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;
909
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);
910
973
  }
911
- if (errors.length !== 0 && !collectErrors) break;
912
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");
913
1044
  return errors;
914
1045
  }
915
- __name(parse, "parse");
916
- return await parse(model, data);
1046
+ const rules = extractRules(model, createErrMsg, equalFn);
1047
+ return await validateWithRules(data, rules, collectErrors);
917
1048
  }
918
1049
  __name(validate, "validate");
919
1050
  // Annotate the CommonJS export names for ESM import in node:
@@ -955,6 +1086,8 @@ __name(validate, "validate");
955
1086
  _createErrorMessage,
956
1087
  activeInstance,
957
1088
  addDecoToClass,
1089
+ extractDataByRules,
1090
+ extractRules,
958
1091
  functionToClass,
959
1092
  get,
960
1093
  getInject,
@@ -982,6 +1115,7 @@ __name(validate, "validate");
982
1115
  setInject,
983
1116
  setMeta,
984
1117
  validate,
1118
+ validateWithRules,
985
1119
  wait,
986
1120
  ...require("reflect-metadata")
987
1121
  });
package/dist/index.mjs CHANGED
@@ -206,9 +206,7 @@ function invoke(instance, key, ...params) {
206
206
  const meta = getMeta(instance, k);
207
207
  return meta.filter((item) => {
208
208
  if (item.__NAME__) {
209
- if (names.includes(item.__NAME__)) {
210
- return false;
211
- }
209
+ if (names.includes(item.__NAME__)) return false;
212
210
  names.push(item.__NAME__);
213
211
  return true;
214
212
  } else {
@@ -216,11 +214,8 @@ function invoke(instance, key, ...params) {
216
214
  }
217
215
  });
218
216
  }).flat().filter((item) => {
219
- if (typeof item[key] === "function") {
220
- return true;
221
- } else {
222
- return false;
223
- }
217
+ if (typeof item[key] === "function") return true;
218
+ else return false;
224
219
  }).map((item) => item[key](instance, ...params))).then((res) => {
225
220
  res.filter((item) => item.status === "rejected").forEach((item) => {
226
221
  console.error(item.reason);
@@ -668,6 +663,70 @@ Base = _ts_decorate([
668
663
  ], Base);
669
664
 
670
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");
671
730
  var _createErrorMessage = /* @__PURE__ */ __name((type, { property, meta }) => {
672
731
  switch (type) {
673
732
  case "const":
@@ -705,128 +764,197 @@ function isObject(value) {
705
764
  return Object.prototype.toString.call(value) === "[object Object]";
706
765
  }
707
766
  __name(isObject, "isObject");
708
- async function validate(model, data, collectErrors = false, createErrMsg = _createErrorMessage, equalFn = (a, b) => a === b) {
709
- async function parse(model2, data2) {
710
- const errors = [];
711
- if (!isObject(data2)) {
712
- errors.push("data must be an object");
713
- return errors;
714
- }
715
- for (const key of getMetaKey(model2)) {
716
- const meta = getMergedMeta(model2, key);
717
- const property = key === SHARE_KEY ? "" : key;
718
- const type = property === "" ? model2 : Reflect.getMetadata("design:type", model2.prototype, key);
719
- const { rules = [], nested, oneOf, min, max, enum: enumMap, required } = meta;
720
- const value = property === "" ? data2 : data2?.[key];
721
- const allRules = [
722
- async (args2) => {
723
- const { value: value2 } = args2;
724
- if ("const" in meta) {
725
- if (!equalFn(value2, meta.const)) return createErrMsg("const", args2);
726
- }
727
- if (required === false && value2 === void 0) return true;
728
- if (required !== false && value2 === void 0) return createErrMsg("required", args2);
729
- if (type === String && typeof value2 !== "string") return createErrMsg("string", args2);
730
- if (type === Number && typeof value2 !== "number") return createErrMsg("number", args2);
731
- if (type === Boolean && typeof value2 !== "boolean") return createErrMsg("boolean", args2);
732
- if (type === Array) {
733
- if (!Array.isArray(value2)) return createErrMsg("array", args2);
734
- if (!nested) return;
735
- for (const i in value2) {
736
- if (nested === String && typeof value2[i] !== "string") return createErrMsg("stringArray", args2);
737
- if (nested === Number && typeof value2[i] !== "number") return createErrMsg("numberArray", args2);
738
- if (nested === Boolean && typeof value2[i] !== "boolean") return createErrMsg("booleanArray", args2);
739
- if (isPhecda(nested)) {
740
- if (!isObject(value2[i])) return createErrMsg("object", {
741
- ...args2,
742
- arrayIndex: i
743
- });
744
- const errs = await parse(nested, value2[i]);
745
- if (errs.length) {
746
- errors.push(errs[0]);
747
- 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
+ });
748
806
  }
749
807
  }
808
+ } else if (nested && isPhecda(nested)) {
809
+ if (!isObject(value2)) return createErrMsg("object", localArgs);
750
810
  }
751
- } else {
752
- if (nested && isPhecda(nested)) {
753
- if (!isObject(value2)) return createErrMsg("object", args2);
754
- const errs = await parse(nested, value2);
755
- 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);
756
815
  }
757
- }
758
- if (min) {
759
- if (typeof value2 === "number" && value2 < min) return createErrMsg("min", args2);
760
- if (typeof value2 === "string" && value2.length < min) return createErrMsg("min", args2);
761
- if (Array.isArray(value2) && value2.length < min) return createErrMsg("min", args2);
762
- }
763
- if (max) {
764
- if (typeof value2 === "number" && value2 > max) return createErrMsg("max", args2);
765
- if (typeof value2 === "string" && value2.length > max) return createErrMsg("max", args2);
766
- if (Array.isArray(value2) && value2.length > max) return createErrMsg("max", args2);
767
- }
768
- if (enumMap) {
769
- if (!Object.values(enumMap).includes(value2)) return createErrMsg("enum", args2);
770
- }
771
- if (oneOf) {
772
- let isCorrect = false;
773
- for (const item of oneOf) {
774
- switch (item) {
775
- case String:
776
- if (typeof value2 === "string") isCorrect = true;
777
- break;
778
- case Number:
779
- if (typeof value2 === "number") isCorrect = true;
780
- break;
781
- case Boolean:
782
- if (typeof value2 === "boolean") isCorrect = true;
783
- break;
784
- default:
785
- if (isPhecda(item)) {
786
- const errs = await validate(item, value2);
787
- if (!errs.length) {
788
- isCorrect = true;
789
- break;
790
- }
791
- } else if (typeof item === "function") {
792
- const ret = await item(args2);
793
- if (ret) {
794
- isCorrect = true;
795
- break;
796
- }
797
- } else {
798
- 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)) {
799
851
  isCorrect = true;
800
852
  break;
801
853
  }
802
- }
854
+ }
803
855
  }
856
+ if (!isCorrect) return createErrMsg("oneOf", localArgs);
804
857
  }
805
- if (!isCorrect) return createErrMsg("oneOf", args2);
806
- }
807
- },
808
- ...rules
809
- ];
810
- const args = {
811
- value,
812
- property,
813
- meta,
814
- model: model2
815
- };
816
- for (const rule of allRules) {
817
- const errMsg = await rule(args);
818
- if (errMsg === true) break;
819
- if (errMsg) {
820
- errors.push(errMsg);
821
- 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;
822
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);
823
883
  }
824
- if (errors.length !== 0 && !collectErrors) break;
825
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");
826
954
  return errors;
827
955
  }
828
- __name(parse, "parse");
829
- return await parse(model, data);
956
+ const rules = extractRules(model, createErrMsg, equalFn);
957
+ return await validateWithRules(data, rules, collectErrors);
830
958
  }
831
959
  __name(validate, "validate");
832
960
  export {
@@ -867,6 +995,8 @@ export {
867
995
  _createErrorMessage,
868
996
  activeInstance,
869
997
  addDecoToClass,
998
+ extractDataByRules,
999
+ extractRules,
870
1000
  functionToClass,
871
1001
  get,
872
1002
  getInject,
@@ -894,5 +1024,6 @@ export {
894
1024
  setInject,
895
1025
  setMeta,
896
1026
  validate,
1027
+ validateWithRules,
897
1028
  wait
898
1029
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phecda-core",
3
- "version": "5.2.1",
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",