@agentshouse/mdruntime 0.1.0-c9b1e59e5baeb → 0.1.0-ca10e63188fc5

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 (226) hide show
  1. package/README.md +18 -15
  2. package/dist/adapter.d.ts +58 -32
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/attached-model.d.ts +5 -1
  5. package/dist/attached-model.d.ts.map +1 -1
  6. package/dist/attached-model.js +21 -4
  7. package/dist/binary.d.ts +0 -5
  8. package/dist/binary.d.ts.map +1 -1
  9. package/dist/binary.js +1 -1
  10. package/dist/bootstrap.d.ts +30 -8
  11. package/dist/bootstrap.d.ts.map +1 -1
  12. package/dist/bootstrap.js +187 -85
  13. package/dist/chain.d.ts +11 -0
  14. package/dist/chain.d.ts.map +1 -0
  15. package/dist/chain.js +64 -0
  16. package/dist/change-set.d.ts +6 -16
  17. package/dist/change-set.d.ts.map +1 -1
  18. package/dist/change-set.js +11 -94
  19. package/dist/change.d.ts +31 -1
  20. package/dist/change.d.ts.map +1 -1
  21. package/dist/change.js +10 -1
  22. package/dist/client.d.ts +9 -5
  23. package/dist/client.d.ts.map +1 -1
  24. package/dist/client.js +80 -6
  25. package/dist/collection.d.ts +11 -20
  26. package/dist/collection.d.ts.map +1 -1
  27. package/dist/collection.js +53 -354
  28. package/dist/compile.d.ts +16 -0
  29. package/dist/compile.d.ts.map +1 -0
  30. package/dist/compile.js +765 -0
  31. package/dist/connection.d.ts +17 -9
  32. package/dist/connection.d.ts.map +1 -1
  33. package/dist/connection.js +2431 -953
  34. package/dist/conversation.d.ts +4 -3
  35. package/dist/conversation.d.ts.map +1 -1
  36. package/dist/conversation.js +81 -68
  37. package/dist/digest.d.ts +7 -0
  38. package/dist/digest.d.ts.map +1 -0
  39. package/dist/digest.js +113 -0
  40. package/dist/durability.d.ts +3 -5
  41. package/dist/durability.d.ts.map +1 -1
  42. package/dist/durability.js +5 -0
  43. package/dist/entry.d.ts +3 -1
  44. package/dist/entry.d.ts.map +1 -1
  45. package/dist/entry.js +18 -24
  46. package/dist/file.d.ts +2 -19
  47. package/dist/file.d.ts.map +1 -1
  48. package/dist/file.js +7 -122
  49. package/dist/fulltext.d.ts +27 -0
  50. package/dist/fulltext.d.ts.map +1 -0
  51. package/dist/fulltext.js +368 -0
  52. package/dist/host.d.ts +4 -4
  53. package/dist/host.d.ts.map +1 -1
  54. package/dist/host.js +45 -15
  55. package/dist/identity.d.ts +0 -1
  56. package/dist/identity.d.ts.map +1 -1
  57. package/dist/identity.js +0 -3
  58. package/dist/index.d.ts +11 -9
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +4 -1
  61. package/dist/live.d.ts +22 -0
  62. package/dist/live.d.ts.map +1 -0
  63. package/dist/live.js +124 -0
  64. package/dist/media.d.ts +8 -0
  65. package/dist/media.d.ts.map +1 -0
  66. package/dist/media.js +205 -0
  67. package/dist/memory.d.ts +2 -1
  68. package/dist/memory.d.ts.map +1 -1
  69. package/dist/memory.js +63 -22
  70. package/dist/messages.d.ts +28 -0
  71. package/dist/messages.d.ts.map +1 -0
  72. package/dist/messages.js +90 -0
  73. package/dist/model.d.ts +6 -0
  74. package/dist/model.d.ts.map +1 -1
  75. package/dist/model.js +7 -1
  76. package/dist/name.d.ts +4 -0
  77. package/dist/name.d.ts.map +1 -0
  78. package/dist/name.js +122 -0
  79. package/dist/observation.d.ts +13 -0
  80. package/dist/observation.d.ts.map +1 -1
  81. package/dist/observation.js +84 -0
  82. package/dist/opened.d.ts +22 -0
  83. package/dist/opened.d.ts.map +1 -0
  84. package/dist/opened.js +107 -0
  85. package/dist/outcome.d.ts +40 -5
  86. package/dist/outcome.d.ts.map +1 -1
  87. package/dist/outcome.js +38 -2
  88. package/dist/overlay.d.ts +1 -4
  89. package/dist/overlay.d.ts.map +1 -1
  90. package/dist/overlay.js +22 -148
  91. package/dist/query.d.ts +11 -1
  92. package/dist/query.d.ts.map +1 -1
  93. package/dist/query.js +13 -1
  94. package/dist/reach.d.ts +11 -3
  95. package/dist/reach.d.ts.map +1 -1
  96. package/dist/reach.js +198 -69
  97. package/dist/readiness.d.ts +20 -9
  98. package/dist/readiness.d.ts.map +1 -1
  99. package/dist/readiness.js +6 -6
  100. package/dist/reads.d.ts.map +1 -1
  101. package/dist/reads.js +4 -2
  102. package/dist/recovery.d.ts +2 -2
  103. package/dist/recovery.d.ts.map +1 -1
  104. package/dist/recovery.js +8 -7
  105. package/dist/refresh.d.ts +15 -14
  106. package/dist/refresh.d.ts.map +1 -1
  107. package/dist/refresh.js +113 -75
  108. package/dist/replica.d.ts +56 -39
  109. package/dist/replica.d.ts.map +1 -1
  110. package/dist/replica.js +278 -197
  111. package/dist/room-draft.d.ts +4 -1
  112. package/dist/room-draft.d.ts.map +1 -1
  113. package/dist/room-draft.js +54 -13
  114. package/dist/rows.d.ts +38 -31
  115. package/dist/rows.d.ts.map +1 -1
  116. package/dist/rows.js +173 -101
  117. package/dist/save.d.ts +49 -14
  118. package/dist/save.d.ts.map +1 -1
  119. package/dist/save.js +352 -98
  120. package/dist/search.d.ts +11 -43
  121. package/dist/search.d.ts.map +1 -1
  122. package/dist/search.js +0 -213
  123. package/dist/shared.d.ts +4 -3
  124. package/dist/shared.d.ts.map +1 -1
  125. package/dist/shared.js +67 -34
  126. package/dist/store.d.ts +92 -96
  127. package/dist/store.d.ts.map +1 -1
  128. package/dist/store.js +329 -720
  129. package/dist/stored.d.ts +152 -0
  130. package/dist/stored.d.ts.map +1 -0
  131. package/dist/stored.js +571 -0
  132. package/dist/tables.d.ts +354 -0
  133. package/dist/tables.d.ts.map +1 -0
  134. package/dist/tables.js +624 -0
  135. package/dist/turn.d.ts +1 -1
  136. package/dist/turn.d.ts.map +1 -1
  137. package/dist/turn.js +31 -20
  138. package/dist/validation.d.ts +30 -0
  139. package/dist/validation.d.ts.map +1 -0
  140. package/dist/validation.js +162 -0
  141. package/dist/windows.d.ts +2 -6
  142. package/dist/windows.d.ts.map +1 -1
  143. package/dist/windows.js +30 -34
  144. package/dist/wire.d.ts +17 -1
  145. package/dist/wire.d.ts.map +1 -1
  146. package/dist/wire.js +5 -1
  147. package/dist/words.d.ts +19 -0
  148. package/dist/words.d.ts.map +1 -0
  149. package/dist/words.js +150 -0
  150. package/package.json +18 -19
  151. package/AGENTS.md +0 -299
  152. package/dist/chat/acts.d.ts +0 -33
  153. package/dist/chat/acts.d.ts.map +0 -1
  154. package/dist/chat/acts.js +0 -36
  155. package/dist/chat/context.d.ts +0 -3
  156. package/dist/chat/context.d.ts.map +0 -1
  157. package/dist/chat/context.js +0 -10
  158. package/dist/chat/earlier.d.ts +0 -10
  159. package/dist/chat/earlier.d.ts.map +0 -1
  160. package/dist/chat/earlier.js +0 -54
  161. package/dist/chat/input.d.ts +0 -9
  162. package/dist/chat/input.d.ts.map +0 -1
  163. package/dist/chat/input.js +0 -99
  164. package/dist/chat/kit.d.ts +0 -61
  165. package/dist/chat/kit.d.ts.map +0 -1
  166. package/dist/chat/kit.js +0 -123
  167. package/dist/chat/lasting.d.ts +0 -6
  168. package/dist/chat/lasting.d.ts.map +0 -1
  169. package/dist/chat/lasting.js +0 -19
  170. package/dist/chat/list.d.ts +0 -28
  171. package/dist/chat/list.d.ts.map +0 -1
  172. package/dist/chat/list.js +0 -99
  173. package/dist/chat/markdown.d.ts +0 -21
  174. package/dist/chat/markdown.d.ts.map +0 -1
  175. package/dist/chat/markdown.js +0 -246
  176. package/dist/chat/message.d.ts +0 -6
  177. package/dist/chat/message.d.ts.map +0 -1
  178. package/dist/chat/message.js +0 -199
  179. package/dist/chat/messages.d.ts +0 -22
  180. package/dist/chat/messages.d.ts.map +0 -1
  181. package/dist/chat/messages.js +0 -175
  182. package/dist/chat/name.d.ts +0 -6
  183. package/dist/chat/name.d.ts.map +0 -1
  184. package/dist/chat/name.js +0 -37
  185. package/dist/chat/opened.d.ts +0 -11
  186. package/dist/chat/opened.d.ts.map +0 -1
  187. package/dist/chat/opened.js +0 -17
  188. package/dist/chat/overlay.d.ts +0 -32
  189. package/dist/chat/overlay.d.ts.map +0 -1
  190. package/dist/chat/overlay.js +0 -107
  191. package/dist/chat/parts.d.ts +0 -57
  192. package/dist/chat/parts.d.ts.map +0 -1
  193. package/dist/chat/parts.js +0 -119
  194. package/dist/chat/pieces.d.ts +0 -62
  195. package/dist/chat/pieces.d.ts.map +0 -1
  196. package/dist/chat/pieces.js +0 -1110
  197. package/dist/chat/process.d.ts +0 -20
  198. package/dist/chat/process.d.ts.map +0 -1
  199. package/dist/chat/process.js +0 -32
  200. package/dist/chat/provider.d.ts +0 -64
  201. package/dist/chat/provider.d.ts.map +0 -1
  202. package/dist/chat/provider.js +0 -637
  203. package/dist/chat/question.d.ts +0 -9
  204. package/dist/chat/question.d.ts.map +0 -1
  205. package/dist/chat/question.js +0 -69
  206. package/dist/chat/questions.d.ts +0 -31
  207. package/dist/chat/questions.d.ts.map +0 -1
  208. package/dist/chat/questions.js +0 -111
  209. package/dist/chat/read.d.ts +0 -5
  210. package/dist/chat/read.d.ts.map +0 -1
  211. package/dist/chat/read.js +0 -13
  212. package/dist/chat/ready.d.ts +0 -34
  213. package/dist/chat/ready.d.ts.map +0 -1
  214. package/dist/chat/ready.js +0 -91
  215. package/dist/chat/steady.d.ts +0 -3
  216. package/dist/chat/steady.d.ts.map +0 -1
  217. package/dist/chat/steady.js +0 -26
  218. package/dist/chat/style.d.ts +0 -4
  219. package/dist/chat/style.d.ts.map +0 -1
  220. package/dist/chat/style.js +0 -302
  221. package/dist/chat.d.ts +0 -14
  222. package/dist/chat.d.ts.map +0 -1
  223. package/dist/chat.js +0 -8
  224. package/dist/read-side.d.ts +0 -27
  225. package/dist/read-side.d.ts.map +0 -1
  226. package/dist/read-side.js +0 -826
