@gmickel/gno 0.7.0 → 0.8.2
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.
- package/LICENSE +21 -0
- package/README.md +90 -50
- package/THIRD_PARTY_NOTICES.md +22 -0
- package/assets/screenshots/webui-ask-answer.png +0 -0
- package/assets/screenshots/webui-collections.png +0 -0
- package/assets/screenshots/webui-editor.png +0 -0
- package/assets/screenshots/webui-home.png +0 -0
- package/assets/skill/SKILL.md +12 -12
- package/assets/skill/cli-reference.md +59 -57
- package/assets/skill/examples.md +8 -7
- package/assets/skill/mcp-reference.md +8 -4
- package/package.json +32 -25
- package/src/app/constants.ts +43 -42
- package/src/cli/colors.ts +1 -1
- package/src/cli/commands/ask.ts +44 -43
- package/src/cli/commands/cleanup.ts +9 -8
- package/src/cli/commands/collection/add.ts +12 -12
- package/src/cli/commands/collection/index.ts +4 -4
- package/src/cli/commands/collection/list.ts +26 -25
- package/src/cli/commands/collection/remove.ts +10 -10
- package/src/cli/commands/collection/rename.ts +10 -10
- package/src/cli/commands/context/add.ts +1 -1
- package/src/cli/commands/context/check.ts +17 -17
- package/src/cli/commands/context/index.ts +4 -4
- package/src/cli/commands/context/list.ts +11 -11
- package/src/cli/commands/context/rm.ts +1 -1
- package/src/cli/commands/doctor.ts +86 -84
- package/src/cli/commands/embed.ts +30 -28
- package/src/cli/commands/get.ts +27 -26
- package/src/cli/commands/index-cmd.ts +9 -9
- package/src/cli/commands/index.ts +16 -16
- package/src/cli/commands/init.ts +13 -12
- package/src/cli/commands/ls.ts +20 -19
- package/src/cli/commands/mcp/config.ts +30 -28
- package/src/cli/commands/mcp/index.ts +4 -4
- package/src/cli/commands/mcp/install.ts +17 -17
- package/src/cli/commands/mcp/paths.ts +133 -133
- package/src/cli/commands/mcp/status.ts +21 -21
- package/src/cli/commands/mcp/uninstall.ts +13 -13
- package/src/cli/commands/mcp.ts +2 -2
- package/src/cli/commands/models/clear.ts +12 -11
- package/src/cli/commands/models/index.ts +5 -5
- package/src/cli/commands/models/list.ts +31 -30
- package/src/cli/commands/models/path.ts +1 -1
- package/src/cli/commands/models/pull.ts +19 -18
- package/src/cli/commands/models/use.ts +4 -4
- package/src/cli/commands/multi-get.ts +38 -36
- package/src/cli/commands/query.ts +21 -20
- package/src/cli/commands/ref-parser.ts +10 -10
- package/src/cli/commands/reset.ts +40 -39
- package/src/cli/commands/search.ts +14 -13
- package/src/cli/commands/serve.ts +4 -4
- package/src/cli/commands/shared.ts +11 -10
- package/src/cli/commands/skill/index.ts +5 -5
- package/src/cli/commands/skill/install.ts +18 -17
- package/src/cli/commands/skill/paths-cmd.ts +11 -10
- package/src/cli/commands/skill/paths.ts +23 -23
- package/src/cli/commands/skill/show.ts +13 -12
- package/src/cli/commands/skill/uninstall.ts +16 -15
- package/src/cli/commands/status.ts +25 -24
- package/src/cli/commands/update.ts +3 -3
- package/src/cli/commands/vsearch.ts +17 -16
- package/src/cli/context.ts +5 -5
- package/src/cli/errors.ts +3 -3
- package/src/cli/format/search-results.ts +37 -37
- package/src/cli/options.ts +43 -43
- package/src/cli/program.ts +455 -459
- package/src/cli/progress.ts +1 -1
- package/src/cli/run.ts +24 -23
- package/src/collection/add.ts +9 -8
- package/src/collection/index.ts +3 -3
- package/src/collection/remove.ts +7 -6
- package/src/collection/types.ts +6 -6
- package/src/config/defaults.ts +1 -1
- package/src/config/index.ts +5 -5
- package/src/config/loader.ts +19 -18
- package/src/config/paths.ts +9 -8
- package/src/config/saver.ts +14 -13
- package/src/config/types.ts +53 -52
- package/src/converters/adapters/markitdownTs/adapter.ts +21 -19
- package/src/converters/adapters/officeparser/adapter.ts +18 -16
- package/src/converters/canonicalize.ts +12 -12
- package/src/converters/errors.ts +26 -22
- package/src/converters/index.ts +8 -8
- package/src/converters/mime.ts +25 -25
- package/src/converters/native/markdown.ts +10 -9
- package/src/converters/native/plaintext.ts +8 -7
- package/src/converters/path.ts +2 -2
- package/src/converters/pipeline.ts +11 -10
- package/src/converters/registry.ts +8 -8
- package/src/converters/types.ts +14 -14
- package/src/converters/versions.ts +4 -4
- package/src/index.ts +4 -4
- package/src/ingestion/chunker.ts +10 -9
- package/src/ingestion/index.ts +6 -6
- package/src/ingestion/language.ts +62 -62
- package/src/ingestion/sync.ts +50 -49
- package/src/ingestion/types.ts +10 -10
- package/src/ingestion/walker.ts +14 -13
- package/src/llm/cache.ts +51 -49
- package/src/llm/errors.ts +40 -36
- package/src/llm/index.ts +9 -9
- package/src/llm/lockfile.ts +6 -6
- package/src/llm/nodeLlamaCpp/adapter.ts +13 -12
- package/src/llm/nodeLlamaCpp/embedding.ts +9 -8
- package/src/llm/nodeLlamaCpp/generation.ts +7 -6
- package/src/llm/nodeLlamaCpp/lifecycle.ts +11 -10
- package/src/llm/nodeLlamaCpp/rerank.ts +6 -5
- package/src/llm/policy.ts +5 -5
- package/src/llm/registry.ts +6 -5
- package/src/llm/types.ts +2 -2
- package/src/mcp/resources/index.ts +15 -13
- package/src/mcp/server.ts +25 -23
- package/src/mcp/tools/get.ts +25 -23
- package/src/mcp/tools/index.ts +32 -29
- package/src/mcp/tools/multi-get.ts +34 -32
- package/src/mcp/tools/query.ts +29 -27
- package/src/mcp/tools/search.ts +14 -12
- package/src/mcp/tools/status.ts +12 -11
- package/src/mcp/tools/vsearch.ts +26 -24
- package/src/pipeline/answer.ts +9 -9
- package/src/pipeline/chunk-lookup.ts +1 -1
- package/src/pipeline/contextual.ts +4 -4
- package/src/pipeline/expansion.ts +23 -21
- package/src/pipeline/explain.ts +21 -21
- package/src/pipeline/fusion.ts +9 -9
- package/src/pipeline/hybrid.ts +41 -42
- package/src/pipeline/index.ts +10 -10
- package/src/pipeline/query-language.ts +39 -39
- package/src/pipeline/rerank.ts +8 -7
- package/src/pipeline/search.ts +22 -22
- package/src/pipeline/types.ts +8 -8
- package/src/pipeline/vsearch.ts +21 -24
- package/src/serve/CLAUDE.md +21 -15
- package/src/serve/config-sync.ts +9 -8
- package/src/serve/context.ts +19 -18
- package/src/serve/index.ts +1 -1
- package/src/serve/jobs.ts +7 -7
- package/src/serve/public/app.tsx +79 -25
- package/src/serve/public/components/AddCollectionDialog.tsx +382 -0
- package/src/serve/public/components/CaptureButton.tsx +60 -0
- package/src/serve/public/components/CaptureModal.tsx +365 -0
- package/src/serve/public/components/IndexingProgress.tsx +333 -0
- package/src/serve/public/components/ShortcutHelpModal.tsx +106 -0
- package/src/serve/public/components/ai-elements/code-block.tsx +42 -32
- package/src/serve/public/components/ai-elements/conversation.tsx +16 -14
- package/src/serve/public/components/ai-elements/inline-citation.tsx +33 -32
- package/src/serve/public/components/ai-elements/loader.tsx +5 -4
- package/src/serve/public/components/ai-elements/message.tsx +39 -37
- package/src/serve/public/components/ai-elements/prompt-input.tsx +97 -95
- package/src/serve/public/components/ai-elements/sources.tsx +12 -10
- package/src/serve/public/components/ai-elements/suggestion.tsx +10 -9
- package/src/serve/public/components/editor/CodeMirrorEditor.tsx +142 -0
- package/src/serve/public/components/editor/MarkdownPreview.tsx +311 -0
- package/src/serve/public/components/editor/index.ts +6 -0
- package/src/serve/public/components/preset-selector.tsx +29 -28
- package/src/serve/public/components/ui/badge.tsx +13 -12
- package/src/serve/public/components/ui/button-group.tsx +13 -12
- package/src/serve/public/components/ui/button.tsx +23 -22
- package/src/serve/public/components/ui/card.tsx +16 -16
- package/src/serve/public/components/ui/carousel.tsx +36 -35
- package/src/serve/public/components/ui/collapsible.tsx +1 -1
- package/src/serve/public/components/ui/command.tsx +17 -15
- package/src/serve/public/components/ui/dialog.tsx +13 -12
- package/src/serve/public/components/ui/dropdown-menu.tsx +13 -12
- package/src/serve/public/components/ui/hover-card.tsx +6 -5
- package/src/serve/public/components/ui/input-group.tsx +45 -43
- package/src/serve/public/components/ui/input.tsx +6 -6
- package/src/serve/public/components/ui/progress.tsx +5 -4
- package/src/serve/public/components/ui/scroll-area.tsx +11 -10
- package/src/serve/public/components/ui/select.tsx +19 -18
- package/src/serve/public/components/ui/separator.tsx +6 -5
- package/src/serve/public/components/ui/table.tsx +18 -18
- package/src/serve/public/components/ui/textarea.tsx +4 -4
- package/src/serve/public/components/ui/tooltip.tsx +5 -4
- package/src/serve/public/globals.css +27 -4
- package/src/serve/public/hooks/use-api.ts +8 -8
- package/src/serve/public/hooks/useCaptureModal.tsx +83 -0
- package/src/serve/public/hooks/useKeyboardShortcuts.ts +85 -0
- package/src/serve/public/index.html +4 -4
- package/src/serve/public/lib/utils.ts +6 -0
- package/src/serve/public/pages/Ask.tsx +27 -26
- package/src/serve/public/pages/Browse.tsx +28 -27
- package/src/serve/public/pages/Collections.tsx +439 -0
- package/src/serve/public/pages/Dashboard.tsx +166 -40
- package/src/serve/public/pages/DocView.tsx +258 -73
- package/src/serve/public/pages/DocumentEditor.tsx +510 -0
- package/src/serve/public/pages/Search.tsx +80 -58
- package/src/serve/routes/api.ts +272 -155
- package/src/serve/security.ts +4 -4
- package/src/serve/server.ts +66 -48
- package/src/store/index.ts +5 -5
- package/src/store/migrations/001-initial.ts +24 -23
- package/src/store/migrations/002-documents-fts.ts +7 -6
- package/src/store/migrations/index.ts +4 -4
- package/src/store/migrations/runner.ts +17 -15
- package/src/store/sqlite/adapter.ts +123 -121
- package/src/store/sqlite/fts5-snowball.ts +24 -23
- package/src/store/sqlite/index.ts +1 -1
- package/src/store/sqlite/setup.ts +12 -12
- package/src/store/sqlite/types.ts +4 -4
- package/src/store/types.ts +19 -19
- package/src/store/vector/index.ts +3 -3
- package/src/store/vector/sqlite-vec.ts +23 -20
- package/src/store/vector/stats.ts +10 -8
- package/src/store/vector/types.ts +2 -2
- package/vendor/fts5-snowball/README.md +6 -6
- package/assets/screenshots/webui-ask-answer.jpg +0 -0
- package/assets/screenshots/webui-home.jpg +0 -0
package/src/ingestion/sync.ts
CHANGED
|
@@ -5,21 +5,14 @@
|
|
|
5
5
|
* @module src/ingestion/sync
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { Collection } from
|
|
9
|
-
import { getDefaultMimeDetector, type MimeDetector } from '../converters/mime';
|
|
10
|
-
import {
|
|
11
|
-
type ConversionPipeline,
|
|
12
|
-
getDefaultPipeline,
|
|
13
|
-
} from '../converters/pipeline';
|
|
14
|
-
import { DEFAULT_LIMITS } from '../converters/types';
|
|
8
|
+
import type { Collection } from "../config/types";
|
|
15
9
|
import type {
|
|
16
10
|
ChunkInput,
|
|
17
11
|
DocumentRow,
|
|
18
12
|
IngestErrorInput,
|
|
19
13
|
StorePort,
|
|
20
14
|
StoreResult,
|
|
21
|
-
} from
|
|
22
|
-
import { defaultChunker } from './chunker';
|
|
15
|
+
} from "../store/types";
|
|
23
16
|
import type {
|
|
24
17
|
ChunkerPort,
|
|
25
18
|
CollectionSyncResult,
|
|
@@ -29,9 +22,17 @@ import type {
|
|
|
29
22
|
SyncResult,
|
|
30
23
|
WalkEntry,
|
|
31
24
|
WalkerPort,
|
|
32
|
-
} from
|
|
33
|
-
|
|
34
|
-
import {
|
|
25
|
+
} from "./types";
|
|
26
|
+
|
|
27
|
+
import { getDefaultMimeDetector, type MimeDetector } from "../converters/mime";
|
|
28
|
+
import {
|
|
29
|
+
type ConversionPipeline,
|
|
30
|
+
getDefaultPipeline,
|
|
31
|
+
} from "../converters/pipeline";
|
|
32
|
+
import { DEFAULT_LIMITS } from "../converters/types";
|
|
33
|
+
import { defaultChunker } from "./chunker";
|
|
34
|
+
import { collectionToWalkConfig, DEFAULT_CHUNK_PARAMS } from "./types";
|
|
35
|
+
import { defaultWalker } from "./walker";
|
|
35
36
|
|
|
36
37
|
/** Default concurrency for file processing */
|
|
37
38
|
const DEFAULT_CONCURRENCY = 1;
|
|
@@ -52,28 +53,28 @@ function decideAction(
|
|
|
52
53
|
): ProcessDecision {
|
|
53
54
|
// No existing doc - must process
|
|
54
55
|
if (!existing) {
|
|
55
|
-
return { kind:
|
|
56
|
+
return { kind: "process", reason: "new file" };
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
// Source hash changed - must process
|
|
59
60
|
if (existing.sourceHash !== sourceHash) {
|
|
60
|
-
return { kind:
|
|
61
|
+
return { kind: "process", reason: "content changed" };
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
// Source unchanged, but check for repair cases:
|
|
64
65
|
|
|
65
66
|
// 1. Previous conversion failed (mirrorHash is null)
|
|
66
67
|
if (!existing.mirrorHash) {
|
|
67
|
-
return { kind:
|
|
68
|
+
return { kind: "repair", reason: "previous conversion failed" };
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
// 2. Document has error recorded
|
|
71
72
|
if (existing.lastErrorCode) {
|
|
72
|
-
return { kind:
|
|
73
|
+
return { kind: "repair", reason: "previous error recorded" };
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
// All good - skip
|
|
76
|
-
return { kind:
|
|
77
|
+
return { kind: "skip", reason: "unchanged" };
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
/**
|
|
@@ -188,7 +189,7 @@ export class SyncService {
|
|
|
188
189
|
* Process a single file through the ingestion pipeline.
|
|
189
190
|
* All store operations are checked and errors are propagated.
|
|
190
191
|
*/
|
|
191
|
-
//
|
|
192
|
+
// oxlint-disable-next-line max-lines-per-function -- file processing with multiple extraction paths
|
|
192
193
|
private async processFile(
|
|
193
194
|
collection: Collection,
|
|
194
195
|
entry: WalkEntry,
|
|
@@ -207,9 +208,9 @@ export class SyncService {
|
|
|
207
208
|
const bytes = await Bun.file(entry.absPath).bytes();
|
|
208
209
|
|
|
209
210
|
// 2. Compute sourceHash
|
|
210
|
-
const hasher = new Bun.CryptoHasher(
|
|
211
|
+
const hasher = new Bun.CryptoHasher("sha256");
|
|
211
212
|
hasher.update(bytes);
|
|
212
|
-
const sourceHash = hasher.digest(
|
|
213
|
+
const sourceHash = hasher.digest("hex");
|
|
213
214
|
|
|
214
215
|
// 3. Check existing doc for skip/repair decision
|
|
215
216
|
const existingResult = await store.getDocument(
|
|
@@ -219,8 +220,8 @@ export class SyncService {
|
|
|
219
220
|
const existing = existingResult.ok ? existingResult.value : null;
|
|
220
221
|
const decision = decideAction(existing, sourceHash);
|
|
221
222
|
|
|
222
|
-
if (decision.kind ===
|
|
223
|
-
return { relPath: entry.relPath, status:
|
|
223
|
+
if (decision.kind === "skip") {
|
|
224
|
+
return { relPath: entry.relPath, status: "unchanged" };
|
|
224
225
|
}
|
|
225
226
|
|
|
226
227
|
// 4. Detect MIME (bytes is already Uint8Array from Bun.file().bytes())
|
|
@@ -268,15 +269,15 @@ export class SyncService {
|
|
|
268
269
|
if (!upsertResult.ok) {
|
|
269
270
|
return {
|
|
270
271
|
relPath: entry.relPath,
|
|
271
|
-
status:
|
|
272
|
-
errorCode:
|
|
272
|
+
status: "error",
|
|
273
|
+
errorCode: "STORE_ERROR",
|
|
273
274
|
errorMessage: upsertResult.error.message,
|
|
274
275
|
};
|
|
275
276
|
}
|
|
276
277
|
|
|
277
278
|
return {
|
|
278
279
|
relPath: entry.relPath,
|
|
279
|
-
status:
|
|
280
|
+
status: "error",
|
|
280
281
|
errorCode: convertResult.error.code,
|
|
281
282
|
errorMessage: convertResult.error.message,
|
|
282
283
|
};
|
|
@@ -303,7 +304,7 @@ export class SyncService {
|
|
|
303
304
|
lastErrorMessage: undefined,
|
|
304
305
|
});
|
|
305
306
|
|
|
306
|
-
const docid = mustOk(docidResult,
|
|
307
|
+
const docid = mustOk(docidResult, "upsertDocument", {
|
|
307
308
|
collection: collection.name,
|
|
308
309
|
relPath: entry.relPath,
|
|
309
310
|
});
|
|
@@ -313,7 +314,7 @@ export class SyncService {
|
|
|
313
314
|
artifact.mirrorHash,
|
|
314
315
|
artifact.markdown
|
|
315
316
|
);
|
|
316
|
-
mustOk(contentResult,
|
|
317
|
+
mustOk(contentResult, "upsertContent", {
|
|
317
318
|
mirrorHash: artifact.mirrorHash,
|
|
318
319
|
});
|
|
319
320
|
|
|
@@ -340,18 +341,18 @@ export class SyncService {
|
|
|
340
341
|
artifact.mirrorHash,
|
|
341
342
|
chunkInputs
|
|
342
343
|
);
|
|
343
|
-
mustOk(chunksResult,
|
|
344
|
+
mustOk(chunksResult, "upsertChunks", {
|
|
344
345
|
mirrorHash: artifact.mirrorHash,
|
|
345
346
|
chunkCount: chunkInputs.length,
|
|
346
347
|
});
|
|
347
348
|
|
|
348
349
|
// 11. Rebuild FTS for this hash - CHECKED
|
|
349
350
|
const ftsResult = await store.rebuildFtsForHash(artifact.mirrorHash);
|
|
350
|
-
mustOk(ftsResult,
|
|
351
|
+
mustOk(ftsResult, "rebuildFtsForHash", {
|
|
351
352
|
mirrorHash: artifact.mirrorHash,
|
|
352
353
|
});
|
|
353
354
|
|
|
354
|
-
const status = existing ?
|
|
355
|
+
const status = existing ? "updated" : "added";
|
|
355
356
|
return {
|
|
356
357
|
relPath: entry.relPath,
|
|
357
358
|
status,
|
|
@@ -359,13 +360,13 @@ export class SyncService {
|
|
|
359
360
|
mirrorHash: artifact.mirrorHash,
|
|
360
361
|
};
|
|
361
362
|
} catch (error) {
|
|
362
|
-
const message = error instanceof Error ? error.message :
|
|
363
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
363
364
|
// Distinguish store errors from other internal errors
|
|
364
365
|
const isStoreError =
|
|
365
|
-
message.startsWith(
|
|
366
|
+
message.startsWith("Store operation failed:") ||
|
|
366
367
|
(error instanceof Error &&
|
|
367
368
|
(error as Error & { context?: unknown }).context !== undefined);
|
|
368
|
-
const code = isStoreError ?
|
|
369
|
+
const code = isStoreError ? "STORE_ERROR" : "INTERNAL";
|
|
369
370
|
|
|
370
371
|
// Record internal error to store (best-effort)
|
|
371
372
|
try {
|
|
@@ -403,7 +404,7 @@ export class SyncService {
|
|
|
403
404
|
|
|
404
405
|
return {
|
|
405
406
|
relPath: entry.relPath,
|
|
406
|
-
status:
|
|
407
|
+
status: "error",
|
|
407
408
|
errorCode: code,
|
|
408
409
|
errorMessage: message,
|
|
409
410
|
};
|
|
@@ -413,7 +414,7 @@ export class SyncService {
|
|
|
413
414
|
/**
|
|
414
415
|
* Sync a single collection.
|
|
415
416
|
*/
|
|
416
|
-
//
|
|
417
|
+
// oxlint-disable-next-line max-lines-per-function -- sync orchestration with git and progress
|
|
417
418
|
async syncCollection(
|
|
418
419
|
collection: Collection,
|
|
419
420
|
store: StorePort,
|
|
@@ -443,31 +444,31 @@ export class SyncService {
|
|
|
443
444
|
// a previously-included file, that doc SHOULD be marked inactive
|
|
444
445
|
const seenPaths = new Set<string>();
|
|
445
446
|
for (const skip of skipped) {
|
|
446
|
-
if (skip.reason ===
|
|
447
|
+
if (skip.reason === "TOO_LARGE") {
|
|
447
448
|
seenPaths.add(skip.relPath);
|
|
448
449
|
}
|
|
449
450
|
}
|
|
450
451
|
|
|
451
452
|
// 3. Record TOO_LARGE errors and track in seenPaths
|
|
452
453
|
for (const skip of skipped) {
|
|
453
|
-
if (skip.reason ===
|
|
454
|
+
if (skip.reason === "TOO_LARGE") {
|
|
454
455
|
const recordResult = await store.recordError({
|
|
455
456
|
collection: collection.name,
|
|
456
457
|
relPath: skip.relPath,
|
|
457
|
-
code:
|
|
458
|
+
code: "TOO_LARGE",
|
|
458
459
|
message: `File size ${skip.size} exceeds limit ${maxBytes}`,
|
|
459
460
|
});
|
|
460
461
|
// Log failure but continue
|
|
461
462
|
if (!recordResult.ok) {
|
|
462
463
|
errors.push({
|
|
463
464
|
relPath: skip.relPath,
|
|
464
|
-
code:
|
|
465
|
+
code: "STORE_ERROR",
|
|
465
466
|
message: `Failed to record error: ${recordResult.error.message}`,
|
|
466
467
|
});
|
|
467
468
|
}
|
|
468
469
|
errors.push({
|
|
469
470
|
relPath: skip.relPath,
|
|
470
|
-
code:
|
|
471
|
+
code: "TOO_LARGE",
|
|
471
472
|
message: `File size ${skip.size} exceeds limit ${maxBytes}`,
|
|
472
473
|
});
|
|
473
474
|
}
|
|
@@ -499,16 +500,16 @@ export class SyncService {
|
|
|
499
500
|
options
|
|
500
501
|
);
|
|
501
502
|
switch (result.status) {
|
|
502
|
-
case
|
|
503
|
+
case "added":
|
|
503
504
|
added += 1;
|
|
504
505
|
break;
|
|
505
|
-
case
|
|
506
|
+
case "updated":
|
|
506
507
|
updated += 1;
|
|
507
508
|
break;
|
|
508
|
-
case
|
|
509
|
+
case "unchanged":
|
|
509
510
|
unchanged += 1;
|
|
510
511
|
break;
|
|
511
|
-
case
|
|
512
|
+
case "error":
|
|
512
513
|
errored += 1;
|
|
513
514
|
if (result.errorCode && result.errorMessage) {
|
|
514
515
|
errors.push({
|
|
@@ -530,7 +531,7 @@ export class SyncService {
|
|
|
530
531
|
const txResult = await store.withTransaction(runBatch);
|
|
531
532
|
if (!txResult.ok) {
|
|
532
533
|
errors.push({
|
|
533
|
-
relPath:
|
|
534
|
+
relPath: "(transaction batch)",
|
|
534
535
|
code: txResult.error.code,
|
|
535
536
|
message: txResult.error.message,
|
|
536
537
|
});
|
|
@@ -566,16 +567,16 @@ export class SyncService {
|
|
|
566
567
|
// Aggregate results
|
|
567
568
|
for (const result of results) {
|
|
568
569
|
switch (result.status) {
|
|
569
|
-
case
|
|
570
|
+
case "added":
|
|
570
571
|
added += 1;
|
|
571
572
|
break;
|
|
572
|
-
case
|
|
573
|
+
case "updated":
|
|
573
574
|
updated += 1;
|
|
574
575
|
break;
|
|
575
|
-
case
|
|
576
|
+
case "unchanged":
|
|
576
577
|
unchanged += 1;
|
|
577
578
|
break;
|
|
578
|
-
case
|
|
579
|
+
case "error":
|
|
579
580
|
errored += 1;
|
|
580
581
|
if (result.errorCode && result.errorMessage) {
|
|
581
582
|
errors.push({
|
package/src/ingestion/types.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @module src/ingestion/types
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { Collection } from
|
|
8
|
+
import type { Collection } from "../config/types";
|
|
9
9
|
|
|
10
10
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
11
11
|
// Walker Types
|
|
@@ -41,7 +41,7 @@ export interface WalkConfig {
|
|
|
41
41
|
export interface SkippedEntry {
|
|
42
42
|
absPath: string;
|
|
43
43
|
relPath: string;
|
|
44
|
-
reason:
|
|
44
|
+
reason: "TOO_LARGE" | "EXCLUDED";
|
|
45
45
|
size?: number;
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -133,11 +133,11 @@ export interface SyncOptions {
|
|
|
133
133
|
|
|
134
134
|
/** Per-file sync status */
|
|
135
135
|
export type FileSyncStatus =
|
|
136
|
-
|
|
|
137
|
-
|
|
|
138
|
-
|
|
|
139
|
-
|
|
|
140
|
-
|
|
|
136
|
+
| "added"
|
|
137
|
+
| "updated"
|
|
138
|
+
| "unchanged"
|
|
139
|
+
| "error"
|
|
140
|
+
| "skipped";
|
|
141
141
|
|
|
142
142
|
/** Per-file sync result */
|
|
143
143
|
export interface FileSyncResult {
|
|
@@ -180,9 +180,9 @@ export interface SyncResult {
|
|
|
180
180
|
|
|
181
181
|
/** Decision for whether to process a file */
|
|
182
182
|
export type ProcessDecision =
|
|
183
|
-
| { kind:
|
|
184
|
-
| { kind:
|
|
185
|
-
| { kind:
|
|
183
|
+
| { kind: "skip"; reason: string }
|
|
184
|
+
| { kind: "process"; reason: string }
|
|
185
|
+
| { kind: "repair"; reason: string };
|
|
186
186
|
|
|
187
187
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
188
188
|
// Language Detection Types
|
package/src/ingestion/walker.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
// node:fs/promises - Bun has no realpath equivalent
|
|
9
|
-
import { realpath } from
|
|
9
|
+
import { realpath } from "node:fs/promises";
|
|
10
10
|
// node:path - Bun has no path manipulation module
|
|
11
11
|
import {
|
|
12
12
|
extname,
|
|
@@ -15,8 +15,9 @@ import {
|
|
|
15
15
|
relative,
|
|
16
16
|
resolve,
|
|
17
17
|
sep,
|
|
18
|
-
} from
|
|
19
|
-
|
|
18
|
+
} from "node:path";
|
|
19
|
+
|
|
20
|
+
import type { SkippedEntry, WalkConfig, WalkEntry, WalkerPort } from "./types";
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* Regex to detect dangerous patterns with parent directory traversal.
|
|
@@ -28,10 +29,10 @@ const DANGEROUS_PATTERN_REGEX = /(?:^|[\\/])\.\./;
|
|
|
28
29
|
* Normalize path to POSIX format (forward slashes).
|
|
29
30
|
*/
|
|
30
31
|
function toPosixPath(path: string): string {
|
|
31
|
-
if (sep ===
|
|
32
|
+
if (sep === "/") {
|
|
32
33
|
return path;
|
|
33
34
|
}
|
|
34
|
-
return path.replaceAll(sep,
|
|
35
|
+
return path.replaceAll(sep, "/");
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
/**
|
|
@@ -40,10 +41,10 @@ function toPosixPath(path: string): string {
|
|
|
40
41
|
*/
|
|
41
42
|
function validatePattern(pattern: string): string | null {
|
|
42
43
|
if (isAbsolute(pattern)) {
|
|
43
|
-
return
|
|
44
|
+
return "Pattern must be relative, not absolute";
|
|
44
45
|
}
|
|
45
46
|
if (DANGEROUS_PATTERN_REGEX.test(pattern)) {
|
|
46
|
-
return
|
|
47
|
+
return "Pattern contains dangerous parent directory reference (..)";
|
|
47
48
|
}
|
|
48
49
|
return null;
|
|
49
50
|
}
|
|
@@ -63,7 +64,7 @@ async function safeRelPath(
|
|
|
63
64
|
|
|
64
65
|
// Reject if relative path escapes root
|
|
65
66
|
// Check for ".." at start followed by separator or end (not just ".." prefix)
|
|
66
|
-
if (rel ===
|
|
67
|
+
if (rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
|
|
67
68
|
return null;
|
|
68
69
|
}
|
|
69
70
|
|
|
@@ -88,7 +89,7 @@ async function safeRelPath(
|
|
|
88
89
|
* - exclude: ["dist"] matches "dist/bundle.js" and "src/dist/output.js"
|
|
89
90
|
*/
|
|
90
91
|
function matchesExclude(relPath: string, excludes: string[]): boolean {
|
|
91
|
-
const parts = relPath.split(
|
|
92
|
+
const parts = relPath.split("/");
|
|
92
93
|
|
|
93
94
|
for (const pattern of excludes) {
|
|
94
95
|
// Check if any path component matches exactly
|
|
@@ -119,7 +120,7 @@ function matchesInclude(relPath: string, include: string[]): boolean {
|
|
|
119
120
|
}
|
|
120
121
|
|
|
121
122
|
return include.some((inc) => {
|
|
122
|
-
const normalizedInc = inc.startsWith(
|
|
123
|
+
const normalizedInc = inc.startsWith(".")
|
|
123
124
|
? inc.toLowerCase()
|
|
124
125
|
: `.${inc.toLowerCase()}`;
|
|
125
126
|
return ext === normalizedInc;
|
|
@@ -178,7 +179,7 @@ export class FileWalker implements WalkerPort {
|
|
|
178
179
|
skipped.push({
|
|
179
180
|
absPath,
|
|
180
181
|
relPath,
|
|
181
|
-
reason:
|
|
182
|
+
reason: "EXCLUDED",
|
|
182
183
|
});
|
|
183
184
|
continue;
|
|
184
185
|
}
|
|
@@ -188,7 +189,7 @@ export class FileWalker implements WalkerPort {
|
|
|
188
189
|
skipped.push({
|
|
189
190
|
absPath,
|
|
190
191
|
relPath,
|
|
191
|
-
reason:
|
|
192
|
+
reason: "EXCLUDED",
|
|
192
193
|
});
|
|
193
194
|
continue;
|
|
194
195
|
}
|
|
@@ -208,7 +209,7 @@ export class FileWalker implements WalkerPort {
|
|
|
208
209
|
skipped.push({
|
|
209
210
|
absPath,
|
|
210
211
|
relPath,
|
|
211
|
-
reason:
|
|
212
|
+
reason: "TOO_LARGE",
|
|
212
213
|
size: stat.size,
|
|
213
214
|
});
|
|
214
215
|
continue;
|