@revealui/core 0.10.2 → 0.11.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/README.md +4 -2
- package/dist/client/admin/components/DocumentForm.js +17 -6
- package/dist/client/richtext/RichTextEditor.d.ts +1 -1
- package/dist/client/richtext/RichTextEditor.d.ts.map +1 -1
- package/dist/client/richtext/RichTextEditor.js +15 -15
- package/dist/client/richtext/components/ImageNodeComponent.js +4 -4
- package/dist/collections/CollectionOperations.d.ts +5 -0
- package/dist/collections/CollectionOperations.d.ts.map +1 -1
- package/dist/collections/CollectionOperations.js +7 -0
- package/dist/collections/operations/create.d.ts.map +1 -1
- package/dist/collections/operations/create.js +20 -0
- package/dist/collections/operations/delete.d.ts.map +1 -1
- package/dist/collections/operations/delete.js +14 -0
- package/dist/collections/operations/drafts.d.ts +33 -0
- package/dist/collections/operations/drafts.d.ts.map +1 -0
- package/dist/collections/operations/drafts.js +43 -0
- package/dist/collections/operations/fieldValidation.d.ts +53 -0
- package/dist/collections/operations/fieldValidation.d.ts.map +1 -0
- package/dist/collections/operations/fieldValidation.js +89 -0
- package/dist/collections/operations/find.d.ts.map +1 -1
- package/dist/collections/operations/find.js +23 -5
- package/dist/collections/operations/findById.d.ts +1 -0
- package/dist/collections/operations/findById.d.ts.map +1 -1
- package/dist/collections/operations/findById.js +33 -21
- package/dist/collections/operations/snapshot.d.ts +30 -0
- package/dist/collections/operations/snapshot.d.ts.map +1 -0
- package/dist/collections/operations/snapshot.js +81 -0
- package/dist/collections/operations/update.d.ts.map +1 -1
- package/dist/collections/operations/update.js +21 -0
- package/dist/error-handling/error-boundary.d.ts.map +1 -1
- package/dist/error-handling/error-boundary.js +11 -7
- package/dist/error-handling/fallback-components.d.ts.map +1 -1
- package/dist/error-handling/fallback-components.js +68 -48
- package/dist/features.d.ts +0 -2
- package/dist/features.d.ts.map +1 -1
- package/dist/features.js +2 -5
- package/dist/generated/types/admin.d.ts +42 -35
- package/dist/generated/types/admin.d.ts.map +1 -1
- package/dist/instance/RevealUIInstance.d.ts.map +1 -1
- package/dist/instance/methods/findById.d.ts +2 -0
- package/dist/instance/methods/findById.d.ts.map +1 -1
- package/dist/license.d.ts +67 -1
- package/dist/license.d.ts.map +1 -1
- package/dist/license.js +184 -25
- package/dist/richtext/exports/client/rcc.d.ts.map +1 -1
- package/dist/richtext/exports/client/rcc.js +6 -1
- package/dist/richtext/exports/server/rsc.d.ts +15 -2
- package/dist/richtext/exports/server/rsc.d.ts.map +1 -1
- package/dist/richtext/exports/server/rsc.js +36 -6
- package/dist/storage/index.d.ts +4 -5
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +10 -13
- package/dist/storage/object-storage.d.ts +5 -5
- package/dist/storage/object-storage.js +5 -5
- package/dist/storage/types.d.ts +5 -12
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/storage/types.js +4 -4
- package/dist/types/runtime.d.ts +51 -0
- package/dist/types/runtime.d.ts.map +1 -1
- package/package.json +22 -23
- package/dist/storage/vercel-blob-provider.d.ts +0 -28
- package/dist/storage/vercel-blob-provider.d.ts.map +0 -1
- package/dist/storage/vercel-blob-provider.js +0 -88
package/README.md
CHANGED
|
@@ -74,9 +74,10 @@ const Posts: RevealUICollection = {
|
|
|
74
74
|
### Server-Side API
|
|
75
75
|
|
|
76
76
|
```typescript
|
|
77
|
-
import {
|
|
77
|
+
import { getRevealUI } from '@revealui/core'
|
|
78
78
|
|
|
79
79
|
// Create, read, update, delete
|
|
80
|
+
const revealui = await getRevealUI({ config })
|
|
80
81
|
const post = await revealui.create({ collection: 'posts', data: { title: 'Hello' } })
|
|
81
82
|
const posts = await revealui.find({ collection: 'posts', where: { status: { equals: 'published' } } })
|
|
82
83
|
```
|
|
@@ -106,7 +107,8 @@ defineField({ name: 'body', type: 'richText' })
|
|
|
106
107
|
### Feature Gating
|
|
107
108
|
|
|
108
109
|
```typescript
|
|
109
|
-
import { isLicensed
|
|
110
|
+
import { isLicensed } from '@revealui/core/license'
|
|
111
|
+
import { isFeatureEnabled } from '@revealui/core/features'
|
|
110
112
|
|
|
111
113
|
if (isLicensed('pro')) {
|
|
112
114
|
// Pro-tier feature
|
|
@@ -177,12 +177,23 @@ function FieldInput({ field, value, onChange }) {
|
|
|
177
177
|
return (_jsx("input", { type: "number", id: field.name, value: typeof value === 'number' ? value : value ? Number(value) : '', onChange: (e) => onChange(Number(e.target.value) || undefined), className: baseClasses, required: field.required, min: field.min, max: field.max }));
|
|
178
178
|
case 'checkbox':
|
|
179
179
|
return (_jsx("input", { type: "checkbox", id: field.name, checked: Boolean(value), onChange: (e) => onChange(e.target.checked), className: "h-4 w-4 text-primary focus:ring-ring border-input rounded" }));
|
|
180
|
-
case 'select':
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
180
|
+
case 'select': {
|
|
181
|
+
const isMulti = field.hasMany === true;
|
|
182
|
+
const selectClasses = `${baseClasses} text-foreground bg-card`;
|
|
183
|
+
const optionNodes = field.options?.map((option) => {
|
|
184
|
+
const optValue = typeof option === 'string' ? option : option.value;
|
|
185
|
+
const optLabel = typeof option === 'string' ? option : option.label;
|
|
186
|
+
return (_jsx("option", { value: optValue, children: optLabel }, optValue));
|
|
187
|
+
});
|
|
188
|
+
if (isMulti) {
|
|
189
|
+
const selected = Array.isArray(value) ? value : value ? [String(value)] : [];
|
|
190
|
+
return (_jsx("select", { id: field.name, multiple: true, value: selected, onChange: (e) => {
|
|
191
|
+
const vals = Array.from(e.target.selectedOptions).map((o) => o.value);
|
|
192
|
+
onChange(vals);
|
|
193
|
+
}, className: `${selectClasses} h-32`, required: field.required, children: optionNodes }));
|
|
194
|
+
}
|
|
195
|
+
return (_jsxs("select", { id: field.name, value: formatTextValue(value), onChange: (e) => onChange(e.target.value), className: selectClasses, required: field.required, children: [_jsx("option", { value: "", children: "Select an option" }), optionNodes] }));
|
|
196
|
+
}
|
|
186
197
|
case 'radio':
|
|
187
198
|
return (_jsx("div", { className: "space-y-2 mt-1", children: field.options?.map((option) => {
|
|
188
199
|
const optValue = typeof option === 'string' ? option : option.value;
|
|
@@ -43,6 +43,6 @@ export interface RichTextEditorProps {
|
|
|
43
43
|
export { FloatingToolbarPlugin } from './plugins/FloatingToolbarPlugin.js';
|
|
44
44
|
export { ToolbarPlugin } from './plugins/ToolbarPlugin.js';
|
|
45
45
|
export declare function RichTextEditor({ editorConfig, initialValue, onChange, onSerializedChange, placeholder, className, editable, autoFocus, onError, namespace, collaborative, documentId, providerFactory, shouldBootstrap, username, cursorColor, clientType, agentModel, }: RichTextEditorProps): import("react").JSX.Element;
|
|
46
|
-
export declare const richTextEditorStyles = "\n.revealui-rich-text-editor {\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n overflow: hidden;\n font-family: system-ui, -apple-system, sans-serif;\n}\n\n.editor-toolbar {\n display: flex;\n gap: 4px;\n padding: 8px;\n border-bottom: 1px solid #e2e8f0;\n background: #f8fafc;\n}\n\n.editor-toolbar--floating {\n border-bottom: none;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n background: white;\n padding: 6px;\n transition: opacity 0.2s ease;\n}\n\n.toolbar-btn {\n padding: 6px 10px;\n border: 1px solid #e2e8f0;\n border-radius: 4px;\n background: white;\n cursor: pointer;\n font-size: 14px;\n transition: all 0.15s ease;\n}\n\n.toolbar-btn:hover {\n background: #f1f5f9;\n}\n\n.toolbar-btn.active {\n background: #3b82f6;\n color: white;\n border-color: #3b82f6;\n}\n\n.editor-container {\n position: relative;\n min-height: 150px;\n}\n\n.editor-input {\n padding: 16px;\n outline: none;\n min-height: 150px;\n}\n\n.editor-placeholder {\n position: absolute;\n top: 16px;\n left: 16px;\n color: #94a3b8;\n pointer-events: none;\n user-select: none;\n}\n\n.editor-paragraph {\n margin: 0 0 8px 0;\n}\n\n.editor-heading-h1 { font-size: 2em; font-weight: bold; margin: 0 0 12px 0; }\n.editor-heading-h2 { font-size: 1.5em; font-weight: bold; margin: 0 0 10px 0; }\n.editor-heading-h3 { font-size: 1.25em; font-weight: bold; margin: 0 0 8px 0; }\n.editor-heading-h4 { font-size: 1em; font-weight: bold; margin: 0 0 8px 0; }\n.editor-heading-h5 { font-size: 0.875em; font-weight: bold; margin: 0 0 8px 0; }\n.editor-heading-h6 { font-size: 0.75em; font-weight: bold; margin: 0 0 8px 0; }\n\n.editor-quote {\n border-left: 4px solid #e2e8f0;\n padding-left: 16px;\n margin: 0 0 8px 0;\n color: #64748b;\n}\n\n.editor-list-ol { list-style: decimal; padding-left: 24px; margin: 0 0 8px 0; }\n.editor-list-ul { list-style: disc; padding-left: 24px; margin: 0 0 8px 0; }\n.editor-listitem { margin: 4px 0; }\n\n.editor-link { color: #3b82f6; text-decoration: underline; }\n\n.editor-text-bold { font-weight: bold; }\n.editor-text-italic { font-style: italic; }\n.editor-text-underline { text-decoration: underline; }\n.editor-text-strikethrough { text-decoration: line-through; }\n.editor-text-code {\n background: #f1f5f9;\n padding: 2px 4px;\n border-radius: 4px;\n font-family: monospace;\n}\n.editor-text-subscript { vertical-align: sub; font-size: smaller; }\n.editor-text-superscript { vertical-align: super; font-size: smaller; }\n\n.editor-code {\n background: #1e293b;\n color: #e2e8f0;\n padding: 16px;\n border-radius: 4px;\n font-family: monospace;\n overflow-x: auto;\n margin: 8px 0;\n}\n\n.collab-cursors-container {\n position: relative;\n pointer-events: none;\n}\n\n.cursor-agent {\n animation: cursor-agent-pulse 2s ease-in-out infinite;\n}\n\n@keyframes cursor-agent-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.6; }\n}\n\n.cursor-inactive {\n opacity: 0.2;\n transition: opacity 1s ease-out;\n}\n";
|
|
46
|
+
export declare const richTextEditorStyles = "\n.revealui-rich-text-editor {\n border: 1px solid var(--rvui-border, #e2e8f0);\n border-radius: 8px;\n overflow: hidden;\n font-family: system-ui, -apple-system, sans-serif;\n}\n\n.editor-toolbar {\n display: flex;\n gap: 4px;\n padding: 8px;\n border-bottom: 1px solid var(--rvui-border, #e2e8f0);\n background: var(--rvui-surface-1, #f8fafc);\n}\n\n.editor-toolbar--floating {\n border-bottom: none;\n border: 1px solid var(--rvui-border, #e2e8f0);\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n background: white;\n padding: 6px;\n transition: opacity 0.2s ease;\n}\n\n.toolbar-btn {\n padding: 6px 10px;\n border: 1px solid var(--rvui-border, #e2e8f0);\n border-radius: 4px;\n background: white;\n cursor: pointer;\n font-size: 14px;\n transition: all 0.15s ease;\n}\n\n.toolbar-btn:hover {\n background: var(--rvui-surface-2, #f1f5f9);\n}\n\n.toolbar-btn.active {\n background: var(--rvui-brand, #3b82f6);\n color: white;\n border-color: var(--rvui-brand, #3b82f6);\n}\n\n.editor-container {\n position: relative;\n min-height: 150px;\n}\n\n.editor-input {\n padding: 16px;\n outline: none;\n min-height: 150px;\n}\n\n.editor-placeholder {\n position: absolute;\n top: 16px;\n left: 16px;\n color: var(--rvui-text-2, #94a3b8);\n pointer-events: none;\n user-select: none;\n}\n\n.editor-paragraph {\n margin: 0 0 8px 0;\n}\n\n.editor-heading-h1 { font-size: 2em; font-weight: bold; margin: 0 0 12px 0; }\n.editor-heading-h2 { font-size: 1.5em; font-weight: bold; margin: 0 0 10px 0; }\n.editor-heading-h3 { font-size: 1.25em; font-weight: bold; margin: 0 0 8px 0; }\n.editor-heading-h4 { font-size: 1em; font-weight: bold; margin: 0 0 8px 0; }\n.editor-heading-h5 { font-size: 0.875em; font-weight: bold; margin: 0 0 8px 0; }\n.editor-heading-h6 { font-size: 0.75em; font-weight: bold; margin: 0 0 8px 0; }\n\n.editor-quote {\n border-left: 4px solid var(--rvui-border, #e2e8f0);\n padding-left: 16px;\n margin: 0 0 8px 0;\n color: var(--rvui-text-2, #64748b);\n}\n\n.editor-list-ol { list-style: decimal; padding-left: 24px; margin: 0 0 8px 0; }\n.editor-list-ul { list-style: disc; padding-left: 24px; margin: 0 0 8px 0; }\n.editor-listitem { margin: 4px 0; }\n\n.editor-link { color: var(--rvui-brand, #3b82f6); text-decoration: underline; }\n\n.editor-text-bold { font-weight: bold; }\n.editor-text-italic { font-style: italic; }\n.editor-text-underline { text-decoration: underline; }\n.editor-text-strikethrough { text-decoration: line-through; }\n.editor-text-code {\n background: var(--rvui-surface-2, #f1f5f9);\n padding: 2px 4px;\n border-radius: 4px;\n font-family: monospace;\n}\n.editor-text-subscript { vertical-align: sub; font-size: smaller; }\n.editor-text-superscript { vertical-align: super; font-size: smaller; }\n\n.editor-code {\n background: var(--rvui-text-0, #1e293b);\n color: var(--rvui-border, #e2e8f0);\n padding: 16px;\n border-radius: 4px;\n font-family: monospace;\n overflow-x: auto;\n margin: 8px 0;\n}\n\n.collab-cursors-container {\n position: relative;\n pointer-events: none;\n}\n\n.cursor-agent {\n animation: cursor-agent-pulse 2s ease-in-out infinite;\n}\n\n@keyframes cursor-agent-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.6; }\n}\n\n.cursor-inactive {\n opacity: 0.2;\n transition: opacity 1s ease-out;\n}\n";
|
|
47
47
|
export default RichTextEditor;
|
|
48
48
|
//# sourceMappingURL=RichTextEditor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RichTextEditor.d.ts","sourceRoot":"","sources":["../../../src/client/richtext/RichTextEditor.tsx"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,KAAK,EACV,WAAW,EAEX,aAAa,EAEb,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAEjB,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC/B,OAAO,KAAK,EACV,cAAc,IAAI,oBAAoB,EAEvC,MAAM,yBAAyB,CAAC;AAWjC,MAAM,WAAW,mBAAmB;IAClC,gDAAgD;IAChD,YAAY,CAAC,EAAE,oBAAoB,CAAC;IACpC,8CAA8C;IAC9C,YAAY,CAAC,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,CAAC;IACrD,oCAAoC;IACpC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/D,8CAA8C;IAC9C,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAC3D,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2BAA2B;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0BAA0B;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IACxD,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,oCAAoC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6CAA6C;IAC7C,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,QAAQ,CAAC;IACxE,+DAA+D;IAC/D,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,UAAU,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAC/B,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA+ID,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AA2B3D,wBAAgB,cAAc,CAAC,EAC7B,YAAY,EACZ,YAAY,EACZ,QAAQ,EACR,kBAAkB,EAClB,WAA+B,EAC/B,SAAc,EACd,QAAe,EACf,SAAiB,EACjB,OAAO,EACP,SAA4B,EAC5B,aAAqB,EACrB,UAAU,EACV,eAAe,EACf,eAAuB,EACvB,QAAQ,EACR,WAAW,EACX,UAAU,EACV,UAAU,GACX,EAAE,mBAAmB,+BA+GrB;AAMD,eAAO,MAAM,oBAAoB,
|
|
1
|
+
{"version":3,"file":"RichTextEditor.d.ts","sourceRoot":"","sources":["../../../src/client/richtext/RichTextEditor.tsx"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,KAAK,EACV,WAAW,EAEX,aAAa,EAEb,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAEjB,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC/B,OAAO,KAAK,EACV,cAAc,IAAI,oBAAoB,EAEvC,MAAM,yBAAyB,CAAC;AAWjC,MAAM,WAAW,mBAAmB;IAClC,gDAAgD;IAChD,YAAY,CAAC,EAAE,oBAAoB,CAAC;IACpC,8CAA8C;IAC9C,YAAY,CAAC,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,CAAC;IACrD,oCAAoC;IACpC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/D,8CAA8C;IAC9C,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAC3D,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2BAA2B;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0BAA0B;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IACxD,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,oCAAoC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6CAA6C;IAC7C,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,QAAQ,CAAC;IACxE,+DAA+D;IAC/D,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,UAAU,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAC/B,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA+ID,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AA2B3D,wBAAgB,cAAc,CAAC,EAC7B,YAAY,EACZ,YAAY,EACZ,QAAQ,EACR,kBAAkB,EAClB,WAA+B,EAC/B,SAAc,EACd,QAAe,EACf,SAAiB,EACjB,OAAO,EACP,SAA4B,EAC5B,aAAqB,EACrB,UAAU,EACV,eAAe,EACf,eAAuB,EACvB,QAAQ,EACR,WAAW,EACX,UAAU,EACV,UAAU,GACX,EAAE,mBAAmB,+BA+GrB;AAMD,eAAO,MAAM,oBAAoB,4xGAmIhC,CAAC;AAEF,eAAe,cAAc,CAAC"}
|
|
@@ -227,7 +227,7 @@ export function RichTextEditor({ editorConfig, initialValue, onChange, onSeriali
|
|
|
227
227
|
// ============================================
|
|
228
228
|
export const richTextEditorStyles = `
|
|
229
229
|
.revealui-rich-text-editor {
|
|
230
|
-
border: 1px solid #e2e8f0;
|
|
230
|
+
border: 1px solid var(--rvui-border, #e2e8f0);
|
|
231
231
|
border-radius: 8px;
|
|
232
232
|
overflow: hidden;
|
|
233
233
|
font-family: system-ui, -apple-system, sans-serif;
|
|
@@ -237,13 +237,13 @@ export const richTextEditorStyles = `
|
|
|
237
237
|
display: flex;
|
|
238
238
|
gap: 4px;
|
|
239
239
|
padding: 8px;
|
|
240
|
-
border-bottom: 1px solid #e2e8f0;
|
|
241
|
-
background: #f8fafc;
|
|
240
|
+
border-bottom: 1px solid var(--rvui-border, #e2e8f0);
|
|
241
|
+
background: var(--rvui-surface-1, #f8fafc);
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
.editor-toolbar--floating {
|
|
245
245
|
border-bottom: none;
|
|
246
|
-
border: 1px solid #e2e8f0;
|
|
246
|
+
border: 1px solid var(--rvui-border, #e2e8f0);
|
|
247
247
|
border-radius: 8px;
|
|
248
248
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
249
249
|
background: white;
|
|
@@ -253,7 +253,7 @@ export const richTextEditorStyles = `
|
|
|
253
253
|
|
|
254
254
|
.toolbar-btn {
|
|
255
255
|
padding: 6px 10px;
|
|
256
|
-
border: 1px solid #e2e8f0;
|
|
256
|
+
border: 1px solid var(--rvui-border, #e2e8f0);
|
|
257
257
|
border-radius: 4px;
|
|
258
258
|
background: white;
|
|
259
259
|
cursor: pointer;
|
|
@@ -262,13 +262,13 @@ export const richTextEditorStyles = `
|
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
.toolbar-btn:hover {
|
|
265
|
-
background: #f1f5f9;
|
|
265
|
+
background: var(--rvui-surface-2, #f1f5f9);
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
.toolbar-btn.active {
|
|
269
|
-
background: #3b82f6;
|
|
269
|
+
background: var(--rvui-brand, #3b82f6);
|
|
270
270
|
color: white;
|
|
271
|
-
border-color: #3b82f6;
|
|
271
|
+
border-color: var(--rvui-brand, #3b82f6);
|
|
272
272
|
}
|
|
273
273
|
|
|
274
274
|
.editor-container {
|
|
@@ -286,7 +286,7 @@ export const richTextEditorStyles = `
|
|
|
286
286
|
position: absolute;
|
|
287
287
|
top: 16px;
|
|
288
288
|
left: 16px;
|
|
289
|
-
color: #94a3b8;
|
|
289
|
+
color: var(--rvui-text-2, #94a3b8);
|
|
290
290
|
pointer-events: none;
|
|
291
291
|
user-select: none;
|
|
292
292
|
}
|
|
@@ -303,24 +303,24 @@ export const richTextEditorStyles = `
|
|
|
303
303
|
.editor-heading-h6 { font-size: 0.75em; font-weight: bold; margin: 0 0 8px 0; }
|
|
304
304
|
|
|
305
305
|
.editor-quote {
|
|
306
|
-
border-left: 4px solid #e2e8f0;
|
|
306
|
+
border-left: 4px solid var(--rvui-border, #e2e8f0);
|
|
307
307
|
padding-left: 16px;
|
|
308
308
|
margin: 0 0 8px 0;
|
|
309
|
-
color: #64748b;
|
|
309
|
+
color: var(--rvui-text-2, #64748b);
|
|
310
310
|
}
|
|
311
311
|
|
|
312
312
|
.editor-list-ol { list-style: decimal; padding-left: 24px; margin: 0 0 8px 0; }
|
|
313
313
|
.editor-list-ul { list-style: disc; padding-left: 24px; margin: 0 0 8px 0; }
|
|
314
314
|
.editor-listitem { margin: 4px 0; }
|
|
315
315
|
|
|
316
|
-
.editor-link { color: #3b82f6; text-decoration: underline; }
|
|
316
|
+
.editor-link { color: var(--rvui-brand, #3b82f6); text-decoration: underline; }
|
|
317
317
|
|
|
318
318
|
.editor-text-bold { font-weight: bold; }
|
|
319
319
|
.editor-text-italic { font-style: italic; }
|
|
320
320
|
.editor-text-underline { text-decoration: underline; }
|
|
321
321
|
.editor-text-strikethrough { text-decoration: line-through; }
|
|
322
322
|
.editor-text-code {
|
|
323
|
-
background: #f1f5f9;
|
|
323
|
+
background: var(--rvui-surface-2, #f1f5f9);
|
|
324
324
|
padding: 2px 4px;
|
|
325
325
|
border-radius: 4px;
|
|
326
326
|
font-family: monospace;
|
|
@@ -329,8 +329,8 @@ export const richTextEditorStyles = `
|
|
|
329
329
|
.editor-text-superscript { vertical-align: super; font-size: smaller; }
|
|
330
330
|
|
|
331
331
|
.editor-code {
|
|
332
|
-
background: #1e293b;
|
|
333
|
-
color: #e2e8f0;
|
|
332
|
+
background: var(--rvui-text-0, #1e293b);
|
|
333
|
+
color: var(--rvui-border, #e2e8f0);
|
|
334
334
|
padding: 16px;
|
|
335
335
|
border-radius: 4px;
|
|
336
336
|
font-family: monospace;
|
|
@@ -64,14 +64,14 @@ export const ImageNodeComponent = (props) => {
|
|
|
64
64
|
}, children: [_jsx("button", { type: "button", onClick: handleEdit, className: "editor-image-btn", style: {
|
|
65
65
|
padding: '4px 8px',
|
|
66
66
|
background: 'white',
|
|
67
|
-
border:
|
|
67
|
+
border: `1px solid var(--rvui-border, #e2e8f0)`,
|
|
68
68
|
borderRadius: '4px',
|
|
69
69
|
cursor: 'pointer',
|
|
70
70
|
fontSize: '12px',
|
|
71
71
|
}, title: "Edit Image", "aria-label": "Edit Image", children: "\u270E" }), _jsx("button", { type: "button", onClick: removeImage, className: "editor-image-btn", style: {
|
|
72
72
|
padding: '4px 8px',
|
|
73
73
|
background: 'white',
|
|
74
|
-
border:
|
|
74
|
+
border: `1px solid var(--rvui-border, #e2e8f0)`,
|
|
75
75
|
borderRadius: '4px',
|
|
76
76
|
cursor: 'pointer',
|
|
77
77
|
fontSize: '12px',
|
|
@@ -87,12 +87,12 @@ export const ImageNodeComponent = (props) => {
|
|
|
87
87
|
width: '100%',
|
|
88
88
|
marginTop: '8px',
|
|
89
89
|
padding: '4px 8px',
|
|
90
|
-
border:
|
|
90
|
+
border: `1px solid var(--rvui-border, #e2e8f0)`,
|
|
91
91
|
borderRadius: '4px',
|
|
92
92
|
} })) : (data.caption && (_jsx("figcaption", { style: {
|
|
93
93
|
marginTop: '8px',
|
|
94
94
|
fontSize: '14px',
|
|
95
|
-
color: '#64748b',
|
|
95
|
+
color: 'var(--rvui-text-2, #64748b)',
|
|
96
96
|
fontStyle: 'italic',
|
|
97
97
|
}, children: data.caption })))] }));
|
|
98
98
|
};
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* hooks, and query building.
|
|
6
6
|
*/
|
|
7
7
|
import type { BatchCreateOptions, BatchDeleteOptions, BatchResult, BatchUpdateOptions, CollectionStorageDescriptor, PopulateType, QueryableDatabaseAdapter, RevealCollectionConfig, RevealCreateOptions, RevealDeleteOptions, RevealDocument, RevealFindOptions, RevealPaginatedResult, RevealRequest, RevealUpdateOptions } from '../types/index.js';
|
|
8
|
+
import { type RestoreOptions, type SnapshotOptions } from './operations/snapshot.js';
|
|
8
9
|
export declare class RevealUICollection {
|
|
9
10
|
config: RevealCollectionConfig;
|
|
10
11
|
storage: CollectionStorageDescriptor | null;
|
|
@@ -16,6 +17,8 @@ export declare class RevealUICollection {
|
|
|
16
17
|
depth?: number;
|
|
17
18
|
req?: RevealRequest;
|
|
18
19
|
populate?: PopulateType;
|
|
20
|
+
overrideAccess?: boolean;
|
|
21
|
+
draft?: boolean;
|
|
19
22
|
}): Promise<RevealDocument | null>;
|
|
20
23
|
create(options: RevealCreateOptions): Promise<RevealDocument>;
|
|
21
24
|
update(options: RevealUpdateOptions): Promise<RevealDocument>;
|
|
@@ -23,5 +26,7 @@ export declare class RevealUICollection {
|
|
|
23
26
|
createMany(options: BatchCreateOptions): Promise<BatchResult<RevealDocument>>;
|
|
24
27
|
updateMany(options: BatchUpdateOptions): Promise<BatchResult<RevealDocument>>;
|
|
25
28
|
deleteMany(options: BatchDeleteOptions): Promise<BatchResult<RevealDocument>>;
|
|
29
|
+
snapshot(options: SnapshotOptions): Promise<RevealDocument>;
|
|
30
|
+
restore(options: RestoreOptions): Promise<RevealDocument>;
|
|
26
31
|
}
|
|
27
32
|
//# sourceMappingURL=CollectionOperations.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CollectionOperations.d.ts","sourceRoot":"","sources":["../../src/collections/CollectionOperations.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,EACX,kBAAkB,EAClB,2BAA2B,EAC3B,YAAY,EACZ,wBAAwB,EACxB,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,EACb,mBAAmB,EACpB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"CollectionOperations.d.ts","sourceRoot":"","sources":["../../src/collections/CollectionOperations.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,EACX,kBAAkB,EAClB,2BAA2B,EAC3B,YAAY,EACZ,wBAAwB,EACxB,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,EACb,mBAAmB,EACpB,MAAM,mBAAmB,CAAC;AAO3B,OAAO,EAEL,KAAK,cAAc,EAEnB,KAAK,eAAe,EACrB,MAAM,0BAA0B,CAAC;AAIlC,qBAAa,kBAAkB;IAC7B,MAAM,EAAE,sBAAsB,CAAC;IAC/B,OAAO,EAAE,2BAA2B,GAAG,IAAI,CAAC;IAC5C,EAAE,EAAE,wBAAwB,GAAG,IAAI,CAAC;gBAGlC,MAAM,EAAE,sBAAsB,EAC9B,EAAE,EAAE,wBAAwB,GAAG,IAAI,EACnC,OAAO,GAAE,2BAA2B,GAAG,IAAW;IAO9C,IAAI,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAIhE,QAAQ,CAAC,OAAO,EAAE;QACtB,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;QACpB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,GAAG,CAAC,EAAE,aAAa,CAAC;QACpB,QAAQ,CAAC,EAAE,YAAY,CAAC;QACxB,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAI5B,MAAM,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,CAAC;IAI7D,MAAM,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,CAAC;IAI7D,MAAM,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,CAAC;IAI7D,UAAU,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;IAI7E,UAAU,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;IAI7E,UAAU,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;IAI7E,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC;IAI3D,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;CAGhE"}
|
|
@@ -10,6 +10,7 @@ import { deleteDocument } from './operations/delete.js';
|
|
|
10
10
|
import { deleteMany } from './operations/deleteMany.js';
|
|
11
11
|
import { find } from './operations/find.js';
|
|
12
12
|
import { findByID } from './operations/findById.js';
|
|
13
|
+
import { createSnapshot, restoreSnapshot, } from './operations/snapshot.js';
|
|
13
14
|
import { update } from './operations/update.js';
|
|
14
15
|
import { updateMany } from './operations/updateMany.js';
|
|
15
16
|
export class RevealUICollection {
|
|
@@ -45,4 +46,10 @@ export class RevealUICollection {
|
|
|
45
46
|
async deleteMany(options) {
|
|
46
47
|
return deleteMany(this.config, this.db, options);
|
|
47
48
|
}
|
|
49
|
+
async snapshot(options) {
|
|
50
|
+
return createSnapshot(this.config, this.db, options);
|
|
51
|
+
}
|
|
52
|
+
async restore(options) {
|
|
53
|
+
return restoreSnapshot(this.config, this.db, options);
|
|
54
|
+
}
|
|
48
55
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/collections/operations/create.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EACV,wBAAwB,EACxB,sBAAsB,EACtB,mBAAmB,EACnB,cAAc,EAGf,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/collections/operations/create.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EACV,wBAAwB,EACxB,sBAAsB,EACtB,mBAAmB,EACnB,cAAc,EAGf,MAAM,sBAAsB,CAAC;AAyC9B,wBAAsB,MAAM,CAC1B,MAAM,EAAE,sBAAsB,EAC9B,EAAE,EAAE,wBAAwB,GAAG,IAAI,EACnC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,cAAc,CAAC,CAoKzB"}
|
|
@@ -8,6 +8,7 @@ import bcrypt from 'bcryptjs';
|
|
|
8
8
|
import { collectJsonFields, serializeValueForDatabase } from '../../utils/json-parsing.js';
|
|
9
9
|
import { flattenFields, isJsonFieldType } from '../../utils/type-guards.js';
|
|
10
10
|
import { runBeforeFieldHooks } from './fieldHooks.js';
|
|
11
|
+
import { runFieldValidators } from './fieldValidation.js';
|
|
11
12
|
import { findByID } from './findById.js';
|
|
12
13
|
import { insertDocumentQuery } from './sqlAdapter.js';
|
|
13
14
|
/**
|
|
@@ -72,6 +73,11 @@ export async function create(config, db, options) {
|
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
75
|
}
|
|
76
|
+
// Run field-level `validate` predicates after the built-in required/email
|
|
77
|
+
// checks and before beforeChange hooks (Payload's beforeValidate → validate →
|
|
78
|
+
// beforeChange ordering). Throws a ValidationError (HTTP 400) on the first
|
|
79
|
+
// failing field.
|
|
80
|
+
await runFieldValidators(config, data, 'create', options.req);
|
|
75
81
|
// Run beforeChange field hooks after validation but before the DB write.
|
|
76
82
|
await runBeforeFieldHooks(config, data, 'create', 'beforeChange', undefined, options.req);
|
|
77
83
|
// Hash password if present and not already hashed (doesn't start with $2a$ or $2b$)
|
|
@@ -79,6 +85,20 @@ export async function create(config, db, options) {
|
|
|
79
85
|
const saltRounds = 12;
|
|
80
86
|
data.password = await bcrypt.hash(data.password, saltRounds);
|
|
81
87
|
}
|
|
88
|
+
// Typed-storage write seam: a registered collectionStorage adapter (e.g. the
|
|
89
|
+
// typed Drizzle bridge) may own this collection's writes. It receives the
|
|
90
|
+
// fully hook-processed `data` and returns the created document, or `undefined`
|
|
91
|
+
// to defer to the dynamic-SQL path below — same not-handled contract as the
|
|
92
|
+
// read seam in findById.ts / find.ts.
|
|
93
|
+
if (db?.collectionStorage?.create) {
|
|
94
|
+
const doc = await db.collectionStorage.create(config, {
|
|
95
|
+
data,
|
|
96
|
+
...(options.req ? { req: options.req } : {}),
|
|
97
|
+
});
|
|
98
|
+
if (doc !== undefined) {
|
|
99
|
+
return doc;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
82
102
|
if (db?.query) {
|
|
83
103
|
// Dynamic collection storage is quarantined in sqlAdapter.ts until this
|
|
84
104
|
// layer is redesigned around typed tables that Drizzle can model directly.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../../src/collections/operations/delete.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,wBAAwB,EACxB,sBAAsB,EACtB,mBAAmB,EACnB,cAAc,EAGf,MAAM,sBAAsB,CAAC;AAsC9B,wBAAsB,cAAc,CAClC,MAAM,EAAE,sBAAsB,EAC9B,EAAE,EAAE,wBAAwB,GAAG,IAAI,EACnC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,cAAc,CAAC,
|
|
1
|
+
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../../src/collections/operations/delete.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,wBAAwB,EACxB,sBAAsB,EACtB,mBAAmB,EACnB,cAAc,EAGf,MAAM,sBAAsB,CAAC;AAsC9B,wBAAsB,cAAc,CAClC,MAAM,EAAE,sBAAsB,EAC9B,EAAE,EAAE,wBAAwB,GAAG,IAAI,EACnC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,cAAc,CAAC,CAiDzB"}
|
|
@@ -49,6 +49,20 @@ export async function deleteDocument(config, db, options) {
|
|
|
49
49
|
throw new Error('Access denied: insufficient permissions to delete this document');
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
+
// Typed-storage write seam: a registered collectionStorage adapter may own
|
|
53
|
+
// this collection's deletes (e.g. soft-delete semantics on a typed table). It
|
|
54
|
+
// returns the deleted document, throws for not-found, or returns `undefined`
|
|
55
|
+
// to defer to the dynamic-SQL path below (same not-handled contract as the
|
|
56
|
+
// read seam).
|
|
57
|
+
if (db?.collectionStorage?.delete) {
|
|
58
|
+
const result = await db.collectionStorage.delete(config, {
|
|
59
|
+
id,
|
|
60
|
+
...(options.req ? { req: options.req } : {}),
|
|
61
|
+
});
|
|
62
|
+
if (result !== undefined) {
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
52
66
|
if (db?.query) {
|
|
53
67
|
// Dynamic collection storage is quarantined in sqlAdapter.ts until this
|
|
54
68
|
// layer is redesigned around typed tables that Drizzle can model directly.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Draft read semantics
|
|
3
|
+
*
|
|
4
|
+
* A collection opts into drafts via `versions.drafts` (see the admin
|
|
5
|
+
* `VersionsConfigSchema`). When drafts are enabled, a document's `_status`
|
|
6
|
+
* column distinguishes `'draft'` from `'published'` state in a single row.
|
|
7
|
+
*
|
|
8
|
+
* These helpers centralize the ONE rule that keeps unpublished content from
|
|
9
|
+
* leaking to readers who should not see it: a read of a drafts-enabled
|
|
10
|
+
* collection is published-only by default, and `draft=true` relaxes ONLY that
|
|
11
|
+
* default. It never touches the `access.read` clause, which find()/findByID()
|
|
12
|
+
* AND-merge separately — so a caller scoped to published content by
|
|
13
|
+
* `access.read` stays published-only even when it asks for `draft=true`.
|
|
14
|
+
*/
|
|
15
|
+
import type { RevealCollectionConfig, RevealWhere } from '../../types/index.js';
|
|
16
|
+
/**
|
|
17
|
+
* True when the collection enables drafts via `versions.drafts`.
|
|
18
|
+
*
|
|
19
|
+
* `versions: true` (version history without drafts) does NOT enable drafts —
|
|
20
|
+
* there is no `_status` lifecycle to gate, so reads are not filtered.
|
|
21
|
+
*/
|
|
22
|
+
export declare function collectionHasDrafts(config: RevealCollectionConfig): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* The additional published-only read filter for a drafts-enabled collection
|
|
25
|
+
* when draft mode is NOT requested. Returns `undefined` when no filter applies
|
|
26
|
+
* (collection has no drafts, or `draft=true` was passed).
|
|
27
|
+
*
|
|
28
|
+
* SECURITY: this is layered ON TOP of `access.read`, never a replacement.
|
|
29
|
+
* `draft=true` removes only this filter; the access clause is AND-merged by the
|
|
30
|
+
* caller and always wins.
|
|
31
|
+
*/
|
|
32
|
+
export declare function publishedOnlyReadFilter(config: RevealCollectionConfig, draft: boolean): RevealWhere | undefined;
|
|
33
|
+
//# sourceMappingURL=drafts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drafts.d.ts","sourceRoot":"","sources":["../../../src/collections/operations/drafts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEhF;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAK3E;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,sBAAsB,EAC9B,KAAK,EAAE,OAAO,GACb,WAAW,GAAG,SAAS,CAIzB"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Draft read semantics
|
|
3
|
+
*
|
|
4
|
+
* A collection opts into drafts via `versions.drafts` (see the admin
|
|
5
|
+
* `VersionsConfigSchema`). When drafts are enabled, a document's `_status`
|
|
6
|
+
* column distinguishes `'draft'` from `'published'` state in a single row.
|
|
7
|
+
*
|
|
8
|
+
* These helpers centralize the ONE rule that keeps unpublished content from
|
|
9
|
+
* leaking to readers who should not see it: a read of a drafts-enabled
|
|
10
|
+
* collection is published-only by default, and `draft=true` relaxes ONLY that
|
|
11
|
+
* default. It never touches the `access.read` clause, which find()/findByID()
|
|
12
|
+
* AND-merge separately — so a caller scoped to published content by
|
|
13
|
+
* `access.read` stays published-only even when it asks for `draft=true`.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* True when the collection enables drafts via `versions.drafts`.
|
|
17
|
+
*
|
|
18
|
+
* `versions: true` (version history without drafts) does NOT enable drafts —
|
|
19
|
+
* there is no `_status` lifecycle to gate, so reads are not filtered.
|
|
20
|
+
*/
|
|
21
|
+
export function collectionHasDrafts(config) {
|
|
22
|
+
const versions = config.versions;
|
|
23
|
+
if (!versions || typeof versions !== 'object')
|
|
24
|
+
return false;
|
|
25
|
+
const drafts = versions.drafts;
|
|
26
|
+
return drafts === true || (typeof drafts === 'object' && drafts !== null);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The additional published-only read filter for a drafts-enabled collection
|
|
30
|
+
* when draft mode is NOT requested. Returns `undefined` when no filter applies
|
|
31
|
+
* (collection has no drafts, or `draft=true` was passed).
|
|
32
|
+
*
|
|
33
|
+
* SECURITY: this is layered ON TOP of `access.read`, never a replacement.
|
|
34
|
+
* `draft=true` removes only this filter; the access clause is AND-merged by the
|
|
35
|
+
* caller and always wins.
|
|
36
|
+
*/
|
|
37
|
+
export function publishedOnlyReadFilter(config, draft) {
|
|
38
|
+
if (draft)
|
|
39
|
+
return undefined;
|
|
40
|
+
if (!collectionHasDrafts(config))
|
|
41
|
+
return undefined;
|
|
42
|
+
return { _status: { equals: 'published' } };
|
|
43
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field Validation Execution
|
|
3
|
+
*
|
|
4
|
+
* Runs field-level `validate` predicates during create/update operations.
|
|
5
|
+
* Mirrors the fieldHooks.ts execution pattern: a single flattenFields traversal
|
|
6
|
+
* over the collection's fields.
|
|
7
|
+
*
|
|
8
|
+
* A field's `validate(value, { value, data, siblingData, operation, req })`
|
|
9
|
+
* returns `true` when the value is valid, or a non-empty string error message
|
|
10
|
+
* when it is not. On the first failure the engine throws a ValidationError
|
|
11
|
+
* (HTTP 400) carrying that message, so the write is rejected at ingress.
|
|
12
|
+
*
|
|
13
|
+
* Enforcement note: this is one of the engine's ingress checks and runs for
|
|
14
|
+
* EVERY collection written through create()/update(). For public page/post
|
|
15
|
+
* rich-text content it COMPLEMENTS — does not replace — the contracts walker
|
|
16
|
+
* (validateContent/validateBlocks, wired into the pages/posts write routes) and
|
|
17
|
+
* render-side sanitizeUrl. See docs/specs/2026-07-02-cms-render-path-hardening.md.
|
|
18
|
+
*/
|
|
19
|
+
import type { RevealCollectionConfig, RevealRequest } from '../../types/index.js';
|
|
20
|
+
/**
|
|
21
|
+
* Thrown when a field's `validate` predicate rejects a value.
|
|
22
|
+
*
|
|
23
|
+
* Carries `status`/`statusCode` 400 so transports surface it as a client error
|
|
24
|
+
* with the validator's message intact (core `rest.ts` reads `status`; the
|
|
25
|
+
* server error middleware can branch on `name === 'ValidationError'`), rather
|
|
26
|
+
* than the generic 500 that a bare Error would produce.
|
|
27
|
+
*/
|
|
28
|
+
export declare class ValidationError extends Error {
|
|
29
|
+
readonly status = 400;
|
|
30
|
+
readonly statusCode = 400;
|
|
31
|
+
readonly field?: string;
|
|
32
|
+
constructor(message: string, field?: string);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Executes every field's `validate` predicate for the fields present in `data`.
|
|
36
|
+
*
|
|
37
|
+
* Called once per write, after the required-field / email-format checks and
|
|
38
|
+
* before the beforeChange field hooks — the same ordering Payload uses
|
|
39
|
+
* (beforeValidate hooks → validate → beforeChange hooks).
|
|
40
|
+
*
|
|
41
|
+
* A field is validated only when its name is present in `data`. This matches the
|
|
42
|
+
* engine's existing email check (which gates on `field.name in data`) and is
|
|
43
|
+
* correct for partial updates, where absent keys are untouched. Required-but-
|
|
44
|
+
* absent fields are still caught by the separate required-field check in the
|
|
45
|
+
* create operation.
|
|
46
|
+
*
|
|
47
|
+
* `flattenFields` recurses through `tabs`/`row` containers, whose child fields
|
|
48
|
+
* remain top-level keys in `data`, so `data[field.name]` and `siblingData: data`
|
|
49
|
+
* resolve correctly. `group`/`array`/`blocks` are validated as a single value
|
|
50
|
+
* (the whole sub-object), matching how the engine treats those JSON fields.
|
|
51
|
+
*/
|
|
52
|
+
export declare function runFieldValidators(config: RevealCollectionConfig, data: Record<string, unknown>, operation: 'create' | 'update', req?: RevealRequest): Promise<void>;
|
|
53
|
+
//# sourceMappingURL=fieldValidation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fieldValidation.d.ts","sourceRoot":"","sources":["../../../src/collections/operations/fieldValidation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGlF;;;;;;;GAOG;AACH,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,MAAM,OAAO;IACtB,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;gBAEZ,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;CAK5C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,sBAAsB,EAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,SAAS,EAAE,QAAQ,GAAG,QAAQ,EAC9B,GAAG,CAAC,EAAE,aAAa,GAClB,OAAO,CAAC,IAAI,CAAC,CAoCf"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field Validation Execution
|
|
3
|
+
*
|
|
4
|
+
* Runs field-level `validate` predicates during create/update operations.
|
|
5
|
+
* Mirrors the fieldHooks.ts execution pattern: a single flattenFields traversal
|
|
6
|
+
* over the collection's fields.
|
|
7
|
+
*
|
|
8
|
+
* A field's `validate(value, { value, data, siblingData, operation, req })`
|
|
9
|
+
* returns `true` when the value is valid, or a non-empty string error message
|
|
10
|
+
* when it is not. On the first failure the engine throws a ValidationError
|
|
11
|
+
* (HTTP 400) carrying that message, so the write is rejected at ingress.
|
|
12
|
+
*
|
|
13
|
+
* Enforcement note: this is one of the engine's ingress checks and runs for
|
|
14
|
+
* EVERY collection written through create()/update(). For public page/post
|
|
15
|
+
* rich-text content it COMPLEMENTS — does not replace — the contracts walker
|
|
16
|
+
* (validateContent/validateBlocks, wired into the pages/posts write routes) and
|
|
17
|
+
* render-side sanitizeUrl. See docs/specs/2026-07-02-cms-render-path-hardening.md.
|
|
18
|
+
*/
|
|
19
|
+
import { flattenFields } from '../../utils/type-guards.js';
|
|
20
|
+
/**
|
|
21
|
+
* Thrown when a field's `validate` predicate rejects a value.
|
|
22
|
+
*
|
|
23
|
+
* Carries `status`/`statusCode` 400 so transports surface it as a client error
|
|
24
|
+
* with the validator's message intact (core `rest.ts` reads `status`; the
|
|
25
|
+
* server error middleware can branch on `name === 'ValidationError'`), rather
|
|
26
|
+
* than the generic 500 that a bare Error would produce.
|
|
27
|
+
*/
|
|
28
|
+
export class ValidationError extends Error {
|
|
29
|
+
status = 400;
|
|
30
|
+
statusCode = 400;
|
|
31
|
+
field;
|
|
32
|
+
constructor(message, field) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = 'ValidationError';
|
|
35
|
+
this.field = field;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Executes every field's `validate` predicate for the fields present in `data`.
|
|
40
|
+
*
|
|
41
|
+
* Called once per write, after the required-field / email-format checks and
|
|
42
|
+
* before the beforeChange field hooks — the same ordering Payload uses
|
|
43
|
+
* (beforeValidate hooks → validate → beforeChange hooks).
|
|
44
|
+
*
|
|
45
|
+
* A field is validated only when its name is present in `data`. This matches the
|
|
46
|
+
* engine's existing email check (which gates on `field.name in data`) and is
|
|
47
|
+
* correct for partial updates, where absent keys are untouched. Required-but-
|
|
48
|
+
* absent fields are still caught by the separate required-field check in the
|
|
49
|
+
* create operation.
|
|
50
|
+
*
|
|
51
|
+
* `flattenFields` recurses through `tabs`/`row` containers, whose child fields
|
|
52
|
+
* remain top-level keys in `data`, so `data[field.name]` and `siblingData: data`
|
|
53
|
+
* resolve correctly. `group`/`array`/`blocks` are validated as a single value
|
|
54
|
+
* (the whole sub-object), matching how the engine treats those JSON fields.
|
|
55
|
+
*/
|
|
56
|
+
export async function runFieldValidators(config, data, operation, req) {
|
|
57
|
+
const fields = flattenFields(config.fields || []);
|
|
58
|
+
for (const field of fields) {
|
|
59
|
+
if (!field.name)
|
|
60
|
+
continue;
|
|
61
|
+
const validate = field.validate;
|
|
62
|
+
if (typeof validate !== 'function')
|
|
63
|
+
continue;
|
|
64
|
+
// Only validate fields carried by this write payload.
|
|
65
|
+
if (!(field.name in data))
|
|
66
|
+
continue;
|
|
67
|
+
const value = data[field.name];
|
|
68
|
+
// `req` is required by FieldValidateArgs; ingress writes always carry one.
|
|
69
|
+
// Bootstrap/test paths may not — pass it through and let validators that
|
|
70
|
+
// depend on `req` fail loudly rather than silently skipping validation.
|
|
71
|
+
// Cast the args object to the predicate's own parameter type: core's
|
|
72
|
+
// RevealRequest and the contracts FieldValidateArgs.req generic differ only
|
|
73
|
+
// in their default user parameter, so the shape is identical.
|
|
74
|
+
const args = {
|
|
75
|
+
value,
|
|
76
|
+
data,
|
|
77
|
+
siblingData: data,
|
|
78
|
+
operation,
|
|
79
|
+
req,
|
|
80
|
+
};
|
|
81
|
+
const result = await validate(value, args);
|
|
82
|
+
if (result !== true) {
|
|
83
|
+
const message = typeof result === 'string' && result.length > 0
|
|
84
|
+
? result
|
|
85
|
+
: `Field '${field.name}' failed validation`;
|
|
86
|
+
throw new ValidationError(message, field.name);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"find.d.ts","sourceRoot":"","sources":["../../../src/collections/operations/find.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAEV,wBAAwB,EACxB,sBAAsB,EACtB,iBAAiB,EACjB,qBAAqB,EAItB,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"find.d.ts","sourceRoot":"","sources":["../../../src/collections/operations/find.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAEV,wBAAwB,EACxB,sBAAsB,EACtB,iBAAiB,EACjB,qBAAqB,EAItB,MAAM,sBAAsB,CAAC;AAkC9B,wBAAsB,IAAI,CACxB,MAAM,EAAE,sBAAsB,EAC9B,EAAE,EAAE,wBAAwB,GAAG,IAAI,EACnC,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,qBAAqB,CAAC,CAuQhC"}
|