rimecms 0.31.3 → 0.31.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +6 -207
  2. package/dist/adapter-sqlite/auth.server.d.ts +1 -1
  3. package/dist/adapter-sqlite/generate-schema/templates.server.d.ts +2 -2
  4. package/dist/adapter-sqlite/generate-schema/templates.server.js +3 -1
  5. package/dist/adapter-sqlite/generate-schema/write.server.js +7 -3
  6. package/dist/core/config/auth/better-auth-hooks.server.d.ts +1 -1
  7. package/dist/core/config/auth/better-auth-hooks.server.js +1 -2
  8. package/dist/core/config/auth/better-auth-permissions.d.ts +27 -67
  9. package/dist/core/config/auth/better-auth.server.d.ts +649 -481
  10. package/dist/core/config/auth/better-auth.server.js +2 -1
  11. package/dist/core/config/server/write.server.js +5 -0
  12. package/dist/core/dev/cli/clear/index.server.js +5 -5
  13. package/dist/core/dev/cli/init/index.server.js +14 -28
  14. package/dist/core/dev/cli/init/templates.d.ts +12 -0
  15. package/dist/core/dev/cli/init/templates.js +47 -3
  16. package/dist/core/dev/constants.d.ts +15 -7
  17. package/dist/core/dev/constants.js +49 -10
  18. package/dist/core/dev/generate/routes/common.server.js +5 -2
  19. package/dist/core/dev/generate/sanitize/index.server.js +3 -3
  20. package/dist/core/dev/generate/types/index.server.js +3 -2
  21. package/dist/core/dev/vite/index.server.js +4 -8
  22. package/dist/core/ensure.server.d.ts +2 -2
  23. package/dist/core/ensure.server.js +17 -8
  24. package/dist/core/handlers/auth.server.js +2 -3
  25. package/dist/core/rime.server.d.ts +1302 -966
  26. package/dist/core/rime.server.js +6 -0
  27. package/dist/fields/number/component/number.css +0 -2
  28. package/dist/fields/relation/component/upload/upload.css +9 -0
  29. package/dist/fields/rich-text/component/drag-handle/drag-handle.css +0 -1
  30. package/dist/fields/rich-text/component/styles/rich-text.css +7 -0
  31. package/dist/fields/rich-text/core/extensions/drag-handle/helpers/findNextElementFromCursor.d.ts +1 -1
  32. package/dist/fields/rich-text/core/svelte/node-view-wrapper.svelte.d.ts +12 -5
  33. package/dist/panel/components/sections/collection/header/SelectUI.svelte.d.ts +3 -3
  34. package/dist/panel/components/sections/document/AuthAPIKeyDialog.svelte.d.ts +3 -3
  35. package/dist/panel/components/sections/live/Consumer.svelte.d.ts +11 -4
  36. package/dist/panel/components/sections/live/LiveEdit.svelte.d.ts +13 -6
  37. package/dist/panel/components/sections/live/LiveFloatingUI.svelte.d.ts +3 -3
  38. package/dist/panel/components/ui/calendar/calendar.css +0 -11
  39. package/dist/panel/components/ui/command/command-group.css +0 -1
  40. package/dist/panel/components/ui/label/label.css +1 -0
  41. package/dist/panel/components/ui/radio-group/radio-group.css +0 -2
  42. package/dist/panel/components/ui/scroll-area/scroll-area.css +4 -0
  43. package/dist/panel/components/ui/sheet/sheet.css +16 -0
  44. package/dist/panel/components/ui/tabs/tabs.css +0 -7
  45. package/dist/panel/style/index.css +0 -6
  46. package/dist/panel/util/auth.js +1 -1
  47. package/package.json +8 -6
  48. package/dist/panel/components/ui/dropdown-menu/index.d.ts +0 -23
@@ -1,6 +1,7 @@
1
1
  import { dev } from '$app/environment';
2
2
  import { env } from '$env/dynamic/public';
3
- import { admin as adminPlugin, apiKey } from 'better-auth/plugins';
3
+ import { apiKey } from '@better-auth/api-key';
4
+ import { admin as adminPlugin } from 'better-auth/plugins';
4
5
  import { betterAuthAfterHook, betterAuthBeforeHook } from './better-auth-hooks.server.js';
5
6
  import { accessControl, admin, staff, user } from './better-auth-permissions.js';
