diff --git a/Dockerfile b/Dockerfile index a6e8b01..d6f9983 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,4 +11,6 @@ WORKDIR /app RUN npm config set registry http://registry.npm.taobao.org/ && npm install pm2@5.2.2 -g +RUN rm -rf node_modules/tsbuffer-validator/index.js && cp tsbufferIndex.js node_modules/tsbuffer-validator/index.js + CMD cd /app && sh zoeninfo.sh "Asia/Tokyo" && pm2-runtime start js_pm2.config.js -- ${Param} diff --git a/package.json b/package.json index fb00982..d55ec03 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,9 @@ "private": true, "scripts": { "dev": "tsrpc-cli dev", - "build": "tsrpc-cli build && copy js_pm2.config.js dist && copy zoeninfo.sh dist && mkdir dist\\json && xcopy src\\json dist\\json /e ", - "build_win": "tsrpc-cli build && copy js_pm2.config.js dist && copy zoeninfo.sh dist && mkdir dist\\json && xcopy src\\json dist\\json /e ", - "build_linux": "tsrpc-cli build && cp js_pm2.config.js dist && cp zoeninfo.sh dist && cp src/json/ -a dist ", + "build": "tsrpc-cli build && copy js_pm2.config.js dist && copy zoeninfo.sh dist && copy tsbufferIndex.js dist && mkdir dist\\json && xcopy src\\json dist\\json /e ", + "build_win": "tsrpc-cli build && copy js_pm2.config.js dist && copy zoeninfo.sh dist && copy tsbufferIndex.js dist && mkdir dist\\json && xcopy src\\json dist\\json /e ", + "build_linux": "tsrpc-cli build && cp js_pm2.config.js dist && cp zoeninfo.sh dist && cp tsbufferIndex.js dist && cp src/json/ -a dist ", "build_docker_win": "npm run build_win && cd dist && npm i --omit=dev", "build_docker_linux": "npm run build_linux && cd dist && npm i --omit=dev", "doc": "tsrpc-cli doc && node js_uploadDoc.js", diff --git a/src/api_s2c/gonglukuangbiao/ApiOpen.ts b/src/api_s2c/gonglukuangbiao/ApiOpen.ts index 419f224..98fcb44 100644 --- a/src/api_s2c/gonglukuangbiao/ApiOpen.ts +++ b/src/api_s2c/gonglukuangbiao/ApiOpen.ts @@ -50,19 +50,44 @@ export default async function (call: ApiCall) { call.succ(db as any); } -export async function refreshEnemys(call: ApiCall) { - let section = [call.conn.gud.mapId - 8, call.conn.gud.mapId + 8]; - //let allUser = Object.values(await G.redis.get('user')).filter(u => u.mapId >= section[0] && u.mapId <= section[1] && u.uid != call.uid); - //let dbUser = await G.mongodb.collection('wanted').find({uid: {$in: allUser.map(u => u.uid)}}).toArray(); +let lastRefreshTime: number +let enemyObject: object = {} - let allUser = await G.mongodb.find('user', { - $and: [ - {mapId: {$gte: section[0]}}, - {mapId: {$lte: section[1]}}, - {uid: {$ne: call.uid}} - ] - }, ['uid']); - let dbUser = await G.mongodb.find('wanted', {uid: {$in: allUser.map(u => u.uid)}}, ['uid', 'wanted']); +/** + * 每小时刷新公路狂飙用户,功能超过30级开放,只查询30级以上的用户,超过80级的,80级到100级为一档 + */ +async function checkAndRefreshEnemy() { + if (lastRefreshTime + 3600 > G.time) return + lastRefreshTime = G.time + for (let i = 3; i <= 8; i++) { + let users = await G.mongodb.collection('user').aggregate([ + { + $match: { + $and: [ + {lv: {$gte: i * 10}}, + {lv: {$lt: i == 8 ? 101 : (i + 1) * 10}}, + ] + } + }, + { + $project: {uid: 1} + }, + { + $sample: {size: 100} + } + ]).toArray() + let dbUser = await G.mongodb.find('wanted', {uid: {$in: users.map(u => u.uid)}}, ['uid', 'wanted']); + + enemyObject[i] = dbUser || [] + } +} + +export async function refreshEnemys(call: ApiCall) { + if (!lastRefreshTime) await checkAndRefreshEnemy() + + let lvDw = Math.floor(call.conn.gud.lv / 10 > 8 ? 8 : call.conn.gud.lv / 10) + + let dbUser = enemyObject[lvDw] || [] let enemys: joinFightData[] = []; @@ -74,7 +99,6 @@ export async function refreshEnemys(call: ApiCall) { let filter = dbUser.filter(u => conf.need <= u.wanted && u.wanted < needMax); let uids = filter.map(f => f.uid); dbUser.remove(u => uids.includes(u.uid)); - let a = configs[0].npc.random() let enemy = uids.length > 0 ? await FightFun.getPlayerFightData(uids.random()) : formatNpcData(configs[0].npc.random()); if (!enemy) enemy = formatNpcData(configs[0].npc.random()) if (enemy.player.isNpc) enemy.player.name = G.gc.gonglukuangbiao.npcname.random() @@ -83,7 +107,7 @@ export async function refreshEnemys(call: ApiCall) { PublicShared.randomNum(G.gc.gonglukuangbiao.wanted[0].wanted[0], G.gc.gonglukuangbiao.wanted[0].wanted[1]) enemys.push(enemy); } - + checkAndRefreshEnemy() return enemys.reverse(); } diff --git a/src/clusterUtils.ts b/src/clusterUtils.ts index 3836098..05b3006 100644 --- a/src/clusterUtils.ts +++ b/src/clusterUtils.ts @@ -2,13 +2,14 @@ import {RedisClientType, createClient} from "redis"; import {redisClient} from "./setRedis"; import {ConnectionStatus, WsServer} from "tsrpc"; import cluster from 'cluster'; -import { clusterFun } from "./clusterFunction"; +import {clusterFun} from "./clusterFunction"; //维护当前uid和pid的对应关系 let uid2processId = {}; //订阅redis let subscribeRedis: RedisClientType let firstPid = null; + //发布和可写redis复用原 setRedis 里的redisClient @@ -20,10 +21,10 @@ let firstPid = null; */ export function clusterSubscribe(key: string, callback: Function) { subscribeRedis.subscribe(G.redis.fromatKey(key), function (msg) { - try{ + try { callback(msg); - }catch(e){ - console.error("clusterSubscribe error",e); + } catch (e) { + console.error("clusterSubscribe error", e); } }); } @@ -43,14 +44,19 @@ export function clusterPublish(key: string, data: any) { * 在集群的N个进程中,只运行一次,在业务逻辑中也可使用 */ export function clusterRunOnce(fun) { + console.log(`${process.pid}环境变量pm_id===>${process.env.pm_id}`); if (process.env.pm_id == null || process.env.pm_id === '0') { //非pm2启动的,或是pm2下启动的第一个进程 + console.log("run clusterRunOnce1 ===>", process.pid) fun(); return; } - if(firstPid == process.pid){ + if(G.config.isG123)return; + + if (firstPid == process.pid) { //pm2的其中一个进程 + console.log("run clusterRunOnce2 ===>", process.pid) fun(); return; } @@ -60,16 +66,16 @@ export function clusterRunOnce(fun) { * 对外方法: * 在uid所在的进程执行fun方法,如果uid不在任何进程的话,则在当前进程执行 */ -export function clusterFunctionRunAtUidProcess(uid:string, fun:string, ...arg:any[]) { - if(!uid2processId[uid] || uid2processId[uid] == process.pid){ - clusterFun[ fun ].call(this, ...arg); +export function clusterFunctionRunAtUidProcess(uid: string, fun: string, ...arg: any[]) { + if (!uid2processId[uid] || uid2processId[uid] == process.pid) { + clusterFun[fun].call(this, ...arg); return; } clusterPublish("RunclusterFunction", JSON.stringify({ "uid": uid, "fun": fun, - "arg" : arg + "arg": arg })) } @@ -159,17 +165,17 @@ async function initSubscribeRedis() { clusterSubscribe('broadcastClusterMsg', function (msg) { let data = JSON.parse(msg); - if(!data.filter){ + if (!data.filter) { //如果不是有条件发送的话 G.server.broadcastMsg(data.msgName, data.msg); - }else { + } else { //但是如果是有条件发送的话 //条件里可能需要进程内的信息,则需要每个进程都分别筛选后执行 if (data.filter.ghId != null) { //指定公会 let conns = G.server.connections; conns = conns.filter(c => c?.gud?.ghId == data.filter.ghId); - conns.length>0 && G.server.broadcastMsg(data.msgName, data.msg, conns); + conns.length > 0 && G.server.broadcastMsg(data.msgName, data.msg, conns); } } }); @@ -178,7 +184,7 @@ async function initSubscribeRedis() { clusterSubscribe('RunclusterFunction', function (msg) { let data = JSON.parse(msg); if (uid2processId[data.uid] == process.pid) { - clusterFun[ data.fun ].call(this, ...data.arg); + clusterFun[data.fun].call(this, ...data.arg); } }); } @@ -193,8 +199,8 @@ export async function clusterMain() { }); //初始化订阅redis await initSubscribeRedis(); - - if(process.env.pm_id != null){ + + if (process.env.pm_id != null) { //pm2启动的,设置key为我的pid firstPid = await redisClient.get(G.redis.fromatKey("firstPid")); if (!firstPid) { @@ -207,7 +213,7 @@ export async function clusterMain() { redisClient.del(G.redis.fromatKey("uid2processId")); }); } - + //初始化玩家pid信息 await initUid2processId(); diff --git a/src/public/scheduler/scheduler_cross_email_pull.ts b/src/public/scheduler/scheduler_cross_email_pull.ts index 71c26da..e955d0c 100644 --- a/src/public/scheduler/scheduler_cross_email_pull.ts +++ b/src/public/scheduler/scheduler_cross_email_pull.ts @@ -22,12 +22,15 @@ export class CrossEmailPull extends Scheduler { G.clientCross.callApi("email/GetCrossEmail", {server_id: G.config.serverId + ""}).then((res) => { if (!res.isSucc) return; res.res.emails.forEach(async (email) => { + let _prize = email?.prizeData?.prize || []; + if (email?.prizeData) delete email.prizeData; + + email["prize"] = _prize; if (email.uid != "system") { await EmailFun.addEmail(email) } else { await EmailFun.addQuanFuEmail(email) } - await EmailFun.addEmail(email) }) if (res.res.emails.length > 0) { G.clientCross.callApi("email/DelCrossEmail", {eids: res.res.emails.map(e => e._id)}) diff --git a/src/public/task.ts b/src/public/task.ts index f9f4171..055ed64 100644 --- a/src/public/task.ts +++ b/src/public/task.ts @@ -788,7 +788,7 @@ export class TaskAllEmitFun { let _val = node.call.req.num; G.emit("Class_task_136", 'Class_task_136', node.call, _val, 0); // todo 统计藏品修复胶,此处只是临时处理,防止线上任务数据出错,正确做法是,统计修复胶的消耗,走136任务的统计 - G.emit("Class_task_134", 'Class_task_134', node.call, _val, 0, {}); + G.emit("Class_task_134", 'Class_task_134', node.call, _val, 0, []); } if (node.call.service.name == 'peijian/LvUp' && node.return.isSucc) { diff --git a/src/public/taskclass.ts b/src/public/taskclass.ts index 5266c85..7271b4e 100644 --- a/src/public/taskclass.ts +++ b/src/public/taskclass.ts @@ -390,7 +390,10 @@ export module manager { async initVal(call: ApiCall, con) { const data = await TanXianFun.getData(call); - return data.useFastGuaJiNum || 0 + if (data?.resetTime > PublicShared.getToDayZeroTime()) { + return data.useFastGuaJiNum || 0 + } + return 0 } } @@ -401,7 +404,10 @@ export module manager { async initVal(call: ApiCall, con) { let data = await JJCFun.getData(call); - return data?.useFightNum || 0 + if (data?.resetTime >= PublicShared.getToDayZeroTime()) { + return data?.useFightNum || 0 + } + return 0 } } @@ -418,7 +424,10 @@ export module manager { async initVal(call: ApiCall, con) { let myData = await G.mongodb.cPlayerInfo('meirishilian').findOne({uid: call.uid, type: 'meirishilian'}); - return R.compose(R.sum(), R.map(i => i[0].useFightNum), R.values())(myData.data.numInfo) || 0 + if (myData.data.refreshTime > PublicShared.getToDayZeroTime()) { + return R.compose(R.sum(), R.map(i => i[0].useFightNum), R.values())(myData.data.numInfo) || 0 + } + return 0 } } @@ -435,7 +444,10 @@ export module manager { async initVal(call: ApiCall, con) { let myData = await G.mongodb.cPlayerInfo('qjzzd').findOne({uid: call.uid, type: 'qjzzd'}); - return myData.useFightNum || 0 + if (myData?.refreFightTime > PublicShared.getToDayZeroTime()) { + return myData?.useFightNum || 0 + } + return 0 } } @@ -491,7 +503,10 @@ export module manager { async initVal(call: ApiCall, con) { let data: Partial = await G.mongodb.collection('wanted').findOne({uid: call.uid}) || {}; - return data.toDayUseNum || 0 + if (data.refreshTime > PublicShared.getToDayZeroTime()) { + return data.toDayUseNum || 0 + } + return 0 } } @@ -766,6 +781,7 @@ export module manager { return call.conn.gud?.helpHeros?.length || 0; } } + // 第56个任务 完成圣诞活动小游戏 export class Class_task_154 extends BaseClass { stype = 154 @@ -775,6 +791,7 @@ export module manager { return 1; } } + // 第57个任务 领取每日任务最终宝箱 export class Class_task_155 extends BaseClass { stype = 155 diff --git a/src/shared/fightControl/fightDPS.ts b/src/shared/fightControl/fightDPS.ts index 3509d39..6129e1a 100644 --- a/src/shared/fightControl/fightDPS.ts +++ b/src/shared/fightControl/fightDPS.ts @@ -37,7 +37,7 @@ export function getFightDPS(fromRole: FightObj, toRole: FightObj, extData: DPS_E 运算速度 自身速度*随机浮动值 随机浮动值 0.9-1.1(1位小数) (-10,10) */ - + if (fromRole.isDead || toRole.isDead) return { num: 0, dps: 0, miss: false, baoji: false, fromRole: fromRole, toRole: toRole }; let miss = false, baoji = false, dps = 0; let fromRoleLv = fromRole.getData('lv'), toRoleLv = toRole.getData('lv'); @@ -182,6 +182,18 @@ export function getBuffDPS(toRole: FightObj, fromRole: FightObj, extData: { xiao // todo 主角技能还没有限制百分比的最大伤害,以下判断修复技能buff没有伤害 if (fromRole.getData('pos') != 7){ dps = dps > maxDps ? maxDps : dps; + } else { + // 主角技能伤害最大值: avg(所有上场干部的maxDps) * 2 + let sumHeroMaxDps = 0, heroCount = 0; + const fromRoleSide = fromRole.getData('side'); + fromRole.fightControl.fightRolesArr.forEach(role => { + if (role.getData('pos') < 7 && role.getData('side') === fromRoleSide) { + sumHeroMaxDps += role.getData('maxdps'); + heroCount++; + } + }); + maxDps = sumHeroMaxDps / heroCount * 2; + dps = dps > maxDps ? maxDps : dps; } return { num: -dps, dps: -dps, miss: miss, baoji: baoji, fromRole: fromRole, toRole: toRole }; diff --git a/tsbufferIndex.js b/tsbufferIndex.js new file mode 100644 index 0000000..f9ac364 --- /dev/null +++ b/tsbufferIndex.js @@ -0,0 +1,1122 @@ +/*! + * TSBuffer Validator v2.1.1 + * ----------------------------------------- + * MIT LICENSE + * KingWorks (C) Copyright 2022 + * https://github.com/k8w/tsbuffer-validator + */ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +require('k8w-extend-native'); +var tslib = require('tslib'); +var tsbufferSchema = require('tsbuffer-schema'); + +var ProtoHelper = /** @class */ (function () { + function ProtoHelper(proto) { + //this._schemaWithUuids = []; + this._uuidIndex = 1; + //this._unionPropertiesCache = {}; + //this._flatInterfaceSchemaCache = {}; + this.proto = proto; + } + /** 将ReferenceTypeSchema层层转换为它最终实际引用的类型 */ + ProtoHelper.prototype.parseReference = function (schema) { + // Reference + if (schema.type === tsbufferSchema.SchemaType.Reference) { + var parsedSchema = this.proto[schema.target]; + if (!parsedSchema) { + throw new Error("Cannot find reference target: ".concat(schema.target)); + } + if (this.isTypeReference(parsedSchema)) { + return this.parseReference(parsedSchema); + } + else { + return parsedSchema; + } + } + // IndexedAccess + else if (schema.type === tsbufferSchema.SchemaType.IndexedAccess) { + if (!this.isInterface(schema.objectType)) { + throw new Error("Error objectType: ".concat(schema.objectType.type)); + } + // find prop item + var flat = this.getFlatInterfaceSchema(schema.objectType); + var propItem = flat.properties.find(function (v) { return v.name === schema.index; }); + var propType = void 0; + if (propItem) { + propType = propItem.type; + } + else { + if (flat.indexSignature) { + propType = flat.indexSignature.type; + } + else { + throw new Error("Error index: ".concat(schema.index)); + } + } + // optional -> | undefined + if (propItem && propItem.optional && // 引用的字段是optional + (propItem.type.type !== tsbufferSchema.SchemaType.Union // 自身不为Union + // 或自身为Union,但没有undefined成员条件 + || propItem.type.members.findIndex(function (v) { return v.type.type === tsbufferSchema.SchemaType.Literal && v.type.literal === undefined; }) === -1)) { + propType = { + type: tsbufferSchema.SchemaType.Union, + members: [ + { id: 0, type: propType }, + { + id: 1, + type: { + type: tsbufferSchema.SchemaType.Literal, + literal: undefined + } + } + ] + }; + } + return this.isTypeReference(propType) ? this.parseReference(propType) : propType; + } + else if (schema.type === tsbufferSchema.SchemaType.Keyof) { + var flatInterface = this.getFlatInterfaceSchema(schema.target); + return { + type: tsbufferSchema.SchemaType.Union, + members: flatInterface.properties.map(function (v, i) { return ({ + id: i, + type: { + type: tsbufferSchema.SchemaType.Literal, + literal: v.name + } + }); }) + }; + } + else { + return schema; + } + }; + ProtoHelper.prototype.isInterface = function (schema, excludeReference) { + if (excludeReference === void 0) { excludeReference = false; } + if (!excludeReference && this.isTypeReference(schema)) { + var parsed = this.parseReference(schema); + return this.isInterface(parsed, excludeReference); + } + else { + return schema.type === tsbufferSchema.SchemaType.Interface || this.isMappedType(schema) && this.parseMappedType(schema).type === tsbufferSchema.SchemaType.Interface; + } + }; + ProtoHelper.prototype.isMappedType = function (schema) { + return schema.type === tsbufferSchema.SchemaType.Pick || + schema.type === tsbufferSchema.SchemaType.Partial || + schema.type === tsbufferSchema.SchemaType.Omit || + schema.type === tsbufferSchema.SchemaType.Overwrite; + }; + ProtoHelper.prototype.isTypeReference = function (schema) { + return schema.type === tsbufferSchema.SchemaType.Reference || schema.type === tsbufferSchema.SchemaType.IndexedAccess || schema.type === tsbufferSchema.SchemaType.Keyof; + }; + ProtoHelper.prototype._getSchemaUuid = function (schema) { + var schemaWithUuid = schema; + if (!schemaWithUuid.uuid) { + //schemaWithUuid.uuid = this._schemaWithUuids.push(schemaWithUuid); + schemaWithUuid.uuid = this._uuidIndex; + this._uuidIndex++; + } + return schemaWithUuid.uuid; + }; + ProtoHelper.prototype.getUnionProperties = function (schema) { + return this._addUnionProperties([], schema.members.map(function (v) { return v.type; }));; + + // var uuid = this._getSchemaUuid(schema); + // if (!this._unionPropertiesCache[uuid]) { + // this._unionPropertiesCache[uuid] = this._addUnionProperties([], schema.members.map(function (v) { return v.type; })); + // } + // return this._unionPropertiesCache[uuid]; + }; + /** + * unionProperties: 在Union或Intersection类型中,出现在任意member中的字段 + */ + ProtoHelper.prototype._addUnionProperties = function (unionProperties, schemas) { + for (var i = 0, len = schemas.length; i < len; ++i) { + var schema = this.parseReference(schemas[i]); + // Interface及其Ref 加入interfaces + if (this.isInterface(schema)) { + var flat = this.getFlatInterfaceSchema(schema); + flat.properties.forEach(function (v) { + unionProperties.binaryInsert(v.name, true); + }); + if (flat.indexSignature) { + var key = "[[".concat(flat.indexSignature.keyType, "]]"); + unionProperties.binaryInsert(key, true); + } + } + // Intersection/Union 递归合并unionProperties + else if (schema.type === tsbufferSchema.SchemaType.Intersection || schema.type === tsbufferSchema.SchemaType.Union) { + this._addUnionProperties(unionProperties, schema.members.map(function (v) { return v.type; })); + } + else if (this.isMappedType(schema)) { + this._addUnionProperties(unionProperties, [this.parseMappedType(schema)]); + } + } + return unionProperties; + }; + /** + * 将unionProperties 扩展到 InterfaceTypeSchema中(optional的any类型) + * 以此来跳过对它们的检查(用于Intersection/Union) + */ + ProtoHelper.prototype.applyUnionProperties = function (schema, unionProperties) { + var newSchema = tslib.__assign(tslib.__assign({}, schema), { properties: schema.properties.slice() }); + var _loop_1 = function (prop) { + if (prop === '[[String]]') { + newSchema.indexSignature = newSchema.indexSignature || { + keyType: tsbufferSchema.SchemaType.String, + type: { type: tsbufferSchema.SchemaType.Any } + }; + } + else if (prop === '[[Number]]') { + newSchema.indexSignature = newSchema.indexSignature || { + keyType: tsbufferSchema.SchemaType.Number, + type: { type: tsbufferSchema.SchemaType.Any } + }; + } + else if (!schema.properties.find(function (v) { return v.name === prop; })) { + newSchema.properties.push({ + id: -1, + name: prop, + optional: true, + type: { + type: tsbufferSchema.SchemaType.Any + } + }); + } + }; + for (var _i = 0, unionProperties_1 = unionProperties; _i < unionProperties_1.length; _i++) { + var prop = unionProperties_1[_i]; + _loop_1(prop); + } + return newSchema; + }; + /** + * 将interface及其引用转换为展平的schema + */ + ProtoHelper.prototype.getFlatInterfaceSchema = function (schema) { + let res; + if (this.isTypeReference(schema)) { + var parsed = this.parseReference(schema); + if (parsed.type !== tsbufferSchema.SchemaType.Interface) { + throw new Error("Cannot flatten non interface type: ".concat(parsed.type)); + } + res = this.getFlatInterfaceSchema(parsed); + } + else if (schema.type === tsbufferSchema.SchemaType.Interface) { + res = this._flattenInterface(schema); + } + else if (this.isMappedType(schema)) { + res = this._flattenMappedType(schema); + } + else { + // @ts-expect-error + throw new Error('Invalid interface type: ' + schema.type); + } + return res; + + // var uuid = this._getSchemaUuid(schema); + // // from cache + // if (this._flatInterfaceSchemaCache[uuid]) { + // return this._flatInterfaceSchemaCache[uuid]; + // } + // if (this.isTypeReference(schema)) { + // var parsed = this.parseReference(schema); + // if (parsed.type !== tsbufferSchema.SchemaType.Interface) { + // throw new Error("Cannot flatten non interface type: ".concat(parsed.type)); + // } + // this._flatInterfaceSchemaCache[uuid] = this.getFlatInterfaceSchema(parsed); + // } + // else if (schema.type === tsbufferSchema.SchemaType.Interface) { + // this._flatInterfaceSchemaCache[uuid] = this._flattenInterface(schema); + // } + // else if (this.isMappedType(schema)) { + // this._flatInterfaceSchemaCache[uuid] = this._flattenMappedType(schema); + // } + // else { + // // @ts-expect-error + // throw new Error('Invalid interface type: ' + schema.type); + // } + // return this._flatInterfaceSchemaCache[uuid]; + }; + /** + * 展平interface + */ + ProtoHelper.prototype._flattenInterface = function (schema) { + var properties = {}; + var indexSignature; + // 自身定义的properties和indexSignature优先级最高 + if (schema.properties) { + for (var _i = 0, _a = schema.properties; _i < _a.length; _i++) { + var prop = _a[_i]; + properties[prop.name] = { + optional: prop.optional, + type: prop.type + }; + } + } + if (schema.indexSignature) { + indexSignature = schema.indexSignature; + } + // extends的优先级次之,补全没有定义的字段 + if (schema.extends) { + for (var _b = 0, _c = schema.extends; _b < _c.length; _b++) { + var extend = _c[_b]; + // 解引用 + var parsedExtRef = this.parseReference(extend.type); + if (this.isMappedType(parsedExtRef)) { + parsedExtRef = this._flattenMappedType(parsedExtRef); + } + if (!this.isInterface(parsedExtRef)) { + throw new Error('SchemaError: extends must from interface but from ' + parsedExtRef.type); + } + // 递归展平extends + var flatenExtendsSchema = this.getFlatInterfaceSchema(parsedExtRef); + // properties + if (flatenExtendsSchema.properties) { + for (var _d = 0, _e = flatenExtendsSchema.properties; _d < _e.length; _d++) { + var prop = _e[_d]; + if (!properties[prop.name]) { + properties[prop.name] = { + optional: prop.optional, + type: prop.type + }; + } + } + } + // indexSignature + if (flatenExtendsSchema.indexSignature && !indexSignature) { + indexSignature = flatenExtendsSchema.indexSignature; + } + } + } + return { + type: tsbufferSchema.SchemaType.Interface, + properties: Object.entries(properties).map(function (v, i) { return ({ + id: i, + name: v[0], + optional: v[1].optional, + type: v[1].type + }); }), + indexSignature: indexSignature + }; + }; + /** 将MappedTypeSchema转换为展平的Interface + */ + ProtoHelper.prototype._flattenMappedType = function (schema) { + // target 解引用 + var target; + if (this.isTypeReference(schema.target)) { + var parsed = this.parseReference(schema.target); + target = parsed; + } + else { + target = schema.target; + } + var flatTarget; + // 内层仍然为MappedType 递归之 + if (target.type === tsbufferSchema.SchemaType.Pick || target.type === tsbufferSchema.SchemaType.Partial || target.type === tsbufferSchema.SchemaType.Omit || target.type === tsbufferSchema.SchemaType.Overwrite) { + flatTarget = this._flattenMappedType(target); + } + else if (target.type === tsbufferSchema.SchemaType.Interface) { + flatTarget = this._flattenInterface(target); + } + else { + throw new Error("Invalid target.type: ".concat(target.type)); + } + // 开始执行Mapped逻辑 + if (schema.type === tsbufferSchema.SchemaType.Pick) { + var properties = []; + var _loop_2 = function (key) { + var propItem = flatTarget.properties.find(function (v) { return v.name === key; }); + if (propItem) { + properties.push({ + id: properties.length, + name: key, + optional: propItem.optional, + type: propItem.type + }); + } + else if (flatTarget.indexSignature) { + properties.push({ + id: properties.length, + name: key, + type: flatTarget.indexSignature.type + }); + } + }; + for (var _i = 0, _a = schema.keys; _i < _a.length; _i++) { + var key = _a[_i]; + _loop_2(key); + } + return { + type: tsbufferSchema.SchemaType.Interface, + properties: properties + }; + } + else if (schema.type === tsbufferSchema.SchemaType.Partial) { + for (var _b = 0, _c = flatTarget.properties; _b < _c.length; _b++) { + var v = _c[_b]; + v.optional = true; + } + return flatTarget; + } + else if (schema.type === tsbufferSchema.SchemaType.Omit) { + var _loop_3 = function (key) { + flatTarget.properties.removeOne(function (v) { return v.name === key; }); + }; + for (var _d = 0, _e = schema.keys; _d < _e.length; _d++) { + var key = _e[_d]; + _loop_3(key); + } + return flatTarget; + } + else if (schema.type === tsbufferSchema.SchemaType.Overwrite) { + var overwrite = this.getFlatInterfaceSchema(schema.overwrite); + if (overwrite.indexSignature) { + flatTarget.indexSignature = overwrite.indexSignature; + } + var _loop_4 = function (prop) { + flatTarget.properties.removeOne(function (v) { return v.name === prop.name; }); + flatTarget.properties.push(prop); + }; + for (var _f = 0, _g = overwrite.properties; _f < _g.length; _f++) { + var prop = _g[_f]; + _loop_4(prop); + } + return flatTarget; + } + else { + throw new Error("Unknown type: ".concat(schema.type)); + } + }; + ProtoHelper.prototype.parseMappedType = function (schema) { + // 解嵌套,例如:Pick>> + var parents = []; + var child = schema; + do { + parents.push(child); + child = this.parseReference(child.target); + } while (this.isMappedType(child)); + // 最内层是 interface,直接返回(validator 会验证 key 匹配) + if (child.type === tsbufferSchema.SchemaType.Interface) { + return child; + } + // PickOmit === PickOmit | PickOmit + else if (child.type === tsbufferSchema.SchemaType.Union || child.type === tsbufferSchema.SchemaType.Intersection) { + var newSchema = { + type: child.type, + members: child.members.map(function (v) { + // 从里面往外装 + var type = v.type; + for (var i = parents.length - 1; i > -1; --i) { + var parent_1 = parents[i]; + type = tslib.__assign(tslib.__assign({}, parent_1), { target: type }); + } + return { + id: v.id, + type: type + }; + }) + }; + return newSchema; + } + else { + throw new Error("Unsupported pattern ".concat(schema.type, "<").concat(child.type, ">")); + } + }; + return ProtoHelper; +}()); + +var _a; +/** @internal */ +var ErrorType; +(function (ErrorType) { + ErrorType["TypeError"] = "TypeError"; + ErrorType["InvalidScalarType"] = "InvalidScalarType"; + ErrorType["TupleOverLength"] = "TupleOverLength"; + ErrorType["InvalidEnumValue"] = "InvalidEnumValue"; + ErrorType["InvalidLiteralValue"] = "InvalidLiteralValue"; + ErrorType["MissingRequiredProperty"] = "MissingRequiredProperty"; + ErrorType["ExcessProperty"] = "ExcessProperty"; + ErrorType["InvalidNumberKey"] = "InvalidNumberKey"; + ErrorType["UnionTypesNotMatch"] = "UnionTypesNotMatch"; + ErrorType["UnionMembersNotMatch"] = "UnionMembersNotMatch"; + ErrorType["CustomError"] = "CustomError"; +})(ErrorType || (ErrorType = {})); +/** @internal */ +var ErrorMsg = (_a = {}, + _a[ErrorType.TypeError] = function (expect, actual) { return "Expected type to be `".concat(expect, "`, actually `").concat(actual, "`."); }, + _a[ErrorType.InvalidScalarType] = function (value, scalarType) { return "`".concat(value, "` is not a valid `").concat(scalarType, "`."); }, + _a[ErrorType.TupleOverLength] = function (valueLength, schemaLength) { return "Value has ".concat(valueLength, " elements but schema allows only ").concat(schemaLength, "."); }, + _a[ErrorType.InvalidEnumValue] = function (value) { return "`".concat(value, "` is not a valid enum member."); }, + _a[ErrorType.InvalidLiteralValue] = function (expected, actual) { return "Expected to equals `".concat(stringify(expected), "`, actually `").concat(stringify(actual), "`"); }, + _a[ErrorType.MissingRequiredProperty] = function (propName) { return "Missing required property `".concat(propName, "`."); }, + _a[ErrorType.ExcessProperty] = function (propName) { return "Excess property `".concat(propName, "` should not exists."); }, + _a[ErrorType.InvalidNumberKey] = function (key) { return "`".concat(key, "` is not a valid key, the key here should be a `number`."); }, + // Union + _a[ErrorType.UnionTypesNotMatch] = function (value, types) { return "`".concat(stringify(value), "` is not matched to `").concat(types.join(' | '), "`"); }, + _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')); }, + _a[ErrorType.CustomError] = function (errMsg) { return errMsg; }, + _a); +/** @internal */ +function stringify(value) { + if (typeof value === 'string') { + var output = JSON.stringify(value); + return "'" + output.substr(1, output.length - 2) + "'"; + } + return JSON.stringify(value); +} + +/** @internal */ +var ValidateResultError = /** @class */ (function () { + function ValidateResultError(error) { + this.isSucc = false; + this.error = error; + } + Object.defineProperty(ValidateResultError.prototype, "errMsg", { + get: function () { + return ValidateResultError.getErrMsg(this.error); + }, + enumerable: false, + configurable: true + }); + ValidateResultError.getErrMsg = function (error) { + var _a; + var errMsg = ErrorMsg[error.type].apply(ErrorMsg, error.params); + if ((_a = error.inner) === null || _a === void 0 ? void 0 : _a.property.length) { + return "Property `".concat(error.inner.property.join('.'), "`: ").concat(errMsg); + } + else { + return errMsg; + } + }; + return ValidateResultError; +}()); +/** @internal */ +var ValidateResultUtil = /** @class */ (function () { + function ValidateResultUtil() { + } + ValidateResultUtil.error = function (type) { + var params = []; + for (var _i = 1; _i < arguments.length; _i++) { + params[_i - 1] = arguments[_i]; + } + return new ValidateResultError({ + type: type, + params: params + }); + }; + ValidateResultUtil.innerError = function (property, value, schema, error) { + var _a; + if (error.error.inner) { + if (typeof property === 'string') { + error.error.inner.property.unshift(property); + } + else { + (_a = error.error.inner.property).unshift.apply(_a, property); + } + } + else { + error.error.inner = { + property: typeof property === 'string' ? [property] : property, + value: value, + schema: schema + }; + } + return error; + }; + ValidateResultUtil.succ = { isSucc: true }; + return ValidateResultUtil; +}()); + +var typedArrays = { + Int8Array: Int8Array, + Int16Array: Int16Array, + Int32Array: Int32Array, + BigInt64Array: typeof BigInt64Array !== 'undefined' ? BigInt64Array : undefined, + Uint8Array: Uint8Array, + Uint16Array: Uint16Array, + Uint32Array: Uint32Array, + BigUint64Array: typeof BigUint64Array !== 'undefined' ? BigUint64Array : undefined, + Float32Array: Float32Array, + Float64Array: Float64Array +}; +/** + * TSBuffer Schema Validator + * @public + */ +var TSBufferValidator = /** @class */ (function () { + function TSBufferValidator(proto, options) { + /** + * Default options + */ + this.options = { + excessPropertyChecks: true, + strictNullChecks: false, + cloneProto: true + }; + if (options) { + this.options = tslib.__assign(tslib.__assign({}, this.options), options); + } + this.proto = this.options.cloneProto ? Object.merge({}, proto) : proto; + this.protoHelper = new ProtoHelper(this.proto); + } + /** + * Validate whether the value is valid to the schema + * @param value - Value to be validated. + * @param schemaId - Schema or schema ID. + * For example, the schema ID for type `Test` in `a/b.ts` may be `a/b/Test`. + */ + TSBufferValidator.prototype.validate = function (value, schemaOrId, options) { + var _a, _b; + var schema; + var schemaId; + // Get schema + if (typeof schemaOrId === 'string') { + schemaId = schemaOrId; + schema = this.proto[schemaId]; + if (!schema) { + throw new Error("Cannot find schema: ".concat(schemaId)); + } + } + else { + schema = schemaOrId; + } + // Merge default options + return this._validate(value, schema, tslib.__assign(tslib.__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 })); + }; + TSBufferValidator.prototype._validate = function (value, schema, options) { + var _a; + var vRes; + // Validate + switch (schema.type) { + case tsbufferSchema.SchemaType.Boolean: + vRes = this._validateBooleanType(value, schema); + break; + case tsbufferSchema.SchemaType.Number: + vRes = this._validateNumberType(value, schema); + break; + case tsbufferSchema.SchemaType.String: + vRes = this._validateStringType(value, schema); + break; + case tsbufferSchema.SchemaType.Array: + vRes = this._validateArrayType(value, schema, options); + break; + case tsbufferSchema.SchemaType.Tuple: + vRes = this._validateTupleType(value, schema, options); + break; + case tsbufferSchema.SchemaType.Enum: + vRes = this._validateEnumType(value, schema); + break; + case tsbufferSchema.SchemaType.Any: + vRes = this._validateAnyType(value); + break; + case tsbufferSchema.SchemaType.Literal: + vRes = this._validateLiteralType(value, schema, (_a = options === null || options === void 0 ? void 0 : options.strictNullChecks) !== null && _a !== void 0 ? _a : this.options.strictNullChecks); + break; + case tsbufferSchema.SchemaType.Object: + vRes = this._validateObjectType(value, schema); + break; + case tsbufferSchema.SchemaType.Interface: + vRes = this._validateInterfaceType(value, schema, options); + break; + case tsbufferSchema.SchemaType.Buffer: + vRes = this._validateBufferType(value, schema); + break; + case tsbufferSchema.SchemaType.IndexedAccess: + case tsbufferSchema.SchemaType.Reference: + case tsbufferSchema.SchemaType.Keyof: + vRes = this._validateReferenceType(value, schema, options); + break; + case tsbufferSchema.SchemaType.Union: + vRes = this._validateUnionType(value, schema, options); + break; + case tsbufferSchema.SchemaType.Intersection: + vRes = this._validateIntersectionType(value, schema, options); + break; + case tsbufferSchema.SchemaType.Pick: + case tsbufferSchema.SchemaType.Omit: + case tsbufferSchema.SchemaType.Partial: + case tsbufferSchema.SchemaType.Overwrite: + vRes = this._validateMappedType(value, schema, options); + break; + case tsbufferSchema.SchemaType.Date: + vRes = this._validateDateType(value); + break; + case tsbufferSchema.SchemaType.NonNullable: + vRes = this._validateNonNullableType(value, schema, options); + break; + case tsbufferSchema.SchemaType.Custom: + var res = schema.validate(value); + vRes = res.isSucc ? ValidateResultUtil.succ : ValidateResultUtil.error(ErrorType.CustomError, res.errMsg); + break; + // 错误的type + default: + // @ts-expect-error + throw new Error("Unsupported schema type: ".concat(schema.type)); + } + //vRes = ValidateResultUtil.succ; + // prune + if (options === null || options === void 0 ? void 0 : options.prune) { + // don't need prune, return original value + if (options.prune.output === undefined) { + options.prune.output = value; + } + // output to parent + if (options.prune.parent) { + options.prune.parent.value[options.prune.parent.key] = options.prune.output; + } + } + return vRes; + }; + /** + * 修剪 Object,移除 Schema 中未定义的 Key + * 需要确保 value 类型合法 + * @param value - value to be validated + * @param schemaOrId -Schema or schema ID. + * @returns Validate result and pruned value. if validate failed, `pruneOutput` would be undefined. + */ + TSBufferValidator.prototype.prune = function (value, schemaOrId, options) { + var _a; + var schema = typeof schemaOrId === 'string' ? this.proto[schemaOrId] : schemaOrId; + if (!schema) { + throw new Error('Cannot find schema: ' + schemaOrId); + } + var prune = {}; + var vRes = this._validate(value, schema, tslib.__assign(tslib.__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 })); + if (vRes.isSucc) { + vRes.pruneOutput = prune.output; + } + return vRes; + }; + TSBufferValidator.prototype._validateBooleanType = function (value, schema) { + var type = this._getTypeof(value); + if (type === 'boolean') { + return ValidateResultUtil.succ; + } + else { + return ValidateResultUtil.error(ErrorType.TypeError, 'boolean', type); + } + }; + TSBufferValidator.prototype._validateNumberType = function (value, schema) { + // 默认为double + var scalarType = schema.scalarType || 'double'; + // Wrong Type + var type = this._getTypeof(value); + var rightType = scalarType.indexOf('big') > -1 ? 'bigint' : 'number'; + if (type !== rightType) { + return ValidateResultUtil.error(ErrorType.TypeError, rightType, type); + } + // scalarType类型检测 + // 整形却为小数 + if (scalarType !== 'double' && type === 'number' && !Number.isInteger(value)) { + return ValidateResultUtil.error(ErrorType.InvalidScalarType, value, scalarType); + } + // 无符号整形却为负数 + if (scalarType.indexOf('uint') > -1 && value < 0) { + return ValidateResultUtil.error(ErrorType.InvalidScalarType, value, scalarType); + } + return ValidateResultUtil.succ; + }; + TSBufferValidator.prototype._validateStringType = function (value, schema) { + var type = this._getTypeof(value); + return type === 'string' ? ValidateResultUtil.succ : ValidateResultUtil.error(ErrorType.TypeError, 'string', type); + }; + TSBufferValidator.prototype._validateArrayType = function (value, schema, options) { + // is Array type + var type = this._getTypeof(value); + if (type !== tsbufferSchema.SchemaType.Array) { + return ValidateResultUtil.error(ErrorType.TypeError, tsbufferSchema.SchemaType.Array, type); + } + // prune output + var prune = options.prune; + if (prune) { + prune.output = Array.from({ length: value.length }); + } + // validate elementType + for (var i = 0; i < value.length; ++i) { + var elemValidateResult = this._validate(value[i], schema.elementType, tslib.__assign(tslib.__assign({}, options), { prune: (prune === null || prune === void 0 ? void 0 : prune.output) ? { + parent: { + value: prune.output, + key: i + } + } : undefined })); + if (!elemValidateResult.isSucc) { + return ValidateResultUtil.innerError('' + i, value[i], schema.elementType, elemValidateResult); + } + } + return ValidateResultUtil.succ; + }; + TSBufferValidator.prototype._validateTupleType = function (value, schema, options) { + // is Array type + var type = this._getTypeof(value); + if (type !== tsbufferSchema.SchemaType.Array) { + return ValidateResultUtil.error(ErrorType.TypeError, tsbufferSchema.SchemaType.Array, type); + } + var prune = options.prune; + // validate length + // excessPropertyChecks 与 prune互斥 + if (!prune && options.excessPropertyChecks && value.length > schema.elementTypes.length) { + return ValidateResultUtil.error(ErrorType.TupleOverLength, value.length, schema.elementTypes.length); + } + // prune output + if (prune) { + prune.output = Array.from({ length: Math.min(value.length, schema.elementTypes.length) }); + } + // validate elementType + for (var i = 0; i < schema.elementTypes.length; ++i) { + // MissingRequiredProperty: NotOptional && is undefined + if (value[i] === undefined || value[i] === null && !options.strictNullChecks) { + var canBeNull = this._canBeNull(schema.elementTypes[i]); + var canBeUndefined = schema.optionalStartIndex !== undefined && i >= schema.optionalStartIndex || this._canBeUndefined(schema.elementTypes[i]); + var isOptional = canBeUndefined || !options.strictNullChecks && canBeNull; + // skip undefined property + if (isOptional) { + // Prune null & undefined->null + if (prune === null || prune === void 0 ? void 0 : prune.output) { + if (value[i] === null && canBeNull + || value[i] === undefined && !canBeUndefined && canBeNull) { + prune.output[i] = null; + } + } + continue; + } + else { + return ValidateResultUtil.error(ErrorType.MissingRequiredProperty, i); + } + } + // element type check + var elemValidateResult = this._validate(value[i], schema.elementTypes[i], { + prune: (prune === null || prune === void 0 ? void 0 : prune.output) ? { + parent: { + value: prune.output, + key: i + } + } : undefined, + strictNullChecks: options.strictNullChecks, + excessPropertyChecks: options.excessPropertyChecks + }); + if (!elemValidateResult.isSucc) { + return ValidateResultUtil.innerError('' + i, value[i], schema.elementTypes[i], elemValidateResult); + } + } + return ValidateResultUtil.succ; + }; + TSBufferValidator.prototype._canBeUndefined = function (schema) { + var _this = this; + if (schema.type === tsbufferSchema.SchemaType.Union) { + return schema.members.some(function (v) { return _this._canBeUndefined(v.type); }); + } + if (schema.type === tsbufferSchema.SchemaType.Literal && schema.literal === undefined) { + return true; + } + return false; + }; + TSBufferValidator.prototype._canBeNull = function (schema) { + var _this = this; + if (schema.type === tsbufferSchema.SchemaType.Union) { + return schema.members.some(function (v) { return _this._canBeNull(v.type); }); + } + if (schema.type === tsbufferSchema.SchemaType.Literal && schema.literal === null) { + return true; + } + return false; + }; + TSBufferValidator.prototype._validateEnumType = function (value, schema) { + // must be string or number + var type = this._getTypeof(value); + if (type !== 'string' && type !== 'number') { + return ValidateResultUtil.error(ErrorType.TypeError, 'string | number', type); + } + // 有值与预设相同 + if (schema.members.some(function (v) { return v.value === value; })) { + return ValidateResultUtil.succ; + } + else { + return ValidateResultUtil.error(ErrorType.InvalidEnumValue, value); + } + }; + TSBufferValidator.prototype._validateAnyType = function (value) { + return ValidateResultUtil.succ; + }; + TSBufferValidator.prototype._validateLiteralType = function (value, schema, strictNullChecks) { + // 非strictNullChecks严格模式,null undefined同等对待 + if (!strictNullChecks && (schema.literal === null || schema.literal === undefined)) { + return value === null || value === undefined ? + ValidateResultUtil.succ + : ValidateResultUtil.error(ErrorType.InvalidLiteralValue, schema.literal, value); + } + return value === schema.literal ? + ValidateResultUtil.succ + : ValidateResultUtil.error(ErrorType.InvalidLiteralValue, schema.literal, value); + }; + TSBufferValidator.prototype._validateObjectType = function (value, schema) { + var type = this._getTypeof(value); + return type === 'Object' || type === 'Array' ? ValidateResultUtil.succ : ValidateResultUtil.error(ErrorType.TypeError, 'Object', type); + }; + TSBufferValidator.prototype._validateInterfaceType = function (value, schema, options) { + var type = this._getTypeof(value); + if (type !== 'Object') { + return ValidateResultUtil.error(ErrorType.TypeError, 'Object', type); + } + // 先展平 + var flatSchema = this.protoHelper.getFlatInterfaceSchema(schema); + // From union or intersecton type + if (options.unionProperties) { + flatSchema = this.protoHelper.applyUnionProperties(flatSchema, options.unionProperties); + } + return this._validateFlatInterface(value, flatSchema, options); + }; + TSBufferValidator.prototype._validateMappedType = function (value, schema, options) { + var parsed = this.protoHelper.parseMappedType(schema); + if (parsed.type === tsbufferSchema.SchemaType.Interface) { + return this._validateInterfaceType(value, schema, options); + } + else if (parsed.type === tsbufferSchema.SchemaType.Union) { + return this._validateUnionType(value, parsed, options); + } + else if (parsed.type === tsbufferSchema.SchemaType.Intersection) { + return this._validateIntersectionType(value, parsed, options); + } + // @ts-expect-error + throw new Error("Invalid ".concat(schema.type, " target type: ").concat(parsed.type)); + }; + TSBufferValidator.prototype._validateFlatInterface = function (value, schema, options) { + // interfaceSignature强制了key必须是数字的情况 + if (schema.indexSignature && schema.indexSignature.keyType === tsbufferSchema.SchemaType.Number) { + for (var key in value) { + if (!this._isNumberKey(key)) { + return ValidateResultUtil.error(ErrorType.InvalidNumberKey, key); + } + } + } + var prune = options.prune; + if (prune) { + prune.output = {}; + } + // Excess property check (与prune互斥) + if (!prune && options.excessPropertyChecks && !schema.indexSignature) { + var validProperties_1 = schema.properties.map(function (v) { return v.name; }); + var firstExcessProperty = Object.keys(value).find(function (v) { return validProperties_1.indexOf(v) === -1; }); + if (firstExcessProperty) { + return ValidateResultUtil.error(ErrorType.ExcessProperty, firstExcessProperty); + } + } + // 校验properties + if (schema.properties) { + for (var _i = 0, _a = schema.properties; _i < _a.length; _i++) { + var property = _a[_i]; + // MissingRequiredProperty: is undefined && !isOptional + if (value[property.name] === undefined || value[property.name] === null && !options.strictNullChecks) { + var canBeNull = this._canBeNull(property.type); + var canBeUndefined = property.optional || this._canBeUndefined(property.type); + var isOptional = canBeUndefined || !options.strictNullChecks && canBeNull; + // skip undefined optional property + if (isOptional) { + // Prune null & undefined->null + if (prune === null || prune === void 0 ? void 0 : prune.output) { + if (value[property.name] === null && canBeNull + || value[property.name] === undefined && !canBeUndefined && canBeNull) { + prune.output[property.name] = null; + } + } + continue; + } + else { + return ValidateResultUtil.error(ErrorType.MissingRequiredProperty, property.name); + } + } + // property本身验证 + var vRes = this._validate(value[property.name], property.type, { + prune: (prune === null || prune === void 0 ? void 0 : prune.output) && property.id > -1 ? { + parent: { + value: prune.output, + key: property.name + } + } : undefined, + strictNullChecks: options.strictNullChecks, + excessPropertyChecks: options.excessPropertyChecks + }); + if (!vRes.isSucc) { + return ValidateResultUtil.innerError(property.name, value[property.name], property.type, vRes); + } + } + } + // 检测indexSignature + if (schema.indexSignature) { + for (var key in value) { + // only prune is (property is pruned already) + // let memberPrune: ValidatePruneOptions | undefined = schema.properties.some(v => v.name === key) ? undefined : {}; + // validate each field + var vRes = this._validate(value[key], schema.indexSignature.type, { + prune: (prune === null || prune === void 0 ? void 0 : prune.output) ? { + parent: { + value: prune.output, + key: key + } + } : undefined, + strictNullChecks: options.strictNullChecks, + excessPropertyChecks: options.excessPropertyChecks + }); + if (!vRes.isSucc) { + return ValidateResultUtil.innerError(key, value[key], schema.indexSignature.type, vRes); + } + } + } + return ValidateResultUtil.succ; + }; + TSBufferValidator.prototype._validateBufferType = function (value, schema) { + var _a, _b; + var type = this._getTypeof(value); + if (type !== 'Object') { + return ValidateResultUtil.error(ErrorType.TypeError, schema.arrayType || 'ArrayBuffer', type); + } + else if (schema.arrayType) { + var typeArrayClass = typedArrays[schema.arrayType]; + if (!typeArrayClass) { + throw new Error("Error TypedArray type: ".concat(schema.arrayType)); + } + 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); + } + else { + 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); + } + }; + TSBufferValidator.prototype._validateReferenceType = function (value, schema, options) { + return this._validate(value, this.protoHelper.parseReference(schema), options); + }; + TSBufferValidator.prototype._validateUnionType = function (value, schema, options) { + var _this = this; + options.unionProperties = options.unionProperties || this.protoHelper.getUnionProperties(schema); + var isObjectPrune = false; + var prune = options.prune; + if (prune && value && Object.getPrototypeOf(value) === Object.prototype) { + isObjectPrune = true; + prune.output = {}; + } + // 有一成功则成功 + var isSomeSucc = false; + var memberErrors = []; + for (var i = 0; i < schema.members.length; ++i) { + var member = schema.members[i]; + var memberType = this.protoHelper.isTypeReference(member.type) ? this.protoHelper.parseReference(member.type) : member.type; + var memberPrune = prune ? {} : undefined; + var vRes = this._validate(value, memberType, tslib.__assign(tslib.__assign({}, options), { prune: memberPrune })); + if (vRes.isSucc) { + isSomeSucc = true; + // if prune object: must prune all members + if (isObjectPrune) { + prune.output = tslib.__assign(tslib.__assign({}, prune.output), memberPrune.output); + } + // not prune object: stop checking after 1st member matched + else { + break; + } + } + else { + memberErrors.push(vRes); + } + } + // 有一成功则成功; + if (isSomeSucc) { + return ValidateResultUtil.succ; + } + // 全部失败,则失败 + else { + // All member error is the same, return the first + var msg0_1 = memberErrors[0].errMsg; + if (memberErrors.every(function (v) { return v.errMsg === msg0_1; })) { + return memberErrors[0]; + } + // mutual exclusion: return the only one + var nonLiteralErrors = memberErrors.filter(function (v) { return v.error.type !== ErrorType.InvalidLiteralValue; }); + if (nonLiteralErrors.length === 1) { + return nonLiteralErrors[0]; + } + // All member error without inner: show simple msg + if (memberErrors.every(function (v) { return !v.error.inner && (v.error.type === ErrorType.TypeError || v.error.type === ErrorType.InvalidLiteralValue); })) { + var valueType = this._getTypeof(value); + var expectedTypes = memberErrors.map(function (v) { return v.error.type === ErrorType.TypeError ? v.error.params[0] : _this._getTypeof(v.error.params[0]); }).distinct(); + // Expected type A|B|C, actually type D + if (expectedTypes.indexOf(valueType) === -1) { + return ValidateResultUtil.error(ErrorType.TypeError, expectedTypes.join(' | '), this._getTypeof(value)); + } + // `'D'` is not matched to `'A'|'B'|'C'` + if (valueType !== 'Object' && valueType !== tsbufferSchema.SchemaType.Array) { + var types = memberErrors.map(function (v) { return v.error.type === ErrorType.TypeError ? v.error.params[0] : stringify(v.error.params[0]); }).distinct(); + return ValidateResultUtil.error(ErrorType.UnionTypesNotMatch, value, types); + } + } + // other errors + return ValidateResultUtil.error(ErrorType.UnionMembersNotMatch, memberErrors); + } + }; + TSBufferValidator.prototype._validateIntersectionType = function (value, schema, options) { + options.unionProperties = options.unionProperties || this.protoHelper.getUnionProperties(schema); + var isObjectPrune = false; + var prune = options.prune; + if (prune && value && Object.getPrototypeOf(value) === Object.prototype) { + prune.output = {}; + isObjectPrune = true; + } + // 有一失败则失败 + for (var i = 0, len = schema.members.length; i < len; ++i) { + // 验证member + var memberType = schema.members[i].type; + memberType = this.protoHelper.isTypeReference(memberType) ? this.protoHelper.parseReference(memberType) : memberType; + var memberPrune = prune ? {} : undefined; + var vRes = this._validate(value, memberType, tslib.__assign(tslib.__assign({}, options), { prune: memberPrune })); + // 有一失败则失败 + if (!vRes.isSucc) { + return vRes; + } + if (isObjectPrune) { + prune.output = tslib.__assign(tslib.__assign({}, prune.output), memberPrune.output); + } + } + // 全成功则成功 + return ValidateResultUtil.succ; + }; + TSBufferValidator.prototype._validateDateType = function (value) { + if (value instanceof Date) { + return ValidateResultUtil.succ; + } + else { + return ValidateResultUtil.error(ErrorType.TypeError, 'Date', this._getTypeof(value)); + } + }; + TSBufferValidator.prototype._validateNonNullableType = function (value, schema, options) { + var type = this._getTypeof(value); + if ((type === 'null' || type === 'undefined') && schema.target.type !== 'Any') { + return ValidateResultUtil.error(ErrorType.TypeError, 'NonNullable', type); + } + return this._validate(value, schema.target, options); + }; + TSBufferValidator.prototype._isNumberKey = function (key) { + var int = parseInt(key); + return !(isNaN(int) || ('' + int) !== key); + }; + TSBufferValidator.prototype._getTypeof = function (value) { + var type = typeof value; + if (type === 'object') { + if (value === null) { + return 'null'; + } + else if (Array.isArray(value)) { + return tsbufferSchema.SchemaType.Array; + } + else { + return 'Object'; + } + } + return type; + }; + return TSBufferValidator; +}()); + +exports.ProtoHelper = ProtoHelper; +exports.TSBufferValidator = TSBufferValidator;