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.
- package/dist/admin/actions/entries.js +46 -45
- package/dist/admin/actions/entries.js.map +1 -1
- package/dist/admin/actions/files.d.ts +5 -0
- package/dist/admin/actions/files.d.ts.map +1 -1
- package/dist/admin/actions/files.js +48 -9
- package/dist/admin/actions/files.js.map +1 -1
- package/dist/admin/actions/search.js +2 -2
- package/dist/admin/actions/search.js.map +1 -1
- package/dist/agent/embedder.js +1 -1
- package/dist/agent/embedder.js.map +1 -1
- package/dist/agent/index.cjs +118 -41
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/tools/index.js +1 -1
- package/dist/agent/tools/index.js.map +1 -1
- package/dist/chunk-NAHOP7TG.js +7 -0
- package/dist/{chunk-L27J3XWV.js.map → chunk-NAHOP7TG.js.map} +1 -1
- package/dist/{chunk-TG624CCO.js → chunk-ZYUK2J5L.js} +4 -43
- package/dist/chunk-ZYUK2J5L.js.map +1 -0
- package/dist/cli/index.js +6 -6
- package/dist/cli/lib/codegen.d.ts +2 -8
- package/dist/cli/lib/codegen.d.ts.map +1 -1
- package/dist/cli/lib/codegen.js +11 -18
- package/dist/cli/lib/codegen.js.map +1 -1
- package/dist/cli/lib/project.js +2 -41
- package/dist/cli/lib/project.js.map +1 -1
- package/dist/components/FormReferenceField.d.ts.map +1 -1
- package/dist/components/FormReferenceField.js +1 -1
- package/dist/components/FormReferenceField.js.map +1 -1
- package/dist/{embeddingsGen-7MXSZQ43.js → embeddingsGen-FXWCPGB7.js} +39 -8
- package/dist/embeddingsGen-FXWCPGB7.js.map +1 -0
- package/dist/index.cjs +88 -68
- package/dist/index.cjs.map +1 -1
- package/dist/{init-N6K464EZ.js → init-KWH66PKY.js} +2 -2
- package/dist/lib/localReader.d.ts +13 -0
- package/dist/lib/localReader.d.ts.map +1 -1
- package/dist/lib/localReader.js +20 -0
- package/dist/lib/localReader.js.map +1 -1
- package/dist/lib/publicSearchIndex.d.ts.map +1 -1
- package/dist/lib/publicSearchIndex.js +2 -2
- package/dist/lib/publicSearchIndex.js.map +1 -1
- package/dist/query.cjs +88 -68
- package/dist/query.cjs.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/{typesGen-EYRZAA56.js → typesGen-MFAL3B4V.js} +32 -6
- package/dist/typesGen-MFAL3B4V.js.map +1 -0
- package/dist/{validate-6AFW6U2X.js → validate-AAW4IGWD.js} +2 -2
- package/package.json +1 -1
- package/dist/admin/consts.d.ts +0 -3
- package/dist/admin/consts.d.ts.map +0 -1
- package/dist/admin/consts.js +0 -24
- package/dist/admin/consts.js.map +0 -1
- package/dist/chunk-L27J3XWV.js +0 -7
- package/dist/chunk-TG624CCO.js.map +0 -1
- package/dist/embeddingsGen-7MXSZQ43.js.map +0 -1
- package/dist/typesGen-EYRZAA56.js.map +0 -1
- /package/dist/{init-N6K464EZ.js.map → init-KWH66PKY.js.map} +0 -0
- /package/dist/{validate-6AFW6U2X.js.map → validate-AAW4IGWD.js.map} +0 -0
|
@@ -4,15 +4,13 @@ import "./registerConfig";
|
|
|
4
4
|
import fsPromises from "fs/promises";
|
|
5
5
|
import path from "path";
|
|
6
6
|
import { getConfig } from "../../lib/configStore";
|
|
7
|
-
import { getContentFiles, getFile } from "./files";
|
|
7
|
+
import { getContentFiles, getFile, getFileJson } from "./files";
|
|
8
8
|
import { isProductionMode } from "../github";
|
|
9
9
|
import { getMediaEntries } from "./media";
|
|
10
10
|
import { getEntryTitleField } from "./utils";
|
|
11
11
|
const getEntryList = async (collection = "**") => {
|
|
12
|
-
var _a, _b, _c;
|
|
13
12
|
const config = getConfig();
|
|
14
13
|
const files = await getContentFiles(collection);
|
|
15
|
-
const entries = [];
|
|
16
14
|
const mediaList = await getMediaEntries().catch(() => []);
|
|
17
15
|
const mediaById = /* @__PURE__ */ new Map();
|
|
18
16
|
for (const m of mediaList) {
|
|
@@ -20,60 +18,63 @@ const getEntryList = async (collection = "**") => {
|
|
|
20
18
|
}
|
|
21
19
|
const imageFieldKeyByType = /* @__PURE__ */ new Map();
|
|
22
20
|
function firstImageFieldKey(type) {
|
|
23
|
-
var
|
|
24
|
-
if (imageFieldKeyByType.has(type)) return (
|
|
21
|
+
var _a, _b;
|
|
22
|
+
if (imageFieldKeyByType.has(type)) return (_a = imageFieldKeyByType.get(type)) != null ? _a : null;
|
|
25
23
|
const collection2 = config.collections[type];
|
|
26
24
|
if (!collection2) {
|
|
27
25
|
imageFieldKeyByType.set(type, null);
|
|
28
26
|
return null;
|
|
29
27
|
}
|
|
30
|
-
const key = (
|
|
28
|
+
const key = (_b = Object.keys(collection2.fields).find((k) => collection2.fields[k].format === "image")) != null ? _b : null;
|
|
31
29
|
imageFieldKeyByType.set(type, key);
|
|
32
30
|
return key;
|
|
33
31
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (imgKey) {
|
|
51
|
-
const value = (_b = content == null ? void 0 : content.fields) == null ? void 0 : _b[imgKey];
|
|
52
|
-
if (typeof value === "string" && value.trim()) {
|
|
53
|
-
const hit = mediaById.get(value.trim());
|
|
54
|
-
if (hit) thumbnailUrl = hit.publicUrl;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
if ((_c = content == null ? void 0 : content.sys) == null ? void 0 : _c.status) {
|
|
58
|
-
status = content.sys.status;
|
|
59
|
-
}
|
|
60
|
-
} catch (_e) {
|
|
61
|
-
}
|
|
62
|
-
if (!isProductionMode()) {
|
|
63
|
-
try {
|
|
64
|
-
const stat = await fsPromises.stat(path.join(
|
|
32
|
+
const isProd = isProductionMode();
|
|
33
|
+
const items = await Promise.all(
|
|
34
|
+
files.map(async (file) => {
|
|
35
|
+
var _a, _b, _c;
|
|
36
|
+
const nameWithoutFolder = file.replace(`${config.contentFolder}/`, "").replace(".json", "");
|
|
37
|
+
const parts = nameWithoutFolder.split("/");
|
|
38
|
+
const type = parts[0];
|
|
39
|
+
const id = parts[parts.length - 1];
|
|
40
|
+
const titleField = getEntryTitleField(type);
|
|
41
|
+
let title = id;
|
|
42
|
+
let status = "merged";
|
|
43
|
+
let updatedAt;
|
|
44
|
+
let thumbnailUrl;
|
|
45
|
+
const [contentResult, statResult] = await Promise.allSettled([
|
|
46
|
+
getFileJson(file),
|
|
47
|
+
isProd ? Promise.resolve(null) : fsPromises.stat(path.join(
|
|
65
48
|
/*turbopackIgnore: true*/
|
|
66
49
|
process.cwd(),
|
|
67
50
|
file
|
|
68
|
-
))
|
|
69
|
-
|
|
70
|
-
|
|
51
|
+
))
|
|
52
|
+
]);
|
|
53
|
+
if (contentResult.status === "fulfilled" && contentResult.value) {
|
|
54
|
+
const content = contentResult.value;
|
|
55
|
+
if (titleField && ((_a = content == null ? void 0 : content.fields) == null ? void 0 : _a[titleField])) {
|
|
56
|
+
title = content.fields[titleField];
|
|
57
|
+
}
|
|
58
|
+
const imgKey = firstImageFieldKey(type);
|
|
59
|
+
if (imgKey) {
|
|
60
|
+
const value = (_b = content == null ? void 0 : content.fields) == null ? void 0 : _b[imgKey];
|
|
61
|
+
if (typeof value === "string" && value.trim()) {
|
|
62
|
+
const hit = mediaById.get(value.trim());
|
|
63
|
+
if (hit) thumbnailUrl = hit.publicUrl;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if ((_c = content == null ? void 0 : content.sys) == null ? void 0 : _c.status) {
|
|
67
|
+
status = content.sys.status;
|
|
68
|
+
}
|
|
71
69
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
70
|
+
if (!isProd && statResult.status === "fulfilled" && statResult.value) {
|
|
71
|
+
updatedAt = statResult.value.mtime.toISOString();
|
|
72
|
+
}
|
|
73
|
+
return { type, id, path: file, title, status, updatedAt, thumbnailUrl };
|
|
74
|
+
})
|
|
75
|
+
);
|
|
76
|
+
items.sort((a, b) => a.title.localeCompare(b.title));
|
|
77
|
+
return items;
|
|
77
78
|
};
|
|
78
79
|
const getEntryBacklinks = async (targetReferenceKey) => {
|
|
79
80
|
var _a, _b, _c, _d;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../admin/actions/entries.ts"],"sourcesContent":["'use server';\n\nimport './registerConfig';\n\nimport fsPromises from 'fs/promises';\nimport path from 'path';\n\nimport { getConfig } from '../../lib/configStore';\nimport type { Config } from '../types';\nimport type { EntryListItem, EntryStatus } from '../../types';\n\nimport { getContentFiles, getFile } from './files';\nimport { isProductionMode } from '../github';\nimport { getMediaEntries } from './media';\nimport { getEntryTitleField } from './utils';\n\nexport const getEntryList = async (collection: string = '**'): Promise<EntryListItem[]> => {\n const config = getConfig();\n const files = await getContentFiles(collection);\n
|
|
1
|
+
{"version":3,"sources":["../../../admin/actions/entries.ts"],"sourcesContent":["'use server';\n\nimport './registerConfig';\n\nimport fsPromises from 'fs/promises';\nimport path from 'path';\n\nimport { getConfig } from '../../lib/configStore';\nimport type { Config } from '../types';\nimport type { EntryListItem, EntryStatus } from '../../types';\n\nimport { getContentFiles, getFile, getFileJson } from './files';\nimport { isProductionMode } from '../github';\nimport { getMediaEntries } from './media';\nimport { getEntryTitleField } from './utils';\n\nexport const getEntryList = async (collection: string = '**'): Promise<EntryListItem[]> => {\n const config = getConfig();\n const files = await getContentFiles(collection);\n // Build a media lookup so we can resolve thumbnail URLs for any entry that\n // has an `image` field. One batched call regardless of entry count.\n const mediaList = await getMediaEntries().catch(() => []);\n const mediaById = new Map<string, { ext: string; publicUrl: string }>();\n for (const m of mediaList) {\n mediaById.set(m.id, { ext: m.extension, publicUrl: m.publicUrl });\n }\n\n const imageFieldKeyByType = new Map<string, string | null>();\n function firstImageFieldKey(type: string): string | null {\n if (imageFieldKeyByType.has(type)) return imageFieldKeyByType.get(type) ?? null;\n const collection = (config as Config).collections[type as keyof Config['collections']];\n if (!collection) {\n imageFieldKeyByType.set(type, null);\n return null;\n }\n const key = Object.keys(collection.fields).find((k) => collection.fields[k].format === 'image') ?? null;\n imageFieldKeyByType.set(type, key);\n return key;\n }\n\n const isProd = isProductionMode();\n\n const items = await Promise.all(\n files.map(async (file) => {\n const nameWithoutFolder = file.replace(`${config.contentFolder}/`, '').replace('.json', '');\n const parts = nameWithoutFolder.split('/');\n const type = parts[0];\n const id = parts[parts.length - 1];\n const titleField = getEntryTitleField(type);\n\n let title = id;\n let status: EntryStatus = 'merged';\n let updatedAt: string | undefined;\n let thumbnailUrl: string | undefined;\n\n const [contentResult, statResult] = await Promise.allSettled([\n getFileJson(file),\n isProd ? Promise.resolve(null) : fsPromises.stat(path.join(/*turbopackIgnore: true*/ process.cwd(), file)),\n ]);\n\n if (contentResult.status === 'fulfilled' && contentResult.value) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const content = contentResult.value as any;\n if (titleField && content?.fields?.[titleField]) {\n title = content.fields[titleField];\n }\n const imgKey = firstImageFieldKey(type);\n if (imgKey) {\n const value = content?.fields?.[imgKey];\n if (typeof value === 'string' && value.trim()) {\n const hit = mediaById.get(value.trim());\n if (hit) thumbnailUrl = hit.publicUrl;\n }\n }\n if (content?.sys?.status) {\n status = content.sys.status;\n }\n }\n\n if (!isProd && statResult.status === 'fulfilled' && statResult.value) {\n updatedAt = (statResult.value as { mtime: Date }).mtime.toISOString();\n }\n\n return { type, id, path: file, title, status, updatedAt, thumbnailUrl };\n }),\n );\n\n items.sort((a, b) => a.title.localeCompare(b.title));\n return items;\n};\n\n/**\n * Find all content entries that reference the given entry via reference fields.\n * Returns entries that contain `targetReferenceKey` in any reference field value.\n */\nexport const getEntryBacklinks = async (targetReferenceKey: string): Promise<EntryListItem[]> => {\n const config = getConfig();\n const allFiles = await getContentFiles('**');\n const backlinks: EntryListItem[] = [];\n\n for (const file of allFiles) {\n try {\n const content = await getFile(file);\n const type = content?.sys?.type;\n if (!type) continue;\n\n const collection = config.collections[type as keyof Config['collections']];\n if (!collection) continue;\n\n const referenceFieldKeys = Object.keys(collection.fields).filter(\n (k) => collection.fields[k].format === 'reference',\n );\n if (referenceFieldKeys.length === 0) continue;\n\n let found = false;\n for (const fieldKey of referenceFieldKeys) {\n const fieldValue = content?.fields?.[fieldKey];\n if (!fieldValue) continue;\n\n // Reference values can be a single string (cardinality 'one') or a JSON array string (cardinality 'many')\n let keys: string[] = [];\n if (typeof fieldValue === 'string') {\n try {\n const parsed = JSON.parse(fieldValue);\n keys = Array.isArray(parsed) ? parsed : [fieldValue];\n } catch {\n keys = [fieldValue];\n }\n } else if (Array.isArray(fieldValue)) {\n keys = fieldValue;\n }\n\n if (keys.includes(targetReferenceKey)) {\n found = true;\n break;\n }\n }\n\n if (found) {\n const nameWithoutFolder = file.replace(`${config.contentFolder}/`, '').replace('.json', '');\n const parts = nameWithoutFolder.split('/');\n const id = parts[parts.length - 1];\n const titleField = getEntryTitleField(type);\n let title = id;\n if (titleField && content?.fields?.[titleField]) {\n title = content.fields[titleField];\n }\n const status: EntryStatus = content?.sys?.status || 'merged';\n backlinks.push({ type, id, path: file, title, status });\n }\n } catch (_e) {\n // Skip files that can't be read\n }\n }\n\n return backlinks;\n};\n"],"mappings":"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","names":["collection"]}
|
|
@@ -16,6 +16,11 @@ export declare const getContentFiles: (collection?: string) => Promise<string[]>
|
|
|
16
16
|
*/
|
|
17
17
|
export declare const getMediaContentFiles: () => Promise<string[]>;
|
|
18
18
|
export declare const getMediaFiles: (folder?: string) => Promise<string[]>;
|
|
19
|
+
/**
|
|
20
|
+
* Read only the raw entry JSON — no companion markdown/richtext merging.
|
|
21
|
+
* Use for list views where companion content is never needed.
|
|
22
|
+
*/
|
|
23
|
+
export declare const getFileJson: (fileName: string) => Promise<Record<string, unknown> | null>;
|
|
19
24
|
export declare const getFile: (fileName: string) => Promise<any>;
|
|
20
25
|
export declare const saveFile: (formData: any, fileName: string, options?: {
|
|
21
26
|
skipStatusTransition?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../../admin/actions/files.ts"],"names":[],"mappings":"AAEA,OAAO,kBAAkB,CAAC;AA+B1B,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAgM9C;;GAEG;AACH,eAAO,MAAM,sCAAsC,QAAa,OAAO,CAAC,IAAI,CAQ3E,CAAC;AAEF,eAAO,MAAM,4BAA4B,GAAU,UAAU,MAAM,EAAE,iBAAiB,MAAM,EAAE,UAAU,MAAM,kBAyB7G,CAAC;AAEF,eAAO,MAAM,eAAe,GAAU,aAAY,MAAa,
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../../admin/actions/files.ts"],"names":[],"mappings":"AAEA,OAAO,kBAAkB,CAAC;AA+B1B,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAgM9C;;GAEG;AACH,eAAO,MAAM,sCAAsC,QAAa,OAAO,CAAC,IAAI,CAQ3E,CAAC;AAEF,eAAO,MAAM,4BAA4B,GAAU,UAAU,MAAM,EAAE,iBAAiB,MAAM,EAAE,UAAU,MAAM,kBAyB7G,CAAC;AAEF,eAAO,MAAM,eAAe,GAAU,aAAY,MAAa,sBAkC9D,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,QAAa,OAAO,CAAC,MAAM,EAAE,CAiB7D,CAAC;AAEF,eAAO,MAAM,aAAa,GAAU,SAAQ,MAAa,sBAqBxD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,WAAW,GAAU,UAAU,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAgC1F,CAAC;AAEF,eAAO,MAAM,OAAO,GAAU,UAAU,MAAM,iBAkF7C,CAAC;AAuBF,eAAO,MAAM,QAAQ,GACnB,UAAU,GAAG,EACb,UAAU,MAAM,EAChB,UAAU;IAAE,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAAE,KAC3C,OAAO,CAAC,cAAc,CAoJxB,CAAC;AAEF,eAAO,MAAM,OAAO,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,aAAa,CAoDjE,CAAC;AAEF,eAAO,MAAM,UAAU,GAAU,UAAU,MAAM,KAAG,OAAO,CAAC,YAAY,CA2DvE,CAAC"}
|
|
@@ -6,9 +6,9 @@ import {
|
|
|
6
6
|
import "./registerConfig";
|
|
7
7
|
import fsPromises from "fs/promises";
|
|
8
8
|
import path from "path";
|
|
9
|
-
import { glob } from "glob";
|
|
10
9
|
import { cookies } from "next/headers";
|
|
11
10
|
import { getConfig } from "../../lib/configStore";
|
|
11
|
+
import { listLocalCollectionFiles, listLocalFilesRecursive, listLocalFilesWithExtensions } from "../../lib/localReader";
|
|
12
12
|
import { getAgentConfig } from "../../agent/configStore";
|
|
13
13
|
import { syncEmbeddingsAfterRemove, syncEmbeddingsAfterUpsert } from "../../agent/embeddingsHook";
|
|
14
14
|
import { BRANCH_HISTORY_FILE_PATH, mergeHistoryContentWithAppendedEntry } from "../../lib/branchHistory";
|
|
@@ -236,8 +236,10 @@ const getContentFiles = async (collection = "**") => {
|
|
|
236
236
|
} catch (e) {
|
|
237
237
|
}
|
|
238
238
|
}
|
|
239
|
-
|
|
240
|
-
|
|
239
|
+
if (collection === "**") {
|
|
240
|
+
return await listLocalFilesRecursive(config.contentFolder, ".json");
|
|
241
|
+
}
|
|
242
|
+
return await listLocalCollectionFiles(`${config.contentFolder}/${collection}`);
|
|
241
243
|
} catch (e) {
|
|
242
244
|
return [];
|
|
243
245
|
}
|
|
@@ -253,8 +255,7 @@ const getMediaContentFiles = async () => {
|
|
|
253
255
|
} catch (_e) {
|
|
254
256
|
}
|
|
255
257
|
}
|
|
256
|
-
|
|
257
|
-
return files || [];
|
|
258
|
+
return await listLocalCollectionFiles(folder);
|
|
258
259
|
} catch (_e) {
|
|
259
260
|
return [];
|
|
260
261
|
}
|
|
@@ -266,19 +267,56 @@ const getMediaFiles = async (folder = "**") => {
|
|
|
266
267
|
if (isProductionMode()) {
|
|
267
268
|
const activeBranch = (_a = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)) == null ? void 0 : _a.value;
|
|
268
269
|
try {
|
|
269
|
-
const
|
|
270
|
-
const allFiles = await listGitHubFilesRecursive(
|
|
270
|
+
const dir2 = folder === "**" ? config.mediaFolder : `${config.mediaFolder}/${folder}`;
|
|
271
|
+
const allFiles = await listGitHubFilesRecursive(dir2, void 0, activeBranch);
|
|
271
272
|
const extensions = config.mediaAllowedFormats;
|
|
272
273
|
return allFiles.filter((f) => extensions.some((ext) => f.endsWith(`.${ext}`)));
|
|
273
274
|
} catch (e) {
|
|
274
275
|
}
|
|
275
276
|
}
|
|
276
|
-
const
|
|
277
|
-
return
|
|
277
|
+
const dir = folder === "**" ? config.mediaFolder : `${config.mediaFolder}/${folder}`;
|
|
278
|
+
return await listLocalFilesWithExtensions(dir, config.mediaAllowedFormats, folder === "**");
|
|
278
279
|
} catch (e) {
|
|
279
280
|
return [];
|
|
280
281
|
}
|
|
281
282
|
};
|
|
283
|
+
const getFileJson = async (fileName) => {
|
|
284
|
+
var _a;
|
|
285
|
+
if (isProductionMode()) {
|
|
286
|
+
let activeBranch;
|
|
287
|
+
try {
|
|
288
|
+
activeBranch = (_a = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)) == null ? void 0 : _a.value;
|
|
289
|
+
} catch (e) {
|
|
290
|
+
}
|
|
291
|
+
try {
|
|
292
|
+
const stored = await getStoredFile(fileName, activeBranch);
|
|
293
|
+
if (stored) return structuredClone(stored.content);
|
|
294
|
+
} catch (e) {
|
|
295
|
+
}
|
|
296
|
+
try {
|
|
297
|
+
const result = await getGitHubFile(fileName, activeBranch);
|
|
298
|
+
if (result) return JSON.parse(result.content);
|
|
299
|
+
} catch (e) {
|
|
300
|
+
}
|
|
301
|
+
try {
|
|
302
|
+
const raw = await readGitHubFilePublic(fileName, activeBranch);
|
|
303
|
+
if (raw) return JSON.parse(raw);
|
|
304
|
+
} catch (e) {
|
|
305
|
+
}
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
try {
|
|
309
|
+
const filePath = path.join(
|
|
310
|
+
/*turbopackIgnore: true*/
|
|
311
|
+
process.cwd(),
|
|
312
|
+
fileName
|
|
313
|
+
);
|
|
314
|
+
const data = await fsPromises.readFile(filePath, { encoding: "utf8" });
|
|
315
|
+
return JSON.parse(data);
|
|
316
|
+
} catch (e) {
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
};
|
|
282
320
|
const getFile = async (fileName) => {
|
|
283
321
|
var _a, _b, _c, _d;
|
|
284
322
|
const config = getConfig();
|
|
@@ -619,6 +657,7 @@ export {
|
|
|
619
657
|
assertFeatureBranchForWritesIfRequired,
|
|
620
658
|
getContentFiles,
|
|
621
659
|
getFile,
|
|
660
|
+
getFileJson,
|
|
622
661
|
getMediaContentFiles,
|
|
623
662
|
getMediaFiles,
|
|
624
663
|
newFile,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../admin/actions/files.ts"],"sourcesContent":["'use server';\n\nimport './registerConfig';\n\nimport fsPromises from 'fs/promises';\nimport path from 'path';\n\nimport { glob } from 'glob';\nimport { cookies } from 'next/headers';\n\nimport { getConfig } from '../../lib/configStore';\nimport { getAgentConfig } from '../../agent/configStore';\nimport { syncEmbeddingsAfterRemove, syncEmbeddingsAfterUpsert } from '../../agent/embeddingsHook';\nimport { BRANCH_HISTORY_FILE_PATH, mergeHistoryContentWithAppendedEntry } from '../../lib/branchHistory';\nimport { companionMarkdownPathsForEntry, companionRichTextPathsForEntry } from '../../lib/companionMarkdown';\nimport { initialFieldsForNewEntry } from '../../lib/initialEntryFields';\nimport { persistedFieldsFromFormStrings } from '../../lib/persistedFormFields';\nimport { normalizeStoredSlug } from '../../lib/slugField';\nimport { validateEntryFields } from '../../lib/validateEntryFields';\nimport type { Config } from '../types';\n\nimport {\n deleteGitHubFile,\n getGitHubFile,\n isProductionMode,\n listGitHubFiles,\n listGitHubFilesRecursive,\n readGitHubFilePublic,\n saveGitHubFile,\n} from '../github';\nimport { applyMutation, getStoredContentFiles, getStoredFile, getStoredFileSha } from '../store/contentStore';\nimport { mediaContentFolder, mediaEntryPath } from '../../lib/mediaPath';\nimport { buildJsons } from './build';\nimport {\n actionErr,\n actionOk,\n getErrorMessage,\n type ActionResult,\n type NewFileResult,\n type SaveFileResult,\n} from './utils';\n\nexport type { SaveFileResult } from './utils';\n\nconst CMS_ACTIVE_BRANCH_COOKIE = 'cms-active-branch';\n\nfunction normalizeContentPath(p: string): string {\n return p.replace(/\\\\/g, '/');\n}\n\n/** Best-effort embedding store sync after content writes. No-op when the agent isn't configured. */\nasync function syncEmbeddingsForUpsertIfEnabled(\n entryPath: string,\n payload: { sys?: { type?: string }; fields?: Record<string, unknown> },\n companions: Record<string, string>,\n branch: string | undefined,\n isProduction: boolean,\n): Promise<void> {\n const agentConfig = getAgentConfig();\n if (!agentConfig) return;\n const config = getConfig();\n await syncEmbeddingsAfterUpsert({ agentConfig, config, entryPath, payload, companions, branch, isProduction });\n}\n\nasync function syncEmbeddingsForRemoveIfEnabled(\n entryPath: string,\n branch: string | undefined,\n isProduction: boolean,\n): Promise<void> {\n const agentConfig = getAgentConfig();\n if (!agentConfig) return;\n await syncEmbeddingsAfterRemove({ agentConfig, entryPath, branch, isProduction });\n}\n\n/** Records the entry path under the active branch in `cms/branch-history.json` (GitHub or local). Best-effort. */\nasync function persistBranchHistoryEntryIfNeeded(activeBranch: string | undefined, entryPath: string): Promise<void> {\n if (!activeBranch) {\n return;\n }\n\n const normalized = normalizeContentPath(entryPath);\n\n if (isProductionMode()) {\n try {\n const historyFile = await getGitHubFile(BRANCH_HISTORY_FILE_PATH, activeBranch);\n const next = mergeHistoryContentWithAppendedEntry(historyFile?.content ?? '', activeBranch, normalized);\n if (next == null) {\n return;\n }\n\n await saveGitHubFile(BRANCH_HISTORY_FILE_PATH, next, 'CMS: track entry in branch history', activeBranch);\n } catch {\n /* best-effort sidecar — primary save must still succeed */\n }\n\n return;\n }\n\n try {\n const abs = path.join(/*turbopackIgnore: true*/ process.cwd(), BRANCH_HISTORY_FILE_PATH);\n let raw = '';\n try {\n raw = await fsPromises.readFile(abs, { encoding: 'utf8' });\n } catch {\n raw = '';\n }\n\n const next = mergeHistoryContentWithAppendedEntry(raw, activeBranch, normalized);\n if (next == null) {\n return;\n }\n\n await fsPromises.mkdir(path.dirname(abs), { recursive: true });\n await fsPromises.writeFile(abs, next, 'utf8');\n } catch {\n /* best-effort sidecar */\n }\n}\n\nasync function assertSlugFieldsUnique(\n entryType: string,\n fileName: string,\n persistedFields: Record<string, unknown>,\n): Promise<{ ok: true } | { ok: false; fieldKey: string; message: string }> {\n const config = getConfig();\n const col = config.collections[entryType as keyof Config['collections']];\n if (!col) {\n return { ok: true };\n }\n\n const slugFieldEntries = Object.entries(col.fields).filter(([, def]) => def.format === 'slug');\n if (slugFieldEntries.length === 0) {\n return { ok: true };\n }\n\n const selfPath = normalizeContentPath(fileName);\n const siblings = await getContentFiles(entryType);\n if (!Array.isArray(siblings)) {\n return { ok: true };\n }\n\n for (const [slugKey] of slugFieldEntries) {\n const candidate = persistedFields[slugKey];\n if (typeof candidate !== 'string' || !candidate.trim()) {\n continue;\n }\n const normCandidate = normalizeStoredSlug(candidate);\n if (!normCandidate) {\n continue;\n }\n\n for (const siblingPath of siblings) {\n if (normalizeContentPath(siblingPath) === selfPath) {\n continue;\n }\n let data: unknown;\n try {\n data = await getFile(siblingPath);\n } catch {\n continue;\n }\n if (!data || typeof data !== 'object') {\n continue;\n }\n const fields = (data as { fields?: Record<string, unknown> }).fields;\n if (!fields) {\n continue;\n }\n const other = fields[slugKey];\n if (typeof other !== 'string' || !other.trim()) {\n continue;\n }\n if (normalizeStoredSlug(other) === normCandidate) {\n return {\n ok: false,\n fieldKey: slugKey,\n message: `Another ${col.label} already uses this slug. Choose a different value.`,\n };\n }\n }\n }\n\n return { ok: true };\n}\n\nconst MEDIA_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\nasync function assertImageFieldsReferenceMediaWithTitle(\n entryType: string,\n strFields: Record<string, string>,\n): Promise<{ ok: true } | { ok: false; fieldKey: string; message: string }> {\n const config = getConfig();\n const col = config.collections[entryType as keyof Config['collections']];\n if (!col) {\n return { ok: true };\n }\n\n for (const [key, def] of Object.entries(col.fields)) {\n if (def.format !== 'image') {\n continue;\n }\n const raw = (strFields[key] ?? '').trim();\n if (!raw || raw.startsWith('/')) {\n continue;\n }\n if (!MEDIA_UUID_RE.test(raw)) {\n continue;\n }\n\n const mediaPath = mediaEntryPath(raw);\n let media: unknown;\n try {\n media = await getFile(mediaPath);\n } catch {\n return {\n ok: false,\n fieldKey: key,\n message: `Media not found for ${def.label}. Choose a valid image in the Media library.`,\n };\n }\n\n const title = (media as { fields?: { title?: unknown } })?.fields?.title;\n if (typeof title !== 'string' || !title.trim()) {\n return {\n ok: false,\n fieldKey: key,\n message: `Selected image is missing a required Title; fix it in the Media library.`,\n };\n }\n }\n\n return { ok: true };\n}\n\n/**\n * In production, reject writes that would target `config.git.baseBranch` (no feature branch cookie).\n */\nexport const assertFeatureBranchForWritesIfRequired = async (): Promise<void> => {\n if (!isProductionMode()) {\n return;\n }\n\n if (!(await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value) {\n throw new Error('Create or select a branch before editing.');\n }\n};\n\nexport const waitForPublicReadConsistency = async (fileName: string, expectedContent: string, readRef?: string) => {\n if (!isProductionMode()) {\n return;\n }\n\n const parsedAttempts = Number.parseInt(process.env.CMS_GITHUB_CONSISTENCY_ATTEMPTS || '8', 10);\n const parsedDelayMs = Number.parseInt(process.env.CMS_GITHUB_CONSISTENCY_DELAY_MS || '250', 10);\n const maxAttempts = Number.isFinite(parsedAttempts) && parsedAttempts > 0 ? parsedAttempts : 1;\n const delayMs = Number.isFinite(parsedDelayMs) && parsedDelayMs >= 0 ? parsedDelayMs : 250;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n const visibleContent = await readGitHubFilePublic(fileName, readRef);\n\n if (visibleContent === expectedContent) {\n return;\n }\n } catch (_e) {\n // Ignore transient read failures and retry within the same save request.\n }\n\n if (attempt < maxAttempts && delayMs > 0) {\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n }\n }\n};\n\nexport const getContentFiles = async (collection: string = '**') => {\n const config = getConfig();\n try {\n if (isProductionMode()) {\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n\n // Try in-memory store first (instant)\n try {\n const stored = await getStoredContentFiles(collection, activeBranch);\n if (stored) return stored;\n } catch {\n // Store unavailable — fall through to direct GitHub API\n }\n\n try {\n if (collection === '**') {\n const listed = await listGitHubFilesRecursive(config.contentFolder, '.json', activeBranch);\n return Array.isArray(listed) ? listed : [];\n }\n\n const listed = await listGitHubFiles(`${config.contentFolder}/${collection}`, '.json', activeBranch);\n return Array.isArray(listed) ? listed : [];\n } catch (e) {\n // Fall back to local files if GitHub API access is not available.\n }\n }\n\n const files = await glob(`${config.contentFolder}/${collection}/*.json`);\n return files || [];\n } catch (e) {\n return [];\n }\n};\n\n/**\n * List media-entry JSON files (e.g. `cms/media/media-<uuid>.json`).\n *\n * Distinct from `getContentFiles`, which lists editorial content under\n * `config.contentFolder`, and from `getMediaFiles`, which lists physical image\n * binaries under `config.mediaFolder`. This is the JSON-entry layer.\n */\nexport const getMediaContentFiles = async (): Promise<string[]> => {\n const folder = mediaContentFolder();\n try {\n if (isProductionMode()) {\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n\n try {\n return await listGitHubFiles(folder, '.json', activeBranch);\n } catch (_e) {\n // Fall back to local files if GitHub API access is not available.\n }\n }\n\n const files = await glob(`${folder}/*.json`);\n return files || [];\n } catch (_e) {\n return [];\n }\n};\n\nexport const getMediaFiles = async (folder: string = '**') => {\n const config = getConfig();\n try {\n if (isProductionMode()) {\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n\n try {\n const dir = folder === '**' ? config.mediaFolder : `${config.mediaFolder}/${folder}`;\n const allFiles = await listGitHubFilesRecursive(dir, undefined, activeBranch);\n const extensions = config.mediaAllowedFormats;\n return allFiles.filter((f) => extensions.some((ext) => f.endsWith(`.${ext}`)));\n } catch (e) {\n // Fall back to local files if GitHub API access is not available.\n }\n }\n\n const files = await glob(`${config.mediaFolder}/${folder}/*.{${config.mediaAllowedFormats.join(',')}}`);\n return files || [];\n } catch (e) {\n return [];\n }\n};\n\nexport const getFile = async (fileName: string) => {\n const config = getConfig();\n try {\n let entry: any;\n\n if (isProductionMode()) {\n // Try in-memory store first (instant, includes pre-merged companions)\n try {\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n const stored = await getStoredFile(fileName, activeBranch);\n\n if (stored) {\n entry = structuredClone(stored.content);\n // Merge pre-cached companion markdown into fields\n if (entry.fields) {\n for (const [fieldName, mdContent] of Object.entries(stored.companionMarkdown)) {\n entry.fields[fieldName] = mdContent;\n }\n }\n return entry;\n }\n } catch {\n // Store unavailable — fall through to direct GitHub API\n }\n\n try {\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n const result = await getGitHubFile(fileName, activeBranch);\n\n if (result) {\n entry = JSON.parse(result.content);\n }\n } catch (e) {\n // Fall back to local file if GitHub API access is not available.\n }\n\n // Cold serverless instance: content store is empty. `getGitHubFile` may return null\n // (404 race) or throw before `readGitHubFilePublic`'s multi-client retry path runs.\n // Never fall through to `fs.readFile` on deploy — the repo is not on disk (ENOENT → 500).\n if (!entry) {\n try {\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n const raw = await readGitHubFilePublic(fileName, activeBranch);\n if (raw) {\n entry = JSON.parse(raw);\n }\n } catch {\n /* best-effort */\n }\n }\n }\n\n if (!entry) {\n if (isProductionMode()) {\n return {};\n }\n const filePath = path.join(/*turbopackIgnore: true*/ process.cwd(), fileName);\n const data = await fsPromises.readFile(filePath, { encoding: 'utf8' });\n entry = JSON.parse(data);\n }\n\n if (!entry) {\n return {};\n }\n\n // Merge companion markdown and richtext files into fields\n const collectionType = entry?.sys?.type;\n if (typeof collectionType === 'string' && entry.fields) {\n const companionPaths = companionMarkdownPathsForEntry(fileName, collectionType, config.collections);\n for (const [fieldName, mdPath] of Object.entries(companionPaths)) {\n entry.fields[fieldName] = await readCompanionMarkdownContent(mdPath);\n }\n const richTextPaths = companionRichTextPathsForEntry(fileName, collectionType, config.collections);\n for (const [fieldName, mdxPath] of Object.entries(richTextPaths)) {\n entry.fields[fieldName] = await readCompanionMarkdownContent(mdxPath);\n }\n }\n\n return entry;\n } catch (e) {\n throw new Error('Failed to get file');\n }\n};\n\n/** Read a companion markdown file as a string. Returns `\"\"` if the file does not exist. */\nasync function readCompanionMarkdownContent(filePath: string): Promise<string> {\n if (isProductionMode()) {\n try {\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n const result = await getGitHubFile(filePath, activeBranch);\n return result?.content ?? '';\n } catch {\n return '';\n }\n }\n\n try {\n return await fsPromises.readFile(path.join(/*turbopackIgnore: true*/ process.cwd(), filePath), {\n encoding: 'utf8',\n });\n } catch {\n return '';\n }\n}\n\nexport const saveFile = async (\n formData: any,\n fileName: string,\n options?: { skipStatusTransition?: boolean },\n): Promise<SaveFileResult> => {\n const config = getConfig();\n try {\n let payload = formData;\n const entryType = payload?.sys?.type;\n const rawFields = payload?.fields;\n if (typeof entryType === 'string' && rawFields && typeof rawFields === 'object' && !Array.isArray(rawFields)) {\n const strFields: Record<string, string> = {};\n for (const [k, v] of Object.entries(rawFields)) {\n if (v == null) {\n strFields[k] = '';\n } else if (typeof v === 'object') {\n strFields[k] = JSON.stringify(v);\n } else {\n strFields[k] = String(v);\n }\n }\n const validated = validateEntryFields(entryType, strFields);\n if (!validated.ok) {\n const first = Object.values(validated.fieldErrors)[0];\n return {\n success: false,\n error: first || 'Validation failed',\n fieldErrors: validated.fieldErrors,\n };\n }\n\n const mediaTitles = await assertImageFieldsReferenceMediaWithTitle(entryType, strFields);\n if (!mediaTitles.ok) {\n return {\n success: false,\n error: mediaTitles.message,\n fieldErrors: { [mediaTitles.fieldKey]: mediaTitles.message },\n };\n }\n\n payload = {\n ...payload,\n fields: persistedFieldsFromFormStrings(entryType, strFields),\n };\n\n const slugUnique = await assertSlugFieldsUnique(entryType, fileName, payload.fields);\n if (!slugUnique.ok) {\n return {\n success: false,\n error: slugUnique.message,\n fieldErrors: { [slugUnique.fieldKey]: slugUnique.message },\n };\n }\n }\n\n // New entries start as draft (hidden from `query()`). First successful save promotes to `changed`\n // so they become eligible for public pages once the workspace branch is published from the header.\n if (!options?.skipStatusTransition && payload?.sys?.status === 'draft') {\n payload = { ...payload, sys: { ...payload.sys, status: 'changed' } };\n }\n\n // Auto-transition published/merged → changed on regular save (not on explicit publish)\n if (!options?.skipStatusTransition && (payload?.sys?.status === 'published' || payload?.sys?.status === 'merged')) {\n payload = { ...payload, sys: { ...payload.sys, status: 'changed' } };\n }\n\n // Extract markdown and richtext fields into companion files, remove from JSON payload\n const markdownContents: Record<string, string> = {};\n if (typeof entryType === 'string' && payload.fields) {\n const companionPaths = companionMarkdownPathsForEntry(fileName, entryType, config.collections);\n for (const [fieldName] of Object.entries(companionPaths)) {\n markdownContents[fieldName] = payload.fields[fieldName] ?? '';\n delete payload.fields[fieldName];\n }\n const richTextPaths = companionRichTextPathsForEntry(fileName, entryType, config.collections);\n for (const [fieldName] of Object.entries(richTextPaths)) {\n markdownContents[fieldName] = payload.fields[fieldName] ?? '';\n delete payload.fields[fieldName];\n }\n }\n\n const data = JSON.stringify(payload, null, 2);\n const normalizedData = `${data}\\n`;\n\n if (isProductionMode()) {\n await assertFeatureBranchForWritesIfRequired();\n const entryTypeLabel = payload?.sys?.type || 'content';\n const entryId = payload?.sys?.id || '';\n const message = `Update ${entryTypeLabel} ${entryId}`;\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n\n // Use cached SHA from the store to skip the pre-read API call\n const cachedSha = (await getStoredFileSha(fileName, activeBranch)) || undefined;\n await saveGitHubFile(fileName, normalizedData, message, activeBranch, cachedSha);\n // Write companion markdown and richtext files\n const mdPaths =\n typeof entryType === 'string' ? companionMarkdownPathsForEntry(fileName, entryType, config.collections) : {};\n for (const [fieldName, mdPath] of Object.entries(mdPaths)) {\n const mdContent = markdownContents[fieldName] ?? '';\n await saveGitHubFile(mdPath, mdContent, `Update ${fieldName} for ${entryType} ${entryId}`, activeBranch);\n }\n const rtPaths =\n typeof entryType === 'string' ? companionRichTextPathsForEntry(fileName, entryType, config.collections) : {};\n for (const [fieldName, rtPath] of Object.entries(rtPaths)) {\n const rtContent = markdownContents[fieldName] ?? '';\n await saveGitHubFile(rtPath, rtContent, `Update ${fieldName} for ${entryType} ${entryId}`, activeBranch);\n }\n await waitForPublicReadConsistency(fileName, normalizedData);\n\n // Write-through: update in-memory store so subsequent reads are instant\n if (activeBranch) {\n applyMutation(activeBranch, {\n type: 'upsert',\n path: fileName,\n content: payload,\n sha: '', // SHA unknown after single-file commit; next tree fetch will correct it\n companions: markdownContents,\n });\n }\n\n await persistBranchHistoryEntryIfNeeded(activeBranch, fileName);\n await syncEmbeddingsForUpsertIfEnabled(fileName, payload, markdownContents, activeBranch, true);\n const built = await buildJsons(fileName);\n\n return built.success ? actionOk() : built;\n }\n\n const cookieStore = await cookies();\n const activeBranchDev = cookieStore.get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n const filePath = path.join(/*turbopackIgnore: true*/ process.cwd(), fileName);\n await fsPromises.writeFile(filePath, normalizedData, 'utf8');\n // Write companion markdown and richtext files\n const mdPaths =\n typeof entryType === 'string' ? companionMarkdownPathsForEntry(fileName, entryType, config.collections) : {};\n for (const [fieldName, mdPath] of Object.entries(mdPaths)) {\n const mdContent = markdownContents[fieldName] ?? '';\n await fsPromises.writeFile(path.join(/*turbopackIgnore: true*/ process.cwd(), mdPath), mdContent, 'utf8');\n }\n const rtPathsDev =\n typeof entryType === 'string' ? companionRichTextPathsForEntry(fileName, entryType, config.collections) : {};\n for (const [fieldName, rtPath] of Object.entries(rtPathsDev)) {\n const rtContent = markdownContents[fieldName] ?? '';\n await fsPromises.writeFile(path.join(/*turbopackIgnore: true*/ process.cwd(), rtPath), rtContent, 'utf8');\n }\n await persistBranchHistoryEntryIfNeeded(activeBranchDev, fileName);\n await syncEmbeddingsForUpsertIfEnabled(fileName, payload, markdownContents, activeBranchDev, false);\n const built = await buildJsons(fileName);\n\n return built.success ? actionOk() : built;\n } catch (e) {\n return actionErr(new Error(`Failed to save file: ${getErrorMessage(e)}`));\n }\n};\n\nexport const newFile = async (type: string): Promise<NewFileResult> => {\n const config = getConfig();\n try {\n const id = crypto.randomUUID();\n const values = {\n sys: {\n id,\n type,\n status: 'draft' as const,\n },\n fields: initialFieldsForNewEntry(type),\n };\n const data = JSON.stringify(values, null, 2);\n const file = `${config.contentFolder}/${type}/${type}-${id}.json`;\n const normalizedData = `${data}\\n`;\n\n if (isProductionMode()) {\n await assertFeatureBranchForWritesIfRequired();\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n await saveGitHubFile(file, normalizedData, `Create new ${type} ${id}`, activeBranch);\n await waitForPublicReadConsistency(file, normalizedData);\n\n // Write-through: add new entry to in-memory store\n if (activeBranch) {\n applyMutation(activeBranch, {\n type: 'upsert',\n path: file,\n content: values,\n sha: '',\n });\n }\n\n await persistBranchHistoryEntryIfNeeded(activeBranch, file);\n await syncEmbeddingsForUpsertIfEnabled(file, values, {}, activeBranch, true);\n const built = await buildJsons(file);\n\n return built.success ? { success: true, path: file } : { success: false, error: built.error };\n }\n\n const cookieStore = await cookies();\n const activeBranchDev = cookieStore.get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n const filePath = path.join(/*turbopackIgnore: true*/ process.cwd(), file);\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n await fsPromises.writeFile(filePath, normalizedData, 'utf8');\n await persistBranchHistoryEntryIfNeeded(activeBranchDev, file);\n await syncEmbeddingsForUpsertIfEnabled(file, values, {}, activeBranchDev, false);\n const built = await buildJsons(file);\n\n return built.success ? { success: true, path: file } : { success: false, error: built.error };\n } catch (e) {\n return { success: false, error: getErrorMessage(e) } satisfies NewFileResult;\n }\n};\n\nexport const removeFile = async (fileName: string): Promise<ActionResult> => {\n const config = getConfig();\n try {\n let collectionType: string | undefined;\n try {\n const existing = await getFile(fileName);\n collectionType = existing?.sys?.type;\n } catch {\n /* best-effort */\n }\n\n // Determine companion markdown and richtext files to delete\n const companionPaths =\n typeof collectionType === 'string'\n ? [\n ...Object.values(companionMarkdownPathsForEntry(fileName, collectionType, config.collections)),\n ...Object.values(companionRichTextPathsForEntry(fileName, collectionType, config.collections)),\n ]\n : [];\n\n if (isProductionMode()) {\n await assertFeatureBranchForWritesIfRequired();\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n await deleteGitHubFile(fileName, `Remove ${fileName}`, activeBranch);\n for (const mdPath of companionPaths) {\n try {\n await deleteGitHubFile(mdPath, `Remove companion ${mdPath}`, activeBranch);\n } catch {\n /* best-effort — companion may not exist */\n }\n }\n\n // Write-through: remove entry from in-memory store\n if (activeBranch) {\n applyMutation(activeBranch, { type: 'delete', path: fileName });\n }\n\n await syncEmbeddingsForRemoveIfEnabled(fileName, activeBranch, true);\n const built = await buildJsons(fileName);\n\n return built.success ? actionOk() : built;\n }\n\n const filePath = path.join(/*turbopackIgnore: true*/ process.cwd(), fileName);\n await fsPromises.unlink(filePath);\n for (const mdPath of companionPaths) {\n try {\n await fsPromises.unlink(path.join(/*turbopackIgnore: true*/ process.cwd(), mdPath));\n } catch {\n /* best-effort — companion may not exist */\n }\n }\n await syncEmbeddingsForRemoveIfEnabled(fileName, undefined, false);\n const built = await buildJsons(fileName);\n\n return built.success ? actionOk() : built;\n } catch (e) {\n return actionErr(e);\n }\n};\n"],"mappings":"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","names":["listed","mdPaths","built"]}
|
|
1
|
+
{"version":3,"sources":["../../../admin/actions/files.ts"],"sourcesContent":["'use server';\n\nimport './registerConfig';\n\nimport fsPromises from 'fs/promises';\nimport path from 'path';\n\nimport { cookies } from 'next/headers';\n\nimport { getConfig } from '../../lib/configStore';\nimport { listLocalCollectionFiles, listLocalFilesRecursive, listLocalFilesWithExtensions } from '../../lib/localReader';\nimport { getAgentConfig } from '../../agent/configStore';\nimport { syncEmbeddingsAfterRemove, syncEmbeddingsAfterUpsert } from '../../agent/embeddingsHook';\nimport { BRANCH_HISTORY_FILE_PATH, mergeHistoryContentWithAppendedEntry } from '../../lib/branchHistory';\nimport { companionMarkdownPathsForEntry, companionRichTextPathsForEntry } from '../../lib/companionMarkdown';\nimport { initialFieldsForNewEntry } from '../../lib/initialEntryFields';\nimport { persistedFieldsFromFormStrings } from '../../lib/persistedFormFields';\nimport { normalizeStoredSlug } from '../../lib/slugField';\nimport { validateEntryFields } from '../../lib/validateEntryFields';\nimport type { Config } from '../types';\n\nimport {\n deleteGitHubFile,\n getGitHubFile,\n isProductionMode,\n listGitHubFiles,\n listGitHubFilesRecursive,\n readGitHubFilePublic,\n saveGitHubFile,\n} from '../github';\nimport { applyMutation, getStoredContentFiles, getStoredFile, getStoredFileSha } from '../store/contentStore';\nimport { mediaContentFolder, mediaEntryPath } from '../../lib/mediaPath';\nimport { buildJsons } from './build';\nimport {\n actionErr,\n actionOk,\n getErrorMessage,\n type ActionResult,\n type NewFileResult,\n type SaveFileResult,\n} from './utils';\n\nexport type { SaveFileResult } from './utils';\n\nconst CMS_ACTIVE_BRANCH_COOKIE = 'cms-active-branch';\n\nfunction normalizeContentPath(p: string): string {\n return p.replace(/\\\\/g, '/');\n}\n\n/** Best-effort embedding store sync after content writes. No-op when the agent isn't configured. */\nasync function syncEmbeddingsForUpsertIfEnabled(\n entryPath: string,\n payload: { sys?: { type?: string }; fields?: Record<string, unknown> },\n companions: Record<string, string>,\n branch: string | undefined,\n isProduction: boolean,\n): Promise<void> {\n const agentConfig = getAgentConfig();\n if (!agentConfig) return;\n const config = getConfig();\n await syncEmbeddingsAfterUpsert({ agentConfig, config, entryPath, payload, companions, branch, isProduction });\n}\n\nasync function syncEmbeddingsForRemoveIfEnabled(\n entryPath: string,\n branch: string | undefined,\n isProduction: boolean,\n): Promise<void> {\n const agentConfig = getAgentConfig();\n if (!agentConfig) return;\n await syncEmbeddingsAfterRemove({ agentConfig, entryPath, branch, isProduction });\n}\n\n/** Records the entry path under the active branch in `cms/branch-history.json` (GitHub or local). Best-effort. */\nasync function persistBranchHistoryEntryIfNeeded(activeBranch: string | undefined, entryPath: string): Promise<void> {\n if (!activeBranch) {\n return;\n }\n\n const normalized = normalizeContentPath(entryPath);\n\n if (isProductionMode()) {\n try {\n const historyFile = await getGitHubFile(BRANCH_HISTORY_FILE_PATH, activeBranch);\n const next = mergeHistoryContentWithAppendedEntry(historyFile?.content ?? '', activeBranch, normalized);\n if (next == null) {\n return;\n }\n\n await saveGitHubFile(BRANCH_HISTORY_FILE_PATH, next, 'CMS: track entry in branch history', activeBranch);\n } catch {\n /* best-effort sidecar — primary save must still succeed */\n }\n\n return;\n }\n\n try {\n const abs = path.join(/*turbopackIgnore: true*/ process.cwd(), BRANCH_HISTORY_FILE_PATH);\n let raw = '';\n try {\n raw = await fsPromises.readFile(abs, { encoding: 'utf8' });\n } catch {\n raw = '';\n }\n\n const next = mergeHistoryContentWithAppendedEntry(raw, activeBranch, normalized);\n if (next == null) {\n return;\n }\n\n await fsPromises.mkdir(path.dirname(abs), { recursive: true });\n await fsPromises.writeFile(abs, next, 'utf8');\n } catch {\n /* best-effort sidecar */\n }\n}\n\nasync function assertSlugFieldsUnique(\n entryType: string,\n fileName: string,\n persistedFields: Record<string, unknown>,\n): Promise<{ ok: true } | { ok: false; fieldKey: string; message: string }> {\n const config = getConfig();\n const col = config.collections[entryType as keyof Config['collections']];\n if (!col) {\n return { ok: true };\n }\n\n const slugFieldEntries = Object.entries(col.fields).filter(([, def]) => def.format === 'slug');\n if (slugFieldEntries.length === 0) {\n return { ok: true };\n }\n\n const selfPath = normalizeContentPath(fileName);\n const siblings = await getContentFiles(entryType);\n if (!Array.isArray(siblings)) {\n return { ok: true };\n }\n\n for (const [slugKey] of slugFieldEntries) {\n const candidate = persistedFields[slugKey];\n if (typeof candidate !== 'string' || !candidate.trim()) {\n continue;\n }\n const normCandidate = normalizeStoredSlug(candidate);\n if (!normCandidate) {\n continue;\n }\n\n for (const siblingPath of siblings) {\n if (normalizeContentPath(siblingPath) === selfPath) {\n continue;\n }\n let data: unknown;\n try {\n data = await getFile(siblingPath);\n } catch {\n continue;\n }\n if (!data || typeof data !== 'object') {\n continue;\n }\n const fields = (data as { fields?: Record<string, unknown> }).fields;\n if (!fields) {\n continue;\n }\n const other = fields[slugKey];\n if (typeof other !== 'string' || !other.trim()) {\n continue;\n }\n if (normalizeStoredSlug(other) === normCandidate) {\n return {\n ok: false,\n fieldKey: slugKey,\n message: `Another ${col.label} already uses this slug. Choose a different value.`,\n };\n }\n }\n }\n\n return { ok: true };\n}\n\nconst MEDIA_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\nasync function assertImageFieldsReferenceMediaWithTitle(\n entryType: string,\n strFields: Record<string, string>,\n): Promise<{ ok: true } | { ok: false; fieldKey: string; message: string }> {\n const config = getConfig();\n const col = config.collections[entryType as keyof Config['collections']];\n if (!col) {\n return { ok: true };\n }\n\n for (const [key, def] of Object.entries(col.fields)) {\n if (def.format !== 'image') {\n continue;\n }\n const raw = (strFields[key] ?? '').trim();\n if (!raw || raw.startsWith('/')) {\n continue;\n }\n if (!MEDIA_UUID_RE.test(raw)) {\n continue;\n }\n\n const mediaPath = mediaEntryPath(raw);\n let media: unknown;\n try {\n media = await getFile(mediaPath);\n } catch {\n return {\n ok: false,\n fieldKey: key,\n message: `Media not found for ${def.label}. Choose a valid image in the Media library.`,\n };\n }\n\n const title = (media as { fields?: { title?: unknown } })?.fields?.title;\n if (typeof title !== 'string' || !title.trim()) {\n return {\n ok: false,\n fieldKey: key,\n message: `Selected image is missing a required Title; fix it in the Media library.`,\n };\n }\n }\n\n return { ok: true };\n}\n\n/**\n * In production, reject writes that would target `config.git.baseBranch` (no feature branch cookie).\n */\nexport const assertFeatureBranchForWritesIfRequired = async (): Promise<void> => {\n if (!isProductionMode()) {\n return;\n }\n\n if (!(await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value) {\n throw new Error('Create or select a branch before editing.');\n }\n};\n\nexport const waitForPublicReadConsistency = async (fileName: string, expectedContent: string, readRef?: string) => {\n if (!isProductionMode()) {\n return;\n }\n\n const parsedAttempts = Number.parseInt(process.env.CMS_GITHUB_CONSISTENCY_ATTEMPTS || '8', 10);\n const parsedDelayMs = Number.parseInt(process.env.CMS_GITHUB_CONSISTENCY_DELAY_MS || '250', 10);\n const maxAttempts = Number.isFinite(parsedAttempts) && parsedAttempts > 0 ? parsedAttempts : 1;\n const delayMs = Number.isFinite(parsedDelayMs) && parsedDelayMs >= 0 ? parsedDelayMs : 250;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n const visibleContent = await readGitHubFilePublic(fileName, readRef);\n\n if (visibleContent === expectedContent) {\n return;\n }\n } catch (_e) {\n // Ignore transient read failures and retry within the same save request.\n }\n\n if (attempt < maxAttempts && delayMs > 0) {\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n }\n }\n};\n\nexport const getContentFiles = async (collection: string = '**') => {\n const config = getConfig();\n try {\n if (isProductionMode()) {\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n\n // Try in-memory store first (instant)\n try {\n const stored = await getStoredContentFiles(collection, activeBranch);\n if (stored) return stored;\n } catch {\n // Store unavailable — fall through to direct GitHub API\n }\n\n try {\n if (collection === '**') {\n const listed = await listGitHubFilesRecursive(config.contentFolder, '.json', activeBranch);\n return Array.isArray(listed) ? listed : [];\n }\n\n const listed = await listGitHubFiles(`${config.contentFolder}/${collection}`, '.json', activeBranch);\n return Array.isArray(listed) ? listed : [];\n } catch (e) {\n // Fall back to local files if GitHub API access is not available.\n }\n }\n\n if (collection === '**') {\n return await listLocalFilesRecursive(config.contentFolder, '.json');\n }\n return await listLocalCollectionFiles(`${config.contentFolder}/${collection}`);\n } catch (e) {\n return [];\n }\n};\n\n/**\n * List media-entry JSON files (e.g. `cms/media/media-<uuid>.json`).\n *\n * Distinct from `getContentFiles`, which lists editorial content under\n * `config.contentFolder`, and from `getMediaFiles`, which lists physical image\n * binaries under `config.mediaFolder`. This is the JSON-entry layer.\n */\nexport const getMediaContentFiles = async (): Promise<string[]> => {\n const folder = mediaContentFolder();\n try {\n if (isProductionMode()) {\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n\n try {\n return await listGitHubFiles(folder, '.json', activeBranch);\n } catch (_e) {\n // Fall back to local files if GitHub API access is not available.\n }\n }\n\n return await listLocalCollectionFiles(folder);\n } catch (_e) {\n return [];\n }\n};\n\nexport const getMediaFiles = async (folder: string = '**') => {\n const config = getConfig();\n try {\n if (isProductionMode()) {\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n\n try {\n const dir = folder === '**' ? config.mediaFolder : `${config.mediaFolder}/${folder}`;\n const allFiles = await listGitHubFilesRecursive(dir, undefined, activeBranch);\n const extensions = config.mediaAllowedFormats;\n return allFiles.filter((f) => extensions.some((ext) => f.endsWith(`.${ext}`)));\n } catch (e) {\n // Fall back to local files if GitHub API access is not available.\n }\n }\n\n const dir = folder === '**' ? config.mediaFolder : `${config.mediaFolder}/${folder}`;\n return await listLocalFilesWithExtensions(dir, config.mediaAllowedFormats, folder === '**');\n } catch (e) {\n return [];\n }\n};\n\n/**\n * Read only the raw entry JSON — no companion markdown/richtext merging.\n * Use for list views where companion content is never needed.\n */\nexport const getFileJson = async (fileName: string): Promise<Record<string, unknown> | null> => {\n if (isProductionMode()) {\n let activeBranch: string | undefined;\n try {\n activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n } catch {}\n\n try {\n const stored = await getStoredFile(fileName, activeBranch);\n if (stored) return structuredClone(stored.content);\n } catch {}\n\n try {\n const result = await getGitHubFile(fileName, activeBranch);\n if (result) return JSON.parse(result.content) as Record<string, unknown>;\n } catch {}\n\n try {\n const raw = await readGitHubFilePublic(fileName, activeBranch);\n if (raw) return JSON.parse(raw) as Record<string, unknown>;\n } catch {}\n\n return null;\n }\n\n try {\n const filePath = path.join(/*turbopackIgnore: true*/ process.cwd(), fileName);\n const data = await fsPromises.readFile(filePath, { encoding: 'utf8' });\n return JSON.parse(data) as Record<string, unknown>;\n } catch {\n return null;\n }\n};\n\nexport const getFile = async (fileName: string) => {\n const config = getConfig();\n try {\n let entry: any;\n\n if (isProductionMode()) {\n // Try in-memory store first (instant, includes pre-merged companions)\n try {\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n const stored = await getStoredFile(fileName, activeBranch);\n\n if (stored) {\n entry = structuredClone(stored.content);\n // Merge pre-cached companion markdown into fields\n if (entry.fields) {\n for (const [fieldName, mdContent] of Object.entries(stored.companionMarkdown)) {\n entry.fields[fieldName] = mdContent;\n }\n }\n return entry;\n }\n } catch {\n // Store unavailable — fall through to direct GitHub API\n }\n\n try {\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n const result = await getGitHubFile(fileName, activeBranch);\n\n if (result) {\n entry = JSON.parse(result.content);\n }\n } catch (e) {\n // Fall back to local file if GitHub API access is not available.\n }\n\n // Cold serverless instance: content store is empty. `getGitHubFile` may return null\n // (404 race) or throw before `readGitHubFilePublic`'s multi-client retry path runs.\n // Never fall through to `fs.readFile` on deploy — the repo is not on disk (ENOENT → 500).\n if (!entry) {\n try {\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n const raw = await readGitHubFilePublic(fileName, activeBranch);\n if (raw) {\n entry = JSON.parse(raw);\n }\n } catch {\n /* best-effort */\n }\n }\n }\n\n if (!entry) {\n if (isProductionMode()) {\n return {};\n }\n const filePath = path.join(/*turbopackIgnore: true*/ process.cwd(), fileName);\n const data = await fsPromises.readFile(filePath, { encoding: 'utf8' });\n entry = JSON.parse(data);\n }\n\n if (!entry) {\n return {};\n }\n\n // Merge companion markdown and richtext files into fields\n const collectionType = entry?.sys?.type;\n if (typeof collectionType === 'string' && entry.fields) {\n const companionPaths = companionMarkdownPathsForEntry(fileName, collectionType, config.collections);\n for (const [fieldName, mdPath] of Object.entries(companionPaths)) {\n entry.fields[fieldName] = await readCompanionMarkdownContent(mdPath);\n }\n const richTextPaths = companionRichTextPathsForEntry(fileName, collectionType, config.collections);\n for (const [fieldName, mdxPath] of Object.entries(richTextPaths)) {\n entry.fields[fieldName] = await readCompanionMarkdownContent(mdxPath);\n }\n }\n\n return entry;\n } catch (e) {\n throw new Error('Failed to get file');\n }\n};\n\n/** Read a companion markdown file as a string. Returns `\"\"` if the file does not exist. */\nasync function readCompanionMarkdownContent(filePath: string): Promise<string> {\n if (isProductionMode()) {\n try {\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n const result = await getGitHubFile(filePath, activeBranch);\n return result?.content ?? '';\n } catch {\n return '';\n }\n }\n\n try {\n return await fsPromises.readFile(path.join(/*turbopackIgnore: true*/ process.cwd(), filePath), {\n encoding: 'utf8',\n });\n } catch {\n return '';\n }\n}\n\nexport const saveFile = async (\n formData: any,\n fileName: string,\n options?: { skipStatusTransition?: boolean },\n): Promise<SaveFileResult> => {\n const config = getConfig();\n try {\n let payload = formData;\n const entryType = payload?.sys?.type;\n const rawFields = payload?.fields;\n if (typeof entryType === 'string' && rawFields && typeof rawFields === 'object' && !Array.isArray(rawFields)) {\n const strFields: Record<string, string> = {};\n for (const [k, v] of Object.entries(rawFields)) {\n if (v == null) {\n strFields[k] = '';\n } else if (typeof v === 'object') {\n strFields[k] = JSON.stringify(v);\n } else {\n strFields[k] = String(v);\n }\n }\n const validated = validateEntryFields(entryType, strFields);\n if (!validated.ok) {\n const first = Object.values(validated.fieldErrors)[0];\n return {\n success: false,\n error: first || 'Validation failed',\n fieldErrors: validated.fieldErrors,\n };\n }\n\n const mediaTitles = await assertImageFieldsReferenceMediaWithTitle(entryType, strFields);\n if (!mediaTitles.ok) {\n return {\n success: false,\n error: mediaTitles.message,\n fieldErrors: { [mediaTitles.fieldKey]: mediaTitles.message },\n };\n }\n\n payload = {\n ...payload,\n fields: persistedFieldsFromFormStrings(entryType, strFields),\n };\n\n const slugUnique = await assertSlugFieldsUnique(entryType, fileName, payload.fields);\n if (!slugUnique.ok) {\n return {\n success: false,\n error: slugUnique.message,\n fieldErrors: { [slugUnique.fieldKey]: slugUnique.message },\n };\n }\n }\n\n // New entries start as draft (hidden from `query()`). First successful save promotes to `changed`\n // so they become eligible for public pages once the workspace branch is published from the header.\n if (!options?.skipStatusTransition && payload?.sys?.status === 'draft') {\n payload = { ...payload, sys: { ...payload.sys, status: 'changed' } };\n }\n\n // Auto-transition published/merged → changed on regular save (not on explicit publish)\n if (!options?.skipStatusTransition && (payload?.sys?.status === 'published' || payload?.sys?.status === 'merged')) {\n payload = { ...payload, sys: { ...payload.sys, status: 'changed' } };\n }\n\n // Extract markdown and richtext fields into companion files, remove from JSON payload\n const markdownContents: Record<string, string> = {};\n if (typeof entryType === 'string' && payload.fields) {\n const companionPaths = companionMarkdownPathsForEntry(fileName, entryType, config.collections);\n for (const [fieldName] of Object.entries(companionPaths)) {\n markdownContents[fieldName] = payload.fields[fieldName] ?? '';\n delete payload.fields[fieldName];\n }\n const richTextPaths = companionRichTextPathsForEntry(fileName, entryType, config.collections);\n for (const [fieldName] of Object.entries(richTextPaths)) {\n markdownContents[fieldName] = payload.fields[fieldName] ?? '';\n delete payload.fields[fieldName];\n }\n }\n\n const data = JSON.stringify(payload, null, 2);\n const normalizedData = `${data}\\n`;\n\n if (isProductionMode()) {\n await assertFeatureBranchForWritesIfRequired();\n const entryTypeLabel = payload?.sys?.type || 'content';\n const entryId = payload?.sys?.id || '';\n const message = `Update ${entryTypeLabel} ${entryId}`;\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n\n // Use cached SHA from the store to skip the pre-read API call\n const cachedSha = (await getStoredFileSha(fileName, activeBranch)) || undefined;\n await saveGitHubFile(fileName, normalizedData, message, activeBranch, cachedSha);\n // Write companion markdown and richtext files\n const mdPaths =\n typeof entryType === 'string' ? companionMarkdownPathsForEntry(fileName, entryType, config.collections) : {};\n for (const [fieldName, mdPath] of Object.entries(mdPaths)) {\n const mdContent = markdownContents[fieldName] ?? '';\n await saveGitHubFile(mdPath, mdContent, `Update ${fieldName} for ${entryType} ${entryId}`, activeBranch);\n }\n const rtPaths =\n typeof entryType === 'string' ? companionRichTextPathsForEntry(fileName, entryType, config.collections) : {};\n for (const [fieldName, rtPath] of Object.entries(rtPaths)) {\n const rtContent = markdownContents[fieldName] ?? '';\n await saveGitHubFile(rtPath, rtContent, `Update ${fieldName} for ${entryType} ${entryId}`, activeBranch);\n }\n await waitForPublicReadConsistency(fileName, normalizedData);\n\n // Write-through: update in-memory store so subsequent reads are instant\n if (activeBranch) {\n applyMutation(activeBranch, {\n type: 'upsert',\n path: fileName,\n content: payload,\n sha: '', // SHA unknown after single-file commit; next tree fetch will correct it\n companions: markdownContents,\n });\n }\n\n await persistBranchHistoryEntryIfNeeded(activeBranch, fileName);\n await syncEmbeddingsForUpsertIfEnabled(fileName, payload, markdownContents, activeBranch, true);\n const built = await buildJsons(fileName);\n\n return built.success ? actionOk() : built;\n }\n\n const cookieStore = await cookies();\n const activeBranchDev = cookieStore.get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n const filePath = path.join(/*turbopackIgnore: true*/ process.cwd(), fileName);\n await fsPromises.writeFile(filePath, normalizedData, 'utf8');\n // Write companion markdown and richtext files\n const mdPaths =\n typeof entryType === 'string' ? companionMarkdownPathsForEntry(fileName, entryType, config.collections) : {};\n for (const [fieldName, mdPath] of Object.entries(mdPaths)) {\n const mdContent = markdownContents[fieldName] ?? '';\n await fsPromises.writeFile(path.join(/*turbopackIgnore: true*/ process.cwd(), mdPath), mdContent, 'utf8');\n }\n const rtPathsDev =\n typeof entryType === 'string' ? companionRichTextPathsForEntry(fileName, entryType, config.collections) : {};\n for (const [fieldName, rtPath] of Object.entries(rtPathsDev)) {\n const rtContent = markdownContents[fieldName] ?? '';\n await fsPromises.writeFile(path.join(/*turbopackIgnore: true*/ process.cwd(), rtPath), rtContent, 'utf8');\n }\n await persistBranchHistoryEntryIfNeeded(activeBranchDev, fileName);\n await syncEmbeddingsForUpsertIfEnabled(fileName, payload, markdownContents, activeBranchDev, false);\n const built = await buildJsons(fileName);\n\n return built.success ? actionOk() : built;\n } catch (e) {\n return actionErr(new Error(`Failed to save file: ${getErrorMessage(e)}`));\n }\n};\n\nexport const newFile = async (type: string): Promise<NewFileResult> => {\n const config = getConfig();\n try {\n const id = crypto.randomUUID();\n const values = {\n sys: {\n id,\n type,\n status: 'draft' as const,\n },\n fields: initialFieldsForNewEntry(type),\n };\n const data = JSON.stringify(values, null, 2);\n const file = `${config.contentFolder}/${type}/${type}-${id}.json`;\n const normalizedData = `${data}\\n`;\n\n if (isProductionMode()) {\n await assertFeatureBranchForWritesIfRequired();\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n await saveGitHubFile(file, normalizedData, `Create new ${type} ${id}`, activeBranch);\n await waitForPublicReadConsistency(file, normalizedData);\n\n // Write-through: add new entry to in-memory store\n if (activeBranch) {\n applyMutation(activeBranch, {\n type: 'upsert',\n path: file,\n content: values,\n sha: '',\n });\n }\n\n await persistBranchHistoryEntryIfNeeded(activeBranch, file);\n await syncEmbeddingsForUpsertIfEnabled(file, values, {}, activeBranch, true);\n const built = await buildJsons(file);\n\n return built.success ? { success: true, path: file } : { success: false, error: built.error };\n }\n\n const cookieStore = await cookies();\n const activeBranchDev = cookieStore.get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n const filePath = path.join(/*turbopackIgnore: true*/ process.cwd(), file);\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n await fsPromises.writeFile(filePath, normalizedData, 'utf8');\n await persistBranchHistoryEntryIfNeeded(activeBranchDev, file);\n await syncEmbeddingsForUpsertIfEnabled(file, values, {}, activeBranchDev, false);\n const built = await buildJsons(file);\n\n return built.success ? { success: true, path: file } : { success: false, error: built.error };\n } catch (e) {\n return { success: false, error: getErrorMessage(e) } satisfies NewFileResult;\n }\n};\n\nexport const removeFile = async (fileName: string): Promise<ActionResult> => {\n const config = getConfig();\n try {\n let collectionType: string | undefined;\n try {\n const existing = await getFile(fileName);\n collectionType = existing?.sys?.type;\n } catch {\n /* best-effort */\n }\n\n // Determine companion markdown and richtext files to delete\n const companionPaths =\n typeof collectionType === 'string'\n ? [\n ...Object.values(companionMarkdownPathsForEntry(fileName, collectionType, config.collections)),\n ...Object.values(companionRichTextPathsForEntry(fileName, collectionType, config.collections)),\n ]\n : [];\n\n if (isProductionMode()) {\n await assertFeatureBranchForWritesIfRequired();\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n await deleteGitHubFile(fileName, `Remove ${fileName}`, activeBranch);\n for (const mdPath of companionPaths) {\n try {\n await deleteGitHubFile(mdPath, `Remove companion ${mdPath}`, activeBranch);\n } catch {\n /* best-effort — companion may not exist */\n }\n }\n\n // Write-through: remove entry from in-memory store\n if (activeBranch) {\n applyMutation(activeBranch, { type: 'delete', path: fileName });\n }\n\n await syncEmbeddingsForRemoveIfEnabled(fileName, activeBranch, true);\n const built = await buildJsons(fileName);\n\n return built.success ? actionOk() : built;\n }\n\n const filePath = path.join(/*turbopackIgnore: true*/ process.cwd(), fileName);\n await fsPromises.unlink(filePath);\n for (const mdPath of companionPaths) {\n try {\n await fsPromises.unlink(path.join(/*turbopackIgnore: true*/ process.cwd(), mdPath));\n } catch {\n /* best-effort — companion may not exist */\n }\n }\n await syncEmbeddingsForRemoveIfEnabled(fileName, undefined, false);\n const built = await buildJsons(fileName);\n\n return built.success ? actionOk() : built;\n } catch (e) {\n return actionErr(e);\n }\n};\n"],"mappings":"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","names":["listed","dir","mdPaths","built"]}
|
|
@@ -3,9 +3,9 @@ import "../../chunk-B5LE2OEC.js";
|
|
|
3
3
|
import "./registerConfig";
|
|
4
4
|
import fsPromises from "fs/promises";
|
|
5
5
|
import path from "path";
|
|
6
|
-
import { glob } from "glob";
|
|
7
6
|
import { cookies } from "next/headers";
|
|
8
7
|
import { getConfig } from "../../lib/configStore";
|
|
8
|
+
import { listLocalFilesRecursive } from "../../lib/localReader";
|
|
9
9
|
import { companionMarkdownPathsForEntry, companionRichTextPathsForEntry } from "../../lib/companionMarkdown";
|
|
10
10
|
import { buildSearchIndex, querySearchIndex } from "../../lib/searchIndex";
|
|
11
11
|
import { isProductionMode } from "../github";
|
|
@@ -27,7 +27,7 @@ async function searchFromStore(query) {
|
|
|
27
27
|
}
|
|
28
28
|
async function searchFromFilesystem(query) {
|
|
29
29
|
const config = getConfig();
|
|
30
|
-
const files = await
|
|
30
|
+
const files = await listLocalFilesRecursive(config.contentFolder, ".json");
|
|
31
31
|
const entries = [];
|
|
32
32
|
for (const file of files) {
|
|
33
33
|
const normalized = file.replace(/\\/g, "/");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../admin/actions/search.ts"],"sourcesContent":["'use server';\n\nimport './registerConfig';\n\nimport fsPromises from 'fs/promises';\nimport path from 'path';\n\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../admin/actions/search.ts"],"sourcesContent":["'use server';\n\nimport './registerConfig';\n\nimport fsPromises from 'fs/promises';\nimport path from 'path';\n\nimport { cookies } from 'next/headers';\n\nimport { getConfig } from '../../lib/configStore';\nimport { listLocalFilesRecursive } from '../../lib/localReader';\nimport { companionMarkdownPathsForEntry, companionRichTextPathsForEntry } from '../../lib/companionMarkdown';\nimport { buildSearchIndex, querySearchIndex, type EntryForSearch, type SearchResult } from '../../lib/searchIndex';\n\nimport { isProductionMode } from '../github';\nimport { getOrBuildSearchIndex } from '../store/contentStore';\n\nexport type { SearchResult } from '../../lib/searchIndex';\n\nconst CMS_ACTIVE_BRANCH_COOKIE = 'cms-active-branch';\n\n/**\n * Search across all CMS content entries. Used by the admin search UI.\n * In production, uses the content store's lazily-built index.\n * In dev, builds the index on-the-fly from local filesystem.\n */\nexport async function searchEntries(query: string): Promise<SearchResult[]> {\n if (!query.trim()) return [];\n\n if (isProductionMode()) {\n return searchFromStore(query);\n }\n\n return searchFromFilesystem(query);\n}\n\nasync function searchFromStore(query: string): Promise<SearchResult[]> {\n const activeBranch = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)?.value;\n const serialized = await getOrBuildSearchIndex(activeBranch);\n if (!serialized) return [];\n return querySearchIndex(serialized, query);\n}\n\nasync function searchFromFilesystem(query: string): Promise<SearchResult[]> {\n const config = getConfig();\n const files = await listLocalFilesRecursive(config.contentFolder, '.json');\n const entries: EntryForSearch[] = [];\n\n for (const file of files) {\n const normalized = file.replace(/\\\\/g, '/');\n try {\n const filePath = path.join(/*turbopackIgnore: true*/ process.cwd(), normalized);\n const data = await fsPromises.readFile(filePath, { encoding: 'utf8' });\n const content = JSON.parse(data) as Record<string, unknown>;\n const sys = content.sys as { type?: string } | undefined;\n const type = sys?.type;\n\n // Read companion markdown/richtext files\n const companions: Record<string, string> = {};\n if (type) {\n const mdPaths = companionMarkdownPathsForEntry(normalized, type, config.collections);\n for (const [fieldName, mdPath] of Object.entries(mdPaths)) {\n try {\n const mdFilePath = path.join(/*turbopackIgnore: true*/ process.cwd(), mdPath);\n companions[fieldName] = await fsPromises.readFile(mdFilePath, { encoding: 'utf8' });\n } catch {\n companions[fieldName] = '';\n }\n }\n const rtPaths = companionRichTextPathsForEntry(normalized, type, config.collections);\n for (const [fieldName, rtPath] of Object.entries(rtPaths)) {\n try {\n const rtFilePath = path.join(/*turbopackIgnore: true*/ process.cwd(), rtPath);\n companions[fieldName] = await fsPromises.readFile(rtFilePath, { encoding: 'utf8' });\n } catch {\n companions[fieldName] = '';\n }\n }\n }\n\n entries.push({\n path: normalized.replace(`${config.contentFolder}/`, ''),\n content,\n companionContent: companions,\n });\n } catch {\n // Skip unreadable files\n }\n }\n\n const serialized = buildSearchIndex(entries, config);\n return querySearchIndex(serialized, query);\n}\n"],"mappings":";;AAEA,OAAO;AAEP,OAAO,gBAAgB;AACvB,OAAO,UAAU;AAEjB,SAAS,eAAe;AAExB,SAAS,iBAAiB;AAC1B,SAAS,+BAA+B;AACxC,SAAS,gCAAgC,sCAAsC;AAC/E,SAAS,kBAAkB,wBAAgE;AAE3F,SAAS,wBAAwB;AACjC,SAAS,6BAA6B;AAItC,MAAM,2BAA2B;AAOjC,eAAsB,cAAc,OAAwC;AAC1E,MAAI,CAAC,MAAM,KAAK,EAAG,QAAO,CAAC;AAE3B,MAAI,iBAAiB,GAAG;AACtB,WAAO,gBAAgB,KAAK;AAAA,EAC9B;AAEA,SAAO,qBAAqB,KAAK;AACnC;AAEA,eAAe,gBAAgB,OAAwC;AApCvE;AAqCE,QAAM,gBAAgB,YAAM,QAAQ,GAAG,IAAI,wBAAwB,MAA7C,mBAAgD;AACtE,QAAM,aAAa,MAAM,sBAAsB,YAAY;AAC3D,MAAI,CAAC,WAAY,QAAO,CAAC;AACzB,SAAO,iBAAiB,YAAY,KAAK;AAC3C;AAEA,eAAe,qBAAqB,OAAwC;AAC1E,QAAM,SAAS,UAAU;AACzB,QAAM,QAAQ,MAAM,wBAAwB,OAAO,eAAe,OAAO;AACzE,QAAM,UAA4B,CAAC;AAEnC,aAAW,QAAQ,OAAO;AACxB,UAAM,aAAa,KAAK,QAAQ,OAAO,GAAG;AAC1C,QAAI;AACF,YAAM,WAAW,KAAK;AAAA;AAAA,QAA+B,QAAQ,IAAI;AAAA,QAAG;AAAA,MAAU;AAC9E,YAAM,OAAO,MAAM,WAAW,SAAS,UAAU,EAAE,UAAU,OAAO,CAAC;AACrE,YAAM,UAAU,KAAK,MAAM,IAAI;AAC/B,YAAM,MAAM,QAAQ;AACpB,YAAM,OAAO,2BAAK;AAGlB,YAAM,aAAqC,CAAC;AAC5C,UAAI,MAAM;AACR,cAAM,UAAU,+BAA+B,YAAY,MAAM,OAAO,WAAW;AACnF,mBAAW,CAAC,WAAW,MAAM,KAAK,OAAO,QAAQ,OAAO,GAAG;AACzD,cAAI;AACF,kBAAM,aAAa,KAAK;AAAA;AAAA,cAA+B,QAAQ,IAAI;AAAA,cAAG;AAAA,YAAM;AAC5E,uBAAW,SAAS,IAAI,MAAM,WAAW,SAAS,YAAY,EAAE,UAAU,OAAO,CAAC;AAAA,UACpF,SAAQ;AACN,uBAAW,SAAS,IAAI;AAAA,UAC1B;AAAA,QACF;AACA,cAAM,UAAU,+BAA+B,YAAY,MAAM,OAAO,WAAW;AACnF,mBAAW,CAAC,WAAW,MAAM,KAAK,OAAO,QAAQ,OAAO,GAAG;AACzD,cAAI;AACF,kBAAM,aAAa,KAAK;AAAA;AAAA,cAA+B,QAAQ,IAAI;AAAA,cAAG;AAAA,YAAM;AAC5E,uBAAW,SAAS,IAAI,MAAM,WAAW,SAAS,YAAY,EAAE,UAAU,OAAO,CAAC;AAAA,UACpF,SAAQ;AACN,uBAAW,SAAS,IAAI;AAAA,UAC1B;AAAA,QACF;AAAA,MACF;AAEA,cAAQ,KAAK;AAAA,QACX,MAAM,WAAW,QAAQ,GAAG,OAAO,aAAa,KAAK,EAAE;AAAA,QACvD;AAAA,QACA,kBAAkB;AAAA,MACpB,CAAC;AAAA,IACH,SAAQ;AAAA,IAER;AAAA,EACF;AAEA,QAAM,aAAa,iBAAiB,SAAS,MAAM;AACnD,SAAO,iBAAiB,YAAY,KAAK;AAC3C;","names":[]}
|
package/dist/agent/embedder.js
CHANGED
|
@@ -35,7 +35,7 @@ class LocalTransformersEmbedder {
|
|
|
35
35
|
const msg = e instanceof Error ? e.message : String(e);
|
|
36
36
|
if (/libonnxruntime|onnxruntime-node|ERR_DLOPEN_FAILED/i.test(msg)) {
|
|
37
37
|
throw new Error(
|
|
38
|
-
`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 '
|
|
38
|
+
`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'.`
|
|
39
39
|
);
|
|
40
40
|
}
|
|
41
41
|
throw new Error(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../agent/embedder.ts"],"sourcesContent":["/**\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 '
|
|
1
|
+
{"version":3,"sources":["../../agent/embedder.ts"],"sourcesContent":["/**\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"],"mappings":";AAoBA,MAAM,mBAAmB;AACzB,MAAM,cAAc;AAmBpB,IAAI,iCAAiC;AACrC,SAAS,+BAAqC;AAC5C,MAAI,+BAAgC;AACpC,MAAI,OAAO,YAAY,eAAe,OAAO,QAAQ,OAAO,WAAY;AACxE,mCAAiC;AACjC,UAAQ,GAAG,sBAAsB,CAAC,WAAoB;AACpD,UAAM,MAAM,kBAAkB,QAAQ,OAAO,UAAU,OAAO,MAAM;AACpE,QAAI,qDAAqD,KAAK,GAAG,GAAG;AAGlE;AAAA,IACF;AAEA,iBAAa,MAAM;AACjB,YAAM;AAAA,IACR,CAAC;AAAA,EACH,CAAC;AACH;AAEA,MAAM,0BAA8C;AAAA,EAMlD,YAAY,UAAkB,kBAAkB,MAAc,aAAa;AAH3E,SAAQ,kBAA6D;AACrE,SAAQ,mBAAiC;AAGvC,SAAK,UAAU;AACf,SAAK,MAAM;AAAA,EACb;AAAA,EAEA,MAAc,cAAkD;AAC9D,QAAI,KAAK,iBAAkB,OAAM,KAAK;AACtC,QAAI,KAAK,gBAAiB,QAAO,KAAK;AACtC,iCAA6B;AAC7B,UAAM,KAAK,YAAgD;AACzD,UAAI;AACJ,UAAI;AACF,cAAM,MAAM,OAAO,2BAA2B;AAAA,MAChD,SAAS,GAAG;AACV,cAAM,MAAM,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;AACrD,YAAI,qDAAqD,KAAK,GAAG,GAAG;AAClE,gBAAM,IAAI;AAAA,YACR,2HACmD,GAAG;AAAA,UAGxD;AAAA,QACF;AACA,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,YAAM,OAAQ,MAAM,IAAI,SAAS,sBAAsB,KAAK,OAAO;AACnE,aAAO;AAAA,IACT,GAAG;AAGH,MAAE,MAAM,CAAC,MAAe;AACtB,WAAK,mBAAmB,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC;AACpE,WAAK,kBAAkB;AAAA,IACzB,CAAC;AACD,SAAK,kBAAkB;AACvB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,MAAM,OAA0C;AACpD,QAAI,MAAM,WAAW,EAAG,QAAO,CAAC;AAChC,UAAM,OAAO,MAAM,KAAK,YAAY;AAGpC,UAAM,SAAS,MAAM,KAAK,OAAO,EAAE,SAAS,QAAQ,WAAW,KAAK,CAAC;AACrE,UAAM,OAAO,OAAO;AACpB,UAAM,MAAsB,CAAC;AAC7B,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAM,QAAQ,IAAI,KAAK;AACvB,YAAM,QAAQ,IAAI,aAAa,KAAK,GAAG;AACvC,YAAM,IAAI,KAAK,SAAS,OAAO,QAAQ,KAAK,GAAG,CAAC;AAChD,UAAI,KAAK,KAAK;AAAA,IAChB;AACA,WAAO;AAAA,EACT;AACF;AAEA,IAAI,kBAAmC;AAGhC,SAAS,qBAA+B;AAC7C,MAAI,CAAC,gBAAiB,mBAAkB,IAAI,0BAA0B;AACtE,SAAO;AACT;AAGO,SAAS,mBAAmB,UAAiC;AAClE,oBAAkB;AACpB;","names":[]}
|