rimecms 0.30.2 → 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.
- package/README.md +2 -40
- package/dist/.rime-modules.d.ts +23 -0
- package/dist/.rime-modules.json +1 -0
- package/dist/adapter-sqlite/generate-schema/column.server.js +1 -4
- package/dist/adapter-sqlite/generate-schema/write.server.js +7 -9
- package/dist/core/areas/config/builder.server.d.ts +0 -1
- package/dist/core/collections/auth/hooks/before-upsert/augment-fields-password.server.d.ts +1 -1
- package/dist/core/collections/config/builder.server.d.ts +0 -1
- package/dist/core/collections/upload/hooks/convert-base64.server.d.ts +1 -1
- package/dist/core/collections/upload/hooks/extract-path.server.d.ts +1 -1
- package/dist/core/collections/upload/hooks/handle-path-creation.server.d.ts +1 -1
- package/dist/core/collections/upload/hooks/process-file-upload.server.d.ts +1 -1
- package/dist/core/config/auth/better-auth-hooks.server.js +6 -3
- package/dist/core/config/auth/better-auth.server.d.ts +3 -3
- package/dist/core/config/client/augment-plugins.d.ts +1 -5
- package/dist/core/config/client/augment-plugins.js +3 -3
- package/dist/core/config/client/build-config.d.ts +1 -5
- package/dist/core/config/config-context.server.d.ts +1 -1
- package/dist/core/config/server/augment-plugins.server.d.ts +2 -42
- package/dist/core/config/server/augment-plugins.server.js +11 -6
- package/dist/core/config/server/build-config.server.d.ts +9 -4007
- package/dist/core/config/types.d.ts +12 -11
- package/dist/core/dev/cache/index.server.js +1 -1
- package/dist/core/dev/cli/build/index.d.ts +1 -0
- package/dist/core/dev/cli/build/index.js +10 -5
- package/dist/core/dev/cli/build/templates.d.ts +1 -1
- package/dist/core/dev/cli/build/templates.js +10 -7
- package/dist/core/dev/cli/clear/index.server.js +2 -0
- package/dist/core/dev/cli/generate-manifest/index.server.d.ts +17 -0
- package/dist/core/dev/cli/generate-manifest/index.server.js +221 -0
- package/dist/core/dev/cli/index.js +43 -0
- package/dist/core/dev/cli/init/index.server.js +13 -6
- package/dist/core/dev/cli/init/templates.d.ts +1 -1
- package/dist/core/dev/cli/init/templates.js +13 -4
- package/dist/core/dev/cli/util/package-manager.server.js +29 -26
- package/dist/core/dev/constants.d.ts +21 -2
- package/dist/core/dev/constants.js +26 -0
- package/dist/core/dev/generate/routes/common.server.js +4 -7
- package/dist/core/dev/generate/routes/index.server.js +25 -4
- package/dist/core/dev/generate/runtime/index.server.d.ts +40 -0
- package/dist/core/dev/generate/runtime/index.server.js +85 -0
- package/dist/core/dev/generate/runtime/parse-exports.server.d.ts +7 -0
- package/dist/core/dev/generate/runtime/parse-exports.server.js +40 -0
- package/dist/core/dev/generate/sanitize/index.server.d.ts +0 -7
- package/dist/core/dev/generate/sanitize/index.server.js +57 -5
- package/dist/core/dev/generate/types/index.server.js +21 -20
- package/dist/core/dev/vite/index.server.js +227 -33
- package/dist/core/ensure.server.d.ts +4 -3
- package/dist/core/ensure.server.js +8 -8
- package/dist/core/fields/builders/boolean-builder.d.ts +1 -2
- package/dist/core/fields/builders/field-builder.d.ts +11 -6
- package/dist/core/fields/builders/field-builder.js +12 -5
- package/dist/core/fields/builders/form-field-builder.d.ts +11 -14
- package/dist/core/fields/builders/form-field-builder.js +17 -15
- package/dist/core/fields/util.js +1 -1
- package/dist/core/handlers/index.js +2 -2
- package/dist/core/operations/blocks/extract.server.js +1 -1
- package/dist/core/operations/hooks/before-read/process-document-fields.server.js +3 -3
- package/dist/core/operations/hooks/before-upsert/data-config-map.server.d.ts +1 -1
- package/dist/core/operations/hooks/before-upsert/handle-new-version.server.d.ts +1 -1
- package/dist/core/operations/hooks/before-upsert/original-config-map.server.d.ts +1 -1
- package/dist/core/operations/hooks/before-upsert/set-default-values.server.d.ts +1 -1
- package/dist/core/operations/hooks/before-upsert/set-default-values.server.js +3 -3
- package/dist/core/operations/hooks/before-upsert/validate-fields.server.d.ts +1 -1
- package/dist/core/operations/hooks/before-upsert/validate-fields.server.js +7 -7
- package/dist/core/operations/hooks/index.server.d.ts +0 -4
- package/dist/core/operations/hooks/index.server.js +0 -4
- package/dist/core/operations/shared/fallback-data-from-original.js +1 -1
- package/dist/core/operations/tree/extract.server.js +1 -1
- package/dist/core/plugins/api-init/index.d.ts +2 -0
- package/dist/core/plugins/api-init/index.js +1 -0
- package/dist/core/plugins/api-init/{index.server.d.ts → module.server.d.ts} +0 -1
- package/dist/core/plugins/api-init/{index.server.js → module.server.js} +2 -3
- package/dist/core/plugins/cache/index.d.ts +2 -5
- package/dist/core/plugins/cache/index.js +1 -21
- package/dist/core/plugins/cache/module.d.ts +4 -0
- package/dist/core/plugins/cache/module.js +22 -0
- package/dist/core/plugins/cache/{index.server.d.ts → module.server.d.ts} +1 -2
- package/dist/core/plugins/cache/{index.server.js → module.server.js} +0 -1
- package/dist/core/plugins/index.d.ts +1 -8
- package/dist/core/plugins/index.js +0 -3
- package/dist/core/plugins/mailer/index.d.ts +2 -0
- package/dist/core/plugins/mailer/index.js +1 -0
- package/dist/core/plugins/mailer/{index.server.d.ts → module.server.d.ts} +0 -1
- package/dist/core/plugins/mailer/{index.server.js → module.server.js} +0 -1
- package/dist/core/plugins/sse/index.d.ts +2 -0
- package/dist/core/plugins/sse/index.js +1 -0
- package/dist/core/plugins/sse/{index.server.d.ts → module.server.d.ts} +0 -1
- package/dist/core/plugins/sse/{index.server.js → module.server.js} +0 -1
- package/dist/core/rime.server.d.ts +16 -16
- package/dist/fields/blocks/index.d.ts +1 -1
- package/dist/fields/blocks/index.js +5 -2
- package/dist/fields/checkbox/index.d.ts +1 -1
- package/dist/fields/checkbox/index.js +3 -2
- package/dist/fields/combobox/index.d.ts +1 -1
- package/dist/fields/combobox/index.js +4 -2
- package/dist/fields/date/index.d.ts +2 -2
- package/dist/fields/date/index.js +7 -5
- package/dist/fields/email/index.d.ts +3 -3
- package/dist/fields/email/index.js +9 -8
- package/dist/fields/group/component/Group.svelte +1 -1
- package/dist/fields/group/index.d.ts +1 -1
- package/dist/fields/group/index.js +4 -2
- package/dist/fields/link/index.d.ts +1 -1
- package/dist/fields/link/index.js +63 -56
- package/dist/fields/link/module.server.d.ts +2 -0
- package/dist/fields/link/{runtime.server.js → module.server.js} +0 -8
- package/dist/fields/number/index.d.ts +1 -1
- package/dist/fields/number/index.js +3 -2
- package/dist/fields/radio/index.d.ts +1 -1
- package/dist/fields/radio/index.js +4 -2
- package/dist/fields/relation/index.d.ts +2 -6
- package/dist/fields/relation/index.js +64 -75
- package/dist/fields/relation/{runtime.d.ts → module.d.ts} +2 -2
- package/dist/fields/relation/module.js +4 -0
- package/dist/fields/relation/module.server.d.ts +5 -0
- package/dist/fields/relation/{runtime.server.js → module.server.js} +3 -6
- package/dist/fields/rich-text/index.d.ts +1 -1
- package/dist/fields/rich-text/index.js +3 -2
- package/dist/fields/select/index.d.ts +1 -1
- package/dist/fields/select/index.js +4 -1
- package/dist/fields/slug/index.d.ts +1 -1
- package/dist/fields/slug/index.js +3 -2
- package/dist/fields/tabs/index.d.ts +1 -1
- package/dist/fields/tabs/index.js +10 -2
- package/dist/fields/text/index.d.ts +1 -1
- package/dist/fields/text/index.js +3 -2
- package/dist/fields/textarea/index.d.ts +1 -1
- package/dist/fields/textarea/index.js +3 -2
- package/dist/fields/time/index.d.ts +1 -1
- package/dist/fields/time/index.js +3 -2
- package/dist/fields/toggle/index.d.ts +1 -1
- package/dist/fields/toggle/index.js +3 -3
- package/dist/fields/tree/index.d.ts +1 -1
- package/dist/fields/tree/index.js +4 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/panel/components/fields/RenderFields.svelte +1 -1
- package/dist/panel/context/collection.svelte.js +5 -3
- package/dist/panel/context/config.svelte.d.ts +2 -2
- package/dist/panel/context/documentForm.svelte.d.ts +2 -2
- package/dist/panel/context/documentForm.svelte.js +47 -28
- package/dist/panel/context/form.svelte.js +3 -3
- package/dist/panel/context/live.svelte.js +1 -1
- package/dist/panel/pages/auth/sign-in/SignIn.svelte +6 -1
- package/dist/server.d.ts +4 -4
- package/dist/server.js +4 -4
- package/dist/types.d.ts +1 -1
- package/dist/util/doc.js +1 -1
- package/package.json +2 -9
- package/dist/fields/blocks/index.server.d.ts +0 -3
- package/dist/fields/blocks/index.server.js +0 -5
- package/dist/fields/checkbox/index.server.d.ts +0 -3
- package/dist/fields/checkbox/index.server.js +0 -3
- package/dist/fields/combobox/index.server.d.ts +0 -3
- package/dist/fields/combobox/index.server.js +0 -4
- package/dist/fields/date/index.server.d.ts +0 -3
- package/dist/fields/date/index.server.js +0 -3
- package/dist/fields/email/index.server.d.ts +0 -4
- package/dist/fields/email/index.server.js +0 -3
- package/dist/fields/group/index.server.d.ts +0 -2
- package/dist/fields/group/index.server.js +0 -12
- package/dist/fields/index.server.d.ts +0 -14
- package/dist/fields/index.server.js +0 -65
- package/dist/fields/link/runtime.server.d.ts +0 -5
- package/dist/fields/number/index.server.d.ts +0 -3
- package/dist/fields/number/index.server.js +0 -3
- package/dist/fields/radio/index.server.d.ts +0 -3
- package/dist/fields/radio/index.server.js +0 -4
- package/dist/fields/relation/runtime.js +0 -4
- package/dist/fields/relation/runtime.server.d.ts +0 -8
- package/dist/fields/rich-text/index.server.d.ts +0 -3
- package/dist/fields/rich-text/index.server.js +0 -3
- package/dist/fields/select/index.server.d.ts +0 -3
- package/dist/fields/select/index.server.js +0 -4
- package/dist/fields/slug/index.server.d.ts +0 -3
- package/dist/fields/slug/index.server.js +0 -3
- package/dist/fields/tabs/index.server.d.ts +0 -3
- package/dist/fields/tabs/index.server.js +0 -18
- package/dist/fields/text/index.server.d.ts +0 -3
- package/dist/fields/text/index.server.js +0 -3
- package/dist/fields/textarea/index.server.d.ts +0 -3
- package/dist/fields/textarea/index.server.js +0 -3
- package/dist/fields/time/index.server.d.ts +0 -3
- package/dist/fields/time/index.server.js +0 -3
- package/dist/fields/toggle/index.server.d.ts +0 -3
- package/dist/fields/toggle/index.server.js +0 -3
- package/dist/fields/tree/index.server.d.ts +0 -3
- package/dist/fields/tree/index.server.js +0 -4
- /package/dist/fields/link/{runtime.d.ts → module.d.ts} +0 -0
- /package/dist/fields/link/{runtime.js → module.js} +0 -0
|
@@ -1,30 +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 {
|
|
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';
|
|
8
11
|
import { sanitize } from '../generate/sanitize/index.server.js';
|
|
9
12
|
dotenv.config({ override: true });
|
|
10
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
|
+
}
|
|
11
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. */
|
|
12
47
|
const VCoreId = '$rime/config';
|
|
13
48
|
const VSchemaId = '$rime/schema';
|
|
14
|
-
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
|
|
20
|
-
|
|
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';
|
|
21
79
|
const resolvedVModule = (name) => '\0' + name;
|
|
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
|
+
}
|
|
22
99
|
return {
|
|
23
100
|
name: 'virtual-rime',
|
|
24
101
|
configureServer(server) {
|
|
25
102
|
// Add a listener for when the server starts
|
|
26
103
|
server.httpServer?.once('listening', () => {
|
|
27
104
|
dev && ensureHasInit();
|
|
105
|
+
regenerateModulesDeclaration();
|
|
28
106
|
// Check if we need to rebuild
|
|
29
107
|
const shouldRebuild = process.argv.includes('--rebuild');
|
|
30
108
|
if (shouldRebuild && existsSync(RIME_DEV_CACHE_DIR)) {
|
|
@@ -32,11 +110,30 @@ export function rime() {
|
|
|
32
110
|
logger.info('--rebuild : node_modules/.rime folder deleted');
|
|
33
111
|
}
|
|
34
112
|
});
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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 () => {
|
|
40
137
|
// Sanitize the config client/server
|
|
41
138
|
try {
|
|
42
139
|
await sanitize();
|
|
@@ -47,7 +144,11 @@ export function rime() {
|
|
|
47
144
|
}
|
|
48
145
|
// Trigger generation
|
|
49
146
|
try {
|
|
50
|
-
|
|
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);
|
|
51
152
|
// The config's default export is the createRime() promise; ssrLoadModule
|
|
52
153
|
// only awaits the module's synchronous evaluation, so we must await it
|
|
53
154
|
// directly to observe init errors (e.g. invalid config) here instead of
|
|
@@ -57,7 +158,25 @@ export function rime() {
|
|
|
57
158
|
catch (error) {
|
|
58
159
|
logger.error('Failed to reload the config', error.message);
|
|
59
160
|
}
|
|
161
|
+
})();
|
|
162
|
+
await reloadInFlight;
|
|
163
|
+
reloadInFlight = null;
|
|
164
|
+
if (reloadQueued) {
|
|
165
|
+
reloadQueued = false;
|
|
166
|
+
await runConfigReload();
|
|
60
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);
|
|
61
180
|
});
|
|
62
181
|
},
|
|
63
182
|
config() {
|
|
@@ -66,12 +185,7 @@ export function rime() {
|
|
|
66
185
|
external: ['sharp']
|
|
67
186
|
},
|
|
68
187
|
optimizeDeps: {
|
|
69
|
-
|
|
70
|
-
// their source files into one chunk, which erases the per-file identity that
|
|
71
|
-
// the $rime/runtime virtual module (see resolveId/load below) relies on to find
|
|
72
|
-
// each field's sibling runtime.ts/runtime.server.ts — pre-bundling makes the
|
|
73
|
-
// "importer" resolve to the chunk itself instead of the real source file.
|
|
74
|
-
exclude: ['sharp', 'rimecms', 'rimecms/fields', 'rimecms/panel'],
|
|
188
|
+
exclude: ['sharp'],
|
|
75
189
|
include: ['@lucide/svelte']
|
|
76
190
|
},
|
|
77
191
|
build: {
|
|
@@ -79,6 +193,14 @@ export function rime() {
|
|
|
79
193
|
external: ['sharp']
|
|
80
194
|
},
|
|
81
195
|
target: 'es2022'
|
|
196
|
+
},
|
|
197
|
+
server: {
|
|
198
|
+
watch: {
|
|
199
|
+
// db/*.sqlite (+ its -wal/-shm sidecars) and logs/ are runtime data, not source —
|
|
200
|
+
// every write (e.g. saving a document) touches them, and without this Vite's default
|
|
201
|
+
// whole-project-root watch treats that churn as a source change and force-reloads.
|
|
202
|
+
ignored: ['**/db/**', '**/logs/**']
|
|
203
|
+
}
|
|
82
204
|
}
|
|
83
205
|
};
|
|
84
206
|
},
|
|
@@ -91,12 +213,18 @@ export function rime() {
|
|
|
91
213
|
}
|
|
92
214
|
return null;
|
|
93
215
|
}
|
|
94
|
-
if (
|
|
216
|
+
if (!isInstalledDependency(import.meta.url) && file.includes('src/lib/core/config')) {
|
|
95
217
|
logger.info('reload core config');
|
|
96
218
|
const module = invalidateVModule(VCoreId);
|
|
97
219
|
if (module)
|
|
98
220
|
return [module];
|
|
99
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
|
+
}
|
|
100
228
|
},
|
|
101
229
|
resolveId(id, importer) {
|
|
102
230
|
if (id === VCoreId) {
|
|
@@ -105,29 +233,95 @@ export function rime() {
|
|
|
105
233
|
if (id === VSchemaId) {
|
|
106
234
|
return resolvedVModule(id);
|
|
107
235
|
}
|
|
108
|
-
if (id ===
|
|
109
|
-
return
|
|
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}`);
|
|
110
256
|
}
|
|
111
257
|
return null;
|
|
112
258
|
},
|
|
113
|
-
load(id) {
|
|
259
|
+
async load(id) {
|
|
114
260
|
const isServer = this.environment?.config?.consumer === 'server';
|
|
115
261
|
if (id === resolvedVModule(VCoreId)) {
|
|
116
262
|
const corePath = isServer ? 'rimecms/config/server' : 'rimecms/config';
|
|
117
263
|
return `export * from '${corePath}';`;
|
|
118
264
|
}
|
|
119
265
|
if (id === resolvedVModule(VSchemaId) && isServer) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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');
|
|
123
272
|
return `export * from '${modulePath}'; export { default } from '${modulePath}';`;
|
|
124
273
|
}
|
|
125
274
|
}
|
|
126
|
-
if (id
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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);
|
|
324
|
+
}
|
|
131
325
|
}
|
|
132
326
|
return null;
|
|
133
327
|
}
|
|
@@ -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
|
|
20
|
-
if (!existsSync(
|
|
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
|
-
|
|
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
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { DefaultValueFn, FieldValidationFunc, FormField } from '../../../fields/types.js';
|
|
2
|
-
import type { DataType } from './form-field-builder.js';
|
|
3
2
|
import { FormFieldBuilder } from './form-field-builder.js';
|
|
4
3
|
type BooleanField = FormField & {
|
|
5
4
|
type: any;
|
|
@@ -10,7 +9,7 @@ type BooleanField = FormField & {
|
|
|
10
9
|
export declare class BooleanFieldBuilder<T extends BooleanField = BooleanField> extends FormFieldBuilder<T> {
|
|
11
10
|
constructor(name: string, type: string);
|
|
12
11
|
defaultValue(value: boolean | DefaultValueFn<boolean>): this;
|
|
13
|
-
get dataType():
|
|
12
|
+
get dataType(): 'boolean';
|
|
14
13
|
compile(): import("../../../util/types.js").WithoutBuilders<T> & {
|
|
15
14
|
component: import("svelte").Component<any>;
|
|
16
15
|
cell?: import("svelte").Component<{
|
|
@@ -7,14 +7,14 @@ import type { Field, FieldAccess } from '../../../fields/types.js';
|
|
|
7
7
|
* generically-typed prop — stay bivariant on their params, exactly like the
|
|
8
8
|
* class methods this replaced. Property syntax would make TS check those
|
|
9
9
|
* params strictly/contravariantly and break that assignability. */
|
|
10
|
-
export type
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
export type FieldUse = {
|
|
11
|
+
accessRead(...args: Parameters<FieldAccess>): boolean;
|
|
12
|
+
accessCreate(...args: Parameters<FieldAccess>): boolean;
|
|
13
|
+
accessUpdate(...args: Parameters<FieldAccess>): boolean;
|
|
14
|
+
generateType(): string;
|
|
14
15
|
};
|
|
15
16
|
export declare class FieldBuilder<T extends Field = Field> {
|
|
16
17
|
field: T;
|
|
17
|
-
_metaUrl?: string;
|
|
18
18
|
constructor(type: string);
|
|
19
19
|
get name(): string;
|
|
20
20
|
className(str: string): this;
|
|
@@ -47,9 +47,14 @@ export declare class FieldBuilder<T extends Field = Field> {
|
|
|
47
47
|
* (`T extends FieldBuilder<any>`) strictly, not with the bivariant
|
|
48
48
|
* leniency normal overrides get, so a param-less base signature breaks
|
|
49
49
|
* assignability everywhere. */
|
|
50
|
-
get
|
|
50
|
+
get use(): FieldUse;
|
|
51
51
|
get component(): Component<any> | null;
|
|
52
52
|
get cell(): Component<{
|
|
53
53
|
value: any;
|
|
54
54
|
}> | null;
|
|
55
|
+
/** The type-generation script's only entry point (via `.use.generateType()`) — `protected`
|
|
56
|
+
* so it never surfaces on the fluent chain (`text('title').generateType()` would be a
|
|
57
|
+
* compile error), while staying a real overridable method so every concrete field can
|
|
58
|
+
* provide its own, unlike a private `#field` which can't be polymorphically overridden. */
|
|
59
|
+
protected generateType(): string;
|
|
55
60
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export class FieldBuilder {
|
|
2
2
|
field;
|
|
3
|
-
_metaUrl;
|
|
4
3
|
constructor(type) {
|
|
5
4
|
this.field = {
|
|
6
5
|
name: '',
|
|
@@ -49,14 +48,15 @@ export class FieldBuilder {
|
|
|
49
48
|
* (`T extends FieldBuilder<any>`) strictly, not with the bivariant
|
|
50
49
|
* leniency normal overrides get, so a param-less base signature breaks
|
|
51
50
|
* assignability everywhere. */
|
|
52
|
-
get
|
|
51
|
+
get use() {
|
|
53
52
|
return {
|
|
54
53
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
55
|
-
|
|
54
|
+
accessRead: (..._args) => true,
|
|
56
55
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
57
|
-
|
|
56
|
+
accessCreate: (..._args) => true,
|
|
58
57
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
59
|
-
|
|
58
|
+
accessUpdate: (..._args) => true,
|
|
59
|
+
generateType: () => this.generateType()
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
62
|
get component() {
|
|
@@ -65,4 +65,11 @@ export class FieldBuilder {
|
|
|
65
65
|
get cell() {
|
|
66
66
|
return null;
|
|
67
67
|
}
|
|
68
|
+
/** The type-generation script's only entry point (via `.use.generateType()`) — `protected`
|
|
69
|
+
* so it never surfaces on the fluent chain (`text('title').generateType()` would be a
|
|
70
|
+
* compile error), while staying a real overridable method so every concrete field can
|
|
71
|
+
* provide its own, unlike a private `#field` which can't be polymorphically overridden. */
|
|
72
|
+
generateType() {
|
|
73
|
+
return '';
|
|
74
|
+
}
|
|
68
75
|
}
|
|
@@ -2,16 +2,16 @@ import type { FieldPanelTableConfig } from '../../../types.js';
|
|
|
2
2
|
import type { Dic } from '../../../util/types.js';
|
|
3
3
|
import type { RequestEvent } from '@sveltejs/kit';
|
|
4
4
|
import type { FieldAccess, FieldHook, FieldHookClient, FieldHookContext, FieldHookShared, FieldValidationFunc, FieldWidth, FormField } from '../../../fields/types.js';
|
|
5
|
-
import { FieldBuilder, type
|
|
5
|
+
import { FieldBuilder, type FieldUse } from './field-builder.js';
|
|
6
6
|
/** Adapter-agnostic storage primitive — column syntax only, not default-value semantics. */
|
|
7
7
|
export type DataType = 'text' | 'boolean' | 'number' | 'timestamp' | 'json';
|
|
8
|
-
/** Method-syntax, same reasoning as `
|
|
8
|
+
/** Method-syntax, same reasoning as `FieldUse` — keeps `run`'s params
|
|
9
9
|
* bivariant across concrete field types so e.g. `SlugFieldBuilder` still
|
|
10
10
|
* satisfies a prop typed `FormFieldBuilder<FormField>`. */
|
|
11
|
-
export type
|
|
11
|
+
export type FormFieldUse<T extends FormField> = FieldUse & {
|
|
12
12
|
isEmpty(value: unknown): boolean;
|
|
13
13
|
validate(value: unknown, context: Parameters<FieldValidationFunc<T>>[1]): true | string;
|
|
14
|
-
|
|
14
|
+
isVisible(doc: Dic, siblings: Dic): boolean;
|
|
15
15
|
onChange(value: unknown, context: Parameters<FieldHookClient>[1]): void;
|
|
16
16
|
beforeRead(value: unknown, context: Omit<FieldHookContext<T>, 'config'>): Promise<any>;
|
|
17
17
|
beforeSave(value: unknown, context: FieldHookContext<T>): Promise<any>;
|
|
@@ -38,15 +38,9 @@ export type FieldReference = FieldReferenceOptions & {
|
|
|
38
38
|
table: string;
|
|
39
39
|
};
|
|
40
40
|
export declare class FormFieldBuilder<T extends FormField = FormField> extends FieldBuilder<T> {
|
|
41
|
-
/**
|
|
42
|
-
*
|
|
43
|
-
|
|
44
|
-
* project's config, and a plain base field would conflict with subclasses
|
|
45
|
-
* that need a computed getter (e.g. `select`, whose dataType depends on
|
|
46
|
-
* `many`). Container builders (Group/Tabs/Blocks/Tree) never get asked,
|
|
47
|
-
* since the adapter's tree-walker special-cases them before reaching the
|
|
48
|
-
* generic column-rendering branch; `column.server.ts` reads `.dataType`
|
|
49
|
-
* via a narrow cast for the same reason it already casts `unique`. */
|
|
41
|
+
/** Column storage type, used to generate the DB schema. Every field
|
|
42
|
+
* builder that stores data must define its own. */
|
|
43
|
+
get dataType(): DataType;
|
|
50
44
|
_references: FieldReference | null;
|
|
51
45
|
constructor(name: string, type: string);
|
|
52
46
|
label(label: string): this;
|
|
@@ -83,7 +77,7 @@ export declare class FormFieldBuilder<T extends FormField = FormField> extends F
|
|
|
83
77
|
onChange(hook: FieldHookClient): this;
|
|
84
78
|
/** Behavior the builder runs on your behalf — invoking a stored function or
|
|
85
79
|
* hook list, not a data read (see `.get` for that). */
|
|
86
|
-
get
|
|
80
|
+
get use(): FormFieldUse<T>;
|
|
87
81
|
hint(hint: string): this;
|
|
88
82
|
clone(): typeof this;
|
|
89
83
|
/** Server-side schema concern, same family as $beforeRead/$beforeSave. */
|
|
@@ -91,4 +85,7 @@ export declare class FormFieldBuilder<T extends FormField = FormField> extends F
|
|
|
91
85
|
$beforeRead(hook: FieldHook<T>): this;
|
|
92
86
|
$beforeSave(hook: FieldHook<T>): this;
|
|
93
87
|
beforeValidate(hook: FieldHookShared): this;
|
|
88
|
+
/** Fallback for a data-bearing field that doesn't override this: still shows up in the
|
|
89
|
+
* generated document type (as `any`) instead of silently vanishing from it. */
|
|
90
|
+
protected generateType(): string;
|
|
94
91
|
}
|
|
@@ -2,15 +2,11 @@ import { capitalize } from '../../../util/string.js';
|
|
|
2
2
|
import cloneDeep from 'clone-deep';
|
|
3
3
|
import { FieldBuilder } from './field-builder.js';
|
|
4
4
|
export class FormFieldBuilder extends FieldBuilder {
|
|
5
|
-
/**
|
|
6
|
-
*
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* `many`). Container builders (Group/Tabs/Blocks/Tree) never get asked,
|
|
11
|
-
* since the adapter's tree-walker special-cases them before reaching the
|
|
12
|
-
* generic column-rendering branch; `column.server.ts` reads `.dataType`
|
|
13
|
-
* via a narrow cast for the same reason it already casts `unique`. */
|
|
5
|
+
/** Column storage type, used to generate the DB schema. Every field
|
|
6
|
+
* builder that stores data must define its own. */
|
|
7
|
+
get dataType() {
|
|
8
|
+
throw new Error(`${this.constructor.name} does not implement dataType`);
|
|
9
|
+
}
|
|
14
10
|
_references = null;
|
|
15
11
|
constructor(name, type) {
|
|
16
12
|
super(type);
|
|
@@ -100,7 +96,7 @@ export class FormFieldBuilder extends FieldBuilder {
|
|
|
100
96
|
}
|
|
101
97
|
/** Behavior the builder runs on your behalf — invoking a stored function or
|
|
102
98
|
* hook list, not a data read (see `.get` for that). */
|
|
103
|
-
get
|
|
99
|
+
get use() {
|
|
104
100
|
return {
|
|
105
101
|
isEmpty: (value) => this.field.isEmpty(value),
|
|
106
102
|
validate: (value, context) => {
|
|
@@ -109,7 +105,7 @@ export class FormFieldBuilder extends FieldBuilder {
|
|
|
109
105
|
}
|
|
110
106
|
return true;
|
|
111
107
|
},
|
|
112
|
-
|
|
108
|
+
isVisible: (doc, siblings) => {
|
|
113
109
|
if (this.field.condition) {
|
|
114
110
|
try {
|
|
115
111
|
return this.field.condition(doc, siblings);
|
|
@@ -121,9 +117,9 @@ export class FormFieldBuilder extends FieldBuilder {
|
|
|
121
117
|
}
|
|
122
118
|
return true;
|
|
123
119
|
},
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
120
|
+
accessRead: (...args) => !!this.field.access?.read?.(...args),
|
|
121
|
+
accessCreate: (...args) => !!this.field.access?.create?.(...args),
|
|
122
|
+
accessUpdate: (...args) => !!this.field.access?.update?.(...args),
|
|
127
123
|
onChange: (value, context) => {
|
|
128
124
|
for (const hook of this.field.hooks?.onChange ?? []) {
|
|
129
125
|
hook(value, context);
|
|
@@ -153,7 +149,8 @@ export class FormFieldBuilder extends FieldBuilder {
|
|
|
153
149
|
defaultValue: (context = {}) => {
|
|
154
150
|
const value = this.field.defaultValue;
|
|
155
151
|
return typeof value === 'function' ? value(context) : value;
|
|
156
|
-
}
|
|
152
|
+
},
|
|
153
|
+
generateType: () => this.generateType()
|
|
157
154
|
};
|
|
158
155
|
}
|
|
159
156
|
hint(hint) {
|
|
@@ -192,4 +189,9 @@ export class FormFieldBuilder extends FieldBuilder {
|
|
|
192
189
|
this.field.hooks.beforeValidate.push(hook);
|
|
193
190
|
return this;
|
|
194
191
|
}
|
|
192
|
+
/** Fallback for a data-bearing field that doesn't override this: still shows up in the
|
|
193
|
+
* generated document type (as `any`) instead of silently vanishing from it. */
|
|
194
|
+
generateType() {
|
|
195
|
+
return `${this.field.name}${this.get.required ? '' : '?'}: any`;
|
|
196
|
+
}
|
|
195
197
|
}
|
package/dist/core/fields/util.js
CHANGED