@lota-sdk/core 0.4.8 → 0.4.9

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