@valbuild/core 0.129.0 → 0.134.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +188 -0
  2. package/dist/declarations/src/index.d.ts +11 -1
  3. package/dist/declarations/src/initSchema.d.ts +20 -0
  4. package/dist/declarations/src/module.d.ts +21 -4
  5. package/dist/declarations/src/preview.d.ts +65 -8
  6. package/dist/declarations/src/render.d.ts +26 -0
  7. package/dist/declarations/src/schema/array.d.ts +8 -5
  8. package/dist/declarations/src/schema/boolean.d.ts +8 -6
  9. package/dist/declarations/src/schema/code.d.ts +8 -6
  10. package/dist/declarations/src/schema/color.d.ts +8 -6
  11. package/dist/declarations/src/schema/date.d.ts +8 -6
  12. package/dist/declarations/src/schema/datetime.d.ts +8 -6
  13. package/dist/declarations/src/schema/discriminatedUnion.d.ts +8 -5
  14. package/dist/declarations/src/schema/enum.d.ts +8 -6
  15. package/dist/declarations/src/schema/file.d.ts +8 -6
  16. package/dist/declarations/src/schema/image.d.ts +8 -6
  17. package/dist/declarations/src/schema/index.d.ts +31 -2
  18. package/dist/declarations/src/schema/keyOf.d.ts +8 -6
  19. package/dist/declarations/src/schema/literal.d.ts +8 -6
  20. package/dist/declarations/src/schema/locale.d.ts +1 -2
  21. package/dist/declarations/src/schema/number.d.ts +8 -6
  22. package/dist/declarations/src/schema/object.d.ts +18 -5
  23. package/dist/declarations/src/schema/record.d.ts +8 -5
  24. package/dist/declarations/src/schema/richtext.d.ts +8 -6
  25. package/dist/declarations/src/schema/route.d.ts +8 -6
  26. package/dist/declarations/src/schema/settings.d.ts +1 -1
  27. package/dist/declarations/src/schema/string.d.ts +8 -11
  28. package/dist/declarations/src/schema/validation/ValidationFix.d.ts +1 -1
  29. package/dist/declarations/src/schema/view.d.ts +197 -0
  30. package/dist/declarations/src/selector/index.d.ts +4 -2
  31. package/dist/declarations/src/selector/view.d.ts +22 -0
  32. package/dist/declarations/src/source/index.d.ts +2 -1
  33. package/dist/declarations/src/source/settings.d.ts +27 -0
  34. package/dist/declarations/src/source/view.d.ts +41 -0
  35. package/dist/declarations/src/source/viewHandle.d.ts +69 -0
  36. package/dist/{index-f5842720.cjs.prod.js → index-48a8f8a5.cjs.prod.js} +1281 -353
  37. package/dist/{index-21fd16b1.cjs.dev.js → index-85a2ffba.cjs.dev.js} +1281 -353
  38. package/dist/{index-06bb2832.esm.js → index-e3458565.esm.js} +1280 -354
  39. package/dist/valbuild-core.cjs.dev.js +3 -1
  40. package/dist/valbuild-core.cjs.prod.js +3 -1
  41. package/dist/valbuild-core.esm.js +1 -1
  42. package/package.json +1 -1
  43. package/patch/dist/valbuild-core-patch.cjs.dev.js +1 -1
  44. package/patch/dist/valbuild-core-patch.cjs.prod.js +1 -1
  45. package/patch/dist/valbuild-core-patch.esm.js +2 -2
