octocms 0.4.17 → 0.4.19
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/README.md +1 -1
- package/dist/admin/actions/agent.d.ts.map +1 -1
- package/dist/admin/actions/agent.js +3 -4
- package/dist/admin/actions/agent.js.map +1 -1
- package/dist/admin/actions/entries.d.ts.map +1 -1
- package/dist/admin/actions/entries.js +79 -48
- package/dist/admin/actions/entries.js.map +1 -1
- package/dist/admin/actions/files.d.ts +0 -1
- package/dist/admin/actions/files.d.ts.map +1 -1
- package/dist/admin/actions/files.js +66 -66
- package/dist/admin/actions/files.js.map +1 -1
- package/dist/admin/actions/git.d.ts.map +1 -1
- package/dist/admin/actions/git.js +0 -2
- package/dist/admin/actions/git.js.map +1 -1
- package/dist/admin/actions/media.d.ts.map +1 -1
- package/dist/admin/actions/media.js +53 -24
- package/dist/admin/actions/media.js.map +1 -1
- package/dist/admin/actions/utils.d.ts +12 -20
- package/dist/admin/actions/utils.d.ts.map +1 -1
- package/dist/admin/actions/utils.js +10 -5
- package/dist/admin/actions/utils.js.map +1 -1
- package/dist/admin/auth/cookies.d.ts +16 -0
- package/dist/admin/auth/cookies.d.ts.map +1 -0
- package/dist/admin/auth/cookies.js +45 -0
- package/dist/admin/auth/cookies.js.map +1 -0
- package/dist/admin/auth/env.d.ts +6 -0
- package/dist/admin/auth/env.d.ts.map +1 -0
- package/dist/admin/auth/env.js +25 -0
- package/dist/admin/auth/env.js.map +1 -0
- package/dist/admin/auth/oauthApp.d.ts +8 -0
- package/dist/admin/auth/oauthApp.d.ts.map +1 -0
- package/dist/admin/auth/oauthApp.js +64 -0
- package/dist/admin/auth/oauthApp.js.map +1 -0
- package/dist/admin/auth/seal.d.ts +6 -0
- package/dist/admin/auth/seal.d.ts.map +1 -0
- package/dist/admin/auth/seal.js +61 -0
- package/dist/admin/auth/seal.js.map +1 -0
- package/dist/admin/auth/session.d.ts +19 -0
- package/dist/admin/auth/session.d.ts.map +1 -0
- package/dist/admin/auth/session.js +89 -0
- package/dist/admin/auth/session.js.map +1 -0
- package/dist/admin/auth/types.d.ts +17 -0
- package/dist/admin/auth/types.d.ts.map +1 -0
- package/dist/admin/auth/types.js +1 -0
- package/dist/admin/auth/types.js.map +1 -0
- package/dist/admin/authRoutes.d.ts +14 -0
- package/dist/admin/authRoutes.d.ts.map +1 -0
- package/dist/admin/authRoutes.js +147 -0
- package/dist/admin/authRoutes.js.map +1 -0
- package/dist/admin/error.d.ts +9 -0
- package/dist/admin/error.d.ts.map +1 -0
- package/dist/admin/error.js +7 -0
- package/dist/admin/error.js.map +1 -0
- package/dist/admin/github.d.ts +20 -2
- package/dist/admin/github.d.ts.map +1 -1
- package/dist/admin/github.js +130 -25
- package/dist/admin/github.js.map +1 -1
- package/dist/admin/index.d.ts +5 -7
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +2 -3
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/pages/AdminErrorView.d.ts +1 -1
- package/dist/admin/pages/AdminErrorView.js.map +1 -1
- package/dist/admin/pages/AdminLayout.d.ts +1 -1
- package/dist/admin/pages/AdminLayout.d.ts.map +1 -1
- package/dist/admin/pages/AdminLayout.js +1 -1
- package/dist/admin/pages/AdminLayout.js.map +1 -1
- package/dist/admin/pages/CollectionPage.d.ts.map +1 -1
- package/dist/admin/pages/CollectionPage.js +2 -3
- package/dist/admin/pages/CollectionPage.js.map +1 -1
- package/dist/admin/pages/ContentModelPage.d.ts.map +1 -1
- package/dist/admin/pages/ContentModelPage.js +2 -3
- package/dist/admin/pages/ContentModelPage.js.map +1 -1
- package/dist/admin/pages/ContentPage.d.ts.map +1 -1
- package/dist/admin/pages/ContentPage.js +2 -3
- package/dist/admin/pages/ContentPage.js.map +1 -1
- package/dist/admin/pages/ContentTypePage.d.ts.map +1 -1
- package/dist/admin/pages/ContentTypePage.js +2 -3
- package/dist/admin/pages/ContentTypePage.js.map +1 -1
- package/dist/admin/pages/EntryPage.d.ts.map +1 -1
- package/dist/admin/pages/EntryPage.js +2 -3
- package/dist/admin/pages/EntryPage.js.map +1 -1
- package/dist/admin/pages/MediaAssetPage.d.ts.map +1 -1
- package/dist/admin/pages/MediaAssetPage.js +2 -3
- package/dist/admin/pages/MediaAssetPage.js.map +1 -1
- package/dist/admin/pages/MediaPage.d.ts.map +1 -1
- package/dist/admin/pages/MediaPage.js +2 -3
- package/dist/admin/pages/MediaPage.js.map +1 -1
- package/dist/admin/provider.d.ts +1 -3
- package/dist/admin/provider.d.ts.map +1 -1
- package/dist/admin/provider.js +2 -7
- package/dist/admin/provider.js.map +1 -1
- package/dist/admin/query/hooks/useEntryList.d.ts +1 -1
- package/dist/admin/query/hooks/useEntryList.d.ts.map +1 -1
- package/dist/admin/store/contentStore.d.ts +20 -1
- package/dist/admin/store/contentStore.d.ts.map +1 -1
- package/dist/admin/store/contentStore.js +297 -16
- package/dist/admin/store/contentStore.js.map +1 -1
- package/dist/admin/store/contentStoreFetch.d.ts +54 -14
- package/dist/admin/store/contentStoreFetch.d.ts.map +1 -1
- package/dist/admin/store/contentStoreFetch.js +197 -65
- package/dist/admin/store/contentStoreFetch.js.map +1 -1
- package/dist/admin/store/contentStoreNextCache.d.ts +18 -0
- package/dist/admin/store/contentStoreNextCache.d.ts.map +1 -0
- package/dist/admin/store/contentStoreNextCache.js +166 -0
- package/dist/admin/store/contentStoreNextCache.js.map +1 -0
- package/dist/admin/store/contentStoreTypes.d.ts +44 -0
- package/dist/admin/store/contentStoreTypes.d.ts.map +1 -1
- package/dist/admin/store/contentStoreTypes.js +76 -0
- package/dist/admin/store/contentStoreTypes.js.map +1 -1
- package/dist/admin/theme/toggle.d.ts.map +1 -1
- package/dist/admin/theme/toggle.js +2 -11
- package/dist/admin/theme/toggle.js.map +1 -1
- package/dist/agent/chatApi.d.ts +2 -2
- package/dist/agent/chatApi.d.ts.map +1 -1
- package/dist/agent/chatApi.js +3 -4
- package/dist/agent/chatApi.js.map +1 -1
- package/dist/{agentDocs-6LQD3JUN.js → agentDocs-PVLGO5EN.js} +2 -2
- package/dist/{chunk-Q73JSGXV.js → chunk-G6BYS6LI.js} +23 -1
- package/dist/chunk-G6BYS6LI.js.map +1 -0
- package/dist/{chunk-FZROCUU3.js → chunk-OJ3KR7WR.js} +54 -9
- package/dist/chunk-OJ3KR7WR.js.map +1 -0
- package/dist/chunk-R6TFBC3O.js +7 -0
- package/dist/{chunk-CIKOAIJP.js.map → chunk-R6TFBC3O.js.map} +1 -1
- package/dist/cli/index.js +7 -7
- package/dist/cli/lib/schemaDocs.d.ts.map +1 -1
- package/dist/cli/lib/schemaDocs.js +14 -1
- package/dist/cli/lib/schemaDocs.js.map +1 -1
- package/dist/cli/lib/templates.js +44 -7
- package/dist/cli/lib/templates.js.map +1 -1
- package/dist/cli/lib/validateConfig.d.ts.map +1 -1
- package/dist/cli/lib/validateConfig.js +22 -0
- package/dist/cli/lib/validateConfig.js.map +1 -1
- package/dist/components/Chat/ChatAgentSetup.js +1 -1
- package/dist/components/Chat/ChatAgentSetup.js.map +1 -1
- package/dist/components/Chat/useChatStream.d.ts.map +1 -1
- package/dist/components/Chat/useChatStream.js +2 -1
- package/dist/components/Chat/useChatStream.js.map +1 -1
- package/dist/components/ContentModel/SchemaOptionFieldInput.js +1 -1
- package/dist/components/ContentModel/SchemaOptionFieldInput.js.map +1 -1
- package/dist/components/Dashboard/DashboardContent.js +1 -1
- package/dist/components/Dashboard/DashboardContent.js.map +1 -1
- package/dist/components/Layout/Layout.d.ts.map +1 -1
- package/dist/components/Layout/Layout.js +24 -21
- package/dist/components/Layout/Layout.js.map +1 -1
- package/dist/components/Layout/LeftNavItem.js +1 -1
- package/dist/components/Layout/LeftNavItem.js.map +1 -1
- package/dist/components/Layout/TopHeader.js +2 -2
- package/dist/components/Layout/TopHeader.js.map +1 -1
- package/dist/components/Layout/UserAccountDialog.d.ts.map +1 -1
- package/dist/components/Layout/UserAccountDialog.js +3 -2
- package/dist/components/Layout/UserAccountDialog.js.map +1 -1
- package/dist/components/MediaAsset/skeletons/MediaMetadataFormSkeleton.js +1 -1
- package/dist/components/MediaAsset/skeletons/MediaMetadataFormSkeleton.js.map +1 -1
- package/dist/components/OctoCmsSurfaceMarker.d.ts +3 -0
- package/dist/components/OctoCmsSurfaceMarker.d.ts.map +1 -0
- package/dist/components/OctoCmsSurfaceMarker.js +21 -0
- package/dist/components/OctoCmsSurfaceMarker.js.map +1 -0
- package/dist/components/public/RichTextContent.js +3 -3
- package/dist/components/public/RichTextContent.js.map +1 -1
- package/dist/components/public/SearchBox.d.ts.map +1 -1
- package/dist/components/public/SearchBox.js +2 -1
- package/dist/components/public/SearchBox.js.map +1 -1
- package/dist/components/ui/BranchChip/BranchChip.js +1 -1
- package/dist/components/ui/BranchChip/BranchChip.js.map +1 -1
- package/dist/components/ui/FormField/richtext/ConditionEmbedEditor.js +1 -1
- package/dist/components/ui/FormField/richtext/ConditionEmbedEditor.js.map +1 -1
- package/dist/components/ui/FormField/richtext/ImageEmbedEditor.js +5 -5
- package/dist/components/ui/FormField/richtext/ImageEmbedEditor.js.map +1 -1
- package/dist/components/ui/FormField/richtext/ReferenceEmbedEditor.d.ts.map +1 -1
- package/dist/components/ui/FormField/richtext/ReferenceEmbedEditor.js +3 -3
- package/dist/components/ui/FormField/richtext/ReferenceEmbedEditor.js.map +1 -1
- package/dist/components/ui/StatusBadge/StatusBadge.js +3 -3
- package/dist/components/ui/StatusBadge/StatusBadge.js.map +1 -1
- package/dist/{embeddingsGen-FXWCPGB7.js → embeddingsGen-2RI5O6UB.js} +3 -3
- package/dist/{github-7HIP6RW3.js → github-AZDUTXBU.js} +194 -122
- package/dist/github-AZDUTXBU.js.map +1 -0
- package/dist/github-public.d.ts +1 -1
- package/dist/github-public.d.ts.map +1 -1
- package/dist/github-public.js +2 -1
- package/dist/github-public.js.map +1 -1
- package/dist/globals.css +1286 -1247
- package/dist/hooks/useCmsSession.d.ts +10 -0
- package/dist/hooks/useCmsSession.d.ts.map +1 -0
- package/dist/hooks/useCmsSession.js +48 -0
- package/dist/hooks/useCmsSession.js.map +1 -0
- package/dist/{init-TZFZEP4O.js → init-QAWQ6FOL.js} +15 -10
- package/dist/init-QAWQ6FOL.js.map +1 -0
- package/dist/lib/adminCacheConfig.d.ts +9 -0
- package/dist/lib/adminCacheConfig.d.ts.map +1 -0
- package/dist/lib/adminCacheConfig.js +20 -0
- package/dist/lib/adminCacheConfig.js.map +1 -0
- package/dist/lib/cmsSurface.d.ts +3 -0
- package/dist/lib/cmsSurface.d.ts.map +1 -0
- package/dist/lib/cmsSurface.js +6 -0
- package/dist/lib/cmsSurface.js.map +1 -0
- package/dist/lib/githubContentMode.d.ts +5 -0
- package/dist/lib/githubContentMode.d.ts.map +1 -1
- package/dist/lib/githubContentMode.js +5 -1
- package/dist/lib/githubContentMode.js.map +1 -1
- package/dist/lib/octocmsApiRoutes.d.ts +15 -0
- package/dist/lib/octocmsApiRoutes.d.ts.map +1 -0
- package/dist/lib/octocmsApiRoutes.js +15 -0
- package/dist/lib/octocmsApiRoutes.js.map +1 -0
- package/dist/query.cjs +7 -7
- package/dist/query.cjs.map +1 -1
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +14 -8
- package/dist/query.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/{typesGen-QWWTC4SZ.js → typesGen-HK3YO52N.js} +2 -2
- package/dist/{update-7KYYD57J.js → update-7EDULZ3C.js} +77 -28
- package/dist/update-7EDULZ3C.js.map +1 -0
- package/dist/{validate-AAW4IGWD.js → validate-UVZSNCOX.js} +2 -2
- package/dist/withOctoCMS.cjs +4 -0
- package/dist/withOctoCMS.cjs.map +1 -1
- package/dist/withOctoCMS.d.ts.map +1 -1
- package/dist/withOctoCMS.js +4 -0
- package/dist/withOctoCMS.js.map +1 -1
- package/docs/admin-cache.md +25 -0
- package/docs/editing-schema.md +2 -0
- package/globals.css +1286 -1247
- package/package.json +7 -6
- package/dist/admin/auth.d.ts +0 -3
- package/dist/admin/auth.d.ts.map +0 -1
- package/dist/admin/auth.js +0 -28
- package/dist/admin/auth.js.map +0 -1
- package/dist/chunk-CIKOAIJP.js +0 -7
- package/dist/chunk-FZROCUU3.js.map +0 -1
- package/dist/chunk-Q73JSGXV.js.map +0 -1
- package/dist/github-7HIP6RW3.js.map +0 -1
- package/dist/init-TZFZEP4O.js.map +0 -1
- package/dist/update-7KYYD57J.js.map +0 -1
- /package/dist/{agentDocs-6LQD3JUN.js.map → agentDocs-PVLGO5EN.js.map} +0 -0
- /package/dist/{embeddingsGen-FXWCPGB7.js.map → embeddingsGen-2RI5O6UB.js.map} +0 -0
- /package/dist/{typesGen-QWWTC4SZ.js.map → typesGen-HK3YO52N.js.map} +0 -0
- /package/dist/{validate-AAW4IGWD.js.map → validate-UVZSNCOX.js.map} +0 -0
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ Visit `http://localhost:3000/cms` to open the editor.
|
|
|
47
47
|
```bash
|
|
48
48
|
GITHUB_ID= # GitHub App client ID
|
|
49
49
|
GITHUB_SECRET= # GitHub App client secret
|
|
50
|
-
NEXTAUTH_SECRET=
|
|
50
|
+
NEXTAUTH_SECRET= # Random string: openssl rand -base64 32
|
|
51
51
|
NEXTAUTH_URL=http://localhost:3000
|
|
52
52
|
|
|
53
53
|
# Required in production
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../../admin/actions/agent.ts"],"names":[],"mappings":"AAEA,OAAO,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../../admin/actions/agent.ts"],"names":[],"mappings":"AAEA,OAAO,kBAAkB,CAAC;AAO1B,MAAM,MAAM,iBAAiB,GACzB;IAAE,OAAO,EAAE,KAAK,CAAA;CAAE,GAClB;IACE,OAAO,EAAE,IAAI,CAAC;IACd,QAAQ,EAAE,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;IAC3C,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEN;;;;;;GAMG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAOvE;AAED,MAAM,MAAM,0BAA0B,GAClC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC;AAEvE;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAsBjG;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,CAAC,CAYzF"}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
"use server";
|
|
2
2
|
import "../../chunk-B5LE2OEC.js";
|
|
3
3
|
import "./registerConfig";
|
|
4
|
-
import { getServerSession } from "next-auth/next";
|
|
5
4
|
import { getAgentConfig } from "../../agent/configStore";
|
|
6
5
|
import { getAgentStatus, isAgentEnabled } from "../../agent/featureFlag";
|
|
7
6
|
import { acceptProposal, isProposal } from "../../agent/proposals";
|
|
8
|
-
import {
|
|
7
|
+
import { getCmsSession } from "../auth/session";
|
|
9
8
|
async function getAgentClientStatus() {
|
|
10
9
|
const cfg = getAgentConfig();
|
|
11
10
|
if (!cfg || !isAgentEnabled(cfg)) return { enabled: false };
|
|
@@ -18,7 +17,7 @@ async function acceptProposalAction(proposal) {
|
|
|
18
17
|
if (!cfg || !isAgentEnabled(cfg)) {
|
|
19
18
|
throw new Error("Chat agent is disabled.");
|
|
20
19
|
}
|
|
21
|
-
const session = await
|
|
20
|
+
const session = await getCmsSession();
|
|
22
21
|
if (!session) {
|
|
23
22
|
throw new Error("Unauthorized.");
|
|
24
23
|
}
|
|
@@ -36,7 +35,7 @@ async function rejectProposalAction(_reason) {
|
|
|
36
35
|
if (!cfg || !isAgentEnabled(cfg)) {
|
|
37
36
|
throw new Error("Chat agent is disabled.");
|
|
38
37
|
}
|
|
39
|
-
const session = await
|
|
38
|
+
const session = await getCmsSession();
|
|
40
39
|
if (!session) {
|
|
41
40
|
throw new Error("Unauthorized.");
|
|
42
41
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../admin/actions/agent.ts"],"sourcesContent":["'use server';\n\nimport './registerConfig';\n\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../admin/actions/agent.ts"],"sourcesContent":["'use server';\n\nimport './registerConfig';\n\nimport { getAgentConfig } from '../../agent/configStore';\nimport { getAgentStatus, isAgentEnabled } from '../../agent/featureFlag';\nimport { acceptProposal, isProposal, type AcceptResult } from '../../agent/proposals';\nimport { getCmsSession } from '../auth/session';\n\nexport type AgentClientStatus =\n | { enabled: false }\n | {\n enabled: true;\n provider: 'anthropic' | 'openai' | 'local';\n model: string;\n };\n\n/**\n * Server-side check exposed to the admin client (Header nav link).\n *\n * Never returns the API key. Returns `{ enabled: false }` when the chat API is\n * disabled (missing config, key, or budget). The `/cms/chat` page still renders\n * a setup guide; this action is for optional client UI that needs a boolean gate.\n */\nexport async function getAgentClientStatus(): Promise<AgentClientStatus> {\n const cfg = getAgentConfig();\n if (!cfg || !isAgentEnabled(cfg)) return { enabled: false };\n // Recompute via getAgentStatus so we exercise the same code path the route uses.\n const status = getAgentStatus(cfg);\n if (!status.enabled) return { enabled: false };\n return { enabled: true, provider: cfg.provider.type, model: cfg.provider.model };\n}\n\nexport type AcceptProposalActionResult =\n | { ok: true; entryPath: string }\n | { ok: false; error: string; fieldErrors?: Record<string, string> };\n\n/**\n * Server action — apply a chat-agent edit/create proposal.\n *\n * Replaces the previous `POST /api/agent/proposals/accept` Route Handler. The\n * client (`useChatStream`) calls this directly via the Server Action transport\n * — no public endpoint, no thin re-export file.\n *\n * Stateless by design: the entire proposal payload arrives over the wire and\n * is re-validated here (and again inside `acceptProposal` via the schema\n * validator + `saveFile`).\n *\n * Returns `{ ok: false, error }` on validation / write failures so the client\n * can render an inline error without try/catch around the action call.\n * Throws only when the agent is disabled or the caller is unauthenticated —\n * those are caller-bug shapes, not user-facing flow.\n */\nexport async function acceptProposalAction(proposal: unknown): Promise<AcceptProposalActionResult> {\n const cfg = getAgentConfig();\n if (!cfg || !isAgentEnabled(cfg)) {\n throw new Error('Chat agent is disabled.');\n }\n\n const session = await getCmsSession();\n if (!session) {\n throw new Error('Unauthorized.');\n }\n\n if (!isProposal(proposal)) {\n return { ok: false, error: 'Invalid proposal payload.' };\n }\n\n const result: AcceptResult = await acceptProposal(proposal);\n if (!result.ok) {\n return result.fieldErrors\n ? { ok: false, error: result.error, fieldErrors: result.fieldErrors }\n : { ok: false, error: result.error };\n }\n return { ok: true, entryPath: result.entryPath };\n}\n\n/**\n * Server action — record that the user dismissed a proposal.\n *\n * Replaces the previous `POST /api/agent/proposals/reject` Route Handler.\n * There's no server-side proposal record to mark rejected; this just\n * acknowledges the click. The client reflects rejection in its own state and\n * feeds the rejection back to the model on the next chat turn as a synthetic\n * system note. The `reason` argument is currently ignored — kept for forward\n * compatibility (e.g. analytics / per-rejection telemetry).\n *\n * Auth-gated to keep the surface consistent with `acceptProposalAction`.\n */\nexport async function rejectProposalAction(_reason?: string | null): Promise<{ ok: true }> {\n const cfg = getAgentConfig();\n if (!cfg || !isAgentEnabled(cfg)) {\n throw new Error('Chat agent is disabled.');\n }\n\n const session = await getCmsSession();\n if (!session) {\n throw new Error('Unauthorized.');\n }\n\n return { ok: true };\n}\n"],"mappings":";;AAEA,OAAO;AAEP,SAAS,sBAAsB;AAC/B,SAAS,gBAAgB,sBAAsB;AAC/C,SAAS,gBAAgB,kBAAqC;AAC9D,SAAS,qBAAqB;AAiB9B,eAAsB,uBAAmD;AACvE,QAAM,MAAM,eAAe;AAC3B,MAAI,CAAC,OAAO,CAAC,eAAe,GAAG,EAAG,QAAO,EAAE,SAAS,MAAM;AAE1D,QAAM,SAAS,eAAe,GAAG;AACjC,MAAI,CAAC,OAAO,QAAS,QAAO,EAAE,SAAS,MAAM;AAC7C,SAAO,EAAE,SAAS,MAAM,UAAU,IAAI,SAAS,MAAM,OAAO,IAAI,SAAS,MAAM;AACjF;AAsBA,eAAsB,qBAAqB,UAAwD;AACjG,QAAM,MAAM,eAAe;AAC3B,MAAI,CAAC,OAAO,CAAC,eAAe,GAAG,GAAG;AAChC,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AAEA,QAAM,UAAU,MAAM,cAAc;AACpC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,eAAe;AAAA,EACjC;AAEA,MAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,WAAO,EAAE,IAAI,OAAO,OAAO,4BAA4B;AAAA,EACzD;AAEA,QAAM,SAAuB,MAAM,eAAe,QAAQ;AAC1D,MAAI,CAAC,OAAO,IAAI;AACd,WAAO,OAAO,cACV,EAAE,IAAI,OAAO,OAAO,OAAO,OAAO,aAAa,OAAO,YAAY,IAClE,EAAE,IAAI,OAAO,OAAO,OAAO,MAAM;AAAA,EACvC;AACA,SAAO,EAAE,IAAI,MAAM,WAAW,OAAO,UAAU;AACjD;AAcA,eAAsB,qBAAqB,SAAgD;AACzF,QAAM,MAAM,eAAe;AAC3B,MAAI,CAAC,OAAO,CAAC,eAAe,GAAG,GAAG;AAChC,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AAEA,QAAM,UAAU,MAAM,cAAc;AACpC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,eAAe;AAAA,EACjC;AAEA,SAAO,EAAE,IAAI,KAAK;AACpB;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entries.d.ts","sourceRoot":"","sources":["../../../admin/actions/entries.ts"],"names":[],"mappings":"AAEA,OAAO,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"entries.d.ts","sourceRoot":"","sources":["../../../admin/actions/entries.ts"],"names":[],"mappings":"AAEA,OAAO,kBAAkB,CAAC;AAS1B,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,aAAa,CAAC;AAQ9D,eAAO,MAAM,YAAY,GAAU,aAAY,MAAa,KAAG,OAAO,CAAC,aAAa,EAAE,CAiGrF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAU,oBAAoB,MAAM,KAAG,OAAO,CAAC,aAAa,EAAE,CAuE3F,CAAC"}
|
|
@@ -3,23 +3,46 @@ import "../../chunk-B5LE2OEC.js";
|
|
|
3
3
|
import "./registerConfig";
|
|
4
4
|
import fsPromises from "fs/promises";
|
|
5
5
|
import path from "path";
|
|
6
|
+
import { cookies } from "next/headers";
|
|
6
7
|
import { getConfig } from "../../lib/configStore";
|
|
7
8
|
import { getContentFiles, getFile, getFileJson } from "./files";
|
|
8
9
|
import { isProductionMode } from "../github";
|
|
9
10
|
import { getMediaEntries } from "./media";
|
|
10
11
|
import { getEntryTitleField } from "./utils";
|
|
12
|
+
import { getStoredEntryListSnapshot, getStoredEntryReferencePaths } from "../store/contentStore";
|
|
11
13
|
const getEntryList = async (collection = "**") => {
|
|
14
|
+
var _a;
|
|
12
15
|
const config = getConfig();
|
|
13
|
-
const
|
|
14
|
-
|
|
16
|
+
const isProd = isProductionMode();
|
|
17
|
+
let storedSnapshot = null;
|
|
18
|
+
if (isProd) {
|
|
19
|
+
try {
|
|
20
|
+
const activeBranch = (_a = (await cookies()).get("cms-active-branch")) == null ? void 0 : _a.value;
|
|
21
|
+
storedSnapshot = await getStoredEntryListSnapshot(collection, activeBranch);
|
|
22
|
+
} catch (e) {
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const files = storedSnapshot ? storedSnapshot.entries.map((entry) => entry.path) : await getContentFiles(collection);
|
|
26
|
+
const storedContentByPath = storedSnapshot ? new Map(storedSnapshot.entries.map((entry) => [entry.path, entry.content])) : null;
|
|
15
27
|
const mediaById = /* @__PURE__ */ new Map();
|
|
16
|
-
|
|
17
|
-
|
|
28
|
+
if (storedSnapshot) {
|
|
29
|
+
for (const entry of storedSnapshot.mediaEntries) {
|
|
30
|
+
const sys = entry.content.sys;
|
|
31
|
+
const fields = entry.content.fields;
|
|
32
|
+
if (typeof (sys == null ? void 0 : sys.id) === "string" && typeof (fields == null ? void 0 : fields.extension) === "string") {
|
|
33
|
+
mediaById.set(sys.id, { publicUrl: `/media/${sys.id}.${fields.extension}` });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
const mediaList = await getMediaEntries().catch(() => []);
|
|
38
|
+
for (const media of mediaList) {
|
|
39
|
+
mediaById.set(media.id, { publicUrl: media.publicUrl });
|
|
40
|
+
}
|
|
18
41
|
}
|
|
19
42
|
const imageFieldKeyByType = /* @__PURE__ */ new Map();
|
|
20
43
|
function firstImageFieldKey(type) {
|
|
21
|
-
var
|
|
22
|
-
if (imageFieldKeyByType.has(type)) return (
|
|
44
|
+
var _a2, _b;
|
|
45
|
+
if (imageFieldKeyByType.has(type)) return (_a2 = imageFieldKeyByType.get(type)) != null ? _a2 : null;
|
|
23
46
|
const collection2 = config.collections[type];
|
|
24
47
|
if (!collection2) {
|
|
25
48
|
imageFieldKeyByType.set(type, null);
|
|
@@ -29,10 +52,9 @@ const getEntryList = async (collection = "**") => {
|
|
|
29
52
|
imageFieldKeyByType.set(type, key);
|
|
30
53
|
return key;
|
|
31
54
|
}
|
|
32
|
-
const isProd = isProductionMode();
|
|
33
55
|
const items = await Promise.all(
|
|
34
56
|
files.map(async (file) => {
|
|
35
|
-
var
|
|
57
|
+
var _a2, _b, _c, _d;
|
|
36
58
|
const nameWithoutFolder = file.replace(`${config.contentFolder}/`, "").replace(".json", "");
|
|
37
59
|
const parts = nameWithoutFolder.split("/");
|
|
38
60
|
const type = parts[0];
|
|
@@ -43,7 +65,7 @@ const getEntryList = async (collection = "**") => {
|
|
|
43
65
|
let updatedAt;
|
|
44
66
|
let thumbnailUrl;
|
|
45
67
|
const [contentResult, statResult] = await Promise.allSettled([
|
|
46
|
-
getFileJson(file),
|
|
68
|
+
storedContentByPath ? Promise.resolve((_a2 = storedContentByPath.get(file)) != null ? _a2 : null) : getFileJson(file),
|
|
47
69
|
isProd ? Promise.resolve(null) : fsPromises.stat(path.join(
|
|
48
70
|
/*turbopackIgnore: true*/
|
|
49
71
|
process.cwd(),
|
|
@@ -52,18 +74,18 @@ const getEntryList = async (collection = "**") => {
|
|
|
52
74
|
]);
|
|
53
75
|
if (contentResult.status === "fulfilled" && contentResult.value) {
|
|
54
76
|
const content = contentResult.value;
|
|
55
|
-
if (titleField && ((
|
|
77
|
+
if (titleField && ((_b = content == null ? void 0 : content.fields) == null ? void 0 : _b[titleField])) {
|
|
56
78
|
title = content.fields[titleField];
|
|
57
79
|
}
|
|
58
80
|
const imgKey = firstImageFieldKey(type);
|
|
59
81
|
if (imgKey) {
|
|
60
|
-
const value = (
|
|
82
|
+
const value = (_c = content == null ? void 0 : content.fields) == null ? void 0 : _c[imgKey];
|
|
61
83
|
if (typeof value === "string" && value.trim()) {
|
|
62
84
|
const hit = mediaById.get(value.trim());
|
|
63
85
|
if (hit) thumbnailUrl = hit.publicUrl;
|
|
64
86
|
}
|
|
65
87
|
}
|
|
66
|
-
if ((
|
|
88
|
+
if ((_d = content == null ? void 0 : content.sys) == null ? void 0 : _d.status) {
|
|
67
89
|
status = content.sys.status;
|
|
68
90
|
}
|
|
69
91
|
}
|
|
@@ -77,54 +99,63 @@ const getEntryList = async (collection = "**") => {
|
|
|
77
99
|
return items;
|
|
78
100
|
};
|
|
79
101
|
const getEntryBacklinks = async (targetReferenceKey) => {
|
|
80
|
-
var _a, _b, _c, _d;
|
|
102
|
+
var _a, _b, _c, _d, _e;
|
|
81
103
|
const config = getConfig();
|
|
82
|
-
|
|
104
|
+
let indexedFiles = null;
|
|
105
|
+
if (isProductionMode()) {
|
|
106
|
+
try {
|
|
107
|
+
const activeBranch = (_a = (await cookies()).get("cms-active-branch")) == null ? void 0 : _a.value;
|
|
108
|
+
indexedFiles = await getStoredEntryReferencePaths(targetReferenceKey, activeBranch);
|
|
109
|
+
} catch (e) {
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const allFiles = indexedFiles != null ? indexedFiles : await getContentFiles("**");
|
|
83
113
|
const backlinks = [];
|
|
84
114
|
for (const file of allFiles) {
|
|
85
115
|
try {
|
|
86
116
|
const content = await getFile(file);
|
|
87
|
-
const type = (
|
|
117
|
+
const type = (_b = content == null ? void 0 : content.sys) == null ? void 0 : _b.type;
|
|
88
118
|
if (!type) continue;
|
|
89
119
|
const collection = config.collections[type];
|
|
90
120
|
if (!collection) continue;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
121
|
+
if (indexedFiles === null) {
|
|
122
|
+
const referenceFieldKeys = Object.keys(collection.fields).filter(
|
|
123
|
+
(k) => collection.fields[k].format === "reference"
|
|
124
|
+
);
|
|
125
|
+
if (referenceFieldKeys.length === 0) continue;
|
|
126
|
+
let found = false;
|
|
127
|
+
for (const fieldKey of referenceFieldKeys) {
|
|
128
|
+
const fieldValue = (_c = content == null ? void 0 : content.fields) == null ? void 0 : _c[fieldKey];
|
|
129
|
+
if (!fieldValue) continue;
|
|
130
|
+
let keys = [];
|
|
131
|
+
if (typeof fieldValue === "string") {
|
|
132
|
+
try {
|
|
133
|
+
const parsed = JSON.parse(fieldValue);
|
|
134
|
+
keys = Array.isArray(parsed) ? parsed : [fieldValue];
|
|
135
|
+
} catch (e) {
|
|
136
|
+
keys = [fieldValue];
|
|
137
|
+
}
|
|
138
|
+
} else if (Array.isArray(fieldValue)) {
|
|
139
|
+
keys = fieldValue;
|
|
140
|
+
}
|
|
141
|
+
if (keys.includes(targetReferenceKey)) {
|
|
142
|
+
found = true;
|
|
143
|
+
break;
|
|
106
144
|
}
|
|
107
|
-
} else if (Array.isArray(fieldValue)) {
|
|
108
|
-
keys = fieldValue;
|
|
109
|
-
}
|
|
110
|
-
if (keys.includes(targetReferenceKey)) {
|
|
111
|
-
found = true;
|
|
112
|
-
break;
|
|
113
145
|
}
|
|
146
|
+
if (!found) continue;
|
|
114
147
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
title = content.fields[titleField];
|
|
123
|
-
}
|
|
124
|
-
const status = ((_d = content == null ? void 0 : content.sys) == null ? void 0 : _d.status) || "merged";
|
|
125
|
-
backlinks.push({ type, id, path: file, title, status });
|
|
148
|
+
const nameWithoutFolder = file.replace(`${config.contentFolder}/`, "").replace(".json", "");
|
|
149
|
+
const parts = nameWithoutFolder.split("/");
|
|
150
|
+
const id = parts[parts.length - 1];
|
|
151
|
+
const titleField = getEntryTitleField(type);
|
|
152
|
+
let title = id;
|
|
153
|
+
if (titleField && ((_d = content == null ? void 0 : content.fields) == null ? void 0 : _d[titleField])) {
|
|
154
|
+
title = content.fields[titleField];
|
|
126
155
|
}
|
|
127
|
-
|
|
156
|
+
const status = ((_e = content == null ? void 0 : content.sys) == null ? void 0 : _e.status) || "merged";
|
|
157
|
+
backlinks.push({ type, id, path: file, title, status });
|
|
158
|
+
} catch (_e2) {
|
|
128
159
|
}
|
|
129
160
|
}
|
|
130
161
|
return backlinks;
|
|
@@ -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, 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"]}
|
|
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 { cookies } from 'next/headers';\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';\nimport { getStoredEntryListSnapshot, getStoredEntryReferencePaths } from '../store/contentStore';\n\nexport const getEntryList = async (collection: string = '**'): Promise<EntryListItem[]> => {\n const config = getConfig();\n const isProd = isProductionMode();\n let storedSnapshot: Awaited<ReturnType<typeof getStoredEntryListSnapshot>> = null;\n\n if (isProd) {\n try {\n const activeBranch = (await cookies()).get('cms-active-branch')?.value;\n storedSnapshot = await getStoredEntryListSnapshot(collection, activeBranch);\n } catch {\n // Store unavailable; retain the direct-read recovery path below.\n }\n }\n\n const files = storedSnapshot ? storedSnapshot.entries.map((entry) => entry.path) : await getContentFiles(collection);\n const storedContentByPath = storedSnapshot\n ? new Map(storedSnapshot.entries.map((entry) => [entry.path, entry.content]))\n : null;\n\n const mediaById = new Map<string, { publicUrl: string }>();\n if (storedSnapshot) {\n for (const entry of storedSnapshot.mediaEntries) {\n const sys = entry.content.sys as { id?: unknown } | undefined;\n const fields = entry.content.fields as { extension?: unknown } | undefined;\n if (typeof sys?.id === 'string' && typeof fields?.extension === 'string') {\n mediaById.set(sys.id, { publicUrl: `/media/${sys.id}.${fields.extension}` });\n }\n }\n } else {\n // Direct-read recovery and local development still use the existing media\n // action, with one batched call regardless of entry count.\n const mediaList = await getMediaEntries().catch(() => []);\n for (const media of mediaList) {\n mediaById.set(media.id, { publicUrl: media.publicUrl });\n }\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 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 storedContentByPath ? Promise.resolve(storedContentByPath.get(file) ?? null) : 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 let indexedFiles: string[] | null = null;\n if (isProductionMode()) {\n try {\n const activeBranch = (await cookies()).get('cms-active-branch')?.value;\n indexedFiles = await getStoredEntryReferencePaths(targetReferenceKey, activeBranch);\n } catch {\n // Store unavailable; retain the direct-read recovery path below.\n }\n }\n\n const allFiles = indexedFiles ?? (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 if (indexedFiles === null) {\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 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 if (!found) continue;\n }\n\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 } catch (_e) {\n // Skip files that can't be read\n }\n }\n\n return backlinks;\n};\n"],"mappings":"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","names":["_a","collection","_e"]}
|
|
@@ -5,7 +5,6 @@ export type { SaveFileResult } from './utils';
|
|
|
5
5
|
* In production, reject writes that would target `config.git.baseBranch` (no feature branch cookie).
|
|
6
6
|
*/
|
|
7
7
|
export declare const assertFeatureBranchForWritesIfRequired: () => Promise<void>;
|
|
8
|
-
export declare const waitForPublicReadConsistency: (fileName: string, expectedContent: string, readRef?: string) => Promise<void>;
|
|
9
8
|
export declare const getContentFiles: (collection?: string) => Promise<string[]>;
|
|
10
9
|
/**
|
|
11
10
|
* List media-entry JSON files (e.g. `cms/media/media-<uuid>.json`).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../../admin/actions/files.ts"],"names":[],"mappings":"AAEA,OAAO,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../../admin/actions/files.ts"],"names":[],"mappings":"AAEA,OAAO,kBAAkB,CAAC;AAsC1B,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAiM9C;;GAEG;AACH,eAAO,MAAM,sCAAsC,QAAa,OAAO,CAAC,IAAI,CAqB3E,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,CAqJxB,CAAC;AAEF,eAAO,MAAM,OAAO,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,aAAa,CAwDjE,CAAC;AAEF,eAAO,MAAM,UAAU,GAAU,UAAU,MAAM,KAAG,OAAO,CAAC,YAAY,CA0DvE,CAAC"}
|
|
@@ -18,15 +18,21 @@ import { persistedFieldsFromFormStrings } from "../../lib/persistedFormFields";
|
|
|
18
18
|
import { normalizeStoredSlug } from "../../lib/slugField";
|
|
19
19
|
import { validateEntryFields } from "../../lib/validateEntryFields";
|
|
20
20
|
import {
|
|
21
|
-
|
|
21
|
+
commitMultipleFilesToGitHub,
|
|
22
|
+
GitHubBranchConflictError,
|
|
22
23
|
getGitHubFile,
|
|
23
24
|
isProductionMode,
|
|
24
25
|
listGitHubFiles,
|
|
25
26
|
listGitHubFilesRecursive,
|
|
26
|
-
readGitHubFilePublic
|
|
27
|
-
saveGitHubFile
|
|
27
|
+
readGitHubFilePublic
|
|
28
28
|
} from "../github";
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
applyCommittedMutations,
|
|
31
|
+
getContentStoreStatus,
|
|
32
|
+
getStoredContentFiles,
|
|
33
|
+
getStoredFile,
|
|
34
|
+
getStoredFileSha
|
|
35
|
+
} from "../store/contentStore";
|
|
30
36
|
import { mediaContentFolder, mediaEntryPath } from "../../lib/mediaPath";
|
|
31
37
|
import { buildJsons } from "./build";
|
|
32
38
|
import {
|
|
@@ -49,24 +55,25 @@ async function syncEmbeddingsForRemoveIfEnabled(entryPath, branch, isProduction)
|
|
|
49
55
|
if (!agentConfig) return;
|
|
50
56
|
await syncEmbeddingsAfterRemove({ agentConfig, entryPath, branch, isProduction });
|
|
51
57
|
}
|
|
52
|
-
async function
|
|
58
|
+
async function getBranchHistoryBatchChange(activeBranch, entryPath) {
|
|
53
59
|
var _a;
|
|
60
|
+
try {
|
|
61
|
+
const historyFile = await getGitHubFile(BRANCH_HISTORY_FILE_PATH, activeBranch);
|
|
62
|
+
const next = mergeHistoryContentWithAppendedEntry(
|
|
63
|
+
(_a = historyFile == null ? void 0 : historyFile.content) != null ? _a : "",
|
|
64
|
+
activeBranch,
|
|
65
|
+
normalizeContentPath(entryPath)
|
|
66
|
+
);
|
|
67
|
+
return next === null ? null : { kind: "upsert-text", path: BRANCH_HISTORY_FILE_PATH, content: next };
|
|
68
|
+
} catch (e) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async function persistLocalBranchHistoryEntryIfNeeded(activeBranch, entryPath) {
|
|
54
73
|
if (!activeBranch) {
|
|
55
74
|
return;
|
|
56
75
|
}
|
|
57
76
|
const normalized = normalizeContentPath(entryPath);
|
|
58
|
-
if (isProductionMode()) {
|
|
59
|
-
try {
|
|
60
|
-
const historyFile = await getGitHubFile(BRANCH_HISTORY_FILE_PATH, activeBranch);
|
|
61
|
-
const next = mergeHistoryContentWithAppendedEntry((_a = historyFile == null ? void 0 : historyFile.content) != null ? _a : "", activeBranch, normalized);
|
|
62
|
-
if (next == null) {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
await saveGitHubFile(BRANCH_HISTORY_FILE_PATH, next, "CMS: track entry in branch history", activeBranch);
|
|
66
|
-
} catch (e) {
|
|
67
|
-
}
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
77
|
try {
|
|
71
78
|
const abs = path.join(
|
|
72
79
|
/*turbopackIgnore: true*/
|
|
@@ -190,28 +197,18 @@ const assertFeatureBranchForWritesIfRequired = async () => {
|
|
|
190
197
|
if (!isProductionMode()) {
|
|
191
198
|
return;
|
|
192
199
|
}
|
|
193
|
-
|
|
200
|
+
const activeBranch = (_a = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)) == null ? void 0 : _a.value;
|
|
201
|
+
if (!activeBranch) {
|
|
194
202
|
throw new Error("Create or select a branch before editing.");
|
|
195
203
|
}
|
|
196
|
-
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
const parsedAttempts = Number.parseInt(process.env.CMS_GITHUB_CONSISTENCY_ATTEMPTS || "8", 10);
|
|
202
|
-
const parsedDelayMs = Number.parseInt(process.env.CMS_GITHUB_CONSISTENCY_DELAY_MS || "250", 10);
|
|
203
|
-
const maxAttempts = Number.isFinite(parsedAttempts) && parsedAttempts > 0 ? parsedAttempts : 1;
|
|
204
|
-
const delayMs = Number.isFinite(parsedDelayMs) && parsedDelayMs >= 0 ? parsedDelayMs : 250;
|
|
205
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
206
|
-
try {
|
|
207
|
-
const visibleContent = await readGitHubFilePublic(fileName, readRef);
|
|
208
|
-
if (visibleContent === expectedContent) {
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
} catch (_e) {
|
|
204
|
+
try {
|
|
205
|
+
const cache = await getContentStoreStatus(activeBranch);
|
|
206
|
+
if (cache.status === "stale") {
|
|
207
|
+
throw new Error("Editing is temporarily disabled because GitHub could not confirm the current branch HEAD.");
|
|
212
208
|
}
|
|
213
|
-
|
|
214
|
-
|
|
209
|
+
} catch (error) {
|
|
210
|
+
if (error instanceof Error && error.message.includes("temporarily disabled")) {
|
|
211
|
+
throw error;
|
|
215
212
|
}
|
|
216
213
|
}
|
|
217
214
|
};
|
|
@@ -485,30 +482,31 @@ const saveFile = async (formData, fileName, options) => {
|
|
|
485
482
|
const entryId = ((_h = payload == null ? void 0 : payload.sys) == null ? void 0 : _h.id) || "";
|
|
486
483
|
const message = `Update ${entryTypeLabel} ${entryId}`;
|
|
487
484
|
const activeBranch = (_i = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)) == null ? void 0 : _i.value;
|
|
488
|
-
|
|
489
|
-
|
|
485
|
+
if (!activeBranch) throw new Error("Create or select a branch before editing.");
|
|
486
|
+
const changes = [{ kind: "upsert-text", path: fileName, content: normalizedData }];
|
|
490
487
|
const mdPaths2 = typeof entryType === "string" ? companionMarkdownPathsForEntry(fileName, entryType, config.collections) : {};
|
|
491
488
|
for (const [fieldName, mdPath] of Object.entries(mdPaths2)) {
|
|
492
489
|
const mdContent = (_j = markdownContents[fieldName]) != null ? _j : "";
|
|
493
|
-
|
|
490
|
+
changes.push({ kind: "upsert-text", path: mdPath, content: mdContent });
|
|
494
491
|
}
|
|
495
492
|
const rtPaths = typeof entryType === "string" ? companionRichTextPathsForEntry(fileName, entryType, config.collections) : {};
|
|
496
493
|
for (const [fieldName, rtPath] of Object.entries(rtPaths)) {
|
|
497
494
|
const rtContent = (_k = markdownContents[fieldName]) != null ? _k : "";
|
|
498
|
-
|
|
495
|
+
changes.push({ kind: "upsert-text", path: rtPath, content: rtContent });
|
|
499
496
|
}
|
|
500
|
-
await
|
|
501
|
-
if (
|
|
502
|
-
|
|
497
|
+
const historyChange = await getBranchHistoryBatchChange(activeBranch, fileName);
|
|
498
|
+
if (historyChange) changes.push(historyChange);
|
|
499
|
+
const commit = await commitMultipleFilesToGitHub(changes, message, activeBranch);
|
|
500
|
+
applyCommittedMutations(activeBranch, commit.sha, [
|
|
501
|
+
{
|
|
503
502
|
type: "upsert",
|
|
504
503
|
path: fileName,
|
|
505
504
|
content: payload,
|
|
506
505
|
sha: "",
|
|
507
|
-
// SHA
|
|
506
|
+
// Blob SHA is corrected by the next manifest refresh.
|
|
508
507
|
companions: markdownContents
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
await persistBranchHistoryEntryIfNeeded(activeBranch, fileName);
|
|
508
|
+
}
|
|
509
|
+
]);
|
|
512
510
|
await syncEmbeddingsForUpsertIfEnabled(fileName, payload, markdownContents, activeBranch, true);
|
|
513
511
|
const built2 = await buildJsons(fileName);
|
|
514
512
|
return built2.success ? actionOk() : built2;
|
|
@@ -539,11 +537,12 @@ const saveFile = async (formData, fileName, options) => {
|
|
|
539
537
|
rtPath
|
|
540
538
|
), rtContent, "utf8");
|
|
541
539
|
}
|
|
542
|
-
await
|
|
540
|
+
await persistLocalBranchHistoryEntryIfNeeded(activeBranchDev, fileName);
|
|
543
541
|
await syncEmbeddingsForUpsertIfEnabled(fileName, payload, markdownContents, activeBranchDev, false);
|
|
544
542
|
const built = await buildJsons(fileName);
|
|
545
543
|
return built.success ? actionOk() : built;
|
|
546
544
|
} catch (e) {
|
|
545
|
+
if (e instanceof GitHubBranchConflictError) return actionErr(e);
|
|
547
546
|
return actionErr(new Error(`Failed to save file: ${getErrorMessage(e)}`));
|
|
548
547
|
}
|
|
549
548
|
};
|
|
@@ -567,17 +566,19 @@ const newFile = async (type) => {
|
|
|
567
566
|
if (isProductionMode()) {
|
|
568
567
|
await assertFeatureBranchForWritesIfRequired();
|
|
569
568
|
const activeBranch = (_a = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)) == null ? void 0 : _a.value;
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
569
|
+
if (!activeBranch) throw new Error("Create or select a branch before editing.");
|
|
570
|
+
const changes = [{ kind: "upsert-text", path: file, content: normalizedData }];
|
|
571
|
+
const historyChange = await getBranchHistoryBatchChange(activeBranch, file);
|
|
572
|
+
if (historyChange) changes.push(historyChange);
|
|
573
|
+
const commit = await commitMultipleFilesToGitHub(changes, `Create new ${type} ${id}`, activeBranch);
|
|
574
|
+
applyCommittedMutations(activeBranch, commit.sha, [
|
|
575
|
+
{
|
|
574
576
|
type: "upsert",
|
|
575
577
|
path: file,
|
|
576
578
|
content: values,
|
|
577
579
|
sha: ""
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
await persistBranchHistoryEntryIfNeeded(activeBranch, file);
|
|
580
|
+
}
|
|
581
|
+
]);
|
|
581
582
|
await syncEmbeddingsForUpsertIfEnabled(file, values, {}, activeBranch, true);
|
|
582
583
|
const built2 = await buildJsons(file);
|
|
583
584
|
return built2.success ? { success: true, path: file } : { success: false, error: built2.error };
|
|
@@ -591,12 +592,12 @@ const newFile = async (type) => {
|
|
|
591
592
|
);
|
|
592
593
|
await fsPromises.mkdir(path.dirname(filePath), { recursive: true });
|
|
593
594
|
await fsPromises.writeFile(filePath, normalizedData, "utf8");
|
|
594
|
-
await
|
|
595
|
+
await persistLocalBranchHistoryEntryIfNeeded(activeBranchDev, file);
|
|
595
596
|
await syncEmbeddingsForUpsertIfEnabled(file, values, {}, activeBranchDev, false);
|
|
596
597
|
const built = await buildJsons(file);
|
|
597
598
|
return built.success ? { success: true, path: file } : { success: false, error: built.error };
|
|
598
599
|
} catch (e) {
|
|
599
|
-
return { success: false, error: getErrorMessage(e) };
|
|
600
|
+
return e instanceof GitHubBranchConflictError ? actionErr(e) : { success: false, error: getErrorMessage(e) };
|
|
600
601
|
}
|
|
601
602
|
};
|
|
602
603
|
const removeFile = async (fileName) => {
|
|
@@ -616,16 +617,16 @@ const removeFile = async (fileName) => {
|
|
|
616
617
|
if (isProductionMode()) {
|
|
617
618
|
await assertFeatureBranchForWritesIfRequired();
|
|
618
619
|
const activeBranch = (_b = (await cookies()).get(CMS_ACTIVE_BRANCH_COOKIE)) == null ? void 0 : _b.value;
|
|
619
|
-
|
|
620
|
+
if (!activeBranch) throw new Error("Create or select a branch before editing.");
|
|
621
|
+
const changes = [{ kind: "delete", path: fileName }];
|
|
620
622
|
for (const mdPath of companionPaths) {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
623
|
+
const cachedSha = await getStoredFileSha(mdPath, activeBranch);
|
|
624
|
+
if (cachedSha !== null || await getGitHubFile(mdPath, activeBranch)) {
|
|
625
|
+
changes.push({ kind: "delete", path: mdPath });
|
|
624
626
|
}
|
|
625
627
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
}
|
|
628
|
+
const commit = await commitMultipleFilesToGitHub(changes, `Remove ${fileName}`, activeBranch);
|
|
629
|
+
applyCommittedMutations(activeBranch, commit.sha, [{ type: "delete", path: fileName }]);
|
|
629
630
|
await syncEmbeddingsForRemoveIfEnabled(fileName, activeBranch, true);
|
|
630
631
|
const built2 = await buildJsons(fileName);
|
|
631
632
|
return built2.success ? actionOk() : built2;
|
|
@@ -662,7 +663,6 @@ export {
|
|
|
662
663
|
getMediaFiles,
|
|
663
664
|
newFile,
|
|
664
665
|
removeFile,
|
|
665
|
-
saveFile
|
|
666
|
-
waitForPublicReadConsistency
|
|
666
|
+
saveFile
|
|
667
667
|
};
|
|
668
668
|
//# sourceMappingURL=files.js.map
|