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.
Files changed (123) hide show
  1. package/README.md +1 -1
  2. package/dist/adapter-sqlite/generate-schema/column.server.js +1 -4
  3. package/dist/adapter-sqlite/generate-schema/write.server.js +1 -3
  4. package/dist/core/config/auth/better-auth-hooks.server.js +6 -3
  5. package/dist/core/dev/cli/build/index.d.ts +1 -0
  6. package/dist/core/dev/cli/build/index.js +10 -5
  7. package/dist/core/dev/cli/build/templates.d.ts +1 -1
  8. package/dist/core/dev/cli/build/templates.js +10 -7
  9. package/dist/core/dev/cli/index.js +1 -0
  10. package/dist/core/dev/cli/init/index.server.js +3 -3
  11. package/dist/core/dev/cli/init/templates.js +8 -1
  12. package/dist/core/dev/cli/util/package-manager.server.js +27 -26
  13. package/dist/core/dev/constants.d.ts +11 -2
  14. package/dist/core/dev/constants.js +11 -0
  15. package/dist/core/dev/generate/runtime/index.server.d.ts +24 -0
  16. package/dist/core/dev/generate/runtime/index.server.js +93 -0
  17. package/dist/core/dev/generate/types/index.server.js +42 -18
  18. package/dist/core/dev/vite/index.server.js +39 -23
  19. package/dist/core/fields/builders/boolean-builder.d.ts +1 -2
  20. package/dist/core/fields/builders/field-builder.d.ts +11 -6
  21. package/dist/core/fields/builders/field-builder.js +12 -5
  22. package/dist/core/fields/builders/form-field-builder.d.ts +11 -14
  23. package/dist/core/fields/builders/form-field-builder.js +17 -15
  24. package/dist/core/fields/util.js +1 -1
  25. package/dist/core/operations/blocks/extract.server.js +1 -1
  26. package/dist/core/operations/hooks/before-read/process-document-fields.server.js +3 -3
  27. package/dist/core/operations/hooks/before-upsert/set-default-values.server.js +3 -3
  28. package/dist/core/operations/hooks/before-upsert/validate-fields.server.js +7 -7
  29. package/dist/core/operations/shared/fallback-data-from-original.js +1 -1
  30. package/dist/core/operations/tree/extract.server.js +1 -1
  31. package/dist/core/plugins/api-init/index.server.js +2 -2
  32. package/dist/fields/blocks/index.d.ts +1 -1
  33. package/dist/fields/blocks/index.js +5 -2
  34. package/dist/fields/checkbox/index.d.ts +1 -1
  35. package/dist/fields/checkbox/index.js +3 -2
  36. package/dist/fields/combobox/index.d.ts +1 -1
  37. package/dist/fields/combobox/index.js +4 -2
  38. package/dist/fields/date/index.d.ts +2 -2
  39. package/dist/fields/date/index.js +7 -5
  40. package/dist/fields/email/index.d.ts +3 -3
  41. package/dist/fields/email/index.js +9 -8
  42. package/dist/fields/group/component/Group.svelte +1 -1
  43. package/dist/fields/group/index.d.ts +1 -1
  44. package/dist/fields/group/index.js +4 -2
  45. package/dist/fields/link/index.d.ts +1 -1
  46. package/dist/fields/link/index.js +9 -5
  47. package/dist/fields/link/module.server.d.ts +2 -0
  48. package/dist/fields/link/{runtime.server.js → module.server.js} +0 -8
  49. package/dist/fields/number/index.d.ts +1 -1
  50. package/dist/fields/number/index.js +3 -2
  51. package/dist/fields/radio/index.d.ts +1 -1
  52. package/dist/fields/radio/index.js +4 -2
  53. package/dist/fields/relation/index.d.ts +2 -6
  54. package/dist/fields/relation/index.js +5 -9
  55. package/dist/fields/relation/{runtime.d.ts → module.d.ts} +2 -2
  56. package/dist/fields/relation/module.js +4 -0
  57. package/dist/fields/relation/module.server.d.ts +5 -0
  58. package/dist/fields/relation/{runtime.server.js → module.server.js} +3 -6
  59. package/dist/fields/rich-text/index.d.ts +1 -1
  60. package/dist/fields/rich-text/index.js +3 -2
  61. package/dist/fields/select/index.d.ts +1 -1
  62. package/dist/fields/select/index.js +4 -1
  63. package/dist/fields/slug/index.d.ts +1 -1
  64. package/dist/fields/slug/index.js +3 -2
  65. package/dist/fields/tabs/index.d.ts +1 -1
  66. package/dist/fields/tabs/index.js +10 -2
  67. package/dist/fields/text/index.d.ts +1 -1
  68. package/dist/fields/text/index.js +3 -2
  69. package/dist/fields/textarea/index.d.ts +1 -1
  70. package/dist/fields/textarea/index.js +3 -2
  71. package/dist/fields/time/index.d.ts +1 -1
  72. package/dist/fields/time/index.js +3 -2
  73. package/dist/fields/toggle/index.d.ts +1 -1
  74. package/dist/fields/toggle/index.js +3 -3
  75. package/dist/fields/tree/index.d.ts +1 -1
  76. package/dist/fields/tree/index.js +4 -2
  77. package/dist/panel/components/fields/RenderFields.svelte +1 -1
  78. package/dist/panel/context/collection.svelte.js +5 -3
  79. package/dist/panel/context/documentForm.svelte.js +47 -28
  80. package/dist/panel/context/form.svelte.js +3 -3
  81. package/dist/util/doc.js +1 -1
  82. package/package.json +1 -8
  83. package/dist/fields/blocks/index.server.d.ts +0 -3
  84. package/dist/fields/blocks/index.server.js +0 -5
  85. package/dist/fields/checkbox/index.server.d.ts +0 -3
  86. package/dist/fields/checkbox/index.server.js +0 -3
  87. package/dist/fields/combobox/index.server.d.ts +0 -3
  88. package/dist/fields/combobox/index.server.js +0 -4
  89. package/dist/fields/date/index.server.d.ts +0 -3
  90. package/dist/fields/date/index.server.js +0 -3
  91. package/dist/fields/email/index.server.d.ts +0 -4
  92. package/dist/fields/email/index.server.js +0 -3
  93. package/dist/fields/group/index.server.d.ts +0 -2
  94. package/dist/fields/group/index.server.js +0 -12
  95. package/dist/fields/index.server.d.ts +0 -14
  96. package/dist/fields/index.server.js +0 -65
  97. package/dist/fields/link/runtime.server.d.ts +0 -5
  98. package/dist/fields/number/index.server.d.ts +0 -3
  99. package/dist/fields/number/index.server.js +0 -3
  100. package/dist/fields/radio/index.server.d.ts +0 -3
  101. package/dist/fields/radio/index.server.js +0 -4
  102. package/dist/fields/relation/runtime.js +0 -4
  103. package/dist/fields/relation/runtime.server.d.ts +0 -8
  104. package/dist/fields/rich-text/index.server.d.ts +0 -3
  105. package/dist/fields/rich-text/index.server.js +0 -3
  106. package/dist/fields/select/index.server.d.ts +0 -3
  107. package/dist/fields/select/index.server.js +0 -4
  108. package/dist/fields/slug/index.server.d.ts +0 -3
  109. package/dist/fields/slug/index.server.js +0 -3
  110. package/dist/fields/tabs/index.server.d.ts +0 -3
  111. package/dist/fields/tabs/index.server.js +0 -18
  112. package/dist/fields/text/index.server.d.ts +0 -3
  113. package/dist/fields/text/index.server.js +0 -3
  114. package/dist/fields/textarea/index.server.d.ts +0 -3
  115. package/dist/fields/textarea/index.server.js +0 -3
  116. package/dist/fields/time/index.server.d.ts +0 -3
  117. package/dist/fields/time/index.server.js +0 -3
  118. package/dist/fields/toggle/index.server.d.ts +0 -3
  119. package/dist/fields/toggle/index.server.js +0 -3
  120. package/dist/fields/tree/index.server.d.ts +0 -3
  121. package/dist/fields/tree/index.server.js +0 -4
  122. /package/dist/fields/link/{runtime.d.ts → module.d.ts} +0 -0
  123. /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
