@openanonymity/nanomem 0.1.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 (66) hide show
  1. package/README.md +194 -0
  2. package/package.json +85 -0
  3. package/src/backends/BaseStorage.js +177 -0
  4. package/src/backends/filesystem.js +177 -0
  5. package/src/backends/indexeddb.js +208 -0
  6. package/src/backends/ram.js +113 -0
  7. package/src/backends/schema.js +42 -0
  8. package/src/bullets/bulletIndex.js +125 -0
  9. package/src/bullets/compaction.js +109 -0
  10. package/src/bullets/index.js +16 -0
  11. package/src/bullets/normalize.js +241 -0
  12. package/src/bullets/parser.js +199 -0
  13. package/src/bullets/scoring.js +53 -0
  14. package/src/cli/auth.js +323 -0
  15. package/src/cli/commands.js +411 -0
  16. package/src/cli/config.js +120 -0
  17. package/src/cli/diff.js +68 -0
  18. package/src/cli/help.js +84 -0
  19. package/src/cli/output.js +269 -0
  20. package/src/cli/spinner.js +54 -0
  21. package/src/cli.js +178 -0
  22. package/src/engine/compactor.js +247 -0
  23. package/src/engine/executors.js +152 -0
  24. package/src/engine/ingester.js +229 -0
  25. package/src/engine/retriever.js +414 -0
  26. package/src/engine/toolLoop.js +176 -0
  27. package/src/imports/chatgpt.js +160 -0
  28. package/src/imports/index.js +14 -0
  29. package/src/imports/markdown.js +104 -0
  30. package/src/imports/oaFastchat.js +124 -0
  31. package/src/index.js +199 -0
  32. package/src/llm/anthropic.js +264 -0
  33. package/src/llm/openai.js +179 -0
  34. package/src/prompt_sets/conversation/ingestion.js +51 -0
  35. package/src/prompt_sets/document/ingestion.js +43 -0
  36. package/src/prompt_sets/index.js +31 -0
  37. package/src/types.js +382 -0
  38. package/src/utils/portability.js +174 -0
  39. package/types/backends/BaseStorage.d.ts +42 -0
  40. package/types/backends/filesystem.d.ts +11 -0
  41. package/types/backends/indexeddb.d.ts +12 -0
  42. package/types/backends/ram.d.ts +8 -0
  43. package/types/backends/schema.d.ts +14 -0
  44. package/types/bullets/bulletIndex.d.ts +47 -0
  45. package/types/bullets/compaction.d.ts +10 -0
  46. package/types/bullets/index.d.ts +36 -0
  47. package/types/bullets/normalize.d.ts +95 -0
  48. package/types/bullets/parser.d.ts +31 -0
  49. package/types/bullets/scoring.d.ts +12 -0
  50. package/types/engine/compactor.d.ts +27 -0
  51. package/types/engine/executors.d.ts +46 -0
  52. package/types/engine/ingester.d.ts +29 -0
  53. package/types/engine/retriever.d.ts +50 -0
  54. package/types/engine/toolLoop.d.ts +9 -0
  55. package/types/imports/chatgpt.d.ts +14 -0
  56. package/types/imports/index.d.ts +3 -0
  57. package/types/imports/markdown.d.ts +31 -0
  58. package/types/imports/oaFastchat.d.ts +30 -0
  59. package/types/index.d.ts +21 -0
  60. package/types/llm/anthropic.d.ts +16 -0
  61. package/types/llm/openai.d.ts +16 -0
  62. package/types/prompt_sets/conversation/ingestion.d.ts +7 -0
  63. package/types/prompt_sets/document/ingestion.d.ts +7 -0
  64. package/types/prompt_sets/index.d.ts +11 -0
  65. package/types/types.d.ts +293 -0
  66. package/types/utils/portability.d.ts +33 -0
