rimecms 0.30.3 → 0.31.0

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 (84) hide show
  1. package/README.md +1 -39
  2. package/dist/.rime-modules.d.ts +23 -0
  3. package/dist/.rime-modules.json +1 -0
  4. package/dist/adapter-sqlite/generate-schema/write.server.js +6 -6
  5. package/dist/core/areas/config/builder.server.d.ts +0 -1
  6. package/dist/core/collections/auth/hooks/before-upsert/augment-fields-password.server.d.ts +1 -1
  7. package/dist/core/collections/config/builder.server.d.ts +0 -1
  8. package/dist/core/collections/upload/hooks/convert-base64.server.d.ts +1 -1
  9. package/dist/core/collections/upload/hooks/extract-path.server.d.ts +1 -1
  10. package/dist/core/collections/upload/hooks/handle-path-creation.server.d.ts +1 -1
  11. package/dist/core/collections/upload/hooks/process-file-upload.server.d.ts +1 -1
  12. package/dist/core/config/auth/better-auth.server.d.ts +3 -3
  13. package/dist/core/config/client/augment-plugins.d.ts +1 -5
  14. package/dist/core/config/client/augment-plugins.js +3 -3
  15. package/dist/core/config/client/build-config.d.ts +1 -5
  16. package/dist/core/config/config-context.server.d.ts +1 -1
  17. package/dist/core/config/server/augment-plugins.server.d.ts +2 -42
  18. package/dist/core/config/server/augment-plugins.server.js +11 -6
  19. package/dist/core/config/server/build-config.server.d.ts +9 -4007
  20. package/dist/core/config/types.d.ts +12 -11
  21. package/dist/core/dev/cache/index.server.js +1 -1
  22. package/dist/core/dev/cli/clear/index.server.js +2 -0
  23. package/dist/core/dev/cli/generate-manifest/index.server.d.ts +17 -0
  24. package/dist/core/dev/cli/generate-manifest/index.server.js +221 -0
  25. package/dist/core/dev/cli/index.js +42 -0
  26. package/dist/core/dev/cli/init/index.server.js +11 -4
  27. package/dist/core/dev/cli/init/templates.d.ts +1 -1
  28. package/dist/core/dev/cli/init/templates.js +5 -3
  29. package/dist/core/dev/cli/util/package-manager.server.js +5 -3
  30. package/dist/core/dev/constants.d.ts +10 -0
  31. package/dist/core/dev/constants.js +15 -0
  32. package/dist/core/dev/generate/routes/common.server.js +4 -7
  33. package/dist/core/dev/generate/routes/index.server.js +25 -4
  34. package/dist/core/dev/generate/runtime/index.server.d.ts +31 -15
  35. package/dist/core/dev/generate/runtime/index.server.js +45 -53
  36. package/dist/core/dev/generate/runtime/parse-exports.server.d.ts +7 -0
  37. package/dist/core/dev/generate/runtime/parse-exports.server.js +40 -0
  38. package/dist/core/dev/generate/sanitize/index.server.d.ts +0 -7
  39. package/dist/core/dev/generate/sanitize/index.server.js +57 -5
  40. package/dist/core/dev/generate/types/index.server.js +16 -39
  41. package/dist/core/dev/vite/index.server.js +217 -39
  42. package/dist/core/ensure.server.d.ts +4 -3
  43. package/dist/core/ensure.server.js +8 -8
  44. package/dist/core/handlers/index.js +2 -2
  45. package/dist/core/operations/hooks/before-upsert/data-config-map.server.d.ts +1 -1
  46. package/dist/core/operations/hooks/before-upsert/handle-new-version.server.d.ts +1 -1
  47. package/dist/core/operations/hooks/before-upsert/original-config-map.server.d.ts +1 -1
  48. package/dist/core/operations/hooks/before-upsert/set-default-values.server.d.ts +1 -1
  49. package/dist/core/operations/hooks/before-upsert/validate-fields.server.d.ts +1 -1
  50. package/dist/core/operations/hooks/index.server.d.ts +0 -4
  51. package/dist/core/operations/hooks/index.server.js +0 -4
  52. package/dist/core/plugins/api-init/index.d.ts +2 -0
  53. package/dist/core/plugins/api-init/index.js +1 -0
  54. package/dist/core/plugins/api-init/{index.server.d.ts → module.server.d.ts} +0 -1
  55. package/dist/core/plugins/api-init/{index.server.js → module.server.js} +0 -1
  56. package/dist/core/plugins/cache/index.d.ts +2 -5
  57. package/dist/core/plugins/cache/index.js +1 -21
  58. package/dist/core/plugins/cache/module.d.ts +4 -0
  59. package/dist/core/plugins/cache/module.js +22 -0
  60. package/dist/core/plugins/cache/{index.server.d.ts → module.server.d.ts} +1 -2
  61. package/dist/core/plugins/cache/{index.server.js → module.server.js} +0 -1
  62. package/dist/core/plugins/index.d.ts +1 -8
  63. package/dist/core/plugins/index.js +0 -3
  64. package/dist/core/plugins/mailer/index.d.ts +2 -0
  65. package/dist/core/plugins/mailer/index.js +1 -0
  66. package/dist/core/plugins/mailer/{index.server.d.ts → module.server.d.ts} +0 -1
  67. package/dist/core/plugins/mailer/{index.server.js → module.server.js} +0 -1
  68. package/dist/core/plugins/sse/index.d.ts +2 -0
  69. package/dist/core/plugins/sse/index.js +1 -0
  70. package/dist/core/plugins/sse/{index.server.d.ts → module.server.d.ts} +0 -1
  71. package/dist/core/plugins/sse/{index.server.js → module.server.js} +0 -1
  72. package/dist/core/rime.server.d.ts +16 -16
  73. package/dist/fields/link/index.js +59 -56
  74. package/dist/fields/relation/index.js +63 -70
  75. package/dist/index.d.ts +3 -3
  76. package/dist/index.js +3 -3
  77. package/dist/panel/context/config.svelte.d.ts +2 -2
  78. package/dist/panel/context/documentForm.svelte.d.ts +2 -2
  79. package/dist/panel/context/live.svelte.js +1 -1
  80. package/dist/panel/pages/auth/sign-in/SignIn.svelte +6 -1
  81. package/dist/server.d.ts +4 -4
  82. package/dist/server.js +4 -4
  83. package/dist/types.d.ts +1 -1
  84. package/package.json +2 -2
