@lota-sdk/core 0.4.8 → 0.4.10
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 +11 -12
- package/src/ai/embedding-cache.ts +96 -22
- package/src/ai-gateway/ai-gateway.ts +766 -223
- package/src/config/agent-defaults.ts +189 -75
- package/src/config/agent-types.ts +54 -4
- package/src/config/background-processing.ts +1 -1
- package/src/config/constants.ts +8 -2
- package/src/config/index.ts +0 -1
- package/src/config/logger.ts +299 -19
- package/src/config/thread-defaults.ts +40 -20
- package/src/create-runtime.ts +200 -449
- package/src/db/base.service.ts +52 -28
- package/src/db/cursor-pagination.ts +71 -30
- package/src/db/memory-query-builder.ts +2 -1
- package/src/db/memory-store.helpers.ts +4 -7
- package/src/db/memory-store.ts +868 -601
- package/src/db/memory.ts +396 -280
- package/src/db/record-id.ts +32 -10
- package/src/db/schema-fingerprint.ts +30 -12
- package/src/db/service-normalization.ts +288 -0
- package/src/db/service.ts +912 -779
- package/src/db/startup.ts +153 -68
- package/src/db/transaction-conflict.ts +15 -0
- package/src/effect/awaitable-effect.ts +96 -0
- package/src/effect/errors.ts +121 -0
- package/src/effect/helpers.ts +123 -0
- package/src/effect/index.ts +24 -0
- package/src/effect/layers.ts +238 -0
- package/src/effect/runtime-ref.ts +25 -0
- package/src/effect/runtime.ts +46 -0
- package/src/effect/services.ts +61 -0
- package/src/effect/zod.ts +43 -0
- package/src/embeddings/provider.ts +128 -83
- package/src/index.ts +48 -1
- package/src/openrouter/direct-provider.ts +11 -35
- package/src/queues/autonomous-job.queue.ts +117 -73
- package/src/queues/context-compaction.queue.ts +50 -17
- package/src/queues/delayed-node-promotion.queue.ts +46 -17
- package/src/queues/document-processor.queue.ts +52 -77
- package/src/queues/memory-consolidation.queue.ts +47 -32
- package/src/queues/organization-learning.queue.ts +26 -4
- package/src/queues/plan-agent-heartbeat.queue.ts +71 -24
- package/src/queues/plan-scheduler.queue.ts +97 -33
- package/src/queues/post-chat-memory.queue.ts +56 -26
- package/src/queues/queue-factory.ts +227 -59
- package/src/queues/standalone-worker.ts +39 -0
- package/src/queues/title-generation.queue.ts +45 -11
- package/src/redis/connection.ts +182 -113
- package/src/redis/index.ts +6 -8
- package/src/redis/org-memory-lock.ts +60 -27
- package/src/redis/redis-lease-lock.ts +200 -121
- package/src/redis/runtime-connection.ts +20 -0
- package/src/redis/stream-context.ts +92 -46
- package/src/runtime/agent-identity-overrides.ts +2 -2
- package/src/runtime/agent-runtime-policy.ts +5 -2
- package/src/runtime/agent-stream-helpers.ts +24 -9
- package/src/runtime/chat-run-orchestration.ts +102 -19
- package/src/runtime/chat-run-registry.ts +36 -2
- package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
- package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +161 -94
- package/src/runtime/domain-layer.ts +192 -0
- package/src/runtime/execution-plan-visibility.ts +2 -2
- package/src/runtime/execution-plan.ts +42 -15
- package/src/runtime/graph-designer.ts +16 -4
- package/src/runtime/helper-model.ts +139 -48
- package/src/runtime/index.ts +7 -8
- package/src/runtime/indexed-repositories-policy.ts +3 -3
- package/src/runtime/{memory-block.ts → memory/memory-block.ts} +50 -36
- package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
- package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +54 -67
- package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
- package/src/runtime/memory/memory-scope.ts +53 -0
- package/src/runtime/plugin-resolution.ts +124 -25
- package/src/runtime/plugin-types.ts +9 -1
- package/src/runtime/post-turn-side-effects.ts +177 -130
- package/src/runtime/retrieval-adapters.ts +40 -6
- package/src/runtime/runtime-accessors.ts +92 -0
- package/src/runtime/runtime-config.ts +150 -61
- package/src/runtime/runtime-extensions.ts +23 -25
- package/src/runtime/runtime-lifecycle.ts +124 -0
- package/src/runtime/runtime-services.ts +386 -0
- package/src/runtime/runtime-token.ts +47 -0
- package/src/runtime/social-chat/social-chat-agent-runner.ts +159 -0
- package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +51 -20
- package/src/runtime/social-chat/social-chat.ts +630 -0
- package/src/runtime/specialist-runner.ts +36 -10
- package/src/runtime/team-consultation/team-consultation-orchestrator.ts +433 -0
- package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
- package/src/runtime/thread-chat-helpers.ts +2 -2
- package/src/runtime/thread-plan-turn.ts +2 -1
- package/src/runtime/thread-turn-context.ts +183 -111
- package/src/runtime/turn-lifecycle.ts +93 -27
- package/src/services/agent-activity.service.ts +287 -203
- package/src/services/agent-executor.service.ts +253 -149
- package/src/services/artifact.service.ts +231 -149
- package/src/services/attachment.service.ts +171 -115
- package/src/services/autonomous-job.service.ts +890 -491
- package/src/services/background-work.service.ts +54 -0
- package/src/services/chat-run-registry.service.ts +13 -1
- package/src/services/context-compaction.service.ts +136 -86
- package/src/services/document-chunk.service.ts +151 -88
- package/src/services/execution-plan/execution-plan-approval.ts +26 -0
- package/src/services/execution-plan/execution-plan-context.ts +29 -0
- package/src/services/execution-plan/execution-plan-graph.ts +278 -0
- package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
- package/src/services/execution-plan/execution-plan-spec.ts +75 -0
- package/src/services/execution-plan/execution-plan.service.ts +1041 -0
- package/src/services/feedback-loop.service.ts +132 -76
- package/src/services/global-orchestrator.service.ts +101 -168
- package/src/services/graph-full-routing.ts +193 -0
- package/src/services/index.ts +19 -21
- package/src/services/institutional-memory.service.ts +213 -125
- package/src/services/learned-skill.service.ts +368 -260
- package/src/services/memory/memory-conversation.ts +95 -0
- package/src/services/memory/memory-errors.ts +27 -0
- package/src/services/memory/memory-org-memory.ts +50 -0
- package/src/services/memory/memory-preseeded.ts +86 -0
- package/src/services/memory/memory-rerank.ts +297 -0
- package/src/services/{memory-utils.ts → memory/memory-utils.ts} +6 -5
- package/src/services/memory/memory.service.ts +674 -0
- package/src/services/memory/rerank.service.ts +201 -0
- package/src/services/monitoring-window.service.ts +92 -70
- package/src/services/mutating-approval.service.ts +62 -53
- package/src/services/node-workspace.service.ts +141 -98
- package/src/services/notification.service.ts +29 -16
- package/src/services/organization-member.service.ts +120 -66
- package/src/services/organization.service.ts +153 -77
- package/src/services/ownership-dispatcher.service.ts +456 -263
- package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
- package/src/services/plan/plan-agent-query.service.ts +322 -0
- package/src/services/{plan-approval.service.ts → plan/plan-approval.service.ts} +45 -22
- package/src/services/plan/plan-artifact.service.ts +60 -0
- package/src/services/plan/plan-builder.service.ts +76 -0
- package/src/services/plan/plan-checkpoint.service.ts +103 -0
- package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
- package/src/services/plan/plan-completion-side-effects.ts +169 -0
- package/src/services/plan/plan-coordination.service.ts +181 -0
- package/src/services/plan/plan-cycle.service.ts +405 -0
- package/src/services/plan/plan-deadline.service.ts +533 -0
- package/src/services/plan/plan-event-delivery.service.ts +266 -0
- package/src/services/plan/plan-executor-context.ts +35 -0
- package/src/services/plan/plan-executor-graph.ts +522 -0
- package/src/services/plan/plan-executor-helpers.ts +307 -0
- package/src/services/plan/plan-executor-persistence.ts +209 -0
- package/src/services/plan/plan-executor.service.ts +1737 -0
- package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
- package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
- package/src/services/plan/plan-run-serialization.ts +15 -0
- package/src/services/plan/plan-run.service.ts +637 -0
- package/src/services/plan/plan-scheduler.service.ts +379 -0
- package/src/services/plan/plan-template.service.ts +224 -0
- package/src/services/plan/plan-transaction-events.ts +36 -0
- package/src/services/plan/plan-validator.service.ts +907 -0
- package/src/services/plan/plan-workspace.service.ts +131 -0
- package/src/services/plugin-executor.service.ts +102 -68
- package/src/services/quality-metrics.service.ts +112 -94
- package/src/services/queue-job.service.ts +288 -231
- package/src/services/recent-activity-title.service.ts +73 -36
- package/src/services/recent-activity.service.ts +274 -259
- package/src/services/skill-resolver.service.ts +38 -12
- package/src/services/social-chat-history.service.ts +190 -122
- package/src/services/system-executor.service.ts +96 -61
- package/src/services/thread/thread-active-run.ts +203 -0
- package/src/services/thread/thread-bootstrap.ts +385 -0
- package/src/services/thread/thread-listing.ts +199 -0
- package/src/services/thread/thread-memory-block.ts +130 -0
- package/src/services/thread/thread-message.service.ts +379 -0
- package/src/services/thread/thread-record-store.ts +155 -0
- package/src/services/thread/thread-title.service.ts +74 -0
- package/src/services/thread/thread-turn-execution.ts +280 -0
- package/src/services/thread/thread-turn-message-context.ts +73 -0
- package/src/services/thread/thread-turn-preparation.service.ts +1148 -0
- package/src/services/thread/thread-turn-streaming.ts +403 -0
- package/src/services/thread/thread-turn-tracing.ts +35 -0
- package/src/services/thread/thread-turn.ts +376 -0
- package/src/services/thread/thread.service.ts +344 -0
- package/src/services/user.service.ts +82 -32
- package/src/services/write-intent-validator.service.ts +63 -51
- package/src/storage/attachment-parser.ts +69 -27
- package/src/storage/attachment-storage.service.ts +334 -275
- package/src/storage/generated-document-storage.service.ts +66 -34
- package/src/system-agents/agent-result.ts +3 -1
- package/src/system-agents/context-compaction.agent.ts +3 -3
- package/src/system-agents/delegated-agent-factory.ts +159 -90
- package/src/system-agents/helper-agent-options.ts +1 -1
- package/src/system-agents/memory-reranker.agent.ts +3 -3
- package/src/system-agents/memory.agent.ts +3 -3
- package/src/system-agents/recent-activity-title-refiner.agent.ts +3 -3
- package/src/system-agents/regular-chat-memory-digest.agent.ts +3 -3
- package/src/system-agents/skill-extractor.agent.ts +3 -3
- package/src/system-agents/skill-manager.agent.ts +3 -3
- package/src/system-agents/thread-router.agent.ts +157 -113
- package/src/system-agents/title-generator.agent.ts +3 -3
- package/src/tools/execution-plan.tool.ts +241 -171
- package/src/tools/fetch-webpage.tool.ts +29 -18
- package/src/tools/firecrawl-client.ts +26 -6
- package/src/tools/index.ts +1 -0
- package/src/tools/memory-block.tool.ts +14 -6
- package/src/tools/plan-approval.tool.ts +57 -47
- package/src/tools/read-file-parts.tool.ts +44 -33
- package/src/tools/remember-memory.tool.ts +65 -45
- package/src/tools/search-web.tool.ts +33 -22
- package/src/tools/search.tool.ts +41 -29
- package/src/tools/team-think.tool.ts +125 -84
- package/src/tools/user-questions.tool.ts +4 -3
- package/src/tools/web-tool-shared.ts +6 -0
- package/src/utils/async.ts +25 -22
- package/src/utils/crypto.ts +21 -0
- package/src/utils/date-time.ts +40 -1
- package/src/utils/errors.ts +111 -20
- package/src/utils/hono-error-handler.ts +24 -39
- package/src/utils/index.ts +2 -1
- package/src/utils/null-proto-record.ts +41 -0
- package/src/utils/sse-keepalive.ts +124 -21
- package/src/workers/bootstrap.ts +164 -52
- package/src/workers/memory-consolidation.worker.ts +325 -237
- package/src/workers/organization-learning.worker.ts +50 -16
- package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
- package/src/workers/regular-chat-memory-digest.runner.ts +185 -114
- package/src/workers/skill-extraction.runner.ts +176 -93
- package/src/workers/utils/file-section-chunker.ts +8 -10
- package/src/workers/utils/repo-structure-extractor.ts +349 -260
- package/src/workers/utils/repomix-file-sections.ts +2 -2
- package/src/workers/utils/thread-message-query.ts +97 -38
- package/src/workers/worker-utils.ts +74 -31
- package/src/config/debug-logger.ts +0 -47
- package/src/config/search.ts +0 -3
- package/src/redis/connection-accessor.ts +0 -26
- package/src/runtime/agent-types.ts +0 -1
- package/src/runtime/context-compaction-runtime.ts +0 -87
- package/src/runtime/memory-scope.ts +0 -43
- package/src/runtime/social-chat-agent-runner.ts +0 -118
- package/src/runtime/social-chat.ts +0 -516
- package/src/runtime/team-consultation-orchestrator.ts +0 -272
- package/src/services/adaptive-playbook.service.ts +0 -152
- package/src/services/artifact-provenance.service.ts +0 -172
- package/src/services/chat-attachments.service.ts +0 -17
- package/src/services/context-compaction-runtime.singleton.ts +0 -13
- package/src/services/execution-plan.service.ts +0 -1118
- package/src/services/memory.service.ts +0 -914
- package/src/services/plan-agent-heartbeat.service.ts +0 -136
- package/src/services/plan-agent-query.service.ts +0 -267
- package/src/services/plan-artifact.service.ts +0 -50
- package/src/services/plan-builder.service.ts +0 -67
- package/src/services/plan-checkpoint.service.ts +0 -81
- package/src/services/plan-completion-side-effects.ts +0 -80
- package/src/services/plan-coordination.service.ts +0 -157
- package/src/services/plan-cycle.service.ts +0 -284
- package/src/services/plan-deadline.service.ts +0 -430
- package/src/services/plan-event-delivery.service.ts +0 -166
- package/src/services/plan-executor.service.ts +0 -1950
- package/src/services/plan-run.service.ts +0 -515
- package/src/services/plan-scheduler.service.ts +0 -240
- package/src/services/plan-template.service.ts +0 -177
- package/src/services/plan-validator.service.ts +0 -818
- package/src/services/plan-workspace.service.ts +0 -83
- package/src/services/rerank.service.ts +0 -156
- package/src/services/thread-message.service.ts +0 -275
- package/src/services/thread-plan-registry.service.ts +0 -22
- package/src/services/thread-title.service.ts +0 -39
- package/src/services/thread-turn-preparation.service.ts +0 -1147
- package/src/services/thread-turn.ts +0 -172
- package/src/services/thread.service.ts +0 -869
- package/src/utils/env.ts +0 -8
- /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
- /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
- /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
- /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
- /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
- /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
- /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
- /package/src/services/{thread.types.ts → thread/thread.types.ts} +0 -0
|
@@ -5,12 +5,19 @@ import {
|
|
|
5
5
|
recordIdSchema,
|
|
6
6
|
} from '@lota-sdk/shared'
|
|
7
7
|
import type { QueueJobError, QueueJobStatus } from '@lota-sdk/shared'
|
|
8
|
-
import {
|
|
8
|
+
import { Context, Duration, Effect, Layer, Schedule } from 'effect'
|
|
9
|
+
import type { RecordId } from 'surrealdb'
|
|
9
10
|
import { z } from 'zod'
|
|
10
11
|
|
|
11
12
|
import { recordIdToString } from '../db/record-id'
|
|
12
|
-
import {
|
|
13
|
+
import type { SurrealDBService } from '../db/service'
|
|
13
14
|
import { TABLES } from '../db/tables'
|
|
15
|
+
import { isRetriableTransactionConflict } from '../db/transaction-conflict'
|
|
16
|
+
import { BadRequestError, DatabaseError } from '../effect/errors'
|
|
17
|
+
import { makeEffectTryPromiseWithMessage } from '../effect/helpers'
|
|
18
|
+
import { DatabaseServiceTag } from '../effect/services'
|
|
19
|
+
import { createDeterministicRecordId } from '../utils/crypto'
|
|
20
|
+
import { nowDate, unsafeDateFrom } from '../utils/date-time'
|
|
14
21
|
import { compactRecord, readRecord, readString, readStringField, stringifyUnknown, truncateText } from '../utils/string'
|
|
15
22
|
|
|
16
23
|
const QueueJobRowSchema = z.object({
|
|
@@ -52,7 +59,6 @@ const QueueJobAttemptRowSchema = z.object({
|
|
|
52
59
|
|
|
53
60
|
const PERSISTENCE_MAX_ATTEMPTS = 4
|
|
54
61
|
const PERSISTENCE_RETRY_BASE_DELAY_MS = 25
|
|
55
|
-
const PERSISTENCE_RETRY_JITTER_MS = 25
|
|
56
62
|
|
|
57
63
|
export interface TrackedBullJobLike {
|
|
58
64
|
queueName: string
|
|
@@ -64,11 +70,6 @@ export interface TrackedBullJobLike {
|
|
|
64
70
|
timestamp?: number
|
|
65
71
|
}
|
|
66
72
|
|
|
67
|
-
function buildDeterministicRecordId(table: string, key: string): RecordId {
|
|
68
|
-
const digest = new Bun.CryptoHasher('sha256').update(key).digest('hex')
|
|
69
|
-
return new RecordId(table, digest)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
73
|
function sanitizeQueueValue(value: unknown, depth = 0): unknown {
|
|
73
74
|
if (value === null || value === undefined) return value
|
|
74
75
|
if (typeof value === 'string') return truncateText(value, 20_000)
|
|
@@ -117,21 +118,27 @@ function toQueueJobError(error: unknown): QueueJobError {
|
|
|
117
118
|
return QueueJobErrorSchema.parse({ message: truncateText(stringifyUnknown(error) ?? 'Unknown error', 5_000) })
|
|
118
119
|
}
|
|
119
120
|
|
|
120
|
-
function getBullmqJobId(job: TrackedBullJobLike): string {
|
|
121
|
+
function getBullmqJobId(job: TrackedBullJobLike): Effect.Effect<string, BadRequestError> {
|
|
121
122
|
const id = job.id
|
|
122
|
-
if (typeof id === 'string' && id.length > 0) return id
|
|
123
|
-
if (typeof id === 'number') return String(id)
|
|
124
|
-
|
|
123
|
+
if (typeof id === 'string' && id.length > 0) return Effect.succeed(id)
|
|
124
|
+
if (typeof id === 'number') return Effect.succeed(String(id))
|
|
125
|
+
return Effect.fail(new BadRequestError({ message: `BullMQ job for queue "${job.queueName}" is missing an id.` }))
|
|
125
126
|
}
|
|
126
127
|
|
|
127
|
-
function getQueueJobRecordId(job: TrackedBullJobLike): RecordId {
|
|
128
|
-
return
|
|
128
|
+
function getQueueJobRecordId(job: TrackedBullJobLike): Effect.Effect<RecordId, BadRequestError> {
|
|
129
|
+
return getBullmqJobId(job).pipe(
|
|
130
|
+
Effect.map((bullmqJobId) => createDeterministicRecordId(TABLES.QUEUE_JOB, `${job.queueName}:${bullmqJobId}`)),
|
|
131
|
+
)
|
|
129
132
|
}
|
|
130
133
|
|
|
131
|
-
function getQueueJobAttemptRecordId(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
134
|
+
function getQueueJobAttemptRecordId(
|
|
135
|
+
job: TrackedBullJobLike,
|
|
136
|
+
attemptNumber: number,
|
|
137
|
+
): Effect.Effect<RecordId, BadRequestError> {
|
|
138
|
+
return getBullmqJobId(job).pipe(
|
|
139
|
+
Effect.map((bullmqJobId) =>
|
|
140
|
+
createDeterministicRecordId(TABLES.QUEUE_JOB_ATTEMPT, `${job.queueName}:${bullmqJobId}:${attemptNumber}`),
|
|
141
|
+
),
|
|
135
142
|
)
|
|
136
143
|
}
|
|
137
144
|
|
|
@@ -178,225 +185,275 @@ function getQueuedStatus(job: TrackedBullJobLike): QueueJobStatus {
|
|
|
178
185
|
return typeof delay === 'number' && delay > 0 ? 'delayed' : 'waiting'
|
|
179
186
|
}
|
|
180
187
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const message = error.message.toLowerCase()
|
|
185
|
-
return (
|
|
186
|
-
message.includes('transaction conflict') ||
|
|
187
|
-
message.includes('transaction read conflict') ||
|
|
188
|
-
message.includes('read or write conflict') ||
|
|
189
|
-
message.includes('write conflict') ||
|
|
190
|
-
message.includes('resource busy') ||
|
|
191
|
-
message.includes('this transaction can be retried')
|
|
192
|
-
)
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
class QueueJobService {
|
|
196
|
-
private async withPersistenceRetry<T>(work: () => Promise<T>): Promise<T> {
|
|
197
|
-
let lastError: unknown = null
|
|
198
|
-
|
|
199
|
-
for (let attempt = 1; attempt <= PERSISTENCE_MAX_ATTEMPTS; attempt += 1) {
|
|
200
|
-
try {
|
|
201
|
-
return await work()
|
|
202
|
-
} catch (error) {
|
|
203
|
-
lastError = error
|
|
204
|
-
const hasMoreAttempts = attempt < PERSISTENCE_MAX_ATTEMPTS
|
|
205
|
-
if (!isRetriablePersistenceConflict(error) || !hasMoreAttempts) {
|
|
206
|
-
throw error
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const backoffMs =
|
|
210
|
-
PERSISTENCE_RETRY_BASE_DELAY_MS * 2 ** (attempt - 1) + Math.floor(Math.random() * PERSISTENCE_RETRY_JITTER_MS)
|
|
211
|
-
await Bun.sleep(backoffMs)
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
throw lastError instanceof Error ? lastError : new Error('Queue job persistence retry exhausted')
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
getQueueJobId(queueName: string, bullmqJobId: string): string {
|
|
219
|
-
return recordIdToString(
|
|
220
|
-
buildDeterministicRecordId(TABLES.QUEUE_JOB, `${queueName}:${bullmqJobId}`),
|
|
221
|
-
TABLES.QUEUE_JOB,
|
|
222
|
-
)
|
|
223
|
-
}
|
|
188
|
+
const effectTryQueueJobPersistence = makeEffectTryPromiseWithMessage(
|
|
189
|
+
(message, cause) => new DatabaseError({ message, cause }),
|
|
190
|
+
)
|
|
224
191
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
await databaseService.upsert(
|
|
233
|
-
TABLES.QUEUE_JOB,
|
|
234
|
-
queueJobId,
|
|
235
|
-
compactRecord({
|
|
236
|
-
queueName: job.queueName,
|
|
237
|
-
jobName: job.name,
|
|
238
|
-
bullmqJobId: getBullmqJobId(job),
|
|
239
|
-
status: getQueuedStatus(job),
|
|
240
|
-
data: wrapFlexibleValue(job.data),
|
|
241
|
-
options: sanitizeQueueValue(job.opts) as Record<string, unknown>,
|
|
242
|
-
context: Object.keys(mergedContext).length > 0 ? sanitizeQueueValue(mergedContext) : undefined,
|
|
243
|
-
deduplicationId: readDeduplicationId(job),
|
|
244
|
-
maxAttempts: readMaxAttempts(job),
|
|
245
|
-
queuedAt,
|
|
246
|
-
}),
|
|
247
|
-
QueueJobRowSchema,
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
return recordIdToString(queueJobId, TABLES.QUEUE_JOB)
|
|
251
|
-
})
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
async markAttemptStarted(job: TrackedBullJobLike): Promise<string> {
|
|
255
|
-
await databaseService.connect()
|
|
256
|
-
const attemptNumber = resolveAttemptNumber(job)
|
|
257
|
-
const queueJobId = getQueueJobRecordId(job)
|
|
258
|
-
const startedAt = new Date()
|
|
259
|
-
|
|
260
|
-
return this.withPersistenceRetry(async () => {
|
|
261
|
-
await databaseService.upsert(
|
|
262
|
-
TABLES.QUEUE_JOB,
|
|
263
|
-
queueJobId,
|
|
264
|
-
compactRecord({
|
|
265
|
-
queueName: job.queueName,
|
|
266
|
-
jobName: job.name,
|
|
267
|
-
bullmqJobId: getBullmqJobId(job),
|
|
268
|
-
status: 'active',
|
|
269
|
-
data: wrapFlexibleValue(job.data),
|
|
270
|
-
options: sanitizeQueueValue(job.opts) as Record<string, unknown>,
|
|
271
|
-
context: sanitizeQueueValue(extractJobContext(job.data)) as Record<string, unknown> | undefined,
|
|
272
|
-
deduplicationId: readDeduplicationId(job),
|
|
273
|
-
maxAttempts: readMaxAttempts(job),
|
|
274
|
-
attemptCount: attemptNumber,
|
|
275
|
-
queuedAt: typeof job.timestamp === 'number' ? new Date(job.timestamp) : startedAt,
|
|
276
|
-
startedAt,
|
|
277
|
-
}),
|
|
278
|
-
QueueJobRowSchema,
|
|
279
|
-
)
|
|
280
|
-
|
|
281
|
-
const attemptId = getQueueJobAttemptRecordId(job, attemptNumber)
|
|
282
|
-
await databaseService.upsert(
|
|
283
|
-
TABLES.QUEUE_JOB_ATTEMPT,
|
|
284
|
-
attemptId,
|
|
285
|
-
{ queueJobId, attemptNumber, status: 'active', startedAt },
|
|
286
|
-
QueueJobAttemptRowSchema,
|
|
287
|
-
)
|
|
288
|
-
|
|
289
|
-
return recordIdToString(queueJobId, TABLES.QUEUE_JOB)
|
|
290
|
-
})
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
async markAttemptCompleted(job: TrackedBullJobLike, result: unknown): Promise<void> {
|
|
294
|
-
await databaseService.connect()
|
|
295
|
-
const attemptNumber = resolveAttemptNumber(job)
|
|
296
|
-
const queueJobId = getQueueJobRecordId(job)
|
|
297
|
-
const attemptId = getQueueJobAttemptRecordId(job, attemptNumber)
|
|
298
|
-
const completedAt = new Date()
|
|
299
|
-
|
|
300
|
-
await this.withPersistenceRetry(async () => {
|
|
301
|
-
const existingAttempt = await databaseService.findOne(
|
|
302
|
-
TABLES.QUEUE_JOB_ATTEMPT,
|
|
303
|
-
{ id: attemptId },
|
|
304
|
-
QueueJobAttemptRowSchema,
|
|
305
|
-
)
|
|
306
|
-
|
|
307
|
-
await databaseService.upsert(
|
|
308
|
-
TABLES.QUEUE_JOB_ATTEMPT,
|
|
309
|
-
attemptId,
|
|
310
|
-
compactRecord({
|
|
311
|
-
queueJobId,
|
|
312
|
-
attemptNumber,
|
|
313
|
-
status: 'completed',
|
|
314
|
-
result: wrapFlexibleValue(result),
|
|
315
|
-
startedAt: existingAttempt?.startedAt ?? completedAt,
|
|
316
|
-
completedAt,
|
|
317
|
-
durationMs: existingAttempt ? Math.max(0, completedAt.getTime() - existingAttempt.startedAt.getTime()) : 0,
|
|
318
|
-
}),
|
|
319
|
-
QueueJobAttemptRowSchema,
|
|
320
|
-
)
|
|
321
|
-
|
|
322
|
-
await databaseService.upsert(
|
|
323
|
-
TABLES.QUEUE_JOB,
|
|
324
|
-
queueJobId,
|
|
325
|
-
compactRecord({
|
|
326
|
-
queueName: job.queueName,
|
|
327
|
-
jobName: job.name,
|
|
328
|
-
bullmqJobId: getBullmqJobId(job),
|
|
329
|
-
status: 'completed',
|
|
330
|
-
data: wrapFlexibleValue(job.data),
|
|
331
|
-
options: sanitizeQueueValue(job.opts) as Record<string, unknown>,
|
|
332
|
-
context: sanitizeQueueValue(extractJobContext(job.data)) as Record<string, unknown> | undefined,
|
|
333
|
-
deduplicationId: readDeduplicationId(job),
|
|
334
|
-
maxAttempts: readMaxAttempts(job),
|
|
335
|
-
attemptCount: attemptNumber,
|
|
336
|
-
queuedAt: typeof job.timestamp === 'number' ? new Date(job.timestamp) : completedAt,
|
|
337
|
-
completedAt,
|
|
338
|
-
result: wrapFlexibleValue(result),
|
|
339
|
-
lastError: undefined,
|
|
340
|
-
}),
|
|
341
|
-
QueueJobRowSchema,
|
|
342
|
-
)
|
|
343
|
-
})
|
|
344
|
-
}
|
|
192
|
+
function tryQueueJobPersistence<A>(
|
|
193
|
+
message: string,
|
|
194
|
+
evaluate: () => PromiseLike<A> | Effect.Effect<A, unknown>,
|
|
195
|
+
): Effect.Effect<A, DatabaseError> {
|
|
196
|
+
return effectTryQueueJobPersistence(evaluate, message)
|
|
197
|
+
}
|
|
345
198
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
const maxAttempts = readMaxAttempts(job)
|
|
354
|
-
const terminal = typeof maxAttempts === 'number' ? attemptNumber >= maxAttempts : true
|
|
355
|
-
|
|
356
|
-
await this.withPersistenceRetry(async () => {
|
|
357
|
-
const existingAttempt = await databaseService.findOne(
|
|
358
|
-
TABLES.QUEUE_JOB_ATTEMPT,
|
|
359
|
-
{ id: attemptId },
|
|
360
|
-
QueueJobAttemptRowSchema,
|
|
361
|
-
)
|
|
199
|
+
function withPersistenceRetry<T>(work: Effect.Effect<T, DatabaseError>): Effect.Effect<T, DatabaseError> {
|
|
200
|
+
return Effect.retry(work, {
|
|
201
|
+
times: PERSISTENCE_MAX_ATTEMPTS - 1,
|
|
202
|
+
schedule: Schedule.jittered(Schedule.exponential(Duration.millis(PERSISTENCE_RETRY_BASE_DELAY_MS), 2)),
|
|
203
|
+
while: isRetriableTransactionConflict,
|
|
204
|
+
})
|
|
205
|
+
}
|
|
362
206
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
compactRecord({
|
|
367
|
-
queueJobId,
|
|
368
|
-
attemptNumber,
|
|
369
|
-
status: 'failed',
|
|
370
|
-
error: normalizedError,
|
|
371
|
-
startedAt: existingAttempt?.startedAt ?? failedAt,
|
|
372
|
-
completedAt: failedAt,
|
|
373
|
-
durationMs: existingAttempt ? Math.max(0, failedAt.getTime() - existingAttempt.startedAt.getTime()) : 0,
|
|
374
|
-
}),
|
|
375
|
-
QueueJobAttemptRowSchema,
|
|
376
|
-
)
|
|
207
|
+
function toPersistenceIdError(message: string, cause: unknown): DatabaseError {
|
|
208
|
+
return new DatabaseError({ message, cause })
|
|
209
|
+
}
|
|
377
210
|
|
|
378
|
-
|
|
211
|
+
export function makeQueueJobService(db: SurrealDBService) {
|
|
212
|
+
return {
|
|
213
|
+
getQueueJobId(queueName: string, bullmqJobId: string): string {
|
|
214
|
+
return recordIdToString(
|
|
215
|
+
createDeterministicRecordId(TABLES.QUEUE_JOB, `${queueName}:${bullmqJobId}`),
|
|
379
216
|
TABLES.QUEUE_JOB,
|
|
380
|
-
queueJobId,
|
|
381
|
-
compactRecord({
|
|
382
|
-
queueName: job.queueName,
|
|
383
|
-
jobName: job.name,
|
|
384
|
-
bullmqJobId: getBullmqJobId(job),
|
|
385
|
-
status: terminal ? 'failed' : 'waiting',
|
|
386
|
-
data: wrapFlexibleValue(job.data),
|
|
387
|
-
options: sanitizeQueueValue(job.opts) as Record<string, unknown>,
|
|
388
|
-
context: sanitizeQueueValue(extractJobContext(job.data)) as Record<string, unknown> | undefined,
|
|
389
|
-
deduplicationId: readDeduplicationId(job),
|
|
390
|
-
maxAttempts,
|
|
391
|
-
attemptCount: attemptNumber,
|
|
392
|
-
queuedAt: typeof job.timestamp === 'number' ? new Date(job.timestamp) : failedAt,
|
|
393
|
-
failedAt: terminal ? failedAt : undefined,
|
|
394
|
-
lastError: normalizedError,
|
|
395
|
-
}),
|
|
396
|
-
QueueJobRowSchema,
|
|
397
217
|
)
|
|
398
|
-
}
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
recordEnqueued(job: TrackedBullJobLike, context?: Record<string, unknown>) {
|
|
221
|
+
return Effect.gen(function* () {
|
|
222
|
+
yield* tryQueueJobPersistence('Failed to connect before recording queued job metadata', () => db.connect())
|
|
223
|
+
|
|
224
|
+
const bullmqJobId = yield* getBullmqJobId(job)
|
|
225
|
+
const queueJobId = yield* getQueueJobRecordId(job)
|
|
226
|
+
const queuedAt = typeof job.timestamp === 'number' ? unsafeDateFrom(job.timestamp) : nowDate()
|
|
227
|
+
const mergedContext = compactRecord({ ...extractJobContext(job.data), ...context })
|
|
228
|
+
|
|
229
|
+
return yield* withPersistenceRetry(
|
|
230
|
+
Effect.gen(function* () {
|
|
231
|
+
yield* tryQueueJobPersistence('Failed to upsert queued job metadata', () =>
|
|
232
|
+
db.upsert(
|
|
233
|
+
TABLES.QUEUE_JOB,
|
|
234
|
+
queueJobId,
|
|
235
|
+
compactRecord({
|
|
236
|
+
queueName: job.queueName,
|
|
237
|
+
jobName: job.name,
|
|
238
|
+
bullmqJobId,
|
|
239
|
+
status: getQueuedStatus(job),
|
|
240
|
+
data: wrapFlexibleValue(job.data),
|
|
241
|
+
options: sanitizeQueueValue(job.opts) as Record<string, unknown>,
|
|
242
|
+
context: Object.keys(mergedContext).length > 0 ? sanitizeQueueValue(mergedContext) : undefined,
|
|
243
|
+
deduplicationId: readDeduplicationId(job),
|
|
244
|
+
maxAttempts: readMaxAttempts(job),
|
|
245
|
+
queuedAt,
|
|
246
|
+
}),
|
|
247
|
+
QueueJobRowSchema,
|
|
248
|
+
),
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
return recordIdToString(queueJobId, TABLES.QUEUE_JOB)
|
|
252
|
+
}),
|
|
253
|
+
)
|
|
254
|
+
})
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
markAttemptStarted(job: TrackedBullJobLike) {
|
|
258
|
+
return Effect.gen(function* () {
|
|
259
|
+
yield* tryQueueJobPersistence('Failed to connect before marking queue job attempt started', () => db.connect())
|
|
260
|
+
|
|
261
|
+
const attemptNumber = resolveAttemptNumber(job)
|
|
262
|
+
const bullmqJobId = yield* getBullmqJobId(job).pipe(
|
|
263
|
+
Effect.mapError((cause) => toPersistenceIdError('Failed to read BullMQ job id.', cause)),
|
|
264
|
+
)
|
|
265
|
+
const queueJobId = yield* getQueueJobRecordId(job).pipe(
|
|
266
|
+
Effect.mapError((cause) => toPersistenceIdError('Failed to derive queue job record id.', cause)),
|
|
267
|
+
)
|
|
268
|
+
const attemptId = yield* getQueueJobAttemptRecordId(job, attemptNumber).pipe(
|
|
269
|
+
Effect.mapError((cause) => toPersistenceIdError('Failed to derive queue job attempt record id.', cause)),
|
|
270
|
+
)
|
|
271
|
+
const startedAt = nowDate()
|
|
272
|
+
const queuedAt = typeof job.timestamp === 'number' ? unsafeDateFrom(job.timestamp) : startedAt
|
|
273
|
+
|
|
274
|
+
return yield* withPersistenceRetry(
|
|
275
|
+
Effect.gen(function* () {
|
|
276
|
+
yield* tryQueueJobPersistence('Failed to upsert queue job start metadata', () =>
|
|
277
|
+
db.upsert(
|
|
278
|
+
TABLES.QUEUE_JOB,
|
|
279
|
+
queueJobId,
|
|
280
|
+
compactRecord({
|
|
281
|
+
queueName: job.queueName,
|
|
282
|
+
jobName: job.name,
|
|
283
|
+
bullmqJobId,
|
|
284
|
+
status: 'active',
|
|
285
|
+
data: wrapFlexibleValue(job.data),
|
|
286
|
+
options: sanitizeQueueValue(job.opts) as Record<string, unknown>,
|
|
287
|
+
context: sanitizeQueueValue(extractJobContext(job.data)) as Record<string, unknown> | undefined,
|
|
288
|
+
deduplicationId: readDeduplicationId(job),
|
|
289
|
+
maxAttempts: readMaxAttempts(job),
|
|
290
|
+
attemptCount: attemptNumber,
|
|
291
|
+
queuedAt,
|
|
292
|
+
startedAt,
|
|
293
|
+
}),
|
|
294
|
+
QueueJobRowSchema,
|
|
295
|
+
),
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
yield* tryQueueJobPersistence('Failed to upsert queue job attempt start metadata', () =>
|
|
299
|
+
db.upsert(
|
|
300
|
+
TABLES.QUEUE_JOB_ATTEMPT,
|
|
301
|
+
attemptId,
|
|
302
|
+
{ queueJobId, attemptNumber, status: 'active', startedAt },
|
|
303
|
+
QueueJobAttemptRowSchema,
|
|
304
|
+
),
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
return recordIdToString(queueJobId, TABLES.QUEUE_JOB)
|
|
308
|
+
}),
|
|
309
|
+
)
|
|
310
|
+
})
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
markAttemptCompleted(job: TrackedBullJobLike, result: unknown) {
|
|
314
|
+
return Effect.gen(function* () {
|
|
315
|
+
yield* tryQueueJobPersistence('Failed to connect before marking queue job attempt completed', () =>
|
|
316
|
+
db.connect(),
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
const attemptNumber = resolveAttemptNumber(job)
|
|
320
|
+
const bullmqJobId = yield* getBullmqJobId(job)
|
|
321
|
+
const queueJobId = yield* getQueueJobRecordId(job)
|
|
322
|
+
const attemptId = yield* getQueueJobAttemptRecordId(job, attemptNumber)
|
|
323
|
+
const completedAt = nowDate()
|
|
324
|
+
const queuedAt = typeof job.timestamp === 'number' ? unsafeDateFrom(job.timestamp) : completedAt
|
|
325
|
+
|
|
326
|
+
yield* withPersistenceRetry(
|
|
327
|
+
Effect.gen(function* () {
|
|
328
|
+
const existingAttempt = yield* tryQueueJobPersistence(
|
|
329
|
+
'Failed to load existing queue job attempt before completion update',
|
|
330
|
+
() => db.findOne(TABLES.QUEUE_JOB_ATTEMPT, { id: attemptId }, QueueJobAttemptRowSchema),
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
yield* tryQueueJobPersistence('Failed to upsert completed queue job attempt metadata', () =>
|
|
334
|
+
db.upsert(
|
|
335
|
+
TABLES.QUEUE_JOB_ATTEMPT,
|
|
336
|
+
attemptId,
|
|
337
|
+
compactRecord({
|
|
338
|
+
queueJobId,
|
|
339
|
+
attemptNumber,
|
|
340
|
+
status: 'completed',
|
|
341
|
+
result: wrapFlexibleValue(result),
|
|
342
|
+
startedAt: existingAttempt?.startedAt ?? completedAt,
|
|
343
|
+
completedAt,
|
|
344
|
+
durationMs: existingAttempt
|
|
345
|
+
? Math.max(0, completedAt.getTime() - existingAttempt.startedAt.getTime())
|
|
346
|
+
: 0,
|
|
347
|
+
}),
|
|
348
|
+
QueueJobAttemptRowSchema,
|
|
349
|
+
),
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
yield* tryQueueJobPersistence('Failed to upsert completed queue job metadata', () =>
|
|
353
|
+
db.upsert(
|
|
354
|
+
TABLES.QUEUE_JOB,
|
|
355
|
+
queueJobId,
|
|
356
|
+
compactRecord({
|
|
357
|
+
queueName: job.queueName,
|
|
358
|
+
jobName: job.name,
|
|
359
|
+
bullmqJobId,
|
|
360
|
+
status: 'completed',
|
|
361
|
+
data: wrapFlexibleValue(job.data),
|
|
362
|
+
options: sanitizeQueueValue(job.opts) as Record<string, unknown>,
|
|
363
|
+
context: sanitizeQueueValue(extractJobContext(job.data)) as Record<string, unknown> | undefined,
|
|
364
|
+
deduplicationId: readDeduplicationId(job),
|
|
365
|
+
maxAttempts: readMaxAttempts(job),
|
|
366
|
+
attemptCount: attemptNumber,
|
|
367
|
+
queuedAt,
|
|
368
|
+
completedAt,
|
|
369
|
+
result: wrapFlexibleValue(result),
|
|
370
|
+
lastError: undefined,
|
|
371
|
+
}),
|
|
372
|
+
QueueJobRowSchema,
|
|
373
|
+
),
|
|
374
|
+
)
|
|
375
|
+
}),
|
|
376
|
+
)
|
|
377
|
+
})
|
|
378
|
+
},
|
|
379
|
+
|
|
380
|
+
markAttemptFailed(job: TrackedBullJobLike, error: unknown) {
|
|
381
|
+
return Effect.gen(function* () {
|
|
382
|
+
yield* tryQueueJobPersistence('Failed to connect before marking queue job attempt failed', () => db.connect())
|
|
383
|
+
|
|
384
|
+
const attemptNumber = resolveAttemptNumber(job)
|
|
385
|
+
const bullmqJobId = yield* getBullmqJobId(job)
|
|
386
|
+
const queueJobId = yield* getQueueJobRecordId(job)
|
|
387
|
+
const attemptId = yield* getQueueJobAttemptRecordId(job, attemptNumber)
|
|
388
|
+
const failedAt = nowDate()
|
|
389
|
+
const queuedAt = typeof job.timestamp === 'number' ? unsafeDateFrom(job.timestamp) : failedAt
|
|
390
|
+
const normalizedError = toQueueJobError(error)
|
|
391
|
+
const maxAttempts = readMaxAttempts(job)
|
|
392
|
+
const terminal = typeof maxAttempts === 'number' ? attemptNumber >= maxAttempts : true
|
|
393
|
+
|
|
394
|
+
yield* withPersistenceRetry(
|
|
395
|
+
Effect.gen(function* () {
|
|
396
|
+
const existingAttempt = yield* tryQueueJobPersistence(
|
|
397
|
+
'Failed to load existing queue job attempt before failure update',
|
|
398
|
+
() => db.findOne(TABLES.QUEUE_JOB_ATTEMPT, { id: attemptId }, QueueJobAttemptRowSchema),
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
yield* tryQueueJobPersistence('Failed to upsert failed queue job attempt metadata', () =>
|
|
402
|
+
db.upsert(
|
|
403
|
+
TABLES.QUEUE_JOB_ATTEMPT,
|
|
404
|
+
attemptId,
|
|
405
|
+
compactRecord({
|
|
406
|
+
queueJobId,
|
|
407
|
+
attemptNumber,
|
|
408
|
+
status: 'failed',
|
|
409
|
+
error: normalizedError,
|
|
410
|
+
startedAt: existingAttempt?.startedAt ?? failedAt,
|
|
411
|
+
completedAt: failedAt,
|
|
412
|
+
durationMs: existingAttempt
|
|
413
|
+
? Math.max(0, failedAt.getTime() - existingAttempt.startedAt.getTime())
|
|
414
|
+
: 0,
|
|
415
|
+
}),
|
|
416
|
+
QueueJobAttemptRowSchema,
|
|
417
|
+
),
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
yield* tryQueueJobPersistence('Failed to upsert failed queue job metadata', () =>
|
|
421
|
+
db.upsert(
|
|
422
|
+
TABLES.QUEUE_JOB,
|
|
423
|
+
queueJobId,
|
|
424
|
+
compactRecord({
|
|
425
|
+
queueName: job.queueName,
|
|
426
|
+
jobName: job.name,
|
|
427
|
+
bullmqJobId,
|
|
428
|
+
status: terminal ? 'failed' : 'waiting',
|
|
429
|
+
data: wrapFlexibleValue(job.data),
|
|
430
|
+
options: sanitizeQueueValue(job.opts) as Record<string, unknown>,
|
|
431
|
+
context: sanitizeQueueValue(extractJobContext(job.data)) as Record<string, unknown> | undefined,
|
|
432
|
+
deduplicationId: readDeduplicationId(job),
|
|
433
|
+
maxAttempts,
|
|
434
|
+
attemptCount: attemptNumber,
|
|
435
|
+
queuedAt,
|
|
436
|
+
failedAt: terminal ? failedAt : undefined,
|
|
437
|
+
lastError: normalizedError,
|
|
438
|
+
}),
|
|
439
|
+
QueueJobRowSchema,
|
|
440
|
+
),
|
|
441
|
+
)
|
|
442
|
+
}),
|
|
443
|
+
)
|
|
444
|
+
})
|
|
445
|
+
},
|
|
399
446
|
}
|
|
400
447
|
}
|
|
401
448
|
|
|
402
|
-
export
|
|
449
|
+
export class QueueJobServiceTag extends Context.Service<QueueJobServiceTag, ReturnType<typeof makeQueueJobService>>()(
|
|
450
|
+
'@lota-sdk/core/QueueJobService',
|
|
451
|
+
) {}
|
|
452
|
+
|
|
453
|
+
export const QueueJobServiceLive = Layer.effect(
|
|
454
|
+
QueueJobServiceTag,
|
|
455
|
+
Effect.gen(function* () {
|
|
456
|
+
const db = yield* DatabaseServiceTag
|
|
457
|
+
return makeQueueJobService(db)
|
|
458
|
+
}),
|
|
459
|
+
)
|