Release package version 0.0.2
All checks were successful
CI / build (push) Successful in 44s

Update files
This commit is contained in:
Andrey Kernichniy 2026-03-09 22:22:24 +07:00
parent bc9dbd24e0
commit ceeec2dc0b
13 changed files with 82 additions and 125 deletions

2
.gitignore vendored
View file

@ -30,4 +30,4 @@ yarn-error.log
.DS_Store
Thumbs.db
playground/gxc-canvas-viewer/**/*.*
playground/*/**/*.*

View file

@ -1,15 +1,9 @@
import { isIgnorableKey, isImmutableKey } from "@gxc-solutions/model-base/decorators";
import { IModel } from "@gxc-solutions/model-base/interfaces";
import { Subject } from "rxjs";
import { ICommand, ICommandExecuter, ICommandResult } from "../interfaces";
import { ICommandAndArg, ICommandExecuter, ICommandResult } from "../interfaces";
export abstract class AbstractExecutor<T extends IModel> implements ICommandExecuter<T> {
protected _changes$ = new Subject<ICommandResult<T>[]>();
readonly changes$ = this._changes$.asObservable();
abstract execute(command: ICommand<T>, object: T): this;
abstract flush(): Promise<void>;
abstract execute(context: ICommandAndArg<T>[]): Promise<ICommandResult<T>[]>;
protected _apply(object: T, values: Partial<T>): boolean {
return Object.keys(values).some((key) => {

View file

@ -0,0 +1,17 @@
import { IModel } from "@gxc-solutions/model-base";
import { ICommand, ICommandAndArg, ICommandArguments } from "../interfaces";
export class CommandsCollector<T extends IModel> {
private _commands: ICommandAndArg<T>[] = [];
constructor(private _: (commands: ICommandAndArg<T>[]) => Promise<void>) {}
push(command: ICommand<T>, args: ICommandArguments<T>) {
this._commands.push({ command, args });
return this;
}
flush() {
this._(this._commands);
}
}

View file

@ -1,27 +0,0 @@
import { IModel } from "@gxc-solutions/model-base/interfaces";
import { IIndexer } from "../interfaces";
import { ParallelCommandExecutor } from "./parallel-executor";
import { Queue } from "./queue";
import { SerialExecutor } from "./serial-executor";
export class CommandsExecutor<T extends IModel> {
private _queue = new Queue();
private _parallel: ParallelCommandExecutor<T>;
private _serial: SerialExecutor<T>;
constructor(private _indexer: IIndexer<T>) {
this._parallel = new ParallelCommandExecutor<T>(this._indexer);
this._serial = new SerialExecutor<T>(this._indexer);
}
async executeParallel() {
while (!this._queue.isEmpty()) {
// const flush = this._queue.dequeue();
// const result = await flush;
}
const result = this._parallel.flush();
this._queue.enqueue(result);
}
executeIndependent() {}
}

View file

@ -0,0 +1,5 @@
export * from "./serial-executor";
export * from "./abstract-executor";
export * from "./parallel-executor";
export * from "./collector";
export * from "./manager";

View file

@ -0,0 +1,31 @@
import { IModel } from "@gxc-solutions/model-base";
import { Subject } from "rxjs";
import { ICommandResult, IIndexer } from "../interfaces";
import { CommandsCollector } from "./collector";
import { ParallelCommandExecutor } from "./parallel-executor";
import { SerialExecutor } from "./serial-executor";
export type RunType = "parallel" | "serial";
export class ExecuteManager {
protected _changes$ = new Subject<ICommandResult<IModel>[]>();
readonly changes$ = this._changes$.asObservable();
constructor(private _indexer: IIndexer<IModel>) {}
run(type: RunType) {
if (type === "parallel") {
return new CommandsCollector(async (commands) => {
const results = await new ParallelCommandExecutor(this._indexer).execute(commands);
this._changes$.next(results);
});
} else if (type === "serial") {
return new CommandsCollector(async (commands) => {
const results = await new SerialExecutor(this._indexer).execute(commands);
this._changes$.next(results);
});
} else {
throw new Error("Unknown run type!");
}
}
}

View file

@ -1,31 +1,19 @@
import { IModel } from "@gxc-solutions/model-base/interfaces";
import { ICommand, ICommandResult, IIndexer } from "../interfaces";
import { ICommandAndArg, ICommandResult, IIndexer } from "../interfaces";
import { AbstractExecutor } from "./abstract-executor";
import { Queue } from "./queue";
// Set of actions for apply once
export class ParallelCommandExecutor<T extends IModel> extends AbstractExecutor<T> {
private _results: Promise<ICommandResult<T>[]>[] = [];
private _queue: Queue<any>;
constructor(private _indexer: IIndexer<T>) {
super();
}
execute(command: ICommand<T>, object: T): this {
const result = command.execute(object);
this._results.push(result);
return this;
}
async flush() {
if (this._results.length > 0) {
const allResults = await Promise.all(this._results);
async execute(context: ICommandAndArg<T>[]): Promise<ICommandResult<T>[]> {
if (context.length > 0) {
const allResults = await Promise.all(context.map(({ command, args }) => command.execute(args)));
const isWrongResult = this._checkResults(allResults);
if (isWrongResult) {
this._results = [];
throw new Error("Commands changed the same object multiple times. This is not allowed.");
}
@ -36,12 +24,11 @@ export class ParallelCommandExecutor<T extends IModel> extends AbstractExecutor<
});
if (!isChanged) {
this._results = [];
return;
return [];
}
this._changes$.next(flattenResults);
return flattenResults;
}
this._results = [];
return [];
}
private _checkResults(results: ICommandResult<T>[][]): boolean {

View file

@ -1,43 +0,0 @@
import { IModel } from "@gxc-solutions/model-base/interfaces";
import { ICommand, IIndexer } from "../interfaces";
import { AbstractExecutor } from "./abstract-executor";
interface ICommandAndArg<T extends IModel> {
command: ICommand<T>;
object: T;
}
// Set of actions for serial execution
export class SerialExecutor<T extends IModel> extends AbstractExecutor<T> {
private _commands: ICommandAndArg<T>[] = [];
private _isFlushed = false;
constructor(private _indexer: IIndexer<T>) {
super();
}
execute(command: ICommand<T>, object: T): this {
if (this._isFlushed) {
throw new Error("Cannot execute commands after flush has been called.");
}
this._commands.push({ command, object });
return this;
}
async flush(): Promise<void> {
this._isFlushed = true;
for (const { command, object } of this._commands) {
const commandResult = await command.execute(object);
const isChanged = commandResult.map(({ id, result /*type */ }) => {
const object = this._indexer.get(id);
return this._apply(object, result);
});
if (isChanged) {
this._changes$.next(commandResult);
}
}
this._commands = [];
this._isFlushed = false;
}
}

View file

@ -1,29 +1,17 @@
import { IModel } from "@gxc-solutions/model-base/interfaces";
import { ICommand, ICommandResult, IIndexer } from "../interfaces";
import { ICommandAndArg, ICommandResult, IIndexer } from "../interfaces";
import { AbstractExecutor } from "./abstract-executor";
interface ICommandAndArg<T extends IModel> {
command: ICommand<T>;
object: T;
}
// Set of actions for serial execution
export class SerialExecutor<T extends IModel> extends AbstractExecutor<T> {
private _commands: ICommandAndArg<T>[] = [];
constructor(private _indexer: IIndexer<T>) {
super();
}
execute(command: ICommand<T>, object: T): this {
this._commands.push({ command, object });
return this;
}
async flush(): Promise<void> {
async execute(context: ICommandAndArg<T>[]): Promise<ICommandResult<T>[]> {
const results = new Map<string, ICommandResult<T>>();
for (const { command, object } of this._commands) {
for (const { command, args: object } of context) {
const commandResult = await command.execute(object);
commandResult.map(({ id, result, type }) => {
if (results.has(id)) {
@ -40,9 +28,8 @@ export class SerialExecutor<T extends IModel> extends AbstractExecutor<T> {
return this._apply(object, result.result);
});
if (!isChanged) {
this._commands = [];
return;
return [];
}
this._changes$.next(Array.from(results.values()));
return Array.from(results.values());
}
}

View file

@ -1,14 +1,20 @@
import { IModel } from "@gxc-solutions/model-base/interfaces";
import { Observable } from "rxjs";
export interface ICommandAndArg<T extends IModel> {
command: ICommand<T>;
args: ICommandArguments<T>;
}
export interface ICommandArguments<T> {
model: T;
}
export interface ICommandExecuter<T extends IModel> {
changes$: Observable<ICommandResult<T>[]>;
execute(command: ICommand<T>, object: T): this;
flush(): Promise<void>;
execute(context: ICommandAndArg<T>[]): Promise<ICommandResult<T>[]>;
}
export interface ICommand<T extends IModel> {
execute(object: T): Promise<ICommandResult<T>[]>;
execute(args: ICommandArguments<T>): Promise<ICommandResult<T>[]>;
}
export interface ICommandResult<T extends IModel> {

View file

@ -1,6 +1,6 @@
{
"name": "@gxc-solutions/command-executer",
"version": "0.0.1",
"version": "0.0.2",
"main": "index.js",
"author": "GXC Solutions",
"publishConfig": {

View file

@ -1,5 +1,5 @@
{
"name": "template-of-lib-repo",
"name": "gxc-command-executer",
"version": "0.0.0",
"main": "index.js",
"scripts": {

View file

@ -1,7 +1,7 @@
{
"compilerOptions": {
"rootDir": "./lib/src",
"outDir": "./playground/lib",
"outDir": "./playground/command-executer",
},
"extends": ["./tsconfig.json"],
"exclude": ["./node_modules/*", "./lib/**/*.test.ts"]