rimecms 0.28.0 → 0.28.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/config/server/validate.server.js +60 -28
- package/dist/core/dev/cli/build/templates.d.ts +1 -1
- package/dist/core/dev/cli/build/templates.js +5 -2
- package/dist/core/dev/cli/generate/index.server.js +12 -8
- package/dist/core/dev/cli/index.js +8 -3
- package/dist/core/dev/vite/index.server.js +7 -3
- package/dist/core/rime.server.d.ts +2 -16
- package/dist/core/rime.server.js +11 -10
- package/package.json +1 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { isAuthConfig } from '../../collections/auth/util.js';
|
|
2
2
|
import cache from '../../dev/cache/index.server.js';
|
|
3
3
|
import { isFormField } from '../../fields/util.js';
|
|
4
|
+
import { logger } from '../../logger/index.server';
|
|
4
5
|
import { isBlocksFieldRaw } from '../../../fields/blocks/index.js';
|
|
5
6
|
import { isGroupFieldRaw } from '../../../fields/group/index.js';
|
|
7
|
+
import { isRelationField } from '../../../fields/relation';
|
|
6
8
|
import { isSelectField } from '../../../fields/select/index.js';
|
|
7
|
-
import { isTabsFieldRaw } from '../../../fields/tabs
|
|
9
|
+
import { isTabsFieldRaw } from '../../../fields/tabs';
|
|
8
10
|
import { isTreeFieldRaw } from '../../../fields/tree/index.js';
|
|
9
11
|
function hasDuplicates(arr) {
|
|
10
12
|
return [...new Set(arr.filter((e, i, a) => a.indexOf(e) !== i))];
|
|
@@ -45,11 +47,11 @@ function hasUsersSlug(config) {
|
|
|
45
47
|
const validateFields = (config) => {
|
|
46
48
|
let errors = [];
|
|
47
49
|
for (const collection of config.collections || []) {
|
|
48
|
-
const collectionErrors = validateDocumentFields(collection);
|
|
50
|
+
const collectionErrors = validateDocumentFields(collection, config);
|
|
49
51
|
errors = [...errors, ...collectionErrors];
|
|
50
52
|
}
|
|
51
53
|
for (const area of config.areas || []) {
|
|
52
|
-
const collectionErrors = validateDocumentFields(area);
|
|
54
|
+
const collectionErrors = validateDocumentFields(area, config);
|
|
53
55
|
errors = [...errors, ...collectionErrors];
|
|
54
56
|
}
|
|
55
57
|
return errors;
|
|
@@ -57,12 +59,12 @@ const validateFields = (config) => {
|
|
|
57
59
|
/**
|
|
58
60
|
*
|
|
59
61
|
*/
|
|
60
|
-
const validateDocumentFields = (config) => {
|
|
62
|
+
const validateDocumentFields = (documentConfig, config) => {
|
|
61
63
|
const errors = [];
|
|
62
|
-
const isCollection = (
|
|
63
|
-
const isAuth = isCollection(
|
|
64
|
+
const isCollection = (documentConfig) => documentConfig.type === 'collection';
|
|
65
|
+
const isAuth = isCollection(documentConfig) && isAuthConfig(documentConfig);
|
|
64
66
|
const registeredBlocks = {};
|
|
65
|
-
const fieldsCompiled =
|
|
67
|
+
const fieldsCompiled = documentConfig.fields.map((f) => f.compile());
|
|
66
68
|
if (isAuth) {
|
|
67
69
|
const rolesField = fieldsCompiled
|
|
68
70
|
.filter(isFormField)
|
|
@@ -73,28 +75,37 @@ const validateDocumentFields = (config) => {
|
|
|
73
75
|
.filter(isFormField)
|
|
74
76
|
.find((f) => f.name === 'email' && f.type === 'email');
|
|
75
77
|
if (!rolesField)
|
|
76
|
-
errors.push(`Field roles is missing in collection ${
|
|
77
|
-
if (!emailField &&
|
|
78
|
-
errors.push(`Field email is missing in collection ${
|
|
78
|
+
errors.push(`Field roles is missing in collection ${documentConfig.slug}`);
|
|
79
|
+
if (!emailField && documentConfig.auth.type !== 'apiKey')
|
|
80
|
+
errors.push(`Field email is missing in collection ${documentConfig.slug}`);
|
|
79
81
|
if (!nameField)
|
|
80
|
-
errors.push(`Field name is missing in collection ${
|
|
82
|
+
errors.push(`Field name is missing in collection ${documentConfig.slug}`);
|
|
81
83
|
if (!rolesField.many)
|
|
82
84
|
errors.push(`Field roles must have "many" enabled : select('roles').options(...).many(), even with a single option`);
|
|
83
85
|
}
|
|
84
86
|
const validateBlockField = (fields, blockType) => {
|
|
85
87
|
const reserved = ['path', 'type', 'ownerId', 'position', 'locale'];
|
|
86
88
|
for (const key of reserved) {
|
|
87
|
-
if (fields
|
|
89
|
+
if (fields
|
|
90
|
+
.filter(isFormField)
|
|
91
|
+
.map((f) => f.name)
|
|
92
|
+
.filter((name) => name === key).length > 1) {
|
|
88
93
|
errors.push(`${key} is a reserved field in blocks (block ${blockType})`);
|
|
89
94
|
}
|
|
90
95
|
}
|
|
91
96
|
};
|
|
92
|
-
const
|
|
97
|
+
const validateRelationField = (field) => {
|
|
98
|
+
const collectionsSlugs = (config.collections || []).map((c) => c.slug);
|
|
99
|
+
if (!collectionsSlugs.includes(field.relationTo)) {
|
|
100
|
+
errors.push(`Relation field ${field.name} references unknown collection ${field.relationTo}, in ${documentConfig.type} ${documentConfig.slug}`);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const validateFields = (fields) => {
|
|
93
104
|
// Check for field name duplication at this level
|
|
94
|
-
const duplicates = hasDuplicates(fields.map((f) => f.name));
|
|
105
|
+
const duplicates = hasDuplicates(fields.filter(isFormField).map((f) => f.name));
|
|
95
106
|
if (duplicates.length) {
|
|
96
107
|
for (const duplicate of duplicates) {
|
|
97
|
-
errors.push(`Duplicate field '${duplicate}' in ${
|
|
108
|
+
errors.push(`Duplicate field '${duplicate}' in ${documentConfig.type} '${documentConfig.slug}'`);
|
|
98
109
|
}
|
|
99
110
|
}
|
|
100
111
|
function validateFieldName(name) {
|
|
@@ -104,16 +115,33 @@ const validateDocumentFields = (config) => {
|
|
|
104
115
|
// Check if string matches pattern and doesn't contain spaces or hyphens
|
|
105
116
|
return pattern.test(name) && !name.includes('-') && !name.includes(' ');
|
|
106
117
|
}
|
|
118
|
+
function validateTabs(field) {
|
|
119
|
+
const duplicates = hasDuplicates(field.tabs.map((t) => t.name));
|
|
120
|
+
if (duplicates.length) {
|
|
121
|
+
errors.push(`Dupplicate tab name ${duplicates} in ${documentConfig.slug}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
107
124
|
for (const field of fields) {
|
|
125
|
+
// Recursive check first into Tabs since tabs are not Formfields
|
|
126
|
+
if (isTabsFieldRaw(field)) {
|
|
127
|
+
validateTabs(field);
|
|
128
|
+
for (const tab of field.tabs) {
|
|
129
|
+
validateFields(tab.fields);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// If field is not a Formfield eg. Separator then continue
|
|
133
|
+
if (!isFormField(field)) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
108
136
|
// Check that a field wich has field._root = true is not localized
|
|
109
137
|
if ('_root' in field && field._root && field.localized) {
|
|
110
|
-
errors.push(`Field ${field.name} of ${
|
|
138
|
+
errors.push(`Field ${field.name} of ${documentConfig.type} ${documentConfig.slug} with _root = true, can't be localized`);
|
|
111
139
|
}
|
|
112
140
|
// Check for malformed field.name
|
|
113
141
|
if (!validateFieldName(field.name)) {
|
|
114
|
-
errors.push(`Field ${field.name} of ${
|
|
142
|
+
errors.push(`Field ${field.name} of ${documentConfig.type} ${documentConfig.slug} should be camelCase`);
|
|
115
143
|
}
|
|
116
|
-
// Recursive check into Blocks
|
|
144
|
+
// Recursive check into Blocks
|
|
117
145
|
if (isBlocksFieldRaw(field)) {
|
|
118
146
|
for (const block of field.blocks) {
|
|
119
147
|
if (block.name in registeredBlocks) {
|
|
@@ -125,24 +153,25 @@ const validateDocumentFields = (config) => {
|
|
|
125
153
|
else {
|
|
126
154
|
registeredBlocks[block.name] = block;
|
|
127
155
|
}
|
|
128
|
-
|
|
156
|
+
validateFields(block.fields.filter(isFormField));
|
|
129
157
|
validateBlockField(block.fields.filter(isFormField), block.name);
|
|
130
158
|
}
|
|
159
|
+
// Recursive check into Tree
|
|
131
160
|
}
|
|
132
161
|
else if (isTreeFieldRaw(field)) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
else if (isTabsFieldRaw(field)) {
|
|
136
|
-
for (const tab of field.tabs) {
|
|
137
|
-
validateFormFields(tab.fields.filter(isFormField));
|
|
138
|
-
}
|
|
162
|
+
validateFields(field.fields.filter(isFormField));
|
|
163
|
+
// Recursive check into Tabs
|
|
139
164
|
}
|
|
140
165
|
else if (isGroupFieldRaw(field)) {
|
|
141
|
-
|
|
166
|
+
validateFields(field.fields.filter(isFormField));
|
|
167
|
+
// Check relation field
|
|
168
|
+
}
|
|
169
|
+
else if (isRelationField(field)) {
|
|
170
|
+
validateRelationField(field);
|
|
142
171
|
}
|
|
143
172
|
}
|
|
144
173
|
};
|
|
145
|
-
|
|
174
|
+
validateFields(documentConfig.fields.map((f) => f.compile()));
|
|
146
175
|
return errors;
|
|
147
176
|
};
|
|
148
177
|
const hasDatabase = (config) => {
|
|
@@ -163,6 +192,7 @@ function validateAuthCollections(config) {
|
|
|
163
192
|
return errors;
|
|
164
193
|
}
|
|
165
194
|
function validate(config) {
|
|
195
|
+
logger.debug('Validating config...');
|
|
166
196
|
const validateFunctions = [
|
|
167
197
|
hasDuplicateSlug,
|
|
168
198
|
hasUsersSlug,
|
|
@@ -174,9 +204,11 @@ function validate(config) {
|
|
|
174
204
|
const errors = isValid(config);
|
|
175
205
|
if (errors.length) {
|
|
176
206
|
cache.clear();
|
|
177
|
-
|
|
207
|
+
errors.map((err) => logger.error(err));
|
|
208
|
+
return false;
|
|
178
209
|
}
|
|
179
210
|
}
|
|
211
|
+
logger.debug('Config is valid');
|
|
180
212
|
return true;
|
|
181
213
|
}
|
|
182
214
|
export default validate;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const polkaServer = "import polka from 'polka';\nimport serveStatic from 'serve-static';\nimport { handler } from './build/handler.js';\n\nconst serve = serveStatic('./static');\n\npolka()\n\t.use(serve)\n\t.use(handler)\n\t.listen(
|
|
1
|
+
export declare const polkaServer = "import polka from 'polka';\nimport serveStatic from 'serve-static';\nimport { handler } from './build/handler.js';\n\nconst serve = serveStatic('./static');\n\nconst port = process.env.PORT || 3000;\nconst host = process.env.HOST || '127.0.0.1';\n\npolka()\n\t.use(serve)\n\t.use(handler)\n\t.listen(port, host, () => {\n\t\tconsole.log(`server running on ${host}:${port}`);\n\t});\n";
|
|
@@ -4,10 +4,13 @@ import { handler } from './build/handler.js';
|
|
|
4
4
|
|
|
5
5
|
const serve = serveStatic('./static');
|
|
6
6
|
|
|
7
|
+
const port = process.env.PORT || 3000;
|
|
8
|
+
const host = process.env.HOST || '127.0.0.1';
|
|
9
|
+
|
|
7
10
|
polka()
|
|
8
11
|
.use(serve)
|
|
9
12
|
.use(handler)
|
|
10
|
-
.listen(
|
|
11
|
-
console.log(
|
|
13
|
+
.listen(port, host, () => {
|
|
14
|
+
console.log(\`server running on \${host}:\${port}\`);
|
|
12
15
|
});
|
|
13
16
|
`;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import cache from '../../cache/index.server.js';
|
|
2
1
|
import { sanitize } from '../../generate/sanitize/index.server.js';
|
|
3
2
|
import { ensureGeneratedConfig, ensureUserConfigExist } from '../../../ensure.server.js';
|
|
4
3
|
import { logger } from '../../../logger/index.server.js';
|
|
@@ -62,18 +61,23 @@ export const generate = async (args) => {
|
|
|
62
61
|
ensureUserConfigExist();
|
|
63
62
|
await sanitizeConfig();
|
|
64
63
|
const importPathJS = ensureGeneratedConfig();
|
|
65
|
-
logger.info('
|
|
64
|
+
logger.info('Starting vite server...');
|
|
66
65
|
const vite = await createServer();
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
try {
|
|
67
|
+
const mod = await vite.ssrLoadModule(importPathJS);
|
|
68
|
+
// The generated config's default export is the createRime() promise;
|
|
69
|
+
// ssrLoadModule only awaits the module's synchronous evaluation, so we
|
|
70
|
+
// must await it directly to observe init failures (e.g. config validation).
|
|
71
|
+
await mod.default;
|
|
72
|
+
logger.info('[✓] Generation completed successfully');
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
await vite.close();
|
|
70
76
|
}
|
|
71
|
-
await vite.close();
|
|
72
|
-
logger.info('[✓] Generation completed successfully');
|
|
73
77
|
}
|
|
74
78
|
const [error] = await trycatch(run);
|
|
75
79
|
if (error) {
|
|
76
|
-
logger.error('Error during generation:', error);
|
|
80
|
+
logger.error('[✗] Error during generation:', error);
|
|
77
81
|
throw error;
|
|
78
82
|
}
|
|
79
83
|
};
|
|
@@ -30,8 +30,13 @@ program
|
|
|
30
30
|
.option('-f, --force', 'Force generation, ignore cache, overwrite routes', false)
|
|
31
31
|
.action(async (args) => {
|
|
32
32
|
const generate = await import('./generate/index.server.js').then((m) => m.generate);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
try {
|
|
34
|
+
await generate({
|
|
35
|
+
force: args.force
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
process.exitCode = 1;
|
|
40
|
+
}
|
|
36
41
|
});
|
|
37
42
|
program.parse(process.argv);
|
|
@@ -36,15 +36,19 @@ export function rime() {
|
|
|
36
36
|
}
|
|
37
37
|
catch (error) {
|
|
38
38
|
logger.error('Error while sanitizing config:', error.message);
|
|
39
|
-
|
|
39
|
+
return;
|
|
40
40
|
}
|
|
41
41
|
// Trigger generation
|
|
42
42
|
try {
|
|
43
|
-
await server.ssrLoadModule(`src/lib/${OUTPUT_DIR}/rime.config.server.ts`);
|
|
43
|
+
const mod = await server.ssrLoadModule(`src/lib/${OUTPUT_DIR}/rime.config.server.ts`);
|
|
44
|
+
// The config's default export is the createRime() promise; ssrLoadModule
|
|
45
|
+
// only awaits the module's synchronous evaluation, so we must await it
|
|
46
|
+
// directly to observe init errors (e.g. invalid config) here instead of
|
|
47
|
+
// letting them become unhandled rejections that crash the dev server.
|
|
48
|
+
await mod.default;
|
|
44
49
|
}
|
|
45
50
|
catch (error) {
|
|
46
51
|
logger.error('Failed to reload the config', error.message);
|
|
47
|
-
throw error;
|
|
48
52
|
}
|
|
49
53
|
}
|
|
50
54
|
});
|
|
@@ -76,14 +76,7 @@ export declare function createRime<const C extends Config>(config: BuildConfig<C
|
|
|
76
76
|
appName: string;
|
|
77
77
|
baseURL: string;
|
|
78
78
|
trustedOrigins: string[];
|
|
79
|
-
isTrustedOrigin: (url: string, settings
|
|
80
|
-
/**
|
|
81
|
-
* Get a collection api
|
|
82
|
-
* @example
|
|
83
|
-
*
|
|
84
|
-
* rime.collection('pages').find({ query: 'where[isHome][equals]=true' })
|
|
85
|
-
*/
|
|
86
|
-
?: {
|
|
79
|
+
isTrustedOrigin: (url: string, settings?: {
|
|
87
80
|
allowRelativePaths: boolean;
|
|
88
81
|
}) => boolean;
|
|
89
82
|
oauthConfig: {
|
|
@@ -2089,14 +2082,7 @@ export declare function createRime<const C extends Config>(config: BuildConfig<C
|
|
|
2089
2082
|
appName: string;
|
|
2090
2083
|
baseURL: string;
|
|
2091
2084
|
trustedOrigins: string[];
|
|
2092
|
-
isTrustedOrigin: (url: string, settings
|
|
2093
|
-
/**
|
|
2094
|
-
* Get a collection api
|
|
2095
|
-
* @example
|
|
2096
|
-
*
|
|
2097
|
-
* rime.collection('pages').find({ query: 'where[isHome][equals]=true' })
|
|
2098
|
-
*/
|
|
2099
|
-
?: {
|
|
2085
|
+
isTrustedOrigin: (url: string, settings?: {
|
|
2100
2086
|
allowRelativePaths: boolean;
|
|
2101
2087
|
}) => boolean;
|
|
2102
2088
|
oauthConfig: {
|
package/dist/core/rime.server.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// import type { Adapter } from '../adapter-sqlite/index.server.js';
|
|
2
2
|
import { dev } from '$app/environment';
|
|
3
|
-
import devCache from './dev/cache/index.server.js';
|
|
4
3
|
import { betterAuth } from 'better-auth';
|
|
5
4
|
import { AreaAPI } from './areas/local-api.server.js';
|
|
6
5
|
import { CollectionAPI } from './collections/local-api.server.js';
|
|
@@ -29,13 +28,18 @@ export async function createRime(config) {
|
|
|
29
28
|
// Generate schema, types, routes
|
|
30
29
|
if (dev) {
|
|
31
30
|
const changed = writeMemo(config);
|
|
32
|
-
const valid =
|
|
33
|
-
if (
|
|
31
|
+
const valid = validate(config);
|
|
32
|
+
if (!valid) {
|
|
34
33
|
throw new RimeError('Config not valid');
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
}
|
|
35
|
+
if (changed) {
|
|
36
|
+
generateRoutes(config);
|
|
37
|
+
await generateSchema(config);
|
|
38
|
+
await generateTypes(config);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
logger.debug('Nothing to generate');
|
|
42
|
+
}
|
|
39
43
|
}
|
|
40
44
|
// Create adapter, consume the generated schema
|
|
41
45
|
const adapter = await createAdapter(configCtx);
|
|
@@ -53,9 +57,6 @@ export async function createRime(config) {
|
|
|
53
57
|
// Register dictionaries for panel Language
|
|
54
58
|
const dictionnaries = await registerTranslation(config.panel.language);
|
|
55
59
|
i18n.init(dictionnaries);
|
|
56
|
-
if (dev) {
|
|
57
|
-
devCache.delete('.init');
|
|
58
|
-
}
|
|
59
60
|
/**
|
|
60
61
|
* Function that define the locale to use in a request event
|
|
61
62
|
* based on this priority, high to low :
|