@@ -0,0 +1,197 @@
1
+ import { Schema, SchemaAssertResult, SerializedSchema } from "./index.js";
2
+ import type { ModuleIdOf, ValModuleBrand } from "../module.js";
3
+ import { ReifiedPreview } from "../preview.js";
4
+ import { FieldRender } from "../render.js";
5
+ import { GenericSelector } from "../selector/index.js";
6
+ import { Source } from "../source/index.js";
7
+ import { ValViewSource } from "../source/view.js";
8
+ import { ModuleFilePath, SourcePath } from "../val/index.js";
9
+ import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
10
+ export type SerializedValViewSchema = {
11
+ type: "view";
12
+ render?: FieldRender;
13
+ /** Never set: a view has no value of its own to preview. Carried for shape parity. */
14
+ preview?: true;
15
+ /** Always false: a view points at a module, and `null` is not a module. */
16
+ opt: false;
17
+ /** Always false: there is no value for a custom validator to look at. */
18
+ customValidate?: false;
19
+ /** The module this field points at. */
20
+ moduleFilePath: ModuleFilePath;
21
+ readonly?: boolean;
22
+ hidden?: boolean;
23
+ description?: string;
24
+ };
25
+ /** The source type of the module a selector points at. */
26
+ type SourceOf<M> = M extends GenericSelector<infer S> ? S : never;
27
+ /**
28
+ * A field that points at ANOTHER module, for the Val editor's benefit.
29
+ *
30
+ * The source is `{ view: "/other.val.ts" }` — a pointer, and nothing else. The
31
+ * module it names keeps its own source, its own patches, its own validation and
32
+ * its own address; this field puts a row on THIS module's screen that an editor
33
+ * clicks through to it.
34
+ *
35
+ * Two type parameters, each earning its place:
36
+ *
37
+ * - `Id` — the target's module file path as a literal, so the author writes
38
+ * `{ view: "/other.val.ts" }` with autocomplete, and naming a different module
39
+ * than the schema does is a type error rather than a validation error.
40
+ * - `T` — the target's source type, carried on a phantom slot so the READ side
41
+ * can say `ValView<T>` rather than `ValView<unknown>`. Nothing reads it yet;
42
+ * it is here so that resolving a view through `useVal` can be added without
43
+ * changing what is stored.
44
+ */
45
+ export declare class ValViewSchema<Id extends string = string, T = unknown> extends Schema<ValViewSource<Id, T>> {
46
+ private readonly moduleFilePath;
47
+ /**
48
+ * The module this points at, as an object.
49
+ *
50
+ * Not serialized and not part of the schema's identity — it is here so the
51
+ * READ path can resolve a view without a path-to-module registry, which the
52
+ * app does not have (`val.modules` is lazy thunks, `<ValModulesClient>` is
53
+ * optional). Absent on a schema that came off the wire, which is fine: the
54
+ * Studio navigates to a view, it does not resolve one.
55
+ */
56
+ private readonly valModule;
57
+ private readonly isReadonly;
58
+ private readonly isHidden;
59
+ private readonly description?;
60
+ private readonly renderInput;
61
+ constructor(moduleFilePath: Id,
62
+ /**
63
+ * The module this points at, as an object.
64
+ *
65
+ * Not serialized and not part of the schema's identity — it is here so the
66
+ * READ path can resolve a view without a path-to-module registry, which the
67
+ * app does not have (`val.modules` is lazy thunks, `<ValModulesClient>` is
68
+ * optional). Absent on a schema that came off the wire, which is fine: the
69
+ * Studio navigates to a view, it does not resolve one.
70
+ */
71
+ valModule?: unknown, isReadonly?: boolean, isHidden?: boolean, description?: string | undefined, renderInput?: FieldRender | null);
72
+ /**
73
+ * Describe this field.
74
+ *
75
+ * Shown next to the field's label in the Val editor — here, the place to say
76
+ * WHY the other module is on this screen, since an editor who follows the row
77
+ * is about to change content that other pages use too.
78
+ *
79
+ * @example
80
+ * import otherVal from "./other.val"; // another module
81
+ * const schema = s.object({
82
+ * shared: s.view(otherVal).describe("Shared by every page"),
83
+ * title: s.string(),
84
+ * });
85
+ * export default c.define("/example.val.ts", schema, {
86
+ * shared: { view: "/other.val.ts" },
87
+ * title: "Hello",
88
+ * });
89
+ */
90
+ describe(description: string | null): ValViewSchema<Id, T>;
91
+ /**
92
+ * The stored pointer must name the module the SCHEMA names.
93
+ *
94
+ * It cannot disagree in a `.val.ts` — the source type is the literal path, so
95
+ * a mismatch does not compile. It can disagree in hand-written JSON (a
96
+ * `.jsonValues()` entry, an external record), which is what this is for.
97
+ */
98
+ protected executeValidate(path: SourcePath, src: ValViewSource<Id, T>): ValidationErrors;
99
+ protected executeCustomValidateAt(_path: SourcePath, _src: ValViewSource<Id, T>): ValidationError[];
100
+ protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<ValViewSource<Id, T>>;
101
+ /**
102
+ * Not available: a view points at a module, and `null` is not a module.
103
+ * Throws if called.
104
+ *
105
+ * @example
106
+ * import otherVal from "./other.val"; // another module
107
+ * // `s.view(otherVal).nullable()` throws — a view is never nullable.
108
+ * const schema = s.object({ shared: s.view(otherVal), title: s.string() });
109
+ * export default c.define("/example.val.ts", schema, {
110
+ * shared: { view: "/other.val.ts" },
111
+ * title: "Hello",
112
+ * });
113
+ */
114
+ nullable(): Schema<ValViewSource<Id, T> | null>;
115
+ /**
116
+ * Marks the row read-only.
117
+ *
118
+ * The view's OWN flag, and it says nothing about the module it names: that
119
+ * module has its own `readonly`, which is what governs editing once an
120
+ * editor is there. A view is a link rather than an editor, so this changes
121
+ * nothing about the row today — it exists because every schema has it, and
122
+ * because a view that renders its target inline would need it.
123
+ *
124
+ * @example
125
+ * import otherVal from "./other.val"; // another module
126
+ * const schema = s.object({
127
+ * shared: s.view(otherVal).readonly(),
128
+ * title: s.string(),
129
+ * });
130
+ * export default c.define("/example.val.ts", schema, {
131
+ * shared: { view: "/other.val.ts" },
132
+ * title: "Hello",
133
+ * });
134
+ */
135
+ readonly(isReadonly?: boolean): ValViewSchema<Id, T>;
136
+ /**
137
+ * Hide the ROW in the editor.
138
+ *
139
+ * The view's OWN flag, and it is the only thing that decides whether the row
140
+ * is drawn — a view whose TARGET is hidden is still shown, and still leads
141
+ * there. That is the point of it: a module that many modules `keyOf` into is
142
+ * noise in the nav and belongs on exactly one page, so it is hidden from the
143
+ * listing and reached through the view that puts it where it belongs.
144
+ *
145
+ * @example
146
+ * import otherVal from "./other.val"; // another module
147
+ * const schema = s.object({
148
+ * shared: s.view(otherVal).hidden(),
149
+ * title: s.string(),
150
+ * });
151
+ * export default c.define("/example.val.ts", schema, {
152
+ * shared: { view: "/other.val.ts" },
153
+ * title: "Hello",
154
+ * });
155
+ */
156
+ hidden(isHidden?: boolean): ValViewSchema<Id, T>;
157
+ /**
158
+ * How this field is laid out in the editor. Static configuration, not a
159
+ * callback — see `render.ts`.
160
+ *
161
+ * @example
162
+ * import otherVal from "./other.val"; // another module
163
+ * const schema = s.object({
164
+ * shared: s.view(otherVal).render({ as: "inline" }),
165
+ * title: s.string(),
166
+ * });
167
+ * export default c.define("/example.val.ts", schema, {
168
+ * shared: { view: "/other.val.ts" },
169
+ * title: "Hello",
170
+ * });
171
+ */
172
+ render(input: FieldRender): ValViewSchema<Id, T>;
173
+ protected executeSerialize(): SerializedSchema;
174
+ protected executePreview(): ReifiedPreview;
175
+ }
176
+ /**
177
+ * Point at another module, so the Val editor shows a way into it from here.
178
+ *
179
+ * A view stores a pointer and nothing else: the module it names keeps its own
180
+ * source, patches, validation and address, and an editor reaches it by clicking
181
+ * through rather than by hunting for it. It is there so content that has to live
182
+ * in its own module — a `keyOf` target, shared settings, a route-keyed record —
183
+ * can still be found from the page it belongs to.
184
+ *
185
+ * Reading it in consuming code gives you a pointer with no fields on it. Read
186
+ * the module it names directly instead.
187
+ *
188
+ * @example
189
+ * import otherVal from "./other.val"; // another module
190
+ * const schema = s.object({ shared: s.view(otherVal), title: s.string() });
191
+ * export default c.define("/example.val.ts", schema, {
192
+ * shared: { view: "/other.val.ts" },
193
+ * title: "Hello",
194
+ * });
195
+ */
196
+ export declare const view: <M extends GenericSelector<Source> & ValModuleBrand>(valModule: M) => ValViewSchema<ModuleIdOf<M>, SourceOf<M>>;
197
+ export {};
@@ -13,11 +13,13 @@ import { AllRichTextOptions, RichTextSource } from "../source/richtext.js";
13
13
  import { RichTextSelector } from "./richtext.js";
