From 9d8220f726877d18656072518ef7f73b01ef7c8b Mon Sep 17 00:00:00 2001 From: Artemy Egorov Date: Sun, 28 Jul 2024 20:21:22 +0300 Subject: [PATCH] doc: update daletl specification refactor all typescript code --- libs/typescript/package.json | 1 - libs/typescript/pnpm-lock.yaml | 9 - libs/typescript/src/daletl/main.ts | 64 ++----- libs/typescript/src/daletl/normalizers.ts | 18 +- libs/typescript/src/daletl/tags/el.ts | 10 +- libs/typescript/src/daletl/tags/heading.ts | 6 +- libs/typescript/src/daletl/types.ts | 44 +++-- libs/typescript/src/daletpack.ts | 18 ++ libs/typescript/src/index.ts | 12 -- libs/typescript/src/lib.ts | 2 +- libs/typescript/src/utils.ts | 24 +-- specification/daletl.md | 88 +++------ specification/tags.md | 199 --------------------- 13 files changed, 99 insertions(+), 396 deletions(-) create mode 100644 libs/typescript/src/daletpack.ts diff --git a/libs/typescript/package.json b/libs/typescript/package.json index 3826e00..4c079b9 100644 --- a/libs/typescript/package.json +++ b/libs/typescript/package.json @@ -39,7 +39,6 @@ "typescript-eslint": "^7.17.0" }, "dependencies": { - "@msgpack/msgpack": "3.0.0-beta2", "zod": "^3.23.8" } } diff --git a/libs/typescript/pnpm-lock.yaml b/libs/typescript/pnpm-lock.yaml index eef30c2..0004d45 100644 --- a/libs/typescript/pnpm-lock.yaml +++ b/libs/typescript/pnpm-lock.yaml @@ -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 diff --git a/libs/typescript/src/daletl/main.ts b/libs/typescript/src/daletl/main.ts index fefaee9..6a41b04 100644 --- a/libs/typescript/src/daletl/main.ts +++ b/libs/typescript/src/daletl/main.ts @@ -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); } diff --git a/libs/typescript/src/daletl/normalizers.ts b/libs/typescript/src/daletl/normalizers.ts index 007216f..1a1eb33 100644 --- a/libs/typescript/src/daletl/normalizers.ts +++ b/libs/typescript/src/daletl/normalizers.ts @@ -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); }; } diff --git a/libs/typescript/src/daletl/tags/el.ts b/libs/typescript/src/daletl/tags/el.ts index 726f2f5..ffc8423 100644 --- a/libs/typescript/src/daletl/tags/el.ts +++ b/libs/typescript/src/daletl/tags/el.ts @@ -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 { diff --git a/libs/typescript/src/daletl/tags/heading.ts b/libs/typescript/src/daletl/tags/heading.ts index fea1b21..e7ba515 100644 --- a/libs/typescript/src/daletl/tags/heading.ts +++ b/libs/typescript/src/daletl/tags/heading.ts @@ -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); } diff --git a/libs/typescript/src/daletl/types.ts b/libs/typescript/src/daletl/types.ts index a5baeaa..f568570 100644 --- a/libs/typescript/src/daletl/types.ts +++ b/libs/typescript/src/daletl/types.ts @@ -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; diff --git a/libs/typescript/src/daletpack.ts b/libs/typescript/src/daletpack.ts new file mode 100644 index 0000000..ff634e7 --- /dev/null +++ b/libs/typescript/src/daletpack.ts @@ -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"); +} diff --git a/libs/typescript/src/index.ts b/libs/typescript/src/index.ts index f0e4eab..e69de29 100644 --- a/libs/typescript/src/index.ts +++ b/libs/typescript/src/index.ts @@ -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)); diff --git a/libs/typescript/src/lib.ts b/libs/typescript/src/lib.ts index 304e9da..a32f5c6 100644 --- a/libs/typescript/src/lib.ts +++ b/libs/typescript/src/lib.ts @@ -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 }; diff --git a/libs/typescript/src/utils.ts b/libs/typescript/src/utils.ts index fb0c0b3..9c9408c 100644 --- a/libs/typescript/src/utils.ts +++ b/libs/typescript/src/utils.ts @@ -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(" "); diff --git a/specification/daletl.md b/specification/daletl.md index 83bd4ac..220944b 100644 --- a/specification/daletl.md +++ b/specification/daletl.md @@ -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, + }, +]; ``` diff --git a/specification/tags.md b/specification/tags.md index 1ab8473..6c63982 100644 --- a/specification/tags.md +++ b/specification/tags.md @@ -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 `` 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"]] -```