@valbuild/core 0.13.3 → 0.13.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md DELETED
@@ -1,525 +0,0 @@
1
- <p align="center">
2
- <h1 align="center">Val</h1>
3
- <p align="center">
4
- ✨ <a href="https://app.val.build">https://app.val.build</a> ✨
5
- <br/>
6
- hard-coded content - super-charged
7
- </p>
8
- </p>
9
-
10
- # 🐉 HERE BE DRAGONS 🐉
11
-
12
- Val is PRE-ALPHA - MOST features are broken and in state of flux.
13
-
14
- This is released only for **INTERNAL** **TESTING** PURPOSES.
15
-
16
- ## Table of contents
17
-
18
- - [Table of contents](#table-of-contents)
19
- - [Introduction](#introduction)
20
- - [Installation](#installation)
21
- - [Getting started](#getting-started)
22
- - [Concepts](#concepts)
23
- - [String](#string)
24
- - [Number](#number)
25
- - [Boolean](#boolean)
26
- - [Optional](#optional)
27
- - [Array](#array)
28
- - [Object](#object)
29
- - [Rich text](#richtext)
30
- - [Image](#image)
31
- - [Internalization](#internalization)
32
- - [Union](#union)
33
- - [One-of references](#one-of-references)
34
- - [Remote](#remote)
35
- - [Selector](#selector)
36
-
37
- ## Introduction
38
-
39
- Val treats your content as code, but it remains fully editable.
40
-
41
- Val is built on top of TypeScript and Git, letting you use types, branches, version control (even rollbacks!) seamlessly.
42
-
43
- - Val is **TypeSafe**: being TypeScript-first means you see errors as you type. In addition you can **refactor**, find references and auto-complete content - just as if it was hard-coded. Safety means no more "add-only" content models, where your content model continues to expand because you hesitate to remove anything.
44
-
45
- - Version **control**: content is tied to commits, which means you can use branches and reverts just as you would normally do.
46
-
47
- - Val is **isomorphic**: if your content scales beyond your code base, Val lets you easily make it remote. Just add `.remote()` to your schema and Val will host your content - your components stays the same. Remote content are handled using immutable references, so remote content are tied to Git commits - rollbacks, branches works as before.
48
-
49
- - Contextual editing: Val is built from the ground up to support contextual editing. Editors can see Val content in the context of the app, and make edits directly there.
50
-
51
- - Full CMS: even hard-coded content has its drawbacks: i18n, images, rich text - Val supports all of these.
52
-
53
- - No signup required: **your** content is yours. Local development requires no sign ups and is free. When you are ready and editors needs access, you can sign up and without any changes to your code base. Head over to [https://app.val.build](https://app.val.build) to get started 🚀.
54
-
55
- ## Installation
56
-
57
- - Make sure you have TypeScript 4.9+, Next 12+ (other meta frameworks will come), React 18+ (other frontend frameworks will come)
58
- - **NOTE**: THIS GUIDE is using the Next `/pages` directory NOT the new `/app` directory!
59
- - Install the packages:
60
-
61
- ```sh
62
- npm install @valbuild/core @valbuild/react @valbuild/server
63
- ```
64
-
65
- - Create your val.config.ts file. NOTE: this file should be in the same directory as `tsconfig.json`:
66
-
67
- ```ts
68
- // ./val.config.ts
69
-
70
- import { initVal } from "@valbuild/core";
71
-
72
- const { s, val } = initVal();
73
-
74
- export { s, val };
75
- ```
76
-
77
- - Update tsconfig.json: enable strict mode and include the Val JSX transformer :
78
-
79
- ```jsonc
80
- // ./tsconfig.json
81
-
82
- {
83
- "compilerOptions": {
84
- ///...
85
- "strict": true,
86
- ///...
87
- "jsxImportSource": "@valbuild/react"
88
- //...
89
- }
90
- ///...
91
- }
92
- ```
93
-
94
- - Enable contextual editing: setup Val endpoints
95
-
96
- ```ts
97
- // ./src/pages/api/val/[...val].ts
98
-
99
- import { createRequestListener } from "@valbuild/server";
100
- import { NextApiHandler } from "next";
101
-
102
- const handler: NextApiHandler = createRequestListener("/api/val", {
103
- valConfigPath: "./val.config",
104
- });
105
-
106
- export default handler;
107
-
108
- export const config = {
109
- api: {
110
- bodyParser: false,
111
- externalResolver: true,
112
- },
113
- };
114
- ```
115
-
116
- - Enable contextual editing: Use the Val provider in the \_app file:
117
-
118
- ```tsx
119
- // ./src/pages/_app.tsx
120
-
121
- import { ValProvider } from "@valbuild/react";
122
- import type { AppProps } from "next/app";
123
-
124
- function MyApp({ Component, pageProps }: AppProps) {
125
- return (
126
- <ValProvider host="/api/val">
127
- <Component {...pageProps} />
128
- </ValProvider>
129
- );
130
- }
131
-
132
- export default MyApp;
133
- ```
134
-
135
- ## Getting started
136
-
137
- ### Create your first Val content file
138
-
139
- Content defined in Val is always defined `.val.{ts|js}` files.
140
-
141
- They must export a default `val.content` where the first argument equals the path of the file relative to the `val.config.{js|ts}` file.
142
-
143
- ```ts
144
- // ./src/content/example/blogs.val.ts
145
-
146
- import { s, val } from "../../../val.config";
147
-
148
- export default val.content(
149
- "/src/content/example/blogs", // <- NOTE: this must be the same path as the file
150
- s.array(s.object({ title: s.string(), text: s.string() })),
151
- [
152
- {
153
- title: "Title 1",
154
- text: "Text 1",
155
- },
156
- {
157
- title: "Title 2",
158
- text: "Text 2",
159
- },
160
- ]
161
- );
162
- ```
163
-
164
- ### Use your content
165
-
166
- ```tsx
167
- // ./src/pages/example/index.tsx
168
-
169
- import { NextPage } from "next";
170
- import { useVal } from "@valbuild/react";
171
- import blogsVal from "@/content/example/blogs.val";
172
-
173
- const Blog: NextPage = () => {
174
- const blog = useVal(blogsVal[0]);
175
- return (
176
- <main>
177
- <article>
178
- <section>
179
- <h1>{blog.title}</h1>
180
- <p>{blog.text}</p>
181
- </section>
182
- </article>
183
- </main>
184
- );
185
- };
186
-
187
- export default Blog;
188
- ```
189
-
190
- ## Concepts
191
-
192
- `.val.{ts|js}` files **MUST** have a default export which is a ValModule. A ValModule is a special type of [Selector](#selectors). Selectors makes it possible to select a subset of content from a ValModule or a another Selector.
193
-
194
- Selectors can be turned to Val types using `useVal` or `fetchVal`.
195
-
196
- Val types is what you use in your components. It is a wrapper around underlying type that adds an id, which is used to automatically tag content in the DOM. You can always extract the underlying value from a Val using the `.val` property.
197
-
198
- ### .val files
199
-
200
- `.val.{ts|js}` files are the files in which you store your content.
201
-
202
- They are evaluated when the content is run, therefore they have a specific set of requirements. They must have a default export that is `val.content`, they must have a `export const schema` with the Schema and they CANNOT import anything other than `val.config` and `@valbuild/core`.
203
-
204
- Example:
205
-
206
- ```ts
207
- import { s, val } from "../val.config";
208
-
209
- export const schema = t.string();
210
-
211
- export default val.content(
212
- "/file/path/relative/to/val/config",
213
- schema,
214
- "My string content"
215
- );
216
- ```
217
-
218
- NOTE: IN THE FUTURE, they will be validated by the eslint-plugin.
219
-
220
- ## String
221
-
222
- ```ts
223
- import { s } from "./val.config";
224
-
225
- s.string(); // <- Schema<string>
226
- ```
227
-
228
- ### String selectors
229
-
230
- See [Selectors](#selector) for more info.
231
-
232
- ### String `.eq`
233
-
234
- ```ts
235
- useVal(stringVal.eq("")); // <- Val<boolean>
236
- ```
237
-
238
- ## Number
239
-
240
- ```ts
241
- import { s } from "./val.config";
242
-
243
- s.number(); // <- Schema<number>
244
- ```
245
-
246
- ### Number selectors
247
-
248
- See [Selectors](#selector) for more info.
249
-
250
- ### Number `.eq`
251
-
252
- ```ts
253
- useVal(numberVal.eq(2)); // <- Val<boolean>
254
- ```
255
-
256
- ## Boolean
257
-
258
- ```ts
259
- import { s } from "./val.config";
260
-
261
- s.boolean(); // <- Schema<boolean>
262
- ```
263
-
264
- ### Boolean selectors
265
-
266
- See [Selectors](#selector) for more info.
267
-
268
- ### Boolean `.eq`
269
-
270
- ```ts
271
- useVal(booleanVal.eq(true)); // <- Val<boolean>
272
- ```
273
-
274
- ## Optional
275
-
276
- All schema types can be optional. An optional schema creates a union of the type and `null`.
277
-
278
- ```ts
279
- import { s } from "./val.config";
280
-
281
- s.string().optional(); // <- Schema<string | null>
282
- ```
283
-
284
- ### Selectors
285
-
286
- ### Accessing the underlying type: `.andThen`
287
-
288
- To use and optional val, you can use the [.andThen](#andthen) selector.
289
-
290
- ## Array
291
-
292
- ```ts
293
- s.array(t.string());
294
- ```
295
-
296
- ### Selecting arrays
297
-
298
- ### `.filter`
299
-
300
- TODO: text
301
-
302
- ```ts
303
- useVal(myArray.filter((item) => item.title.eq("Title 1")));
304
- ```
305
-
306
- ### `.map`
307
-
308
- TODO:
309
-
310
- ```ts
311
- useVal(myArray.map((item) => ({ test: item.title })));
312
- ```
313
-
314
- ## Object
315
-
316
- ```ts
317
- s.object({
318
- myProperty: s.string(),
319
- });
320
- ```
321
-
322
- ### Selecting objects
323
-
324
- You can select Selector objects almost as if they were normal objects. The exception is that you cannot use the spread (`...`) operator.
325
-
326
- Example:
327
-
328
- ```ts
329
- useVal({ foo: myObjectVal.hello });
330
- ```
331
-
332
- ## RichText
333
-
334
- ### RichText Schema
335
-
336
- ```ts
337
- import { s } from "./val.config";
338
-
339
- s.richtext();
340
- ```
341
-
342
- ### Initializing RichText content
343
-
344
- To initialize some text content using a RichText schema, you can use follow the example below:
345
-
346
- ```ts
347
- import { s, val } from "./val.config";
348
-
349
- // TODO: need some other way of doing this:
350
- export default val.content("/example/richtext.ts", s.richtext(), {
351
- children: [
352
- {
353
- type: "paragraph",
354
- version: 1,
355
- indent: 0,
356
- direction: "ltr",
357
- format: "",
358
- children: [
359
- {
360
- type: "text",
361
- version: 1,
362
- mode: "normal",
363
- format: 0,
364
- detail: 0,
365
- style: "",
366
- text: "TODO: update me",
367
- },
368
- ],
369
- },
370
- ],
371
- direction: "ltr",
372
- format: "",
373
- indent: 0,
374
- type: "root",
375
- version: 1,
376
- });
377
- ```
378
-
379
- ### Render RichText
380
-
381
- You can use the `ValRichText` component to render content.
382
-
383
- ```tsx
384
- "use client";
385
- import { useVal, ValRichText } from "@valbuild/react";
386
- import richtextVal from "./richtext";
387
-
388
- export default function Page() {
389
- const richtext = useVal(richtextVal);
390
- return <ValRichText>{richtext}</ValRichText>;
391
- }
392
- ```
393
-
394
- ## Image
395
-
396
- ### Schema
397
-
398
- ```ts
399
- s.image();
400
- ```
401
-
402
- ### Initializing image content
403
-
404
- Local images must be stored under the `.public` folder.
405
-
406
- ```ts
407
- import { s, val } from "../val.config";
408
-
409
- export const schema = s.image();
410
-
411
- export default val.content("/image", schema, val.file("/public/myfile.jpg"));
412
- ```
413
-
414
- NOTE: This will not validate, since images requires `width`, `height` and a `sha256` checksum. You can fix this validation in the UI by opening the image and clicking the Fix button.
415
-
416
- ### Using images in components
417
-
418
- Images are transformed to object that have a `url` property which can be used to render them.
419
-
420
- Example:
421
-
422
- ```tsx
423
- // in a Functional Component
424
- const image = useVal(imageVal);
425
-
426
- return <img src={image.url} />;
427
- ```
428
-
429
- ## Internalization
430
-
431
- **NOTE**: WORKS ONLY ON THE TYPE LEVEL
432
-
433
- To enable i18n, you must update your Val with the locales you want to enforce.
434
-
435
- Example:
436
-
437
- ```ts
438
- // ./val.config.ts
439
-
440
- import { initVal } from "@valbuild/core";
441
-
442
- const { s, val } = initVal({
443
- locales: {
444
- required: ["en_US", "fr_FR"],
445
- fallback: "en_US",
446
- },
447
- });
448
-
449
- export { s, val };
450
- ```
451
-
452
- ## Union
453
-
454
- **NOTE**: WORKS ONLY ON THE TYPE LEVEL
455
-
456
- TODO: fill in.
457
-
458
- ```ts
459
- s.union(
460
- "type",
461
- s.object({ type: s.literal("type1"), bar: s.string() }),
462
- s.object({ type: s.literal("type2"), foo: s.number() })
463
- );
464
- ```
465
-
466
- ### Selecting unions
467
-
468
- ### `.fold`
469
-
470
- TODO: description
471
-
472
- ```ts
473
- useVal(myUnionVal.fold("type")({
474
-
475
- })
476
- ```
477
-
478
- ## One of references
479
-
480
- **NOTE**: Currently not possible to change from UI
481
-
482
- ```ts
483
-
484
- ```
485
-
486
- # Remote
487
-
488
- **NOTE**: WORKS ONLY ON THE TYPE LEVEL
489
-
490
- All schemas can be converted into `.remote`.
491
-
492
- TODO: add description.
493
-
494
- Example:
495
-
496
- ```ts
497
- export const schema = s.object({ stuff: s.string() });
498
-
499
- export default val.content("/remote/example", schema, val.remote("REFERENCE"));
500
- ```
501
-
502
- ## Selector
503
-
504
- To select parts of a your content you should use Selectors.
505
- If you make the content `.remote`
506
-
507
- ### `.andThen`
508
-
509
- All selectors can use `andThen` method which is similar to the `&&` operator. You can use this to only do operations on optionals that are defined. NOTE: only TRUTHY arguments are passed in to `andThen` (i.e. the empty string, `''` is NOT truthy).
510
-
511
- Given the example schema:
512
-
513
- ```ts
514
- // ./maybeArray.val.ts
515
- //...
516
-
517
- export const schema = t.array(t.string()).optional();
518
-
519
- //...
520
- ```
521
-
522
- ```ts
523
- import maybeArrayVal from "./maybeArray.val";
524
- useVal(maybeArrayVal.andThen((array) => array.filter((v) => v.eq("foo"))));
525
- ```