package/dist/entry.js CHANGED
@@ -1,9 +1,8 @@
1
1
  import { attachedModel } from './attached-model.js';
2
- import { collectionOf, passingFrontmatter } from './collection.js';
2
+ import { answerQuery } from './compile.js';
3
3
  import { NO_MODEL } from './model.js';
4
4
  import { invalid, planOf, plain, QueryError, } from './query.js';
5
- import { createReadSide } from './read-side.js';
6
- import { rowsOf } from './rows.js';
5
+ import { entered, REPLICA_TABLES } from './stored.js';
7
6
  export { QueryError } from './query.js';
8
7
  const PARAMETER = '$param';
9
8
  const KIND = 'as';
@@ -110,46 +109,41 @@ export function entryRefusal(template) {
110
109
  throw error;
111
110
  }
112
111
  }
113
- export function entryScope(rooms) {
114
- const readSide = createReadSide();
112
+ export function entryScope(rooms, roomTypes, connection) {
113
+ for (const table of REPLICA_TABLES)
114
+ connection.run(table);
115
115
  const held = [];
116
- const documents = rooms.flatMap((room) => {
116
+ const keys = rooms.map((room) => {
117
117
  const model = attachedModel({
118
118
  kind: 'model',
119
119
  snapshot: { identity: room.locator, version: '', schema: room.schema, revision: null },
120
- }, NO_MODEL).state;
120
+ }, NO_MODEL, roomTypes).state;
121
121
  held.push({
122
122
  locator: room.locator,
123
123
  available: model.status !== 'unavailable',
124
124
  types: model.status === 'unavailable' ? [] : Object.values(model.types),
125
125
  });
126
126
  const composition = { model, selection: null };
127
- const collection = collectionOf(room.documents, composition);
128
- return Object.keys(room.documents)
127
+ const key = entered(connection, room.locator, composition, Object.keys(room.documents)
129
128
  .sort()
130
129
  .map((path) => ({
131
- locator: room.locator,
132
130
  path,
133
- before: [],
134
- after: rowsOf(room.locator, path, passingFrontmatter(collection, path) ?? null, composition, null, null, null),
135
- }));
136
- });
137
- const seeded = readSide.apply({
138
- documents,
139
- drafts: [],
140
- rooms: [],
141
- queues: [],
142
- presentations: [],
143
- messages: [],
144
- conversations: [],
131
+ content: room.documents[path],
132
+ binary: null,
133
+ revision: null,
134
+ fileId: null,
135
+ change: null,
136
+ written: null,
137
+ })));
138
+ return { locator: room.locator, key };
145
139
  });
