@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.
Files changed (272) hide show
  1. package/package.json +11 -12
  2. package/src/ai/embedding-cache.ts +96 -22
  3. package/src/ai-gateway/ai-gateway.ts +766 -223
  4. package/src/config/agent-defaults.ts +189 -75
  5. package/src/config/agent-types.ts +54 -4
  6. package/src/config/background-processing.ts +1 -1
  7. package/src/config/constants.ts +8 -2
  8. package/src/config/index.ts +0 -1
  9. package/src/config/logger.ts +299 -19
  10. package/src/config/thread-defaults.ts +40 -20
  11. package/src/create-runtime.ts +200 -449
  12. package/src/db/base.service.ts +52 -28
  13. package/src/db/cursor-pagination.ts +71 -30
  14. package/src/db/memory-query-builder.ts +2 -1
  15. package/src/db/memory-store.helpers.ts +4 -7
  16. package/src/db/memory-store.ts +868 -601
  17. package/src/db/memory.ts +396 -280
  18. package/src/db/record-id.ts +32 -10
  19. package/src/db/schema-fingerprint.ts +30 -12
  20. package/src/db/service-normalization.ts +288 -0
  21. package/src/db/service.ts +912 -779
  22. package/src/db/startup.ts +153 -68
  23. package/src/db/transaction-conflict.ts +15 -0
  24. package/src/effect/awaitable-effect.ts +96 -0
  25. package/src/effect/errors.ts +121 -0
  26. package/src/effect/helpers.ts +123 -0
  27. package/src/effect/index.ts +24 -0
  28. package/src/effect/layers.ts +238 -0
  29. package/src/effect/runtime-ref.ts +25 -0
  30. package/src/effect/runtime.ts +46 -0
  31. package/src/effect/services.ts +61 -0
  32. package/src/effect/zod.ts +43 -0
  33. package/src/embeddings/provider.ts +128 -83
  34. package/src/index.ts +48 -1
  35. package/src/openrouter/direct-provider.ts +11 -35
  36. package/src/queues/autonomous-job.queue.ts +117 -73
  37. package/src/queues/context-compaction.queue.ts +50 -17
  38. package/src/queues/delayed-node-promotion.queue.ts +46 -17
  39. package/src/queues/document-processor.queue.ts +52 -77
  40. package/src/queues/memory-consolidation.queue.ts +47 -32
  41. package/src/queues/organization-learning.queue.ts +26 -4
  42. package/src/queues/plan-agent-heartbeat.queue.ts +71 -24
  43. package/src/queues/plan-scheduler.queue.ts +97 -33
  44. package/src/queues/post-chat-memory.queue.ts +56 -26
  45. package/src/queues/queue-factory.ts +227 -59
  46. package/src/queues/standalone-worker.ts +39 -0
  47. package/src/queues/title-generation.queue.ts +45 -11
  48. package/src/redis/connection.ts +182 -113
  49. package/src/redis/index.ts +6 -8
  50. package/src/redis/org-memory-lock.ts +60 -27
  51. package/src/redis/redis-lease-lock.ts +200 -121
  52. package/src/redis/runtime-connection.ts +20 -0
  53. package/src/redis/stream-context.ts +92 -46
  54. package/src/runtime/agent-identity-overrides.ts +2 -2
  55. package/src/runtime/agent-runtime-policy.ts +5 -2
  56. package/src/runtime/agent-stream-helpers.ts +24 -9
  57. package/src/runtime/chat-run-orchestration.ts +102 -19
  58. package/src/runtime/chat-run-registry.ts +36 -2
  59. package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
  60. package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +161 -94
  61. package/src/runtime/domain-layer.ts +192 -0
  62. package/src/runtime/execution-plan-visibility.ts +2 -2
  63. package/src/runtime/execution-plan.ts +42 -15
  64. package/src/runtime/graph-designer.ts +16 -4
  65. package/src/runtime/helper-model.ts +139 -48
  66. package/src/runtime/index.ts +7 -8
  67. package/src/runtime/indexed-repositories-policy.ts +3 -3
  68. package/src/runtime/{memory-block.ts → memory/memory-block.ts} +50 -36
  69. package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
  70. package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +54 -67
  71. package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
  72. package/src/runtime/memory/memory-scope.ts +53 -0
  73. package/src/runtime/plugin-resolution.ts +124 -25
  74. package/src/runtime/plugin-types.ts +9 -1
  75. package/src/runtime/post-turn-side-effects.ts +177 -130
  76. package/src/runtime/retrieval-adapters.ts +40 -6
  77. package/src/runtime/runtime-accessors.ts +92 -0
  78. package/src/runtime/runtime-config.ts +150 -61
  79. package/src/runtime/runtime-extensions.ts +23 -25
  80. package/src/runtime/runtime-lifecycle.ts +124 -0
  81. package/src/runtime/runtime-services.ts +386 -0
  82. package/src/runtime/runtime-token.ts +47 -0
  83. package/src/runtime/social-chat/social-chat-agent-runner.ts +159 -0
  84. package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +51 -20
  85. package/src/runtime/social-chat/social-chat.ts +630 -0
  86. package/src/runtime/specialist-runner.ts +36 -10
  87. package/src/runtime/team-consultation/team-consultation-orchestrator.ts +433 -0
  88. package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
  89. package/src/runtime/thread-chat-helpers.ts +2 -2
  90. package/src/runtime/thread-plan-turn.ts +2 -1
  91. package/src/runtime/thread-turn-context.ts +183 -111
  92. package/src/runtime/turn-lifecycle.ts +93 -27
  93. package/src/services/agent-activity.service.ts +287 -203
  94. package/src/services/agent-executor.service.ts +253 -149
  95. package/src/services/artifact.service.ts +231 -149
  96. package/src/services/attachment.service.ts +171 -115
  97. package/src/services/autonomous-job.service.ts +890 -491
  98. package/src/services/background-work.service.ts +54 -0
  99. package/src/services/chat-run-registry.service.ts +13 -1
  100. package/src/services/context-compaction.service.ts +136 -86
  101. package/src/services/document-chunk.service.ts +151 -88
  102. package/src/services/execution-plan/execution-plan-approval.ts +26 -0
  103. package/src/services/execution-plan/execution-plan-context.ts +29 -0
  104. package/src/services/execution-plan/execution-plan-graph.ts +278 -0
  105. package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
  106. package/src/services/execution-plan/execution-plan-spec.ts +75 -0
  107. package/src/services/execution-plan/execution-plan.service.ts +1041 -0
  108. package/src/services/feedback-loop.service.ts +132 -76
  109. package/src/services/global-orchestrator.service.ts +101 -168
  110. package/src/services/graph-full-routing.ts +193 -0
  111. package/src/services/index.ts +19 -21
  112. package/src/services/institutional-memory.service.ts +213 -125
  113. package/src/services/learned-skill.service.ts +368 -260
  114. package/src/services/memory/memory-conversation.ts +95 -0
  115. package/src/services/memory/memory-errors.ts +27 -0
  116. package/src/services/memory/memory-org-memory.ts +50 -0
  117. package/src/services/memory/memory-preseeded.ts +86 -0
  118. package/src/services/memory/memory-rerank.ts +297 -0
  119. package/src/services/{memory-utils.ts → memory/memory-utils.ts} +6 -5
  120. package/src/services/memory/memory.service.ts +674 -0
  121. package/src/services/memory/rerank.service.ts +201 -0
  122. package/src/services/monitoring-window.service.ts +92 -70
  123. package/src/services/mutating-approval.service.ts +62 -53
  124. package/src/services/node-workspace.service.ts +141 -98
  125. package/src/services/notification.service.ts +29 -16
  126. package/src/services/organization-member.service.ts +120 -66
  127. package/src/services/organization.service.ts +153 -77
  128. package/src/services/ownership-dispatcher.service.ts +456 -263
  129. package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
  130. package/src/services/plan/plan-agent-query.service.ts +322 -0
  131. package/src/services/{plan-approval.service.ts → plan/plan-approval.service.ts} +45 -22
  132. package/src/services/plan/plan-artifact.service.ts +60 -0
  133. package/src/services/plan/plan-builder.service.ts +76 -0
  134. package/src/services/plan/plan-checkpoint.service.ts +103 -0
  135. package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
  136. package/src/services/plan/plan-completion-side-effects.ts +169 -0
  137. package/src/services/plan/plan-coordination.service.ts +181 -0
  138. package/src/services/plan/plan-cycle.service.ts +405 -0
  139. package/src/services/plan/plan-deadline.service.ts +533 -0
  140. package/src/services/plan/plan-event-delivery.service.ts +266 -0
  141. package/src/services/plan/plan-executor-context.ts +35 -0
  142. package/src/services/plan/plan-executor-graph.ts +522 -0
  143. package/src/services/plan/plan-executor-helpers.ts +307 -0
  144. package/src/services/plan/plan-executor-persistence.ts +209 -0
  145. package/src/services/plan/plan-executor.service.ts +1737 -0
  146. package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
  147. package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
  148. package/src/services/plan/plan-run-serialization.ts +15 -0
  149. package/src/services/plan/plan-run.service.ts +637 -0
  150. package/src/services/plan/plan-scheduler.service.ts +379 -0
  151. package/src/services/plan/plan-template.service.ts +224 -0
  152. package/src/services/plan/plan-transaction-events.ts +36 -0
  153. package/src/services/plan/plan-validator.service.ts +907 -0
  154. package/src/services/plan/plan-workspace.service.ts +131 -0
  155. package/src/services/plugin-executor.service.ts +102 -68
  156. package/src/services/quality-metrics.service.ts +112 -94
  157. package/src/services/queue-job.service.ts +288 -231
  158. package/src/services/recent-activity-title.service.ts +73 -36
  159. package/src/services/recent-activity.service.ts +274 -259
  160. package/src/services/skill-resolver.service.ts +38 -12
  161. package/src/services/social-chat-history.service.ts +190 -122
  162. package/src/services/system-executor.service.ts +96 -61
  163. package/src/services/thread/thread-active-run.ts +203 -0
  164. package/src/services/thread/thread-bootstrap.ts +385 -0
  165. package/src/services/thread/thread-listing.ts +199 -0
  166. package/src/services/thread/thread-memory-block.ts +130 -0
  167. package/src/services/thread/thread-message.service.ts +379 -0
  168. package/src/services/thread/thread-record-store.ts +155 -0
  169. package/src/services/thread/thread-title.service.ts +74 -0
  170. package/src/services/thread/thread-turn-execution.ts +280 -0
  171. package/src/services/thread/thread-turn-message-context.ts +73 -0
  172. package/src/services/thread/thread-turn-preparation.service.ts +1148 -0
  173. package/src/services/thread/thread-turn-streaming.ts +403 -0
  174. package/src/services/thread/thread-turn-tracing.ts +35 -0
  175. package/src/services/thread/thread-turn.ts +376 -0
  176. package/src/services/thread/thread.service.ts +344 -0
  177. package/src/services/user.service.ts +82 -32
  178. package/src/services/write-intent-validator.service.ts +63 -51
  179. package/src/storage/attachment-parser.ts +69 -27
  180. package/src/storage/attachment-storage.service.ts +334 -275
  181. package/src/storage/generated-document-storage.service.ts +66 -34
  182. package/src/system-agents/agent-result.ts +3 -1
  183. package/src/system-agents/context-compaction.agent.ts +3 -3
  184. package/src/system-agents/delegated-agent-factory.ts +159 -90
  185. package/src/system-agents/helper-agent-options.ts +1 -1
  186. package/src/system-agents/memory-reranker.agent.ts +3 -3
  187. package/src/system-agents/memory.agent.ts +3 -3
  188. package/src/system-agents/recent-activity-title-refiner.agent.ts +3 -3
  189. package/src/system-agents/regular-chat-memory-digest.agent.ts +3 -3
  190. package/src/system-agents/skill-extractor.agent.ts +3 -3
  191. package/src/system-agents/skill-manager.agent.ts +3 -3
  192. package/src/system-agents/thread-router.agent.ts +157 -113
  193. package/src/system-agents/title-generator.agent.ts +3 -3
  194. package/src/tools/execution-plan.tool.ts +241 -171
  195. package/src/tools/fetch-webpage.tool.ts +29 -18
  196. package/src/tools/firecrawl-client.ts +26 -6
  197. package/src/tools/index.ts +1 -0
  198. package/src/tools/memory-block.tool.ts +14 -6
  199. package/src/tools/plan-approval.tool.ts +57 -47
  200. package/src/tools/read-file-parts.tool.ts +44 -33
  201. package/src/tools/remember-memory.tool.ts +65 -45
  202. package/src/tools/search-web.tool.ts +33 -22
  203. package/src/tools/search.tool.ts +41 -29
  204. package/src/tools/team-think.tool.ts +125 -84
  205. package/src/tools/user-questions.tool.ts +4 -3
  206. package/src/tools/web-tool-shared.ts +6 -0
  207. package/src/utils/async.ts +25 -22
  208. package/src/utils/crypto.ts +21 -0
  209. package/src/utils/date-time.ts +40 -1
  210. package/src/utils/errors.ts +111 -20
  211. package/src/utils/hono-error-handler.ts +24 -39
  212. package/src/utils/index.ts +2 -1
  213. package/src/utils/null-proto-record.ts +41 -0
  214. package/src/utils/sse-keepalive.ts +124 -21
  215. package/src/workers/bootstrap.ts +164 -52
  216. package/src/workers/memory-consolidation.worker.ts +325 -237
  217. package/src/workers/organization-learning.worker.ts +50 -16
  218. package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
  219. package/src/workers/regular-chat-memory-digest.runner.ts +185 -114
  220. package/src/workers/skill-extraction.runner.ts +176 -93
  221. package/src/workers/utils/file-section-chunker.ts +8 -10
  222. package/src/workers/utils/repo-structure-extractor.ts +349 -260
  223. package/src/workers/utils/repomix-file-sections.ts +2 -2
  224. package/src/workers/utils/thread-message-query.ts +97 -38
  225. package/src/workers/worker-utils.ts +74 -31
  226. package/src/config/debug-logger.ts +0 -47
  227. package/src/config/search.ts +0 -3
  228. package/src/redis/connection-accessor.ts +0 -26
  229. package/src/runtime/agent-types.ts +0 -1
  230. package/src/runtime/context-compaction-runtime.ts +0 -87
  231. package/src/runtime/memory-scope.ts +0 -43
  232. package/src/runtime/social-chat-agent-runner.ts +0 -118
  233. package/src/runtime/social-chat.ts +0 -516
  234. package/src/runtime/team-consultation-orchestrator.ts +0 -272
  235. package/src/services/adaptive-playbook.service.ts +0 -152
  236. package/src/services/artifact-provenance.service.ts +0 -172
  237. package/src/services/chat-attachments.service.ts +0 -17
  238. package/src/services/context-compaction-runtime.singleton.ts +0 -13
  239. package/src/services/execution-plan.service.ts +0 -1118
  240. package/src/services/memory.service.ts +0 -914
  241. package/src/services/plan-agent-heartbeat.service.ts +0 -136
  242. package/src/services/plan-agent-query.service.ts +0 -267
  243. package/src/services/plan-artifact.service.ts +0 -50
  244. package/src/services/plan-builder.service.ts +0 -67
  245. package/src/services/plan-checkpoint.service.ts +0 -81
  246. package/src/services/plan-completion-side-effects.ts +0 -80
  247. package/src/services/plan-coordination.service.ts +0 -157
  248. package/src/services/plan-cycle.service.ts +0 -284
  249. package/src/services/plan-deadline.service.ts +0 -430
  250. package/src/services/plan-event-delivery.service.ts +0 -166
  251. package/src/services/plan-executor.service.ts +0 -1950
  252. package/src/services/plan-run.service.ts +0 -515
  253. package/src/services/plan-scheduler.service.ts +0 -240
  254. package/src/services/plan-template.service.ts +0 -177
  255. package/src/services/plan-validator.service.ts +0 -818
  256. package/src/services/plan-workspace.service.ts +0 -83
  257. package/src/services/rerank.service.ts +0 -156
  258. package/src/services/thread-message.service.ts +0 -275
  259. package/src/services/thread-plan-registry.service.ts +0 -22
  260. package/src/services/thread-title.service.ts +0 -39
  261. package/src/services/thread-turn-preparation.service.ts +0 -1147
  262. package/src/services/thread-turn.ts +0 -172
  263. package/src/services/thread.service.ts +0 -869
  264. package/src/utils/env.ts +0 -8
  265. /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
  266. /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
  267. /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
  268. /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
  269. /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
  270. /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
  271. /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
  272. /package/src/services/{thread.types.ts → thread/thread.types.ts} +0 -0
