@mvriu5/payload-ai 1.6.3 → 1.6.4
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/components/action-toast/ActionToast.d.ts +1 -14
- package/dist/components/action-toast/ActionToast.js +1 -1
- package/dist/components/ai-input/AIInput.js +1 -1
- package/dist/components/audit-log-list/AuditLogList.d.ts +2 -3
- package/dist/components/diff-dialog/DiffDialog.d.ts +2 -6
- package/dist/components/diff-dialog/DiffDialog.js +1 -1
- package/dist/components/hooks/useAIChatStream.d.ts +1 -1
- package/dist/components/hooks/useAISettings.d.ts +1 -1
- package/dist/components/hooks/useAISettings.js +1 -1
- package/dist/components/hooks/useDocumentMentionSuggestions.js +1 -1
- package/dist/components/hooks/useMentions.js +1 -1
- package/dist/components/hooks/usePluginConfig.d.ts +6 -6
- package/dist/components/hooks/usePluginConfig.js +1 -1
- package/dist/{payload → features}/collectionPermissions.js +1 -1
- package/dist/{ai → features}/promptCaching.d.ts +2 -2
- package/dist/features/proposals/containerNormalization.d.ts +8 -0
- package/dist/features/proposals/containerNormalization.js +112 -0
- package/dist/{payload/proposalData.d.ts → features/proposals/data.d.ts} +5 -10
- package/dist/features/proposals/data.js +113 -0
- package/dist/features/proposals/fieldNormalization.d.ts +2 -0
- package/dist/features/proposals/fieldNormalization.js +69 -0
- package/dist/features/proposals/normalizationTypes.d.ts +31 -0
- package/dist/features/proposals/normalizationTypes.js +1 -0
- package/dist/features/proposals/optionInference.d.ts +13 -0
- package/dist/features/proposals/optionInference.js +46 -0
- package/dist/features/proposals/recordNormalization.d.ts +6 -0
- package/dist/features/proposals/recordNormalization.js +78 -0
- package/dist/features/proposals/relationshipNormalization.d.ts +8 -0
- package/dist/features/proposals/relationshipNormalization.js +71 -0
- package/dist/{ai/proposalRepair.d.ts → features/proposals/repair.d.ts} +1 -1
- package/dist/{ai/proposalSigning.d.ts → features/proposals/signing.d.ts} +2 -5
- package/dist/{ai/proposalSigning.js → features/proposals/signing.js} +1 -1
- package/dist/features/proposals/target.d.ts +52 -0
- package/dist/features/proposals/target.js +125 -0
- package/dist/features/proposals/types.d.ts +49 -0
- package/dist/features/proposals/types.js +1 -0
- package/dist/features/proposals/validation.d.ts +12 -0
- package/dist/features/proposals/validation.js +26 -0
- package/dist/features/providers/requestContext.d.ts +53 -0
- package/dist/features/providers/requestContext.js +90 -0
- package/dist/{ai/providerRuntime.d.ts → features/providers/runtime.d.ts} +1 -1
- package/dist/{ai/providerRuntime.js → features/providers/runtime.js} +1 -1
- package/dist/features/schema/context.d.ts +7 -0
- package/dist/features/schema/context.js +11 -0
- package/dist/{payload/schemaContext.d.ts → features/schema/description.d.ts} +6 -33
- package/dist/{payload/schemaContext.js → features/schema/description.js} +3 -207
- package/dist/{payload/toolFieldSelection.d.ts → features/schema/fieldSelection.d.ts} +1 -1
- package/dist/{payload/toolFieldSelection.js → features/schema/fieldSelection.js} +2 -2
- package/dist/features/schema/mentionDocuments.d.ts +15 -0
- package/dist/features/schema/mentionDocuments.js +50 -0
- package/dist/features/schema/mentions.d.ts +18 -0
- package/dist/features/schema/mentions.js +100 -0
- package/dist/{payload/normalizeData.js → features/schema/normalize.js} +1 -1
- package/dist/features/schema/registry.d.ts +30 -0
- package/dist/features/schema/registry.js +73 -0
- package/dist/{payload → features/schema}/toolSchemas.d.ts +1 -1
- package/dist/{payload → features/schema}/toolSchemas.js +1 -1
- package/dist/{ai → features}/sensitiveData.js +1 -1
- package/dist/{ai → features}/tokenUsage.d.ts +1 -2
- package/dist/handlers/apply/audit.d.ts +42 -0
- package/dist/handlers/apply/audit.js +150 -0
- package/dist/handlers/apply/operations.d.ts +18 -0
- package/dist/handlers/apply/operations.js +377 -0
- package/dist/handlers/apply/types.d.ts +33 -0
- package/dist/handlers/apply/types.js +1 -0
- package/dist/handlers/apply/validation.d.ts +45 -0
- package/dist/handlers/apply/validation.js +125 -0
- package/dist/handlers/applyActionHandler.d.ts +1 -6
- package/dist/handlers/applyActionHandler.js +13 -757
- package/dist/handlers/auditLogHandler.js +4 -8
- package/dist/handlers/chat/intent.d.ts +32 -0
- package/dist/handlers/chat/intent.js +155 -0
- package/dist/handlers/chat/prompt.d.ts +20 -0
- package/dist/handlers/chat/prompt.js +55 -0
- package/dist/handlers/chat/proposalTools.d.ts +96 -0
- package/dist/handlers/chat/proposalTools.js +451 -0
- package/dist/handlers/chat/streaming.d.ts +38 -0
- package/dist/handlers/chat/streaming.js +136 -0
- package/dist/handlers/chat/toolFactory.d.ts +79 -0
- package/dist/handlers/chat/toolFactory.js +156 -0
- package/dist/handlers/chat/types.d.ts +57 -0
- package/dist/handlers/chat/types.js +1 -0
- package/dist/handlers/chatHandler.d.ts +1 -44
- package/dist/handlers/chatHandler.js +186 -1162
- package/dist/handlers/generateFieldHandler.d.ts +3 -8
- package/dist/handlers/generateFieldHandler.js +26 -97
- package/dist/handlers/http.d.ts +17 -0
- package/dist/handlers/http.js +21 -0
- package/dist/handlers/mediaUploadHandler.js +12 -34
- package/dist/handlers/mentionSuggestionHandler.d.ts +1 -1
- package/dist/handlers/mentionSuggestionHandler.js +6 -10
- package/dist/handlers/proposalDiffHandler.d.ts +1 -1
- package/dist/handlers/proposalDiffHandler.js +72 -195
- package/dist/handlers/translateDocumentHandler.d.ts +3 -8
- package/dist/handlers/translateDocumentHandler.js +26 -82
- package/dist/index.d.ts +4 -30
- package/dist/index.js +31 -421
- package/dist/plugin/admin.d.ts +30 -0
- package/dist/plugin/admin.js +155 -0
- package/dist/plugin/collections.d.ts +3 -0
- package/dist/plugin/collections.js +169 -0
- package/dist/plugin/endpoints.d.ts +23 -0
- package/dist/plugin/endpoints.js +84 -0
- package/dist/plugin/options.d.ts +4 -0
- package/dist/plugin/options.js +13 -0
- package/dist/plugin/types.d.ts +28 -0
- package/dist/plugin/types.js +1 -0
- package/dist/{payload/shared.d.ts → utils/data.d.ts} +2 -12
- package/package.json +1 -1
- package/dist/payload/proposalData.js +0 -578
- /package/dist/{payload → features}/collectionPermissions.d.ts +0 -0
- /package/dist/{payload → features/content}/documentTranslation.d.ts +0 -0
- /package/dist/{payload → features/content}/documentTranslation.js +0 -0
- /package/dist/{payload/textFieldGeneration.d.ts → features/content/fieldGeneration.d.ts} +0 -0
- /package/dist/{payload/textFieldGeneration.js → features/content/fieldGeneration.js} +0 -0
- /package/dist/{ai → features}/promptCaching.js +0 -0
- /package/dist/{ai/proposalRepair.js → features/proposals/repair.js} +0 -0
- /package/dist/{ai/providerOptions.d.ts → features/providers/options.d.ts} +0 -0
- /package/dist/{ai/providerOptions.js → features/providers/options.js} +0 -0
- /package/dist/{payload/normalizeData.d.ts → features/schema/normalize.d.ts} +0 -0
- /package/dist/{ai → features}/sensitiveData.d.ts +0 -0
- /package/dist/{ai → features}/tokenUsage.js +0 -0
- /package/dist/{payload/shared.js → utils/data.js} +0 -0
- /package/dist/{payload → utils}/logging.d.ts +0 -0
- /package/dist/{payload → utils}/logging.js +0 -0
|
@@ -1,17 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
_aiSignature?: {
|
|
3
|
-
expiresAt: string;
|
|
4
|
-
value: string;
|
|
5
|
-
};
|
|
6
|
-
action: "create" | "delete" | "update" | "updateGlobal";
|
|
7
|
-
collection?: string;
|
|
8
|
-
data?: Record<string, unknown>;
|
|
9
|
-
id?: string;
|
|
10
|
-
label: string;
|
|
11
|
-
locale?: string;
|
|
12
|
-
localizedData?: Record<string, Record<string, unknown>>;
|
|
13
|
-
slug?: string;
|
|
14
|
-
};
|
|
1
|
+
import type { ActionProposal } from "../../features/proposals/types.js";
|
|
15
2
|
type ActionToastProps = {
|
|
16
3
|
apiRoute: string;
|
|
17
4
|
description?: string;
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Button, CheckIcon, SwapIcon } from "@payloadcms/ui";
|
|
3
3
|
import { formatAdminURL } from "payload/shared";
|
|
4
4
|
import { useState } from "react";
|
|
5
|
-
import { redactSensitiveData } from "../../
|
|
5
|
+
import { redactSensitiveData } from "../../features/sensitiveData.js";
|
|
6
6
|
import { DiffDialog } from "../diff-dialog/DiffDialog.js";
|
|
7
7
|
import { TextShimmer } from "../text-shimmer/TextShimmer.js";
|
|
8
8
|
import styles from "./ActionToast.module.css";
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import { Button, ChevronIcon, useAuth, useConfig, useDocumentForm, useDocumentInfo, useLocale } from "@payloadcms/ui";
|
|
4
4
|
import { formatAdminURL } from "payload/shared";
|
|
5
5
|
import { useEffect, useRef, useState } from "react";
|
|
6
|
-
import { mergeData } from "../../
|
|
6
|
+
import { mergeData } from "../../utils/data.js";
|
|
7
7
|
import { ActionToast } from "../action-toast/ActionToast.js";
|
|
8
8
|
import { useAIChatStream } from "../hooks/useAIChatStream.js";
|
|
9
9
|
import { useAISettings } from "../hooks/useAISettings.js";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { ActionProposal } from "
|
|
2
|
-
import { type ProposalDiff } from "../diff-dialog/DiffDialog.js";
|
|
1
|
+
import type { ActionProposal, ActionProposalReference, ProposalDiff } from "../../features/proposals/types.js";
|
|
3
2
|
export type AppliedChange = {
|
|
4
3
|
action?: ActionProposal["action"] | null;
|
|
5
4
|
additions: number;
|
|
@@ -22,7 +21,7 @@ export type AppliedChange = {
|
|
|
22
21
|
export type ActiveDiff = {
|
|
23
22
|
change: AppliedChange | null;
|
|
24
23
|
diff: ProposalDiff;
|
|
25
|
-
proposal:
|
|
24
|
+
proposal: ActionProposalReference;
|
|
26
25
|
};
|
|
27
26
|
declare const AuditLogList: () => import("react").JSX.Element;
|
|
28
27
|
export default AuditLogList;
|
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ActionProposalReference, ProposalDiff } from "../../features/proposals/types.js";
|
|
2
2
|
import type { AppliedChange } from "../audit-log-list/AuditLogList.js";
|
|
3
|
-
export type ProposalDiff = {
|
|
4
|
-
after: unknown;
|
|
5
|
-
before: unknown;
|
|
6
|
-
};
|
|
7
3
|
type DiffDialogProps = {
|
|
8
4
|
change?: AppliedChange;
|
|
9
5
|
diff: ProposalDiff;
|
|
10
6
|
onClose: () => void;
|
|
11
|
-
proposal:
|
|
7
|
+
proposal: ActionProposalReference;
|
|
12
8
|
tokenUsage?: {
|
|
13
9
|
inputTokens?: number;
|
|
14
10
|
outputTokens?: number;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
|
-
import { getJSONLineKey } from "../../
|
|
3
|
+
import { getJSONLineKey } from "../../utils/data.js";
|
|
4
4
|
import styles from "./DiffDialog.module.css";
|
|
5
5
|
import { Button } from "@payloadcms/ui";
|
|
6
6
|
const formatDiffValue = (value)=>{
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RefObject } from "react";
|
|
2
|
-
import type { ActionProposal } from "
|
|
2
|
+
import type { ActionProposal } from "../../features/proposals/types.js";
|
|
3
3
|
import type { Mention } from "./useMentions.js";
|
|
4
4
|
export type TokenUsage = {
|
|
5
5
|
inputTokens?: number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { formatAdminURL } from "payload/shared";
|
|
3
3
|
import { useEffect, useState } from "react";
|
|
4
|
-
import { isAbortError } from "../../
|
|
4
|
+
import { isAbortError } from "../../utils/data.js";
|
|
5
5
|
const managedSelectionKey = "payload-ai:selected-managed-model";
|
|
6
6
|
const getStoredModelKey = (provider)=>`payload-ai:selected-model:${provider}`;
|
|
7
7
|
const getStoredModel = (provider)=>{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { formatAdminURL } from "payload/shared";
|
|
3
3
|
import { useEffect, useState } from "react";
|
|
4
|
-
import { isAbortError } from "../../
|
|
4
|
+
import { isAbortError } from "../../utils/data.js";
|
|
5
5
|
const fetchDocumentMentionSuggestions = async ({ apiRoute, collectionSlug, query, signal })=>{
|
|
6
6
|
const res = await fetch(formatAdminURL({
|
|
7
7
|
apiRoute,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
-
import { getSerializableLabel, isInternalCollection } from "../../
|
|
3
|
+
import { getSerializableLabel, isInternalCollection } from "../../utils/data.js";
|
|
4
4
|
import { createBadgePrefix, getTextNodeAtOffset, replaceTextRangeWithBadge } from "../ai-input/badge.js";
|
|
5
5
|
import { useDocumentMentionSuggestions } from "../hooks/useDocumentMentionSuggestions.js";
|
|
6
6
|
const isMentionBoundary = (character)=>{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AIProviderProfile } from "../../
|
|
1
|
+
import { type AIProviderProfile } from "../../features/providers/options.js";
|
|
2
2
|
type MediaConfig = {
|
|
3
3
|
acceptedMimeTypes?: string[];
|
|
4
4
|
collectionSlug: string;
|
|
@@ -22,11 +22,11 @@ export declare const usePluginConfig: (config: {
|
|
|
22
22
|
aiModelConfig: {
|
|
23
23
|
defaults: Record<"claude" | "google" | "mistral" | "openai" | "openrouter", string>;
|
|
24
24
|
providers: {
|
|
25
|
-
claude: import("../../
|
|
26
|
-
google: import("../../
|
|
27
|
-
mistral: import("../../
|
|
28
|
-
openai: import("../../
|
|
29
|
-
openrouter: import("../../
|
|
25
|
+
claude: import("../../features/providers/options.js").AIProviderModelOption[];
|
|
26
|
+
google: import("../../features/providers/options.js").AIProviderModelOption[];
|
|
27
|
+
mistral: import("../../features/providers/options.js").AIProviderModelOption[];
|
|
28
|
+
openai: import("../../features/providers/options.js").AIProviderModelOption[];
|
|
29
|
+
openrouter: import("../../features/providers/options.js").AIProviderModelOption[];
|
|
30
30
|
};
|
|
31
31
|
};
|
|
32
32
|
defaultLocale: string | undefined;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useMemo } from "react";
|
|
2
|
-
import { getLegacyAIProviderProfiles, getResolvedAIModelConfig } from "../../
|
|
2
|
+
import { getLegacyAIProviderProfiles, getResolvedAIModelConfig } from "../../features/providers/options.js";
|
|
3
3
|
export const usePluginConfig = (config)=>{
|
|
4
4
|
const pluginConfig = config.admin?.custom?.payloadAiPlugin;
|
|
5
5
|
const aiModelConfig = useMemo(()=>getResolvedAIModelConfig(pluginConfig?.models), [
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { SystemModelMessage } from "ai";
|
|
2
|
-
import type { AIProvider } from "./
|
|
2
|
+
import type { AIProvider } from "./providers/options.js";
|
|
3
3
|
type PromptCacheContext = {
|
|
4
4
|
cacheable: Record<string, unknown>[];
|
|
5
5
|
dynamic: Record<string, unknown>[];
|
|
6
6
|
};
|
|
7
|
-
type PromptCacheProviderOptions = NonNullable<SystemModelMessage["providerOptions"]>;
|
|
7
|
+
export type PromptCacheProviderOptions = NonNullable<SystemModelMessage["providerOptions"]>;
|
|
8
8
|
export declare const splitPromptCacheContext: (mentionContext: Record<string, unknown>[]) => PromptCacheContext;
|
|
9
9
|
export declare const createCachedSystemMessages: ({ cacheableContext, cacheableInstructions, dynamicInstructions, enabled, provider, staticInstructions, }: {
|
|
10
10
|
cacheableContext: Record<string, unknown>[];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { NormalizeFieldValueArgs } from "./normalizationTypes.js";
|
|
2
|
+
export declare const containerNormalizers: {
|
|
3
|
+
array: ({ field, normalizeRecord, path, value, ...state }: NormalizeFieldValueArgs) => (Record<string, unknown> | undefined)[] | undefined;
|
|
4
|
+
blocks: ({ field, normalizeRecord, path, value, ...state }: NormalizeFieldValueArgs) => ({
|
|
5
|
+
blockType: string;
|
|
6
|
+
} | undefined)[] | undefined;
|
|
7
|
+
group: ({ field, normalizeRecord, path, value, ...state }: NormalizeFieldValueArgs) => Record<string, unknown> | undefined;
|
|
8
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { isRecord } from "../../utils/data.js";
|
|
2
|
+
const normalizeGroup = ({ field, normalizeRecord, path, value, ...state })=>{
|
|
3
|
+
if (!isRecord(value)) {
|
|
4
|
+
state.issues.push({
|
|
5
|
+
code: "invalid_container",
|
|
6
|
+
message: "Group fields must be objects.",
|
|
7
|
+
path
|
|
8
|
+
});
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
return normalizeRecord({
|
|
12
|
+
...state,
|
|
13
|
+
allowSafeFallback: false,
|
|
14
|
+
enforceRequiredChildren: state.mode === "create" || state.enforceRequiredChildren,
|
|
15
|
+
fields: field.fields || [],
|
|
16
|
+
path,
|
|
17
|
+
value
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
const getArrayItemLabelField = (field)=>{
|
|
21
|
+
const childFields = (field.fields || []).filter((childField)=>Boolean(childField.name));
|
|
22
|
+
return childFields.find((childField)=>childField.name === "label") || childFields.find((childField)=>childField.name === "title") || childFields.find((childField)=>childField.name === "name") || childFields.find((childField)=>childField.name === "value") || childFields[0];
|
|
23
|
+
};
|
|
24
|
+
const normalizeArray = ({ field, normalizeRecord, path, value, ...state })=>{
|
|
25
|
+
if (!Array.isArray(value)) {
|
|
26
|
+
state.issues.push({
|
|
27
|
+
code: "invalid_array",
|
|
28
|
+
message: "Array fields must use an array value.",
|
|
29
|
+
path
|
|
30
|
+
});
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
const itemLabelField = getArrayItemLabelField(field);
|
|
34
|
+
return value.map((item, index)=>{
|
|
35
|
+
const itemPath = `${path}.${index}`;
|
|
36
|
+
if (!isRecord(item) && !itemLabelField) {
|
|
37
|
+
state.issues.push({
|
|
38
|
+
code: "invalid_array",
|
|
39
|
+
message: "Array items must be objects for this field.",
|
|
40
|
+
path: itemPath
|
|
41
|
+
});
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
return normalizeRecord({
|
|
45
|
+
...state,
|
|
46
|
+
allowSafeFallback: false,
|
|
47
|
+
enforceRequiredChildren: true,
|
|
48
|
+
fields: field.fields || [],
|
|
49
|
+
path: itemPath,
|
|
50
|
+
value: isRecord(item) ? item : {
|
|
51
|
+
[itemLabelField.name]: item
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
const getBlockType = (item)=>typeof item.blockType === "string" ? item.blockType : typeof item.type === "string" ? item.type : typeof item.slug === "string" ? item.slug : null;
|
|
57
|
+
const normalizeBlocks = ({ field, normalizeRecord, path, value, ...state })=>{
|
|
58
|
+
if (!Array.isArray(value)) {
|
|
59
|
+
state.issues.push({
|
|
60
|
+
code: "invalid_blocks",
|
|
61
|
+
message: "Blocks fields must use an array value.",
|
|
62
|
+
path
|
|
63
|
+
});
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
return value.map((item, index)=>{
|
|
67
|
+
const itemPath = `${path}.${index}`;
|
|
68
|
+
if (!isRecord(item)) {
|
|
69
|
+
state.issues.push({
|
|
70
|
+
code: "invalid_blocks",
|
|
71
|
+
message: "Each block entry must be an object.",
|
|
72
|
+
path: itemPath
|
|
73
|
+
});
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
const blockType = getBlockType(item);
|
|
77
|
+
if (!blockType) {
|
|
78
|
+
state.issues.push({
|
|
79
|
+
code: "invalid_block_type",
|
|
80
|
+
message: "Each block entry must include blockType.",
|
|
81
|
+
path: itemPath
|
|
82
|
+
});
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
const block = field.blocks?.find((candidate)=>candidate.slug === blockType);
|
|
86
|
+
if (!block) {
|
|
87
|
+
state.issues.push({
|
|
88
|
+
code: "invalid_block_type",
|
|
89
|
+
message: `Unknown block type "${blockType}".`,
|
|
90
|
+
path: `${itemPath}.blockType`
|
|
91
|
+
});
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
const { blockType: _blockType, slug: _slug, type: _type, ...blockData } = item;
|
|
95
|
+
return {
|
|
96
|
+
...normalizeRecord({
|
|
97
|
+
...state,
|
|
98
|
+
allowSafeFallback: false,
|
|
99
|
+
enforceRequiredChildren: true,
|
|
100
|
+
fields: block.fields || [],
|
|
101
|
+
path: itemPath,
|
|
102
|
+
value: blockData
|
|
103
|
+
}),
|
|
104
|
+
blockType
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
export const containerNormalizers = {
|
|
109
|
+
array: normalizeArray,
|
|
110
|
+
blocks: normalizeBlocks,
|
|
111
|
+
group: normalizeGroup
|
|
112
|
+
};
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { type CollectionConfig, type FieldConfig } from "
|
|
3
|
-
type ProposalMode
|
|
4
|
-
export type ProposalValidationIssue
|
|
5
|
-
code: "empty_localized_data" | "invalid_array" | "invalid_block_type" | "invalid_blocks" | "invalid_checkbox" | "invalid_container" | "invalid_date" | "invalid_option" | "invalid_relationship" | "missing_required_field" | "non_localized_field_in_secondary_locale" | "unknown_field";
|
|
6
|
-
message: string;
|
|
7
|
-
path: string;
|
|
8
|
-
};
|
|
1
|
+
import type { LocalizedDataInput } from "./types.js";
|
|
2
|
+
import { type CollectionConfig, type FieldConfig } from "../schema/normalize.js";
|
|
3
|
+
import type { ProposalMode, ProposalValidationIssue } from "./normalizationTypes.js";
|
|
4
|
+
export type { ProposalValidationIssue } from "./normalizationTypes.js";
|
|
9
5
|
export type PreparedProposalData = {
|
|
10
6
|
coercedFields: string[];
|
|
11
7
|
data?: Record<string, unknown>;
|
|
@@ -20,7 +16,7 @@ type PrepareProposalDataArgs = {
|
|
|
20
16
|
localizedData?: LocalizedDataInput;
|
|
21
17
|
mode: ProposalMode;
|
|
22
18
|
};
|
|
23
|
-
export declare const prepareProposalWriteData: ({ collectionConfig, data, inferenceText, label, localizedData, mode }: PrepareProposalDataArgs) => PreparedProposalData;
|
|
19
|
+
export declare const prepareProposalWriteData: ({ collectionConfig, data, inferenceText, label, localizedData, mode, }: PrepareProposalDataArgs) => PreparedProposalData;
|
|
24
20
|
export declare const applyLocalizedRequiredFallbackToPreparedData: ({ fallbackSource, fields, preparedData, }: {
|
|
25
21
|
fallbackSource: Record<string, unknown>;
|
|
26
22
|
fields: readonly FieldConfig[];
|
|
@@ -28,4 +24,3 @@ export declare const applyLocalizedRequiredFallbackToPreparedData: ({ fallbackSo
|
|
|
28
24
|
}) => {
|
|
29
25
|
[x: string]: unknown;
|
|
30
26
|
};
|
|
31
|
-
export {};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { isRecord } from "../../utils/data.js";
|
|
2
|
+
import { getSchemaFields, normalizeAuthData } from "../schema/normalize.js";
|
|
3
|
+
import { prepareSingleLocaleData } from "./recordNormalization.js";
|
|
4
|
+
export const prepareProposalWriteData = ({ collectionConfig, data, inferenceText, label, localizedData, mode })=>{
|
|
5
|
+
const schemaFields = getSchemaFields(collectionConfig);
|
|
6
|
+
const titleFieldName = typeof collectionConfig?.admin?.useAsTitle === "string" ? collectionConfig.admin.useAsTitle : undefined;
|
|
7
|
+
const issues = [];
|
|
8
|
+
const coercedFields = [];
|
|
9
|
+
if (localizedData) {
|
|
10
|
+
const localeEntries = Object.entries(localizedData);
|
|
11
|
+
if (localeEntries.length === 0) {
|
|
12
|
+
issues.push({
|
|
13
|
+
code: "empty_localized_data",
|
|
14
|
+
message: "localizedData must contain at least one locale entry.",
|
|
15
|
+
path: "localizedData"
|
|
16
|
+
});
|
|
17
|
+
return {
|
|
18
|
+
coercedFields,
|
|
19
|
+
issues,
|
|
20
|
+
localizedData: {}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const normalizedLocalizedData = {};
|
|
24
|
+
for (const [locale, localeValue] of localeEntries){
|
|
25
|
+
if (!isRecord(localeValue)) {
|
|
26
|
+
issues.push({
|
|
27
|
+
code: "invalid_container",
|
|
28
|
+
message: "Each locale entry must be an object.",
|
|
29
|
+
path: `localizedData.${locale}`
|
|
30
|
+
});
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
normalizedLocalizedData[locale] = prepareSingleLocaleData({
|
|
34
|
+
coercedFields,
|
|
35
|
+
data: localeValue,
|
|
36
|
+
fields: schemaFields,
|
|
37
|
+
inferenceText,
|
|
38
|
+
issues,
|
|
39
|
+
label,
|
|
40
|
+
mode,
|
|
41
|
+
titleFieldName
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
const [firstLocale] = localeEntries[0] || [];
|
|
45
|
+
if (firstLocale) {
|
|
46
|
+
for (const field of schemaFields.filter((candidate)=>Boolean(candidate.name))){
|
|
47
|
+
if (field.localized) continue;
|
|
48
|
+
for (const [locale, localeValue] of Object.entries(normalizedLocalizedData)){
|
|
49
|
+
if (locale === firstLocale || localeValue[field.name] === undefined) continue;
|
|
50
|
+
issues.push({
|
|
51
|
+
code: "non_localized_field_in_secondary_locale",
|
|
52
|
+
message: "Non-localized fields may only be set in the first locale entry.",
|
|
53
|
+
path: `localizedData.${locale}.${field.name}`
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
coercedFields,
|
|
60
|
+
issues,
|
|
61
|
+
localizedData: Object.fromEntries(Object.entries(normalizedLocalizedData).map(([locale, localeValue])=>[
|
|
62
|
+
locale,
|
|
63
|
+
normalizeAuthData(collectionConfig, {
|
|
64
|
+
coercedFields: [],
|
|
65
|
+
data: localeValue,
|
|
66
|
+
droppedFields: []
|
|
67
|
+
}).data
|
|
68
|
+
]))
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const normalizedData = prepareSingleLocaleData({
|
|
72
|
+
coercedFields,
|
|
73
|
+
data: data || {},
|
|
74
|
+
fields: schemaFields,
|
|
75
|
+
inferenceText,
|
|
76
|
+
issues,
|
|
77
|
+
label,
|
|
78
|
+
mode,
|
|
79
|
+
titleFieldName
|
|
80
|
+
});
|
|
81
|
+
const authNormalized = normalizeAuthData(collectionConfig, {
|
|
82
|
+
coercedFields: [],
|
|
83
|
+
data: normalizedData,
|
|
84
|
+
droppedFields: []
|
|
85
|
+
});
|
|
86
|
+
coercedFields.push(...authNormalized.coercedFields);
|
|
87
|
+
return {
|
|
88
|
+
coercedFields,
|
|
89
|
+
data: authNormalized.data,
|
|
90
|
+
issues
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
export const applyLocalizedRequiredFallbackToPreparedData = ({ fallbackSource, fields, preparedData })=>{
|
|
94
|
+
const mergedData = {
|
|
95
|
+
...preparedData
|
|
96
|
+
};
|
|
97
|
+
for (const field of fields.filter((candidate)=>Boolean(candidate.name))){
|
|
98
|
+
if (field.localized && field.required && mergedData[field.name] === undefined && fallbackSource[field.name] !== undefined) {
|
|
99
|
+
mergedData[field.name] = fallbackSource[field.name];
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (field.type === "group" && field.fields?.length && isRecord(fallbackSource[field.name])) {
|
|
103
|
+
const currentValue = isRecord(mergedData[field.name]) ? mergedData[field.name] : {};
|
|
104
|
+
const nestedValue = applyLocalizedRequiredFallbackToPreparedData({
|
|
105
|
+
fallbackSource: fallbackSource[field.name],
|
|
106
|
+
fields: field.fields,
|
|
107
|
+
preparedData: currentValue
|
|
108
|
+
});
|
|
109
|
+
if (Object.keys(nestedValue).length > 0) mergedData[field.name] = nestedValue;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return mergedData;
|
|
113
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { createLexicalText } from "../schema/normalize.js";
|
|
2
|
+
import { containerNormalizers } from "./containerNormalization.js";
|
|
3
|
+
import { getFieldOptionValues, inferOptionValueFromText } from "./optionInference.js";
|
|
4
|
+
import { normalizeRelationshipValue } from "./relationshipNormalization.js";
|
|
5
|
+
const normalizeCheckbox = ({ issues, path, value })=>{
|
|
6
|
+
if (typeof value === "boolean") return value;
|
|
7
|
+
if (value === "true") return true;
|
|
8
|
+
if (value === "false") return false;
|
|
9
|
+
issues.push({
|
|
10
|
+
code: "invalid_checkbox",
|
|
11
|
+
message: "Checkbox fields must use true or false.",
|
|
12
|
+
path
|
|
13
|
+
});
|
|
14
|
+
return undefined;
|
|
15
|
+
};
|
|
16
|
+
const normalizeOption = ({ coercedFields, field, inferenceText, issues, label, path, value })=>{
|
|
17
|
+
const optionValues = getFieldOptionValues(field);
|
|
18
|
+
const stringValue = value === null ? "" : String(value);
|
|
19
|
+
const inferredOptionValue = inferOptionValueFromText({
|
|
20
|
+
field,
|
|
21
|
+
requireFieldMention: true,
|
|
22
|
+
text: inferenceText || label
|
|
23
|
+
});
|
|
24
|
+
if (inferredOptionValue && optionValues.includes(inferredOptionValue)) {
|
|
25
|
+
if (stringValue !== inferredOptionValue) coercedFields.push(path);
|
|
26
|
+
return inferredOptionValue;
|
|
27
|
+
}
|
|
28
|
+
if (stringValue && optionValues.includes(stringValue)) return stringValue;
|
|
29
|
+
issues.push({
|
|
30
|
+
code: "invalid_option",
|
|
31
|
+
message: `Field must use one of: ${optionValues.join(", ")}.`,
|
|
32
|
+
path
|
|
33
|
+
});
|
|
34
|
+
return undefined;
|
|
35
|
+
};
|
|
36
|
+
const normalizeText = ({ value })=>value === null ? value : String(value);
|
|
37
|
+
const normalizeDate = ({ issues, path, value })=>{
|
|
38
|
+
const date = new Date(String(value));
|
|
39
|
+
if (!Number.isNaN(date.getTime())) return date.toISOString();
|
|
40
|
+
issues.push({
|
|
41
|
+
code: "invalid_date",
|
|
42
|
+
message: "Date fields must use a valid date value.",
|
|
43
|
+
path
|
|
44
|
+
});
|
|
45
|
+
return undefined;
|
|
46
|
+
};
|
|
47
|
+
const normalizeRelationship = ({ field, issues, path, value })=>normalizeRelationshipValue({
|
|
48
|
+
field,
|
|
49
|
+
issues,
|
|
50
|
+
path,
|
|
51
|
+
value
|
|
52
|
+
});
|
|
53
|
+
const fieldNormalizers = {
|
|
54
|
+
...containerNormalizers,
|
|
55
|
+
checkbox: normalizeCheckbox,
|
|
56
|
+
date: normalizeDate,
|
|
57
|
+
email: normalizeText,
|
|
58
|
+
radio: normalizeOption,
|
|
59
|
+
relationship: normalizeRelationship,
|
|
60
|
+
richText: ({ value })=>createLexicalText(value),
|
|
61
|
+
select: normalizeOption,
|
|
62
|
+
text: normalizeText,
|
|
63
|
+
textarea: normalizeText,
|
|
64
|
+
upload: normalizeRelationship
|
|
65
|
+
};
|
|
66
|
+
export const normalizeFieldValue = (args)=>{
|
|
67
|
+
const normalizer = args.field.type ? fieldNormalizers[args.field.type] : undefined;
|
|
68
|
+
return normalizer ? normalizer(args) : args.value;
|
|
69
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { FieldConfig } from "../schema/normalize.js";
|
|
2
|
+
export type ProposalMode = "create" | "update";
|
|
3
|
+
export type ProposalValidationIssue = {
|
|
4
|
+
code: "empty_localized_data" | "invalid_array" | "invalid_block_type" | "invalid_blocks" | "invalid_checkbox" | "invalid_container" | "invalid_date" | "invalid_option" | "invalid_relationship" | "missing_required_field" | "non_localized_field_in_secondary_locale" | "unknown_field";
|
|
5
|
+
message: string;
|
|
6
|
+
path: string;
|
|
7
|
+
};
|
|
8
|
+
export type NormalizationState = {
|
|
9
|
+
coercedFields: string[];
|
|
10
|
+
inferenceText?: string;
|
|
11
|
+
issues: ProposalValidationIssue[];
|
|
12
|
+
label: string;
|
|
13
|
+
mode: ProposalMode;
|
|
14
|
+
titleFieldName?: string;
|
|
15
|
+
};
|
|
16
|
+
export type NormalizeRecordArgs = NormalizationState & {
|
|
17
|
+
allowSafeFallback: boolean;
|
|
18
|
+
enforceRequiredChildren: boolean;
|
|
19
|
+
fields: readonly FieldConfig[];
|
|
20
|
+
path: string;
|
|
21
|
+
value: Record<string, unknown>;
|
|
22
|
+
};
|
|
23
|
+
type NormalizeRecord = (args: NormalizeRecordArgs) => Record<string, unknown>;
|
|
24
|
+
export type NormalizeFieldValueArgs = NormalizationState & {
|
|
25
|
+
enforceRequiredChildren: boolean;
|
|
26
|
+
field: FieldConfig;
|
|
27
|
+
normalizeRecord: NormalizeRecord;
|
|
28
|
+
path: string;
|
|
29
|
+
value: unknown;
|
|
30
|
+
};
|
|
31
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FieldConfig } from "../schema/normalize.js";
|
|
2
|
+
export declare const getFieldOptionValues: (field: FieldConfig) => string[];
|
|
3
|
+
export declare const inferOptionValueFromText: ({ field, requireFieldMention, text, }: {
|
|
4
|
+
field: FieldConfig;
|
|
5
|
+
requireFieldMention?: boolean;
|
|
6
|
+
text?: string;
|
|
7
|
+
}) => string | undefined;
|
|
8
|
+
export declare const getDefaultFieldValue: ({ field, inferenceText, label, titleFieldName, }: {
|
|
9
|
+
field: FieldConfig;
|
|
10
|
+
inferenceText?: string;
|
|
11
|
+
label: string;
|
|
12
|
+
titleFieldName?: string;
|
|
13
|
+
}) => {} | null | undefined;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { getOptionValue, getSafeProposalLabel } from "../../utils/data.js";
|
|
2
|
+
const isTitleLikeField = (field, titleFieldName)=>{
|
|
3
|
+
const normalizedName = field.name?.toLowerCase();
|
|
4
|
+
return Boolean(field.name && (field.name === titleFieldName || normalizedName === "title" || normalizedName === "name" || normalizedName === "label" || normalizedName === "headline"));
|
|
5
|
+
};
|
|
6
|
+
export const getFieldOptionValues = (field)=>(field.options || []).map((option)=>getOptionValue(option)).filter((option)=>Boolean(option));
|
|
7
|
+
const escapeRegExp = (value)=>value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8
|
+
const getFieldNameVariants = (fieldName)=>{
|
|
9
|
+
if (!fieldName) return [];
|
|
10
|
+
const spacedName = fieldName.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").toLowerCase();
|
|
11
|
+
return Array.from(new Set([
|
|
12
|
+
fieldName.toLowerCase(),
|
|
13
|
+
spacedName
|
|
14
|
+
])).filter(Boolean);
|
|
15
|
+
};
|
|
16
|
+
export const inferOptionValueFromText = ({ field, requireFieldMention = false, text })=>{
|
|
17
|
+
const normalizedText = text?.trim().toLowerCase();
|
|
18
|
+
if (!normalizedText) return undefined;
|
|
19
|
+
if (requireFieldMention && !getFieldNameVariants(field.name).some((variant)=>new RegExp(`\\b${escapeRegExp(variant)}\\b`, "i").test(normalizedText))) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
return getFieldOptionValues(field).sort((left, right)=>right.length - left.length).find((optionValue)=>new RegExp(`\\b${escapeRegExp(optionValue.toLowerCase())}\\b`, "i").test(normalizedText));
|
|
23
|
+
};
|
|
24
|
+
export const getDefaultFieldValue = ({ field, inferenceText, label, titleFieldName })=>{
|
|
25
|
+
if (field.name === "_status") return "draft";
|
|
26
|
+
if (field.type === "checkbox") return false;
|
|
27
|
+
if (field.type === "select" || field.type === "radio") {
|
|
28
|
+
const inferredOptionValue = inferOptionValueFromText({
|
|
29
|
+
field,
|
|
30
|
+
requireFieldMention: true,
|
|
31
|
+
text: inferenceText || label
|
|
32
|
+
});
|
|
33
|
+
if (inferredOptionValue !== undefined) return inferredOptionValue;
|
|
34
|
+
if (field.defaultValue !== undefined) return field.defaultValue;
|
|
35
|
+
return getOptionValue(field.options?.[0]);
|
|
36
|
+
}
|
|
37
|
+
if (field.defaultValue !== undefined) return field.defaultValue;
|
|
38
|
+
if ([
|
|
39
|
+
"email",
|
|
40
|
+
"text",
|
|
41
|
+
"textarea"
|
|
42
|
+
].includes(field.type || "") && isTitleLikeField(field, titleFieldName)) {
|
|
43
|
+
return getSafeProposalLabel(label);
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type FieldConfig } from "../schema/normalize.js";
|
|
2
|
+
import type { NormalizationState } from "./normalizationTypes.js";
|
|
3
|
+
export declare const prepareSingleLocaleData: ({ data, fields, ...state }: NormalizationState & {
|
|
4
|
+
data: Record<string, unknown>;
|
|
5
|
+
fields: readonly FieldConfig[];
|
|
6
|
+
}) => Record<string, unknown>;
|