@wirecat/cli-core 0.399.0 → 0.401.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/README.md +1 -1
  3. package/dist/analysis/consents.d.ts +11 -6
  4. package/dist/analysis/consents.js +5 -2
  5. package/dist/cli/bot/mcp.js +2 -1
  6. package/dist/cli/bot/store.js +1 -1
  7. package/dist/cli/config-command.js +20 -1
  8. package/dist/cli/context.d.ts +2 -0
  9. package/dist/cli/context.js +1 -1
  10. package/dist/cli/effects.d.ts +4 -1
  11. package/dist/cli/effects.js +10 -2
  12. package/dist/cli/index.d.ts +3 -2
  13. package/dist/cli/index.js +3 -2
  14. package/dist/cli/json-fields.js +12 -1
  15. package/dist/cli/meeting-evidence-answer.d.ts +46 -0
  16. package/dist/cli/meeting-evidence-answer.js +22 -0
  17. package/dist/cli/meeting-preparation-answer.js +1 -13
  18. package/dist/cli/messenger/account-command.js +15 -6
  19. package/dist/cli/messenger/account-profile-query.d.ts +2 -0
  20. package/dist/cli/messenger/account-profile-query.js +13 -0
  21. package/dist/cli/messenger/account-sessions-command.js +1 -2
  22. package/dist/cli/messenger/admin-commands.js +12 -12
  23. package/dist/cli/messenger/admin-contacts-command.js +6 -3
  24. package/dist/cli/messenger/admin-folders-command.js +5 -5
  25. package/dist/cli/messenger/admin-moderation-command.js +4 -4
  26. package/dist/cli/messenger/admin-statistics-command.js +6 -14
  27. package/dist/cli/messenger/analysis-consents-query.d.ts +5 -0
  28. package/dist/cli/messenger/analysis-consents-query.js +20 -0
  29. package/dist/cli/messenger/archive-commands.js +3 -3
  30. package/dist/cli/messenger/attachments-command.js +3 -0
  31. package/dist/cli/messenger/backfill-command.js +5 -4
  32. package/dist/cli/messenger/chat-queries.d.ts +1 -0
  33. package/dist/cli/messenger/chat-queries.js +12 -2
  34. package/dist/cli/messenger/chats-command.js +2 -2
  35. package/dist/cli/messenger/chats-documents-command.js +7 -5
  36. package/dist/cli/messenger/chats-members-command.js +7 -3
  37. package/dist/cli/messenger/chats-read-command.js +1 -1
  38. package/dist/cli/messenger/chats-tracking-command.js +14 -6
  39. package/dist/cli/messenger/contacts-command.js +9 -5
  40. package/dist/cli/messenger/conversations-command.js +20 -16
  41. package/dist/cli/messenger/doctor-command.js +4 -1
  42. package/dist/cli/messenger/document-links-query.d.ts +2 -0
  43. package/dist/cli/messenger/document-links-query.js +9 -0
  44. package/dist/cli/messenger/download-command.js +1 -0
  45. package/dist/cli/messenger/flood-command.d.ts +2 -2
  46. package/dist/cli/messenger/flood-command.js +3 -3
  47. package/dist/cli/messenger/gaps-command.js +2 -2
  48. package/dist/cli/messenger/guard-commands.js +35 -13
  49. package/dist/cli/messenger/images-command.js +1 -0
  50. package/dist/cli/messenger/media-archive-command.js +2 -0
  51. package/dist/cli/messenger/media-archive-move-command.js +1 -0
  52. package/dist/cli/messenger/media-archive-timer-command.js +1 -1
  53. package/dist/cli/messenger/mentions-command.js +15 -2
  54. package/dist/cli/messenger/message-writes.d.ts +192 -2
  55. package/dist/cli/messenger/message-writes.js +6 -2
  56. package/dist/cli/messenger/messages-command.js +2 -0
  57. package/dist/cli/messenger/messages-delete-command.js +1 -0
  58. package/dist/cli/messenger/messages-edit-command.js +1 -0
  59. package/dist/cli/messenger/messages-evidence-command.js +3 -1
  60. package/dist/cli/messenger/messages-forward-command.js +1 -0
  61. package/dist/cli/messenger/messages-pin-command.js +3 -0
  62. package/dist/cli/messenger/messages-send-command.js +10 -6
  63. package/dist/cli/messenger/metadata-command.js +6 -1
  64. package/dist/cli/messenger/models-command.js +10 -5
  65. package/dist/cli/messenger/polls-command.js +4 -0
  66. package/dist/cli/messenger/private-people-command.js +3 -3
  67. package/dist/cli/messenger/rankings-command.js +6 -11
  68. package/dist/cli/messenger/reactions-command.js +2 -0
  69. package/dist/cli/messenger/recipients-query.d.ts +5 -0
  70. package/dist/cli/messenger/recipients-query.js +18 -0
  71. package/dist/cli/messenger/records-command.js +13 -8
  72. package/dist/cli/messenger/review.js +1 -1
  73. package/dist/cli/messenger/schedule-command.js +2 -2
  74. package/dist/cli/messenger/search-answers.js +2 -0
  75. package/dist/cli/messenger/search-command.js +3 -1
  76. package/dist/cli/messenger/searches-command.js +5 -1
  77. package/dist/cli/messenger/sends-query.d.ts +5 -0
  78. package/dist/cli/messenger/sends-query.js +33 -0
  79. package/dist/cli/messenger/serve-command.js +1 -0
  80. package/dist/cli/messenger/server-command.js +0 -3
  81. package/dist/cli/messenger/statistics-query.d.ts +14 -0
  82. package/dist/cli/messenger/statistics-query.js +143 -0
  83. package/dist/cli/messenger/stats-command.js +5 -5
  84. package/dist/cli/messenger/store-maintenance-command.js +10 -10
  85. package/dist/cli/messenger/stored.js +4 -1
  86. package/dist/cli/messenger/tags-command.js +7 -1
  87. package/dist/cli/messenger/tasks-command.js +57 -14
  88. package/dist/cli/messenger/transcribe-command.js +1 -0
  89. package/dist/cli/messenger/watch-answers.d.ts +6 -0
  90. package/dist/cli/messenger/watch-answers.js +69 -0
  91. package/dist/cli/messenger/watch-command.d.ts +1 -1
  92. package/dist/cli/messenger/watch-command.js +56 -7
  93. package/dist/cli/program.js +5 -1
  94. package/dist/cli/runs/command.js +25 -38
  95. package/dist/cli/runs/query.d.ts +5 -0
  96. package/dist/cli/runs/query.js +32 -0
  97. package/dist/cli/runs/search-command.d.ts +4 -0
  98. package/dist/cli/runs/search-command.js +27 -0
  99. package/dist/cli/runs/search-query.d.ts +4 -0
  100. package/dist/cli/runs/search-query.js +50 -0
  101. package/dist/cli/runs/search.d.ts +2 -1
  102. package/dist/cli/runs/search.js +6 -6
  103. package/dist/documents/index.d.ts +1 -0
  104. package/dist/documents/index.js +1 -0
  105. package/dist/domain/attachments.js +9 -1
  106. package/dist/domain/rankings-options.js +3 -3
  107. package/dist/entities/account-change.d.ts +89 -0
  108. package/dist/entities/account-change.js +20 -0
  109. package/dist/entities/account-profile.d.ts +7 -0
  110. package/dist/entities/account-profile.js +8 -0
  111. package/dist/entities/analysis-consent.d.ts +9 -0
  112. package/dist/entities/analysis-consent.js +4 -0
  113. package/dist/entities/attachment-results.d.ts +96 -0
  114. package/dist/entities/attachment-results.js +31 -0
  115. package/dist/entities/batch-result.d.ts +27 -0
  116. package/dist/entities/batch-result.js +18 -0
  117. package/dist/entities/chat-admin.d.ts +234 -0
  118. package/dist/entities/chat-admin.js +84 -0
  119. package/dist/entities/chat-change.d.ts +25 -0
  120. package/dist/entities/chat-change.js +11 -0
  121. package/dist/entities/chat-metadata.d.ts +34 -0
  122. package/dist/entities/chat-metadata.js +15 -0
  123. package/dist/entities/chat-moderation.d.ts +44 -0
  124. package/dist/entities/chat-moderation.js +37 -0
  125. package/dist/entities/chat-tracking.d.ts +26 -0
  126. package/dist/entities/chat-tracking.js +23 -0
  127. package/dist/entities/contact-activity.d.ts +52 -0
  128. package/dist/entities/contact-activity.js +42 -0
  129. package/dist/entities/contact-context.d.ts +1568 -0
  130. package/dist/entities/contact-context.js +191 -0
  131. package/dist/entities/contact-write.d.ts +22 -0
  132. package/dist/entities/contact-write.js +6 -0
  133. package/dist/entities/conversation-preparation.d.ts +63 -0
  134. package/dist/entities/conversation-preparation.js +23 -0
  135. package/dist/entities/conversation-results.d.ts +567 -0
  136. package/dist/entities/conversation-results.js +94 -0
  137. package/dist/entities/document-link.d.ts +13 -0
  138. package/dist/entities/document-link.js +11 -0
  139. package/dist/entities/extraction-report.d.ts +95 -0
  140. package/dist/entities/extraction-report.js +34 -0
  141. package/dist/entities/hit.d.ts +4 -0
  142. package/dist/entities/hit.js +4 -0
  143. package/dist/entities/image-embedding.d.ts +9 -0
  144. package/dist/entities/image-embedding.js +10 -0
  145. package/dist/entities/index.d.ts +8284 -1921
  146. package/dist/entities/index.js +178 -3
  147. package/dist/entities/listener.d.ts +2150 -0
  148. package/dist/entities/listener.js +46 -0
  149. package/dist/entities/media-archive.d.ts +43 -0
  150. package/dist/entities/media-archive.js +32 -0
  151. package/dist/entities/meeting-evidence.d.ts +86 -0
  152. package/dist/entities/meeting-evidence.js +25 -0
  153. package/dist/entities/meeting-preparation-parts.d.ts +14 -0
  154. package/dist/entities/meeting-preparation-parts.js +15 -13
  155. package/dist/entities/meeting-task-proposal.d.ts +14 -0
  156. package/dist/entities/meeting-task-proposal.js +19 -0
  157. package/dist/entities/meeting.d.ts +1 -1
  158. package/dist/entities/meeting.js +1 -1
  159. package/dist/entities/mention.d.ts +63 -0
  160. package/dist/entities/mention.js +44 -0
  161. package/dist/entities/message-change.d.ts +19 -0
  162. package/dist/entities/message-change.js +6 -0
  163. package/dist/entities/message-context.d.ts +617 -0
  164. package/dist/entities/message-context.js +20 -0
  165. package/dist/entities/message-read.d.ts +73 -0
  166. package/dist/entities/message-read.js +58 -0
  167. package/dist/entities/message-transfer.d.ts +379 -0
  168. package/dist/entities/message-transfer.js +83 -0
  169. package/dist/entities/message-write.d.ts +378 -0
  170. package/dist/entities/message-write.js +10 -0
  171. package/dist/entities/model-key.d.ts +14 -0
  172. package/dist/entities/model-key.js +10 -0
  173. package/dist/entities/poll.d.ts +40 -0
  174. package/dist/entities/poll.js +16 -0
  175. package/dist/entities/private-contact.d.ts +10 -0
  176. package/dist/entities/private-contact.js +4 -0
  177. package/dist/entities/reaction-change.d.ts +7 -0
  178. package/dist/entities/reaction-change.js +8 -0
  179. package/dist/entities/recipient.d.ts +29 -0
  180. package/dist/entities/recipient.js +11 -0
  181. package/dist/entities/recorded-run.d.ts +103 -0
  182. package/dist/entities/recorded-run.js +44 -0
  183. package/dist/entities/saved-search.d.ts +4 -0
  184. package/dist/entities/saved-search.js +2 -0
  185. package/dist/entities/send-attempt.d.ts +31 -0
  186. package/dist/entities/send-attempt.js +29 -0
  187. package/dist/entities/store-maintenance.d.ts +319 -0
  188. package/dist/entities/store-maintenance.js +145 -0
  189. package/dist/entities/store-records.d.ts +442 -0
  190. package/dist/entities/store-records.js +99 -0
  191. package/dist/entities/tag-change.d.ts +74 -0
  192. package/dist/entities/tag-change.js +31 -0
  193. package/dist/entities/task-pair.d.ts +31 -0
  194. package/dist/entities/task-pair.js +23 -0
  195. package/dist/entities/task-results.d.ts +524 -0
  196. package/dist/entities/task-results.js +142 -0
  197. package/dist/errors/reasons.js +3 -0
  198. package/dist/mcp/agent.d.ts +3 -1
  199. package/dist/mcp/agent.js +17 -10
  200. package/dist/mcp/bot/server.d.ts +2 -0
  201. package/dist/mcp/bot/server.js +1 -0
  202. package/dist/mcp/server.js +3 -1
  203. package/dist/mcp/tools/attachments.js +1 -0
  204. package/dist/meetings/cli/index.js +4 -2
  205. package/dist/meetings/cli/passage.js +1 -0
  206. package/dist/meetings/index.d.ts +2 -1
  207. package/dist/meetings/index.js +1 -0
  208. package/dist/meetings/links.d.ts +16 -0
  209. package/dist/meetings/read-answers.d.ts +185 -0
  210. package/dist/meetings/read-answers.js +213 -0
  211. package/dist/ports/messenger.d.ts +10 -1
  212. package/dist/runs/run.d.ts +10 -2
  213. package/dist/runs/run.js +11 -3
  214. package/dist/sends/guard.d.ts +1 -1
  215. package/dist/sends/guard.js +9 -5
  216. package/dist/sends/journal.d.ts +9 -2
  217. package/dist/sends/journal.js +9 -2
  218. package/dist/sends/permissions.d.ts +5 -1
  219. package/dist/sends/permissions.js +42 -2
  220. package/dist/sends/recipients.d.ts +8 -0
  221. package/dist/sends/recipients.js +9 -0
  222. package/dist/sends/upload.d.ts +4 -4
  223. package/dist/sends/upload.js +24 -20
  224. package/dist/services/admin-statistics.js +3 -3
  225. package/dist/services/chat-documents.d.ts +1 -0
  226. package/dist/services/chat-documents.js +9 -3
  227. package/dist/services/chats.d.ts +1 -1
  228. package/dist/services/chats.js +5 -2
  229. package/dist/services/documents/mirrors.d.ts +98 -0
  230. package/dist/services/documents/mirrors.js +144 -0
  231. package/dist/services/meeting-links.d.ts +2 -17
  232. package/dist/services/messages.js +1 -0
  233. package/dist/services/search-all-meetings.js +21 -8
  234. package/dist/services/task-rules.d.ts +1 -1
  235. package/dist/services/tasks.d.ts +2 -2
  236. package/dist/services/tasks.js +1 -1
  237. package/dist/store/exports.d.ts +1 -1
  238. package/dist/store/exports.js +1 -1
  239. package/dist/store/messages.d.ts +3 -1
  240. package/dist/store/messages.js +2 -2
  241. package/dist/store/roster.d.ts +4 -1
  242. package/dist/store/roster.js +2 -2
  243. package/dist/store/sqlite/chats.d.ts +12 -0
  244. package/dist/store/sqlite/chats.js +1 -0
  245. package/dist/store/sqlite/document-links.d.ts +1 -1
  246. package/dist/store/sqlite/document-links.js +5 -5
  247. package/dist/store/sqlite/drizzle/bun.js +3 -3
  248. package/dist/store/sqlite/drizzle/bun.js.map +1 -1
  249. package/dist/store/sqlite/drizzle/core.js +2 -2
  250. package/dist/store/sqlite/drizzle/core.js.map +1 -1
  251. package/dist/store/sqlite/drizzle/node.js +3 -3
  252. package/dist/store/sqlite/drizzle/node.js.map +1 -1
  253. package/dist/store/sqlite/drizzle/{shared-O7CTKP45.js → shared-HKXF6HKX.js} +57 -57
  254. package/dist/store/sqlite/drizzle/{shared-O7CTKP45.js.map → shared-HKXF6HKX.js.map} +1 -1
  255. package/dist/store/sqlite/involvements.js +2 -1
  256. package/dist/store/sqlite/roster.d.ts +2 -1
  257. package/dist/store/sqlite/roster.js +3 -2
  258. package/package.json +2 -2
  259. package/parity.json +14 -69