@@ -1,7 +1,8 @@
1
+ import { Effect } from 'effect'
1
2
  import type { RecordIdValue } from 'surrealdb'
2
3
  import { RecordId, StringRecordId } from 'surrealdb'
3
4
 
4
- import { BadRequestError } from '../utils/errors'
5
+ import { BadRequestError } from '../effect/errors'
5
6
 
6
7
  export interface RecordIdShape {
7
8
  tb: string
@@ -21,13 +22,6 @@ interface NamedConstructor {
21
22
  name?: unknown
22
23
  }
23
24
 
24
- class InvalidRecordIdError extends BadRequestError {
25
- constructor(message: string) {
26
- super(message)
27
- this.name = 'InvalidRecordIdError'
28
- }
29
- }
30
-
31
25
  export function isSurrealRecordIdValue(value: unknown): boolean {
32
26
  if (!value || typeof value !== 'object') {
33
27
  return false
@@ -42,6 +36,23 @@ export function isSurrealRecordIdValue(value: unknown): boolean {
42
36
  return typeof constructorName === 'string' && SURREAL_RECORD_ID_CLASS_NAMES.has(constructorName)
43
37
  }
44
38
 
39
+ export function isRecordIdInput(value: unknown): value is RecordIdInput {
40
+ if (typeof value === 'string' || value instanceof RecordId || value instanceof StringRecordId) {
41
+ return true
42
+ }
43
+
44
+ if (isSurrealRecordIdValue(value)) {
45
+ return true
46
+ }
47
+
48
+ if (!value || typeof value !== 'object') {
49
+ return false
50
+ }
51
+
52
+ const record = value as { tb?: unknown; id?: unknown }
53
+ return typeof record.tb === 'string' && record.id !== undefined
54
+ }
55
+
45
56
  export function ensureRecordId(value: RecordIdInput, fallbackTable?: string): RecordId {
46
57
  if (value instanceof RecordId) {
47
58
  return value
@@ -58,7 +69,7 @@ export function ensureRecordId(value: RecordIdInput, fallbackTable?: string): Re
58
69
  return new RecordId(tableIdMatch[1], tableIdMatch[2])
59
70
  }
60
71
  if (!fallbackTable) {
61
- throw new InvalidRecordIdError('Missing table for record id')
72
+ throw new BadRequestError({ message: 'Missing table for record id' })
62
73
  }
63
74
  return new RecordId(fallbackTable, value)
64
75
  }
@@ -70,9 +81,20 @@ export function ensureRecordId(value: RecordIdInput, fallbackTable?: string): Re
70
81
  }
71
82
  }
72
83
 
73
- throw new InvalidRecordIdError('Invalid record id value')
84
+ throw new BadRequestError({ message: 'Invalid record id value' })
74
85
  }
