@graview/guest 0.1.8 → 0.1.10
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 +301 -2
- package/client.js +2 -0
- package/dist/channel.d.ts +11 -3
- package/dist/channel.d.ts.map +1 -1
- package/dist/channel.js +5 -1
- package/dist/channel.js.map +1 -1
- package/dist/cli.d.ts +16 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +75 -0
- package/dist/cli.js.map +1 -0
- package/dist/client-script.d.ts +3 -0
- package/dist/client-script.d.ts.map +1 -0
- package/dist/client-script.js +21 -0
- package/dist/client-script.js.map +1 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.generated.d.ts +4 -0
- package/dist/client.generated.d.ts.map +1 -0
- package/dist/client.generated.js +9 -0
- package/dist/client.generated.js.map +1 -0
- package/dist/client.js +12 -0
- package/dist/client.js.map +1 -0
- package/dist/headless/describe.d.ts +19 -0
- package/dist/headless/describe.d.ts.map +1 -0
- package/dist/headless/describe.js +255 -0
- package/dist/headless/describe.js.map +1 -0
- package/dist/headless/draw.d.ts +17 -0
- package/dist/headless/draw.d.ts.map +1 -0
- package/dist/headless/draw.js +25 -0
- package/dist/headless/draw.js.map +1 -0
- package/dist/headless/index.d.ts +99 -0
- package/dist/headless/index.d.ts.map +1 -0
- package/dist/headless/index.js +143 -0
- package/dist/headless/index.js.map +1 -0
- package/dist/headless/judge.d.ts +16 -0
- package/dist/headless/judge.d.ts.map +1 -0
- package/dist/headless/judge.js +52 -0
- package/dist/headless/judge.js.map +1 -0
- package/dist/headless/node.d.ts +8 -0
- package/dist/headless/node.d.ts.map +1 -0
- package/dist/headless/node.js +80 -0
- package/dist/headless/node.js.map +1 -0
- package/dist/headless/protocol.d.ts +71 -0
- package/dist/headless/protocol.d.ts.map +1 -0
- package/dist/headless/protocol.js +14 -0
- package/dist/headless/protocol.js.map +1 -0
- package/dist/headless/runtime.d.ts +2 -0
- package/dist/headless/runtime.d.ts.map +1 -0
- package/dist/headless/runtime.generated.d.ts +2 -0
- package/dist/headless/runtime.generated.d.ts.map +1 -0
- package/dist/headless/runtime.generated.js +8 -0
- package/dist/headless/runtime.generated.js.map +1 -0
- package/dist/headless/runtime.js +171 -0
- package/dist/headless/runtime.js.map +1 -0
- package/dist/headless/tree.d.ts +54 -0
- package/dist/headless/tree.d.ts.map +1 -0
- package/dist/headless/tree.js +128 -0
- package/dist/headless/tree.js.map +1 -0
- package/dist/host/css.d.ts +118 -0
- package/dist/host/css.d.ts.map +1 -0
- package/dist/host/css.js +963 -0
- package/dist/host/css.js.map +1 -0
- package/dist/host/draw-budget.d.ts +10 -0
- package/dist/host/draw-budget.d.ts.map +1 -0
- package/dist/host/draw-budget.js +29 -0
- package/dist/host/draw-budget.js.map +1 -0
- package/dist/host/frame.d.ts +19 -1
- package/dist/host/frame.d.ts.map +1 -1
- package/dist/host/frame.js +21 -2
- package/dist/host/frame.js.map +1 -1
- package/dist/host/index.d.ts +4 -3
- package/dist/host/index.d.ts.map +1 -1
- package/dist/host/index.js +7 -1
- package/dist/host/index.js.map +1 -1
- package/dist/host/kit.d.ts +6 -2
- package/dist/host/kit.d.ts.map +1 -1
- package/dist/host/kit.js +12 -5
- package/dist/host/kit.js.map +1 -1
- package/dist/host/links.d.ts +24 -0
- package/dist/host/links.d.ts.map +1 -0
- package/dist/host/links.js +35 -0
- package/dist/host/links.js.map +1 -0
- package/dist/host/manifest.d.ts +50 -0
- package/dist/host/manifest.d.ts.map +1 -0
- package/dist/host/manifest.js +103 -0
- package/dist/host/manifest.js.map +1 -0
- package/dist/host/open-draw.d.ts +44 -0
- package/dist/host/open-draw.d.ts.map +1 -0
- package/dist/host/open-draw.js +108 -0
- package/dist/host/open-draw.js.map +1 -0
- package/dist/host/open-judge.d.ts +53 -0
- package/dist/host/open-judge.d.ts.map +1 -0
- package/dist/host/open-judge.js +95 -0
- package/dist/host/open-judge.js.map +1 -0
- package/dist/host/open-render.d.ts +32 -0
- package/dist/host/open-render.d.ts.map +1 -0
- package/dist/host/open-render.js +262 -0
- package/dist/host/open-render.js.map +1 -0
- package/dist/host/press.d.ts +15 -0
- package/dist/host/press.d.ts.map +1 -0
- package/dist/host/press.js +156 -0
- package/dist/host/press.js.map +1 -0
- package/dist/host/react.d.ts +15 -2
- package/dist/host/react.d.ts.map +1 -1
- package/dist/host/react.js +13 -5
- package/dist/host/react.js.map +1 -1
- package/dist/host/session.d.ts +94 -1
- package/dist/host/session.d.ts.map +1 -1
- package/dist/host/session.js +103 -14
- package/dist/host/session.js.map +1 -1
- package/dist/host/theme.d.ts +14 -0
- package/dist/host/theme.d.ts.map +1 -0
- package/dist/host/theme.js +51 -0
- package/dist/host/theme.js.map +1 -0
- package/dist/host/view-runtime.generated.d.ts +2 -0
- package/dist/host/view-runtime.generated.d.ts.map +1 -0
- package/dist/host/view-runtime.generated.js +7 -0
- package/dist/host/view-runtime.generated.js.map +1 -0
- package/dist/host/view-source.d.ts +5 -0
- package/dist/host/view-source.d.ts.map +1 -0
- package/dist/host/view-source.js +39 -0
- package/dist/host/view-source.js.map +1 -0
- package/dist/host/view.d.ts +134 -0
- package/dist/host/view.d.ts.map +1 -0
- package/dist/host/view.js +381 -0
- package/dist/host/view.js.map +1 -0
- package/dist/host/views.d.ts +10 -0
- package/dist/host/views.d.ts.map +1 -0
- package/dist/host/views.js +15 -0
- package/dist/host/views.js.map +1 -0
- package/dist/host/worker-react.d.ts +51 -0
- package/dist/host/worker-react.d.ts.map +1 -0
- package/dist/host/worker-react.js +123 -0
- package/dist/host/worker-react.js.map +1 -0
- package/dist/host/worker-start.d.ts +49 -0
- package/dist/host/worker-start.d.ts.map +1 -0
- package/dist/host/worker-start.js +122 -0
- package/dist/host/worker-start.js.map +1 -0
- package/dist/host/worker.d.ts +17 -12
- package/dist/host/worker.d.ts.map +1 -1
- package/dist/host/worker.js +58 -117
- package/dist/host/worker.js.map +1 -1
- package/dist/host/writes.d.ts +49 -0
- package/dist/host/writes.d.ts.map +1 -0
- package/dist/host/writes.js +76 -0
- package/dist/host/writes.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/open-kit.d.ts +148 -0
- package/dist/open-kit.d.ts.map +1 -0
- package/dist/open-kit.js +321 -0
- package/dist/open-kit.js.map +1 -0
- package/dist/protocol.d.ts +109 -3
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +13 -0
- package/dist/protocol.js.map +1 -1
- package/dist/worker/harden.d.ts +13 -2
- package/dist/worker/harden.d.ts.map +1 -1
- package/dist/worker/harden.js +10 -4
- package/dist/worker/harden.js.map +1 -1
- package/dist/worker/natives.d.ts +1 -0
- package/dist/worker/natives.d.ts.map +1 -1
- package/dist/worker/natives.js +4 -0
- package/dist/worker/natives.js.map +1 -1
- package/dist/worker/view-global.d.ts +70 -0
- package/dist/worker/view-global.d.ts.map +1 -0
- package/dist/worker/view-global.js +264 -0
- package/dist/worker/view-global.js.map +1 -0
- package/dist/worker/view.d.ts +14 -0
- package/dist/worker/view.d.ts.map +1 -0
- package/dist/worker/view.js +64 -0
- package/dist/worker/view.js.map +1 -0
- package/package.json +30 -4
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { manifestActs } from "./manifest.js";
|
|
2
|
+
/*
|
|
3
|
+
* WRITES THAT CANNOT LEAK (FR-92).
|
|
4
|
+
*
|
|
5
|
+
* A view runs for every member with that member's sight, and its author is
|
|
6
|
+
* not the member: a chat that read a record somebody else wrote may follow
|
|
7
|
+
* what was hidden in it. The danger is a view reading what this viewer may
|
|
8
|
+
* see and writing it where somebody else may see it. So:
|
|
9
|
+
*
|
|
10
|
+
* An act must be named in the view's manifest, or it is refused.
|
|
11
|
+
*
|
|
12
|
+
* Where the app's sight is total — every member sees every record — there
|
|
13
|
+
* is nothing to leak, and an act the view asks for from its own code
|
|
14
|
+
* applies with the arguments it gives (`sightIsTotal`).
|
|
15
|
+
*
|
|
16
|
+
* Otherwise an act applies only from a press the HOST saw — a trusted
|
|
17
|
+
* click on an element the view drew with `data-act` — in that press's own
|
|
18
|
+
* handler, and its arguments come only from
|
|
19
|
+
* · the record the element is bound to (`data-record`, on it or the
|
|
20
|
+
* nearest element around it), one the view was shown;
|
|
21
|
+
* · the manifest's constants for the act; and
|
|
22
|
+
* · the values of fields the host itself read, each one the viewer
|
|
23
|
+
* typed: the last change to it a trusted `input` or `change` event,
|
|
24
|
+
* and nothing the view drew changing it since.
|
|
25
|
+
* A field whose value the view set — its `value` attribute, a later
|
|
26
|
+
* update, a textarea's text, an option's `selected` — is FILLED BY THE
|
|
27
|
+
* VIEW, and a press that would carry it is refused. A view's code cannot
|
|
28
|
+
* type: what it dispatches lands in its own worker's DOM, never the
|
|
29
|
+
* host's, and every event it could cause there is untrusted.
|
|
30
|
+
*
|
|
31
|
+
* Either way the act is applied as the viewer, `via: "view:<name>"`, under
|
|
32
|
+
* the view's allowance, and undoable like any other.
|
|
33
|
+
*
|
|
34
|
+
* Pure: no DOM. The host's region reads the press and the fields
|
|
35
|
+
* (press.ts, fetched with the region's drawing), and judges them there.
|
|
36
|
+
*/
|
|
37
|
+
/** Whether every member of the app sees every record: no sight is declared, or every kind is seen by everybody, whole. */
|
|
38
|
+
export function sightIsTotal(store) {
|
|
39
|
+
const sees = store.policy?.sees ?? [];
|
|
40
|
+
if (sees.length === 0)
|
|
41
|
+
return true;
|
|
42
|
+
return store.schema.kinds.every((kind) => sees.some((sight) => sight.roles === "*" && !sight.own && sight.kinds.includes(kind)));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The values the app's own declaration allows an argument — an enum's
|
|
46
|
+
* options or a literal's values, through `optional()`, `default()` and the
|
|
47
|
+
* like — or undefined when it declares no such list.
|
|
48
|
+
*/
|
|
49
|
+
export function declaredValues(schema) {
|
|
50
|
+
let at = schema;
|
|
51
|
+
for (let depth = 0; depth < 8 && at?._zod?.def; depth += 1) {
|
|
52
|
+
const def = at._zod.def;
|
|
53
|
+
if (def["type"] === "enum")
|
|
54
|
+
return Object.values(def["entries"]);
|
|
55
|
+
if (def["type"] === "literal")
|
|
56
|
+
return def["values"];
|
|
57
|
+
if (!("innerType" in def))
|
|
58
|
+
return undefined;
|
|
59
|
+
at = def["innerType"];
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
export const refuse = (reason, message) => ({ ok: false, reason, message });
|
|
64
|
+
export function entryFor(manifest, asked) {
|
|
65
|
+
return manifestActs(manifest).find((one) => (one.as ?? one.act) === asked) ?? manifestActs(manifest).find((one) => one.act === asked && one.as === undefined);
|
|
66
|
+
}
|
|
67
|
+
/** An act the view asked for from its own code (`graview.act`). */
|
|
68
|
+
export function judgeCodeAct(store, manifest, name, args) {
|
|
69
|
+
const entry = entryFor(manifest, name);
|
|
70
|
+
if (!entry)
|
|
71
|
+
return refuse("undeclared", `This view may not ask for “${name}”: its manifest does not name it.`);
|
|
72
|
+
if (!sightIsTotal(store))
|
|
73
|
+
return refuse("press-only", "In this app some members may not see some records, so a view's act applies only when you press it.");
|
|
74
|
+
return { ok: true, name: entry.act, args: { ...args, ...(entry.constants ?? {}) } };
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=writes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writes.js","sourceRoot":"","sources":["../../src/host/writes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAA6C,MAAM,eAAe,CAAC;AAExF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,0HAA0H;AAC1H,MAAM,UAAU,YAAY,CAAsB,KAAe;IAC/D,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC;IACtC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,OAAQ,KAAK,CAAC,MAAM,CAAC,KAA2B,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC1J,CAAC;AAkBD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,MAAe;IAC5C,IAAI,EAAE,GAAG,MAAkE,CAAC;IAC5E,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC3D,MAAM,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;QACxB,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,MAAM;YAAE,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAA4B,CAAC,CAAC;QAC5F,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,SAAS;YAAE,OAAO,GAAG,CAAC,QAAQ,CAAc,CAAC;QACjE,IAAI,CAAC,CAAC,WAAW,IAAI,GAAG,CAAC;YAAE,OAAO,SAAS,CAAC;QAC5C,EAAE,GAAG,GAAG,CAAC,WAAW,CAAc,CAAC;IACrC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAcD,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,MAAoB,EAAE,OAAe,EAAU,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;AAE1G,MAAM,UAAU,QAAQ,CAAC,QAA4B,EAAE,KAAa;IAClE,OAAO,YAAY,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,IAAI,YAAY,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,KAAK,KAAK,IAAI,GAAG,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC;AAChK,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,YAAY,CAAsB,KAAe,EAAE,QAA4B,EAAE,IAAY,EAAE,IAAuC;IACpJ,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC,KAAK;QAAE,OAAO,MAAM,CAAC,YAAY,EAAE,8BAA8B,IAAI,mCAAmC,CAAC,CAAC;IAC/G,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC,YAAY,EAAE,oGAAoG,CAAC,CAAC;IAC5J,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,SAAS,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;AACtF,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { connectGuest } from "./guest.js";
|
|
2
2
|
export type { ConnectGuestOptions, Guest } from "./guest.js";
|
|
3
3
|
export { GUEST_PROTOCOL, GUEST_SANDBOX, OPAQUE_ORIGIN, isGuestReady, isHostHello } from "./protocol.js";
|
|
4
|
-
export type { GuestAct, GuestAnswer, GuestEdge, GuestEvent, GuestNode, GuestProps, GuestReady, GuestRefusal, GuestRequest, HostHello, HostMessage, } from "./protocol.js";
|
|
4
|
+
export type { GuestAct, GuestAnswer, GuestEdge, GuestDomEvent, GuestEvent, GuestNode, GuestPlace, GuestPressed, GuestProps, GuestTheme, GuestReady, GuestRefusal, GuestRequest, HostHello, HostMessage, } from "./protocol.js";
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,YAAY,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACxG,YAAY,EACV,QAAQ,EACR,WAAW,EACX,SAAS,EACT,UAAU,EACV,SAAS,EACT,UAAU,EACV,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,WAAW,GACZ,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,YAAY,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACxG,YAAY,EACV,QAAQ,EACR,WAAW,EACX,SAAS,EACT,aAAa,EACb,UAAU,EACV,SAAS,EACT,UAAU,EACV,YAAY,EACZ,UAAU,EACV,UAAU,EACV,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,WAAW,GACZ,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE OPEN KIT (FR-90): what a worker view may draw, declared once.
|
|
3
|
+
*
|
|
4
|
+
* Graview Cloud's ADR 0007 put it in one line: safety comes from what a
|
|
5
|
+
* view can REACH, not from what it can DRAW. So the kit is broad — most of
|
|
6
|
+
* HTML's sectioning, text, lists, tables, disclosure and form controls,
|
|
7
|
+
* images, SVG's shapes, paths, text and gradients, and CSS for layout,
|
|
8
|
+
* colour, type, transitions, keyframes and media queries — and what is left
|
|
9
|
+
* out is exactly what could fetch, escape the region, or speak for the app:
|
|
10
|
+
*
|
|
11
|
+
* fetch `<script>`, `<iframe>`, `<object>`, `<embed>`, `<link>`, `<meta>`,
|
|
12
|
+
* `<base>`, `<style>` outside the view's own stylesheet, media and
|
|
13
|
+
* canvas, SVG `<image>`, `<feImage>`, `<foreignObject>`, SVG `<a>`,
|
|
14
|
+
* SMIL (which can rewrite an `href`), every `src`/`href`/`srcset`
|
|
15
|
+
* but an image's `data:` or same-origin `blob:` source and an SVG
|
|
16
|
+
* reference to `#id` in the view's own drawing, and in CSS every
|
|
17
|
+
* `url()` but `url(#id)` on a paint, `@import`, `@font-face`,
|
|
18
|
+
* `@namespace`, and every function not on `CSS_FUNCTIONS`
|
|
19
|
+
* (`image-set()`, `cross-fade()`, `element()`, `paint()`,
|
|
20
|
+
* `attr()`, `expression()`, …).
|
|
21
|
+
* escape `position: fixed` and `sticky`, `:host` and `::slotted`, the
|
|
22
|
+
* top layer (`popover`, `<dialog>`), `autofocus`, `accesskey`,
|
|
23
|
+
* and `<form>`, whose submission is a navigation.
|
|
24
|
+
* speak `role="alert"`, the landmark roles the app's own chrome uses
|
|
25
|
+
* and the elements that carry them (`<nav>`, `<header>`,
|
|
26
|
+
* `<footer>`, `<aside>`, `<search>` are drawn as `<div>`, `<output>`
|
|
27
|
+
* as `<span>`, and a `<section>` is never named),
|
|
28
|
+
* `aria-live="assertive"`, and password and file inputs.
|
|
29
|
+
*
|
|
30
|
+
* The host draws from this declaration (host/open-render.ts, with
|
|
31
|
+
* host/css.ts for every stylesheet, style attribute and SVG paint), and the
|
|
32
|
+
* worker's runtime reads the same tables to tell an author at once what the
|
|
33
|
+
* host will not draw (worker/view.ts). Adding an element or an attribute
|
|
34
|
+
* here makes it drawable on both sides; nothing else changes.
|
|
35
|
+
*
|
|
36
|
+
* This module has no imports: a view's runtime carries it and nothing of
|
|
37
|
+
* the framework.
|
|
38
|
+
*/
|
|
39
|
+
/**
|
|
40
|
+
* What an attribute's value may be:
|
|
41
|
+
*
|
|
42
|
+
* text any string, as text (a title, a label, an id)
|
|
43
|
+
* number a finite number
|
|
44
|
+
* boolean present or absent
|
|
45
|
+
* oneOf one of a closed list, compared without case
|
|
46
|
+
* css a CSS value for the property of the attribute's own name (an
|
|
47
|
+
* SVG presentation attribute: `fill`, `stroke`, …), sanitised
|
|
48
|
+
* as a stylesheet's value is
|
|
49
|
+
* geometry SVG geometry and transforms (`d`, `points`, `viewBox`,
|
|
50
|
+
* `transform`): numbers, letters, commas and brackets, never `url`
|
|
51
|
+
* image an image's source: a `data:image/…` URL, or a `blob:` URL of
|
|
52
|
+
* the host page's own origin
|
|
53
|
+
* fragment a reference to an element of the view's own drawing: `#id`
|
|
54
|
+
* style a declaration list, sanitised as a stylesheet's declarations
|
|
55
|
+
*/
|
|
56
|
+
export type OpenAttribute = "text" | "number" | "boolean" | "css" | "geometry" | "image" | "fragment" | "style" | {
|
|
57
|
+
readonly oneOf: readonly string[];
|
|
58
|
+
};
|
|
59
|
+
export interface OpenElement {
|
|
60
|
+
/** The attributes this element takes beyond its namespace's global ones, by canonical name. */
|
|
61
|
+
readonly attributes?: Readonly<Record<string, OpenAttribute>>;
|
|
62
|
+
/** It holds nothing (`img`, `input`, `br`). */
|
|
63
|
+
readonly empty?: true;
|
|
64
|
+
}
|
|
65
|
+
/** Attributes every HTML element takes. `aria-*` and `data-*` are read by pattern (`isAriaAttribute`, `isDataAttribute`). */
|
|
66
|
+
export declare const HTML_GLOBAL_ATTRIBUTES: Readonly<Record<string, OpenAttribute>>;
|
|
67
|
+
/** The HTML elements a view may draw, by name. */
|
|
68
|
+
export declare const HTML_ELEMENTS: Readonly<Record<string, OpenElement>>;
|
|
69
|
+
/** Attributes every SVG element takes. */
|
|
70
|
+
export declare const SVG_GLOBAL_ATTRIBUTES: Readonly<Record<string, OpenAttribute>>;
|
|
71
|
+
/** The SVG elements a view may draw, by canonical (camel-cased) name. */
|
|
72
|
+
export declare const SVG_ELEMENTS: Readonly<Record<string, OpenElement>>;
|
|
73
|
+
/**
|
|
74
|
+
* What is refused, and why, for an author and for the tests: none of these
|
|
75
|
+
* is in the tables above, and a test holds them out. Not the allowlist —
|
|
76
|
+
* anything not above is refused — but the names a reader looks for.
|
|
77
|
+
*/
|
|
78
|
+
export declare const NEVER_DRAWN: {
|
|
79
|
+
readonly html: readonly ["script", "iframe", "frame", "frameset", "object", "embed", "applet", "portal", "link", "meta", "base", "style", "form", "template", "slot", "noscript", "audio", "video", "source", "track", "picture", "canvas", "dialog", "datalist", "main", "html", "head", "body", "title"];
|
|
80
|
+
readonly svg: readonly ["image", "feImage", "filter", "foreignObject", "a", "script", "style", "animate", "animateMotion", "animateTransform", "set", "discard", "view", "switch", "cursor", "font-face"];
|
|
81
|
+
readonly attributes: readonly ["src (outside img)", "href", "xlink:href (outside a #fragment)", "srcset", "sizes", "action", "formaction", "ping", "poster", "background", "on*", "popover", "popovertarget", "autofocus", "accesskey", "contenteditable", "is", "nonce", "autocomplete", "form", "list"];
|
|
82
|
+
};
|
|
83
|
+
/** Roles that would make a view speak as the app's own chrome or notices. */
|
|
84
|
+
export declare const REFUSED_ROLES: readonly ["alert", "alertdialog", "dialog", "banner", "main", "navigation", "contentinfo", "complementary", "search", "form", "region", "application", "status", "log", "marquee", "timer"];
|
|
85
|
+
/**
|
|
86
|
+
* Elements whose own role is a landmark or a notice's, drawn as an element
|
|
87
|
+
* with none: what they hold is drawn, but they speak as the app's chrome
|
|
88
|
+
* (`<nav>` is a navigation landmark, `<header>` a banner, `<output>` a
|
|
89
|
+
* status) to assistive technology. Setting a role on them cannot cover it:
|
|
90
|
+
* `role="none"` on a focusable or named element is ignored, and the
|
|
91
|
+
* landmark comes back. A view's stylesheet reaches them by class, not by
|
|
92
|
+
* these names.
|
|
93
|
+
*/
|
|
94
|
+
export declare const HTML_DRAWN_AS: Readonly<Record<string, string>>;
|
|
95
|
+
/** Attributes that would name an element: a named `<section>` is a region landmark, so a section is never named. */
|
|
96
|
+
export declare const UNNAMED_ELEMENTS: Readonly<Record<string, readonly string[]>>;
|
|
97
|
+
/** `aria-*`, by pattern. */
|
|
98
|
+
export declare function isAriaAttribute(name: string): boolean;
|
|
99
|
+
/** `data-*`, by pattern. */
|
|
100
|
+
export declare function isDataAttribute(name: string): boolean;
|
|
101
|
+
/**
|
|
102
|
+
* THE CSS A VIEW MAY WRITE: an allowlist of properties, so a property a
|
|
103
|
+
* browser ships tomorrow that takes an image is refused until somebody adds
|
|
104
|
+
* it here, rather than drawn until somebody notices.
|
|
105
|
+
*/
|
|
106
|
+
export declare const CSS_PROPERTIES: readonly string[];
|
|
107
|
+
/**
|
|
108
|
+
* The CSS functions a value may call. Every one makes a value of its own
|
|
109
|
+
* arguments; none names a resource. `url()` is not here: it is read apart,
|
|
110
|
+
* and kept only as `url(#id)` on a property in `CSS_FRAGMENT_PROPERTIES`.
|
|
111
|
+
*/
|
|
112
|
+
export declare const CSS_FUNCTIONS: readonly string[];
|
|
113
|
+
/**
|
|
114
|
+
* Where `url(#id)` stays: the paints and references an SVG drawing makes
|
|
115
|
+
* to its own gradients, clip paths, masks and markers. A fragment of the
|
|
116
|
+
* view's own drawing is resolved in the view's own tree and fetches
|
|
117
|
+
* nothing; anything else in a `url()` is refused wherever it is written.
|
|
118
|
+
*/
|
|
119
|
+
export declare const CSS_FRAGMENT_PROPERTIES: readonly string[];
|
|
120
|
+
/** The at-rules a stylesheet may hold. `@import`, `@font-face`, `@namespace`, `@property`, `@layer` and the rest are refused. */
|
|
121
|
+
export declare const CSS_AT_RULES: readonly string[];
|
|
122
|
+
/**
|
|
123
|
+
* The values a property may take when it may take only some: `position`
|
|
124
|
+
* stays in the region (no `fixed`, no `sticky`), and so does `var()` — a
|
|
125
|
+
* property held to a list is held to a keyword, so a custom property
|
|
126
|
+
* cannot carry `fixed` into it.
|
|
127
|
+
*/
|
|
128
|
+
export declare const CSS_KEYWORD_PROPERTIES: Readonly<Record<string, readonly string[]>>;
|
|
129
|
+
/** Selectors a view's stylesheet may not write: each reaches past the view's own tree. */
|
|
130
|
+
export declare const CSS_REFUSED_SELECTORS: readonly string[];
|
|
131
|
+
/** The largest stylesheet, in characters, a view may give. */
|
|
132
|
+
export declare const OPEN_MAX_STYLESHEET = 64000;
|
|
133
|
+
/** The longest attribute value, in characters; geometry (`d`, `points`) may be longer. */
|
|
134
|
+
export declare const OPEN_MAX_ATTRIBUTE = 4000;
|
|
135
|
+
export declare const OPEN_MAX_GEOMETRY = 100000;
|
|
136
|
+
/** The largest image a view may hold, as its `data:` URL's length. */
|
|
137
|
+
export declare const OPEN_MAX_IMAGE = 512000;
|
|
138
|
+
/** The longest text a node may hold, in characters; longer is cut. */
|
|
139
|
+
export declare const OPEN_MAX_TEXT = 10000;
|
|
140
|
+
export declare const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
|
|
141
|
+
export declare const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
142
|
+
export declare function svgElementName(name: string): string | undefined;
|
|
143
|
+
/** The canonical name of an attribute an element takes, from any casing (`viewbox` → `viewBox`), with what it may hold; or undefined. */
|
|
144
|
+
export declare function openAttribute(namespace: "html" | "svg", element: string, name: string): {
|
|
145
|
+
readonly name: string;
|
|
146
|
+
readonly kind: OpenAttribute;
|
|
147
|
+
} | undefined;
|
|
148
|
+
//# sourceMappingURL=open-kit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-kit.d.ts","sourceRoot":"","sources":["../src/open-kit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,OAAO,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AAExJ,MAAM,WAAW,WAAW;IAC1B,+FAA+F;IAC/F,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;IAC9D,+CAA+C;IAC/C,QAAQ,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC;CACvB;AAKD,6HAA6H;AAC7H,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAe1E,CAAC;AAEF,kDAAkD;AAClD,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAuD/D,CAAC;AAWF,0CAA0C;AAC1C,eAAO,MAAM,qBAAqB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CASzE,CAAC;AAMF,yEAAyE;AACzE,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CA0B9D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW;;;;CAId,CAAC;AAEX,6EAA6E;AAC7E,eAAO,MAAM,aAAa,6LAA8L,CAAC;AAEzN;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAA6F,CAAC;AAEzJ,oHAAoH;AACpH,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAA2D,CAAC;AAErI,4BAA4B;AAC5B,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD;AAED,4BAA4B;AAC5B,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EA6C3C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,MAAM,EAW1C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAAkG,CAAC;AAExJ,iIAAiI;AACjI,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAAoD,CAAC;AAE/F;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAK9E,CAAC;AAEF,0FAA0F;AAC1F,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAA8L,CAAC;AAElP,8DAA8D;AAC9D,eAAO,MAAM,mBAAmB,QAAS,CAAC;AAC1C,0FAA0F;AAC1F,eAAO,MAAM,kBAAkB,OAAQ,CAAC;AACxC,eAAO,MAAM,iBAAiB,SAAU,CAAC;AACzC,sEAAsE;AACtE,eAAO,MAAM,cAAc,SAAU,CAAC;AACtC,sEAAsE;AACtE,eAAO,MAAM,aAAa,QAAS,CAAC;AAEpC,eAAO,MAAM,cAAc,iCAAiC,CAAC;AAC7D,eAAO,MAAM,aAAa,+BAA+B,CAAC;AAI1D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE/D;AAED,yIAAyI;AACzI,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;CAAE,GAAG,SAAS,CAW3J"}
|
package/dist/open-kit.js
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE OPEN KIT (FR-90): what a worker view may draw, declared once.
|
|
3
|
+
*
|
|
4
|
+
* Graview Cloud's ADR 0007 put it in one line: safety comes from what a
|
|
5
|
+
* view can REACH, not from what it can DRAW. So the kit is broad — most of
|
|
6
|
+
* HTML's sectioning, text, lists, tables, disclosure and form controls,
|
|
7
|
+
* images, SVG's shapes, paths, text and gradients, and CSS for layout,
|
|
8
|
+
* colour, type, transitions, keyframes and media queries — and what is left
|
|
9
|
+
* out is exactly what could fetch, escape the region, or speak for the app:
|
|
10
|
+
*
|
|
11
|
+
* fetch `<script>`, `<iframe>`, `<object>`, `<embed>`, `<link>`, `<meta>`,
|
|
12
|
+
* `<base>`, `<style>` outside the view's own stylesheet, media and
|
|
13
|
+
* canvas, SVG `<image>`, `<feImage>`, `<foreignObject>`, SVG `<a>`,
|
|
14
|
+
* SMIL (which can rewrite an `href`), every `src`/`href`/`srcset`
|
|
15
|
+
* but an image's `data:` or same-origin `blob:` source and an SVG
|
|
16
|
+
* reference to `#id` in the view's own drawing, and in CSS every
|
|
17
|
+
* `url()` but `url(#id)` on a paint, `@import`, `@font-face`,
|
|
18
|
+
* `@namespace`, and every function not on `CSS_FUNCTIONS`
|
|
19
|
+
* (`image-set()`, `cross-fade()`, `element()`, `paint()`,
|
|
20
|
+
* `attr()`, `expression()`, …).
|
|
21
|
+
* escape `position: fixed` and `sticky`, `:host` and `::slotted`, the
|
|
22
|
+
* top layer (`popover`, `<dialog>`), `autofocus`, `accesskey`,
|
|
23
|
+
* and `<form>`, whose submission is a navigation.
|
|
24
|
+
* speak `role="alert"`, the landmark roles the app's own chrome uses
|
|
25
|
+
* and the elements that carry them (`<nav>`, `<header>`,
|
|
26
|
+
* `<footer>`, `<aside>`, `<search>` are drawn as `<div>`, `<output>`
|
|
27
|
+
* as `<span>`, and a `<section>` is never named),
|
|
28
|
+
* `aria-live="assertive"`, and password and file inputs.
|
|
29
|
+
*
|
|
30
|
+
* The host draws from this declaration (host/open-render.ts, with
|
|
31
|
+
* host/css.ts for every stylesheet, style attribute and SVG paint), and the
|
|
32
|
+
* worker's runtime reads the same tables to tell an author at once what the
|
|
33
|
+
* host will not draw (worker/view.ts). Adding an element or an attribute
|
|
34
|
+
* here makes it drawable on both sides; nothing else changes.
|
|
35
|
+
*
|
|
36
|
+
* This module has no imports: a view's runtime carries it and nothing of
|
|
37
|
+
* the framework.
|
|
38
|
+
*/
|
|
39
|
+
const none = {};
|
|
40
|
+
const empty = { empty: true };
|
|
41
|
+
/** Attributes every HTML element takes. `aria-*` and `data-*` are read by pattern (`isAriaAttribute`, `isDataAttribute`). */
|
|
42
|
+
export const HTML_GLOBAL_ATTRIBUTES = {
|
|
43
|
+
id: "text",
|
|
44
|
+
class: "text",
|
|
45
|
+
title: "text",
|
|
46
|
+
lang: "text",
|
|
47
|
+
dir: { oneOf: ["ltr", "rtl", "auto"] },
|
|
48
|
+
hidden: "text",
|
|
49
|
+
/* -1 or 0: a view orders its own controls by where it draws them, never ahead of the app's. */
|
|
50
|
+
tabindex: { oneOf: ["-1", "0"] },
|
|
51
|
+
role: "text",
|
|
52
|
+
style: "style",
|
|
53
|
+
translate: { oneOf: ["yes", "no"] },
|
|
54
|
+
spellcheck: { oneOf: ["true", "false"] },
|
|
55
|
+
draggable: { oneOf: ["true", "false"] },
|
|
56
|
+
inert: "boolean",
|
|
57
|
+
};
|
|
58
|
+
/** The HTML elements a view may draw, by name. */
|
|
59
|
+
export const HTML_ELEMENTS = {
|
|
60
|
+
// sectioning and grouping
|
|
61
|
+
div: none, section: none, article: none, aside: none, header: none, footer: none, nav: none, hgroup: none, search: none, address: none,
|
|
62
|
+
figure: none, figcaption: none, blockquote: none, p: none, pre: none, hr: empty, br: empty, wbr: empty,
|
|
63
|
+
h1: none, h2: none, h3: none, h4: none, h5: none, h6: none,
|
|
64
|
+
// text
|
|
65
|
+
span: none, strong: none, em: none, b: none, i: none, u: none, s: none, small: none, mark: none, abbr: none, cite: none, q: none,
|
|
66
|
+
code: none, kbd: none, samp: none, var: none, sub: none, sup: none, dfn: none, bdi: none, bdo: none, ruby: none, rt: none, rp: none,
|
|
67
|
+
time: { attributes: { datetime: "text" } },
|
|
68
|
+
data: { attributes: { value: "text" } },
|
|
69
|
+
del: { attributes: { datetime: "text" } },
|
|
70
|
+
ins: { attributes: { datetime: "text" } },
|
|
71
|
+
/* A link goes to a record or a place of this app, never to an address (FR-93): it has no `href` to give. */
|
|
72
|
+
a: none,
|
|
73
|
+
// lists
|
|
74
|
+
ul: none, menu: none, dl: none, dt: none, dd: none,
|
|
75
|
+
ol: { attributes: { start: "number", reversed: "boolean", type: { oneOf: ["1", "a", "A", "i", "I"] } } },
|
|
76
|
+
li: { attributes: { value: "number" } },
|
|
77
|
+
// tables
|
|
78
|
+
table: none, caption: none, thead: none, tbody: none, tfoot: none, tr: none,
|
|
79
|
+
colgroup: { attributes: { span: "number" } },
|
|
80
|
+
col: { attributes: { span: "number" }, empty: true },
|
|
81
|
+
th: { attributes: { colspan: "number", rowspan: "number", headers: "text", scope: { oneOf: ["row", "col", "rowgroup", "colgroup"] }, abbr: "text" } },
|
|
82
|
+
td: { attributes: { colspan: "number", rowspan: "number", headers: "text" } },
|
|
83
|
+
// disclosure
|
|
84
|
+
details: { attributes: { open: "boolean", name: "text" } },
|
|
85
|
+
summary: none,
|
|
86
|
+
// controls: no form, so nothing is ever submitted
|
|
87
|
+
button: { attributes: { type: { oneOf: ["button"] }, disabled: "boolean", name: "text", value: "text" } },
|
|
88
|
+
input: {
|
|
89
|
+
empty: true,
|
|
90
|
+
attributes: {
|
|
91
|
+
/*
|
|
92
|
+
* Never password or file (a view does not ask for either), image (it has a src), hidden or submit.
|
|
93
|
+
* No `list`: a suggestion the person picks from a view's `<datalist>` is typed by the browser, trusted,
|
|
94
|
+
* and would pass the view's words off as theirs (FR-92).
|
|
95
|
+
*/
|
|
96
|
+
type: { oneOf: ["text", "search", "number", "range", "checkbox", "radio", "date", "time", "datetime-local", "month", "week", "color", "email", "tel", "url"] },
|
|
97
|
+
name: "text", value: "text", placeholder: "text", min: "text", max: "text", step: "text", checked: "boolean", disabled: "boolean",
|
|
98
|
+
readonly: "boolean", required: "boolean", maxlength: "number", minlength: "number", size: "number", pattern: "text",
|
|
99
|
+
multiple: "boolean", inputmode: "text", enterkeyhint: "text",
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
select: { attributes: { name: "text", disabled: "boolean", multiple: "boolean", required: "boolean", size: "number" } },
|
|
103
|
+
option: { attributes: { value: "text", selected: "boolean", disabled: "boolean", label: "text" } },
|
|
104
|
+
optgroup: { attributes: { label: "text", disabled: "boolean" } },
|
|
105
|
+
textarea: { attributes: { name: "text", placeholder: "text", rows: "number", cols: "number", disabled: "boolean", readonly: "boolean", required: "boolean", maxlength: "number", minlength: "number", wrap: { oneOf: ["soft", "hard", "off"] } } },
|
|
106
|
+
label: { attributes: { for: "text" } },
|
|
107
|
+
fieldset: { attributes: { disabled: "boolean", name: "text" } },
|
|
108
|
+
legend: none,
|
|
109
|
+
output: none,
|
|
110
|
+
meter: { attributes: { value: "number", min: "number", max: "number", low: "number", high: "number", optimum: "number" } },
|
|
111
|
+
progress: { attributes: { value: "number", max: "number" } },
|
|
112
|
+
// images, held in the drawing: data: or the host's own blob:
|
|
113
|
+
img: { empty: true, attributes: { src: "image", alt: "text", width: "number", height: "number", decoding: { oneOf: ["async", "sync", "auto"] } } },
|
|
114
|
+
};
|
|
115
|
+
/** SVG's presentation attributes: CSS properties, sanitised as a stylesheet's are. */
|
|
116
|
+
const PRESENTATION = [
|
|
117
|
+
"fill", "fill-opacity", "fill-rule", "stroke", "stroke-width", "stroke-linecap", "stroke-linejoin", "stroke-dasharray", "stroke-dashoffset",
|
|
118
|
+
"stroke-opacity", "stroke-miterlimit", "opacity", "color", "display", "visibility", "transform-origin", "clip-path", "clip-rule",
|
|
119
|
+
"marker-start", "marker-mid", "marker-end", "font-family", "font-size", "font-weight", "font-style", "text-anchor", "dominant-baseline",
|
|
120
|
+
"alignment-baseline", "letter-spacing", "text-decoration", "paint-order", "vector-effect", "shape-rendering", "pointer-events",
|
|
121
|
+
"stop-color", "stop-opacity", "overflow", "mask",
|
|
122
|
+
];
|
|
123
|
+
/** Attributes every SVG element takes. */
|
|
124
|
+
export const SVG_GLOBAL_ATTRIBUTES = {
|
|
125
|
+
id: "text",
|
|
126
|
+
class: "text",
|
|
127
|
+
style: "style",
|
|
128
|
+
lang: "text",
|
|
129
|
+
role: "text",
|
|
130
|
+
tabindex: { oneOf: ["-1", "0"] },
|
|
131
|
+
transform: "geometry",
|
|
132
|
+
...Object.fromEntries(PRESENTATION.map((name) => [name, "css"])),
|
|
133
|
+
};
|
|
134
|
+
const box = { x: "geometry", y: "geometry", width: "geometry", height: "geometry" };
|
|
135
|
+
const viewport = { viewBox: "geometry", preserveAspectRatio: "geometry" };
|
|
136
|
+
const gradient = { gradientUnits: { oneOf: ["userSpaceOnUse", "objectBoundingBox"] }, gradientTransform: "geometry", spreadMethod: { oneOf: ["pad", "reflect", "repeat"] }, href: "fragment" };
|
|
137
|
+
/** The SVG elements a view may draw, by canonical (camel-cased) name. */
|
|
138
|
+
export const SVG_ELEMENTS = {
|
|
139
|
+
svg: { attributes: { ...box, ...viewport } },
|
|
140
|
+
g: none,
|
|
141
|
+
defs: none,
|
|
142
|
+
symbol: { attributes: { ...box, ...viewport, refX: "geometry", refY: "geometry" } },
|
|
143
|
+
/* `use` within the drawing only: `#id`, never a document of its own. */
|
|
144
|
+
use: { attributes: { ...box, href: "fragment" } },
|
|
145
|
+
title: none,
|
|
146
|
+
desc: none,
|
|
147
|
+
path: { attributes: { d: "geometry", pathLength: "number" } },
|
|
148
|
+
rect: { attributes: { ...box, rx: "geometry", ry: "geometry", pathLength: "number" } },
|
|
149
|
+
circle: { attributes: { cx: "geometry", cy: "geometry", r: "geometry", pathLength: "number" } },
|
|
150
|
+
ellipse: { attributes: { cx: "geometry", cy: "geometry", rx: "geometry", ry: "geometry", pathLength: "number" } },
|
|
151
|
+
line: { attributes: { x1: "geometry", y1: "geometry", x2: "geometry", y2: "geometry", pathLength: "number" } },
|
|
152
|
+
polyline: { attributes: { points: "geometry", pathLength: "number" } },
|
|
153
|
+
polygon: { attributes: { points: "geometry", pathLength: "number" } },
|
|
154
|
+
text: { attributes: { x: "geometry", y: "geometry", dx: "geometry", dy: "geometry", rotate: "geometry", textLength: "geometry", lengthAdjust: { oneOf: ["spacing", "spacingAndGlyphs"] } } },
|
|
155
|
+
tspan: { attributes: { x: "geometry", y: "geometry", dx: "geometry", dy: "geometry", rotate: "geometry", textLength: "geometry" } },
|
|
156
|
+
textPath: { attributes: { href: "fragment", startOffset: "geometry", method: { oneOf: ["align", "stretch"] }, spacing: { oneOf: ["auto", "exact"] }, side: { oneOf: ["left", "right"] } } },
|
|
157
|
+
linearGradient: { attributes: { x1: "geometry", y1: "geometry", x2: "geometry", y2: "geometry", ...gradient } },
|
|
158
|
+
radialGradient: { attributes: { cx: "geometry", cy: "geometry", r: "geometry", fx: "geometry", fy: "geometry", fr: "geometry", ...gradient } },
|
|
159
|
+
stop: { attributes: { offset: "geometry" } },
|
|
160
|
+
clipPath: { attributes: { clipPathUnits: { oneOf: ["userSpaceOnUse", "objectBoundingBox"] } } },
|
|
161
|
+
mask: { attributes: { ...box, maskUnits: { oneOf: ["userSpaceOnUse", "objectBoundingBox"] }, maskContentUnits: { oneOf: ["userSpaceOnUse", "objectBoundingBox"] } } },
|
|
162
|
+
pattern: { attributes: { ...box, ...viewport, patternUnits: { oneOf: ["userSpaceOnUse", "objectBoundingBox"] }, patternContentUnits: { oneOf: ["userSpaceOnUse", "objectBoundingBox"] }, patternTransform: "geometry", href: "fragment" } },
|
|
163
|
+
marker: { attributes: { ...viewport, markerWidth: "geometry", markerHeight: "geometry", refX: "geometry", refY: "geometry", orient: "geometry", markerUnits: { oneOf: ["strokeWidth", "userSpaceOnUse"] } } },
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* What is refused, and why, for an author and for the tests: none of these
|
|
167
|
+
* is in the tables above, and a test holds them out. Not the allowlist —
|
|
168
|
+
* anything not above is refused — but the names a reader looks for.
|
|
169
|
+
*/
|
|
170
|
+
export const NEVER_DRAWN = {
|
|
171
|
+
html: ["script", "iframe", "frame", "frameset", "object", "embed", "applet", "portal", "link", "meta", "base", "style", "form", "template", "slot", "noscript", "audio", "video", "source", "track", "picture", "canvas", "dialog", "datalist", "main", "html", "head", "body", "title"],
|
|
172
|
+
svg: ["image", "feImage", "filter", "foreignObject", "a", "script", "style", "animate", "animateMotion", "animateTransform", "set", "discard", "view", "switch", "cursor", "font-face"],
|
|
173
|
+
attributes: ["src (outside img)", "href", "xlink:href (outside a #fragment)", "srcset", "sizes", "action", "formaction", "ping", "poster", "background", "on*", "popover", "popovertarget", "autofocus", "accesskey", "contenteditable", "is", "nonce", "autocomplete", "form", "list"],
|
|
174
|
+
};
|
|
175
|
+
/** Roles that would make a view speak as the app's own chrome or notices. */
|
|
176
|
+
export const REFUSED_ROLES = ["alert", "alertdialog", "dialog", "banner", "main", "navigation", "contentinfo", "complementary", "search", "form", "region", "application", "status", "log", "marquee", "timer"];
|
|
177
|
+
/**
|
|
178
|
+
* Elements whose own role is a landmark or a notice's, drawn as an element
|
|
179
|
+
* with none: what they hold is drawn, but they speak as the app's chrome
|
|
180
|
+
* (`<nav>` is a navigation landmark, `<header>` a banner, `<output>` a
|
|
181
|
+
* status) to assistive technology. Setting a role on them cannot cover it:
|
|
182
|
+
* `role="none"` on a focusable or named element is ignored, and the
|
|
183
|
+
* landmark comes back. A view's stylesheet reaches them by class, not by
|
|
184
|
+
* these names.
|
|
185
|
+
*/
|
|
186
|
+
export const HTML_DRAWN_AS = { nav: "div", header: "div", footer: "div", aside: "div", search: "div", output: "span" };
|
|
187
|
+
/** Attributes that would name an element: a named `<section>` is a region landmark, so a section is never named. */
|
|
188
|
+
export const UNNAMED_ELEMENTS = { section: ["aria-label", "aria-labelledby", "title"] };
|
|
189
|
+
/** `aria-*`, by pattern. */
|
|
190
|
+
export function isAriaAttribute(name) {
|
|
191
|
+
return /^aria-[a-z]+$/.test(name);
|
|
192
|
+
}
|
|
193
|
+
/** `data-*`, by pattern. */
|
|
194
|
+
export function isDataAttribute(name) {
|
|
195
|
+
return /^data-[a-z0-9][a-z0-9_.-]*$/.test(name);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* THE CSS A VIEW MAY WRITE: an allowlist of properties, so a property a
|
|
199
|
+
* browser ships tomorrow that takes an image is refused until somebody adds
|
|
200
|
+
* it here, rather than drawn until somebody notices.
|
|
201
|
+
*/
|
|
202
|
+
export const CSS_PROPERTIES = [
|
|
203
|
+
// box and layout
|
|
204
|
+
"display", "position", "top", "right", "bottom", "left", "inset", "inset-block", "inset-inline", "inset-block-start", "inset-block-end", "inset-inline-start", "inset-inline-end",
|
|
205
|
+
"width", "height", "min-width", "min-height", "max-width", "max-height", "inline-size", "block-size", "min-inline-size", "min-block-size", "max-inline-size", "max-block-size",
|
|
206
|
+
"margin", "margin-top", "margin-right", "margin-bottom", "margin-left", "margin-block", "margin-inline", "margin-block-start", "margin-block-end", "margin-inline-start", "margin-inline-end",
|
|
207
|
+
"padding", "padding-top", "padding-right", "padding-bottom", "padding-left", "padding-block", "padding-inline", "padding-block-start", "padding-block-end", "padding-inline-start", "padding-inline-end",
|
|
208
|
+
"box-sizing", "overflow", "overflow-x", "overflow-y", "overflow-wrap", "float", "clear", "z-index", "aspect-ratio", "object-fit", "object-position", "vertical-align",
|
|
209
|
+
"isolation", "contain", "container", "container-type", "container-name", "content-visibility",
|
|
210
|
+
// flex and grid
|
|
211
|
+
"flex", "flex-direction", "flex-wrap", "flex-flow", "flex-grow", "flex-shrink", "flex-basis", "order",
|
|
212
|
+
"grid", "grid-template", "grid-template-columns", "grid-template-rows", "grid-template-areas", "grid-auto-columns", "grid-auto-rows", "grid-auto-flow",
|
|
213
|
+
"grid-area", "grid-column", "grid-row", "grid-column-start", "grid-column-end", "grid-row-start", "grid-row-end",
|
|
214
|
+
"gap", "row-gap", "column-gap", "align-items", "align-content", "align-self", "justify-items", "justify-content", "justify-self", "place-items", "place-content", "place-self",
|
|
215
|
+
"columns", "column-count", "column-width", "column-rule", "column-rule-color", "column-rule-style", "column-rule-width", "column-span", "column-fill",
|
|
216
|
+
// colour and surface
|
|
217
|
+
"color", "background", "background-color", "background-image", "background-position", "background-position-x", "background-position-y", "background-size", "background-repeat",
|
|
218
|
+
"background-origin", "background-clip", "background-attachment", "background-blend-mode",
|
|
219
|
+
"border", "border-top", "border-right", "border-bottom", "border-left", "border-block", "border-inline", "border-block-start", "border-block-end", "border-inline-start", "border-inline-end",
|
|
220
|
+
"border-width", "border-style", "border-color", "border-top-width", "border-right-width", "border-bottom-width", "border-left-width",
|
|
221
|
+
"border-top-style", "border-right-style", "border-bottom-style", "border-left-style", "border-top-color", "border-right-color", "border-bottom-color", "border-left-color",
|
|
222
|
+
"border-radius", "border-top-left-radius", "border-top-right-radius", "border-bottom-right-radius", "border-bottom-left-radius",
|
|
223
|
+
"border-start-start-radius", "border-start-end-radius", "border-end-start-radius", "border-end-end-radius", "border-collapse", "border-spacing",
|
|
224
|
+
"outline", "outline-width", "outline-style", "outline-color", "outline-offset", "box-shadow", "opacity", "visibility", "mix-blend-mode", "filter", "backdrop-filter",
|
|
225
|
+
"clip-path", "accent-color", "caret-color", "color-scheme", "appearance", "-webkit-appearance",
|
|
226
|
+
// type
|
|
227
|
+
"font", "font-family", "font-size", "font-weight", "font-style", "font-stretch", "font-variant", "font-variant-numeric", "font-variant-caps", "font-variant-ligatures",
|
|
228
|
+
"font-feature-settings", "font-variation-settings", "font-kerning", "font-optical-sizing", "font-size-adjust", "font-synthesis",
|
|
229
|
+
"line-height", "letter-spacing", "word-spacing", "text-align", "text-align-last", "text-indent", "text-transform", "text-decoration", "text-decoration-line",
|
|
230
|
+
"text-decoration-color", "text-decoration-style", "text-decoration-thickness", "text-underline-offset", "text-underline-position", "text-shadow", "text-overflow",
|
|
231
|
+
"text-wrap", "text-wrap-mode", "text-wrap-style", "white-space", "white-space-collapse", "word-break", "line-break", "hyphens", "tab-size", "direction", "unicode-bidi", "writing-mode",
|
|
232
|
+
"text-orientation", "text-rendering", "-webkit-font-smoothing", "-webkit-line-clamp", "-webkit-box-orient", "line-clamp", "-webkit-text-fill-color", "-webkit-background-clip",
|
|
233
|
+
"list-style", "list-style-type", "list-style-position", "content", "quotes", "counter-reset", "counter-increment", "counter-set",
|
|
234
|
+
"table-layout", "caption-side", "empty-cells",
|
|
235
|
+
// motion
|
|
236
|
+
"transform", "transform-origin", "transform-box", "transform-style", "translate", "rotate", "scale", "perspective", "perspective-origin", "backface-visibility",
|
|
237
|
+
"transition", "transition-property", "transition-duration", "transition-timing-function", "transition-delay", "transition-behavior",
|
|
238
|
+
"animation", "animation-name", "animation-duration", "animation-timing-function", "animation-delay", "animation-iteration-count", "animation-direction",
|
|
239
|
+
"animation-fill-mode", "animation-play-state", "animation-composition", "will-change",
|
|
240
|
+
// interaction
|
|
241
|
+
"cursor", "pointer-events", "user-select", "-webkit-user-select", "touch-action", "resize", "scroll-behavior", "overscroll-behavior", "overscroll-behavior-x", "overscroll-behavior-y",
|
|
242
|
+
"scroll-snap-type", "scroll-snap-align", "scroll-snap-stop", "scroll-margin", "scroll-padding", "scrollbar-width", "scrollbar-color", "scrollbar-gutter",
|
|
243
|
+
// SVG
|
|
244
|
+
"fill", "fill-opacity", "fill-rule", "stroke", "stroke-width", "stroke-linecap", "stroke-linejoin", "stroke-dasharray", "stroke-dashoffset", "stroke-opacity", "stroke-miterlimit",
|
|
245
|
+
"stop-color", "stop-opacity", "clip-rule", "marker", "marker-start", "marker-mid", "marker-end", "paint-order", "vector-effect", "shape-rendering", "text-anchor",
|
|
246
|
+
"dominant-baseline", "alignment-baseline", "r", "cx", "cy", "rx", "ry", "x", "y", "d", "mask",
|
|
247
|
+
];
|
|
248
|
+
/**
|
|
249
|
+
* The CSS functions a value may call. Every one makes a value of its own
|
|
250
|
+
* arguments; none names a resource. `url()` is not here: it is read apart,
|
|
251
|
+
* and kept only as `url(#id)` on a property in `CSS_FRAGMENT_PROPERTIES`.
|
|
252
|
+
*/
|
|
253
|
+
export const CSS_FUNCTIONS = [
|
|
254
|
+
"var", "env", "calc", "min", "max", "clamp", "round", "mod", "rem", "abs", "sign", "sin", "cos", "tan", "asin", "acos", "atan", "atan2", "pow", "sqrt", "hypot", "log", "exp",
|
|
255
|
+
"rgb", "rgba", "hsl", "hsla", "hwb", "lab", "lch", "oklab", "oklch", "color", "color-mix", "light-dark",
|
|
256
|
+
"linear-gradient", "radial-gradient", "conic-gradient", "repeating-linear-gradient", "repeating-radial-gradient", "repeating-conic-gradient",
|
|
257
|
+
"translate", "translatex", "translatey", "translatez", "translate3d", "rotate", "rotatex", "rotatey", "rotatez", "rotate3d", "scale", "scalex", "scaley", "scalez", "scale3d",
|
|
258
|
+
"skew", "skewx", "skewy", "matrix", "matrix3d", "perspective",
|
|
259
|
+
"cubic-bezier", "steps", "linear",
|
|
260
|
+
"repeat", "minmax", "fit-content",
|
|
261
|
+
"blur", "brightness", "contrast", "drop-shadow", "grayscale", "hue-rotate", "invert", "saturate", "sepia", "opacity",
|
|
262
|
+
"circle", "ellipse", "inset", "polygon", "path", "xywh", "rect",
|
|
263
|
+
"counter", "counters",
|
|
264
|
+
];
|
|
265
|
+
/**
|
|
266
|
+
* Where `url(#id)` stays: the paints and references an SVG drawing makes
|
|
267
|
+
* to its own gradients, clip paths, masks and markers. A fragment of the
|
|
268
|
+
* view's own drawing is resolved in the view's own tree and fetches
|
|
269
|
+
* nothing; anything else in a `url()` is refused wherever it is written.
|
|
270
|
+
*/
|
|
271
|
+
export const CSS_FRAGMENT_PROPERTIES = ["fill", "stroke", "clip-path", "mask", "marker", "marker-start", "marker-mid", "marker-end"];
|
|
272
|
+
/** The at-rules a stylesheet may hold. `@import`, `@font-face`, `@namespace`, `@property`, `@layer` and the rest are refused. */
|
|
273
|
+
export const CSS_AT_RULES = ["media", "supports", "container", "keyframes"];
|
|
274
|
+
/**
|
|
275
|
+
* The values a property may take when it may take only some: `position`
|
|
276
|
+
* stays in the region (no `fixed`, no `sticky`), and so does `var()` — a
|
|
277
|
+
* property held to a list is held to a keyword, so a custom property
|
|
278
|
+
* cannot carry `fixed` into it.
|
|
279
|
+
*/
|
|
280
|
+
export const CSS_KEYWORD_PROPERTIES = {
|
|
281
|
+
position: ["static", "relative", "absolute"],
|
|
282
|
+
/* Never `base-select`: a customizable select's picker is drawn in the top layer, over the whole page, styled by the view (Chrome 135+). */
|
|
283
|
+
appearance: ["auto", "none", "menulist-button", "textfield"],
|
|
284
|
+
"-webkit-appearance": ["auto", "none", "menulist-button", "textfield"],
|
|
285
|
+
};
|
|
286
|
+
/** Selectors a view's stylesheet may not write: each reaches past the view's own tree. */
|
|
287
|
+
export const CSS_REFUSED_SELECTORS = ["host", "host-context", "slotted", "part", "backdrop", "picker", "view-transition", "view-transition-group", "view-transition-image-pair", "view-transition-old", "view-transition-new"];
|
|
288
|
+
/** The largest stylesheet, in characters, a view may give. */
|
|
289
|
+
export const OPEN_MAX_STYLESHEET = 64_000;
|
|
290
|
+
/** The longest attribute value, in characters; geometry (`d`, `points`) may be longer. */
|
|
291
|
+
export const OPEN_MAX_ATTRIBUTE = 4_000;
|
|
292
|
+
export const OPEN_MAX_GEOMETRY = 100_000;
|
|
293
|
+
/** The largest image a view may hold, as its `data:` URL's length. */
|
|
294
|
+
export const OPEN_MAX_IMAGE = 512_000;
|
|
295
|
+
/** The longest text a node may hold, in characters; longer is cut. */
|
|
296
|
+
export const OPEN_MAX_TEXT = 10_000;
|
|
297
|
+
export const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
|
|
298
|
+
export const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
299
|
+
/** An SVG element's canonical name from any casing of it (`lineargradient` → `linearGradient`). */
|
|
300
|
+
const SVG_NAMES = new Map(Object.keys(SVG_ELEMENTS).map((name) => [name.toLowerCase(), name]));
|
|
301
|
+
export function svgElementName(name) {
|
|
302
|
+
return SVG_NAMES.get(name.toLowerCase());
|
|
303
|
+
}
|
|
304
|
+
/** The canonical name of an attribute an element takes, from any casing (`viewbox` → `viewBox`), with what it may hold; or undefined. */
|
|
305
|
+
export function openAttribute(namespace, element, name) {
|
|
306
|
+
const lower = name.toLowerCase();
|
|
307
|
+
const tables = namespace === "html" ? [HTML_ELEMENTS[element]?.attributes, HTML_GLOBAL_ATTRIBUTES] : [SVG_ELEMENTS[element]?.attributes, SVG_GLOBAL_ATTRIBUTES];
|
|
308
|
+
/* `xlink:href` is SVG 1's `href`; it is drawn as SVG 2's. */
|
|
309
|
+
const asked = namespace === "svg" && lower === "xlink:href" ? "href" : lower;
|
|
310
|
+
for (const table of tables) {
|
|
311
|
+
if (!table)
|
|
312
|
+
continue;
|
|
313
|
+
for (const [canonical, kind] of Object.entries(table))
|
|
314
|
+
if (canonical.toLowerCase() === asked)
|
|
315
|
+
return { name: canonical, kind };
|
|
316
|
+
}
|
|
317
|
+
if (isAriaAttribute(lower) || isDataAttribute(lower))
|
|
318
|
+
return { name: lower, kind: "text" };
|
|
319
|
+
return undefined;
|
|
320
|
+
}
|
|
321
|
+
//# sourceMappingURL=open-kit.js.map
|