codeweaver 3.0.2 → 3.0.6
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/package.json +2 -2
- package/src/routers/products/product.controller.ts +1 -1
- package/src/utilities/assignment.ts +12 -17
- package/src/utilities/conversion.ts +12 -21
- package/src/utilities/error-handling.ts +3 -1
- package/src/utilities/parallel/chanel.ts +2 -4
- package/src/utilities/parallel/parallel.ts +24 -9
- package/src/utilities/parallel/worker-pool.ts +3 -10
package/package.json
CHANGED
|
@@ -161,7 +161,7 @@ export default class ProductController {
|
|
|
161
161
|
}
|
|
162
162
|
const product = await this.get(id);
|
|
163
163
|
if (product != null) {
|
|
164
|
-
await assign(
|
|
164
|
+
await assign(product, updateData, ZodProduct);
|
|
165
165
|
await productCache.delete(updateData.id.toString());
|
|
166
166
|
await productsCache.delete("key");
|
|
167
167
|
} else {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { parallelMap } from "./parallel/parallel";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Strictly assign obj (type T1) to T2 using a Zod schema.
|
|
@@ -14,33 +14,28 @@ import { ResponseError } from "./error-handling";
|
|
|
14
14
|
* @returns T2 representing the destination after assignment
|
|
15
15
|
*/
|
|
16
16
|
export default async function assign<T1 extends object, T2 extends object>(
|
|
17
|
-
source: T1,
|
|
18
17
|
destination: T2,
|
|
19
|
-
|
|
18
|
+
source: T1,
|
|
19
|
+
destinationSchema?: z.ZodObject<any>
|
|
20
20
|
): Promise<T2> {
|
|
21
|
-
let keys = Object.keys(
|
|
21
|
+
let keys = Object.keys(destinationSchema?.shape ?? destination);
|
|
22
22
|
|
|
23
23
|
// Iterate schema keys
|
|
24
|
-
await
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
})
|
|
30
|
-
);
|
|
24
|
+
await parallelMap(keys, async (key) => {
|
|
25
|
+
if (source.hasOwnProperty(key)) {
|
|
26
|
+
(destination as any)[key] = (source as any)[key];
|
|
27
|
+
}
|
|
28
|
+
});
|
|
31
29
|
|
|
32
|
-
if (
|
|
30
|
+
if (destinationSchema != null) {
|
|
33
31
|
// Validate using the schema on the subset (this will also coerce if the schema has transforms)
|
|
34
|
-
const parseResult = await
|
|
32
|
+
const parseResult = await destinationSchema.safeParseAsync(destination);
|
|
35
33
|
if (parseResult.success == false) {
|
|
36
34
|
// Build a descriptive error message from the first issue
|
|
37
35
|
const issue = parseResult.error.issues?.[0];
|
|
38
36
|
const path = issue?.path?.length ? issue.path.join(".") : "value";
|
|
39
37
|
const message = issue?.message ?? "Schema validation failed";
|
|
40
|
-
throw new
|
|
41
|
-
`Validation failed for "${path}": ${message}`,
|
|
42
|
-
500
|
|
43
|
-
);
|
|
38
|
+
throw new Error(`Validation failed for "${path}": ${message}`);
|
|
44
39
|
}
|
|
45
40
|
}
|
|
46
41
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z, ZodRawShape } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { parallelMap } from "./parallel/parallel";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Helper: normalize and validate a numeric string for integer parsing.
|
|
@@ -41,10 +41,7 @@ export function stringToInteger(input: string): number {
|
|
|
41
41
|
try {
|
|
42
42
|
return parseIntegerStrict(input);
|
|
43
43
|
} catch {
|
|
44
|
-
throw new
|
|
45
|
-
"The input parameter must be a valid integer.",
|
|
46
|
-
400
|
|
47
|
-
);
|
|
44
|
+
throw new Error("The input parameter must be a valid integer.");
|
|
48
45
|
}
|
|
49
46
|
}
|
|
50
47
|
|
|
@@ -71,9 +68,8 @@ export function stringToBoolean(input: string): boolean {
|
|
|
71
68
|
return false;
|
|
72
69
|
}
|
|
73
70
|
|
|
74
|
-
throw new
|
|
75
|
-
"The input parameter must be a boolean (e.g., true/false, 1/0)."
|
|
76
|
-
400
|
|
71
|
+
throw new Error(
|
|
72
|
+
"The input parameter must be a boolean (e.g., true/false, 1/0)."
|
|
77
73
|
);
|
|
78
74
|
}
|
|
79
75
|
|
|
@@ -98,7 +94,7 @@ export function stringToNumber(input: string): number {
|
|
|
98
94
|
|
|
99
95
|
return n;
|
|
100
96
|
} catch {
|
|
101
|
-
throw new
|
|
97
|
+
throw new Error("The input parameter must be a valid number.");
|
|
102
98
|
}
|
|
103
99
|
}
|
|
104
100
|
|
|
@@ -121,7 +117,7 @@ export async function convert<T1 extends object, T2 extends object>(
|
|
|
121
117
|
// Derive the runtime keys from the schema's shape
|
|
122
118
|
const shape = (schema as any)._def?.shape as ZodRawShape | undefined;
|
|
123
119
|
if (!shape) {
|
|
124
|
-
throw new
|
|
120
|
+
throw new Error("Provided schema has no shape.");
|
|
125
121
|
}
|
|
126
122
|
|
|
127
123
|
const keysSchema = Object.keys(shape) as Array<keyof any>;
|
|
@@ -131,13 +127,11 @@ export async function convert<T1 extends object, T2 extends object>(
|
|
|
131
127
|
const candidate: any = {};
|
|
132
128
|
|
|
133
129
|
// Iterate schema keys
|
|
134
|
-
await
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
})
|
|
140
|
-
);
|
|
130
|
+
await parallelMap(keysSchema, async (key) => {
|
|
131
|
+
if ((data as any).hasOwnProperty(key)) {
|
|
132
|
+
candidate[key] = (data as any)[key];
|
|
133
|
+
}
|
|
134
|
+
});
|
|
141
135
|
|
|
142
136
|
// Validate against the schema
|
|
143
137
|
if (ignoreValidation) {
|
|
@@ -151,10 +145,7 @@ export async function convert<T1 extends object, T2 extends object>(
|
|
|
151
145
|
}));
|
|
152
146
|
|
|
153
147
|
// You can log issues or throw a structured error
|
|
154
|
-
throw new
|
|
155
|
-
`Validation failed: ${JSON.stringify(issues)}`,
|
|
156
|
-
500
|
|
157
|
-
);
|
|
148
|
+
throw new Error(`Validation failed: ${JSON.stringify(issues)}`);
|
|
158
149
|
}
|
|
159
150
|
|
|
160
151
|
// Return the validated data typed as T2
|
|
@@ -45,7 +45,9 @@ export class ResponseError extends Error {
|
|
|
45
45
|
super(message);
|
|
46
46
|
|
|
47
47
|
// If a custom stack is provided, you might assign it; otherwise, the runtime stack will be used.
|
|
48
|
-
if (stack)
|
|
48
|
+
if (stack) {
|
|
49
|
+
this.stack = stack;
|
|
50
|
+
}
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { ResponseError } from "../error-handling";
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Event callback type for channel send event.
|
|
5
3
|
* @template T - Type of the value sent through the channel.
|
|
@@ -38,7 +36,7 @@ export class Channel<T> {
|
|
|
38
36
|
*/
|
|
39
37
|
public async send(value: T): Promise<void> {
|
|
40
38
|
if (this.closed) {
|
|
41
|
-
throw new
|
|
39
|
+
throw new Error("Channel is closed");
|
|
42
40
|
}
|
|
43
41
|
if (this.receivers.length > 0) {
|
|
44
42
|
const receiver = this.receivers.shift()!;
|
|
@@ -62,7 +60,7 @@ export class Channel<T> {
|
|
|
62
60
|
return this.queue.shift()!;
|
|
63
61
|
}
|
|
64
62
|
if (this.closed) {
|
|
65
|
-
throw new
|
|
63
|
+
throw new Error("Channel is closed");
|
|
66
64
|
}
|
|
67
65
|
return await new Promise<T>((resolve) => {
|
|
68
66
|
this.receivers.push(resolve);
|
|
@@ -1,7 +1,20 @@
|
|
|
1
|
-
import { ResponseError } from "../error-handling";
|
|
2
|
-
import path from "path";
|
|
3
1
|
import { WorkerPool } from "./worker-pool";
|
|
4
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Creates a setter function that, when invoked, rebinds the captured
|
|
5
|
+
* destination binding to the provided source value.
|
|
6
|
+
*
|
|
7
|
+
* @template T - The type of both `destination` and `source`, constrained to object types.
|
|
8
|
+
* @param {T} destination - The destination value (captured by the closure).
|
|
9
|
+
* @param {T} source - The source value to which `destination` will be rebound when the returned function runs.
|
|
10
|
+
* @returns {() => void} A function that, when called, rebinds the captured `destination` to `source`.
|
|
11
|
+
*/
|
|
12
|
+
export function set<T extends object>(destination: T, source: T): () => void {
|
|
13
|
+
return () => {
|
|
14
|
+
destination = source;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
5
18
|
/**
|
|
6
19
|
* Executes multiple asynchronous or synchronous tasks in parallel and returns their results as an array.
|
|
7
20
|
*
|
|
@@ -13,7 +26,9 @@ import { WorkerPool } from "./worker-pool";
|
|
|
13
26
|
* const results = await parallel(
|
|
14
27
|
* () => fetchUser(1),
|
|
15
28
|
* () => fetchUser(2),
|
|
16
|
-
*
|
|
29
|
+
* fetchProduct,
|
|
30
|
+
* set(user.id, 1),
|
|
31
|
+
* set(user.name, "Bob")
|
|
17
32
|
* );
|
|
18
33
|
* console.log(results); // [user1, user2, user3]
|
|
19
34
|
*/
|
|
@@ -60,11 +75,11 @@ export async function parallelMapWithConcurrencyLevel<T, U>(
|
|
|
60
75
|
concurrencyLevel: number = Infinity
|
|
61
76
|
): Promise<U[]> {
|
|
62
77
|
if (!Array.isArray(items)) {
|
|
63
|
-
throw new
|
|
78
|
+
throw new TypeError("Items must be an array");
|
|
64
79
|
}
|
|
65
80
|
|
|
66
81
|
if (concurrencyLevel <= 0) {
|
|
67
|
-
throw new
|
|
82
|
+
throw new Error("Concurrency must be greater than 0");
|
|
68
83
|
}
|
|
69
84
|
|
|
70
85
|
// If concurrency is not finite, use the simple Promise.all approach
|
|
@@ -117,11 +132,11 @@ export async function parallelCpuMap<T, R>(
|
|
|
117
132
|
throw new Error("concurrency must be greater than 0");
|
|
118
133
|
}
|
|
119
134
|
|
|
120
|
-
// Worker pool setup
|
|
121
|
-
const workerPath = path.resolve(__dirname, mapperWorkerFilePath);
|
|
122
|
-
|
|
123
135
|
// Instantiate a concrete pool
|
|
124
|
-
const workerPool = new WorkerPool<T, R>(
|
|
136
|
+
const workerPool = new WorkerPool<T, R>(
|
|
137
|
+
mapperWorkerFilePath,
|
|
138
|
+
concurrencyLevel
|
|
139
|
+
);
|
|
125
140
|
|
|
126
141
|
try {
|
|
127
142
|
// Dispatch all items and collect results in order
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Worker } from "worker_threads";
|
|
2
2
|
import os from "os";
|
|
3
|
-
import { ResponseError } from "../error-handling";
|
|
4
3
|
|
|
5
4
|
export type Task<T> = { id: number; payload: T };
|
|
6
5
|
export type Result<R> = { id: number; result?: R; error?: string };
|
|
@@ -28,9 +27,7 @@ export class WorkerPool<T, R> {
|
|
|
28
27
|
if (code !== 0) {
|
|
29
28
|
// Notify all pending promises about the exit
|
|
30
29
|
for (const [, p] of this.pending) {
|
|
31
|
-
p.reject(
|
|
32
|
-
new ResponseError(`Worker ${i} exited with code ${code}`, 500)
|
|
33
|
-
);
|
|
30
|
+
p.reject(new Error(`Worker ${i} exited with code ${code}`));
|
|
34
31
|
}
|
|
35
32
|
this.pending.clear();
|
|
36
33
|
}
|
|
@@ -56,10 +53,7 @@ export class WorkerPool<T, R> {
|
|
|
56
53
|
// This simple version broadcasts the error to all pending tasks for safety.
|
|
57
54
|
for (const [id, entry] of this.pending) {
|
|
58
55
|
entry.reject(
|
|
59
|
-
new
|
|
60
|
-
`Worker ${workerIndex} error: ${err?.message ?? err}`,
|
|
61
|
-
500
|
|
62
|
-
)
|
|
56
|
+
new Error(`Worker ${workerIndex} error: ${err?.message ?? err}`)
|
|
63
57
|
);
|
|
64
58
|
}
|
|
65
59
|
this.pending.clear();
|
|
@@ -77,8 +71,7 @@ export class WorkerPool<T, R> {
|
|
|
77
71
|
|
|
78
72
|
// Map all items using a round-robin distribution across workers
|
|
79
73
|
async mapAll(items: T[]): Promise<R[]> {
|
|
80
|
-
if (!Array.isArray(items))
|
|
81
|
-
throw new ResponseError("Items must be an array", 400);
|
|
74
|
+
if (!Array.isArray(items)) throw new Error("Items must be an array");
|
|
82
75
|
const results: R[] = new Array(items.length);
|
|
83
76
|
|
|
84
77
|
const workerCount = this.workers.length;
|