14
14
  import { JsonSource } from "../source/json.js";
15
15
  import { ExternalRecordSrc } from "../source/external.js";
16
+ import { ValViewSource } from "../source/view.js";
17
+ import { ValView } from "./view.js";
16
18
  import { SettingsSource } from "../source/settings.js";
17
- export type Selector<T extends Source> = Source extends T ? GenericSelector<T> : T extends MediaSource ? GenericSelector<T> : T extends JsonSource ? GenericSelector<JsonSource> : T extends ExternalRecordSrc ? GenericSelector<T> : T extends RichTextSource<infer O> ? RichTextSelector<O> : T extends SourceObject ? ObjectSelector<T> : T extends SourceArray ? ArraySelector<T> : T extends SettingsSource ? GenericSelector<T> : T extends string ? StringSelector<T> : T extends number ? NumberSelector<T> : T extends boolean ? BooleanSelector<T> : T extends null ? PrimitiveSelector<null> : never;
19
+ export type Selector<T extends Source> = Source extends T ? GenericSelector<T> : T extends MediaSource ? GenericSelector<T> : T extends JsonSource ? GenericSelector<JsonSource> : T extends ExternalRecordSrc ? GenericSelector<T> : T extends ValViewSource<string, infer Target> ? ValView<Target> : T extends RichTextSource<infer O> ? RichTextSelector<O> : T extends SourceObject ? ObjectSelector<T> : T extends SourceArray ? ArraySelector<T> : T extends SettingsSource ? GenericSelector<T> : T extends string ? StringSelector<T> : T extends number ? NumberSelector<T> : T extends boolean ? BooleanSelector<T> : T extends null ? PrimitiveSelector<null> : never;
18
20
  export type SelectorSource = SourcePrimitive | undefined | readonly SelectorSource[] | {
19
21
  [key: string]: SelectorSource;
20
- } | MediaSource | JsonSource | ExternalRecordSrc | SettingsSource | RichTextSource<AllRichTextOptions> | GenericSelector<Source>;
22
+ } | MediaSource | JsonSource | ExternalRecordSrc | ValViewSource | ValView<Source> | SettingsSource | RichTextSource<AllRichTextOptions> | GenericSelector<Source>;
21
23
  /**
22
24
  * @internal
23
25
  */
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Type-level-only slot naming the source type of the module a {@link ValView}
3
+ * points at. Required, not optional: nothing else should ever be a `ValView` by
4
+ * accident, and reading a property off one has to be an error.
5
+ */
6
+ declare const ViewTarget: unique symbol;
7
+ /**
8
+ * A pointer to another module, as consuming code sees it.
9
+ *
10
+ * It has no properties on purpose. A view exists for the Val editor — it puts a
11
+ * way into another module on this module's screen — and there is nothing in it
12
+ * to render. Read the module it names directly.
13
+ *
14
+ * `T` is the target's source type. It is what lets `useVal(page.header)` be
15
+ * typed as the header's content — the overload that resolves a handle
16
+ * constrains `T` to `Source` there rather than here, because `ValViewSource` is a
17
+ * member of the `Source` union and a constraint here is a circular reference.
18
+ */
19
+ export type ValView<T> = {
20
+ readonly [ViewTarget]: T;
21
+ };
22
+ export {};
@@ -1,9 +1,10 @@
1
1
  import { ExternalRecordSrc } from "./external.js";
