@collage-dam/mcp-server 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (306) hide show
  1. package/.env.example +56 -0
  2. package/CHANGELOG.md +90 -0
  3. package/LICENSE +21 -0
  4. package/README.md +512 -0
  5. package/dist/client.d.ts +497 -0
  6. package/dist/client.d.ts.map +1 -0
  7. package/dist/client.js +1162 -0
  8. package/dist/client.js.map +1 -0
  9. package/dist/conventions/confirmation.d.ts +89 -0
  10. package/dist/conventions/confirmation.d.ts.map +1 -0
  11. package/dist/conventions/confirmation.js +132 -0
  12. package/dist/conventions/confirmation.js.map +1 -0
  13. package/dist/conventions/dry-run/batch-executor.d.ts +36 -0
  14. package/dist/conventions/dry-run/batch-executor.d.ts.map +1 -0
  15. package/dist/conventions/dry-run/batch-executor.js +89 -0
  16. package/dist/conventions/dry-run/batch-executor.js.map +1 -0
  17. package/dist/conventions/dry-run/diff-renderer.d.ts +34 -0
  18. package/dist/conventions/dry-run/diff-renderer.d.ts.map +1 -0
  19. package/dist/conventions/dry-run/diff-renderer.js +158 -0
  20. package/dist/conventions/dry-run/diff-renderer.js.map +1 -0
  21. package/dist/conventions/dry-run/index.d.ts +13 -0
  22. package/dist/conventions/dry-run/index.d.ts.map +1 -0
  23. package/dist/conventions/dry-run/index.js +10 -0
  24. package/dist/conventions/dry-run/index.js.map +1 -0
  25. package/dist/conventions/dry-run/mutating-tool.d.ts +64 -0
  26. package/dist/conventions/dry-run/mutating-tool.d.ts.map +1 -0
  27. package/dist/conventions/dry-run/mutating-tool.js +88 -0
  28. package/dist/conventions/dry-run/mutating-tool.js.map +1 -0
  29. package/dist/conventions/dry-run/summary.d.ts +66 -0
  30. package/dist/conventions/dry-run/summary.d.ts.map +1 -0
  31. package/dist/conventions/dry-run/summary.js +185 -0
  32. package/dist/conventions/dry-run/summary.js.map +1 -0
  33. package/dist/conventions/dry-run/types.d.ts +597 -0
  34. package/dist/conventions/dry-run/types.d.ts.map +1 -0
  35. package/dist/conventions/dry-run/types.js +108 -0
  36. package/dist/conventions/dry-run/types.js.map +1 -0
  37. package/dist/conventions/dry-run/with-dry-run.d.ts +66 -0
  38. package/dist/conventions/dry-run/with-dry-run.d.ts.map +1 -0
  39. package/dist/conventions/dry-run/with-dry-run.js +219 -0
  40. package/dist/conventions/dry-run/with-dry-run.js.map +1 -0
  41. package/dist/conventions/env.d.ts +49 -0
  42. package/dist/conventions/env.d.ts.map +1 -0
  43. package/dist/conventions/env.js +84 -0
  44. package/dist/conventions/env.js.map +1 -0
  45. package/dist/conventions/errors.d.ts +68 -0
  46. package/dist/conventions/errors.d.ts.map +1 -0
  47. package/dist/conventions/errors.js +81 -0
  48. package/dist/conventions/errors.js.map +1 -0
  49. package/dist/conventions/logger.d.ts +28 -0
  50. package/dist/conventions/logger.d.ts.map +1 -0
  51. package/dist/conventions/logger.js +105 -0
  52. package/dist/conventions/logger.js.map +1 -0
  53. package/dist/conventions/pagination.d.ts +37 -0
  54. package/dist/conventions/pagination.d.ts.map +1 -0
  55. package/dist/conventions/pagination.js +53 -0
  56. package/dist/conventions/pagination.js.map +1 -0
  57. package/dist/conventions/rate-limiter.d.ts +54 -0
  58. package/dist/conventions/rate-limiter.d.ts.map +1 -0
  59. package/dist/conventions/rate-limiter.js +143 -0
  60. package/dist/conventions/rate-limiter.js.map +1 -0
  61. package/dist/conventions/response-budget.d.ts +66 -0
  62. package/dist/conventions/response-budget.d.ts.map +1 -0
  63. package/dist/conventions/response-budget.js +89 -0
  64. package/dist/conventions/response-budget.js.map +1 -0
  65. package/dist/conventions/schema-version.d.ts +27 -0
  66. package/dist/conventions/schema-version.d.ts.map +1 -0
  67. package/dist/conventions/schema-version.js +29 -0
  68. package/dist/conventions/schema-version.js.map +1 -0
  69. package/dist/conventions/state-store-redis.d.ts +32 -0
  70. package/dist/conventions/state-store-redis.d.ts.map +1 -0
  71. package/dist/conventions/state-store-redis.js +77 -0
  72. package/dist/conventions/state-store-redis.js.map +1 -0
  73. package/dist/conventions/state-store.d.ts +46 -0
  74. package/dist/conventions/state-store.d.ts.map +1 -0
  75. package/dist/conventions/state-store.js +105 -0
  76. package/dist/conventions/state-store.js.map +1 -0
  77. package/dist/index.d.ts +5 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +421 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/prompts/collection-audit.d.ts +13 -0
  82. package/dist/prompts/collection-audit.d.ts.map +1 -0
  83. package/dist/prompts/collection-audit.js +168 -0
  84. package/dist/prompts/collection-audit.js.map +1 -0
  85. package/dist/prompts/create-distribution.d.ts +15 -0
  86. package/dist/prompts/create-distribution.d.ts.map +1 -0
  87. package/dist/prompts/create-distribution.js +111 -0
  88. package/dist/prompts/create-distribution.js.map +1 -0
  89. package/dist/prompts/helpers.d.ts +20 -0
  90. package/dist/prompts/helpers.d.ts.map +1 -0
  91. package/dist/prompts/helpers.js +53 -0
  92. package/dist/prompts/helpers.js.map +1 -0
  93. package/dist/prompts/library-health-audit.d.ts +13 -0
  94. package/dist/prompts/library-health-audit.d.ts.map +1 -0
  95. package/dist/prompts/library-health-audit.js +131 -0
  96. package/dist/prompts/library-health-audit.js.map +1 -0
  97. package/dist/prompts/usage-insights.d.ts +13 -0
  98. package/dist/prompts/usage-insights.d.ts.map +1 -0
  99. package/dist/prompts/usage-insights.js +98 -0
  100. package/dist/prompts/usage-insights.js.map +1 -0
  101. package/dist/prompts/wrap-prompt-as-tool.d.ts +48 -0
  102. package/dist/prompts/wrap-prompt-as-tool.d.ts.map +1 -0
  103. package/dist/prompts/wrap-prompt-as-tool.js +61 -0
  104. package/dist/prompts/wrap-prompt-as-tool.js.map +1 -0
  105. package/dist/resources/asset-by-id.d.ts +4 -0
  106. package/dist/resources/asset-by-id.d.ts.map +1 -0
  107. package/dist/resources/asset-by-id.js +27 -0
  108. package/dist/resources/asset-by-id.js.map +1 -0
  109. package/dist/resources/collections.d.ts +5 -0
  110. package/dist/resources/collections.d.ts.map +1 -0
  111. package/dist/resources/collections.js +48 -0
  112. package/dist/resources/collections.js.map +1 -0
  113. package/dist/resources/custom-fields.d.ts +4 -0
  114. package/dist/resources/custom-fields.d.ts.map +1 -0
  115. package/dist/resources/custom-fields.js +30 -0
  116. package/dist/resources/custom-fields.js.map +1 -0
  117. package/dist/resources/folders.d.ts +5 -0
  118. package/dist/resources/folders.d.ts.map +1 -0
  119. package/dist/resources/folders.js +73 -0
  120. package/dist/resources/folders.js.map +1 -0
  121. package/dist/resources/helpers.d.ts +17 -0
  122. package/dist/resources/helpers.d.ts.map +1 -0
  123. package/dist/resources/helpers.js +59 -0
  124. package/dist/resources/helpers.js.map +1 -0
  125. package/dist/resources/portals.d.ts +5 -0
  126. package/dist/resources/portals.d.ts.map +1 -0
  127. package/dist/resources/portals.js +81 -0
  128. package/dist/resources/portals.js.map +1 -0
  129. package/dist/resources/recent-and-dashboard.d.ts +5 -0
  130. package/dist/resources/recent-and-dashboard.d.ts.map +1 -0
  131. package/dist/resources/recent-and-dashboard.js +42 -0
  132. package/dist/resources/recent-and-dashboard.js.map +1 -0
  133. package/dist/tools/asset-selection.d.ts +102 -0
  134. package/dist/tools/asset-selection.d.ts.map +1 -0
  135. package/dist/tools/asset-selection.js +133 -0
  136. package/dist/tools/asset-selection.js.map +1 -0
  137. package/dist/tools/audit/audit-folder-structure.d.ts +108 -0
  138. package/dist/tools/audit/audit-folder-structure.d.ts.map +1 -0
  139. package/dist/tools/audit/audit-folder-structure.js +260 -0
  140. package/dist/tools/audit/audit-folder-structure.js.map +1 -0
  141. package/dist/tools/audit/audit-naming-conventions.d.ts +83 -0
  142. package/dist/tools/audit/audit-naming-conventions.d.ts.map +1 -0
  143. package/dist/tools/audit/audit-naming-conventions.js +238 -0
  144. package/dist/tools/audit/audit-naming-conventions.js.map +1 -0
  145. package/dist/tools/audit/audit-tagging-hygiene.d.ts +77 -0
  146. package/dist/tools/audit/audit-tagging-hygiene.d.ts.map +1 -0
  147. package/dist/tools/audit/audit-tagging-hygiene.js +402 -0
  148. package/dist/tools/audit/audit-tagging-hygiene.js.map +1 -0
  149. package/dist/tools/audit/detect-duplicates.d.ts +62 -0
  150. package/dist/tools/audit/detect-duplicates.d.ts.map +1 -0
  151. package/dist/tools/audit/detect-duplicates.js +0 -0
  152. package/dist/tools/audit/detect-duplicates.js.map +1 -0
  153. package/dist/tools/audit/types.d.ts +526 -0
  154. package/dist/tools/audit/types.d.ts.map +1 -0
  155. package/dist/tools/audit/types.js +188 -0
  156. package/dist/tools/audit/types.js.map +1 -0
  157. package/dist/tools/bulk-move-assets.d.ts +78 -0
  158. package/dist/tools/bulk-move-assets.d.ts.map +1 -0
  159. package/dist/tools/bulk-move-assets.js +122 -0
  160. package/dist/tools/bulk-move-assets.js.map +1 -0
  161. package/dist/tools/bulk-normalize-filenames.d.ts +62 -0
  162. package/dist/tools/bulk-normalize-filenames.d.ts.map +1 -0
  163. package/dist/tools/bulk-normalize-filenames.js +237 -0
  164. package/dist/tools/bulk-normalize-filenames.js.map +1 -0
  165. package/dist/tools/bulk-rename-assets.d.ts +79 -0
  166. package/dist/tools/bulk-rename-assets.d.ts.map +1 -0
  167. package/dist/tools/bulk-rename-assets.js +139 -0
  168. package/dist/tools/bulk-rename-assets.js.map +1 -0
  169. package/dist/tools/bulk-tags.d.ts +107 -0
  170. package/dist/tools/bulk-tags.d.ts.map +1 -0
  171. package/dist/tools/bulk-tags.js +220 -0
  172. package/dist/tools/bulk-tags.js.map +1 -0
  173. package/dist/tools/client-adapters.d.ts +76 -0
  174. package/dist/tools/client-adapters.d.ts.map +1 -0
  175. package/dist/tools/client-adapters.js +648 -0
  176. package/dist/tools/client-adapters.js.map +1 -0
  177. package/dist/tools/collection-membership.d.ts +90 -0
  178. package/dist/tools/collection-membership.d.ts.map +1 -0
  179. package/dist/tools/collection-membership.js +195 -0
  180. package/dist/tools/collection-membership.js.map +1 -0
  181. package/dist/tools/create-collection.d.ts +63 -0
  182. package/dist/tools/create-collection.d.ts.map +1 -0
  183. package/dist/tools/create-collection.js +151 -0
  184. package/dist/tools/create-collection.js.map +1 -0
  185. package/dist/tools/create-folder.d.ts +46 -0
  186. package/dist/tools/create-folder.d.ts.map +1 -0
  187. package/dist/tools/create-folder.js +83 -0
  188. package/dist/tools/create-folder.js.map +1 -0
  189. package/dist/tools/create-share-link.d.ts +107 -0
  190. package/dist/tools/create-share-link.d.ts.map +1 -0
  191. package/dist/tools/create-share-link.js +239 -0
  192. package/dist/tools/create-share-link.js.map +1 -0
  193. package/dist/tools/get-asset-details.d.ts +401 -0
  194. package/dist/tools/get-asset-details.d.ts.map +1 -0
  195. package/dist/tools/get-asset-details.js +56 -0
  196. package/dist/tools/get-asset-details.js.map +1 -0
  197. package/dist/tools/get-collection.d.ts +126 -0
  198. package/dist/tools/get-collection.d.ts.map +1 -0
  199. package/dist/tools/get-collection.js +52 -0
  200. package/dist/tools/get-collection.js.map +1 -0
  201. package/dist/tools/get-embed-code.d.ts +195 -0
  202. package/dist/tools/get-embed-code.d.ts.map +1 -0
  203. package/dist/tools/get-embed-code.js +214 -0
  204. package/dist/tools/get-embed-code.js.map +1 -0
  205. package/dist/tools/insights/analyze-share-links.d.ts +159 -0
  206. package/dist/tools/insights/analyze-share-links.d.ts.map +1 -0
  207. package/dist/tools/insights/analyze-share-links.js +314 -0
  208. package/dist/tools/insights/analyze-share-links.js.map +1 -0
  209. package/dist/tools/insights/insight-cache.d.ts +36 -0
  210. package/dist/tools/insights/insight-cache.d.ts.map +1 -0
  211. package/dist/tools/insights/insight-cache.js +98 -0
  212. package/dist/tools/insights/insight-cache.js.map +1 -0
  213. package/dist/tools/insights/report-asset-activation.d.ts +149 -0
  214. package/dist/tools/insights/report-asset-activation.d.ts.map +1 -0
  215. package/dist/tools/insights/report-asset-activation.js +380 -0
  216. package/dist/tools/insights/report-asset-activation.js.map +1 -0
  217. package/dist/tools/insights/report-stale-assets.d.ts +120 -0
  218. package/dist/tools/insights/report-stale-assets.d.ts.map +1 -0
  219. package/dist/tools/insights/report-stale-assets.js +281 -0
  220. package/dist/tools/insights/report-stale-assets.js.map +1 -0
  221. package/dist/tools/insights/report-top-assets.d.ts +139 -0
  222. package/dist/tools/insights/report-top-assets.d.ts.map +1 -0
  223. package/dist/tools/insights/report-top-assets.js +407 -0
  224. package/dist/tools/insights/report-top-assets.js.map +1 -0
  225. package/dist/tools/list-categories.d.ts +127 -0
  226. package/dist/tools/list-categories.d.ts.map +1 -0
  227. package/dist/tools/list-categories.js +68 -0
  228. package/dist/tools/list-categories.js.map +1 -0
  229. package/dist/tools/list-collections.d.ts +127 -0
  230. package/dist/tools/list-collections.d.ts.map +1 -0
  231. package/dist/tools/list-collections.js +53 -0
  232. package/dist/tools/list-collections.js.map +1 -0
  233. package/dist/tools/list-custom-fields.d.ts +125 -0
  234. package/dist/tools/list-custom-fields.d.ts.map +1 -0
  235. package/dist/tools/list-custom-fields.js +51 -0
  236. package/dist/tools/list-custom-fields.js.map +1 -0
  237. package/dist/tools/list-share-links.d.ts +192 -0
  238. package/dist/tools/list-share-links.d.ts.map +1 -0
  239. package/dist/tools/list-share-links.js +92 -0
  240. package/dist/tools/list-share-links.js.map +1 -0
  241. package/dist/tools/list-workspaces.d.ts +88 -0
  242. package/dist/tools/list-workspaces.d.ts.map +1 -0
  243. package/dist/tools/list-workspaces.js +71 -0
  244. package/dist/tools/list-workspaces.js.map +1 -0
  245. package/dist/tools/move-asset.d.ts +48 -0
  246. package/dist/tools/move-asset.d.ts.map +1 -0
  247. package/dist/tools/move-asset.js +85 -0
  248. package/dist/tools/move-asset.js.map +1 -0
  249. package/dist/tools/rename-asset.d.ts +88 -0
  250. package/dist/tools/rename-asset.d.ts.map +1 -0
  251. package/dist/tools/rename-asset.js +100 -0
  252. package/dist/tools/rename-asset.js.map +1 -0
  253. package/dist/tools/rename-folder.d.ts +55 -0
  254. package/dist/tools/rename-folder.d.ts.map +1 -0
  255. package/dist/tools/rename-folder.js +101 -0
  256. package/dist/tools/rename-folder.js.map +1 -0
  257. package/dist/tools/revoke-share-link.d.ts +55 -0
  258. package/dist/tools/revoke-share-link.d.ts.map +1 -0
  259. package/dist/tools/revoke-share-link.js +77 -0
  260. package/dist/tools/revoke-share-link.js.map +1 -0
  261. package/dist/tools/search/facets.d.ts +34 -0
  262. package/dist/tools/search/facets.d.ts.map +1 -0
  263. package/dist/tools/search/facets.js +147 -0
  264. package/dist/tools/search/facets.js.map +1 -0
  265. package/dist/tools/search/filter-builder.d.ts +33 -0
  266. package/dist/tools/search/filter-builder.d.ts.map +1 -0
  267. package/dist/tools/search/filter-builder.js +111 -0
  268. package/dist/tools/search/filter-builder.js.map +1 -0
  269. package/dist/tools/search/search-assets.d.ts +41 -0
  270. package/dist/tools/search/search-assets.d.ts.map +1 -0
  271. package/dist/tools/search/search-assets.js +162 -0
  272. package/dist/tools/search/search-assets.js.map +1 -0
  273. package/dist/tools/search/search-collections.d.ts +35 -0
  274. package/dist/tools/search/search-collections.d.ts.map +1 -0
  275. package/dist/tools/search/search-collections.js +103 -0
  276. package/dist/tools/search/search-collections.js.map +1 -0
  277. package/dist/tools/search/types.d.ts +1047 -0
  278. package/dist/tools/search/types.d.ts.map +1 -0
  279. package/dist/tools/search/types.js +216 -0
  280. package/dist/tools/search/types.js.map +1 -0
  281. package/dist/tools/update-asset-metadata.d.ts +78 -0
  282. package/dist/tools/update-asset-metadata.d.ts.map +1 -0
  283. package/dist/tools/update-asset-metadata.js +203 -0
  284. package/dist/tools/update-asset-metadata.js.map +1 -0
  285. package/dist/tools/update-collection.d.ts +69 -0
  286. package/dist/tools/update-collection.d.ts.map +1 -0
  287. package/dist/tools/update-collection.js +142 -0
  288. package/dist/tools/update-collection.js.map +1 -0
  289. package/dist/tools/view-category-contents.d.ts +231 -0
  290. package/dist/tools/view-category-contents.d.ts.map +1 -0
  291. package/dist/tools/view-category-contents.js +97 -0
  292. package/dist/tools/view-category-contents.js.map +1 -0
  293. package/dist/types.d.ts +1326 -0
  294. package/dist/types.d.ts.map +1 -0
  295. package/dist/types.js +288 -0
  296. package/dist/types.js.map +1 -0
  297. package/dist/typesense.d.ts +84 -0
  298. package/dist/typesense.d.ts.map +1 -0
  299. package/dist/typesense.js +243 -0
  300. package/dist/typesense.js.map +1 -0
  301. package/docs/api-field-verification.md +244 -0
  302. package/docs/deployment-runbook.md +446 -0
  303. package/docs/security-review.md +195 -0
  304. package/docs/typesense-filter-schema.md +262 -0
  305. package/docs/verified-endpoints.md +38 -0
  306. package/package.json +72 -0
