@prysm-ai/utils 0.1.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/async.d.ts +18 -0
- package/async.js +99 -0
- package/index.d.ts +3 -0
- package/index.js +3 -0
- package/package.json +26 -0
- package/time.d.ts +21 -0
- package/time.js +70 -0
- package/validation.d.ts +14 -0
- package/validation.js +68 -0
package/async.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number, timeoutError?: Error): Promise<T>;
|
|
2
|
+
export declare function retry<T>(fn: () => Promise<T>, options?: {
|
|
3
|
+
maxAttempts?: number;
|
|
4
|
+
delayMs?: number;
|
|
5
|
+
backoff?: 'linear' | 'exponential';
|
|
6
|
+
onRetry?: (attempt: number, error: Error) => void;
|
|
7
|
+
}): Promise<T>;
|
|
8
|
+
export declare function sleep(ms: number): Promise<void>;
|
|
9
|
+
export declare function parallel<T>(tasks: (() => Promise<T>)[], maxConcurrency?: number): Promise<T[]>;
|
|
10
|
+
export declare class AsyncQueue<T> {
|
|
11
|
+
private concurrency;
|
|
12
|
+
private queue;
|
|
13
|
+
private running;
|
|
14
|
+
constructor(concurrency?: number);
|
|
15
|
+
add(task: () => Promise<T>): Promise<T>;
|
|
16
|
+
private process;
|
|
17
|
+
get size(): number;
|
|
18
|
+
}
|
package/async.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Async utilities
|
|
2
|
+
export async function withTimeout(promise, timeoutMs, timeoutError) {
|
|
3
|
+
let timeoutId;
|
|
4
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
5
|
+
timeoutId = setTimeout(() => {
|
|
6
|
+
reject(timeoutError ?? new Error(`Operation timed out after ${timeoutMs}ms`));
|
|
7
|
+
}, timeoutMs);
|
|
8
|
+
});
|
|
9
|
+
try {
|
|
10
|
+
return await Promise.race([promise, timeoutPromise]);
|
|
11
|
+
}
|
|
12
|
+
finally {
|
|
13
|
+
clearTimeout(timeoutId);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export async function retry(fn, options = {}) {
|
|
17
|
+
const { maxAttempts = 3, delayMs = 1000, backoff = 'linear', onRetry } = options;
|
|
18
|
+
let lastError;
|
|
19
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
20
|
+
try {
|
|
21
|
+
return await fn();
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
lastError = error;
|
|
25
|
+
if (attempt < maxAttempts) {
|
|
26
|
+
const delay = backoff === 'exponential'
|
|
27
|
+
? delayMs * Math.pow(2, attempt - 1)
|
|
28
|
+
: delayMs * attempt;
|
|
29
|
+
onRetry?.(attempt, lastError);
|
|
30
|
+
await sleep(delay);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
throw lastError;
|
|
35
|
+
}
|
|
36
|
+
export function sleep(ms) {
|
|
37
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
38
|
+
}
|
|
39
|
+
export async function parallel(tasks, maxConcurrency) {
|
|
40
|
+
if (!maxConcurrency) {
|
|
41
|
+
return Promise.all(tasks.map(fn => fn()));
|
|
42
|
+
}
|
|
43
|
+
const results = [];
|
|
44
|
+
const executing = [];
|
|
45
|
+
for (const task of tasks) {
|
|
46
|
+
const p = task().then(result => {
|
|
47
|
+
results.push(result);
|
|
48
|
+
});
|
|
49
|
+
executing.push(p);
|
|
50
|
+
if (executing.length >= maxConcurrency) {
|
|
51
|
+
await Promise.race(executing);
|
|
52
|
+
executing.splice(executing.findIndex(e => e === p), 1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
await Promise.all(executing);
|
|
56
|
+
return results;
|
|
57
|
+
}
|
|
58
|
+
export class AsyncQueue {
|
|
59
|
+
concurrency;
|
|
60
|
+
queue = [];
|
|
61
|
+
running = 0;
|
|
62
|
+
constructor(concurrency = 1) {
|
|
63
|
+
this.concurrency = concurrency;
|
|
64
|
+
}
|
|
65
|
+
async add(task) {
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
this.queue.push(async () => {
|
|
68
|
+
try {
|
|
69
|
+
const result = await task();
|
|
70
|
+
resolve(result);
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
reject(error);
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
this.process();
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async process() {
|
|
82
|
+
if (this.running >= this.concurrency)
|
|
83
|
+
return;
|
|
84
|
+
if (this.queue.length === 0)
|
|
85
|
+
return;
|
|
86
|
+
this.running++;
|
|
87
|
+
const task = this.queue.shift();
|
|
88
|
+
try {
|
|
89
|
+
await task();
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
this.running--;
|
|
93
|
+
this.process();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
get size() {
|
|
97
|
+
return this.queue.length;
|
|
98
|
+
}
|
|
99
|
+
}
|
package/index.d.ts
ADDED
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@prysm-ai/utils",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Shared utilities",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"build:watch": "tsc --watch",
|
|
17
|
+
"typecheck": "tsc --noEmit",
|
|
18
|
+
"clean": "rm -rf dist"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"zod": "^3.23.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"typescript": "^5.4.0"
|
|
25
|
+
}
|
|
26
|
+
}
|
package/time.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface TimeContextOptions {
|
|
2
|
+
/** Timezone for display (e.g., 'UTC', 'America/New_York', 'Asia/Shanghai') */
|
|
3
|
+
timezone?: string;
|
|
4
|
+
/** Additional context to inject */
|
|
5
|
+
additionalContext?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function formatTimeForTimezone(date: Date, timezone: string): string;
|
|
8
|
+
export declare function getCurrentTimeISO(): string;
|
|
9
|
+
export declare function getCurrentTimeForTimezone(timezone: string): string;
|
|
10
|
+
export interface TimeContextResult {
|
|
11
|
+
utcTime: string;
|
|
12
|
+
localTime: string;
|
|
13
|
+
timezone: string;
|
|
14
|
+
formattedContext: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function buildTimeContext(options?: TimeContextOptions): TimeContextResult;
|
|
17
|
+
export interface TimeContextMessage {
|
|
18
|
+
role: string;
|
|
19
|
+
content: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function injectTimeContext(messages: TimeContextMessage[], options?: TimeContextOptions): TimeContextMessage[];
|
package/time.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const DEFAULT_TIME_CONTEXT = `Current UTC time: {{utcTime}}. For other timezones, use the time tool.`;
|
|
2
|
+
export function formatTimeForTimezone(date, timezone) {
|
|
3
|
+
try {
|
|
4
|
+
return new Intl.DateTimeFormat('en-US', {
|
|
5
|
+
timeZone: timezone,
|
|
6
|
+
year: 'numeric',
|
|
7
|
+
month: '2-digit',
|
|
8
|
+
day: '2-digit',
|
|
9
|
+
hour: '2-digit',
|
|
10
|
+
minute: '2-digit',
|
|
11
|
+
second: '2-digit',
|
|
12
|
+
hour12: false,
|
|
13
|
+
timeZoneName: 'short',
|
|
14
|
+
}).format(date);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return date.toISOString();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function getCurrentTimeISO() {
|
|
21
|
+
return new Date().toISOString();
|
|
22
|
+
}
|
|
23
|
+
export function getCurrentTimeForTimezone(timezone) {
|
|
24
|
+
return formatTimeForTimezone(new Date(), timezone);
|
|
25
|
+
}
|
|
26
|
+
export function buildTimeContext(options = {}) {
|
|
27
|
+
const timezone = options.timezone ?? 'UTC';
|
|
28
|
+
const now = new Date();
|
|
29
|
+
const utcTime = now.toISOString();
|
|
30
|
+
const localTime = formatTimeForTimezone(now, timezone);
|
|
31
|
+
let formattedContext = DEFAULT_TIME_CONTEXT
|
|
32
|
+
.replace('{{utcTime}}', utcTime);
|
|
33
|
+
if (options.additionalContext) {
|
|
34
|
+
formattedContext += '\n' + options.additionalContext;
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
utcTime,
|
|
38
|
+
localTime,
|
|
39
|
+
timezone,
|
|
40
|
+
formattedContext,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export function injectTimeContext(messages, options = {}) {
|
|
44
|
+
const { formattedContext } = buildTimeContext(options);
|
|
45
|
+
if (messages.length === 0) {
|
|
46
|
+
return [{
|
|
47
|
+
role: 'system',
|
|
48
|
+
content: formattedContext,
|
|
49
|
+
}];
|
|
50
|
+
}
|
|
51
|
+
const systemMessages = messages.filter(m => m.role === 'system');
|
|
52
|
+
if (systemMessages.length > 0) {
|
|
53
|
+
const lastSystemMessage = systemMessages[systemMessages.length - 1];
|
|
54
|
+
const updatedSystemMessage = {
|
|
55
|
+
...lastSystemMessage,
|
|
56
|
+
content: lastSystemMessage.content + '\n\n' + formattedContext,
|
|
57
|
+
};
|
|
58
|
+
const updatedMessages = messages.map(m => {
|
|
59
|
+
if (m === lastSystemMessage) {
|
|
60
|
+
return updatedSystemMessage;
|
|
61
|
+
}
|
|
62
|
+
return m;
|
|
63
|
+
});
|
|
64
|
+
return updatedMessages;
|
|
65
|
+
}
|
|
66
|
+
return [{
|
|
67
|
+
role: 'system',
|
|
68
|
+
content: formattedContext,
|
|
69
|
+
}, ...messages];
|
|
70
|
+
}
|
package/validation.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ZodError, ZodType } from 'zod';
|
|
2
|
+
export declare function deepMerge<T extends Record<string, unknown>>(target: T, ...sources: Partial<T>[]): T;
|
|
3
|
+
export declare function sortObjectKeys<T extends Record<string, unknown>>(obj: T): T;
|
|
4
|
+
export declare function validate<T>(schema: ZodType<T>, data: unknown): T;
|
|
5
|
+
export declare function safeValidate<T>(schema: ZodType<T>, data: unknown): {
|
|
6
|
+
success: true;
|
|
7
|
+
data: T;
|
|
8
|
+
} | {
|
|
9
|
+
success: false;
|
|
10
|
+
error: ZodError;
|
|
11
|
+
};
|
|
12
|
+
export declare function requireFields(obj: Record<string, unknown>, fields: string[]): string[];
|
|
13
|
+
export declare function isValidUUID(str: string): boolean;
|
|
14
|
+
export declare function isValidEmail(email: string): boolean;
|
package/validation.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Deep merge utility
|
|
2
|
+
export function deepMerge(target, ...sources) {
|
|
3
|
+
const result = { ...target };
|
|
4
|
+
for (const source of sources) {
|
|
5
|
+
for (const key of Object.keys(source)) {
|
|
6
|
+
const sourceValue = source[key];
|
|
7
|
+
const targetValue = result[key];
|
|
8
|
+
if (sourceValue !== null &&
|
|
9
|
+
typeof sourceValue === 'object' &&
|
|
10
|
+
!Array.isArray(sourceValue) &&
|
|
11
|
+
targetValue !== null &&
|
|
12
|
+
typeof targetValue === 'object' &&
|
|
13
|
+
!Array.isArray(targetValue) &&
|
|
14
|
+
!(sourceValue instanceof Date) &&
|
|
15
|
+
!(targetValue instanceof Date)) {
|
|
16
|
+
result[key] = deepMerge(targetValue, sourceValue);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
result[key] = sourceValue;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
// Object sort for cache key generation
|
|
26
|
+
export function sortObjectKeys(obj) {
|
|
27
|
+
return Object.keys(obj)
|
|
28
|
+
.sort()
|
|
29
|
+
.reduce((acc, key) => {
|
|
30
|
+
const value = obj[key];
|
|
31
|
+
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
32
|
+
acc[key] = sortObjectKeys(value);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
acc[key] = value;
|
|
36
|
+
}
|
|
37
|
+
return acc;
|
|
38
|
+
}, {});
|
|
39
|
+
}
|
|
40
|
+
// Validate with Zod schema
|
|
41
|
+
export function validate(schema, data) {
|
|
42
|
+
return schema.parse(data);
|
|
43
|
+
}
|
|
44
|
+
// Safe validate (returns result instead of throwing)
|
|
45
|
+
export function safeValidate(schema, data) {
|
|
46
|
+
const result = schema.safeParse(data);
|
|
47
|
+
if (result.success) {
|
|
48
|
+
return { success: true, data: result.data };
|
|
49
|
+
}
|
|
50
|
+
return { success: false, error: result.error };
|
|
51
|
+
}
|
|
52
|
+
// Required fields check
|
|
53
|
+
export function requireFields(obj, fields) {
|
|
54
|
+
return fields.filter(field => {
|
|
55
|
+
const value = obj[field];
|
|
56
|
+
return value === undefined || value === null;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
// isValidUUID
|
|
60
|
+
export function isValidUUID(str) {
|
|
61
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
62
|
+
return uuidRegex.test(str);
|
|
63
|
+
}
|
|
64
|
+
// isValidEmail
|
|
65
|
+
export function isValidEmail(email) {
|
|
66
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
67
|
+
return emailRegex.test(email);
|
|
68
|
+
}
|