includio-cms 0.6.2 → 0.7.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/CHANGELOG.md +28 -0
- package/ROADMAP.md +18 -2
- package/dist/admin/api/accept-invite.js +2 -2
- package/dist/admin/auth-client.d.ts +2122 -2122
- package/dist/admin/client/admin/admin-layout.svelte +5 -1
- package/dist/admin/client/admin/admin-layout.svelte.d.ts +2 -0
- package/dist/admin/client/index.d.ts +1 -0
- package/dist/admin/client/index.js +1 -0
- package/dist/admin/components/fields/blocks-field.svelte +3 -3
- package/dist/admin/components/fields/field-renderer.svelte +9 -0
- package/dist/admin/components/fields/image-field.svelte +2 -2
- package/dist/admin/components/fields/media-field.svelte +4 -4
- package/dist/admin/components/media/file/file-miniature.svelte +6 -6
- package/dist/admin/helpers/build-custom-fields-map.d.ts +7 -0
- package/dist/admin/helpers/build-custom-fields-map.js +13 -0
- package/dist/admin/helpers/index.d.ts +7 -0
- package/dist/admin/helpers/index.js +7 -0
- package/dist/admin/helpers/use-field.d.ts +26 -0
- package/dist/admin/helpers/use-field.js +9 -0
- package/dist/admin/state/custom-fields.svelte.d.ts +3 -0
- package/dist/admin/state/custom-fields.svelte.js +10 -0
- package/dist/admin/styles/admin.css +1 -1
- package/dist/admin/ui/index.d.ts +12 -0
- package/dist/admin/ui/index.js +14 -0
- package/dist/components/ui/accordion/accordion.svelte.d.ts +1 -1
- package/dist/components/ui/calendar/calendar.svelte.d.ts +1 -1
- package/dist/components/ui/command/command-dialog.svelte.d.ts +1 -1
- package/dist/components/ui/command/command-input.svelte.d.ts +1 -1
- package/dist/components/ui/command/command.svelte.d.ts +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte.d.ts +1 -1
- package/dist/components/ui/input/input.svelte +2 -2
- package/dist/components/ui/input/input.svelte.d.ts +1 -1
- package/dist/components/ui/input-group/input-group-input.svelte.d.ts +2 -2
- package/dist/components/ui/input-group/input-group-textarea.svelte.d.ts +1 -1
- package/dist/components/ui/input-group/input-group.svelte +1 -1
- package/dist/components/ui/radio-group/radio-group.svelte.d.ts +1 -1
- package/dist/components/ui/select/select-trigger.svelte +1 -1
- package/dist/components/ui/sidebar/sidebar-input.svelte.d.ts +2 -2
- package/dist/components/ui/tabs/tabs.svelte.d.ts +1 -1
- package/dist/components/ui/textarea/textarea.svelte +1 -1
- package/dist/components/ui/textarea/textarea.svelte.d.ts +1 -1
- package/dist/components/ui/toggle-group/toggle-group-item.svelte.d.ts +1 -1
- package/dist/components/ui/toggle-group/toggle-group.svelte.d.ts +1 -1
- package/dist/core/cms.d.ts +7 -4
- package/dist/core/cms.js +52 -2
- package/dist/core/fields/fieldSchemaToTs.d.ts +11 -0
- package/dist/core/fields/fieldSchemaToTs.js +30 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +1 -0
- package/dist/core/server/fields/populateEntry.js +50 -0
- package/dist/core/server/fields/resolveRichtextLinks.js +3 -2
- package/dist/core/server/fields/resolveUrlFields.js +2 -2
- package/dist/core/server/fields/slugResolver.d.ts +5 -0
- package/dist/core/server/fields/slugResolver.js +16 -0
- package/dist/core/server/fields/utils/resolveMedia.d.ts +12 -0
- package/dist/core/server/fields/utils/resolveMedia.js +23 -0
- package/dist/core/server/generator/fields.d.ts +2 -0
- package/dist/core/server/generator/fields.js +8 -0
- package/dist/core/server/generator/generator.js +9 -1
- package/dist/db-postgres/index.d.ts +6 -1
- package/dist/db-postgres/index.js +1 -0
- package/dist/server/auth.d.ts +1 -1358
- package/dist/server/auth.js +3 -23
- package/dist/sveltekit/index.d.ts +1 -0
- package/dist/sveltekit/index.js +1 -0
- package/dist/sveltekit/server/handle.js +21 -2
- package/dist/types/cms.d.ts +8 -3
- package/dist/types/config.d.ts +1 -0
- package/dist/types/fields.d.ts +9 -2
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.js +1 -0
- package/dist/types/plugins.d.ts +19 -1
- package/dist/updates/0.7.0/index.d.ts +2 -0
- package/dist/updates/0.7.0/index.js +16 -0
- package/dist/updates/0.7.1/index.d.ts +2 -0
- package/dist/updates/0.7.1/index.js +16 -0
- package/dist/updates/index.js +3 -1
- package/package.json +14 -1
package/dist/core/cms.js
CHANGED
|
@@ -1,23 +1,31 @@
|
|
|
1
|
+
import { setSchemaGetCMS } from './fields/fieldSchemaToTs.js';
|
|
2
|
+
import { betterAuth } from 'better-auth';
|
|
3
|
+
import { drizzleAdapter } from 'better-auth/adapters/drizzle';
|
|
4
|
+
import { admin } from 'better-auth/plugins';
|
|
5
|
+
import { resetPasswordEmailTemplate } from '../admin/email/reset-password-template.js';
|
|
6
|
+
import * as authSchema from '../server/db/schema/auth-schema.js';
|
|
1
7
|
export class CMS {
|
|
2
8
|
config;
|
|
3
9
|
databaseAdapter;
|
|
4
10
|
filesAdapter;
|
|
5
11
|
emailAdapter;
|
|
6
|
-
|
|
12
|
+
authConfig;
|
|
7
13
|
aiAdapter = null;
|
|
14
|
+
_betterAuth = null;
|
|
8
15
|
collections;
|
|
9
16
|
singles;
|
|
10
17
|
forms;
|
|
11
18
|
languages;
|
|
12
19
|
mediaConfig;
|
|
13
20
|
plugins = [];
|
|
21
|
+
customFields = new Map();
|
|
14
22
|
apiKeys = [];
|
|
15
23
|
constructor(config) {
|
|
16
24
|
this.config = config;
|
|
17
25
|
this.databaseAdapter = config.db;
|
|
18
26
|
this.filesAdapter = config.files;
|
|
19
27
|
this.emailAdapter = config.email ?? null;
|
|
20
|
-
this.
|
|
28
|
+
this.authConfig = config.auth ?? null;
|
|
21
29
|
this.aiAdapter = config.ai || null;
|
|
22
30
|
this.mediaConfig = config.media || {};
|
|
23
31
|
this.collections = {};
|
|
@@ -44,7 +52,49 @@ export class CMS {
|
|
|
44
52
|
this.apiKeys = config.apiKeys || [];
|
|
45
53
|
if (config.plugins) {
|
|
46
54
|
this.plugins = config.plugins;
|
|
55
|
+
for (const plugin of this.plugins) {
|
|
56
|
+
for (const def of plugin.fields ?? []) {
|
|
57
|
+
if (this.customFields.has(def.fieldType)) {
|
|
58
|
+
throw new Error(`Duplicate custom field type: "${def.fieldType}" (plugin: "${plugin.slug}")`);
|
|
59
|
+
}
|
|
60
|
+
this.customFields.set(def.fieldType, def);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
47
63
|
}
|
|
64
|
+
setSchemaGetCMS(() => this);
|
|
65
|
+
}
|
|
66
|
+
get auth() {
|
|
67
|
+
if (!this._betterAuth) {
|
|
68
|
+
if (!this.authConfig) {
|
|
69
|
+
throw new Error('Auth not configured. Add `auth` to your CMS config.');
|
|
70
|
+
}
|
|
71
|
+
const drizzleDb = this.databaseAdapter._drizzle;
|
|
72
|
+
if (!drizzleDb) {
|
|
73
|
+
throw new Error('Database adapter does not expose a drizzle instance (_drizzle).');
|
|
74
|
+
}
|
|
75
|
+
const emailAdapter = this.emailAdapter;
|
|
76
|
+
this._betterAuth = betterAuth({
|
|
77
|
+
database: drizzleAdapter(drizzleDb, { provider: 'pg', schema: authSchema }),
|
|
78
|
+
secret: this.authConfig.secret,
|
|
79
|
+
baseURL: this.authConfig.baseURL,
|
|
80
|
+
emailAndPassword: {
|
|
81
|
+
enabled: true,
|
|
82
|
+
async sendResetPassword({ user, url }, request) {
|
|
83
|
+
if (!emailAdapter) {
|
|
84
|
+
throw new Error('Email adapter not configured');
|
|
85
|
+
}
|
|
86
|
+
const lang = request?.headers.get('Accept-Language')?.startsWith('pl') ? 'pl' : 'en';
|
|
87
|
+
const { subject, html } = resetPasswordEmailTemplate({
|
|
88
|
+
resetUrl: url,
|
|
89
|
+
lang
|
|
90
|
+
});
|
|
91
|
+
await emailAdapter.sendMail({ to: [user.email], subject, html });
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
plugins: [admin()]
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return this._betterAuth;
|
|
48
98
|
}
|
|
49
99
|
getBySlug(slug) {
|
|
50
100
|
const config = this.collections[slug] || this.singles[slug];
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import type { Field } from '../../types/fields.js';
|
|
2
|
+
import type { CustomFieldDefinition } from '../../types/plugins.js';
|
|
2
3
|
import { z } from 'zod';
|
|
4
|
+
/**
|
|
5
|
+
* Set custom fields map for client-side use (where getCMS() is unavailable).
|
|
6
|
+
*/
|
|
7
|
+
export declare function setSchemaCustomFields(customFields: Map<string, CustomFieldDefinition>): void;
|
|
8
|
+
/**
|
|
9
|
+
* Set CMS getter for server-side use (avoids static import of server-only cms.ts).
|
|
10
|
+
*/
|
|
11
|
+
export declare function setSchemaGetCMS(getter: () => {
|
|
12
|
+
customFields: Map<string, CustomFieldDefinition>;
|
|
13
|
+
}): void;
|
|
3
14
|
type AnyZodObject = z.ZodObject<any, any>;
|
|
4
15
|
interface GenerateZodSchemaOptions {
|
|
5
16
|
parentRequired?: boolean;
|
|
@@ -1,4 +1,28 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
let _customFieldsOverride = null;
|
|
3
|
+
/**
|
|
4
|
+
* Set custom fields map for client-side use (where getCMS() is unavailable).
|
|
5
|
+
*/
|
|
6
|
+
export function setSchemaCustomFields(customFields) {
|
|
7
|
+
_customFieldsOverride = customFields;
|
|
8
|
+
}
|
|
9
|
+
let _getCMS = null;
|
|
10
|
+
/**
|
|
11
|
+
* Set CMS getter for server-side use (avoids static import of server-only cms.ts).
|
|
12
|
+
*/
|
|
13
|
+
export function setSchemaGetCMS(getter) {
|
|
14
|
+
_getCMS = getter;
|
|
15
|
+
}
|
|
16
|
+
function getCustomFieldDef(fieldType) {
|
|
17
|
+
if (_customFieldsOverride)
|
|
18
|
+
return _customFieldsOverride.get(fieldType);
|
|
19
|
+
try {
|
|
20
|
+
return _getCMS?.().customFields.get(fieldType);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
2
26
|
export function generateZodSchemaFromField(field, languages, options = {
|
|
3
27
|
parentRequired: true
|
|
4
28
|
}) {
|
|
@@ -283,6 +307,12 @@ export function generateZodSchemaFromField(field, languages, options = {
|
|
|
283
307
|
}
|
|
284
308
|
return schema;
|
|
285
309
|
}
|
|
310
|
+
case 'custom': {
|
|
311
|
+
const customDef = getCustomFieldDef(field.fieldType);
|
|
312
|
+
if (!customDef)
|
|
313
|
+
return z.any();
|
|
314
|
+
return customDef.zodSchema(field, languages);
|
|
315
|
+
}
|
|
286
316
|
default:
|
|
287
317
|
return z.any();
|
|
288
318
|
}
|
package/dist/core/index.d.ts
CHANGED
package/dist/core/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { resolveMediaFields } from './resolveImageFields.js';
|
|
|
4
4
|
import { resolveRelationFields } from './resolveRelationFields.js';
|
|
5
5
|
import { resolveRichtextLinks } from './resolveRichtextLinks.js';
|
|
6
6
|
import { resolveUrlFields } from './resolveUrlFields.js';
|
|
7
|
+
import { getCMS } from '../../cms.js';
|
|
7
8
|
function translateInlineBlockData(data, fields, language) {
|
|
8
9
|
for (const field of fields) {
|
|
9
10
|
const val = data[field.slug];
|
|
@@ -45,11 +46,60 @@ function translateInlineBlockData(data, fields, language) {
|
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
48
|
}
|
|
49
|
+
async function resolveCustomFields(data, fields) {
|
|
50
|
+
// Check if any custom fields exist before accessing CMS
|
|
51
|
+
const hasCustom = fields.some((f) => f.type === 'custom' ||
|
|
52
|
+
f.type === 'object' ||
|
|
53
|
+
f.type === 'blocks');
|
|
54
|
+
if (!hasCustom)
|
|
55
|
+
return data;
|
|
56
|
+
let cms;
|
|
57
|
+
try {
|
|
58
|
+
cms = getCMS();
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return data;
|
|
62
|
+
}
|
|
63
|
+
const result = { ...data };
|
|
64
|
+
for (const field of fields) {
|
|
65
|
+
const val = data[field.slug];
|
|
66
|
+
switch (field.type) {
|
|
67
|
+
case 'custom': {
|
|
68
|
+
const def = cms.customFields.get(field.fieldType);
|
|
69
|
+
if (def?.populateResolver && val != null) {
|
|
70
|
+
result[field.slug] = await def.populateResolver(val, field);
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
case 'object':
|
|
75
|
+
if (val && typeof val === 'object' && 'data' in val) {
|
|
76
|
+
result[field.slug] = {
|
|
77
|
+
...val,
|
|
78
|
+
data: await resolveCustomFields(val.data, field.fields)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
case 'blocks':
|
|
83
|
+
if (Array.isArray(val)) {
|
|
84
|
+
result[field.slug] = await Promise.all(val.map(async (item) => {
|
|
85
|
+
const blockDef = field.of.find((d) => d.slug === item.slug);
|
|
86
|
+
if (blockDef) {
|
|
87
|
+
return { ...item, data: await resolveCustomFields(item.data, blockDef.fields) };
|
|
88
|
+
}
|
|
89
|
+
return item;
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
48
97
|
export async function populateEntryData(data, fields, language) {
|
|
49
98
|
let populatedData = await resolveRelationFields(data, fields, language);
|
|
50
99
|
populatedData = await resolveUrlFields(populatedData, fields, language);
|
|
51
100
|
populatedData = await resolveMediaFields(populatedData, fields);
|
|
52
101
|
populatedData = await resolveRichtextLinks(populatedData, fields, language);
|
|
102
|
+
populatedData = (await resolveCustomFields(populatedData, fields));
|
|
53
103
|
populatedData = translateObject(populatedData, language);
|
|
54
104
|
translateInlineBlockData(populatedData, fields, language);
|
|
55
105
|
return populatedData;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getCMS } from '../../cms.js';
|
|
2
2
|
import { extractEntryIds as extractEntryIdsFromDoc, extractMediaIds as extractMediaIdsFromDoc, cloneDoc, walkLinkMarks, walkInlineBlockNodes } from '../../../admin/components/tiptap/structured-content-utils.js';
|
|
3
|
-
import { getEntrySlugPath, getSlugFromEntryData } from './slugResolver.js';
|
|
3
|
+
import { getEntrySlugPath, getSlugFromEntryData, getEntryPath } from './slugResolver.js';
|
|
4
4
|
const ENTRY_ID_RE = /data-entry-id="([0-9a-f-]{36})"/g;
|
|
5
5
|
function extractEntryIds(html) {
|
|
6
6
|
const ids = [];
|
|
@@ -130,7 +130,8 @@ export async function resolveRichtextLinks(data, fields, language) {
|
|
|
130
130
|
const slugPath = configSlug ? getEntrySlugPath(configSlug) : 'seo.slug';
|
|
131
131
|
const slug = getSlugFromEntryData(rawData, slugPath, language);
|
|
132
132
|
if (slug) {
|
|
133
|
-
|
|
133
|
+
const configSlug = entryConfigSlugMap[version.entryId];
|
|
134
|
+
slugMap[version.entryId] = configSlug ? getEntryPath(configSlug, slug) : slug;
|
|
134
135
|
}
|
|
135
136
|
}
|
|
136
137
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { urlFieldDataSchema, urlFieldDataWithRelationSchema } from '../../../schemas/field/url.js';
|
|
2
2
|
import { walkInlineBlockNodes, cloneDoc } from '../../../admin/components/tiptap/structured-content-utils.js';
|
|
3
3
|
import { getDbEntries, getDbEntryVersions } from '../entries/operations/get.js';
|
|
4
|
-
import { getEntrySlugPath, getSlugFromEntryData } from './slugResolver.js';
|
|
4
|
+
import { getEntrySlugPath, getSlugFromEntryData, getEntryPath } from './slugResolver.js';
|
|
5
5
|
import { isExternalUrl, mergeRel } from '../../fields/urlUtils.js';
|
|
6
6
|
const FLAT_KEY = '_flat';
|
|
7
7
|
/**
|
|
@@ -121,7 +121,7 @@ export async function resolveUrlFields(data, fields, language) {
|
|
|
121
121
|
const slugPath = getEntrySlugPath(entry.slug);
|
|
122
122
|
const slug = getSlugFromEntryData(version.data, slugPath, language);
|
|
123
123
|
if (slug)
|
|
124
|
-
slugMap[entry.id] = slug;
|
|
124
|
+
slugMap[entry.id] = getEntryPath(entry.slug, slug);
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
}
|
|
@@ -8,3 +8,8 @@ export declare function getEntrySlugPath(configSlug: string): string;
|
|
|
8
8
|
* Handles both plain strings and localized objects { lang: string }.
|
|
9
9
|
*/
|
|
10
10
|
export declare function getSlugFromEntryData(data: Record<string, unknown>, slugPath: string, language?: string): string | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Build full entry path using collection's pathTemplate.
|
|
13
|
+
* If no template configured, returns the raw slug.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getEntryPath(configSlug: string, slug: string): string;
|
|
@@ -32,3 +32,19 @@ export function getSlugFromEntryData(data, slugPath, language) {
|
|
|
32
32
|
}
|
|
33
33
|
return undefined;
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Build full entry path using collection's pathTemplate.
|
|
37
|
+
* If no template configured, returns the raw slug.
|
|
38
|
+
*/
|
|
39
|
+
export function getEntryPath(configSlug, slug) {
|
|
40
|
+
try {
|
|
41
|
+
const config = getCMS().getBySlug(configSlug);
|
|
42
|
+
if (config.pathTemplate) {
|
|
43
|
+
return config.pathTemplate.replace('{slug}', slug);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Config not found — fall through
|
|
48
|
+
}
|
|
49
|
+
return slug;
|
|
50
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ImageFieldStyle } from '../../../../types/fields.js';
|
|
2
|
+
import type { ImageStyle, MediaFile } from '../../../../types/media.js';
|
|
3
|
+
export interface ResolvedMedia {
|
|
4
|
+
data: MediaFile;
|
|
5
|
+
styles: Record<string, ImageStyle>;
|
|
6
|
+
blurDataUrl: string | null;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Resolve media files by IDs and generate image styles.
|
|
10
|
+
* Useful for plugins that need to resolve media references (e.g. photo-grid).
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveMediaWithStyles(mediaIds: string[], styles?: ImageFieldStyle[]): Promise<Record<string, ResolvedMedia>>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getCMS } from '../../../cms.js';
|
|
2
|
+
import { getImageStyles } from './imageStyles.js';
|
|
3
|
+
/**
|
|
4
|
+
* Resolve media files by IDs and generate image styles.
|
|
5
|
+
* Useful for plugins that need to resolve media references (e.g. photo-grid).
|
|
6
|
+
*/
|
|
7
|
+
export async function resolveMediaWithStyles(mediaIds, styles) {
|
|
8
|
+
if (!mediaIds.length)
|
|
9
|
+
return {};
|
|
10
|
+
const cms = getCMS();
|
|
11
|
+
const files = await cms.databaseAdapter.getMediaFiles({ data: { ids: mediaIds } });
|
|
12
|
+
// Synthetic ImageField to pass to getImageStyles
|
|
13
|
+
const syntheticField = {
|
|
14
|
+
type: 'image',
|
|
15
|
+
slug: '_resolved',
|
|
16
|
+
styles: styles ?? []
|
|
17
|
+
};
|
|
18
|
+
const entries = await Promise.all(files.map(async (file) => {
|
|
19
|
+
const { styles: resolvedStyles, blurDataUrl } = await getImageStyles(syntheticField, file);
|
|
20
|
+
return [file.id, { data: file, styles: resolvedStyles, blurDataUrl }];
|
|
21
|
+
}));
|
|
22
|
+
return Object.fromEntries(entries);
|
|
23
|
+
}
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
import type { Field } from '../../../types/fields.js';
|
|
2
|
+
import type { CustomFieldDefinition } from '../../../types/plugins.js';
|
|
3
|
+
export declare function setGeneratorCustomFields(customFields: Map<string, CustomFieldDefinition>): void;
|
|
2
4
|
export declare function generateTsTypeFromFields(fields: Field[]): string;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { toPascalCase } from './utils.js';
|
|
2
|
+
let _customFields = new Map();
|
|
3
|
+
export function setGeneratorCustomFields(customFields) {
|
|
4
|
+
_customFields = customFields;
|
|
5
|
+
}
|
|
2
6
|
function getFieldTypeAsString(field) {
|
|
3
7
|
switch (field.type) {
|
|
4
8
|
case 'text':
|
|
@@ -82,6 +86,10 @@ function getFieldTypeAsString(field) {
|
|
|
82
86
|
urlParts.push('newTab?: boolean');
|
|
83
87
|
return `{ ${urlParts.join('; ')} }`;
|
|
84
88
|
}
|
|
89
|
+
case 'custom': {
|
|
90
|
+
const customDef = _customFields.get(field.fieldType);
|
|
91
|
+
return customDef?.tsType ?? 'unknown';
|
|
92
|
+
}
|
|
85
93
|
default:
|
|
86
94
|
return 'any';
|
|
87
95
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { generateTsTypeFromFields } from './fields.js';
|
|
3
|
+
import { generateTsTypeFromFields, setGeneratorCustomFields } from './fields.js';
|
|
4
4
|
import { generateTsTypeFromFormFields } from './formFields.js';
|
|
5
5
|
import { generateZodSchemaStringFromFormFieldsAsString } from './formFieldSchemaToString.js';
|
|
6
6
|
import { toPascalCase } from './utils.js';
|
|
@@ -203,6 +203,14 @@ function generateRemote(config) {
|
|
|
203
203
|
writeFileSync(filePath, code);
|
|
204
204
|
}
|
|
205
205
|
export function generateRuntime(config) {
|
|
206
|
+
// Build custom fields map from plugins for type generation
|
|
207
|
+
const customFields = new Map();
|
|
208
|
+
for (const plugin of config.plugins ?? []) {
|
|
209
|
+
for (const field of plugin.fields ?? []) {
|
|
210
|
+
customFields.set(field.fieldType, field);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
setGeneratorCustomFields(customFields);
|
|
206
214
|
createCmsRuntimeDir();
|
|
207
215
|
generateTypes(config);
|
|
208
216
|
generateAPI(config);
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { drizzle } from 'drizzle-orm/postgres-js';
|
|
1
2
|
import type { Config } from './types.js';
|
|
3
|
+
import * as schema from './schema/index.js';
|
|
2
4
|
import type { DatabaseAdapter } from '../types/adapters/db.js';
|
|
3
|
-
export
|
|
5
|
+
export type DatabaseAdapterWithDrizzle = DatabaseAdapter & {
|
|
6
|
+
_drizzle: ReturnType<typeof drizzle<typeof schema>>;
|
|
7
|
+
};
|
|
8
|
+
export declare function pg(config: Config): DatabaseAdapterWithDrizzle;
|
|
@@ -88,6 +88,7 @@ export function pg(config) {
|
|
|
88
88
|
const client = postgres(config.databaseUrl);
|
|
89
89
|
const db = drizzle(client, { schema });
|
|
90
90
|
return {
|
|
91
|
+
_drizzle: db,
|
|
91
92
|
createEntry: async (data) => {
|
|
92
93
|
return db.transaction(async (tx) => {
|
|
93
94
|
const [newEntry] = await tx.insert(schema.entriesTable).values(data).returning();
|