@vexcms/core 0.0.1 → 0.0.3
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 +155 -0
- package/dist/index.d.ts +77 -33
- package/dist/index.js +85 -13
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/README.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# @vexcms/core
|
|
2
|
+
|
|
3
|
+
The foundational package for [VEX CMS](https://github.com/vexcms) — a headless content management system built for [Convex](https://convex.dev).
|
|
4
|
+
|
|
5
|
+
`@vexcms/core` provides the configuration API, field type system, schema generation, type generation, and all core utilities that power the VEX CMS ecosystem. It has no direct Convex dependency — Convex is a peer dependency only.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @vexcms/core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
### Configuration API
|
|
16
|
+
|
|
17
|
+
Define your CMS structure with a type-safe, declarative API:
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { defineConfig, defineCollection, text, richtext, select } from "@vexcms/core"
|
|
21
|
+
|
|
22
|
+
const posts = defineCollection({
|
|
23
|
+
slug: "posts",
|
|
24
|
+
labels: { singular: "Post", plural: "Posts" },
|
|
25
|
+
fields: {
|
|
26
|
+
title: text({ label: "Title", required: true }),
|
|
27
|
+
content: richtext({ label: "Content" }),
|
|
28
|
+
status: select({
|
|
29
|
+
label: "Status",
|
|
30
|
+
options: [
|
|
31
|
+
{ label: "Draft", value: "draft" },
|
|
32
|
+
{ label: "Published", value: "published" },
|
|
33
|
+
],
|
|
34
|
+
defaultValue: "draft",
|
|
35
|
+
}),
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
export default defineConfig({
|
|
40
|
+
collections: [posts],
|
|
41
|
+
admin: { user: "user" },
|
|
42
|
+
basePath: "/admin",
|
|
43
|
+
})
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Field Types
|
|
47
|
+
|
|
48
|
+
13 built-in field types with full TypeScript inference:
|
|
49
|
+
|
|
50
|
+
| Field | Description |
|
|
51
|
+
|-------|-------------|
|
|
52
|
+
| `text` | String with optional min/max length |
|
|
53
|
+
| `number` | Numeric with optional min/max/step |
|
|
54
|
+
| `checkbox` | Boolean toggle |
|
|
55
|
+
| `select` | Single or multi-value enum with options |
|
|
56
|
+
| `date` | Date stored as epoch milliseconds |
|
|
57
|
+
| `imageUrl` | URL string for images |
|
|
58
|
+
| `relationship` | Reference to another collection (single or hasMany) |
|
|
59
|
+
| `upload` | Reference to media collection documents (single or hasMany) |
|
|
60
|
+
| `json` | Arbitrary JSON data |
|
|
61
|
+
| `array` | Wraps any field type in an array |
|
|
62
|
+
| `richtext` | Plate/Slate JSON editor documents |
|
|
63
|
+
| `ui` | Non-persisted custom render components |
|
|
64
|
+
| `blocks` | Ordered array of block instances (discriminated union) |
|
|
65
|
+
|
|
66
|
+
### Blocks System
|
|
67
|
+
|
|
68
|
+
Define reusable content blocks for flexible page building:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { defineBlock, text, richtext } from "@vexcms/core"
|
|
72
|
+
|
|
73
|
+
const heroBlock = defineBlock({
|
|
74
|
+
slug: "hero",
|
|
75
|
+
label: "Hero Section",
|
|
76
|
+
fields: {
|
|
77
|
+
heading: text({ label: "Heading", required: true }),
|
|
78
|
+
body: richtext({ label: "Body" }),
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Collections, Globals & Media
|
|
84
|
+
|
|
85
|
+
- **Collections** — Content types with typed fields, versioning/draft workflow, database indexes, search indexes, and admin UI configuration
|
|
86
|
+
- **Globals** — Singleton settings (site config, navigation, etc.) with the same field system
|
|
87
|
+
- **Media Collections** — File storage with auto-injected fields (storageId, filename, mimeType, size, url, alt, width, height)
|
|
88
|
+
|
|
89
|
+
### Schema & Type Generation
|
|
90
|
+
|
|
91
|
+
Generates Convex schema and TypeScript types from your config:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { generateVexSchema, generateVexTypes } from "@vexcms/core"
|
|
95
|
+
|
|
96
|
+
const schemaSource = generateVexSchema(config) // → vex.schema.ts
|
|
97
|
+
const typesSource = generateVexTypes(config) // → vex.types.ts
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Versioning & Drafts
|
|
101
|
+
|
|
102
|
+
Per-collection draft/publish workflow with autosave:
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
defineCollection({
|
|
106
|
+
slug: "posts",
|
|
107
|
+
versions: {
|
|
108
|
+
drafts: true,
|
|
109
|
+
autosave: { interval: 2000 },
|
|
110
|
+
maxPerDoc: 100,
|
|
111
|
+
},
|
|
112
|
+
// ...
|
|
113
|
+
})
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Access Control (RBAC)
|
|
117
|
+
|
|
118
|
+
Field-level and collection-level permissions:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { defineAccess } from "@vexcms/core"
|
|
122
|
+
|
|
123
|
+
const access = defineAccess({
|
|
124
|
+
posts: {
|
|
125
|
+
read: true,
|
|
126
|
+
update: { mode: "allow", fields: ["title", "content"] },
|
|
127
|
+
delete: false,
|
|
128
|
+
},
|
|
129
|
+
})
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Auto-Migration
|
|
133
|
+
|
|
134
|
+
Schema diffing and migration planning for safe schema changes:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { diffSchema, planMigration } from "@vexcms/core"
|
|
138
|
+
|
|
139
|
+
const diff = diffSchema(oldSchema, newSchema)
|
|
140
|
+
const ops = planMigration(config, oldSchema, newSchema)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Live Preview
|
|
144
|
+
|
|
145
|
+
Per-collection iframe preview with responsive breakpoints and snapshot system.
|
|
146
|
+
|
|
147
|
+
### Convex Integration Utilities
|
|
148
|
+
|
|
149
|
+
Generic document CRUD operations, query helpers with draft support, and preview snapshot management — all framework-agnostic.
|
|
150
|
+
|
|
151
|
+
## Peer Dependencies
|
|
152
|
+
|
|
153
|
+
- `convex` — Convex backend
|
|
154
|
+
- `react` — React 18+
|
|
155
|
+
- `@tanstack/react-table` — Table utilities for admin column generation
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ComponentType } from 'react';
|
|
2
2
|
import * as convex_server from 'convex/server';
|
|
3
|
-
import { TableDefinition, GenericDataModel, GenericMutationCtx, GenericQueryCtx, TableNamesInDataModel, PaginationOptions } from 'convex/server';
|
|
4
|
-
import { VObject, GenericValidator, ObjectType, PropertyValidators } from 'convex/values';
|
|
3
|
+
import { TableDefinition, GenericDataModel, GenericMutationCtx, GenericQueryCtx, QueryBuilder, RegisteredQuery, TableNamesInDataModel, PaginationOptions } from 'convex/server';
|
|
4
|
+
import { VObject, GenericValidator, ObjectType, PropertyValidators, v } from 'convex/values';
|
|
5
5
|
import { ColumnDef } from '@tanstack/react-table';
|
|
6
6
|
import { ZodTypeAny, z } from 'zod';
|
|
7
7
|
|
|
@@ -893,6 +893,12 @@ interface VexCollection<TFields extends Record<string, any> = any, TExtraKeys ex
|
|
|
893
893
|
* Auth adapter collections default to `false` — set explicitly to opt in.
|
|
894
894
|
*/
|
|
895
895
|
generateApi?: boolean;
|
|
896
|
+
/**
|
|
897
|
+
* Internal marker set by resolveMediaCollection().
|
|
898
|
+
* When true, ExtractFieldKeys includes DefaultMediaFieldKeys.
|
|
899
|
+
* @internal — do not set manually.
|
|
900
|
+
*/
|
|
901
|
+
_isMedia?: true;
|
|
896
902
|
/**
|
|
897
903
|
* TypeScript interface name used in generated `vex.types.ts`.
|
|
898
904
|
* If not set, auto-generated from slug via PascalCase conversion.
|
|
@@ -1094,6 +1100,11 @@ interface AdminConfig {
|
|
|
1094
1100
|
};
|
|
1095
1101
|
/** Global live preview defaults */
|
|
1096
1102
|
livePreview?: AdminLivePreviewConfig;
|
|
1103
|
+
/** Onboarding tour configuration */
|
|
1104
|
+
onboarding: {
|
|
1105
|
+
/** Whether the onboarding tour is disabled. Default: false (tour enabled) */
|
|
1106
|
+
disabled: boolean;
|
|
1107
|
+
};
|
|
1097
1108
|
}
|
|
1098
1109
|
/**
|
|
1099
1110
|
* Admin page metadata configuration.
|
|
@@ -1178,6 +1189,22 @@ interface AdminConfigInput {
|
|
|
1178
1189
|
* Individual collections can override these breakpoints.
|
|
1179
1190
|
*/
|
|
1180
1191
|
livePreview?: AdminLivePreviewConfig;
|
|
1192
|
+
/**
|
|
1193
|
+
* Onboarding tour configuration.
|
|
1194
|
+
*
|
|
1195
|
+
* Default:
|
|
1196
|
+
* ```
|
|
1197
|
+
* disabled: false
|
|
1198
|
+
* ```
|
|
1199
|
+
*/
|
|
1200
|
+
onboarding?: {
|
|
1201
|
+
/**
|
|
1202
|
+
* Disable the onboarding tour for all users.
|
|
1203
|
+
*
|
|
1204
|
+
* Default: `false`
|
|
1205
|
+
*/
|
|
1206
|
+
disabled?: boolean;
|
|
1207
|
+
};
|
|
1181
1208
|
}
|
|
1182
1209
|
|
|
1183
1210
|
/** Schema generation configuration. */
|
|
@@ -1362,7 +1389,9 @@ type ExtractSlug<T> = T extends {
|
|
|
1362
1389
|
* @example
|
|
1363
1390
|
* type K = ExtractFieldKeys<typeof posts>; // "title" | "slug" | "status" | "featured"
|
|
1364
1391
|
*/
|
|
1365
|
-
type ExtractFieldKeys<T> = T extends VexCollection<infer TFields, infer TExtraKeys> ?
|
|
1392
|
+
type ExtractFieldKeys<T> = T extends VexCollection<infer TFields, infer TExtraKeys> ? T extends {
|
|
1393
|
+
_isMedia: true;
|
|
1394
|
+
} ? (keyof TFields & string) | (TExtraKeys & string) | DefaultMediaFieldKeys : (keyof TFields & string) | (TExtraKeys & string) : T extends VexMediaCollection<infer TFields> ? (keyof TFields & string) | DefaultMediaFieldKeys : T extends VexGlobal<infer TFields> ? keyof TFields & string : never;
|
|
1366
1395
|
/**
|
|
1367
1396
|
* Extract the inferred document type from a VexCollection or VexGlobal.
|
|
1368
1397
|
* Includes `_id` (system field) and auth extra keys (typed as `string`).
|
|
@@ -2286,12 +2315,17 @@ declare function deletePreviewSnapshot<DataModel extends GenericDataModel>(props
|
|
|
2286
2315
|
documentId: string;
|
|
2287
2316
|
}): Promise<void>;
|
|
2288
2317
|
/**
|
|
2289
|
-
* Gets the preview
|
|
2318
|
+
* Gets the preview data for a document.
|
|
2319
|
+
*
|
|
2320
|
+
* Lookup order:
|
|
2321
|
+
* 1. Preview snapshot (transient, written by admin form on each change)
|
|
2322
|
+
* 2. Latest version from vex_versions (draft or published, excluding autosave/previewSnapshot)
|
|
2323
|
+
* 3. null (fall back to main document)
|
|
2290
2324
|
*
|
|
2291
2325
|
* @param props.ctx - Convex query context
|
|
2292
2326
|
* @param props.collection - Collection slug
|
|
2293
2327
|
* @param props.documentId - Document ID
|
|
2294
|
-
* @returns The snapshot data, or null if no preview
|
|
2328
|
+
* @returns The snapshot data, or null if no preview/version exists
|
|
2295
2329
|
*/
|
|
2296
2330
|
declare function getPreviewSnapshot<DataModel extends GenericDataModel>(props: {
|
|
2297
2331
|
ctx: GenericQueryCtx<DataModel>;
|
|
@@ -2322,51 +2356,61 @@ interface VexQueryCtx<DataModel extends GenericDataModel = GenericDataModel> ext
|
|
|
2322
2356
|
drafts: VexDraftsMode;
|
|
2323
2357
|
}
|
|
2324
2358
|
/**
|
|
2325
|
-
*
|
|
2359
|
+
* Create a typed vexQuery builder from your project's query builder.
|
|
2326
2360
|
*
|
|
2327
|
-
*
|
|
2328
|
-
*
|
|
2329
|
-
* 2. Pass an extended context with `drafts` mode to the handler
|
|
2330
|
-
* 3. Preserve full type safety for args and return types
|
|
2331
|
-
*
|
|
2332
|
-
* The handler receives a `VexQueryCtx` which includes `ctx.drafts`.
|
|
2333
|
-
* Use this to decide what content to return.
|
|
2361
|
+
* Call this once in your project to get a `vexQuery` function that
|
|
2362
|
+
* preserves full return type inference from your DataModel.
|
|
2334
2363
|
*
|
|
2335
2364
|
* @example
|
|
2336
2365
|
* ```ts
|
|
2337
|
-
*
|
|
2338
|
-
* import {
|
|
2366
|
+
* // convex/vex/helpers.ts
|
|
2367
|
+
* import { createVexQuery } from "@vexcms/core";
|
|
2368
|
+
* import { query } from "../_generated/server";
|
|
2369
|
+
*
|
|
2370
|
+
* export const vexQuery = createVexQuery(query);
|
|
2371
|
+
* ```
|
|
2339
2372
|
*
|
|
2340
|
-
*
|
|
2373
|
+
* Then use it in your query files:
|
|
2374
|
+
* ```ts
|
|
2375
|
+
* // convex/pages.ts
|
|
2376
|
+
* import { vexQuery } from "./vex/helpers";
|
|
2377
|
+
* import { getPreviewSnapshot } from "@vexcms/core";
|
|
2378
|
+
*
|
|
2379
|
+
* export const getBySlug = vexQuery({
|
|
2341
2380
|
* args: { slug: v.string() },
|
|
2342
2381
|
* handler: async (ctx, args) => {
|
|
2343
|
-
* const
|
|
2344
|
-
* .query("
|
|
2382
|
+
* const page = await ctx.db
|
|
2383
|
+
* .query("pages")
|
|
2345
2384
|
* .withIndex("by_slug", (q) => q.eq("slug", args.slug))
|
|
2346
2385
|
* .first();
|
|
2347
|
-
*
|
|
2348
|
-
* if (!post) return null;
|
|
2349
|
-
*
|
|
2386
|
+
* if (!page) return null;
|
|
2350
2387
|
* if (ctx.drafts === "snapshot") {
|
|
2351
|
-
* const snapshot = await getPreviewSnapshot({
|
|
2352
|
-
*
|
|
2353
|
-
* collection: "posts",
|
|
2354
|
-
* documentId: post._id,
|
|
2355
|
-
* });
|
|
2356
|
-
* if (snapshot) {
|
|
2357
|
-
* return { ...post, ...snapshot };
|
|
2358
|
-
* }
|
|
2388
|
+
* const snapshot = await getPreviewSnapshot({ ctx, collection: "pages", documentId: page._id });
|
|
2389
|
+
* if (snapshot) return { ...page, ...snapshot };
|
|
2359
2390
|
* }
|
|
2360
|
-
*
|
|
2361
|
-
* return post;
|
|
2391
|
+
* return page;
|
|
2362
2392
|
* },
|
|
2363
2393
|
* });
|
|
2364
2394
|
* ```
|
|
2365
2395
|
*/
|
|
2396
|
+
declare function createVexQuery<DataModel extends GenericDataModel>(_queryBuilder: QueryBuilder<DataModel, "public">): <Args extends PropertyValidators, Output>(props: {
|
|
2397
|
+
args: Args;
|
|
2398
|
+
handler: (ctx: VexQueryCtx<DataModel>, args: ObjectType<Args>) => Output | Promise<Output>;
|
|
2399
|
+
}) => RegisteredQuery<"public", ObjectType<Args & {
|
|
2400
|
+
_vexDrafts: typeof v.optional<any>;
|
|
2401
|
+
}>, Awaited<Output>>;
|
|
2402
|
+
/**
|
|
2403
|
+
* Generic vexQuery for use without project-specific types.
|
|
2404
|
+
* Prefer `createVexQuery(query)` for full type inference.
|
|
2405
|
+
*
|
|
2406
|
+
* @deprecated Use `createVexQuery(query)` instead for proper return type inference.
|
|
2407
|
+
*/
|
|
2366
2408
|
declare function vexQuery<Args extends PropertyValidators, Output>(props: {
|
|
2367
2409
|
args: Args;
|
|
2368
2410
|
handler: (ctx: VexQueryCtx, args: ObjectType<Args>) => Output | Promise<Output>;
|
|
2369
|
-
}):
|
|
2411
|
+
}): RegisteredQuery<"public", ObjectType<Args & {
|
|
2412
|
+
_vexDrafts: typeof v.optional<any>;
|
|
2413
|
+
}>, Awaited<Output>>;
|
|
2370
2414
|
|
|
2371
2415
|
declare function listDocuments<DataModel extends GenericDataModel>(props: {
|
|
2372
2416
|
args: {
|
|
@@ -2623,4 +2667,4 @@ declare function planMigration(props: {
|
|
|
2623
2667
|
config: VexConfig;
|
|
2624
2668
|
}): MigrationOp[];
|
|
2625
2669
|
|
|
2626
|
-
export { ALL_SYSTEM_FIELDS, type AccessAction, type AdminConfig, type AdminConfigInput, type AdminLivePreviewConfig, type AdminMetaInput, type AdminSidebarInput, type AnyVexCollection, type ArrayFieldDef, type AuthCollectionFieldKeys, type AuthTableFieldKeys, type BlockAdminConfig, type BlockDef, type BlocksFieldDef, type CellComponentProps, type CheckboxFieldDef, type ClientMediaConfig, type ClientVexConfig, type CollectionAdminConfig, type CollectionKind, type CollectionQueryImports, DEFAULT_AUTOSAVE_INTERVAL, DEFAULT_BREAKPOINTS, DEFAULT_MAX_VERSIONS_PER_DOC, type DateFieldDef, type DefaultMediaFieldKeys, type DistributiveOmit, type ExtractDocType, type ExtractFieldKeys, type ExtractSlug, type ExtractSlugs, type FieldAdminConfig, type FieldComponentProps, type FieldPermissionResult, type FileStorageAdapter, GENERATED_HEADER, type GeneratedFiles, type GlobalAdminConfig, type ImageUrlFieldDef, type IndexConfig, type InferBlockUnion, type InferFieldType, type InferFieldsType, type JsonFieldDef, LOCKED_MEDIA_FIELDS, type LivePreviewBreakpoint, type LivePreviewConfig, type LockedMediaField, type LookupBySlug, type MediaConfig, type MediaConfigInput, type MergedCollectionResult, type MigrationOp, type NumberFieldDef, OVERRIDABLE_MEDIA_FIELDS, type OverridableMediaField, PREVIEW_SNAPSHOT_DEBOUNCE_MS, type PermissionCallbackProps, type PermissionCheck, type RelationshipFieldDef, type RemovedFieldInfo, type ResolvedCollectionMatch, type ResolvedFieldPermissions, type ResolvedIndex, type ResolvedSearchIndex, type ResourcePermissions, type RichTextDocument, type RichTextElement, type RichTextFieldDef, type RichTextText, type RolesWithPermissions, type SchemaDiff, type SchemaFieldInfo, type SearchIndexConfig, type SelectFieldDef, type SelectOption, type TextFieldDef, type UIFieldDef, type UploadFieldDef, VERSION_SYSTEM_FIELDS, type VersioningFieldKeys, type VersionsConfig, type VexAccessConfig, VexAccessConfigError, VexAccessError, type VexAccessInput, type VexAccessInputBase, type VexAccessInputWithOrg, type VexAuthAdapter, VexAuthConfigError, VexBlockValidationError, type VexCollection, type VexConfig, type VexConfigInput, type VexDraftsMode, type VexEditorAdapter, type VexEditorComponentProps, VexError, type VexField, VexFieldValidationError, type VexGlobal, type VexMediaCollection, VexMediaConfigError, type VexQueryCtx, type VexRenderComponentProps, VexSlugConflictError, addRemovedFieldsAsOptional, array, blocks, checkbox, createDocument, date, defineAccess, defineBlock, defineCollection, defineConfig, defineMediaCollection, deleteDocument, deletePreviewSnapshot, diffSchema, extendTable, extractLivePreviewConfigs, extractUserFields, fieldMetaToZod, findCollectionBySlug, generateCollectionQueries, generateColumns, generateFormDefaultValues, generateFormSchema, generateIndexFile, generateVexSchema, generateVexTypes, getAllCollections, getDocument, getPreviewSnapshot, hasPermission, imageUrl, isMediaCollection, json, listDocuments, makeFieldsOptional, mergeAuthCollectionWithUserCollection, number, planMigration, relationship, resolvePreviewURL, richtext, sanitizeConfigForClient, searchDocuments, select, shouldReloadURL, slugToInterfaceName, text, toTitleCase, ui, updateDocument, upload, upsertPreviewSnapshot, vexQuery };
|
|
2670
|
+
export { ALL_SYSTEM_FIELDS, type AccessAction, type AdminConfig, type AdminConfigInput, type AdminLivePreviewConfig, type AdminMetaInput, type AdminSidebarInput, type AnyVexCollection, type ArrayFieldDef, type AuthCollectionFieldKeys, type AuthTableFieldKeys, type BlockAdminConfig, type BlockDef, type BlocksFieldDef, type CellComponentProps, type CheckboxFieldDef, type ClientMediaConfig, type ClientVexConfig, type CollectionAdminConfig, type CollectionKind, type CollectionQueryImports, DEFAULT_AUTOSAVE_INTERVAL, DEFAULT_BREAKPOINTS, DEFAULT_MAX_VERSIONS_PER_DOC, type DateFieldDef, type DefaultMediaFieldKeys, type DistributiveOmit, type ExtractDocType, type ExtractFieldKeys, type ExtractSlug, type ExtractSlugs, type FieldAdminConfig, type FieldComponentProps, type FieldPermissionResult, type FileStorageAdapter, GENERATED_HEADER, type GeneratedFiles, type GlobalAdminConfig, type ImageUrlFieldDef, type IndexConfig, type InferBlockUnion, type InferFieldType, type InferFieldsType, type JsonFieldDef, LOCKED_MEDIA_FIELDS, type LivePreviewBreakpoint, type LivePreviewConfig, type LockedMediaField, type LookupBySlug, type MediaConfig, type MediaConfigInput, type MergedCollectionResult, type MigrationOp, type NumberFieldDef, OVERRIDABLE_MEDIA_FIELDS, type OverridableMediaField, PREVIEW_SNAPSHOT_DEBOUNCE_MS, type PermissionCallbackProps, type PermissionCheck, type RelationshipFieldDef, type RemovedFieldInfo, type ResolvedCollectionMatch, type ResolvedFieldPermissions, type ResolvedIndex, type ResolvedSearchIndex, type ResourcePermissions, type RichTextDocument, type RichTextElement, type RichTextFieldDef, type RichTextText, type RolesWithPermissions, type SchemaDiff, type SchemaFieldInfo, type SearchIndexConfig, type SelectFieldDef, type SelectOption, type TextFieldDef, type UIFieldDef, type UploadFieldDef, VERSION_SYSTEM_FIELDS, type VersioningFieldKeys, type VersionsConfig, type VexAccessConfig, VexAccessConfigError, VexAccessError, type VexAccessInput, type VexAccessInputBase, type VexAccessInputWithOrg, type VexAuthAdapter, VexAuthConfigError, VexBlockValidationError, type VexCollection, type VexConfig, type VexConfigInput, type VexDraftsMode, type VexEditorAdapter, type VexEditorComponentProps, VexError, type VexField, VexFieldValidationError, type VexGlobal, type VexMediaCollection, VexMediaConfigError, type VexQueryCtx, type VexRenderComponentProps, VexSlugConflictError, addRemovedFieldsAsOptional, array, blocks, checkbox, createDocument, createVexQuery, date, defineAccess, defineBlock, defineCollection, defineConfig, defineMediaCollection, deleteDocument, deletePreviewSnapshot, diffSchema, extendTable, extractLivePreviewConfigs, extractUserFields, fieldMetaToZod, findCollectionBySlug, generateCollectionQueries, generateColumns, generateFormDefaultValues, generateFormSchema, generateIndexFile, generateVexSchema, generateVexTypes, getAllCollections, getDocument, getPreviewSnapshot, hasPermission, imageUrl, isMediaCollection, json, listDocuments, makeFieldsOptional, mergeAuthCollectionWithUserCollection, number, planMigration, relationship, resolvePreviewURL, richtext, sanitizeConfigForClient, searchDocuments, select, shouldReloadURL, slugToInterfaceName, text, toTitleCase, ui, updateDocument, upload, upsertPreviewSnapshot, vexQuery };
|
package/dist/index.js
CHANGED
|
@@ -137,6 +137,9 @@ var BASE_VEX_CONFIG = {
|
|
|
137
137
|
user: "users",
|
|
138
138
|
sidebar: {
|
|
139
139
|
hideGlobals: false
|
|
140
|
+
},
|
|
141
|
+
onboarding: {
|
|
142
|
+
disabled: false
|
|
140
143
|
}
|
|
141
144
|
},
|
|
142
145
|
schema: {
|
|
@@ -170,7 +173,8 @@ function resolveMediaCollection(props) {
|
|
|
170
173
|
fields: defaults,
|
|
171
174
|
tableName: props.mediaCollection.tableName,
|
|
172
175
|
labels: props.mediaCollection.labels,
|
|
173
|
-
admin: adminConfig
|
|
176
|
+
admin: adminConfig,
|
|
177
|
+
_isMedia: true
|
|
174
178
|
};
|
|
175
179
|
}
|
|
176
180
|
function defineConfig(vexConfig) {
|
|
@@ -189,6 +193,10 @@ function defineConfig(vexConfig) {
|
|
|
189
193
|
...BASE_VEX_CONFIG.admin.sidebar,
|
|
190
194
|
...vexConfig.admin?.sidebar
|
|
191
195
|
},
|
|
196
|
+
onboarding: {
|
|
197
|
+
...BASE_VEX_CONFIG.admin.onboarding,
|
|
198
|
+
...vexConfig.admin?.onboarding
|
|
199
|
+
},
|
|
192
200
|
livePreview: vexConfig.admin?.livePreview
|
|
193
201
|
},
|
|
194
202
|
schema: {
|
|
@@ -1956,7 +1964,7 @@ function fieldToTypeString(props) {
|
|
|
1956
1964
|
case "json":
|
|
1957
1965
|
return "Record<string, unknown>";
|
|
1958
1966
|
case "richtext":
|
|
1959
|
-
return "
|
|
1967
|
+
return "RichTextDocument";
|
|
1960
1968
|
case "ui":
|
|
1961
1969
|
return "never";
|
|
1962
1970
|
case "select": {
|
|
@@ -2105,10 +2113,37 @@ function generateVexTypes(props) {
|
|
|
2105
2113
|
if (config.collections.length > 0 || (config.media?.collections?.length ?? 0) > 0 || config.globals.length > 0) {
|
|
2106
2114
|
needsIdImport = true;
|
|
2107
2115
|
}
|
|
2116
|
+
let needsRichTextImport = false;
|
|
2117
|
+
function checkForRichTextFields(fields) {
|
|
2118
|
+
for (const field of Object.values(fields)) {
|
|
2119
|
+
if (field.type === "richtext") {
|
|
2120
|
+
needsRichTextImport = true;
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
for (const col of config.collections) {
|
|
2125
|
+
checkForRichTextFields(col.fields);
|
|
2126
|
+
}
|
|
2127
|
+
if (config.media?.collections) {
|
|
2128
|
+
for (const col of config.media.collections) {
|
|
2129
|
+
checkForRichTextFields(col.fields);
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
for (const g of config.globals) {
|
|
2133
|
+
checkForRichTextFields(g.fields);
|
|
2134
|
+
}
|
|
2135
|
+
for (const block of blocksBySlug.values()) {
|
|
2136
|
+
checkForRichTextFields(block.fields);
|
|
2137
|
+
}
|
|
2108
2138
|
parts.push("// \u26A0\uFE0F AUTO-GENERATED BY VEX CMS \u2014 DO NOT EDIT \u26A0\uFE0F");
|
|
2109
2139
|
parts.push("");
|
|
2110
2140
|
if (needsIdImport) {
|
|
2111
2141
|
parts.push("import type { Id } from './_generated/dataModel';");
|
|
2142
|
+
}
|
|
2143
|
+
if (needsRichTextImport) {
|
|
2144
|
+
parts.push("import type { RichTextDocument } from '@vexcms/core';");
|
|
2145
|
+
}
|
|
2146
|
+
if (needsIdImport || needsRichTextImport) {
|
|
2112
2147
|
parts.push("");
|
|
2113
2148
|
}
|
|
2114
2149
|
const sortedBlockSlugs = [...blocksBySlug.keys()].sort();
|
|
@@ -2399,12 +2434,31 @@ async function deletePreviewSnapshot(props) {
|
|
|
2399
2434
|
}
|
|
2400
2435
|
}
|
|
2401
2436
|
async function getPreviewSnapshot(props) {
|
|
2402
|
-
const
|
|
2437
|
+
const previewEntry = await props.ctx.db.query("vex_versions").withIndex(
|
|
2403
2438
|
"by_document_status",
|
|
2404
2439
|
(q) => q.eq("collection", props.collection).eq("documentId", props.documentId).eq("status", "previewSnapshot")
|
|
2405
2440
|
).first();
|
|
2406
|
-
if (
|
|
2407
|
-
|
|
2441
|
+
if (previewEntry) {
|
|
2442
|
+
return previewEntry.snapshot;
|
|
2443
|
+
}
|
|
2444
|
+
const allVersions = await props.ctx.db.query("vex_versions").withIndex(
|
|
2445
|
+
"by_document",
|
|
2446
|
+
(q) => q.eq("collection", props.collection).eq("documentId", props.documentId)
|
|
2447
|
+
).collect();
|
|
2448
|
+
let latestVersion = null;
|
|
2449
|
+
let maxVersion = -1;
|
|
2450
|
+
for (const v2 of allVersions) {
|
|
2451
|
+
if (v2.status === "previewSnapshot" || v2.status === "autosave") continue;
|
|
2452
|
+
const ver = v2.version;
|
|
2453
|
+
if (ver > maxVersion) {
|
|
2454
|
+
maxVersion = ver;
|
|
2455
|
+
latestVersion = v2;
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
if (latestVersion) {
|
|
2459
|
+
return latestVersion.snapshot;
|
|
2460
|
+
}
|
|
2461
|
+
return null;
|
|
2408
2462
|
}
|
|
2409
2463
|
|
|
2410
2464
|
// src/convex/vexQuery.ts
|
|
@@ -2412,6 +2466,30 @@ import {
|
|
|
2412
2466
|
queryGeneric
|
|
2413
2467
|
} from "convex/server";
|
|
2414
2468
|
import { v } from "convex/values";
|
|
2469
|
+
function wrapHandler(handler) {
|
|
2470
|
+
return async (ctx, args) => {
|
|
2471
|
+
const { _vexDrafts, ...userArgs } = args;
|
|
2472
|
+
const drafts = _vexDrafts !== void 0 ? _vexDrafts : "snapshot";
|
|
2473
|
+
const vexCtx = Object.assign(
|
|
2474
|
+
Object.create(Object.getPrototypeOf(ctx)),
|
|
2475
|
+
ctx,
|
|
2476
|
+
{ drafts }
|
|
2477
|
+
);
|
|
2478
|
+
return handler(vexCtx, userArgs);
|
|
2479
|
+
};
|
|
2480
|
+
}
|
|
2481
|
+
function createVexQuery(_queryBuilder) {
|
|
2482
|
+
return (props) => {
|
|
2483
|
+
const mergedArgs = {
|
|
2484
|
+
...props.args,
|
|
2485
|
+
_vexDrafts: v.optional(v.union(v.literal("snapshot"), v.boolean()))
|
|
2486
|
+
};
|
|
2487
|
+
return queryGeneric({
|
|
2488
|
+
args: mergedArgs,
|
|
2489
|
+
handler: wrapHandler(props.handler)
|
|
2490
|
+
});
|
|
2491
|
+
};
|
|
2492
|
+
}
|
|
2415
2493
|
function vexQuery(props) {
|
|
2416
2494
|
const mergedArgs = {
|
|
2417
2495
|
...props.args,
|
|
@@ -2419,14 +2497,7 @@ function vexQuery(props) {
|
|
|
2419
2497
|
};
|
|
2420
2498
|
return queryGeneric({
|
|
2421
2499
|
args: mergedArgs,
|
|
2422
|
-
handler:
|
|
2423
|
-
const { _vexDrafts, ...userArgs } = args;
|
|
2424
|
-
const drafts = _vexDrafts !== void 0 ? _vexDrafts : "snapshot";
|
|
2425
|
-
const vexCtx = Object.assign(Object.create(Object.getPrototypeOf(ctx)), ctx, {
|
|
2426
|
-
drafts
|
|
2427
|
-
});
|
|
2428
|
-
return props.handler(vexCtx, userArgs);
|
|
2429
|
-
}
|
|
2500
|
+
handler: wrapHandler(props.handler)
|
|
2430
2501
|
});
|
|
2431
2502
|
}
|
|
2432
2503
|
|
|
@@ -3106,6 +3177,7 @@ export {
|
|
|
3106
3177
|
blocks,
|
|
3107
3178
|
checkbox,
|
|
3108
3179
|
createDocument,
|
|
3180
|
+
createVexQuery,
|
|
3109
3181
|
date,
|
|
3110
3182
|
defineAccess,
|
|
3111
3183
|
defineBlock,
|