rimecms 0.30.2 → 0.30.3
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 +1 -1
- package/dist/adapter-sqlite/generate-schema/column.server.js +1 -4
- package/dist/adapter-sqlite/generate-schema/write.server.js +1 -3
- package/dist/core/config/auth/better-auth-hooks.server.js +6 -3
- 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/index.js +1 -0
- package/dist/core/dev/cli/init/index.server.js +3 -3
- package/dist/core/dev/cli/init/templates.js +8 -1
- package/dist/core/dev/cli/util/package-manager.server.js +27 -26
- package/dist/core/dev/constants.d.ts +11 -2
- package/dist/core/dev/constants.js +11 -0
- package/dist/core/dev/generate/runtime/index.server.d.ts +24 -0
- package/dist/core/dev/generate/runtime/index.server.js +93 -0
- package/dist/core/dev/generate/types/index.server.js +42 -18
- package/dist/core/dev/vite/index.server.js +39 -23
- 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/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/set-default-values.server.js +3 -3
- package/dist/core/operations/hooks/before-upsert/validate-fields.server.js +7 -7
- 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.server.js +2 -2
- 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 +9 -5
- 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 +5 -9
- 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/panel/components/fields/RenderFields.svelte +1 -1
- package/dist/panel/context/collection.svelte.js +5 -3
- package/dist/panel/context/documentForm.svelte.js +47 -28
- package/dist/panel/context/form.svelte.js +3 -3
- package/dist/util/doc.js +1 -1
- package/package.json +1 -8
- 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
|
@@ -4,21 +4,27 @@ 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, OUTPUT_DIR } from '../constants.js';
|
|
7
|
+
import { INPUT_DIR, isInstalledDependency, OUTPUT_DIR } from '../constants.js';
|
|
8
|
+
import { buildRuntimeRegistry } from '../generate/runtime/index.server.js';
|
|
8
9
|
import { sanitize } from '../generate/sanitize/index.server.js';
|
|
9
10
|
dotenv.config({ override: true });
|
|
10
11
|
const dev = process.env.NODE_ENV === 'development';
|
|
11
12
|
export function rime() {
|
|
12
13
|
const VCoreId = '$rime/config';
|
|
13
14
|
const VSchemaId = '$rime/schema';
|
|
14
|
-
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
|
|
20
|
-
|
|
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/';
|
|
21
25
|
const resolvedVModule = (name) => '\0' + name;
|
|
26
|
+
let runtimeRegistry = null;
|
|
27
|
+
const getRuntimeRegistry = () => (runtimeRegistry ??= buildRuntimeRegistry());
|
|
22
28
|
return {
|
|
23
29
|
name: 'virtual-rime',
|
|
24
30
|
configureServer(server) {
|
|
@@ -66,12 +72,7 @@ export function rime() {
|
|
|
66
72
|
external: ['sharp']
|
|
67
73
|
},
|
|
68
74
|
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'],
|
|
75
|
+
exclude: ['sharp'],
|
|
75
76
|
include: ['@lucide/svelte']
|
|
76
77
|
},
|
|
77
78
|
build: {
|
|
@@ -79,6 +80,14 @@ export function rime() {
|
|
|
79
80
|
external: ['sharp']
|
|
80
81
|
},
|
|
81
82
|
target: 'es2022'
|
|
83
|
+
},
|
|
84
|
+
server: {
|
|
85
|
+
watch: {
|
|
86
|
+
// db/*.sqlite (+ its -wal/-shm sidecars) and logs/ are runtime data, not source —
|
|
87
|
+
// every write (e.g. saving a document) touches them, and without this Vite's default
|
|
88
|
+
// whole-project-root watch treats that churn as a source change and force-reloads.
|
|
89
|
+
ignored: ['**/db/**', '**/logs/**']
|
|
90
|
+
}
|
|
82
91
|
}
|
|
83
92
|
};
|
|
84
93
|
},
|
|
@@ -91,22 +100,27 @@ export function rime() {
|
|
|
91
100
|
}
|
|
92
101
|
return null;
|
|
93
102
|
}
|
|
94
|
-
if (
|
|
103
|
+
if (!isInstalledDependency(import.meta.url) && file.includes('src/lib/core/config')) {
|
|
95
104
|
logger.info('reload core config');
|
|
96
105
|
const module = invalidateVModule(VCoreId);
|
|
97
106
|
if (module)
|
|
98
107
|
return [module];
|
|
99
108
|
}
|
|
100
109
|
},
|
|
101
|
-
resolveId(id
|
|
110
|
+
resolveId(id) {
|
|
102
111
|
if (id === VCoreId) {
|
|
103
112
|
return resolvedVModule(id);
|
|
104
113
|
}
|
|
105
114
|
if (id === VSchemaId) {
|
|
106
115
|
return resolvedVModule(id);
|
|
107
116
|
}
|
|
108
|
-
if (id
|
|
109
|
-
|
|
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);
|
|
110
124
|
}
|
|
111
125
|
return null;
|
|
112
126
|
},
|
|
@@ -123,11 +137,13 @@ export function rime() {
|
|
|
123
137
|
return `export * from '${modulePath}'; export { default } from '${modulePath}';`;
|
|
124
138
|
}
|
|
125
139
|
}
|
|
126
|
-
if (id.startsWith(
|
|
127
|
-
const
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
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}';`;
|
|
146
|
+
}
|
|
131
147
|
}
|
|
132
148
|
return null;
|
|
133
149
|
}
|
|
@@ -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
|
@@ -5,7 +5,7 @@ export function extractBlocks({ data, configMap }) {
|
|
|
5
5
|
Object.entries(configMap).forEach(([path, config]) => {
|
|
6
6
|
if (config.type === 'blocks') {
|
|
7
7
|
const value = getValueAtPath(path, data);
|
|
8
|
-
const isEmptyValue = config.
|
|
8
|
+
const isEmptyValue = config.use.isEmpty(value);
|
|
9
9
|
if (value && !isEmptyValue) {
|
|
10
10
|
value.forEach((block, index) => {
|
|
11
11
|
if (block.type) {
|
|
@@ -10,12 +10,12 @@ export const processDocumentFields = Hooks.beforeRead(async (args) => {
|
|
|
10
10
|
for (const [key, config] of Object.entries(configMap)) {
|
|
11
11
|
let value = getValueAtPath(key, doc);
|
|
12
12
|
let isEmpty;
|
|
13
|
-
if (!config.
|
|
13
|
+
if (!config.use.accessRead(event.locals.user)) {
|
|
14
14
|
doc = deleteValueAtPath(doc, key);
|
|
15
15
|
continue;
|
|
16
16
|
}
|
|
17
17
|
if (value !== undefined && value !== null) {
|
|
18
|
-
value = await config.
|
|
18
|
+
value = await config.use.beforeRead(value, {
|
|
19
19
|
event,
|
|
20
20
|
operation: args.context,
|
|
21
21
|
documentId: doc.id
|
|
@@ -23,7 +23,7 @@ export const processDocumentFields = Hooks.beforeRead(async (args) => {
|
|
|
23
23
|
doc = setValueAtPath(key, doc, value);
|
|
24
24
|
}
|
|
25
25
|
try {
|
|
26
|
-
isEmpty = config.
|
|
26
|
+
isEmpty = config.use.isEmpty(value);
|
|
27
27
|
}
|
|
28
28
|
catch {
|
|
29
29
|
isEmpty = false;
|
|
@@ -17,7 +17,7 @@ export const setDefaultValues = Hooks.beforeUpsert(async (args) => {
|
|
|
17
17
|
let isEmpty;
|
|
18
18
|
const shouldAddDefault = operation === 'create' || (operation === 'update' && config.get.required);
|
|
19
19
|
try {
|
|
20
|
-
isEmpty = config.
|
|
20
|
+
isEmpty = config.use.isEmpty(value);
|
|
21
21
|
}
|
|
22
22
|
catch {
|
|
23
23
|
isEmpty = false;
|
|
@@ -60,11 +60,11 @@ const defaultRelationValue = async (config, key, adapter) => {
|
|
|
60
60
|
documentId: documentId
|
|
61
61
|
}));
|
|
62
62
|
};
|
|
63
|
-
return await buildRelation(config.
|
|
63
|
+
return await buildRelation(config.use.defaultValue({ event: getRequestEvent() }));
|
|
64
64
|
};
|
|
65
65
|
export const getDefaultValue = async ({ key, config, adapter }) => {
|
|
66
66
|
if (config instanceof RelationFieldBuilder) {
|
|
67
67
|
return await defaultRelationValue(config, key, adapter);
|
|
68
68
|
}
|
|
69
|
-
return config.
|
|
69
|
+
return config.use.defaultValue({ event: getRequestEvent() });
|
|
70
70
|
};
|
|
@@ -63,7 +63,7 @@ export const validateFields = Hooks.beforeUpsert(async (args) => {
|
|
|
63
63
|
/* Field hook before validate
|
|
64
64
|
/****************************************************/
|
|
65
65
|
if (value !== undefined && value !== null && !skipHooks) {
|
|
66
|
-
value = await config.
|
|
66
|
+
value = await config.use.beforeValidate(value, { config, data: args.data });
|
|
67
67
|
output = setValueAtPath(key, output, value);
|
|
68
68
|
}
|
|
69
69
|
/****************************************************/
|
|
@@ -71,7 +71,7 @@ export const validateFields = Hooks.beforeUpsert(async (args) => {
|
|
|
71
71
|
/****************************************************/
|
|
72
72
|
if (config.get.validate && value !== undefined && value !== null && !skipHooks) {
|
|
73
73
|
try {
|
|
74
|
-
const valid = config.
|
|
74
|
+
const valid = config.use.validate(value, {
|
|
75
75
|
data: output,
|
|
76
76
|
operation,
|
|
77
77
|
id: operation === 'update' ? args.context.originalDoc?.id : undefined,
|
|
@@ -93,7 +93,7 @@ export const validateFields = Hooks.beforeUpsert(async (args) => {
|
|
|
93
93
|
/* Field hook before Save
|
|
94
94
|
*/
|
|
95
95
|
if (value !== undefined && value !== null && !skipHooks) {
|
|
96
|
-
value = await config.
|
|
96
|
+
value = await config.use.beforeSave(value, {
|
|
97
97
|
config: config.get,
|
|
98
98
|
event,
|
|
99
99
|
operation: args.context
|
|
@@ -104,7 +104,7 @@ export const validateFields = Hooks.beforeUpsert(async (args) => {
|
|
|
104
104
|
/* Access
|
|
105
105
|
/****************************************************/
|
|
106
106
|
if (operation === 'update' && !skipAccess) {
|
|
107
|
-
const authorizedFieldUpdate = config.
|
|
107
|
+
const authorizedFieldUpdate = config.use.accessUpdate(user, {
|
|
108
108
|
id: args.context.originalDoc?.id
|
|
109
109
|
});
|
|
110
110
|
if (!authorizedFieldUpdate) {
|
|
@@ -114,7 +114,7 @@ export const validateFields = Hooks.beforeUpsert(async (args) => {
|
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
if (operation === 'create' && !skipAccess) {
|
|
117
|
-
const authorizedFieldCreate = config.
|
|
117
|
+
const authorizedFieldCreate = config.use.accessCreate(user, {
|
|
118
118
|
id: undefined
|
|
119
119
|
});
|
|
120
120
|
if (!authorizedFieldCreate) {
|
|
@@ -124,12 +124,12 @@ export const validateFields = Hooks.beforeUpsert(async (args) => {
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
// Required
|
|
127
|
-
if (config.get.required && config.
|
|
127
|
+
if (config.get.required && config.use.isEmpty(value)) {
|
|
128
128
|
if (skipRequired) {
|
|
129
129
|
// The field's own type-correct default (falling back to '' only
|
|
130
130
|
// when none is set) — a hardcoded '' here was wrong for every
|
|
131
131
|
// non-string required field (number, checkbox, date, ...).
|
|
132
|
-
output = setValueAtPath(key, output, config.
|
|
132
|
+
output = setValueAtPath(key, output, config.use.defaultValue({ event }) ?? '');
|
|
133
133
|
}
|
|
134
134
|
else {
|
|
135
135
|
errors[key] = RimeFormError.REQUIRED_FIELD;
|
|
@@ -22,7 +22,7 @@ export function extractTreeBlocks({ data, configMap }) {
|
|
|
22
22
|
Object.entries(configMap).forEach(([path, config]) => {
|
|
23
23
|
if (config.type === 'tree') {
|
|
24
24
|
const value = getValueAtPath(path, data);
|
|
25
|
-
const isEmptyValue = config.
|
|
25
|
+
const isEmptyValue = config.use.isEmpty(value);
|
|
26
26
|
if (value && !isEmptyValue) {
|
|
27
27
|
value.forEach((item, index) => {
|
|
28
28
|
processTreeItem(item, index, path, undefined);
|
|
@@ -28,7 +28,7 @@ export const apiInit = definePlugin(() => {
|
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
30
|
event.locals.isInit = true;
|
|
31
|
-
const [signUpError] = await trycatch(() => event.locals.rime.auth.api.signUpEmail({
|
|
31
|
+
const [signUpError, response] = await trycatch(() => event.locals.rime.auth.api.signUpEmail({
|
|
32
32
|
body: {
|
|
33
33
|
email: data.email,
|
|
34
34
|
name: data.name,
|
|
@@ -42,7 +42,7 @@ export const apiInit = definePlugin(() => {
|
|
|
42
42
|
formData: { email: data.email }
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
|
-
return json({ initialized: true });
|
|
45
|
+
return json({ initialized: true, user: response.user });
|
|
46
46
|
};
|
|
47
47
|
return {
|
|
48
48
|
name: 'apiInit',
|
|
@@ -7,7 +7,6 @@ import type { Component } from 'svelte';
|
|
|
7
7
|
export declare const blocks: (name: string, blocks: BlockBuilder[]) => BlocksBuilder;
|
|
8
8
|
export declare const block: (name: string) => BlockBuilder;
|
|
9
9
|
export declare class BlocksBuilder extends FormFieldBuilder<BlocksField> {
|
|
10
|
-
_metaUrl: string;
|
|
11
10
|
constructor(name: string, blocks: BlockBuilder[]);
|
|
12
11
|
get component(): Component<import("./component/props.js").BlocksProps, {}, "">;
|
|
13
12
|
get cell(): Component<{
|
|
@@ -17,6 +16,7 @@ export declare class BlocksBuilder extends FormFieldBuilder<BlocksField> {
|
|
|
17
16
|
}, {}, "">;
|
|
18
17
|
localized(): this;
|
|
19
18
|
compile(): any;
|
|
19
|
+
protected generateType(): string;
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
22
|
* Checks if a field is a blocks field.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FormFieldBuilder } from '../../core/fields/builders/form-field-builder.js';
|
|
2
|
+
import { toPascalCase } from '../../util/string.js';
|
|
2
3
|
import { number } from '../number/index.js';
|
|
3
4
|
import { text } from '../text/index.js';
|
|
4
5
|
import Blocks from './component/Blocks.svelte';
|
|
@@ -6,8 +7,6 @@ import Cell from './component/Cell.svelte';
|
|
|
6
7
|
export const blocks = (name, blocks) => new BlocksBuilder(name, blocks);
|
|
7
8
|
export const block = (name) => new BlockBuilder(name);
|
|
8
9
|
export class BlocksBuilder extends FormFieldBuilder {
|
|
9
|
-
//
|
|
10
|
-
_metaUrl = import.meta.url;
|
|
11
10
|
constructor(name, blocks) {
|
|
12
11
|
super(name, 'blocks');
|
|
13
12
|
this.field.blocks = blocks;
|
|
@@ -67,6 +66,10 @@ export class BlocksBuilder extends FormFieldBuilder {
|
|
|
67
66
|
cell: this.cell || undefined
|
|
68
67
|
};
|
|
69
68
|
}
|
|
69
|
+
generateType() {
|
|
70
|
+
const blockNames = this.field.blocks.map((block) => `Block${toPascalCase(block.name)}`);
|
|
71
|
+
return `${this.name}: Array<${blockNames.join(' | ')}>,`;
|
|
72
|
+
}
|
|
70
73
|
}
|
|
71
74
|
/**
|
|
72
75
|
* Checks if a field is a blocks field.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { FormField } from '../types.js';
|
|
2
2
|
import { BooleanFieldBuilder } from '../../core/fields/builders/boolean-builder.js';
|
|
3
3
|
export declare class CheckboxFieldBuilder extends BooleanFieldBuilder<CheckboxField> {
|
|
4
|
-
_metaUrl: string;
|
|
5
4
|
get component(): import("svelte").Component<import("./component/props.js").CheckboxProps, {}, "">;
|
|
5
|
+
protected generateType(): string;
|
|
6
6
|
}
|
|
7
7
|
export declare const checkbox: (name: string) => CheckboxFieldBuilder;
|
|
8
8
|
/****************************************************/
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { BooleanFieldBuilder } from '../../core/fields/builders/boolean-builder.js';
|
|
2
2
|
import Checkbox from './component/Checkbox.svelte';
|
|
3
3
|
export class CheckboxFieldBuilder extends BooleanFieldBuilder {
|
|
4
|
-
//
|
|
5
|
-
_metaUrl = import.meta.url;
|
|
6
4
|
get component() {
|
|
7
5
|
return Checkbox;
|
|
8
6
|
}
|
|
7
|
+
generateType() {
|
|
8
|
+
return `${this.name}: boolean`;
|
|
9
|
+
}
|
|
9
10
|
}
|
|
10
11
|
export const checkbox = (name) => new CheckboxFieldBuilder(name, 'checkbox');
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { DefaultValueFn, FormField, OptionWithIcon } from '../types.js';
|
|
2
2
|
import { PickOneFieldBuilder } from '../../core/fields/builders/select-builder.js';
|
|
3
3
|
export declare class ComboBoxFieldBuilder extends PickOneFieldBuilder<ComboBoxField> {
|
|
4
|
-
_metaUrl: string;
|
|
5
4
|
get component(): import("svelte").Component<import("./component/props.js").ComboBoxProps, {}, "">;
|
|
6
5
|
options(...options: OptionWithIcon[] | string[]): this;
|
|
7
6
|
defaultValue(value: string | DefaultValueFn<string>): this;
|
|
7
|
+
protected generateType(): string;
|
|
8
8
|
}
|
|
9
9
|
export declare const combobox: (name: string) => ComboBoxFieldBuilder;
|
|
10
10
|
/****************************************************/
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { PickOneFieldBuilder } from '../../core/fields/builders/select-builder.js';
|
|
2
2
|
import Combobox from './component/ComboBox.svelte';
|
|
3
3
|
export class ComboBoxFieldBuilder extends PickOneFieldBuilder {
|
|
4
|
-
//
|
|
5
|
-
_metaUrl = import.meta.url;
|
|
6
4
|
get component() {
|
|
7
5
|
return Combobox;
|
|
8
6
|
}
|
|
@@ -13,5 +11,9 @@ export class ComboBoxFieldBuilder extends PickOneFieldBuilder {
|
|
|
13
11
|
this.field.defaultValue = value;
|
|
14
12
|
return this;
|
|
15
13
|
}
|
|
14
|
+
generateType() {
|
|
15
|
+
const optionsJoinedType = this.get.options.map((o) => `'${o.value}'`).join(' | ');
|
|
16
|
+
return `${this.name}${this.get.required ? '' : '?'}: ${optionsJoinedType}`;
|
|
17
|
+
}
|
|
16
18
|
}
|
|
17
19
|
export const combobox = (name) => new ComboBoxFieldBuilder(name, 'combobox');
|
|
@@ -3,8 +3,8 @@ import { FormFieldBuilder } from '../../core/fields/builders/form-field-builder.
|
|
|
3
3
|
import type { DefaultValueFn, FormField } from '../types.js';
|
|
4
4
|
export declare const date: (name: string) => DateFieldBuilder;
|
|
5
5
|
export declare class DateFieldBuilder extends FormFieldBuilder<DateField> {
|
|
6
|
-
_metaUrl: string;
|
|
7
6
|
constructor(name: string);
|
|
7
|
+
get dataType(): DataType;
|
|
8
8
|
get component(): import("svelte").Component<{
|
|
9
9
|
path: string;
|
|
10
10
|
config: DateFieldBuilder;
|
|
@@ -14,8 +14,8 @@ export declare class DateFieldBuilder extends FormFieldBuilder<DateField> {
|
|
|
14
14
|
value: Date | undefined;
|
|
15
15
|
}, {}, "">;
|
|
16
16
|
defaultValue(value: Date | DefaultValueFn<Date>): this;
|
|
17
|
-
get dataType(): DataType;
|
|
18
17
|
isTitle(): this;
|
|
18
|
+
protected generateType(): string;
|
|
19
19
|
}
|
|
20
20
|
/****************************************************/
|
|
21
21
|
export type DateField = FormField & {
|