@@ -0,0 +1,55 @@
1
+ import { z } from 'zod';
2
+ import { MutatingTool, type ApplyFn, type MutatingToolDeps, type PlanResult } from '../conventions/dry-run/index.js';
3
+ import type { DryRunPlan } from '../conventions/dry-run/index.js';
4
+ import { type McpToolError } from '../conventions/errors.js';
5
+ export declare const RenameFolderInputSchema: z.ZodObject<{
6
+ category_id: z.ZodUnion<[z.ZodNumber, z.ZodString]>;
7
+ new_name: z.ZodString;
8
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
9
+ /** Optional current name for diff display; looked up if absent. */
10
+ current_name: z.ZodOptional<z.ZodString>;
11
+ }, "strip", z.ZodTypeAny, {
12
+ category_id: string | number;
13
+ new_name: string;
14
+ description?: string | null | undefined;
15
+ current_name?: string | undefined;
16
+ }, {
17
+ category_id: string | number;
18
+ new_name: string;
19
+ description?: string | null | undefined;
20
+ current_name?: string | undefined;
21
+ }>;
22
+ export type RenameFolderInput = z.infer<typeof RenameFolderInputSchema>;
23
+ export declare const RENAME_FOLDER_DESCRIPTION: string;
24
+ export interface RenameFolderItemResult {
25
+ folder_id: string;
26
+ new_name: string;
27
+ description: string | null;
28
+ }
29
+ export interface RenameFolderClient {
30
+ /** Lookup current name for diff display. Returns null if unavailable. */
31
+ getCurrentFolderName(categoryId: string): Promise<string | null>;
32
+ renameFolder(input: {
33
+ category_id: string;
34
+ folder_name: string;
35
+ description?: string | null;
36
+ }): Promise<{
37
+ ok: true;
38
+ } | {
39
+ ok: false;
40
+ error: McpToolError;
41
+ }>;
42
+ }
43
+ export interface RenameFolderDeps extends MutatingToolDeps<RenameFolderInput> {
44
+ client: RenameFolderClient;
45
+ }
46
+ export declare class RenameFolderTool extends MutatingTool<RenameFolderInput, RenameFolderItemResult> {
47
+ readonly name = "rename_folder";
48
+ protected readonly chunkSize = 1;
49
+ private readonly client;
50
+ constructor(deps: RenameFolderDeps);
51
+ protected plan(input: RenameFolderInput): Promise<PlanResult>;
52
+ protected apply: ApplyFn<RenameFolderItemResult>;
53
+ }
54
+ export type RenameFolderPlan = DryRunPlan<RenameFolderInput>;
55
+ //# sourceMappingURL=rename-folder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rename-folder.d.ts","sourceRoot":"","sources":["../../src/tools/rename-folder.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,YAAY,EACZ,KAAK,OAAO,EACZ,KAAK,gBAAgB,EACrB,KAAK,UAAU,EAEhB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAgB,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAG9E,eAAO,MAAM,uBAAuB;;;;IAIlC,mEAAmE;;;;;;;;;;;;EAEnE,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,eAAO,MAAM,yBAAyB,QAGG,CAAC;AAE1C,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACjE,YAAY,CAAC,KAAK,EAAE;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC7B,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,YAAY,CAAA;KAAE,CAAC,CAAC;CAChE;AAED,MAAM,WAAW,gBAAiB,SAAQ,gBAAgB,CAAC,iBAAiB,CAAC;IAC3E,MAAM,EAAE,kBAAkB,CAAC;CAC5B;AAED,qBAAa,gBAAiB,SAAQ,YAAY,CAChD,iBAAiB,EACjB,sBAAsB,CACvB;IACC,QAAQ,CAAC,IAAI,mBAAmB;IAChC,mBAA4B,SAAS,KAAK;IAE1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;gBAEhC,IAAI,EAAE,gBAAgB;cAKlB,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC;IA6CnE,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,sBAAsB,CAAC,CA0C9C;CACH;AAED,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC"}
@@ -0,0 +1,101 @@
1
+ // ── rename_folder ────────────────────────────────────────────────────
2
+ // Renames one folder, optionally updating its description. Two-step
3
+ // dry-run flow.
4
+ //
5
+ // Upstream surface:
6
+ // POST digital-assets/category/rename/{id} { workspace_id, folder_name, description? }
7
+ //
8
+ // MVP scope: one folder per call. Bulk rename is Phase 1.5.
9
+ import { z } from 'zod';
10
+ import { MutatingTool, } from '../conventions/dry-run/index.js';
11
+ import { createToolError } from '../conventions/errors.js';
12
+ import { IdSchema } from '../types.js';
13
+ export const RenameFolderInputSchema = z.object({
14
+ category_id: IdSchema,
15
+ new_name: z.string().min(1).max(255),
16
+ description: z.string().max(10_000).nullable().optional(),
17
+ /** Optional current name for diff display; looked up if absent. */
18
+ current_name: z.string().optional(),
19
+ });
20
+ export const RENAME_FOLDER_DESCRIPTION = 'Rename a folder (and optionally update its description). Two-step: ' +
21
+ 'first call returns a preview plus a confirmation_token; second call ' +
22
+ '(with the token) executes the rename.';
23
+ export class RenameFolderTool extends MutatingTool {
24
+ name = 'rename_folder';
25
+ chunkSize = 1;
26
+ client;
27
+ constructor(deps) {
28
+ super(deps);
29
+ this.client = deps.client;
30
+ }
31
+ async plan(input) {
32
+ const id = String(input.category_id);
33
+ const before = input.current_name ??
34
+ (await this.client.getCurrentFolderName(id)) ??
35
+ '(unknown)';
36
+ const after = { folder_name: input.new_name };
37
+ if (input.description !== undefined) {
38
+ after['description'] = input.description ?? '';
39
+ }
40
+ const change = {
41
+ id,
42
+ operation: 'update',
43
+ before: { folder_name: before },
44
+ after,
45
+ metadata: {
46
+ category_id: id,
47
+ new_name: input.new_name,
48
+ description: input.description ?? null,
49
+ },
50
+ };
51
+ const riskFlags = [];
52
+ if (before === input.new_name) {
53
+ riskFlags.push({
54
+ code: 'noop_rename',
55
+ severity: 'info',
56
+ message: 'rename to identical name (no-op)',
57
+ count: 1,
58
+ });
59
+ }
60
+ if (input.new_name.length > 100) {
61
+ riskFlags.push({
62
+ code: 'long_folder_name',
63
+ severity: 'warning',
64
+ message: 'name exceeds 100 characters',
65
+ count: 1,
66
+ });
67
+ }
68
+ return { changes: [change], riskFlags };
69
+ }
70
+ apply = async (change) => {
71
+ const meta = (change.metadata ?? {});
72
+ if (typeof meta.category_id !== 'string' ||
73
+ typeof meta.new_name !== 'string' ||
74
+ meta.new_name.length === 0) {
75
+ return {
76
+ ok: false,
77
+ error: createToolError('VALIDATION', `change ${change.id} missing category_id or new_name`),
78
+ };
79
+ }
80
+ const callInput = {
81
+ category_id: meta.category_id,
82
+ folder_name: meta.new_name,
83
+ };
84
+ if (meta.description !== undefined && meta.description !== null) {
85
+ callInput.description = meta.description;
86
+ }
87
+ const result = await this.client.renameFolder(callInput);
88
+ if (!result.ok) {
89
+ return { ok: false, error: result.error };
90
+ }
91
+ return {
92
+ ok: true,
93
+ result: {
94
+ folder_id: meta.category_id,
95
+ new_name: meta.new_name,
96
+ description: meta.description ?? null,
97
+ },
98
+ };
99
+ };
100
+ }
101
+ //# sourceMappingURL=rename-folder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rename-folder.js","sourceRoot":"","sources":["../../src/tools/rename-folder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import { z } from 'zod';
2
+ import { MutatingTool, type ApplyFn, type MutatingToolDeps, type PlanResult } from '../conventions/dry-run/index.js';
3
+ import type { DryRunPlan } from '../conventions/dry-run/index.js';
4
+ import { type McpToolError } from '../conventions/errors.js';
5
+ export declare const RevokeShareLinkInputSchema: z.ZodObject<{
6
+ share_id: z.ZodUnion<[z.ZodNumber, z.ZodString]>;
7
+ }, "strip", z.ZodTypeAny, {
8
+ share_id: string | number;
9
+ }, {
10
+ share_id: string | number;
11
+ }>;
12
+ export type RevokeShareLinkInput = z.infer<typeof RevokeShareLinkInputSchema>;
13
+ export declare const REVOKE_SHARE_LINK_DESCRIPTION: string;
14
+ export interface RevokeShareLinkItemResult {
15
+ share_id: string;
16
+ revoked: true;
17
+ }
18
+ export interface ShareLinkSnapshot {
19
+ id: string;
20
+ title: string | null;
21
+ share_url: string | null;
22
+ expiration: string | null;
23
+ password_protected: boolean;
24
+ }
25
+ export interface RevokeShareLinkClient {
26
+ /** Read the current state of the share link for diff display. */
27
+ getShareLink(shareId: string): Promise<{
28
+ ok: true;
29
+ snapshot: ShareLinkSnapshot;
30
+ } | {
31
+ ok: false;
32
+ error: McpToolError;
33
+ }>;
34
+ /** Revoke the share link. */
35
+ revokeShareLink(shareId: string): Promise<{
36
+ ok: true;
37
+ } | {
38
+ ok: false;
39
+ error: McpToolError;
40
+ }>;
41
+ }
42
+ export interface RevokeShareLinkDeps extends MutatingToolDeps<RevokeShareLinkInput> {
43
+ client: RevokeShareLinkClient;
44
+ }
45
+ export declare class RevokeShareLinkTool extends MutatingTool<RevokeShareLinkInput, RevokeShareLinkItemResult> {
46
+ readonly name = "revoke_share_link";
47
+ protected readonly chunkSize = 1;
48
+ private readonly client;
49
+ constructor(deps: RevokeShareLinkDeps);
50
+ protected plan(input: RevokeShareLinkInput): Promise<PlanResult>;
51
+ protected apply: ApplyFn<RevokeShareLinkItemResult>;
52
+ }
53
+ export declare function buildRevokeShareLinkTool(deps: RevokeShareLinkDeps): RevokeShareLinkTool;
54
+ export type RevokeShareLinkPlan = DryRunPlan<RevokeShareLinkInput>;
55
+ //# sourceMappingURL=revoke-share-link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revoke-share-link.d.ts","sourceRoot":"","sources":["../../src/tools/revoke-share-link.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,YAAY,EACZ,KAAK,OAAO,EACZ,KAAK,gBAAgB,EACrB,KAAK,UAAU,EAEhB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAgB,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAK7D,eAAO,MAAM,0BAA0B;;;;;;EAErC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE9E,eAAO,MAAM,6BAA6B,QAIF,CAAC;AAIzC,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,IAAI,CAAC;CACf;AAID,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,iEAAiE;IACjE,YAAY,CACV,OAAO,EAAE,MAAM,GACd,OAAO,CACN;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,iBAAiB,CAAA;KAAE,GACzC;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,YAAY,CAAA;KAAE,CACrC,CAAC;IACF,6BAA6B;IAC7B,eAAe,CACb,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,YAAY,CAAA;KAAE,CAAC,CAAC;CAC/D;AAID,MAAM,WAAW,mBACf,SAAQ,gBAAgB,CAAC,oBAAoB,CAAC;IAC9C,MAAM,EAAE,qBAAqB,CAAC;CAC/B;AAED,qBAAa,mBAAoB,SAAQ,YAAY,CACnD,oBAAoB,EACpB,yBAAyB,CAC1B;IACC,QAAQ,CAAC,IAAI,uBAAuB;IACpC,mBAA4B,SAAS,KAAK;IAE1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;gBAEnC,IAAI,EAAE,mBAAmB;cAKrB,IAAI,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,UAAU,CAAC;IAsCtE,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,yBAAyB,CAAC,CASjD;CACH;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,mBAAmB,GACxB,mBAAmB,CAErB;AAED,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,oBAAoB,CAAC,CAAC"}
@@ -0,0 +1,77 @@
1
+ // ── revoke_share_link ────────────────────────────────────────────────
2
+ // Revokes a single share link — once revoked, the URL stops working.
3
+ // Wraps the dry-run framework so the preview shows the link being revoked
4
+ // plus its current state before execute.
5
+ //
6
+ // Upstream surface (verified via Admin-Frontend
7
+ // `pages/_workspace_id/dam/sharing/index.vue:1059`):
8
+ // POST digital-assets/dashboard/delete-share-assets-url
9
+ // body: { workspace_id, share_url_id: number[] }
10
+ //
11
+ // We send a single-id list because this tool is one-at-a-time. Bulk
12
+ // revocation can be layered on later via the same upstream endpoint
13
+ // (`delete-multiple-share-assets-url`) if SOW priorities shift.
14
+ import { z } from 'zod';
15
+ import { MutatingTool, } from '../conventions/dry-run/index.js';
16
+ import { IdSchema } from '../types.js';
17
+ // ── Tool I/O Schema ──────────────────────────────────────────────────
18
+ export const RevokeShareLinkInputSchema = z.object({
19
+ share_id: IdSchema,
20
+ });
21
+ export const REVOKE_SHARE_LINK_DESCRIPTION = 'Revoke a single share link. Two-step: first call returns a preview ' +
22
+ '(with the link being revoked and its current state) plus a ' +
23
+ 'confirmation_token; second call (with the token) executes the revoke. ' +
24
+ 'Once revoked, the URL stops working.';
25
+ export class RevokeShareLinkTool extends MutatingTool {
26
+ name = 'revoke_share_link';
27
+ chunkSize = 1;
28
+ client;
29
+ constructor(deps) {
30
+ super(deps);
31
+ this.client = deps.client;
32
+ }
33
+ async plan(input) {
34
+ const id = String(input.share_id);
35
+ const snap = await this.client.getShareLink(id);
36
+ if (!snap.ok) {
37
+ throw new Error(`failed to read share link ${id}: ${snap.error.code} — ${snap.error.message}`);
38
+ }
39
+ const before = {
40
+ id: snap.snapshot.id,
41
+ title: snap.snapshot.title,
42
+ share_url: snap.snapshot.share_url,
43
+ expiration: snap.snapshot.expiration,
44
+ password_protected: snap.snapshot.password_protected,
45
+ revoked: false,
46
+ };
47
+ const after = { ...before, revoked: true };
48
+ const change = {
49
+ id,
50
+ operation: 'delete',
51
+ before,
52
+ after,
53
+ };
54
+ const riskFlags = [];
55
+ riskFlags.push({
56
+ code: 'irreversible',
57
+ severity: 'warning',
58
+ message: 'Revoke is irreversible: once executed, the share URL stops working immediately and recipients lose access.',
59
+ count: 1,
60
+ });
61
+ return { changes: [change], riskFlags };
62
+ }
63
+ apply = async (change) => {
64
+ const result = await this.client.revokeShareLink(change.id);
65
+ if (!result.ok) {
66
+ return { ok: false, error: result.error };
67
+ }
68
+ return {
69
+ ok: true,
70
+ result: { share_id: change.id, revoked: true },
71
+ };
72
+ };
73
+ }
74
+ export function buildRevokeShareLinkTool(deps) {
75
+ return new RevokeShareLinkTool(deps);
76
+ }
77
+ //# sourceMappingURL=revoke-share-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revoke-share-link.js","sourceRoot":"","sources":["../../src/tools/revoke-share-link.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { RawFacetCount } from '../../typesense.js';
2
+ import type { CompactAssetHit, SearchFacets, SearchNextStepHint, VerboseAssetHit } from './types.js';
3
+ /**
4
+ * Project raw Typesense facet counts into the structured `SearchFacets`
5
+ * block. Unknown facet fields are dropped on the floor — we only surface
6
+ * the ones the spec explicitly enumerates.
7
+ */
8
+ export declare function buildSearchFacets(rawFacets: ReadonlyArray<RawFacetCount>, hits: ReadonlyArray<CompactAssetHit | VerboseAssetHit>, topN?: number): SearchFacets;
9
+ interface HintGeneratorContext {
10
+ found: number;
11
+ truncated: boolean;
12
+ /** Has at least one tag been returned? Used to avoid a hint when no tags exist. */
13
+ hasTags: boolean;
14
+ /** Number of hits where `tags` is empty/missing. */
15
+ untaggedCount: number;
16
+ }
17
+ /**
18
+ * Generate structured next-step hints for a `search_assets` response.
19
+ * Hints are emitted only when there is something *useful* to suggest —
20
+ * empty arrays mean "no obvious next step." Consumers can chain these
21
+ * into multi-tool LLM workflows without re-interpreting search results.
22
+ */
23
+ export declare function buildAssetSearchNextSteps(ctx: HintGeneratorContext): SearchNextStepHint[];
24
+ /**
25
+ * Next-step hints for `search_collections` — currently lighter than the
26
+ * asset side because the dam_collections index has fewer signals to act
27
+ * on. Surfaces a single hint when results are empty so the LLM can fall
28
+ * back to `list_collections`.
29
+ */
30
+ export declare function buildCollectionSearchNextSteps(found: number): SearchNextStepHint[];
31
+ /** Count untagged hits in a verbose projection. Compact projection has no tags column. */
32
+ export declare function countUntagged(hits: ReadonlyArray<CompactAssetHit | VerboseAssetHit>): number;
33
+ export {};
34
+ //# sourceMappingURL=facets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"facets.d.ts","sourceRoot":"","sources":["../../../src/tools/search/facets.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EACV,eAAe,EAEf,YAAY,EACZ,kBAAkB,EAClB,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,aAAa,CAAC,aAAa,CAAC,EACvC,IAAI,EAAE,aAAa,CAAC,eAAe,GAAG,eAAe,CAAC,EACtD,IAAI,SAAK,GACR,YAAY,CAkCd;AAqCD,UAAU,oBAAoB;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,mFAAmF;IACnF,OAAO,EAAE,OAAO,CAAC;IACjB,oDAAoD;IACpD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,oBAAoB,GACxB,kBAAkB,EAAE,CA8BtB;AAED;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,MAAM,GACZ,kBAAkB,EAAE,CAWtB;AAED,0FAA0F;AAC1F,wBAAgB,aAAa,CAC3B,IAAI,EAAE,aAAa,CAAC,eAAe,GAAG,eAAe,CAAC,GACrD,MAAM,CAUR"}
@@ -0,0 +1,147 @@
1
+ // ── Facets + Next-step hints generator ───────────────────────────────
2
+ // Shapes the raw Typesense `facet_counts` array into the structured
3
+ // `SearchFacets` block declared in `./types.ts`, and produces a list of
4
+ // machine-readable `next_step_hints` based on what the search returned.
5
+ //
6
+ // Both generators are pure — given the same inputs they emit the same
7
+ // outputs, which makes them trivial to unit-test without touching
8
+ // Typesense.
9
+ /**
10
+ * Project raw Typesense facet counts into the structured `SearchFacets`
11
+ * block. Unknown facet fields are dropped on the floor — we only surface
12
+ * the ones the spec explicitly enumerates.
13
+ */
14
+ export function buildSearchFacets(rawFacets, hits, topN = 10) {
15
+ const facets = {
16
+ tags: [],
17
+ file_type: [],
18
+ date_histogram: [],
19
+ score_distribution: [],
20
+ };
21
+ for (const f of rawFacets) {
22
+ const counts = f.counts.slice(0, topN).map((c) => ({
23
+ value: c.value,
24
+ count: c.count,
25
+ }));
26
+ if (f.field_name === 'tags')
27
+ facets.tags = counts;
28
+ else if (f.field_name === 'file_type')
29
+ facets.file_type = counts;
30
+ else if (f.field_name === 'modified_at') {
31
+ facets.date_histogram = bucketDateFacets(counts);
32
+ }
33
+ }
34
+ // Score distribution — Typesense doesn't surface this as a facet, so we
35
+ // synthesise it from the hit-side (each hit's `_text_match` would need
36
+ // to be passed down for a true distribution). Keeping it empty here is
37
+ // intentional; richer scoring lives in the verbose-projection path
38
+ // when we hydrate via `view-detail`.
39
+ if (hits.length === 0) {
40
+ facets.score_distribution = [];
41
+ }
42
+ else {
43
+ facets.score_distribution = [
44
+ { value: 'returned', count: hits.length },
45
+ ];
46
+ }
47
+ return facets;
48
+ }
49
+ /**
50
+ * Bucket raw `modified_at` facet values (already strings of unix-seconds)
51
+ * into the four canonical buckets: 30d, 90d, 365d, older.
52
+ */
53
+ function bucketDateFacets(counts) {
54
+ const now = Math.floor(Date.now() / 1000);
55
+ const day = 86_400;
56
+ const buckets = {
57
+ '30d': 0,
58
+ '90d': 0,
59
+ '365d': 0,
60
+ older: 0,
61
+ };
62
+ for (const c of counts) {
63
+ const ts = Number(c.value);
64
+ if (!Number.isFinite(ts)) {
65
+ buckets.older += c.count;
66
+ continue;
67
+ }
68
+ const ageDays = (now - ts) / day;
69
+ if (ageDays <= 30)
70
+ buckets['30d'] += c.count;
71
+ else if (ageDays <= 90)
72
+ buckets['90d'] += c.count;
73
+ else if (ageDays <= 365)
74
+ buckets['365d'] += c.count;
75
+ else
76
+ buckets.older += c.count;
77
+ }
78
+ return [
79
+ { value: '30d', count: buckets['30d'] },
80
+ { value: '90d', count: buckets['90d'] },
81
+ { value: '365d', count: buckets['365d'] },
82
+ { value: 'older', count: buckets.older },
83
+ ];
84
+ }
85
+ /**
86
+ * Generate structured next-step hints for a `search_assets` response.
87
+ * Hints are emitted only when there is something *useful* to suggest —
88
+ * empty arrays mean "no obvious next step." Consumers can chain these
89
+ * into multi-tool LLM workflows without re-interpreting search results.
90
+ */
91
+ export function buildAssetSearchNextSteps(ctx) {
92
+ const hints = [];
93
+ if (ctx.untaggedCount > 0) {
94
+ hints.push({
95
+ tool: 'audit_tagging_hygiene',
96
+ reason_code: 'untagged_assets_found',
97
+ reason: `${ctx.untaggedCount} hit(s) have no tags — consider running the tagging-hygiene audit.`,
98
+ });
99
+ }
100
+ if (ctx.truncated) {
101
+ hints.push({
102
+ tool: 'search_assets',
103
+ reason_code: 'response_truncated',
104
+ reason: 'Result page was hard-capped to stay under the response budget. Use the returned next_cursor to continue.',
105
+ });
106
+ }
107
+ if (ctx.found > 1000) {
108
+ hints.push({
109
+ tool: 'search_assets',
110
+ reason_code: 'large_result_set',
111
+ reason: 'Result set is large; consider narrowing with tag/file_type/date filters before bulk operations.',
112
+ });
113
+ }
114
+ return hints;
115
+ }
116
+ /**
117
+ * Next-step hints for `search_collections` — currently lighter than the
118
+ * asset side because the dam_collections index has fewer signals to act
119
+ * on. Surfaces a single hint when results are empty so the LLM can fall
120
+ * back to `list_collections`.
121
+ */
122
+ export function buildCollectionSearchNextSteps(found) {
123
+ if (found === 0) {
124
+ return [
125
+ {
126
+ tool: 'list_collections',
127
+ reason_code: 'empty_result',
128
+ reason: 'No matches found — list all collections in the workspace as a fallback.',
129
+ },
130
+ ];
131
+ }
132
+ return [];
133
+ }
134
+ /** Count untagged hits in a verbose projection. Compact projection has no tags column. */
135
+ export function countUntagged(hits) {
136
+ let n = 0;
137
+ for (const h of hits) {
138
+ if (!('tags' in h) || h.tags === null || h.tags === undefined) {
139
+ n += 1;
140
+ continue;
141
+ }
142
+ if (Array.isArray(h.tags) && h.tags.length === 0)
143
+ n += 1;
144
+ }
145
+ return n;
146
+ }
147
+ //# sourceMappingURL=facets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"facets.js","sourceRoot":"","sources":["../../../src/tools/search/facets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { SearchAssetFilters, Visibility } from './types.js';
2
+ /** Convert a `Visibility` enum value to the numeric Typesense column. */
3
+ export declare function visibilityToNumber(v: Visibility): 0 | 1;
4
+ /**
5
+ * Coerce a date bound (ISO-8601 string or unix-second integer) to a unix
6
+ * timestamp in seconds. Returns null if the input is unparseable.
7
+ */
8
+ export declare function toUnixSeconds(bound: number | string): number | null;
9
+ /**
10
+ * Build the `filter_by` clauses for the asset filters. Returns an array of
11
+ * clause strings (each already wrapped in parens where needed) — the
12
+ * caller joins them with ` && ` and prepends the workspace clause via the
13
+ * wrapper.
14
+ *
15
+ * Ordering and grammar match the Admin-Frontend
16
+ * `mixins/search-common-functions.js` builders. See
17
+ * `docs/typesense-filter-schema.md` for the full citation set.
18
+ */
19
+ export declare function buildAssetFilterClauses(filters: SearchAssetFilters | undefined): string[];
20
+ /**
21
+ * Escape a value for use inside a `filter_by` clause. Typesense supports
22
+ * backtick-quoted strings to handle special characters; for the asset
23
+ * filter surface (tag names, file types, IDs) we keep escaping minimal —
24
+ * if the value contains `,`, `]`, ``` ` ```, or whitespace we wrap in
25
+ * backticks. Plain identifiers pass through.
26
+ */
27
+ export declare function escapeFilterValue(value: string): string;
28
+ /**
29
+ * Join filter clauses with ` && `. Empty array → empty string.
30
+ * Exposed for tests so they can validate the output as a plain string.
31
+ */
32
+ export declare function joinFilterClauses(clauses: ReadonlyArray<string>): string;
33
+ //# sourceMappingURL=filter-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-builder.d.ts","sourceRoot":"","sources":["../../../src/tools/search/filter-builder.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEjE,yEAAyE;AACzE,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,UAAU,GAAG,CAAC,GAAG,CAAC,CAGvD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CASnE;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,kBAAkB,GAAG,SAAS,GACtC,MAAM,EAAE,CAsDV;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMvD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,MAAM,CAExE"}
@@ -0,0 +1,111 @@
1
+ // ── Typesense filter_by builder ──────────────────────────────────────
2
+ // Translates the typed `SearchAssetFilters` envelope into the literal
3
+ // `filter_by` string Typesense expects. Clause grammar is documented in
4
+ // `docs/typesense-filter-schema.md`.
5
+ /** Convert a `Visibility` enum value to the numeric Typesense column. */
6
+ export function visibilityToNumber(v) {
7
+ if (v === 'public' || v === 0)
8
+ return 0;
9
+ return 1;
10
+ }
11
+ /**
12
+ * Coerce a date bound (ISO-8601 string or unix-second integer) to a unix
13
+ * timestamp in seconds. Returns null if the input is unparseable.
14
+ */
15
+ export function toUnixSeconds(bound) {
16
+ if (typeof bound === 'number') {
17
+ if (!Number.isFinite(bound) || bound < 0)
18
+ return null;
19
+ return Math.floor(bound);
20
+ }
21
+ // ISO-8601 or any Date.parse-recognised string
22
+ const ms = Date.parse(bound);
23
+ if (Number.isNaN(ms))
24
+ return null;
25
+ return Math.floor(ms / 1000);
26
+ }
27
+ /**
28
+ * Build the `filter_by` clauses for the asset filters. Returns an array of
29
+ * clause strings (each already wrapped in parens where needed) — the
30
+ * caller joins them with ` && ` and prepends the workspace clause via the
31
+ * wrapper.
32
+ *
33
+ * Ordering and grammar match the Admin-Frontend
34
+ * `mixins/search-common-functions.js` builders. See
35
+ * `docs/typesense-filter-schema.md` for the full citation set.
36
+ */
37
+ export function buildAssetFilterClauses(filters) {
38
+ if (filters === undefined)
39
+ return [];
40
+ const clauses = [];
41
+ if (filters.tags !== undefined && filters.tags.length > 0) {
42
+ // Use the `tags:=[a,b,c]` array shorthand which Typesense interprets
43
+ // as include-any-of. Avoids the backslash-escape used by the frontend
44
+ // for exact match — semantically equivalent for non-special tag names.
45
+ const list = filters.tags
46
+ .map((t) => escapeFilterValue(t))
47
+ .join(',');
48
+ clauses.push(`tags:=[${list}]`);
49
+ }
50
+ if (filters.file_type !== undefined && filters.file_type.length > 0) {
51
+ const list = filters.file_type
52
+ .map((t) => escapeFilterValue(t))
53
+ .join(',');
54
+ clauses.push(`file_type:=[${list}]`);
55
+ }
56
+ if (filters.visibility !== undefined) {
57
+ clauses.push(`visibility:=${visibilityToNumber(filters.visibility)}`);
58
+ }
59
+ if (filters.collection_ids !== undefined && filters.collection_ids.length > 0) {
60
+ const list = filters.collection_ids
61
+ .map((id) => escapeFilterValue(String(id)))
62
+ .join(',');
63
+ clauses.push(`collection_ids:=[${list}]`);
64
+ }
65
+ if (filters.category_id !== undefined) {
66
+ clauses.push(`category_id:=${escapeFilterValue(String(filters.category_id))}`);
67
+ }
68
+ if (filters.created_after !== undefined) {
69
+ const ts = toUnixSeconds(filters.created_after);
70
+ if (ts !== null)
71
+ clauses.push(`created_at:>=${ts}`);
72
+ }
73
+ if (filters.created_before !== undefined) {
74
+ const ts = toUnixSeconds(filters.created_before);
75
+ if (ts !== null)
76
+ clauses.push(`created_at:<=${ts}`);
77
+ }
78
+ if (filters.modified_after !== undefined) {
79
+ const ts = toUnixSeconds(filters.modified_after);
80
+ if (ts !== null)
81
+ clauses.push(`modified_at:>=${ts}`);
82
+ }
83
+ if (filters.modified_before !== undefined) {
84
+ const ts = toUnixSeconds(filters.modified_before);
85
+ if (ts !== null)
86
+ clauses.push(`modified_at:<=${ts}`);
87
+ }
88
+ return clauses;
89
+ }
90
+ /**
91
+ * Escape a value for use inside a `filter_by` clause. Typesense supports
92
+ * backtick-quoted strings to handle special characters; for the asset
93
+ * filter surface (tag names, file types, IDs) we keep escaping minimal —
94
+ * if the value contains `,`, `]`, ``` ` ```, or whitespace we wrap in
95
+ * backticks. Plain identifiers pass through.
96
+ */
97
+ export function escapeFilterValue(value) {
98
+ if (/[\s,\]`\\]/.test(value)) {
99
+ const escaped = value.replace(/`/g, '\\`');
100
+ return '`' + escaped + '`';
101
+ }
102
+ return value;
103
+ }
104
+ /**
105
+ * Join filter clauses with ` && `. Empty array → empty string.
106
+ * Exposed for tests so they can validate the output as a plain string.
107
+ */
108
+ export function joinFilterClauses(clauses) {
109
+ return clauses.filter((c) => c.length > 0).join(' && ');
110
+ }
111
+ //# sourceMappingURL=filter-builder.js.map