doc: update daletl specification refactor all typescript code

This commit is contained in:
Artemy Egorov 2024-07-28 20:21:22 +03:00
parent 90db2e6c73
commit 9d8220f726
13 changed files with 99 additions and 396 deletions

View file

@ -39,7 +39,6 @@
"typescript-eslint": "^7.17.0" "typescript-eslint": "^7.17.0"
}, },
"dependencies": { "dependencies": {
"@msgpack/msgpack": "3.0.0-beta2",
"zod": "^3.23.8" "zod": "^3.23.8"
} }
} }

View file

@ -8,9 +8,6 @@ importers:
.: .:
dependencies: dependencies:
'@msgpack/msgpack':
specifier: 3.0.0-beta2
version: 3.0.0-beta2
zod: zod:
specifier: ^3.23.8 specifier: ^3.23.8
version: 3.23.8 version: 3.23.8
@ -67,10 +64,6 @@ packages:
resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==}
engines: {node: '>=18.18'} engines: {node: '>=18.18'}
'@msgpack/msgpack@3.0.0-beta2':
resolution: {integrity: sha512-y+l1PNV0XDyY8sM3YtuMLK5vE3/hkfId+Do8pLo/OPxfxuFAUwcGz3oiiUuV46/aBpwTzZ+mRWVMtlSKbradhw==}
engines: {node: '>= 14'}
'@nodelib/fs.scandir@2.1.5': '@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
@ -575,8 +568,6 @@ snapshots:
'@humanwhocodes/retry@0.3.0': {} '@humanwhocodes/retry@0.3.0': {}
'@msgpack/msgpack@3.0.0-beta2': {}
'@nodelib/fs.scandir@2.1.5': '@nodelib/fs.scandir@2.1.5':
dependencies: dependencies:
'@nodelib/fs.stat': 2.0.5 '@nodelib/fs.stat': 2.0.5

View file