146
140
  const planned = { listed: true, rooms: held };
147
141
  return {
148
142
  async results(queries) {
149
- await seeded;
150
143
  const results = {};
151
144
  for (const [name, query] of Object.entries(queries)) {
152
- results[name] = (await readSide.query(planOf(query, planned), () => null)).rows;
145
+ const plan = planOf(query, planned);
146
+ results[name] = answerQuery(connection, plan, keys.filter(({ locator }) => plan.rooms === null || plan.rooms.includes(locator)));
153
147
  }
154
148
  return results;
155
149
  },
package/dist/file.d.ts CHANGED
@@ -1,20 +1,3 @@
1
- export type ChatDraftFile = {
2
- name: string;
3
- blob: Blob;
4
- };
5
- export type FileDeclaration = {
6
- name: string;
7
- mediaType: string;
8
- byteLength: number;
9
- contentHash: string;
10
- width?: number;
11
- height?: number;
12
- };
13
- export type Extent = {
14
- width: number;
15
- height: number;
16
- };
17
- export declare function extentOf(bytes: Uint8Array): Extent | null;
18
- export declare function hexDigest(digest: ArrayBuffer): string;
19
- export declare function declaredFile(file: ChatDraftFile): Promise<FileDeclaration>;
1
+ import type { AttachRequest } from './adapter.js';
2
+ export declare function attachedFile(locator: string, file: File): Promise<AttachRequest>;
20
3
  //# sourceMappingURL=file.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../src/file.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AA2GvD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAQzD;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAErD;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,CAAC,CAUhF"}
1
+ {"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../src/file.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAGlD,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,CAStF"}
package/dist/file.js CHANGED
@@ -1,126 +1,11 @@
1
- const PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
2
- const WEBP_SYNC = [0x9d, 0x01, 0x2a];
3
- const SHAPELESS_JPEG_MARKERS = new Set([0xc4, 0xc8, 0xcc]);
4
- const BMFF_CONTAINERS = new Set(['meta', 'iprp', 'ipco']);
5
- function starts(bytes, signature, at = 0) {
6
- return signature.every((value, index) => bytes[at + index] === value);
7
- }
8
- function text(bytes, at, length) {
9
- return String.fromCharCode(...bytes.subarray(at, at + length));
10
- }
11
- function bigEndian(bytes, at, length) {
12
- let value = 0;
13
- for (let index = 0; index < length; index += 1)
14
- value = value * 256 + (bytes[at + index] ?? 0);
15
- return value;
16
- }
17
- function littleEndian(bytes, at, length) {
18
- let value = 0;
19
- for (let index = length - 1; index >= 0; index -= 1)
20
- value = value * 256 + (bytes[at + index] ?? 0);
21
- return value;
22
- }
23
- function pngExtent(bytes) {
24
- if (bytes.length < 24 || !starts(bytes, PNG_SIGNATURE))
25
- return null;
26
- if (text(bytes, 12, 4) !== 'IHDR')
27
- return null;
28
- return { width: bigEndian(bytes, 16, 4), height: bigEndian(bytes, 20, 4) };
29
- }
30
- function gifExtent(bytes) {
31
- if (bytes.length < 10 || text(bytes, 0, 3) !== 'GIF')
32
- return null;
33
- return { width: littleEndian(bytes, 6, 2), height: littleEndian(bytes, 8, 2) };
34
- }
35
- function jpegExtent(bytes) {
36
- if (bytes.length < 4 || bytes[0] !== 0xff || bytes[1] !== 0xd8)
37
- return null;
38
- let at = 2;
39
- while (at + 9 < bytes.length) {
40
- if (bytes[at] !== 0xff)
41
- return null;
42
- const marker = bytes[at + 1] ?? 0;
43
- if (marker === 0xd8 || marker === 0x01 || (marker >= 0xd0 && marker <= 0xd7)) {
44
- at += 2;
45
- continue;
46
- }
47
- const length = bigEndian(bytes, at + 2, 2);
48
- if (marker >= 0xc0 && marker <= 0xcf && !SHAPELESS_JPEG_MARKERS.has(marker)) {
49
- return { width: bigEndian(bytes, at + 7, 2), height: bigEndian(bytes, at + 5, 2) };
50
- }
51
- if (length < 2)
52
- return null;
53
- at += 2 + length;
54
- }
55
- return null;
56
- }
57
- function webpExtent(bytes) {
58
- if (bytes.length < 30 || text(bytes, 0, 4) !== 'RIFF' || text(bytes, 8, 4) !== 'WEBP')
59
- return null;
60
- const chunk = text(bytes, 12, 4);
61
- if (chunk === 'VP8X') {
62
- return {
63
- width: littleEndian(bytes, 24, 3) + 1,
64
- height: littleEndian(bytes, 27, 3) + 1,
65
- };
66
- }
67
- if (chunk === 'VP8 ' && starts(bytes, WEBP_SYNC, 23)) {
68
- return {
69
- width: littleEndian(bytes, 26, 2) & 0x3fff,
70
- height: littleEndian(bytes, 28, 2) & 0x3fff,
71
- };
72
- }
73
- if (chunk === 'VP8L' && bytes[20] === 0x2f) {
74
- const packed = littleEndian(bytes, 21, 4);
75
- return { width: (packed & 0x3fff) + 1, height: ((packed >>> 14) & 0x3fff) + 1 };
76
- }
77
- return null;
78
- }
79
- function spatialExtent(bytes, from, to) {
80
- let at = from;
81
- while (at + 8 <= to) {
82
- const size = bigEndian(bytes, at, 4);
83
- const name = text(bytes, at + 4, 4);
84
- const end = size === 0 ? to : at + size;
85
- if (size === 1 || end <= at || end > to)
86
- return null;
87
- if (name === 'ispe') {
88
- if (end < at + 20)
89
- return null;
90
- return { width: bigEndian(bytes, at + 12, 4), height: bigEndian(bytes, at + 16, 4) };
91
- }
92
- if (BMFF_CONTAINERS.has(name)) {
93
- const inner = spatialExtent(bytes, at + (name === 'meta' ? 12 : 8), end);
94
- if (inner !== null)
95
- return inner;
96
- }
97
- at = end;
98
- }
99
- return null;
100
- }
101
- function bmffExtent(bytes) {
102
- if (bytes.length < 16 || text(bytes, 4, 4) !== 'ftyp')
103
- return null;
104
- return spatialExtent(bytes, 0, bytes.length);
105
- }
106
- export function extentOf(bytes) {
107
- return (pngExtent(bytes) ??
108
- jpegExtent(bytes) ??
109
- gifExtent(bytes) ??
110
- webpExtent(bytes) ??
111
- bmffExtent(bytes));
112
- }
113
- export function hexDigest(digest) {
114
- return [...new Uint8Array(digest)].map((value) => value.toString(16).padStart(2, '0')).join('');
115
- }
116
- export async function declaredFile(file) {
117
- const bytes = new Uint8Array(await file.blob.arrayBuffer());
118
- const extent = file.blob.type.startsWith('image/') ? extentOf(bytes) : null;
1
+ import { sha256 } from './digest.js';
2
+ export async function attachedFile(locator, file) {
119
3
  return {
4
+ locator,
120
5
  name: file.name,
121
- mediaType: file.blob.type,
122
- byteLength: bytes.byteLength,
123
- contentHash: hexDigest(await crypto.subtle.digest('SHA-256', bytes)),
124
- ...(extent === null ? {} : extent),
6
+ mediaType: file.type,
7
+ byteLength: file.size,
8
+ sha256: await sha256(file),
9
+ blob: file,
125
10
  };
126
11
  }
@@ -0,0 +1,27 @@
1
+ import { type QueryRoom } from './compile.js';
2
+ import type { Plan } from './query.js';
3
+ import type { HitCursor, SearchWindow } from './search.js';
4
+ import type { Connection } from './tables.js';
5
+ export declare const SEARCH_TABLES: readonly string[];
6
+ export type SearchAsk = {
7
+ readonly text: string;
8
+ readonly limit: number;
9
+ readonly after: readonly [HitCursor, HitCursor] | null;
10
+ readonly total: boolean;
11
+ readonly documents: {
12
+ readonly plan: Plan;
13
+ readonly rooms: readonly QueryRoom[];
14
+ readonly beneath: readonly {
15
+ readonly key: number;
16
+ readonly roots: readonly string[];
17
+ }[];
18
+ } | null;
19
+ readonly conversations: readonly string[];
20
+ };
21
+ export declare function indexDocument(connection: Connection, id: number, path: string, content: string): void;
22
+ export declare function indexMessage(connection: Connection, id: number, text: string): void;
23
+ export declare function unindexDocuments(connection: Connection, ids: Iterable<number>): void;
24
+ export declare function unindexMessages(connection: Connection, ids: Iterable<number>): void;
25
+ export declare function unindexAll(connection: Connection): void;
26
+ export declare function searched(connection: Connection, ask: SearchAsk): SearchWindow;
27
+ //# sourceMappingURL=fulltext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fulltext.d.ts","sourceRoot":"","sources":["../src/fulltext.ts"],"names":[],"mappings":"AACA,OAAO,EAA2B,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,KAAK,EAGV,SAAS,EAET,YAAY,EAEb,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,UAAU,EAAS,MAAM,aAAa,CAAC;AAmBrD,eAAO,MAAM,aAAa,EAAE,SAAS,MAAM,EAG1C,CAAC;AAsCF,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,GAAG,IAAI,CAAC;IACvD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE;QAClB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;QACpB,QAAQ,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;QACrC,QAAQ,CAAC,OAAO,EAAE,SAAS;YAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;SAAE,EAAE,CAAC;KAC1F,GAAG,IAAI,CAAC;IACT,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3C,CAAC;AA2CF,wBAAgB,aAAa,CAC3B,UAAU,EAAE,UAAU,EACtB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACd,IAAI,CAgBN;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAOnF;AAWD,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,CAEpF;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,CAEnF;AAED,wBAAgB,UAAU,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAIvD;AAgTD,wBAAgB,QAAQ,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,SAAS,GAAG,YAAY,CAsB7E"}
@@ -0,0 +1,368 @@
1
+ import { parse } from '@agentshouse/mdmodel';
2
+ import { selected } from './compile.js';
3
+ import { markdownPath } from './rows.js';
4
+ import { indexed, matchClauses, matched, matchExpression, queryTerms, snippet, } from './words.js';
5
+ const TOKENIZER = "porter unicode61 remove_diacritics 2 categories 'L* N* Co M*'";
6
+ const CONTENTLESS = `content='', contentless_delete=1, detail=full, tokenize="${TOKENIZER}"`;
7
+ const DOCUMENTS = 'replica_search';
8
+ const MESSAGES = 'conversation_search';
9
+ export const SEARCH_TABLES = [
10
+ `CREATE VIRTUAL TABLE ${DOCUMENTS} USING fts5(path, fields, sections, preamble, ${CONTENTLESS})`,
11
+ `CREATE VIRTUAL TABLE ${MESSAGES} USING fts5(text, ${CONTENTLESS})`,
12
+ ];
13
+ const CHUNKS = 4096;
14
+ const CHUNK_CHARACTERS = 32_768;
15
+ const CANDIDATES = 5_000;
16
+ const LEAST_WINDOW = 32;
17
+ const SNIPPET_REACH = 80;
18
+ function scalars(value) {
19
+ if (value === null || value === undefined)
20
+ return [];
21
+ if (Array.isArray(value))
22
+ return value.flatMap(scalars);
23
+ if (typeof value === 'object')
24
+ return Object.values(value).flatMap(scalars);
25
+ return [String(value)];
26
+ }
27
+ function partsOf(path, content) {
28
+ if (!markdownPath(path))
29
+ return null;
30
+ const read = parse(content, path);
31
+ if (!read.ok)
32
+ return null;
33
+ return {
34
+ fields: scalars(read.document.fields).join('\n'),
35
+ sections: Object.entries(read.document.sections)
36
+ .map(([heading, text]) => `${heading}\n${text}`)
37
+ .join('\n'),
38
+ preamble: read.document.preamble,
39
+ path,
40
+ };
41
+ }
42
+ function chunked(text) {
43
+ const chunks = [];
44
+ let start = 0;
45
+ do {
46
+ let end = Math.min(text.length, start + CHUNK_CHARACTERS);
47
+ if (end < text.length) {
48
+ const space = text.lastIndexOf(' ', end);
49
+ if (space > start + CHUNK_CHARACTERS / 2)
50
+ end = space;
51
+ else if (/[\ud800-\udbff]/u.test(text[end - 1]))
52
+ end -= 1;
53
+ }
54
+ chunks.push(text.slice(start, end));
55
+ start = end;
56
+ } while (start < text.length && chunks.length < CHUNKS);
57
+ return chunks;
58
+ }
59
+ function rowOf(id, chunk) {
60
+ return id * CHUNKS + chunk;
61
+ }
62
+ export function indexDocument(connection, id, path, content) {
63
+ const parts = partsOf(path, content);
64
+ if (parts === null)
65
+ return;
66
+ chunked(parts.sections).forEach((sections, chunk) => {
67
+ const head = chunk === 0;
68
+ connection.run(`INSERT INTO ${DOCUMENTS} (rowid, path, fields, sections, preamble) VALUES (?, ?, ?, ?, ?)`, [
69
+ rowOf(id, chunk),
70
+ head ? indexed(parts.path) : null,
71
+ head ? indexed(parts.fields) : null,
72
+ indexed(sections),
73
+ head ? indexed(parts.preamble) : null,
74
+ ]);
75
+ });
76
+ }
77
+ export function indexMessage(connection, id, text) {
78
+ chunked(text).forEach((chunk, at) => {
79
+ connection.run(`INSERT INTO ${MESSAGES} (rowid, text) VALUES (?, ?)`, [
80
+ rowOf(id, at),
81
+ indexed(chunk),
82
+ ]);
83
+ });
84
+ }
85
+ function unindexed(connection, table, ids) {
86
+ for (const id of ids) {
87
+ connection.run(`DELETE FROM ${table} WHERE rowid >= ? AND rowid < ?`, [
88
+ rowOf(id, 0),
89
+ rowOf(id + 1, 0),
90
+ ]);
91
+ }
92
+ }
93
+ export function unindexDocuments(connection, ids) {
94
+ unindexed(connection, DOCUMENTS, ids);
95
+ }
96
+ export function unindexMessages(connection, ids) {
97
+ unindexed(connection, MESSAGES, ids);
98
+ }
99
+ export function unindexAll(connection) {
100
+ for (const table of [DOCUMENTS, MESSAGES]) {
101
+ connection.run(`INSERT INTO ${table} (${table}) VALUES ('delete-all')`);
102
+ }
103
+ }
104
+ function pair(left, right) {
105
+ if (left[0] !== right[0])
106
+ return left[0] < right[0] ? -1 : 1;
107
+ if (left[1] !== right[1])
108
+ return left[1] < right[1] ? -1 : 1;
109
+ return 0;
110
+ }
111
+ function byRank(left, right) {
112
+ return right.score - left.score || pair(left.tie, right.tie);
113
+ }
114
+ function beyond(hit, cursor) {
115
+ if (cursor.length === 0)
116
+ return true;
117
+ return (hit.score < cursor[0] || (hit.score === cursor[0] && pair(hit.tie, [cursor[1], cursor[2]]) > 0));
118
+ }
119
+ function rankedWithin(connection, listed, sources, cursor, needed) {
120
+ const { table } = listed;
121
+ let window = Math.max(needed * 2, LEAST_WINDOW);
122
+ for (;;) {
123
+ const rows = [];
124
+ const boundaries = [];
125
+ for (const source of sources) {
126
+ const found = connection.rows(`SELECT rowid, bm25(${table}) AS score FROM ${table} WHERE ${table} MATCH ? ${source.sql} ORDER BY score, rowid LIMIT ?`, [source.expression, ...source.bind, window]);
127
+ for (const row of found)
128
+ rows.push([Number(row.rowid), Number(row.score)]);
129
+ if (found.length === window)
130
+ boundaries.push(-Number(found.at(-1).score));
131
+ }
132
+ rows.sort((left, right) => left[1] - right[1] || left[0] - right[0]);
133
+ const best = new Map();
134
+ for (const [row, score] of rows) {
135
+ const id = Math.floor(row / CHUNKS);
136
+ if (!best.has(id))
137
+ best.set(id, { chunk: row % CHUNKS, score: -score });
138
+ }
139
+ const ties = listed.ties([...best.keys()]);
140
+ const kept = [...best]
141
+ .flatMap(([id, held]) => {
142
+ const tie = ties.get(id);
143
+ return tie === undefined ? [] : [{ id, ...held, tie }];
144
+ })
145
+ .sort(byRank)
146
+ .filter((hit) => beyond(hit, cursor))
147
+ .slice(0, needed);
148
+ const last = kept.at(-1);
149
+ if (boundaries.length === 0)
150
+ return kept;
151
+ if (kept.length === needed &&
152
+ last !== undefined &&
153
+ boundaries.every((boundary) => boundary < last.score)) {
154
+ return kept;
155
+ }
156
+ window *= 4;
157
+ }
158
+ }
159
+ function reaches(connection, listed, expression, reach) {
160
+ const { table, scope } = listed;
161
+ const [counted] = connection.rows(`SELECT count(*) AS held FROM (SELECT rowid FROM ${table} WHERE ${table} MATCH ? AND ${scope.sql} LIMIT ?)`, [expression, ...scope.bind, reach + 1]);
162
+ return Number(counted?.held ?? 0) > reach;
163
+ }
164
+ function candidates(connection, listed, clauses, cursor, needed) {
165
+ const { table, scope } = listed;
166
+ const expression = matchExpression(clauses);
167
+ const within = { sql: `AND ${scope.sql}`, bind: scope.bind };
168
+ let reach = CANDIDATES;
169
+ for (;;) {
170
+ const [edge] = connection.rows(`SELECT rowid FROM ${table} WHERE ${table} MATCH ? ${within.sql} ORDER BY rowid LIMIT 1 OFFSET ?`, [expression, ...within.bind, reach]);
171
+ const floor = edge === undefined ? null : Number(edge.rowid);
172
+ const sources = [
173
+ floor === null
174
+ ? { expression, ...within }
175
+ : { expression, sql: `${within.sql} AND rowid < ?`, bind: [...within.bind, floor] },
176
+ ];
177
+ if (floor !== null) {
178
+ const common = new Set();
179
+ const selective = [];
180
+ const frequent = [];
181
+ for (const clause of clauses) {
182
+ if (clause.kind === 'phrase')
183
+ continue;
184
+ if (clause.kind !== 'term' && common.has(clause.term)) {
185
+ frequent.push(clause);
186
+ continue;
187
+ }
188
+ if (reaches(connection, listed, clause.text, reach)) {
189
+ if (clause.kind === 'term')
190
+ common.add(clause.term);
191
+ frequent.push(clause);
192
+ }
193
+ else
194
+ selective.push(clause);
195
+ }
196
+ const phrase = clauses.find((clause) => clause.kind === 'phrase');
197
+ if (phrase !== undefined) {
198
+ const rare = !clauses
199
+ .filter((clause) => clause.kind === 'term')
200
+ .every((clause) => common.has(clause.term)) ||
201
+ !reaches(connection, listed, phrase.text, reach);
202
+ (rare ? selective : frequent).push(phrase);
203
+ }
204
+ if (selective.length > 0) {
205
+ const chosen = `(${matchExpression(selective)})`;
206
+ const tail = { sql: `${within.sql} AND rowid >= ?`, bind: [...within.bind, floor] };
207
+ if (frequent.length === 0)
208
+ sources.push({ ...tail, expression: chosen });
209
+ else {
210
+ const rest = `(${matchExpression(frequent)})`;
211
+ sources.push({ ...tail, expression: `${chosen} AND ${rest}` }, { ...tail, expression: `${chosen} NOT ${rest}` });
212
+ }
213
+ }
214
+ }
215
+ const kept = rankedWithin(connection, listed, sources, cursor, needed);
216
+ if (floor === null || kept.length >= needed)
217
+ return kept;
218
+ reach *= 4;
219
+ }
220
+ }
221
+ function windowOf(connection, listed, clauses, cursor, limit) {
222
+ if (cursor === null || listed === null || clauses === null)
223
+ return { kept: [], cursor: null };
224
+ const found = candidates(connection, listed, clauses, cursor, limit + 1);
225
+ if (found.length <= limit)
226
+ return { kept: found, cursor: null };
227
+ const kept = found.slice(0, limit);
228
+ const last = kept.at(-1);
229
+ return { kept, cursor: last === undefined ? cursor : [last.score, ...last.tie] };
230
+ }
231
+ function totalOf(connection, listed, clauses) {
232
+ if (listed === null || clauses === null)
233
+ return 0;
234
+ const { table, scope } = listed;
235
+ const [counted] = connection.rows(`SELECT count(DISTINCT rowid / ${CHUNKS}) AS held FROM ${table} WHERE ${table} MATCH ? AND ${scope.sql}`, [matchExpression(clauses), ...scope.bind]);
236
+ return Number(counted?.held ?? 0);
237
+ }
238
+ function ids(found) {
239
+ return JSON.stringify(found);
240
+ }
241
+ function documentsListed(connection, documents) {
242
+ if (documents.rooms.length === 0)
243
+ return null;
244
+ const filter = selected(connection, documents.plan, documents.rooms);
245
+ const outside = documents.beneath.flatMap(({ key, roots }) => roots.map((root) => ({
246
+ sql: 'NOT (d.key = ? AND (d.path = ? OR (d.path >= ? AND d.path < ?)))',
247
+ bind: [key, root, `${root}/`, `${root}0`],
248
+ })));
249
+ const conditions = [filter, ...outside];
250
+ const locators = new Map(documents.rooms.map(({ key, locator }) => [key, locator]));
251
+ return {
252
+ table: DOCUMENTS,
253
+ scope: {
254
+ sql: `EXISTS (SELECT 1 FROM replica_entries d WHERE d.id = ${DOCUMENTS}.rowid / ${CHUNKS} AND d.type IS NOT NULL AND ${conditions.map(({ sql }) => `(${sql})`).join(' AND ')})`,
255
+ bind: conditions.flatMap(({ bind }) => bind),
256
+ },
257
+ ties(found) {
258
+ const tied = new Map();
259
+ for (const row of connection.rows('SELECT id, key, path FROM replica_entries WHERE id IN (SELECT value FROM json_each(?))', [ids(found)])) {
260
+ const locator = locators.get(Number(row.key));
261
+ if (locator !== undefined)
262
+ tied.set(Number(row.id), [locator, String(row.path)]);
263
+ }
264
+ return tied;
265
+ },
266
+ };
267
+ }
268
+ function messagesListed(connection, conversations) {
269
+ if (conversations.length === 0)
270
+ return null;
271
+ return {
272
+ table: MESSAGES,
273
+ scope: {
274
+ sql: `EXISTS (SELECT 1 FROM conversation_messages m WHERE m.id = ${MESSAGES}.rowid / ${CHUNKS} AND m.conversation IN (SELECT value FROM json_each(?)))`,
275
+ bind: [JSON.stringify(conversations)],
276
+ },
277
+ ties(found) {
278
+ return new Map(connection
279
+ .rows('SELECT id, conversation, identity FROM conversation_messages WHERE id IN (SELECT value FROM json_each(?))', [ids(found)])
280
+ .map((row) => [Number(row.id), [String(row.conversation), String(row.identity)]]));
281
+ },
282
+ };
283
+ }
284
+ function cut(candidates, terms) {
285
+ for (const [part, text] of candidates) {
286
+ const found = snippet(text, terms, SNIPPET_REACH);
287
+ if (found !== null)
288
+ return { snippet: { part, text: found.text, at: [...found.at] } };
289
+ }
290
+ return {};
291
+ }
292
+ function documentHit(connection, hit, terms) {
293
+ const [row] = connection.rows('SELECT type, content FROM replica_entries WHERE id = ?', [hit.id]);
294
+ if (row === undefined)
295
+ return null;
296
+ const [room, path] = hit.tie;
297
+ const parts = typeof row.content === 'string' ? partsOf(path, row.content) : null;
298
+ const whole = parts === null
299
+ ? []
300
+ : [
301
+ ['fields', parts.fields],
302
+ ['sections', parts.sections],
303
+ ['preamble', parts.preamble],
304
+ ['path', parts.path],
305
+ ];
306
+ const matches = {};
307
+ for (const [part, text] of whole) {
308
+ for (const term of matched(text, terms))
309
+ (matches[term] ??= []).push(part);
310
+ }
311
+ const sections = parts === null ? [] : chunked(parts.sections);
312
+ const chunk = parts === null
313
+ ? []
314
+ : hit.chunk === 0
315
+ ? [
316
+ ['fields', parts.fields],
317
+ ['sections', sections[0] ?? ''],
318
+ ['preamble', parts.preamble],
319
+ ['path', parts.path],
320
+ ]
321
+ : [['sections', sections[hit.chunk] ?? '']];
322
+ return {
323
+ room,
324
+ path,
325
+ type: String(row.type),
326
+ score: hit.score,
327
+ matches,
328
+ ...cut(chunk, terms),
329
+ };
330
+ }
331
+ function messageHit(connection, hit, terms) {
332
+ const [row] = connection.rows('SELECT text FROM conversation_messages WHERE id = ?', [hit.id]);
333
+ const text = typeof row?.text === 'string' ? row.text : '';
334
+ const matches = {};
335
+ for (const term of matched(text, terms))
336
+ matches[term] = ['text'];
337
+ const [conversation, identity] = hit.tie;
338
+ return {
339
+ conversation,
340
+ identity,
341
+ score: hit.score,
342
+ matches,
343
+ ...cut([['text', chunked(text)[hit.chunk] ?? '']], terms),
344
+ };
345
+ }
346
+ export function searched(connection, ask) {
347
+ const clauses = matchClauses(ask.text);
348
+ const terms = queryTerms(ask.text);
349
+ const documents = ask.documents === null ? null : documentsListed(connection, ask.documents);
350
+ const messages = messagesListed(connection, ask.conversations);
351
+ const [documentsAfter, messagesAfter] = ask.after ?? [[], []];
352
+ const documentWindow = windowOf(connection, documents, clauses, documentsAfter, ask.limit);
353
+ const messageWindow = windowOf(connection, messages, clauses, messagesAfter, ask.limit);
354
+ const more = documentWindow.cursor !== null || messageWindow.cursor !== null;
355
+ return {
356
+ documents: documentWindow.kept.flatMap((hit) => documentHit(connection, hit, terms) ?? []),
357
+ messages: messageWindow.kept.map((hit) => messageHit(connection, hit, terms)),
358
+ ...(more ? { next: JSON.stringify([documentWindow.cursor, messageWindow.cursor]) } : {}),
359
+ ...(ask.total
360
+ ? {
361
+ total: {
362
+ documents: totalOf(connection, documents, clauses),
363
+ messages: totalOf(connection, messages, clauses),
364
+ },
365
+ }
366
+ : {}),
367
+ };
368
+ }
package/dist/host.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { type MdRuntimeOptions } from './connection.js';
2
2
  import type { PortReach } from './reach.js';
3
- import type { CapacityCause, CodeAvailability, Persistence, Readiness, SearchObligation } from './readiness.js';
3
+ import type { CodeAvailability, Persistence, Readiness, SearchObligation, Space } from './readiness.js';
4
4
  import type { EntrySeed } from './seed.js';
5
5
  import { type RuntimePort } from './wire.js';
6
6
  export type MdRuntimeHostOptions = MdRuntimeOptions & {
@@ -11,13 +11,14 @@ export type AttachedPort = {
11
11
  narrow(reach: PortReach): void;
12
12
  readiness(deliver: (readiness: Readiness) => void): () => void;
13
13
  answered(queries: readonly unknown[], done: () => void): void;
14
- code(code: CodeAvailability, capacity: CapacityCause | null): void;
14
+ code(code: CodeAvailability, space: Space | null): void;
15
15
  search(search: SearchObligation): void;
16
16
  };
17
17
  export type MdRuntimeHost = {
18
18
  attach(port: RuntimePort, app: string, release?: string, reach?: PortReach, code?: CodeAvailability, search?: SearchObligation): AttachedPort;
19
19
  seed(entry: EntrySeed): void;
20
20
  persisted(answer: Persistence): void;
21
+ shown(onScreen: boolean): void;
21
22
  endAuthorization(holder?: string): Promise<void>;
22
23
  lose(locators: readonly string[]): Promise<void>;
23
24
  stop(): Promise<void>;
@@ -25,8 +26,7 @@ export type MdRuntimeHost = {
25
26
  export declare function serveMdRuntime(options: MdRuntimeHostOptions): MdRuntimeHost;
26
27
  export { entrySeedOf, type EntrySeed } from './seed.js';
27
28
  export type { PortReach, ReachedDocument } from './reach.js';
28
- export type { CapacityCause, CodeAvailability, Persistence, SearchObligation, } from './readiness.js';
29
+ export type { CodeAvailability, Persistence, SearchObligation, Space } from './readiness.js';
29
30
  export { createRecovery, retryDelay, type Attempt, type Attempted, type AttemptOptions, type Recovery, type RecoveryOptions, } from './recovery.js';
30
- export { AUTHORED_SCHEMA, authoredBinding, authoredBy, authoredHolder, bindAuthored, openDatabase, STORE_SCHEMA, transacted, } from './store.js';
31
31
  export type { RuntimePort } from './wire.js';
32
32
  //# sourceMappingURL=host.d.ts.map