bunqueue 1.0.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/LICENSE +21 -0
- package/README.md +640 -0
- package/dist/application/dlqManager.d.ts +19 -0
- package/dist/application/dlqManager.d.ts.map +1 -0
- package/dist/application/dlqManager.js +44 -0
- package/dist/application/dlqManager.js.map +1 -0
- package/dist/application/eventsManager.d.ts +28 -0
- package/dist/application/eventsManager.d.ts.map +1 -0
- package/dist/application/eventsManager.js +59 -0
- package/dist/application/eventsManager.js.map +1 -0
- package/dist/application/jobLogsManager.d.ts +20 -0
- package/dist/application/jobLogsManager.d.ts.map +1 -0
- package/dist/application/jobLogsManager.js +28 -0
- package/dist/application/jobLogsManager.js.map +1 -0
- package/dist/application/metricsExporter.d.ts +24 -0
- package/dist/application/metricsExporter.d.ts.map +1 -0
- package/dist/application/metricsExporter.js +80 -0
- package/dist/application/metricsExporter.js.map +1 -0
- package/dist/application/operations/ack.d.ts +48 -0
- package/dist/application/operations/ack.d.ts.map +1 -0
- package/dist/application/operations/ack.js +109 -0
- package/dist/application/operations/ack.js.map +1 -0
- package/dist/application/operations/index.d.ts +10 -0
- package/dist/application/operations/index.d.ts.map +1 -0
- package/dist/application/operations/index.js +10 -0
- package/dist/application/operations/index.js.map +1 -0
- package/dist/application/operations/jobManagement.d.ts +32 -0
- package/dist/application/operations/jobManagement.d.ts.map +1 -0
- package/dist/application/operations/jobManagement.js +122 -0
- package/dist/application/operations/jobManagement.js.map +1 -0
- package/dist/application/operations/pull.d.ts +36 -0
- package/dist/application/operations/pull.d.ts.map +1 -0
- package/dist/application/operations/pull.js +109 -0
- package/dist/application/operations/pull.js.map +1 -0
- package/dist/application/operations/push.d.ts +36 -0
- package/dist/application/operations/push.d.ts.map +1 -0
- package/dist/application/operations/push.js +94 -0
- package/dist/application/operations/push.js.map +1 -0
- package/dist/application/operations/queryOperations.d.ts +33 -0
- package/dist/application/operations/queryOperations.d.ts.map +1 -0
- package/dist/application/operations/queryOperations.js +57 -0
- package/dist/application/operations/queryOperations.js.map +1 -0
- package/dist/application/operations/queueControl.d.ts +32 -0
- package/dist/application/operations/queueControl.d.ts.map +1 -0
- package/dist/application/operations/queueControl.js +72 -0
- package/dist/application/operations/queueControl.js.map +1 -0
- package/dist/application/queueManager.d.ts +113 -0
- package/dist/application/queueManager.d.ts.map +1 -0
- package/dist/application/queueManager.js +406 -0
- package/dist/application/queueManager.js.map +1 -0
- package/dist/application/webhookManager.d.ts +35 -0
- package/dist/application/webhookManager.d.ts.map +1 -0
- package/dist/application/webhookManager.js +109 -0
- package/dist/application/webhookManager.js.map +1 -0
- package/dist/application/workerManager.d.ts +48 -0
- package/dist/application/workerManager.d.ts.map +1 -0
- package/dist/application/workerManager.js +121 -0
- package/dist/application/workerManager.js.map +1 -0
- package/dist/domain/queue/index.d.ts +6 -0
- package/dist/domain/queue/index.d.ts.map +1 -0
- package/dist/domain/queue/index.js +6 -0
- package/dist/domain/queue/index.js.map +1 -0
- package/dist/domain/queue/priorityQueue.d.ts +45 -0
- package/dist/domain/queue/priorityQueue.d.ts.map +1 -0
- package/dist/domain/queue/priorityQueue.js +203 -0
- package/dist/domain/queue/priorityQueue.js.map +1 -0
- package/dist/domain/queue/shard.d.ts +98 -0
- package/dist/domain/queue/shard.d.ts.map +1 -0
- package/dist/domain/queue/shard.js +245 -0
- package/dist/domain/queue/shard.js.map +1 -0
- package/dist/domain/types/command.d.ts +270 -0
- package/dist/domain/types/command.d.ts.map +1 -0
- package/dist/domain/types/command.js +6 -0
- package/dist/domain/types/command.js.map +1 -0
- package/dist/domain/types/cron.d.ts +32 -0
- package/dist/domain/types/cron.d.ts.map +1 -0
- package/dist/domain/types/cron.js +31 -0
- package/dist/domain/types/cron.js.map +1 -0
- package/dist/domain/types/index.d.ts +9 -0
- package/dist/domain/types/index.d.ts.map +1 -0
- package/dist/domain/types/index.js +9 -0
- package/dist/domain/types/index.js.map +1 -0
- package/dist/domain/types/job.d.ts +94 -0
- package/dist/domain/types/job.d.ts.map +1 -0
- package/dist/domain/types/job.js +81 -0
- package/dist/domain/types/job.js.map +1 -0
- package/dist/domain/types/queue.d.ts +86 -0
- package/dist/domain/types/queue.d.ts.map +1 -0
- package/dist/domain/types/queue.js +84 -0
- package/dist/domain/types/queue.js.map +1 -0
- package/dist/domain/types/response.d.ts +158 -0
- package/dist/domain/types/response.d.ts.map +1 -0
- package/dist/domain/types/response.js +86 -0
- package/dist/domain/types/response.js.map +1 -0
- package/dist/domain/types/webhook.d.ts +33 -0
- package/dist/domain/types/webhook.d.ts.map +1 -0
- package/dist/domain/types/webhook.js +20 -0
- package/dist/domain/types/webhook.js.map +1 -0
- package/dist/domain/types/worker.d.ts +27 -0
- package/dist/domain/types/worker.d.ts.map +1 -0
- package/dist/domain/types/worker.js +27 -0
- package/dist/domain/types/worker.js.map +1 -0
- package/dist/infrastructure/persistence/index.d.ts +6 -0
- package/dist/infrastructure/persistence/index.d.ts.map +1 -0
- package/dist/infrastructure/persistence/index.js +6 -0
- package/dist/infrastructure/persistence/index.js.map +1 -0
- package/dist/infrastructure/persistence/schema.d.ts +14 -0
- package/dist/infrastructure/persistence/schema.d.ts.map +1 -0
- package/dist/infrastructure/persistence/schema.js +123 -0
- package/dist/infrastructure/persistence/schema.js.map +1 -0
- package/dist/infrastructure/persistence/sqlite.d.ts +42 -0
- package/dist/infrastructure/persistence/sqlite.d.ts.map +1 -0
- package/dist/infrastructure/persistence/sqlite.js +164 -0
- package/dist/infrastructure/persistence/sqlite.js.map +1 -0
- package/dist/infrastructure/persistence/statements.d.ts +55 -0
- package/dist/infrastructure/persistence/statements.d.ts.map +1 -0
- package/dist/infrastructure/persistence/statements.js +42 -0
- package/dist/infrastructure/persistence/statements.js.map +1 -0
- package/dist/infrastructure/scheduler/cronParser.d.ts +44 -0
- package/dist/infrastructure/scheduler/cronParser.d.ts.map +1 -0
- package/dist/infrastructure/scheduler/cronParser.js +90 -0
- package/dist/infrastructure/scheduler/cronParser.js.map +1 -0
- package/dist/infrastructure/scheduler/cronScheduler.d.ts +68 -0
- package/dist/infrastructure/scheduler/cronScheduler.d.ts.map +1 -0
- package/dist/infrastructure/scheduler/cronScheduler.js +172 -0
- package/dist/infrastructure/scheduler/cronScheduler.js.map +1 -0
- package/dist/infrastructure/scheduler/index.d.ts +6 -0
- package/dist/infrastructure/scheduler/index.d.ts.map +1 -0
- package/dist/infrastructure/scheduler/index.js +6 -0
- package/dist/infrastructure/scheduler/index.js.map +1 -0
- package/dist/infrastructure/server/handler.d.ts +13 -0
- package/dist/infrastructure/server/handler.d.ts.map +1 -0
- package/dist/infrastructure/server/handler.js +167 -0
- package/dist/infrastructure/server/handler.js.map +1 -0
- package/dist/infrastructure/server/handlers/advanced.d.ts +68 -0
- package/dist/infrastructure/server/handlers/advanced.d.ts.map +1 -0
- package/dist/infrastructure/server/handlers/advanced.js +99 -0
- package/dist/infrastructure/server/handlers/advanced.js.map +1 -0
- package/dist/infrastructure/server/handlers/core.d.ts +36 -0
- package/dist/infrastructure/server/handlers/core.d.ts.map +1 -0
- package/dist/infrastructure/server/handlers/core.js +82 -0
- package/dist/infrastructure/server/handlers/core.js.map +1 -0
- package/dist/infrastructure/server/handlers/cron.d.ts +20 -0
- package/dist/infrastructure/server/handlers/cron.d.ts.map +1 -0
- package/dist/infrastructure/server/handlers/cron.js +56 -0
- package/dist/infrastructure/server/handlers/cron.js.map +1 -0
- package/dist/infrastructure/server/handlers/dlq.d.ts +20 -0
- package/dist/infrastructure/server/handlers/dlq.d.ts.map +1 -0
- package/dist/infrastructure/server/handlers/dlq.js +31 -0
- package/dist/infrastructure/server/handlers/dlq.js.map +1 -0
- package/dist/infrastructure/server/handlers/index.d.ts +7 -0
- package/dist/infrastructure/server/handlers/index.d.ts.map +1 -0
- package/dist/infrastructure/server/handlers/index.js +7 -0
- package/dist/infrastructure/server/handlers/index.js.map +1 -0
- package/dist/infrastructure/server/handlers/management.d.ts +36 -0
- package/dist/infrastructure/server/handlers/management.d.ts.map +1 -0
- package/dist/infrastructure/server/handlers/management.js +75 -0
- package/dist/infrastructure/server/handlers/management.js.map +1 -0
- package/dist/infrastructure/server/handlers/monitoring.d.ts +18 -0
- package/dist/infrastructure/server/handlers/monitoring.d.ts.map +1 -0
- package/dist/infrastructure/server/handlers/monitoring.js +102 -0
- package/dist/infrastructure/server/handlers/monitoring.js.map +1 -0
- package/dist/infrastructure/server/handlers/query.d.ts +32 -0
- package/dist/infrastructure/server/handlers/query.d.ts.map +1 -0
- package/dist/infrastructure/server/handlers/query.js +61 -0
- package/dist/infrastructure/server/handlers/query.js.map +1 -0
- package/dist/infrastructure/server/http.d.ts +43 -0
- package/dist/infrastructure/server/http.d.ts.map +1 -0
- package/dist/infrastructure/server/http.js +344 -0
- package/dist/infrastructure/server/http.js.map +1 -0
- package/dist/infrastructure/server/index.d.ts +8 -0
- package/dist/infrastructure/server/index.d.ts.map +1 -0
- package/dist/infrastructure/server/index.js +8 -0
- package/dist/infrastructure/server/index.js.map +1 -0
- package/dist/infrastructure/server/protocol.d.ts +44 -0
- package/dist/infrastructure/server/protocol.d.ts.map +1 -0
- package/dist/infrastructure/server/protocol.js +129 -0
- package/dist/infrastructure/server/protocol.js.map +1 -0
- package/dist/infrastructure/server/rateLimiter.d.ts +31 -0
- package/dist/infrastructure/server/rateLimiter.d.ts.map +1 -0
- package/dist/infrastructure/server/rateLimiter.js +79 -0
- package/dist/infrastructure/server/rateLimiter.js.map +1 -0
- package/dist/infrastructure/server/tcp.d.ts +36 -0
- package/dist/infrastructure/server/tcp.d.ts.map +1 -0
- package/dist/infrastructure/server/tcp.js +101 -0
- package/dist/infrastructure/server/tcp.js.map +1 -0
- package/dist/infrastructure/server/types.d.ts +11 -0
- package/dist/infrastructure/server/types.d.ts.map +1 -0
- package/dist/infrastructure/server/types.js +5 -0
- package/dist/infrastructure/server/types.js.map +1 -0
- package/dist/main.d.ts +6 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +111 -0
- package/dist/main.js.map +1 -0
- package/dist/shared/hash.d.ts +30 -0
- package/dist/shared/hash.d.ts.map +1 -0
- package/dist/shared/hash.js +69 -0
- package/dist/shared/hash.js.map +1 -0
- package/dist/shared/index.d.ts +6 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +6 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/lock.d.ts +70 -0
- package/dist/shared/lock.d.ts.map +1 -0
- package/dist/shared/lock.js +207 -0
- package/dist/shared/lock.js.map +1 -0
- package/dist/shared/logger.d.ts +38 -0
- package/dist/shared/logger.d.ts.map +1 -0
- package/dist/shared/logger.js +74 -0
- package/dist/shared/logger.js.map +1 -0
- package/dist/shared/serialization.d.ts +23 -0
- package/dist/shared/serialization.d.ts.map +1 -0
- package/dist/shared/serialization.js +63 -0
- package/dist/shared/serialization.js.map +1 -0
- package/package.json +82 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protocol Rate Limiter
|
|
3
|
+
* Prevents abuse by limiting requests per client
|
|
4
|
+
*/
|
|
5
|
+
const DEFAULT_CONFIG = {
|
|
6
|
+
windowMs: 60_000,
|
|
7
|
+
maxRequests: 1000,
|
|
8
|
+
cleanupIntervalMs: 60_000,
|
|
9
|
+
};
|
|
10
|
+
/** Rate limiter for protocol-level request limiting */
|
|
11
|
+
export class ProtocolRateLimiter {
|
|
12
|
+
requests = new Map();
|
|
13
|
+
config;
|
|
14
|
+
cleanupInterval = null;
|
|
15
|
+
constructor(config = {}) {
|
|
16
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
17
|
+
this.startCleanup();
|
|
18
|
+
}
|
|
19
|
+
/** Check if a request from clientId is allowed */
|
|
20
|
+
isAllowed(clientId) {
|
|
21
|
+
const now = Date.now();
|
|
22
|
+
const timestamps = this.requests.get(clientId) ?? [];
|
|
23
|
+
// Remove timestamps outside the window
|
|
24
|
+
const valid = timestamps.filter((t) => now - t < this.config.windowMs);
|
|
25
|
+
if (valid.length >= this.config.maxRequests) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
valid.push(now);
|
|
29
|
+
this.requests.set(clientId, valid);
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
/** Get remaining requests for a client */
|
|
33
|
+
getRemaining(clientId) {
|
|
34
|
+
const now = Date.now();
|
|
35
|
+
const timestamps = this.requests.get(clientId) ?? [];
|
|
36
|
+
const valid = timestamps.filter((t) => now - t < this.config.windowMs);
|
|
37
|
+
return Math.max(0, this.config.maxRequests - valid.length);
|
|
38
|
+
}
|
|
39
|
+
/** Remove a client from tracking */
|
|
40
|
+
removeClient(clientId) {
|
|
41
|
+
this.requests.delete(clientId);
|
|
42
|
+
}
|
|
43
|
+
/** Start cleanup interval */
|
|
44
|
+
startCleanup() {
|
|
45
|
+
if (this.config.cleanupIntervalMs) {
|
|
46
|
+
this.cleanupInterval = setInterval(() => {
|
|
47
|
+
this.cleanup();
|
|
48
|
+
}, this.config.cleanupIntervalMs);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Clean up old entries */
|
|
52
|
+
cleanup() {
|
|
53
|
+
const now = Date.now();
|
|
54
|
+
for (const [clientId, timestamps] of this.requests) {
|
|
55
|
+
const valid = timestamps.filter((t) => now - t < this.config.windowMs);
|
|
56
|
+
if (valid.length === 0) {
|
|
57
|
+
this.requests.delete(clientId);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
this.requests.set(clientId, valid);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Stop the rate limiter */
|
|
65
|
+
stop() {
|
|
66
|
+
if (this.cleanupInterval) {
|
|
67
|
+
clearInterval(this.cleanupInterval);
|
|
68
|
+
this.cleanupInterval = null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Global rate limiter instance */
|
|
73
|
+
let globalRateLimiter = null;
|
|
74
|
+
/** Get or create global rate limiter */
|
|
75
|
+
export function getRateLimiter(config) {
|
|
76
|
+
globalRateLimiter ??= new ProtocolRateLimiter(config);
|
|
77
|
+
return globalRateLimiter;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=rateLimiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rateLimiter.js","sourceRoot":"","sources":["../../../src/infrastructure/server/rateLimiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TCP Server
|
|
3
|
+
* Handles TCP connections with JSON line protocol
|
|
4
|
+
*/
|
|
5
|
+
import type { Socket } from 'bun';
|
|
6
|
+
import type { QueueManager } from '../../application/queueManager';
|
|
7
|
+
import { type HandlerContext } from './handler';
|
|
8
|
+
import { LineBuffer, type ConnectionState } from './protocol';
|
|
9
|
+
/** TCP Server configuration */
|
|
10
|
+
export interface TcpServerConfig {
|
|
11
|
+
port: number;
|
|
12
|
+
hostname?: string;
|
|
13
|
+
authTokens?: string[];
|
|
14
|
+
}
|
|
15
|
+
/** Per-connection data */
|
|
16
|
+
interface ConnectionData {
|
|
17
|
+
state: ConnectionState;
|
|
18
|
+
buffer: LineBuffer;
|
|
19
|
+
ctx: HandlerContext;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create and start TCP server
|
|
23
|
+
*/
|
|
24
|
+
export declare function createTcpServer(queueManager: QueueManager, config: TcpServerConfig): {
|
|
25
|
+
server: Bun.TCPSocketListener<ConnectionData>;
|
|
26
|
+
connections: Map<string, Socket<ConnectionData>>;
|
|
27
|
+
/** Get connection count */
|
|
28
|
+
getConnectionCount(): number;
|
|
29
|
+
/** Broadcast to all connections */
|
|
30
|
+
broadcast(message: string): void;
|
|
31
|
+
/** Stop the server */
|
|
32
|
+
stop(): void;
|
|
33
|
+
};
|
|
34
|
+
export type TcpServer = ReturnType<typeof createTcpServer>;
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=tcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tcp.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/server/tcp.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAClC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,EACL,UAAU,EAKV,KAAK,eAAe,EACrB,MAAM,YAAY,CAAC;AAKpB,+BAA+B;AAC/B,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,0BAA0B;AAC1B,UAAU,cAAc;IACtB,KAAK,EAAE,eAAe,CAAC;IACvB,MAAM,EAAE,UAAU,CAAC;IACnB,GAAG,EAAE,cAAc,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,eAAe;;;IAgF/E,2BAA2B;0BACL,MAAM;IAI5B,mCAAmC;uBAChB,MAAM,GAAG,IAAI;IAMhC,sBAAsB;YACd,IAAI;EASf;AAED,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TCP Server
|
|
3
|
+
* Handles TCP connections with JSON line protocol
|
|
4
|
+
*/
|
|
5
|
+
import { handleCommand } from './handler';
|
|
6
|
+
import { LineBuffer, parseCommand, serializeResponse, errorResponse, createConnectionState, } from './protocol';
|
|
7
|
+
import { uuid } from '../../shared/hash';
|
|
8
|
+
import { tcpLog } from '../../shared/logger';
|
|
9
|
+
import { getRateLimiter } from './rateLimiter';
|
|
10
|
+
/**
|
|
11
|
+
* Create and start TCP server
|
|
12
|
+
*/
|
|
13
|
+
export function createTcpServer(queueManager, config) {
|
|
14
|
+
const authTokens = new Set(config.authTokens ?? []);
|
|
15
|
+
const connections = new Map();
|
|
16
|
+
const server = Bun.listen({
|
|
17
|
+
hostname: config.hostname ?? '0.0.0.0',
|
|
18
|
+
port: config.port,
|
|
19
|
+
socket: {
|
|
20
|
+
open(socket) {
|
|
21
|
+
const clientId = uuid();
|
|
22
|
+
const state = createConnectionState(clientId);
|
|
23
|
+
const ctx = {
|
|
24
|
+
queueManager,
|
|
25
|
+
authTokens,
|
|
26
|
+
authenticated: authTokens.size === 0, // Auto-auth if no tokens
|
|
27
|
+
};
|
|
28
|
+
socket.data = {
|
|
29
|
+
state,
|
|
30
|
+
buffer: new LineBuffer(),
|
|
31
|
+
ctx,
|
|
32
|
+
};
|
|
33
|
+
connections.set(clientId, socket);
|
|
34
|
+
tcpLog.info('Client connected', { clientId });
|
|
35
|
+
},
|
|
36
|
+
async data(socket, data) {
|
|
37
|
+
const { buffer, ctx, state } = socket.data;
|
|
38
|
+
const rateLimiter = getRateLimiter();
|
|
39
|
+
// Check rate limit
|
|
40
|
+
if (!rateLimiter.isAllowed(state.clientId)) {
|
|
41
|
+
socket.write(errorResponse('Rate limit exceeded') + '\n');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const text = new TextDecoder().decode(data);
|
|
45
|
+
const lines = buffer.addData(text);
|
|
46
|
+
for (const line of lines) {
|
|
47
|
+
const cmd = parseCommand(line);
|
|
48
|
+
if (!cmd) {
|
|
49
|
+
socket.write(errorResponse('Invalid command') + '\n');
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const response = await handleCommand(cmd, ctx);
|
|
54
|
+
socket.write(serializeResponse(response) + '\n');
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
58
|
+
socket.write(errorResponse(message, cmd.reqId) + '\n');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
close(socket) {
|
|
63
|
+
const clientId = socket.data.state.clientId;
|
|
64
|
+
connections.delete(clientId);
|
|
65
|
+
getRateLimiter().removeClient(clientId);
|
|
66
|
+
tcpLog.info('Client disconnected', { clientId });
|
|
67
|
+
},
|
|
68
|
+
error(_socket, error) {
|
|
69
|
+
tcpLog.error('Connection error', { error: error.message });
|
|
70
|
+
},
|
|
71
|
+
drain(_socket) {
|
|
72
|
+
// Called when socket is ready for more writes after backpressure
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
tcpLog.info('Server listening', { host: config.hostname ?? '0.0.0.0', port: config.port });
|
|
77
|
+
return {
|
|
78
|
+
server,
|
|
79
|
+
connections,
|
|
80
|
+
/** Get connection count */
|
|
81
|
+
getConnectionCount() {
|
|
82
|
+
return connections.size;
|
|
83
|
+
},
|
|
84
|
+
/** Broadcast to all connections */
|
|
85
|
+
broadcast(message) {
|
|
86
|
+
for (const socket of connections.values()) {
|
|
87
|
+
socket.write(message + '\n');
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
/** Stop the server */
|
|
91
|
+
stop() {
|
|
92
|
+
server.stop();
|
|
93
|
+
for (const socket of connections.values()) {
|
|
94
|
+
socket.end();
|
|
95
|
+
}
|
|
96
|
+
connections.clear();
|
|
97
|
+
tcpLog.info('Server stopped');
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=tcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tcp.js","sourceRoot":"","sources":["../../../src/infrastructure/server/tcp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server types
|
|
3
|
+
*/
|
|
4
|
+
import type { QueueManager } from '../../application/queueManager';
|
|
5
|
+
/** Handler context passed to all command handlers */
|
|
6
|
+
export interface HandlerContext {
|
|
7
|
+
queueManager: QueueManager;
|
|
8
|
+
authTokens: Set<string>;
|
|
9
|
+
authenticated: boolean;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/server/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEnE,qDAAqD;AACrD,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,YAAY,CAAC;IAC3B,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;CACxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/infrastructure/server/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bunQ - High-performance job queue server for Bun
|
|
3
|
+
* Main entry point
|
|
4
|
+
*/
|
|
5
|
+
import { QueueManager } from './application/queueManager';
|
|
6
|
+
import { createTcpServer } from './infrastructure/server/tcp';
|
|
7
|
+
import { createHttpServer } from './infrastructure/server/http';
|
|
8
|
+
import { Logger, serverLog, statsLog } from './shared/logger';
|
|
9
|
+
/** Load configuration from environment variables */
|
|
10
|
+
function loadConfig() {
|
|
11
|
+
return {
|
|
12
|
+
tcpPort: parseInt(process.env.TCP_PORT ?? '6789'),
|
|
13
|
+
httpPort: parseInt(process.env.HTTP_PORT ?? '6790'),
|
|
14
|
+
hostname: process.env.HOST ?? '0.0.0.0',
|
|
15
|
+
authTokens: process.env.AUTH_TOKENS?.split(',').filter(Boolean) ?? [],
|
|
16
|
+
dataPath: process.env.DATA_PATH ?? process.env.SQLITE_PATH,
|
|
17
|
+
corsOrigins: process.env.CORS_ALLOW_ORIGIN?.split(',').filter(Boolean) ?? ['*'],
|
|
18
|
+
requireAuthForMetrics: process.env.METRICS_AUTH === 'true',
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/** Print startup banner */
|
|
22
|
+
function printBanner(config) {
|
|
23
|
+
console.log(`
|
|
24
|
+
╔═══════════════════════════════════════════════════════════╗
|
|
25
|
+
║ ║
|
|
26
|
+
║ 🐰 bunQ ║
|
|
27
|
+
║ High-performance job queue server for Bun ║
|
|
28
|
+
║ ║
|
|
29
|
+
╠═══════════════════════════════════════════════════════════╣
|
|
30
|
+
║ ║
|
|
31
|
+
║ TCP Port: ${String(config.tcpPort).padEnd(44)}║
|
|
32
|
+
║ HTTP Port: ${String(config.httpPort).padEnd(44)}║
|
|
33
|
+
║ Host: ${config.hostname.padEnd(44)}║
|
|
34
|
+
║ Auth: ${(config.authTokens.length > 0 ? 'Enabled' : 'Disabled').padEnd(44)}║
|
|
35
|
+
║ Storage: ${(config.dataPath ?? 'In-memory').padEnd(44)}║
|
|
36
|
+
║ ║
|
|
37
|
+
╚═══════════════════════════════════════════════════════════╝
|
|
38
|
+
`);
|
|
39
|
+
}
|
|
40
|
+
/** Main function */
|
|
41
|
+
function main() {
|
|
42
|
+
const config = loadConfig();
|
|
43
|
+
printBanner(config);
|
|
44
|
+
// Create queue manager
|
|
45
|
+
const queueManager = new QueueManager({
|
|
46
|
+
dataPath: config.dataPath,
|
|
47
|
+
});
|
|
48
|
+
// Start TCP server
|
|
49
|
+
const tcpServer = createTcpServer(queueManager, {
|
|
50
|
+
port: config.tcpPort,
|
|
51
|
+
hostname: config.hostname,
|
|
52
|
+
authTokens: config.authTokens,
|
|
53
|
+
});
|
|
54
|
+
// Start HTTP server
|
|
55
|
+
const httpServer = createHttpServer(queueManager, {
|
|
56
|
+
port: config.httpPort,
|
|
57
|
+
hostname: config.hostname,
|
|
58
|
+
authTokens: config.authTokens,
|
|
59
|
+
corsOrigins: config.corsOrigins,
|
|
60
|
+
requireAuthForMetrics: config.requireAuthForMetrics,
|
|
61
|
+
});
|
|
62
|
+
// Graceful shutdown
|
|
63
|
+
const shutdown = async (signal) => {
|
|
64
|
+
serverLog.info(`Received ${signal}, shutting down...`);
|
|
65
|
+
tcpServer.stop();
|
|
66
|
+
httpServer.stop();
|
|
67
|
+
const shutdownTimeout = 30_000;
|
|
68
|
+
const start = Date.now();
|
|
69
|
+
while (Date.now() - start < shutdownTimeout) {
|
|
70
|
+
const stats = queueManager.getStats();
|
|
71
|
+
if (stats.active === 0)
|
|
72
|
+
break;
|
|
73
|
+
serverLog.info(`Waiting for ${stats.active} active jobs...`);
|
|
74
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
75
|
+
}
|
|
76
|
+
queueManager.shutdown();
|
|
77
|
+
serverLog.info('Shutdown complete');
|
|
78
|
+
process.exit(0);
|
|
79
|
+
};
|
|
80
|
+
process.on('SIGINT', () => void shutdown('SIGINT'));
|
|
81
|
+
process.on('SIGTERM', () => void shutdown('SIGTERM'));
|
|
82
|
+
// Print stats periodically
|
|
83
|
+
setInterval(() => {
|
|
84
|
+
const stats = queueManager.getStats();
|
|
85
|
+
const workerStats = queueManager.workerManager.getStats();
|
|
86
|
+
statsLog.info('Queue statistics', {
|
|
87
|
+
waiting: stats.waiting,
|
|
88
|
+
active: stats.active,
|
|
89
|
+
delayed: stats.delayed,
|
|
90
|
+
completed: stats.completed,
|
|
91
|
+
dlq: stats.dlq,
|
|
92
|
+
tcp: tcpServer.getConnectionCount(),
|
|
93
|
+
ws: httpServer.getWsClientCount(),
|
|
94
|
+
sse: httpServer.getSseClientCount(),
|
|
95
|
+
workers: `${workerStats.active}/${workerStats.total}`,
|
|
96
|
+
});
|
|
97
|
+
}, 30_000);
|
|
98
|
+
}
|
|
99
|
+
// Enable JSON logging if requested
|
|
100
|
+
if (process.env.LOG_FORMAT === 'json') {
|
|
101
|
+
Logger.enableJsonMode();
|
|
102
|
+
}
|
|
103
|
+
// Run
|
|
104
|
+
try {
|
|
105
|
+
main();
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
serverLog.error('Fatal error', { error: String(err) });
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fast hash functions
|
|
3
|
+
* FNV-1a implementation for consistent hashing
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* FNV-1a hash function (32-bit)
|
|
7
|
+
* Fast, good distribution for strings
|
|
8
|
+
*/
|
|
9
|
+
export declare function fnv1a(str: string): number;
|
|
10
|
+
/**
|
|
11
|
+
* Calculate shard index from string
|
|
12
|
+
* Uses 32 shards (& 0x1F mask)
|
|
13
|
+
*/
|
|
14
|
+
export declare const SHARD_COUNT = 32;
|
|
15
|
+
export declare const SHARD_MASK = 31;
|
|
16
|
+
export declare function shardIndex(key: string): number;
|
|
17
|
+
/**
|
|
18
|
+
* Calculate processing shard index from job ID
|
|
19
|
+
*/
|
|
20
|
+
export declare function processingShardIndex(jobId: bigint): number;
|
|
21
|
+
/**
|
|
22
|
+
* Generate a simple UUID v4
|
|
23
|
+
*/
|
|
24
|
+
export declare function uuid(): string;
|
|
25
|
+
/**
|
|
26
|
+
* Constant-time string comparison
|
|
27
|
+
* Prevents timing attacks on token validation
|
|
28
|
+
*/
|
|
29
|
+
export declare function constantTimeEqual(a: string, b: string): boolean;
|
|
30
|
+
//# sourceMappingURL=hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/shared/hash.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH;;;GAGG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAOzC;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW,KAAK,CAAC;AAC9B,eAAO,MAAM,UAAU,KAAO,CAAC;AAE/B,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;GAEG;AACH,wBAAgB,IAAI,IAAI,MAAM,CAe7B;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAO/D"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fast hash functions
|
|
3
|
+
* FNV-1a implementation for consistent hashing
|
|
4
|
+
*/
|
|
5
|
+
const FNV_PRIME = 0x01000193;
|
|
6
|
+
const FNV_OFFSET = 0x811c9dc5;
|
|
7
|
+
/**
|
|
8
|
+
* FNV-1a hash function (32-bit)
|
|
9
|
+
* Fast, good distribution for strings
|
|
10
|
+
*/
|
|
11
|
+
export function fnv1a(str) {
|
|
12
|
+
let hash = FNV_OFFSET;
|
|
13
|
+
for (let i = 0; i < str.length; i++) {
|
|
14
|
+
hash ^= str.charCodeAt(i);
|
|
15
|
+
hash = Math.imul(hash, FNV_PRIME);
|
|
16
|
+
}
|
|
17
|
+
return hash >>> 0; // Convert to unsigned 32-bit
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Calculate shard index from string
|
|
21
|
+
* Uses 32 shards (& 0x1F mask)
|
|
22
|
+
*/
|
|
23
|
+
export const SHARD_COUNT = 32;
|
|
24
|
+
export const SHARD_MASK = 0x1f;
|
|
25
|
+
export function shardIndex(key) {
|
|
26
|
+
return fnv1a(key) & SHARD_MASK;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Calculate processing shard index from job ID
|
|
30
|
+
*/
|
|
31
|
+
export function processingShardIndex(jobId) {
|
|
32
|
+
return Number(jobId & BigInt(SHARD_MASK));
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Generate a simple UUID v4
|
|
36
|
+
*/
|
|
37
|
+
export function uuid() {
|
|
38
|
+
const hex = '0123456789abcdef';
|
|
39
|
+
let result = '';
|
|
40
|
+
for (let i = 0; i < 36; i++) {
|
|
41
|
+
if (i === 8 || i === 13 || i === 18 || i === 23) {
|
|
42
|
+
result += '-';
|
|
43
|
+
}
|
|
44
|
+
else if (i === 14) {
|
|
45
|
+
result += '4';
|
|
46
|
+
}
|
|
47
|
+
else if (i === 19) {
|
|
48
|
+
result += hex[(Math.random() * 4) | 8];
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
result += hex[(Math.random() * 16) | 0];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Constant-time string comparison
|
|
58
|
+
* Prevents timing attacks on token validation
|
|
59
|
+
*/
|
|
60
|
+
export function constantTimeEqual(a, b) {
|
|
61
|
+
if (a.length !== b.length)
|
|
62
|
+
return false;
|
|
63
|
+
let result = 0;
|
|
64
|
+
for (let i = 0; i < a.length; i++) {
|
|
65
|
+
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
66
|
+
}
|
|
67
|
+
return result === 0;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=hash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/shared/hash.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,SAAS,GAAG,UAAU,CAAC;AAC7B,MAAM,UAAU,GAAG,UAAU,CAAC;AAE9B;;;GAGG;AACH,MAAM,UAAU,KAAK,CAAC,GAAW;IAC/B,IAAI,IAAI,GAAG,UAAU,CAAC;IACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,6BAA6B;AAClD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAC9B,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC;AAE/B,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO,KAAK,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC;AACjC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,OAAO,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;AAC5C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,IAAI;IAClB,MAAM,GAAG,GAAG,kBAAkB,CAAC;IAC/B,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;YAChD,MAAM,IAAI,GAAG,CAAC;QAChB,CAAC;aAAM,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;YACpB,MAAM,IAAI,GAAG,CAAC;QAChB,CAAC;aAAM,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;YACpB,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACzC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,CAAS,EAAE,CAAS;IACpD,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACxC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,MAAM,IAAI,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,MAAM,KAAK,CAAC,CAAC;AACtB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shared/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/shared/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async Lock implementation
|
|
3
|
+
* Provides read-write locks with timeout support
|
|
4
|
+
*/
|
|
5
|
+
/** Lock acquisition result */
|
|
6
|
+
export interface LockGuard {
|
|
7
|
+
release(): void;
|
|
8
|
+
}
|
|
9
|
+
/** Lock timeout error */
|
|
10
|
+
export declare class LockTimeoutError extends Error {
|
|
11
|
+
constructor(message?: string);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Simple async mutex lock
|
|
15
|
+
* FIFO ordering for fairness
|
|
16
|
+
*/
|
|
17
|
+
export declare class AsyncLock {
|
|
18
|
+
private locked;
|
|
19
|
+
private readonly queue;
|
|
20
|
+
/**
|
|
21
|
+
* Acquire the lock
|
|
22
|
+
* @param timeoutMs - Maximum time to wait (default: 5000ms)
|
|
23
|
+
*/
|
|
24
|
+
acquire(timeoutMs?: number): Promise<LockGuard>;
|
|
25
|
+
/** Check if lock is held */
|
|
26
|
+
isLocked(): boolean;
|
|
27
|
+
/** Get queue length */
|
|
28
|
+
getQueueLength(): number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Read-Write Lock
|
|
32
|
+
* Multiple readers OR single writer
|
|
33
|
+
* Writers have priority to prevent starvation
|
|
34
|
+
*/
|
|
35
|
+
export declare class RWLock {
|
|
36
|
+
private readers;
|
|
37
|
+
private writer;
|
|
38
|
+
private writerWaiting;
|
|
39
|
+
private readonly readerQueue;
|
|
40
|
+
private readonly writerQueue;
|
|
41
|
+
/**
|
|
42
|
+
* Acquire read lock
|
|
43
|
+
* Multiple readers can hold simultaneously
|
|
44
|
+
*/
|
|
45
|
+
acquireRead(timeoutMs?: number): Promise<LockGuard>;
|
|
46
|
+
/**
|
|
47
|
+
* Acquire write lock
|
|
48
|
+
* Exclusive access, no readers or other writers
|
|
49
|
+
*/
|
|
50
|
+
acquireWrite(timeoutMs?: number): Promise<LockGuard>;
|
|
51
|
+
/** Get current state */
|
|
52
|
+
getState(): {
|
|
53
|
+
readers: number;
|
|
54
|
+
writer: boolean;
|
|
55
|
+
writerWaiting: number;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Execute with lock, ensuring release on error
|
|
60
|
+
*/
|
|
61
|
+
export declare function withLock<T>(lock: AsyncLock, fn: () => T | Promise<T>, timeoutMs?: number): Promise<T>;
|
|
62
|
+
/**
|
|
63
|
+
* Execute with read lock
|
|
64
|
+
*/
|
|
65
|
+
export declare function withReadLock<T>(lock: RWLock, fn: () => T | Promise<T>, timeoutMs?: number): Promise<T>;
|
|
66
|
+
/**
|
|
67
|
+
* Execute with write lock
|
|
68
|
+
*/
|
|
69
|
+
export declare function withWriteLock<T>(lock: RWLock, fn: () => T | Promise<T>, timeoutMs?: number): Promise<T>;
|
|
70
|
+
//# sourceMappingURL=lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../src/shared/lock.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,8BAA8B;AAC9B,MAAM,WAAW,SAAS;IACxB,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,yBAAyB;AACzB,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,GAAE,MAAqC;CAI3D;AAED;;;GAGG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;IAE/C;;;OAGG;IACG,OAAO,CAAC,SAAS,GAAE,MAAa,GAAG,OAAO,CAAC,SAAS,CAAC;IAkC3D,4BAA4B;IAC5B,QAAQ,IAAI,OAAO;IAInB,uBAAuB;IACvB,cAAc,IAAI,MAAM;CAGzB;AAED;;;;GAIG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyB;IACrD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyB;IAErD;;;OAGG;IACG,WAAW,CAAC,SAAS,GAAE,MAAa,GAAG,OAAO,CAAC,SAAS,CAAC;IAqC/D;;;OAGG;IACG,YAAY,CAAC,SAAS,GAAE,MAAa,GAAG,OAAO,CAAC,SAAS,CAAC;IAkDhE,wBAAwB;IACxB,QAAQ,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE;CAOxE;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAC9B,IAAI,EAAE,SAAS,EACf,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EACxB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,CAAC,CAAC,CAOZ;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EACxB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,CAAC,CAAC,CAOZ;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,CAAC,EACnC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EACxB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,CAAC,CAAC,CAOZ"}
|