75
86
 
76
87
  export function recordIdToString(value: RecordIdInput, fallbackTable?: string): string {
77
88
  return ensureRecordId(value, fallbackTable).toString()
78
89
  }
90
+
91
+ export function ensureRecordIdEffect(
92
+ value: RecordIdInput,
93
+ fallbackTable?: string,
94
+ ): Effect.Effect<RecordId, BadRequestError> {
95
+ return Effect.try({
96
+ try: () => ensureRecordId(value, fallbackTable),
97
+ catch: (error) =>
98
+ new BadRequestError({ message: error instanceof Error ? error.message : 'Invalid record id value' }),
99
+ })
100
+ }
@@ -1,20 +1,38 @@
1
+ import { Schema, Effect } from 'effect'
2
+
3
+ import { createSha256Hasher } from '../utils/crypto'
4
+
1
5
  function toSchemaFilePath(value: string | URL): string {
2
6
  return value instanceof URL ? value.pathname : value
3
7
  }
4
8
 
5
- export async function computeSchemaFingerprint(schemaFiles: readonly (string | URL)[]): Promise<string> {
6
- const hash = new Bun.CryptoHasher('sha256')
9
+ class SchemaFingerprintError extends Schema.TaggedErrorClass<SchemaFingerprintError>()('SchemaFingerprintError', {
10
+ message: Schema.String,
11
+ cause: Schema.Defect,
12
+ }) {}
13
+
14
+ export function computeSchemaFingerprint(schemaFiles: readonly (string | URL)[]): Promise<string> {
15
+ return Effect.runPromise(
16
+ Effect.gen(function* () {
17
+ const hash = createSha256Hasher()
18
+
19
+ for (const schemaFile of schemaFiles) {
20
+ const sortKey = toSchemaFilePath(schemaFile)
21
+ const file = schemaFile instanceof URL ? Bun.file(schemaFile.pathname) : Bun.file(schemaFile)
22
+
23
+ hash.update(sortKey)
24
+ hash.update('\0')
7
25
 
8
- // Sequential reads required: hash must be computed in deterministic file order
9
- for (const schemaFile of schemaFiles) {
10
- const sortKey = toSchemaFilePath(schemaFile)
11
- const file = schemaFile instanceof URL ? Bun.file(schemaFile.pathname) : Bun.file(schemaFile)
26
+ const contents = yield* Effect.tryPromise({
27
+ try: () => file.text(),
28
+ catch: (cause) => new SchemaFingerprintError({ message: `Failed to read schema file ${sortKey}.`, cause }),
29
+ })
12
30
 
13
- hash.update(sortKey)
14
- hash.update('\0')
15
- hash.update((await file.text()).trim())
16
- hash.update('\0')
17
- }
31
+ hash.update(contents.trim())
32
+ hash.update('\0')
33
+ }
18
34
 
19
- return hash.digest('hex')
35
+ return hash.digest('hex')
36
+ }),
37
+ )
20
38
  }
@@ -0,0 +1,288 @@
1
+ /**
2
+ * Normalization helpers for SurrealDB service inputs.
3
+ *
4
+ * Helpers that can fail return `Effect<T, SurrealDBError>` so callers can
5
+ * `yield*` them inside `Effect.gen` and use `catchTag('SurrealDBError')`
6
+ * without losing the typed failure channel.
7
+ */
8
+ import { Effect, Schema } from 'effect'
9
+ import { BoundQuery, RecordId, StringRecordId, Table, and, eq } from 'surrealdb'
10
+ import type { ExprLike, Mutation } from 'surrealdb'
11
+ import type { z } from 'zod'
12
+
13
+ import { isRecord } from '../utils/string'
14
+ import type { RecordIdInput } from './record-id'
15
+ import { ensureRecordId, isRecordIdInput, isSurrealRecordIdValue } from './record-id'
16
+ import type { DatabaseTable } from './tables'
17
+
18
+ export class SurrealDBError extends Schema.TaggedErrorClass<SurrealDBError>()('SurrealDBError', {
19
+ message: Schema.String,
20
+ query: Schema.optional(Schema.String),
21
+ cause: Schema.optional(Schema.Defect),
22
+ }) {}
23
+
24
+ export type RecordMutation = Extract<Mutation, 'content' | 'replace' | 'merge'>
25
+
26
+ type TransactionQueryDescriptor = { query: string; bindings?: Record<string, unknown> }
27
+
28
+ type ParseSchema = <TSchema extends z.ZodTypeAny>(schema: TSchema, value: unknown) => z.infer<TSchema>
29
+
30
+ function isSurrealDBError(error: unknown): error is SurrealDBError {
31
+ return isRecord(error) && error._tag === 'SurrealDBError'
32
+ }
33
+
34
+ function toSurrealDBError(error: unknown, fallbackMessage: string): SurrealDBError {
35
+ if (isSurrealDBError(error)) {
36
+ return error
37
+ }
38
+ if (error instanceof Error) {
39
+ return new SurrealDBError({ message: `${fallbackMessage}: ${error.message}`, cause: error })
40
+ }
41
+ return new SurrealDBError({ message: fallbackMessage })
42
+ }
43
+
44
+ function isExplicitRecordIdValue(value: unknown): value is RecordIdInput {
45
+ if (value instanceof RecordId || value instanceof StringRecordId) {
46
+ return true
47
+ }
48
+
49
+ if (isSurrealRecordIdValue(value)) {
50
+ return true
51
+ }
52
+
53
+ if (!isRecord(value)) {
54
+ return false
55
+ }
56
+
57
+ return typeof value.tb === 'string' && value.id !== undefined && Object.keys(value).length === 2
58
+ }
59
+
60
+ export function configureMutation<
61
+ TBuilder extends {
62
+ content: (data: Record<string, unknown>) => TBuilder
63
+ replace: (data: Record<string, unknown>) => TBuilder
64
+ merge: (data: Record<string, unknown>) => TBuilder
65
+ },
66
+ >(builder: TBuilder, mutation: RecordMutation, data: Record<string, unknown>): TBuilder {
67
+ if (mutation === 'content') {
68
+ return builder.content(data)
69
+ }
70
+ if (mutation === 'replace') {
71
+ return builder.replace(data)
72
+ }
73
+ return builder.merge(data)
74
+ }
75
+
76
+ function isTransactionQueryDescriptor(value: unknown): value is TransactionQueryDescriptor {
77
+ return isRecord(value) && typeof value.query === 'string'
78
+ }
79
+
80
+ export function normalizeTransactionRecordId(value: unknown, context: string): Effect.Effect<RecordId, SurrealDBError> {
81
+ if (!isRecordIdInput(value)) {
82
+ return Effect.fail(new SurrealDBError({ message: `Invalid record id for ${context}` }))
83
+ }
84
+
85
+ return Effect.try({
86
+ try: () => ensureRecordId(value),
87
+ catch: (error) => toSurrealDBError(error, `Invalid record id for ${context}`),
88
+ })
89
+ }
90
+
91
+ export function normalizeRecordIdForTable(
92
+ id: unknown,
93
+ table: DatabaseTable,
94
+ ): Effect.Effect<ReturnType<typeof ensureRecordId>, SurrealDBError> {
95
+ if (!isRecordIdInput(id)) {
96
+ return Effect.fail(new SurrealDBError({ message: `Invalid record id for table "${table}"` }))
97
+ }
98
+
99
+ return Effect.try({
100
+ try: () => {
101
+ const recordId = ensureRecordId(id, table)
102
+ const resolvedTable = String(recordId.table)
103
+ if (resolvedTable !== table) {
104
+ throw new SurrealDBError({
105
+ message: `Record id table mismatch: expected "${table}" but got "${resolvedTable}"`,
106
+ })
107
+ }
108
+ return recordId
109
+ },
110
+ catch: (error) => toSurrealDBError(error, `Invalid record id for table "${table}"`),
111
+ })
112
+ }
113
+
114
+ export function normalizeSurrealValue(value: unknown): unknown {
115
+ if (
116
+ value === null ||
117
+ value === undefined ||
118
+ value instanceof Date ||
119
+ value instanceof RecordId ||
120
+ value instanceof StringRecordId ||
121
+ value instanceof Table
122
+ ) {
123
+ return value
124
+ }
125
+
126
+ if (Array.isArray(value)) {
127
+ return value.map((entry) => normalizeSurrealValue(entry))
128
+ }
129
+
130
+ if (isExplicitRecordIdValue(value)) {
131
+ return ensureRecordId(value)
132
+ }
133
+
134
+ if (!isRecord(value)) {
135
+ return value
136
+ }
137
+
138
+ return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, normalizeSurrealValue(entry)]))
139
+ }
140
+
141
+ export function normalizeQueryRows(
142
+ statement: unknown,
143
+ schema: z.ZodTypeAny | undefined,
144
+ parseSchema: ParseSchema,
145
+ ): unknown[] {
146
+ if (Array.isArray(statement)) {
147
+ return schema ? statement.map((row) => parseSchema(schema, row)) : statement
148
+ }
149
+ if (statement === null || statement === undefined) {
150
+ return []
151
+ }
152
+ return schema ? [parseSchema(schema, statement)] : [statement]
153
+ }
154
+
155
+ export function assertValidIdentifier(name: string, context: string): Effect.Effect<void, SurrealDBError> {
156
+ if (!/^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(name)) {
157
+ return Effect.fail(new SurrealDBError({ message: `Invalid ${context}: "${name}"` }))
158
+ }
159
+ return Effect.void
160
+ }
161
+
162
+ export function buildFilterExpression(filter: Record<string, unknown>): ExprLike | undefined {
163
+ const entries = Object.entries(filter)
164
+ if (entries.length === 0) {
165
+ return undefined
166
+ }
167
+
168
+ const expressions = entries.map(([key, value]) => eq(key, normalizeSurrealValue(value)))
169
+ if (expressions.length === 1) {
170
+ return expressions[0]
171
+ }
172
+
173
+ return and(...expressions)
174
+ }
175
+
176
+ export function buildBoundFilterClauses(
177
+ filter: Record<string, unknown>,
178
+ ): Effect.Effect<{ clause: string; bindings: Record<string, unknown> }, SurrealDBError> {
179
+ const entries = Object.entries(filter)
180
+ if (entries.length === 0) {
181
+ return Effect.fail(new SurrealDBError({ message: 'Expected a non-empty filter' }))
182
+ }
183
+
184
+ return Effect.gen(function* () {
185
+ const bindings: Record<string, unknown> = {}
186
+ const clauses: string[] = []
187
+ for (let index = 0; index < entries.length; index++) {
188
+ const [field, value] = entries[index]
189
+ yield* assertValidIdentifier(field, 'filter field')
190
+
191
+ const bindingKey = `filter_${index}`
192
+ bindings[bindingKey] = yield* Effect.try({
193
+ try: () => normalizeSurrealValue(value),
194
+ catch: (error) => toSurrealDBError(error, `Invalid filter value for field "${field}"`),
195
+ })
196
+ clauses.push(`${field} = $${bindingKey}`)
197
+ }
198
+
199
+ return { clause: clauses.join(' AND '), bindings }
200
+ })
201
+ }
202
+
203
+ export function normalizeBoundQuery<T extends unknown[] = unknown[]>(
204
+ query: BoundQuery<T>,
205
+ ): Effect.Effect<BoundQuery<T>, SurrealDBError> {
206
+ return Effect.map(normalizeBindings(query.bindings), (bindings) => new BoundQuery<T>(query.query, bindings))
207
+ }
208
+
209
+ export function normalizeTransactionQuery(query: unknown): Effect.Effect<BoundQuery, SurrealDBError> {
210
+ if (query instanceof BoundQuery) {
211
+ return Effect.map(normalizeBindings(query.bindings), (bindings) => new BoundQuery(query.query, bindings))
212
+ }
213
+
214
+ if (isTransactionQueryDescriptor(query)) {
215
+ return Effect.map(normalizeBindings(query.bindings), (bindings) => new BoundQuery(query.query, bindings))
216
+ }
217
+
218
+ return Effect.fail(new SurrealDBError({ message: 'Invalid transaction query' }))
219
+ }
220
+
221
+ function normalizeBindings(
222
+ bindings?: Record<string, unknown>,
223
+ ): Effect.Effect<Record<string, unknown> | undefined, SurrealDBError> {
224
+ if (!bindings) {
225
+ return Effect.void as Effect.Effect<undefined, SurrealDBError>
226
+ }
227
+
228
+ return Effect.try({
229
+ try: () => {
230
+ const normalized = normalizeSurrealValue(bindings)
231
+ if (!isRecord(normalized)) {
232
+ throw new SurrealDBError({ message: 'Invalid bindings value' })
233
+ }
234
+ return normalized
235
+ },
236
+ catch: (error) => toSurrealDBError(error, 'Invalid bindings value'),
237
+ })
238
+ }
239
+
240
+ export function normalizeMutationData(data: Record<string, unknown>): Record<string, unknown> {
241
+ return Object.fromEntries(
242
+ Object.entries(data)
243
+ .map(([key, value]) => [key, value === undefined ? undefined : normalizeSurrealValue(value)] as const)
244
+ .filter((entry): entry is readonly [string, unknown] => entry[1] !== undefined),
245
+ )
246
+ }
247
+
248
+ export function normalizeTableValue(value: unknown): Effect.Effect<Table<string>, SurrealDBError> {
249
+ if (value instanceof Table) {
250
+ return Effect.succeed(new Table<string>(value.name as string))
251
+ }
252
+
253
+ if (typeof value === 'string' && value.length > 0) {
254
+ return Effect.succeed(new Table(value))
255
+ }
256
+
257
+ return Effect.fail(new SurrealDBError({ message: 'Invalid table value' }))
258
+ }
259
+
260
+ export function normalizeCreateTarget(value: unknown): Effect.Effect<Table<string> | RecordId, SurrealDBError> {
261
+ if (
262
+ isExplicitRecordIdValue(value) ||
263
+ (typeof value === 'string' && isRecordIdInput(value) && /^[A-Za-z_][A-Za-z0-9_]*:/.test(value))
264
+ ) {
265
+ return Effect.try({
266
+ try: () => ensureRecordId(value),
267
+ catch: (error) => toSurrealDBError(error, 'Invalid record id'),
268
+ })
269
+ }
270
+
271
+ return normalizeTableValue(value)
272
+ }
273
+
274
+ export function describeInvalidValue(value: unknown): string {
275
+ if (typeof value === 'string') {
276
+ return value
277
+ }
278
+
279
+ try {
280
+ const serialized = JSON.stringify(value)
281
+ if (typeof serialized === 'string') {
282
+ return serialized
283
+ }
284
+ return Object.prototype.toString.call(value)
285
+ } catch {
286
+ return Object.prototype.toString.call(value)
287
+ }
288
+ }