6
7
  export function getBaseAuthConfig(ctx) {
@@ -1,5 +1,6 @@
1
1
  import { flatten } from 'flat';
2
2
  import cache from '../../dev/cache/index.server.js';
3
+ import { CONFIG_DIR } from '../../dev/constants.js';
3
4
  /**
4
5
  * We actually need to serialize config values that will trigger
5
6
  * types, routes, or schema generations, meaning that for now
@@ -59,6 +60,10 @@ const writeMemo = (config) => {
59
60
  throw new Error(`Config error : Unable to parse value for key ${key}: ${err.message}`);
60
61
  }
61
62
  })
63
+ // Not part of the user's config object, but generated output (routes, schema, hooks) is
64
+ // relative to it — a RIME_CONFIG_DIR change must invalidate the memo just like a config
65
+ // change does, even when the config content itself is byte-identical.
66
+ .concat(`CONFIG_DIR:${CONFIG_DIR}`)
62
67
  .join('\n');
63
68
  const cached = cache.get('config');
64
69
  if (cached !== memoStr) {
@@ -2,14 +2,14 @@ import { RIME_DEV_CACHE_DIR } from '../../../constant.server.js';
2
2
  import { logger } from '../../../logger/index.server.js';
3
3
  import { rmSync } from 'node:fs';
4
4
  import path from 'node:path';
5
- import { INPUT_DIR, OUTPUT_DIR } from '../../constants.js';
5
+ import { CONFIG_DIR, GENERATED_DIR } from '../../constants.js';
6
6
  import { prompt } from '../util/prompt.server.js';
7
7
  const clearMessage = `Are you sure you want to delete all related rime files (Y/n):
8
8
  - ./static/medias
9
9
  - ./db
10
10
  - ./src/routes/(rime)
11
- - ./src/lib/${INPUT_DIR}
12
- - ./src/lib/${OUTPUT_DIR}
11
+ - ./${CONFIG_DIR}
12
+ - ./${GENERATED_DIR}
13
13
  - ./src/app.generated.d.ts
14
14
  - ./src/rime.generated.d.ts
15
15
  - ./src/rime.modules.generated.d.ts
@@ -28,8 +28,8 @@ export const clear = async (args) => {
28
28
  // Remove directories
29
29
  rmSync(RIME_DEV_CACHE_DIR, { recursive: true, force: true });
30
30
  rmSync(path.join('src', 'routes', '(rime)'), { recursive: true, force: true });
31
- rmSync(path.join('src', 'lib', INPUT_DIR), { recursive: true, force: true });
32
- rmSync(path.join('src', 'lib', OUTPUT_DIR), { recursive: true, force: true });
31
+ rmSync(CONFIG_DIR, { recursive: true, force: true });
32
+ rmSync(GENERATED_DIR, { recursive: true, force: true });
33
33
  rmSync(path.join('db'), { recursive: true, force: true });
34
34
  rmSync(path.join('static', 'medias'), { recursive: true, force: true });
35
35
  // Remove files
@@ -9,7 +9,7 @@ import { cp, mkdir } from 'fs/promises';
9
9
  import fs from 'node:fs';
10
10
  import path from 'path';
11
11
  import { fileURLToPath } from 'url';
12
- import { INPUT_DIR } from '../../constants.js';
12
+ import { CONFIG_DIR, OUTPUT_DIR } from '../../constants.js';
13
13
  import { generate } from '../generate/index.server.js';
14
14
  import { installDependencies } from '../util/package-manager.server.js';
15
15
  import { getPackageInfoByKey } from '../util/package.server.js';
@@ -25,6 +25,7 @@ export const init = async ({ force, name: incomingName, skipInstall }) => {
25
25
  const envUpdates = {
26
26
  BETTER_AUTH_SECRET: randomId(32),
27
27
  PUBLIC_RIME_URL: 'http://localhost:5173',
28
+ RIME_CONFIG_DIR: CONFIG_DIR,
28
29
  '# RIME_SMTP_USER': 'user@mail.com',
29
30
  '# RIME_SMTP_PASSWORD': 'supersecret',
30
31
  '# RIME_SMTP_PORT': '465',
@@ -53,7 +54,7 @@ export const init = async ({ force, name: incomingName, skipInstall }) => {
53
54
  }
54
55
  }
55
56
  function setConfig(name) {
56
- const configDirPath = path.resolve(root, 'src/lib', INPUT_DIR);
57
+ const configDirPath = path.resolve(root, CONFIG_DIR);
57
58
  const configPath = path.join(configDirPath, 'rime.config.server.ts');
58
59
  if (!existsSync(configPath)) {
59
60
  if (!existsSync(configDirPath)) {
@@ -83,21 +84,24 @@ export const init = async ({ force, name: incomingName, skipInstall }) => {
83
84
  return;
84
85
  }
85
86
  let gitignoreContent = readFileSync(gitignorePath, 'utf-8');
87
+ // OUTPUT_DIR is derived from RIME_CONFIG_DIR, so its first character isn't always '+' —
88
+ // escape every regex metacharacter generically instead of hand-picking one to backslash.
89
+ const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
86
90
  const updates = [
87
- '\\.cache',
91
+ '.cache',
88
92
  '/logs',
89
93
  '/db',
90
- '\\+rime.generated',
94
+ OUTPUT_DIR,
91
95
  'src/app.generated.d.ts',
92
96
  'src/rime.generated.d.ts'
93
97
  ];
94
98
  if (!gitignoreContent.includes('# rime'))
95
99
  gitignoreContent += '\n# rime';
96
100
  for (const line of updates) {
97
- const exists = gitignoreContent.match(new RegExp(`^${line}`, 'm'));
101
+ const exists = gitignoreContent.match(new RegExp(`^${escapeRegExp(line)}`, 'm'));
98
102
  if (!exists) {
99
103
  // Add new value if doesn't exist
100
- gitignoreContent += `\n${line.replace('\\', '')}`;
104
+ gitignoreContent += `\n${line}`;
101
105
  }
102
106
  }
103
107
  writeFileSync(gitignorePath, gitignoreContent);
@@ -108,10 +112,11 @@ export const init = async ({ force, name: incomingName, skipInstall }) => {
108
112
  if (!existsSync(drizzleConfigPath)) {
109
113
  writeFileSync(drizzleConfigPath, templates.drizzleConfig(name.toString()));
110
114
  logger.info('[✓] Drizzle config added');
115
+ return;
111
116
  }
112
- else {
113
- logger.info('[✓] Drizzle config already exists (skip)');
114
- }
117
+ logger.info(templates.regenerateDrizzleConfig(root)
118
+ ? '[✓] Drizzle config schema path updated'
119
+ : '[✓] Drizzle config already exists (skip)');
115
120
  }
116
121
  function configureVite() {
117
122
  const configPath = path.resolve(root, 'vite.config.ts');
@@ -182,23 +187,6 @@ export const init = async ({ force, name: incomingName, skipInstall }) => {
182
187
  }
183
188
  return null;
184
189
  }
185
- function setHooks() {
186
- const hooksPath = path.join(root, 'src', 'hooks.server.ts');
187
- const srcDir = path.join(root, 'src');
188
- // Check if file exists
189
- if (!existsSync(hooksPath)) {
190
- // Ensure src directory exists
191
- if (!existsSync(srcDir)) {
192
- mkdirSync(srcDir, { recursive: true });
193
- }
194
- // Create hooks.server.ts with template content
195
- writeFileSync(hooksPath, templates.hooks(), 'utf-8');
196
- logger.info('[✓] hooks.server.ts created');
197
- }
198
- else {
199
- logger.info('[✓] hooks.server.ts already exists (skip)');
200
- }
201
- }
202
190
  async function copyAssets() {
203
191
  try {
204
192
  const currentDir = path.dirname(fileURLToPath(import.meta.url));
@@ -219,7 +207,6 @@ export const init = async ({ force, name: incomingName, skipInstall }) => {
219
207
  setConfig(name);
220
208
  setDatabase();
221
209
  setDrizzle(name);
222
- setHooks();
223
210
  configureVite();
224
211
  await copyAssets();
225
212
  !skipInstall && installDependencies();
@@ -244,7 +231,6 @@ export const init = async ({ force, name: incomingName, skipInstall }) => {
244
231
  setConfig(name);
245
232
  setDatabase();
246
233
  setDrizzle(name);
247
- setHooks();
248
234
  configureVite();
249
235
  await copyAssets();
250
236
  !skipInstall && installDependencies();
@@ -2,3 +2,15 @@ export declare const env: () => string;
2
2
  export declare const defaultConfig: (name: string) => string;
3
3
  export declare const drizzleConfig: (name: string) => string;
4
4
  export declare const hooks: () => string;
5
+ /** Writes src/hooks.server.ts if missing, or rewrites it if its content would differ from
6
+ * what's expected now (e.g. after RIME_CONFIG_DIR changes the import path) — called both at
7
+ * `rime init` and whenever the dev server detects a config change, so it never goes stale
8
+ * between the two. Returns whether it wrote anything. */
9
+ export declare function regenerateHooks(root?: string): boolean;
10
+ /** Patches drizzle.config.ts's schema path line if it's stale (e.g. after RIME_CONFIG_DIR
11
+ * changes) — only that one line, since the rest of the file may carry real customizations
12
+ * (verbose, custom fields, etc.). Must run before generateSchema()'s own drizzle-kit
13
+ * generate/migrate shell-out, which reads this file from disk. No-op if the file doesn't
14
+ * exist — creating it from scratch needs a project name, which is `rime init`'s job
15
+ * (setDrizzle() in init/index.server.ts). Returns whether it patched anything. */
16
+ export declare function regenerateDrizzleConfig(root?: string): boolean;
@@ -1,8 +1,11 @@
1
1
  import { randomId } from '../../../../util/random.js';
2
- import { configImportPaths, OUTPUT_DIR } from '../../constants.js';
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { CONFIG_DIR, GENERATED_DIR, configImportPaths } from '../../constants.js';
3
5
  const PACKAGE = 'rimecms';
4
6
  export const env = () => `BETTER_AUTH_SECRET=${randomId(32)}
5
7
  PUBLIC_RIME_URL=http://localhost:5173
8
+ RIME_CONFIG_DIR=${CONFIG_DIR}
6
9
 
7
10
  # RIME_CACHE_ENABLED=false
8
11
  # RIME_SMTP_USER=user@mail.com
@@ -40,7 +43,7 @@ export const drizzleConfig = (name) => `
40
43
  import { defineConfig, type Config } from 'drizzle-kit';
41
44
 
42
45
  export const config: Config = {
43
- schema: './src/lib/${OUTPUT_DIR}/schema.server.ts',
46
+ schema: './${GENERATED_DIR}/schema.server.ts',
44
47
  out: './db',
45
48
  strict: false,
46
49
  dialect: 'sqlite',
@@ -53,9 +56,50 @@ export default defineConfig(config);
53
56
  `;
54
57
  // Regenerated fresh by rime init every time (setHooks() only skips if the file already
55
58
  // exists, and `rime clear`/useConfig.js delete it first) — safe to make mode-aware.
59
+ const HOOKS_DIR = path.resolve(process.cwd(), 'src');
56
60
  export const hooks = () => `import { sequence } from '@sveltejs/kit/hooks';
57
61
  import { handlers } from '${PACKAGE}/server';
58
- import config from '.${configImportPaths().server.replace('$lib', '/lib')}';
62
+ import config from '${configImportPaths(HOOKS_DIR).server}';
59
63
 
60
64
  export const handle = sequence(...(await handlers(config)));
61
65
  `;
66
+ /** Writes src/hooks.server.ts if missing, or rewrites it if its content would differ from
67
+ * what's expected now (e.g. after RIME_CONFIG_DIR changes the import path) — called both at
68
+ * `rime init` and whenever the dev server detects a config change, so it never goes stale
69
+ * between the two. Returns whether it wrote anything. */
70
+ export function regenerateHooks(root = process.cwd()) {
71
+ const hooksPath = path.join(root, 'src', 'hooks.server.ts');
72
+ const srcDir = path.join(root, 'src');
73
+ const content = hooks();
74
+ if (!existsSync(hooksPath)) {
75
+ if (!existsSync(srcDir)) {
76
+ mkdirSync(srcDir, { recursive: true });
77
+ }
78
+ writeFileSync(hooksPath, content, 'utf-8');
79
+ return true;
80
+ }
81
+ if (readFileSync(hooksPath, 'utf-8') !== content) {
82
+ writeFileSync(hooksPath, content, 'utf-8');
83
+ return true;
84
+ }
85
+ return false;
86
+ }
87
+ const DRIZZLE_SCHEMA_LINE_REGEX = /^\s*schema:\s*['"][^'"]*['"],?\s*$/m;
88
+ /** Patches drizzle.config.ts's schema path line if it's stale (e.g. after RIME_CONFIG_DIR
89
+ * changes) — only that one line, since the rest of the file may carry real customizations
90
+ * (verbose, custom fields, etc.). Must run before generateSchema()'s own drizzle-kit
91
+ * generate/migrate shell-out, which reads this file from disk. No-op if the file doesn't
92
+ * exist — creating it from scratch needs a project name, which is `rime init`'s job
93
+ * (setDrizzle() in init/index.server.ts). Returns whether it patched anything. */
94
+ export function regenerateDrizzleConfig(root = process.cwd()) {
95
+ const drizzleConfigPath = path.join(root, 'drizzle.config.ts');
96
+ if (!existsSync(drizzleConfigPath))
97
+ return false;
98
+ const expectedSchemaLine = ` schema: './${GENERATED_DIR}/schema.server.ts',`;
99
+ const content = readFileSync(drizzleConfigPath, 'utf-8');
100
+ if (DRIZZLE_SCHEMA_LINE_REGEX.test(content) && !content.includes(expectedSchemaLine)) {
101
+ writeFileSync(drizzleConfigPath, content.replace(DRIZZLE_SCHEMA_LINE_REGEX, expectedSchemaLine));
102
+ return true;
103
+ }
104
+ return false;
105
+ }
@@ -1,15 +1,23 @@
1
- export declare const OUTPUT_DIR = "+rime.generated";
2
- export declare const INPUT_DIR = "+rime";
3
- /** Where the panel/live route templates (common.server.ts) should import the client config
4
- * from, and where the dev-server watcher/hot-reload should read the server config from —
5
- * one source of truth instead of hardcoding the path at each call site. */
6
- export declare function configImportPaths(): {
1
+ export declare const CONFIG_DIR: string;
2
+ export declare const GENERATED_DIR: string;
3
+ export declare const INPUT_DIR: string;
4
+ export declare const OUTPUT_DIR: string;
5
+ /** Turns an absolute path into an import specifier relative to `fromDir`, always prefixed
6
+ * with `./` or `../` so it never gets mistaken for a bare package specifier. */
7
+ export declare function relativeImportSpecifier(fromDir: string, toAbsolutePath: string): string;
8
+ export declare function generatedConfigServerPath(): string;
9
+ /** Where the panel/live route templates (common.server.ts) and the generated hooks.server.ts
10
+ * should import the client/server config from — a relative import specifier computed from
11
+ * `fromDir` (the absolute directory of the file doing the importing), so it resolves whether
12
+ * GENERATED_DIR lives under src/lib or elsewhere. One source of truth instead of hardcoding the
13
+ * path (or relying on the $lib alias, which only ever points at src/lib) at each call site. */
14
+ export declare function configImportPaths(fromDir: string): {
7
15
  client: string;
8
16
  server: string;
9
17
  };
10
18
  /** Same idea as configImportPaths — one source of truth for where the generated schema
11
19
  * lives instead of hardcoding the path at each call site. */
12
- export declare function schemaPath(root?: string): string;
20
+ export declare function schemaPath(projectRoot?: string): string;
13
21
  /**
14
22
  * True when `metaUrl` (pass the caller's own `import.meta.url` — this can't be one shared
15
23
  * value, it depends on which file is asking) is running from inside an installed `rimecms`
@@ -1,19 +1,58 @@
1
+ import dotenv from 'dotenv';
1
2
  import path from 'node:path';
2
- export const OUTPUT_DIR = '+rime.generated';
3
- export const INPUT_DIR = '+rime';
4
- /** Where the panel/live route templates (common.server.ts) should import the client config
5
- * from, and where the dev-server watcher/hot-reload should read the server config from —
6
- * one source of truth instead of hardcoding the path at each call site. */
7
- export function configImportPaths() {
3
+ // Must run before RIME_CONFIG_DIR is read below. ES import hoisting evaluates this module's
4
+ // top-level code before any *importing* file's own top-level statements — including a
5
+ // dotenv.config() call there — so loading .env here, first, is the only way every entry point
6
+ // (CLI, dev-server, drizzle.config.ts, etc.) reliably sees it. override:true so .env is the
7
+ // explicit source of truth, not whatever a shell plugin (autoenv, direnv, ...) already exported
8
+ // from some other project's .env. The only dotenv.config() call in the codebase — a second one
9
+ // elsewhere would race this one.
10
+ dotenv.config({ override: true });
11
+ const root = process.cwd();
12
+ const SRC_ROOT = path.resolve(root, 'src');
13
+ export const CONFIG_DIR = process.env.RIME_CONFIG_DIR || 'src/+rime';
14
+ export const GENERATED_DIR = `${CONFIG_DIR}.generated`;
15
+ export const INPUT_DIR = path.basename(CONFIG_DIR);
16
+ export const OUTPUT_DIR = `${INPUT_DIR}.generated`;
17
+ const RESERVED_DIRS = new Set(['lib', 'routes', 'static', 'params'].map((d) => path.resolve(SRC_ROOT, d)));
18
+ for (const dir of [CONFIG_DIR, GENERATED_DIR]) {
19
+ const resolved = path.resolve(root, dir);
20
+ if (resolved === SRC_ROOT || !resolved.startsWith(SRC_ROOT + path.sep)) {
21
+ throw new Error(`RIME_CONFIG_DIR must resolve to a subdirectory of ${SRC_ROOT} (got "${dir}")`);
22
+ }
23
+ if (RESERVED_DIRS.has(resolved)) {
24
+ throw new Error(`RIME_CONFIG_DIR must not be src/lib, src/routes, src/static, or src/params (got "${dir}")`);
25
+ }
26
+ }
27
+ /** Turns an absolute path into an import specifier relative to `fromDir`, always prefixed
28
+ * with `./` or `../` so it never gets mistaken for a bare package specifier. */
29
+ export function relativeImportSpecifier(fromDir, toAbsolutePath) {
30
+ const rel = path.relative(fromDir, toAbsolutePath).split(path.sep).join('/');
31
+ return rel.startsWith('.') ? rel : `./${rel}`;
32
+ }
33
+ /** Root-relative filesystem path (posix-style) to a file inside the generated config dir —
34
+ * for Vite's ssrLoadModule, which wants a real file path, not an import specifier. */
35
+ function generatedFilePath(file) {
36
+ return path.relative(root, path.resolve(root, GENERATED_DIR, file)).split(path.sep).join('/');
37
+ }
38
+ export function generatedConfigServerPath() {
39
+ return generatedFilePath('rime.config.server.ts');
40
+ }
41
+ /** Where the panel/live route templates (common.server.ts) and the generated hooks.server.ts
42
+ * should import the client/server config from — a relative import specifier computed from
43
+ * `fromDir` (the absolute directory of the file doing the importing), so it resolves whether
44
+ * GENERATED_DIR lives under src/lib or elsewhere. One source of truth instead of hardcoding the
45
+ * path (or relying on the $lib alias, which only ever points at src/lib) at each call site. */
46
+ export function configImportPaths(fromDir) {
8
47
  return {
9
- client: `$lib/${OUTPUT_DIR}/rime.config.js`,
10
- server: `$lib/${OUTPUT_DIR}/rime.config.server.js`
48
+ client: relativeImportSpecifier(fromDir, path.resolve(root, GENERATED_DIR, 'rime.config.js')),
49
+ server: relativeImportSpecifier(fromDir, path.resolve(root, GENERATED_DIR, 'rime.config.server.js'))
11
50
  };
12
51
  }
13
52
  /** Same idea as configImportPaths — one source of truth for where the generated schema
14
53
  * lives instead of hardcoding the path at each call site. */
15
- export function schemaPath(root = process.cwd()) {
16
- return path.resolve(root, 'src/lib', OUTPUT_DIR, 'schema.server.ts');
54
+ export function schemaPath(projectRoot = process.cwd()) {
55
+ return path.resolve(projectRoot, GENERATED_DIR, 'schema.server.ts');
17
56
  }
18
57
  /**
19
58
  * True when `metaUrl` (pass the caller's own `import.meta.url` — this can't be one shared
@@ -1,5 +1,8 @@
1
1
  import { PACKAGE_NAME } from '../../../constant.server.js';
2
+ import path from 'node:path';
2
3
  import { configImportPaths } from '../../constants.js';
4
+ const PANEL_LAYOUT_DIR = path.resolve(process.cwd(), 'src/routes/(rime)/panel');
5
+ const LIVE_PAGE_DIR = path.resolve(process.cwd(), 'src/routes/(rime)/panel/live-edit');
3
6
  /**
4
7
  * Main base layout
5
8
  * /+layout.server.ts
@@ -146,7 +149,7 @@ export const load = (event: ServerLoadEvent) => event.locals.routes.panel.load.r
146
149
  const panelLayout = () => `
147
150
  <script>
148
151
  import { Panel } from '${PACKAGE_NAME}/panel';
149
- import config from '${configImportPaths().client}';
152
+ import config from '${configImportPaths(PANEL_LAYOUT_DIR).client}';
150
153
 
151
154
  const { children, data } = $props();
152
155
 
@@ -196,7 +199,7 @@ export const load = (event: ServerLoadEvent) => event.locals.routes.panel.load.d
196
199
  const livePage = () => `
197
200
  <script>
198
201
  import { Live } from '${PACKAGE_NAME}/panel';
199
- import config from '${configImportPaths().client}';
202
+ import config from '${configImportPaths(LIVE_PAGE_DIR).client}';
200
203
 
201
204
  const { data } = $props();
202
205
  </script>
@@ -5,7 +5,7 @@ import { babelParse } from 'ast-kit';
5
5
  import fs from 'node:fs';
6
6
  import path from 'node:path';
7
7
  import { logger } from '../../../logger/index.server.js';
8
- import { INPUT_DIR, OUTPUT_DIR } from '../../constants.js';
8
+ import { CONFIG_DIR, GENERATED_DIR, OUTPUT_DIR } from '../../constants.js';
9
9
 
10
10
  /**
11
11
  * $ prefix-based sanitizer that rules:
@@ -22,8 +22,8 @@ const TOP_LEVEL_CONFIG = 'rime.config.server.ts';
22
22
 
23
23
  export async function sanitize() {
24
24
  const root = process.cwd();
25
- const configDir = path.resolve(root, 'src/lib/', INPUT_DIR);
26
- const outputDir = path.resolve(root, 'src/lib/', OUTPUT_DIR);
25
+ const configDir = path.resolve(root, CONFIG_DIR);
26
+ const outputDir = path.resolve(root, GENERATED_DIR);
27
27
 
28
28
  logger.info('Sanitizing config...');
29
29
 
@@ -12,7 +12,7 @@ import { trycatchSync } from '../../../../util/function.js';
12
12
  import { capitalize, toPascalCase } from '../../../../util/string.js';
13
13
  import fs from 'node:fs';
14
14
  import path from 'node:path';
15
- import { isInstalledDependency, OUTPUT_DIR } from '../../constants.js';
15
+ import { GENERATED_DIR, isInstalledDependency, relativeImportSpecifier } from '../../constants.js';
16
16
  const IS_RIME_REPO = !isInstalledDependency(import.meta.url);
17
17
  // Relation type
18
18
  const relationValueType = `
@@ -259,7 +259,8 @@ export async function generateTypesString(config) {
259
259
  const blocksTypeNames = `export type BlockTypes = ${registeredBlocks.map((name) => `'${name}'`).join('|')}\n`;
260
260
  const anyBlock = `export type AnyBlock = ${registeredBlocks.map((name) => `Block${toPascalCase(name)}`).join('|')}\n`;
261
261
  const typeImports = `import type { ${Array.from(imports).join(', ')} } from '${PACKAGE_NAME}/types'`;
262
- const rimeConfigServerPath = `./lib/${OUTPUT_DIR}/rime.config.server.ts`;
262
+ // app.generated.d.ts always sits at src/app.generated.d.ts (see generateTypes() below).
263
+ const rimeConfigServerPath = relativeImportSpecifier(path.resolve(process.cwd(), 'src'), path.resolve(process.cwd(), GENERATED_DIR, 'rime.config.server.ts'));
263
264
  const locals = `declare global {
264
265
  namespace App {
265
266
  interface Locals {
@@ -1,15 +1,13 @@
1
- import dotenv from 'dotenv';
2
1
  import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
2
  import path from 'node:path';
4
3
  import { RIME_DEV_CACHE_DIR } from '../../constant.server.js';
5
4
  import { ensureHasInit } from '../../ensure.server.js';
6
5
  import { logger } from '../../logger/index.server.js';
7
6
  import { getPackageInfoByKey } from '../cli/util/package.server.js';
8
- import { INPUT_DIR, isInstalledDependency, OUTPUT_DIR, schemaPath } from '../constants.js';
7
+ import { CONFIG_DIR, GENERATED_DIR, generatedConfigServerPath, isInstalledDependency, schemaPath } from '../constants.js';
9
8
  import { findInstalledPackageRoot, findModulePair, scanModulePairs } from '../generate/runtime/index.server.js';
10
9
  import { parseExportNames } from '../generate/runtime/parse-exports.server.js';
11
10
  import { sanitize } from '../generate/sanitize/index.server.js';
12
- dotenv.config({ override: true });
13
11
  const dev = process.env.NODE_ENV === 'development';
14
12
  /** Picks the client or server side of a module.(server.)ts pair. When the requested side
15
13
  * wasn't authored (e.g. a collection's $url/$hooks, real only server-side, imported anyway by
@@ -132,9 +130,8 @@ export function rime() {
132
130
  try {
133
131
  // ssrLoadModule wants the real .ts file path, not a $lib/*.js-style import
134
132
  // specifier — different consumer than configImportPaths() (used for generated
135
- // source code), so computed directly here.
136
- const serverConfigPath = `src/lib/${OUTPUT_DIR}/rime.config.server.ts`;
137
- const mod = await server.ssrLoadModule(serverConfigPath);
133
+ // source code).
134
+ const mod = await server.ssrLoadModule(generatedConfigServerPath());
138
135
  // The config's default export is the createRime() promise; ssrLoadModule
139
136
  // only awaits the module's synchronous evaluation, so we must await it
140
137
  // directly to observe init errors (e.g. invalid config) here instead of
@@ -153,8 +150,7 @@ export function rime() {
153
150
  }
154
151
  }
155
152
  server.watcher.on('change', (modulePath) => {
156
- const isConfigChange = modulePath.includes(`src/lib/${INPUT_DIR}`) &&
157
- !modulePath.includes(`src/lib/${OUTPUT_DIR}`);
153
+ const isConfigChange = modulePath.includes(CONFIG_DIR) && !modulePath.includes(GENERATED_DIR);
158
154
  if (!isConfigChange)
159
155
  return;
160
156
  if (reloadTimer)
@@ -1,10 +1,10 @@
1
1
  import type { BuiltCollection } from '../types.js';
2
2
  /**
3
- * Ensure sanitize config exists — the generated server config inside rime.generated/.
3
+ * Ensure sanitize config exists — the generated server config inside the generated config dir.
4
4
  */
5
5
  export declare function ensureGeneratedConfig(): string;
6
6
  /**
7
- * Ensure user config exists — the src/lib/+rime/ folder convention.
7
+ * Ensure user config exists — the CONFIG_DIR folder convention.
8
8
  */
9
9
  export declare function ensureUserConfigExist(): void;
10
10
  /**
@@ -1,25 +1,34 @@
1
1
  import { existsSync, mkdirSync } from 'node:fs';
2
2
  import path from 'node:path';
3
- import { INPUT_DIR, OUTPUT_DIR, schemaPath } from './dev/constants.js';
3
+ import { CONFIG_DIR, GENERATED_DIR, generatedConfigServerPath, schemaPath } from './dev/constants.js';
4
4
  const projectRoot = process.cwd();
5
5
  /**
6
- * Ensure sanitize config exists — the generated server config inside rime.generated/.
6
+ * Ensure sanitize config exists — the generated server config inside the generated config dir.
7
7
  */
8
8
  export function ensureGeneratedConfig() {
9
- const configGeneratedPath = path.resolve(projectRoot, 'src/lib', OUTPUT_DIR, 'rime.config.server.ts');
9
+ const configGeneratedPath = path.resolve(projectRoot, GENERATED_DIR, 'rime.config.server.ts');
10
10
  if (!existsSync(configGeneratedPath)) {
11
11
  throw new Error('Unable to find generated config');
12
12
  }
13
- return path.join('$lib', OUTPUT_DIR, 'rime.config.server.js');
13
+ return generatedConfigServerPath();
14
14
  }
15
15
  /**
16
- * Ensure user config exists — the src/lib/+rime/ folder convention.
16
+ * Ensure user config exists — the CONFIG_DIR folder convention.
17
17
  */
18
18
  export function ensureUserConfigExist() {
19
- const folderConfig = path.resolve(projectRoot, 'src/lib', INPUT_DIR, 'rime.config.server.ts');
20
- if (!existsSync(folderConfig)) {
21
- throw new Error('Unable to find config, did you run rime init');
19
+ const folderConfig = path.resolve(projectRoot, CONFIG_DIR, 'rime.config.server.ts');
20
+ if (existsSync(folderConfig))
21
+ return;
22
+ // Every project set up before RIME_CONFIG_DIR existed has its config here — if it's there
23
+ // but not at CONFIG_DIR, this is almost certainly an upgrade whose .env predates the env
24
+ // var, not a missing init, so say that instead of the generic message.
25
+ const LEGACY_DEFAULT = 'src/lib/+rime';
26
+ if (CONFIG_DIR !== LEGACY_DEFAULT &&
27
+ existsSync(path.resolve(projectRoot, LEGACY_DEFAULT, 'rime.config.server.ts'))) {
28
+ throw new Error(`Unable to find config at ${CONFIG_DIR} — found one at ${LEGACY_DEFAULT} instead. ` +
29
+ `Set RIME_CONFIG_DIR=${LEGACY_DEFAULT} in your .env to keep using it there, or move it to ${CONFIG_DIR} to match the current default.`);
22
30
  }
31
+ throw new Error('Unable to find config, did you run rime init');
23
32
  }
24
33
  /**
25
34
  * Ensure schema exists (see adapter-sqlite/generate-schema/write.server.ts, which writes to
@@ -70,12 +70,11 @@ async function handleApiKeyAuth(headers, user, auth) {
70
70
  const result = await auth.api.verifyApiKey({
71
71
  body: { key: apiKey }
72
72
  });
73
- if (!result.valid || !result.key || typeof result.key.permissions !== 'string') {
73
+ if (!result.valid || !result.key || !result.key.permissions) {
74
74
  logger.error(RimeError.UNAUTHORIZED, 'Invalid api key');
75
75
  throw error(401, RimeError.UNAUTHORIZED);
76
76
  }
77
- const permissions = JSON.parse(result.key.permissions);
78
- user.roles = permissions.roles;
77
+ user.roles = result.key.permissions.roles;
79
78
  }
80
79
  /**
81
80
  * Builds complete user data by combining auth and CMS information