2
+ import { ValViewSource } from "./view.js";
2
3
  import { JsonSource } from "./json.js";
3
4
  import { MediaSource } from "./media.js";
4
5
  import { RichTextOptions, RichTextSource } from "./richtext.js";
5
6
  import { SettingsSource } from "./settings.js";
6
- export type Source = SourcePrimitive | SourceObject | SourceArray | MediaSource | JsonSource | ExternalRecordSrc | SettingsSource | RichTextSource<RichTextOptions>;
7
+ export type Source = SourcePrimitive | SourceObject | SourceArray | MediaSource | JsonSource | ExternalRecordSrc | ValViewSource | SettingsSource | RichTextSource<RichTextOptions>;
7
8
  export type SourceObject = {
8
9
  [key in string]: Source;
9
10
  } & {
@@ -25,6 +25,33 @@ export type SettingsSource = {
25
25
  assistant?: AssistantSettingsSource;
26
26
  theme?: ThemeSettingsSource;
27
27
  locales?: LocalesSettingsSource;
28
+ studio?: StudioSettingsSource;
29
+ };
30
+ /**
31
+ * How the Studio behaves for the people editing this project.
32
+ *
33
+ * Not how it looks — that is {@link ThemeSettingsSource} — and not one person's
34
+ * own preferences, which stay in their browser. This is the project's word on
35
+ * what its editors are offered, which is why it is content: a team that has
36
+ * decided something about the way they work should not need a developer and a
37
+ * deploy to say so.
38
+ */
39
+ export type StudioSettingsSource = {
40
+ /**
41
+ * Whether editors are offered the guided tour of the Studio.
42
+ *
43
+ * Unset means yes, because the person this exists for is the one who has not
44
+ * answered any question yet. Setting it to `false` turns the offer off for
45
+ * everyone on the project, permanently and for good — which is the point:
46
+ * a team that finds it noisy can be rid of it once rather than each person
47
+ * dismissing it on each machine they use.
48
+ *
49
+ * It governs the OFFER, not the tour. Whoever wants it can still run it from
50
+ * Quick actions; what goes is the glowing button and the prompt on the empty
51
+ * editor. Whether a given person has already been through it is a fact about
52
+ * that browser and is not here.
53
+ */
54
+ tour?: boolean | null;
28
55
  };
29
56
  /**
30
57
  * How the Studio looks in this project.
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Type-level-only slot carrying the SOURCE TYPE of the module a view names.
3
+ *
4
+ * Optional, and that is what makes the marker writable: `{ view: "/foo.val.ts" }`
5
+ * is a plain literal, and an optional symbol-keyed property costs the author
6
+ * nothing to satisfy. It exists so the read side can say WHAT is behind the
7
+ * pointer — `ValView<FooSrc>` rather than `ValView<unknown>` — without the
8
+ * author having to write a type they do not have.
9
+ */
10
+ declare const ViewTargetSource: unique symbol;
11
+ /**
12
+ * The source of a view field: a pointer to another module, and nothing else.
13
+ *
14
+ * Written as a plain object, like media and for the same reason — the same
15
+ * value has to work in a `.val.ts` and in a `*.val.json` entry, and a literal
16
+ * survives the static extraction that a constructor call does not. See
17
+ * `architecture/media.md`.
18
+ *
19
+ * `Id` is the target's module file path as a LITERAL type, inferred from the
20
+ * module handed to `s.view(...)`. So the path autocompletes at the call site,
21
+ * and a source naming a different module than its schema does is a type error.
22
+ *
23
+ * Nothing decides "this is a view" by looking at the value; the SCHEMA does
24
+ * (`type === "view"`). {@link isValViewSource} exists for the few places that have
25
+ * a value and no schema, and is not the normal way to ask.
26
+ */
27
+ export type ValViewSource<Id extends string = string, T = unknown> = {
28
+ readonly view: Id;
29
+ } & {
30
+ readonly [ViewTargetSource]?: T;
31
+ };
32
+ /**
33
+ * Is this value shaped like a view marker?
34
+ *
35
+ * A shape test, not an identity: `view` is a reserved object key
36
+ * (`ObjectSchemaProps`), so no ordinary object source can take this shape — but
37
+ * hand-written JSON can, which is why validation checks the value against the
38
+ * schema rather than trusting it.
39
+ */
40
+ export declare function isValViewSource(value: unknown): value is ValViewSource;
41
+ export {};
@@ -0,0 +1,69 @@
1
+ import type { ValModule } from "../module.js";
2
+ import { Schema } from "../schema/index.js";
3
+ import type { GenericSelector, SelectorSource } from "../selector/index.js";
4
+ import type { ValView } from "../selector/view.js";
5
+ import type { Source, SourceObject } from "./index.js";
6
+ import { ValViewSource } from "./view.js";
7
+ /**
8
+ * Where a resolved view handle keeps the module it points at.
9
+ *
10
+ * A symbol, so it is invisible to `JSON.stringify`, `Object.keys`, the source
11
+ * SHAs and every walk that treats source as data — the handle still IS
12
+ * `{ view: "/foo.val.ts" }` everywhere that matters.
13
+ */
14
+ declare const ViewTargetModule: unique symbol;
15
+ /**
16
+ * A view's source with the module it names attached.
17
+ *
18
+ * `useVal(page.header)` needs the target's SOURCE and its SCHEMA, and the
19
+ * stored pointer has neither — it is a path. The app has no way to turn a path
20
+ * back into a module either: `val.modules` holds lazy `import()` thunks, and
21
+ * `<ValModulesClient>` is optional. So the module travels with the handle.
22
+ *
23
+ * Attached by the READ path (`stegaEncode`) rather than written into source,
24
+ * and that is the whole reason it works in draft mode: with an edit pending,
25
+ * the source comes from the overlay store as plain JSON that never went near a
26
+ * module. The schema is the module's own either way, so the encoder always has
27
+ * it to hand.
28
+ */
29
+ export type ValViewHandle = ValViewSource & {
30
+ readonly [ViewTargetModule]: unknown;
31
+ };
32
+ /** Attach the module a view names to its pointer. */
33
+ export declare function createViewHandle(source: ValViewSource, valModule: unknown): ValViewHandle;
34
+ /** Is this a view pointer that carries the module it names? */
35
+ export declare function isViewHandle(value: unknown): value is ValViewHandle;
36
+ /** The module a handle carries, or undefined if it carries none. */
37
+ export declare function viewHandleModule(value: unknown): unknown;
38
+ /**
39
+ * What a reader that needs a MODULE accepts, rather than a value.
40
+ *
41
+ * `useValKey`, `useValRoute`, `useValRouteUrl` and their `fetch*` counterparts
42
+ * all read a path, a schema and a source off what they are handed, so — unlike
43
+ * `useVal` — they cannot take an arbitrary selector. They CAN take a view
44
+ * pointing at one, which is the point of a page declaring which module it
45
+ * shows. {@link resolveViewedModule} is the runtime half, and takes exactly
46
+ * this, so the two cannot drift.
47
+ */
48
+ export type ResolvableModule<S extends Source = SourceObject> = ValModule<GenericSelector<S>> | ValView<S>;
49
+ /**
50
+ * What a reader was actually handed: the module itself, or — when it is a view
51
+ * handle — the module that view points at.
52
+ *
53
+ * Every reader that needs a module rather than a value goes through this:
54
+ * `useValKey`, `useValRoute`, `useValRouteUrl` and their `fetch*` counterparts
55
+ * all pull the path, the schema and the source off the thing they are given,
56
+ * and a view handle has none of those. It is `{ view: "/foo.val.ts" }` with the
57
+ * module on a symbol, so reading `Internal.getValPath` off it gives
58
+ * `undefined`, and every one of those readers treats `undefined` as its own
59
+ * "no route matched" answer. Without this the call is not an error — it is a
60
+ * silently empty one.
61
+ *
62
+ * A POINTER with no module on it throws rather than being passed through, for
63
+ * the same reason `stegaEncode` throws on one: symbols do not survive
64
+ * serialization, so a handle that crossed the server/client boundary as a prop
65
+ * arrives looking like content and is not.
66
+ */
67
+ export declare function resolveViewedModule<S extends Source>(selector: ResolvableModule<S>): ValModule<GenericSelector<S>>;
68
+ export declare function viewModulesOf(schema: Schema<SelectorSource> | undefined): Map<string, unknown>;
69
+ export {};