@praxisjs/concurrent 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # @praxisjs/concurrent
2
2
 
3
+ ## 1.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 21f2053: Redesign `@Task`, `@Queue`, and `@Pool` as field decorators
8
+
9
+ The decorators now go on a separate field instead of the async method itself. The method name is always the first argument, followed by options. Reactive state is accessed as sub-properties on the field with full TypeScript intellisense via `TaskOf`, `QueueOf`, and `PoolOf` type helpers.
10
+
11
+ ```ts
12
+ // Before
13
+ @Task()
14
+ async loadUser(id: number) { ... }
15
+ // this.loadUser_loading() — no intellisense
16
+
17
+ // After
18
+ async loadUser(id: number) { ... }
19
+
20
+ @Task('loadUser')
21
+ taskLoadUser!: TaskOf<MyClass, 'loadUser'>
22
+ // this.taskLoadUser.loading() ✓
23
+ // this.taskLoadUser.error() ✓
24
+ ```
25
+
26
+ `@Pool` argument order changed: method name is now first, concurrency second (previously `@Pool(3, 'method')`, now `@Pool('method', 3)`).
27
+
28
+ ### Patch Changes
29
+
30
+ - Updated dependencies [2b8c768]
31
+ - @praxisjs/decorators@0.7.0
32
+
3
33
  ## 1.1.1
4
34
 
5
35
  ### Patch Changes
@@ -1,11 +1,11 @@
1
1
  import { describe, it, expect } from "vitest";
2
2
  import { Task, Queue, Pool } from "../decorators";
