octocms 0.4.12 → 0.4.13

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 (58) hide show
  1. package/dist/admin/actions/entries.js +46 -45
  2. package/dist/admin/actions/entries.js.map +1 -1
  3. package/dist/admin/actions/files.d.ts +5 -0
  4. package/dist/admin/actions/files.d.ts.map +1 -1
  5. package/dist/admin/actions/files.js +48 -9
  6. package/dist/admin/actions/files.js.map +1 -1
  7. package/dist/admin/actions/search.js +2 -2
  8. package/dist/admin/actions/search.js.map +1 -1
  9. package/dist/agent/embedder.js +1 -1
  10. package/dist/agent/embedder.js.map +1 -1
  11. package/dist/agent/index.cjs +118 -41
  12. package/dist/agent/index.cjs.map +1 -1
  13. package/dist/agent/tools/index.js +1 -1
  14. package/dist/agent/tools/index.js.map +1 -1
  15. package/dist/chunk-NAHOP7TG.js +7 -0
  16. package/dist/{chunk-L27J3XWV.js.map → chunk-NAHOP7TG.js.map} +1 -1
  17. package/dist/{chunk-TG624CCO.js → chunk-ZYUK2J5L.js} +4 -43
  18. package/dist/chunk-ZYUK2J5L.js.map +1 -0
  19. package/dist/cli/index.js +6 -6
  20. package/dist/cli/lib/codegen.d.ts +2 -8
  21. package/dist/cli/lib/codegen.d.ts.map +1 -1
  22. package/dist/cli/lib/codegen.js +11 -18
  23. package/dist/cli/lib/codegen.js.map +1 -1
  24. package/dist/cli/lib/project.js +2 -41
  25. package/dist/cli/lib/project.js.map +1 -1
  26. package/dist/components/FormReferenceField.d.ts.map +1 -1
  27. package/dist/components/FormReferenceField.js +1 -1
  28. package/dist/components/FormReferenceField.js.map +1 -1
  29. package/dist/{embeddingsGen-7MXSZQ43.js → embeddingsGen-FXWCPGB7.js} +39 -8
  30. package/dist/embeddingsGen-FXWCPGB7.js.map +1 -0
  31. package/dist/index.cjs +88 -68
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/{init-N6K464EZ.js → init-KWH66PKY.js} +2 -2
  34. package/dist/lib/localReader.d.ts +13 -0
  35. package/dist/lib/localReader.d.ts.map +1 -1
  36. package/dist/lib/localReader.js +20 -0
  37. package/dist/lib/localReader.js.map +1 -1
  38. package/dist/lib/publicSearchIndex.d.ts.map +1 -1
  39. package/dist/lib/publicSearchIndex.js +2 -2
  40. package/dist/lib/publicSearchIndex.js.map +1 -1
  41. package/dist/query.cjs +88 -68
  42. package/dist/query.cjs.map +1 -1
  43. package/dist/types.cjs.map +1 -1
  44. package/dist/types.d.ts.map +1 -1
  45. package/dist/{typesGen-EYRZAA56.js → typesGen-MFAL3B4V.js} +32 -6
  46. package/dist/typesGen-MFAL3B4V.js.map +1 -0
  47. package/dist/{validate-6AFW6U2X.js → validate-AAW4IGWD.js} +2 -2
  48. package/package.json +1 -1
  49. package/dist/admin/consts.d.ts +0 -3
  50. package/dist/admin/consts.d.ts.map +0 -1
  51. package/dist/admin/consts.js +0 -24
  52. package/dist/admin/consts.js.map +0 -1
  53. package/dist/chunk-L27J3XWV.js +0 -7
  54. package/dist/chunk-TG624CCO.js.map +0 -1
  55. package/dist/embeddingsGen-7MXSZQ43.js.map +0 -1
  56. package/dist/typesGen-EYRZAA56.js.map +0 -1
  57. /package/dist/{init-N6K464EZ.js.map → init-KWH66PKY.js.map} +0 -0
  58. /package/dist/{validate-6AFW6U2X.js.map → validate-AAW4IGWD.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../cli/lib/project.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'fs';\nimport { dirname, join, resolve } from 'path';\n\n/**\n * Walk up from `startDir` until we find a directory containing a\n * `next.config.ts` that includes `withOctoCMS`.\n * Returns the absolute path to the project root.\n */\nexport function resolveProjectRoot(startDir?: string): string {\n let dir = startDir ?? process.cwd();\n for (;;) {\n const nextConfigPath = join(dir, 'next.config.ts');\n if (existsSync(nextConfigPath) && readFileSync(nextConfigPath, 'utf8').includes('withOctoCMS')) {\n return dir;\n }\n const parent = dirname(dir);\n if (parent === dir) {\n throw new Error('Could not find next.config.ts with withOctoCMS — are you inside an OctoCMS project?');\n }\n dir = parent;\n }\n}\n\n/**\n * Load the CMS config from a project root. Uses `jiti` to handle TypeScript\n * files and tsconfig path aliases at runtime.\n */\nexport async function loadProjectConfig(projectRoot: string) {\n // Dynamic import so jiti stays an optional peer when published\n const { createJiti } = await import('jiti');\n // In this monorepo, octocms/ lives at the project root. When installed as an npm\n // package it lives in node_modules/octocms — jiti resolves it normally in that case.\n const alias: Record<string, string> = {};\n const localOctocms = resolve(projectRoot, 'octocms');\n if (existsSync(localOctocms)) {\n alias['octocms/'] = localOctocms + '/';\n }\n const jiti = createJiti(join(projectRoot, '__cli_loader__.ts'), {\n fsCache: false,\n moduleCache: false,\n alias,\n });\n const mod = (await jiti.import(join(projectRoot, 'cms', 'octocms.config.ts'), {\n default: true,\n try: true,\n })) as Record<string, unknown> | undefined;\n if (!mod || !mod.configOctoCMS) {\n throw new Error('cms/octocms.config.ts must export a `configOctoCMS` object (use defineConfig())');\n }\n return mod.configOctoCMS as import('../../types').Config;\n}\n\n/**\n * Load the COLLECTIONS array from `octocms/admin/consts.ts`.\n */\nexport async function loadCollections(projectRoot: string): Promise<readonly string[]> {\n const { createJiti } = await import('jiti');\n const alias: Record<string, string> = {};\n const localOctocms = resolve(projectRoot, 'octocms');\n if (existsSync(localOctocms)) {\n alias['octocms/'] = localOctocms + '/';\n }\n const jiti = createJiti(join(projectRoot, '__cli_loader__.ts'), {\n fsCache: false,\n moduleCache: false,\n alias,\n });\n const mod = (await jiti.import(join(projectRoot, 'octocms/admin/consts.ts'), {\n default: true,\n try: true,\n })) as Record<string, unknown> | undefined;\n if (!mod || !Array.isArray(mod.COLLECTIONS)) {\n throw new Error('Could not load COLLECTIONS from octocms/admin/consts.ts');\n }\n return mod.COLLECTIONS as readonly string[];\n}\n\n/**\n * Load FIELD_TYPES from `octocms/admin/consts.ts`.\n */\nexport async function loadFieldTypes(projectRoot: string): Promise<readonly string[]> {\n const { createJiti } = await import('jiti');\n const alias: Record<string, string> = {};\n const localOctocms = resolve(projectRoot, 'octocms');\n if (existsSync(localOctocms)) {\n alias['octocms/'] = localOctocms + '/';\n }\n const jiti = createJiti(join(projectRoot, '__cli_loader__.ts'), {\n fsCache: false,\n moduleCache: false,\n alias,\n });\n const mod = (await jiti.import(join(projectRoot, 'octocms/admin/consts.ts'), {\n default: true,\n try: true,\n })) as Record<string, unknown> | undefined;\n if (!mod || !Array.isArray(mod.FIELD_TYPES)) {\n throw new Error('Could not load FIELD_TYPES from octocms/admin/consts.ts');\n }\n return mod.FIELD_TYPES as readonly string[];\n}\n"],"mappings":";AAAA,SAAS,YAAY,oBAAoB;AACzC,SAAS,SAAS,MAAM,eAAe;AAOhC,SAAS,mBAAmB,UAA2B;AAC5D,MAAI,MAAM,8BAAY,QAAQ,IAAI;AAClC,aAAS;AACP,UAAM,iBAAiB,KAAK,KAAK,gBAAgB;AACjD,QAAI,WAAW,cAAc,KAAK,aAAa,gBAAgB,MAAM,EAAE,SAAS,aAAa,GAAG;AAC9F,aAAO;AAAA,IACT;AACA,UAAM,SAAS,QAAQ,GAAG;AAC1B,QAAI,WAAW,KAAK;AAClB,YAAM,IAAI,MAAM,0FAAqF;AAAA,IACvG;AACA,UAAM;AAAA,EACR;AACF;AAMA,eAAsB,kBAAkB,aAAqB;AAE3D,QAAM,EAAE,WAAW,IAAI,MAAM,OAAO,MAAM;AAG1C,QAAM,QAAgC,CAAC;AACvC,QAAM,eAAe,QAAQ,aAAa,SAAS;AACnD,MAAI,WAAW,YAAY,GAAG;AAC5B,UAAM,UAAU,IAAI,eAAe;AAAA,EACrC;AACA,QAAM,OAAO,WAAW,KAAK,aAAa,mBAAmB,GAAG;AAAA,IAC9D,SAAS;AAAA,IACT,aAAa;AAAA,IACb;AAAA,EACF,CAAC;AACD,QAAM,MAAO,MAAM,KAAK,OAAO,KAAK,aAAa,OAAO,mBAAmB,GAAG;AAAA,IAC5E,SAAS;AAAA,IACT,KAAK;AAAA,EACP,CAAC;AACD,MAAI,CAAC,OAAO,CAAC,IAAI,eAAe;AAC9B,UAAM,IAAI,MAAM,iFAAiF;AAAA,EACnG;AACA,SAAO,IAAI;AACb;AAKA,eAAsB,gBAAgB,aAAiD;AACrF,QAAM,EAAE,WAAW,IAAI,MAAM,OAAO,MAAM;AAC1C,QAAM,QAAgC,CAAC;AACvC,QAAM,eAAe,QAAQ,aAAa,SAAS;AACnD,MAAI,WAAW,YAAY,GAAG;AAC5B,UAAM,UAAU,IAAI,eAAe;AAAA,EACrC;AACA,QAAM,OAAO,WAAW,KAAK,aAAa,mBAAmB,GAAG;AAAA,IAC9D,SAAS;AAAA,IACT,aAAa;AAAA,IACb;AAAA,EACF,CAAC;AACD,QAAM,MAAO,MAAM,KAAK,OAAO,KAAK,aAAa,yBAAyB,GAAG;AAAA,IAC3E,SAAS;AAAA,IACT,KAAK;AAAA,EACP,CAAC;AACD,MAAI,CAAC,OAAO,CAAC,MAAM,QAAQ,IAAI,WAAW,GAAG;AAC3C,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AACA,SAAO,IAAI;AACb;AAKA,eAAsB,eAAe,aAAiD;AACpF,QAAM,EAAE,WAAW,IAAI,MAAM,OAAO,MAAM;AAC1C,QAAM,QAAgC,CAAC;AACvC,QAAM,eAAe,QAAQ,aAAa,SAAS;AACnD,MAAI,WAAW,YAAY,GAAG;AAC5B,UAAM,UAAU,IAAI,eAAe;AAAA,EACrC;AACA,QAAM,OAAO,WAAW,KAAK,aAAa,mBAAmB,GAAG;AAAA,IAC9D,SAAS;AAAA,IACT,aAAa;AAAA,IACb;AAAA,EACF,CAAC;AACD,QAAM,MAAO,MAAM,KAAK,OAAO,KAAK,aAAa,yBAAyB,GAAG;AAAA,IAC3E,SAAS;AAAA,IACT,KAAK;AAAA,EACP,CAAC;AACD,MAAI,CAAC,OAAO,CAAC,MAAM,QAAQ,IAAI,WAAW,GAAG;AAC3C,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AACA,SAAO,IAAI;AACb;","names":[]}
1
+ {"version":3,"sources":["../../../cli/lib/project.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'fs';\nimport { dirname, join, resolve } from 'path';\n\n/**\n * Walk up from `startDir` until we find a directory containing a\n * `next.config.ts` that includes `withOctoCMS`.\n * Returns the absolute path to the project root.\n */\nexport function resolveProjectRoot(startDir?: string): string {\n let dir = startDir ?? process.cwd();\n for (;;) {\n const nextConfigPath = join(dir, 'next.config.ts');\n if (existsSync(nextConfigPath) && readFileSync(nextConfigPath, 'utf8').includes('withOctoCMS')) {\n return dir;\n }\n const parent = dirname(dir);\n if (parent === dir) {\n throw new Error('Could not find next.config.ts with withOctoCMS — are you inside an OctoCMS project?');\n }\n dir = parent;\n }\n}\n\n/**\n * Load the CMS config from a project root. Uses `jiti` to handle TypeScript\n * files and tsconfig path aliases at runtime.\n */\nexport async function loadProjectConfig(projectRoot: string) {\n // Dynamic import so jiti stays an optional peer when published\n const { createJiti } = await import('jiti');\n // In this monorepo, octocms/ lives at the project root. When installed as an npm\n // package it lives in node_modules/octocms — jiti resolves it normally in that case.\n const alias: Record<string, string> = {};\n const localOctocms = resolve(projectRoot, 'octocms');\n if (existsSync(localOctocms)) {\n alias['octocms/'] = localOctocms + '/';\n }\n const jiti = createJiti(join(projectRoot, '__cli_loader__.ts'), {\n fsCache: false,\n moduleCache: false,\n alias,\n });\n const mod = (await jiti.import(join(projectRoot, 'cms', 'octocms.config.ts'), {\n default: true,\n try: true,\n })) as Record<string, unknown> | undefined;\n if (!mod || !mod.configOctoCMS) {\n throw new Error('cms/octocms.config.ts must export a `configOctoCMS` object (use defineConfig())');\n }\n return mod.configOctoCMS as import('../../types').Config;\n}\n\n/**\n * Derive the collection name list from the user's config.\n *\n * Collection identity lives in `cms/schema.json` (mirrored into\n * `cms/__generated__/schema.ts`); the package has no business hard-coding\n * names. This mirrors `regenerateAll`'s pattern: `Object.keys(config.collections)`.\n */\nexport async function loadCollections(projectRoot: string): Promise<readonly string[]> {\n const config = await loadProjectConfig(projectRoot);\n return Object.keys(config.collections);\n}\n"],"mappings":";AAAA,SAAS,YAAY,oBAAoB;AACzC,SAAS,SAAS,MAAM,eAAe;AAOhC,SAAS,mBAAmB,UAA2B;AAC5D,MAAI,MAAM,8BAAY,QAAQ,IAAI;AAClC,aAAS;AACP,UAAM,iBAAiB,KAAK,KAAK,gBAAgB;AACjD,QAAI,WAAW,cAAc,KAAK,aAAa,gBAAgB,MAAM,EAAE,SAAS,aAAa,GAAG;AAC9F,aAAO;AAAA,IACT;AACA,UAAM,SAAS,QAAQ,GAAG;AAC1B,QAAI,WAAW,KAAK;AAClB,YAAM,IAAI,MAAM,0FAAqF;AAAA,IACvG;AACA,UAAM;AAAA,EACR;AACF;AAMA,eAAsB,kBAAkB,aAAqB;AAE3D,QAAM,EAAE,WAAW,IAAI,MAAM,OAAO,MAAM;AAG1C,QAAM,QAAgC,CAAC;AACvC,QAAM,eAAe,QAAQ,aAAa,SAAS;AACnD,MAAI,WAAW,YAAY,GAAG;AAC5B,UAAM,UAAU,IAAI,eAAe;AAAA,EACrC;AACA,QAAM,OAAO,WAAW,KAAK,aAAa,mBAAmB,GAAG;AAAA,IAC9D,SAAS;AAAA,IACT,aAAa;AAAA,IACb;AAAA,EACF,CAAC;AACD,QAAM,MAAO,MAAM,KAAK,OAAO,KAAK,aAAa,OAAO,mBAAmB,GAAG;AAAA,IAC5E,SAAS;AAAA,IACT,KAAK;AAAA,EACP,CAAC;AACD,MAAI,CAAC,OAAO,CAAC,IAAI,eAAe;AAC9B,UAAM,IAAI,MAAM,iFAAiF;AAAA,EACnG;AACA,SAAO,IAAI;AACb;AASA,eAAsB,gBAAgB,aAAiD;AACrF,QAAM,SAAS,MAAM,kBAAkB,WAAW;AAClD,SAAO,OAAO,KAAK,OAAO,WAAW;AACvC;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"FormReferenceField.d.ts","sourceRoot":"","sources":["../../components/FormReferenceField.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAU,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAYnE,KAAK,uBAAuB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yFAAyF;IACzF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,oBAAoB,CAAC;CAClC,CAAC;AA+UF,QAAA,MAAM,kBAAkB,GAAI,kGAUzB,uBAAuB,4CA+RzB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"FormReferenceField.d.ts","sourceRoot":"","sources":["../../components/FormReferenceField.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAU,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAYnE,KAAK,uBAAuB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yFAAyF;IACzF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,oBAAoB,CAAC;CAClC,CAAC;AA+UF,QAAA,MAAM,kBAAkB,GAAI,kGAUzB,uBAAuB,4CAkSzB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -314,7 +314,7 @@ const FormReferenceField = ({
314
314
  if (collection) {
315
315
  return [collection];
316
316
  }
317
- return Object.keys(config.collections).filter((c) => c !== "homePage");
317
+ return Object.entries(config.collections).filter(([, col]) => col == null ? void 0 : col.hasMany).map(([name2]) => name2);
318
318
  }, [reference, collection, config.collections]);
319
319
  const cardinality = (reference == null ? void 0 : reference.cardinality) || "many";
320
320
  const maxItems = cardinality === "one" ? 1 : reference == null ? void 0 : reference.max;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../components/FormReferenceField.tsx"],"sourcesContent":["'use client';\n\nimport { useQueryClient } from '@tanstack/react-query';\nimport { GripVertical, Pencil, Plus, Search, Trash2 } from 'lucide-react';\nimport React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { getEntryList } from '../admin/actions/entries';\nimport { useNewFile } from '../admin/query/hooks/useNewFile';\nimport { queryKeys } from '../admin/query/keys';\nimport type { Config, ReferenceFieldConfig } from '../admin/types';\nimport { useConfig } from '../hooks/useConfig';\nimport { useEntryStack } from '../hooks/useEntryStack';\n\nimport { toast } from '../hooks/useToast';\nimport { toContentPath, toReferenceKey } from '../lib/referenceKeys';\nimport { cn } from '../lib/utils';\nimport type { EntryListItem, ReferenceItem } from '../types';\nimport { FieldHintAndError } from './FieldHintAndError';\nimport { Button } from './ui/button';\nimport { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from './ui';\n\ntype FormReferenceFieldProps = {\n label: string;\n name: string;\n value: string;\n required?: boolean;\n hint?: string;\n /** Set when submit-time validation fails (takes precedence over inline min/max hint). */\n error?: string;\n onClearError?: (name: string) => void;\n /** @deprecated Use `reference` config instead */\n collection?: string;\n reference?: ReferenceFieldConfig;\n};\n\n// ─── Drag-and-drop sortable list ─────────────────────────────────────────────\n\nconst DraggableItem = ({\n item,\n index,\n onRemove,\n onEdit,\n onDragStart,\n onDragOver,\n onDragEnd,\n onDrop,\n}: {\n item: ReferenceItem;\n index: number;\n onRemove: (index: number) => void;\n onEdit?: (item: ReferenceItem) => void;\n onDragStart: (index: number) => void;\n onDragOver: (e: React.DragEvent, index: number) => void;\n onDragEnd: () => void;\n onDrop: (index: number) => void;\n}) => {\n const config = useConfig();\n const collectionLabel = config.collections[item.type as keyof Config['collections']]?.label || item.type;\n\n return (\n <div\n draggable\n onDragStart={() => onDragStart(index)}\n onDragOver={(e) => onDragOver(e, index)}\n onDragEnd={onDragEnd}\n onDrop={() => onDrop(index)}\n className=\"flex items-center gap-2 rounded-lg border border-border bg-card px-3 py-2 transition-colors hover:bg-muted/50 cursor-grab active:cursor-grabbing\"\n >\n <GripVertical className=\"w-4 h-4 text-muted-foreground flex-none\" />\n <button type=\"button\" onClick={() => onEdit?.(item)} className=\"flex-1 min-w-0 text-left cursor-pointer group\">\n <div className=\"text-sm font-medium truncate group-hover:text-primary transition-colors\">{item.title}</div>\n <div className=\"text-xs text-muted-foreground\">{collectionLabel}</div>\n </button>\n <button\n type=\"button\"\n onClick={() => onEdit?.(item)}\n className=\"flex-none p-1 rounded hover:bg-primary/10 text-muted-foreground hover:text-primary transition-colors\"\n title=\"Edit inline\"\n >\n <Pencil className=\"w-3.5 h-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => onRemove(index)}\n className=\"flex-none p-1 rounded hover:bg-destructive/10 text-muted-foreground hover:text-destructive transition-colors\"\n >\n <Trash2 className=\"w-3.5 h-3.5\" />\n </button>\n </div>\n );\n};\n\n// ─── Add existing content modal ──────────────────────────────────────────────\n\nconst AddExistingModal = ({\n open,\n onOpenChange,\n allowedCollections,\n selectedKeys,\n onSelect,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n allowedCollections: string[];\n selectedKeys: Set<string>;\n onSelect: (items: ReferenceItem[]) => void;\n}) => {\n const config = useConfig();\n const queryClient = useQueryClient();\n const [entries, setEntries] = useState<EntryListItem[]>([]);\n const [search, setSearch] = useState('');\n const [typeFilter, setTypeFilter] = useState<string>('all');\n const [checked, setChecked] = useState<Set<string>>(new Set());\n const [isLoading, setIsLoading] = useState(false);\n const searchRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n if (!open) return;\n setIsLoading(true);\n setChecked(new Set());\n setSearch('');\n setTypeFilter('all');\n\n const load = async () => {\n try {\n const allEntries: EntryListItem[] = [];\n for (const col of allowedCollections) {\n const list = await queryClient.ensureQueryData({\n queryKey: queryKeys.entries.list(col),\n queryFn: () => getEntryList(col),\n });\n allEntries.push(...list);\n }\n setEntries(allEntries);\n } finally {\n setIsLoading(false);\n }\n };\n\n void load();\n }, [open, allowedCollections, queryClient]);\n\n // Focus search input when modal opens\n useEffect(() => {\n if (open) {\n setTimeout(() => searchRef.current?.focus(), 100);\n }\n }, [open]);\n\n const filtered = useMemo(() => {\n let result = entries;\n if (typeFilter !== 'all') {\n result = result.filter((e) => e.type === typeFilter);\n }\n if (search.trim()) {\n const q = search.toLowerCase();\n result = result.filter((e) => e.title.toLowerCase().includes(q) || e.type.toLowerCase().includes(q));\n }\n return result;\n }, [entries, typeFilter, search]);\n\n const toggleCheck = (referenceKey: string) => {\n setChecked((prev) => {\n const next = new Set(prev);\n if (next.has(referenceKey)) {\n next.delete(referenceKey);\n } else {\n next.add(referenceKey);\n }\n return next;\n });\n };\n\n const handleConfirm = () => {\n const selected = entries\n .filter((e) => checked.has(toReferenceKey(e.path)))\n .map((e) => ({ type: e.type, id: e.id, path: toReferenceKey(e.path), title: e.title }));\n onSelect(selected);\n onOpenChange(false);\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-w-2xl max-h-[80vh] flex flex-col\">\n <DialogHeader>\n <DialogTitle>Add existing content</DialogTitle>\n </DialogHeader>\n\n <div className=\"flex gap-2\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-2.5 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground\" />\n <input\n ref={searchRef}\n type=\"text\"\n placeholder=\"Filter entries...\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"w-full pl-9 pr-3 py-2 text-sm rounded-md border border-input bg-background\"\n />\n </div>\n {allowedCollections.length > 1 && (\n <select\n value={typeFilter}\n onChange={(e) => setTypeFilter(e.target.value)}\n className=\"px-3 py-2 text-sm rounded-md border border-input bg-background\"\n >\n <option value=\"all\">All types</option>\n {allowedCollections.map((col) => (\n <option key={col} value={col}>\n {config.collections[col as keyof Config['collections']]?.label || col}\n </option>\n ))}\n </select>\n )}\n </div>\n\n <div className=\"flex-1 overflow-y-auto min-h-0 border border-border rounded-md\">\n {isLoading ? (\n <div className=\"p-8 text-center text-muted-foreground text-sm\">Loading...</div>\n ) : filtered.length === 0 ? (\n <div className=\"p-8 text-center text-muted-foreground text-sm\">No entries found</div>\n ) : (\n <div className=\"divide-y divide-border\">\n {filtered.map((entry) => {\n const referenceKey = toReferenceKey(entry.path);\n const alreadyAdded = selectedKeys.has(referenceKey);\n const isChecked = checked.has(referenceKey);\n const collectionLabel =\n config.collections[entry.type as keyof Config['collections']]?.label || entry.type;\n\n return (\n <label\n key={entry.path}\n className={cn(\n 'flex items-center gap-3 px-3 py-2.5 cursor-pointer transition-colors',\n alreadyAdded ? 'opacity-40 cursor-not-allowed' : 'hover:bg-muted/50',\n isChecked && !alreadyAdded && 'bg-primary/5',\n )}\n >\n <input\n type=\"checkbox\"\n checked={isChecked || alreadyAdded}\n disabled={alreadyAdded}\n onChange={() => toggleCheck(referenceKey)}\n className=\"rounded border-input\"\n />\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium truncate\">{entry.title}</div>\n <div className=\"text-xs text-muted-foreground\">{collectionLabel}</div>\n </div>\n {alreadyAdded && <span className=\"text-xs text-muted-foreground flex-none\">Already added</span>}\n </label>\n );\n })}\n </div>\n )}\n </div>\n\n <DialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => onOpenChange(false)}>\n Cancel\n </Button>\n <Button type=\"button\" onClick={handleConfirm} disabled={checked.size === 0}>\n Add {checked.size > 0 ? `(${checked.size})` : ''}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n};\n\n// ─── Create new entry modal ──────────────────────────────────────────────────\n\nconst CreateNewModal = ({\n open,\n onOpenChange,\n allowedCollections,\n onCreate,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n allowedCollections: string[];\n onCreate: (item: ReferenceItem) => void;\n}) => {\n const config = useConfig();\n const [selectedType, setSelectedType] = useState(allowedCollections[0] || '');\n const { mutateAsync: createEntry, isPending } = useNewFile();\n\n useEffect(() => {\n if (open && allowedCollections.length > 0) {\n setSelectedType(allowedCollections[0]);\n }\n }, [open, allowedCollections]);\n\n const handleCreate = () => {\n void createEntry(selectedType)\n .then((result) => {\n const filePath = result.path;\n const id = filePath.replace(`${config.contentFolder}/${selectedType}/`, '').replace('.json', '');\n const referenceKey = toReferenceKey(filePath);\n\n const item: ReferenceItem = {\n type: selectedType,\n id,\n path: referenceKey,\n title: `New ${config.collections[selectedType as keyof Config['collections']]?.label || selectedType}`,\n };\n\n onCreate(item);\n onOpenChange(false);\n toast({\n title: `Created new ${config.collections[selectedType as keyof Config['collections']]?.label || selectedType}`,\n variant: 'success',\n });\n })\n .catch((e: Error) => {\n toast({ title: e.message, variant: 'destructive' });\n });\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-w-sm\">\n <DialogHeader>\n <DialogTitle>Create new entry</DialogTitle>\n </DialogHeader>\n\n {allowedCollections.length > 1 ? (\n <div>\n <label htmlFor=\"create-ref-type-select\" className=\"block text-sm font-medium mb-1.5\">\n Content type\n </label>\n <select\n id=\"create-ref-type-select\"\n value={selectedType}\n onChange={(e) => setSelectedType(e.target.value)}\n className=\"w-full px-3 py-2 text-sm rounded-md border border-input bg-background\"\n >\n {allowedCollections.map((col) => (\n <option key={col} value={col}>\n {config.collections[col as keyof Config['collections']]?.label || col}\n </option>\n ))}\n </select>\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground\">\n Create a new{' '}\n <strong>{config.collections[selectedType as keyof Config['collections']]?.label || selectedType}</strong>{' '}\n entry and add it to this field.\n </p>\n )}\n\n <DialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => onOpenChange(false)}>\n Cancel\n </Button>\n <Button type=\"button\" onClick={handleCreate} disabled={isPending}>\n {isPending ? 'Creating...' : 'Create & add'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n};\n\n// ─── Main component ──────────────────────────────────────────────────────────\n\nconst FormReferenceField = ({\n label,\n name,\n value = '[]',\n required,\n hint,\n error: submitError,\n onClearError,\n collection,\n reference,\n}: FormReferenceFieldProps) => {\n const config = useConfig();\n const queryClient = useQueryClient();\n const [items, setItems] = useState<ReferenceItem[]>([]);\n const [isExistingOpen, setIsExistingOpen] = useState(false);\n const [isCreateOpen, setIsCreateOpen] = useState(false);\n const [dragIndex, setDragIndex] = useState<number | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const { pushEntry, refreshTick } = useEntryStack();\n // True after the first parse-from-`value` pass; subsequent runs (driven by refreshTick)\n // patch titles on the live items list and drop deleted entries instead of re-parsing `value`.\n const loadedRef = useRef(false);\n\n // Resolve allowed collections from new or legacy config\n const allowedCollections = useMemo(() => {\n if (reference?.collections && reference.collections.length > 0) {\n return reference.collections as string[];\n }\n if (collection) {\n return [collection] as string[];\n }\n // Default: all non-media collections\n return Object.keys(config.collections).filter((c) => c !== 'homePage');\n }, [reference, collection, config.collections]);\n\n const cardinality = reference?.cardinality || 'many';\n const maxItems = cardinality === 'one' ? 1 : reference?.max;\n const minItems = cardinality === 'one' ? (required ? 1 : 0) : Math.max(reference?.min ?? 0, required ? 1 : 0);\n\n // Initial load (parse `value`) and subsequent refresh ticks (patch titles, drop deleted).\n useEffect(() => {\n let cancelled = false;\n\n const fetchEntryMap = async () => {\n const allEntries: EntryListItem[] = [];\n for (const col of allowedCollections) {\n const list = await queryClient.ensureQueryData({\n queryKey: queryKeys.entries.list(col),\n queryFn: () => getEntryList(col),\n });\n allEntries.push(...list);\n }\n return new Map(allEntries.map((e) => [toReferenceKey(e.path), e]));\n };\n\n const initialLoad = async () => {\n let parsedPaths: string[] = [];\n try {\n const parsed = JSON.parse(value);\n if (cardinality === 'one') {\n parsedPaths = typeof parsed === 'string' && parsed ? [parsed] : Array.isArray(parsed) ? parsed : [];\n } else {\n parsedPaths = Array.isArray(parsed) ? parsed : [];\n }\n } catch (_e) {\n if (cardinality === 'one' && value && value !== '[]') {\n parsedPaths = [value];\n }\n }\n\n if (parsedPaths.length === 0) {\n if (cancelled) return;\n setItems([]);\n setIsLoading(false);\n loadedRef.current = true;\n return;\n }\n\n const entryMap = await fetchEntryMap();\n if (cancelled) return;\n\n const resolved: ReferenceItem[] = parsedPaths.map((p) => {\n const rawValue = String(p ?? '');\n const normalizedKey = rawValue.includes('/') ? '' : rawValue.endsWith('.json') ? rawValue : `${rawValue}.json`;\n if (!normalizedKey || !toContentPath(normalizedKey)) {\n return { type: '', id: rawValue, path: rawValue, title: rawValue };\n }\n\n const entry = entryMap.get(normalizedKey);\n if (entry) {\n return { type: entry.type, id: entry.id, path: normalizedKey, title: entry.title };\n }\n\n const fallbackContentPath = toContentPath(normalizedKey);\n const parts = fallbackContentPath\n ? fallbackContentPath.replace('cms/content/', '').replace('.json', '').split('/')\n : [];\n const fallbackId = parts.length > 0 ? parts[parts.length - 1] : normalizedKey.replace('.json', '');\n const fallbackType = parts.length > 0 ? parts[0] : '';\n return { type: fallbackType, id: fallbackId, path: normalizedKey, title: fallbackId };\n });\n\n setItems(resolved);\n setIsLoading(false);\n loadedRef.current = true;\n };\n\n const refreshTitles = async () => {\n const entryMap = await fetchEntryMap();\n if (cancelled) return;\n setItems((prev) =>\n prev\n .map((item): ReferenceItem | null => {\n const entry = entryMap.get(item.path);\n if (entry) return { ...item, title: entry.title, type: entry.type, id: entry.id };\n // Item references a path that's no longer in the allowed collections — treat as deleted.\n return null;\n })\n .filter((x): x is ReferenceItem => x !== null),\n );\n };\n\n if (loadedRef.current) {\n refreshTitles();\n } else {\n initialLoad();\n }\n\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- tick-driven title sync; first parse uses latest `value` once via `loadedRef`\n }, [refreshTick, queryClient, allowedCollections]);\n\n const selectedKeys = useMemo(() => new Set(items.map((i) => i.path)), [items]);\n\n // Serialize value for form submission\n const serializedValue = useMemo(() => {\n const paths = items.map((i) => i.path);\n if (cardinality === 'one') {\n return paths[0] || '';\n }\n return JSON.stringify(paths);\n }, [items, cardinality]);\n\n const handleRemove = useCallback(\n (index: number) => {\n setItems((prev) => prev.filter((_, i) => i !== index));\n onClearError?.(name);\n },\n [name, onClearError],\n );\n\n const handleAddExisting = useCallback(\n (newItems: ReferenceItem[]) => {\n setItems((prev) => {\n const combined = [...prev, ...newItems];\n return combined;\n });\n onClearError?.(name);\n },\n [name, onClearError],\n );\n\n const handleCreate = useCallback(\n (item: ReferenceItem) => {\n setItems((prev) => [...prev, item]);\n onClearError?.(name);\n },\n [name, onClearError],\n );\n\n // Open inline editor for a referenced entry\n const handleEdit = useCallback(\n (item: ReferenceItem) => {\n const contentPath = toContentPath(item.path);\n if (!contentPath) return;\n pushEntry({ id: item.id, type: item.type, path: contentPath, title: item.title });\n },\n [pushEntry],\n );\n\n // Drag and drop handlers\n const handleDragStart = useCallback((index: number) => {\n setDragIndex(index);\n }, []);\n\n const handleDragOver = useCallback((e: React.DragEvent, _index: number) => {\n e.preventDefault();\n }, []);\n\n const handleDragEnd = useCallback(() => {\n setDragIndex(null);\n }, []);\n\n const handleDrop = useCallback(\n (targetIndex: number) => {\n if (dragIndex === null || dragIndex === targetIndex) return;\n\n setItems((prev) => {\n const next = [...prev];\n const [dragged] = next.splice(dragIndex, 1);\n next.splice(targetIndex, 0, dragged);\n return next;\n });\n setDragIndex(null);\n onClearError?.(name);\n },\n [dragIndex, name, onClearError],\n );\n\n const canAdd = maxItems === undefined || items.length < maxItems;\n const validationError =\n minItems !== undefined && minItems > 0 && items.length < minItems\n ? `At least ${minItems} ${minItems === 1 ? 'item' : 'items'} required`\n : null;\n const displayError = submitError ?? validationError;\n\n return (\n <div className=\"mb-6\">\n <div className=\"block text-xs font-medium text-muted-foreground mb-1.5\">\n {label}\n {required && <span className=\"text-destructive ml-1\">*</span>}\n {cardinality === 'one' && <span className=\"text-xs text-muted-foreground ml-2\">(single)</span>}\n {maxItems !== undefined && cardinality === 'many' && (\n <span className=\"text-xs text-muted-foreground ml-2\">\n ({items.length}/{maxItems})\n </span>\n )}\n </div>\n\n {isLoading ? (\n <div className=\"text-sm text-muted-foreground py-4\">Loading references...</div>\n ) : (\n <>\n {/* Selected items list */}\n {items.length > 0 && (\n <div className=\"flex flex-col gap-1.5 mb-3\">\n {items.map((item, i) => (\n <DraggableItem\n key={`${item.path}-${i}`}\n item={item}\n index={i}\n onRemove={handleRemove}\n onEdit={handleEdit}\n onDragStart={handleDragStart}\n onDragOver={handleDragOver}\n onDragEnd={handleDragEnd}\n onDrop={handleDrop}\n />\n ))}\n </div>\n )}\n\n {items.length === 0 && (\n <div className=\"text-sm text-muted-foreground py-4 border border-dashed border-border rounded-lg text-center\">\n No items selected\n </div>\n )}\n\n {/* Action buttons */}\n {canAdd && (\n <div className=\"flex gap-2 mt-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => setIsExistingOpen(true)}>\n <Plus className=\"w-4 h-4\" />\n Add existing\n </Button>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => setIsCreateOpen(true)}>\n <Plus className=\"w-4 h-4\" />\n Create new\n </Button>\n </div>\n )}\n </>\n )}\n\n <FieldHintAndError hint={hint} error={displayError ?? undefined} />\n\n {/* Hidden input for form submission */}\n <input type=\"hidden\" name={name} value={serializedValue} />\n\n {/* Modals */}\n <AddExistingModal\n open={isExistingOpen}\n onOpenChange={setIsExistingOpen}\n allowedCollections={allowedCollections}\n selectedKeys={selectedKeys}\n onSelect={handleAddExisting}\n />\n <CreateNewModal\n open={isCreateOpen}\n onOpenChange={setIsCreateOpen}\n allowedCollections={allowedCollections}\n onCreate={handleCreate}\n />\n </div>\n );\n};\n\nexport default FormReferenceField;\n"],"mappings":"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","names":["_a"]}
1
+ {"version":3,"sources":["../../components/FormReferenceField.tsx"],"sourcesContent":["'use client';\n\nimport { useQueryClient } from '@tanstack/react-query';\nimport { GripVertical, Pencil, Plus, Search, Trash2 } from 'lucide-react';\nimport React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { getEntryList } from '../admin/actions/entries';\nimport { useNewFile } from '../admin/query/hooks/useNewFile';\nimport { queryKeys } from '../admin/query/keys';\nimport type { Config, ReferenceFieldConfig } from '../admin/types';\nimport { useConfig } from '../hooks/useConfig';\nimport { useEntryStack } from '../hooks/useEntryStack';\n\nimport { toast } from '../hooks/useToast';\nimport { toContentPath, toReferenceKey } from '../lib/referenceKeys';\nimport { cn } from '../lib/utils';\nimport type { EntryListItem, ReferenceItem } from '../types';\nimport { FieldHintAndError } from './FieldHintAndError';\nimport { Button } from './ui/button';\nimport { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from './ui';\n\ntype FormReferenceFieldProps = {\n label: string;\n name: string;\n value: string;\n required?: boolean;\n hint?: string;\n /** Set when submit-time validation fails (takes precedence over inline min/max hint). */\n error?: string;\n onClearError?: (name: string) => void;\n /** @deprecated Use `reference` config instead */\n collection?: string;\n reference?: ReferenceFieldConfig;\n};\n\n// ─── Drag-and-drop sortable list ─────────────────────────────────────────────\n\nconst DraggableItem = ({\n item,\n index,\n onRemove,\n onEdit,\n onDragStart,\n onDragOver,\n onDragEnd,\n onDrop,\n}: {\n item: ReferenceItem;\n index: number;\n onRemove: (index: number) => void;\n onEdit?: (item: ReferenceItem) => void;\n onDragStart: (index: number) => void;\n onDragOver: (e: React.DragEvent, index: number) => void;\n onDragEnd: () => void;\n onDrop: (index: number) => void;\n}) => {\n const config = useConfig();\n const collectionLabel = config.collections[item.type as keyof Config['collections']]?.label || item.type;\n\n return (\n <div\n draggable\n onDragStart={() => onDragStart(index)}\n onDragOver={(e) => onDragOver(e, index)}\n onDragEnd={onDragEnd}\n onDrop={() => onDrop(index)}\n className=\"flex items-center gap-2 rounded-lg border border-border bg-card px-3 py-2 transition-colors hover:bg-muted/50 cursor-grab active:cursor-grabbing\"\n >\n <GripVertical className=\"w-4 h-4 text-muted-foreground flex-none\" />\n <button type=\"button\" onClick={() => onEdit?.(item)} className=\"flex-1 min-w-0 text-left cursor-pointer group\">\n <div className=\"text-sm font-medium truncate group-hover:text-primary transition-colors\">{item.title}</div>\n <div className=\"text-xs text-muted-foreground\">{collectionLabel}</div>\n </button>\n <button\n type=\"button\"\n onClick={() => onEdit?.(item)}\n className=\"flex-none p-1 rounded hover:bg-primary/10 text-muted-foreground hover:text-primary transition-colors\"\n title=\"Edit inline\"\n >\n <Pencil className=\"w-3.5 h-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={() => onRemove(index)}\n className=\"flex-none p-1 rounded hover:bg-destructive/10 text-muted-foreground hover:text-destructive transition-colors\"\n >\n <Trash2 className=\"w-3.5 h-3.5\" />\n </button>\n </div>\n );\n};\n\n// ─── Add existing content modal ──────────────────────────────────────────────\n\nconst AddExistingModal = ({\n open,\n onOpenChange,\n allowedCollections,\n selectedKeys,\n onSelect,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n allowedCollections: string[];\n selectedKeys: Set<string>;\n onSelect: (items: ReferenceItem[]) => void;\n}) => {\n const config = useConfig();\n const queryClient = useQueryClient();\n const [entries, setEntries] = useState<EntryListItem[]>([]);\n const [search, setSearch] = useState('');\n const [typeFilter, setTypeFilter] = useState<string>('all');\n const [checked, setChecked] = useState<Set<string>>(new Set());\n const [isLoading, setIsLoading] = useState(false);\n const searchRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n if (!open) return;\n setIsLoading(true);\n setChecked(new Set());\n setSearch('');\n setTypeFilter('all');\n\n const load = async () => {\n try {\n const allEntries: EntryListItem[] = [];\n for (const col of allowedCollections) {\n const list = await queryClient.ensureQueryData({\n queryKey: queryKeys.entries.list(col),\n queryFn: () => getEntryList(col),\n });\n allEntries.push(...list);\n }\n setEntries(allEntries);\n } finally {\n setIsLoading(false);\n }\n };\n\n void load();\n }, [open, allowedCollections, queryClient]);\n\n // Focus search input when modal opens\n useEffect(() => {\n if (open) {\n setTimeout(() => searchRef.current?.focus(), 100);\n }\n }, [open]);\n\n const filtered = useMemo(() => {\n let result = entries;\n if (typeFilter !== 'all') {\n result = result.filter((e) => e.type === typeFilter);\n }\n if (search.trim()) {\n const q = search.toLowerCase();\n result = result.filter((e) => e.title.toLowerCase().includes(q) || e.type.toLowerCase().includes(q));\n }\n return result;\n }, [entries, typeFilter, search]);\n\n const toggleCheck = (referenceKey: string) => {\n setChecked((prev) => {\n const next = new Set(prev);\n if (next.has(referenceKey)) {\n next.delete(referenceKey);\n } else {\n next.add(referenceKey);\n }\n return next;\n });\n };\n\n const handleConfirm = () => {\n const selected = entries\n .filter((e) => checked.has(toReferenceKey(e.path)))\n .map((e) => ({ type: e.type, id: e.id, path: toReferenceKey(e.path), title: e.title }));\n onSelect(selected);\n onOpenChange(false);\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-w-2xl max-h-[80vh] flex flex-col\">\n <DialogHeader>\n <DialogTitle>Add existing content</DialogTitle>\n </DialogHeader>\n\n <div className=\"flex gap-2\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-2.5 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground\" />\n <input\n ref={searchRef}\n type=\"text\"\n placeholder=\"Filter entries...\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"w-full pl-9 pr-3 py-2 text-sm rounded-md border border-input bg-background\"\n />\n </div>\n {allowedCollections.length > 1 && (\n <select\n value={typeFilter}\n onChange={(e) => setTypeFilter(e.target.value)}\n className=\"px-3 py-2 text-sm rounded-md border border-input bg-background\"\n >\n <option value=\"all\">All types</option>\n {allowedCollections.map((col) => (\n <option key={col} value={col}>\n {config.collections[col as keyof Config['collections']]?.label || col}\n </option>\n ))}\n </select>\n )}\n </div>\n\n <div className=\"flex-1 overflow-y-auto min-h-0 border border-border rounded-md\">\n {isLoading ? (\n <div className=\"p-8 text-center text-muted-foreground text-sm\">Loading...</div>\n ) : filtered.length === 0 ? (\n <div className=\"p-8 text-center text-muted-foreground text-sm\">No entries found</div>\n ) : (\n <div className=\"divide-y divide-border\">\n {filtered.map((entry) => {\n const referenceKey = toReferenceKey(entry.path);\n const alreadyAdded = selectedKeys.has(referenceKey);\n const isChecked = checked.has(referenceKey);\n const collectionLabel =\n config.collections[entry.type as keyof Config['collections']]?.label || entry.type;\n\n return (\n <label\n key={entry.path}\n className={cn(\n 'flex items-center gap-3 px-3 py-2.5 cursor-pointer transition-colors',\n alreadyAdded ? 'opacity-40 cursor-not-allowed' : 'hover:bg-muted/50',\n isChecked && !alreadyAdded && 'bg-primary/5',\n )}\n >\n <input\n type=\"checkbox\"\n checked={isChecked || alreadyAdded}\n disabled={alreadyAdded}\n onChange={() => toggleCheck(referenceKey)}\n className=\"rounded border-input\"\n />\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium truncate\">{entry.title}</div>\n <div className=\"text-xs text-muted-foreground\">{collectionLabel}</div>\n </div>\n {alreadyAdded && <span className=\"text-xs text-muted-foreground flex-none\">Already added</span>}\n </label>\n );\n })}\n </div>\n )}\n </div>\n\n <DialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => onOpenChange(false)}>\n Cancel\n </Button>\n <Button type=\"button\" onClick={handleConfirm} disabled={checked.size === 0}>\n Add {checked.size > 0 ? `(${checked.size})` : ''}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n};\n\n// ─── Create new entry modal ──────────────────────────────────────────────────\n\nconst CreateNewModal = ({\n open,\n onOpenChange,\n allowedCollections,\n onCreate,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n allowedCollections: string[];\n onCreate: (item: ReferenceItem) => void;\n}) => {\n const config = useConfig();\n const [selectedType, setSelectedType] = useState(allowedCollections[0] || '');\n const { mutateAsync: createEntry, isPending } = useNewFile();\n\n useEffect(() => {\n if (open && allowedCollections.length > 0) {\n setSelectedType(allowedCollections[0]);\n }\n }, [open, allowedCollections]);\n\n const handleCreate = () => {\n void createEntry(selectedType)\n .then((result) => {\n const filePath = result.path;\n const id = filePath.replace(`${config.contentFolder}/${selectedType}/`, '').replace('.json', '');\n const referenceKey = toReferenceKey(filePath);\n\n const item: ReferenceItem = {\n type: selectedType,\n id,\n path: referenceKey,\n title: `New ${config.collections[selectedType as keyof Config['collections']]?.label || selectedType}`,\n };\n\n onCreate(item);\n onOpenChange(false);\n toast({\n title: `Created new ${config.collections[selectedType as keyof Config['collections']]?.label || selectedType}`,\n variant: 'success',\n });\n })\n .catch((e: Error) => {\n toast({ title: e.message, variant: 'destructive' });\n });\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-w-sm\">\n <DialogHeader>\n <DialogTitle>Create new entry</DialogTitle>\n </DialogHeader>\n\n {allowedCollections.length > 1 ? (\n <div>\n <label htmlFor=\"create-ref-type-select\" className=\"block text-sm font-medium mb-1.5\">\n Content type\n </label>\n <select\n id=\"create-ref-type-select\"\n value={selectedType}\n onChange={(e) => setSelectedType(e.target.value)}\n className=\"w-full px-3 py-2 text-sm rounded-md border border-input bg-background\"\n >\n {allowedCollections.map((col) => (\n <option key={col} value={col}>\n {config.collections[col as keyof Config['collections']]?.label || col}\n </option>\n ))}\n </select>\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground\">\n Create a new{' '}\n <strong>{config.collections[selectedType as keyof Config['collections']]?.label || selectedType}</strong>{' '}\n entry and add it to this field.\n </p>\n )}\n\n <DialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => onOpenChange(false)}>\n Cancel\n </Button>\n <Button type=\"button\" onClick={handleCreate} disabled={isPending}>\n {isPending ? 'Creating...' : 'Create & add'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n};\n\n// ─── Main component ──────────────────────────────────────────────────────────\n\nconst FormReferenceField = ({\n label,\n name,\n value = '[]',\n required,\n hint,\n error: submitError,\n onClearError,\n collection,\n reference,\n}: FormReferenceFieldProps) => {\n const config = useConfig();\n const queryClient = useQueryClient();\n const [items, setItems] = useState<ReferenceItem[]>([]);\n const [isExistingOpen, setIsExistingOpen] = useState(false);\n const [isCreateOpen, setIsCreateOpen] = useState(false);\n const [dragIndex, setDragIndex] = useState<number | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const { pushEntry, refreshTick } = useEntryStack();\n // True after the first parse-from-`value` pass; subsequent runs (driven by refreshTick)\n // patch titles on the live items list and drop deleted entries instead of re-parsing `value`.\n const loadedRef = useRef(false);\n\n // Resolve allowed collections from new or legacy config\n const allowedCollections = useMemo(() => {\n if (reference?.collections && reference.collections.length > 0) {\n return reference.collections as string[];\n }\n if (collection) {\n return [collection] as string[];\n }\n // Default: every `hasMany` collection. Singletons are 1-of-1 fixed-ID\n // entries — referencing them would never produce a meaningful list.\n return Object.entries(config.collections)\n .filter(([, col]) => col?.hasMany)\n .map(([name]) => name);\n }, [reference, collection, config.collections]);\n\n const cardinality = reference?.cardinality || 'many';\n const maxItems = cardinality === 'one' ? 1 : reference?.max;\n const minItems = cardinality === 'one' ? (required ? 1 : 0) : Math.max(reference?.min ?? 0, required ? 1 : 0);\n\n // Initial load (parse `value`) and subsequent refresh ticks (patch titles, drop deleted).\n useEffect(() => {\n let cancelled = false;\n\n const fetchEntryMap = async () => {\n const allEntries: EntryListItem[] = [];\n for (const col of allowedCollections) {\n const list = await queryClient.ensureQueryData({\n queryKey: queryKeys.entries.list(col),\n queryFn: () => getEntryList(col),\n });\n allEntries.push(...list);\n }\n return new Map(allEntries.map((e) => [toReferenceKey(e.path), e]));\n };\n\n const initialLoad = async () => {\n let parsedPaths: string[] = [];\n try {\n const parsed = JSON.parse(value);\n if (cardinality === 'one') {\n parsedPaths = typeof parsed === 'string' && parsed ? [parsed] : Array.isArray(parsed) ? parsed : [];\n } else {\n parsedPaths = Array.isArray(parsed) ? parsed : [];\n }\n } catch (_e) {\n if (cardinality === 'one' && value && value !== '[]') {\n parsedPaths = [value];\n }\n }\n\n if (parsedPaths.length === 0) {\n if (cancelled) return;\n setItems([]);\n setIsLoading(false);\n loadedRef.current = true;\n return;\n }\n\n const entryMap = await fetchEntryMap();\n if (cancelled) return;\n\n const resolved: ReferenceItem[] = parsedPaths.map((p) => {\n const rawValue = String(p ?? '');\n const normalizedKey = rawValue.includes('/') ? '' : rawValue.endsWith('.json') ? rawValue : `${rawValue}.json`;\n if (!normalizedKey || !toContentPath(normalizedKey)) {\n return { type: '', id: rawValue, path: rawValue, title: rawValue };\n }\n\n const entry = entryMap.get(normalizedKey);\n if (entry) {\n return { type: entry.type, id: entry.id, path: normalizedKey, title: entry.title };\n }\n\n const fallbackContentPath = toContentPath(normalizedKey);\n const parts = fallbackContentPath\n ? fallbackContentPath.replace('cms/content/', '').replace('.json', '').split('/')\n : [];\n const fallbackId = parts.length > 0 ? parts[parts.length - 1] : normalizedKey.replace('.json', '');\n const fallbackType = parts.length > 0 ? parts[0] : '';\n return { type: fallbackType, id: fallbackId, path: normalizedKey, title: fallbackId };\n });\n\n setItems(resolved);\n setIsLoading(false);\n loadedRef.current = true;\n };\n\n const refreshTitles = async () => {\n const entryMap = await fetchEntryMap();\n if (cancelled) return;\n setItems((prev) =>\n prev\n .map((item): ReferenceItem | null => {\n const entry = entryMap.get(item.path);\n if (entry) return { ...item, title: entry.title, type: entry.type, id: entry.id };\n // Item references a path that's no longer in the allowed collections — treat as deleted.\n return null;\n })\n .filter((x): x is ReferenceItem => x !== null),\n );\n };\n\n if (loadedRef.current) {\n refreshTitles();\n } else {\n initialLoad();\n }\n\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- tick-driven title sync; first parse uses latest `value` once via `loadedRef`\n }, [refreshTick, queryClient, allowedCollections]);\n\n const selectedKeys = useMemo(() => new Set(items.map((i) => i.path)), [items]);\n\n // Serialize value for form submission\n const serializedValue = useMemo(() => {\n const paths = items.map((i) => i.path);\n if (cardinality === 'one') {\n return paths[0] || '';\n }\n return JSON.stringify(paths);\n }, [items, cardinality]);\n\n const handleRemove = useCallback(\n (index: number) => {\n setItems((prev) => prev.filter((_, i) => i !== index));\n onClearError?.(name);\n },\n [name, onClearError],\n );\n\n const handleAddExisting = useCallback(\n (newItems: ReferenceItem[]) => {\n setItems((prev) => {\n const combined = [...prev, ...newItems];\n return combined;\n });\n onClearError?.(name);\n },\n [name, onClearError],\n );\n\n const handleCreate = useCallback(\n (item: ReferenceItem) => {\n setItems((prev) => [...prev, item]);\n onClearError?.(name);\n },\n [name, onClearError],\n );\n\n // Open inline editor for a referenced entry\n const handleEdit = useCallback(\n (item: ReferenceItem) => {\n const contentPath = toContentPath(item.path);\n if (!contentPath) return;\n pushEntry({ id: item.id, type: item.type, path: contentPath, title: item.title });\n },\n [pushEntry],\n );\n\n // Drag and drop handlers\n const handleDragStart = useCallback((index: number) => {\n setDragIndex(index);\n }, []);\n\n const handleDragOver = useCallback((e: React.DragEvent, _index: number) => {\n e.preventDefault();\n }, []);\n\n const handleDragEnd = useCallback(() => {\n setDragIndex(null);\n }, []);\n\n const handleDrop = useCallback(\n (targetIndex: number) => {\n if (dragIndex === null || dragIndex === targetIndex) return;\n\n setItems((prev) => {\n const next = [...prev];\n const [dragged] = next.splice(dragIndex, 1);\n next.splice(targetIndex, 0, dragged);\n return next;\n });\n setDragIndex(null);\n onClearError?.(name);\n },\n [dragIndex, name, onClearError],\n );\n\n const canAdd = maxItems === undefined || items.length < maxItems;\n const validationError =\n minItems !== undefined && minItems > 0 && items.length < minItems\n ? `At least ${minItems} ${minItems === 1 ? 'item' : 'items'} required`\n : null;\n const displayError = submitError ?? validationError;\n\n return (\n <div className=\"mb-6\">\n <div className=\"block text-xs font-medium text-muted-foreground mb-1.5\">\n {label}\n {required && <span className=\"text-destructive ml-1\">*</span>}\n {cardinality === 'one' && <span className=\"text-xs text-muted-foreground ml-2\">(single)</span>}\n {maxItems !== undefined && cardinality === 'many' && (\n <span className=\"text-xs text-muted-foreground ml-2\">\n ({items.length}/{maxItems})\n </span>\n )}\n </div>\n\n {isLoading ? (\n <div className=\"text-sm text-muted-foreground py-4\">Loading references...</div>\n ) : (\n <>\n {/* Selected items list */}\n {items.length > 0 && (\n <div className=\"flex flex-col gap-1.5 mb-3\">\n {items.map((item, i) => (\n <DraggableItem\n key={`${item.path}-${i}`}\n item={item}\n index={i}\n onRemove={handleRemove}\n onEdit={handleEdit}\n onDragStart={handleDragStart}\n onDragOver={handleDragOver}\n onDragEnd={handleDragEnd}\n onDrop={handleDrop}\n />\n ))}\n </div>\n )}\n\n {items.length === 0 && (\n <div className=\"text-sm text-muted-foreground py-4 border border-dashed border-border rounded-lg text-center\">\n No items selected\n </div>\n )}\n\n {/* Action buttons */}\n {canAdd && (\n <div className=\"flex gap-2 mt-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => setIsExistingOpen(true)}>\n <Plus className=\"w-4 h-4\" />\n Add existing\n </Button>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => setIsCreateOpen(true)}>\n <Plus className=\"w-4 h-4\" />\n Create new\n </Button>\n </div>\n )}\n </>\n )}\n\n <FieldHintAndError hint={hint} error={displayError ?? undefined} />\n\n {/* Hidden input for form submission */}\n <input type=\"hidden\" name={name} value={serializedValue} />\n\n {/* Modals */}\n <AddExistingModal\n open={isExistingOpen}\n onOpenChange={setIsExistingOpen}\n allowedCollections={allowedCollections}\n selectedKeys={selectedKeys}\n onSelect={handleAddExisting}\n />\n <CreateNewModal\n open={isCreateOpen}\n onOpenChange={setIsCreateOpen}\n allowedCollections={allowedCollections}\n onCreate={handleCreate}\n />\n </div>\n );\n};\n\nexport default FormReferenceField;\n"],"mappings":"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","names":["_a","name"]}
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  loadCollections,
6
6
  loadProjectConfig
7
- } from "./chunk-TG624CCO.js";
7
+ } from "./chunk-ZYUK2J5L.js";
8
8
  import {
9
9
  log
10
10
  } from "./chunk-6PHFHGTZ.js";
@@ -13,9 +13,8 @@ import {
13
13
  } from "./chunk-DDAAVRWG.js";
14
14
 
15
15
  // cli/commands/embeddingsGen.ts
16
- import { mkdirSync, promises as fsPromises2, writeFileSync } from "fs";
16
+ import { mkdirSync, promises as fsPromises3, writeFileSync } from "fs";
17
17
  import { dirname, join } from "path";
18
- import { glob } from "glob";
19
18
 
20
19
  // agent/embeddings.ts
21
20
  import { createHash } from "crypto";
@@ -132,7 +131,7 @@ var LocalTransformersEmbedder = class {
132
131
  const msg = e instanceof Error ? e.message : String(e);
133
132
  if (/libonnxruntime|onnxruntime-node|ERR_DLOPEN_FAILED/i.test(msg)) {
134
133
  throw new Error(
135
- `Embeddings unavailable: '@huggingface/transformers' loaded but its native dependency 'onnxruntime-node' could not load (${msg}). On Vercel, add './node_modules/onnxruntime-node/**' to 'outputFileTracingIncludes' for the relevant routes, or rely on offline 'npm run embeddings:gen'.`
134
+ `Embeddings unavailable: '@huggingface/transformers' loaded but its native dependency 'onnxruntime-node' could not load (${msg}). On Vercel, add './node_modules/onnxruntime-node/**' to 'outputFileTracingIncludes' for the relevant routes, or rely on offline 'npx octocms embeddings:gen'.`
136
135
  );
137
136
  }
138
137
  throw new Error(
@@ -304,6 +303,38 @@ async function embedAll(paths, options) {
304
303
  return next;
305
304
  }
306
305
 
306
+ // lib/localReader.ts
307
+ import fsPromises2 from "fs/promises";
308
+ import path2 from "path";
309
+ async function listLocalFilesRecursive(dirPath, ext) {
310
+ return listLocalFilesWithExtensions(dirPath, [ext], true);
311
+ }
312
+ async function listLocalFilesWithExtensions(dirPath, extensions, recursive = false) {
313
+ const fullDir = path2.join(process.cwd(), dirPath);
314
+ const normalizedExts = extensions.map((e) => e.startsWith(".") ? e : `.${e}`);
315
+ try {
316
+ if (recursive) {
317
+ const names = await fsPromises2.readdir(fullDir, { recursive: true });
318
+ return names.filter((n) => normalizedExts.some((e) => n.endsWith(e))).map((n) => `${dirPath}/${n}`.replace(/\\/g, "/")).sort();
319
+ }
320
+ const entries = await fsPromises2.readdir(fullDir, { withFileTypes: true });
321
+ return entries.filter((e) => e.isFile() && normalizedExts.some((ext) => e.name.endsWith(ext))).map((e) => `${dirPath}/${e.name}`).sort();
322
+ } catch (error) {
323
+ if ((error == null ? void 0 : error.code) === "ENOENT") return [];
324
+ throw error;
325
+ }
326
+ }
327
+ async function listLocalCollectionFiles(dirPath) {
328
+ const fullDir = path2.join(process.cwd(), dirPath);
329
+ try {
330
+ const entries = await fsPromises2.readdir(fullDir, { withFileTypes: true });
331
+ return entries.filter((e) => e.isFile() && e.name.endsWith(".json")).map((e) => `${dirPath}/${e.name}`);
332
+ } catch (error) {
333
+ if ((error == null ? void 0 : error.code) === "ENOENT") return [];
334
+ throw error;
335
+ }
336
+ }
337
+
307
338
  // cli/commands/embeddingsGen.ts
308
339
  async function embeddingsGenCommand(projectRoot) {
309
340
  var _a;
@@ -327,8 +358,8 @@ async function embeddingsGenCommand(projectRoot) {
327
358
  let paths = [];
328
359
  try {
329
360
  const [contentPaths, mediaPaths] = await Promise.all([
330
- glob(`${config.contentFolder}/**/*.json`),
331
- glob(`${mediaFolder}/*.json`)
361
+ listLocalFilesRecursive(config.contentFolder, ".json"),
362
+ listLocalCollectionFiles(mediaFolder)
332
363
  ]);
333
364
  paths = [...contentPaths, ...mediaPaths];
334
365
  } finally {
@@ -376,11 +407,11 @@ async function embeddingsGenCommand(projectRoot) {
376
407
  mkdirSync(dirname(dest), { recursive: true });
377
408
  writeFileSync(dest, serializeStore(next), "utf8");
378
409
  log.success(`${EMBEDDINGS_STORE_PATH} (${Object.keys(next.entries).length} vectors, dim=${next.dim})`);
379
- await fsPromises2.utimes(dest, /* @__PURE__ */ new Date(), /* @__PURE__ */ new Date()).catch(() => {
410
+ await fsPromises3.utimes(dest, /* @__PURE__ */ new Date(), /* @__PURE__ */ new Date()).catch(() => {
380
411
  });
381
412
  log.blank();
382
413
  }
383
414
  export {
384
415
  embeddingsGenCommand
385
416
  };
386
- //# sourceMappingURL=embeddingsGen-7MXSZQ43.js.map
417
+ //# sourceMappingURL=embeddingsGen-FXWCPGB7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../cli/commands/embeddingsGen.ts","../agent/embeddings.ts","../lib/companionMarkdown.ts","../agent/embedText.ts","../agent/embedder.ts","../agent/storeFormat.ts","../lib/localReader.ts"],"sourcesContent":["/**\n * `octocms embeddings:gen` — Generate `cms/__generated__/embeddings.json` for\n * the chat agent's retrieval index.\n *\n * Mirrors the `types:gen` pattern: loads the user's config, walks every\n * content entry on disk, embeds new/changed entries via `@huggingface/transformers`,\n * and writes the merged store atomically. Re-running on unchanged content is a\n * fast no-op (hash-keyed skip).\n */\n\nimport { mkdirSync, promises as fsPromises, writeFileSync } from 'fs';\nimport { dirname, join } from 'path';\n\nimport { embedAll, EMBEDDINGS_STORE_PATH, loadEmbeddings, serializeStore } from '../../agent/embeddings';\nimport { listLocalCollectionFiles, listLocalFilesRecursive } from '../../lib/localReader';\nimport { log } from '../lib/logger';\nimport { loadCollections, loadProjectConfig } from '../lib/project';\nimport { validateConfig } from '../lib/validateConfig';\n\nexport async function embeddingsGenCommand(projectRoot: string): Promise<void> {\n log.header('Generate embeddings');\n\n const config = await loadProjectConfig(projectRoot);\n const collections = await loadCollections(projectRoot);\n\n log.info('Validating config...');\n try {\n validateConfig(config, collections);\n } catch (e) {\n log.error(String((e as Error).message));\n process.exitCode = 1;\n return;\n }\n log.success(`${collections.length} collections validated`);\n\n // Walk both the editorial content folder AND the media-entry folder.\n // Media entries are searchable too (chat agent's `searchContent` returns\n // them like any other hit) and live outside `contentFolder` since the move\n // to a top-level `cms/media/`.\n log.blank();\n log.info('Discovering entries...');\n const cwdBefore = process.cwd();\n process.chdir(projectRoot);\n const mediaFolder = (config as typeof config & { mediaContentFolder?: string }).mediaContentFolder ?? 'cms/media';\n let paths: string[] = [];\n try {\n const [contentPaths, mediaPaths] = await Promise.all([\n listLocalFilesRecursive(config.contentFolder, '.json'),\n listLocalCollectionFiles(mediaFolder),\n ]);\n paths = [...contentPaths, ...mediaPaths];\n } finally {\n process.chdir(cwdBefore);\n }\n paths = paths.map((p) => p.replace(/\\\\/g, '/')).sort();\n log.success(`${paths.length} entries`);\n\n log.blank();\n log.info('Loading existing store (if any)...');\n // loadEmbeddings reads from process.cwd() — temporarily switch so it picks\n // up the project's local store, not the CLI invocation directory.\n process.chdir(projectRoot);\n let previous;\n try {\n previous = await loadEmbeddings();\n } finally {\n process.chdir(cwdBefore);\n }\n const previousCount = Object.keys(previous.entries).length;\n log.success(`${previousCount} existing vectors`);\n\n log.blank();\n log.info('Embedding entries (model loads on first run; ~3–10s cold)...');\n let lastDone = -1;\n process.chdir(projectRoot);\n let next;\n try {\n next = await embedAll(paths, {\n collections: config.collections,\n previous,\n onProgress: (done, total) => {\n // Only print when the count crosses a 10% boundary to avoid noisy logs.\n const decile = Math.floor((done / Math.max(total, 1)) * 10);\n const prevDecile = Math.floor((Math.max(lastDone, 0) / Math.max(total, 1)) * 10);\n if (decile !== prevDecile) {\n log.step(`${done} / ${total}`);\n }\n lastDone = done;\n },\n });\n } catch (e) {\n log.error(`Embedding failed: ${(e as Error).message}`);\n process.exitCode = 1;\n return;\n } finally {\n process.chdir(cwdBefore);\n }\n\n const dest = join(projectRoot, EMBEDDINGS_STORE_PATH);\n mkdirSync(dirname(dest), { recursive: true });\n writeFileSync(dest, serializeStore(next), 'utf8');\n log.success(`${EMBEDDINGS_STORE_PATH} (${Object.keys(next.entries).length} vectors, dim=${next.dim})`);\n\n // Touch the file so callers see a deterministic mtime in tests.\n await fsPromises.utimes(dest, new Date(), new Date()).catch(() => {});\n log.blank();\n}\n","/**\n * Store I/O for the embeddings index.\n *\n * The store is a single committed JSON file (`cms/__generated__/embeddings.json`)\n * holding one row per content entry: `{ hash, vec }`. `hash` is a sha256 of the\n * embedding text — when an entry's text hasn't changed we skip re-embedding,\n * which keeps `embeddings:gen` fast and idempotent.\n *\n * Used by:\n * - The CLI command `embeddings:gen` for full / incremental rebuilds.\n * - The save/new/remove server actions to keep the store in sync with content.\n * - Phase 2's `searchContent` for cosine retrieval (read-only).\n */\n\nimport { createHash } from 'crypto';\nimport { promises as fsPromises } from 'fs';\nimport path from 'path';\n\nimport type { Config } from '../types';\nimport { companionFilePathsForEntry } from '../lib/companionMarkdown';\nimport { entryToEmbeddingText } from './embedText';\nimport { getDefaultEmbedder, type Embedder, DEFAULT_DIM, DEFAULT_MODEL_ID } from './embedder';\nimport { decodeFloat32, encodeFloat32 } from './storeFormat';\n\nexport const EMBEDDINGS_STORE_PATH = 'cms/__generated__/embeddings.json';\n\nexport type EmbeddingsRecord = {\n /** sha256 of the embedding text — used to skip re-embed when content is unchanged. */\n hash: string;\n /** Base64-encoded `Float32Array`. */\n vec: string;\n};\n\nexport type EmbeddingsStore = {\n model: string;\n dim: number;\n /** Map from content path (e.g. `\"cms/content/post/post-abc.json\"`) to record. */\n entries: Record<string, EmbeddingsRecord>;\n};\n\n/** Hash an embedding text into a stable cache key. */\nexport function hashEmbeddingText(text: string): string {\n return createHash('sha256').update(text).digest('hex');\n}\n\n/** Construct an empty store with the given embedder's metadata. */\nexport function emptyStore(embedder?: Embedder): EmbeddingsStore {\n const e = embedder ?? null;\n return {\n model: e?.modelId ?? DEFAULT_MODEL_ID,\n dim: e?.dim ?? DEFAULT_DIM,\n entries: {},\n };\n}\n\nfunction normalizeContentPath(p: string): string {\n return p.replace(/\\\\/g, '/');\n}\n\n/**\n * Read the embeddings store from disk. Returns an empty store when the file\n * doesn't exist yet — first run after enabling the agent.\n *\n * Production reads from the active branch via the GitHub admin helpers;\n * `branch` is forwarded to `getGitHubFile` when provided. Dev / CLI reads\n * straight from the local FS.\n */\nexport async function loadEmbeddings(branch?: string): Promise<EmbeddingsStore> {\n const raw = await readStoreRaw(branch);\n if (!raw) return emptyStore();\n try {\n const parsed = JSON.parse(raw) as Partial<EmbeddingsStore>;\n if (!parsed || typeof parsed !== 'object' || !parsed.entries || typeof parsed.entries !== 'object') {\n return emptyStore();\n }\n return {\n model: typeof parsed.model === 'string' ? parsed.model : DEFAULT_MODEL_ID,\n dim: typeof parsed.dim === 'number' ? parsed.dim : DEFAULT_DIM,\n entries: parsed.entries as Record<string, EmbeddingsRecord>,\n };\n } catch {\n return emptyStore();\n }\n}\n\nasync function readStoreRaw(branch?: string): Promise<string | null> {\n // Try GitHub first when running in production / a Next.js server context;\n // fall back silently to local FS so the CLI works offline.\n try {\n const isProd = process.env.NODE_ENV === 'production';\n if (isProd) {\n const { getGitHubFile } = await import('../admin/github');\n const file = await getGitHubFile(EMBEDDINGS_STORE_PATH, branch);\n if (file) return file.content;\n }\n } catch {\n /* fall through to local FS */\n }\n try {\n const abs = path.join(process.cwd(), 'cms', '__generated__', 'embeddings.json');\n return await fsPromises.readFile(abs, { encoding: 'utf8' });\n } catch {\n return null;\n }\n}\n\n/** Serialise a store to the canonical on-disk format (JSON, sorted keys, trailing newline). */\nexport function serializeStore(store: EmbeddingsStore): string {\n const sortedEntries: Record<string, EmbeddingsRecord> = {};\n for (const key of Object.keys(store.entries).sort()) {\n sortedEntries[key] = store.entries[key];\n }\n const ordered: EmbeddingsStore = {\n model: store.model,\n dim: store.dim,\n entries: sortedEntries,\n };\n return JSON.stringify(ordered, null, 2) + '\\n';\n}\n\n/** Pure helper — return a new store with `path` removed. */\nexport function removeEntryFromStore(store: EmbeddingsStore, entryPath: string): EmbeddingsStore {\n const norm = normalizeContentPath(entryPath);\n if (!(norm in store.entries)) return store;\n const next: Record<string, EmbeddingsRecord> = { ...store.entries };\n delete next[norm];\n return { ...store, entries: next };\n}\n\n/** Pure helper — return a new store with `path` upserted. */\nexport function upsertEntryInStore(\n store: EmbeddingsStore,\n entryPath: string,\n record: EmbeddingsRecord,\n): EmbeddingsStore {\n const norm = normalizeContentPath(entryPath);\n return {\n ...store,\n entries: { ...store.entries, [norm]: record },\n };\n}\n\nasync function readEntryAndCompanionsLocal(\n filePath: string,\n collections: Config['collections'],\n): Promise<{\n entry: { sys?: { type?: string }; fields?: Record<string, unknown> };\n companions: Record<string, string>;\n} | null> {\n const abs = path.join(process.cwd(), 'cms', filePath.replace(/^cms[\\\\/]/, ''));\n let raw: string;\n try {\n raw = await fsPromises.readFile(abs, { encoding: 'utf8' });\n } catch {\n return null;\n }\n let entry: any;\n try {\n entry = JSON.parse(raw);\n } catch {\n return null;\n }\n const collectionType = entry?.sys?.type;\n const companions: Record<string, string> = {};\n if (typeof collectionType === 'string') {\n const paths = companionFilePathsForEntry(filePath, collectionType, collections);\n for (const [field, p] of Object.entries(paths)) {\n try {\n companions[field] = await fsPromises.readFile(path.join(process.cwd(), 'cms', p.replace(/^cms[\\\\/]/, '')), {\n encoding: 'utf8',\n });\n } catch {\n companions[field] = '';\n }\n }\n }\n return { entry, companions };\n}\n\n/**\n * Compute (or look up) the embedding for a single entry on disk.\n *\n * - `store` provides the previous record (if any) so we can skip re-embedding\n * when the text hash hasn't changed.\n * - `embedder` defaults to the local transformers singleton.\n *\n * Returns `null` when the entry can't be read (deleted between listing and\n * embedding, or unreadable JSON).\n */\nexport async function embedEntry(\n filePath: string,\n options: {\n collections: Config['collections'];\n store?: EmbeddingsStore;\n embedder?: Embedder;\n },\n): Promise<{ path: string; hash: string; vec: Float32Array; skipped: boolean } | null> {\n const { collections, store, embedder = getDefaultEmbedder() } = options;\n const norm = normalizeContentPath(filePath);\n const loaded = await readEntryAndCompanionsLocal(filePath, collections);\n if (!loaded) return null;\n\n const text = entryToEmbeddingText(loaded.entry, loaded.companions);\n const hash = hashEmbeddingText(text);\n\n const existing = store?.entries[norm];\n if (existing && existing.hash === hash && store?.dim === embedder.dim) {\n return { path: norm, hash, vec: decodeFloat32(existing.vec), skipped: true };\n }\n\n const [vec] = await embedder.embed([text]);\n return { path: norm, hash, vec, skipped: false };\n}\n\n/**\n * Embed an entry from already-in-memory content. Used by the save/new server\n * actions to avoid an extra disk roundtrip — the entry payload and companion\n * map are right there in `saveFile`'s scope.\n */\nexport async function embedEntryFromMemory(\n entry: { fields?: Record<string, unknown> },\n companions: Record<string, string>,\n options: {\n store?: EmbeddingsStore;\n embedder?: Embedder;\n } = {},\n): Promise<{ hash: string; vec: Float32Array; skipped: boolean; record: EmbeddingsRecord }> {\n const { embedder = getDefaultEmbedder() } = options;\n const text = entryToEmbeddingText(entry, companions);\n const hash = hashEmbeddingText(text);\n const [vec] = await embedder.embed([text]);\n return {\n hash,\n vec,\n skipped: false,\n record: { hash, vec: encodeFloat32(vec) },\n };\n}\n\n/**\n * Rebuild the store for `paths` in a single batched embedder call. Reuses\n * existing records when their content hash matches — full rebuilds against\n * unchanged content are no-ops.\n */\nexport async function embedAll(\n paths: readonly string[],\n options: {\n collections: Config['collections'];\n previous?: EmbeddingsStore;\n embedder?: Embedder;\n /** Optional progress callback — `(done, total)` per entry processed. */\n onProgress?: (done: number, total: number) => void;\n },\n): Promise<EmbeddingsStore> {\n const { collections, previous, embedder = getDefaultEmbedder(), onProgress } = options;\n const next = emptyStore(embedder);\n\n // First pass: compute texts + hashes; mark which need re-embedding.\n type PendingEntry = { path: string; text: string; hash: string };\n const toEmbed: PendingEntry[] = [];\n let processed = 0;\n\n for (const filePath of paths) {\n const norm = normalizeContentPath(filePath);\n const loaded = await readEntryAndCompanionsLocal(filePath, collections);\n if (!loaded) continue;\n const text = entryToEmbeddingText(loaded.entry, loaded.companions);\n const hash = hashEmbeddingText(text);\n const existing = previous?.entries[norm];\n if (existing && existing.hash === hash && previous?.dim === embedder.dim) {\n next.entries[norm] = existing;\n processed++;\n onProgress?.(processed, paths.length);\n continue;\n }\n toEmbed.push({ path: norm, text, hash });\n }\n\n // Second pass: batched embedding. Chunk to avoid OOM on giant collections.\n const BATCH = 32;\n for (let i = 0; i < toEmbed.length; i += BATCH) {\n const chunk = toEmbed.slice(i, i + BATCH);\n const vecs = await embedder.embed(chunk.map((c) => c.text));\n for (let j = 0; j < chunk.length; j++) {\n const { path: p, hash } = chunk[j];\n next.entries[p] = { hash, vec: encodeFloat32(vecs[j]) };\n processed++;\n onProgress?.(processed, paths.length);\n }\n }\n\n return next;\n}\n","import type { Config } from '../types';\n\n/** Derive the companion `.md` file path for a markdown field from the entry JSON path. */\nexport function companionMarkdownPath(entryJsonPath: string, fieldName: string): string {\n const base = entryJsonPath.replace(/\\.json$/, '');\n return `${base}.${fieldName}.md`;\n}\n\n/** Derive the companion `.mdx` file path for a richtext field from the entry JSON path. */\nexport function companionRichTextPath(entryJsonPath: string, fieldName: string): string {\n const base = entryJsonPath.replace(/\\.json$/, '');\n return `${base}.${fieldName}.mdx`;\n}\n\n/** Return field names that have `format: 'markdown'` for the given collection type. */\nexport function getMarkdownFieldNames(collectionType: string, collections: Config['collections']): string[] {\n const col = (collections as Record<string, any>)[collectionType];\n if (!col) return [];\n return Object.entries(col.fields)\n .filter(([, def]: [string, any]) => def.format === 'markdown')\n .map(([key]) => key);\n}\n\n/** Return field names that have `format: 'richtext'` for the given collection type. */\nexport function getRichTextFieldNames(collectionType: string, collections: Config['collections']): string[] {\n const col = (collections as Record<string, any>)[collectionType];\n if (!col) return [];\n return Object.entries(col.fields)\n .filter(([, def]: [string, any]) => def.format === 'richtext')\n .map(([key]) => key);\n}\n\n/** Return a map of `{ fieldName: companionPath }` for all markdown fields in the collection. */\nexport function companionMarkdownPathsForEntry(\n entryJsonPath: string,\n collectionType: string,\n collections: Config['collections'],\n): Record<string, string> {\n const names = getMarkdownFieldNames(collectionType, collections);\n const result: Record<string, string> = {};\n for (const name of names) {\n result[name] = companionMarkdownPath(entryJsonPath, name);\n }\n return result;\n}\n\n/** Return a map of `{ fieldName: companionPath }` for all richtext fields in the collection. */\nexport function companionRichTextPathsForEntry(\n entryJsonPath: string,\n collectionType: string,\n collections: Config['collections'],\n): Record<string, string> {\n const names = getRichTextFieldNames(collectionType, collections);\n const result: Record<string, string> = {};\n for (const name of names) {\n result[name] = companionRichTextPath(entryJsonPath, name);\n }\n return result;\n}\n\n/**\n * Return a map of `{ fieldName: companionPath }` for ALL companion-file fields\n * (both markdown `.md` and richtext `.mdx`) in the collection.\n */\nexport function companionFilePathsForEntry(\n entryJsonPath: string,\n collectionType: string,\n collections: Config['collections'],\n): Record<string, string> {\n return {\n ...companionMarkdownPathsForEntry(entryJsonPath, collectionType, collections),\n ...companionRichTextPathsForEntry(entryJsonPath, collectionType, collections),\n };\n}\n","/**\n * Pure helper that turns a content entry into the plain-text payload we feed\n * to the embedding model. One vector per entry — every leaf field plus\n * companion `.md` / `.mdx` content is flattened into a single string.\n *\n * Reference fields keep the raw key strings (e.g. `\"author-abc.json\"`) — we\n * don't resolve them. The model still gets a useful signal (the key encodes\n * the collection) and we avoid recursive lookups during indexing.\n *\n * Field-name labels are included to give the model coarse semantic anchors\n * (`title:`, `body:`, …) without overwhelming the actual content.\n */\n\ntype EntryLike = {\n fields?: Record<string, unknown>;\n};\n\nfunction valueToText(value: unknown): string {\n if (value == null) return '';\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'boolean') return String(value);\n if (Array.isArray(value)) {\n return value.map(valueToText).filter(Boolean).join(', ');\n }\n if (typeof value === 'object') {\n // For objects (image fields, JSON blobs, etc.) — flatten leaf string/number values.\n const parts: string[] = [];\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n const text = valueToText(v);\n if (text) parts.push(`${k}: ${text}`);\n }\n return parts.join(', ');\n }\n return '';\n}\n\n/**\n * Flatten an entry's fields plus companion-file contents into a single\n * embedding-ready string. Companion contents take precedence when a field\n * name appears in both (matches `getFile`'s merge order — companion wins).\n */\nexport function entryToEmbeddingText(entry: EntryLike, companions: Record<string, string> = {}): string {\n const lines: string[] = [];\n const fields = entry.fields ?? {};\n\n for (const [key, value] of Object.entries(fields)) {\n if (key in companions) continue; // handled below — companion content wins\n const text = valueToText(value).trim();\n if (text) lines.push(`${key}: ${text}`);\n }\n\n for (const [key, content] of Object.entries(companions)) {\n const trimmed = (content ?? '').trim();\n if (trimmed) lines.push(`${key}: ${trimmed}`);\n }\n\n return lines.join('\\n');\n}\n","/**\n * Embedding adapter — turns text into 384-dim float vectors.\n *\n * The default implementation, `LocalTransformersEmbedder`, runs\n * `Xenova/bge-small-en-v1.5` in-process via `@huggingface/transformers`. No\n * network calls after the model is cached, no LM Studio dependency, no key.\n *\n * The adapter is intentionally tiny so we can swap in a hosted provider\n * (Voyage / OpenAI) later without touching the rest of the agent.\n */\n\nexport interface Embedder {\n /** Embed a batch of texts. The returned array matches `texts` 1:1. */\n embed(texts: string[]): Promise<Float32Array[]>;\n /** Vector dimensionality — 384 for bge-small, etc. */\n readonly dim: number;\n /** Identifier written into the store header so we can detect model swaps. */\n readonly modelId: string;\n}\n\nconst DEFAULT_MODEL_ID = 'Xenova/bge-small-en-v1.5';\nconst DEFAULT_DIM = 384;\n\ntype FeatureExtractionPipeline = (\n texts: string[],\n options: { pooling: 'mean'; normalize: boolean },\n) => Promise<{ data: Float32Array; dims: number[] }>;\n\n/**\n * `onnxruntime-node` (a transitive dep of `@huggingface/transformers`) loads\n * `libonnxruntime.so.1` via a synchronous `require` at module top-level. On\n * Vercel the .so isn't bundled by default, so the require throws and — worse\n * — the package's internal init also fires an *unhandled* rejection that\n * crashes the lambda with exit 128 even though our own `try`/`catch` handles\n * the import.\n *\n * Install a one-time process-level filter that swallows just those native-\n * load rejections. Other unhandled rejections still crash with the default\n * Node behaviour.\n */\nlet nativeRejectionFilterInstalled = false;\nfunction installNativeRejectionFilter(): void {\n if (nativeRejectionFilterInstalled) return;\n if (typeof process === 'undefined' || typeof process.on !== 'function') return;\n nativeRejectionFilterInstalled = true;\n process.on('unhandledRejection', (reason: unknown) => {\n const msg = reason instanceof Error ? reason.message : String(reason);\n if (/libonnxruntime|onnxruntime-node|ERR_DLOPEN_FAILED/i.test(msg)) {\n // Already surfaced via the embedder's caller catch path. Swallow so\n // the lambda doesn't exit 128 on a content write.\n return;\n }\n // Preserve Node's default crash behaviour for unrelated rejections.\n setImmediate(() => {\n throw reason;\n });\n });\n}\n\nclass LocalTransformersEmbedder implements Embedder {\n readonly modelId: string;\n readonly dim: number;\n private pipelinePromise: Promise<FeatureExtractionPipeline> | null = null;\n private permanentFailure: Error | null = null;\n\n constructor(modelId: string = DEFAULT_MODEL_ID, dim: number = DEFAULT_DIM) {\n this.modelId = modelId;\n this.dim = dim;\n }\n\n private async getPipeline(): Promise<FeatureExtractionPipeline> {\n if (this.permanentFailure) throw this.permanentFailure;\n if (this.pipelinePromise) return this.pipelinePromise;\n installNativeRejectionFilter();\n const p = (async (): Promise<FeatureExtractionPipeline> => {\n let mod: typeof import('@huggingface/transformers');\n try {\n mod = await import('@huggingface/transformers');\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n if (/libonnxruntime|onnxruntime-node|ERR_DLOPEN_FAILED/i.test(msg)) {\n throw new Error(\n `Embeddings unavailable: '@huggingface/transformers' loaded but its native ` +\n `dependency 'onnxruntime-node' could not load (${msg}). On Vercel, add ` +\n `'./node_modules/onnxruntime-node/**' to 'outputFileTracingIncludes' ` +\n `for the relevant routes, or rely on offline 'npx octocms embeddings:gen'.`,\n );\n }\n throw new Error(\n `Embeddings require the optional peer dep '@huggingface/transformers'. Install it with: npm install @huggingface/transformers`,\n );\n }\n const pipe = (await mod.pipeline('feature-extraction', this.modelId)) as unknown as FeatureExtractionPipeline;\n return pipe;\n })();\n // Mark as handled at promise-creation time so a rejection that beats the\n // first await (e.g. native-lib load) doesn't fire unhandledRejection.\n p.catch((e: unknown) => {\n this.permanentFailure = e instanceof Error ? e : new Error(String(e));\n this.pipelinePromise = null;\n });\n this.pipelinePromise = p;\n return p;\n }\n\n async embed(texts: string[]): Promise<Float32Array[]> {\n if (texts.length === 0) return [];\n const pipe = await this.getPipeline();\n // bge-small expects mean-pooled, L2-normalised embeddings — the model card\n // and Xenova's transformers.js examples both prescribe these defaults.\n const output = await pipe(texts, { pooling: 'mean', normalize: true });\n const flat = output.data;\n const out: Float32Array[] = [];\n for (let i = 0; i < texts.length; i++) {\n const start = i * this.dim;\n const slice = new Float32Array(this.dim);\n slice.set(flat.subarray(start, start + this.dim));\n out.push(slice);\n }\n return out;\n }\n}\n\nlet defaultEmbedder: Embedder | null = null;\n\n/** Lazy singleton — first call kicks off the model load (~3–10s cold). */\nexport function getDefaultEmbedder(): Embedder {\n if (!defaultEmbedder) defaultEmbedder = new LocalTransformersEmbedder();\n return defaultEmbedder;\n}\n\n/** Test seam: replace or reset the singleton. */\nexport function setDefaultEmbedder(embedder: Embedder | null): void {\n defaultEmbedder = embedder;\n}\n\nexport { LocalTransformersEmbedder, DEFAULT_MODEL_ID, DEFAULT_DIM };\n","/**\n * On-disk format helpers for the embeddings store.\n *\n * Embedding vectors are stored as base64-encoded `Float32Array` payloads in\n * `cms/__generated__/embeddings.json`. Base64 keeps them ASCII-safe for JSON\n * (no NaN escaping, no array-of-numbers bloat) while still round-tripping\n * losslessly to `Float32Array` for in-process cosine similarity.\n */\n\n/** Encode a `Float32Array` to a base64 string. */\nexport function encodeFloat32(vec: Float32Array): string {\n // Wrap the underlying buffer in a Uint8Array view, then base64-encode it.\n const bytes = new Uint8Array(vec.buffer, vec.byteOffset, vec.byteLength);\n return Buffer.from(bytes).toString('base64');\n}\n\n/** Decode a base64 string back into a `Float32Array`. */\nexport function decodeFloat32(b64: string): Float32Array {\n const buf = Buffer.from(b64, 'base64');\n // Copy into a fresh ArrayBuffer so the returned view is not tied to Buffer's\n // pooled allocator (Buffer instances may share memory with other Buffers).\n const ab = new ArrayBuffer(buf.byteLength);\n new Uint8Array(ab).set(buf);\n return new Float32Array(ab);\n}\n\n/**\n * Cosine similarity in `[-1, 1]`. Returns `0` for zero-length inputs or when\n * either vector has zero magnitude. Both vectors must have the same length —\n * mismatched lengths throw to surface programmer error early.\n */\nexport function cosineSimilarity(a: Float32Array, b: Float32Array): number {\n if (a.length !== b.length) {\n throw new Error(`cosineSimilarity: length mismatch (${a.length} vs ${b.length})`);\n }\n if (a.length === 0) return 0;\n\n let dot = 0;\n let normA = 0;\n let normB = 0;\n for (let i = 0; i < a.length; i++) {\n const x = a[i];\n const y = b[i];\n dot += x * y;\n normA += x * x;\n normB += y * y;\n }\n\n if (normA === 0 || normB === 0) return 0;\n return dot / (Math.sqrt(normA) * Math.sqrt(normB));\n}\n","import fsPromises from 'fs/promises';\nimport path from 'path';\n\n/** Read a JSON content file from local disk. Returns null on ENOENT or parse error. */\nexport async function readLocalContentFile(filePath: string): Promise<unknown | null> {\n const fullPath = path.join(process.cwd(), filePath);\n try {\n const data = await fsPromises.readFile(fullPath, { encoding: 'utf8' });\n return JSON.parse(data);\n } catch (error: any) {\n if (error?.code === 'ENOENT' || error instanceof SyntaxError) return null;\n throw error;\n }\n}\n\n/** Read a raw text file (e.g. companion .md) from local disk. Returns '' on ENOENT. */\nexport async function readLocalRawFile(filePath: string): Promise<string> {\n const fullPath = path.join(process.cwd(), filePath);\n try {\n return await fsPromises.readFile(fullPath, { encoding: 'utf8' });\n } catch (error: any) {\n if (error?.code === 'ENOENT') return '';\n throw error;\n }\n}\n\n/**\n * List all files under `dirPath` whose name ends with `ext`, recursively.\n * Returns paths like `dirPath/sub/file.ext` with forward slashes, sorted.\n * Returns [] if the directory does not exist.\n */\nexport async function listLocalFilesRecursive(dirPath: string, ext: string): Promise<string[]> {\n return listLocalFilesWithExtensions(dirPath, [ext], true);\n}\n\n/**\n * List files in `dirPath` whose name ends with any of the given extensions.\n * Pass `recursive: true` to descend into subdirectories.\n * Extensions may include or omit the leading dot.\n * Returns [] if the directory does not exist.\n */\nexport async function listLocalFilesWithExtensions(\n dirPath: string,\n extensions: string[],\n recursive = false,\n): Promise<string[]> {\n const fullDir = path.join(process.cwd(), dirPath);\n const normalizedExts = extensions.map((e) => (e.startsWith('.') ? e : `.${e}`));\n\n try {\n if (recursive) {\n const names = (await fsPromises.readdir(fullDir, { recursive: true })) as unknown as string[];\n return names\n .filter((n) => normalizedExts.some((e) => n.endsWith(e)))\n .map((n) => `${dirPath}/${n}`.replace(/\\\\/g, '/'))\n .sort();\n }\n\n const entries = await fsPromises.readdir(fullDir, { withFileTypes: true });\n return (entries as import('fs').Dirent[])\n .filter((e) => e.isFile() && normalizedExts.some((ext) => e.name.endsWith(ext)))\n .map((e) => `${dirPath}/${e.name}`)\n .sort();\n } catch (error: any) {\n if (error?.code === 'ENOENT') return [];\n throw error;\n }\n}\n\n/** List .json files in a collection directory on local disk. Returns [] if directory does not exist. */\nexport async function listLocalCollectionFiles(dirPath: string): Promise<string[]> {\n const fullDir = path.join(process.cwd(), dirPath);\n try {\n const entries = await fsPromises.readdir(fullDir, { withFileTypes: true });\n return entries.filter((e) => e.isFile() && e.name.endsWith('.json')).map((e) => `${dirPath}/${e.name}`);\n } catch (error: any) {\n if (error?.code === 'ENOENT') return [];\n throw error;\n }\n}\n"],"mappings":"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","names":["fsPromises","fsPromises","path","path","fsPromises","fsPromises"]}