add synclocker and async queue
This commit is contained in:
parent
1a35a4a35e
commit
6b4108bddf
96
dist/common/AsyncQueue.cjs
vendored
Normal file
96
dist/common/AsyncQueue.cjs
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/common/AsyncQueue.ts
|
||||
var AsyncQueue_exports = {};
|
||||
__export(AsyncQueue_exports, {
|
||||
createAsyncQueue: () => createAsyncQueue,
|
||||
createAsyncQueues: () => createAsyncQueues
|
||||
});
|
||||
module.exports = __toCommonJS(AsyncQueue_exports);
|
||||
function createAsyncQueue(opts = { dedupeConcurrent: false }) {
|
||||
const { dedupeConcurrent } = opts;
|
||||
let queue = [];
|
||||
let running;
|
||||
let nextPromise = new DeferredPromise();
|
||||
const push = (task) => {
|
||||
let taskPromise = new DeferredPromise();
|
||||
if (dedupeConcurrent) {
|
||||
queue = [];
|
||||
if (nextPromise.started)
|
||||
nextPromise = new DeferredPromise();
|
||||
taskPromise = nextPromise;
|
||||
}
|
||||
queue.push(() => {
|
||||
taskPromise.started = true;
|
||||
task().then(taskPromise.resolve).catch(taskPromise.reject);
|
||||
return taskPromise.promise;
|
||||
});
|
||||
if (!running)
|
||||
running = start();
|
||||
return taskPromise.promise;
|
||||
};
|
||||
const start = async () => {
|
||||
while (queue.length) {
|
||||
const task = queue.shift();
|
||||
await task().catch(() => {
|
||||
});
|
||||
}
|
||||
running = void 0;
|
||||
};
|
||||
return {
|
||||
push,
|
||||
flush: () => running || Promise.resolve(),
|
||||
get size() {
|
||||
return queue.length;
|
||||
}
|
||||
};
|
||||
}
|
||||
var createAsyncQueues = (opts = { dedupeConcurrent: false }) => {
|
||||
const queues = {};
|
||||
const push = (queueId, task) => {
|
||||
if (!queues[queueId])
|
||||
queues[queueId] = createAsyncQueue(opts);
|
||||
return queues[queueId].push(task);
|
||||
};
|
||||
const flush = (queueId) => {
|
||||
if (!queues[queueId])
|
||||
queues[queueId] = createAsyncQueue(opts);
|
||||
return queues[queueId].flush();
|
||||
};
|
||||
return { push, flush };
|
||||
};
|
||||
var DeferredPromise = class {
|
||||
constructor() {
|
||||
this.started = false;
|
||||
this.resolve = () => {
|
||||
};
|
||||
this.reject = () => {
|
||||
};
|
||||
this.promise = new Promise((res, rej) => {
|
||||
this.resolve = res;
|
||||
this.reject = rej;
|
||||
});
|
||||
}
|
||||
};
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
createAsyncQueue,
|
||||
createAsyncQueues
|
||||
});
|
||||
//# sourceMappingURL=AsyncQueue.cjs.map
|
1
dist/common/AsyncQueue.cjs.map
vendored
Normal file
1
dist/common/AsyncQueue.cjs.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/common/AsyncQueue.ts"],"sourcesContent":["type Callback<T> = () => Promise<T>\n\nexport type AsyncQueue<T = void> = {\n push: (task: Callback<T>) => Promise<T>\n flush: () => Promise<void>\n size: number\n}\n\n/**\n * Ensures that each callback pushed onto the queue is executed in series.\n * Such a quetie 😻\n * @param opts.dedupeConcurrent If dedupeConcurrent is `true` it ensures that if multiple\n * tasks are pushed onto the queue while there is an active task, only the\n * last one will be executed, once the active task has completed.\n * e.g. in the below example, only 0 and 3 will be executed.\n * ```\n * const queue = createAsyncQueue({ dedupeConcurrent: true })\n * queue.push(async () => console.log(0)) // returns 0\n * queue.push(async () => console.log(1)) // returns 3\n * queue.push(async () => console.log(2)) // returns 3\n * queue.push(async () => console.log(3)) // returns 3\n * ```\n * */\nexport function createAsyncQueue<T = void>(opts = { dedupeConcurrent: false }): AsyncQueue<T> {\n const { dedupeConcurrent } = opts\n let queue: Callback<T>[] = []\n let running: Promise<void> | undefined\n let nextPromise = new DeferredPromise<T>()\n const push = (task: Callback<T>) => {\n let taskPromise = new DeferredPromise<T>()\n if (dedupeConcurrent) {\n queue = []\n if (nextPromise.started) nextPromise = new DeferredPromise<T>()\n taskPromise = nextPromise\n }\n queue.push(() => {\n taskPromise.started = true\n task().then(taskPromise.resolve).catch(taskPromise.reject)\n return taskPromise.promise\n })\n if (!running) running = start()\n return taskPromise.promise\n }\n const start = async () => {\n while (queue.length) {\n const task = queue.shift()!\n await task().catch(() => {})\n }\n running = undefined\n }\n return {\n push,\n flush: () => running || Promise.resolve(),\n get size() {\n return queue.length\n },\n }\n}\n\nexport const createAsyncQueues = <T = void>(opts = { dedupeConcurrent: false }) => {\n const queues: { [queueId: string]: AsyncQueue<T> } = {}\n const push = (queueId: string, task: Callback<T>) => {\n if (!queues[queueId]) queues[queueId] = createAsyncQueue<T>(opts)\n return queues[queueId].push(task)\n }\n const flush = (queueId: string) => {\n if (!queues[queueId]) queues[queueId] = createAsyncQueue<T>(opts)\n return queues[queueId].flush()\n }\n return { push, flush }\n}\n\nclass DeferredPromise<T = void, E = any> {\n started = false\n resolve: (x: T | PromiseLike<T>) => void = () => {}\n reject: (x: E) => void = () => {}\n promise: Promise<T>\n\n constructor() {\n this.promise = new Promise<T>((res, rej) => {\n this.resolve = res\n this.reject = rej\n })\n }\n}\n\n// function main() {\n// const queue = createAsyncQueue()\n// queue.push(async () => {\n// console.log(0)\n// }) // returns 0\n// queue.push(async () => {\n// console.log(1)\n\n// return new Promise((resolve, reject) => {\n// setTimeout(() => {\n// console.log('12')\n// resolve()\n// }, 1000)\n// })\n// }) // returns 3\n// queue.push(async () => console.log(2)) // returns 3\n// queue.push(async () => console.log(3)) // returns 3\n// console.log('hi')\n// }\n\n// main()\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuBO,SAAS,iBAA2B,OAAO,EAAE,kBAAkB,MAAM,GAAkB;AAC5F,QAAM,EAAE,iBAAiB,IAAI;AAC7B,MAAI,QAAuB,CAAC;AAC5B,MAAI;AACJ,MAAI,cAAc,IAAI,gBAAmB;AACzC,QAAM,OAAO,CAAC,SAAsB;AAClC,QAAI,cAAc,IAAI,gBAAmB;AACzC,QAAI,kBAAkB;AACpB,cAAQ,CAAC;AACT,UAAI,YAAY;AAAS,sBAAc,IAAI,gBAAmB;AAC9D,oBAAc;AAAA,IAChB;AACA,UAAM,KAAK,MAAM;AACf,kBAAY,UAAU;AACtB,WAAK,EAAE,KAAK,YAAY,OAAO,EAAE,MAAM,YAAY,MAAM;AACzD,aAAO,YAAY;AAAA,IACrB,CAAC;AACD,QAAI,CAAC;AAAS,gBAAU,MAAM;AAC9B,WAAO,YAAY;AAAA,EACrB;AACA,QAAM,QAAQ,YAAY;AACxB,WAAO,MAAM,QAAQ;AACnB,YAAM,OAAO,MAAM,MAAM;AACzB,YAAM,KAAK,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAC7B;AACA,cAAU;AAAA,EACZ;AACA,SAAO;AAAA,IACL;AAAA,IACA,OAAO,MAAM,WAAW,QAAQ,QAAQ;AAAA,IACxC,IAAI,OAAO;AACT,aAAO,MAAM;AAAA,IACf;AAAA,EACF;AACF;AAEO,IAAM,oBAAoB,CAAW,OAAO,EAAE,kBAAkB,MAAM,MAAM;AACjF,QAAM,SAA+C,CAAC;AACtD,QAAM,OAAO,CAAC,SAAiB,SAAsB;AACnD,QAAI,CAAC,OAAO,OAAO;AAAG,aAAO,OAAO,IAAI,iBAAoB,IAAI;AAChE,WAAO,OAAO,OAAO,EAAE,KAAK,IAAI;AAAA,EAClC;AACA,QAAM,QAAQ,CAAC,YAAoB;AACjC,QAAI,CAAC,OAAO,OAAO;AAAG,aAAO,OAAO,IAAI,iBAAoB,IAAI;AAChE,WAAO,OAAO,OAAO,EAAE,MAAM;AAAA,EAC/B;AACA,SAAO,EAAE,MAAM,MAAM;AACvB;AAEA,IAAM,kBAAN,MAAyC;AAAA,EAMvC,cAAc;AALd,mBAAU;AACV,mBAA2C,MAAM;AAAA,IAAC;AAClD,kBAAyB,MAAM;AAAA,IAAC;AAI9B,SAAK,UAAU,IAAI,QAAW,CAAC,KAAK,QAAQ;AAC1C,WAAK,UAAU;AACf,WAAK,SAAS;AAAA,IAChB,CAAC;AAAA,EACH;AACF;","names":[]}
|
32
dist/common/AsyncQueue.d.cts
vendored
Normal file
32
dist/common/AsyncQueue.d.cts
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
type Callback<T> = () => Promise<T>;
|
||||
type AsyncQueue<T = void> = {
|
||||
push: (task: Callback<T>) => Promise<T>;
|
||||
flush: () => Promise<void>;
|
||||
size: number;
|
||||
};
|
||||
/**
|
||||
* Ensures that each callback pushed onto the queue is executed in series.
|
||||
* Such a quetie 😻
|
||||
* @param opts.dedupeConcurrent If dedupeConcurrent is `true` it ensures that if multiple
|
||||
* tasks are pushed onto the queue while there is an active task, only the
|
||||
* last one will be executed, once the active task has completed.
|
||||
* e.g. in the below example, only 0 and 3 will be executed.
|
||||
* ```
|
||||
* const queue = createAsyncQueue({ dedupeConcurrent: true })
|
||||
* queue.push(async () => console.log(0)) // returns 0
|
||||
* queue.push(async () => console.log(1)) // returns 3
|
||||
* queue.push(async () => console.log(2)) // returns 3
|
||||
* queue.push(async () => console.log(3)) // returns 3
|
||||
* ```
|
||||
* */
|
||||
declare function createAsyncQueue<T = void>(opts?: {
|
||||
dedupeConcurrent: boolean;
|
||||
}): AsyncQueue<T>;
|
||||
declare const createAsyncQueues: <T = void>(opts?: {
|
||||
dedupeConcurrent: boolean;
|
||||
}) => {
|
||||
push: (queueId: string, task: Callback<T>) => Promise<T>;
|
||||
flush: (queueId: string) => Promise<void>;
|
||||
};
|
||||
|
||||
export { type AsyncQueue, createAsyncQueue, createAsyncQueues };
|
32
dist/common/AsyncQueue.d.ts
vendored
Normal file
32
dist/common/AsyncQueue.d.ts
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
type Callback<T> = () => Promise<T>;
|
||||
type AsyncQueue<T = void> = {
|
||||
push: (task: Callback<T>) => Promise<T>;
|
||||
flush: () => Promise<void>;
|
||||
size: number;
|
||||
};
|
||||
/**
|
||||
* Ensures that each callback pushed onto the queue is executed in series.
|
||||
* Such a quetie 😻
|
||||
* @param opts.dedupeConcurrent If dedupeConcurrent is `true` it ensures that if multiple
|
||||
* tasks are pushed onto the queue while there is an active task, only the
|
||||
* last one will be executed, once the active task has completed.
|
||||
* e.g. in the below example, only 0 and 3 will be executed.
|
||||
* ```
|
||||
* const queue = createAsyncQueue({ dedupeConcurrent: true })
|
||||
* queue.push(async () => console.log(0)) // returns 0
|
||||
* queue.push(async () => console.log(1)) // returns 3
|
||||
* queue.push(async () => console.log(2)) // returns 3
|
||||
* queue.push(async () => console.log(3)) // returns 3
|
||||
* ```
|
||||
* */
|
||||
declare function createAsyncQueue<T = void>(opts?: {
|
||||
dedupeConcurrent: boolean;
|
||||
}): AsyncQueue<T>;
|
||||
declare const createAsyncQueues: <T = void>(opts?: {
|
||||
dedupeConcurrent: boolean;
|
||||
}) => {
|
||||
push: (queueId: string, task: Callback<T>) => Promise<T>;
|
||||
flush: (queueId: string) => Promise<void>;
|
||||
};
|
||||
|
||||
export { type AsyncQueue, createAsyncQueue, createAsyncQueues };
|
71
dist/common/AsyncQueue.js
vendored
Normal file
71
dist/common/AsyncQueue.js
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
// src/common/AsyncQueue.ts
|
||||
function createAsyncQueue(opts = { dedupeConcurrent: false }) {
|
||||
const { dedupeConcurrent } = opts;
|
||||
let queue = [];
|
||||
let running;
|
||||
let nextPromise = new DeferredPromise();
|
||||
const push = (task) => {
|
||||
let taskPromise = new DeferredPromise();
|
||||
if (dedupeConcurrent) {
|
||||
queue = [];
|
||||
if (nextPromise.started)
|
||||
nextPromise = new DeferredPromise();
|
||||
taskPromise = nextPromise;
|
||||
}
|
||||
queue.push(() => {
|
||||
taskPromise.started = true;
|
||||
task().then(taskPromise.resolve).catch(taskPromise.reject);
|
||||
return taskPromise.promise;
|
||||
});
|
||||
if (!running)
|
||||
running = start();
|
||||
return taskPromise.promise;
|
||||
};
|
||||
const start = async () => {
|
||||
while (queue.length) {
|
||||
const task = queue.shift();
|
||||
await task().catch(() => {
|
||||
});
|
||||
}
|
||||
running = void 0;
|
||||
};
|
||||
return {
|
||||
push,
|
||||
flush: () => running || Promise.resolve(),
|
||||
get size() {
|
||||
return queue.length;
|
||||
}
|
||||
};
|
||||
}
|
||||
var createAsyncQueues = (opts = { dedupeConcurrent: false }) => {
|
||||
const queues = {};
|
||||
const push = (queueId, task) => {
|
||||
if (!queues[queueId])
|
||||
queues[queueId] = createAsyncQueue(opts);
|
||||
return queues[queueId].push(task);
|
||||
};
|
||||
const flush = (queueId) => {
|
||||
if (!queues[queueId])
|
||||
queues[queueId] = createAsyncQueue(opts);
|
||||
return queues[queueId].flush();
|
||||
};
|
||||
return { push, flush };
|
||||
};
|
||||
var DeferredPromise = class {
|
||||
constructor() {
|
||||
this.started = false;
|
||||
this.resolve = () => {
|
||||
};
|
||||
this.reject = () => {
|
||||
};
|
||||
this.promise = new Promise((res, rej) => {
|
||||
this.resolve = res;
|
||||
this.reject = rej;
|
||||
});
|
||||
}
|
||||
};
|
||||
export {
|
||||
createAsyncQueue,
|
||||
createAsyncQueues
|
||||
};
|
||||
//# sourceMappingURL=AsyncQueue.js.map
|
1
dist/common/AsyncQueue.js.map
vendored
Normal file
1
dist/common/AsyncQueue.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/common/AsyncQueue.ts"],"sourcesContent":["type Callback<T> = () => Promise<T>\n\nexport type AsyncQueue<T = void> = {\n push: (task: Callback<T>) => Promise<T>\n flush: () => Promise<void>\n size: number\n}\n\n/**\n * Ensures that each callback pushed onto the queue is executed in series.\n * Such a quetie 😻\n * @param opts.dedupeConcurrent If dedupeConcurrent is `true` it ensures that if multiple\n * tasks are pushed onto the queue while there is an active task, only the\n * last one will be executed, once the active task has completed.\n * e.g. in the below example, only 0 and 3 will be executed.\n * ```\n * const queue = createAsyncQueue({ dedupeConcurrent: true })\n * queue.push(async () => console.log(0)) // returns 0\n * queue.push(async () => console.log(1)) // returns 3\n * queue.push(async () => console.log(2)) // returns 3\n * queue.push(async () => console.log(3)) // returns 3\n * ```\n * */\nexport function createAsyncQueue<T = void>(opts = { dedupeConcurrent: false }): AsyncQueue<T> {\n const { dedupeConcurrent } = opts\n let queue: Callback<T>[] = []\n let running: Promise<void> | undefined\n let nextPromise = new DeferredPromise<T>()\n const push = (task: Callback<T>) => {\n let taskPromise = new DeferredPromise<T>()\n if (dedupeConcurrent) {\n queue = []\n if (nextPromise.started) nextPromise = new DeferredPromise<T>()\n taskPromise = nextPromise\n }\n queue.push(() => {\n taskPromise.started = true\n task().then(taskPromise.resolve).catch(taskPromise.reject)\n return taskPromise.promise\n })\n if (!running) running = start()\n return taskPromise.promise\n }\n const start = async () => {\n while (queue.length) {\n const task = queue.shift()!\n await task().catch(() => {})\n }\n running = undefined\n }\n return {\n push,\n flush: () => running || Promise.resolve(),\n get size() {\n return queue.length\n },\n }\n}\n\nexport const createAsyncQueues = <T = void>(opts = { dedupeConcurrent: false }) => {\n const queues: { [queueId: string]: AsyncQueue<T> } = {}\n const push = (queueId: string, task: Callback<T>) => {\n if (!queues[queueId]) queues[queueId] = createAsyncQueue<T>(opts)\n return queues[queueId].push(task)\n }\n const flush = (queueId: string) => {\n if (!queues[queueId]) queues[queueId] = createAsyncQueue<T>(opts)\n return queues[queueId].flush()\n }\n return { push, flush }\n}\n\nclass DeferredPromise<T = void, E = any> {\n started = false\n resolve: (x: T | PromiseLike<T>) => void = () => {}\n reject: (x: E) => void = () => {}\n promise: Promise<T>\n\n constructor() {\n this.promise = new Promise<T>((res, rej) => {\n this.resolve = res\n this.reject = rej\n })\n }\n}\n\n// function main() {\n// const queue = createAsyncQueue()\n// queue.push(async () => {\n// console.log(0)\n// }) // returns 0\n// queue.push(async () => {\n// console.log(1)\n\n// return new Promise((resolve, reject) => {\n// setTimeout(() => {\n// console.log('12')\n// resolve()\n// }, 1000)\n// })\n// }) // returns 3\n// queue.push(async () => console.log(2)) // returns 3\n// queue.push(async () => console.log(3)) // returns 3\n// console.log('hi')\n// }\n\n// main()\n"],"mappings":";AAuBO,SAAS,iBAA2B,OAAO,EAAE,kBAAkB,MAAM,GAAkB;AAC5F,QAAM,EAAE,iBAAiB,IAAI;AAC7B,MAAI,QAAuB,CAAC;AAC5B,MAAI;AACJ,MAAI,cAAc,IAAI,gBAAmB;AACzC,QAAM,OAAO,CAAC,SAAsB;AAClC,QAAI,cAAc,IAAI,gBAAmB;AACzC,QAAI,kBAAkB;AACpB,cAAQ,CAAC;AACT,UAAI,YAAY;AAAS,sBAAc,IAAI,gBAAmB;AAC9D,oBAAc;AAAA,IAChB;AACA,UAAM,KAAK,MAAM;AACf,kBAAY,UAAU;AACtB,WAAK,EAAE,KAAK,YAAY,OAAO,EAAE,MAAM,YAAY,MAAM;AACzD,aAAO,YAAY;AAAA,IACrB,CAAC;AACD,QAAI,CAAC;AAAS,gBAAU,MAAM;AAC9B,WAAO,YAAY;AAAA,EACrB;AACA,QAAM,QAAQ,YAAY;AACxB,WAAO,MAAM,QAAQ;AACnB,YAAM,OAAO,MAAM,MAAM;AACzB,YAAM,KAAK,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAC7B;AACA,cAAU;AAAA,EACZ;AACA,SAAO;AAAA,IACL;AAAA,IACA,OAAO,MAAM,WAAW,QAAQ,QAAQ;AAAA,IACxC,IAAI,OAAO;AACT,aAAO,MAAM;AAAA,IACf;AAAA,EACF;AACF;AAEO,IAAM,oBAAoB,CAAW,OAAO,EAAE,kBAAkB,MAAM,MAAM;AACjF,QAAM,SAA+C,CAAC;AACtD,QAAM,OAAO,CAAC,SAAiB,SAAsB;AACnD,QAAI,CAAC,OAAO,OAAO;AAAG,aAAO,OAAO,IAAI,iBAAoB,IAAI;AAChE,WAAO,OAAO,OAAO,EAAE,KAAK,IAAI;AAAA,EAClC;AACA,QAAM,QAAQ,CAAC,YAAoB;AACjC,QAAI,CAAC,OAAO,OAAO;AAAG,aAAO,OAAO,IAAI,iBAAoB,IAAI;AAChE,WAAO,OAAO,OAAO,EAAE,MAAM;AAAA,EAC/B;AACA,SAAO,EAAE,MAAM,MAAM;AACvB;AAEA,IAAM,kBAAN,MAAyC;AAAA,EAMvC,cAAc;AALd,mBAAU;AACV,mBAA2C,MAAM;AAAA,IAAC;AAClD,kBAAyB,MAAM;AAAA,IAAC;AAI9B,SAAK,UAAU,IAAI,QAAW,CAAC,KAAK,QAAQ;AAC1C,WAAK,UAAU;AACf,WAAK,SAAS;AAAA,IAChB,CAAC;AAAA,EACH;AACF;","names":[]}
|
94
dist/common/SyncLocker.cjs
vendored
Normal file
94
dist/common/SyncLocker.cjs
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var __decorateClass = (decorators, target, key, kind) => {
|
||||
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
||||
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
||||
if (decorator = decorators[i])
|
||||
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
||||
if (kind && result)
|
||||
__defProp(target, key, result);
|
||||
return result;
|
||||
};
|
||||
|
||||
// src/common/SyncLocker.ts
|
||||
var SyncLocker_exports = {};
|
||||
__export(SyncLocker_exports, {
|
||||
SyncLocker: () => SyncLocker
|
||||
});
|
||||
module.exports = __toCommonJS(SyncLocker_exports);
|
||||
|
||||
// src/decorators/singleton.ts
|
||||
var SINGLETON_KEY = Symbol();
|
||||
var singleton = (classTarget) => new Proxy(classTarget, {
|
||||
construct(target, argumentsList, newTarget) {
|
||||
if (target.prototype !== newTarget.prototype) {
|
||||
return Reflect.construct(target, argumentsList, newTarget);
|
||||
}
|
||||
if (!target[SINGLETON_KEY]) {
|
||||
target[SINGLETON_KEY] = Reflect.construct(target, argumentsList, newTarget);
|
||||
}
|
||||
return target[SINGLETON_KEY];
|
||||
}
|
||||
});
|
||||
|
||||
// src/common/ZError.ts
|
||||
var ZError = class {
|
||||
constructor(statusCode, message) {
|
||||
this.statusCode = statusCode;
|
||||
this.message = message;
|
||||
}
|
||||
};
|
||||
|
||||
// src/common/SyncLocker.ts
|
||||
var SyncLocker = class {
|
||||
constructor() {
|
||||
this.map = /* @__PURE__ */ new Map();
|
||||
}
|
||||
lock(req) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ""}`;
|
||||
if (this.map.has(key)) {
|
||||
return false;
|
||||
}
|
||||
this.map.set(key, true);
|
||||
return true;
|
||||
}
|
||||
unlock(req) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ""}`;
|
||||
this.map.delete(key);
|
||||
}
|
||||
checkLock(req) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ""}`;
|
||||
if (this.map.has(key)) {
|
||||
throw new ZError(100, "request too fast");
|
||||
}
|
||||
this.lock(req);
|
||||
return true;
|
||||
}
|
||||
isLocked(req) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ""}`;
|
||||
return this.map.has(key);
|
||||
}
|
||||
};
|
||||
SyncLocker = __decorateClass([
|
||||
singleton
|
||||
], SyncLocker);
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
SyncLocker
|
||||
});
|
||||
//# sourceMappingURL=SyncLocker.cjs.map
|
1
dist/common/SyncLocker.cjs.map
vendored
Normal file
1
dist/common/SyncLocker.cjs.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/common/SyncLocker.ts","../../src/decorators/singleton.ts","../../src/common/ZError.ts"],"sourcesContent":["import { singleton } from 'decorators/singleton'\nimport { ZError } from './ZError'\n\ninterface IRequest {\n method: string\n url: string\n user?: {\n id: string\n }\n}\n\n@singleton\nexport class SyncLocker {\n map: Map<string, boolean> = new Map()\n\n public lock(req: IRequest) {\n const key = `${req.method}:${req.url}:${req.user?.id || ''}`\n if (this.map.has(key)) {\n return false\n }\n this.map.set(key, true)\n return true\n }\n\n public unlock(req: IRequest) {\n const key = `${req.method}:${req.url}:${req.user?.id || ''}`\n this.map.delete(key)\n }\n\n public checkLock(req: IRequest) {\n const key = `${req.method}:${req.url}:${req.user?.id || ''}`\n if (this.map.has(key)) {\n throw new ZError(100, 'request too fast')\n }\n this.lock(req)\n return true\n }\n\n public isLocked(req: IRequest) {\n const key = `${req.method}:${req.url}:${req.user?.id || ''}`\n return this.map.has(key)\n }\n}\n","/**\n * 单例化一个class\n * 使用方法:\n * @singleton\n * class Test {}\n * new Test() === new Test() // returns `true`\n * 也可以不使用 decorator\n * const TestSingleton = singleton(Test)\n * new TestSingleton() === new TestSingleton() //returns 'true'\n */\n\nexport const SINGLETON_KEY = Symbol()\n\nexport type Singleton<T extends new (...args: any[]) => any> = T & {\n [SINGLETON_KEY]: T extends new (...args: any[]) => infer I ? I : never\n}\nexport const singleton = <T extends new (...args: any[]) => any>(classTarget: T) =>\n new Proxy(classTarget, {\n construct(target: Singleton<T>, argumentsList, newTarget) {\n // Skip proxy for children\n if (target.prototype !== newTarget.prototype) {\n return Reflect.construct(target, argumentsList, newTarget)\n }\n if (!target[SINGLETON_KEY]) {\n target[SINGLETON_KEY] = Reflect.construct(target, argumentsList, newTarget)\n }\n return target[SINGLETON_KEY]\n },\n })\n","\nexport class ZError implements Error {\n code: string\n statusCode?: number\n message: string\n name: string\n\n constructor(statusCode: number, message: string) {\n this.statusCode = statusCode\n this.message = message\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACWO,IAAM,gBAAgB,OAAO;AAK7B,IAAM,YAAY,CAAwC,gBAC/D,IAAI,MAAM,aAAa;AAAA,EACrB,UAAU,QAAsB,eAAe,WAAW;AAExD,QAAI,OAAO,cAAc,UAAU,WAAW;AAC5C,aAAO,QAAQ,UAAU,QAAQ,eAAe,SAAS;AAAA,IAC3D;AACA,QAAI,CAAC,OAAO,aAAa,GAAG;AAC1B,aAAO,aAAa,IAAI,QAAQ,UAAU,QAAQ,eAAe,SAAS;AAAA,IAC5E;AACA,WAAO,OAAO,aAAa;AAAA,EAC7B;AACF,CAAC;;;AC3BI,IAAM,SAAN,MAA8B;AAAA,EAMnC,YAAY,YAAoB,SAAiB;AAC/C,SAAK,aAAa;AAClB,SAAK,UAAU;AAAA,EACjB;AACF;;;AFCO,IAAM,aAAN,MAAiB;AAAA,EAAjB;AACL,eAA4B,oBAAI,IAAI;AAAA;AAAA,EAE7B,KAAK,KAAe;AACzB,UAAM,MAAM,GAAG,IAAI,MAAM,IAAI,IAAI,GAAG,IAAI,IAAI,MAAM,MAAM,EAAE;AAC1D,QAAI,KAAK,IAAI,IAAI,GAAG,GAAG;AACrB,aAAO;AAAA,IACT;AACA,SAAK,IAAI,IAAI,KAAK,IAAI;AACtB,WAAO;AAAA,EACT;AAAA,EAEO,OAAO,KAAe;AAC3B,UAAM,MAAM,GAAG,IAAI,MAAM,IAAI,IAAI,GAAG,IAAI,IAAI,MAAM,MAAM,EAAE;AAC1D,SAAK,IAAI,OAAO,GAAG;AAAA,EACrB;AAAA,EAEO,UAAU,KAAe;AAC9B,UAAM,MAAM,GAAG,IAAI,MAAM,IAAI,IAAI,GAAG,IAAI,IAAI,MAAM,MAAM,EAAE;AAC1D,QAAI,KAAK,IAAI,IAAI,GAAG,GAAG;AACrB,YAAM,IAAI,OAAO,KAAK,kBAAkB;AAAA,IAC1C;AACA,SAAK,KAAK,GAAG;AACb,WAAO;AAAA,EACT;AAAA,EAEO,SAAS,KAAe;AAC7B,UAAM,MAAM,GAAG,IAAI,MAAM,IAAI,IAAI,GAAG,IAAI,IAAI,MAAM,MAAM,EAAE;AAC1D,WAAO,KAAK,IAAI,IAAI,GAAG;AAAA,EACzB;AACF;AA9Ba,aAAN;AAAA,EADN;AAAA,GACY;","names":[]}
|
16
dist/common/SyncLocker.d.cts
vendored
Normal file
16
dist/common/SyncLocker.d.cts
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
interface IRequest {
|
||||
method: string;
|
||||
url: string;
|
||||
user?: {
|
||||
id: string;
|
||||
};
|
||||
}
|
||||
declare class SyncLocker {
|
||||
map: Map<string, boolean>;
|
||||
lock(req: IRequest): boolean;
|
||||
unlock(req: IRequest): void;
|
||||
checkLock(req: IRequest): boolean;
|
||||
isLocked(req: IRequest): boolean;
|
||||
}
|
||||
|
||||
export { SyncLocker };
|
16
dist/common/SyncLocker.d.ts
vendored
Normal file
16
dist/common/SyncLocker.d.ts
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
interface IRequest {
|
||||
method: string;
|
||||
url: string;
|
||||
user?: {
|
||||
id: string;
|
||||
};
|
||||
}
|
||||
declare class SyncLocker {
|
||||
map: Map<string, boolean>;
|
||||
lock(req: IRequest): boolean;
|
||||
unlock(req: IRequest): void;
|
||||
checkLock(req: IRequest): boolean;
|
||||
isLocked(req: IRequest): boolean;
|
||||
}
|
||||
|
||||
export { SyncLocker };
|
71
dist/common/SyncLocker.js
vendored
Normal file
71
dist/common/SyncLocker.js
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __decorateClass = (decorators, target, key, kind) => {
|
||||
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
||||
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
||||
if (decorator = decorators[i])
|
||||
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
||||
if (kind && result)
|
||||
__defProp(target, key, result);
|
||||
return result;
|
||||
};
|
||||
|
||||
// src/decorators/singleton.ts
|
||||
var SINGLETON_KEY = Symbol();
|
||||
var singleton = (classTarget) => new Proxy(classTarget, {
|
||||
construct(target, argumentsList, newTarget) {
|
||||
if (target.prototype !== newTarget.prototype) {
|
||||
return Reflect.construct(target, argumentsList, newTarget);
|
||||
}
|
||||
if (!target[SINGLETON_KEY]) {
|
||||
target[SINGLETON_KEY] = Reflect.construct(target, argumentsList, newTarget);
|
||||
}
|
||||
return target[SINGLETON_KEY];
|
||||
}
|
||||
});
|
||||
|
||||
// src/common/ZError.ts
|
||||
var ZError = class {
|
||||
constructor(statusCode, message) {
|
||||
this.statusCode = statusCode;
|
||||
this.message = message;
|
||||
}
|
||||
};
|
||||
|
||||
// src/common/SyncLocker.ts
|
||||
var SyncLocker = class {
|
||||
constructor() {
|
||||
this.map = /* @__PURE__ */ new Map();
|
||||
}
|
||||
lock(req) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ""}`;
|
||||
if (this.map.has(key)) {
|
||||
return false;
|
||||
}
|
||||
this.map.set(key, true);
|
||||
return true;
|
||||
}
|
||||
unlock(req) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ""}`;
|
||||
this.map.delete(key);
|
||||
}
|
||||
checkLock(req) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ""}`;
|
||||
if (this.map.has(key)) {
|
||||
throw new ZError(100, "request too fast");
|
||||
}
|
||||
this.lock(req);
|
||||
return true;
|
||||
}
|
||||
isLocked(req) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ""}`;
|
||||
return this.map.has(key);
|
||||
}
|
||||
};
|
||||
SyncLocker = __decorateClass([
|
||||
singleton
|
||||
], SyncLocker);
|
||||
export {
|
||||
SyncLocker
|
||||
};
|
||||
//# sourceMappingURL=SyncLocker.js.map
|
1
dist/common/SyncLocker.js.map
vendored
Normal file
1
dist/common/SyncLocker.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/decorators/singleton.ts","../../src/common/ZError.ts","../../src/common/SyncLocker.ts"],"sourcesContent":["/**\n * 单例化一个class\n * 使用方法:\n * @singleton\n * class Test {}\n * new Test() === new Test() // returns `true`\n * 也可以不使用 decorator\n * const TestSingleton = singleton(Test)\n * new TestSingleton() === new TestSingleton() //returns 'true'\n */\n\nexport const SINGLETON_KEY = Symbol()\n\nexport type Singleton<T extends new (...args: any[]) => any> = T & {\n [SINGLETON_KEY]: T extends new (...args: any[]) => infer I ? I : never\n}\nexport const singleton = <T extends new (...args: any[]) => any>(classTarget: T) =>\n new Proxy(classTarget, {\n construct(target: Singleton<T>, argumentsList, newTarget) {\n // Skip proxy for children\n if (target.prototype !== newTarget.prototype) {\n return Reflect.construct(target, argumentsList, newTarget)\n }\n if (!target[SINGLETON_KEY]) {\n target[SINGLETON_KEY] = Reflect.construct(target, argumentsList, newTarget)\n }\n return target[SINGLETON_KEY]\n },\n })\n","\nexport class ZError implements Error {\n code: string\n statusCode?: number\n message: string\n name: string\n\n constructor(statusCode: number, message: string) {\n this.statusCode = statusCode\n this.message = message\n }\n}\n","import { singleton } from 'decorators/singleton'\nimport { ZError } from './ZError'\n\ninterface IRequest {\n method: string\n url: string\n user?: {\n id: string\n }\n}\n\n@singleton\nexport class SyncLocker {\n map: Map<string, boolean> = new Map()\n\n public lock(req: IRequest) {\n const key = `${req.method}:${req.url}:${req.user?.id || ''}`\n if (this.map.has(key)) {\n return false\n }\n this.map.set(key, true)\n return true\n }\n\n public unlock(req: IRequest) {\n const key = `${req.method}:${req.url}:${req.user?.id || ''}`\n this.map.delete(key)\n }\n\n public checkLock(req: IRequest) {\n const key = `${req.method}:${req.url}:${req.user?.id || ''}`\n if (this.map.has(key)) {\n throw new ZError(100, 'request too fast')\n }\n this.lock(req)\n return true\n }\n\n public isLocked(req: IRequest) {\n const key = `${req.method}:${req.url}:${req.user?.id || ''}`\n return this.map.has(key)\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAWO,IAAM,gBAAgB,OAAO;AAK7B,IAAM,YAAY,CAAwC,gBAC/D,IAAI,MAAM,aAAa;AAAA,EACrB,UAAU,QAAsB,eAAe,WAAW;AAExD,QAAI,OAAO,cAAc,UAAU,WAAW;AAC5C,aAAO,QAAQ,UAAU,QAAQ,eAAe,SAAS;AAAA,IAC3D;AACA,QAAI,CAAC,OAAO,aAAa,GAAG;AAC1B,aAAO,aAAa,IAAI,QAAQ,UAAU,QAAQ,eAAe,SAAS;AAAA,IAC5E;AACA,WAAO,OAAO,aAAa;AAAA,EAC7B;AACF,CAAC;;;AC3BI,IAAM,SAAN,MAA8B;AAAA,EAMnC,YAAY,YAAoB,SAAiB;AAC/C,SAAK,aAAa;AAClB,SAAK,UAAU;AAAA,EACjB;AACF;;;ACCO,IAAM,aAAN,MAAiB;AAAA,EAAjB;AACL,eAA4B,oBAAI,IAAI;AAAA;AAAA,EAE7B,KAAK,KAAe;AACzB,UAAM,MAAM,GAAG,IAAI,MAAM,IAAI,IAAI,GAAG,IAAI,IAAI,MAAM,MAAM,EAAE;AAC1D,QAAI,KAAK,IAAI,IAAI,GAAG,GAAG;AACrB,aAAO;AAAA,IACT;AACA,SAAK,IAAI,IAAI,KAAK,IAAI;AACtB,WAAO;AAAA,EACT;AAAA,EAEO,OAAO,KAAe;AAC3B,UAAM,MAAM,GAAG,IAAI,MAAM,IAAI,IAAI,GAAG,IAAI,IAAI,MAAM,MAAM,EAAE;AAC1D,SAAK,IAAI,OAAO,GAAG;AAAA,EACrB;AAAA,EAEO,UAAU,KAAe;AAC9B,UAAM,MAAM,GAAG,IAAI,MAAM,IAAI,IAAI,GAAG,IAAI,IAAI,MAAM,MAAM,EAAE;AAC1D,QAAI,KAAK,IAAI,IAAI,GAAG,GAAG;AACrB,YAAM,IAAI,OAAO,KAAK,kBAAkB;AAAA,IAC1C;AACA,SAAK,KAAK,GAAG;AACb,WAAO;AAAA,EACT;AAAA,EAEO,SAAS,KAAe;AAC7B,UAAM,MAAM,GAAG,IAAI,MAAM,IAAI,IAAI,GAAG,IAAI,IAAI,MAAM,MAAM,EAAE;AAC1D,WAAO,KAAK,IAAI,IAAI,GAAG;AAAA,EACzB;AACF;AA9Ba,aAAN;AAAA,EADN;AAAA,GACY;","names":[]}
|
116
dist/index.cjs
vendored
116
dist/index.cjs
vendored
@ -15,12 +15,24 @@ var __copyProps = (to, from, except, desc) => {
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var __decorateClass = (decorators, target, key, kind) => {
|
||||
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
||||
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
||||
if (decorator = decorators[i])
|
||||
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
||||
if (kind && result)
|
||||
__defProp(target, key, result);
|
||||
return result;
|
||||
};
|
||||
|
||||
// src/index.ts
|
||||
var src_exports = {};
|
||||
__export(src_exports, {
|
||||
SINGLETON_KEY: () => SINGLETON_KEY,
|
||||
SyncLocker: () => SyncLocker,
|
||||
ZError: () => ZError,
|
||||
createAsyncQueue: () => createAsyncQueue,
|
||||
createAsyncQueues: () => createAsyncQueues,
|
||||
singleton: () => singleton
|
||||
});
|
||||
module.exports = __toCommonJS(src_exports);
|
||||
@ -46,10 +58,114 @@ var singleton = (classTarget) => new Proxy(classTarget, {
|
||||
return target[SINGLETON_KEY];
|
||||
}
|
||||
});
|
||||
|
||||
// src/common/SyncLocker.ts
|
||||
var SyncLocker = class {
|
||||
constructor() {
|
||||
this.map = /* @__PURE__ */ new Map();
|
||||
}
|
||||
lock(req) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ""}`;
|
||||
if (this.map.has(key)) {
|
||||
return false;
|
||||
}
|
||||
this.map.set(key, true);
|
||||
return true;
|
||||
}
|
||||
unlock(req) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ""}`;
|
||||
this.map.delete(key);
|
||||
}
|
||||
checkLock(req) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ""}`;
|
||||
if (this.map.has(key)) {
|
||||
throw new ZError(100, "request too fast");
|
||||
}
|
||||
this.lock(req);
|
||||
return true;
|
||||
}
|
||||
isLocked(req) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ""}`;
|
||||
return this.map.has(key);
|
||||
}
|
||||
};
|
||||
SyncLocker = __decorateClass([
|
||||
singleton
|
||||
], SyncLocker);
|
||||
|
||||
// src/common/AsyncQueue.ts
|
||||
function createAsyncQueue(opts = { dedupeConcurrent: false }) {
|
||||
const { dedupeConcurrent } = opts;
|
||||
let queue = [];
|
||||
let running;
|
||||
let nextPromise = new DeferredPromise();
|
||||
const push = (task) => {
|
||||
let taskPromise = new DeferredPromise();
|
||||
if (dedupeConcurrent) {
|
||||
queue = [];
|
||||
if (nextPromise.started)
|
||||
nextPromise = new DeferredPromise();
|
||||
taskPromise = nextPromise;
|
||||
}
|
||||
queue.push(() => {
|
||||
taskPromise.started = true;
|
||||
task().then(taskPromise.resolve).catch(taskPromise.reject);
|
||||
return taskPromise.promise;
|
||||
});
|
||||
if (!running)
|
||||
running = start();
|
||||
return taskPromise.promise;
|
||||
};
|
||||
const start = async () => {
|
||||
while (queue.length) {
|
||||
const task = queue.shift();
|
||||
await task().catch(() => {
|
||||
});
|
||||
}
|
||||
running = void 0;
|
||||
};
|
||||
return {
|
||||
push,
|
||||
flush: () => running || Promise.resolve(),
|
||||
get size() {
|
||||
return queue.length;
|
||||
}
|
||||
};
|
||||
}
|
||||
var createAsyncQueues = (opts = { dedupeConcurrent: false }) => {
|
||||
const queues = {};
|
||||
const push = (queueId, task) => {
|
||||
if (!queues[queueId])
|
||||
queues[queueId] = createAsyncQueue(opts);
|
||||
return queues[queueId].push(task);
|
||||
};
|
||||
const flush = (queueId) => {
|
||||
if (!queues[queueId])
|
||||
queues[queueId] = createAsyncQueue(opts);
|
||||
return queues[queueId].flush();
|
||||
};
|
||||
return { push, flush };
|
||||
};
|
||||
var DeferredPromise = class {
|
||||
constructor() {
|
||||
this.started = false;
|
||||
this.resolve = () => {
|
||||
};
|
||||
this.reject = () => {
|
||||
};
|
||||
this.promise = new Promise((res, rej) => {
|
||||
this.resolve = res;
|
||||
this.reject = rej;
|
||||
});
|
||||
}
|
||||
};
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
SINGLETON_KEY,
|
||||
SyncLocker,
|
||||
ZError,
|
||||
createAsyncQueue,
|
||||
createAsyncQueues,
|
||||
singleton
|
||||
});
|
||||
//# sourceMappingURL=index.cjs.map
|
2
dist/index.cjs.map
vendored
2
dist/index.cjs.map
vendored
File diff suppressed because one or more lines are too long
2
dist/index.d.cts
vendored
2
dist/index.d.cts
vendored
@ -1,4 +1,6 @@
|
||||
export { ZError } from './common/ZError.cjs';
|
||||
export { SyncLocker } from './common/SyncLocker.cjs';
|
||||
export { AsyncQueue, createAsyncQueue, createAsyncQueues } from './common/AsyncQueue.cjs';
|
||||
|
||||
/**
|
||||
* 单例化一个class
|
||||
|
2
dist/index.d.ts
vendored
2
dist/index.d.ts
vendored
@ -1,4 +1,6 @@
|
||||
export { ZError } from './common/ZError.js';
|
||||
export { SyncLocker } from './common/SyncLocker.js';
|
||||
export { AsyncQueue, createAsyncQueue, createAsyncQueues } from './common/AsyncQueue.js';
|
||||
|
||||
/**
|
||||
* 单例化一个class
|
||||
|
116
dist/index.js
vendored
116
dist/index.js
vendored
@ -1,3 +1,15 @@
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __decorateClass = (decorators, target, key, kind) => {
|
||||
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
||||
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
||||
if (decorator = decorators[i])
|
||||
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
||||
if (kind && result)
|
||||
__defProp(target, key, result);
|
||||
return result;
|
||||
};
|
||||
|
||||
// src/common/ZError.ts
|
||||
var ZError = class {
|
||||
constructor(statusCode, message) {
|
||||
@ -19,9 +31,113 @@ var singleton = (classTarget) => new Proxy(classTarget, {
|
||||
return target[SINGLETON_KEY];
|
||||
}
|
||||
});
|
||||
|
||||
// src/common/SyncLocker.ts
|
||||
var SyncLocker = class {
|
||||
constructor() {
|
||||
this.map = /* @__PURE__ */ new Map();
|
||||
}
|
||||
lock(req) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ""}`;
|
||||
if (this.map.has(key)) {
|
||||
return false;
|
||||
}
|
||||
this.map.set(key, true);
|
||||
return true;
|
||||
}
|
||||
unlock(req) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ""}`;
|
||||
this.map.delete(key);
|
||||
}
|
||||
checkLock(req) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ""}`;
|
||||
if (this.map.has(key)) {
|
||||
throw new ZError(100, "request too fast");
|
||||
}
|
||||
this.lock(req);
|
||||
return true;
|
||||
}
|
||||
isLocked(req) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ""}`;
|
||||
return this.map.has(key);
|
||||
}
|
||||
};
|
||||
SyncLocker = __decorateClass([
|
||||
singleton
|
||||
], SyncLocker);
|
||||
|
||||
// src/common/AsyncQueue.ts
|
||||
function createAsyncQueue(opts = { dedupeConcurrent: false }) {
|
||||
const { dedupeConcurrent } = opts;
|
||||
let queue = [];
|
||||
let running;
|
||||
let nextPromise = new DeferredPromise();
|
||||
const push = (task) => {
|
||||
let taskPromise = new DeferredPromise();
|
||||
if (dedupeConcurrent) {
|
||||
queue = [];
|
||||
if (nextPromise.started)
|
||||
nextPromise = new DeferredPromise();
|
||||
taskPromise = nextPromise;
|
||||
}
|
||||
queue.push(() => {
|
||||
taskPromise.started = true;
|
||||
task().then(taskPromise.resolve).catch(taskPromise.reject);
|
||||
return taskPromise.promise;
|
||||
});
|
||||
if (!running)
|
||||
running = start();
|
||||
return taskPromise.promise;
|
||||
};
|
||||
const start = async () => {
|
||||
while (queue.length) {
|
||||
const task = queue.shift();
|
||||
await task().catch(() => {
|
||||
});
|
||||
}
|
||||
running = void 0;
|
||||
};
|
||||
return {
|
||||
push,
|
||||
flush: () => running || Promise.resolve(),
|
||||
get size() {
|
||||
return queue.length;
|
||||
}
|
||||
};
|
||||
}
|
||||
var createAsyncQueues = (opts = { dedupeConcurrent: false }) => {
|
||||
const queues = {};
|
||||
const push = (queueId, task) => {
|
||||
if (!queues[queueId])
|
||||
queues[queueId] = createAsyncQueue(opts);
|
||||
return queues[queueId].push(task);
|
||||
};
|
||||
const flush = (queueId) => {
|
||||
if (!queues[queueId])
|
||||
queues[queueId] = createAsyncQueue(opts);
|
||||
return queues[queueId].flush();
|
||||
};
|
||||
return { push, flush };
|
||||
};
|
||||
var DeferredPromise = class {
|
||||
constructor() {
|
||||
this.started = false;
|
||||
this.resolve = () => {
|
||||
};
|
||||
this.reject = () => {
|
||||
};
|
||||
this.promise = new Promise((res, rej) => {
|
||||
this.resolve = res;
|
||||
this.reject = rej;
|
||||
});
|
||||
}
|
||||
};
|
||||
export {
|
||||
SINGLETON_KEY,
|
||||
SyncLocker,
|
||||
ZError,
|
||||
createAsyncQueue,
|
||||
createAsyncQueues,
|
||||
singleton
|
||||
};
|
||||
//# sourceMappingURL=index.js.map
|
2
dist/index.js.map
vendored
2
dist/index.js.map
vendored
File diff suppressed because one or more lines are too long
107
src/common/AsyncQueue.ts
Normal file
107
src/common/AsyncQueue.ts
Normal file
@ -0,0 +1,107 @@
|
||||
type Callback<T> = () => Promise<T>
|
||||
|
||||
export type AsyncQueue<T = void> = {
|
||||
push: (task: Callback<T>) => Promise<T>
|
||||
flush: () => Promise<void>
|
||||
size: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that each callback pushed onto the queue is executed in series.
|
||||
* Such a quetie 😻
|
||||
* @param opts.dedupeConcurrent If dedupeConcurrent is `true` it ensures that if multiple
|
||||
* tasks are pushed onto the queue while there is an active task, only the
|
||||
* last one will be executed, once the active task has completed.
|
||||
* e.g. in the below example, only 0 and 3 will be executed.
|
||||
* ```
|
||||
* const queue = createAsyncQueue({ dedupeConcurrent: true })
|
||||
* queue.push(async () => console.log(0)) // returns 0
|
||||
* queue.push(async () => console.log(1)) // returns 3
|
||||
* queue.push(async () => console.log(2)) // returns 3
|
||||
* queue.push(async () => console.log(3)) // returns 3
|
||||
* ```
|
||||
* */
|
||||
export function createAsyncQueue<T = void>(opts = { dedupeConcurrent: false }): AsyncQueue<T> {
|
||||
const { dedupeConcurrent } = opts
|
||||
let queue: Callback<T>[] = []
|
||||
let running: Promise<void> | undefined
|
||||
let nextPromise = new DeferredPromise<T>()
|
||||
const push = (task: Callback<T>) => {
|
||||
let taskPromise = new DeferredPromise<T>()
|
||||
if (dedupeConcurrent) {
|
||||
queue = []
|
||||
if (nextPromise.started) nextPromise = new DeferredPromise<T>()
|
||||
taskPromise = nextPromise
|
||||
}
|
||||
queue.push(() => {
|
||||
taskPromise.started = true
|
||||
task().then(taskPromise.resolve).catch(taskPromise.reject)
|
||||
return taskPromise.promise
|
||||
})
|
||||
if (!running) running = start()
|
||||
return taskPromise.promise
|
||||
}
|
||||
const start = async () => {
|
||||
while (queue.length) {
|
||||
const task = queue.shift()!
|
||||
await task().catch(() => {})
|
||||
}
|
||||
running = undefined
|
||||
}
|
||||
return {
|
||||
push,
|
||||
flush: () => running || Promise.resolve(),
|
||||
get size() {
|
||||
return queue.length
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export const createAsyncQueues = <T = void>(opts = { dedupeConcurrent: false }) => {
|
||||
const queues: { [queueId: string]: AsyncQueue<T> } = {}
|
||||
const push = (queueId: string, task: Callback<T>) => {
|
||||
if (!queues[queueId]) queues[queueId] = createAsyncQueue<T>(opts)
|
||||
return queues[queueId].push(task)
|
||||
}
|
||||
const flush = (queueId: string) => {
|
||||
if (!queues[queueId]) queues[queueId] = createAsyncQueue<T>(opts)
|
||||
return queues[queueId].flush()
|
||||
}
|
||||
return { push, flush }
|
||||
}
|
||||
|
||||
class DeferredPromise<T = void, E = any> {
|
||||
started = false
|
||||
resolve: (x: T | PromiseLike<T>) => void = () => {}
|
||||
reject: (x: E) => void = () => {}
|
||||
promise: Promise<T>
|
||||
|
||||
constructor() {
|
||||
this.promise = new Promise<T>((res, rej) => {
|
||||
this.resolve = res
|
||||
this.reject = rej
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// function main() {
|
||||
// const queue = createAsyncQueue()
|
||||
// queue.push(async () => {
|
||||
// console.log(0)
|
||||
// }) // returns 0
|
||||
// queue.push(async () => {
|
||||
// console.log(1)
|
||||
|
||||
// return new Promise((resolve, reject) => {
|
||||
// setTimeout(() => {
|
||||
// console.log('12')
|
||||
// resolve()
|
||||
// }, 1000)
|
||||
// })
|
||||
// }) // returns 3
|
||||
// queue.push(async () => console.log(2)) // returns 3
|
||||
// queue.push(async () => console.log(3)) // returns 3
|
||||
// console.log('hi')
|
||||
// }
|
||||
|
||||
// main()
|
43
src/common/SyncLocker.ts
Normal file
43
src/common/SyncLocker.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { singleton } from 'decorators/singleton'
|
||||
import { ZError } from './ZError'
|
||||
|
||||
interface IRequest {
|
||||
method: string
|
||||
url: string
|
||||
user?: {
|
||||
id: string
|
||||
}
|
||||
}
|
||||
|
||||
@singleton
|
||||
export class SyncLocker {
|
||||
map: Map<string, boolean> = new Map()
|
||||
|
||||
public lock(req: IRequest) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ''}`
|
||||
if (this.map.has(key)) {
|
||||
return false
|
||||
}
|
||||
this.map.set(key, true)
|
||||
return true
|
||||
}
|
||||
|
||||
public unlock(req: IRequest) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ''}`
|
||||
this.map.delete(key)
|
||||
}
|
||||
|
||||
public checkLock(req: IRequest) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ''}`
|
||||
if (this.map.has(key)) {
|
||||
throw new ZError(100, 'request too fast')
|
||||
}
|
||||
this.lock(req)
|
||||
return true
|
||||
}
|
||||
|
||||
public isLocked(req: IRequest) {
|
||||
const key = `${req.method}:${req.url}:${req.user?.id || ''}`
|
||||
return this.map.has(key)
|
||||
}
|
||||
}
|
@ -1,2 +1,4 @@
|
||||
export { ZError } from './common/ZError'
|
||||
export * from './decorators/singleton'
|
||||
export { SyncLocker } from './common/SyncLocker'
|
||||
export * from './decorators/singleton'
|
||||
export * from './common/AsyncQueue'
|
Loading…
x
Reference in New Issue
Block a user