watashi-db 0.0.13 → 0.0.14
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/CLAUDE.md +36 -0
- package/LICENSE +1 -1
- package/README.md +33 -2
- package/cowork-plugin/skills/groom/SKILL.md +51 -15
- package/cowork-plugin/skills/recall/SKILL.md +5 -6
- package/cowork-plugin/skills/reflect/SKILL.md +4 -4
- package/cowork-plugin/skills/remember/SKILL.md +3 -3
- package/cowork-plugin/skills/session-start/SKILL.md +3 -3
- package/dist/config/schema.js +1 -1
- package/dist/constants.d.ts +5 -1
- package/dist/constants.js +19 -3
- package/dist/constants.js.map +1 -1
- package/dist/database/archive.js +6 -6
- package/dist/database/queries-core.d.ts +75 -1
- package/dist/database/queries-core.js +283 -12
- package/dist/database/queries-core.js.map +1 -1
- package/dist/database/queries.d.ts +71 -1
- package/dist/database/queries.js +29 -0
- package/dist/database/queries.js.map +1 -1
- package/dist/database/schema.d.ts +1 -0
- package/dist/database/schema.js +1915 -214
- package/dist/database/schema.js.map +1 -1
- package/dist/embedding/embed-on-write.d.ts +7 -1
- package/dist/embedding/embed-on-write.js +8 -3
- package/dist/embedding/embed-on-write.js.map +1 -1
- package/dist/hook.js +9 -6
- package/dist/hook.js.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/resources/config-guide-content.d.ts +1 -1
- package/dist/resources/config-guide-content.js +2 -2
- package/dist/server-instructions.js +16 -17
- package/dist/server-instructions.js.map +1 -1
- package/dist/server.d.ts +4 -1
- package/dist/server.js +42 -18
- package/dist/server.js.map +1 -1
- package/dist/setup.js +5 -6
- package/dist/setup.js.map +1 -1
- package/dist/store/federation.d.ts +12 -1
- package/dist/store/federation.js +38 -0
- package/dist/store/federation.js.map +1 -1
- package/dist/store/sync-manager.d.ts +1 -1
- package/dist/store/sync-manager.js +9 -9
- package/dist/tools/claim-tools.d.ts +1 -1
- package/dist/tools/claim-tools.js +7 -7
- package/dist/tools/claim-tools.js.map +1 -1
- package/dist/tools/decision-tools.d.ts +1 -1
- package/dist/tools/decision-tools.js +2 -2
- package/dist/tools/decision-tools.js.map +1 -1
- package/dist/tools/episode-tools.d.ts +1 -1
- package/dist/tools/episode-tools.js +2 -2
- package/dist/tools/episode-tools.js.map +1 -1
- package/dist/tools/file-tools.d.ts +3 -0
- package/dist/tools/file-tools.js +347 -0
- package/dist/tools/file-tools.js.map +1 -0
- package/dist/tools/get-tools.d.ts +1 -1
- package/dist/tools/get-tools.js +39 -5
- package/dist/tools/get-tools.js.map +1 -1
- package/dist/tools/knowledge-tools.d.ts +1 -1
- package/dist/tools/knowledge-tools.js +2 -2
- package/dist/tools/knowledge-tools.js.map +1 -1
- package/dist/tools/maintenance-tools.d.ts +1 -1
- package/dist/tools/maintenance-tools.js +38 -6
- package/dist/tools/maintenance-tools.js.map +1 -1
- package/dist/tools/memo-tools.d.ts +7 -11
- package/dist/tools/memo-tools.js +499 -307
- package/dist/tools/memo-tools.js.map +1 -1
- package/dist/tools/query-tools.d.ts +1 -1
- package/dist/tools/query-tools.js +28 -5
- package/dist/tools/query-tools.js.map +1 -1
- package/dist/types.d.ts +370 -48
- package/dist/types.js +124 -16
- package/dist/types.js.map +1 -1
- package/misc/20260316_110841_groom-recipe.md +483 -0
- package/misc/20260316_xaml-testing-library-recipe.md +817 -0
- package/package.json +4 -2
- package/scripts/update-license-version.sh +7 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ulid } from "ulid";
|
|
2
|
+
import { DEFAULT_SEARCH_ENTITY_TYPES } from "../types.js";
|
|
2
3
|
import { claimScoreExpression, computeClaimScore } from "../scoring.js";
|
|
3
4
|
// === exp() サポート検出(SQLite数学関数) ===
|
|
4
5
|
/** exp() が使えるかを DB ごとにキャッシュ */
|
|
@@ -748,11 +749,11 @@ export function getClaimChecks(db, claimId) {
|
|
|
748
749
|
export function insertAuditLog(db, params) {
|
|
749
750
|
const id = ulid();
|
|
750
751
|
const now = new Date().toISOString();
|
|
751
|
-
// 2026-03-03 追加 (V21):
|
|
752
|
-
const
|
|
752
|
+
// 2026-03-03 追加 (V21): device_id でどのデバイスで作られたか追跡
|
|
753
|
+
const deviceIdRow = db.prepare("SELECT value FROM store_meta WHERE key = 'device_id'").get();
|
|
753
754
|
db.prepare(`
|
|
754
|
-
INSERT INTO audit_log (id, operation, entity_type, entity_id, summary, details, client_name, client_version, session_id, source_tool,
|
|
755
|
-
VALUES (@id, @operation, @entity_type, @entity_id, @summary, @details, @client_name, @client_version, @session_id, @source_tool, @
|
|
755
|
+
INSERT INTO audit_log (id, operation, entity_type, entity_id, summary, details, client_name, client_version, session_id, source_tool, device_id, created_at)
|
|
756
|
+
VALUES (@id, @operation, @entity_type, @entity_id, @summary, @details, @client_name, @client_version, @session_id, @source_tool, @device_id, @created_at)
|
|
756
757
|
`).run({
|
|
757
758
|
id,
|
|
758
759
|
operation: params.operation,
|
|
@@ -764,7 +765,7 @@ export function insertAuditLog(db, params) {
|
|
|
764
765
|
client_version: params.provenance?.client_version ?? null,
|
|
765
766
|
session_id: params.provenance?.session_id ?? null,
|
|
766
767
|
source_tool: params.source_tool ?? null,
|
|
767
|
-
|
|
768
|
+
device_id: deviceIdRow?.value ?? null,
|
|
768
769
|
created_at: now,
|
|
769
770
|
});
|
|
770
771
|
}
|
|
@@ -1121,6 +1122,11 @@ export function getTopicSummary(db, tagLimit = 10, decisionLimit = 5, scope) {
|
|
|
1121
1122
|
activeMemos = db.prepare(`SELECT COUNT(*) as count FROM user_memos WHERE status = 'active'${scopeFilter}`).get(scopeValues).count;
|
|
1122
1123
|
}
|
|
1123
1124
|
catch { /* V16未適用 */ }
|
|
1125
|
+
let activeFiles = 0;
|
|
1126
|
+
try {
|
|
1127
|
+
activeFiles = db.prepare(`SELECT COUNT(*) as count FROM user_files WHERE status = 'active'${scopeFilter}`).get(scopeValues).count;
|
|
1128
|
+
}
|
|
1129
|
+
catch { /* V31未適用 */ }
|
|
1124
1130
|
// Issue #47: エンティティタイトルから具体的キーワード候補を抽出
|
|
1125
1131
|
let topicKeywords = [];
|
|
1126
1132
|
try {
|
|
@@ -1148,7 +1154,7 @@ export function getTopicSummary(db, tagLimit = 10, decisionLimit = 5, scope) {
|
|
|
1148
1154
|
topicKeywords,
|
|
1149
1155
|
recentDecisions,
|
|
1150
1156
|
activeScopes,
|
|
1151
|
-
counts: { activeClaims, activeEpisodes, activeDecisions, activeTheories, activeInsights, activeModels, activeMemos },
|
|
1157
|
+
counts: { activeClaims, activeEpisodes, activeDecisions, activeTheories, activeInsights, activeModels, activeMemos, activeFiles },
|
|
1152
1158
|
};
|
|
1153
1159
|
}
|
|
1154
1160
|
// === V9: Theory CRUD ===
|
|
@@ -2551,6 +2557,10 @@ export function updateUserIssue(db, id, updates) {
|
|
|
2551
2557
|
setClauses.push("entries = @entries");
|
|
2552
2558
|
values.entries = updates.entries;
|
|
2553
2559
|
}
|
|
2560
|
+
if (updates.l1_embedding !== undefined) {
|
|
2561
|
+
setClauses.push("l1_embedding = @l1_embedding");
|
|
2562
|
+
values.l1_embedding = updates.l1_embedding;
|
|
2563
|
+
}
|
|
2554
2564
|
if (updates.priority !== undefined) {
|
|
2555
2565
|
setClauses.push("priority = @priority");
|
|
2556
2566
|
values.priority = updates.priority;
|
|
@@ -2649,6 +2659,157 @@ export function getUserIssuesWithL1Embedding(db, scope, limit = 100) {
|
|
|
2649
2659
|
}
|
|
2650
2660
|
return db.prepare("SELECT * FROM user_issues WHERE status = 'open' AND l1_embedding IS NOT NULL ORDER BY updated_at DESC LIMIT ?").all(limit);
|
|
2651
2661
|
}
|
|
2662
|
+
export function insertUserTopic(db, params) {
|
|
2663
|
+
const id = ulid();
|
|
2664
|
+
const now = new Date().toISOString();
|
|
2665
|
+
// device_id を自動取得(store_meta から)
|
|
2666
|
+
let deviceId = params.device_id ?? null;
|
|
2667
|
+
if (!deviceId) {
|
|
2668
|
+
const row = db.prepare("SELECT value FROM store_meta WHERE key = 'device_id'").get();
|
|
2669
|
+
deviceId = row?.value ?? null;
|
|
2670
|
+
}
|
|
2671
|
+
db.prepare(`
|
|
2672
|
+
INSERT INTO user_topics (id, title, summary, device_id, cwd, conversation_id, priority, tags, scope, search_summary, l1_embedding, source_tool, client_name, client_version, status, created_at, updated_at)
|
|
2673
|
+
VALUES (@id, @title, @summary, @device_id, @cwd, @conversation_id, @priority, @tags, @scope, @search_summary, @l1_embedding, @source_tool, @client_name, @client_version, 'active', @created_at, @updated_at)
|
|
2674
|
+
`).run({
|
|
2675
|
+
id,
|
|
2676
|
+
title: params.title,
|
|
2677
|
+
summary: params.summary,
|
|
2678
|
+
device_id: deviceId,
|
|
2679
|
+
cwd: params.cwd ?? null,
|
|
2680
|
+
conversation_id: params.conversation_id ?? null,
|
|
2681
|
+
priority: params.priority ?? "medium",
|
|
2682
|
+
tags: JSON.stringify(params.tags),
|
|
2683
|
+
scope: params.scope ?? "global",
|
|
2684
|
+
search_summary: params.search_summary ?? null,
|
|
2685
|
+
l1_embedding: params.l1_embedding ?? null,
|
|
2686
|
+
source_tool: params.source_tool ?? null,
|
|
2687
|
+
client_name: params.client_name ?? null,
|
|
2688
|
+
client_version: params.client_version ?? null,
|
|
2689
|
+
created_at: now,
|
|
2690
|
+
updated_at: now,
|
|
2691
|
+
});
|
|
2692
|
+
return db.prepare("SELECT * FROM user_topics WHERE id = ?").get(id);
|
|
2693
|
+
}
|
|
2694
|
+
export function getUserTopicById(db, id) {
|
|
2695
|
+
return db.prepare("SELECT * FROM user_topics WHERE id = ?").get(id);
|
|
2696
|
+
}
|
|
2697
|
+
export function updateUserTopic(db, id, updates) {
|
|
2698
|
+
const existing = getUserTopicById(db, id);
|
|
2699
|
+
if (!existing)
|
|
2700
|
+
return undefined;
|
|
2701
|
+
const setClauses = [];
|
|
2702
|
+
const values = { id };
|
|
2703
|
+
if (updates.title !== undefined) {
|
|
2704
|
+
setClauses.push("title = @title");
|
|
2705
|
+
values.title = updates.title;
|
|
2706
|
+
}
|
|
2707
|
+
if (updates.summary !== undefined) {
|
|
2708
|
+
setClauses.push("summary = @summary");
|
|
2709
|
+
values.summary = updates.summary;
|
|
2710
|
+
}
|
|
2711
|
+
if (updates.conversation_id !== undefined) {
|
|
2712
|
+
setClauses.push("conversation_id = @conversation_id");
|
|
2713
|
+
values.conversation_id = updates.conversation_id;
|
|
2714
|
+
}
|
|
2715
|
+
if (updates.cwd !== undefined) {
|
|
2716
|
+
setClauses.push("cwd = @cwd");
|
|
2717
|
+
values.cwd = updates.cwd;
|
|
2718
|
+
}
|
|
2719
|
+
if (updates.priority !== undefined) {
|
|
2720
|
+
setClauses.push("priority = @priority");
|
|
2721
|
+
values.priority = updates.priority;
|
|
2722
|
+
}
|
|
2723
|
+
if (updates.tags !== undefined) {
|
|
2724
|
+
setClauses.push("tags = @tags");
|
|
2725
|
+
values.tags = JSON.stringify(updates.tags);
|
|
2726
|
+
}
|
|
2727
|
+
if (updates.scope !== undefined) {
|
|
2728
|
+
setClauses.push("scope = @scope");
|
|
2729
|
+
values.scope = updates.scope;
|
|
2730
|
+
}
|
|
2731
|
+
if (updates.search_summary !== undefined) {
|
|
2732
|
+
setClauses.push("search_summary = @search_summary");
|
|
2733
|
+
values.search_summary = updates.search_summary;
|
|
2734
|
+
}
|
|
2735
|
+
if (updates.status !== undefined) {
|
|
2736
|
+
setClauses.push("status = @status");
|
|
2737
|
+
values.status = updates.status;
|
|
2738
|
+
}
|
|
2739
|
+
if (setClauses.length === 0)
|
|
2740
|
+
return existing;
|
|
2741
|
+
// summary/title 変更時は l1_embedding を無効化(陳腐化防止、backfill_embeddings で再生成)
|
|
2742
|
+
if (updates.summary !== undefined || updates.title !== undefined) {
|
|
2743
|
+
setClauses.push("l1_embedding = NULL");
|
|
2744
|
+
}
|
|
2745
|
+
setClauses.push("updated_at = @updated_at");
|
|
2746
|
+
values.updated_at = new Date().toISOString();
|
|
2747
|
+
db.prepare(`UPDATE user_topics SET ${setClauses.join(", ")} WHERE id = @id`).run(values);
|
|
2748
|
+
return db.prepare("SELECT * FROM user_topics WHERE id = ?").get(id);
|
|
2749
|
+
}
|
|
2750
|
+
export function listUserTopics(db, params) {
|
|
2751
|
+
const sanitized = params.query ? sanitizeFtsQuery(params.query) : null;
|
|
2752
|
+
const limit = params.limit ?? 20;
|
|
2753
|
+
const conditions = [];
|
|
2754
|
+
const values = { limit };
|
|
2755
|
+
if (params.status) {
|
|
2756
|
+
conditions.push("m.status = @status");
|
|
2757
|
+
values.status = params.status;
|
|
2758
|
+
}
|
|
2759
|
+
if (params.priority) {
|
|
2760
|
+
conditions.push("m.priority = @priority");
|
|
2761
|
+
values.priority = params.priority;
|
|
2762
|
+
}
|
|
2763
|
+
if (params.tag) {
|
|
2764
|
+
conditions.push("m.tags LIKE @tag_like");
|
|
2765
|
+
values.tag_like = `%"${params.tag}"%`;
|
|
2766
|
+
}
|
|
2767
|
+
if (params.scope) {
|
|
2768
|
+
conditions.push("(m.scope = @scope OR m.scope = 'global')");
|
|
2769
|
+
values.scope = params.scope;
|
|
2770
|
+
}
|
|
2771
|
+
// FTS5検索
|
|
2772
|
+
if (sanitized) {
|
|
2773
|
+
try {
|
|
2774
|
+
const ftsConditions = [...conditions];
|
|
2775
|
+
const ftsWhere = ftsConditions.length > 0 ? "AND " + ftsConditions.join(" AND ") : "";
|
|
2776
|
+
const ftsResults = db.prepare(`
|
|
2777
|
+
SELECT m.* FROM user_topics m
|
|
2778
|
+
JOIN user_topics_fts f ON m.rowid = f.rowid
|
|
2779
|
+
WHERE user_topics_fts MATCH @query ${ftsWhere}
|
|
2780
|
+
ORDER BY rank
|
|
2781
|
+
LIMIT @limit
|
|
2782
|
+
`).all({ ...values, query: sanitized });
|
|
2783
|
+
if (ftsResults.length > 0)
|
|
2784
|
+
return ftsResults;
|
|
2785
|
+
}
|
|
2786
|
+
catch { /* FTSエラー時はLIKEフォールバック */ }
|
|
2787
|
+
}
|
|
2788
|
+
// LIKEフォールバック or 全件取得
|
|
2789
|
+
if (params.query) {
|
|
2790
|
+
const likeParam = `%${params.query.slice(0, 100)}%`;
|
|
2791
|
+
conditions.push("(m.title LIKE @like_query OR m.summary LIKE @like_query OR m.search_summary LIKE @like_query)");
|
|
2792
|
+
values.like_query = likeParam;
|
|
2793
|
+
}
|
|
2794
|
+
const whereClause = conditions.length > 0 ? "WHERE " + conditions.join(" AND ") : "";
|
|
2795
|
+
return db.prepare(`
|
|
2796
|
+
SELECT m.* FROM user_topics m ${whereClause}
|
|
2797
|
+
ORDER BY m.updated_at DESC
|
|
2798
|
+
LIMIT @limit
|
|
2799
|
+
`).all(values);
|
|
2800
|
+
}
|
|
2801
|
+
export function updateUserTopicL1Embedding(db, id, l1_embedding) {
|
|
2802
|
+
db.prepare("UPDATE user_topics SET l1_embedding = @l1_embedding WHERE id = @id").run({ id, l1_embedding });
|
|
2803
|
+
}
|
|
2804
|
+
export function getUserTopicsWithoutL1Embedding(db, limit) {
|
|
2805
|
+
return db.prepare("SELECT * FROM user_topics WHERE status = 'active' AND l1_embedding IS NULL ORDER BY updated_at DESC LIMIT ?").all(limit);
|
|
2806
|
+
}
|
|
2807
|
+
export function getUserTopicsWithL1Embedding(db, scope, limit = 100) {
|
|
2808
|
+
if (scope) {
|
|
2809
|
+
return db.prepare("SELECT * FROM user_topics WHERE status = 'active' AND l1_embedding IS NOT NULL AND (scope = ? OR scope = 'global') ORDER BY updated_at DESC LIMIT ?").all(scope, limit);
|
|
2810
|
+
}
|
|
2811
|
+
return db.prepare("SELECT * FROM user_topics WHERE status = 'active' AND l1_embedding IS NOT NULL ORDER BY updated_at DESC LIMIT ?").all(limit);
|
|
2812
|
+
}
|
|
2652
2813
|
/**
|
|
2653
2814
|
* 横断検索(FTS5 + LIKEフォールバック)
|
|
2654
2815
|
* unified_search_items テーブルを使い、全6エンティティを横断してFTS5検索する。
|
|
@@ -2662,11 +2823,13 @@ export function unifiedSearch(db, query, scope, entityTypes, limit) {
|
|
|
2662
2823
|
scopeFilter = "AND (u.scope = @scope OR u.scope = 'global')";
|
|
2663
2824
|
baseValues.scope = scope;
|
|
2664
2825
|
}
|
|
2826
|
+
// entity_types 未指定時はデフォルト検索対象を使用(user_file はオプトイン型のため除外)
|
|
2827
|
+
const effectiveTypes = (entityTypes && entityTypes.length > 0) ? entityTypes : [...DEFAULT_SEARCH_ENTITY_TYPES];
|
|
2665
2828
|
let typeFilter = "";
|
|
2666
|
-
|
|
2667
|
-
const placeholders =
|
|
2829
|
+
{
|
|
2830
|
+
const placeholders = effectiveTypes.map((_, i) => `@et${i}`).join(", ");
|
|
2668
2831
|
typeFilter = `AND u.entity_type IN (${placeholders})`;
|
|
2669
|
-
|
|
2832
|
+
effectiveTypes.forEach((t, i) => { baseValues[`et${i}`] = t; });
|
|
2670
2833
|
}
|
|
2671
2834
|
// 1. FTS5検索
|
|
2672
2835
|
if (sanitized) {
|
|
@@ -2727,10 +2890,12 @@ export function getUnifiedSearchWithL1Embedding(db, scope, entityTypes, limit) {
|
|
|
2727
2890
|
conditions.push("(scope = @scope OR scope = 'global')");
|
|
2728
2891
|
values.scope = scope;
|
|
2729
2892
|
}
|
|
2730
|
-
|
|
2731
|
-
|
|
2893
|
+
// entity_types 未指定時はデフォルト検索対象を使用(user_file はオプトイン型のため除外)
|
|
2894
|
+
const effectiveTypes = (entityTypes && entityTypes.length > 0) ? entityTypes : [...DEFAULT_SEARCH_ENTITY_TYPES];
|
|
2895
|
+
{
|
|
2896
|
+
const placeholders = effectiveTypes.map((_, i) => `@et${i}`).join(", ");
|
|
2732
2897
|
conditions.push(`entity_type IN (${placeholders})`);
|
|
2733
|
-
|
|
2898
|
+
effectiveTypes.forEach((t, i) => { values[`et${i}`] = t; });
|
|
2734
2899
|
}
|
|
2735
2900
|
const whereClause = conditions.length > 0
|
|
2736
2901
|
? "WHERE " + conditions.join(" AND ")
|
|
@@ -2834,6 +2999,8 @@ const ENTITY_TYPE_TO_TABLE = {
|
|
|
2834
2999
|
user_memo: "user_memos",
|
|
2835
3000
|
user_plan: "user_plans",
|
|
2836
3001
|
user_issue: "user_issues",
|
|
3002
|
+
user_topic: "user_topics",
|
|
3003
|
+
user_file: "user_files",
|
|
2837
3004
|
};
|
|
2838
3005
|
/**
|
|
2839
3006
|
* entity_id から entity_type を解決する(unified_search_items を使用)。
|
|
@@ -2982,4 +3149,108 @@ export function applyTombstones(db) {
|
|
|
2982
3149
|
}
|
|
2983
3150
|
return deleted;
|
|
2984
3151
|
}
|
|
3152
|
+
// ============================================================
|
|
3153
|
+
// UserFile (File Vault)
|
|
3154
|
+
// ============================================================
|
|
3155
|
+
export function insertUserFile(db, params) {
|
|
3156
|
+
const id = ulid();
|
|
3157
|
+
const now = new Date().toISOString();
|
|
3158
|
+
db.prepare(`
|
|
3159
|
+
INSERT INTO user_files (id, title, description, original_filename, original_encoding, file_data, file_hash, file_size, tags, scope, search_summary, l1_embedding, source_tool, client_name, client_version, status, created_at, updated_at)
|
|
3160
|
+
VALUES (@id, @title, @description, @original_filename, @original_encoding, @file_data, @file_hash, @file_size, @tags, @scope, @search_summary, @l1_embedding, @source_tool, @client_name, @client_version, 'active', @created_at, @updated_at)
|
|
3161
|
+
`).run({
|
|
3162
|
+
id,
|
|
3163
|
+
title: params.title,
|
|
3164
|
+
description: params.description ?? null,
|
|
3165
|
+
original_filename: params.original_filename,
|
|
3166
|
+
original_encoding: params.original_encoding ?? "utf-8",
|
|
3167
|
+
file_data: params.file_data,
|
|
3168
|
+
file_hash: params.file_hash,
|
|
3169
|
+
file_size: params.file_size,
|
|
3170
|
+
tags: JSON.stringify(params.tags),
|
|
3171
|
+
scope: params.scope ?? "global",
|
|
3172
|
+
search_summary: params.search_summary ?? null,
|
|
3173
|
+
l1_embedding: params.l1_embedding ?? null,
|
|
3174
|
+
source_tool: params.source_tool ?? null,
|
|
3175
|
+
client_name: params.client_name ?? null,
|
|
3176
|
+
client_version: params.client_version ?? null,
|
|
3177
|
+
created_at: now,
|
|
3178
|
+
updated_at: now,
|
|
3179
|
+
});
|
|
3180
|
+
return db.prepare("SELECT * FROM user_files WHERE id = ?").get(id);
|
|
3181
|
+
}
|
|
3182
|
+
export function getUserFileById(db, id) {
|
|
3183
|
+
return db.prepare("SELECT * FROM user_files WHERE id = ?").get(id);
|
|
3184
|
+
}
|
|
3185
|
+
export function getUserFileByTitle(db, title) {
|
|
3186
|
+
// 完全一致を優先、なければ部分一致
|
|
3187
|
+
const exact = db.prepare("SELECT * FROM user_files WHERE title = ? AND status = 'active'").get(title);
|
|
3188
|
+
if (exact)
|
|
3189
|
+
return exact;
|
|
3190
|
+
return db.prepare("SELECT * FROM user_files WHERE title LIKE ? AND status = 'active' ORDER BY updated_at DESC LIMIT 1").get(`%${title}%`);
|
|
3191
|
+
}
|
|
3192
|
+
export function updateUserFile(db, id, updates) {
|
|
3193
|
+
const setClauses = [];
|
|
3194
|
+
const params = { id };
|
|
3195
|
+
if (updates.title !== undefined) {
|
|
3196
|
+
setClauses.push("title = @title");
|
|
3197
|
+
params.title = updates.title;
|
|
3198
|
+
}
|
|
3199
|
+
if (updates.description !== undefined) {
|
|
3200
|
+
setClauses.push("description = @description");
|
|
3201
|
+
params.description = updates.description;
|
|
3202
|
+
}
|
|
3203
|
+
if (updates.file_data !== undefined) {
|
|
3204
|
+
setClauses.push("file_data = @file_data");
|
|
3205
|
+
params.file_data = updates.file_data;
|
|
3206
|
+
}
|
|
3207
|
+
if (updates.file_hash !== undefined) {
|
|
3208
|
+
setClauses.push("file_hash = @file_hash");
|
|
3209
|
+
params.file_hash = updates.file_hash;
|
|
3210
|
+
}
|
|
3211
|
+
if (updates.file_size !== undefined) {
|
|
3212
|
+
setClauses.push("file_size = @file_size");
|
|
3213
|
+
params.file_size = updates.file_size;
|
|
3214
|
+
}
|
|
3215
|
+
if (updates.tags !== undefined) {
|
|
3216
|
+
setClauses.push("tags = @tags");
|
|
3217
|
+
params.tags = JSON.stringify(updates.tags);
|
|
3218
|
+
}
|
|
3219
|
+
if (updates.scope !== undefined) {
|
|
3220
|
+
setClauses.push("scope = @scope");
|
|
3221
|
+
params.scope = updates.scope;
|
|
3222
|
+
}
|
|
3223
|
+
if (updates.search_summary !== undefined) {
|
|
3224
|
+
setClauses.push("search_summary = @search_summary");
|
|
3225
|
+
params.search_summary = updates.search_summary;
|
|
3226
|
+
}
|
|
3227
|
+
if (updates.status !== undefined) {
|
|
3228
|
+
setClauses.push("status = @status");
|
|
3229
|
+
params.status = updates.status;
|
|
3230
|
+
}
|
|
3231
|
+
if (setClauses.length === 0)
|
|
3232
|
+
return getUserFileById(db, id);
|
|
3233
|
+
setClauses.push("updated_at = @updated_at");
|
|
3234
|
+
params.updated_at = new Date().toISOString();
|
|
3235
|
+
db.prepare(`UPDATE user_files SET ${setClauses.join(", ")} WHERE id = @id`).run(params);
|
|
3236
|
+
return getUserFileById(db, id);
|
|
3237
|
+
}
|
|
3238
|
+
export function listUserFiles(db, params) {
|
|
3239
|
+
const conditions = ["status = @status"];
|
|
3240
|
+
const bindParams = { status: params.status ?? "active" };
|
|
3241
|
+
if (params.scope) {
|
|
3242
|
+
conditions.push("(scope = @scope OR scope = 'global')");
|
|
3243
|
+
bindParams.scope = params.scope;
|
|
3244
|
+
}
|
|
3245
|
+
const sql = `SELECT id, title, description, original_filename, original_encoding, file_hash, file_size, tags, scope, search_summary, status, client_name, client_version, source_tool, created_at, updated_at FROM user_files WHERE ${conditions.join(" AND ")} ORDER BY updated_at DESC`;
|
|
3246
|
+
let rows = db.prepare(sql).all(bindParams);
|
|
3247
|
+
// タグフィルタリング(アプリケーション層)
|
|
3248
|
+
if (params.tags && params.tags.length > 0) {
|
|
3249
|
+
rows = rows.filter(r => {
|
|
3250
|
+
const rowTags = JSON.parse(r.tags);
|
|
3251
|
+
return params.tags.some(t => rowTags.includes(t));
|
|
3252
|
+
});
|
|
3253
|
+
}
|
|
3254
|
+
return rows;
|
|
3255
|
+
}
|
|
2985
3256
|
//# sourceMappingURL=queries-core.js.map
|