@@ -1,12 +1,18 @@
1
1
  import { googleDocumentLinks } from "../domain/google-documents.js";
2
- import { formatLocator } from "../domain/locator.js";
2
+ import { formatLocator, parseLocator } from "../domain/locator.js";
3
3
  import { accountStore } from "./deps.js";
4
4
  import { chatIdIn } from "./message-resolution.js";
5
5
  export const chatDocumentsService = (deps) => ({
6
- list: async (chat, { limit, offset }) => {
6
+ list: async (chat, { limit, offset, ref }) => {
7
7
  const store = await accountStore(deps);
8
8
  const chatId = await chatIdIn(deps.messenger, chat, store, { includeLeft: true });
9
- const items = (await store.messages.documentLinkMessages(chatId)).flatMap((message) => googleDocumentLinks(message.text).map(({ url, documentId }) => {
9
+ const target = ref === undefined ? undefined : parseLocator(ref);
10
+ const sameScope = target === undefined ||
11
+ (target.provider === store.account.provider && target.account === store.account.account && target.chat === chatId);
12
+ const messages = sameScope
13
+ ? await store.messages.documentLinkMessages(chatId, target ? { messageId: target.message } : {})
14
+ : [];
15
+ const items = messages.flatMap((message) => googleDocumentLinks(message.text).map(({ url, documentId }) => {
10
16
  const stored = documentId === null ? undefined : message.documents.find((one) => one.fileId === documentId);
11
17
  return {
12
18
  url,
@@ -79,7 +79,7 @@ export interface ChatsService {
79
79
  track?: boolean;
80
80
  }): Promise<FetchedMembers>;
81
81
  /** The chats whose members `serve` fetches daily, from the store. */
82
- tracked(): Promise<TrackedChat[]>;
82
+ tracked(query?: QueryAst): Promise<TrackedChat[]>;
83
83
  /** One chat's tracking and its member counts of the last `TRACKED_DAYS` days, tracked or not. */
84
84
  trackedChat(chat: string): Promise<{
85
85
  chatId: Id;
@@ -7,7 +7,7 @@ import { syncsOneChat } from "../ports/traits.js";
7
7
  import { typedCommand } from "../runs/scope.js";
8
8
  import { codeOf, guardedWrite } from "../sends/guarded.js";
9
9
  import { newOperationId } from "../sends/send-id.js";
10
- import { CHAT_COLUMNS, queryFilter, } from "../store/store.js";
10
+ import { CHAT_COLUMNS, queryFilter, TRACKED_CHAT_COLUMNS, } from "../store/store.js";
11
11
  import { chatStats } from "./chat-stats.js";
12
12
  import { accountStore, accountStoreIfOpen, fromStore, nothingStored, unlessBusy } from "./deps.js";
13
13
  import { memberEvents } from "./member-history.js";
@@ -210,7 +210,10 @@ export const chatsService = (deps) => ({
210
210
  const tracked = (await store.roster.trackedChats()).some((one) => one.chatId === chatId);
211
211
  return { chatId, read: members.length, participants, complete, more, ...change, tracked };
212
212
  },
213
- tracked: async () => (await accountStore(deps)).roster.trackedChats(),
213
+ tracked: async (query) => {
214
+ const where = query ? queryFilter(query, TRACKED_CHAT_COLUMNS, { command: "chats tracking list" }) : undefined;
215
+ return (await accountStore(deps)).roster.trackedChats(where ? { where } : {});
216
+ },
214
217
  memberHistory: async (chat, { since }) => {
215
218
  const store = await accountStore(deps);
216
219
  const chatId = await chatIdIn(deps.messenger, chat, store, { includeLeft: true });
@@ -0,0 +1,98 @@
1
+ import type { DocumentTarget, Keep, PushStatePort } from "../../documents/push-model.js";
2
+ import { type PushConflict, type PushOptions } from "./push.js";
3
+ /** A local folder mirrored into a remote folder, as the app's profile config keeps it. */
4
+ export interface Mirror {
5
+ id: string;
6
+ path: string;
7
+ folder: string;
8
+ }
9
+ export declare const mirrorId: (root: string, folder: string) => string;
10
+ /**
11
+ * A mirror's sync state in `<dir>/<id>.json`. A missing file is a first sync; an unreadable or invalid one is
12
+ * refused rather than reset, since a reset would push every file again as new.
13
+ */
14
+ export declare const fileMirrorState: <S>(dir: string, mirror: Mirror, principal: string, kinds: [string, ...string[]]) => PushStatePort<S>;
15
+ /** What the next sync of each mirror would create or send, from local files and the state alone. */
16
+ export declare const mirrorStatus: <S>(mirrors: Mirror[], kindOf: (path: string) => string | undefined, stateOf: (mirror: Mirror) => PushStatePort<S>) => Promise<{
17
+ id: string;
18
+ path: string;
19
+ folder: string;
20
+ create: string[];
21
+ update: string[];
22
+ unchanged: number;
23
+ orphans: string[];
24
+ unsupported: number;
25
+ remote_checked: boolean;
26
+ }[]>;
27
+ /** A conflict as the user reads it, with the remote side named the way the app names it (`google`). */
28
+ export declare const conflictReport: (conflict: PushConflict, remote: string) => {
29
+ path: string;
30
+ url: string;
31
+ reason: string;
32
+ parts?: {
33
+ [x: string]: string;
34
+ last_sync: string;
35
+ local: string;
36
+ }[] | undefined;
37
+ local_changed_at?: string | undefined;
38
+ };
39
+ export interface MirrorSyncOptions<S> extends Pick<PushOptions<S>, "principal" | "recreate" | "guard" | "operationId" | "errorOf" | "signal"> {
40
+ mirrors: Mirror[];
41
+ target: DocumentTarget<S>;
42
+ stateOf(mirror: Mirror): PushStatePort<S>;
43
+ keep?: Keep;
44
+ /** Taken once for all the mirrors, so a second sync or a watcher's next cycle waits its turn. */
45
+ lock(): Promise<{
46
+ assertHeld(): void;
47
+ release(): Promise<void> | void;
48
+ }>;
49
+ /** How the app names the remote side in conflict reports. */
50
+ remote: string;
51
+ }
52
+ /**
53
+ * Syncs each mirror both ways. A mirror with conflicts, missing remote files or errors does not stop the others;
54
+ * it gets one `mirror.partial` issue, so the caller can answer `complete: false` and fail after printing.
55
+ */
56
+ export declare const syncMirrors: <S>(options: MirrorSyncOptions<S>) => Promise<{
57
+ items: {
58
+ created: string[];
59
+ updated: string[];
60
+ pulled: string[];
61
+ unchanged: number;
62
+ missing: string[];
63
+ orphans: {
64
+ path: string;
65
+ url: string;
66
+ }[];
67
+ errors: {
68
+ path: string;
69
+ code: string;
70
+ message: string;
71
+ }[];
72
+ unsupported: number;
73
+ plan?: {
74
+ create: string[];
75
+ update: string[];
76
+ orphans: string[];
77
+ };
78
+ stopped?: true;
79
+ id: string;
80
+ path: string;
81
+ conflicts: {
82
+ path: string;
83
+ url: string;
84
+ reason: string;
85
+ parts?: {
86
+ [x: string]: string;
87
+ last_sync: string;
88
+ local: string;
89
+ }[] | undefined;
90
+ local_changed_at?: string | undefined;
91
+ }[];
92
+ }[];
93
+ issues: {
94
+ code: "provider_error";
95
+ reason: string;
96
+ source: string;
97
+ }[];
98
+ }>;
@@ -0,0 +1,144 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdirSync, readFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { CliError, writeSecurely } from "@wirecat/cli-base";
5
+ import * as v from "valibot";
6
+ import { planPush, pushDocuments, scanLocal } from "./push.js";
7
+ export const mirrorId = (root, folder) => createHash("sha256")
8
+ .update(JSON.stringify([root, folder]))
9
+ .digest("hex")
10
+ .slice(0, 8);
11
+ const ID = v.pipe(v.string(), v.regex(/^[A-Za-z0-9_-]{1,200}$/));
12
+ const stateSchema = (kinds) => v.object({
13
+ version: v.literal(1),
14
+ principal: v.string(),
15
+ root: v.string(),
16
+ target: ID,
17
+ folders: v.record(v.string(), ID),
18
+ entries: v.record(v.string(), v.object({
19
+ id: ID,
20
+ url: v.string(),
21
+ kind: v.string(),
22
+ hash: v.string(),
23
+ // Checked by kind only: the rest is the provider's own read-back.
24
+ snapshot: v.looseObject({ kind: v.picklist(kinds) }),
25
+ pushedAt: v.string(),
26
+ })),
27
+ pending: v.record(v.string(), v.object({ origin: v.string(), operationId: v.string() })),
28
+ });
29
+ /**
30
+ * A mirror's sync state in `<dir>/<id>.json`. A missing file is a first sync; an unreadable or invalid one is
31
+ * refused rather than reset, since a reset would push every file again as new.
32
+ */
33
+ export const fileMirrorState = (dir, mirror, principal, kinds) => {
34
+ const path = join(dir, `${mirror.id}.json`);
35
+ const schema = stateSchema(kinds);
36
+ return {
37
+ read: () => {
38
+ let value;
39
+ try {
40
+ value = JSON.parse(readFileSync(path, "utf8"));
41
+ }
42
+ catch (error) {
43
+ if (error.code === "ENOENT")
44
+ return {
45
+ version: 1,
46
+ principal,
47
+ root: mirror.path,
48
+ target: mirror.folder,
49
+ folders: {},
50
+ entries: {},
51
+ pending: {},
52
+ };
53
+ throw new CliError("configuration_error", "mirror state is unreadable; refusing to reset it", {
54
+ reason: "state.invalid",
55
+ });
56
+ }
57
+ const parsed = v.safeParse(schema, value);
58
+ if (!parsed.success)
59
+ throw new CliError("configuration_error", "mirror state is invalid; refusing to reset it", {
60
+ reason: "state.invalid",
61
+ });
62
+ return parsed.output;
63
+ },
64
+ save: (state) => {
65
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
66
+ writeSecurely(path, `${JSON.stringify(state)}\n`, 0o600);
67
+ },
68
+ };
69
+ };
70
+ /** What the next sync of each mirror would create or send, from local files and the state alone. */
71
+ export const mirrorStatus = async (mirrors, kindOf, stateOf) => {
72
+ const items = [];
73
+ for (const mirror of mirrors) {
74
+ const { files, unsupported } = await scanLocal(mirror.path, kindOf);
75
+ const plan = planPush(files, stateOf(mirror).read());
76
+ items.push({
77
+ id: mirror.id,
78
+ path: mirror.path,
79
+ folder: mirror.folder,
80
+ create: plan.create.map((file) => file.path),
81
+ update: plan.update.map((file) => file.path),
82
+ unchanged: plan.unchanged.length,
83
+ orphans: plan.orphans,
84
+ unsupported,
85
+ remote_checked: false,
86
+ });
87
+ }
88
+ return items;
89
+ };
90
+ /** A conflict as the user reads it, with the remote side named the way the app names it (`google`). */
91
+ export const conflictReport = (conflict, remote) => ({
92
+ path: conflict.path,
93
+ url: conflict.url,
94
+ reason: conflict.reason,
95
+ ...(conflict.parts
96
+ ? { parts: conflict.parts.map((part) => ({ last_sync: part.base, local: part.local, [remote]: part.remote })) }
97
+ : {}),
98
+ ...(conflict.localChangedAt ? { local_changed_at: conflict.localChangedAt } : {}),
99
+ ...(conflict.remoteChangedAt ? { [`${remote}_changed_at`]: conflict.remoteChangedAt } : {}),
100
+ ...(conflict.remoteChangedBy ? { [`${remote}_changed_by`]: conflict.remoteChangedBy } : {}),
101
+ });
102
+ /**
103
+ * Syncs each mirror both ways. A mirror with conflicts, missing remote files or errors does not stop the others;
104
+ * it gets one `mirror.partial` issue, so the caller can answer `complete: false` and fail after printing.
105
+ */
106
+ export const syncMirrors = async (options) => {
107
+ const { mirrors, keep, remote } = options;
108
+ const lock = await options.lock();
109
+ const results = [];
110
+ try {
111
+ for (const mirror of mirrors)
112
+ results.push({
113
+ id: mirror.id,
114
+ path: mirror.path,
115
+ ...(await pushDocuments({
116
+ root: mirror.path,
117
+ folder: mirror.folder,
118
+ target: options.target,
119
+ state: options.stateOf(mirror),
120
+ principal: options.principal,
121
+ ...(keep ? { keep } : {}),
122
+ recreate: options.recreate === true,
123
+ guard: options.guard,
124
+ operationId: options.operationId,
125
+ errorOf: options.errorOf,
126
+ assertHeld: () => lock.assertHeld(),
127
+ ...(options.signal ? { signal: options.signal } : {}),
128
+ })),
129
+ });
130
+ }
131
+ finally {
132
+ await lock.release();
133
+ }
134
+ const issues = results
135
+ .filter((result) => result.errors.length || result.conflicts.length || result.missing.length)
136
+ .map((result) => ({ code: "provider_error", reason: "mirror.partial", source: `mirror ${result.id}` }));
137
+ return {
138
+ items: results.map(({ conflicts, ...result }) => ({
139
+ ...result,
140
+ conflicts: conflicts.map((conflict) => conflictReport(conflict, remote)),
141
+ })),
142
+ issues,
143
+ };
144
+ };
@@ -1,22 +1,7 @@
1
- import type { MeetingChatLinesInput, MeetingHitSearchRequest } from "../meetings/index.js";
1
+ import type { MeetingChatLinesInput, MeetingHitSearchRequest, MeetingLink } from "../meetings/index.js";
2
2
  import type { MessageStore } from "../store/store.js";
3
+ export type { MeetingLink };
3
4
  export type MeetingLinksRequest = Omit<MeetingHitSearchRequest, "limit" | "after" | "maxReadBytes"> & Pick<MeetingChatLinesInput, "maxMessages">;
4
- /** One link as the meetings' chat holds it: where and how often it was shared. */
5
- export interface MeetingLink {
6
- url: string;
7
- domain: string | null;
8
- /** The Google Docs, Sheets, Slides, Forms or Drive file it names. */
9
- documentId: string | null;
10
- count: number;
11
- firstAt: number;
12
- lastAt: number;
13
- senders: string[];
14
- meetings: {
15
- meetingId: number;
16
- accountId: number;
17
- title: string | null;
18
- }[];
19
- }
20
5
  /** Links shared in the chat of the selected meetings, most shared first; found when read, not stored. */
21
6
  export declare const readMeetingLinks: (store: Pick<MessageStore, "meetings"> & {
22
7
  knowledge?: MessageStore["knowledge"];
@@ -283,6 +283,7 @@ export const messagesService = (deps) => {
283
283
  sendId: id,
284
284
  operationId: id,
285
285
  length: text.length,
286
+ ...(attachments.length > 1 ? { count: attachments.length } : {}),
286
287
  ...(replyTo === undefined ? {} : { replyTo }),
287
288
  ...(threadId === undefined ? {} : { threadId }),
288
289
  ...(sendAs === undefined ? {} : { sendAs }),
@@ -4,6 +4,7 @@ import { formatMeetingReference } from "../meetings/index.js";
4
4
  import { entriesOfList, fitting } from "../search/account-list.js";
5
5
  import { SEARCH_QUERY } from "../search/lucene/types.js";
6
6
  import { codeOf } from "../sends/guarded.js";
7
+ import { MAX_SEARCH_OFFSET } from "./messages-archive-search.js";
7
8
  import { RRF_K } from "./notes-search.js";
8
9
  import { RESOURCES_SEARCHED, searchAll, } from "./search-all.js";
9
10
  import { listContextOf } from "./search-kind.js";
@@ -139,26 +140,35 @@ export const searchAllIncludingMeetings = async (store, account, request, messen
139
140
  whole(request.meetingCursor.hitOffset, 0, Number.MAX_SAFE_INTEGER, "cursor hit offset");
140
141
  }
141
142
  cancelled(request.signal);
142
- const legacy = await searchAll(store, account, { ...request, only: only.filter((one) => one !== "meetings") }, messenger, searchMessages);
143
+ const others = { ...request, only: only.filter((one) => one !== "meetings") };
144
+ if (!only.includes("meetings")) {
145
+ const found = await searchAll(store, account, others, messenger, searchMessages);
146
+ cancelled(request.signal);
147
+ return found;
148
+ }
149
+ // Meetings rank with the rest, so a later page is the same merge read deeper, then cut.
150
+ const offset = request.offset ?? 0;
151
+ const window = offset + request.limit;
152
+ const legacy = await searchAll(store, account, { ...others, offset: 0, limit: window }, messenger, searchMessages);
143
153
  cancelled(request.signal);
144
- if (!only.includes("meetings"))
145
- return legacy;
146
154
  const compiled = meetingQuery(request.text, request.exact);
147
155
  if ("reason" in compiled)
148
156
  return {
149
157
  ...legacy,
158
+ items: legacy.items.slice(offset),
150
159
  skipped: [...legacy.skipped, { resource: "meetings", code: "validation_error", reason: compiled.reason }],
151
160
  };
152
161
  let meetings;
153
162
  const meetingAccount = request.meetingAccount ?? account;
154
163
  try {
155
- meetings = await collectMeetings(store, meetingAccount, request, compiled.query);
164
+ meetings = await collectMeetings(store, meetingAccount, { ...request, limit: window }, compiled.query);
156
165
  }
157
166
  catch (error) {
158
167
  if (codeOf(error) !== "not_found")
159
168
  throw error;
160
169
  return {
161
170
  ...legacy,
171
+ items: legacy.items.slice(offset),
162
172
  skipped: [
163
173
  ...legacy.skipped,
164
174
  { resource: "meetings", code: "not_found", reason: "the selected meeting account is not stored" },
@@ -176,13 +186,16 @@ export const searchAllIncludingMeetings = async (store, account, request, messen
176
186
  ranked.set(key, held);
177
187
  });
178
188
  const ordered = [...ranked.values()].sort((a, b) => b.score - a.score).map(({ item }) => item);
179
- const items = ordered.slice(0, request.limit);
180
- const returnedMeetings = items.filter((item) => item.kind === "meeting").length;
189
+ const items = ordered.slice(offset, window);
190
+ const returnedMeetings = ordered.slice(0, window).filter((item) => item.kind === "meeting").length;
181
191
  const nextCursor = meetings.items[returnedMeetings]?.before ?? (!meetings.coverage.complete ? meetings.after : undefined);
192
+ const more = legacy.hasMore || ordered.length > window;
193
+ const { nextOffset: _, ...rest } = legacy;
182
194
  return {
183
- ...legacy,
195
+ ...rest,
184
196
  items,
185
- hasMore: legacy.hasMore || ordered.length > request.limit ? true : meetings.coverage.complete ? false : null,
197
+ hasMore: more ? true : meetings.coverage.complete ? false : null,
198
+ ...(more && window <= MAX_SEARCH_OFFSET ? { nextOffset: window } : {}),
186
199
  searched: [...legacy.searched, "meetings"],
187
200
  accounts: [
188
201
  ...legacy.accounts,
@@ -5,7 +5,7 @@ import { type TaskCategory } from "./task-categories.js";
5
5
  export interface RuleResult {
6
6
  added: number;
7
7
  closed: number;
8
- /** Opened tasks that may be the same job as another: on the review list (`tasks duplicates`). */
8
+ /** Opened tasks that may be the same job as another: on the review list (`tasks duplicates list`). */
9
9
  review: number;
10
10
  /** Tasks the rules found but did not open: over the per-chat limit, or a dry run. */
11
11
  held?: number;
@@ -26,7 +26,7 @@ export declare const TASK_QUERY: {
26
26
  readonly backend: "sql";
27
27
  readonly fields: import("@wirecat/cli-base/query").FieldSpec[];
28
28
  };
29
- /** Which pairs `tasks duplicates` lists: those waiting for the reviewing agent, or those it left to the owner. */
29
+ /** Which pairs `tasks duplicates list` lists: those waiting for the reviewing agent, or those it left to the owner. */
30
30
  export declare const PAIR_DECISIONS: readonly ["review", "unsure"];
31
31
  export type PairDecision = (typeof PAIR_DECISIONS)[number];
32
32
  export declare const TASK_PAIR_QUERY: {
@@ -168,7 +168,7 @@ export interface TasksService {
168
168
  /**
169
169
  * Links a task to stored things (`msg:`, `chat:`, `person:`, `document:`…) or, for what the store does
170
170
  * not hold, to the text as written — a URL, a message not saved yet. After a `msg:` target, `review` counts the
171
- * pairs the duplicate check sent to `tasks duplicates`.
171
+ * pairs the duplicate check sent to `tasks duplicates list`.
172
172
  */
173
173
  link(id: string, targets: {
174
174
  references?: readonly string[];
@@ -49,7 +49,7 @@ export const TASK_QUERY = {
49
49
  },
50
50
  }),
51
51
  };
52
- /** Which pairs `tasks duplicates` lists: those waiting for the reviewing agent, or those it left to the owner. */
52
+ /** Which pairs `tasks duplicates list` lists: those waiting for the reviewing agent, or those it left to the owner. */
53
53
  export const PAIR_DECISIONS = ["review", "unsure"];
54
54
  export const TASK_PAIR_QUERY = {
55
55
  entity: "task_pair",
@@ -17,7 +17,7 @@ export type { AdminStoreRequest, AdminStoreResult } from "./sqlite/admin-statist
17
17
  export { type AttachmentTextEntry, type AttachmentView, attachmentColumns, type FileAttachment, resetAttachmentWords, type TextOrigin, } from "./sqlite/attachment-texts.js";
18
18
  export { pendingNormalization } from "./sqlite/backfill.js";
19
19
  export type { ChatRekey, ChatRekeys } from "./sqlite/chat-rekey.js";
20
- export { CHAT_COLUMNS, titlePart } from "./sqlite/chats.js";
20
+ export { CHAT_COLUMNS, TRACKED_CHAT_COLUMNS, titlePart } from "./sqlite/chats.js";
21
21
  export { CHAT_LIST_KEY, type ChatCompleteness, fetchedKey, historyStartKey } from "./sqlite/completeness.js";
22
22
  export { CONVERSATION_COLUMNS } from "./sqlite/conversations.js";
23
23
  export type { CounterTarget } from "./sqlite/counters.js";
@@ -1,7 +1,7 @@
1
1
  export { accountGroupsOf } from "./sqlite/account-groups.js";
2
2
  export { attachmentColumns, resetAttachmentWords, } from "./sqlite/attachment-texts.js";
3
3
  export { pendingNormalization } from "./sqlite/backfill.js";
4
- export { CHAT_COLUMNS, titlePart } from "./sqlite/chats.js";
4
+ export { CHAT_COLUMNS, TRACKED_CHAT_COLUMNS, titlePart } from "./sqlite/chats.js";
5
5
  export { CHAT_LIST_KEY, fetchedKey, historyStartKey } from "./sqlite/completeness.js";
6
6
  export { CONVERSATION_COLUMNS } from "./sqlite/conversations.js";
7
7
  export { messageOfEmail, threadConversation } from "./sqlite/emails.js";
@@ -128,7 +128,9 @@ export interface AccountMessages {
128
128
  /** Keeps a finished transcript; an empty one is not kept, so the message is heard again. */
129
129
  keepTranscript(key: AccountKey, chatId: Id, messageId: Id, text: string, source: string): Promise<void>;
130
130
  /** The chat's messages that may hold a Google document link, newest first, with the documents linked from each. */
131
- documentLinkMessages(key: AccountKey, chatId: Id): Promise<DocumentLinkMessage[]>;
131
+ documentLinkMessages(key: AccountKey, chatId: Id, options?: {
132
+ messageId?: Id;
133
+ }): Promise<DocumentLinkMessage[]>;
132
134
  /** Messages of the account that name this person, newest first. */
133
135
  mentioning(key: AccountKey, person: {
134
136
  id: Id;
@@ -123,9 +123,9 @@ export const accountMessagesOver = (context) => {
123
123
  return;
124
124
  write(database, () => transcripts.keepTranscript(context, chatPkFor(accountPk(key), chatId), messageId, text, source));
125
125
  },
126
- documentLinkMessages: async (key, chatId) => {
126
+ documentLinkMessages: async (key, chatId, { messageId } = {}) => {
127
127
  const chatKey = chatKeyOf(key, chatId);
128
- return chatKey === undefined ? [] : documentLinkMessages(context, chatKey);
128
+ return chatKey === undefined ? [] : documentLinkMessages(context, chatKey, messageId);
129
129
  },
130
130
  mentioning: async (key, person, limit) => {
131
131
  const accountKey = findAccountPk(key);
@@ -1,6 +1,7 @@
1
1
  import type { ChatEvent, Id, Member } from "../domain/models.js";
2
2
  import type { RetentionOptions } from "../domain/retention.js";
3
3
  import type { StoreContext } from "./sqlite/open.js";
4
+ import type { Where } from "./sqlite/query-filter.js";
4
5
  import { type RetentionResult } from "./sqlite/retention.js";
5
6
  import type { MemberCount, MemberStay, ProfileRevision, RosterChange, RosterRead, TrackedChat } from "./sqlite/roster.js";
6
7
  import type { AccountKey } from "./types.js";
@@ -35,7 +36,9 @@ export interface RosterStore {
35
36
  }): Promise<ChatEvent[]>;
36
37
  /** Starts or stops `serve`'s daily fetch of the chat's members; history already kept stays. */
37
38
  trackMembers(key: AccountKey, chatId: Id, tracked: boolean): Promise<void>;
38
- trackedChats(key: AccountKey): Promise<TrackedChat[]>;
39
+ trackedChats(key: AccountKey, options?: {
40
+ where?: Where;
41
+ }): Promise<TrackedChat[]>;
39
42
  retention(key: AccountKey, chatId: Id, options: RetentionOptions, evidence?: {
40
43
  cohort: string;
41
44
  cursor?: string;
@@ -52,9 +52,9 @@ export const rosterStoreOver = (context) => {
52
52
  }));
53
53
  },
54
54
  trackMembers: async (key, chatId, tracked) => write(database, () => roster.setTracked(context, chatPkFor(accountPk(key), chatId), tracked)),
55
- trackedChats: async (key) => {
55
+ trackedChats: async (key, { where } = {}) => {
56
56
  const accountKey = findAccountPk(key);
57
- return accountKey === undefined ? [] : roster.trackedChats(context, accountKey);
57
+ return accountKey === undefined ? [] : roster.trackedChats(context, accountKey, where);
58
58
  },
59
59
  retention: async (key, chatId, options, evidence) => {
60
60
  const accountKey = findAccountPk(key);
@@ -94,6 +94,18 @@ export declare const CHAT_COLUMNS: {
94
94
  readonly sql: "chats.participants_count";
95
95
  };
96
96
  };
97
+ export declare const TRACKED_CHAT_COLUMNS: {
98
+ readonly chat_id: {
99
+ readonly type: "kw";
100
+ readonly sql: "chats.external_id";
101
+ };
102
+ readonly title: {
103
+ readonly where: (node: import("@wirecat/cli-base/query").Predicate) => {
104
+ sql: string;
105
+ params: string[];
106
+ };
107
+ };
108
+ };
97
109
  /** `title:*book*` and `title:book` ask the same: the title holds "book". */
98
110
  export declare const titlePart: (value: string) => string;
99
111
  export declare const listChats: ({ orm, now }: StoreContext, accountKey: number, { limit, offset, ...filter }: {
@@ -340,6 +340,7 @@ export const CHAT_COLUMNS = {
340
340
  last_message_at: { type: "time", sql: "chats.last_message_at" },
341
341
  participants_count: { type: "num", sql: "chats.participants_count" },
342
342
  };
343
+ export const TRACKED_CHAT_COLUMNS = { chat_id: CHAT_COLUMNS.id, title: CHAT_COLUMNS.title };
343
344
  /** `title:*book*` and `title:book` ask the same: the title holds "book". */
344
345
  export const titlePart = (value) => value.replace(/^\*+|\*+$/g, "").trim();
345
346
  export const listChats = ({ orm, now }, accountKey, { limit, offset = 0, ...filter }) => {
@@ -14,4 +14,4 @@ export interface DocumentLinkMessage {
14
14
  }[];
15
15
  }
16
16
  /** Newest first. Read from the text, so an edit that removed a link drops it whatever link rows remain. */
17
- export declare const documentLinkMessages: ({ database }: StoreContext, chatKey: number) => DocumentLinkMessage[];
17
+ export declare const documentLinkMessages: ({ database }: StoreContext, chatKey: number, messageId?: Id) => DocumentLinkMessage[];
@@ -1,13 +1,13 @@
1
1
  import { linkKind } from "./link-kinds.js";
2
2
  /** Newest first. Read from the text, so an edit that removed a link drops it whatever link rows remain. */
3
- export const documentLinkMessages = ({ database }, chatKey) => {
4
- const rows = database
5
- .prepare("SELECT m.id, m.external_id, m.text, m.sent_at, m.sender_name, " +
3
+ export const documentLinkMessages = ({ database }, chatKey, messageId) => {
4
+ const read = database.prepare("SELECT m.id, m.external_id, m.text, m.sent_at, m.sender_name, " +
6
5
  "coalesce(m.sender_chat_external_id, i.external_id) AS sender_id FROM messages m " +
7
6
  "LEFT JOIN identities i ON i.id = m.sender_identity_id " +
8
7
  "WHERE m.chat_id = ? AND m.deleted_at IS NULL AND (m.text LIKE '%google.com%' OR m.text LIKE '%forms.gle%') " +
9
- "ORDER BY m.sent_at DESC, m.id DESC")
10
- .all(chatKey);
8
+ (messageId === undefined ? "" : "AND m.external_id = ? ") +
9
+ "ORDER BY m.sent_at DESC, m.id DESC");
10
+ const rows = messageId === undefined ? read.all(chatKey) : read.all(chatKey, messageId);
11
11
  const linked = database.prepare("SELECT json_extract(d.front_matter, '$.fileId') AS file_id, d.title FROM links l " +
12
12
  "JOIN documents d ON d.id = l.to_id AND d.deleted_at IS NULL " +
13
13
  `WHERE l.from_type = 'message' AND l.from_id = ? AND l.to_type = 'document' AND l.kind = ${linkKind("links-to")}`);
@@ -10,9 +10,9 @@ import {
10
10
  entityKind,
11
11
  jitCompatCheck,
12
12
  sql
13
- } from "./shared-O7CTKP45.js";
13
+ } from "./shared-HKXF6HKX.js";
14
14
 
15
- // node_modules/.pnpm/drizzle-orm@1.0.0-rc.4_valibot@1.5.0_typescript@7.0.2__zod@4.6.5/node_modules/drizzle-orm/bun-sqlite/session.js
15
+ // node_modules/.pnpm/drizzle-orm@1.0.0-rc.4_valibot@1.5.0_typescript@7.0.2__zod@4.6.7/node_modules/drizzle-orm/bun-sqlite/session.js
16
16
  var SQLiteBunSession = class extends SQLiteAsyncSession {
17
17
  static [entityKind] = "SQLiteBunSession";
18
18
  logger;
@@ -76,7 +76,7 @@ var SQLiteBunTransaction = class SQLiteBunTransaction2 extends SQLiteAsyncTransa
76
76
  }
77
77
  };
78
78
 
79
- // node_modules/.pnpm/drizzle-orm@1.0.0-rc.4_valibot@1.5.0_typescript@7.0.2__zod@4.6.5/node_modules/drizzle-orm/bun-sqlite/driver.js
79
+ // node_modules/.pnpm/drizzle-orm@1.0.0-rc.4_valibot@1.5.0_typescript@7.0.2__zod@4.6.7/node_modules/drizzle-orm/bun-sqlite/driver.js
80
80
  import { Database } from "bun:sqlite";
81
81
  var SQLiteBunDatabase = class extends SQLiteAsyncDatabase {
82
82
  static [entityKind] = "SQLiteBunDatabase";
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../../../../node_modules/.pnpm/drizzle-orm@1.0.0-rc.4_valibot@1.5.0_typescript@7.0.2__zod@4.6.5/node_modules/src/bun-sqlite/session.ts", "../../../../node_modules/.pnpm/drizzle-orm@1.0.0-rc.4_valibot@1.5.0_typescript@7.0.2__zod@4.6.5/node_modules/src/bun-sqlite/driver.ts"],
3
+ "sources": ["../../../../node_modules/.pnpm/drizzle-orm@1.0.0-rc.4_valibot@1.5.0_typescript@7.0.2__zod@4.6.7/node_modules/src/bun-sqlite/session.ts", "../../../../node_modules/.pnpm/drizzle-orm@1.0.0-rc.4_valibot@1.5.0_typescript@7.0.2__zod@4.6.7/node_modules/src/bun-sqlite/driver.ts"],
4
4
  "sourcesContent": ["/// <reference types=\"bun-types\" />\n\nimport type { Changes, Database } from 'bun:sqlite';\nimport { entityKind } from '~/entity.ts';\nimport { DrizzleQueryError } from '~/errors.ts';\nimport type { Logger } from '~/logger.ts';\nimport { NoopLogger } from '~/logger.ts';\nimport type { AnyRelations } from '~/relations.ts';\nimport { type Query, sql } from '~/sql/sql.ts';\nimport {\n\tSQLiteAsyncPreparedQuery,\n\ttype SQLiteAsyncPreparedQueryConfig as PreparedQueryConfigBase,\n\tSQLiteAsyncSession,\n\tSQLiteAsyncTransaction,\n\ttype SQLiteQueryExecutors,\n} from '~/sqlite-core/async/session.ts';\nimport type { SQLiteDialect } from '~/sqlite-core/dialect.ts';\nimport type { SQLiteExecuteMethod, SQLiteTransactionConfig } from '~/sqlite-core/session.ts';\nimport type { DrizzleTypeError } from '~/utils.ts';\n\nexport interface SQLiteBunSessionOptions {\n\tlogger?: Logger;\n\tcache?: Cache;\n}\n\nexport type SQLiteBunRunResult = Changes;\n\ntype PreparedQueryConfig = Omit<PreparedQueryConfigBase, 'statement' | 'run'>;\n\nexport class SQLiteBunSession<TRelations extends AnyRelations>\n\textends SQLiteAsyncSession<'sync', SQLiteBunRunResult, TRelations>\n{\n\tstatic override readonly [entityKind]: string = 'SQLiteBunSession';\n\n\tprivate logger: Logger;\n\n\tconstructor(\n\t\tprivate client: Database,\n\t\tdialect: SQLiteDialect,\n\t\tprivate relations: TRelations,\n\t\tprivate options: SQLiteBunSessionOptions = {},\n\t) {\n\t\tsuper(dialect, 'sync');\n\t\tthis.logger = options.logger ?? new NoopLogger();\n\t}\n\n\texec(query: string): void {\n\t\tthis.client.exec(query);\n\t}\n\n\tprepareQuery<T extends Omit<PreparedQueryConfig, 'run'>>(\n\t\tquery: Query,\n\t\tmode: 'arrays' | 'objects' | 'raw',\n\t\t_prepare: boolean,\n\t\texecuteMethod?: SQLiteExecuteMethod,\n\t\tmapper?: (rows: any[]) => any,\n\t\tqueryMetadata?: {\n\t\t\ttype: 'select' | 'update' | 'delete' | 'insert';\n\t\t\ttables: string[];\n\t\t},\n\t): SQLiteAsyncPreparedQuery<T & { run: SQLiteBunRunResult }> {\n\t\tlet stmt: ReturnType<typeof this.client.query>;\n\t\ttry {\n\t\t\tstmt = this.client.query(query.sql);\n\t\t} catch (e) {\n\t\t\tthrow new DrizzleQueryError(query.sql, query.params, e as Error);\n\t\t}\n\t\tconst executors: SQLiteQueryExecutors<'sync'> = {\n\t\t\tall: (params) => {\n\t\t\t\tif (mode === 'arrays') return stmt.values(...params as any[]);\n\t\t\t\treturn stmt.all(...params as any[]);\n\t\t\t},\n\t\t\tget: (params) => {\n\t\t\t\tif (mode === 'arrays') return stmt.values(...params as any[])[0];\n\t\t\t\treturn stmt.get(...params as any[]);\n\t\t\t},\n\t\t\trun: (params) => {\n\t\t\t\treturn stmt.run(...params as any[]);\n\t\t\t},\n\t\t\tvalues: (params) => {\n\t\t\t\treturn stmt.values(...params as any[]);\n\t\t\t},\n\t\t};\n\n\t\treturn new SQLiteAsyncPreparedQuery(\n\t\t\t'sync',\n\t\t\texecuteMethod,\n\t\t\texecutors,\n\t\t\tquery,\n\t\t\tmapper,\n\t\t\tmode,\n\t\t\tthis.logger,\n\t\t\tundefined,\n\t\t\tqueryMetadata,\n\t\t\tundefined,\n\t\t);\n\t}\n\n\toverride transaction<T>(\n\t\ttransaction: (tx: SQLiteBunTransaction<TRelations>) => T,\n\t\tconfig: SQLiteTransactionConfig = {},\n\t): T {\n\t\tconst tx = new SQLiteBunTransaction('sync', this.dialect, this, this.relations);\n\t\tlet result: T | undefined;\n\t\tconst nativeTx = this.client.transaction(() => {\n\t\t\tresult = transaction(tx);\n\t\t});\n\t\tnativeTx[config.behavior ?? 'deferred']();\n\t\treturn result!;\n\t}\n}\n\nexport class SQLiteBunTransaction<\n\tTRelations extends AnyRelations,\n> extends SQLiteAsyncTransaction<'sync', SQLiteBunRunResult, TRelations> {\n\tstatic override readonly [entityKind]: string = 'SQLiteBunTransaction';\n\n\toverride transaction<T>(\n\t\ttransaction: (\n\t\t\ttx: SQLiteBunTransaction<TRelations>,\n\t\t) => T extends Promise<any> ? DrizzleTypeError<\"Sync drivers can't use async functions in transactions!\">\n\t\t\t: T,\n\t): T {\n\t\tconst savepointName = `sp${this.nestedIndex}`;\n\t\tconst tx = new SQLiteBunTransaction(\n\t\t\t'sync',\n\t\t\tthis.dialect,\n\t\t\tthis.session,\n\t\t\tthis._.relations,\n\t\t\tthis.nestedIndex + 1,\n\t\t);\n\t\tthis.session.run(sql.raw(`savepoint ${savepointName}`));\n\t\ttry {\n\t\t\tconst result = transaction(tx);\n\t\t\tthis.session.run(sql.raw(`release savepoint ${savepointName}`));\n\t\t\treturn result as T;\n\t\t} catch (err) {\n\t\t\tthis.session.run(sql.raw(`rollback to savepoint ${savepointName}`));\n\t\t\tthrow err;\n\t\t}\n\t}\n}\n", "/// <reference types=\"bun-types\" />\n\nimport { Database } from 'bun:sqlite';\nimport { entityKind } from '~/entity.ts';\nimport { DefaultLogger } from '~/logger.ts';\nimport type { AnyRelations, EmptyRelations } from '~/relations.ts';\nimport { SQLiteAsyncDatabase } from '~/sqlite-core/async/db.ts';\nimport { SQLiteDialect } from '~/sqlite-core/dialect.ts';\nimport type { DrizzleSQLiteConfig } from '~/sqlite-core/utils.ts';\nimport { jitCompatCheck } from '~/utils.ts';\nimport { type SQLiteBunRunResult, SQLiteBunSession } from './session.ts';\n\nexport class SQLiteBunDatabase<\n\tTRelations extends AnyRelations = EmptyRelations,\n> extends SQLiteAsyncDatabase<'sync', SQLiteBunRunResult, TRelations> {\n\tstatic override readonly [entityKind]: string = 'SQLiteBunDatabase';\n}\n\ntype DrizzleSqliteBunDatabaseOptions = {\n\t/**\n\t * Open the database as read-only (no write operations, no create).\n\t *\n\t * Equivalent to {@link constants.SQLITE_OPEN_READONLY}\n\t */\n\treadonly?: boolean;\n\t/**\n\t * Allow creating a new database\n\t *\n\t * Equivalent to {@link constants.SQLITE_OPEN_CREATE}\n\t */\n\tcreate?: boolean;\n\t/**\n\t * Open the database as read-write\n\t *\n\t * Equivalent to {@link constants.SQLITE_OPEN_READWRITE}\n\t */\n\treadwrite?: boolean;\n};\n\nexport type DrizzleBunSqliteDatabaseConfig =\n\t| ({\n\t\tsource?: string;\n\t} & DrizzleSqliteBunDatabaseOptions)\n\t| string\n\t| undefined;\n\nfunction construct<\n\tTRelations extends AnyRelations = EmptyRelations,\n>(\n\tclient: Database,\n\tconfig: DrizzleSQLiteConfig<TRelations> = {},\n): SQLiteBunDatabase<TRelations> & {\n\t$client: Database;\n} {\n\tconst dialect = new SQLiteDialect({\n\t\tuseJitMappers: jitCompatCheck(config.jit),\n\t});\n\tlet logger;\n\tif (config.logger === true) {\n\t\tlogger = new DefaultLogger();\n\t} else if (config.logger !== false) {\n\t\tlogger = config.logger;\n\t}\n\n\tconst relations = config.relations ?? {} as TRelations;\n\tconst session = new SQLiteBunSession<TRelations>(\n\t\tclient,\n\t\tdialect,\n\t\trelations,\n\t\t{\n\t\t\tlogger,\n\t\t},\n\t);\n\tconst db = new SQLiteBunDatabase('sync', dialect, session, relations);\n\t(<any> db).$client = client;\n\n\treturn db as any;\n}\n\nexport function drizzle<\n\tTRelations extends AnyRelations = EmptyRelations,\n\tTClient extends Database = Database,\n>(\n\t...params:\n\t\t| []\n\t\t| [\n\t\t\tstring,\n\t\t]\n\t\t| [\n\t\t\tstring,\n\t\t\tDrizzleSQLiteConfig<TRelations>,\n\t\t]\n\t\t| [\n\t\t\t(\n\t\t\t\t& DrizzleSQLiteConfig<TRelations>\n\t\t\t\t& ({\n\t\t\t\t\tconnection?: DrizzleBunSqliteDatabaseConfig;\n\t\t\t\t} | {\n\t\t\t\t\tclient: TClient;\n\t\t\t\t})\n\t\t\t),\n\t\t]\n): SQLiteBunDatabase<TRelations> & {\n\t$client: TClient;\n} {\n\tif (params[0] === undefined || typeof params[0] === 'string') {\n\t\tconst instance = params[0] === undefined ? new Database() : new Database(params[0]);\n\n\t\treturn construct(instance, params[1]) as any;\n\t}\n\n\tconst { connection, client, ...DrizzleSQLiteConfig } = params[0] as\n\t\t& ({\n\t\t\tconnection?: DrizzleBunSqliteDatabaseConfig | string;\n\t\t\tclient?: TClient;\n\t\t})\n\t\t& DrizzleSQLiteConfig<TRelations>;\n\n\tif (client) return construct(client, DrizzleSQLiteConfig) as any;\n\n\tif (typeof connection === 'object') {\n\t\tconst { source, ...opts } = connection;\n\n\t\tconst options = Object.values(opts).filter((v) => v !== undefined).length ? opts : undefined;\n\n\t\tconst instance = new Database(source, options);\n\n\t\treturn construct(instance, DrizzleSQLiteConfig) as any;\n\t}\n\n\tconst instance = new Database(connection);\n\n\treturn construct(instance, DrizzleSQLiteConfig) as any;\n}\n\nexport namespace drizzle {\n\texport function mock<TRelations extends AnyRelations = EmptyRelations>(\n\t\tconfig?: DrizzleSQLiteConfig<TRelations>,\n\t): SQLiteBunDatabase<TRelations> & {\n\t\t$client: '$client is not available on drizzle.mock()';\n\t} {\n\t\treturn construct({} as any, config) as any;\n\t}\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": ["SQLiteBunTransaction"]