@ -1,76 +1,36 @@
import { encode, decode } from "@msgpack/msgpack"; import { encode, decode } from "../daletpack";
import { import { ParseError, CommonTag, Body, Root, Tag, CommonBody } from "./types";
ParseError,
RawTag,
Tag,
RawTagAsArray,
RawBody,
Body,
RootRaw,
} from "./types";
import El from "./tags/el";
import { TagNormalizers } from "./normalizers"; import { TagNormalizers } from "./normalizers";
export function parseTag(raw_tag: RawTag): Tag { export function parseTag(tag: Tag): CommonTag {
if (typeof raw_tag === "string") { return TagNormalizers[tag.id](tag);
return new El(raw_tag);
}
if (Array.isArray(raw_tag)) {
if (Array.isArray(raw_tag[0])) {
raw_tag = raw_tag as RawTag[];
return new El(raw_tag.map(parseTag));
}
if (typeof raw_tag[0] === "number") {
return TagNormalizers[(raw_tag as RawTagAsArray)[0]](
raw_tag as RawTagAsArray
);
}
}
throw new ParseError("Invalid tag");
} }
export function parseBody(body: RawBody): Body { export function parseBody(body: Body): CommonBody {
if (typeof body === "string") {
return body;
}
if (body === null) { if (body === null) {
return null; return null;
} }
if (Array.isArray(body)) { return body.map((t) => parseTag(t));
if (Array.isArray(body[0])) {
return body.map((t) => parseTag(t as RawTag));
}
if (typeof body[0] === "number") {
return [parseTag(body)];
}
}
throw new ParseError("Invalid tag body");
} }
export function parse(root_data: Uint8Array): Root { export function parse(root_data: Uint8Array): RootClass {
const root = decode(root_data); const root = decode(root_data);
if (!Array.isArray(root)) { if (!Array.isArray(root)) {
throw new ParseError("Daletl root must be array"); throw new ParseError("Daletl root must be array");
} }
return new Root(root.map(parseTag)); return new RootClass(root.map(parseTag));
} }
export class Root { export class RootClass {
root: Tag[]; root: CommonTag[];
constructor(root: Tag[]) { constructor(root: CommonTag[]) {
this.root = root; this.root = root;
} }
get raw(): RootRaw { get raw(): Root {
return this.root.map((t) => t.raw); return this.root.map((t) => t.raw);
} }

View file

@ -1,8 +1,8 @@
import { parseBody } from "./main"; import { parseBody } from "./main";
import El from "./tags/el"; import El from "./tags/el";
import Heading from "./tags/heading"; import Heading from "./tags/heading";
import { RawTagAsArray } from "./types";
import { z } from "zod"; import { z } from "zod";
import { Tag } from "./types";
const textOrTag = z.custom((b) => b !== null); const textOrTag = z.custom((b) => b !== null);
const text = z.string(); const text = z.string();
@ -16,15 +16,15 @@ export { TagNormalizers };
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
function n(body: z.ZodTypeAny, argument: z.ZodTypeAny, T: any) { function n(body: z.ZodTypeAny, argument: z.ZodTypeAny, T: any) {
return (tag: RawTagAsArray) => { return (tag: Tag) => {
const parsedBody = parseBody(tag[1]); const parsedBody = parseBody(tag.body);
z.tuple([z.number().int(), body, argument]).parse([ z.object({ id: z.number().int(), body, argument }).parse({
tag[0], id: tag.id,
parsedBody, body: parsedBody,
tag[2], argument,
]); });
return new T(parsedBody, tag[2]); return new T(parsedBody, argument);
}; };
} }

View file

@ -1,13 +1,13 @@
import { bodyToRaw, chtml } from "../../utils"; import { bodyToRaw, chtml } from "../../utils";
import { RawTag, Tag } from "../types"; import { CommonTag, Tag } from "../types";
export default class El extends Tag { export default class El extends CommonTag {
constructor(body: string | Tag[]) { constructor(body: CommonTag[]) {
super(0, body, null); super(0, body, null);
} }
get raw(): RawTag { get raw(): Tag {
return bodyToRaw(this.body)!; return { id: this.id, body: bodyToRaw(this.body), argument: null };
} }
toHtml(classes?: boolean): string { toHtml(classes?: boolean): string {

View file

@ -1,8 +1,8 @@
import { chtml } from "../../utils"; import { chtml } from "../../utils";
import { Tag } from "../types"; import { CommonTag } from "../types";
export default class Heading extends Tag { export default class Heading extends CommonTag {
constructor(body: string, argument?: number | null) { constructor(body: CommonTag[], argument?: number | null) {
super(1, body, argument || null); super(1, body, argument || null);
} }

View file

@ -1,13 +1,15 @@
import { encode } from "@msgpack/msgpack"; import { encodeTag } from "../daletpack";
import { bodyToRaw } from "../utils"; import { bodyToRaw } from "../utils";
export type RawBody = string | null | RawTag[] | RawTag; export type Root = Tag[];
export type RawId = number; export type Argument = string | number | null;
export type RawArgument = number | string | null; export type Body = Tag[] | null;
export type RawTagAsArray = [RawId, RawBody, RawArgument] | [RawId, RawBody];
export type RawTag = RawTagAsArray | number | string | RawTag[]; export interface Tag {
export type RootRaw = RawTag[]; id: number;
body: Body;
argument: Argument;
}
export class ParseError extends Error { export class ParseError extends Error {
constructor(message: string = "Parse error") { constructor(message: string = "Parse error") {
@ -16,27 +18,24 @@ export class ParseError extends Error {
} }
} }
export abstract class Tag { export abstract class CommonTag {
id: number; id: number;
body: Body; body: CommonBody;
argument: Argument; argument: Argument;
constructor(id: number, body: Body, argument: Argument) { constructor(id: number, body: CommonBody, argument: Argument) {
this.id = id; this.id = id;
this.body = body; this.body = body;
this.argument = argument; this.argument = argument;
} }
get raw(): RawTag { get raw(): Tag {
if (this.argument == null) { return {
if (this.body == null) { id: this.id,
return this.id; body: bodyToRaw(this.body),
} argument: this.argument,
};
return [this.id, bodyToRaw(this.body)];
}
return [this.id, bodyToRaw(this.body), this.argument];
} }
encode(): Uint8Array { encode(): Uint8Array {
return encode(this.raw); return encodeTag(this.raw);
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
toHtml(classes?: boolean): string { toHtml(classes?: boolean): string {
@ -44,7 +43,6 @@ export abstract class Tag {
} }
} }
export type Body = string | Tag[] | null; export type CommonBody = CommonTag[] | null;
export type Argument = RawArgument;
export type TagNormalizer = (tag: RawTagAsArray) => Tag; export type TagNormalizer = (tag: Tag) => CommonTag;

View file

@ -0,0 +1,18 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { Root, Tag } from "./daletl/types";
export function encodeTag(tag: Tag): Uint8Array {
throw new Error("encodeTag is not implemented");
}
export function encode(root: Root): Uint8Array {
throw new Error("encode is not implemented");
}
export function decodeTag(data: Uint8Array): Tag {
throw new Error("decodeTag is not implemented");
}
export function decode(data: Uint8Array): Root {
throw new Error("decode is not implemented");
}

View file

@ -1,12 +0,0 @@
import { parse, Root, El, Heading } from "./lib";
const data = new Root([
new El("I am Element"),
new Heading("I am heading", 1),
]).encode();
const root = parse(data);
console.log(root.raw, "\n");
console.log(root.toHtml());
console.log(root.toHtml(false));

View file

@ -1,5 +1,5 @@
import El from "./daletl/tags/el"; import El from "./daletl/tags/el";
import Heading from "./daletl/tags/heading"; import Heading from "./daletl/tags/heading";
export { parse, Root } from "./daletl/main"; export { parse, RootClass } from "./daletl/main";
export { El, Heading }; export { El, Heading };

View file

@ -1,20 +1,14 @@
import { Body, RawBody, RawTag, Tag } from "./daletl/types"; import { Body, CommonBody, CommonTag, Tag } from "./daletl/types";
export function bodyToRaw(body: Body): RawBody {
if (typeof body === "string") {
return body;
}
if (Array.isArray(body)) {
if (Array.isArray(body[0])) {
return body.map(getRaw);
}
}
export function bodyToRaw(body: CommonBody): Body {
if (body === null) {
return null; return null;
}
return body.map((t) => t.raw);
} }
export function bodyToHtml(body: Body): string { export function bodyToHtml(body: CommonBody): string {
if (typeof body === "string") { if (typeof body === "string") {
return body; return body;
} }
@ -26,7 +20,7 @@ export function bodyToHtml(body: Body): string {
return ""; return "";
} }
export function getRaw(t: Tag): RawTag { export function getRaw(t: CommonTag): Tag {
return t.raw; return t.raw;
} }
@ -38,14 +32,14 @@ export function chtml(
tag: string, tag: string,
classNames: string, classNames: string,
classes: boolean = true, classes: boolean = true,
body?: Body, body?: CommonBody,
props?: Props props?: Props
) { ) {
const classProp = classes ? { class: classNames } : {}; const classProp = classes ? { class: classNames } : {};
return html(tag, body, { ...props, ...classProp }); return html(tag, body, { ...props, ...classProp });
} }
function html(tag: string, body?: Body, props?: Props) { function html(tag: string, body?: CommonBody, props?: Props) {
const pr = Object.entries(props || {}) const pr = Object.entries(props || {})
.map(([key, value]) => `${key}="${value}"`) .map(([key, value]) => `${key}="${value}"`)
.join(" "); .join(" ");

View file

@ -1,85 +1,39 @@
# Daletl specification for Dalet v1.0-preview # Daletl specification for Dalet v1.0-preview
## Data format ## Daletl
Daletl is data representation for Dalet interfaces. [DaletPack](./daletpack.md) serializes/deserializes into Daletl.
Daletl must be serialized as [DaletPack](./daletpack.md). All data transfer between server and client is done in this format. Daletl must be serialized as [DaletPack](./daletpack.md). All data transfer between server and client is done in this format.
### Root ### Root
Daletl root is array of tags. For convenience, we will use the json5 representation of the data. Daletl root is array of tags. For convenience, we will use the typescript notation.
```json5 ```typescript
[] type Root = Tag[];
``` ```
### Tag ### Tag
All tags specification is in [Tags](./tags.md). All tags specification is in [Tags](./tags.md).
Each tag may be one of four types: ```typescript
interface Tag {
#### Data Representation id: number;
body: Tag[] | null;
##### As array of 1-3 elements argument: string | number | null;
}
1. Tag id
2. Tag body (optional if argument is null)
3. Tag argument (optional)
Tag id is integer number.
Body can be only a string, null, array of tags or tag (equals to array of tags with 1 tag).
Argument can be number or string.
###### Heading example
```json5
[1, "This is heading", 1]
``` ```
###### Unordered list example ### Example
```json5 ```typescript
[ const root: Root = [
4, {
[ id: 1,
[0, "Item 1"], body: "I am Heading with level 1",
[0, "Item 2"], argument: 1,
], },
] ];
```
##### As string
String becomes element tag.
```json5
"Element"
```
equals to
```json5
[0, "Element"]
```
##### As array of tags
If array not started with a number. The implication is that this turns into an “element” tag
```json5
["Element", [1, "Heading"]]
```
equals to
```json5
[
0,
[
[0, "Element"],
[1, "Heading"],
],
]
``` ```

View file

@ -19,12 +19,6 @@ el: { h[1]: I am first level heading }
Element also used if no tag is specified. Element also used if no tag is specified.
``` ```
**Daletl example (json5 representation)**:
```json5
"I am Element"
```
## 1. Heading ## 1. Heading
| Property | Description | | Property | Description |
@ -46,12 +40,6 @@ h[1]: Dalet
h[3]: Low level h[3]: Low level
``` ```
**Daletl example (json5 representation)**:
```json5
[1, "Dalet", 1]
```
## 2. Paragraph ## 2. Paragraph
| Property | Description | | Property | Description |
@ -69,12 +57,6 @@ Paragraph is used for text formatting.
p: This is a paragraph p: This is a paragraph
``` ```
**Daletl example (json5 representation)**:
```json5
[2, "This is a paragraph"]
```
## 3. Line break ## 3. Line break
| Property | Description | | Property | Description |
@ -92,12 +74,6 @@ Line break is used to insert a line break into the text.
br br
``` ```
**Daletl example (json5 representation)**:
```json5
3
```
## 4. Unordered list ## 4. Unordered list
| Property | Description | | Property | Description |
@ -118,12 +94,6 @@ ul: {
} }
``` ```
**Daletl example (json5 representation)**:
```json5
[4, ["Item 1", "Item 2"]]
```
## 5. Ordered list ## 5. Ordered list
| Property | Description | | Property | Description |
@ -145,12 +115,6 @@ ol: {
} }
``` ```
**Daletl example (json5 representation)**:
```json5
[5, ["Item", "Item", "Item"]]
```
## 6. Row ## 6. Row
| Property | Description | | Property | Description |
@ -181,13 +145,6 @@ Argument converts to numbers in daletl.
start -> 0 start -> 0
end -> 1 end -> 1
**Daletl example (json5 representation)**:
```json5
[6, ["Left", "Right"]]
[6, ["Left", "Right"], 0]
```
## 7. Link ## 7. Link
| Property | Description | | Property | Description |
@ -205,12 +162,6 @@ Link to other sites. On click the link opens in new tab.
link[https://example.com]: I am Link link[https://example.com]: I am Link
``` ```
**Daletl example (json5 representation)**:
```json5
[7, "I am Link", "https://example.com"]
```
## 8. Navlink ## 8. Navlink
| Property | Description | | Property | Description |
@ -228,12 +179,6 @@ Link to the same site. On click the link opens in current tab.
navlink[/specification]: I am Navlink navlink[/specification]: I am Navlink
``` ```
**Daletl example (json5 representation)**:
```json5
[8, "I am Navlink", "/specification"]
```
## 9. Button ## 9. Button
| Property | Description | | Property | Description |
@ -251,12 +196,6 @@ Same as link, but with button style.
btn[https://example.com]: I am Button btn[https://example.com]: I am Button
``` ```
**Daletl example (json5 representation)**:
```json5
[9, "I am Button", "https://example.com"]
```
## 10. NavButton ## 10. NavButton
| Property | Description | | Property | Description |
@ -274,12 +213,6 @@ Same as navlink, but with button style.
navbtn[https://example.com]: I am NavButton navbtn[https://example.com]: I am NavButton
``` ```
**Daletl example (json5 representation)**:
```json5
[10, "I am NavButton", "https://example.com"]
```
## 11. Image ## 11. Image
| Property | Description | | Property | Description |
@ -297,12 +230,6 @@ Displays an image.
img[/dalet.png] img[/dalet.png]
``` ```
**Daletl example (json5 representation)**:
```json5
[11, null, "/dalet.png"]
```
## 12. Table ## 12. Table
| Property | Description | | Property | Description |
@ -323,30 +250,6 @@ table: {
} }
``` ```
**Daletl example (json5 representation)**:
```json5
[
12,
[
[
13,
[
[0, "Name"],
[0, "Age"],
],
],
[
13,
[
[0, "Elon"],
[0, "53"],
],
],
],
]
```
## 13. Table Column ## 13. Table Column
| Property | Description | | Property | Description |
@ -367,18 +270,6 @@ tcol: {
} }
``` ```
**Daletl example (json5 representation)**:
```json5
[
13,
[
[0, "Name"],
[0, "Age"],
],
]
```
## 14. Table Primary Column ## 14. Table Primary Column
| Property | Description | | Property | Description |
@ -399,18 +290,6 @@ tpcol: {
} }
``` ```
**Daletl example (json5 representation)**:
```json5
[
14,
[
[0, "Name"],
[0, "Age"],
],
]
```
## 15. Horizontal rule ## 15. Horizontal rule
| Property | Description | | Property | Description |
@ -428,12 +307,6 @@ Creates a horizontal rule.
hr hr
``` ```
**Daletl example (json5 representation)**:
```json5
15
```
## 16. Bold ## 16. Bold
| Property | Description | | Property | Description |
@ -451,12 +324,6 @@ Creates **bold** text.
b: I am Bold b: I am Bold
``` ```
**Daletl example (json5 representation)**:
```json5
[16, "I am Bold"]
```
## 17. Italic ## 17. Italic
| Property | Description | | Property | Description |
@ -474,12 +341,6 @@ Creates _italic_ text.
i: I am Italic i: I am Italic
``` ```
**Daletl example (json5 representation)**:
```json5
[17, "I am Italic"]
```
## 18. Blockquote ## 18. Blockquote
| Property | Description | | Property | Description |
@ -497,12 +358,6 @@ Creates a blockquote.
bq: I am Blockquote bq: I am Blockquote
``` ```
**Daletl example (json5 representation)**:
```json5
[18, "I am Blockquote"]
```
## 19. Footnote Link ## 19. Footnote Link
| Property | Description | | Property | Description |
@ -520,12 +375,6 @@ Link to footnote.
footlnk[1] footlnk[1]
``` ```
**Daletl example (json5 representation)**:
```json5
[19, null, 1]
```
## 20. Footnote ## 20. Footnote
| Property | Description | | Property | Description |
@ -543,12 +392,6 @@ Creates footnote.
footn[1]: I am Footnote footn[1]: I am Footnote
``` ```
**Daletl example (json5 representation)**:
```json5
[20, "I am Footnote", 1]
```
## 21. Anchor ## 21. Anchor
| Property | Description | | Property | Description |
@ -566,12 +409,6 @@ Creates anchor. Like `<a href="#argument"></a>` in HTML.
a[0] a[0]
``` ```
**Daletl example (json5 representation)**:
```json5
[21, null, 0]
```
## 22. Strikethrough ## 22. Strikethrough
| Property | Description | | Property | Description |
@ -589,12 +426,6 @@ Creates ~~strikethrough~~ text.
s: I am Strikethrough s: I am Strikethrough
``` ```
**Daletl example (json5 representation)**:
```json5
[22, "I am Strikethrough"]
```
## 23. Superscript ## 23. Superscript
| Property | Description | | Property | Description |
@ -612,12 +443,6 @@ Creates ^superscript^ text.
sup: I am Superscript sup: I am Superscript
``` ```
**Daletl example (json5 representation)**:
```json5
[23, "I am Superscript"]
```
## 24. Subscript ## 24. Subscript
| Property | Description | | Property | Description |
@ -635,12 +460,6 @@ Creates ~subscript~ text.
sub: I am Subscript sub: I am Subscript
``` ```
**Daletl example (json5 representation)**:
```json5
[24, "I am Subscript"]
```
## 25. Disclosure ## 25. Disclosure
| Property | Description | | Property | Description |
@ -658,12 +477,6 @@ Creates disclosure element.
disc[Click to expand]: I am Disclosure disc[Click to expand]: I am Disclosure
``` ```
**Daletl example (json5 representation)**:
```json5
[25, "I am Disclosure", "Click to expand"]
```
## 26. Block ## 26. Block
| Property | Description | | Property | Description |
@ -686,12 +499,6 @@ Argument converts to numbers in daletl.
start -> 0 start -> 0
end -> 1 end -> 1
**Daletl example (json5 representation)**:
```json5
[26, "I am Block", 0]
```
## 27. Carousel ## 27. Carousel
| Property | Description | | Property | Description |
@ -711,9 +518,3 @@ carousel: {
Example 2, Example 2,
} }
``` ```
**Daletl example (json5 representation)**:
```json5
[27, ["Example 1", "Example 2"]]
```