@jarenjs/view 0.34.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/README.md +181 -0
- package/dist/types/dom.d.ts +226 -0
- package/dist/types/helpers/index.d.ts +15 -0
- package/dist/types/helpers/memo.d.ts +49 -0
- package/dist/types/helpers/metrics.d.ts +31 -0
- package/dist/types/helpers/svg.d.ts +164 -0
- package/dist/types/helpers/theme.d.ts +47 -0
- package/dist/types/helpers/url.d.ts +71 -0
- package/dist/types/html.d.ts +80 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/safe.d.ts +121 -0
- package/dist/types/vnode.d.ts +113 -0
- package/docs/VIEW-FORMAT.md +538 -0
- package/package.json +63 -0
- package/schemas/jaren-vnode-safe.schema.json +92 -0
- package/schemas/jaren-vnode.schema.json +127 -0
- package/src/dom.js +1173 -0
- package/src/helpers/index.js +35 -0
- package/src/helpers/memo.js +61 -0
- package/src/helpers/metrics.js +116 -0
- package/src/helpers/svg.js +233 -0
- package/src/helpers/theme.js +75 -0
- package/src/helpers/url.js +154 -0
- package/src/html.js +197 -0
- package/src/index.js +34 -0
- package/src/safe.js +278 -0
- package/src/vnode.js +170 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://jarenjs.dev/schemas/jaren-vnode-safe/0.1",
|
|
4
|
+
"title": "Jaren safe vnode document",
|
|
5
|
+
"description": "The SAFE vnode profile: the vnode grammar with the structural constraints a schema can express for an UNTRUSTED document. Tag and property names must be bare identifiers (closing tag- and attribute-name injection), property names may not begin with `on` (no inline handlers) and may not be a scripting sink, and a set of document-embedding tags is forbidden. This is a defense-in-depth pre-filter, NOT the authoritative safety boundary: the runtime safe render policy (`createSafePolicy`, applied by `{ safe: true }`) restricts tags to a full allow-list, sanitizes URL attributes and inline styles, and strips event/widget bindings — things a static schema cannot do. Validate with this AND render in safe mode. See safe.js and VIEW-FORMAT.md.",
|
|
6
|
+
"$ref": "#/$defs/vnode",
|
|
7
|
+
"$defs": {
|
|
8
|
+
"vnode": {
|
|
9
|
+
"anyOf": [
|
|
10
|
+
{ "type": ["string", "number", "boolean", "null"] },
|
|
11
|
+
{ "$ref": "#/$defs/element" },
|
|
12
|
+
{ "$ref": "#/$defs/list" }
|
|
13
|
+
],
|
|
14
|
+
"description": "No widget branch: a widget mounts imperative JavaScript, which an untrusted document must not do, so `jaren-widget` is not a safe-profile node."
|
|
15
|
+
},
|
|
16
|
+
"safeName": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"pattern": "^[A-Za-z][A-Za-z0-9-]*$",
|
|
19
|
+
"description": "A bare identifier: a letter followed by letters, digits or hyphens. No spaces, angle brackets, equals signs, slashes, colons or quotes — the characters every structural-injection payload needs."
|
|
20
|
+
},
|
|
21
|
+
"element": {
|
|
22
|
+
"type": "array",
|
|
23
|
+
"minItems": 1,
|
|
24
|
+
"prefixItems": [
|
|
25
|
+
{
|
|
26
|
+
"allOf": [
|
|
27
|
+
{ "$ref": "#/$defs/safeName" },
|
|
28
|
+
{
|
|
29
|
+
"not": {
|
|
30
|
+
"enum": [
|
|
31
|
+
"script", "style", "iframe", "object", "embed", "base",
|
|
32
|
+
"link", "meta", "noscript", "template", "frame", "frameset",
|
|
33
|
+
"html", "head", "body", "title", "foreignObject", "portal",
|
|
34
|
+
"jaren-widget"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
"description": "Document-structure, script/style carriers and document-embedding elements are forbidden. The runtime policy enforces the positive allow-list; this rejects the highest-risk tags structurally."
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"items": {
|
|
43
|
+
"anyOf": [
|
|
44
|
+
{ "$ref": "#/$defs/props" },
|
|
45
|
+
{ "$ref": "#/$defs/vnode" }
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"list": {
|
|
50
|
+
"type": "array",
|
|
51
|
+
"prefixItems": [
|
|
52
|
+
{
|
|
53
|
+
"allOf": [
|
|
54
|
+
{ "not": { "type": "string" } },
|
|
55
|
+
{ "$ref": "#/$defs/vnode" }
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
"items": { "$ref": "#/$defs/vnode" }
|
|
60
|
+
},
|
|
61
|
+
"props": {
|
|
62
|
+
"type": "object",
|
|
63
|
+
"propertyNames": {
|
|
64
|
+
"allOf": [
|
|
65
|
+
{ "$ref": "#/$defs/safeName" },
|
|
66
|
+
{
|
|
67
|
+
"not": { "pattern": "^[Oo][Nn]" },
|
|
68
|
+
"description": "No property name may begin with `on`: inline event-handler attributes are forbidden, and so is the `on` binding object (an untrusted view must not bind host actions)."
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"not": { "pattern": "^[Ii][Ss]$" },
|
|
72
|
+
"description": "The `is` attribute is forbidden: it upgrades an element to a registered customized built-in when the markup is parsed, running host-registered code."
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"not": {
|
|
76
|
+
"pattern": "^([Ii][Nn][Nn][Ee][Rr]|[Oo][Uu][Tt][Ee][Rr])[Hh][Tt][Mm][Ll]$|^[Ss][Rr][Cc][Dd][Oo][Cc]$|[Dd][Aa][Nn][Gg][Ee][Rr][Oo][Uu][Ss][Ll][Yy]"
|
|
77
|
+
},
|
|
78
|
+
"description": "The HTML-parsing sinks (innerHTML, outerHTML, srcdoc, dangerouslySetInnerHTML) are forbidden by name, in any casing."
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
"properties": {
|
|
83
|
+
"key": { "type": ["string", "number"] },
|
|
84
|
+
"style": { "type": ["string", "object"] }
|
|
85
|
+
},
|
|
86
|
+
"additionalProperties": {
|
|
87
|
+
"type": ["string", "number", "boolean", "null"]
|
|
88
|
+
},
|
|
89
|
+
"description": "A safe props object: identifier names only, no on* bindings, no HTML sinks. URL and style VALUES still need the runtime policy — a schema cannot read a `javascript:` scheme out of an href."
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://jarenjs.dev/schemas/jaren-vnode/0.1",
|
|
4
|
+
"title": "Jaren vnode document",
|
|
5
|
+
"description": "The Jaren view vocabulary: a user interface as a JSON value. Text is a string or number; an element is an array [tag, props?, ...children]; an array whose first item is not a string is a list spliced into its parent; null and booleans render nothing. See docs/VIEW-FORMAT.md for the normative contract.",
|
|
6
|
+
"$ref": "#/$defs/vnode",
|
|
7
|
+
"$defs": {
|
|
8
|
+
"vnode": {
|
|
9
|
+
"anyOf": [
|
|
10
|
+
{ "type": ["string", "number", "boolean", "null"] },
|
|
11
|
+
{ "$ref": "#/$defs/element" },
|
|
12
|
+
{ "$ref": "#/$defs/widget" },
|
|
13
|
+
{ "$ref": "#/$defs/list" }
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
"element": {
|
|
17
|
+
"type": "array",
|
|
18
|
+
"minItems": 1,
|
|
19
|
+
"prefixItems": [
|
|
20
|
+
{
|
|
21
|
+
"type": "string",
|
|
22
|
+
"minLength": 1,
|
|
23
|
+
"not": { "const": "jaren-widget" },
|
|
24
|
+
"description": "The tag name. The reserved widget tag is excluded: a jaren-widget node must satisfy the strict widget shape and can never validate through this generic branch."
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"items": {
|
|
28
|
+
"anyOf": [
|
|
29
|
+
{ "$ref": "#/$defs/props" },
|
|
30
|
+
{ "$ref": "#/$defs/vnode" }
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
"description": "An element vnode: [tag, props?, ...children]. When the second item is an object it is the props; every further item is a child."
|
|
34
|
+
},
|
|
35
|
+
"list": {
|
|
36
|
+
"type": "array",
|
|
37
|
+
"prefixItems": [
|
|
38
|
+
{
|
|
39
|
+
"allOf": [
|
|
40
|
+
{ "not": { "type": "string" } },
|
|
41
|
+
{ "$ref": "#/$defs/vnode" }
|
|
42
|
+
],
|
|
43
|
+
"description": "The documented distinction, enforced: an array whose first item IS a string is an element (or a widget), never a list. The first item is still a vnode like every other."
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"items": { "$ref": "#/$defs/vnode" },
|
|
47
|
+
"description": "A child list, spliced into the parent's children in place. Distinguished from an element by its first item not being a string."
|
|
48
|
+
},
|
|
49
|
+
"widget": {
|
|
50
|
+
"type": "array",
|
|
51
|
+
"minItems": 2,
|
|
52
|
+
"maxItems": 2,
|
|
53
|
+
"prefixItems": [
|
|
54
|
+
{
|
|
55
|
+
"const": "jaren-widget",
|
|
56
|
+
"description": "The reserved widget tag."
|
|
57
|
+
},
|
|
58
|
+
{ "$ref": "#/$defs/widgetProps" }
|
|
59
|
+
],
|
|
60
|
+
"description": "A widget vnode: [\"jaren-widget\", props] mounts a registered JavaScript widget into a host element the renderer owns. A widget node has NO vnode children — the widget owns the host's subtree. The reserved tag is excluded from the generic element branch, so a malformed widget node fails validation instead of passing as a plain element. See docs/VIEW-FORMAT.md section 7."
|
|
61
|
+
},
|
|
62
|
+
"widgetProps": {
|
|
63
|
+
"type": "object",
|
|
64
|
+
"required": ["name"],
|
|
65
|
+
"properties": {
|
|
66
|
+
"name": {
|
|
67
|
+
"type": "string",
|
|
68
|
+
"minLength": 1,
|
|
69
|
+
"description": "The registered widget name."
|
|
70
|
+
},
|
|
71
|
+
"props": {
|
|
72
|
+
"description": "The widget's JSON props, any JSON value; compared by reference across renders."
|
|
73
|
+
},
|
|
74
|
+
"tag": {
|
|
75
|
+
"type": "string",
|
|
76
|
+
"minLength": 1,
|
|
77
|
+
"description": "The host element's tag; default div (producers inside an svg subtree pick an SVG container like g)."
|
|
78
|
+
},
|
|
79
|
+
"key": {
|
|
80
|
+
"type": ["string", "number"],
|
|
81
|
+
"description": "Reconciliation identity among siblings; never rendered."
|
|
82
|
+
},
|
|
83
|
+
"on": {
|
|
84
|
+
"type": "object",
|
|
85
|
+
"additionalProperties": {
|
|
86
|
+
"description": "An opaque event binding, delivered as-is to the renderer's onEvent hook. In @jarenjs/app: an action name, or { action, with?, event?, preventDefault?, stopPropagation? } - event is an array of requested $event field names; the two boolean controls (default false) run synchronously in the native event callback, before the action is queued (APP-FORMAT section 3.1)."
|
|
87
|
+
},
|
|
88
|
+
"description": "Event bindings on the HOST element, by DOM event type."
|
|
89
|
+
},
|
|
90
|
+
"style": {
|
|
91
|
+
"type": ["string", "object"],
|
|
92
|
+
"description": "A CSS declaration string, or an object of declarations (camelCase keys become kebab-case)."
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
"additionalProperties": {
|
|
96
|
+
"type": ["string", "number", "boolean", "null"],
|
|
97
|
+
"description": "Any other prop applies to the HOST element exactly as on any element vnode."
|
|
98
|
+
},
|
|
99
|
+
"description": "The props object of a widget vnode: name/props/tag configure the widget; everything else is a host-element prop."
|
|
100
|
+
},
|
|
101
|
+
"props": {
|
|
102
|
+
"type": "object",
|
|
103
|
+
"properties": {
|
|
104
|
+
"key": {
|
|
105
|
+
"type": ["string", "number"],
|
|
106
|
+
"description": "Reconciliation identity among siblings; never rendered."
|
|
107
|
+
},
|
|
108
|
+
"on": {
|
|
109
|
+
"type": "object",
|
|
110
|
+
"additionalProperties": {
|
|
111
|
+
"description": "An opaque event binding, delivered as-is to the renderer's onEvent hook. In @jarenjs/app: an action name, or { action, with?, event?, preventDefault?, stopPropagation? } - event is an array of requested $event field names; the two boolean controls (default false) run synchronously in the native event callback, before the action is queued (APP-FORMAT section 3.1)."
|
|
112
|
+
},
|
|
113
|
+
"description": "Event bindings by DOM event type."
|
|
114
|
+
},
|
|
115
|
+
"style": {
|
|
116
|
+
"type": ["string", "object"],
|
|
117
|
+
"description": "A CSS declaration string, or an object of declarations (camelCase keys become kebab-case)."
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"additionalProperties": {
|
|
121
|
+
"type": ["string", "number", "boolean", "null"],
|
|
122
|
+
"description": "Any other prop writes through to the DOM: as a property when the node has one, as an attribute otherwise. true renders a bare attribute; false and null remove it."
|
|
123
|
+
},
|
|
124
|
+
"description": "The props object of an element."
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|