@@ -1,36 +1,108 @@
1
1
  import dotenv from 'dotenv';
2
- import { existsSync, rmSync } from 'node:fs';
2
+ import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { RIME_DEV_CACHE_DIR } from '../../constant.server.js';
5
5
  import { ensureHasInit } from '../../ensure.server.js';
6
6
  import { logger } from '../../logger/index.server.js';
7
- import { INPUT_DIR, isInstalledDependency, OUTPUT_DIR } from '../constants.js';
8
- import { buildRuntimeRegistry } from '../generate/runtime/index.server.js';
7
+ import { getPackageInfoByKey } from '../cli/util/package.server.js';
8
+ import { INPUT_DIR, isInstalledDependency, OUTPUT_DIR, schemaPath } from '../constants.js';
9
+ import { findInstalledPackageRoot, findModulePair, scanModulePairs } from '../generate/runtime/index.server.js';
10
+ import { parseExportNames } from '../generate/runtime/parse-exports.server.js';
9
11
  import { sanitize } from '../generate/sanitize/index.server.js';
10
12
  dotenv.config({ override: true });
11
13
  const dev = process.env.NODE_ENV === 'development';
14
+ /** Picks the client or server side of a module.(server.)ts pair. When the requested side
15
+ * wasn't authored (e.g. a collection's $url/$hooks, real only server-side, imported anyway by
16
+ * the isomorphic index.ts a client build also needs for its field definitions), stubs each of
17
+ * the *other* side's export names to `undefined` instead of returning nothing — ESM does
18
+ * static named-export binding, so an empty module isn't enough, `import { x } from ...` throws
19
+ * a SyntaxError at link time if `x` isn't actually exported, it doesn't lazily become
20
+ * `undefined`. Harmless: server-only exports are already stripped from client-side types, so
21
+ * nothing client-side ever reads the stubbed value — it only has to exist. */
22
+ function exportFrom(entry, isServer) {
23
+ const target = isServer ? entry.server : entry.client;
24
+ if (target)
25
+ return `export * from '${target.replace(/\.ts$/, '.js')}';`;
26
+ const other = isServer ? entry.client : entry.server;
27
+ if (!other)
28
+ return '';
29
+ return parseExportNames(other)
30
+ .map((name) => name === 'default' ? 'export default undefined;' : `export const ${name} = undefined;`)
31
+ .join('\n');
32
+ }
33
+ /** Splits `<pkg>/<subpath>` or `@scope/<pkg>/<subpath>` into its package name and the rest —
34
+ * a scoped name is two path segments, a plain one is one. */
35
+ function splitPackageSpecifier(rest) {
36
+ const segments = rest.split('/');
37
+ const pkgName = rest.startsWith('@') ? segments.slice(0, 2).join('/') : segments[0];
38
+ const subpath = segments.slice(pkgName.split('/').length).join('/');
39
+ return { pkgName, subpath };
40
+ }
12
41
  export function rime() {
42
+ /** Two reserved names, always resolved to *the currently running app's* generated
43
+ * config/schema, independent of who's asking — a third-party plugin importing
44
+ * `$rime/config` gets the host app's config, never its own. Unrelated to the modules
45
+ * mechanism below (a package's own client/server split): different problem, same `$rime/`
46
+ * namespace only by convention. */
13
47
  const VCoreId = '$rime/config';
14
48
  const VSchemaId = '$rime/schema';
15
- /** Any field's client/server split: rime's own (relation/index.ts, link/index.ts) and a
16
- * consumer app's own local fields alike. `import { x } from '$rime/<name>'` resolves `<name>`
17
- * against a registry built once (see generate/runtime/index.server.ts's buildRuntimeRegistry,
18
- * which scans for `<name>/module.ts` + `module.server.ts` pairs) to either `module.server.ts`
19
- * (server builds) or `module.ts` (browser builds). The specifier is a static string baked into
20
- * the source, so it survives esbuild's dep-optimizer flattening — unlike an importer-relative
21
- * lookup, which breaks the moment the importing file gets pre-bundled (importer becomes the
22
- * flattened chunk, not the real source file). `config` and `schema` are reserved names (see
23
- * VCoreId/VSchemaId below, checked first) — a field can't be registered under either. */
24
- const VPrefix = '$rime/';
49
+ /** `$rime/modules` — a package's own client/server split, resolved relative to *whoever's
50
+ * asking*, always self-contained. Two forms:
51
+ *
52
+ * - The bare barrel, `$rime/modules`, addressed by *export name* rather than path — dev
53
+ * only, live-scans this project's own `src/lib` for every `module.(server.)ts` pair (see
54
+ * `regenerateModulesDeclaration` below for the matching local-dev `.d.ts`). Never ships:
55
+ * a prepack step (the `generate-manifest` CLI command) AST-rewrites every `$rime/modules`
56
+ * import into the qualified form below before publish, so a consumer never sees the
57
+ * barrel at all.
58
+ * - The qualified form, `$rime/modules/<pkg>/<path>`, always package-qualified — even for a
59
+ * package's own internal references to itself. Two ways it resolves, both in `resolveId`
60
+ * (not `load`, which never receives `importer`): `<pkg>` equals this project's own
61
+ * `package.json` name → self-reference, resolved directly off this project's own
62
+ * `src/lib`. Otherwise → an installed dependency, its root found by walking up
63
+ * `node_modules/<pkg>` from the importer's own directory (`findInstalledPackageRoot` —
64
+ * pure directory existence, no `exports`-conditions matching, since a package whose
65
+ * `exports` map declares only conditions neither `require.resolve` nor
66
+ * `import.meta.resolve` use fails to resolve through either) and its shipped
67
+ * `dist/.rime-modules.json` read directly — no directory-convention guessing beyond that,
68
+ * immune to Vite's dep-optimizer flattening a package (reading a known filename off disk
69
+ * never touches the import graph the optimizer rewrites). Full design:
70
+ * docs/plans/imports.md. */
71
+ const VModulesId = '$rime/modules';
72
+ const VModulesPrefix = '$rime/modules/';
73
+ const ownPackageName = getPackageInfoByKey('name');
74
+ // Separates a resolved root from the original specifier inside one virtual module id — never
75
+ // appears in a real path or a JS specifier, so it's an unambiguous split point. Only the
76
+ // third-party qualified-form case needs it (the root has to travel with the id since load()
77
+ // doesn't receive importer); the barrel and self-reference cases carry no baked-in root.
78
+ const ROOT_SEP = '\x01';
25
79
  const resolvedVModule = (name) => '\0' + name;
26
- let runtimeRegistry = null;
27
- const getRuntimeRegistry = () => (runtimeRegistry ??= buildRuntimeRegistry());
80
+ // Local-dev types for the $rime/modules barrel — declares every module.(server.)ts pair
81
+ // currently under this project's own src/lib, live. Regenerated on startup and whenever a
82
+ // module file appears/changes/disappears (see the watcher below) — never ships, gitignored,
83
+ // same treatment as app.generated.d.ts/rime.generated.d.ts.
84
+ //
85
+ // $lib/-aliased paths, not bare relative/absolute ones — verified directly (a plain relative
86
+ // or absolute `export * from` inside a `declare module` block silently resolves to no
87
+ // exports at all; TS only picks up the re-exported names through a real configured alias).
88
+ function regenerateModulesDeclaration() {
89
+ const libDir = path.resolve(process.cwd(), 'src/lib');
90
+ const pairs = scanModulePairs(libDir);
91
+ const exports = Array.from(pairs.values())
92
+ .map((entry) => entry.server || entry.client)
93
+ .filter(Boolean)
94
+ .map((file) => `$lib/${path.relative(libDir, file).split(path.sep).join('/')}`.replace(/\.ts$/, '.js'))
95
+ .map((aliased) => ` export * from '${aliased}';`)
96
+ .join('\n');
97
+ writeFileSync(path.resolve(process.cwd(), 'src/rime.modules.generated.d.ts'), `declare module '$rime/modules' {\n${exports}\n}\n`);
98
+ }
28
99
  return {
29
100
  name: 'virtual-rime',
30
101
  configureServer(server) {
31
102
  // Add a listener for when the server starts
32
103
  server.httpServer?.once('listening', () => {
33
104
  dev && ensureHasInit();
105
+ regenerateModulesDeclaration();
34
106
  // Check if we need to rebuild
35
107
  const shouldRebuild = process.argv.includes('--rebuild');
36
108
  if (shouldRebuild && existsSync(RIME_DEV_CACHE_DIR)) {
@@ -38,11 +110,30 @@ export function rime() {
38
110
  logger.info('--rebuild : node_modules/.rime folder deleted');
39
111
  }
40
112
  });
41
- // Add a watcher for sanitizing config changes
42
- // and trigger schema/routes/types generation
43
- server.watcher.on('change', async (modulePath) => {
44
- if (modulePath.includes(`src/lib/${INPUT_DIR}`) &&
45
- !modulePath.includes(`src/lib/${OUTPUT_DIR}`)) {
113
+ // module.ts/module.server.ts appearing, changing, or disappearing under this project's
114
+ // own src/lib — regenerate the $rime/modules declaration above so the IDE picks up the
115
+ // new/changed/removed split immediately. Listens on add/unlink too, unlike the config
116
+ // watcher below (change-only) — a new split is exactly as likely to be a brand new file
117
+ // as an edit to an existing one.
118
+ const isModuleFile = (p) => /[/\\]module(\.server)?\.ts$/.test(p) &&
119
+ p.includes(`${path.sep}src${path.sep}lib${path.sep}`);
120
+ server.watcher.on('add', (p) => isModuleFile(p) && regenerateModulesDeclaration());
121
+ server.watcher.on('unlink', (p) => isModuleFile(p) && regenerateModulesDeclaration());
122
+ server.watcher.on('change', (p) => isModuleFile(p) && regenerateModulesDeclaration());
123
+ // Add a watcher for sanitizing config changes and triggering schema/routes/types
124
+ // generation. Debounced + single-flight: a config change is rarely one file (e.g. a
125
+ // plugin swap copying several files under +rime/ at once) — without this, each file
126
+ // fires its own overlapping sanitize()+generate()+migrate() run against the same db/
127
+ // and sqlite file, racing each other.
128
+ let reloadTimer = null;
129
+ let reloadInFlight = null;
130
+ let reloadQueued = false;
131
+ async function runConfigReload() {
132
+ if (reloadInFlight) {
133
+ reloadQueued = true;
134
+ return;
135
+ }
136
+ reloadInFlight = (async () => {
46
137
  // Sanitize the config client/server
47
138
  try {
48
139
  await sanitize();
@@ -53,7 +144,11 @@ export function rime() {
53
144
  }
54
145
  // Trigger generation
55
146
  try {
56
- const mod = await server.ssrLoadModule(`src/lib/${OUTPUT_DIR}/rime.config.server.ts`);
147
+ // ssrLoadModule wants the real .ts file path, not a $lib/*.js-style import
148
+ // specifier — different consumer than configImportPaths() (used for generated
149
+ // source code), so computed directly here.
150
+ const serverConfigPath = `src/lib/${OUTPUT_DIR}/rime.config.server.ts`;
151
+ const mod = await server.ssrLoadModule(serverConfigPath);
57
152
  // The config's default export is the createRime() promise; ssrLoadModule
58
153
  // only awaits the module's synchronous evaluation, so we must await it
59
154
  // directly to observe init errors (e.g. invalid config) here instead of
@@ -63,7 +158,25 @@ export function rime() {
63
158
  catch (error) {
64
159
  logger.error('Failed to reload the config', error.message);
65
160
  }
161
+ })();
162
+ await reloadInFlight;
163
+ reloadInFlight = null;
164
+ if (reloadQueued) {
165
+ reloadQueued = false;
166
+ await runConfigReload();
66
167
  }
168
+ }
169
+ server.watcher.on('change', (modulePath) => {
170
+ const isConfigChange = modulePath.includes(`src/lib/${INPUT_DIR}`) &&
171
+ !modulePath.includes(`src/lib/${OUTPUT_DIR}`);
172
+ if (!isConfigChange)
173
+ return;
174
+ if (reloadTimer)
175
+ clearTimeout(reloadTimer);
176
+ reloadTimer = setTimeout(() => {
177
+ reloadTimer = null;
178
+ runConfigReload();
179
+ }, 150);
67
180
  });
68
181
  },
69
182
  config() {
@@ -106,43 +219,108 @@ export function rime() {
106
219
  if (module)
107
220
  return [module];
108
221
  }
222
+ if (/[/\\]module(\.server)?\.ts$/.test(file) &&
223
+ file.includes(`${path.sep}src${path.sep}lib${path.sep}`)) {
224
+ const module = invalidateVModule(VModulesId);
225
+ if (module)
226
+ return [module];
227
+ }
109
228
  },
110
- resolveId(id) {
229
+ resolveId(id, importer) {
111
230
  if (id === VCoreId) {
112
231
  return resolvedVModule(id);
113
232
  }
114
233
  if (id === VSchemaId) {
115
234
  return resolvedVModule(id);
116
235
  }
117
- if (id.startsWith(VPrefix)) {
118
- const name = id.slice(VPrefix.length);
119
- // Unregistered name: return null so it surfaces as a normal "failed to resolve
120
- // import" error instead of a silent virtual-module 404.
121
- if (!getRuntimeRegistry().has(name))
122
- return null;
123
- return resolvedVModule(id);
236
+ if (id === VModulesId) {
237
+ return resolvedVModule(id); // barrel, no root to bake in — load() always scans this project's own src/lib
238
+ }
239
+ if (id.startsWith(VModulesPrefix)) {
240
+ const { pkgName } = splitPackageSpecifier(id.slice(VModulesPrefix.length));
241
+ if (pkgName === ownPackageName) {
242
+ return resolvedVModule(id); // self-reference: load() scans this project's own src/lib too
243
+ }
244
+ if (!importer) {
245
+ throw new Error(`$rime/modules: '${id}' has no importer to resolve '${pkgName}' from`);
246
+ }
247
+ // Third-party dependency: find its installed root now, while `importer` is still
248
+ // available (load() doesn't receive it) — baked into the id via ROOT_SEP. Walks up
249
+ // node_modules directly (see findInstalledPackageRoot) rather than resolving through
250
+ // the package's own `exports` map — confirmed in practice that both `require.resolve`
251
+ // and `import.meta.resolve` gate on a condition set matching, and a package whose
252
+ // exports only declare conditions neither uses (rime's own included: `types`/`svelte`/
253
+ // `import`, no `require`/`default`) fails to resolve at all through either.
254
+ const pkgRoot = findInstalledPackageRoot(pkgName, path.dirname(importer));
255
+ return resolvedVModule(`${pkgRoot}${ROOT_SEP}${id}`);
124
256
  }
125
257
  return null;
126
258
  },
127
- load(id) {
259
+ async load(id) {
128
260
  const isServer = this.environment?.config?.consumer === 'server';
129
261
  if (id === resolvedVModule(VCoreId)) {
130
262
  const corePath = isServer ? 'rimecms/config/server' : 'rimecms/config';
131
263
  return `export * from '${corePath}';`;
132
264
  }
133
265
  if (id === resolvedVModule(VSchemaId) && isServer) {
134
- const schemaPath = path.resolve(process.cwd(), `src/lib/${OUTPUT_DIR}/schema.server.ts`);
135
- if (existsSync(schemaPath)) {
136
- const modulePath = schemaPath.replace('.ts', '.js');
266
+ // Mirrors write.server.ts (adapter-sqlite/generate-schema) — the adapter itself never
267
+ // needed to change for this, it already goes through this virtual module instead of a
268
+ // hardcoded path.
269
+ const schemaFilePath = schemaPath();
270
+ if (existsSync(schemaFilePath)) {
271
+ const modulePath = schemaFilePath.replace('.ts', '.js');
137
272
  return `export * from '${modulePath}'; export { default } from '${modulePath}';`;
138
273
  }
139
274
  }
140
- if (id.startsWith(resolvedVModule(VPrefix))) {
141
- const name = id.slice(resolvedVModule(VPrefix).length);
142
- const entry = getRuntimeRegistry().get(name);
143
- if (entry) {
144
- const target = (isServer ? entry.server : entry.client).replace(/\.ts$/, '.js');
145
- return `export * from '${target}';`;
275
+ if (id === resolvedVModule(VModulesId)) {
276
+ // Dev-mode barrel — re-exports every module.(server.)ts pair found under this
277
+ // project's own src/lib, live. Never ships: prepack rewrites every '$rime/modules'
278
+ // import into a qualified one before publish, so this only ever runs during dev.
279
+ const pairs = scanModulePairs(path.resolve(process.cwd(), 'src/lib'));
280
+ return Array.from(pairs.values())
281
+ .map((entry) => exportFrom(entry, isServer))
282
+ .filter(Boolean)
283
+ .join('\n');
284
+ }
285
+ // Self-reference qualified form — resolveId returned this bare, no root baked in
286
+ // (matches VModulesId's shape above): resolve directly off this project's own src/lib.
287
+ if (id.startsWith(resolvedVModule(VModulesPrefix))) {
288
+ const subpath = splitPackageSpecifier(id.slice(resolvedVModule(VModulesPrefix).length)).subpath;
289
+ const pair = findModulePair(path.resolve(process.cwd(), 'src/lib'), subpath);
290
+ if (!pair)
291
+ throw new Error(`${id.slice(1)}: doesn't resolve under this project's own src/lib`);
292
+ return exportFrom(pair, isServer);
293
+ }
294
+ // Third-party qualified form — resolveId baked the resolved package root in via
295
+ // ROOT_SEP: read what it shipped at prepack, don't guess.
296
+ {
297
+ const modulesSepIndex = id.indexOf(ROOT_SEP);
298
+ const modulesSpecifier = modulesSepIndex === -1 ? '' : id.slice(modulesSepIndex + 1);
299
+ if (modulesSpecifier.startsWith(VModulesPrefix)) {
300
+ const pkgRoot = id.slice(1, modulesSepIndex);
301
+ const rest = modulesSpecifier.slice(VModulesPrefix.length);
302
+ const { pkgName, subpath } = splitPackageSpecifier(rest);
303
+ const distDir = path.join(pkgRoot, 'dist');
304
+ const manifestPath = path.join(distDir, '.rime-modules.json');
305
+ if (!existsSync(manifestPath)) {
306
+ throw new Error(`$rime/modules/${pkgName}: no manifest at ${manifestPath} — does ${pkgName} run 'rime generate-manifest' at prepack?`);
307
+ }
308
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
309
+ const pair = manifest[subpath];
310
+ if (!pair) {
311
+ throw new Error(`$rime/modules/${pkgName}/${subpath}: not found in ${pkgName}'s manifest`);
312
+ }
313
+ // Manifest entries are stored relative to *that package's own* dist/ (written by
314
+ // generate-manifest on whatever machine packed it) - re-rooted onto distDir (this
315
+ // installed copy's real, resolved location) here, not trusted as absolute. An
316
+ // absolute path baked in at pack time would silently read the package author's own
317
+ // working-copy dist/ instead of what's actually installed here whenever both
318
+ // happen to exist on the same machine (exactly what authoring rime itself hit).
319
+ const resolvedPair = {
320
+ client: pair.client ? path.join(distDir, pair.client) : '',
321
+ server: pair.server ? path.join(distDir, pair.server) : ''
322
+ };
323
+ return exportFrom(resolvedPair, isServer);
146
324
  }
147
325
  }
148
326
  return null;
@@ -1,14 +1,15 @@
1
1
  import type { BuiltCollection } from '../types.js';
2
2
  /**
3
- * Ensure sanitize config exists
3
+ * Ensure sanitize config exists — the generated server config inside rime.generated/.
4
4
  */
5
5
  export declare function ensureGeneratedConfig(): string;
6
6
  /**
7
- * Ensure user config exists
7
+ * Ensure user config exists — the src/lib/+rime/ folder convention.
8
8
  */
9
9
  export declare function ensureUserConfigExist(): void;
10
10
  /**
11
- * Ensure schema exists
11
+ * Ensure schema exists (see adapter-sqlite/generate-schema/write.server.ts, which writes to
12
+ * the same path).
12
13
  */
13
14
  export declare function ensureSchema(): void;
14
15
  /**
@@ -1,9 +1,9 @@
1
1
  import { existsSync, mkdirSync } from 'node:fs';
2
2
  import path from 'node:path';
3
- import { INPUT_DIR, OUTPUT_DIR } from './dev/constants.js';
3
+ import { INPUT_DIR, OUTPUT_DIR, schemaPath } from './dev/constants.js';
4
4
  const projectRoot = process.cwd();
5
5
  /**
6
- * Ensure sanitize config exists
6
+ * Ensure sanitize config exists — the generated server config inside rime.generated/.
7
7
  */
8
8
  export function ensureGeneratedConfig() {
9
9
  const configGeneratedPath = path.resolve(projectRoot, 'src/lib', OUTPUT_DIR, 'rime.config.server.ts');
@@ -13,20 +13,20 @@ export function ensureGeneratedConfig() {
13
13
  return path.join('$lib', OUTPUT_DIR, 'rime.config.server.js');
14
14
  }
15
15
  /**
16
- * Ensure user config exists
16
+ * Ensure user config exists — the src/lib/+rime/ folder convention.
17
17
  */
18
18
  export function ensureUserConfigExist() {
19
- const configPath = path.resolve(projectRoot, 'src/lib', INPUT_DIR, 'rime.config.ts');
20
- if (!existsSync(configPath)) {
19
+ const folderConfig = path.resolve(projectRoot, 'src/lib', INPUT_DIR, 'rime.config.server.ts');
20
+ if (!existsSync(folderConfig)) {
21
21
  throw new Error('Unable to find config, did you run rime init');
22
22
  }
23
23
  }
24
24
  /**
25
- * Ensure schema exists
25
+ * Ensure schema exists (see adapter-sqlite/generate-schema/write.server.ts, which writes to
26
+ * the same path).
26
27
  */
27
28
  export function ensureSchema() {
28
- const schemaPath = path.resolve(projectRoot, 'src/lib', OUTPUT_DIR, 'schema.server.ts');
29
- if (!existsSync(schemaPath)) {
29
+ if (!existsSync(schemaPath(projectRoot))) {
30
30
  throw new Error('Unable to find schema, did you run rime init');
31
31
  }
32
32
  }
@@ -10,7 +10,7 @@ export default async function (rime) {
10
10
  createCMSHandler(await rime),
11
11
  handleAuth,
12
12
  handleCORS,
13
- handleRoutes,
14
- ...createPluginsHandler(await rime)
13
+ ...createPluginsHandler(await rime),
14
+ handleRoutes
15
15
  ];
16
16
  }
@@ -1 +1 @@
1
- export declare const buildDataConfigMap: import("../index.server.js").Hook<"raw", "update" | "create", "before">;
1
+ export declare const buildDataConfigMap: import("../index.server.js").Hook<"raw", "create" | "update", "before">;
@@ -2,4 +2,4 @@
2
2
  * Handles version-related operations for document updates
3
3
  * Manages specific version updates and new version creation
4
4
  */
5
- export declare const handleNewVersion: import("../index.server.js").Hook<"raw", "update" | "create", "before">;
5
+ export declare const handleNewVersion: import("../index.server.js").Hook<"raw", "create" | "update", "before">;
@@ -1 +1 @@
1
- export declare const buildOriginalDocConfigMap: import("../index.server.js").Hook<"raw", "update" | "create", "before">;
1
+ export declare const buildOriginalDocConfigMap: import("../index.server.js").Hook<"raw", "create" | "update", "before">;
@@ -1,6 +1,6 @@
1
1
  import type { Adapter } from '../../../../adapter-sqlite/index.server.js';
2
2
  import type { FormFieldBuilder } from '../../../fields/builders/form-field-builder.js';
3
- export declare const setDefaultValues: import("../index.server.js").Hook<"raw", "update" | "create", "before">;
3
+ export declare const setDefaultValues: import("../index.server.js").Hook<"raw", "create" | "update", "before">;
4
4
  type GetDefaultValue = (args: {
5
5
  key: string;
6
6
  config: FormFieldBuilder;
@@ -1 +1 @@
1
- export declare const validateFields: import("../index.server.js").Hook<"raw", "update" | "create", "before">;
1
+ export declare const validateFields: import("../index.server.js").Hook<"raw", "create" | "update", "before">;
@@ -81,10 +81,6 @@ export declare const Hooks: {
81
81
  * Creates a before delete hook
82
82
  */
83
83
  beforeDelete: <S extends DocType = "raw">(handler: Hook<S, "delete", "before">) => Hook<S, "delete", "before">;
84
- /**
85
- * Creates an after read hook
86
- */
87
- afterRead: <S extends DocType = "raw">(handler: Hook<S, "read", "after">) => Hook<S, "read", "after">;
88
84
  /**
89
85
  * Creates an after create hook
90
86
  */
@@ -26,10 +26,6 @@ export const Hooks = {
26
26
  * Creates a before delete hook
27
27
  */
28
28
  beforeDelete: (handler) => handler,
29
- /**
30
- * Creates an after read hook
31
- */
32
- afterRead: (handler) => handler,
33
29
  /**
34
30
  * Creates an after create hook
35
31
  */
@@ -0,0 +1,2 @@
1
+ /// <reference path="../../../.rime-modules.d.ts" />
2
+ export { apiInit } from "$rime/modules/rimecms/core/plugins/api-init";
@@ -0,0 +1 @@
1
+ export { apiInit } from "$rime/modules/rimecms/core/plugins/api-init";
@@ -1,7 +1,6 @@
1
1
  import { type RequestHandler } from '@sveltejs/kit';
2
2
  export declare const apiInit: () => {
3
3
  readonly name: "apiInit";
4
- readonly type: "server";
5
4
  readonly routes: {
6
5
  readonly '/api/init': {
7
6
  readonly POST: RequestHandler;
@@ -46,7 +46,6 @@ export const apiInit = definePlugin(() => {
46
46
  };
47
47
  return {
48
48
  name: 'apiInit',
49
- type: 'server',
50
49
  routes: {
51
50
  '/api/init': {
52
51
  POST: requestHandler
@@ -1,5 +1,2 @@
1
- export declare const cacheClient: () => {
2
- readonly name: "cache/client";
3
- readonly type: "client";
4
- readonly configure: <const C extends import("../../config/types.js").SanitizedConfigClient>(config: C) => C;
5
- };
1
+ /// <reference path="../../../.rime-modules.d.ts" />
2
+ export { cache } from "$rime/modules/rimecms/core/plugins/cache";
@@ -1,21 +1 @@
1
- import { definePluginClient } from '../index.js';
2
- import HeaderButton from './HeaderButton.svelte';
3
- export const cacheClient = definePluginClient(() => {
4
- return {
5
- name: 'cache/client',
6
- type: 'client',
7
- configure: (config) => {
8
- config = {
9
- ...config,
10
- panel: {
11
- ...(config.panel || {}),
12
- components: {
13
- ...(config.panel?.components || { header: [], collectionHeader: [] }),
14
- header: [...(config.panel?.components?.header || []), HeaderButton]
15
- }
16
- }
17
- };
18
- return config;
19
- }
20
- };
21
- });
1
+ export { cache } from "$rime/modules/rimecms/core/plugins/cache";
@@ -0,0 +1,4 @@
1
+ export declare const cache: () => {
2
+ readonly name: "cache";
3
+ readonly configure: <const C extends import("../../config/types.js").Config | import("../../config/types.js").SanitizedConfigClient>(config: C) => C;
4
+ };
@@ -0,0 +1,22 @@
1
+ import { definePlugin } from '../index.js';
2
+ import HeaderButton from './HeaderButton.svelte';
3
+ // Signature has to match module.server.ts's — the same call site (whatever the consumer
4
+ // wrote in rime.config.ts) compiles against both, whichever one this build resolves to.
5
+ export const cache = definePlugin(() => {
6
+ return {
7
+ name: 'cache',
8
+ configure: (config) => {
9
+ config = {
10
+ ...config,
11
+ panel: {
12
+ ...(config.panel || {}),
13
+ components: {
14
+ ...(config.panel?.components || { header: [], collectionHeader: [] }),
15
+ header: [...(config.panel?.components?.header || []), HeaderButton]
16
+ }
17
+ }
18
+ };
19
+ return config;
20
+ }
21
+ };
22
+ });
@@ -26,8 +26,7 @@ type CacheOptions = {
26
26
  export declare const toHash: (str: string) => string;
27
27
  export declare const cache: (options?: CacheOptions) => {
28
28
  readonly name: "cache";
29
- readonly type: "server";
30
- readonly configure: <const C extends import("../../config/types.js").Config>(config: C) => C;
29
+ readonly configure: <const C extends import("../../config/types.js").Config | import("../../config/types.js").SanitizedConfigClient>(config: C) => C;
31
30
  readonly routes: {
32
31
  readonly '/api/clear-cache': {
33
32
  readonly POST: () => Response;
@@ -45,7 +45,6 @@ export const cache = definePlugin((options) => {
45
45
  };
46
46
  return {
47
47
  name: 'cache',
48
- type: 'server',
49
48
  configure: (config) => {
50
49
  config = {
51
50
  ...config,
@@ -2,19 +2,12 @@ import type { Config, RouteConfig } from '../../types.js';
2
2
  import type { Handle } from '@sveltejs/kit';
3
3
  import type { SanitizedConfigClient } from '../config/types.js';
4
4
  type MaybeAsyncFunction = (...args: any[]) => any | Promise<any>;
5
- export type PluginClient = {
6
- name: string;
7
- type: 'client';
8
- configure?: <const C extends SanitizedConfigClient>(config: C) => C;
9
- };
10
5
  export type Plugin = {
11
6
  name: string;
12
- type: 'server';
13
- configure?: <const C extends Config>(config: C) => C;
7
+ configure?: <const C extends Config | SanitizedConfigClient>(config: C) => C;
14
8
  actions?: Record<string, MaybeAsyncFunction>;
15
9
  routes?: Record<string, RouteConfig>;
16
10
  handler?: Handle;
17
11
  };
18
12
  export declare function definePlugin<const F extends (options?: any) => Plugin>(factory: F): F;
19
- export declare function definePluginClient<const F extends (options?: any) => PluginClient>(factory: F): F;
20
13
  export {};
@@ -1,6 +1,3 @@
1
1
  export function definePlugin(factory) {
2
2
  return factory;
3
3
  }
4
- export function definePluginClient(factory) {
5
- return factory;
6
- }
@@ -0,0 +1,2 @@
1
+ /// <reference path="../../../.rime-modules.d.ts" />
2
+ export { mailer } from "$rime/modules/rimecms/core/plugins/mailer";
@@ -0,0 +1 @@
1
+ export { mailer } from "$rime/modules/rimecms/core/plugins/mailer";
@@ -1,7 +1,6 @@
1
1
  import type SMTPTransport from 'nodemailer/lib/smtp-transport';
2
2
  export declare const mailer: (smtpConfig: SMTPConfig) => {
3
3
  readonly name: "mailer";
4
- readonly type: "server";
5
4
  readonly actions: {
6
5
  readonly sendMail: (args: {
7
6
  to: string;
@@ -25,7 +25,6 @@ export const mailer = definePlugin((smtpConfig) => {
25
25
  };
26
26
  return {
27
27
  name: 'mailer',
28
- type: 'server',
29
28
  actions: {
30
29
  sendMail
31
30
  }
@@ -0,0 +1,2 @@
1
+ /// <reference path="../../../.rime-modules.d.ts" />
2
+ export { sse } from "$rime/modules/rimecms/core/plugins/sse";
@@ -0,0 +1 @@
1
+ export { sse } from "$rime/modules/rimecms/core/plugins/sse";