@valbuild/core 0.120.0 → 0.124.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 (44) hide show
  1. package/CHANGELOG.md +123 -0
  2. package/dist/declarations/src/index.d.ts +12 -1
  3. package/dist/declarations/src/initSchema.d.ts +27 -0
  4. package/dist/declarations/src/initVal.d.ts +13 -8
  5. package/dist/declarations/src/module.d.ts +18 -1
  6. package/dist/declarations/src/schema/array.d.ts +2 -2
  7. package/dist/declarations/src/schema/boolean.d.ts +2 -2
  8. package/dist/declarations/src/schema/code.d.ts +2 -2
  9. package/dist/declarations/src/schema/color.d.ts +2 -2
  10. package/dist/declarations/src/schema/date.d.ts +2 -2
  11. package/dist/declarations/src/schema/datetime.d.ts +2 -2
  12. package/dist/declarations/src/schema/file.d.ts +2 -2
  13. package/dist/declarations/src/schema/files.d.ts +2 -5
  14. package/dist/declarations/src/schema/hasRemoteFileSchema.d.ts +21 -3
  15. package/dist/declarations/src/schema/image.d.ts +2 -2
  16. package/dist/declarations/src/schema/images.d.ts +1 -6
  17. package/dist/declarations/src/schema/index.d.ts +12 -3
  18. package/dist/declarations/src/schema/keyOf.d.ts +2 -2
  19. package/dist/declarations/src/schema/literal.d.ts +2 -2
  20. package/dist/declarations/src/schema/number.d.ts +2 -2
  21. package/dist/declarations/src/schema/object.d.ts +2 -2
  22. package/dist/declarations/src/schema/record.d.ts +80 -4
  23. package/dist/declarations/src/schema/richtext.d.ts +2 -2
  24. package/dist/declarations/src/schema/route.d.ts +2 -2
  25. package/dist/declarations/src/schema/settings.d.ts +102 -0
  26. package/dist/declarations/src/schema/string.d.ts +2 -2
  27. package/dist/declarations/src/schema/union.d.ts +2 -2
  28. package/dist/declarations/src/schema/validation/ValidationFix.d.ts +1 -1
  29. package/dist/declarations/src/selector/index.d.ts +4 -2
  30. package/dist/declarations/src/settingsModule.d.ts +60 -0
  31. package/dist/declarations/src/source/external.d.ts +91 -0
  32. package/dist/declarations/src/source/externalRef.d.ts +46 -0
  33. package/dist/declarations/src/source/index.d.ts +4 -2
  34. package/dist/declarations/src/source/settings.d.ts +94 -0
  35. package/dist/{index-861944d8.cjs.prod.js → index-22a40d42.cjs.prod.js} +1324 -446
  36. package/dist/{index-2f65e194.esm.js → index-9c14df37.esm.js} +1318 -447
  37. package/dist/{index-01f673d2.cjs.dev.js → index-fcd087bd.cjs.dev.js} +1324 -446
  38. package/dist/valbuild-core.cjs.dev.js +8 -1
  39. package/dist/valbuild-core.cjs.prod.js +8 -1
  40. package/dist/valbuild-core.esm.js +1 -1
  41. package/package.json +1 -1
  42. package/patch/dist/valbuild-core-patch.cjs.dev.js +1 -1
  43. package/patch/dist/valbuild-core-patch.cjs.prod.js +1 -1
  44. package/patch/dist/valbuild-core-patch.esm.js +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,128 @@
1
1
  # @valbuild/core
2
2
 