- /** General-purpose server/browser split, usable anywhere in the app (not just rime's own
15
- * field code — see relation/index.ts for the first user). `import { x } from '$rime/runtime'`
16
- * resolves to a sibling `server.ts` (server builds) or `browser.ts` (browser builds) next
17
- * to whichever file does the importing — the importer's path travels encoded in the
18
- * resolved id since `load()` only receives the id, not the importer. */
19
- const VRuntimeId = '$rime/runtime';
20
- const VRuntimeMarker = `\0${VRuntimeId}::`;
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
- // rimecms/fields and rimecms/panel must not be pre-bundled: esbuild flattens
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 (process.env.IS_PACKAGE_DEV && file.includes('src/lib/core/config')) {
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, importer) {
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 === VRuntimeId && importer) {
109
- return `${VRuntimeMarker}${importer}`;
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(VRuntimeMarker)) {
127
- const importer = id.slice(VRuntimeMarker.length);
128
- const dir = path.dirname(importer);
129
- const target = path.join(dir, isServer ? 'runtime.server.ts' : 'runtime.ts');
130
- return `export * from '${target.replace(/\.ts$/, '.js')}';`;
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(): 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 FieldRun = {
11
- canRead(...args: Parameters<FieldAccess>): boolean;
12
- canCreate(...args: Parameters<FieldAccess>): boolean;
13
- canUpdate(...args: Parameters<FieldAccess>): boolean;
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 run(): FieldRun;
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 run() {
51
+ get use() {
53
52
  return {
54
53
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
55
- canRead: (..._args) => true,
54
+ accessRead: (..._args) => true,
56
55
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
57
- canCreate: (..._args) => true,
56
+ accessCreate: (..._args) => true,
58
57
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
59
- canUpdate: (..._args) => true
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 FieldRun } from './field-builder.js';
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 `FieldRun` — keeps `run`'s params
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 FormFieldRun<T extends FormField> = FieldRun & {
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
- condition(doc: Dic, siblings: Dic): boolean;
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
- /** Every concrete leaf field builder is expected to implement a
42
- * `get dataType(): DataType` accessor (see e.g. `$lib/fields/text/index.ts`).
43
- * Not declared here — TS doesn't allow `declare` on accessors in this
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 run(): FormFieldRun<T>;
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
- /** Every concrete leaf field builder is expected to implement a
6
- * `get dataType(): DataType` accessor (see e.g. `$lib/fields/text/index.ts`).
7
- * Not declared here — TS doesn't allow `declare` on accessors in this
8
- * project's config, and a plain base field would conflict with subclasses
9
- * that need a computed getter (e.g. `select`, whose dataType depends on
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 run() {
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
- condition: (doc, siblings) => {
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
- canRead: (...args) => !!this.field.access?.read?.(...args),
125
- canCreate: (...args) => !!this.field.access?.create?.(...args),
126
- canUpdate: (...args) => !!this.field.access?.update?.(...args),
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
  }
@@ -56,7 +56,7 @@ export const emptyValuesFromFieldConfig = (arr) => {
56
56
  }
57
57
  // Handle default values
58
58
  else {
59
- emptyValue = config.run.defaultValue();
59
+ emptyValue = config.use.defaultValue();
60
60
  }
61
61
  return [config.name, emptyValue];
62
62
  }));
@@ -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.run.isEmpty(value);
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.run.canRead(event.locals.user)) {
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.run.beforeRead(value, {
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.run.isEmpty(value);
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.run.isEmpty(value);
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.run.defaultValue({ event: getRequestEvent() }));
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.run.defaultValue({ event: getRequestEvent() });
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.run.beforeValidate(value, { config, data: args.data });
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.run.validate(value, {
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.run.beforeSave(value, {
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.run.canUpdate(user, {
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.run.canCreate(user, {
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.run.isEmpty(value)) {
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.run.defaultValue({ event }) ?? '');
132
+ output = setValueAtPath(key, output, config.use.defaultValue({ event }) ?? '');
133
133
  }
134
134
  else {
135
135
  errors[key] = RimeFormError.REQUIRED_FIELD;
@@ -16,7 +16,7 @@ export const fallbackDataFromOriginal = async (args) => {
16
16
  let value = getValueAtPath(key, output);
17
17
  let isEmpty;
18
18
  try {
19
- isEmpty = config.run.isEmpty(value);
19
+ isEmpty = config.use.isEmpty(value);
20
20
  }
21
21
  catch {
22
22
  isEmpty = false;
@@ -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.run.isEmpty(value);
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 & {