@dinoxx/dinox-cli 2.0.0 → 2.1.1

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 (48) hide show
  1. package/dist/commands/notes/repo/read.d.ts +8 -0
  2. package/dist/commands/notes/repo/read.js +321 -0
  3. package/dist/commands/notes/repo/resolve.d.ts +8 -0
  4. package/dist/commands/notes/repo/resolve.js +187 -0
  5. package/dist/commands/notes/repo/search.d.ts +42 -0
  6. package/dist/commands/notes/repo/search.js +513 -0
  7. package/dist/commands/notes/repo/shared.d.ts +190 -0
  8. package/dist/commands/notes/repo/shared.js +102 -0
  9. package/dist/commands/notes/repo/write.d.ts +24 -0
  10. package/dist/commands/notes/repo/write.js +251 -0
  11. package/dist/commands/notes/repo.d.ts +7 -186
  12. package/dist/commands/notes/repo.js +7 -1360
  13. package/dist/commands/schema/commandMetadata.js +4 -4
  14. package/dist/commands/schema/index.d.ts +1 -0
  15. package/dist/commands/schema/index.js +23 -2
  16. package/dist/commands/schema/renderSkills.d.ts +4 -0
  17. package/dist/commands/schema/renderSkills.js +41 -2
  18. package/dist/commands/skills/index.d.ts +26 -0
  19. package/dist/commands/skills/index.js +115 -11
  20. package/dist/commands/storage/repo/configs.d.ts +30 -0
  21. package/dist/commands/storage/repo/configs.js +259 -0
  22. package/dist/commands/storage/repo/operations.d.ts +25 -0
  23. package/dist/commands/storage/repo/operations.js +337 -0
  24. package/dist/commands/storage/repo/remote.d.ts +32 -0
  25. package/dist/commands/storage/repo/remote.js +132 -0
  26. package/dist/commands/storage/repo/resources.d.ts +12 -0
  27. package/dist/commands/storage/repo/resources.js +134 -0
  28. package/dist/commands/storage/repo/shared.d.ts +181 -0
  29. package/dist/commands/storage/repo/shared.js +36 -0
  30. package/dist/commands/storage/repo/targets.d.ts +12 -0
  31. package/dist/commands/storage/repo/targets.js +178 -0
  32. package/dist/commands/storage/repo.d.ts +3 -166
  33. package/dist/commands/storage/repo.js +4 -1034
  34. package/dist/contract/snapshot.d.ts +30 -0
  35. package/dist/contract/snapshot.js +114 -0
  36. package/dist/daemon/client.js +2 -2
  37. package/dist/powersync/uploader.js +27 -2
  38. package/dist/skills/commandRefs.d.ts +21 -0
  39. package/dist/skills/commandRefs.js +132 -0
  40. package/dist/skills/generate.d.ts +13 -0
  41. package/dist/skills/generate.js +80 -0
  42. package/dist/skills/registry.d.ts +13 -0
  43. package/dist/skills/registry.js +58 -0
  44. package/dist/utils/errorCodes.d.ts +311 -0
  45. package/dist/utils/errorCodes.js +92 -0
  46. package/dist/utils/errors.d.ts +13 -2
  47. package/dist/utils/errors.js +11 -0
  48. package/package.json +11 -4
@@ -0,0 +1,513 @@
1
+ // Note search: keyword/FTS search, filters (tags, dates, boxes, starred), pagination.
2
+ import { DinoxError } from '../../../utils/errors.js';
3
+ import { tokenizeWithJieba } from '../../../utils/tokenize.js';
4
+ import { buildNoteSearchSqlFilter } from '../searchSql.js';
5
+ import { loadZettelBoxNameMap, normalizeStoredStarred, parseStoredTags, parseZettelBoxIds, resolveSummary, } from './shared.js';
6
+ import { resolveZettelBoxIdsForSearch } from './resolve.js';
7
+ /**
8
+ * Resolve box names to ids and run the note search. Shared by the local command path and
9
+ * the daemon RPC handler so both produce identical results from one implementation.
10
+ */
11
+ export async function executeNoteSearch(db, params) {
12
+ const zettelBoxIds = await resolveZettelBoxIdsForSearch(db, params.boxValues);
13
+ const result = await searchNotesPage(db, params.keyword, {
14
+ includeDeleted: params.includeDeleted,
15
+ tagsExpression: params.tagsExpression,
16
+ createdAtFrom: params.createdAtFrom,
17
+ createdAtTo: params.createdAtTo,
18
+ starred: params.starred,
19
+ zettelBoxIds,
20
+ matchEmptyZettelBoxes: params.matchEmptyZettelBoxes,
21
+ sqlExpression: params.sqlExpression,
22
+ limit: params.limit,
23
+ offset: params.offset,
24
+ });
25
+ return { rows: result.rows, totalCount: result.totalCount, zettelBoxIds };
26
+ }
27
+ export async function searchNotes(db, query, options) {
28
+ return (await searchNotesPage(db, query, options)).rows;
29
+ }
30
+ export async function searchNotesPage(db, query, options) {
31
+ const normalizedQuery = query.trim();
32
+ const tagFilter = buildTagFilter(options.tagsExpression, 'n');
33
+ const createdAtFilter = buildCreatedAtFilter(options.createdAtFrom, options.createdAtTo, 'n');
34
+ const starredFilter = buildStarredFilter(options.starred, 'n');
35
+ const zettelBoxFilter = buildZettelBoxFilter(options.zettelBoxIds, options.matchEmptyZettelBoxes, 'n');
36
+ const sqlFilter = await buildNoteSearchSqlFilter({
37
+ expression: options.sqlExpression,
38
+ noteAlias: 'n',
39
+ resolveZettelBoxNames: (names) => resolveZettelBoxIdsForSearch(db, names),
40
+ });
41
+ let rows = [];
42
+ if (!options.includeDeleted) {
43
+ const ftsMatch = buildFtsMatchQuery(normalizedQuery);
44
+ if (ftsMatch) {
45
+ try {
46
+ const ftsResult = await searchNotesWithFts(db, ftsMatch, tagFilter, createdAtFilter, starredFilter, zettelBoxFilter, sqlFilter, options.limit, options.offset);
47
+ return {
48
+ rows: await enrichSearchRows(db, ftsResult.rows),
49
+ totalCount: ftsResult.totalCount,
50
+ };
51
+ }
52
+ catch (error) {
53
+ const message = error instanceof Error ? error.message : String(error);
54
+ const normalized = message.toLowerCase();
55
+ // FTS table may not be initialized yet, or query syntax may fail.
56
+ if (normalized.includes('no such table') ||
57
+ normalized.includes('fts5') ||
58
+ normalized.includes('malformed match') ||
59
+ normalized.includes('syntax error')) {
60
+ // Fallback to legacy LIKE search below.
61
+ }
62
+ else {
63
+ throw error;
64
+ }
65
+ }
66
+ }
67
+ }
68
+ const whereClauses = [
69
+ options.includeDeleted ? '1 = 1' : '(n.is_del IS NULL OR n.is_del = 0)',
70
+ ];
71
+ const params = [];
72
+ if (normalizedQuery.length > 0) {
73
+ const like = `%${escapeLikePattern(normalizedQuery)}%`;
74
+ whereClauses.push(`
75
+ (
76
+ (n.title IS NOT NULL AND n.title LIKE ? ESCAPE '\\')
77
+ OR (n.content_text IS NOT NULL AND n.content_text LIKE ? ESCAPE '\\')
78
+ OR (n.content_md IS NOT NULL AND n.content_md LIKE ? ESCAPE '\\')
79
+ )
80
+ `);
81
+ params.push(like, like, like);
82
+ }
83
+ if (tagFilter) {
84
+ whereClauses.push(tagFilter.sql);
85
+ params.push(...tagFilter.params);
86
+ }
87
+ if (createdAtFilter) {
88
+ whereClauses.push(createdAtFilter.sql);
89
+ params.push(...createdAtFilter.params);
90
+ }
91
+ if (starredFilter) {
92
+ whereClauses.push(starredFilter.sql);
93
+ params.push(...starredFilter.params);
94
+ }
95
+ if (zettelBoxFilter) {
96
+ whereClauses.push(zettelBoxFilter.sql);
97
+ params.push(...zettelBoxFilter.params);
98
+ }
99
+ if (sqlFilter) {
100
+ whereClauses.push(sqlFilter.sql);
101
+ params.push(...sqlFilter.params);
102
+ }
103
+ const baseFromWhere = `
104
+ FROM c_note n
105
+ WHERE ${whereClauses.join('\n AND ')}
106
+ `;
107
+ const paginated = typeof options.limit === 'number';
108
+ const totalCount = paginated ? await countSearchRows(db, baseFromWhere, params) : null;
109
+ if (totalCount === 0) {
110
+ return { rows: [], totalCount };
111
+ }
112
+ rows = await db.getAll(`
113
+ SELECT
114
+ n.id,
115
+ n.title,
116
+ n.summary,
117
+ n.content_text,
118
+ n.tags,
119
+ n.zettel_boxes,
120
+ n.created_at,
121
+ n.starred_at,
122
+ CASE WHEN n.starred_at IS NOT NULL THEN 1 ELSE 0 END AS is_starred
123
+ ${baseFromWhere}
124
+ ORDER BY COALESCE(n.updated_at, n.created_at) DESC, n.id DESC
125
+ ${buildPaginationSql(options.limit, options.offset)}
126
+ `, appendPaginationParams(params, options.limit, options.offset));
127
+ return { rows: await enrichSearchRows(db, rows), totalCount: totalCount ?? rows.length };
128
+ }
129
+ async function searchNotesWithFts(db, matchQuery, tagFilter, createdAtFilter, starredFilter, zettelBoxFilter, sqlFilter, limit, offset) {
130
+ const whereClauses = ['(n.is_del IS NULL OR n.is_del = 0)', 'note_local_fts MATCH ?'];
131
+ const params = [matchQuery];
132
+ if (tagFilter) {
133
+ whereClauses.push(tagFilter.sql);
134
+ params.push(...tagFilter.params);
135
+ }
136
+ if (createdAtFilter) {
137
+ whereClauses.push(createdAtFilter.sql);
138
+ params.push(...createdAtFilter.params);
139
+ }
140
+ if (starredFilter) {
141
+ whereClauses.push(starredFilter.sql);
142
+ params.push(...starredFilter.params);
143
+ }
144
+ if (zettelBoxFilter) {
145
+ whereClauses.push(zettelBoxFilter.sql);
146
+ params.push(...zettelBoxFilter.params);
147
+ }
148
+ if (sqlFilter) {
149
+ whereClauses.push(sqlFilter.sql);
150
+ params.push(...sqlFilter.params);
151
+ }
152
+ const baseFromWhere = `
153
+ FROM note_local_fts f
154
+ JOIN c_note n ON n.id = f.note_id
155
+ WHERE ${whereClauses.join('\n AND ')}
156
+ `;
157
+ const paginated = typeof limit === 'number';
158
+ const totalCount = paginated ? await countSearchRows(db, baseFromWhere, params) : null;
159
+ if (totalCount === 0) {
160
+ return { rows: [], totalCount };
161
+ }
162
+ const rows = await db.getAll(`
163
+ SELECT
164
+ n.id,
165
+ n.title,
166
+ n.summary,
167
+ n.content_text,
168
+ n.tags,
169
+ n.zettel_boxes,
170
+ n.created_at,
171
+ n.starred_at,
172
+ CASE WHEN n.starred_at IS NOT NULL THEN 1 ELSE 0 END AS is_starred
173
+ ${baseFromWhere}
174
+ ORDER BY bm25(note_local_fts, 0.0, 8.0, 4.0, 1.0) ASC, COALESCE(n.updated_at, n.created_at) DESC, n.id DESC
175
+ ${buildPaginationSql(limit, offset)}
176
+ `, appendPaginationParams(params, limit, offset));
177
+ return { rows, totalCount: totalCount ?? rows.length };
178
+ }
179
+ function escapeLikePattern(value) {
180
+ return value.replace(/[\\%_]/g, (match) => `\\${match}`);
181
+ }
182
+ async function countSearchRows(db, fromWhereSql, params) {
183
+ const rows = await db.getAll(`
184
+ SELECT COUNT(*) AS total_count
185
+ ${fromWhereSql}
186
+ `, params);
187
+ const value = Number(rows[0]?.total_count ?? 0);
188
+ return Number.isFinite(value) && value > 0 ? Math.trunc(value) : 0;
189
+ }
190
+ function buildPaginationSql(limit, offset) {
191
+ return typeof limit === 'number' ? 'LIMIT ? OFFSET ?' : '';
192
+ }
193
+ function appendPaginationParams(params, limit, offset) {
194
+ if (typeof limit !== 'number') {
195
+ return params;
196
+ }
197
+ return [...params, limit, offset ?? 0];
198
+ }
199
+ async function enrichSearchRows(db, rows) {
200
+ const allBoxIds = Array.from(new Set(rows.flatMap((row) => parseZettelBoxIds(row.zettel_boxes))));
201
+ const boxNameMap = await loadZettelBoxNameMap(db, allBoxIds);
202
+ return rows.map((row) => {
203
+ const summary = resolveSummary(row.summary, row.content_text);
204
+ const zettelBoxes = parseZettelBoxIds(row.zettel_boxes)
205
+ .map((id) => boxNameMap.get(id))
206
+ .filter((name) => typeof name === 'string' && name.length > 0);
207
+ return {
208
+ id: row.id,
209
+ title: row.title?.trim() ?? '',
210
+ summary,
211
+ tags: parseStoredTags(row.tags),
212
+ created_at: row.created_at,
213
+ starred_at: row.starred_at ?? null,
214
+ is_starred: normalizeStoredStarred(row.is_starred),
215
+ zettel_boxes: zettelBoxes,
216
+ };
217
+ });
218
+ }
219
+ function buildFtsMatchQuery(query) {
220
+ const tokens = tokenizeWithJieba(query, 32)
221
+ .map((token) => token.trim())
222
+ .filter((token) => token.length > 0);
223
+ if (tokens.length === 0) {
224
+ return null;
225
+ }
226
+ const unique = Array.from(new Set(tokens));
227
+ return unique.map((token) => `"${escapeFtsToken(token)}"`).join(' AND ');
228
+ }
229
+ function escapeFtsToken(token) {
230
+ return token.replace(/"/g, '""');
231
+ }
232
+ function buildTagFilter(tagsExpression, noteAlias) {
233
+ const expression = tagsExpression?.trim() ?? '';
234
+ if (!expression) {
235
+ return null;
236
+ }
237
+ if (isExplicitEmptyArrayLiteral(expression)) {
238
+ return {
239
+ sql: `NOT (${buildJsonArrayHasValuesSql(noteAlias, 'tags', 'tag_item')})`,
240
+ params: [],
241
+ };
242
+ }
243
+ let ast;
244
+ try {
245
+ ast = parseTagExpression(expression);
246
+ }
247
+ catch (error) {
248
+ const message = error instanceof Error ? error.message : String(error);
249
+ throw new DinoxError(`Invalid --tags expression: ${message}`);
250
+ }
251
+ const params = [];
252
+ const sql = compileTagExpression(ast, noteAlias, params);
253
+ return { sql: `(${sql})`, params };
254
+ }
255
+ function buildCreatedAtFilter(createdAtFrom, createdAtTo, noteAlias) {
256
+ const clauses = [];
257
+ const params = [];
258
+ if (createdAtFrom) {
259
+ clauses.push(`(${noteAlias}.created_at IS NOT NULL AND ${noteAlias}.created_at >= ?)`);
260
+ params.push(createdAtFrom);
261
+ }
262
+ if (createdAtTo) {
263
+ clauses.push(`(${noteAlias}.created_at IS NOT NULL AND ${noteAlias}.created_at <= ?)`);
264
+ params.push(createdAtTo);
265
+ }
266
+ if (clauses.length === 0) {
267
+ return null;
268
+ }
269
+ return {
270
+ sql: `(${clauses.join(' AND ')})`,
271
+ params,
272
+ };
273
+ }
274
+ function buildStarredFilter(starred, noteAlias) {
275
+ if (typeof starred !== 'boolean') {
276
+ return null;
277
+ }
278
+ return {
279
+ sql: starred ? `(${noteAlias}.starred_at IS NOT NULL)` : `(${noteAlias}.starred_at IS NULL)`,
280
+ params: [],
281
+ };
282
+ }
283
+ function buildZettelBoxFilter(zettelBoxIds, matchEmptyZettelBoxes, noteAlias) {
284
+ const normalizedIds = Array.from(new Set((zettelBoxIds ?? [])
285
+ .map((id) => id.trim())
286
+ .filter((id) => id.length > 0)));
287
+ const shouldMatchEmpty = Boolean(matchEmptyZettelBoxes);
288
+ if (normalizedIds.length === 0 && !shouldMatchEmpty) {
289
+ return null;
290
+ }
291
+ const clauses = [];
292
+ const params = [];
293
+ if (normalizedIds.length > 0) {
294
+ const placeholders = normalizedIds.map(() => '?').join(', ');
295
+ clauses.push(`
296
+ EXISTS (
297
+ SELECT 1
298
+ FROM json_each(
299
+ CASE
300
+ WHEN json_valid(COALESCE(${noteAlias}.zettel_boxes, '')) THEN ${noteAlias}.zettel_boxes
301
+ ELSE '[]'
302
+ END
303
+ ) box_item
304
+ WHERE TRIM(CAST(box_item.value AS TEXT)) IN (${placeholders})
305
+ )
306
+ `);
307
+ params.push(...normalizedIds);
308
+ }
309
+ if (shouldMatchEmpty) {
310
+ clauses.push(`NOT (${buildJsonArrayHasValuesSql(noteAlias, 'zettel_boxes', 'box_item')})`);
311
+ }
312
+ if (clauses.length === 1) {
313
+ return { sql: clauses[0], params };
314
+ }
315
+ return {
316
+ sql: `(${clauses.join(' OR ')})`,
317
+ params,
318
+ };
319
+ }
320
+ function parseTagExpression(expression) {
321
+ const tokens = tokenizeTagExpression(expression);
322
+ if (tokens.length === 0) {
323
+ throw new Error('Expression is empty');
324
+ }
325
+ let index = 0;
326
+ const peek = () => tokens[index] ?? null;
327
+ const consume = (expected) => {
328
+ const token = peek();
329
+ if (!token) {
330
+ throw new Error('Unexpected end of expression');
331
+ }
332
+ if (expected && token.type !== expected) {
333
+ throw new Error(`Expected ${expected} at position ${token.position + 1}`);
334
+ }
335
+ index += 1;
336
+ return token;
337
+ };
338
+ const parsePrimary = () => {
339
+ const token = peek();
340
+ if (!token) {
341
+ throw new Error('Unexpected end of expression');
342
+ }
343
+ if (token.type === 'TERM') {
344
+ consume('TERM');
345
+ const value = normalizeTagValue(token.value);
346
+ if (!value) {
347
+ throw new Error(`Empty tag at position ${token.position + 1}`);
348
+ }
349
+ return { kind: 'tag', value };
350
+ }
351
+ if (token.type === 'LPAREN') {
352
+ consume('LPAREN');
353
+ const node = parseOr();
354
+ const close = peek();
355
+ if (!close || close.type !== 'RPAREN') {
356
+ throw new Error(`Missing ')' for '(' at position ${token.position + 1}`);
357
+ }
358
+ consume('RPAREN');
359
+ return node;
360
+ }
361
+ throw new Error(`Unexpected token '${token.value}' at position ${token.position + 1}`);
362
+ };
363
+ const parseUnary = () => {
364
+ const token = peek();
365
+ if (token?.type === 'NOT') {
366
+ consume('NOT');
367
+ return { kind: 'not', child: parseUnary() };
368
+ }
369
+ return parsePrimary();
370
+ };
371
+ const parseAnd = () => {
372
+ let node = parseUnary();
373
+ while (peek()?.type === 'AND') {
374
+ consume('AND');
375
+ node = { kind: 'and', left: node, right: parseUnary() };
376
+ }
377
+ return node;
378
+ };
379
+ const parseOr = () => {
380
+ let node = parseAnd();
381
+ while (peek()?.type === 'OR') {
382
+ consume('OR');
383
+ node = { kind: 'or', left: node, right: parseAnd() };
384
+ }
385
+ return node;
386
+ };
387
+ const ast = parseOr();
388
+ const extra = peek();
389
+ if (extra) {
390
+ throw new Error(`Unexpected token '${extra.value}' at position ${extra.position + 1}`);
391
+ }
392
+ return ast;
393
+ }
394
+ function tokenizeTagExpression(expression) {
395
+ const tokens = [];
396
+ let i = 0;
397
+ while (i < expression.length) {
398
+ const ch = expression[i];
399
+ if (/\s/.test(ch)) {
400
+ i += 1;
401
+ continue;
402
+ }
403
+ if (ch === '(') {
404
+ tokens.push({ type: 'LPAREN', value: ch, position: i });
405
+ i += 1;
406
+ continue;
407
+ }
408
+ if (ch === ')') {
409
+ tokens.push({ type: 'RPAREN', value: ch, position: i });
410
+ i += 1;
411
+ continue;
412
+ }
413
+ if (ch === '"' || ch === "'") {
414
+ const quoted = readQuotedToken(expression, i);
415
+ tokens.push({ type: 'TERM', value: quoted.value, position: i });
416
+ i = quoted.next;
417
+ continue;
418
+ }
419
+ let j = i;
420
+ while (j < expression.length) {
421
+ const next = expression[j];
422
+ if (/\s/.test(next) || next === '(' || next === ')') {
423
+ break;
424
+ }
425
+ j += 1;
426
+ }
427
+ const raw = expression.slice(i, j);
428
+ const upper = raw.toUpperCase();
429
+ if (upper === 'AND' || upper === 'OR' || upper === 'NOT') {
430
+ tokens.push({ type: upper, value: raw, position: i });
431
+ }
432
+ else {
433
+ tokens.push({ type: 'TERM', value: raw, position: i });
434
+ }
435
+ i = j;
436
+ }
437
+ return tokens;
438
+ }
439
+ function readQuotedToken(expression, start) {
440
+ const quote = expression[start];
441
+ let i = start + 1;
442
+ let value = '';
443
+ while (i < expression.length) {
444
+ const ch = expression[i];
445
+ if (ch === '\\') {
446
+ if (i + 1 >= expression.length) {
447
+ throw new Error(`Invalid escape at position ${i + 1}`);
448
+ }
449
+ value += expression[i + 1];
450
+ i += 2;
451
+ continue;
452
+ }
453
+ if (ch === quote) {
454
+ return { value, next: i + 1 };
455
+ }
456
+ value += ch;
457
+ i += 1;
458
+ }
459
+ throw new Error(`Unclosed quote at position ${start + 1}`);
460
+ }
461
+ function compileTagExpression(node, noteAlias, params) {
462
+ if (node.kind === 'tag') {
463
+ params.push(node.value);
464
+ return `
465
+ EXISTS (
466
+ SELECT 1
467
+ FROM json_each(
468
+ CASE
469
+ WHEN json_valid(COALESCE(${noteAlias}.tags, '')) THEN ${noteAlias}.tags
470
+ ELSE '[]'
471
+ END
472
+ ) tag_item
473
+ WHERE LOWER(TRIM(CAST(tag_item.value AS TEXT))) = ?
474
+ )
475
+ `;
476
+ }
477
+ if (node.kind === 'not') {
478
+ return `NOT (${compileTagExpression(node.child, noteAlias, params)})`;
479
+ }
480
+ if (node.kind === 'and') {
481
+ return `(${compileTagExpression(node.left, noteAlias, params)}) AND (${compileTagExpression(node.right, noteAlias, params)})`;
482
+ }
483
+ return `(${compileTagExpression(node.left, noteAlias, params)}) OR (${compileTagExpression(node.right, noteAlias, params)})`;
484
+ }
485
+ function normalizeTagValue(value) {
486
+ return value.trim().toLowerCase();
487
+ }
488
+ function isExplicitEmptyArrayLiteral(value) {
489
+ if (!value.startsWith('[')) {
490
+ return false;
491
+ }
492
+ try {
493
+ const parsed = JSON.parse(value);
494
+ return Array.isArray(parsed) && parsed.length === 0;
495
+ }
496
+ catch {
497
+ return false;
498
+ }
499
+ }
500
+ function buildJsonArrayHasValuesSql(noteAlias, field, itemAlias) {
501
+ return `
502
+ EXISTS (
503
+ SELECT 1
504
+ FROM json_each(
505
+ CASE
506
+ WHEN json_valid(COALESCE(${noteAlias}.${field}, '')) THEN ${noteAlias}.${field}
507
+ ELSE '[]'
508
+ END
509
+ ) ${itemAlias}
510
+ WHERE LENGTH(TRIM(CAST(${itemAlias}.value AS TEXT))) > 0
511
+ )
512
+ `;
513
+ }
@@ -0,0 +1,190 @@
1
+ import type { AbstractPowerSyncDatabase } from '@powersync/common';
2
+ export declare const MAX_SEARCH_SUMMARY_CHARS = 200;
3
+ export type NoteRow = {
4
+ id: string;
5
+ title: string | null;
6
+ content_md: string | null;
7
+ content_json: string | null;
8
+ content_text: string | null;
9
+ summary: string | null;
10
+ zettel_boxes: string | null;
11
+ tags: string | null;
12
+ created_at: string | null;
13
+ updated_at: string | null;
14
+ is_del: number | null;
15
+ starred_at: string | null;
16
+ is_starred: number | null;
17
+ version: number | null;
18
+ };
19
+ export type SearchNotesOptions = {
20
+ includeDeleted: boolean;
21
+ starred?: boolean;
22
+ tagsExpression?: string;
23
+ createdAtFrom?: string;
24
+ createdAtTo?: string;
25
+ zettelBoxIds?: string[];
26
+ matchEmptyZettelBoxes?: boolean;
27
+ sqlExpression?: string;
28
+ };
29
+ export type SearchResultRow = Pick<NoteRow, 'id' | 'title' | 'summary' | 'content_text' | 'tags' | 'zettel_boxes' | 'created_at' | 'starred_at' | 'is_starred'>;
30
+ export type ZettelBoxRow = {
31
+ id: string | null;
32
+ name: string | null;
33
+ path: string | null;
34
+ };
35
+ export type BoxNameLookup = {
36
+ name: string;
37
+ ids: string[];
38
+ paths: string[];
39
+ };
40
+ export type SearchedNote = {
41
+ id: string;
42
+ title: string;
43
+ summary: string;
44
+ tags: string[];
45
+ created_at: string | null;
46
+ starred_at: string | null;
47
+ is_starred: boolean;
48
+ zettel_boxes: string[];
49
+ };
50
+ export type NoteDetailRow = {
51
+ id: string;
52
+ created_at: string | null;
53
+ updated_at: string | null;
54
+ title: string | null;
55
+ type: string | null;
56
+ source: string | null;
57
+ tags: string | null;
58
+ is_del: number | null;
59
+ is_pub: number | null;
60
+ properties: string | null;
61
+ starred_at: string | null;
62
+ is_starred: number | null;
63
+ content_md: string | null;
64
+ content_text: string | null;
65
+ content_json: string | null;
66
+ zettel_boxes_raw: string | null;
67
+ };
68
+ export type NoteExportRow = {
69
+ id: string;
70
+ created_at: string | null;
71
+ updated_at: string | null;
72
+ title: string | null;
73
+ type: string | null;
74
+ source: string | null;
75
+ tags: string | null;
76
+ summary: string | null;
77
+ is_del: number | null;
78
+ is_pub: number | null;
79
+ properties: string | null;
80
+ starred_at: string | null;
81
+ is_starred: number | null;
82
+ content_md: string | null;
83
+ content_text: string | null;
84
+ content_json: string | null;
85
+ zettel_boxes_raw: string | null;
86
+ };
87
+ export type NoteDetail = {
88
+ id: string;
89
+ created_at: string | null;
90
+ updated_at: string | null;
91
+ title: string | null;
92
+ type: string | null;
93
+ source: string | null;
94
+ tags: string[];
95
+ is_del: number | null;
96
+ is_pub: boolean;
97
+ properties: Record<string, unknown>;
98
+ starred_at: string | null;
99
+ is_starred: boolean;
100
+ content_md: string | null;
101
+ content_text: string | null;
102
+ content_json: unknown;
103
+ zettel_boxes: string[];
104
+ };
105
+ export type NoteExportItem = {
106
+ id: string;
107
+ created_at: string | null;
108
+ updated_at: string | null;
109
+ title: string | null;
110
+ type: string | null;
111
+ source: string | null;
112
+ summary: string | null;
113
+ tags: string[];
114
+ is_del: number | null;
115
+ is_pub: boolean;
116
+ properties: Record<string, unknown>;
117
+ starred_at: string | null;
118
+ is_starred: boolean;
119
+ content_md: string;
120
+ content_text: string | null;
121
+ content_json: unknown;
122
+ box_ids: string[];
123
+ boxes: string[];
124
+ };
125
+ export type NoteContextRow = {
126
+ id: string;
127
+ title: string | null;
128
+ summary: string | null;
129
+ content_text: string | null;
130
+ tags: string | null;
131
+ created_at: string | null;
132
+ updated_at: string | null;
133
+ is_del: number | null;
134
+ starred_at: string | null;
135
+ is_starred: number | null;
136
+ zettel_boxes_raw: string | null;
137
+ };
138
+ export type NoteContext = {
139
+ id: string;
140
+ title: string;
141
+ summary: string;
142
+ tags: string[];
143
+ created_at: string | null;
144
+ updated_at: string | null;
145
+ is_del: number | null;
146
+ starred_at: string | null;
147
+ is_starred: boolean;
148
+ zettel_boxes: string[];
149
+ links: {
150
+ backlinks: number;
151
+ outlinks: number;
152
+ };
153
+ };
154
+ export type NotePreview = {
155
+ id: string;
156
+ title: string;
157
+ requested_lines: number;
158
+ total_lines: number;
159
+ returned_lines: number;
160
+ truncated: boolean;
161
+ preview: string;
162
+ updated_at: string | null;
163
+ };
164
+ export type NoteUpdateStateRow = {
165
+ id: string;
166
+ tags: string | null;
167
+ zettel_boxes: string | null;
168
+ is_del: number | null;
169
+ starred_at: string | null;
170
+ };
171
+ export type NoteUpdatableStateRow = {
172
+ id: string;
173
+ is_del: number | null;
174
+ };
175
+ export type UpdateNoteResult = {
176
+ id: string;
177
+ updated: boolean;
178
+ changed: string[];
179
+ tags: string[];
180
+ zettelBoxIds: string[];
181
+ is_starred: boolean;
182
+ };
183
+ export declare function normalizeStoredFlag(value: unknown): boolean;
184
+ export declare function normalizeStoredStarred(value: unknown): boolean;
185
+ export declare function parseStoredTags(raw: string | null): string[];
186
+ export declare function parseZettelBoxIds(raw: string | null): string[];
187
+ export declare function loadZettelBoxNameMap(db: AbstractPowerSyncDatabase, ids: string[]): Promise<Map<string, string>>;
188
+ export declare function resolveSummary(summary: string | null, contentText: string | null): string;
189
+ export declare function truncateSearchSummary(value: string): string;
190
+ export declare function normalizeLookupKey(value: string): string;