3
+ ## 0.124.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#622](https://github.com/valbuild/val/pull/622) [`aa89fc2`](https://github.com/valbuild/val/commit/aa89fc26de7028b3c5a1666b99afc03b39e92769) Thanks [@freekh](https://github.com/freekh)! - **Breaking:** `s.images()` and `s.files()` no longer take a `remote` option. Use
8
+ `.remote()`, the same way `s.image()` and `s.file()` already did.
9
+
10
+ ```ts
11
+ // before
12
+ s.images({ directory: "/public/val/images", remote: true });
13
+ s.files({
14
+ directory: "/public/val/docs",
15
+ accept: "application/pdf",
16
+ remote: true,
17
+ });
18
+
19
+ // after
20
+ s.images({ directory: "/public/val/images" }).remote();
21
+ s.files({
22
+ directory: "/public/val/docs",
23
+ accept: "application/pdf",
24
+ }).remote();
25
+ ```
26
+
27
+ Behaviour is unchanged — remote is still off unless asked for. This only makes
28
+ remote one thing spelled one way across all four media schemas, instead of an
29
+ option on the two collections and a method on the two fields. `remote: false`
30
+ has no replacement because it was the default; drop it.
31
+
32
+ ## 0.121.0
33
+
34
+ ### Minor Changes
35
+
36
+ - [#605](https://github.com/valbuild/val/pull/605) [`6794d29`](https://github.com/valbuild/val/commit/6794d2980bc81284ab7f2cc667f01cc21c9e3a79) Thanks [@freekh](https://github.com/freekh)! - `s.settings()`: the project's settings, as content.
37
+
38
+ A settings module is one per project, at the root of the content tree:
39
+
40
+ ```typescript
41
+ // settings.val.ts
42
+ export default c.define("/settings.val.ts", s.settings(), {});
43
+ ```
44
+
45
+ Register it in `val.modules.ts` like any other module, and it shows up in the
46
+ Studio under the cog at the foot of the left rail. Everything in it is content:
47
+ it is edited as a draft, it appears in the publish diff, and it is the same for
48
+ everyone working on the project.
49
+
50
+ Every key is optional, at every level, so `{}` is a complete settings module —
51
+ and stays one as sections are added. What it holds today is the assistant:
52
+
53
+ ```typescript
54
+ export default c.define("/settings.val.ts", s.settings(), {
55
+ assistant: {
56
+ enabled: true,
57
+ context: "A CMS for developers, run by a team of four in Oslo.",
58
+ tone: "Plain and direct. British English, sentence case in headings.",
59
+ },
60
+ });
61
+ ```
62
+
63
+ `context` is background the assistant would otherwise guess at; `tone` is how it
64
+ should write when it writes content. Both are sent with every message it makes.
65
+
66
+ `enabled` decides whether editors have an assistant, and it has **three** states
67
+ rather than two:
68
+
69
+ - `true` — they do.
70
+ - `false` — they do not, and every trace of it goes: no button in the top bar,
71
+ no row in the quick actions, no panel, nothing sent.
72
+ - unset — nobody has decided. The assistant is still **shown**, and asks to be
73
+ turned on before it is used. Hiding an assistant nobody has decided about
74
+ means nobody discovers it; quietly enabling one means a project starts sending
75
+ its content to a model because it did not know to say no.
76
+
77
+ A project with no settings module at all has an assistant, as before: there is
78
+ nowhere to record a decision, and nowhere for the prompt to write the answer.
79
+
80
+ **Breaking: `ai.chat` is gone from `val.config.ts`.** Whether the assistant is
81
+ available is a decision about the project's content, made by the people who edit
82
+ it, so it moved to settings — turning the chat on used to take a developer, a
83
+ deploy and a code review of a boolean. Remove the whole block:
84
+
85
+ ```diff
86
+ const { s, c, val, config } = initVal({
87
+ - ai: {
88
+ - chat: {
89
+ - experimental: { enable: true },
90
+ - suggestions: ["Summarize", "Fix typos at this page"],
91
+ - title: "Ask me anything",
92
+ - description: "Val can answer questions about the content.",
93
+ - },
94
+ - },
95
+ });
96
+ ```
97
+
98
+ `experimental.enable` becomes `assistant.enabled` in the settings module.
99
+ `suggestions`, `title` and `description` are removed with nothing replacing
100
+ them: the assistant now opens with its own copy. A project that had the chat
101
+ enabled and wants it to stay on for everyone should write
102
+ `assistant: { enabled: true }` — otherwise editors are offered it and asked.
103
+
104
+ `ai.commitMessages` stays in `val.config.ts`, and is unaffected.
105
+
106
+ Two settings modules, or one in a subdirectory, is a module error: the dev
107
+ server refuses to serve sources, `npx val validate` reports it against the file,
108
+ and the Studio says so rather than picking one.
109
+
110
+ ### Patch Changes
111
+
112
+ - [#607](https://github.com/valbuild/val/pull/607) [`2bcbee1`](https://github.com/valbuild/val/commit/2bcbee1be682c2bbd5b7bc7d152ddd4204162fd2) Thanks [@freekh](https://github.com/freekh)! - `.readonly()` and `.hidden()` now take the flag as an argument, so a schema can
113
+ decide these from a variable instead of only from whether the call was written at
114
+ all:
115
+
116
+ ```ts
117
+ s.string().readonly(!canEdit);
118
+ s.image().hidden(hideMedia);
119
+ ```
120
+
121
+ The argument defaults to `true`, so `.readonly()` and `.readonly(true)` are the
122
+ same thing and nothing about existing schemas changes. Passing `false` leaves the
123
+ field editable or visible, which is also what a schema is without the call - it
124
+ is there so the flag can come from a variable.
125
+
3
126
  ## 0.120.0
4
127
 
5
128
  ### Minor Changes
@@ -3,13 +3,14 @@ export { modules, type ValModules } from "./modules.js";
3
3
  export { extractValModules, computeValModuleShas, type ExtractedValModules, type ValModuleShaEntry, type ValModuleShas, type ExtractedModuleError, } from "./extractValModules.js";
4
4
  export type { InitVal, ValConfig, ValConstructor, ContentConstructor, } from "./initVal.js";
5
5
  export { Schema, type SerializedSchema, type SelectorOfSchema } from "./schema/index.js";
6
- export { hasRemoteFileSchema } from "./schema/hasRemoteFileSchema.js";
6
+ export { hasRemoteFileSchema, hasMediaSchema, } from "./schema/hasRemoteFileSchema.js";
7
7
  export type { ImageMetadata, ImageEncodeOption, ImageEncodeOptions, } from "./schema/image.js";
8
8
  export type { FileMetadata } from "./schema/file.js";
9
9
  export type { ValModule, SerializedModule, InferValModuleType } from "./module.js";
10
10
  export type { SourceObject, SourcePrimitive, Source } from "./source/index.js";
11
11
  export type { FileSource } from "./source/media.js";
12
12
  export type { JsonSource, JsonOf, JsonImportThunk } from "./source/json.js";
13
+ export type { ExternalRecordSrc, ExternalRecordWritableSrc, ExternalItemOf, ExternalLabelOf, ExternalReadonlyOf, } from "./source/external.js";
13
14
  export type { RemoteRef } from "./source/remote.js";
14
15
  export { DEFAULT_VAL_REMOTE_HOST } from "./schema/remote.js";
15
16
  export type { RawString } from "./schema/string.js";
@@ -35,6 +36,8 @@ import { getMimeType, mimeTypeToFileExt, filenameToMimeType, mimeTypeMatchesAcce
35
36
  import { type ImageMetadata } from "./schema/image.js";
36
37
  import { type FileMetadata } from "./schema/file.js";
37
38
  import { isJson, getJsonImport, resolveJsonValues } from "./source/json.js";
39
+ import { isExternal, hasInlineExternalEntries } from "./source/external.js";
40
+ import { createExternalFileRef, splitExternalFileRef, isExternalFileRef } from "./source/externalRef.js";
38
41
  import { createRemoteRef } from "./source/remote.js";
39
42
  import { getValidationBasis, getValidationHash } from "./remote/validationBasis.js";
40
43
  import { getFileHash, hashToRemoteFileHash } from "./remote/fileHash.js";
@@ -50,6 +53,9 @@ export { type SerializedBooleanSchema, BooleanSchema } from "./schema/boolean.js
50
53
  export { type SerializedImageSchema, ImageSchema } from "./schema/image.js";
51
54
  export { type SerializedFileSchema, FileSchema } from "./schema/file.js";
52
55
  export { type SerializedDateSchema, DateSchema } from "./schema/date.js";
56
+ export { type SerializedSettingsSchema, SettingsSchema, } from "./schema/settings.js";
57
+ export { type SettingsSource, type AssistantSettingsSource, type AssistantAvailability, ASSISTANT_SETTINGS_MAX_LENGTH, assistantAvailability, } from "./source/settings.js";
58
+ export { type ResolvedSettingsModule, SETTINGS_MODULE_CONVENTION, isRootModuleFilePath, resolveSettingsModule, } from "./settingsModule.js";
53
59
  export { type SerializedColorSchema, type ColorOptions, ColorSchema, } from "./schema/color.js";
54
60
  export { type SerializedCodeSchema, type CodeOptions, type CodeLanguage, CodeSchema, CODE_LANGUAGES, } from "./schema/code.js";
55
61
  export { type ColorFormat, type ParsedColor, COLOR_FORMATS, DEFAULT_COLOR_FORMAT, } from "./schema/colorFormat.js";
@@ -108,6 +114,11 @@ declare const Internal: {
108
114
  isJson: typeof isJson;
109
115
  getJsonImport: typeof getJsonImport;
110
116
  resolveJsonValues: typeof resolveJsonValues;
117
+ isExternal: typeof isExternal;
118
+ hasInlineExternalEntries: typeof hasInlineExternalEntries;
119
+ createExternalFileRef: typeof createExternalFileRef;
120
+ splitExternalFileRef: typeof splitExternalFileRef;
121
+ isExternalFileRef: typeof isExternalFileRef;
111
122
  createValPathOfItem: typeof createValPathOfItem;
112
123
  getSHA256Hash: (bits: Uint8Array) => string;
113
124
  initSchema: typeof initSchema;
@@ -18,6 +18,7 @@ import { color } from "./schema/color.js";
18
18
  import { route } from "./schema/route.js";
19
19
  import { router } from "./schema/router.js";
20
20
  import { images } from "./schema/images.js";
21
+ import { settings } from "./schema/settings.js";
21
22
  export type InitSchema = {
22
23
  /**
23
24
  * Define a string.
@@ -299,6 +300,31 @@ export type InitSchema = {
299
300
  * ```
300
301
  */
301
302
  readonly files: typeof files;
303
+ /**
304
+ * Define the project's settings.
305
+ *
306
+ * One per project, at the root of the content tree — a module file path with
307
+ * no directory segment, `/settings.val.ts` by convention. Every section is
308
+ * optional, so `{}` is a complete settings module and stays one as sections
309
+ * are added.
310
+ *
311
+ * @example
312
+ * ```typescript
313
+ * export default c.define("/settings.val.ts", s.settings(), {});
314
+ * ```
315
+ *
316
+ * @example
317
+ * ```typescript
318
+ * export default c.define("/settings.val.ts", s.settings(), {
319
+ * assistant: {
320
+ * enabled: true,
321
+ * context: "A CMS for developers. British English throughout.",
322
+ * tone: "Plain and direct. Sentence case in headings.",
323
+ * },
324
+ * });
325
+ * ```
326
+ */
327
+ readonly settings: typeof settings;
302
328
  };
303
329
  export declare function initSchema(): {
304
330
  string: <T extends string>(options?: Record<string, never>) => import("./index.js").StringSchema<T>;
@@ -349,4 +375,5 @@ export declare function initSchema(): {
349
375
  mimeType: string;
350
376
  alt: string | null;
351
377
  }>, import("./schema/index.js").Schema<string>, Record<string, import("./schema/images.js").ImagesEntryMetadata>>;
378
+ settings: typeof settings;
352
379
  };
@@ -2,9 +2,11 @@ import { define } from "./module.js";
2
2
  import { InitSchema } from "./initSchema.js";
3
3
  import { getValPath as getPath } from "./val/index.js";
4
4
  import { json } from "./source/json.js";
5
+ import { external } from "./source/external.js";
5
6
  export type ContentConstructor = {
6
7
  define: typeof define;
7
8
  json: typeof json;
9
+ external: typeof external;
8
10
  };
9
11
  export type ValConstructor = {
10
12
  unstable_getPath: typeof getPath;
@@ -20,17 +22,20 @@ export type ValConfig = {
20
22
  gitBranch?: string;
21
23
  defaultTheme?: "dark" | "light";
22
24
  ai?: {
25
+ /**
26
+ * The AI commit-message summariser.
27
+ *
28
+ * Still config, and deliberately: it is about how the repository is
29
+ * written to rather than about the content, and it is getting settings of
30
+ * its own. The ASSISTANT is not here — it is `s.settings()`, under
31
+ * `assistant.enabled`, because whether editors have a chat is a decision
32
+ * about the
33
+ * project's content, made by the people who edit it, and published like any
34
+ * other content change.
35
+ */
23
36
  commitMessages?: {
24
37
  disabled?: boolean;
25
38
  };
26
- chat?: {
27
- experimental?: {
28
- enable?: boolean;
29
- };
30
- suggestions?: string[];
31
- title?: string;
32
- description?: string;
33
- };
34
39
  };
35
40
  };
36
41
  export type InitVal = {
@@ -1,6 +1,7 @@
1
1
  import { Schema, SelectorOfSchema, SerializedSchema } from "./schema/index.js";
2
2
  import { GenericSelector, SelectorOf, SelectorSource } from "./selector/index.js";
3
3
  import { Source } from "./source/index.js";
4
+ import { ExternalRecordSrc } from "./source/external.js";
4
5
  import { ModuleFilePath, ModulePath, SourcePath } from "./val/index.js";
5
6
  import { Json } from "./Json.js";
6
7
  import { RawString } from "./schema/string.js";
@@ -15,8 +16,24 @@ export type ReplaceRawStringWithString<T extends SelectorSource> = SelectorSourc
15
16
  } ? {
16
17
  [key in keyof T]: ReplaceRawStringWithString<T[key]>;
17
18
  } : T extends SelectorSource[] ? ReplaceRawStringWithString<T[number]>[] : T;
19
+ /**
20
+ * What may be WRITTEN as an external record's source, on top of the marker.
21
+ *
22
+ * Entries inline in the `.val.ts` are accepted on purpose — the same call
23
+ * `.jsonValues()` makes for the same reason: pasting content straight into the
24
+ * module is the natural first thing to write, and a type error there is a dead
25
+ * end, because the author cannot see what to write instead. Validation reports
26
+ * them as an `external:upload` fix, and they are readable at runtime meanwhile.
27
+ *
28
+ * Widened HERE rather than in the schema's own source type so that the MODULE
29
+ * stays typed on the marker alone. An adapter is bound to a module, and a module
30
+ * whose type were "marker or plain record" would be indistinguishable from an
31
+ * ordinary record — which would let `entry()` accept a module that is not
32
+ * external at all.
33
+ */
34
+ type InlineEntriesFor<S> = S extends ExternalRecordSrc<infer Item, string, boolean> ? Record<string, Item> : never;
18
35
  export declare function define<T extends Schema<SelectorSource>>(id: string, // TODO: `/${string}`
19
- schema: T, source: ReplaceRawStringWithString<SelectorOfSchema<T>>): ValModule<SelectorOfSchema<T>>;
36
+ schema: T, source: ReplaceRawStringWithString<SelectorOfSchema<T>> | InlineEntriesFor<SelectorOfSchema<T>>): ValModule<SelectorOfSchema<T>>;
20
37
  export declare function getSource<T extends Source>(valModule: ValModule<T>): T;
21
38
  /**
22
39
  * Whether `value` is a Val module, i.e. something {@link define} produced.
@@ -43,8 +43,8 @@ export declare class ArraySchema<T extends Schema<SelectorSource>, Src extends S
43
43
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
44
44
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
45
45
  nullable(): ArraySchema<T, Src | null>;
46
- readonly(): ArraySchema<T, Src>;
47
- hidden(): ArraySchema<T, Src>;
46
+ readonly(isReadonly?: boolean): ArraySchema<T, Src>;
47
+ hidden(isHidden?: boolean): ArraySchema<T, Src>;
48
48
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
49
49
  protected executeSerialize(): SerializedArraySchema;
50
50
  protected executePreview(sourcePath: SourcePath | ModuleFilePath, src: Src, scope?: PreviewScope): ReifiedPreview;
@@ -29,8 +29,8 @@ export declare class BooleanSchema<Src extends boolean | null> extends Schema<Sr
29
29
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
30
30
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
31
31
  nullable(): BooleanSchema<Src | null>;
32
- readonly(): BooleanSchema<Src>;
33
- hidden(): BooleanSchema<Src>;
32
+ readonly(isReadonly?: boolean): BooleanSchema<Src>;
33
+ hidden(isHidden?: boolean): BooleanSchema<Src>;
34
34
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
35
35
  /**
36
36
  * How this field is laid out in the editor when it is the item of an array
@@ -61,8 +61,8 @@ export declare class CodeSchema<Src extends string | null> extends Schema<Src> {
61
61
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
62
62
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
63
63
  nullable(): CodeSchema<Src | null>;
64
- readonly(): CodeSchema<Src>;
65
- hidden(): CodeSchema<Src>;
64
+ readonly(isReadonly?: boolean): CodeSchema<Src>;
65
+ hidden(isHidden?: boolean): CodeSchema<Src>;
66
66
  /**
67
67
  * How this field is laid out in the editor when it is the item of an array
68
68
  * or record: `{ as: "inline" }` renders the field itself inside each row,
@@ -56,8 +56,8 @@ export declare class ColorSchema<Src extends string | null> extends Schema<Src>
56
56
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
57
57
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
58
58
  nullable(): ColorSchema<Src | null>;
59
- readonly(): ColorSchema<Src>;
60
- hidden(): ColorSchema<Src>;
59
+ readonly(isReadonly?: boolean): ColorSchema<Src>;
60
+ hidden(isHidden?: boolean): ColorSchema<Src>;
61
61
  /**
62
62
  * How this field is laid out in the editor when it is the item of an array
63
63
  * or record: `{ as: "inline" }` renders the field itself inside each row,
@@ -50,8 +50,8 @@ export declare class DateSchema<Src extends string | null> extends Schema<Src> {
50
50
  from(from: string): DateSchema<Src>;
51
51
  to(to: string): DateSchema<Src>;
52
52
  nullable(): DateSchema<Src | null>;
53
- readonly(): DateSchema<Src>;
54
- hidden(): DateSchema<Src>;
53
+ readonly(isReadonly?: boolean): DateSchema<Src>;
54
+ hidden(isHidden?: boolean): DateSchema<Src>;
55
55
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
56
56
  /**
57
57
  * How this field is laid out in the editor when it is the item of an array
@@ -54,8 +54,8 @@ export declare class DateTimeSchema<Src extends string | null> extends Schema<Sr
54
54
  from(from: string): DateTimeSchema<Src>;
55
55
  to(to: string): DateTimeSchema<Src>;
56
56
  nullable(): DateTimeSchema<Src | null>;
57
- readonly(): DateTimeSchema<Src>;
58
- hidden(): DateTimeSchema<Src>;
57
+ readonly(isReadonly?: boolean): DateTimeSchema<Src>;
58
+ hidden(isHidden?: boolean): DateTimeSchema<Src>;
59
59
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
60
60
  /**
61
61
  * How this field is laid out in the editor when it is the item of an array
@@ -48,8 +48,8 @@ export declare class FileSchema<Src extends FileSource | null> extends Schema<Sr
48
48
  private galleryEntries;
49
49
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
50
50
  nullable(): FileSchema<Src | null>;
51
- readonly(): FileSchema<Src>;
52
- hidden(): FileSchema<Src>;
51
+ readonly(isReadonly?: boolean): FileSchema<Src>;
52
+ hidden(isHidden?: boolean): FileSchema<Src>;
53
53
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
54
54
  /**
55
55
  * How this field is laid out in the editor when it is the item of an array
@@ -21,11 +21,6 @@ export type FilesOptions = {
21
21
  * a directory with every other one.
22
22
  */
23
23
  directory: "/public" | `/public/${string}`;
24
- /**
25
- * Whether remote files are allowed
26
- * @default false
27
- */
28
- remote?: boolean;
29
24
  };
30
25
  /**
31
26
  * Metadata for a file entry in the files record
@@ -43,6 +38,8 @@ type FilesItemSrc = {
43
38
  /**
44
39
  * Define a collection of files.
45
40
  *
41
+ * Remote is off by default: call `.remote()` on the result to allow remote files.
42
+ *
46
43
  * @example
47
44
  * ```typescript
48
45
  * const schema = s.files({
@@ -5,9 +5,10 @@ import type { SerializedSchema } from "./index.js";
5
5
  * The one answer to that question. There used to be two — `hasRemoteFileSchema`
6
6
  * in the server, gating whether `/save` demands remote credentials, and
7
7
  * `findRequiredRemoteFiles` in the Studio, gating the `/remote/settings` fetch —
8
- * and they disagreed about `s.images({ remote: true })`: the Studio's counted a
9
- * remote media record, the server's did not, because a media collection
10
- * serializes as a `record` of metadata rather than as an image schema.
8
+ * and they disagreed about `s.images({ ... }).remote()`: the Studio's version
9
+ * counted a remote media record, the server's did not, because a media
10
+ * collection serializes as a `record` of metadata rather than as an image
11
+ * schema.
11
12
  *
12
13
  * The server's answer was the wrong one, and silently so. With it false,
13
14
  * `/save` runs `saveOrUploadFiles` in `skip-remote` mode, and that mode does not
@@ -32,3 +33,20 @@ import type { SerializedSchema } from "./index.js";
32
33
  * instead would reintroduce exactly the silent skip described above.
33
34
  */
34
35
  export declare function hasRemoteFileSchema(schema: SerializedSchema): boolean;
36
+ /**
37
+ * Does this schema hold any media at all — remote or not?
38
+ *
39
+ * The sibling of {@link hasRemoteFileSchema}, and the same walk minus the
40
+ * `remote` predicate. It answers the question an external record has to ask
41
+ * before it will accept a binding: an item schema containing media needs a
42
+ * `putFile`/`getFile` pair, and without one that is a schema error.
43
+ *
44
+ * It is a walk over the SERIALIZED schema for the reasons given above, and for
45
+ * one more that is specific to this question: the two cases most likely to be
46
+ * missed are invisible to a value type. A media collection (`s.images()` /
47
+ * `s.files()`) serializes as a record of metadata with no image schema inside
48
+ * it, and a richtext inline image lives in a constructor argument. A check
49
+ * written against the item TYPE would silently pass both — and the gallery case
50
+ * already has a data-loss incident attached to it (see above).
51
+ */
52
+ export declare function hasMediaSchema(schema: SerializedSchema): boolean;
@@ -93,8 +93,8 @@ export declare class ImageSchema<Src extends ImageSource | null> extends Schema<
93
93
  private galleryEntries;
94
94
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
95
95
  nullable(): ImageSchema<Src | null>;
96
- readonly(): ImageSchema<Src>;
97
- hidden(): ImageSchema<Src>;
96
+ readonly(isReadonly?: boolean): ImageSchema<Src>;
97
+ hidden(isHidden?: boolean): ImageSchema<Src>;
98
98
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
99
99
  /**
100
100
  * How this field is laid out in the editor when it is the item of an array
@@ -35,11 +35,6 @@ export type ImagesOptions<Accept extends `image/${string}`> = {
35
35
  * - s.record(s.string(), s.string()) for locale-based alt text
36
36
  */
37
37
  alt?: AltSchema;
38
- /**
39
- * Whether remote images are allowed
40
- * @default false
41
- */
42
- remote?: boolean;
43
38
  /**
44
39
  * Re-encode uploads in the browser before they are uploaded.
45
40
  *
@@ -79,7 +74,7 @@ type ImagesItemSrc = {
79
74
  *
80
75
  * `directory` is required — it decides where uploads land, so it is not something
81
76
  * to be inferred. The rest defaults: any image type (`"image/*"`), nullable alt
82
- * text, remote disabled.
77
+ * text, remote disabled. Call `.remote()` on the result to allow remote images.
83
78
  *
84
79
  * @example
85
80
  * ```typescript
@@ -17,11 +17,12 @@ import { SerializedColorSchema } from "./color.js";
17
17
  import { SerializedDateSchema } from "./date.js";
18
18
  import { SerializedDateTimeSchema } from "./datetime.js";
19
19
  import { SerializedRouteSchema } from "./route.js";
20
+ import { SerializedSettingsSchema } from "./settings.js";
20
21
  import { ValidationError, ValidationErrors } from "./validation/ValidationError.js";
21
22
  import { FileSource } from "../source/media.js";
22
23
  import { GenericRichTextSourceNode, RichTextSource } from "../source/richtext.js";
23
24
  import { ReifiedPreview, PreviewScope, PreviewItem } from "../preview.js";
24
- export type SerializedSchema = SerializedStringSchema | SerializedLiteralSchema | SerializedBooleanSchema | SerializedNumberSchema | SerializedObjectSchema | SerializedArraySchema | SerializedUnionSchema | SerializedRichTextSchema | SerializedRecordSchema | SerializedKeyOfSchema | SerializedFileSchema | SerializedDateSchema | SerializedDateTimeSchema | SerializedColorSchema | SerializedCodeSchema | SerializedRouteSchema | SerializedImageSchema;
25
+ export type SerializedSchema = SerializedStringSchema | SerializedLiteralSchema | SerializedBooleanSchema | SerializedNumberSchema | SerializedObjectSchema | SerializedArraySchema | SerializedUnionSchema | SerializedRichTextSchema | SerializedRecordSchema | SerializedKeyOfSchema | SerializedFileSchema | SerializedDateSchema | SerializedDateTimeSchema | SerializedColorSchema | SerializedCodeSchema | SerializedRouteSchema | SerializedSettingsSchema | SerializedImageSchema;
25
26
  type Primitives = number | string | boolean | null | FileSource;
26
27
  export type AssertError = {
27
28
  message: string;
@@ -97,15 +98,23 @@ export declare abstract class Schema<Src extends SelectorSource> {
97
98
  *
98
99
  * This is a UI-only flag: the field is rendered disabled in the editor, but
99
100
  * the value is not otherwise validated or enforced differently.
101
+ *
102
+ * The flag defaults to `true`, so `.readonly()` and `.readonly(true)` are the
103
+ * same thing. `.readonly(false)` leaves the field editable, which is what a
104
+ * schema is anyway - pass it when the decision comes from a variable.
100
105
  */
101
- abstract readonly(): Schema<Src>;
106
+ abstract readonly(isReadonly?: boolean): Schema<Src>;
102
107
  /**
103
108
  * Hide this field from the Val editor.
104
109
  *
105
110
  * This is a UI-only flag: the field is not rendered in the editor, but the
106
111
  * value is still stored, validated and serialized as normal.
112
+ *
113
+ * The flag defaults to `true`, so `.hidden()` and `.hidden(true)` are the
114
+ * same thing. `.hidden(false)` leaves the field visible, which is what a
115
+ * schema is anyway - pass it when the decision comes from a variable.
107
116
  */
108
- abstract hidden(): Schema<Src>;
117
+ abstract hidden(isHidden?: boolean): Schema<Src>;
109
118
  protected abstract executeSerialize(): SerializedSchema;
110
119
  /**
111
120
  * @param scope Which paths the caller needs a preview for. Absent means the
@@ -47,8 +47,8 @@ export declare class KeyOfSchema<Sel extends GenericSelector<SourceObject>, Src
47
47
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
48
48
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
49
49
  nullable(): KeyOfSchema<Sel, Src | null>;
50
- readonly(): KeyOfSchema<Sel, Src>;
51
- hidden(): KeyOfSchema<Sel, Src>;
50
+ readonly(isReadonly?: boolean): KeyOfSchema<Sel, Src>;
51
+ hidden(isHidden?: boolean): KeyOfSchema<Sel, Src>;
52
52
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
53
53
  /**
54
54
  * How this field is laid out in the editor when it is the item of an array
@@ -31,8 +31,8 @@ export declare class LiteralSchema<Src extends string | null> extends Schema<Src
31
31
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
32
32
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
33
33
  nullable(): LiteralSchema<Src | null>;
34
- readonly(): LiteralSchema<Src>;
35
- hidden(): LiteralSchema<Src>;
34
+ readonly(isReadonly?: boolean): LiteralSchema<Src>;
35
+ hidden(isHidden?: boolean): LiteralSchema<Src>;
36
36
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
37
37
  /**
38
38
  * How this field is laid out in the editor when it is the item of an array
@@ -35,8 +35,8 @@ export declare class NumberSchema<Src extends number | null> extends Schema<Src>
35
35
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
36
36
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
37
37
  nullable(): NumberSchema<Src | null>;
38
- readonly(): NumberSchema<Src>;
39
- hidden(): NumberSchema<Src>;
38
+ readonly(isReadonly?: boolean): NumberSchema<Src>;
39
+ hidden(isHidden?: boolean): NumberSchema<Src>;
40
40
  max(max: number): NumberSchema<Src>;
41
41
  min(min: number): NumberSchema<Src>;
42
42
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
@@ -55,8 +55,8 @@ export declare class ObjectSchema<Props extends ObjectSchemaProps, Src extends O
55
55
  protected executeValidate(path: SourcePath, src: Src): ValidationErrors;
56
56
  protected executeAssert(path: SourcePath, src: unknown): SchemaAssertResult<Src>;
57
57
  nullable(): ObjectSchema<Props, Src | null>;
58
- readonly(): ObjectSchema<Props, Src>;
59
- hidden(): ObjectSchema<Props, Src>;
58
+ readonly(isReadonly?: boolean): ObjectSchema<Props, Src>;
59
+ hidden(isHidden?: boolean): ObjectSchema<Props, Src>;
60
60
  protected executeCustomValidateAt(path: SourcePath, src: Src): ValidationError[];
61
61
  /**
62
62
  * How this field is laid out in the editor when it is the item of an array