@aphexcms/cms-core 9.7.0 → 9.8.1
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/agent-chat-state.svelte.d.ts +38 -0
- package/dist/agent-chat-state.svelte.d.ts.map +1 -0
- package/dist/agent-chat-state.svelte.js +86 -0
- package/dist/ai/content-workspace-tools.d.ts +3 -0
- package/dist/ai/content-workspace-tools.d.ts.map +1 -0
- package/dist/ai/content-workspace-tools.js +50 -0
- package/dist/ai/default-system-prompt.d.ts +2 -0
- package/dist/ai/default-system-prompt.d.ts.map +1 -0
- package/dist/ai/default-system-prompt.js +15 -0
- package/dist/ai/index.d.ts +2 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +4 -0
- package/dist/ai/interfaces/ai-provider.d.ts +67 -0
- package/dist/ai/interfaces/ai-provider.d.ts.map +1 -0
- package/dist/ai/interfaces/ai-provider.js +16 -0
- package/dist/ai/run-agent-turn.d.ts +29 -0
- package/dist/ai/run-agent-turn.d.ts.map +1 -0
- package/dist/ai/run-agent-turn.js +166 -0
- package/dist/api/agent-chat.d.ts +27 -0
- package/dist/api/agent-chat.d.ts.map +1 -0
- package/dist/api/agent-chat.js +64 -0
- package/dist/api/documents.d.ts +8 -3
- package/dist/api/documents.d.ts.map +1 -1
- package/dist/api/documents.js +6 -6
- package/dist/api/schemas/agent-chat.d.ts +45 -0
- package/dist/api/schemas/agent-chat.d.ts.map +1 -0
- package/dist/api/schemas/agent-chat.js +35 -0
- package/dist/api/schemas/agent-operations.d.ts +13 -0
- package/dist/api/schemas/agent-operations.d.ts.map +1 -0
- package/dist/api/schemas/agent-operations.js +15 -0
- package/dist/api/schemas/documents.d.ts +22 -0
- package/dist/api/schemas/documents.d.ts.map +1 -1
- package/dist/api/schemas/documents.js +20 -2
- package/dist/chunk-recovery.d.ts +33 -0
- package/dist/chunk-recovery.d.ts.map +1 -0
- package/dist/chunk-recovery.js +131 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -0
- package/dist/client/ui.d.ts +1 -0
- package/dist/client/ui.d.ts.map +1 -1
- package/dist/client/ui.js +1 -0
- package/dist/components/AdminApp.svelte +37 -3
- package/dist/components/AdminApp.svelte.d.ts.map +1 -1
- package/dist/components/admin/ActivityView.svelte +251 -42
- package/dist/components/admin/ActivityView.svelte.d.ts.map +1 -1
- package/dist/components/admin/AgentChat.svelte +977 -0
- package/dist/components/admin/AgentChat.svelte.d.ts +17 -0
- package/dist/components/admin/AgentChat.svelte.d.ts.map +1 -0
- package/dist/components/admin/DocumentEditor.svelte +273 -60
- package/dist/components/admin/DocumentEditor.svelte.d.ts +6 -0
- package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -1
- package/dist/components/admin/ObjectModal.svelte +4 -2
- package/dist/components/admin/fields/ArrayField.svelte +4 -1
- package/dist/components/admin/fields/ReferenceField.svelte +12 -1
- package/dist/components/admin/fields/ReferenceField.svelte.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -0
- package/dist/components/layout/Sidebar.svelte +26 -2
- package/dist/components/layout/Sidebar.svelte.d.ts +1 -0
- package/dist/components/layout/Sidebar.svelte.d.ts.map +1 -1
- package/dist/db/interfaces/agent-change-sets.d.ts +17 -0
- package/dist/db/interfaces/agent-change-sets.d.ts.map +1 -0
- package/dist/db/interfaces/agent-change-sets.js +1 -0
- package/dist/db/interfaces/document.d.ts +25 -3
- package/dist/db/interfaces/document.d.ts.map +1 -1
- package/dist/db/interfaces/document.js +19 -1
- package/dist/db/interfaces/index.d.ts +4 -1
- package/dist/db/interfaces/index.d.ts.map +1 -1
- package/dist/db/interfaces/index.js +1 -1
- package/dist/document-refresh.svelte.d.ts +2 -0
- package/dist/document-refresh.svelte.d.ts.map +1 -1
- package/dist/document-refresh.svelte.js +15 -0
- package/dist/document-workspace-registry.svelte.d.ts +15 -0
- package/dist/document-workspace-registry.svelte.d.ts.map +1 -0
- package/dist/document-workspace-registry.svelte.js +30 -0
- package/dist/field-validation/utils.d.ts.map +1 -1
- package/dist/field-validation/utils.js +211 -29
- package/dist/hooks.d.ts +2 -0
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +2 -0
- package/dist/lib/agent-chat-state.svelte.d.ts +38 -0
- package/dist/lib/agent-chat-state.svelte.d.ts.map +1 -0
- package/dist/lib/agent-chat-state.svelte.js +87 -0
- package/dist/lib/agent-chat-state.svelte.js.map +1 -0
- package/dist/lib/ai/content-workspace-tools.d.ts +3 -0
- package/dist/lib/ai/content-workspace-tools.d.ts.map +1 -0
- package/dist/lib/ai/content-workspace-tools.js +51 -0
- package/dist/lib/ai/content-workspace-tools.js.map +1 -0
- package/dist/lib/ai/default-system-prompt.d.ts +2 -0
- package/dist/lib/ai/default-system-prompt.d.ts.map +1 -0
- package/dist/lib/ai/default-system-prompt.js +16 -0
- package/dist/lib/ai/default-system-prompt.js.map +1 -0
- package/dist/lib/ai/index.d.ts +2 -0
- package/dist/lib/ai/index.d.ts.map +1 -0
- package/dist/lib/ai/index.js +5 -0
- package/dist/lib/ai/index.js.map +1 -0
- package/dist/lib/ai/interfaces/ai-provider.d.ts +67 -0
- package/dist/lib/ai/interfaces/ai-provider.d.ts.map +1 -0
- package/dist/lib/ai/interfaces/ai-provider.js +17 -0
- package/dist/lib/ai/interfaces/ai-provider.js.map +1 -0
- package/dist/lib/ai/run-agent-turn.d.ts +29 -0
- package/dist/lib/ai/run-agent-turn.d.ts.map +1 -0
- package/dist/lib/ai/run-agent-turn.js +167 -0
- package/dist/lib/ai/run-agent-turn.js.map +1 -0
- package/dist/lib/api/agent-chat.d.ts +27 -0
- package/dist/lib/api/agent-chat.d.ts.map +1 -0
- package/dist/lib/api/agent-chat.js +65 -0
- package/dist/lib/api/agent-chat.js.map +1 -0
- package/dist/lib/api/documents.d.ts +8 -3
- package/dist/lib/api/documents.d.ts.map +1 -1
- package/dist/lib/api/documents.js +6 -6
- package/dist/lib/api/documents.js.map +1 -1
- package/dist/lib/api/schemas/agent-chat.d.ts +45 -0
- package/dist/lib/api/schemas/agent-chat.d.ts.map +1 -0
- package/dist/lib/api/schemas/agent-chat.js +36 -0
- package/dist/lib/api/schemas/agent-chat.js.map +1 -0
- package/dist/lib/api/schemas/agent-operations.d.ts +13 -0
- package/dist/lib/api/schemas/agent-operations.d.ts.map +1 -0
- package/dist/lib/api/schemas/agent-operations.js +16 -0
- package/dist/lib/api/schemas/agent-operations.js.map +1 -0
- package/dist/lib/api/schemas/documents.d.ts +22 -0
- package/dist/lib/api/schemas/documents.d.ts.map +1 -1
- package/dist/lib/api/schemas/documents.js +20 -2
- package/dist/lib/api/schemas/documents.js.map +1 -1
- package/dist/lib/chunk-recovery.d.ts +33 -0
- package/dist/lib/chunk-recovery.d.ts.map +1 -0
- package/dist/lib/chunk-recovery.js +132 -0
- package/dist/lib/chunk-recovery.js.map +1 -0
- package/dist/lib/client/index.d.ts +1 -0
- package/dist/lib/client/index.d.ts.map +1 -1
- package/dist/lib/client/index.js +1 -0
- package/dist/lib/client/index.js.map +1 -1
- package/dist/lib/client/ui.d.ts +1 -0
- package/dist/lib/client/ui.d.ts.map +1 -1
- package/dist/lib/client/ui.js +1 -0
- package/dist/lib/client/ui.js.map +1 -1
- package/dist/lib/components/index.d.ts +1 -0
- package/dist/lib/components/index.d.ts.map +1 -1
- package/dist/lib/components/index.js +1 -0
- package/dist/lib/components/index.js.map +1 -1
- package/dist/lib/db/interfaces/agent-change-sets.d.ts +17 -0
- package/dist/lib/db/interfaces/agent-change-sets.d.ts.map +1 -0
- package/dist/lib/db/interfaces/agent-change-sets.js +2 -0
- package/dist/lib/db/interfaces/agent-change-sets.js.map +1 -0
- package/dist/lib/db/interfaces/document.d.ts +25 -3
- package/dist/lib/db/interfaces/document.d.ts.map +1 -1
- package/dist/lib/db/interfaces/document.js +19 -1
- package/dist/lib/db/interfaces/document.js.map +1 -1
- package/dist/lib/db/interfaces/index.d.ts +4 -1
- package/dist/lib/db/interfaces/index.d.ts.map +1 -1
- package/dist/lib/db/interfaces/index.js +1 -1
- package/dist/lib/db/interfaces/index.js.map +1 -1
- package/dist/lib/document-refresh.svelte.d.ts +2 -0
- package/dist/lib/document-refresh.svelte.d.ts.map +1 -1
- package/dist/lib/document-refresh.svelte.js +15 -0
- package/dist/lib/document-refresh.svelte.js.map +1 -1
- package/dist/lib/document-workspace-registry.svelte.d.ts +15 -0
- package/dist/lib/document-workspace-registry.svelte.d.ts.map +1 -0
- package/dist/lib/document-workspace-registry.svelte.js +31 -0
- package/dist/lib/document-workspace-registry.svelte.js.map +1 -0
- package/dist/lib/field-validation/utils.d.ts.map +1 -1
- package/dist/lib/field-validation/utils.js +211 -29
- package/dist/lib/field-validation/utils.js.map +1 -1
- package/dist/lib/hooks.d.ts +2 -0
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/hooks.js +2 -0
- package/dist/lib/hooks.js.map +1 -1
- package/dist/lib/local-api/collection-api.d.ts +7 -2
- package/dist/lib/local-api/collection-api.d.ts.map +1 -1
- package/dist/lib/local-api/collection-api.js +17 -10
- package/dist/lib/local-api/collection-api.js.map +1 -1
- package/dist/lib/mcp/tools.d.ts +39 -4
- package/dist/lib/mcp/tools.d.ts.map +1 -1
- package/dist/lib/mcp/tools.js +546 -362
- package/dist/lib/mcp/tools.js.map +1 -1
- package/dist/lib/plugins/resolver.d.ts +8 -1
- package/dist/lib/plugins/resolver.d.ts.map +1 -1
- package/dist/lib/plugins/resolver.js +14 -1
- package/dist/lib/plugins/resolver.js.map +1 -1
- package/dist/lib/plugins/types.d.ts +23 -2
- package/dist/lib/plugins/types.d.ts.map +1 -1
- package/dist/lib/plugins/types.js.map +1 -1
- package/dist/lib/preview/stega.d.ts +2 -2
- package/dist/lib/preview/stega.d.ts.map +1 -1
- package/dist/lib/preview/stega.js +52 -29
- package/dist/lib/preview/stega.js.map +1 -1
- package/dist/lib/schema-utils/validator.d.ts.map +1 -1
- package/dist/lib/schema-utils/validator.js +5 -2
- package/dist/lib/schema-utils/validator.js.map +1 -1
- package/dist/lib/server/api/index.d.ts.map +1 -1
- package/dist/lib/server/api/index.js +8 -0
- package/dist/lib/server/api/index.js.map +1 -1
- package/dist/lib/server/api/routes/agent-change-sets.d.ts +4 -0
- package/dist/lib/server/api/routes/agent-change-sets.d.ts.map +1 -0
- package/dist/lib/server/api/routes/agent-change-sets.js +132 -0
- package/dist/lib/server/api/routes/agent-change-sets.js.map +1 -0
- package/dist/lib/server/api/routes/agent-chat.d.ts +14 -0
- package/dist/lib/server/api/routes/agent-chat.d.ts.map +1 -0
- package/dist/lib/server/api/routes/agent-chat.js +224 -0
- package/dist/lib/server/api/routes/agent-chat.js.map +1 -0
- package/dist/lib/server/api/routes/document-versions.d.ts.map +1 -1
- package/dist/lib/server/api/routes/document-versions.js +16 -2
- package/dist/lib/server/api/routes/document-versions.js.map +1 -1
- package/dist/lib/server/api/routes/documents-by-id.d.ts.map +1 -1
- package/dist/lib/server/api/routes/documents-by-id.js +11 -1
- package/dist/lib/server/api/routes/documents-by-id.js.map +1 -1
- package/dist/lib/server/api/routes/documents-publish.d.ts.map +1 -1
- package/dist/lib/server/api/routes/documents-publish.js +36 -3
- package/dist/lib/server/api/routes/documents-publish.js.map +1 -1
- package/dist/lib/server/api/routes/jobs.d.ts.map +1 -1
- package/dist/lib/server/api/routes/jobs.js +4 -2
- package/dist/lib/server/api/routes/jobs.js.map +1 -1
- package/dist/lib/server/api/routes/resolve-created-by.d.ts +13 -0
- package/dist/lib/server/api/routes/resolve-created-by.d.ts.map +1 -0
- package/dist/lib/server/api/routes/resolve-created-by.js +31 -0
- package/dist/lib/server/api/routes/resolve-created-by.js.map +1 -0
- package/dist/lib/server/index.d.ts +1 -0
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +1 -0
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/services/references-service.d.ts +1 -1
- package/dist/lib/services/references-service.d.ts.map +1 -1
- package/dist/lib/services/version-service.d.ts +4 -4
- package/dist/lib/services/version-service.d.ts.map +1 -1
- package/dist/lib/services/version-service.js +15 -11
- package/dist/lib/services/version-service.js.map +1 -1
- package/dist/lib/type-gen.d.ts.map +1 -1
- package/dist/lib/type-gen.js +11 -0
- package/dist/lib/type-gen.js.map +1 -1
- package/dist/lib/types/agent-change-sets.d.ts +70 -0
- package/dist/lib/types/agent-change-sets.d.ts.map +1 -0
- package/dist/lib/types/agent-change-sets.js +7 -0
- package/dist/lib/types/agent-change-sets.js.map +1 -0
- package/dist/lib/types/agent-stream.d.ts +60 -0
- package/dist/lib/types/agent-stream.d.ts.map +1 -0
- package/dist/lib/types/agent-stream.js +12 -0
- package/dist/lib/types/agent-stream.js.map +1 -0
- package/dist/lib/types/agent-tools.d.ts +65 -0
- package/dist/lib/types/agent-tools.d.ts.map +1 -0
- package/dist/lib/types/agent-tools.js +2 -0
- package/dist/lib/types/agent-tools.js.map +1 -0
- package/dist/lib/types/config.d.ts +19 -0
- package/dist/lib/types/config.d.ts.map +1 -1
- package/dist/lib/types/document-workspace.d.ts +53 -0
- package/dist/lib/types/document-workspace.d.ts.map +1 -0
- package/dist/lib/types/document-workspace.js +15 -0
- package/dist/lib/types/document-workspace.js.map +1 -0
- package/dist/lib/types/document.d.ts +5 -0
- package/dist/lib/types/document.d.ts.map +1 -1
- package/dist/lib/types/index.d.ts +2 -0
- package/dist/lib/types/index.d.ts.map +1 -1
- package/dist/lib/types/index.js +2 -0
- package/dist/lib/types/index.js.map +1 -1
- package/dist/lib/types/schemas.js +1 -1
- package/dist/lib/types/schemas.js.map +1 -1
- package/dist/lib/utils/fetch-remote-file.d.ts +11 -0
- package/dist/lib/utils/fetch-remote-file.d.ts.map +1 -0
- package/dist/lib/utils/fetch-remote-file.js +169 -0
- package/dist/lib/utils/fetch-remote-file.js.map +1 -0
- package/dist/local-api/collection-api.d.ts +7 -2
- package/dist/local-api/collection-api.d.ts.map +1 -1
- package/dist/local-api/collection-api.js +17 -10
- package/dist/mcp/tools.d.ts +39 -4
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +546 -362
- package/dist/plugins/resolver.d.ts +8 -1
- package/dist/plugins/resolver.d.ts.map +1 -1
- package/dist/plugins/resolver.js +14 -1
- package/dist/plugins/types.d.ts +23 -2
- package/dist/plugins/types.d.ts.map +1 -1
- package/dist/preview/stega.d.ts +2 -2
- package/dist/preview/stega.d.ts.map +1 -1
- package/dist/preview/stega.js +52 -29
- package/dist/schema-utils/validator.d.ts.map +1 -1
- package/dist/schema-utils/validator.js +5 -2
- package/dist/server/api/index.d.ts.map +1 -1
- package/dist/server/api/index.js +8 -0
- package/dist/server/api/routes/agent-change-sets.d.ts +4 -0
- package/dist/server/api/routes/agent-change-sets.d.ts.map +1 -0
- package/dist/server/api/routes/agent-change-sets.js +131 -0
- package/dist/server/api/routes/agent-chat.d.ts +14 -0
- package/dist/server/api/routes/agent-chat.d.ts.map +1 -0
- package/dist/server/api/routes/agent-chat.js +223 -0
- package/dist/server/api/routes/document-versions.d.ts.map +1 -1
- package/dist/server/api/routes/document-versions.js +16 -2
- package/dist/server/api/routes/documents-by-id.d.ts.map +1 -1
- package/dist/server/api/routes/documents-by-id.js +11 -1
- package/dist/server/api/routes/documents-publish.d.ts.map +1 -1
- package/dist/server/api/routes/documents-publish.js +36 -3
- package/dist/server/api/routes/jobs.d.ts.map +1 -1
- package/dist/server/api/routes/jobs.js +4 -2
- package/dist/server/api/routes/resolve-created-by.d.ts +13 -0
- package/dist/server/api/routes/resolve-created-by.d.ts.map +1 -0
- package/dist/server/api/routes/resolve-created-by.js +30 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -0
- package/dist/services/references-service.d.ts +1 -1
- package/dist/services/references-service.d.ts.map +1 -1
- package/dist/services/version-service.d.ts +4 -4
- package/dist/services/version-service.d.ts.map +1 -1
- package/dist/services/version-service.js +15 -11
- package/dist/type-gen.d.ts.map +1 -1
- package/dist/type-gen.js +11 -0
- package/dist/types/agent-change-sets.d.ts +70 -0
- package/dist/types/agent-change-sets.d.ts.map +1 -0
- package/dist/types/agent-change-sets.js +6 -0
- package/dist/types/agent-stream.d.ts +60 -0
- package/dist/types/agent-stream.d.ts.map +1 -0
- package/dist/types/agent-stream.js +11 -0
- package/dist/types/agent-tools.d.ts +65 -0
- package/dist/types/agent-tools.d.ts.map +1 -0
- package/dist/types/agent-tools.js +1 -0
- package/dist/types/config.d.ts +19 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/document-workspace.d.ts +53 -0
- package/dist/types/document-workspace.d.ts.map +1 -0
- package/dist/types/document-workspace.js +14 -0
- package/dist/types/document.d.ts +5 -0
- package/dist/types/document.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/schemas.js +1 -1
- package/dist/utils/fetch-remote-file.d.ts +11 -0
- package/dist/utils/fetch-remote-file.d.ts.map +1 -0
- package/dist/utils/fetch-remote-file.js +168 -0
- package/package.json +5 -2
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { streamSSE } from 'hono/streaming';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { agentChatRequest } from '../../../api/schemas/agent-chat.js';
|
|
5
|
+
import { recordWorkspaceOperationRequest } from '../../../api/schemas/agent-operations.js';
|
|
6
|
+
import { authToContext } from '../../../local-api/auth-helpers.js';
|
|
7
|
+
import { resolveAgentTools } from '../../../mcp/tools.js';
|
|
8
|
+
import { runAgentTurn } from '../../../ai/run-agent-turn.js';
|
|
9
|
+
import { DEFAULT_AGENT_SYSTEM_PROMPT } from '../../../ai/default-system-prompt.js';
|
|
10
|
+
import { cmsLogger } from '../../../utils/logger.js';
|
|
11
|
+
const SUMMARY_MAX_LENGTH = 200;
|
|
12
|
+
// A mutating tool's `data` is `unknown` at this boundary (AgentToolResult.data) — every tool
|
|
13
|
+
// returns a different shape (list_collections -> {collections}, upload_asset -> {asset, ...},
|
|
14
|
+
// document tools -> {document, validation}), and this route dispatches by name against
|
|
15
|
+
// whatever's registered, including plugin-contributed tools it's never seen at compile time.
|
|
16
|
+
// There's no static type to give it, so the id is pulled out via a runtime-validated parse
|
|
17
|
+
// (the same idiom `run-agent-turn.ts` already uses for tool arguments) rather than an `as`
|
|
18
|
+
// cast asserting a shape without checking it.
|
|
19
|
+
const toolResultWithDocumentId = z.object({ document: z.object({ id: z.string() }) });
|
|
20
|
+
/** `create_document` has no `id` argument (nothing to reference before it exists) — its new
|
|
21
|
+
* id only shows up in the result. Falls back to that when the args don't have one, so a
|
|
22
|
+
* create is still attributable to a document instead of being silently dropped. */
|
|
23
|
+
function resolveDocumentId(args, data) {
|
|
24
|
+
if (typeof args.id === 'string')
|
|
25
|
+
return args.id;
|
|
26
|
+
const parsed = toolResultWithDocumentId.safeParse(data);
|
|
27
|
+
return parsed.success ? parsed.data.document.id : null;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Best-effort audit record for one mutating tool call — looks up the two most recent
|
|
31
|
+
* document-version rows to capture `versionBefore`/`versionAfter` (the version an undo would
|
|
32
|
+
* restore to, and the one this write produced), then records the operation. Never throws: a
|
|
33
|
+
* failure to record the audit trail must never break the actual tool call or the SSE stream.
|
|
34
|
+
* Exported for unit testing — not part of the route's own public surface.
|
|
35
|
+
*/
|
|
36
|
+
export async function recordMutatingOperation(aphexCMS, context, changeSetId, toolName, args, success, error, data) {
|
|
37
|
+
const collection = typeof args.collection === 'string' ? args.collection : null;
|
|
38
|
+
// `resolveDocumentId` checks `args.id` first, so a failed update_document/publish_document
|
|
39
|
+
// (which does have `args.id`) is still attributable — it only falls through to parsing
|
|
40
|
+
// `data` for the rare case for `create_document`, whose result won't parse on failure anyway.
|
|
41
|
+
const documentId = resolveDocumentId(args, data);
|
|
42
|
+
if (!collection || !documentId)
|
|
43
|
+
return;
|
|
44
|
+
try {
|
|
45
|
+
const { databaseAdapter, localAPI } = aphexCMS;
|
|
46
|
+
const { versions } = await localAPI.versionService.listVersions(databaseAdapter, context.organizationId, documentId, { limit: 2 });
|
|
47
|
+
const [versionAfter, versionBefore] = versions;
|
|
48
|
+
await databaseAdapter.recordOperation({
|
|
49
|
+
changeSetId,
|
|
50
|
+
organizationId: context.organizationId,
|
|
51
|
+
collection,
|
|
52
|
+
documentId,
|
|
53
|
+
toolName,
|
|
54
|
+
arguments: args,
|
|
55
|
+
success,
|
|
56
|
+
error,
|
|
57
|
+
versionBefore: versionBefore?.versionNumber ?? null,
|
|
58
|
+
versionAfter: versionAfter?.versionNumber ?? null
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
cmsLogger.error('[agent-chat] failed to record agent operation (continuing):', err);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export const agentChatRouter = new Hono();
|
|
66
|
+
/**
|
|
67
|
+
* POST /api/agent/chat — the in-admin agent's streaming chat endpoint (Milestone 2 item 5
|
|
68
|
+
* of references/content-copilot-phase-1-plan.md). Stateless per call: the browser sends
|
|
69
|
+
* the full running conversation each time (conversation persistence is a separate,
|
|
70
|
+
* not-yet-built piece — see the plan). Requires an authenticated session; individual tool
|
|
71
|
+
* calls are separately capability-gated by `resolveAgentTools`/each tool's own checks —
|
|
72
|
+
* this endpoint is a dumb transport over the same tool-execution service MCP uses, not an
|
|
73
|
+
* additional authorization layer.
|
|
74
|
+
*
|
|
75
|
+
* 404s (rather than 401) when no `aiProvider` is configured, so the route doesn't exist as
|
|
76
|
+
* a surface at all on an instance that hasn't opted in — same "don't advertise an unset
|
|
77
|
+
* feature" posture as `workers-run.ts`'s worker secret gate.
|
|
78
|
+
*
|
|
79
|
+
* Also records an audit/undo trail (`cms_agent_change_sets`/`cms_agent_operations`): a
|
|
80
|
+
* change-set row is created eagerly for every turn (so token usage is captured even for a
|
|
81
|
+
* pure Q&A turn with no mutations), and every mutating tool call gets an operation row. All
|
|
82
|
+
* of this recording is best-effort — see `recordMutatingOperation` above and the try/catch
|
|
83
|
+
* around change-set creation/completion below — a failure here must never break the chat.
|
|
84
|
+
*/
|
|
85
|
+
agentChatRouter.post('/chat', async (c) => {
|
|
86
|
+
const { aphexCMS, auth } = c.var;
|
|
87
|
+
const { aiProvider, agentModel } = aphexCMS.config;
|
|
88
|
+
const { databaseAdapter } = aphexCMS;
|
|
89
|
+
if (!aiProvider)
|
|
90
|
+
return c.json({ success: false, error: 'Not found' }, 404);
|
|
91
|
+
if (!auth)
|
|
92
|
+
return c.json({ success: false, error: 'Unauthorized' }, 401);
|
|
93
|
+
if (!agentModel) {
|
|
94
|
+
return c.json({ success: false, error: 'Server misconfigured: no agentModel configured for aiProvider' }, 501);
|
|
95
|
+
}
|
|
96
|
+
const body = await c.req.json().catch(() => null);
|
|
97
|
+
const parsed = agentChatRequest.safeParse(body);
|
|
98
|
+
if (!parsed.success) {
|
|
99
|
+
return c.json({ success: false, error: 'Invalid request', issues: parsed.error.issues }, 400);
|
|
100
|
+
}
|
|
101
|
+
const context = authToContext(auth);
|
|
102
|
+
const tools = resolveAgentTools({ aphexCMS, context }, { documentContext: parsed.data.documentContext });
|
|
103
|
+
const toolsByName = new Map(tools.map((t) => [t.definition.name, t]));
|
|
104
|
+
const model = parsed.data.model ?? agentModel;
|
|
105
|
+
const abortController = new AbortController();
|
|
106
|
+
// A turn paused for a workspace tool resumes as a brand-new HTTP request (see
|
|
107
|
+
// `types/agent-stream.ts`) — the client echoes back the change-set id from the leg that
|
|
108
|
+
// paused so every leg of one logical turn records against the same row, instead of a
|
|
109
|
+
// fresh `cms_agent_change_sets` row per leg.
|
|
110
|
+
let changeSetId = parsed.data.changeSetId ?? null;
|
|
111
|
+
if (!changeSetId) {
|
|
112
|
+
try {
|
|
113
|
+
const firstUserMessage = parsed.data.messages.find((m) => m.role === 'user')?.content ?? '';
|
|
114
|
+
const changeSet = await databaseAdapter.createChangeSet({
|
|
115
|
+
organizationId: context.organizationId,
|
|
116
|
+
createdBy: context.user?.id ?? null,
|
|
117
|
+
summary: firstUserMessage.slice(0, SUMMARY_MAX_LENGTH) || null,
|
|
118
|
+
provider: aiProvider.name,
|
|
119
|
+
model
|
|
120
|
+
});
|
|
121
|
+
changeSetId = changeSet.id;
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
cmsLogger.error('[agent-chat] failed to create change-set (continuing without one):', err);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return streamSSE(c, async (stream) => {
|
|
128
|
+
c.req.raw.signal.addEventListener('abort', () => abortController.abort());
|
|
129
|
+
let promptTokens = parsed.data.priorUsage?.promptTokens ?? 0;
|
|
130
|
+
let completionTokens = parsed.data.priorUsage?.completionTokens ?? 0;
|
|
131
|
+
let turnFailed = false;
|
|
132
|
+
let turnPaused = false;
|
|
133
|
+
// toolCall args aren't repeated on the matching toolResult event, so stash them here
|
|
134
|
+
// to correlate the two when it's time to record a mutating operation.
|
|
135
|
+
const argsByToolCallId = new Map();
|
|
136
|
+
try {
|
|
137
|
+
for await (const event of runAgentTurn({
|
|
138
|
+
aiProvider,
|
|
139
|
+
model,
|
|
140
|
+
messages: parsed.data.messages,
|
|
141
|
+
tools,
|
|
142
|
+
toolContext: { aphexCMS, context },
|
|
143
|
+
systemPrompt: aphexCMS.config.agentSystemPrompt ?? DEFAULT_AGENT_SYSTEM_PROMPT,
|
|
144
|
+
signal: abortController.signal
|
|
145
|
+
})) {
|
|
146
|
+
if (event.type === 'usage') {
|
|
147
|
+
promptTokens += event.promptTokens;
|
|
148
|
+
completionTokens += event.completionTokens;
|
|
149
|
+
}
|
|
150
|
+
else if (event.type === 'toolCall') {
|
|
151
|
+
argsByToolCallId.set(event.toolCallId, event.arguments);
|
|
152
|
+
}
|
|
153
|
+
else if (event.type === 'toolResult' && changeSetId) {
|
|
154
|
+
const tool = toolsByName.get(event.name);
|
|
155
|
+
const args = argsByToolCallId.get(event.toolCallId);
|
|
156
|
+
if (tool?.definition.mutates && args) {
|
|
157
|
+
await recordMutatingOperation(aphexCMS, context, changeSetId, event.name, args, event.success, event.error, event.data);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else if (event.type === 'error') {
|
|
161
|
+
turnFailed = true;
|
|
162
|
+
}
|
|
163
|
+
if (event.type === 'done') {
|
|
164
|
+
event.changeSetId = changeSetId;
|
|
165
|
+
if (event.finishReason === 'awaiting_workspace_tool')
|
|
166
|
+
turnPaused = true;
|
|
167
|
+
}
|
|
168
|
+
await stream.writeSSE({ data: JSON.stringify(event) });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
turnFailed = true;
|
|
173
|
+
cmsLogger.error('[agent-chat] turn failed:', err);
|
|
174
|
+
await stream.writeSSE({
|
|
175
|
+
data: JSON.stringify({
|
|
176
|
+
type: 'error',
|
|
177
|
+
message: err instanceof Error ? err.message : 'Unknown error'
|
|
178
|
+
})
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
finally {
|
|
182
|
+
// A paused leg isn't the end of the turn — the client resumes with the same
|
|
183
|
+
// changeSetId, and only the truly-terminal leg should mark the change-set complete.
|
|
184
|
+
if (changeSetId && !turnPaused) {
|
|
185
|
+
try {
|
|
186
|
+
await databaseAdapter.completeChangeSet(context.organizationId, changeSetId, {
|
|
187
|
+
status: turnFailed ? 'failed' : 'completed',
|
|
188
|
+
promptTokens,
|
|
189
|
+
completionTokens
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
cmsLogger.error('[agent-chat] failed to complete change-set (continuing):', err);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
/**
|
|
200
|
+
* POST /api/agent/operations — records an audit/undo row for a tool the *client* executed
|
|
201
|
+
* against a live `DocumentWorkspace` (the `content_patch_fields`/`content_save_draft` bridge
|
|
202
|
+
* tools — see `ai/content-workspace-tools.ts`). `agent-chat.ts`'s own `recordMutatingOperation`
|
|
203
|
+
* call above only sees tool results resolved server-side inside `runAgentTurn`'s loop, so a
|
|
204
|
+
* workspace tool's result — resolved client-side after a paused turn — needs this separate
|
|
205
|
+
* path to reach the same `cms_agent_change_sets`/`cms_agent_operations` tables. Same
|
|
206
|
+
* session-authenticated posture as `/chat`; not itself a mutation of document data (the
|
|
207
|
+
* client already made that call directly via the normal document API), purely bookkeeping.
|
|
208
|
+
*/
|
|
209
|
+
agentChatRouter.post('/operations', async (c) => {
|
|
210
|
+
const { aphexCMS, auth } = c.var;
|
|
211
|
+
if (!aphexCMS.config.aiProvider)
|
|
212
|
+
return c.json({ success: false, error: 'Not found' }, 404);
|
|
213
|
+
if (!auth)
|
|
214
|
+
return c.json({ success: false, error: 'Unauthorized' }, 401);
|
|
215
|
+
const body = await c.req.json().catch(() => null);
|
|
216
|
+
const parsed = recordWorkspaceOperationRequest.safeParse(body);
|
|
217
|
+
if (!parsed.success) {
|
|
218
|
+
return c.json({ success: false, error: 'Invalid request', issues: parsed.error.issues }, 400);
|
|
219
|
+
}
|
|
220
|
+
const context = authToContext(auth);
|
|
221
|
+
await recordMutatingOperation(aphexCMS, context, parsed.data.changeSetId, parsed.data.toolName, { collection: parsed.data.collection, id: parsed.data.id, ...parsed.data.arguments }, parsed.data.success, parsed.data.error, parsed.data.data);
|
|
222
|
+
return c.json({ success: true });
|
|
223
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-versions.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/api/routes/document-versions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"document-versions.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/api/routes/document-versions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAO5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzC,eAAO,MAAM,sBAAsB,EAAE,IAAI,CAAC,QAAQ,CAsL/C,CAAC"}
|
|
@@ -3,7 +3,8 @@ import { zValidator } from '@hono/zod-validator';
|
|
|
3
3
|
import { authToContext } from '../../../local-api/auth-helpers.js';
|
|
4
4
|
import { cmsLogger } from '../../../utils/logger.js';
|
|
5
5
|
import { hasCapability } from '../../../types/capabilities.js';
|
|
6
|
-
import {
|
|
6
|
+
import { RevisionConflictError } from '../../../db/interfaces/index.js';
|
|
7
|
+
import { listVersionsQuery, restoreVersionRequest } from '../../../api/schemas/documents.js';
|
|
7
8
|
export const documentVersionsRouter = new Hono()
|
|
8
9
|
.get('/:id/versions', zValidator('query', listVersionsQuery, (result, c) => {
|
|
9
10
|
if (!result.success) {
|
|
@@ -103,7 +104,12 @@ export const documentVersionsRouter = new Hono()
|
|
|
103
104
|
if (isNaN(versionNumber)) {
|
|
104
105
|
return c.json({ success: false, error: 'Version must be a number' }, 400);
|
|
105
106
|
}
|
|
106
|
-
const
|
|
107
|
+
const body = await c.req.json().catch(() => ({}));
|
|
108
|
+
const parsed = restoreVersionRequest.safeParse(body);
|
|
109
|
+
if (!parsed.success) {
|
|
110
|
+
return c.json({ success: false, error: 'Invalid request', issues: parsed.error.issues }, 400);
|
|
111
|
+
}
|
|
112
|
+
const document = await localAPI.versionService.restoreVersion(databaseAdapter, context.organizationId, id, versionNumber, context.user?.id, parsed.data.expectedRevision);
|
|
107
113
|
if (!document) {
|
|
108
114
|
return c.json({ success: false, error: 'Version not found or restore failed' }, 404);
|
|
109
115
|
}
|
|
@@ -115,6 +121,14 @@ export const documentVersionsRouter = new Hono()
|
|
|
115
121
|
}
|
|
116
122
|
catch (error) {
|
|
117
123
|
cmsLogger.error('Failed to restore document version:', error);
|
|
124
|
+
if (error instanceof RevisionConflictError) {
|
|
125
|
+
return c.json({
|
|
126
|
+
success: false,
|
|
127
|
+
error: 'Conflict',
|
|
128
|
+
message: error.message,
|
|
129
|
+
currentRevision: error.currentRevision
|
|
130
|
+
}, 409);
|
|
131
|
+
}
|
|
118
132
|
return c.json({ success: false, error: 'Failed to restore version' }, 500);
|
|
119
133
|
}
|
|
120
134
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"documents-by-id.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/api/routes/documents-by-id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"documents-by-id.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/api/routes/documents-by-id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAQ5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzC,eAAO,MAAM,mBAAmB,EAAE,IAAI,CAAC,QAAQ,CAiO5C,CAAC"}
|
|
@@ -3,6 +3,7 @@ import { zValidator } from '@hono/zod-validator';
|
|
|
3
3
|
import { authToContext } from '../../../local-api/auth-helpers.js';
|
|
4
4
|
import { PermissionError } from '../../../local-api/permissions.js';
|
|
5
5
|
import { SingletonOperationError } from '../../../local-api/collection-api.js';
|
|
6
|
+
import { RevisionConflictError } from '../../../db/interfaces/index.js';
|
|
6
7
|
import { cmsLogger } from '../../../utils/logger.js';
|
|
7
8
|
import { updateDocumentRequest } from '../../../api/schemas/documents.js';
|
|
8
9
|
export const documentsByIdRouter = new Hono()
|
|
@@ -82,7 +83,8 @@ export const documentsByIdRouter = new Hono()
|
|
|
82
83
|
}, 400);
|
|
83
84
|
}
|
|
84
85
|
const result = await collection.update(context, id, documentData, {
|
|
85
|
-
publish: shouldPublish
|
|
86
|
+
publish: shouldPublish,
|
|
87
|
+
expectedRevision: parsed.expectedRevision
|
|
86
88
|
});
|
|
87
89
|
if (!result) {
|
|
88
90
|
return c.json({ success: false, error: 'Document not found' }, 404);
|
|
@@ -98,6 +100,14 @@ export const documentsByIdRouter = new Hono()
|
|
|
98
100
|
if (error instanceof PermissionError) {
|
|
99
101
|
return c.json({ success: false, error: 'Forbidden', message: error.message }, 403);
|
|
100
102
|
}
|
|
103
|
+
if (error instanceof RevisionConflictError) {
|
|
104
|
+
return c.json({
|
|
105
|
+
success: false,
|
|
106
|
+
error: 'Conflict',
|
|
107
|
+
message: error.message,
|
|
108
|
+
currentRevision: error.currentRevision
|
|
109
|
+
}, 409);
|
|
110
|
+
}
|
|
101
111
|
if (error instanceof Error && error.message.includes('validation errors')) {
|
|
102
112
|
return c.json({ success: false, error: 'Validation failed', message: error.message }, 400);
|
|
103
113
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"documents-publish.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/api/routes/documents-publish.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"documents-publish.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/api/routes/documents-publish.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAU5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzC,eAAO,MAAM,sBAAsB,EAAE,IAAI,CAAC,QAAQ,CAkU/C,CAAC"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
2
|
import { authToContext } from '../../../local-api/auth-helpers.js';
|
|
3
3
|
import { PermissionError } from '../../../local-api/permissions.js';
|
|
4
|
+
import { RevisionConflictError } from '../../../db/interfaces/index.js';
|
|
4
5
|
import { cmsLogger } from '../../../utils/logger.js';
|
|
5
|
-
import { scheduleDocumentRequest } from '../../../api/schemas/documents.js';
|
|
6
|
+
import { scheduleDocumentRequest, publishDocumentRequest, unpublishDocumentRequest } from '../../../api/schemas/documents.js';
|
|
6
7
|
export const documentsPublishRouter = new Hono()
|
|
7
8
|
.post('/:id/schedule', async (c) => {
|
|
8
9
|
try {
|
|
@@ -141,7 +142,16 @@ export const documentsPublishRouter = new Hono()
|
|
|
141
142
|
message: `Collection '${found.type}' not found`
|
|
142
143
|
}, 400);
|
|
143
144
|
}
|
|
144
|
-
|
|
145
|
+
// Body is optional — a bare POST with no body is still valid, only
|
|
146
|
+
// carries the CAS guard when the caller has one to send.
|
|
147
|
+
const body = await c.req.json().catch(() => ({}));
|
|
148
|
+
const parsed = publishDocumentRequest.safeParse(body);
|
|
149
|
+
if (!parsed.success) {
|
|
150
|
+
return c.json({ success: false, error: 'Invalid request', issues: parsed.error.issues }, 400);
|
|
151
|
+
}
|
|
152
|
+
const publishedDocument = await collection.publish(context, id, {
|
|
153
|
+
expectedRevision: parsed.data.expectedRevision
|
|
154
|
+
});
|
|
145
155
|
if (!publishedDocument) {
|
|
146
156
|
return c.json({
|
|
147
157
|
success: false,
|
|
@@ -160,6 +170,14 @@ export const documentsPublishRouter = new Hono()
|
|
|
160
170
|
if (error instanceof PermissionError) {
|
|
161
171
|
return c.json({ success: false, error: 'Forbidden', message: error.message }, 403);
|
|
162
172
|
}
|
|
173
|
+
if (error instanceof RevisionConflictError) {
|
|
174
|
+
return c.json({
|
|
175
|
+
success: false,
|
|
176
|
+
error: 'Conflict',
|
|
177
|
+
message: error.message,
|
|
178
|
+
currentRevision: error.currentRevision
|
|
179
|
+
}, 409);
|
|
180
|
+
}
|
|
163
181
|
if (error instanceof Error && error.message.includes('validation errors')) {
|
|
164
182
|
return c.json({
|
|
165
183
|
success: false,
|
|
@@ -202,7 +220,14 @@ export const documentsPublishRouter = new Hono()
|
|
|
202
220
|
message: `Collection '${found.type}' not found`
|
|
203
221
|
}, 400);
|
|
204
222
|
}
|
|
205
|
-
const
|
|
223
|
+
const body = await c.req.json().catch(() => ({}));
|
|
224
|
+
const parsed = unpublishDocumentRequest.safeParse(body);
|
|
225
|
+
if (!parsed.success) {
|
|
226
|
+
return c.json({ success: false, error: 'Invalid request', issues: parsed.error.issues }, 400);
|
|
227
|
+
}
|
|
228
|
+
const unpublishedDocument = await collection.unpublish(context, id, {
|
|
229
|
+
expectedRevision: parsed.data.expectedRevision
|
|
230
|
+
});
|
|
206
231
|
if (!unpublishedDocument) {
|
|
207
232
|
return c.json({
|
|
208
233
|
success: false,
|
|
@@ -221,6 +246,14 @@ export const documentsPublishRouter = new Hono()
|
|
|
221
246
|
if (error instanceof PermissionError) {
|
|
222
247
|
return c.json({ success: false, error: 'Forbidden', message: error.message }, 403);
|
|
223
248
|
}
|
|
249
|
+
if (error instanceof RevisionConflictError) {
|
|
250
|
+
return c.json({
|
|
251
|
+
success: false,
|
|
252
|
+
error: 'Conflict',
|
|
253
|
+
message: error.message,
|
|
254
|
+
currentRevision: error.currentRevision
|
|
255
|
+
}, 409);
|
|
256
|
+
}
|
|
224
257
|
return c.json({
|
|
225
258
|
success: false,
|
|
226
259
|
error: 'Failed to unpublish document',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/api/routes/jobs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAK5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/api/routes/jobs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAK5B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AA6CzC,eAAO,MAAM,UAAU,EAAE,IAAI,CAAC,QAAQ,CAkDnC,CAAC"}
|
|
@@ -3,6 +3,7 @@ import { z } from 'zod';
|
|
|
3
3
|
import { authToContext } from '../../../local-api/auth-helpers.js';
|
|
4
4
|
import { hasCapability } from '../../../types/capabilities.js';
|
|
5
5
|
import { cmsLogger } from '../../../utils/logger.js';
|
|
6
|
+
import { withCreatedByNames } from './resolve-created-by.js';
|
|
6
7
|
// Read-only history / observability for the durable spine. Gated on `document.read`:
|
|
7
8
|
// the operational log of publish work is visible to anyone who can read content.
|
|
8
9
|
// Mirrors JobStatus (types/events) as a zod enum — the API contract's source of truth.
|
|
@@ -76,14 +77,15 @@ export const jobsRouter = new Hono()
|
|
|
76
77
|
return c.json({ success: false, error: 'Invalid query', issues: parsed.error.issues }, 400);
|
|
77
78
|
}
|
|
78
79
|
const { type, limit, offset } = parsed.data;
|
|
79
|
-
const { databaseAdapter } = c.var.aphexCMS;
|
|
80
|
+
const { databaseAdapter, auth } = c.var.aphexCMS;
|
|
80
81
|
const page = await databaseAdapter.listEvents({
|
|
81
82
|
organizationId: gate.organizationId,
|
|
82
83
|
type,
|
|
83
84
|
limit,
|
|
84
85
|
offset
|
|
85
86
|
});
|
|
86
|
-
|
|
87
|
+
const items = await withCreatedByNames(page.items, auth);
|
|
88
|
+
return c.json({ success: true, data: items, pagination: toPagination(page) });
|
|
87
89
|
}
|
|
88
90
|
catch (error) {
|
|
89
91
|
cmsLogger.error('Failed to list events:', error);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AuthProvider } from '../../../auth/provider.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves `createdBy` user ids on a list of rows to a display name (or `'API Key'` for a
|
|
4
|
+
* `apikey:<id>` synthetic id — see `authToContext`'s API-key branch). Shared by every
|
|
5
|
+
* read-only history view (`GET /api/events`, `GET /api/agent/change-sets`, ...) — an audit
|
|
6
|
+
* trail whose whole point is accountability needs to show *who*, not a raw id.
|
|
7
|
+
*/
|
|
8
|
+
export declare function withCreatedByNames<T extends {
|
|
9
|
+
createdBy: string | null;
|
|
10
|
+
}>(rows: T[], auth: AuthProvider | undefined): Promise<Array<T & {
|
|
11
|
+
createdByName: string | null;
|
|
12
|
+
}>>;
|
|
13
|
+
//# sourceMappingURL=resolve-created-by.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-created-by.d.ts","sourceRoot":"","sources":["../../../../src/lib/server/api/routes/resolve-created-by.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,SAAS;IAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAC9E,IAAI,EAAE,CAAC,EAAE,EACT,IAAI,EAAE,YAAY,GAAG,SAAS,GAC5B,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG;IAAE,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAAC,CAyBtD"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves `createdBy` user ids on a list of rows to a display name (or `'API Key'` for a
|
|
3
|
+
* `apikey:<id>` synthetic id — see `authToContext`'s API-key branch). Shared by every
|
|
4
|
+
* read-only history view (`GET /api/events`, `GET /api/agent/change-sets`, ...) — an audit
|
|
5
|
+
* trail whose whole point is accountability needs to show *who*, not a raw id.
|
|
6
|
+
*/
|
|
7
|
+
export async function withCreatedByNames(rows, auth) {
|
|
8
|
+
const userIds = [...new Set(rows.map((r) => r.createdBy).filter((id) => !!id))];
|
|
9
|
+
const userMap = new Map();
|
|
10
|
+
if (userIds.length > 0 && auth) {
|
|
11
|
+
await Promise.all(userIds.map(async (userId) => {
|
|
12
|
+
if (userId.startsWith('apikey:')) {
|
|
13
|
+
userMap.set(userId, 'API Key');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const user = await auth.getUserById(userId);
|
|
18
|
+
if (user)
|
|
19
|
+
userMap.set(userId, user.name || user.email);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// User not found — skip
|
|
23
|
+
}
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
return rows.map((r) => ({
|
|
27
|
+
...r,
|
|
28
|
+
createdByName: r.createdBy ? (userMap.get(r.createdBy) ?? null) : null
|
|
29
|
+
}));
|
|
30
|
+
}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from '../types/index.js';
|
|
|
2
2
|
export * from '../auth/provider.js';
|
|
3
3
|
export * from '../cache/index.js';
|
|
4
4
|
export * from '../email/index.js';
|
|
5
|
+
export * from '../ai/index.js';
|
|
5
6
|
export { AuthError, type AuthErrorCode } from '../auth/auth-errors.js';
|
|
6
7
|
export { createCMSConfig } from '../config.js';
|
|
7
8
|
export { cmsLogger, setLogger, type Logger } from '../utils/logger.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/server/index.ts"],"names":[],"mappings":"AAIA,cAAc,gBAAgB,CAAC;AAE/B,cAAc,kBAAkB,CAAC;AAEjC,cAAc,gBAAgB,CAAC;AAE/B,cAAc,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/server/index.ts"],"names":[],"mappings":"AAIA,cAAc,gBAAgB,CAAC;AAE/B,cAAc,kBAAkB,CAAC;AAEjC,cAAc,gBAAgB,CAAC;AAE/B,cAAc,gBAAgB,CAAC;AAE/B,cAAc,aAAa,CAAC;AAG5B,OAAO,EAAE,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpE,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAG5C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAGpE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,OAAO,EAAE,aAAa,EAAE,0BAA0B,EAAE,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAGxF,cAAc,wBAAwB,CAAC;AAGvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAG7C,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAKzD,OAAO,EAAE,GAAG,IAAI,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI5D,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,aAAa,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAG/F,cAAc,eAAe,CAAC;AAG9B,cAAc,uBAAuB,CAAC;AAGtC,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAGhE,OAAO,EAAE,qBAAqB,EAAE,KAAK,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAGxF,OAAO,EACN,oBAAoB,EACpB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACN,QAAQ,EACR,cAAc,EACd,WAAW,EACX,aAAa,EACb,uBAAuB,EACvB,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,WAAW,EACX,aAAa,EACb,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EACN,iBAAiB,EACjB,KAAK,OAAO,EACZ,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,MAAM,cAAc,CAAC"}
|
package/dist/server/index.js
CHANGED
|
@@ -5,6 +5,7 @@ export * from '../types/index.js';
|
|
|
5
5
|
export * from '../auth/provider.js';
|
|
6
6
|
export * from '../cache/index.js';
|
|
7
7
|
export * from '../email/index.js';
|
|
8
|
+
export * from '../ai/index.js';
|
|
8
9
|
// Authentication errors
|
|
9
10
|
export { AuthError } from '../auth/auth-errors.js';
|
|
10
11
|
// Configuration system
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"references-service.d.ts","sourceRoot":"","sources":["../../src/lib/services/references-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"references-service.d.ts","sourceRoot":"","sources":["../../src/lib/services/references-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD;;;;;;;;;GASG;AACH,qBAAa,iBAAiB;IACjB,OAAO,CAAC,eAAe;gBAAf,eAAe,EAAE,eAAe;IAEpD;;;OAGG;IACG,iBAAiB,CACtB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,UAAU,GAAG,IAAI,EACzB,QAAQ,EAAE,UAAU,EAAE,GACpB,OAAO,CAAC,IAAI,CAAC;IAShB;;;;;;;OAOG;IACG,eAAe,CACpB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,UAAU,EAAE,EACrB,gBAAgB,EAAE,MAAM,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC,GACjF,OAAO,CAAC,IAAI,CAAC;CAwBhB"}
|
|
@@ -27,7 +27,7 @@ export declare class VersionService {
|
|
|
27
27
|
* Publish + snapshot on an already-transactional adapter. Caller owns the tx
|
|
28
28
|
* and retention. Returns the published document (or null if publish was a no-op).
|
|
29
29
|
*/
|
|
30
|
-
publishTx(tx: DatabaseAdapter, organizationId: string, documentId: string): Promise<Document | null>;
|
|
30
|
+
publishTx(tx: DatabaseAdapter, organizationId: string, documentId: string, expectedRevision?: number): Promise<Document | null>;
|
|
31
31
|
/**
|
|
32
32
|
* Public retention trigger for callers that manage their own transaction and
|
|
33
33
|
* therefore can't rely on `saveWithVersion`/`publishWithVersion` to run it.
|
|
@@ -36,15 +36,15 @@ export declare class VersionService {
|
|
|
36
36
|
/**
|
|
37
37
|
* Save draft and create version atomically using adapter transaction.
|
|
38
38
|
*/
|
|
39
|
-
saveWithVersion(db: DatabaseAdapter, organizationId: string, documentId: string, data: any, userId?: string): Promise<Document | null>;
|
|
39
|
+
saveWithVersion(db: DatabaseAdapter, organizationId: string, documentId: string, data: any, userId?: string, expectedRevision?: number): Promise<Document | null>;
|
|
40
40
|
/**
|
|
41
41
|
* Publish and create version.
|
|
42
42
|
*/
|
|
43
|
-
publishWithVersion(db: DatabaseAdapter, organizationId: string, documentId: string): Promise<Document | null>;
|
|
43
|
+
publishWithVersion(db: DatabaseAdapter, organizationId: string, documentId: string, expectedRevision?: number): Promise<Document | null>;
|
|
44
44
|
/**
|
|
45
45
|
* Restore a version to draft. Creates a 'draft' version entry.
|
|
46
46
|
*/
|
|
47
|
-
restoreVersion(db: DatabaseAdapter, organizationId: string, documentId: string, versionNumber: number, userId?: string): Promise<Document | null>;
|
|
47
|
+
restoreVersion(db: DatabaseAdapter, organizationId: string, documentId: string, versionNumber: number, userId?: string, expectedRevision?: number): Promise<Document | null>;
|
|
48
48
|
listVersions(db: DatabaseAdapter, organizationId: string, documentId: string, options?: {
|
|
49
49
|
limit?: number;
|
|
50
50
|
offset?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version-service.d.ts","sourceRoot":"","sources":["../../src/lib/services/version-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAG7E;;;;;;GAMG;AACH,qBAAa,cAAc;IAC1B,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE;IAI9C;;OAEG;IACG,aAAa,CAClB,EAAE,EAAE,eAAe,EACnB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,OAAO,GAAG,SAAS,EAC9B,IAAI,EAAE,GAAG,EACT,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,GACpB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAelC;;;;OAIG;IACG,UAAU,CACf,EAAE,EAAE,eAAe,EACnB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,OAAO,GAAG,SAAS,EAC9B,IAAI,EAAE,GAAG,EACT,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,GACpB,OAAO,CAAC,IAAI,CAAC;IAWhB;;;OAGG;IACG,SAAS,CACd,EAAE,EAAE,eAAe,EACnB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"version-service.d.ts","sourceRoot":"","sources":["../../src/lib/services/version-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAG7E;;;;;;GAMG;AACH,qBAAa,cAAc;IAC1B,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE;IAI9C;;OAEG;IACG,aAAa,CAClB,EAAE,EAAE,eAAe,EACnB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,OAAO,GAAG,SAAS,EAC9B,IAAI,EAAE,GAAG,EACT,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,GACpB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAelC;;;;OAIG;IACG,UAAU,CACf,EAAE,EAAE,eAAe,EACnB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,OAAO,GAAG,SAAS,EAC9B,IAAI,EAAE,GAAG,EACT,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,GACpB,OAAO,CAAC,IAAI,CAAC;IAWhB;;;OAGG;IACG,SAAS,CACd,EAAE,EAAE,eAAe,EACnB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,gBAAgB,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAoB3B;;;OAGG;IACG,mBAAmB,CACxB,EAAE,EAAE,eAAe,EACnB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IAIhB;;OAEG;IACG,eAAe,CACpB,EAAE,EAAE,eAAe,EACnB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,GAAG,EACT,MAAM,CAAC,EAAE,MAAM,EACf,gBAAgB,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAwB3B;;OAEG;IACG,kBAAkB,CACvB,EAAE,EAAE,eAAe,EACnB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,gBAAgB,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAiB3B;;OAEG;IACG,cAAc,CACnB,EAAE,EAAE,eAAe,EACnB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,MAAM,CAAC,EAAE,MAAM,EACf,gBAAgB,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IA6BrB,YAAY,CACjB,EAAE,EAAE,eAAe,EACnB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3C,OAAO,CAAC,mBAAmB,CAAC;IAKzB,UAAU,CACf,EAAE,EAAE,eAAe,EACnB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,GACnB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;YAKpB,gBAAgB;CAuB9B"}
|
|
@@ -47,8 +47,8 @@ export class VersionService {
|
|
|
47
47
|
* Publish + snapshot on an already-transactional adapter. Caller owns the tx
|
|
48
48
|
* and retention. Returns the published document (or null if publish was a no-op).
|
|
49
49
|
*/
|
|
50
|
-
async publishTx(tx, organizationId, documentId) {
|
|
51
|
-
const result = await tx.publishDoc(organizationId, documentId);
|
|
50
|
+
async publishTx(tx, organizationId, documentId, expectedRevision) {
|
|
51
|
+
const result = await tx.publishDoc(organizationId, documentId, expectedRevision);
|
|
52
52
|
if (result) {
|
|
53
53
|
await this.snapshotTx(tx, organizationId, documentId, 'publish', result.publishedData, result.updatedBy);
|
|
54
54
|
// Transactional outbox: record the durable fact in the same tx as the publish, so
|
|
@@ -69,13 +69,15 @@ export class VersionService {
|
|
|
69
69
|
/**
|
|
70
70
|
* Save draft and create version atomically using adapter transaction.
|
|
71
71
|
*/
|
|
72
|
-
async saveWithVersion(db, organizationId, documentId, data, userId) {
|
|
72
|
+
async saveWithVersion(db, organizationId, documentId, data, userId, expectedRevision) {
|
|
73
73
|
// No versioning support: a single write, atomic on its own.
|
|
74
74
|
if (!db.createDocumentVersion) {
|
|
75
|
-
return db.updateDocDraft(organizationId, documentId, data, userId);
|
|
75
|
+
return db.updateDocDraft(organizationId, documentId, data, userId, expectedRevision);
|
|
76
76
|
}
|
|
77
|
+
// RevisionConflictError propagates out of the transaction un-swallowed —
|
|
78
|
+
// callers must surface it distinctly from a validation error.
|
|
77
79
|
const updated = await db.withTransaction(async (txAdapter) => {
|
|
78
|
-
const result = await txAdapter.updateDocDraft(organizationId, documentId, data, userId);
|
|
80
|
+
const result = await txAdapter.updateDocDraft(organizationId, documentId, data, userId, expectedRevision);
|
|
79
81
|
if (result)
|
|
80
82
|
await this.snapshotTx(txAdapter, organizationId, documentId, 'draft', data, userId);
|
|
81
83
|
return result;
|
|
@@ -87,15 +89,15 @@ export class VersionService {
|
|
|
87
89
|
/**
|
|
88
90
|
* Publish and create version.
|
|
89
91
|
*/
|
|
90
|
-
async publishWithVersion(db, organizationId, documentId) {
|
|
92
|
+
async publishWithVersion(db, organizationId, documentId, expectedRevision) {
|
|
91
93
|
// Publish + version snapshot must commit together: a crash between them
|
|
92
94
|
// would leave a published document with no 'publish' version row. Mirror
|
|
93
95
|
// saveWithVersion / restoreVersion and run both writes in one transaction.
|
|
94
96
|
// No versioning support: a single write, atomic on its own.
|
|
95
97
|
if (!db.createDocumentVersion) {
|
|
96
|
-
return db.publishDoc(organizationId, documentId);
|
|
98
|
+
return db.publishDoc(organizationId, documentId, expectedRevision);
|
|
97
99
|
}
|
|
98
|
-
const published = await db.withTransaction((txAdapter) => this.publishTx(txAdapter, organizationId, documentId));
|
|
100
|
+
const published = await db.withTransaction((txAdapter) => this.publishTx(txAdapter, organizationId, documentId, expectedRevision));
|
|
99
101
|
if (published)
|
|
100
102
|
await this.enforceRetention(db, documentId, organizationId);
|
|
101
103
|
return published;
|
|
@@ -103,18 +105,20 @@ export class VersionService {
|
|
|
103
105
|
/**
|
|
104
106
|
* Restore a version to draft. Creates a 'draft' version entry.
|
|
105
107
|
*/
|
|
106
|
-
async restoreVersion(db, organizationId, documentId, versionNumber, userId) {
|
|
108
|
+
async restoreVersion(db, organizationId, documentId, versionNumber, userId, expectedRevision) {
|
|
107
109
|
if (!db.getDocumentVersion)
|
|
108
110
|
return null;
|
|
109
111
|
const version = await db.getDocumentVersion(organizationId, documentId, versionNumber);
|
|
110
112
|
if (!version)
|
|
111
113
|
return null;
|
|
114
|
+
// A restore is itself a draft write — just as capable of clobbering a
|
|
115
|
+
// concurrent edit as a normal save, so it goes through the same CAS guard.
|
|
112
116
|
// No versioning support: a single write, atomic on its own.
|
|
113
117
|
if (!db.createDocumentVersion) {
|
|
114
|
-
return db.updateDocDraft(organizationId, documentId, version.data, userId);
|
|
118
|
+
return db.updateDocDraft(organizationId, documentId, version.data, userId, expectedRevision);
|
|
115
119
|
}
|
|
116
120
|
const restored = await db.withTransaction(async (txAdapter) => {
|
|
117
|
-
const result = await txAdapter.updateDocDraft(organizationId, documentId, version.data, userId);
|
|
121
|
+
const result = await txAdapter.updateDocDraft(organizationId, documentId, version.data, userId, expectedRevision);
|
|
118
122
|
if (result)
|
|
119
123
|
await this.snapshotTx(txAdapter, organizationId, documentId, 'draft', version.data, userId);
|
|
120
124
|
return result;
|
package/dist/type-gen.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"type-gen.d.ts","sourceRoot":"","sources":["../src/lib/type-gen.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAoJzD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,CAG3E;AA6VD;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,CA8I3D;
|
|
1
|
+
{"version":3,"file":"type-gen.d.ts","sourceRoot":"","sources":["../src/lib/type-gen.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAoJzD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,CAG3E;AA6VD;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,CA8I3D;AA2GD,wBAAsB,uBAAuB,CAC5C,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,WAAW,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAoDf"}
|