index.mjs 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098
  1. /*!
  2. * TSBuffer Validator v2.1.2
  3. * -----------------------------------------
  4. * MIT LICENSE
  5. * KingWorks (C) Copyright 2023
  6. * https://github.com/k8w/tsbuffer-validator
  7. */
  8. import 'k8w-extend-native';
  9. import { __assign } from 'tslib';
  10. import { SchemaType } from 'tsbuffer-schema';
  11. var ProtoHelper = /** @class */ (function () {
  12. function ProtoHelper(proto) {
  13. this._schemaWithUuids = [];
  14. this._unionPropertiesCache = {};
  15. this._flatInterfaceSchemaCache = {};
  16. this._parseMappedTypeCache = new WeakMap();
  17. this.proto = proto;
  18. }
  19. /** 将ReferenceTypeSchema层层转换为它最终实际引用的类型 */
  20. ProtoHelper.prototype.parseReference = function (schema) {
  21. // Reference
  22. if (schema.type === SchemaType.Reference) {
  23. var parsedSchema = this.proto[schema.target];
  24. if (!parsedSchema) {
  25. throw new Error("Cannot find reference target: ".concat(schema.target));
  26. }
  27. if (this.isTypeReference(parsedSchema)) {
  28. return this.parseReference(parsedSchema);
  29. }
  30. else {
  31. return parsedSchema;
  32. }
  33. }
  34. // IndexedAccess
  35. else if (schema.type === SchemaType.IndexedAccess) {
  36. if (!this.isInterface(schema.objectType)) {
  37. throw new Error("Error objectType: ".concat(schema.objectType.type));
  38. }
  39. // find prop item
  40. var flat = this.getFlatInterfaceSchema(schema.objectType);
  41. var propItem = flat.properties.find(function (v) { return v.name === schema.index; });
  42. var propType = void 0;
  43. if (propItem) {
  44. propType = propItem.type;
  45. }
  46. else {
  47. if (flat.indexSignature) {
  48. propType = flat.indexSignature.type;
  49. }
  50. else {
  51. throw new Error("Error index: ".concat(schema.index));
  52. }
  53. }
  54. // optional -> | undefined
  55. if (propItem && propItem.optional && // 引用的字段是optional
  56. (propItem.type.type !== SchemaType.Union // 自身不为Union
  57. // 或自身为Union,但没有undefined成员条件
  58. || propItem.type.members.findIndex(function (v) { return v.type.type === SchemaType.Literal && v.type.literal === undefined; }) === -1)) {
  59. propType = {
  60. type: SchemaType.Union,
  61. members: [
  62. { id: 0, type: propType },
  63. {
  64. id: 1,
  65. type: {
  66. type: SchemaType.Literal,
  67. literal: undefined
  68. }
  69. }
  70. ]
  71. };
  72. }
  73. return this.isTypeReference(propType) ? this.parseReference(propType) : propType;
  74. }
  75. else if (schema.type === SchemaType.Keyof) {
  76. var flatInterface = this.getFlatInterfaceSchema(schema.target);
  77. return {
  78. type: SchemaType.Union,
  79. members: flatInterface.properties.map(function (v, i) { return ({
  80. id: i,
  81. type: {
  82. type: SchemaType.Literal,
  83. literal: v.name
  84. }
  85. }); })
  86. };
  87. }
  88. else {
  89. return schema;
  90. }
  91. };
  92. ProtoHelper.prototype.isInterface = function (schema, excludeReference) {
  93. if (excludeReference === void 0) { excludeReference = false; }
  94. if (!excludeReference && this.isTypeReference(schema)) {
  95. var parsed = this.parseReference(schema);
  96. return this.isInterface(parsed, excludeReference);
  97. }
  98. else {
  99. return schema.type === SchemaType.Interface || this.isMappedType(schema) && this.parseMappedType(schema).type === SchemaType.Interface;
  100. }
  101. };
  102. ProtoHelper.prototype.isMappedType = function (schema) {
  103. return schema.type === SchemaType.Pick ||
  104. schema.type === SchemaType.Partial ||
  105. schema.type === SchemaType.Omit ||
  106. schema.type === SchemaType.Overwrite;
  107. };
  108. ProtoHelper.prototype.isTypeReference = function (schema) {
  109. return schema.type === SchemaType.Reference || schema.type === SchemaType.IndexedAccess || schema.type === SchemaType.Keyof;
  110. };
  111. ProtoHelper.prototype._getSchemaUuid = function (schema) {
  112. var schemaWithUuid = schema;
  113. if (!schemaWithUuid.uuid) {
  114. schemaWithUuid.uuid = this._schemaWithUuids.push(schemaWithUuid);
  115. }
  116. return schemaWithUuid.uuid;
  117. };
  118. ProtoHelper.prototype.getUnionProperties = function (schema) {
  119. var uuid = this._getSchemaUuid(schema);
  120. if (!this._unionPropertiesCache[uuid]) {
  121. this._unionPropertiesCache[uuid] = this._addUnionProperties([], schema.members.map(function (v) { return v.type; }));
  122. }
  123. return this._unionPropertiesCache[uuid];
  124. };
  125. /**
  126. * unionProperties: 在Union或Intersection类型中,出现在任意member中的字段
  127. */
  128. ProtoHelper.prototype._addUnionProperties = function (unionProperties, schemas) {
  129. for (var i = 0, len = schemas.length; i < len; ++i) {
  130. var schema = this.parseReference(schemas[i]);
  131. // Interface及其Ref 加入interfaces
  132. if (this.isInterface(schema)) {
  133. var flat = this.getFlatInterfaceSchema(schema);
  134. flat.properties.forEach(function (v) {
  135. unionProperties.binaryInsert(v.name, true);
  136. });
  137. if (flat.indexSignature) {
  138. var key = "[[".concat(flat.indexSignature.keyType, "]]");
  139. unionProperties.binaryInsert(key, true);
  140. }
  141. }
  142. // Intersection/Union 递归合并unionProperties
  143. else if (schema.type === SchemaType.Intersection || schema.type === SchemaType.Union) {
  144. this._addUnionProperties(unionProperties, schema.members.map(function (v) { return v.type; }));
  145. }
  146. else if (this.isMappedType(schema)) {
  147. this._addUnionProperties(unionProperties, [this.parseMappedType(schema)]);
  148. }
  149. }
  150. return unionProperties;
  151. };
  152. /**
  153. * 将unionProperties 扩展到 InterfaceTypeSchema中(optional的any类型)
  154. * 以此来跳过对它们的检查(用于Intersection/Union)
  155. */
  156. ProtoHelper.prototype.applyUnionProperties = function (schema, unionProperties) {
  157. var newSchema = __assign(__assign({}, schema), { properties: schema.properties.slice() });
  158. var _loop_1 = function (prop) {
  159. if (prop === '[[String]]') {
  160. newSchema.indexSignature = newSchema.indexSignature || {
  161. keyType: SchemaType.String,
  162. type: { type: SchemaType.Any }
  163. };
  164. }
  165. else if (prop === '[[Number]]') {
  166. newSchema.indexSignature = newSchema.indexSignature || {
  167. keyType: SchemaType.Number,
  168. type: { type: SchemaType.Any }
  169. };
  170. }
  171. else if (!schema.properties.find(function (v) { return v.name === prop; })) {
  172. newSchema.properties.push({
  173. id: -1,
  174. name: prop,
  175. optional: true,
  176. type: {
  177. type: SchemaType.Any
  178. }
  179. });
  180. }
  181. };
  182. for (var _i = 0, unionProperties_1 = unionProperties; _i < unionProperties_1.length; _i++) {
  183. var prop = unionProperties_1[_i];
  184. _loop_1(prop);
  185. }
  186. return newSchema;
  187. };
  188. /**
  189. * 将interface及其引用转换为展平的schema
  190. */
  191. ProtoHelper.prototype.getFlatInterfaceSchema = function (schema) {
  192. var uuid = this._getSchemaUuid(schema);
  193. // from cache
  194. if (this._flatInterfaceSchemaCache[uuid]) {
  195. return this._flatInterfaceSchemaCache[uuid];
  196. }
  197. if (this.isTypeReference(schema)) {
  198. var parsed = this.parseReference(schema);
  199. if (parsed.type !== SchemaType.Interface) {
  200. throw new Error("Cannot flatten non interface type: ".concat(parsed.type));
  201. }
  202. this._flatInterfaceSchemaCache[uuid] = this.getFlatInterfaceSchema(parsed);
  203. }
  204. else if (schema.type === SchemaType.Interface) {
  205. this._flatInterfaceSchemaCache[uuid] = this._flattenInterface(schema);
  206. }
  207. else if (this.isMappedType(schema)) {
  208. this._flatInterfaceSchemaCache[uuid] = this._flattenMappedType(schema);
  209. }
  210. else {
  211. // @ts-expect-error
  212. throw new Error('Invalid interface type: ' + schema.type);
  213. }
  214. return this._flatInterfaceSchemaCache[uuid];
  215. };
  216. /**
  217. * 展平interface
  218. */
  219. ProtoHelper.prototype._flattenInterface = function (schema) {
  220. var properties = {};
  221. var indexSignature;
  222. // 自身定义的properties和indexSignature优先级最高
  223. if (schema.properties) {
  224. for (var _i = 0, _a = schema.properties; _i < _a.length; _i++) {
  225. var prop = _a[_i];
  226. properties[prop.name] = {
  227. optional: prop.optional,
  228. type: prop.type
  229. };
  230. }
  231. }
  232. if (schema.indexSignature) {
  233. indexSignature = schema.indexSignature;
  234. }
  235. // extends的优先级次之,补全没有定义的字段
  236. if (schema.extends) {
  237. for (var _b = 0, _c = schema.extends; _b < _c.length; _b++) {
  238. var extend = _c[_b];
  239. // 解引用
  240. var parsedExtRef = this.parseReference(extend.type);
  241. if (this.isMappedType(parsedExtRef)) {
  242. parsedExtRef = this._flattenMappedType(parsedExtRef);
  243. }
  244. if (!this.isInterface(parsedExtRef)) {
  245. throw new Error('SchemaError: extends must from interface but from ' + parsedExtRef.type);
  246. }
  247. // 递归展平extends
  248. var flatenExtendsSchema = this.getFlatInterfaceSchema(parsedExtRef);
  249. // properties
  250. if (flatenExtendsSchema.properties) {
  251. for (var _d = 0, _e = flatenExtendsSchema.properties; _d < _e.length; _d++) {
  252. var prop = _e[_d];
  253. if (!properties[prop.name]) {
  254. properties[prop.name] = {
  255. optional: prop.optional,
  256. type: prop.type
  257. };
  258. }
  259. }
  260. }
  261. // indexSignature
  262. if (flatenExtendsSchema.indexSignature && !indexSignature) {
  263. indexSignature = flatenExtendsSchema.indexSignature;
  264. }
  265. }
  266. }
  267. return {
  268. type: SchemaType.Interface,
  269. properties: Object.entries(properties).map(function (v, i) { return ({
  270. id: i,
  271. name: v[0],
  272. optional: v[1].optional,
  273. type: v[1].type
  274. }); }),
  275. indexSignature: indexSignature
  276. };
  277. };
  278. /** 将MappedTypeSchema转换为展平的Interface
  279. */
  280. ProtoHelper.prototype._flattenMappedType = function (schema) {
  281. // target 解引用
  282. var target;
  283. if (this.isTypeReference(schema.target)) {
  284. var parsed = this.parseReference(schema.target);
  285. target = parsed;
  286. }
  287. else {
  288. target = schema.target;
  289. }
  290. var flatTarget;
  291. // 内层仍然为MappedType 递归之
  292. if (target.type === SchemaType.Pick || target.type === SchemaType.Partial || target.type === SchemaType.Omit || target.type === SchemaType.Overwrite) {
  293. flatTarget = this._flattenMappedType(target);
  294. }
  295. else if (target.type === SchemaType.Interface) {
  296. flatTarget = this._flattenInterface(target);
  297. }
  298. else {
  299. throw new Error("Invalid target.type: ".concat(target.type));
  300. }
  301. // 开始执行Mapped逻辑
  302. if (schema.type === SchemaType.Pick) {
  303. var properties = [];
  304. var _loop_2 = function (key) {
  305. var propItem = flatTarget.properties.find(function (v) { return v.name === key; });
  306. if (propItem) {
  307. properties.push({
  308. id: properties.length,
  309. name: key,
  310. optional: propItem.optional,
  311. type: propItem.type
  312. });
  313. }
  314. else if (flatTarget.indexSignature) {
  315. properties.push({
  316. id: properties.length,
  317. name: key,
  318. type: flatTarget.indexSignature.type
  319. });
  320. }
  321. };
  322. for (var _i = 0, _a = schema.keys; _i < _a.length; _i++) {
  323. var key = _a[_i];
  324. _loop_2(key);
  325. }
  326. return {
  327. type: SchemaType.Interface,
  328. properties: properties
  329. };
  330. }
  331. else if (schema.type === SchemaType.Partial) {
  332. for (var _b = 0, _c = flatTarget.properties; _b < _c.length; _b++) {
  333. var v = _c[_b];
  334. v.optional = true;
  335. }
  336. return flatTarget;
  337. }
  338. else if (schema.type === SchemaType.Omit) {
  339. var _loop_3 = function (key) {
  340. flatTarget.properties.removeOne(function (v) { return v.name === key; });
  341. };
  342. for (var _d = 0, _e = schema.keys; _d < _e.length; _d++) {
  343. var key = _e[_d];
  344. _loop_3(key);
  345. }
  346. return flatTarget;
  347. }
  348. else if (schema.type === SchemaType.Overwrite) {
  349. var overwrite = this.getFlatInterfaceSchema(schema.overwrite);
  350. if (overwrite.indexSignature) {
  351. flatTarget.indexSignature = overwrite.indexSignature;
  352. }
  353. var _loop_4 = function (prop) {
  354. flatTarget.properties.removeOne(function (v) { return v.name === prop.name; });
  355. flatTarget.properties.push(prop);
  356. };
  357. for (var _f = 0, _g = overwrite.properties; _f < _g.length; _f++) {
  358. var prop = _g[_f];
  359. _loop_4(prop);
  360. }
  361. return flatTarget;
  362. }
  363. else {
  364. throw new Error("Unknown type: ".concat(schema.type));
  365. }
  366. };
  367. ProtoHelper.prototype.parseMappedType = function (schema) {
  368. var cache = this._parseMappedTypeCache.get(schema);
  369. if (cache) {
  370. return cache;
  371. }
  372. // 解嵌套,例如:Pick<Pick<Omit, XXX, 'a'|'b'>>>
  373. var parents = [];
  374. var child = schema;
  375. do {
  376. parents.push(child);
  377. child = this.parseReference(child.target);
  378. } while (this.isMappedType(child));
  379. // 最内层是 interface,直接返回(validator 会验证 key 匹配)
  380. if (child.type === SchemaType.Interface) {
  381. this._parseMappedTypeCache.set(schema, child);
  382. return child;
  383. }
  384. // PickOmit<A|B> === PickOmit<A> | PickOmit<B>
  385. else if (child.type === SchemaType.Union || child.type === SchemaType.Intersection) {
  386. var newSchema = {
  387. type: child.type,
  388. members: child.members.map(function (v) {
  389. // 从里面往外装
  390. var type = v.type;
  391. for (var i = parents.length - 1; i > -1; --i) {
  392. var parent_1 = parents[i];
  393. type = __assign(__assign({}, parent_1), { target: type });
  394. }
  395. return {
  396. id: v.id,
  397. type: type
  398. };
  399. })
  400. };
  401. this._parseMappedTypeCache.set(schema, newSchema);
  402. return newSchema;
  403. }
  404. else {
  405. throw new Error("Unsupported pattern ".concat(schema.type, "<").concat(child.type, ">"));
  406. }
  407. };
  408. return ProtoHelper;
  409. }());
  410. var _a;
  411. /** @internal */
  412. var ErrorType;
  413. (function (ErrorType) {
  414. ErrorType["TypeError"] = "TypeError";
  415. ErrorType["InvalidScalarType"] = "InvalidScalarType";
  416. ErrorType["TupleOverLength"] = "TupleOverLength";
  417. ErrorType["InvalidEnumValue"] = "InvalidEnumValue";
  418. ErrorType["InvalidLiteralValue"] = "InvalidLiteralValue";
  419. ErrorType["MissingRequiredProperty"] = "MissingRequiredProperty";
  420. ErrorType["ExcessProperty"] = "ExcessProperty";
  421. ErrorType["InvalidNumberKey"] = "InvalidNumberKey";
  422. ErrorType["UnionTypesNotMatch"] = "UnionTypesNotMatch";
  423. ErrorType["UnionMembersNotMatch"] = "UnionMembersNotMatch";
  424. ErrorType["CustomError"] = "CustomError";
  425. })(ErrorType || (ErrorType = {}));
  426. /** @internal */
  427. var ErrorMsg = (_a = {},
  428. _a[ErrorType.TypeError] = function (expect, actual) { return "Expected type to be `".concat(expect, "`, actually `").concat(actual, "`."); },
  429. _a[ErrorType.InvalidScalarType] = function (value, scalarType) { return "`".concat(value, "` is not a valid `").concat(scalarType, "`."); },
  430. _a[ErrorType.TupleOverLength] = function (valueLength, schemaLength) { return "Value has ".concat(valueLength, " elements but schema allows only ").concat(schemaLength, "."); },
  431. _a[ErrorType.InvalidEnumValue] = function (value) { return "`".concat(value, "` is not a valid enum member."); },
  432. _a[ErrorType.InvalidLiteralValue] = function (expected, actual) { return "Expected to equals `".concat(stringify(expected), "`, actually `").concat(stringify(actual), "`"); },
  433. _a[ErrorType.MissingRequiredProperty] = function (propName) { return "Missing required property `".concat(propName, "`."); },
  434. _a[ErrorType.ExcessProperty] = function (propName) { return "Excess property `".concat(propName, "` should not exists."); },
  435. _a[ErrorType.InvalidNumberKey] = function (key) { return "`".concat(key, "` is not a valid key, the key here should be a `number`."); },
  436. // Union
  437. _a[ErrorType.UnionTypesNotMatch] = function (value, types) { return "`".concat(stringify(value), "` is not matched to `").concat(types.join(' | '), "`"); },
  438. _a[ErrorType.UnionMembersNotMatch] = function (memberErrors) { return "No union member matched, detail:\n".concat(memberErrors.map(function (v, i) { return " <".concat(i, "> ").concat(v.errMsg); }).join('\n')); },
  439. _a[ErrorType.CustomError] = function (errMsg) { return errMsg; },
  440. _a);
  441. /** @internal */
  442. function stringify(value) {
  443. if (typeof value === 'string') {
  444. var output = JSON.stringify(value);
  445. return "'" + output.substr(1, output.length - 2) + "'";
  446. }
  447. return JSON.stringify(value);
  448. }
  449. /** @internal */
  450. var ValidateResultError = /** @class */ (function () {
  451. function ValidateResultError(error) {
  452. this.isSucc = false;
  453. this.error = error;
  454. }
  455. Object.defineProperty(ValidateResultError.prototype, "errMsg", {
  456. get: function () {
  457. return ValidateResultError.getErrMsg(this.error);
  458. },
  459. enumerable: false,
  460. configurable: true
  461. });
  462. ValidateResultError.getErrMsg = function (error) {
  463. var _a;
  464. var errMsg = ErrorMsg[error.type].apply(ErrorMsg, error.params);
  465. if ((_a = error.inner) === null || _a === void 0 ? void 0 : _a.property.length) {
  466. return "Property `".concat(error.inner.property.join('.'), "`: ").concat(errMsg);
  467. }
  468. else {
  469. return errMsg;
  470. }
  471. };
  472. return ValidateResultError;
  473. }());
  474. /** @internal */
  475. var ValidateResultUtil = /** @class */ (function () {
  476. function ValidateResultUtil() {
  477. }
  478. ValidateResultUtil.error = function (type) {
  479. var params = [];
  480. for (var _i = 1; _i < arguments.length; _i++) {
  481. params[_i - 1] = arguments[_i];
  482. }
  483. return new ValidateResultError({
  484. type: type,
  485. params: params
  486. });
  487. };
  488. ValidateResultUtil.innerError = function (property, value, schema, error) {
  489. var _a;
  490. if (error.error.inner) {
  491. if (typeof property === 'string') {
  492. error.error.inner.property.unshift(property);
  493. }
  494. else {
  495. (_a = error.error.inner.property).unshift.apply(_a, property);
  496. }
  497. }
  498. else {
  499. error.error.inner = {
  500. property: typeof property === 'string' ? [property] : property,
  501. value: value,
  502. schema: schema
  503. };
  504. }
  505. return error;
  506. };
  507. ValidateResultUtil.succ = { isSucc: true };
  508. return ValidateResultUtil;
  509. }());
  510. var typedArrays = {
  511. Int8Array: Int8Array,
  512. Int16Array: Int16Array,
  513. Int32Array: Int32Array,
  514. BigInt64Array: typeof BigInt64Array !== 'undefined' ? BigInt64Array : undefined,
  515. Uint8Array: Uint8Array,
  516. Uint16Array: Uint16Array,
  517. Uint32Array: Uint32Array,
  518. BigUint64Array: typeof BigUint64Array !== 'undefined' ? BigUint64Array : undefined,
  519. Float32Array: Float32Array,
  520. Float64Array: Float64Array
  521. };
  522. /**
  523. * TSBuffer Schema Validator
  524. * @public
  525. */
  526. var TSBufferValidator = /** @class */ (function () {
  527. function TSBufferValidator(proto, options) {
  528. /**
  529. * Default options
  530. */
  531. this.options = {
  532. excessPropertyChecks: true,
  533. strictNullChecks: false,
  534. cloneProto: true
  535. };
  536. if (options) {
  537. this.options = __assign(__assign({}, this.options), options);
  538. }
  539. this.proto = this.options.cloneProto ? Object.merge({}, proto) : proto;
  540. this.protoHelper = new ProtoHelper(this.proto);
  541. }
  542. /**
  543. * Validate whether the value is valid to the schema
  544. * @param value - Value to be validated.
  545. * @param schemaId - Schema or schema ID.
  546. * For example, the schema ID for type `Test` in `a/b.ts` may be `a/b/Test`.
  547. */
  548. TSBufferValidator.prototype.validate = function (value, schemaOrId, options) {
  549. var _a, _b;
  550. var schema;
  551. var schemaId;
  552. // Get schema
  553. if (typeof schemaOrId === 'string') {
  554. schemaId = schemaOrId;
  555. schema = this.proto[schemaId];
  556. if (!schema) {
  557. throw new Error("Cannot find schema: ".concat(schemaId));
  558. }
  559. }
  560. else {
  561. schema = schemaOrId;
  562. }
  563. // Merge default options
  564. return this._validate(value, schema, __assign(__assign({}, options), { excessPropertyChecks: (_a = options === null || options === void 0 ? void 0 : options.excessPropertyChecks) !== null && _a !== void 0 ? _a : this.options.excessPropertyChecks, strictNullChecks: (_b = options === null || options === void 0 ? void 0 : options.strictNullChecks) !== null && _b !== void 0 ? _b : this.options.strictNullChecks }));
  565. };
  566. TSBufferValidator.prototype._validate = function (value, schema, options) {
  567. var _a;
  568. var vRes;
  569. // Validate
  570. switch (schema.type) {
  571. case SchemaType.Boolean:
  572. vRes = this._validateBooleanType(value, schema);
  573. break;
  574. case SchemaType.Number:
  575. vRes = this._validateNumberType(value, schema);
  576. break;
  577. case SchemaType.String:
  578. vRes = this._validateStringType(value, schema);
  579. break;
  580. case SchemaType.Array:
  581. vRes = this._validateArrayType(value, schema, options);
  582. break;
  583. case SchemaType.Tuple:
  584. vRes = this._validateTupleType(value, schema, options);
  585. break;
  586. case SchemaType.Enum:
  587. vRes = this._validateEnumType(value, schema);
  588. break;
  589. case SchemaType.Any:
  590. vRes = this._validateAnyType(value);
  591. break;
  592. case SchemaType.Literal:
  593. vRes = this._validateLiteralType(value, schema, (_a = options === null || options === void 0 ? void 0 : options.strictNullChecks) !== null && _a !== void 0 ? _a : this.options.strictNullChecks);
  594. break;
  595. case SchemaType.Object:
  596. vRes = this._validateObjectType(value, schema);
  597. break;
  598. case SchemaType.Interface:
  599. vRes = this._validateInterfaceType(value, schema, options);
  600. break;
  601. case SchemaType.Buffer:
  602. vRes = this._validateBufferType(value, schema);
  603. break;
  604. case SchemaType.IndexedAccess:
  605. case SchemaType.Reference:
  606. case SchemaType.Keyof:
  607. vRes = this._validateReferenceType(value, schema, options);
  608. break;
  609. case SchemaType.Union:
  610. vRes = this._validateUnionType(value, schema, options);
  611. break;
  612. case SchemaType.Intersection:
  613. vRes = this._validateIntersectionType(value, schema, options);
  614. break;
  615. case SchemaType.Pick:
  616. case SchemaType.Omit:
  617. case SchemaType.Partial:
  618. case SchemaType.Overwrite:
  619. vRes = this._validateMappedType(value, schema, options);
  620. break;
  621. case SchemaType.Date:
  622. vRes = this._validateDateType(value);
  623. break;
  624. case SchemaType.NonNullable:
  625. vRes = this._validateNonNullableType(value, schema, options);
  626. break;
  627. case SchemaType.Custom:
  628. var res = schema.validate(value);
  629. vRes = res.isSucc ? ValidateResultUtil.succ : ValidateResultUtil.error(ErrorType.CustomError, res.errMsg);
  630. break;
  631. // 错误的type
  632. default:
  633. // @ts-expect-error
  634. throw new Error("Unsupported schema type: ".concat(schema.type));
  635. }
  636. // prune
  637. if (options === null || options === void 0 ? void 0 : options.prune) {
  638. // don't need prune, return original value
  639. if (options.prune.output === undefined) {
  640. options.prune.output = value;
  641. }
  642. // output to parent
  643. if (options.prune.parent) {
  644. options.prune.parent.value[options.prune.parent.key] = options.prune.output;
  645. }
  646. }
  647. return vRes;
  648. };
  649. /**
  650. * 修剪 Object,移除 Schema 中未定义的 Key
  651. * 需要确保 value 类型合法
  652. * @param value - value to be validated
  653. * @param schemaOrId -Schema or schema ID.
  654. * @returns Validate result and pruned value. if validate failed, `pruneOutput` would be undefined.
  655. */
  656. TSBufferValidator.prototype.prune = function (value, schemaOrId, options) {
  657. var _a;
  658. var schema = typeof schemaOrId === 'string' ? this.proto[schemaOrId] : schemaOrId;
  659. if (!schema) {
  660. throw new Error('Cannot find schema: ' + schemaOrId);
  661. }
  662. var prune = {};
  663. var vRes = this._validate(value, schema, __assign(__assign({}, options), { prune: prune, excessPropertyChecks: false, strictNullChecks: (_a = options === null || options === void 0 ? void 0 : options.strictNullChecks) !== null && _a !== void 0 ? _a : this.options.strictNullChecks }));
  664. if (vRes.isSucc) {
  665. vRes.pruneOutput = prune.output;
  666. }
  667. return vRes;
  668. };
  669. TSBufferValidator.prototype._validateBooleanType = function (value, schema) {
  670. var type = this._getTypeof(value);
  671. if (type === 'boolean') {
  672. return ValidateResultUtil.succ;
  673. }
  674. else {
  675. return ValidateResultUtil.error(ErrorType.TypeError, 'boolean', type);
  676. }
  677. };
  678. TSBufferValidator.prototype._validateNumberType = function (value, schema) {
  679. // 默认为double
  680. var scalarType = schema.scalarType || 'double';
  681. // Wrong Type
  682. var type = this._getTypeof(value);
  683. var rightType = scalarType.indexOf('big') > -1 ? 'bigint' : 'number';
  684. if (type !== rightType) {
  685. return ValidateResultUtil.error(ErrorType.TypeError, rightType, type);
  686. }
  687. // scalarType类型检测
  688. // 整形却为小数
  689. if (scalarType !== 'double' && type === 'number' && !Number.isInteger(value)) {
  690. return ValidateResultUtil.error(ErrorType.InvalidScalarType, value, scalarType);
  691. }
  692. // 无符号整形却为负数
  693. if (scalarType.indexOf('uint') > -1 && value < 0) {
  694. return ValidateResultUtil.error(ErrorType.InvalidScalarType, value, scalarType);
  695. }
  696. return ValidateResultUtil.succ;
  697. };
  698. TSBufferValidator.prototype._validateStringType = function (value, schema) {
  699. var type = this._getTypeof(value);
  700. return type === 'string' ? ValidateResultUtil.succ : ValidateResultUtil.error(ErrorType.TypeError, 'string', type);
  701. };
  702. TSBufferValidator.prototype._validateArrayType = function (value, schema, options) {
  703. // is Array type
  704. var type = this._getTypeof(value);
  705. if (type !== SchemaType.Array) {
  706. return ValidateResultUtil.error(ErrorType.TypeError, SchemaType.Array, type);
  707. }
  708. // prune output
  709. var prune = options.prune;
  710. if (prune) {
  711. prune.output = Array.from({ length: value.length });
  712. }
  713. // validate elementType
  714. for (var i = 0; i < value.length; ++i) {
  715. var elemValidateResult = this._validate(value[i], schema.elementType, __assign(__assign({}, options), { prune: (prune === null || prune === void 0 ? void 0 : prune.output) ? {
  716. parent: {
  717. value: prune.output,
  718. key: i
  719. }
  720. } : undefined }));
  721. if (!elemValidateResult.isSucc) {
  722. return ValidateResultUtil.innerError('' + i, value[i], schema.elementType, elemValidateResult);
  723. }
  724. }
  725. return ValidateResultUtil.succ;
  726. };
  727. TSBufferValidator.prototype._validateTupleType = function (value, schema, options) {
  728. // is Array type
  729. var type = this._getTypeof(value);
  730. if (type !== SchemaType.Array) {
  731. return ValidateResultUtil.error(ErrorType.TypeError, SchemaType.Array, type);
  732. }
  733. var prune = options.prune;
  734. // validate length
  735. // excessPropertyChecks 与 prune互斥
  736. if (!prune && options.excessPropertyChecks && value.length > schema.elementTypes.length) {
  737. return ValidateResultUtil.error(ErrorType.TupleOverLength, value.length, schema.elementTypes.length);
  738. }
  739. // prune output
  740. if (prune) {
  741. prune.output = Array.from({ length: Math.min(value.length, schema.elementTypes.length) });
  742. }
  743. // validate elementType
  744. for (var i = 0; i < schema.elementTypes.length; ++i) {
  745. // MissingRequiredProperty: NotOptional && is undefined
  746. if (value[i] === undefined || value[i] === null && !options.strictNullChecks) {
  747. var canBeNull = this._canBeNull(schema.elementTypes[i]);
  748. var canBeUndefined = schema.optionalStartIndex !== undefined && i >= schema.optionalStartIndex || this._canBeUndefined(schema.elementTypes[i]);
  749. var isOptional = canBeUndefined || !options.strictNullChecks && canBeNull;
  750. // skip undefined property
  751. if (isOptional) {
  752. // Prune null & undefined->null
  753. if (prune === null || prune === void 0 ? void 0 : prune.output) {
  754. if (value[i] === null && canBeNull
  755. || value[i] === undefined && !canBeUndefined && canBeNull) {
  756. prune.output[i] = null;
  757. }
  758. }
  759. continue;
  760. }
  761. else {
  762. return ValidateResultUtil.error(ErrorType.MissingRequiredProperty, i);
  763. }
  764. }
  765. // element type check
  766. var elemValidateResult = this._validate(value[i], schema.elementTypes[i], {
  767. prune: (prune === null || prune === void 0 ? void 0 : prune.output) ? {
  768. parent: {
  769. value: prune.output,
  770. key: i
  771. }
  772. } : undefined,
  773. strictNullChecks: options.strictNullChecks,
  774. excessPropertyChecks: options.excessPropertyChecks
  775. });
  776. if (!elemValidateResult.isSucc) {
  777. return ValidateResultUtil.innerError('' + i, value[i], schema.elementTypes[i], elemValidateResult);
  778. }
  779. }
  780. return ValidateResultUtil.succ;
  781. };
  782. TSBufferValidator.prototype._canBeUndefined = function (schema) {
  783. var _this = this;
  784. if (schema.type === SchemaType.Union) {
  785. return schema.members.some(function (v) { return _this._canBeUndefined(v.type); });
  786. }
  787. if (schema.type === SchemaType.Literal && schema.literal === undefined) {
  788. return true;
  789. }
  790. return false;
  791. };
  792. TSBufferValidator.prototype._canBeNull = function (schema) {
  793. var _this = this;
  794. if (schema.type === SchemaType.Union) {
  795. return schema.members.some(function (v) { return _this._canBeNull(v.type); });
  796. }
  797. if (schema.type === SchemaType.Literal && schema.literal === null) {
  798. return true;
  799. }
  800. return false;
  801. };
  802. TSBufferValidator.prototype._validateEnumType = function (value, schema) {
  803. // must be string or number
  804. var type = this._getTypeof(value);
  805. if (type !== 'string' && type !== 'number') {
  806. return ValidateResultUtil.error(ErrorType.TypeError, 'string | number', type);
  807. }
  808. // 有值与预设相同
  809. if (schema.members.some(function (v) { return v.value === value; })) {
  810. return ValidateResultUtil.succ;
  811. }
  812. else {
  813. return ValidateResultUtil.error(ErrorType.InvalidEnumValue, value);
  814. }
  815. };
  816. TSBufferValidator.prototype._validateAnyType = function (value) {
  817. return ValidateResultUtil.succ;
  818. };
  819. TSBufferValidator.prototype._validateLiteralType = function (value, schema, strictNullChecks) {
  820. // 非strictNullChecks严格模式,null undefined同等对待
  821. if (!strictNullChecks && (schema.literal === null || schema.literal === undefined)) {
  822. return value === null || value === undefined ?
  823. ValidateResultUtil.succ
  824. : ValidateResultUtil.error(ErrorType.InvalidLiteralValue, schema.literal, value);
  825. }
  826. return value === schema.literal ?
  827. ValidateResultUtil.succ
  828. : ValidateResultUtil.error(ErrorType.InvalidLiteralValue, schema.literal, value);
  829. };
  830. TSBufferValidator.prototype._validateObjectType = function (value, schema) {
  831. var type = this._getTypeof(value);
  832. return type === 'Object' || type === 'Array' ? ValidateResultUtil.succ : ValidateResultUtil.error(ErrorType.TypeError, 'Object', type);
  833. };
  834. TSBufferValidator.prototype._validateInterfaceType = function (value, schema, options) {
  835. var type = this._getTypeof(value);
  836. if (type !== 'Object') {
  837. return ValidateResultUtil.error(ErrorType.TypeError, 'Object', type);
  838. }
  839. // 先展平
  840. var flatSchema = this.protoHelper.getFlatInterfaceSchema(schema);
  841. // From union or intersecton type
  842. if (options.unionProperties) {
  843. flatSchema = this.protoHelper.applyUnionProperties(flatSchema, options.unionProperties);
  844. }
  845. return this._validateFlatInterface(value, flatSchema, options);
  846. };
  847. TSBufferValidator.prototype._validateMappedType = function (value, schema, options) {
  848. var parsed = this.protoHelper.parseMappedType(schema);
  849. if (parsed.type === SchemaType.Interface) {
  850. return this._validateInterfaceType(value, schema, options);
  851. }
  852. else if (parsed.type === SchemaType.Union) {
  853. return this._validateUnionType(value, parsed, options);
  854. }
  855. else if (parsed.type === SchemaType.Intersection) {
  856. return this._validateIntersectionType(value, parsed, options);
  857. }
  858. // @ts-expect-error
  859. throw new Error("Invalid ".concat(schema.type, " target type: ").concat(parsed.type));
  860. };
  861. TSBufferValidator.prototype._validateFlatInterface = function (value, schema, options) {
  862. // interfaceSignature强制了key必须是数字的情况
  863. if (schema.indexSignature && schema.indexSignature.keyType === SchemaType.Number) {
  864. for (var key in value) {
  865. if (!this._isNumberKey(key)) {
  866. return ValidateResultUtil.error(ErrorType.InvalidNumberKey, key);
  867. }
  868. }
  869. }
  870. var prune = options.prune;
  871. if (prune) {
  872. prune.output = {};
  873. }
  874. // Excess property check (与prune互斥)
  875. if (!prune && options.excessPropertyChecks && !schema.indexSignature) {
  876. var validProperties_1 = schema.properties.map(function (v) { return v.name; });
  877. var firstExcessProperty = Object.keys(value).find(function (v) { return validProperties_1.indexOf(v) === -1; });
  878. if (firstExcessProperty) {
  879. return ValidateResultUtil.error(ErrorType.ExcessProperty, firstExcessProperty);
  880. }
  881. }
  882. // 校验properties
  883. if (schema.properties) {
  884. for (var _i = 0, _a = schema.properties; _i < _a.length; _i++) {
  885. var property = _a[_i];
  886. // MissingRequiredProperty: is undefined && !isOptional
  887. if (value[property.name] === undefined || value[property.name] === null && !options.strictNullChecks) {
  888. var canBeNull = this._canBeNull(property.type);
  889. var canBeUndefined = property.optional || this._canBeUndefined(property.type);
  890. var isOptional = canBeUndefined || !options.strictNullChecks && canBeNull;
  891. // skip undefined optional property
  892. if (isOptional) {
  893. // Prune null & undefined->null
  894. if (prune === null || prune === void 0 ? void 0 : prune.output) {
  895. if (value[property.name] === null && canBeNull
  896. || value[property.name] === undefined && !canBeUndefined && canBeNull) {
  897. prune.output[property.name] = null;
  898. }
  899. }
  900. continue;
  901. }
  902. else {
  903. return ValidateResultUtil.error(ErrorType.MissingRequiredProperty, property.name);
  904. }
  905. }
  906. // property本身验证
  907. var vRes = this._validate(value[property.name], property.type, {
  908. prune: (prune === null || prune === void 0 ? void 0 : prune.output) && property.id > -1 ? {
  909. parent: {
  910. value: prune.output,
  911. key: property.name
  912. }
  913. } : undefined,
  914. strictNullChecks: options.strictNullChecks,
  915. excessPropertyChecks: options.excessPropertyChecks
  916. });
  917. if (!vRes.isSucc) {
  918. return ValidateResultUtil.innerError(property.name, value[property.name], property.type, vRes);
  919. }
  920. }
  921. }
  922. // 检测indexSignature
  923. if (schema.indexSignature) {
  924. for (var key in value) {
  925. // only prune is (property is pruned already)
  926. // let memberPrune: ValidatePruneOptions | undefined = schema.properties.some(v => v.name === key) ? undefined : {};
  927. // validate each field
  928. var vRes = this._validate(value[key], schema.indexSignature.type, {
  929. prune: (prune === null || prune === void 0 ? void 0 : prune.output) ? {
  930. parent: {
  931. value: prune.output,
  932. key: key
  933. }
  934. } : undefined,
  935. strictNullChecks: options.strictNullChecks,
  936. excessPropertyChecks: options.excessPropertyChecks
  937. });
  938. if (!vRes.isSucc) {
  939. return ValidateResultUtil.innerError(key, value[key], schema.indexSignature.type, vRes);
  940. }
  941. }
  942. }
  943. return ValidateResultUtil.succ;
  944. };
  945. TSBufferValidator.prototype._validateBufferType = function (value, schema) {
  946. var _a, _b;
  947. var type = this._getTypeof(value);
  948. if (type !== 'Object') {
  949. return ValidateResultUtil.error(ErrorType.TypeError, schema.arrayType || 'ArrayBuffer', type);
  950. }
  951. else if (schema.arrayType) {
  952. var typeArrayClass = typedArrays[schema.arrayType];
  953. if (!typeArrayClass) {
  954. throw new Error("Error TypedArray type: ".concat(schema.arrayType));
  955. }
  956. return value instanceof typeArrayClass ? ValidateResultUtil.succ : ValidateResultUtil.error(ErrorType.TypeError, schema.arrayType, (_a = value === null || value === void 0 ? void 0 : value.constructor) === null || _a === void 0 ? void 0 : _a.name);
  957. }
  958. else {
  959. return value instanceof ArrayBuffer ? ValidateResultUtil.succ : ValidateResultUtil.error(ErrorType.TypeError, 'ArrayBuffer', (_b = value === null || value === void 0 ? void 0 : value.constructor) === null || _b === void 0 ? void 0 : _b.name);
  960. }
  961. };
  962. TSBufferValidator.prototype._validateReferenceType = function (value, schema, options) {
  963. return this._validate(value, this.protoHelper.parseReference(schema), options);
  964. };
  965. TSBufferValidator.prototype._validateUnionType = function (value, schema, options) {
  966. var _this = this;
  967. options.unionProperties = options.unionProperties || this.protoHelper.getUnionProperties(schema);
  968. var isObjectPrune = false;
  969. var prune = options.prune;
  970. if (prune && value && Object.getPrototypeOf(value) === Object.prototype) {
  971. isObjectPrune = true;
  972. prune.output = {};
  973. }
  974. // 有一成功则成功
  975. var isSomeSucc = false;
  976. var memberErrors = [];
  977. for (var i = 0; i < schema.members.length; ++i) {
  978. var member = schema.members[i];
  979. var memberType = this.protoHelper.isTypeReference(member.type) ? this.protoHelper.parseReference(member.type) : member.type;
  980. var memberPrune = prune ? {} : undefined;
  981. var vRes = this._validate(value, memberType, __assign(__assign({}, options), { prune: memberPrune }));
  982. if (vRes.isSucc) {
  983. isSomeSucc = true;
  984. // if prune object: must prune all members
  985. if (isObjectPrune) {
  986. prune.output = __assign(__assign({}, prune.output), memberPrune.output);
  987. }
  988. // not prune object: stop checking after 1st member matched
  989. else {
  990. break;
  991. }
  992. }
  993. else {
  994. memberErrors.push(vRes);
  995. }
  996. }
  997. // 有一成功则成功;
  998. if (isSomeSucc) {
  999. return ValidateResultUtil.succ;
  1000. }
  1001. // 全部失败,则失败
  1002. else {
  1003. // All member error is the same, return the first
  1004. var msg0_1 = memberErrors[0].errMsg;
  1005. if (memberErrors.every(function (v) { return v.errMsg === msg0_1; })) {
  1006. return memberErrors[0];
  1007. }
  1008. // mutual exclusion: return the only one
  1009. var nonLiteralErrors = memberErrors.filter(function (v) { return v.error.type !== ErrorType.InvalidLiteralValue; });
  1010. if (nonLiteralErrors.length === 1) {
  1011. return nonLiteralErrors[0];
  1012. }
  1013. // All member error without inner: show simple msg
  1014. if (memberErrors.every(function (v) { return !v.error.inner && (v.error.type === ErrorType.TypeError || v.error.type === ErrorType.InvalidLiteralValue); })) {
  1015. var valueType = this._getTypeof(value);
  1016. var expectedTypes = memberErrors.map(function (v) { return v.error.type === ErrorType.TypeError ? v.error.params[0] : _this._getTypeof(v.error.params[0]); }).distinct();
  1017. // Expected type A|B|C, actually type D
  1018. if (expectedTypes.indexOf(valueType) === -1) {
  1019. return ValidateResultUtil.error(ErrorType.TypeError, expectedTypes.join(' | '), this._getTypeof(value));
  1020. }
  1021. // `'D'` is not matched to `'A'|'B'|'C'`
  1022. if (valueType !== 'Object' && valueType !== SchemaType.Array) {
  1023. var types = memberErrors.map(function (v) { return v.error.type === ErrorType.TypeError ? v.error.params[0] : stringify(v.error.params[0]); }).distinct();
  1024. return ValidateResultUtil.error(ErrorType.UnionTypesNotMatch, value, types);
  1025. }
  1026. }
  1027. // other errors
  1028. return ValidateResultUtil.error(ErrorType.UnionMembersNotMatch, memberErrors);
  1029. }
  1030. };
  1031. TSBufferValidator.prototype._validateIntersectionType = function (value, schema, options) {
  1032. options.unionProperties = options.unionProperties || this.protoHelper.getUnionProperties(schema);
  1033. var isObjectPrune = false;
  1034. var prune = options.prune;
  1035. if (prune && value && Object.getPrototypeOf(value) === Object.prototype) {
  1036. prune.output = {};
  1037. isObjectPrune = true;
  1038. }
  1039. // 有一失败则失败
  1040. for (var i = 0, len = schema.members.length; i < len; ++i) {
  1041. // 验证member
  1042. var memberType = schema.members[i].type;
  1043. memberType = this.protoHelper.isTypeReference(memberType) ? this.protoHelper.parseReference(memberType) : memberType;
  1044. var memberPrune = prune ? {} : undefined;
  1045. var vRes = this._validate(value, memberType, __assign(__assign({}, options), { prune: memberPrune }));
  1046. // 有一失败则失败
  1047. if (!vRes.isSucc) {
  1048. return vRes;
  1049. }
  1050. if (isObjectPrune) {
  1051. prune.output = __assign(__assign({}, prune.output), memberPrune.output);
  1052. }
  1053. }
  1054. // 全成功则成功
  1055. return ValidateResultUtil.succ;
  1056. };
  1057. TSBufferValidator.prototype._validateDateType = function (value) {
  1058. if (value instanceof Date) {
  1059. return ValidateResultUtil.succ;
  1060. }
  1061. else {
  1062. return ValidateResultUtil.error(ErrorType.TypeError, 'Date', this._getTypeof(value));
  1063. }
  1064. };
  1065. TSBufferValidator.prototype._validateNonNullableType = function (value, schema, options) {
  1066. var type = this._getTypeof(value);
  1067. if ((type === 'null' || type === 'undefined') && schema.target.type !== 'Any') {
  1068. return ValidateResultUtil.error(ErrorType.TypeError, 'NonNullable', type);
  1069. }
  1070. return this._validate(value, schema.target, options);
  1071. };
  1072. TSBufferValidator.prototype._isNumberKey = function (key) {
  1073. var int = parseInt(key);
  1074. return !(isNaN(int) || ('' + int) !== key);
  1075. };
  1076. TSBufferValidator.prototype._getTypeof = function (value) {
  1077. var type = typeof value;
  1078. if (type === 'object') {
  1079. if (value === null) {
  1080. return 'null';
  1081. }
  1082. else if (Array.isArray(value)) {
  1083. return SchemaType.Array;
  1084. }
  1085. else {
  1086. return 'Object';
  1087. }
  1088. }
  1089. return type;
  1090. };
  1091. return TSBufferValidator;
  1092. }());
  1093. export { ProtoHelper, TSBufferValidator };