package/src/types.js ADDED
@@ -0,0 +1,382 @@
1
+ /**
2
+ * Shared type definitions for @openanonymity/nanomem.
3
+ *
4
+ * This file contains only JSDoc typedefs. It has no runtime code.
5
+ * Import types with: @import { TypeName } from './types.js'
6
+ */
7
+
8
+ // ─── Bullet types ────────────────────────────────────────────────────────────
9
+
10
+ /**
11
+ * @typedef {'working' | 'long_term' | 'history'} Tier
12
+ */
13
+
14
+ /**
15
+ * @typedef {'active' | 'superseded' | 'expired' | 'uncertain'} Status
16
+ */
17
+
18
+ /**
19
+ * @typedef {'user_statement' | 'llm_infer' | 'document' | 'document_infer' | 'assistant_summary' | 'inference' | 'system'} Source
20
+ */
21
+
22
+ /**
23
+ * @typedef {'high' | 'medium' | 'low'} Confidence
24
+ */
25
+
26
+ /**
27
+ * @typedef {object} Bullet
28
+ * @property {string} text
29
+ * @property {string | null} topic
30
+ * @property {string | null} updatedAt
31
+ * @property {string | null} expiresAt
32
+ * @property {string | null} reviewAt
33
+ * @property {Tier} tier
34
+ * @property {Status} status
35
+ * @property {Source | null} source
36
+ * @property {Confidence | null} confidence
37
+ * @property {boolean} explicitTier
38
+ * @property {boolean} explicitStatus
39
+ * @property {boolean} explicitSource
40
+ * @property {boolean} explicitConfidence
41
+ * @property {string} heading
42
+ * @property {string} section
43
+ * @property {number} lineIndex
44
+ */
45
+
46
+ /**
47
+ * @typedef {object} EnsureBulletMetadataOptions
48
+ * @property {string} [defaultTopic]
49
+ * @property {Tier} [defaultTier]
50
+ * @property {Status} [defaultStatus]
51
+ * @property {Source} [defaultSource]
52
+ * @property {Confidence} [defaultConfidence]
53
+ * @property {string} [updatedAt]
54
+ */
55
+
56
+ /**
57
+ * @typedef {object} CompactionResult
58
+ * @property {Bullet[]} working
59
+ * @property {Bullet[]} longTerm
60
+ * @property {Bullet[]} history
61
+ * @property {Bullet[]} active
62
+ * @property {Bullet[]} archive
63
+ */
64
+
65
+ /**
66
+ * @typedef {object} CompactBulletsOptions
67
+ * @property {string} [today]
68
+ * @property {number} [maxActivePerTopic]
69
+ * @property {string} [defaultTopic]
70
+ */
71
+
72
+ // ─── Storage types ───────────────────────────────────────────────────────────
73
+
74
+ /**
75
+ * @typedef {object} StorageMetadata
76
+ * @property {string} [oneLiner]
77
+ * @property {number} [itemCount]
78
+ * @property {string[]} [titles]
79
+ */
80
+
81
+ /**
82
+ * @typedef {object} ExportRecord
83
+ * @property {string} path
84
+ * @property {string} [content]
85
+ * @property {string} [oneLiner]
86
+ * @property {number} [itemCount]
87
+ * @property {string[]} [titles]
88
+ * @property {string} [parentPath]
89
+ * @property {number} [createdAt]
90
+ * @property {number} [updatedAt]
91
+ */
92
+
93
+ /**
94
+ * @typedef {object} SearchResult
95
+ * @property {string} path
96
+ * @property {string[]} lines
97
+ */
98
+
99
+ /**
100
+ * @typedef {object} ListResult
101
+ * @property {string[]} files
102
+ * @property {string[]} dirs
103
+ */
104
+
105
+ // ─── LLM types ──────────────────────────────────────────────────────────────
106
+
107
+ /**
108
+ * @typedef {object} ToolCallFunction
109
+ * @property {string} name
110
+ * @property {string} arguments
111
+ */
112
+
113
+ /**
114
+ * @typedef {object} ToolCall
115
+ * @property {string} id
116
+ * @property {'function'} type
117
+ * @property {ToolCallFunction} function
118
+ */
119
+
120
+ /**
121
+ * @typedef {object} ToolFunctionParameters
122
+ * @property {'object'} type
123
+ * @property {Record<string, { type: string; description?: string }>} properties
124
+ * @property {string[]} required
125
+ */
126
+
127
+ /**
128
+ * @typedef {object} ToolFunctionDef
129
+ * @property {string} name
130
+ * @property {string} description
131
+ * @property {ToolFunctionParameters} parameters
132
+ */
133
+
134
+ /**
135
+ * @typedef {object} ToolDefinition
136
+ * @property {'function'} type
137
+ * @property {ToolFunctionDef} function
138
+ */
139
+
140
+ /**
141
+ * @typedef {object} LLMMessage
142
+ * @property {'system' | 'user' | 'assistant' | 'tool'} role
143
+ * @property {string | null} [content]
144
+ * @property {ToolCall[]} [tool_calls]
145
+ * @property {string} [tool_call_id]
146
+ */
147
+
148
+ /**
149
+ * @typedef {object} ChatCompletionParams
150
+ * @property {string} model
151
+ * @property {LLMMessage[]} messages
152
+ * @property {ToolDefinition[]} [tools]
153
+ * @property {number} [max_tokens]
154
+ * @property {number} [temperature]
155
+ */
156
+
157
+ /**
158
+ * @typedef {object} StreamChatCompletionParams
159
+ * @property {string} model
160
+ * @property {LLMMessage[]} messages
161
+ * @property {ToolDefinition[]} [tools]
162
+ * @property {number} [max_tokens]
163
+ * @property {number} [temperature]
164
+ * @property {(text: string) => void} [onDelta]
165
+ * @property {(text: string) => void} [onReasoning]
166
+ */
167
+
168
+ /**
169
+ * @typedef {object} ChatCompletionResponse
170
+ * @property {string} content
171
+ * @property {ToolCall[]} tool_calls
172
+ * @property {string} [finish_reason]
173
+ * @property {{ prompt_tokens: number; completion_tokens: number } | null} usage
174
+ */
175
+
176
+ /**
177
+ * @typedef {object} LLMClient
178
+ * @property {(params: ChatCompletionParams) => Promise<ChatCompletionResponse>} createChatCompletion
179
+ * @property {(params: StreamChatCompletionParams) => Promise<ChatCompletionResponse>} streamChatCompletion
180
+ */
181
+
182
+ /**
183
+ * @typedef {object} LLMClientOptions
184
+ * @property {string} apiKey
185
+ * @property {string} [baseUrl]
186
+ * @property {Record<string, string>} [headers]
187
+ */
188
+
189
+ // ─── Engine types ────────────────────────────────────────────────────────────
190
+
191
+ /**
192
+ * @typedef {'init' | 'retrieval' | 'fallback' | 'tool_call' | 'reasoning' | 'loading' | 'complete'} ProgressStage
193
+ */
194
+
195
+ /**
196
+ * @typedef {object} ProgressEvent
197
+ * @property {ProgressStage} stage
198
+ * @property {string} message
199
+ * @property {string} [tool]
200
+ * @property {Record<string, any>} [args]
201
+ * @property {string | Record<string, any>} [result]
202
+ * @property {string[]} [paths]
203
+ * @property {number} [iteration]
204
+ * @property {string} [path]
205
+ */
206
+
207
+ /**
208
+ * @typedef {object} RetrievalResult
209
+ * @property {{ path: string; content: string }[]} files
210
+ * @property {string[]} paths
211
+ * @property {string | null} assembledContext
212
+ */
213
+
214
+ /**
215
+ * @typedef {object} IngestOptions
216
+ * @property {string} [updatedAt]
217
+ * @property {'conversation' | 'document' | string} [mode] - Prompt set to use for extraction
218
+ * @property {'conversation' | 'document' | string} [extractionMode] - Alias for mode (deprecated, use mode)
219
+ * @property {string} [sessionTitle]
220
+ */
221
+
222
+ /**
223
+ * @typedef {object} IngestResult
224
+ * @property {'processed' | 'skipped' | 'error'} status
225
+ * @property {number} writeCalls
226
+ * @property {Array<{path: string, before: string | null, after: string | null}>} [writes]
227
+ * @property {string} [error]
228
+ */
229
+
230
+ /**
231
+ * @typedef {object} ToolCallLogEntry
232
+ * @property {string} name
233
+ * @property {Record<string, any>} args
234
+ * @property {string} result
235
+ * @property {string} toolCallId
236
+ */
237
+
238
+ /**
239
+ * @typedef {object} ToolLoopResult
240
+ * @property {string} textResponse
241
+ * @property {{ name: string; arguments: Record<string, any> } | null} terminalToolResult
242
+ * @property {LLMMessage[]} messages
243
+ * @property {number} iterations
244
+ * @property {ToolCallLogEntry[]} toolCallLog
245
+ */
246
+
247
+ /**
248
+ * @typedef {object} ToolLoopOptions
249
+ * @property {LLMClient} llmClient
250
+ * @property {string} model
251
+ * @property {ToolDefinition[]} tools
252
+ * @property {Record<string, ToolExecutor>} toolExecutors
253
+ * @property {LLMMessage[]} messages
254
+ * @property {string | null} [terminalTool]
255
+ * @property {number} [maxIterations]
256
+ * @property {number} [maxOutputTokens]
257
+ * @property {number} [temperature]
258
+ * @property {((name: string, args: Record<string, any>, result: string) => void) | null} [onToolCall]
259
+ * @property {((text: string, iteration: number) => void) | null} [onModelText]
260
+ * @property {((chunk: string, iteration: number) => void) | null} [onReasoning]
261
+ * @property {AbortSignal | null} [signal]
262
+ */
263
+
264
+ /**
265
+ * @typedef {object} ExtractionExecutorHooks
266
+ * @property {(content: string, path: string) => string} [normalizeContent]
267
+ * @property {(existing: string | null, incoming: string, path: string) => string} [mergeWithExisting]
268
+ * @property {(path: string) => Promise<void>} [refreshIndex]
269
+ * @property {(path: string, before: string, after: string) => void} [onWrite]
270
+ */
271
+
272
+ /**
273
+ * @typedef {(args: any) => Promise<string>} ToolExecutor
274
+ */
275
+
276
+ /**
277
+ * @typedef {object} StorageBackend
278
+ * @property {() => Promise<void>} init
279
+ * @property {(path: string) => Promise<string | null>} read
280
+ * @property {(path: string, content: string) => Promise<void>} write
281
+ * @property {(path: string) => Promise<void>} delete
282
+ * @property {(path: string) => Promise<boolean>} exists
283
+ * @property {(query: string) => Promise<SearchResult[]>} search
284
+ * @property {(dirPath?: string) => Promise<ListResult>} ls
285
+ * @property {() => Promise<string | null>} getTree
286
+ * @property {() => Promise<void>} rebuildTree
287
+ * @property {() => Promise<ExportRecord[]>} exportAll
288
+ * @property {() => Promise<void>} clear
289
+ */
290
+
291
+ /**
292
+ * @typedef {object} StorageFacade
293
+ * @property {(path: string) => Promise<string | null>} read
294
+ * @property {(path: string, content: string) => Promise<void>} write
295
+ * @property {(path: string) => Promise<void>} delete
296
+ * @property {(path: string) => Promise<boolean>} exists
297
+ * @property {(query: string) => Promise<SearchResult[]>} search
298
+ * @property {(dirPath?: string) => Promise<ListResult>} ls
299
+ * @property {() => Promise<string | null>} getTree
300
+ * @property {() => Promise<void>} rebuildTree
301
+ * @property {() => Promise<ExportRecord[]>} exportAll
302
+ * @property {() => Promise<void>} clear
303
+ */
304
+
305
+ // ─── Memory bank config ─────────────────────────────────────────────────────
306
+
307
+ /**
308
+ * @typedef {object} MemoryBankLLMConfig
309
+ * @property {string} apiKey
310
+ * @property {string} [baseUrl]
311
+ * @property {string} [model]
312
+ * @property {'openai' | 'anthropic' | 'tinfoil' | 'custom' | string} [provider]
313
+ * @property {Record<string, string>} [headers]
314
+ */
315
+
316
+ /**
317
+ * @typedef {object} MemoryBankConfig
318
+ * @property {MemoryBankLLMConfig} [llm]
319
+ * @property {LLMClient} [llmClient]
320
+ * @property {string} [model]
321
+ * @property {'ram' | 'filesystem' | 'indexeddb' | StorageBackend} [storage]
322
+ * @property {string} [storagePath]
323
+ * @property {(event: ProgressEvent) => void} [onProgress]
324
+ * @property {(event: ProgressEvent) => void} [onCompactProgress]
325
+ * @property {(name: string, args: Record<string, any>, result: string) => void} [onToolCall]
326
+ * @property {(text: string) => void} [onModelText]
327
+ */
328
+
329
+ // ─── Import types ────────────────────────────────────────────────────────────
330
+
331
+ /**
332
+ * @typedef {object} Message
333
+ * @property {'user' | 'assistant'} role
334
+ * @property {string} content
335
+ */
336
+
337
+ /**
338
+ * @typedef {object} ChatGptSession
339
+ * @property {string | null} title
340
+ * @property {Message[]} messages
341
+ * @property {string | null} updatedAt
342
+ */
343
+
344
+ /**
345
+ * @typedef {object} SessionSummary
346
+ * @property {string} id
347
+ * @property {string} title
348
+ * @property {number | null} createdAt
349
+ * @property {number | null} updatedAt
350
+ * @property {number | null} messageCount
351
+ * @property {string | null} model
352
+ */
353
+
354
+ /**
355
+ * @typedef {object} SessionWithConversation
356
+ * @property {SessionSummary} session
357
+ * @property {Message[]} conversation
358
+ */
359
+
360
+ // ─── Bullet index types ──────────────────────────────────────────────────────
361
+
362
+ /**
363
+ * @typedef {object} BulletItem
364
+ * @property {string} path
365
+ * @property {Bullet} bullet
366
+ * @property {number} fileUpdatedAt
367
+ */
368
+
369
+ /**
370
+ * @typedef {object} MemoryBank
371
+ * @property {() => Promise<void>} init
372
+ * @property {(query: string, conversationText?: string) => Promise<RetrievalResult | null>} retrieve
373
+ * @property {(messages: Message[], options?: IngestOptions) => Promise<IngestResult>} ingest
374
+ * @property {() => Promise<{filesChanged: number, filesTotal: number} | undefined>} compact
375
+ * @property {StorageFacade} storage
376
+ * @property {() => Promise<string>} serialize
377
+ * @property {() => Promise<Uint8Array>} toZip
378
+ * @property {StorageBackend} _backend
379
+ * @property {import('./bullets/bulletIndex.js').MemoryBulletIndex} _bulletIndex
380
+ */
381
+
382
+ export {};
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Portability utilities — convert memory state to/from portable formats.
3
+ *
4
+ * serialize(records) → string (line-delimited text, one file per block)
5
+ * deserialize(str) → records (inverse of serialize)
6
+ * toZip(records) → Uint8Array (valid ZIP archive, STORE mode, zero deps)
7
+ *
8
+ * `records` is the shape returned by backend.exportAll():
9
+ * [{ path: string, content: string }, ...]
10
+ */
11
+ /** @import { ExportRecord } from '../types.js' */
12
+
13
+ // ─── Text serialization ───────────────────────────────────────────────────────
14
+
15
+ const FILE_PREFIX = '--- FILE: ';
16
+
17
+ /**
18
+ * Convert an array of {path, content} records into a single portable string.
19
+ *
20
+ * Format:
21
+ * --- FILE: path/to/file.md
22
+ * <file content>
23
+ * --- FILE: other/file.md
24
+ * <file content>
25
+ *
26
+ * Note: file content must not contain a line that starts with "--- FILE: ".
27
+ * This is extremely unlikely for memory markdown but worth being aware of.
28
+ * @param {ExportRecord[]} records
29
+ * @returns {string}
30
+ */
31
+ export function serialize(records) {
32
+ return records
33
+ .filter(({ path }) => !path.endsWith('_tree.md'))
34
+ .map(({ path, content }) => `${FILE_PREFIX}${path}\n${content ?? ''}`)
35
+ .join('\n');
36
+ }
37
+
38
+ /**
39
+ * Reconstruct records from a serialized string produced by serialize().
40
+ * @param {string} str
41
+ * @returns {{ path: string, content: string }[]}
42
+ */
43
+ export function deserialize(str) {
44
+ const records = [];
45
+ const lines = str.split('\n');
46
+ let currentPath = null;
47
+ let currentLines = [];
48
+
49
+ for (const line of lines) {
50
+ if (line.startsWith(FILE_PREFIX)) {
51
+ if (currentPath !== null) {
52
+ records.push({ path: currentPath, content: currentLines.join('\n') });
53
+ }
54
+ currentPath = line.slice(FILE_PREFIX.length);
55
+ currentLines = [];
56
+ } else {
57
+ if (currentPath !== null) currentLines.push(line);
58
+ }
59
+ }
60
+
61
+ if (currentPath !== null) {
62
+ records.push({ path: currentPath, content: currentLines.join('\n') });
63
+ }
64
+
65
+ return records;
66
+ }
67
+
68
+ // ─── ZIP archive ──────────────────────────────────────────────────────────────
69
+
70
+ // CRC-32 lookup table (built once at module load)
71
+ const CRC_TABLE = (() => {
72
+ const t = new Uint32Array(256);
73
+ for (let i = 0; i < 256; i++) {
74
+ let c = i;
75
+ for (let j = 0; j < 8; j++) c = (c & 1) ? (0xedb88320 ^ (c >>> 1)) : (c >>> 1);
76
+ t[i] = c;
77
+ }
78
+ return t;
79
+ })();
80
+
81
+ function crc32(data) {
82
+ let crc = 0xffffffff;
83
+ for (let i = 0; i < data.length; i++) crc = CRC_TABLE[(crc ^ data[i]) & 0xff] ^ (crc >>> 8);
84
+ return (crc ^ 0xffffffff) >>> 0;
85
+ }
86
+
87
+ function u16(n) { return [(n & 0xff), (n >> 8) & 0xff]; }
88
+ function u32(n) { return [(n & 0xff), (n >> 8) & 0xff, (n >> 16) & 0xff, (n >> 24) & 0xff]; }
89
+
90
+ function concatU8(arrays) {
91
+ const total = arrays.reduce((s, a) => s + a.length, 0);
92
+ const out = new Uint8Array(total);
93
+ let off = 0;
94
+ for (const a of arrays) { out.set(a, off); off += a.length; }
95
+ return out;
96
+ }
97
+
98
+ /**
99
+ * Produce a valid ZIP archive (STORE, no compression) from an array of records.
100
+ * Works in Node.js ≥ 18 and modern browsers (uses TextEncoder + Uint8Array only).
101
+ *
102
+ * @param {ExportRecord[]} records
103
+ * @returns {Uint8Array}
104
+ */
105
+ export function toZip(records) {
106
+ const enc = new TextEncoder();
107
+ const localParts = []; // raw bytes for local file entries
108
+ const centralParts = []; // raw bytes for central directory entries
109
+ let localOffset = 0;
110
+ const filtered = records.filter(r => !r.path.endsWith('_tree.md'));
111
+
112
+ for (const { path, content } of filtered) {
113
+ const name = enc.encode(path);
114
+ const data = enc.encode(content ?? '');
115
+ const checksum = crc32(data);
116
+ const size = data.length;
117
+
118
+ // Local file header (30 bytes) + name + data
119
+ const localHeader = new Uint8Array([
120
+ 0x50, 0x4b, 0x03, 0x04, // signature
121
+ 0x14, 0x00, // version needed: 2.0
122
+ 0x00, 0x00, // flags
123
+ 0x00, 0x00, // compression: STORE
124
+ 0x00, 0x00, // mod time
125
+ 0x00, 0x00, // mod date
126
+ ...u32(checksum),
127
+ ...u32(size), // compressed size
128
+ ...u32(size), // uncompressed size
129
+ ...u16(name.length),
130
+ 0x00, 0x00, // extra field length
131
+ ]);
132
+ localParts.push(localHeader, name, data);
133
+
134
+ // Central directory entry (46 bytes) + name
135
+ const cdEntry = new Uint8Array([
136
+ 0x50, 0x4b, 0x01, 0x02, // signature
137
+ 0x14, 0x00, // version made by: 2.0
138
+ 0x14, 0x00, // version needed: 2.0
139
+ 0x00, 0x00, // flags
140
+ 0x00, 0x00, // compression: STORE
141
+ 0x00, 0x00, // mod time
142
+ 0x00, 0x00, // mod date
143
+ ...u32(checksum),
144
+ ...u32(size), // compressed size
145
+ ...u32(size), // uncompressed size
146
+ ...u16(name.length),
147
+ 0x00, 0x00, // extra field length
148
+ 0x00, 0x00, // file comment length
149
+ 0x00, 0x00, // disk number start
150
+ 0x00, 0x00, // internal attributes
151
+ 0x00, 0x00, 0x00, 0x00, // external attributes
152
+ ...u32(localOffset),
153
+ ]);
154
+ centralParts.push(cdEntry, name);
155
+
156
+ localOffset += localHeader.length + name.length + data.length;
157
+ }
158
+
159
+ const centralDir = concatU8(centralParts);
160
+
161
+ // End of central directory record (22 bytes)
162
+ const eocd = new Uint8Array([
163
+ 0x50, 0x4b, 0x05, 0x06, // signature
164
+ 0x00, 0x00, // disk number
165
+ 0x00, 0x00, // disk with central dir start
166
+ ...u16(filtered.length), // entries on this disk
167
+ ...u16(filtered.length), // total entries
168
+ ...u32(centralDir.length),
169
+ ...u32(localOffset), // central dir offset
170
+ 0x00, 0x00, // comment length
171
+ ]);
172
+
173
+ return concatU8([...localParts, centralDir, eocd]);
174
+ }
@@ -0,0 +1,42 @@
1
+ export class BaseStorage {
2
+ /** @returns {Promise<void>} */ init(): Promise<void>;
3
+ /** @returns {Promise<string | null>} */ _readRaw(_path: any): Promise<string | null>;
4
+ _writeRaw(_path: any, _content: any, _meta: any): Promise<void>;
5
+ /** @param {string} _path @returns {Promise<void>} */ delete(_path: string): Promise<void>;
6
+ /** @param {string} _path @returns {Promise<boolean>} */ exists(_path: string): Promise<boolean>;
7
+ /** @returns {Promise<void>} */ rebuildTree(): Promise<void>;
8
+ /** @returns {Promise<ExportRecord[]>} */ exportAll(): Promise<ExportRecord[]>;
9
+ /** @returns {Promise<void>} */ clear(): Promise<void>;
10
+ /**
11
+ * @param {string} path
12
+ * @returns {Promise<string | null>}
13
+ */
14
+ read(path: string): Promise<string | null>;
15
+ /**
16
+ * @param {string} path
17
+ * @param {string} content
18
+ * @returns {Promise<void>}
19
+ */
20
+ write(path: string, content: string): Promise<void>;
21
+ /** @returns {Promise<string | null>} */
22
+ getTree(): Promise<string | null>;
23
+ /**
24
+ * @param {string} query
25
+ * @returns {Promise<SearchResult[]>}
26
+ */
27
+ search(query: string): Promise<SearchResult[]>;
28
+ /**
29
+ * @param {string} [dirPath]
30
+ * @returns {Promise<ListResult>}
31
+ */
32
+ ls(dirPath?: string): Promise<ListResult>;
33
+ _isInternalPath(path: any): boolean;
34
+ /** Override for efficient path listing. Default uses exportAll(). */
35
+ _listAllPaths(): Promise<string[]>;
36
+ _parentPath(filePath: any): any;
37
+ /** Generate a one-line summary of file content for the index. */
38
+ _generateOneLiner(content: any): any;
39
+ }
40
+ import type { ExportRecord } from '../types.js';
41
+ import type { SearchResult } from '../types.js';
42
+ import type { ListResult } from '../types.js';
@@ -0,0 +1,11 @@
1
+ export class FileSystemStorage extends BaseStorage {
2
+ constructor(rootDir: any);
3
+ _root: any;
4
+ _initialized: boolean;
5
+ _writeRaw(path: any, content: any, _meta?: {}): Promise<void>;
6
+ _listAllPaths(): Promise<any>;
7
+ _resolve(path: any): string;
8
+ _ensureDir(dirPath: any): Promise<void>;
9
+ _walkFiles(dir?: string): any;
10
+ }
11
+ import { BaseStorage } from './BaseStorage.js';
@@ -0,0 +1,12 @@
1
+ export class IndexedDBStorage extends BaseStorage {
2
+ /** @type {IDBDatabase | null} */
3
+ db: IDBDatabase | null;
4
+ /** @type {Promise<IDBDatabase> | null} */
5
+ _initPromise: Promise<IDBDatabase> | null;
6
+ _bootstrap(): Promise<void>;
7
+ _readRaw(path: any): Promise<any>;
8
+ _writeRaw(path: any, content: any, meta?: {}): Promise<void>;
9
+ _get(path: any): Promise<any>;
10
+ _getAll(): Promise<any>;
11
+ }
12
+ import { BaseStorage } from './BaseStorage.js';
@@ -0,0 +1,8 @@
1
+ export class InMemoryStorage extends BaseStorage {
2
+ _files: Map<any, any>;
3
+ _initialized: boolean;
4
+ _readRaw(path: any): Promise<any>;
5
+ _writeRaw(path: any, content: any, meta?: {}): Promise<void>;
6
+ _listAllPaths(): Promise<any[]>;
7
+ }
8
+ import { BaseStorage } from './BaseStorage.js';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Memory index generation and bootstrap.
3
+ */
4
+ /** @import { ExportRecord } from '../types.js' */
5
+ export function buildBootstrapIndex(): string;
6
+ export function createBootstrapRecords(now?: number): {
7
+ path: string;
8
+ content: string;
9
+ oneLiner: string;
10
+ parentPath: string;
11
+ createdAt: number;
12
+ updatedAt: number;
13
+ }[];
14
+ export function buildTree(files: any): string;
@@ -0,0 +1,47 @@
1
+ export class MemoryBulletIndex {
2
+ /**
3
+ * @param {StorageBackend} backend
4
+ */
5
+ constructor(backend: StorageBackend);
6
+ _backend: StorageBackend;
7
+ _initialized: boolean;
8
+ _initPromise: Promise<void> | null;
9
+ _pathToBullets: Map<any, any>;
10
+ _pathToUpdatedAt: Map<any, any>;
11
+ /** @returns {Promise<void>} */
12
+ init(): Promise<void>;
13
+ /** @returns {Promise<void>} */
14
+ rebuild(): Promise<void>;
15
+ _rebuild(): Promise<void>;
16
+ _parseForIndex(path: any, content: any): Bullet[] | {
17
+ text: string;
18
+ topic: any;
19
+ updatedAt: string;
20
+ expiresAt: null;
21
+ reviewAt: null;
22
+ tier: string;
23
+ status: string;
24
+ source: null;
25
+ confidence: null;
26
+ explicitTier: boolean;
27
+ explicitStatus: boolean;
28
+ explicitSource: boolean;
29
+ explicitConfidence: boolean;
30
+ heading: string;
31
+ section: string;
32
+ lineIndex: number;
33
+ }[];
34
+ /**
35
+ * @param {string} path
36
+ * @returns {Promise<void>}
37
+ */
38
+ refreshPath(path: string): Promise<void>;
39
+ /**
40
+ * @param {string[]} paths
41
+ * @returns {BulletItem[]}
42
+ */
43
+ getBulletsForPaths(paths: string[]): BulletItem[];
44
+ }
45
+ import type { StorageBackend } from '../types.js';
46
+ import type { Bullet } from '../types.js';
47
+ import type { BulletItem } from '../types.js';