rimecms 0.31.3 → 0.31.5

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 (88) 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/adapter-sqlite/transform.server.js +1 -1
  7. package/dist/core/collections/upload/util/path.d.ts +2 -1
  8. package/dist/core/collections/upload/util/path.js +3 -3
  9. package/dist/core/config/auth/better-auth-hooks.server.d.ts +1 -1
  10. package/dist/core/config/auth/better-auth-hooks.server.js +1 -2
  11. package/dist/core/config/auth/better-auth-permissions.d.ts +27 -67
  12. package/dist/core/config/auth/better-auth.server.d.ts +649 -481
  13. package/dist/core/config/auth/better-auth.server.js +2 -1
  14. package/dist/core/config/server/write.server.js +5 -0
  15. package/dist/core/constant.d.ts +10 -0
  16. package/dist/core/constant.js +22 -0
  17. package/dist/core/dev/cli/build/templates.js +2 -0
  18. package/dist/core/dev/cli/clear/index.server.js +5 -5
  19. package/dist/core/dev/cli/init/index.server.js +15 -28
  20. package/dist/core/dev/cli/init/templates.d.ts +14 -0
  21. package/dist/core/dev/cli/init/templates.js +58 -3
  22. package/dist/core/dev/constants.d.ts +20 -7
  23. package/dist/core/dev/constants.js +58 -10
  24. package/dist/core/dev/generate/routes/common.server.js +36 -33
  25. package/dist/core/dev/generate/routes/index.server.d.ts +6 -3
  26. package/dist/core/dev/generate/routes/index.server.js +13 -7
  27. package/dist/core/dev/generate/routes/util.server.d.ts +14 -6
  28. package/dist/core/dev/generate/routes/util.server.js +17 -6
  29. package/dist/core/dev/generate/sanitize/index.server.js +3 -3
  30. package/dist/core/dev/generate/types/index.server.js +3 -2
  31. package/dist/core/dev/vite/index.server.js +4 -8
  32. package/dist/core/ensure.server.d.ts +2 -2
  33. package/dist/core/ensure.server.js +17 -8
  34. package/dist/core/handlers/auth.server.js +18 -11
  35. package/dist/core/handlers/routes.server.d.ts +1 -1
  36. package/dist/core/handlers/routes.server.js +4 -3
  37. package/dist/core/operations/hooks/before-read/populate-url.server.js +2 -1
  38. package/dist/core/plugins/cache/handler.server.js +1 -1
  39. package/dist/core/rime.server.d.ts +1302 -966
  40. package/dist/core/rime.server.js +6 -0
  41. package/dist/fields/number/component/number.css +0 -2
  42. package/dist/fields/relation/component/upload/upload.css +9 -0
  43. package/dist/fields/rich-text/component/drag-handle/drag-handle.css +0 -1
  44. package/dist/fields/rich-text/component/styles/rich-text.css +7 -0
  45. package/dist/fields/rich-text/core/extensions/drag-handle/helpers/findNextElementFromCursor.d.ts +1 -1
  46. package/dist/fields/rich-text/core/svelte/node-view-wrapper.svelte.d.ts +12 -5
  47. package/dist/panel/components/sections/collection/header/SelectUI.svelte.d.ts +3 -3
  48. package/dist/panel/components/sections/collection/list/row/Row.svelte +2 -1
  49. package/dist/panel/components/sections/collection/tree/CollectionTreeNode.svelte +2 -1
  50. package/dist/panel/components/sections/document/AuthAPIKeyDialog.svelte.d.ts +3 -3
  51. package/dist/panel/components/sections/document/AuthFooter.svelte +2 -1
  52. package/dist/panel/components/sections/document/Versions.svelte +1 -1
  53. package/dist/panel/components/sections/live/Consumer.svelte.d.ts +11 -4
  54. package/dist/panel/components/sections/live/LiveEdit.svelte.d.ts +13 -6
  55. package/dist/panel/components/sections/live/LiveFloatingUI.svelte.d.ts +3 -3
  56. package/dist/panel/components/ui/calendar/calendar.css +0 -11
  57. package/dist/panel/components/ui/card-resource/card-resource.svelte +2 -1
  58. package/dist/panel/components/ui/command/command-group.css +0 -1
  59. package/dist/panel/components/ui/label/label.css +1 -0
  60. package/dist/panel/components/ui/nav/Nav.svelte +1 -1
  61. package/dist/panel/components/ui/nav/NavItem.svelte +3 -2
  62. package/dist/panel/components/ui/nav/UserButton.svelte +3 -3
  63. package/dist/panel/components/ui/radio-group/radio-group.css +0 -2
  64. package/dist/panel/components/ui/scroll-area/scroll-area.css +4 -0
  65. package/dist/panel/components/ui/sheet/sheet.css +16 -0
  66. package/dist/panel/components/ui/tabs/tabs.css +0 -7
  67. package/dist/panel/context/documentForm.svelte.js +5 -6
  68. package/dist/panel/navigation.d.ts +7 -2
  69. package/dist/panel/navigation.js +11 -6
  70. package/dist/panel/pages/area/actions.server.js +4 -3
  71. package/dist/panel/pages/area/load.server.js +3 -2
  72. package/dist/panel/pages/auth/forgot-password/ForgotPassword.svelte +2 -1
  73. package/dist/panel/pages/auth/reset-password/ResetPassword.svelte +2 -1
  74. package/dist/panel/pages/auth/sign-in/SignIn.svelte +3 -2
  75. package/dist/panel/pages/auth/sign-in/actions.server.js +2 -2
  76. package/dist/panel/pages/auth/sign-in/load.server.d.ts +1 -1
  77. package/dist/panel/pages/auth/sign-in/load.server.js +2 -2
  78. package/dist/panel/pages/collection/load.server.js +9 -4
  79. package/dist/panel/pages/collection-document/actions.server.js +5 -3
  80. package/dist/panel/pages/collection-document/load.server.js +10 -6
  81. package/dist/panel/pages/dashboard/load.server.js +5 -4
  82. package/dist/panel/pages/live/Live.svelte +3 -7
  83. package/dist/panel/style/index.css +0 -6
  84. package/dist/panel/util/auth.js +1 -1
  85. package/dist/panel/util/url.d.ts +30 -1
  86. package/dist/panel/util/url.js +36 -2
  87. package/package.json +8 -6
  88. package/dist/panel/components/ui/dropdown-menu/index.d.ts +0 -23
@@ -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=panel]');
5
+ const LIVE_PAGE_DIR = path.resolve(process.cwd(), 'src/routes/(rime)/[panel=panel]/live-edit');
3
6
  /**
4
7
  * Main base layout
5
8
  * /+layout.server.ts
@@ -79,7 +82,7 @@ export const load = async ({ locals }: ServerLoadEvent) => {
79
82
  };`;
80
83
  /**
81
84
  * Login page template
82
- * (rime)/panel/sign-in/+page.svelte
85
+ * (rime)/[panel=panel]/sign-in/+page.svelte
83
86
  */
84
87
  const signInPage = () => `
85
88
  <script>
@@ -102,7 +105,7 @@ export const actions = {
102
105
  `;
103
106
  /**
104
107
  * Forgot password page template
105
- * (rime)/forgot-password/+page.svelte
108
+ * (rime)/[panel=panel]/forgot-password/+page.svelte
106
109
  */
107
110
  const forgotPasswordPage = () => `
108
111
  <script>
@@ -112,7 +115,7 @@ const forgotPasswordPage = () => `
112
115
  <ForgotPassword {data} />`;
113
116
  /**
114
117
  * Forgot password page server template
115
- * (rime)/forgot-password/+page.server.ts
118
+ * (rime)/[panel=panel]/forgot-password/+page.server.ts
116
119
  */
117
120
  const forgotPasswordPageServer = () => `
118
121
  import { type ServerLoadEvent } from '@sveltejs/kit';
@@ -121,7 +124,7 @@ export const load = (event: ServerLoadEvent) => event.locals.routes.panel.load.f
121
124
  `;
122
125
  /**
123
126
  * Reset password page template
124
- * (rime)/reset-password/+page.svelte
127
+ * (rime)/[panel=panel]/reset-password/+page.svelte
125
128
  */
126
129
  const resetPasswordPage = () => `
127
130
  <script>
@@ -132,7 +135,7 @@ const resetPasswordPage = () => `
132
135
  <ResetPassword {data} />`;
133
136
  /**
134
137
  * Reset password page server template
135
- * (rime)/reset-password/+page.server.ts
138
+ * (rime)/[panel=panel]/reset-password/+page.server.ts
136
139
  */
137
140
  const resetPasswordPageServer = () => `
138
141
  import { type ServerLoadEvent } from '@sveltejs/kit';
@@ -141,12 +144,12 @@ export const load = (event: ServerLoadEvent) => event.locals.routes.panel.load.r
141
144
  `;
142
145
  /**
143
146
  * Panel layout template
144
- * (rime)/panel/+layout.svelte
147
+ * (rime)/[panel=panel]/+layout.svelte
145
148
  */
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
 
@@ -161,7 +164,7 @@ const panelLayout = () => `
161
164
  </Panel>`;
162
165
  /**
163
166
  * Panel layout server template
164
- * (rime)/panel/+layout.server.ts
167
+ * (rime)/[panel=panel]/+layout.server.ts
165
168
  */
166
169
  const panelLayoutServer = () => `
167
170
  import { type ServerLoadEvent } from '@sveltejs/kit';
@@ -172,7 +175,7 @@ export const load = async ({ locals }: ServerLoadEvent) => {
172
175
  };`;
173
176
  /**
174
177
  * Panel page template
175
- * (rime)/panel/+page.svelte
178
+ * (rime)/[panel=panel]/+page.svelte
176
179
  */
177
180
  const panelPage = () => `
178
181
  <script>
@@ -183,7 +186,7 @@ const panelPage = () => `
183
186
  <Dashboard entries={data.entries} user={data.user} />`;
184
187
  /**
185
188
  * Panel page load template
186
- * (rime)/panel/+page.server.ts
189
+ * (rime)/[panel=panel]/+page.server.ts
187
190
  */
188
191
  const panelPageServer = () => `
189
192
  import { type ServerLoadEvent } from '@sveltejs/kit';
@@ -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>
@@ -226,7 +229,7 @@ export const load = (event: ServerLoadEvent) => event.locals.routes.panel.load.l
226
229
  /****************************************************/
227
230
  /**
228
231
  * Collection list page
229
- * (rime)/panel/[slug=collection]/+page.svelte
232
+ * (rime)/[panel=panel]/[slug=collection]/+page.svelte
230
233
  */
231
234
  const collectionListPage = () => `
232
235
  <script>
@@ -236,7 +239,7 @@ const collectionListPage = () => `
236
239
 
237
240
  <Collection {data} slug={data.slug} />`;
238
241
  /**
239
- * (rime)/panel/[slug=collection]/+page.server.ts
242
+ * (rime)/[panel=panel]/[slug=collection]/+page.server.ts
240
243
  */
241
244
  const collectionListPageServer = () => `
242
245
  import { type ServerLoadEvent } from '@sveltejs/kit';
@@ -247,7 +250,7 @@ export const load = async (event: ServerLoadEvent) => {
247
250
  };`;
248
251
  /**
249
252
  * Area document page
250
- * (rime)/panel/[slug=area]/+page.svelte
253
+ * (rime)/[panel=panel]/[slug=area]/+page.svelte
251
254
  */
252
255
  const areaDocPage = () => `
253
256
  <script>
@@ -257,7 +260,7 @@ const areaDocPage = () => `
257
260
 
258
261
  <Area {data} />`;
259
262
  /**
260
- * (rime)/panel/[slug=area]/+page.server.ts
263
+ * (rime)/[panel=panel]/[slug=area]/+page.server.ts
261
264
  */
262
265
  const areaDocPageServer = () => `
263
266
  import type { RequestEvent, ServerLoadEvent } from '@sveltejs/kit';
@@ -269,7 +272,7 @@ export const actions = {
269
272
  };`;
270
273
  /**
271
274
  * Collection document page
272
- * (rime)/panel/[slug=collection]/[id]/+page.svelte
275
+ * (rime)/[panel=panel]/[slug=collection]/[id]/+page.svelte
273
276
  */
274
277
  const collectionDocPage = () => `
275
278
  <script>
@@ -279,7 +282,7 @@ const collectionDocPage = () => `
279
282
 
280
283
  <CollectionDoc {data} />`;
281
284
  /**
282
- * (rime)/panel/[slug=collection]/[id]/+page.server.ts
285
+ * (rime)/[panel=panel]/[slug=collection]/[id]/+page.server.ts
283
286
  */
284
287
  const collectionDocPageServer = () => `
285
288
  import type { RequestEvent, ServerLoadEvent } from '@sveltejs/kit';
@@ -292,7 +295,7 @@ export const actions = {
292
295
  };`;
293
296
  /**
294
297
  * Collection document versions page
295
- * (rime)/panel/[slug=collection]/[id]/versions/+page.svelte
298
+ * (rime)/[panel=panel]/[slug=collection]/[id]/versions/+page.svelte
296
299
  */
297
300
  const collectionDocVersionsPage = () => `
298
301
  <script>
@@ -302,7 +305,7 @@ const collectionDocVersionsPage = () => `
302
305
 
303
306
  <CollectionDocVersions data={data} />`;
304
307
  /**
305
- * (rime)/panel/[slug=collection]/[id]/versions/+page.server.ts
308
+ * (rime)/[panel=panel]/[slug=collection]/[id]/versions/+page.server.ts
306
309
  */
307
310
  const collectionDocVersionsPageServer = () => `
308
311
  import { type ServerLoadEvent } from '@sveltejs/kit';
@@ -311,7 +314,7 @@ export const load = (event: ServerLoadEvent) =>
311
314
  event.locals.routes.panel.load.documentVersions(event);`;
312
315
  /**
313
316
  * Area document versions page
314
- * (rime)/panel/[slug=area]/versions/+page.svelte
317
+ * (rime)/[panel=panel]/[slug=area]/versions/+page.svelte
315
318
  */
316
319
  const areaVersionsPage = () => `
317
320
  <script>
@@ -321,7 +324,7 @@ const areaVersionsPage = () => `
321
324
 
322
325
  <AreaVersionsDoc data={data} />`;
323
326
  /**
324
- * (rime)/panel/[slug=area]/versions/+page.server.ts
327
+ * (rime)/[panel=panel]/[slug=area]/versions/+page.server.ts
325
328
  */
326
329
  const areaVersionsPageServer = () => `
327
330
  import { type ServerLoadEvent } from '@sveltejs/kit';
@@ -436,45 +439,45 @@ export const commonRoutes = {
436
439
  layoutServer: rootLayoutServer,
437
440
  error: error
438
441
  },
439
- '(rime)/panel/sign-in': {
442
+ '(rime)/[panel=panel]/sign-in': {
440
443
  'page@(rime)': signInPage,
441
444
  pageServer: signInPageServer
442
445
  },
443
- '(rime)/forgot-password': {
444
- page: forgotPasswordPage,
446
+ '(rime)/[panel=panel]/forgot-password': {
447
+ 'page@(rime)': forgotPasswordPage,
445
448
  pageServer: forgotPasswordPageServer
446
449
  },
447
- '(rime)/reset-password': {
448
- page: resetPasswordPage,
450
+ '(rime)/[panel=panel]/reset-password': {
451
+ 'page@(rime)': resetPasswordPage,
449
452
  pageServer: resetPasswordPageServer
450
453
  },
451
- '(rime)/panel': {
454
+ '(rime)/[panel=panel]': {
452
455
  layout: panelLayout,
453
456
  layoutServer: panelLayoutServer,
454
457
  page: panelPage,
455
458
  pageServer: panelPageServer
456
459
  },
457
- '(rime)/panel/live-edit': {
460
+ '(rime)/[panel=panel]/live-edit': {
458
461
  'page@(rime)': livePage,
459
462
  pageServer: livePageServer
460
463
  },
461
- '(rime)/panel/[slug=collection]': {
464
+ '(rime)/[panel=panel]/[slug=collection]': {
462
465
  page: collectionListPage,
463
466
  pageServer: collectionListPageServer
464
467
  },
465
- '(rime)/panel/[slug=collection]/[id]': {
468
+ '(rime)/[panel=panel]/[slug=collection]/[id]': {
466
469
  page: collectionDocPage,
467
470
  pageServer: collectionDocPageServer
468
471
  },
469
- '(rime)/panel/[slug=collection]/[id]/versions': {
472
+ '(rime)/[panel=panel]/[slug=collection]/[id]/versions': {
470
473
  page: collectionDocVersionsPage,
471
474
  pageServer: collectionDocVersionsPageServer
472
475
  },
473
- '(rime)/panel/[slug=area]': {
476
+ '(rime)/[panel=panel]/[slug=area]': {
474
477
  page: areaDocPage,
475
478
  pageServer: areaDocPageServer
476
479
  },
477
- '(rime)/panel/[slug=area]/versions': {
480
+ '(rime)/[panel=panel]/[slug=area]/versions': {
478
481
  page: areaVersionsPage,
479
482
  pageServer: areaVersionsPageServer
480
483
  },
@@ -2,11 +2,14 @@ import type { Config } from '../../../../types.js';
2
2
  /**
3
3
  * Main function to generate browser routes based on configuration.
4
4
  * commonRoutes is a fixed set of files (layout, sign-in, dashboard, the
5
- * matcher-disambiguated /panel/[slug=collection|area]/... and
5
+ * matcher-disambiguated [panel=panel]/[slug=collection|area]/... and
6
6
  * /api/[slug=collection|area]/... dynamic routes) — it no longer varies with
7
7
  * how many collections/areas are configured, so there's no per-collection/area
8
- * loop here anymore. The src/params/collection.ts and area.ts matchers backing
9
- * those routes DO bake in the actual slug list, so they're rewritten here too.
8
+ * loop here anymore. The folder is always the literal `[panel=panel]` segment;
9
+ * only src/params/panel.ts's matcher content varies with RIME_PANEL_ROUTE, so
10
+ * the URL itself can be hidden from admin-path scanners without ever renaming
11
+ * a directory. Same idea backs src/params/collection.ts and area.ts, which
12
+ * bake in the actual slug list — all three are rewritten here.
10
13
  */
11
14
  declare function generateRoutes<T extends Config>(config: T): void;
12
15
  export default generateRoutes;
@@ -1,3 +1,4 @@
1
+ import { PANEL_ROUTE } from '../../constants.js';
1
2
  import { logger } from '../../../logger/index.server.js';
2
3
  import fs from 'fs';
3
4
  import path from 'path';
@@ -8,11 +9,14 @@ const projectRoot = process.cwd();
8
9
  /**
9
10
  * Main function to generate browser routes based on configuration.
10
11
  * commonRoutes is a fixed set of files (layout, sign-in, dashboard, the
11
- * matcher-disambiguated /panel/[slug=collection|area]/... and
12
+ * matcher-disambiguated [panel=panel]/[slug=collection|area]/... and
12
13
  * /api/[slug=collection|area]/... dynamic routes) — it no longer varies with
13
14
  * how many collections/areas are configured, so there's no per-collection/area
14
- * loop here anymore. The src/params/collection.ts and area.ts matchers backing
15
- * those routes DO bake in the actual slug list, so they're rewritten here too.
15
+ * loop here anymore. The folder is always the literal `[panel=panel]` segment;
16
+ * only src/params/panel.ts's matcher content varies with RIME_PANEL_ROUTE, so
17
+ * the URL itself can be hidden from admin-path scanners without ever renaming
18
+ * a directory. Same idea backs src/params/collection.ts and area.ts, which
19
+ * bake in the actual slug list — all three are rewritten here.
16
20
  */
17
21
  function generateRoutes(config) {
18
22
  logger.info('Routes generation...');
@@ -23,7 +27,7 @@ function generateRoutes(config) {
23
27
  // 2. Ensure base directories exist
24
28
  const rootRoutes = path.resolve(projectRoot, 'src', 'routes');
25
29
  const rimeRoutes = path.join(rootRoutes, '(rime)');
26
- const panelRoute = path.join(rimeRoutes, 'panel');
30
+ const panelRoute = path.join(rimeRoutes, '[panel=panel]');
27
31
  const paramsDir = path.resolve(projectRoot, 'src', 'params');
28
32
  fs.rmSync(rimeRoutes, { recursive: true, force: true });
29
33
  ensureDir(rootRoutes);
@@ -44,8 +48,10 @@ function generateRoutes(config) {
44
48
  writeRouteFile(rootRoutes, pattern, fileType, templateFn());
45
49
  }
46
50
  }
47
- // 4. Write the [slug=collection]/[slug=area] param matchers, only when needed — and remove any
48
- // stale matcher left over from a previous config that did have collections/areas.
51
+ // 4. Write the [panel=panel] param matcher — always present, unlike collection/area — plus
52
+ // the [slug=collection]/[slug=area] matchers, only when needed, removing any stale matcher
53
+ // left over from a previous config that did have collections/areas.
54
+ fs.writeFileSync(path.join(paramsDir, 'panel.ts'), paramMatcher([PANEL_ROUTE]));
49
55
  const collectionMatcherPath = path.join(paramsDir, 'collection.ts');
50
56
  if (hasCollections) {
51
57
  fs.writeFileSync(collectionMatcherPath, paramMatcher((config.collections || []).map((c) => c.kebab)));
@@ -64,7 +70,7 @@ function generateRoutes(config) {
64
70
  const customRoutes = config.panel?.routes;
65
71
  if (customRoutes) {
66
72
  for (const [route, routeConfig] of Object.entries(customRoutes)) {
67
- const routePath = path.join('(rime)', 'panel', route);
73
+ const routePath = path.join('(rime)', '[panel=panel]', route);
68
74
  writeRouteFile(rootRoutes, routePath, 'page', customRoute(routeConfig));
69
75
  }
70
76
  }
@@ -8,10 +8,18 @@ export type Routes = Record<string, RouteDefinition>;
8
8
  /**
9
9
  * Check if routes need to be regenerated based on config changes.
10
10
  * Panel/API routes are a fixed set of dynamic-segment files (file count never
11
- * grows with schema size), but the [slug=collection]/[slug=area] param
12
- * matchers under src/params/ bake in the actual slug lists, so adding,
13
- * removing, or renaming a collection/area still needs a regen to keep those
14
- * matchers in sync — same trigger as custom routes and panel CSS.
11
+ * grows with schema size), but the [panel=panel]/[slug=collection]/[slug=area]
12
+ * param matchers under src/params/ bake in the actual accepted value(s), so
13
+ * changing RIME_PANEL_ROUTE or RIME_CONFIG_DIR, or adding/removing/renaming a
14
+ * collection/area, still needs a regen to keep those matchers in sync — same
15
+ * trigger as custom routes and panel CSS. Both PANEL_ROUTE and CONFIG_DIR must
16
+ * stay in this memo: they're read once at process start, so a changed value
17
+ * only takes effect after a restart, and only if the restart's regen actually
18
+ * notices the change. CONFIG_DIR specifically is baked into the panel/live
19
+ * layout's config import path (see configImportPaths() calls in
20
+ * common.server.ts) — leaving it out would silently keep that import stale
21
+ * instead of erroring, the same failure mode RIME_CONFIG_DIR hit once for
22
+ * hooks.server.ts (see regenerateHooks() in cli/init/templates.ts).
15
23
  * @returns true if routes should be regenerated, false otherwise
16
24
  */
17
25
  export declare function shouldRegenerateRoutes<T extends Config>(config: T): boolean;
@@ -33,10 +41,10 @@ export declare function ensureDir(dirPath: string): void;
33
41
  * @example
34
42
  * // Write a page file for a collection
35
43
  * const basePath = '/path/to/src/routes';
36
- * const routePath = '(rime)/panel/news';
44
+ * const routePath = '(rime)/[panel=panel]/news';
37
45
  * const fileType = 'page';
38
46
  * const content = '<script>\n import { Collection } from "rime/panel"\n</script>...';
39
47
  * writeRouteFile(basePath, routePath, fileType, content);
40
- * // Creates /path/to/src/routes/(rime)/panel/news/+page.svelte with the provided content
48
+ * // Creates /path/to/src/routes/(rime)/[panel=panel]/news/+page.svelte with the provided content
41
49
  */
42
50
  export declare function writeRouteFile(basePath: string, routePath: string, fileType: string, content: string): void;
@@ -1,18 +1,29 @@
1
1
  import cache from '../../cache/index.server.js';
2
+ import { CONFIG_DIR, PANEL_ROUTE } from '../../constants.js';
2
3
  import { slugify } from '../../../../util/string.js';
3
4
  import fs from 'fs';
4
5
  import path from 'path';
5
6
  /**
6
7
  * Check if routes need to be regenerated based on config changes.
7
8
  * Panel/API routes are a fixed set of dynamic-segment files (file count never
8
- * grows with schema size), but the [slug=collection]/[slug=area] param
9
- * matchers under src/params/ bake in the actual slug lists, so adding,
10
- * removing, or renaming a collection/area still needs a regen to keep those
11
- * matchers in sync — same trigger as custom routes and panel CSS.
9
+ * grows with schema size), but the [panel=panel]/[slug=collection]/[slug=area]
10
+ * param matchers under src/params/ bake in the actual accepted value(s), so
11
+ * changing RIME_PANEL_ROUTE or RIME_CONFIG_DIR, or adding/removing/renaming a
12
+ * collection/area, still needs a regen to keep those matchers in sync — same
13
+ * trigger as custom routes and panel CSS. Both PANEL_ROUTE and CONFIG_DIR must
14
+ * stay in this memo: they're read once at process start, so a changed value
15
+ * only takes effect after a restart, and only if the restart's regen actually
16
+ * notices the change. CONFIG_DIR specifically is baked into the panel/live
17
+ * layout's config import path (see configImportPaths() calls in
18
+ * common.server.ts) — leaving it out would silently keep that import stale
19
+ * instead of erroring, the same failure mode RIME_CONFIG_DIR hit once for
20
+ * hooks.server.ts (see regenerateHooks() in cli/init/templates.ts).
12
21
  * @returns true if routes should be regenerated, false otherwise
13
22
  */
14
23
  export function shouldRegenerateRoutes(config) {
15
24
  const memo = `
25
+ panel:${PANEL_ROUTE}
26
+ config:${CONFIG_DIR}
16
27
  custom:${config.panel?.routes
17
28
  ? Object.entries(config.panel.routes)
18
29
  .map(([k, v]) => `${k}-${slugify(v.component.toString())}`)
@@ -51,11 +62,11 @@ export function ensureDir(dirPath) {
51
62
  * @example
52
63
  * // Write a page file for a collection
53
64
  * const basePath = '/path/to/src/routes';
54
- * const routePath = '(rime)/panel/news';
65
+ * const routePath = '(rime)/[panel=panel]/news';
55
66
  * const fileType = 'page';
56
67
  * const content = '<script>\n import { Collection } from "rime/panel"\n</script>...';
57
68
  * writeRouteFile(basePath, routePath, fileType, content);
58
- * // Creates /path/to/src/routes/(rime)/panel/news/+page.svelte with the provided content
69
+ * // Creates /path/to/src/routes/(rime)/[panel=panel]/news/+page.svelte with the provided content
59
70
  */
60
71
  export function writeRouteFile(basePath, routePath, fileType, content) {
61
72
  const dir = path.join(basePath, routePath);
@@ -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
@@ -1,16 +1,24 @@
1
+ import { isPublicPanelAuthRoute } from '../constant.js';
1
2
  import { RimeError } from '../errors/index.js';
2
3
  import { error, redirect } from '@sveltejs/kit';
3
4
  import { BETTER_AUTH_ROLES } from '../collections/auth/constant.server.js';
4
5
  import { logger } from '../logger/index.server.js';
5
6
  const dev = process.env.NODE_ENV === 'development';
6
7
  /**
7
- * Analyzes the current route to determine authentication requirements
8
+ * Analyzes the current route to determine authentication requirements.
9
+ * isPanel/isPublicAuthRoute are derived from the matched route, not the
10
+ * pathname — event.params.panel only resolves when the request matched the
11
+ * [panel=panel] matcher, and event.route.id's literal folder names never
12
+ * carry the configured RIME_PANEL_ROUTE value — so neither check needs to
13
+ * know the actual (hideable) panel segment. sign-in/forgot-password/reset-password
14
+ * all live under [panel=panel] too but must stay reachable without a session,
15
+ * hence the isPublicAuthRoute carve-out.
8
16
  */
9
- function analyzeRoute(pathname) {
10
- const isSignIn = pathname === '/panel/sign-in';
11
- const isPanel = pathname.startsWith('/panel') && !isSignIn;
12
- const isAPI = pathname.startsWith('/api');
13
- return { isSignIn, isPanel, isAPI };
17
+ function analyzeRoute(event) {
18
+ const isPublicAuthRoute = isPublicPanelAuthRoute(event.route.id);
19
+ const isPanel = event.params.panel !== undefined && !isPublicAuthRoute;
20
+ const isAPI = event.url.pathname.startsWith('/api');
21
+ return { isPublicAuthRoute, isPanel, isAPI };
14
22
  }
15
23
  /**
16
24
  * Ensures panel is properly set up before allowing access
@@ -31,7 +39,7 @@ async function authenticateRequest(headers, auth) {
31
39
  */
32
40
  function handleUnauthenticated(event, resolve, routeInfo) {
33
41
  if (routeInfo.isPanel) {
34
- throw redirect(303, '/panel/sign-in');
42
+ throw redirect(303, `/${event.params.panel}/sign-in`);
35
43
  }
36
44
  event.locals.user = undefined;
37
45
  event.locals.session = undefined;
@@ -70,12 +78,11 @@ async function handleApiKeyAuth(headers, user, auth) {
70
78
  const result = await auth.api.verifyApiKey({
71
79
  body: { key: apiKey }
72
80
  });
73
- if (!result.valid || !result.key || typeof result.key.permissions !== 'string') {
81
+ if (!result.valid || !result.key || !result.key.permissions) {
74
82
  logger.error(RimeError.UNAUTHORIZED, 'Invalid api key');
75
83
  throw error(401, RimeError.UNAUTHORIZED);
76
84
  }
77
- const permissions = JSON.parse(result.key.permissions);
78
- user.roles = permissions.roles;
85
+ user.roles = result.key.permissions.roles;
79
86
  }
80
87
  /**
81
88
  * Builds complete user data by combining auth and CMS information
@@ -133,7 +140,7 @@ function cleanupUser(userData, routeInfo) {
133
140
  */
134
141
  export const handleAuth = async ({ event, resolve }) => {
135
142
  const rime = event.locals.rime;
136
- const routeInfo = analyzeRoute(event.url.pathname);
143
+ const routeInfo = analyzeRoute(event);
137
144
  // Ensure auth is set up
138
145
  if (routeInfo.isPanel) {
139
146
  await ensureFirstAuthSetup(rime);
@@ -31,7 +31,7 @@ export declare const routeHandlers: {
31
31
  aria: import("../../types.js").Route[];
32
32
  }>;
33
33
  live: typeof liveLoad;
34
- signIn: ({ locals }: ServerLoadEvent) => Promise<{
34
+ signIn: ({ locals, params }: ServerLoadEvent) => Promise<{
35
35
  forgotPasswordEnabled: boolean;
36
36
  image: string | null;
37
37
  form: {};
@@ -1,3 +1,4 @@
1
+ import { isPublicPanelAuthRoute } from '../constant.js';
1
2
  import { prototypeKebabToSlug } from '../naming.js';
2
3
  import buildNavigation from '../../panel/navigation.js';
3
4
  import { areaFormActions } from '../../panel/pages/area/actions.server.js';
@@ -56,9 +57,9 @@ export const routeHandlers = {
56
57
  };
57
58
  export const handleRoutes = async ({ event, resolve }) => {
58
59
  const { rime, user } = event.locals;
59
- const IS_SIGN_IN_ROUTE = event.url.pathname === '/panel/sign-in';
60
+ const IS_PUBLIC_AUTH_ROUTE = isPublicPanelAuthRoute(event.route.id);
60
61
  const IS_API_ROUTE = event.url.pathname.startsWith('/api');
61
- const IS_PANEL_ROUTE = event.url.pathname.startsWith('/panel') && !IS_SIGN_IN_ROUTE;
62
+ const IS_PANEL_ROUTE = event.params.panel !== undefined && !IS_PUBLIC_AUTH_ROUTE;
62
63
  // event.params.slug comes straight from the URL, always kebab-case (see
63
64
  // src/params/collection.ts, area.ts). Collection/area slugs themselves are
64
65
  // camelCase, so rewrite once here — every downstream handler (panel
@@ -72,7 +73,7 @@ export const handleRoutes = async ({ event, resolve }) => {
72
73
  }
73
74
  // build panel navigation
74
75
  if (IS_PANEL_ROUTE && event.request.method === 'GET') {
75
- event.locals.navigation = buildNavigation(rime.config.raw, user);
76
+ event.locals.navigation = buildNavigation(rime.config.raw, user, event.params.panel);
76
77
  }
77
78
  event.locals.routes = routeHandlers;
78
79
  // Handle custom routes from config and plugins