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"
},
"dependencies": {
"@msgpack/msgpack": "3.0.0-beta2",
"zod": "^3.23.8"
}
}

View file

@ -8,9 +8,6 @@ importers:
.:
dependencies:
'@msgpack/msgpack':
specifier: 3.0.0-beta2
version: 3.0.0-beta2
zod:
specifier: ^3.23.8
version: 3.23.8
@ -67,10 +64,6 @@ packages:
resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==}
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':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@ -575,8 +568,6 @@ snapshots:
'@humanwhocodes/retry@0.3.0': {}
'@msgpack/msgpack@3.0.0-beta2': {}
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5

View file

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

View file

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

View file

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

View file

@ -1,8 +1,8 @@
import { chtml } from "../../utils";
import { Tag } from "../types";
import { CommonTag } from "../types";
export default class Heading extends Tag {
constructor(body: string, argument?: number | null) {
export default class Heading extends CommonTag {
constructor(body: CommonTag[], argument?: number | 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";
export type RawBody = string | null | RawTag[] | RawTag;
export type RawId = number;
export type RawArgument = number | string | null;
export type RawTagAsArray = [RawId, RawBody, RawArgument] | [RawId, RawBody];
export type Root = Tag[];
export type Argument = string | number | null;
export type Body = Tag[] | null;
export type RawTag = RawTagAsArray | number | string | RawTag[];
export type RootRaw = RawTag[];
export interface Tag {
id: number;
body: Body;
argument: Argument;
}
export class ParseError extends 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;
body: Body;
body: CommonBody;
argument: Argument;
constructor(id: number, body: Body, argument: Argument) {
constructor(id: number, body: CommonBody, argument: Argument) {
this.id = id;
this.body = body;
this.argument = argument;
}
get raw(): RawTag {
if (this.argument == null) {
if (this.body == null) {
return this.id;
}
return [this.id, bodyToRaw(this.body)];
}
return [this.id, bodyToRaw(this.body), this.argument];
get raw(): Tag {
return {
id: this.id,
body: bodyToRaw(this.body),
argument: this.argument,
};
}
encode(): Uint8Array {
return encode(this.raw);
return encodeTag(this.raw);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
toHtml(classes?: boolean): string {
@ -44,7 +43,6 @@ export abstract class Tag {
}
}
export type Body = string | Tag[] | null;
export type Argument = RawArgument;
export type CommonBody = CommonTag[] | null;
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 Heading from "./daletl/tags/heading";
export { parse, Root } from "./daletl/main";
export { parse, RootClass } from "./daletl/main";
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;
export function bodyToRaw(body: CommonBody): Body {
if (body === null) {
return null;
}
if (Array.isArray(body)) {
if (Array.isArray(body[0])) {
return body.map(getRaw);
}
}
return null;
return body.map((t) => t.raw);
}
export function bodyToHtml(body: Body): string {
export function bodyToHtml(body: CommonBody): string {
if (typeof body === "string") {
return body;
}
@ -26,7 +20,7 @@ export function bodyToHtml(body: Body): string {
return "";
}
export function getRaw(t: Tag): RawTag {
export function getRaw(t: CommonTag): Tag {
return t.raw;
}
@ -38,14 +32,14 @@ export function chtml(
tag: string,
classNames: string,
classes: boolean = true,
body?: Body,
body?: CommonBody,
props?: Props
) {
const classProp = classes ? { class: classNames } : {};
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 || {})
.map(([key, value]) => `${key}="${value}"`)
.join(" ");

View file

@ -1,85 +1,39 @@
# 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.
### 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
All tags specification is in [Tags](./tags.md).
Each tag may be one of four types:
#### Data Representation
##### As array of 1-3 elements
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]
```typescript
interface Tag {
id: number;
body: Tag[] | null;
argument: string | number | null;
}
```
###### Unordered list example
### Example
```json5
[
4,
[
[0, "Item 1"],
[0, "Item 2"],
],
]
```
##### 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"],
],
]
```typescript
const root: Root = [
{
id: 1,
body: "I am Heading with level 1",
argument: 1,
},
];
```

View file

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