3
- function methodCtx(name) {
3
+ function fieldCtx(name) {
4
4
  const initializers = [];
5
5
  return {
6
6
  ctx: {
7
7
  name,
8
- kind: "method",
8
+ kind: "field",
9
9
  addInitializer(fn) { initializers.push(fn); },
10
10
  },
11
11
  run(instance) { initializers.forEach((fn) => { fn.call(instance); }); },
@@ -13,149 +13,126 @@ function methodCtx(name) {
13
13
  }
14
14
  // ── Task ──────────────────────────────────────────────────────────────────────
15
15
  describe("Task decorator", () => {
16
- it("replaces method with a task runner and exposes signals", async () => {
17
- const { ctx, run } = methodCtx("load");
18
- const original = async (x) => x * 2;
19
- Task()(original, ctx);
20
- const instance = {};
16
+ it("wraps the named method and exposes signals as sub-properties", async () => {
17
+ const { ctx, run } = fieldCtx("taskLoad");
18
+ Task("load")(undefined, ctx);
19
+ const instance = {
20
+ load: async (x) => x * 2,
21
+ };
21
22
  run(instance);
22
- expect(typeof instance.load).toBe("function");
23
- expect(instance.load_loading).toBeDefined();
24
- expect(instance.load_error).toBeDefined();
25
- expect(instance.load_lastResult).toBeDefined();
26
- const result = await instance.load(5);
23
+ const taskLoad = instance.taskLoad;
24
+ expect(typeof taskLoad).toBe("function");
25
+ expect(taskLoad.loading).toBeDefined();
26
+ expect(taskLoad.error).toBeDefined();
27
+ expect(taskLoad.lastResult).toBeDefined();
28
+ const result = await taskLoad(5);
27
29
  expect(result).toBe(10);
28
- expect(instance.load_lastResult()).toBe(10);
30
+ expect(taskLoad.lastResult()).toBe(10);
29
31
  });
30
- it("sets loading signal while running", async () => {
31
- const { ctx, run } = methodCtx("fetch");
32
+ it("sets loading while running", async () => {
33
+ const { ctx, run } = fieldCtx("taskFetch");
34
+ Task("fetch")(undefined, ctx);
32
35
  let resolve;
33
- const original = async () => new Promise((r) => { resolve = r; });
34
- Task()(original, ctx);
35
- const instance = {};
36
+ const instance = {
37
+ fetch: async () => new Promise((r) => { resolve = r; }),
38
+ };
36
39
  run(instance);
37
- const p = instance.fetch();
38
- expect(instance.fetch_loading()).toBe(true);
40
+ const taskFetch = instance.taskFetch;
41
+ const p = taskFetch();
42
+ expect(taskFetch.loading()).toBe(true);
39
43
  resolve("done");
40
44
  await p;
41
- expect(instance.fetch_loading()).toBe(false);
45
+ expect(taskFetch.loading()).toBe(false);
42
46
  });
43
47
  });
44
48
  // ── Queue ─────────────────────────────────────────────────────────────────────
45
49
  describe("Queue decorator", () => {
46
- it("replaces method with a queue runner and exposes signals", async () => {
47
- const { ctx, run } = methodCtx("save");
48
- const original = async (x) => String(x);
49
- Queue()(original, ctx);
50
- const instance = {};
50
+ it("wraps the named method and exposes signals", async () => {
51
+ const { ctx, run } = fieldCtx("taskSave");
52
+ Queue("save")(undefined, ctx);
53
+ const instance = {
54
+ save: async (x) => String(x),
55
+ };
51
56
  run(instance);
52
- expect(typeof instance.save).toBe("function");
53
- expect(instance.save_loading).toBeDefined();
54
- expect(instance.save_pending).toBeDefined();
55
- expect(instance.save_error).toBeDefined();
56
- const result = await instance.save("hello");
57
+ const taskSave = instance.taskSave;
58
+ expect(typeof taskSave).toBe("function");
59
+ expect(taskSave.loading).toBeDefined();
60
+ expect(taskSave.pending).toBeDefined();
61
+ expect(taskSave.error).toBeDefined();
62
+ expect(taskSave.clear).toBeDefined();
63
+ const result = await taskSave("hello");
57
64
  expect(result).toBe("hello");
58
65
  });
59
- });
60
- // ── Queue (additional) ────────────────────────────────────────────────────────
61
- describe("Queue decorator (additional)", () => {
62
- it("clear() is accessible on the instance via method_clear property", async () => {
63
- const { ctx, run } = methodCtx("save");
66
+ it("clear() rejects queued calls with QueueClearedError", async () => {
67
+ const { ctx, run } = fieldCtx("taskSave");
68
+ Queue("save")(undefined, ctx);
64
69
  let resolveFirst;
65
- const original = async (_, idx) => idx === 0
66
- ? new Promise((r) => { resolveFirst = r; })
67
- : Promise.resolve();
68
- Queue()(original, ctx);
69
- const instance = {};
70
+ const instance = {
71
+ save: async (_, idx) => idx === 0
72
+ ? new Promise((r) => { resolveFirst = r; })
73
+ : Promise.resolve(),
74
+ };
70
75
  run(instance);
71
- expect(typeof instance.save_clear).toBe("function");
72
- const save = instance.save;
73
- save(null, 0);
74
- const p1 = save(null, 1);
75
- // Call clear via the exposed property
76
- instance.save_clear();
76
+ const taskSave = instance.taskSave;
77
+ taskSave(null, 0);
78
+ const p1 = taskSave(null, 1);
79
+ taskSave.clear();
77
80
  await expect(p1).rejects.toThrow("Queue cleared");
78
81
  resolveFirst();
79
82
  });
80
83
  });
81
84
  // ── Pool ──────────────────────────────────────────────────────────────────────
82
85
  describe("Pool decorator", () => {
83
- it("replaces method with a pool runner and exposes signals", async () => {
84
- const { ctx, run } = methodCtx("process");
85
- const original = async (x) => x + 1;
86
- Pool(2)(original, ctx);
87
- const instance = {};
86
+ it("wraps the named method and exposes signals", async () => {
87
+ const { ctx, run } = fieldCtx("taskProcess");
88
+ Pool("process", 2)(undefined, ctx);
89
+ const instance = {
90
+ process: async (x) => x + 1,
91
+ };
88
92
  run(instance);
89
- expect(typeof instance.process).toBe("function");
90
- expect(instance.process_loading).toBeDefined();
91
- expect(instance.process_active).toBeDefined();
92
- expect(instance.process_pending).toBeDefined();
93
- expect(instance.process_error).toBeDefined();
94
- const result = await instance.process(9);
93
+ const taskProcess = instance.taskProcess;
94
+ expect(typeof taskProcess).toBe("function");
95
+ expect(taskProcess.loading).toBeDefined();
96
+ expect(taskProcess.active).toBeDefined();
97
+ expect(taskProcess.pending).toBeDefined();
98
+ expect(taskProcess.error).toBeDefined();
99
+ const result = await taskProcess(9);
95
100
  expect(result).toBe(10);
96
101
  });
97
102
  it("respects concurrency limit", async () => {
98
- const { ctx, run } = methodCtx("work");
103
+ const { ctx, run } = fieldCtx("taskWork");
104
+ Pool("work", 2)(undefined, ctx);
99
105
  const resolvers = [];
100
- const original = async () => new Promise((r) => resolvers.push(r));
101
- Pool(2)(original, ctx);
102
- const instance = {};
106
+ const instance = {
107
+ work: async () => new Promise((r) => resolvers.push(r)),
108
+ };
103
109
  run(instance);
104
- const work = instance.work;
105
- const t1 = work();
106
- const t2 = work();
107
- work(); // queued — concurrency limit is 2
108
- expect(instance.work_active()).toBe(2);
109
- expect(instance.work_pending()).toBe(1);
110
+ const taskWork = instance.taskWork;
111
+ const t1 = taskWork();
112
+ const t2 = taskWork();
113
+ taskWork();
114
+ expect(taskWork.active()).toBe(2);
115
+ expect(taskWork.pending()).toBe(1);
110
116
  resolvers.forEach((r) => { r(); });
111
117
  await Promise.all([t1, t2]);
112
118
  });
113
- it("Pool(-1, fn) — clamps to 1, does not allow unlimited concurrency", async () => {
114
- const { ctx, run } = methodCtx("work");
119
+ it("defaults concurrency to 1", async () => {
120
+ const { ctx, run } = fieldCtx("taskWork");
121
+ Pool("work")(undefined, ctx);
115
122
  const resolvers = [];
116
- const original = async () => new Promise((r) => resolvers.push(r));
117
- Pool(-1)(original, ctx);
118
- const instance = {};
123
+ const instance = {
124
+ work: async () => new Promise((r) => resolvers.push(r)),
125
+ };
119
126
  run(instance);
120
- const work = instance.work;
121
- const t1 = work();
122
- const t2 = work();
123
- // With concurrency clamped to 1, only one task should be active at a time
124
- expect(instance.work_active()).toBe(1);
125
- expect(instance.work_pending()).toBe(1);
127
+ const taskWork = instance.taskWork;
128
+ const t1 = taskWork();
129
+ const t2 = taskWork();
130
+ expect(taskWork.active()).toBe(1);
131
+ expect(taskWork.pending()).toBe(1);
126
132
  resolvers[0]();
127
133
  await t1;
128
134
  resolvers[1]();
129
135
  await t2;
130
136
  });
131
- it("decorated method called concurrently up to pool limit — active() signal is accurate", async () => {
132
- const { ctx, run } = methodCtx("process");
133
- const resolvers = [];
134
- const original = async () => new Promise((r) => resolvers.push(r));
135
- Pool(3)(original, ctx);
136
- const instance = {};
137
- run(instance);
138
- const process = instance.process;
139
- const active = instance.process_active;
140
- const t1 = process();
141
- expect(active()).toBe(1);
142
- const t2 = process();
143
- expect(active()).toBe(2);
144
- const t3 = process();
145
- expect(active()).toBe(3);
146
- // 4th task exceeds limit, goes to pending
147
- const t4 = process();
148
- expect(active()).toBe(3);
149
- expect(instance.process_pending()).toBe(1);
150
- // Resolve t1 first — this will allow t4 to start and push its resolver
151
- resolvers[0]();
152
- await t1;
153
- // Now t4 has started and pushed a resolver
154
- resolvers[1]();
155
- resolvers[2]();
156
- resolvers[3]();
157
- await Promise.all([t2, t3, t4]);
158
- expect(active()).toBe(0);
159
- });
160
137
  });
161
138
  //# sourceMappingURL=decorators.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.test.js","sourceRoot":"","sources":["../../src/__tests__/decorators.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"decorators.test.js","sourceRoot":"","sources":["../../src/__tests__/decorators.test.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,28 @@
1
- export declare function Task(): (value: (...args: any[]) => Promise<any>, context: ClassMethodDecoratorContext<any>) => void;
2
- export declare function Queue(): (value: (...args: any[]) => Promise<any>, context: ClassMethodDecoratorContext<any>) => void;
3
- export declare function Pool(concurrency: number): (value: (...args: any[]) => Promise<any>, context: ClassMethodDecoratorContext<any>) => void;
1
+ import type { Computed } from "@praxisjs/shared";
2
+ type AnyAsyncFn = (this: any, ...args: any[]) => Promise<any>;
3
+ export type TaskDecorated<T extends AnyAsyncFn> = ((...args: Parameters<T>) => ReturnType<T>) & {
4
+ loading: Computed<boolean>;
5
+ error: Computed<Error | null>;
6
+ lastResult: Computed<Awaited<ReturnType<T>> | null>;
7
+ cancelAll(): void;
8
+ };
9
+ export type QueueDecorated<T extends AnyAsyncFn> = ((...args: Parameters<T>) => ReturnType<T>) & {
10
+ loading: Computed<boolean>;
11
+ pending: Computed<number>;
12
+ error: Computed<Error | null>;
13
+ clear(): void;
14
+ };
15
+ export type PoolDecorated<T extends AnyAsyncFn> = ((...args: Parameters<T>) => ReturnType<T>) & {
16
+ loading: Computed<boolean>;
17
+ active: Computed<number>;
18
+ pending: Computed<number>;
19
+ error: Computed<Error | null>;
20
+ };
21
+ export type TaskOf<C, K extends keyof C> = C[K] extends AnyAsyncFn ? TaskDecorated<C[K]> : never;
22
+ export type QueueOf<C, K extends keyof C> = C[K] extends AnyAsyncFn ? QueueDecorated<C[K]> : never;
23
+ export type PoolOf<C, K extends keyof C> = C[K] extends AnyAsyncFn ? PoolDecorated<C[K]> : never;
24
+ export declare function Task(methodName: string): (_value: undefined, context: ClassFieldDecoratorContext<object, unknown>) => void;
25
+ export declare function Queue(methodName: string): (_value: undefined, context: ClassFieldDecoratorContext<object, unknown>) => void;
26
+ export declare function Pool(methodName: string, concurrency?: number): (_value: undefined, context: ClassFieldDecoratorContext<object, unknown>) => void;
27
+ export {};
4
28
  //# sourceMappingURL=decorators.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAMA,wBAAgB,IAAI,IAYD,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,2BAA2B,CAAC,GAAG,CAAC,KAAK,IAAI,CAC9G;AAED,wBAAgB,KAAK,IAYF,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,2BAA2B,CAAC,GAAG,CAAC,KAAK,IAAI,CAC9G;AAED,wBAAgB,IAAI,CAAC,WAAW,EAAE,MAAM,GAYrB,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,2BAA2B,CAAC,GAAG,CAAC,KAAK,IAAI,CAC9G"}
1
+ {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAOjD,KAAK,UAAU,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;AAI9D,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,UAAU,IAC5C,CAAC,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG;IAC5C,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC3B,KAAK,EAAE,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAC9B,UAAU,EAAE,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACpD,SAAS,IAAI,IAAI,CAAC;CACnB,CAAC;AAEJ,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,UAAU,IAC7C,CAAC,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG;IAC5C,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC3B,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC1B,KAAK,EAAE,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAC9B,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AAEJ,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,UAAU,IAC5C,CAAC,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG;IAC5C,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC3B,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzB,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC1B,KAAK,EAAE,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;CAC/B,CAAC;AAIJ,MAAM,MAAM,MAAM,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IACrC,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;AAExD,MAAM,MAAM,OAAO,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IACtC,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;AAEzD,MAAM,MAAM,MAAM,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IACrC,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;AAIxD,wBAAgB,IAAI,CAAC,UAAU,EAAE,MAAM,qFAYtC;AAID,wBAAgB,KAAK,CAAC,UAAU,EAAE,MAAM,qFAYvC;AAID,wBAAgB,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,SAAI,qFAYvD"}
@@ -1,47 +1,47 @@
1
- import { createMethodDecorator } from "@praxisjs/decorators";
1
+ import { createFieldDecorator } from "@praxisjs/decorators";
2
2
  import { pool } from "./pool";
3
3
  import { queue } from "./queue";
4
4
  import { task } from "./task";
5
- export function Task() {
6
- return createMethodDecorator({
7
- wrap(original, instance, name) {
8
- const self = instance;
9
- const t = task(original.bind(instance));
10
- self[`${name}_loading`] = t.loading;
11
- self[`${name}_error`] = t.error;
12
- self[`${name}_lastResult`] = t.lastResult;
13
- return (...args) => t(...args);
5
+ // ── @Task ──────────────────────────────────────────────────────────────────────
6
+ export function Task(methodName) {
7
+ return createFieldDecorator({
8
+ bind(instance, _name) {
9
+ const inst = instance;
10
+ return {
11
+ descriptor: {
12
+ value: task(inst[methodName].bind(instance)),
13
+ writable: true,
14
+ },
15
+ };
14
16
  },
15
- // Concurrent decorators work on any class, not just StatefulComponent
16
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
17
  });
18
18
  }
19
- export function Queue() {
20
- return createMethodDecorator({
21
- wrap(original, instance, name) {
22
- const self = instance;
23
- const q = queue(original.bind(instance));
24
- self[`${name}_loading`] = q.loading;
25
- self[`${name}_pending`] = q.pending;
26
- self[`${name}_error`] = q.error;
27
- self[`${name}_clear`] = () => { q.clear(); };
28
- return (...args) => q(...args);
19
+ // ── @Queue ─────────────────────────────────────────────────────────────────────
20
+ export function Queue(methodName) {
21
+ return createFieldDecorator({
22
+ bind(instance, _name) {
23
+ const inst = instance;
24
+ return {
25
+ descriptor: {
26
+ value: queue(inst[methodName].bind(instance)),
27
+ writable: true,
28
+ },
29
+ };
29
30
  },
30
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
31
  });
32
32
  }
33
- export function Pool(concurrency) {
34
- return createMethodDecorator({
35
- wrap(original, instance, name) {
36
- const self = instance;
37
- const p = pool(concurrency, original.bind(instance));
38
- self[`${name}_loading`] = p.loading;
39
- self[`${name}_active`] = p.active;
40
- self[`${name}_pending`] = p.pending;
41
- self[`${name}_error`] = p.error;
42
- return (...args) => p(...args);
33
+ // ── @Pool ──────────────────────────────────────────────────────────────────────
34
+ export function Pool(methodName, concurrency = 1) {
35
+ return createFieldDecorator({
36
+ bind(instance, _name) {
37
+ const inst = instance;
38
+ return {
39
+ descriptor: {
40
+ value: pool(concurrency, inst[methodName].bind(instance)),
41
+ writable: true,
42
+ },
43
+ };
43
44
  },
44
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
45
  });
46
46
  }
47
47
  //# sourceMappingURL=decorators.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAE9B,MAAM,UAAU,IAAI;IAClB,OAAO,qBAAqB,CAAC;QAC3B,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI;YAC3B,MAAM,IAAI,GAAG,QAAmC,CAAC;YACjD,MAAM,CAAC,GAAG,IAAI,CAAE,QAAqD,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YACtF,IAAI,CAAC,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC;YACpC,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;YAChC,IAAI,CAAC,GAAG,IAAI,aAAa,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC;YAC1C,OAAO,CAAC,GAAG,IAAe,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC5C,CAAC;QACH,sEAAsE;QACtE,8DAA8D;KAC7D,CAA4G,CAAC;AAChH,CAAC;AAED,MAAM,UAAU,KAAK;IACnB,OAAO,qBAAqB,CAAC;QAC3B,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI;YAC3B,MAAM,IAAI,GAAG,QAAmC,CAAC;YACjD,MAAM,CAAC,GAAG,KAAK,CAAE,QAAqD,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YACvF,IAAI,CAAC,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC;YACpC,IAAI,CAAC,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC;YACpC,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;YAChC,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAC,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;YAC7C,OAAO,CAAC,GAAG,IAAe,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC5C,CAAC;QACH,8DAA8D;KAC7D,CAA4G,CAAC;AAChH,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,WAAmB;IACtC,OAAO,qBAAqB,CAAC;QAC3B,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI;YAC3B,MAAM,IAAI,GAAG,QAAmC,CAAC;YACjD,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,EAAG,QAAqD,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YACnG,IAAI,CAAC,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC;YACpC,IAAI,CAAC,GAAG,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;YAClC,IAAI,CAAC,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC;YACpC,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;YAChC,OAAO,CAAC,GAAG,IAAe,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC5C,CAAC;QACH,8DAA8D;KAC7D,CAA4G,CAAC;AAChH,CAAC"}
1
+ {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAG5D,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AA0C9B,kFAAkF;AAElF,MAAM,UAAU,IAAI,CAAC,UAAkB;IACrC,OAAO,oBAAoB,CAAC;QAC1B,IAAI,CAAC,QAAgB,EAAE,KAAa;YAClC,MAAM,IAAI,GAAG,QAAsC,CAAC;YACpD,OAAO;gBACL,UAAU,EAAE;oBACV,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAY;oBACvD,QAAQ,EAAE,IAAI;iBACf;aACF,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,kFAAkF;AAElF,MAAM,UAAU,KAAK,CAAC,UAAkB;IACtC,OAAO,oBAAoB,CAAC;QAC1B,IAAI,CAAC,QAAgB,EAAE,KAAa;YAClC,MAAM,IAAI,GAAG,QAAsC,CAAC;YACpD,OAAO;gBACL,UAAU,EAAE;oBACV,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAY;oBACxD,QAAQ,EAAE,IAAI;iBACf;aACF,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,kFAAkF;AAElF,MAAM,UAAU,IAAI,CAAC,UAAkB,EAAE,WAAW,GAAG,CAAC;IACtD,OAAO,oBAAoB,CAAC;QAC1B,IAAI,CAAC,QAAgB,EAAE,KAAa;YAClC,MAAM,IAAI,GAAG,QAAsC,CAAC;YACpD,OAAO;gBACL,UAAU,EAAE;oBACV,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAY;oBACpE,QAAQ,EAAE,IAAI;iBACf;aACF,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { Task, Queue, Pool } from "./decorators";
2
+ export type { TaskDecorated, QueueDecorated, PoolDecorated, TaskOf, QueueOf, PoolOf } from "./decorators";
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACjD,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@praxisjs/concurrent",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -15,7 +15,7 @@
15
15
  },
16
16
  "dependencies": {
17
17
  "@praxisjs/core": "1.1.0",
18
- "@praxisjs/decorators": "0.6.1",
18
+ "@praxisjs/decorators": "0.7.0",
19
19
  "@praxisjs/shared": "0.2.0"
20
20
  },
21
21
  "scripts": {
@@ -2,14 +2,14 @@ import { describe, it, expect } from "vitest";
2
2
 
3
3
  import { Task, Queue, Pool } from "../decorators";
4
4
 
5
- function methodCtx(name: string) {
5
+ function fieldCtx(name: string) {
6
6
  const initializers: Array<(this: unknown) => void> = [];
7
7
  return {
8
8
  ctx: {
9
9
  name,
10
- kind: "method" as const,
10
+ kind: "field" as const,
11
11
  addInitializer(fn: (this: unknown) => void) { initializers.push(fn); },
12
- } as ClassMethodDecoratorContext,
12
+ } as ClassFieldDecoratorContext,
13
13
  run(instance: unknown) { initializers.forEach((fn) => { fn.call(instance); }); },
14
14
  };
15
15
  }
@@ -17,87 +17,98 @@ function methodCtx(name: string) {
17
17
  // ── Task ──────────────────────────────────────────────────────────────────────
18
18
 
19
19
  describe("Task decorator", () => {
20
- it("replaces method with a task runner and exposes signals", async () => {
21
- const { ctx, run } = methodCtx("load");
22
- const original = async (x: unknown) => (x as number) * 2;
23
- Task()(original, ctx);
20
+ it("wraps the named method and exposes signals as sub-properties", async () => {
21
+ const { ctx, run } = fieldCtx("taskLoad");
22
+ Task("load")(undefined, ctx);
24
23
 
25
- const instance: Record<string, unknown> = {};
24
+ const instance: Record<string, unknown> = {
25
+ load: async (x: unknown) => (x as number) * 2,
26
+ };
26
27
  run(instance);
27
28
 
28
- expect(typeof instance.load).toBe("function");
29
- expect(instance.load_loading).toBeDefined();
30
- expect(instance.load_error).toBeDefined();
31
- expect(instance.load_lastResult).toBeDefined();
29
+ const taskLoad = instance.taskLoad as {
30
+ (...args: unknown[]): Promise<number>;
31
+ loading: () => boolean;
32
+ error: () => Error | null;
33
+ lastResult: () => number | null;
34
+ };
32
35
 
33
- const result = await (instance.load as (x: number) => Promise<number>)(5);
36
+ expect(typeof taskLoad).toBe("function");
37
+ expect(taskLoad.loading).toBeDefined();
38
+ expect(taskLoad.error).toBeDefined();
39
+ expect(taskLoad.lastResult).toBeDefined();
40
+
41
+ const result = await taskLoad(5);
34
42
  expect(result).toBe(10);
35
- expect(
36
- (instance.load_lastResult as () => number)(),
37
- ).toBe(10);
43
+ expect(taskLoad.lastResult()).toBe(10);
38
44
  });
39
45
 
40
- it("sets loading signal while running", async () => {
41
- const { ctx, run } = methodCtx("fetch");
42
- let resolve!: (v: string) => void;
43
- const original = async () => new Promise<string>((r) => { resolve = r; });
44
- Task()(original, ctx);
46
+ it("sets loading while running", async () => {
47
+ const { ctx, run } = fieldCtx("taskFetch");
48
+ Task("fetch")(undefined, ctx);
45
49
 
46
- const instance: Record<string, unknown> = {};
50
+ let resolve!: (v: string) => void;
51
+ const instance: Record<string, unknown> = {
52
+ fetch: async () => new Promise<string>((r) => { resolve = r; }),
53
+ };
47
54
  run(instance);
48
55
 
49
- const p = (instance.fetch as () => Promise<string>)();
50
- expect((instance.fetch_loading as () => boolean)()).toBe(true);
56
+ const taskFetch = instance.taskFetch as { (): Promise<string>; loading: () => boolean };
57
+ const p = taskFetch();
58
+ expect(taskFetch.loading()).toBe(true);
51
59
  resolve("done");
52
60
  await p;
53
- expect((instance.fetch_loading as () => boolean)()).toBe(false);
61
+ expect(taskFetch.loading()).toBe(false);
54
62
  });
55
63
  });
56
64
 
57
65
  // ── Queue ─────────────────────────────────────────────────────────────────────
58
66
 
59
67
  describe("Queue decorator", () => {
60
- it("replaces method with a queue runner and exposes signals", async () => {
61
- const { ctx, run } = methodCtx("save");
62
- const original = async (x: unknown) => String(x);
63
- Queue()(original, ctx);
68
+ it("wraps the named method and exposes signals", async () => {
69
+ const { ctx, run } = fieldCtx("taskSave");
70
+ Queue("save")(undefined, ctx);
64
71
 
65
- const instance: Record<string, unknown> = {};
72
+ const instance: Record<string, unknown> = {
73
+ save: async (x: unknown) => String(x),
74
+ };
66
75
  run(instance);
67
76
 
68
- expect(typeof instance.save).toBe("function");
69
- expect(instance.save_loading).toBeDefined();
70
- expect(instance.save_pending).toBeDefined();
71
- expect(instance.save_error).toBeDefined();
72
-
73
- const result = await (instance.save as (x: string) => Promise<string>)("hello");
77
+ const taskSave = instance.taskSave as {
78
+ (...args: unknown[]): Promise<string>;
79
+ loading: () => boolean;
80
+ pending: () => number;
81
+ error: () => Error | null;
82
+ clear: () => void;
83
+ };
84
+
85
+ expect(typeof taskSave).toBe("function");
86
+ expect(taskSave.loading).toBeDefined();
87
+ expect(taskSave.pending).toBeDefined();
88
+ expect(taskSave.error).toBeDefined();
89
+ expect(taskSave.clear).toBeDefined();
90
+
91
+ const result = await taskSave("hello");
74
92
  expect(result).toBe("hello");
75
93
  });
76
- });
77
94
 
78
- // ── Queue (additional) ────────────────────────────────────────────────────────
95
+ it("clear() rejects queued calls with QueueClearedError", async () => {
96
+ const { ctx, run } = fieldCtx("taskSave");
97
+ Queue("save")(undefined, ctx);
79
98
 
80
- describe("Queue decorator (additional)", () => {
81
- it("clear() is accessible on the instance via method_clear property", async () => {
82
- const { ctx, run } = methodCtx("save");
83
99
  let resolveFirst!: () => void;
84
- const original = async (_: unknown, idx: unknown) =>
85
- idx === 0
86
- ? new Promise<void>((r) => { resolveFirst = r; })
87
- : Promise.resolve();
88
- Queue()(original, ctx);
89
-
90
- const instance: Record<string, unknown> = {};
100
+ const instance: Record<string, unknown> = {
101
+ save: async (_: unknown, idx: unknown) =>
102
+ idx === 0
103
+ ? new Promise<void>((r) => { resolveFirst = r; })
104
+ : Promise.resolve(),
105
+ };
91
106
  run(instance);
92
107
 
93
- expect(typeof instance.save_clear).toBe("function");
94
-
95
- const save = instance.save as (...args: unknown[]) => Promise<unknown>;
96
- save(null, 0);
97
- const p1 = save(null, 1);
98
-
99
- // Call clear via the exposed property
100
- (instance.save_clear as () => void)();
108
+ const taskSave = instance.taskSave as { (...args: unknown[]): Promise<unknown>; clear: () => void };
109
+ taskSave(null, 0);
110
+ const p1 = taskSave(null, 1);
111
+ taskSave.clear();
101
112
 
102
113
  await expect(p1).rejects.toThrow("Queue cleared");
103
114
  resolveFirst();
@@ -107,104 +118,75 @@ describe("Queue decorator (additional)", () => {
107
118
  // ── Pool ──────────────────────────────────────────────────────────────────────
108
119
 
109
120
  describe("Pool decorator", () => {
110
- it("replaces method with a pool runner and exposes signals", async () => {
111
- const { ctx, run } = methodCtx("process");
112
- const original = async (x: unknown) => (x as number) + 1;
113
- Pool(2)(original, ctx);
121
+ it("wraps the named method and exposes signals", async () => {
122
+ const { ctx, run } = fieldCtx("taskProcess");
123
+ Pool("process", 2)(undefined, ctx);
114
124
 
115
- const instance: Record<string, unknown> = {};
125
+ const instance: Record<string, unknown> = {
126
+ process: async (x: unknown) => (x as number) + 1,
127
+ };
116
128
  run(instance);
117
129
 
118
- expect(typeof instance.process).toBe("function");
119
- expect(instance.process_loading).toBeDefined();
120
- expect(instance.process_active).toBeDefined();
121
- expect(instance.process_pending).toBeDefined();
122
- expect(instance.process_error).toBeDefined();
123
-
124
- const result = await (instance.process as (x: number) => Promise<number>)(9);
130
+ const taskProcess = instance.taskProcess as {
131
+ (...args: unknown[]): Promise<number>;
132
+ loading: () => boolean;
133
+ active: () => number;
134
+ pending: () => number;
135
+ error: () => Error | null;
136
+ };
137
+
138
+ expect(typeof taskProcess).toBe("function");
139
+ expect(taskProcess.loading).toBeDefined();
140
+ expect(taskProcess.active).toBeDefined();
141
+ expect(taskProcess.pending).toBeDefined();
142
+ expect(taskProcess.error).toBeDefined();
143
+
144
+ const result = await taskProcess(9);
125
145
  expect(result).toBe(10);
126
146
  });
127
147
 
128
148
  it("respects concurrency limit", async () => {
129
- const { ctx, run } = methodCtx("work");
130
- const resolvers: Array<() => void> = [];
131
- const original = async () => new Promise<void>((r) => resolvers.push(r));
132
- Pool(2)(original, ctx);
149
+ const { ctx, run } = fieldCtx("taskWork");
150
+ Pool("work", 2)(undefined, ctx);
133
151
 
134
- const instance: Record<string, unknown> = {};
152
+ const resolvers: Array<() => void> = [];
153
+ const instance: Record<string, unknown> = {
154
+ work: async () => new Promise<void>((r) => resolvers.push(r)),
155
+ };
135
156
  run(instance);
136
157
 
137
- const work = instance.work as () => Promise<void>;
138
- const t1 = work();
139
- const t2 = work();
140
- work(); // queued — concurrency limit is 2
158
+ const taskWork = instance.taskWork as { (): Promise<void>; active: () => number; pending: () => number };
159
+ const t1 = taskWork();
160
+ const t2 = taskWork();
161
+ taskWork();
141
162
 
142
- expect((instance.work_active as () => number)()).toBe(2);
143
- expect((instance.work_pending as () => number)()).toBe(1);
163
+ expect(taskWork.active()).toBe(2);
164
+ expect(taskWork.pending()).toBe(1);
144
165
 
145
166
  resolvers.forEach((r) => { r(); });
146
167
  await Promise.all([t1, t2]);
147
168
  });
148
169
 
149
- it("Pool(-1, fn) — clamps to 1, does not allow unlimited concurrency", async () => {
150
- const { ctx, run } = methodCtx("work");
151
- const resolvers: Array<() => void> = [];
152
- const original = async () => new Promise<void>((r) => resolvers.push(r));
153
- Pool(-1)(original, ctx);
154
-
155
- const instance: Record<string, unknown> = {};
156
- run(instance);
157
-
158
- const work = instance.work as () => Promise<void>;
159
- const t1 = work();
160
- const t2 = work();
170
+ it("defaults concurrency to 1", async () => {
171
+ const { ctx, run } = fieldCtx("taskWork");
172
+ Pool("work")(undefined, ctx);
161
173
 
162
- // With concurrency clamped to 1, only one task should be active at a time
163
- expect((instance.work_active as () => number)()).toBe(1);
164
- expect((instance.work_pending as () => number)()).toBe(1);
165
-
166
- resolvers[0]();
167
- await t1;
168
- resolvers[1]();
169
- await t2;
170
- });
171
-
172
- it("decorated method called concurrently up to pool limit — active() signal is accurate", async () => {
173
- const { ctx, run } = methodCtx("process");
174
174
  const resolvers: Array<() => void> = [];
175
- const original = async () => new Promise<void>((r) => resolvers.push(r));
176
- Pool(3)(original, ctx);
177
-
178
- const instance: Record<string, unknown> = {};
175
+ const instance: Record<string, unknown> = {
176
+ work: async () => new Promise<void>((r) => resolvers.push(r)),
177
+ };
179
178
  run(instance);
180
179
 
181
- const process = instance.process as () => Promise<void>;
182
- const active = instance.process_active as () => number;
183
-
184
- const t1 = process();
185
- expect(active()).toBe(1);
186
-
187
- const t2 = process();
188
- expect(active()).toBe(2);
189
-
190
- const t3 = process();
191
- expect(active()).toBe(3);
180
+ const taskWork = instance.taskWork as { (): Promise<void>; active: () => number; pending: () => number };
181
+ const t1 = taskWork();
182
+ const t2 = taskWork();
192
183
 
193
- // 4th task exceeds limit, goes to pending
194
- const t4 = process();
195
- expect(active()).toBe(3);
196
- expect((instance.process_pending as () => number)()).toBe(1);
184
+ expect(taskWork.active()).toBe(1);
185
+ expect(taskWork.pending()).toBe(1);
197
186
 
198
- // Resolve t1 first — this will allow t4 to start and push its resolver
199
187
  resolvers[0]();
200
188
  await t1;
201
-
202
- // Now t4 has started and pushed a resolver
203
189
  resolvers[1]();
204
- resolvers[2]();
205
- resolvers[3]();
206
- await Promise.all([t2, t3, t4]);
207
-
208
- expect(active()).toBe(0);
190
+ await t2;
209
191
  });
210
192
  });
package/src/decorators.ts CHANGED
@@ -1,50 +1,94 @@
1
- import { createMethodDecorator } from "@praxisjs/decorators";
1
+ import { createFieldDecorator } from "@praxisjs/decorators";
2
+ import type { Computed } from "@praxisjs/shared";
2
3
 
3
4
  import { pool } from "./pool";
4
5
  import { queue } from "./queue";
5
6
  import { task } from "./task";
6
7
 
7
- export function Task() {
8
- return createMethodDecorator({
9
- wrap(original, instance, name) {
10
- const self = instance as Record<string, unknown>;
11
- const t = task((original as (...args: unknown[]) => Promise<unknown>).bind(instance));
12
- self[`${name}_loading`] = t.loading;
13
- self[`${name}_error`] = t.error;
14
- self[`${name}_lastResult`] = t.lastResult;
15
- return (...args: unknown[]) => t(...args);
8
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
+ type AnyAsyncFn = (this: any, ...args: any[]) => Promise<any>;
10
+
11
+ // ── Decorated types ────────────────────────────────────────────────────────────
12
+
13
+ export type TaskDecorated<T extends AnyAsyncFn> =
14
+ ((...args: Parameters<T>) => ReturnType<T>) & {
15
+ loading: Computed<boolean>;
16
+ error: Computed<Error | null>;
17
+ lastResult: Computed<Awaited<ReturnType<T>> | null>;
18
+ cancelAll(): void;
19
+ };
20
+
21
+ export type QueueDecorated<T extends AnyAsyncFn> =
22
+ ((...args: Parameters<T>) => ReturnType<T>) & {
23
+ loading: Computed<boolean>;
24
+ pending: Computed<number>;
25
+ error: Computed<Error | null>;
26
+ clear(): void;
27
+ };
28
+
29
+ export type PoolDecorated<T extends AnyAsyncFn> =
30
+ ((...args: Parameters<T>) => ReturnType<T>) & {
31
+ loading: Computed<boolean>;
32
+ active: Computed<number>;
33
+ pending: Computed<number>;
34
+ error: Computed<Error | null>;
35
+ };
36
+
37
+ // ── Type helpers ───────────────────────────────────────────────────────────────
38
+
39
+ export type TaskOf<C, K extends keyof C> =
40
+ C[K] extends AnyAsyncFn ? TaskDecorated<C[K]> : never;
41
+
42
+ export type QueueOf<C, K extends keyof C> =
43
+ C[K] extends AnyAsyncFn ? QueueDecorated<C[K]> : never;
44
+
45
+ export type PoolOf<C, K extends keyof C> =
46
+ C[K] extends AnyAsyncFn ? PoolDecorated<C[K]> : never;
47
+
48
+ // ── @Task ──────────────────────────────────────────────────────────────────────
49
+
50
+ export function Task(methodName: string) {
51
+ return createFieldDecorator({
52
+ bind(instance: object, _name: string) {
53
+ const inst = instance as Record<string, AnyAsyncFn>;
54
+ return {
55
+ descriptor: {
56
+ value: task(inst[methodName].bind(instance)) as unknown,
57
+ writable: true,
58
+ },
59
+ };
16
60
  },
17
- // Concurrent decorators work on any class, not just StatefulComponent
18
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
- }) as unknown as (value: (...args: any[]) => Promise<any>, context: ClassMethodDecoratorContext<any>) => void;
61
+ });
20
62
  }
21
63
 
22
- export function Queue() {
23
- return createMethodDecorator({
24
- wrap(original, instance, name) {
25
- const self = instance as Record<string, unknown>;
26
- const q = queue((original as (...args: unknown[]) => Promise<unknown>).bind(instance));
27
- self[`${name}_loading`] = q.loading;
28
- self[`${name}_pending`] = q.pending;
29
- self[`${name}_error`] = q.error;
30
- self[`${name}_clear`] = () => { q.clear(); };
31
- return (...args: unknown[]) => q(...args);
64
+ // ── @Queue ─────────────────────────────────────────────────────────────────────
65
+
66
+ export function Queue(methodName: string) {
67
+ return createFieldDecorator({
68
+ bind(instance: object, _name: string) {
69
+ const inst = instance as Record<string, AnyAsyncFn>;
70
+ return {
71
+ descriptor: {
72
+ value: queue(inst[methodName].bind(instance)) as unknown,
73
+ writable: true,
74
+ },
75
+ };
32
76
  },
33
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
- }) as unknown as (value: (...args: any[]) => Promise<any>, context: ClassMethodDecoratorContext<any>) => void;
77
+ });
35
78
  }
36
79
 
37
- export function Pool(concurrency: number) {
38
- return createMethodDecorator({
39
- wrap(original, instance, name) {
40
- const self = instance as Record<string, unknown>;
41
- const p = pool(concurrency, (original as (...args: unknown[]) => Promise<unknown>).bind(instance));
42
- self[`${name}_loading`] = p.loading;
43
- self[`${name}_active`] = p.active;
44
- self[`${name}_pending`] = p.pending;
45
- self[`${name}_error`] = p.error;
46
- return (...args: unknown[]) => p(...args);
80
+ // ── @Pool ──────────────────────────────────────────────────────────────────────
81
+
82
+ export function Pool(methodName: string, concurrency = 1) {
83
+ return createFieldDecorator({
84
+ bind(instance: object, _name: string) {
85
+ const inst = instance as Record<string, AnyAsyncFn>;
86
+ return {
87
+ descriptor: {
88
+ value: pool(concurrency, inst[methodName].bind(instance)) as unknown,
89
+ writable: true,
90
+ },
91
+ };
47
92
  },
48
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
- }) as unknown as (value: (...args: any[]) => Promise<any>, context: ClassMethodDecoratorContext<any>) => void;
93
+ });
50
94
  }
package/src/index.ts CHANGED
@@ -1 +1,2 @@
1
1
  export { Task, Queue, Pool } from "./decorators";
2
+ export type { TaskDecorated, QueueDecorated, PoolDecorated, TaskOf, QueueOf, PoolOf } from "./decorators";