@mark1russell7/render-biblo 0.0.0-stage → 0.2.0
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/LICENSE +21 -0
- package/README.md +43 -2
- package/build/index.d.ts +209 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +549 -0
- package/build/index.js.map +1 -0
- package/package.json +66 -3
- package/src/class.ts +64 -0
- package/src/index.ts +9 -0
- package/src/instance.ts +23 -0
- package/src/nodeops.ts +31 -0
- package/src/registry.ts +515 -0
- package/src/traits.ts +99 -0
package/package.json
CHANGED
|
@@ -1,6 +1,69 @@
|
|
|
1
1
|
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/package",
|
|
2
3
|
"name": "@mark1russell7/render-biblo",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
4
|
+
"version": "0.2.0",
|
|
5
|
+
"description": "The class registry and the instance store of render, and the class-level NodeOps.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"classes",
|
|
8
|
+
"templates",
|
|
9
|
+
"traits",
|
|
10
|
+
"prototype",
|
|
11
|
+
"reactive",
|
|
12
|
+
"typescript"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://mark1russell7.github.io/render/",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/mark1russell7/render/issues"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/mark1russell7/render.git",
|
|
21
|
+
"directory": "packages/biblo"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"author": "Mark Russell (https://github.com/mark1russell7)",
|
|
25
|
+
"type": "module",
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"main": "./build/index.js",
|
|
28
|
+
"types": "./build/index.d.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./build/index.d.ts",
|
|
32
|
+
"default": "./build/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./package.json": "./package.json"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"build",
|
|
38
|
+
"src",
|
|
39
|
+
"!src/**/*.test.ts",
|
|
40
|
+
"!src/**/*.test.tsx",
|
|
41
|
+
"!src/main.tsx",
|
|
42
|
+
"!src/page.css"
|
|
43
|
+
],
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=22.12"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@mark1russell7/coeval": "^0.4.0",
|
|
52
|
+
"@mark1russell7/render-dsl": "0.2.0",
|
|
53
|
+
"@mark1russell7/render-node": "0.2.0",
|
|
54
|
+
"@mark1russell7/render-optional": "0.1.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@mark1russell7/cue": "github:mark1russell7/cue",
|
|
58
|
+
"@types/node": "^26.6.4",
|
|
59
|
+
"tsdown": "^0.23.1",
|
|
60
|
+
"typescript": "^7.0.2",
|
|
61
|
+
"vitest": "^5.0.3"
|
|
62
|
+
},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"build": "tsdown",
|
|
65
|
+
"test": "vitest run",
|
|
66
|
+
"test:watch": "vitest",
|
|
67
|
+
"typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.test.json"
|
|
68
|
+
}
|
|
6
69
|
}
|
package/src/class.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Expr } from "@mark1russell7/render-dsl";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A cell template of a class. It has an expression (a coeval document), and optionally a type: the name of
|
|
5
|
+
* another class. A typed cell holds an instance of that class, thus the class level knows the paths through its children.
|
|
6
|
+
*/
|
|
7
|
+
export type CellDef = {
|
|
8
|
+
readonly expr: Expr;
|
|
9
|
+
/** The class of the child instance that this cell holds. */
|
|
10
|
+
readonly type?: string | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* The bindings of a typed cell: expressions that replace cells of the child.
|
|
13
|
+
* They are in the scope of the child. For example, `{ value: ref("parent", "key") }` binds the cell `value`
|
|
14
|
+
* of the child to the cell `key` of this instance.
|
|
15
|
+
*/
|
|
16
|
+
readonly bindings?: Readonly<Record<string, Expr>> | undefined;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A class is a template. Instances share it, and nothing copies it.
|
|
21
|
+
*
|
|
22
|
+
* - `cells`: the reactive structure, which is the data that each instance holds.
|
|
23
|
+
* - `methods`: the behavior. The extends chain resolves them, and the most specific method wins.
|
|
24
|
+
* - `extends`: the parent class. Cells and methods both resolve through it.
|
|
25
|
+
*
|
|
26
|
+
* The values of `methods` are opaque. Each layer reads the method names that it knows, for example
|
|
27
|
+
* `render` for splay and `splash` for the node engine. The class `Top` gives the defaults. An expression method is
|
|
28
|
+
* a coeval document, thus a class is data from end to end.
|
|
29
|
+
*/
|
|
30
|
+
export type ComponentClass = {
|
|
31
|
+
readonly name: string;
|
|
32
|
+
readonly cells: Readonly<Record<string, CellDef>>;
|
|
33
|
+
readonly extends?: string | undefined;
|
|
34
|
+
readonly methods?: Readonly<Record<string, unknown>> | undefined;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** This function makes a class. */
|
|
38
|
+
export const componentClass = (
|
|
39
|
+
name: string,
|
|
40
|
+
cells: Readonly<Record<string, CellDef>>,
|
|
41
|
+
ext?: string,
|
|
42
|
+
methods?: Readonly<Record<string, unknown>>,
|
|
43
|
+
): ComponentClass => ({
|
|
44
|
+
name,
|
|
45
|
+
cells,
|
|
46
|
+
...(ext !== undefined ? { extends: ext } : {}),
|
|
47
|
+
...(methods !== undefined ? { methods } : {}),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* This function gives a copy of a class with more cells and methods. The copy keeps the name.
|
|
52
|
+
* An override with the name of an existing cell or method replaces it.
|
|
53
|
+
*/
|
|
54
|
+
export const extendClass = (
|
|
55
|
+
base: ComponentClass,
|
|
56
|
+
overrides: {
|
|
57
|
+
readonly cells?: Readonly<Record<string, CellDef>>;
|
|
58
|
+
readonly methods?: Readonly<Record<string, unknown>>;
|
|
59
|
+
},
|
|
60
|
+
): ComponentClass => ({
|
|
61
|
+
...base,
|
|
62
|
+
cells: { ...base.cells, ...overrides.cells },
|
|
63
|
+
methods: { ...base.methods, ...overrides.methods },
|
|
64
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { type CellDef, type ComponentClass, componentClass, extendClass } from "./class.ts";
|
|
2
|
+
export type { InstanceId, Scope, Instance } from "./instance.ts";
|
|
3
|
+
export {
|
|
4
|
+
type Biblo, biblo, forkBiblo, registerClass, registerClasses, resolveCells, resolveMethods,
|
|
5
|
+
instantiate, destroyInstance, moveChild, updateClass, ownerOf, renameClass,
|
|
6
|
+
registerTrait, unregisterTrait, resolveTraits, classFingerprint, primeOf,
|
|
7
|
+
} from "./registry.ts";
|
|
8
|
+
export { type Trait, type TraitResolution, requirementAtoms } from "./traits.ts";
|
|
9
|
+
export { classNodeOps } from "./nodeops.ts";
|
package/src/instance.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** The identity of an instance. It is also the ID of the root node of the instance. */
|
|
2
|
+
export type InstanceId = string;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The scope of an instance: the names that its expressions can use.
|
|
6
|
+
* `ref("self", ...)` reads this instance, and `ref("parent", ...)` reads the parent instance.
|
|
7
|
+
*/
|
|
8
|
+
export type Scope = {
|
|
9
|
+
readonly self: InstanceId;
|
|
10
|
+
readonly parent: InstanceId | undefined;
|
|
11
|
+
/** The child instances, in order. Splay renders them, and dehydrate reads them. */
|
|
12
|
+
readonly children: readonly InstanceId[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* An instance is small: an ID, a class name and a scope. It copies no data of its class.
|
|
17
|
+
* The values of its cells are in the node store, in the slots of its root node.
|
|
18
|
+
*/
|
|
19
|
+
export type Instance = {
|
|
20
|
+
readonly id: InstanceId;
|
|
21
|
+
readonly classRef: string;
|
|
22
|
+
readonly scope: Scope;
|
|
23
|
+
};
|
package/src/nodeops.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { NodeOps, SplashFn, FlowFn, DerefFn, TargetsFn, Node, NodeStore } from "@mark1russell7/render-node";
|
|
2
|
+
import { defaultOps } from "@mark1russell7/render-node";
|
|
3
|
+
import type { Biblo } from "./registry.ts";
|
|
4
|
+
import { ownerOf, resolveMethods } from "./registry.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* This function makes the class-level reactive semantics (the defaults of `Top`).
|
|
8
|
+
*
|
|
9
|
+
* The ops of the result use the `splash`, `flow`, `deref` and `targets` methods of the class that owns each node.
|
|
10
|
+
* The extends chain resolves them, thus the defaults of `Top` apply to all classes, and a class can
|
|
11
|
+
* change them. A node without an owner, or a class without the method, uses `fallback`.
|
|
12
|
+
*
|
|
13
|
+
* The node layer knows nothing of biblo. This factory is how the class layer gives semantics to the
|
|
14
|
+
* node layer without a dependency in the wrong direction. Give the result to `nodeStore`.
|
|
15
|
+
*/
|
|
16
|
+
export const classNodeOps = (b: Biblo, fallback: NodeOps = defaultOps): NodeOps => {
|
|
17
|
+
type Methods = { readonly splash: SplashFn; readonly flow: FlowFn; readonly deref: DerefFn; readonly targets: TargetsFn };
|
|
18
|
+
const methodFor = <K extends keyof Methods>(n: Node, store: NodeStore, name: K): Methods[K] => {
|
|
19
|
+
const owner = ownerOf(b, store, n.id);
|
|
20
|
+
const inst = owner === undefined ? undefined : b.instances.get(owner);
|
|
21
|
+
const method = inst ? resolveMethods(b, inst.classRef)[name] : undefined;
|
|
22
|
+
return typeof method === "function" ? (method as Methods[K]) : fallback[name];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
splash: (value, target, store) => methodFor(target, store, "splash")(value, target, store),
|
|
27
|
+
flow: (target, store) => methodFor(target, store, "flow")(target, store),
|
|
28
|
+
deref: (root, path, store) => methodFor(root, store, "deref")(root, path, store),
|
|
29
|
+
targets: (root, path, store) => methodFor(root, store, "targets")(root, path, store),
|
|
30
|
+
};
|
|
31
|
+
};
|