From 017433fdf90b34fad8624a5b1e29df5346ca0060 Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Tue, 31 Mar 2020 11:51:47 +0300 Subject: [PATCH] Rewrite task queue --- package-lock.json | 49 ++++-- package.json | 5 +- src/Action/Action.ts | 3 - src/Action/ActionController.ts | 6 + src/Action/GoToBuildingAction.ts | 8 +- src/Action/GoToResourceFieldsAction.ts | 8 +- src/Action/StoreRemainingBuildTimeAction.ts | 7 +- src/Action/UpgradeBuildingAction.ts | 9 +- src/Dashboard.ts | 9 +- src/Errors.ts | 9 +- src/Scheduler.ts | 125 +++++++++------- src/Storage/TaskQueue.ts | 158 +++++++++----------- src/Task/TaskController.ts | 5 + src/Task/UpgradeBuildingTask.ts | 12 +- src/TaskQueueRenderer.ts | 25 ++-- src/utils.ts | 12 ++ tsconfig.json | 2 +- 17 files changed, 253 insertions(+), 199 deletions(-) delete mode 100644 src/Action/Action.ts create mode 100644 src/Action/ActionController.ts create mode 100644 src/Task/TaskController.ts diff --git a/package-lock.json b/package-lock.json index edd961a..5fd683e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -345,6 +345,15 @@ "integrity": "sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w==", "dev": true }, + "@types/nanoid": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/nanoid/-/nanoid-2.1.0.tgz", + "integrity": "sha512-xdkn/oRTA0GSNPLIKZgHWqDTWZsVrieKomxJBOQUK9YDD+zfSgmwD5t4WJYra5S7XyhTw7tfvwznW+pFexaepQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/node": { "version": "13.9.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.4.tgz", @@ -363,12 +372,6 @@ "integrity": "sha512-4kHAkbV/OfW2kb5BLVUuUMoumB3CP8rHqlw48aHvFy5tf9ER0AfOonBlX29l/DD68G70DmyhRlSYfQPSYpC5Vw==", "dev": true }, - "@types/uuid": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-7.0.2.tgz", - "integrity": "sha512-8Ly3zIPTnT0/8RCU6Kg/G3uTICf9sRwYOpUzSIM3503tLIKcnJPRuinHhXngJUy2MntrEf6dlpOHXJju90Qh5w==", - "dev": true - }, "@webassemblyjs/ast": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", @@ -3964,6 +3967,12 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, + "nanoid": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.0.2.tgz", + "integrity": "sha512-WOjyy/xu3199NlQiQWlx7VbspSFlGtOxa1bRX9ebmXOnp1fje4bJfjPs1wLQ8jZbJUfD+yceJmw879ZSaVJkdQ==", + "dev": true + }, "nanomatch": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", @@ -4789,6 +4798,28 @@ "safe-buffer": "^5.1.0" } }, + "raw-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.0.tgz", + "integrity": "sha512-iINUOYvl1cGEmfoaLjnZXt4bKfT2LJnZZib5N/LLyAphC+Dd11vNP9CNVb38j+SAJpFI1uo8j9frmih53ASy7Q==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.5.0" + }, + "dependencies": { + "schema-utils": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz", + "integrity": "sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ==", + "dev": true, + "requires": { + "ajv": "^6.12.0", + "ajv-keywords": "^3.4.1" + } + } + } + }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", @@ -5871,12 +5902,6 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "uuid": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.2.tgz", - "integrity": "sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw==", - "dev": true - }, "v8-compile-cache": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", diff --git a/package.json b/package.json index 02d83fd..3572a83 100644 --- a/package.json +++ b/package.json @@ -24,20 +24,21 @@ "@types/chai": "^4.2.11", "@types/jquery": "^3.3.34", "@types/mocha": "^7.0.2", + "@types/nanoid": "^2.1.0", "@types/node": "^13.9.4", "@types/url-parse": "^1.4.3", - "@types/uuid": "^7.0.2", "chai": "^4.2.0", "jquery": "^3.4.1", "mocha": "^7.1.1", "mocha-junit-reporter": "^1.23.3", + "nanoid": "^3.0.2", "nyc": "^15.0.0", "prettier": "^1.19.1", + "raw-loader": "^4.0.0", "ts-loader": "^6.2.2", "ts-node": "^8.8.1", "typescript": "^3.8.3", "url-parse": "^1.4.7", - "uuid": "^7.0.2", "webpack": "^4.42.1", "webpack-cli": "^3.3.11" } diff --git a/src/Action/Action.ts b/src/Action/Action.ts deleted file mode 100644 index 7966315..0000000 --- a/src/Action/Action.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default abstract class Action { - abstract async run(args); -} diff --git a/src/Action/ActionController.ts b/src/Action/ActionController.ts new file mode 100644 index 0000000..c1724da --- /dev/null +++ b/src/Action/ActionController.ts @@ -0,0 +1,6 @@ +import { Args } from '../Common'; +import { Task } from '../Storage/TaskQueue'; + +export default abstract class ActionController { + abstract async run(args: Args, task: Task); +} diff --git a/src/Action/GoToBuildingAction.ts b/src/Action/GoToBuildingAction.ts index 83e6a86..5355d72 100644 --- a/src/Action/GoToBuildingAction.ts +++ b/src/Action/GoToBuildingAction.ts @@ -1,9 +1,11 @@ -import Action from './Action'; +import ActionController from './ActionController'; +import { Args } from '../Common'; +import { Task } from '../Storage/TaskQueue'; -export default class GoToBuildingAction extends Action { +export default class GoToBuildingAction extends ActionController { static NAME = 'go_to_building'; - async run(args): Promise { + async run(args: Args, task: Task): Promise { window.location.assign('/build.php?id=' + args.id); return null; } diff --git a/src/Action/GoToResourceFieldsAction.ts b/src/Action/GoToResourceFieldsAction.ts index 290a62b..f12c9ad 100644 --- a/src/Action/GoToResourceFieldsAction.ts +++ b/src/Action/GoToResourceFieldsAction.ts @@ -1,8 +1,10 @@ -import Action from './Action'; +import ActionController from './ActionController'; +import { Args } from '../Common'; +import { Task } from '../Storage/TaskQueue'; -export default class GoToResourceFieldsAction extends Action { +export default class GoToResourceFieldsAction extends ActionController { static NAME = 'go_to_resource_fields'; - async run(): Promise { + async run(args: Args, task: Task): Promise { window.location.assign('/dorf1.php'); } } diff --git a/src/Action/StoreRemainingBuildTimeAction.ts b/src/Action/StoreRemainingBuildTimeAction.ts index 71436ec..be876bc 100644 --- a/src/Action/StoreRemainingBuildTimeAction.ts +++ b/src/Action/StoreRemainingBuildTimeAction.ts @@ -1,10 +1,11 @@ -import Action from './Action'; +import ActionController from './ActionController'; import { Args } from '../Common'; +import { Task } from '../Storage/TaskQueue'; -export default class StoreRemainingBuildTimeAction extends Action { +export default class StoreRemainingBuildTimeAction extends ActionController { static NAME = 'store_remaining_build_time'; - async run(args: Args): Promise { + async run(args: Args, task: Task): Promise { const timer = jQuery('.buildDuration .timer'); // if (timer.length === 1) { // const remainingSeconds = +timer.val(); diff --git a/src/Action/UpgradeBuildingAction.ts b/src/Action/UpgradeBuildingAction.ts index 598ab25..ac13826 100644 --- a/src/Action/UpgradeBuildingAction.ts +++ b/src/Action/UpgradeBuildingAction.ts @@ -1,9 +1,10 @@ -import Action from './Action'; +import ActionController from './ActionController'; import { Args } from '../Common'; import { TryLaterError } from '../Errors'; import Scheduler from '../Scheduler'; +import { Task } from '../Storage/TaskQueue'; -export default class UpgradeBuildingAction extends Action { +export default class UpgradeBuildingAction extends ActionController { static NAME = 'upgrade_building'; private scheduler: Scheduler; @@ -12,12 +13,12 @@ export default class UpgradeBuildingAction extends Action { this.scheduler = scheduler; } - async run(args: Args): Promise { + async run(args: Args, task: Task): Promise { const btn = jQuery( '.upgradeButtonsContainer .section1 button.green.build' ); if (btn.length === 1) { - this.scheduler.completeCurrentTask(); + this.scheduler.completeTask(task.id); btn.trigger('click'); } else { throw new TryLaterError(60, 'No upgrade button, try later'); diff --git a/src/Dashboard.ts b/src/Dashboard.ts index c115e11..df15a40 100644 --- a/src/Dashboard.ts +++ b/src/Dashboard.ts @@ -1,6 +1,5 @@ import * as URLParse from 'url-parse'; -import { markPage, sleep, sleepShort } from './utils'; -import { v4 as uuid } from 'uuid'; +import { markPage, sleep, uniqId } from './utils'; import Scheduler from './Scheduler'; import UpgradeBuildingTask from './Task/UpgradeBuildingTask'; import { Command } from './Common'; @@ -23,7 +22,7 @@ export default class Dashboard { console.log('PARSED LOCATION', p); markPage('Dashboard', this.version); - new TaskQueueRenderer().render(this.scheduler.taskState()); + new TaskQueueRenderer().render(this.scheduler.getTaskItems()); if (p.pathname === '/dorf1.php') { this.showSlotIds('buildingSlot'); @@ -35,7 +34,7 @@ export default class Dashboard { if (p.pathname === '/build.php') { console.log('BUILD PAGE DETECTED'); - const id = uuid(); + const id = uniqId(); jQuery('.upgradeButtonsContainer .section1').append( `` ); @@ -43,7 +42,7 @@ export default class Dashboard { const queueItem = new Command(UpgradeBuildingTask.NAME, { id: p.query['id'], }); - this.scheduler.pushTask(queueItem); + this.scheduler.scheduleTask(queueItem); return false; }); } diff --git a/src/Errors.ts b/src/Errors.ts index d092f4c..15b590f 100644 --- a/src/Errors.ts +++ b/src/Errors.ts @@ -1,9 +1,12 @@ +import { TaskId } from './Storage/TaskQueue'; + export class TryLaterError extends Error { readonly seconds: number; - constructor(s: number, msg: string = '') { + readonly id: TaskId; + constructor(seconds: number, id: TaskId, msg: string = '') { super(msg); - this.seconds = s; - // Set the prototype explicitly. + this.id = id; + this.seconds = seconds; Object.setPrototypeOf(this, TryLaterError.prototype); } } diff --git a/src/Scheduler.ts b/src/Scheduler.ts index 9c410d8..da59977 100644 --- a/src/Scheduler.ts +++ b/src/Scheduler.ts @@ -1,12 +1,14 @@ -import { markPage, sleepLong, sleepShort } from './utils'; +import { markPage, sleepLong, sleepShort, timestamp } from './utils'; import UpgradeBuildingTask from './Task/UpgradeBuildingTask'; import GoToBuildingAction from './Action/GoToBuildingAction'; import UpgradeBuildingAction from './Action/UpgradeBuildingAction'; import { TryLaterError } from './Errors'; -import { TaskQueue, ImmutableState } from './Storage/TaskQueue'; +import { TaskQueue, TaskList, Task, TaskId } from './Storage/TaskQueue'; import ActionQueue from './Storage/ActionQueue'; import { Args, Command } from './Common'; import TaskQueueRenderer from './TaskQueueRenderer'; +import ActionController from './Action/ActionController'; +import TaskController from './Task/TaskController'; enum SleepType { Long, @@ -29,31 +31,53 @@ export default class Scheduler { async run() { await sleepShort(); markPage('Executor', this.version); - new TaskQueueRenderer().render(this.taskQueue.state()); + new TaskQueueRenderer().render(this.taskQueue.seeItems()); + while (true) { - await this.sleep(); - const actionItem = this.popAction(); - this.log('POP ACTION ITEM', actionItem); - if (actionItem !== null) { - const action = this.createAction(actionItem); - this.log('POP ACTION', action); - if (action) { - await this.runAction(action, actionItem.args); - } - } else { - const taskItem = this.getTask(); - this.log('POP TASK ITEM', taskItem); - if (taskItem !== null) { - const task = this.createTask(taskItem); - this.log('POP TASK', task); - if (task !== null) { - task.run(taskItem.args); - } - } - } + await this.doLoopStep(); } } + private async doLoopStep() { + await this.sleep(); + const currentTs = timestamp(); + const taskCommand = this.taskQueue.get(currentTs); + + // текущего таска нет, очищаем очередь действий по таску + if (taskCommand === undefined) { + this.log('NO ACTIVE TASK'); + this.actionQueue.clear(); + return; + } + + const actionCommand = this.popActionCommand(); + + this.log('CURRENT TASK', taskCommand); + this.log('CURRENT ACTION', actionCommand); + + if (actionCommand) { + return await this.processActionCommand(actionCommand, taskCommand); + } + + if (taskCommand) { + return await this.processTaskCommand(taskCommand); + } + } + + private async processActionCommand(cmd: Command, task: Task) { + const actionController = this.createActionControllerByName(cmd.name); + this.log('PROCESS ACTION CTR', actionController); + if (actionController) { + await this.runAction(actionController, cmd.args, task); + } + } + + private async processTaskCommand(task: Task) { + const taskController = this.createTaskControllerByName(task.cmd.name); + this.log('PROCESS TASK CTR', taskController, task); + taskController?.run(task); + } + private async sleep() { if (this.sleepType === SleepType.Long) { await sleepLong(); @@ -67,69 +91,64 @@ export default class Scheduler { this.sleepType = SleepType.Long; } - taskState(): ImmutableState { - return this.taskQueue.state(); + getTaskItems(): TaskList { + return this.taskQueue.seeItems(); } - pushTask(task: Command): void { + completeTask(id: TaskId) { + this.taskQueue.complete(id); + } + + scheduleTask(task: Command): void { this.log('PUSH TASK', task); - this.taskQueue.push(task); - } - - pushAction(action: Command): void { - this.log('PUSH ACTION', action); - this.actionQueue.push(action); + this.taskQueue.push(task, timestamp()); } scheduleActions(actions: Array): void { this.actionQueue.assign(actions); } - completeCurrentTask() { - this.taskQueue.next(); - } - - private getTask(): Command | null { - return this.taskQueue.current() || this.taskQueue.next(); - } - - private createTask(taskItem: Command) { - switch (taskItem.name) { + private createTaskControllerByName( + taskName: string + ): TaskController | undefined { + switch (taskName) { case UpgradeBuildingTask.NAME: return new UpgradeBuildingTask(this); } - this.log('UNKNOWN TASK', taskItem.name); - return null; + this.log('UNKNOWN TASK', taskName); + return undefined; } - private popAction() { + private popActionCommand(): Command | undefined { const actionItem = this.actionQueue.pop(); if (actionItem === undefined) { - return null; + return undefined; } this.log('UNKNOWN ACTION', actionItem.name); return actionItem; } - private createAction(actionItem: Command) { - if (actionItem.name === GoToBuildingAction.NAME) { + private createActionControllerByName( + actonName: string + ): ActionController | undefined { + if (actonName === GoToBuildingAction.NAME) { return new GoToBuildingAction(); } - if (actionItem.name === UpgradeBuildingAction.NAME) { + if (actonName === UpgradeBuildingAction.NAME) { return new UpgradeBuildingAction(this); } - return null; + return undefined; } - private async runAction(action, args: Args) { + private async runAction(action: ActionController, args: Args, task: Task) { try { - await action.run(args); + await action.run(args, task); } catch (e) { console.warn('ACTION ABORTED', e.message); if (e instanceof TryLaterError) { console.warn('TRY AFTER', e.seconds); this.actionQueue.clear(); - this.taskQueue.postpone(e.seconds); + this.taskQueue.postpone(task.id, e.seconds); this.nextSleepLong(); } } diff --git a/src/Storage/TaskQueue.ts b/src/Storage/TaskQueue.ts index 68e0aa9..4621dfe 100644 --- a/src/Storage/TaskQueue.ts +++ b/src/Storage/TaskQueue.ts @@ -1,110 +1,88 @@ import { Command } from '../Common'; +import { uniqId } from '../utils'; -const QUEUE_NAME = 'task_queue:v2'; +const QUEUE_NAME = 'task_queue:v3'; -class CommandWithTime { - readonly cmd: Command; +export type TaskId = string; + +function uniqTaskId(): TaskId { + return uniqId(); +} + +export class Task { + readonly id: TaskId; readonly ts: number; - constructor(cmd: Command, ts: number) { - this.cmd = cmd; + readonly cmd: Command; + constructor(id: TaskId, ts: number, cmd: Command) { + this.id = id; this.ts = ts; + this.cmd = cmd; + } + + withTime(ts: number): Task { + return new Task(this.id, ts, this.cmd); } } -export class State { - current: CommandWithTime | null; - items: Array; - constructor( - current: CommandWithTime | null, - items: Array - ) { - items.sort((x: CommandWithTime, y: CommandWithTime) => x.ts - y.ts); - this.current = current; - this.items = items; - } - - push(cmd: Command, ts: number): State { - const items = this.items.slice(); - items.push(new CommandWithTime(cmd, ts)); - return new State(this.current, items); - } - - next(): State { - const items = this.items.slice(); - const first = items.shift(); - if (first === undefined) { - return new State(null, []); - } - return new State(first, items); - } - - postpone(ds: number): State { - const current = this.current; - let items = this.items.slice(); - if (current) { - const cmd = new CommandWithTime(current.cmd, current.ts + ds); - items.push(cmd); - } - return new State(null, items); - } -} - -export class ImmutableState { - readonly current: CommandWithTime | null; - readonly items: Array; - constructor(state: State) { - this.current = state.current; - this.items = state.items; - } -} +export type TaskList = Array; export class TaskQueue { - push(cmd: Command, ts: number | null = null) { - this.log('PUSH TASK', cmd, ts); - const state = this.getState(); - this.flushState(state.push(cmd, ts || this.defaultTs())); + private static normalize(items: TaskList): TaskList { + return items.sort((x, y) => x.ts - y.ts); } - current(): Command | null { - let current = this.getState().current; - return current ? current.cmd : null; + push(cmd: Command, ts: number): Task { + const id = uniqTaskId(); + const task = new Task(id, ts, cmd); + this.log('PUSH TASK', id, ts, cmd); + let items = this.getItems(); + items.push(task); + this.flushItems(items); + return task; } - next(): Command | null { - let state = this.getState().next(); - let current = state.current ? state.current.cmd : null; - this.flushState(state); - return current; - } - - postpone(ds: number) { - const state = this.getState().postpone(ds); - this.flushState(state); - } - - state(): ImmutableState { - return new ImmutableState(this.getState()); - } - - private defaultTs(): number { - return Math.floor(Date.now() / 1000); - } - - private getState(): State { - const serialized = localStorage.getItem(QUEUE_NAME); - if (serialized === null) { - return new State(null, []); + get(ts: number): Task | undefined { + const readyItems = this.getItems().filter(t => t.ts <= ts); + if (readyItems.length === 0) { + return undefined; } - - const s = JSON.parse(serialized) as State; - - this.log('STATE', s); - - return new State(s.current, s.items); + return readyItems[0]; } - private flushState(state: State): void { - localStorage.setItem(QUEUE_NAME, JSON.stringify(state)); + complete(id: TaskId) { + const [_, items] = this.shiftTask(id); + this.flushItems(items); + } + + postpone(id: TaskId, deltaSeconds: number) { + const [task, items] = this.shiftTask(id); + if (task) { + items.push(task.withTime(task.ts + deltaSeconds)); + } + this.flushItems(items); + } + + seeItems(): TaskList { + return this.getItems(); + } + + private shiftTask(id: TaskId): [Task | undefined, TaskList] { + const items = this.getItems(); + const task = items.find(t => t.id === id); + const tail = items.filter(t => t.id !== id); + return [task, tail]; + } + + private getItems(): TaskList { + const serialized = localStorage.getItem(QUEUE_NAME); + return serialized !== null ? (JSON.parse(serialized) as TaskList) : []; + } + + private flushItems(items: TaskList): void { + localStorage.setItem( + QUEUE_NAME, + JSON.stringify(TaskQueue.normalize(items)) + ); } private log(...args) { diff --git a/src/Task/TaskController.ts b/src/Task/TaskController.ts new file mode 100644 index 0000000..55a8dd3 --- /dev/null +++ b/src/Task/TaskController.ts @@ -0,0 +1,5 @@ +import { Task } from '../Storage/TaskQueue'; + +export default abstract class TaskController { + abstract run(task: Task); +} diff --git a/src/Task/UpgradeBuildingTask.ts b/src/Task/UpgradeBuildingTask.ts index 5e7f327..7ec9468 100644 --- a/src/Task/UpgradeBuildingTask.ts +++ b/src/Task/UpgradeBuildingTask.ts @@ -1,18 +1,22 @@ import Scheduler from '../Scheduler'; import GoToBuildingAction from '../Action/GoToBuildingAction'; import UpgradeBuildingAction from '../Action/UpgradeBuildingAction'; -import { Args, Command } from '../Common'; +import { Command } from '../Common'; +import { Task } from '../Storage/TaskQueue'; +import TaskController from './TaskController'; -export default class UpgradeBuildingTask { +export default class UpgradeBuildingTask extends TaskController { static NAME = 'upgrade_building'; private scheduler: Scheduler; constructor(scheduler: Scheduler) { + super(); this.scheduler = scheduler; } - run(args: Args) { - console.log('RUN', UpgradeBuildingTask.NAME, 'with', args); + run(task: Task) { + console.log('RUN', UpgradeBuildingTask.NAME, 'with', task); + const args = { ...task.cmd.args, taskId: task.id }; this.scheduler.scheduleActions([ new Command(GoToBuildingAction.NAME, args), new Command(UpgradeBuildingAction.NAME, args), diff --git a/src/TaskQueueRenderer.ts b/src/TaskQueueRenderer.ts index a0c2eec..a35a2da 100644 --- a/src/TaskQueueRenderer.ts +++ b/src/TaskQueueRenderer.ts @@ -1,9 +1,10 @@ -import { ImmutableState } from './Storage/TaskQueue'; +import { TaskList } from './Storage/TaskQueue'; +import { uniqId } from './utils'; -const ID = 'id-832654376836436939356'; +const ID = uniqId(); export default class TaskQueueRenderer { - render(state: ImmutableState) { + render(tasks: TaskList) { const ul = jQuery('
    ') .attr({ id: ID }) .css({ @@ -15,18 +16,16 @@ export default class TaskQueueRenderer { 'z-index': '9999', padding: '8px 6px', }); - if (state.current) { - let cmd = state.current.cmd; + tasks.forEach(task => { ul.append( jQuery('
  • ').text( - 'Current: ' + cmd.name + ' ' + JSON.stringify(cmd.args) - ) - ); - } - state.items.forEach(c => { - ul.append( - jQuery('
  • ').text( - c.cmd.name + ' ' + JSON.stringify(c.cmd.args) + task.ts + + ' ' + + task.cmd.name + + ' ' + + JSON.stringify(task.cmd.args) + + ' ' + + task.id ) ); }); diff --git a/src/utils.ts b/src/utils.ts index c40fbbc..8e00531 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,3 +1,7 @@ +import { customAlphabet } from 'nanoid'; + +const smallIdGenerator = customAlphabet('1234567890abcdef', 6); + export function sleep(ms: number) { return new Promise(resolve => setTimeout(resolve, ms)); } @@ -14,6 +18,14 @@ export async function sleepLong() { return await sleep(ms); } +export function uniqId(): string { + return 'id' + smallIdGenerator(); +} + +export function timestamp(): number { + return Math.floor(Date.now() / 1000); +} + export function markPage(text: string, version: string) { jQuery('body').append( '