@kevisual/project-search 0.0.5 → 0.0.7

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/dist/app.d.ts CHANGED
@@ -42,6 +42,7 @@ type FileProjectData = {
42
42
  * 文件对应相关信息
43
43
  *
44
44
  * 具体文件路径
45
+ * /workspace/projects/project-search/src/index.ts
45
46
  */
46
47
  filepath: string;
47
48
  content?: string;
@@ -49,6 +50,7 @@ type FileProjectData = {
49
50
  size: number;
50
51
  /**
51
52
  * 项目路径,文件所在的项目路径,方便搜索结果展示和过滤
53
+ * /workspace/projects/project-search
52
54
  */
53
55
  projectPath: string;
54
56
  repo: string;
@@ -73,6 +75,7 @@ declare class ProjectSearch {
73
75
  searchFiles(query?: string, options?: {
74
76
  projectPath?: string;
75
77
  repo?: string;
78
+ filepath?: string;
76
79
  title?: string;
77
80
  tags?: string | string[];
78
81
  summary?: string;
@@ -232,6 +235,7 @@ declare class ProjectManager implements ProjectManagerInterface {
232
235
  content: string;
233
236
  type: string;
234
237
  } | null>;
238
+ writeFile(filepath: string, content: string): Promise<boolean>;
235
239
  deleteFile(filepath: string): Promise<boolean>;
236
240
  }
237
241
 
package/dist/app.js CHANGED
@@ -25142,21 +25142,25 @@ class ProjectSearch {
25142
25142
  async searchFiles(query = "", options) {
25143
25143
  const filter = [];
25144
25144
  if (options?.projectPath)
25145
- filter.push(`projectPath = "${options.projectPath}"`);
25145
+ filter.push(`projectPath starts_with "${options.projectPath}"`);
25146
25146
  if (options?.repo)
25147
25147
  filter.push(`repo = "${options.repo}"`);
25148
+ if (options?.filepath)
25149
+ filter.push(`filepath starts_with "${options.filepath}"`);
25150
+ if (options?.link)
25151
+ filter.push(`link = "${options.link}"`);
25152
+ const searchTerms = [];
25148
25153
  if (options?.title)
25149
- filter.push(`title = "${options.title}"`);
25154
+ searchTerms.push(options.title);
25155
+ if (options?.summary)
25156
+ searchTerms.push(options.summary);
25157
+ if (options?.description)
25158
+ searchTerms.push(options.description);
25150
25159
  if (options?.tags) {
25151
25160
  const tags = Array.isArray(options.tags) ? options.tags : [options.tags];
25152
- tags.forEach((tag) => filter.push(`tags = "${tag}"`));
25161
+ tags.forEach((tag) => searchTerms.push(tag));
25153
25162
  }
25154
- if (options?.summary)
25155
- filter.push(`summary = "${options.summary}"`);
25156
- if (options?.description)
25157
- filter.push(`description = "${options.description}"`);
25158
- if (options?.link)
25159
- filter.push(`link = "${options.link}"`);
25163
+ const fullQuery = [query, ...searchTerms].filter(Boolean).join(" ");
25160
25164
  const limit = options?.limit ?? 1000;
25161
25165
  const search = {
25162
25166
  filter: filter.length ? filter.join(" AND ") : undefined,
@@ -25167,7 +25171,7 @@ class ProjectSearch {
25167
25171
  let allHits = [];
25168
25172
  let offset = 0;
25169
25173
  while (true) {
25170
- const searchResults = await this.index.search(query, {
25174
+ const searchResults = await this.index.search(fullQuery, {
25171
25175
  ...search,
25172
25176
  limit: Math.min(limit - allHits.length, 1000),
25173
25177
  offset
@@ -25496,6 +25500,7 @@ var import__2 = __toESM(require_eventemitter32(), 1);
25496
25500
 
25497
25501
  // src/project/manager.ts
25498
25502
  import fs3 from "node:fs";
25503
+ import path4 from "node:path";
25499
25504
  class ProjectManager {
25500
25505
  projects = new Map;
25501
25506
  projectSearch;
@@ -25606,6 +25611,20 @@ class ProjectManager {
25606
25611
  type: "base64"
25607
25612
  };
25608
25613
  }
25614
+ async writeFile(filepath, content) {
25615
+ try {
25616
+ const buffer = Buffer.from(content, "base64");
25617
+ const dir = path4.dirname(filepath);
25618
+ if (!fs3.existsSync(dir)) {
25619
+ fs3.mkdirSync(dir, { recursive: true });
25620
+ }
25621
+ fs3.writeFileSync(filepath, buffer);
25622
+ return true;
25623
+ } catch (error48) {
25624
+ console.error("写入文件失败:", error48);
25625
+ return false;
25626
+ }
25627
+ }
25609
25628
  async deleteFile(filepath) {
25610
25629
  if (!fileIsExist(filepath)) {
25611
25630
  return false;
@@ -26215,7 +26234,7 @@ var manager = useContextKey("project-manager", new ProjectManager({
26215
26234
  }));
26216
26235
  // src/file-search/index.ts
26217
26236
  var import_fast_glob2 = __toESM(require_out4(), 1);
26218
- import path4 from "node:path";
26237
+ import path5 from "node:path";
26219
26238
  import fs4 from "node:fs";
26220
26239
  var defaultIgnore = [
26221
26240
  "node_modules",
@@ -26235,7 +26254,7 @@ var defaultIgnore = [
26235
26254
  class FileSearch {
26236
26255
  async searchFiles(options = {}) {
26237
26256
  const { cwd = process.cwd(), ignore = [] } = options;
26238
- const ignoreFile = path4.join(cwd, ".gitignore");
26257
+ const ignoreFile = path5.join(cwd, ".gitignore");
26239
26258
  const gitIgnorePatterns = fs4.existsSync(ignoreFile) ? fs4.readFileSync(ignoreFile, "utf-8").split(`
26240
26259
  `).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")) : [];
26241
26260
  const allIgnore = [...defaultIgnore, ...ignore, ...gitIgnorePatterns];
@@ -27027,10 +27046,10 @@ function mergeDefs2(...defs) {
27027
27046
  function cloneDef2(schema) {
27028
27047
  return mergeDefs2(schema._zod.def);
27029
27048
  }
27030
- function getElementAtPath2(obj, path5) {
27031
- if (!path5)
27049
+ function getElementAtPath2(obj, path6) {
27050
+ if (!path6)
27032
27051
  return obj;
27033
- return path5.reduce((acc, key) => acc?.[key], obj);
27052
+ return path6.reduce((acc, key) => acc?.[key], obj);
27034
27053
  }
27035
27054
  function promiseAllObject2(promisesObj) {
27036
27055
  const keys = Object.keys(promisesObj);
@@ -27411,11 +27430,11 @@ function aborted2(x, startIndex = 0) {
27411
27430
  }
27412
27431
  return false;
27413
27432
  }
27414
- function prefixIssues2(path5, issues) {
27433
+ function prefixIssues2(path6, issues) {
27415
27434
  return issues.map((iss) => {
27416
27435
  var _a2;
27417
27436
  (_a2 = iss).path ?? (_a2.path = []);
27418
- iss.path.unshift(path5);
27437
+ iss.path.unshift(path6);
27419
27438
  return iss;
27420
27439
  });
27421
27440
  }
@@ -27598,7 +27617,7 @@ function formatError2(error48, mapper = (issue3) => issue3.message) {
27598
27617
  }
27599
27618
  function treeifyError2(error48, mapper = (issue3) => issue3.message) {
27600
27619
  const result = { errors: [] };
27601
- const processError = (error49, path5 = []) => {
27620
+ const processError = (error49, path6 = []) => {
27602
27621
  var _a2, _b;
27603
27622
  for (const issue3 of error49.issues) {
27604
27623
  if (issue3.code === "invalid_union" && issue3.errors.length) {
@@ -27608,7 +27627,7 @@ function treeifyError2(error48, mapper = (issue3) => issue3.message) {
27608
27627
  } else if (issue3.code === "invalid_element") {
27609
27628
  processError({ issues: issue3.issues }, issue3.path);
27610
27629
  } else {
27611
- const fullpath = [...path5, ...issue3.path];
27630
+ const fullpath = [...path6, ...issue3.path];
27612
27631
  if (fullpath.length === 0) {
27613
27632
  result.errors.push(mapper(issue3));
27614
27633
  continue;
@@ -27640,8 +27659,8 @@ function treeifyError2(error48, mapper = (issue3) => issue3.message) {
27640
27659
  }
27641
27660
  function toDotPath2(_path) {
27642
27661
  const segs = [];
27643
- const path5 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
27644
- for (const seg of path5) {
27662
+ const path6 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
27663
+ for (const seg of path6) {
27645
27664
  if (typeof seg === "number")
27646
27665
  segs.push(`[${seg}]`);
27647
27666
  else if (typeof seg === "symbol")
@@ -39388,13 +39407,13 @@ function resolveRef2(ref, ctx) {
39388
39407
  if (!ref.startsWith("#")) {
39389
39408
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
39390
39409
  }
39391
- const path5 = ref.slice(1).split("/").filter(Boolean);
39392
- if (path5.length === 0) {
39410
+ const path6 = ref.slice(1).split("/").filter(Boolean);
39411
+ if (path6.length === 0) {
39393
39412
  return ctx.rootSchema;
39394
39413
  }
39395
39414
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
39396
- if (path5[0] === defsKey) {
39397
- const key = path5[1];
39415
+ if (path6[0] === defsKey) {
39416
+ const key = path6[1];
39398
39417
  if (!key || !ctx.defs[key]) {
39399
39418
  throw new Error(`Reference not found: ${ref}`);
39400
39419
  }
@@ -39937,6 +39956,7 @@ app.route({
39937
39956
  args: {
39938
39957
  q: exports_external2.string().optional().describe("搜索关键词,选填;留空或不传则返回全部文件"),
39939
39958
  projectPath: exports_external2.string().optional().describe("按项目根目录路径过滤,仅返回该项目下的文件,选填"),
39959
+ filepath: exports_external2.string().optional().describe("按文件绝对路径过滤,选填"),
39940
39960
  repo: exports_external2.string().optional().describe("按代码仓库标识过滤(如 owner/repo),选填"),
39941
39961
  title: exports_external2.string().optional().describe("按人工标注的标题字段过滤,选填"),
39942
39962
  tags: exports_external2.array(exports_external2.string()).optional().describe("按人工标注的标签列表过滤,选填"),
@@ -39949,13 +39969,13 @@ app.route({
39949
39969
  }
39950
39970
  }
39951
39971
  }).define(async (ctx) => {
39952
- let { q, projectPath, repo, title, tags, summary, description, link, sort, limit, getContent = false } = ctx.query;
39972
+ let { q, projectPath, filepath, repo, title, tags, summary, description, link, sort, limit, getContent = false } = ctx.query;
39953
39973
  if (!q) {
39954
39974
  sort = sort ?? ["projectPath:asc"];
39955
39975
  limit = limit ?? 1000;
39956
39976
  }
39957
39977
  const projectSearch = manager.projectSearch;
39958
- const hits = await projectSearch.searchFiles(q, { projectPath, repo, title, tags, summary, description, link, sort, limit, getContent });
39978
+ const hits = await projectSearch.searchFiles(q, { projectPath, filepath, repo, title, tags, summary, description, link, sort, limit, getContent });
39959
39979
  ctx.body = { list: hits };
39960
39980
  }).addTo(app);
39961
39981
 
@@ -39981,6 +40001,33 @@ app.route({
39981
40001
  ctx.throw(500, "读取文件失败");
39982
40002
  }
39983
40003
  }).addTo(app);
40004
+ app.route({
40005
+ path: "project-file",
40006
+ key: "update-content",
40007
+ middleware: ["auth-admin"],
40008
+ description: "将 base64 编码的内容写入指定文件路径,用于更新或创建文件",
40009
+ metadata: {
40010
+ args: {
40011
+ filepath: exports_external2.string().nonempty().describe("要写入的文件绝对路径,必填"),
40012
+ content: exports_external2.string().nonempty().describe("文件内容的 base64 编码,必填")
40013
+ }
40014
+ }
40015
+ }).define(async (ctx) => {
40016
+ const { filepath, content } = ctx.query;
40017
+ if (!filepath)
40018
+ ctx.throw(400, "filepath 不能为空");
40019
+ if (!content)
40020
+ ctx.throw(400, "content 不能为空");
40021
+ try {
40022
+ const success3 = await manager.writeFile(filepath, content);
40023
+ if (!success3) {
40024
+ ctx.throw(500, "写入文件失败");
40025
+ }
40026
+ ctx.body = { success: true };
40027
+ } catch (error49) {
40028
+ ctx.throw(500, "写入文件失败");
40029
+ }
40030
+ }).addTo(app);
39984
40031
  app.route({
39985
40032
  path: "project-file",
39986
40033
  key: "update",
package/dist/remote.js CHANGED
@@ -25142,21 +25142,25 @@ class ProjectSearch {
25142
25142
  async searchFiles(query = "", options) {
25143
25143
  const filter = [];
25144
25144
  if (options?.projectPath)
25145
- filter.push(`projectPath = "${options.projectPath}"`);
25145
+ filter.push(`projectPath starts_with "${options.projectPath}"`);
25146
25146
  if (options?.repo)
25147
25147
  filter.push(`repo = "${options.repo}"`);
25148
+ if (options?.filepath)
25149
+ filter.push(`filepath starts_with "${options.filepath}"`);
25150
+ if (options?.link)
25151
+ filter.push(`link = "${options.link}"`);
25152
+ const searchTerms = [];
25148
25153
  if (options?.title)
25149
- filter.push(`title = "${options.title}"`);
25154
+ searchTerms.push(options.title);
25155
+ if (options?.summary)
25156
+ searchTerms.push(options.summary);
25157
+ if (options?.description)
25158
+ searchTerms.push(options.description);
25150
25159
  if (options?.tags) {
25151
25160
  const tags = Array.isArray(options.tags) ? options.tags : [options.tags];
25152
- tags.forEach((tag) => filter.push(`tags = "${tag}"`));
25161
+ tags.forEach((tag) => searchTerms.push(tag));
25153
25162
  }
25154
- if (options?.summary)
25155
- filter.push(`summary = "${options.summary}"`);
25156
- if (options?.description)
25157
- filter.push(`description = "${options.description}"`);
25158
- if (options?.link)
25159
- filter.push(`link = "${options.link}"`);
25163
+ const fullQuery = [query, ...searchTerms].filter(Boolean).join(" ");
25160
25164
  const limit = options?.limit ?? 1000;
25161
25165
  const search = {
25162
25166
  filter: filter.length ? filter.join(" AND ") : undefined,
@@ -25167,7 +25171,7 @@ class ProjectSearch {
25167
25171
  let allHits = [];
25168
25172
  let offset = 0;
25169
25173
  while (true) {
25170
- const searchResults = await this.index.search(query, {
25174
+ const searchResults = await this.index.search(fullQuery, {
25171
25175
  ...search,
25172
25176
  limit: Math.min(limit - allHits.length, 1000),
25173
25177
  offset
@@ -25496,6 +25500,7 @@ var import__2 = __toESM(require_eventemitter32(), 1);
25496
25500
 
25497
25501
  // src/project/manager.ts
25498
25502
  import fs3 from "node:fs";
25503
+ import path4 from "node:path";
25499
25504
  class ProjectManager {
25500
25505
  projects = new Map;
25501
25506
  projectSearch;
@@ -25606,6 +25611,20 @@ class ProjectManager {
25606
25611
  type: "base64"
25607
25612
  };
25608
25613
  }
25614
+ async writeFile(filepath, content) {
25615
+ try {
25616
+ const buffer = Buffer.from(content, "base64");
25617
+ const dir = path4.dirname(filepath);
25618
+ if (!fs3.existsSync(dir)) {
25619
+ fs3.mkdirSync(dir, { recursive: true });
25620
+ }
25621
+ fs3.writeFileSync(filepath, buffer);
25622
+ return true;
25623
+ } catch (error48) {
25624
+ console.error("写入文件失败:", error48);
25625
+ return false;
25626
+ }
25627
+ }
25609
25628
  async deleteFile(filepath) {
25610
25629
  if (!fileIsExist(filepath)) {
25611
25630
  return false;
@@ -26994,10 +27013,10 @@ function mergeDefs2(...defs) {
26994
27013
  function cloneDef2(schema) {
26995
27014
  return mergeDefs2(schema._zod.def);
26996
27015
  }
26997
- function getElementAtPath2(obj, path4) {
26998
- if (!path4)
27016
+ function getElementAtPath2(obj, path5) {
27017
+ if (!path5)
26999
27018
  return obj;
27000
- return path4.reduce((acc, key) => acc?.[key], obj);
27019
+ return path5.reduce((acc, key) => acc?.[key], obj);
27001
27020
  }
27002
27021
  function promiseAllObject2(promisesObj) {
27003
27022
  const keys = Object.keys(promisesObj);
@@ -27378,11 +27397,11 @@ function aborted2(x, startIndex = 0) {
27378
27397
  }
27379
27398
  return false;
27380
27399
  }
27381
- function prefixIssues2(path4, issues) {
27400
+ function prefixIssues2(path5, issues) {
27382
27401
  return issues.map((iss) => {
27383
27402
  var _a2;
27384
27403
  (_a2 = iss).path ?? (_a2.path = []);
27385
- iss.path.unshift(path4);
27404
+ iss.path.unshift(path5);
27386
27405
  return iss;
27387
27406
  });
27388
27407
  }
@@ -27565,7 +27584,7 @@ function formatError2(error48, mapper = (issue3) => issue3.message) {
27565
27584
  }
27566
27585
  function treeifyError2(error48, mapper = (issue3) => issue3.message) {
27567
27586
  const result = { errors: [] };
27568
- const processError = (error49, path4 = []) => {
27587
+ const processError = (error49, path5 = []) => {
27569
27588
  var _a2, _b;
27570
27589
  for (const issue3 of error49.issues) {
27571
27590
  if (issue3.code === "invalid_union" && issue3.errors.length) {
@@ -27575,7 +27594,7 @@ function treeifyError2(error48, mapper = (issue3) => issue3.message) {
27575
27594
  } else if (issue3.code === "invalid_element") {
27576
27595
  processError({ issues: issue3.issues }, issue3.path);
27577
27596
  } else {
27578
- const fullpath = [...path4, ...issue3.path];
27597
+ const fullpath = [...path5, ...issue3.path];
27579
27598
  if (fullpath.length === 0) {
27580
27599
  result.errors.push(mapper(issue3));
27581
27600
  continue;
@@ -27607,8 +27626,8 @@ function treeifyError2(error48, mapper = (issue3) => issue3.message) {
27607
27626
  }
27608
27627
  function toDotPath2(_path) {
27609
27628
  const segs = [];
27610
- const path4 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
27611
- for (const seg of path4) {
27629
+ const path5 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
27630
+ for (const seg of path5) {
27612
27631
  if (typeof seg === "number")
27613
27632
  segs.push(`[${seg}]`);
27614
27633
  else if (typeof seg === "symbol")
@@ -39355,13 +39374,13 @@ function resolveRef2(ref, ctx) {
39355
39374
  if (!ref.startsWith("#")) {
39356
39375
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
39357
39376
  }
39358
- const path4 = ref.slice(1).split("/").filter(Boolean);
39359
- if (path4.length === 0) {
39377
+ const path5 = ref.slice(1).split("/").filter(Boolean);
39378
+ if (path5.length === 0) {
39360
39379
  return ctx.rootSchema;
39361
39380
  }
39362
39381
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
39363
- if (path4[0] === defsKey) {
39364
- const key = path4[1];
39382
+ if (path5[0] === defsKey) {
39383
+ const key = path5[1];
39365
39384
  if (!key || !ctx.defs[key]) {
39366
39385
  throw new Error(`Reference not found: ${ref}`);
39367
39386
  }
@@ -39904,6 +39923,7 @@ app.route({
39904
39923
  args: {
39905
39924
  q: exports_external2.string().optional().describe("搜索关键词,选填;留空或不传则返回全部文件"),
39906
39925
  projectPath: exports_external2.string().optional().describe("按项目根目录路径过滤,仅返回该项目下的文件,选填"),
39926
+ filepath: exports_external2.string().optional().describe("按文件绝对路径过滤,选填"),
39907
39927
  repo: exports_external2.string().optional().describe("按代码仓库标识过滤(如 owner/repo),选填"),
39908
39928
  title: exports_external2.string().optional().describe("按人工标注的标题字段过滤,选填"),
39909
39929
  tags: exports_external2.array(exports_external2.string()).optional().describe("按人工标注的标签列表过滤,选填"),
@@ -39916,13 +39936,13 @@ app.route({
39916
39936
  }
39917
39937
  }
39918
39938
  }).define(async (ctx) => {
39919
- let { q, projectPath, repo, title, tags, summary, description, link, sort, limit, getContent = false } = ctx.query;
39939
+ let { q, projectPath, filepath, repo, title, tags, summary, description, link, sort, limit, getContent = false } = ctx.query;
39920
39940
  if (!q) {
39921
39941
  sort = sort ?? ["projectPath:asc"];
39922
39942
  limit = limit ?? 1000;
39923
39943
  }
39924
39944
  const projectSearch = manager.projectSearch;
39925
- const hits = await projectSearch.searchFiles(q, { projectPath, repo, title, tags, summary, description, link, sort, limit, getContent });
39945
+ const hits = await projectSearch.searchFiles(q, { projectPath, filepath, repo, title, tags, summary, description, link, sort, limit, getContent });
39926
39946
  ctx.body = { list: hits };
39927
39947
  }).addTo(app);
39928
39948
 
@@ -39948,6 +39968,33 @@ app.route({
39948
39968
  ctx.throw(500, "读取文件失败");
39949
39969
  }
39950
39970
  }).addTo(app);
39971
+ app.route({
39972
+ path: "project-file",
39973
+ key: "update-content",
39974
+ middleware: ["auth-admin"],
39975
+ description: "将 base64 编码的内容写入指定文件路径,用于更新或创建文件",
39976
+ metadata: {
39977
+ args: {
39978
+ filepath: exports_external2.string().nonempty().describe("要写入的文件绝对路径,必填"),
39979
+ content: exports_external2.string().nonempty().describe("文件内容的 base64 编码,必填")
39980
+ }
39981
+ }
39982
+ }).define(async (ctx) => {
39983
+ const { filepath, content } = ctx.query;
39984
+ if (!filepath)
39985
+ ctx.throw(400, "filepath 不能为空");
39986
+ if (!content)
39987
+ ctx.throw(400, "content 不能为空");
39988
+ try {
39989
+ const success3 = await manager.writeFile(filepath, content);
39990
+ if (!success3) {
39991
+ ctx.throw(500, "写入文件失败");
39992
+ }
39993
+ ctx.body = { success: true };
39994
+ } catch (error49) {
39995
+ ctx.throw(500, "写入文件失败");
39996
+ }
39997
+ }).addTo(app);
39951
39998
  app.route({
39952
39999
  path: "project-file",
39953
40000
  key: "update",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kevisual/project-search",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -21,11 +21,11 @@
21
21
  "@kevisual/dts": "^0.0.4",
22
22
  "@kevisual/remote-app": "^0.0.7",
23
23
  "@kevisual/router": "^0.1.1",
24
+ "es-toolkit": "^1.45.1",
25
+ "eventemitter3": "^5.0.4",
24
26
  "fast-glob": "^3.3.3",
25
27
  "meilisearch": "^0.55.0",
26
- "zod": "^4.3.6",
27
- "es-toolkit": "^1.45.1",
28
- "eventemitter3": "^5.0.4"
28
+ "zod": "^4.3.6"
29
29
  },
30
30
  "dependencies": {
31
31
  "@parcel/watcher": "^2.5.6"