@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/dist/declarations/src/index.d.ts +7 -4
- package/dist/declarations/src/initSchema.d.ts +1 -1
- package/dist/declarations/src/initVal.d.ts +2 -0
- package/dist/declarations/src/module.d.ts +3 -2
- package/dist/declarations/src/schema/richtext.d.ts +4 -64
- package/dist/declarations/src/selector/index.d.ts +3 -2
- package/dist/declarations/src/source/index.d.ts +3 -2
- package/dist/declarations/src/source/remote.d.ts +2 -2
- package/dist/declarations/src/source/richtext.d.ts +63 -0
- package/dist/declarations/src/val/index.d.ts +1 -0
- package/dist/{index-2f1631cf.cjs.prod.js → index-01a259ae.cjs.prod.js} +4 -0
- package/dist/{index-55916dcd.esm.js → index-3b733a02.esm.js} +4 -1
- package/dist/{index-b49a2c60.cjs.dev.js → index-f0d4530a.cjs.dev.js} +4 -0
- package/dist/valbuild-core.cjs.dev.js +24 -262
- package/dist/valbuild-core.cjs.prod.js +24 -262
- package/dist/valbuild-core.esm.js +26 -263
- package/expr/dist/valbuild-core-expr.cjs.dev.js +1 -1
- package/expr/dist/valbuild-core-expr.cjs.prod.js +1 -1
- package/expr/dist/valbuild-core-expr.esm.js +1 -1
- package/package.json +1 -1
- package/src/index.ts +15 -12
- package/src/initVal.ts +5 -1
- package/src/module.ts +2 -5
- package/src/schema/richtext.ts +6 -384
- package/src/selector/index.ts +4 -0
- package/src/source/index.ts +3 -1
- package/src/source/remote.ts +2 -2
- package/src/source/richtext.ts +116 -0
- package/src/val/index.ts +10 -0
- package/README.md +0 -525
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
|
-
```
|