适配5.0的框架改动
This commit is contained in:
parent
6a823bc27b
commit
1918037164
|
|
@ -5,7 +5,6 @@ const tslib_1 = require("tslib");
|
||||||
const fs_1 = require("fs");
|
const fs_1 = require("fs");
|
||||||
const path_1 = require("path");
|
const path_1 = require("path");
|
||||||
const node_schedule_1 = require("node-schedule");
|
const node_schedule_1 = require("node-schedule");
|
||||||
const env_1 = require("oak-domain/lib/compiler/env");
|
|
||||||
const IntrinsicLogics_1 = require("oak-domain/lib/store/IntrinsicLogics");
|
const IntrinsicLogics_1 = require("oak-domain/lib/store/IntrinsicLogics");
|
||||||
const lodash_1 = require("oak-domain/lib/utils/lodash");
|
const lodash_1 = require("oak-domain/lib/utils/lodash");
|
||||||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||||
|
|
@ -13,8 +12,9 @@ const types_1 = require("oak-domain/lib/types");
|
||||||
const DbStore_1 = require("./DbStore");
|
const DbStore_1 = require("./DbStore");
|
||||||
const index_1 = tslib_1.__importStar(require("oak-common-aspect/lib/index"));
|
const index_1 = tslib_1.__importStar(require("oak-common-aspect/lib/index"));
|
||||||
const assert_1 = tslib_1.__importDefault(require("assert"));
|
const assert_1 = tslib_1.__importDefault(require("assert"));
|
||||||
|
const dependencyBuilder_1 = require("oak-domain/lib/compiler/dependencyBuilder");
|
||||||
const DataSubscriber_1 = tslib_1.__importDefault(require("./cluster/DataSubscriber"));
|
const DataSubscriber_1 = tslib_1.__importDefault(require("./cluster/DataSubscriber"));
|
||||||
const env_2 = require("./cluster/env");
|
const env_1 = require("./cluster/env");
|
||||||
const Synchronizer_1 = tslib_1.__importDefault(require("./Synchronizer"));
|
const Synchronizer_1 = tslib_1.__importDefault(require("./Synchronizer"));
|
||||||
class AppLoader extends types_1.AppLoader {
|
class AppLoader extends types_1.AppLoader {
|
||||||
dbStore;
|
dbStore;
|
||||||
|
|
@ -35,58 +35,10 @@ class AppLoader extends types_1.AppLoader {
|
||||||
return require(depFilePath).default;
|
return require(depFilePath).default;
|
||||||
}
|
}
|
||||||
}).filter(ele => !!ele);
|
}).filter(ele => !!ele);
|
||||||
if (!sth) {
|
if (sth) {
|
||||||
if (sthExternal.length > 0 && sthExternal[0] instanceof Array) {
|
sthExternal.push(sth);
|
||||||
sth = [];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sth = {};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (sth instanceof Array) {
|
return (0, lodash_1.mergeConcatMany)(sthExternal);
|
||||||
sthExternal.forEach((sth2, idx) => {
|
|
||||||
(0, assert_1.default)(sth2 instanceof Array, `${(0, path_1.join)(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象不是数组,与项目对应路径的输出不一致`);
|
|
||||||
sth.push(...sth2);
|
|
||||||
});
|
|
||||||
return sth;
|
|
||||||
}
|
|
||||||
(0, assert_1.default)(typeof sth === 'object');
|
|
||||||
const sthOut = {};
|
|
||||||
sthExternal.forEach((sth2, idx) => {
|
|
||||||
(0, assert_1.default)(typeof sth2 === 'object' && !(sth2 instanceof Array), `${(0, path_1.join)(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象不是非数组对象,与项目对应路径的输出不一致`);
|
|
||||||
const inter = (0, lodash_1.intersection)(Object.keys(sthOut), Object.keys(sth2));
|
|
||||||
if (inter.length > 0) {
|
|
||||||
console.warn(`${(0, path_1.join)(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象中的key值【${inter.join(',')}】与其它对应路径输出的key值有冲突,请仔细检查避免错误`);
|
|
||||||
inter.forEach((ele) => {
|
|
||||||
if (sth2[ele] instanceof Array && sthOut[ele]) {
|
|
||||||
(0, assert_1.default)(sthOut[ele] instanceof Array, `${(0, path_1.join)(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象的${ele}键值是数组,但之前的相应对象的${ele}却不是,请仔细检查以避免错误`);
|
|
||||||
console.warn(`${(0, path_1.join)(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象中的key值【${ele}】与其它对应路径输出的key值【${ele}】将以数组格式进行合并,请仔细检查避免错误`);
|
|
||||||
sth2[ele].push(...sthOut[ele]);
|
|
||||||
}
|
|
||||||
else if (!(sth2[ele] instanceof Array) && sthOut[ele]) {
|
|
||||||
(0, assert_1.default)(!(sthOut[ele] instanceof Array), `${(0, path_1.join)(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象的${ele}键值不是数组,但之前的相应对象的${ele}却是,请仔细检查以避免错误`);
|
|
||||||
console.warn(`${(0, path_1.join)(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象中的key值【${ele}】将对与其它对应路径输出的key值【${ele}】进行覆盖,请仔细检查避免错误`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Object.assign(sthOut, sth2);
|
|
||||||
});
|
|
||||||
const inter = (0, lodash_1.intersection)(Object.keys(sthOut), Object.keys(sth));
|
|
||||||
if (inter.length > 0) {
|
|
||||||
inter.forEach((ele) => {
|
|
||||||
if (sth[ele] instanceof Array && sthOut[ele]) {
|
|
||||||
(0, assert_1.default)(sthOut[ele] instanceof Array, `项目${filePath}中的default输出对象的${ele}键值是数组,但之前的相应对象的${ele}却不是,请仔细检查以避免错误`);
|
|
||||||
console.warn(`项目${filePath}中的default输出对象中的key值【${ele}】与其它引用包该路径输出的key值【${ele}】将以数组格式进行合并,请仔细检查避免错误`);
|
|
||||||
sth[ele].push(...sthOut[ele]);
|
|
||||||
}
|
|
||||||
else if (!(sth[ele] instanceof Array) && sthOut[ele]) {
|
|
||||||
(0, assert_1.default)(!(sthOut[ele] instanceof Array), `项目${filePath}中的default输出对象的${ele}键值不是数组,但之前的相应对象的${ele}却是,请仔细检查以避免错误`);
|
|
||||||
console.warn(`项目${filePath}中的default输出对象中的key值【${ele}】将对其它引用包该路径输出的key值【${ele}】进行覆盖,请仔细检查避免错误`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Object.assign(sthOut, sth);
|
|
||||||
return sthOut;
|
|
||||||
}
|
}
|
||||||
async makeContext(cxtStr, headers) {
|
async makeContext(cxtStr, headers) {
|
||||||
const context = this.contextBuilder(this.dbStore);
|
const context = this.contextBuilder(this.dbStore);
|
||||||
|
|
@ -112,8 +64,9 @@ class AppLoader extends types_1.AppLoader {
|
||||||
super(path);
|
super(path);
|
||||||
const { dbConfig } = this.getConfiguration();
|
const { dbConfig } = this.getConfiguration();
|
||||||
const { storageSchema } = require(`${path}/lib/oak-app-domain/Storage`);
|
const { storageSchema } = require(`${path}/lib/oak-app-domain/Storage`);
|
||||||
const { authDeduceRelationMap, selectFreeEntities, updateFreeDict } = require(`${path}/lib/config/relation`);
|
const { authDeduceRelationMap, selectFreeEntities, updateFreeDict } = require(`${path}/lib/configuration/index`).default;
|
||||||
this.externalDependencies = require((0, env_1.OAK_EXTERNAL_LIBS_FILEPATH)((0, path_1.join)(path, 'lib')));
|
const depGraph = (0, dependencyBuilder_1.analyzeDepedency)(process.cwd());
|
||||||
|
this.externalDependencies = Object.keys(depGraph.nodeDict);
|
||||||
this.aspectDict = Object.assign({}, index_1.default, this.requireSth('lib/aspects/index'));
|
this.aspectDict = Object.assign({}, index_1.default, this.requireSth('lib/aspects/index'));
|
||||||
this.dbStore = new DbStore_1.DbStore(storageSchema, () => this.contextBuilder(this.dbStore), dbConfig, authDeduceRelationMap, selectFreeEntities, updateFreeDict);
|
this.dbStore = new DbStore_1.DbStore(storageSchema, () => this.contextBuilder(this.dbStore), dbConfig, authDeduceRelationMap, selectFreeEntities, updateFreeDict);
|
||||||
if (ns) {
|
if (ns) {
|
||||||
|
|
@ -122,7 +75,7 @@ class AppLoader extends types_1.AppLoader {
|
||||||
const { BackendRuntimeContext } = require(`${path}/lib/context/BackendRuntimeContext`);
|
const { BackendRuntimeContext } = require(`${path}/lib/context/BackendRuntimeContext`);
|
||||||
this.contextBuilder = (store) => {
|
this.contextBuilder = (store) => {
|
||||||
const context = new BackendRuntimeContext(store);
|
const context = new BackendRuntimeContext(store);
|
||||||
context.clusterInfo = (0, env_2.getClusterInfo)();
|
context.clusterInfo = (0, env_1.getClusterInfo)();
|
||||||
const originCommit = context.commit;
|
const originCommit = context.commit;
|
||||||
context.commit = async () => {
|
context.commit = async () => {
|
||||||
const { eventOperationMap, opRecords } = context;
|
const { eventOperationMap, opRecords } = context;
|
||||||
|
|
@ -147,7 +100,7 @@ class AppLoader extends types_1.AppLoader {
|
||||||
const triggers = this.requireSth('lib/triggers/index');
|
const triggers = this.requireSth('lib/triggers/index');
|
||||||
const checkers = this.requireSth('lib/checkers/index');
|
const checkers = this.requireSth('lib/checkers/index');
|
||||||
const { ActionDefDict } = require(`${this.path}/lib/oak-app-domain/ActionDefDict`);
|
const { ActionDefDict } = require(`${this.path}/lib/oak-app-domain/ActionDefDict`);
|
||||||
const { triggers: adTriggers, checkers: adCheckers } = (0, IntrinsicLogics_1.makeIntrinsicCTWs)(this.dbStore.getSchema(), ActionDefDict);
|
const { triggers: adTriggers, checkers: adCheckers } = (0, IntrinsicLogics_1.makeIntrinsicLogics)(this.dbStore.getSchema(), ActionDefDict);
|
||||||
triggers.forEach((trigger) => this.registerTrigger(trigger));
|
triggers.forEach((trigger) => this.registerTrigger(trigger));
|
||||||
adTriggers.forEach((trigger) => this.registerTrigger(trigger));
|
adTriggers.forEach((trigger) => this.registerTrigger(trigger));
|
||||||
checkers.forEach((checker) => this.dbStore.registerChecker(checker));
|
checkers.forEach((checker) => this.dbStore.registerChecker(checker));
|
||||||
|
|
@ -331,7 +284,7 @@ class AppLoader extends types_1.AppLoader {
|
||||||
startWatchers() {
|
startWatchers() {
|
||||||
const watchers = this.requireSth('lib/watchers/index');
|
const watchers = this.requireSth('lib/watchers/index');
|
||||||
const { ActionDefDict } = require(`${this.path}/lib/oak-app-domain/ActionDefDict`);
|
const { ActionDefDict } = require(`${this.path}/lib/oak-app-domain/ActionDefDict`);
|
||||||
const { watchers: adWatchers } = (0, IntrinsicLogics_1.makeIntrinsicCTWs)(this.dbStore.getSchema(), ActionDefDict);
|
const { watchers: adWatchers } = (0, IntrinsicLogics_1.makeIntrinsicLogics)(this.dbStore.getSchema(), ActionDefDict);
|
||||||
const totalWatchers = watchers.concat(adWatchers);
|
const totalWatchers = watchers.concat(adWatchers);
|
||||||
let count = 0;
|
let count = 0;
|
||||||
const execOne = async (watcher, start) => {
|
const execOne = async (watcher, start) => {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import { existsSync } from 'fs';
|
import { existsSync } from 'fs';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { scheduleJob } from 'node-schedule';
|
import { scheduleJob } from 'node-schedule';
|
||||||
import { OAK_EXTERNAL_LIBS_FILEPATH } from 'oak-domain/lib/compiler/env';
|
import { makeIntrinsicLogics } from "oak-domain/lib/store/IntrinsicLogics";
|
||||||
import { makeIntrinsicCTWs } from "oak-domain/lib/store/IntrinsicLogics";
|
import { intersection, mergeConcatMany, omit } from 'oak-domain/lib/utils/lodash';
|
||||||
import { intersection, omit } from 'oak-domain/lib/utils/lodash';
|
|
||||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||||
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
|
||||||
import { AppLoader as GeneralAppLoader, Trigger, Checker, Aspect, CreateOpResult, SyncConfig, EntityDict, Watcher, BBWatcher, WBWatcher, OpRecord, Routine, FreeRoutine, Timer, FreeTimer, StorageSchema, OperationResult } from "oak-domain/lib/types";
|
import { AppLoader as GeneralAppLoader, Trigger, Checker, Aspect, CreateOpResult, SyncConfig, EntityDict, Watcher, BBWatcher, WBWatcher, OpRecord, Routine, FreeRoutine, Timer, FreeTimer, StorageSchema, OperationResult } from "oak-domain/lib/types";
|
||||||
|
|
@ -15,6 +14,7 @@ import { Endpoint, EndpointItem } from 'oak-domain/lib/types/Endpoint';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import { IncomingHttpHeaders, IncomingMessage } from 'http';
|
import { IncomingHttpHeaders, IncomingMessage } from 'http';
|
||||||
import { Server as SocketIoServer, Namespace } from 'socket.io';
|
import { Server as SocketIoServer, Namespace } from 'socket.io';
|
||||||
|
import { analyzeDepedency } from 'oak-domain/lib/compiler/dependencyBuilder';
|
||||||
|
|
||||||
import DataSubscriber from './cluster/DataSubscriber';
|
import DataSubscriber from './cluster/DataSubscriber';
|
||||||
import { getClusterInfo } from './cluster/env';
|
import { getClusterInfo } from './cluster/env';
|
||||||
|
|
@ -47,69 +47,11 @@ export class AppLoader<ED extends EntityDict & BaseEntityDict, Cxt extends Backe
|
||||||
ele => !!ele
|
ele => !!ele
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!sth) {
|
if (sth) {
|
||||||
if (sthExternal.length > 0 && sthExternal[0] instanceof Array) {
|
sthExternal.push(sth);
|
||||||
sth = [];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sth = {};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sth instanceof Array) {
|
return mergeConcatMany(sthExternal);
|
||||||
sthExternal.forEach(
|
|
||||||
(sth2, idx) => {
|
|
||||||
assert(sth2 instanceof Array, `${join(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象不是数组,与项目对应路径的输出不一致`);
|
|
||||||
sth.push(...sth2);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return sth;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(typeof sth === 'object');
|
|
||||||
const sthOut: Record<string, any> = {};
|
|
||||||
sthExternal.forEach(
|
|
||||||
(sth2, idx) => {
|
|
||||||
assert(typeof sth2 === 'object' && !(sth2 instanceof Array), `${join(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象不是非数组对象,与项目对应路径的输出不一致`);
|
|
||||||
const inter = intersection(Object.keys(sthOut), Object.keys(sth2));
|
|
||||||
if (inter.length > 0) {
|
|
||||||
console.warn(`${join(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象中的key值【${inter.join(',')}】与其它对应路径输出的key值有冲突,请仔细检查避免错误`);
|
|
||||||
inter.forEach(
|
|
||||||
(ele) => {
|
|
||||||
if (sth2[ele] instanceof Array && sthOut[ele]) {
|
|
||||||
assert(sthOut[ele] instanceof Array, `${join(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象的${ele}键值是数组,但之前的相应对象的${ele}却不是,请仔细检查以避免错误`);
|
|
||||||
console.warn(`${join(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象中的key值【${ele}】与其它对应路径输出的key值【${ele}】将以数组格式进行合并,请仔细检查避免错误`);
|
|
||||||
sth2[ele].push(...sthOut[ele]);
|
|
||||||
}
|
|
||||||
else if (!(sth2[ele] instanceof Array) && sthOut[ele]) {
|
|
||||||
assert(!(sthOut[ele] instanceof Array), `${join(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象的${ele}键值不是数组,但之前的相应对象的${ele}却是,请仔细检查以避免错误`);
|
|
||||||
console.warn(`${join(this.path, 'node_modules', this.externalDependencies[idx], filePath)}中的default输出对象中的key值【${ele}】将对与其它对应路径输出的key值【${ele}】进行覆盖,请仔细检查避免错误`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Object.assign(sthOut, sth2);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const inter = intersection(Object.keys(sthOut), Object.keys(sth));
|
|
||||||
if (inter.length > 0) {
|
|
||||||
inter.forEach(
|
|
||||||
(ele) => {
|
|
||||||
if (sth[ele] instanceof Array && sthOut[ele]) {
|
|
||||||
assert(sthOut[ele] instanceof Array, `项目${filePath}中的default输出对象的${ele}键值是数组,但之前的相应对象的${ele}却不是,请仔细检查以避免错误`);
|
|
||||||
console.warn(`项目${filePath}中的default输出对象中的key值【${ele}】与其它引用包该路径输出的key值【${ele}】将以数组格式进行合并,请仔细检查避免错误`);
|
|
||||||
sth[ele].push(...sthOut[ele]);
|
|
||||||
}
|
|
||||||
else if (!(sth[ele] instanceof Array) && sthOut[ele]) {
|
|
||||||
assert(!(sthOut[ele] instanceof Array), `项目${filePath}中的default输出对象的${ele}键值不是数组,但之前的相应对象的${ele}却是,请仔细检查以避免错误`);
|
|
||||||
console.warn(`项目${filePath}中的default输出对象中的key值【${ele}】将对其它引用包该路径输出的key值【${ele}】进行覆盖,请仔细检查避免错误`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Object.assign(sthOut, sth);
|
|
||||||
return sthOut;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async makeContext(cxtStr?: string, headers?: IncomingHttpHeaders) {
|
protected async makeContext(cxtStr?: string, headers?: IncomingHttpHeaders) {
|
||||||
|
|
@ -140,8 +82,9 @@ export class AppLoader<ED extends EntityDict & BaseEntityDict, Cxt extends Backe
|
||||||
super(path);
|
super(path);
|
||||||
const { dbConfig } = this.getConfiguration();
|
const { dbConfig } = this.getConfiguration();
|
||||||
const { storageSchema } = require(`${path}/lib/oak-app-domain/Storage`);
|
const { storageSchema } = require(`${path}/lib/oak-app-domain/Storage`);
|
||||||
const { authDeduceRelationMap, selectFreeEntities, updateFreeDict } = require(`${path}/lib/config/relation`)
|
const { authDeduceRelationMap, selectFreeEntities, updateFreeDict } = require(`${path}/lib/configuration/index`).default;
|
||||||
this.externalDependencies = require(OAK_EXTERNAL_LIBS_FILEPATH(join(path, 'lib')));
|
const depGraph = analyzeDepedency(process.cwd());
|
||||||
|
this.externalDependencies = Object.keys(depGraph.nodeDict);
|
||||||
this.aspectDict = Object.assign({}, generalAspectDict, this.requireSth('lib/aspects/index'));
|
this.aspectDict = Object.assign({}, generalAspectDict, this.requireSth('lib/aspects/index'));
|
||||||
this.dbStore = new DbStore<ED, Cxt>(storageSchema, () => this.contextBuilder(this.dbStore), dbConfig, authDeduceRelationMap, selectFreeEntities, updateFreeDict);
|
this.dbStore = new DbStore<ED, Cxt>(storageSchema, () => this.contextBuilder(this.dbStore), dbConfig, authDeduceRelationMap, selectFreeEntities, updateFreeDict);
|
||||||
if (ns) {
|
if (ns) {
|
||||||
|
|
@ -187,7 +130,7 @@ export class AppLoader<ED extends EntityDict & BaseEntityDict, Cxt extends Backe
|
||||||
const checkers = this.requireSth('lib/checkers/index');
|
const checkers = this.requireSth('lib/checkers/index');
|
||||||
const { ActionDefDict } = require(`${this.path}/lib/oak-app-domain/ActionDefDict`);
|
const { ActionDefDict } = require(`${this.path}/lib/oak-app-domain/ActionDefDict`);
|
||||||
|
|
||||||
const { triggers: adTriggers, checkers: adCheckers } = makeIntrinsicCTWs(this.dbStore.getSchema(), ActionDefDict);
|
const { triggers: adTriggers, checkers: adCheckers } = makeIntrinsicLogics(this.dbStore.getSchema(), ActionDefDict);
|
||||||
|
|
||||||
triggers.forEach(
|
triggers.forEach(
|
||||||
(trigger: Trigger<ED, keyof ED, Cxt>) => this.registerTrigger(trigger)
|
(trigger: Trigger<ED, keyof ED, Cxt>) => this.registerTrigger(trigger)
|
||||||
|
|
@ -413,7 +356,7 @@ export class AppLoader<ED extends EntityDict & BaseEntityDict, Cxt extends Backe
|
||||||
const watchers = this.requireSth('lib/watchers/index');
|
const watchers = this.requireSth('lib/watchers/index');
|
||||||
const { ActionDefDict } = require(`${this.path}/lib/oak-app-domain/ActionDefDict`);
|
const { ActionDefDict } = require(`${this.path}/lib/oak-app-domain/ActionDefDict`);
|
||||||
|
|
||||||
const { watchers: adWatchers } = makeIntrinsicCTWs(this.dbStore.getSchema(), ActionDefDict);
|
const { watchers: adWatchers } = makeIntrinsicLogics(this.dbStore.getSchema(), ActionDefDict);
|
||||||
const totalWatchers = (<Watcher<ED, keyof ED, Cxt>[]>watchers).concat(adWatchers);
|
const totalWatchers = (<Watcher<ED, keyof ED, Cxt>[]>watchers).concat(adWatchers);
|
||||||
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue