@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
package/src/db/startup.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { recordIdSchema } from '@lota-sdk/shared'
|
|
2
|
+
import { Duration, Effect, Match, Schedule } from 'effect'
|
|
2
3
|
import { BoundQuery, RecordId } from 'surrealdb'
|
|
3
4
|
import { z } from 'zod'
|
|
4
5
|
|
|
6
|
+
import { DatabaseError } from '../effect/errors'
|
|
7
|
+
import { effectTryPromise } from '../effect/helpers'
|
|
8
|
+
import { nowDate } from '../utils/date-time'
|
|
5
9
|
import { getErrorMessage } from '../utils/errors'
|
|
6
10
|
import type { SurrealDBService, SurrealDatabaseLogger } from './service'
|
|
11
|
+
import type { SurrealDBError } from './service-normalization'
|
|
7
12
|
import { TABLES } from './tables'
|
|
8
13
|
|
|
9
14
|
const DATABASE_BOOTSTRAP_KEY = 'database-schema-ready'
|
|
@@ -21,48 +26,67 @@ const RuntimeBootstrapRecordSchema = z.object({
|
|
|
21
26
|
|
|
22
27
|
type StartupLogger = Pick<SurrealDatabaseLogger, 'info' | 'warn' | 'error'>
|
|
23
28
|
|
|
29
|
+
type BootstrapWaitFailure =
|
|
30
|
+
| { _tag: 'schema-not-ready'; currentFingerprint: string }
|
|
31
|
+
| { _tag: 'read-failed'; cause: unknown }
|
|
32
|
+
|
|
24
33
|
function shouldLogRetry(attempt: number): boolean {
|
|
25
34
|
return attempt === 1 || attempt % RETRY_LOG_INTERVAL === 0
|
|
26
35
|
}
|
|
27
36
|
|
|
28
|
-
|
|
29
|
-
connect: () =>
|
|
37
|
+
function connectWithStartupRetryEffectInternal(params: {
|
|
38
|
+
connect: () => PromiseLike<void> | Effect.Effect<void, SurrealDBError>
|
|
30
39
|
label: string
|
|
31
40
|
logger?: StartupLogger
|
|
32
41
|
retryDelayMs?: number
|
|
33
42
|
maxWaitMs?: number
|
|
34
|
-
}):
|
|
43
|
+
}): Effect.Effect<void, DatabaseError> {
|
|
35
44
|
const retryDelayMs = params.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS
|
|
36
45
|
const maxWaitMs = params.maxWaitMs ?? DEFAULT_MAX_WAIT_MS
|
|
37
|
-
const startedAt = Date.now()
|
|
38
46
|
|
|
39
47
|
let attempt = 0
|
|
40
|
-
let lastError: unknown = null
|
|
41
|
-
|
|
42
|
-
while (Date.now() - startedAt <= maxWaitMs) {
|
|
43
|
-
attempt += 1
|
|
44
|
-
|
|
45
|
-
try {
|
|
46
|
-
await params.connect()
|
|
47
|
-
return
|
|
48
|
-
} catch (error) {
|
|
49
|
-
lastError = error
|
|
50
|
-
if (shouldLogRetry(attempt)) {
|
|
51
|
-
params.logger?.warn?.(
|
|
52
|
-
`Waiting for ${params.label} (${attempt}, elapsed=${Date.now() - startedAt}ms): ${getErrorMessage(error)}`,
|
|
53
|
-
)
|
|
54
|
-
}
|
|
55
|
-
await Bun.sleep(retryDelayMs)
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
48
|
|
|
59
|
-
|
|
60
|
-
|
|
49
|
+
const connectEffect = effectTryPromise(
|
|
50
|
+
() => params.connect(),
|
|
51
|
+
(error) => new DatabaseError({ message: `Waiting for ${params.label}: ${getErrorMessage(error)}`, cause: error }),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return connectEffect.pipe(
|
|
55
|
+
Effect.tapError((error) =>
|
|
56
|
+
Effect.sync(() => {
|
|
57
|
+
attempt++
|
|
58
|
+
if (shouldLogRetry(attempt)) {
|
|
59
|
+
params.logger?.warn?.(error.message)
|
|
60
|
+
}
|
|
61
|
+
}),
|
|
62
|
+
),
|
|
63
|
+
Effect.retry({
|
|
64
|
+
times: Math.max(0, Math.ceil(maxWaitMs / retryDelayMs) - 1),
|
|
65
|
+
schedule: Schedule.fixed(Duration.millis(retryDelayMs)),
|
|
66
|
+
}),
|
|
67
|
+
Effect.mapError((error) => {
|
|
68
|
+
params.logger?.error?.(`Timed out waiting for ${params.label}: ${getErrorMessage(error)}`)
|
|
69
|
+
return new DatabaseError({
|
|
70
|
+
message: `Timed out waiting for ${params.label}: ${getErrorMessage(error)}`,
|
|
71
|
+
cause: error,
|
|
72
|
+
})
|
|
73
|
+
}),
|
|
74
|
+
)
|
|
61
75
|
}
|
|
62
76
|
|
|
63
|
-
|
|
77
|
+
export function connectWithStartupRetry(params: {
|
|
78
|
+
connect: () => PromiseLike<void> | Effect.Effect<void, SurrealDBError>
|
|
79
|
+
label: string
|
|
80
|
+
logger?: StartupLogger
|
|
81
|
+
retryDelayMs?: number
|
|
82
|
+
maxWaitMs?: number
|
|
83
|
+
}): Promise<void> {
|
|
84
|
+
return Effect.runPromise(connectWithStartupRetryEffectInternal(params))
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function readDatabaseBootstrapRecord(
|
|
64
88
|
databaseService: SurrealDBService,
|
|
65
|
-
):
|
|
89
|
+
): Effect.Effect<z.infer<typeof RuntimeBootstrapRecordSchema> | null, SurrealDBError> {
|
|
66
90
|
return databaseService.queryOne(
|
|
67
91
|
new BoundQuery(
|
|
68
92
|
`SELECT *
|
|
@@ -75,74 +99,135 @@ async function readDatabaseBootstrapRecord(
|
|
|
75
99
|
)
|
|
76
100
|
}
|
|
77
101
|
|
|
78
|
-
|
|
102
|
+
function waitForDatabaseBootstrapEffectInternal(params: {
|
|
79
103
|
databaseService: SurrealDBService
|
|
80
104
|
expectedFingerprint?: string | null
|
|
81
105
|
label: string
|
|
82
106
|
logger?: StartupLogger
|
|
83
|
-
connect?: () =>
|
|
107
|
+
connect?: () => PromiseLike<void> | Effect.Effect<void, SurrealDBError>
|
|
84
108
|
retryDelayMs?: number
|
|
85
109
|
maxWaitMs?: number
|
|
86
|
-
}):
|
|
110
|
+
}): Effect.Effect<void, DatabaseError> {
|
|
87
111
|
const expectedFingerprint = params.expectedFingerprint?.trim()
|
|
88
112
|
if (!expectedFingerprint) {
|
|
89
|
-
return
|
|
113
|
+
return Effect.void
|
|
90
114
|
}
|
|
91
115
|
|
|
92
116
|
const retryDelayMs = params.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS
|
|
93
117
|
const maxWaitMs = params.maxWaitMs ?? DEFAULT_MAX_WAIT_MS
|
|
94
|
-
const startedAt = Date.now()
|
|
95
|
-
|
|
96
118
|
let attempt = 0
|
|
97
|
-
let lastError: unknown = null
|
|
98
119
|
|
|
99
|
-
|
|
100
|
-
|
|
120
|
+
const readBootstrapRecord = Effect.gen(function* () {
|
|
121
|
+
const connect = params.connect
|
|
122
|
+
if (connect) {
|
|
123
|
+
yield* effectTryPromise(
|
|
124
|
+
() => connect(),
|
|
125
|
+
(error): BootstrapWaitFailure => ({ _tag: 'read-failed', cause: error }),
|
|
126
|
+
)
|
|
127
|
+
}
|
|
101
128
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
129
|
+
return yield* effectTryPromise(
|
|
130
|
+
() => readDatabaseBootstrapRecord(params.databaseService),
|
|
131
|
+
(error): BootstrapWaitFailure => ({ _tag: 'read-failed', cause: error }),
|
|
132
|
+
)
|
|
133
|
+
})
|
|
106
134
|
|
|
107
|
-
|
|
135
|
+
const waitForExpectedFingerprint = readBootstrapRecord.pipe(
|
|
136
|
+
Effect.flatMap((record) => {
|
|
108
137
|
if (record?.schemaFingerprint === expectedFingerprint) {
|
|
109
|
-
return
|
|
138
|
+
return Effect.void
|
|
110
139
|
}
|
|
111
140
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
)
|
|
120
|
-
}
|
|
121
|
-
} catch (error) {
|
|
122
|
-
lastError = error
|
|
123
|
-
if (shouldLogRetry(attempt)) {
|
|
124
|
-
params.logger?.warn?.(`Waiting for ${params.label} schema readiness (${attempt}): ${getErrorMessage(error)}`)
|
|
125
|
-
}
|
|
126
|
-
}
|
|
141
|
+
const currentFingerprint =
|
|
142
|
+
typeof record?.schemaFingerprint === 'string' && record.schemaFingerprint.length > 0
|
|
143
|
+
? record.schemaFingerprint
|
|
144
|
+
: 'missing'
|
|
145
|
+
return Effect.fail<BootstrapWaitFailure>({ _tag: 'schema-not-ready', currentFingerprint })
|
|
146
|
+
}),
|
|
147
|
+
)
|
|
127
148
|
|
|
128
|
-
|
|
129
|
-
|
|
149
|
+
const logFailure = Match.type<BootstrapWaitFailure>().pipe(
|
|
150
|
+
Match.tag('schema-not-ready', (failure) => {
|
|
151
|
+
params.logger?.info?.(
|
|
152
|
+
`Waiting for ${params.label} schema readiness (${attempt}, expected=${expectedFingerprint}, current=${failure.currentFingerprint})`,
|
|
153
|
+
)
|
|
154
|
+
}),
|
|
155
|
+
Match.tag('read-failed', (failure) => {
|
|
156
|
+
params.logger?.warn?.(
|
|
157
|
+
`Waiting for ${params.label} schema readiness (${attempt}): ${getErrorMessage(failure.cause)}`,
|
|
158
|
+
)
|
|
159
|
+
}),
|
|
160
|
+
Match.exhaustive,
|
|
161
|
+
)
|
|
130
162
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
163
|
+
const toTimeoutError = Match.type<BootstrapWaitFailure>().pipe(
|
|
164
|
+
Match.tag(
|
|
165
|
+
'schema-not-ready',
|
|
166
|
+
() => new DatabaseError({ message: `Timed out waiting for ${params.label} schema readiness` }),
|
|
167
|
+
),
|
|
168
|
+
Match.tag(
|
|
169
|
+
'read-failed',
|
|
170
|
+
(failure) =>
|
|
171
|
+
new DatabaseError({ message: `Timed out waiting for ${params.label} schema readiness`, cause: failure.cause }),
|
|
172
|
+
),
|
|
173
|
+
Match.exhaustive,
|
|
174
|
+
)
|
|
134
175
|
|
|
135
|
-
|
|
176
|
+
return waitForExpectedFingerprint.pipe(
|
|
177
|
+
Effect.tapError((failure) =>
|
|
178
|
+
Effect.sync(() => {
|
|
179
|
+
attempt++
|
|
180
|
+
if (!shouldLogRetry(attempt)) return
|
|
181
|
+
logFailure(failure)
|
|
182
|
+
}),
|
|
183
|
+
),
|
|
184
|
+
Effect.retry({
|
|
185
|
+
times: Math.max(0, Math.ceil(maxWaitMs / retryDelayMs) - 1),
|
|
186
|
+
schedule: Schedule.fixed(Duration.millis(retryDelayMs)),
|
|
187
|
+
}),
|
|
188
|
+
Effect.mapError(toTimeoutError),
|
|
189
|
+
)
|
|
136
190
|
}
|
|
137
191
|
|
|
138
|
-
export
|
|
192
|
+
export function waitForDatabaseBootstrap(params: {
|
|
193
|
+
databaseService: SurrealDBService
|
|
194
|
+
expectedFingerprint?: string | null
|
|
195
|
+
label: string
|
|
196
|
+
logger?: StartupLogger
|
|
197
|
+
connect?: () => PromiseLike<void> | Effect.Effect<void, SurrealDBError>
|
|
198
|
+
retryDelayMs?: number
|
|
199
|
+
maxWaitMs?: number
|
|
200
|
+
}): Promise<void> {
|
|
201
|
+
return Effect.runPromise(waitForDatabaseBootstrapEffectInternal(params))
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function publishDatabaseBootstrap(params: {
|
|
139
205
|
databaseService: SurrealDBService
|
|
140
206
|
schemaFingerprint: string
|
|
141
207
|
}): Promise<void> {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
208
|
+
return Effect.runPromise(publishDatabaseBootstrapEffect(params))
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function publishDatabaseBootstrapEffect(params: {
|
|
212
|
+
databaseService: SurrealDBService
|
|
213
|
+
schemaFingerprint: string
|
|
214
|
+
}): Effect.Effect<void, DatabaseError> {
|
|
215
|
+
return Effect.asVoid(
|
|
216
|
+
params.databaseService
|
|
217
|
+
.upsert(
|
|
218
|
+
TABLES.RUNTIME_BOOTSTRAP,
|
|
219
|
+
new RecordId(TABLES.RUNTIME_BOOTSTRAP, DATABASE_BOOTSTRAP_KEY),
|
|
220
|
+
{ key: DATABASE_BOOTSTRAP_KEY, schemaFingerprint: params.schemaFingerprint, readyAt: nowDate() },
|
|
221
|
+
RuntimeBootstrapRecordSchema,
|
|
222
|
+
)
|
|
223
|
+
.pipe(
|
|
224
|
+
Effect.mapError(
|
|
225
|
+
(error) =>
|
|
226
|
+
new DatabaseError({
|
|
227
|
+
message: `Failed to publish database bootstrap: ${getErrorMessage(error)}`,
|
|
228
|
+
cause: error,
|
|
229
|
+
}),
|
|
230
|
+
),
|
|
231
|
+
),
|
|
147
232
|
)
|
|
148
233
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function isRetriableTransactionConflict(error: unknown): boolean {
|
|
2
|
+
if (!(error instanceof Error)) {
|
|
3
|
+
return false
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const message = error.message.toLowerCase()
|
|
7
|
+
return (
|
|
8
|
+
message.includes('transaction conflict') ||
|
|
9
|
+
message.includes('transaction read conflict') ||
|
|
10
|
+
message.includes('read or write conflict') ||
|
|
11
|
+
message.includes('write conflict') ||
|
|
12
|
+
message.includes('resource busy') ||
|
|
13
|
+
message.includes('this transaction can be retried')
|
|
14
|
+
)
|
|
15
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Effect } from 'effect'
|
|
2
|
+
|
|
3
|
+
export type AwaitableEffect<A, E> = Effect.Effect<A, E, never> & Promise<A>
|
|
4
|
+
|
|
5
|
+
export type AwaitableValue<T> = T extends (...args: infer TArgs) => Effect.Effect<infer A, infer E, never>
|
|
6
|
+
? (...args: TArgs) => AwaitableEffect<A, E>
|
|
7
|
+
: T extends (...args: infer TArgs) => infer TResult
|
|
8
|
+
? (...args: TArgs) => TResult
|
|
9
|
+
: T
|
|
10
|
+
|
|
11
|
+
type AwaitableServiceMethods<T> = {
|
|
12
|
+
[K in keyof T]: AwaitableValue<T[K]>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type AwaitableService<T> = AwaitableServiceMethods<T>
|
|
16
|
+
|
|
17
|
+
export type MaybeAwaitableService<T extends object> = T | AwaitableService<T>
|
|
18
|
+
|
|
19
|
+
type AwaitableEffectOptions = { runPromise?: <A, E>(effect: Effect.Effect<A, E, never>) => Promise<A> }
|
|
20
|
+
|
|
21
|
+
type Callable = (...args: Array<unknown>) => unknown
|
|
22
|
+
|
|
23
|
+
function isCallable(value: unknown): value is Callable {
|
|
24
|
+
return typeof value === 'function'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isRuntimeEffect(value: unknown): value is Effect.Effect<unknown, never, never> {
|
|
28
|
+
return Effect.isEffect(value)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Wraps an Effect so it can be `await`-ed as if it were a Promise, while still
|
|
33
|
+
* passing `Effect.isEffect` checks and exposing the underlying Effect properties.
|
|
34
|
+
*
|
|
35
|
+
* Implementation: returns a `Proxy` around the Effect that only intercepts the
|
|
36
|
+
* PromiseLike methods (`then`/`catch`/`finally`) plus `Symbol.toStringTag`. The
|
|
37
|
+
* Promise is created lazily on first `await` and cached for the wrapper's
|
|
38
|
+
* lifetime so repeated awaits share a result. The original Effect object is
|
|
39
|
+
* never mutated.
|
|
40
|
+
*/
|
|
41
|
+
export function toAwaitableEffect<A, E>(
|
|
42
|
+
effect: Effect.Effect<A, E, never>,
|
|
43
|
+
options?: AwaitableEffectOptions,
|
|
44
|
+
): AwaitableEffect<A, E> {
|
|
45
|
+
const runPromise = options?.runPromise ?? Effect.runPromise
|
|
46
|
+
let cachedPromise: Promise<A> | undefined
|
|
47
|
+
const getPromise = (): Promise<A> => (cachedPromise ??= runPromise(effect))
|
|
48
|
+
|
|
49
|
+
const then: Promise<A>['then'] = (onFulfilled, onRejected) => getPromise().then(onFulfilled, onRejected)
|
|
50
|
+
const catchMethod: Promise<A>['catch'] = (onRejected) => getPromise().catch(onRejected)
|
|
51
|
+
const finallyMethod: Promise<A>['finally'] = (onFinally) => getPromise().finally(onFinally)
|
|
52
|
+
|
|
53
|
+
return new Proxy(effect as object, {
|
|
54
|
+
get(target, property, receiver) {
|
|
55
|
+
if (property === 'then') return then
|
|
56
|
+
if (property === 'catch') return catchMethod
|
|
57
|
+
if (property === 'finally') return finallyMethod
|
|
58
|
+
if (property === Symbol.toStringTag) return 'Promise'
|
|
59
|
+
return Reflect.get(target, property, receiver) as unknown
|
|
60
|
+
},
|
|
61
|
+
}) as AwaitableEffect<A, E>
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Returns a proxy over a service whose Effect-returning methods are
|
|
66
|
+
* transparently wrapped with `toAwaitableEffect`. Non-Effect return values
|
|
67
|
+
* pass through untouched.
|
|
68
|
+
*/
|
|
69
|
+
export function toAwaitableService<T extends object>(
|
|
70
|
+
service: T,
|
|
71
|
+
options?: AwaitableEffectOptions,
|
|
72
|
+
): AwaitableService<T> {
|
|
73
|
+
const wrappedMethods = new Map<PropertyKey, Callable>()
|
|
74
|
+
|
|
75
|
+
return new Proxy(service, {
|
|
76
|
+
get(target, property, receiver) {
|
|
77
|
+
const value = Reflect.get(target, property, receiver)
|
|
78
|
+
if (!isCallable(value)) {
|
|
79
|
+
return value
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const existing = wrappedMethods.get(property)
|
|
83
|
+
if (existing) {
|
|
84
|
+
return existing
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const wrapped: Callable = (...args) => {
|
|
88
|
+
const result = value.apply(target, args)
|
|
89
|
+
return isRuntimeEffect(result) ? toAwaitableEffect(result, options) : result
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
wrappedMethods.set(property, wrapped)
|
|
93
|
+
return wrapped
|
|
94
|
+
},
|
|
95
|
+
}) as AwaitableService<T>
|
|
96
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Schema } from 'effect'
|
|
2
|
+
|
|
3
|
+
const ValidationIssueSchema = Schema.Struct({ path: Schema.String, message: Schema.String })
|
|
4
|
+
|
|
5
|
+
export type ValidationIssue = typeof ValidationIssueSchema.Type
|
|
6
|
+
|
|
7
|
+
export class DatabaseError extends Schema.TaggedErrorClass<DatabaseError>()('DatabaseError', {
|
|
8
|
+
message: Schema.String,
|
|
9
|
+
query: Schema.optional(Schema.String),
|
|
10
|
+
cause: Schema.optional(Schema.Defect),
|
|
11
|
+
}) {}
|
|
12
|
+
|
|
13
|
+
export class ServiceError extends Schema.TaggedErrorClass<ServiceError>()('ServiceError', {
|
|
14
|
+
message: Schema.String,
|
|
15
|
+
cause: Schema.optional(Schema.Defect),
|
|
16
|
+
}) {}
|
|
17
|
+
|
|
18
|
+
export class BaseServicePersistenceError extends Schema.TaggedErrorClass<BaseServicePersistenceError>()(
|
|
19
|
+
'BaseServicePersistenceError',
|
|
20
|
+
{ message: Schema.String, cause: Schema.Defect },
|
|
21
|
+
) {}
|
|
22
|
+
|
|
23
|
+
export class RedisError extends Schema.TaggedErrorClass<RedisError>()('RedisError', {
|
|
24
|
+
message: Schema.String,
|
|
25
|
+
cause: Schema.optional(Schema.Defect),
|
|
26
|
+
}) {}
|
|
27
|
+
|
|
28
|
+
export class TimeoutError extends Schema.TaggedErrorClass<TimeoutError>()('TimeoutError', {
|
|
29
|
+
operation: Schema.String,
|
|
30
|
+
ms: Schema.Number,
|
|
31
|
+
}) {}
|
|
32
|
+
|
|
33
|
+
export class LockAcquisitionError extends Schema.TaggedErrorClass<LockAcquisitionError>()('LockAcquisitionError', {
|
|
34
|
+
lockKey: Schema.String,
|
|
35
|
+
maxWaitMs: Schema.Number,
|
|
36
|
+
}) {}
|
|
37
|
+
|
|
38
|
+
export class LockLostError extends Schema.TaggedErrorClass<LockLostError>()('LockLostError', {
|
|
39
|
+
lockKey: Schema.String,
|
|
40
|
+
}) {}
|
|
41
|
+
|
|
42
|
+
export class NotFoundError extends Schema.TaggedErrorClass<NotFoundError>()('NotFoundError', {
|
|
43
|
+
resource: Schema.String,
|
|
44
|
+
id: Schema.optional(Schema.String),
|
|
45
|
+
message: Schema.String,
|
|
46
|
+
}) {}
|
|
47
|
+
|
|
48
|
+
export class BadRequestError extends Schema.TaggedErrorClass<BadRequestError>()('BadRequestError', {
|
|
49
|
+
message: Schema.String,
|
|
50
|
+
}) {}
|
|
51
|
+
|
|
52
|
+
export class ConflictError extends Schema.TaggedErrorClass<ConflictError>()('ConflictError', {
|
|
53
|
+
message: Schema.String,
|
|
54
|
+
}) {}
|
|
55
|
+
|
|
56
|
+
export class ForbiddenError extends Schema.TaggedErrorClass<ForbiddenError>()('ForbiddenError', {
|
|
57
|
+
message: Schema.String,
|
|
58
|
+
}) {}
|
|
59
|
+
|
|
60
|
+
export class ValidationError extends Schema.TaggedErrorClass<ValidationError>()('ValidationError', {
|
|
61
|
+
message: Schema.String,
|
|
62
|
+
issues: Schema.optional(Schema.Array(ValidationIssueSchema)),
|
|
63
|
+
}) {}
|
|
64
|
+
|
|
65
|
+
export class AiGenerationError extends Schema.TaggedErrorClass<AiGenerationError>()('AiGenerationError', {
|
|
66
|
+
message: Schema.String,
|
|
67
|
+
source: Schema.String,
|
|
68
|
+
status: Schema.optional(Schema.Number),
|
|
69
|
+
rateLimited: Schema.optional(Schema.Boolean),
|
|
70
|
+
retryAfter: Schema.optional(Schema.String),
|
|
71
|
+
providerData: Schema.optional(Schema.String),
|
|
72
|
+
responseBody: Schema.optional(Schema.String),
|
|
73
|
+
url: Schema.optional(Schema.String),
|
|
74
|
+
}) {}
|
|
75
|
+
|
|
76
|
+
export class ThreadTurnError extends Schema.TaggedErrorClass<ThreadTurnError>()('ThreadTurnError', {
|
|
77
|
+
message: Schema.String,
|
|
78
|
+
reason: Schema.Literals(['bad-request', 'conflict']),
|
|
79
|
+
}) {}
|
|
80
|
+
|
|
81
|
+
export class ActiveThreadRunConflictError extends Schema.TaggedErrorClass<ActiveThreadRunConflictError>()(
|
|
82
|
+
'ActiveThreadRunConflictError',
|
|
83
|
+
{ threadId: Schema.String, activeRunId: Schema.String, message: Schema.String },
|
|
84
|
+
) {}
|
|
85
|
+
|
|
86
|
+
export class ConfigurationError extends Schema.TaggedErrorClass<ConfigurationError>()('ConfigurationError', {
|
|
87
|
+
message: Schema.String,
|
|
88
|
+
key: Schema.optional(Schema.String),
|
|
89
|
+
}) {}
|
|
90
|
+
|
|
91
|
+
const EFFECT_ERROR_CLASSES = [
|
|
92
|
+
ActiveThreadRunConflictError,
|
|
93
|
+
AiGenerationError,
|
|
94
|
+
BadRequestError,
|
|
95
|
+
BaseServicePersistenceError,
|
|
96
|
+
ConfigurationError,
|
|
97
|
+
ConflictError,
|
|
98
|
+
DatabaseError,
|
|
99
|
+
ForbiddenError,
|
|
100
|
+
LockAcquisitionError,
|
|
101
|
+
LockLostError,
|
|
102
|
+
NotFoundError,
|
|
103
|
+
RedisError,
|
|
104
|
+
ServiceError,
|
|
105
|
+
ThreadTurnError,
|
|
106
|
+
TimeoutError,
|
|
107
|
+
ValidationError,
|
|
108
|
+
] as const
|
|
109
|
+
|
|
110
|
+
export type EffectError = InstanceType<(typeof EFFECT_ERROR_CLASSES)[number]>
|
|
111
|
+
|
|
112
|
+
const EFFECT_ERROR_TAGS = new Set(EFFECT_ERROR_CLASSES.map((cls) => cls.name))
|
|
113
|
+
|
|
114
|
+
export function isEffectError(error: unknown): error is EffectError {
|
|
115
|
+
if (!error || typeof error !== 'object') {
|
|
116
|
+
return false
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const tag = (error as { _tag?: unknown })._tag
|
|
120
|
+
return typeof tag === 'string' && EFFECT_ERROR_TAGS.has(tag)
|
|
121
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Cause, Effect } from 'effect'
|
|
2
|
+
|
|
3
|
+
import { ServiceError } from './errors'
|
|
4
|
+
|
|
5
|
+
export function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
|
|
6
|
+
if (typeof value !== 'object' && typeof value !== 'function') {
|
|
7
|
+
return false
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (value === null) {
|
|
11
|
+
return false
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return 'then' in value && typeof value.then === 'function'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type PromiseEffectEvaluator<A, E = never, R = never> =
|
|
18
|
+
| (() => PromiseLike<A> | Effect.Effect<A, E, R>)
|
|
19
|
+
| ((signal: AbortSignal) => PromiseLike<A> | Effect.Effect<A, E, R>)
|
|
20
|
+
|
|
21
|
+
function invokePromiseEffectEvaluator<A, E, R>(
|
|
22
|
+
evaluate: PromiseEffectEvaluator<A, E, R>,
|
|
23
|
+
): PromiseLike<A> | Effect.Effect<A, E, R> {
|
|
24
|
+
return (evaluate as () => PromiseLike<A> | Effect.Effect<A, E, R>)()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function effectTryPromise<A, E = never, R = never>(
|
|
28
|
+
evaluate: PromiseEffectEvaluator<A, E, R>,
|
|
29
|
+
): Effect.Effect<A, Cause.UnknownError, R>
|
|
30
|
+
export function effectTryPromise<A, E1, E2, R>(
|
|
31
|
+
evaluate: PromiseEffectEvaluator<A, E1, R>,
|
|
32
|
+
onError: (cause: unknown) => E2,
|
|
33
|
+
): Effect.Effect<A, E2, R>
|
|
34
|
+
export function effectTryPromise<A, E1, E2, R>(
|
|
35
|
+
evaluate: PromiseEffectEvaluator<A, E1, R>,
|
|
36
|
+
onError?: (cause: unknown) => E2,
|
|
37
|
+
): Effect.Effect<A, E2 | Cause.UnknownError, R> {
|
|
38
|
+
return Effect.suspend(() => {
|
|
39
|
+
try {
|
|
40
|
+
const value = invokePromiseEffectEvaluator(evaluate)
|
|
41
|
+
if (Effect.isEffect(value)) {
|
|
42
|
+
if (onError) {
|
|
43
|
+
return value.pipe(Effect.mapError((cause) => onError(cause)))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return value.pipe(Effect.mapError((cause) => new Cause.UnknownError(cause)))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return onError
|
|
50
|
+
? Effect.tryPromise({ try: () => value, catch: onError })
|
|
51
|
+
: Effect.tryPromise({ try: () => value, catch: (cause) => new Cause.UnknownError(cause) })
|
|
52
|
+
} catch (cause) {
|
|
53
|
+
return Effect.fail(onError ? onError(cause) : new Cause.UnknownError(cause))
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function effectTryServicePromise<A, E = never, R = never>(
|
|
59
|
+
evaluate: PromiseEffectEvaluator<A, E, R>,
|
|
60
|
+
message: string,
|
|
61
|
+
): Effect.Effect<A, ServiceError, R> {
|
|
62
|
+
return Effect.suspend(() => {
|
|
63
|
+
try {
|
|
64
|
+
const value = invokePromiseEffectEvaluator(evaluate)
|
|
65
|
+
if (Effect.isEffect(value)) {
|
|
66
|
+
return value.pipe(Effect.mapError((cause) => new ServiceError({ message, cause })))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return Effect.tryPromise({ try: () => value, catch: (cause) => new ServiceError({ message, cause }) })
|
|
70
|
+
} catch (cause) {
|
|
71
|
+
return Effect.fail(new ServiceError({ message, cause }))
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function makeEffectTryPromiseWithMessage<E>(onError: (message: string, cause: unknown) => E) {
|
|
77
|
+
return <A, E1 = never, R = never>(
|
|
78
|
+
evaluate: PromiseEffectEvaluator<A, E1, R>,
|
|
79
|
+
message: string,
|
|
80
|
+
): Effect.Effect<A, E, R> => effectTryPromise(evaluate, (cause) => onError(message, cause))
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function makeEffectTryPromiseWithOperation<E>(
|
|
84
|
+
onError: (operation: string, message: string, cause: unknown) => E,
|
|
85
|
+
) {
|
|
86
|
+
return <A, E1 = never, R = never>(
|
|
87
|
+
operation: string,
|
|
88
|
+
message: string,
|
|
89
|
+
evaluate: PromiseEffectEvaluator<A, E1, R>,
|
|
90
|
+
): Effect.Effect<A, E, R> => effectTryPromise(evaluate, (cause) => onError(operation, message, cause))
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function effectTryMaybeAsync<A>(evaluate: () => A | PromiseLike<A>): Effect.Effect<A, Cause.UnknownError, never>
|
|
94
|
+
export function effectTryMaybeAsync<A, E>(
|
|
95
|
+
evaluate: () => A | PromiseLike<A>,
|
|
96
|
+
onError: (cause: unknown) => E,
|
|
97
|
+
): Effect.Effect<A, E, never>
|
|
98
|
+
export function effectTryMaybeAsync<A, E>(
|
|
99
|
+
evaluate: () => A | PromiseLike<A>,
|
|
100
|
+
onError?: (cause: unknown) => E,
|
|
101
|
+
): Effect.Effect<A, E | Cause.UnknownError, never> {
|
|
102
|
+
return Effect.suspend(() => {
|
|
103
|
+
try {
|
|
104
|
+
const value = evaluate()
|
|
105
|
+
return isPromiseLike(value)
|
|
106
|
+
? onError
|
|
107
|
+
? effectTryPromise(() => value, onError)
|
|
108
|
+
: effectTryPromise(() => value)
|
|
109
|
+
: Effect.succeed(value)
|
|
110
|
+
} catch (cause) {
|
|
111
|
+
return Effect.fail(onError ? onError(cause) : new Cause.UnknownError(cause))
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function iterateEffect<A, E, R>(
|
|
117
|
+
initial: A,
|
|
118
|
+
options: { while: (state: A) => boolean; body: (state: A) => Effect.Effect<A, E, R> },
|
|
119
|
+
): Effect.Effect<A, E, R> {
|
|
120
|
+
const step = (state: A): Effect.Effect<A, E, R> =>
|
|
121
|
+
options.while(state) ? options.body(state).pipe(Effect.flatMap(step)) : Effect.succeed(state)
|
|
122
|
+
return Effect.suspend(() => step(initial))
|
|
123
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export * from './awaitable-effect'
|
|
2
|
+
export * from './helpers'
|
|
3
|
+
export * from './layers'
|
|
4
|
+
export * from './runtime'
|
|
5
|
+
export * from './services'
|
|
6
|
+
export * from './zod'
|
|
7
|
+
export {
|
|
8
|
+
ActiveThreadRunConflictError,
|
|
9
|
+
AiGenerationError,
|
|
10
|
+
BaseServicePersistenceError,
|
|
11
|
+
ConfigurationError,
|
|
12
|
+
ConflictError,
|
|
13
|
+
DatabaseError,
|
|
14
|
+
ForbiddenError,
|
|
15
|
+
LockAcquisitionError,
|
|
16
|
+
LockLostError,
|
|
17
|
+
RedisError,
|
|
18
|
+
ServiceError,
|
|
19
|
+
ThreadTurnError,
|
|
20
|
+
TimeoutError,
|
|
21
|
+
ValidationError,
|
|
22
|
+
isEffectError,
|
|
23
|
+
} from './errors'
|
|
24
|
+
export type { EffectError, ValidationIssue } from './errors'
|