@graview/guest 0.1.9 → 0.1.12

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.
Files changed (173) hide show
  1. package/README.md +353 -3
  2. package/client.js +2 -0
  3. package/dist/channel.d.ts +11 -3
  4. package/dist/channel.d.ts.map +1 -1
  5. package/dist/channel.js +5 -1
  6. package/dist/channel.js.map +1 -1
  7. package/dist/cli.d.ts +16 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +75 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/client-script.d.ts +3 -0
  12. package/dist/client-script.d.ts.map +1 -0
  13. package/dist/client-script.js +21 -0
  14. package/dist/client-script.js.map +1 -0
  15. package/dist/client.d.ts +2 -0
  16. package/dist/client.d.ts.map +1 -0
  17. package/dist/client.generated.d.ts +4 -0
  18. package/dist/client.generated.d.ts.map +1 -0
  19. package/dist/client.generated.js +9 -0
  20. package/dist/client.generated.js.map +1 -0
  21. package/dist/client.js +12 -0
  22. package/dist/client.js.map +1 -0
  23. package/dist/headless/describe.d.ts +19 -0
  24. package/dist/headless/describe.d.ts.map +1 -0
  25. package/dist/headless/describe.js +255 -0
  26. package/dist/headless/describe.js.map +1 -0
  27. package/dist/headless/draw.d.ts +17 -0
  28. package/dist/headless/draw.d.ts.map +1 -0
  29. package/dist/headless/draw.js +25 -0
  30. package/dist/headless/draw.js.map +1 -0
  31. package/dist/headless/index.d.ts +99 -0
  32. package/dist/headless/index.d.ts.map +1 -0
  33. package/dist/headless/index.js +143 -0
  34. package/dist/headless/index.js.map +1 -0
  35. package/dist/headless/judge.d.ts +16 -0
  36. package/dist/headless/judge.d.ts.map +1 -0
  37. package/dist/headless/judge.js +52 -0
  38. package/dist/headless/judge.js.map +1 -0
  39. package/dist/headless/node.d.ts +8 -0
  40. package/dist/headless/node.d.ts.map +1 -0
  41. package/dist/headless/node.js +80 -0
  42. package/dist/headless/node.js.map +1 -0
  43. package/dist/headless/protocol.d.ts +75 -0
  44. package/dist/headless/protocol.d.ts.map +1 -0
  45. package/dist/headless/protocol.js +14 -0
  46. package/dist/headless/protocol.js.map +1 -0
  47. package/dist/headless/runtime.d.ts +2 -0
  48. package/dist/headless/runtime.d.ts.map +1 -0
  49. package/dist/headless/runtime.generated.d.ts +2 -0
  50. package/dist/headless/runtime.generated.d.ts.map +1 -0
  51. package/dist/headless/runtime.generated.js +8 -0
  52. package/dist/headless/runtime.generated.js.map +1 -0
  53. package/dist/headless/runtime.js +171 -0
  54. package/dist/headless/runtime.js.map +1 -0
  55. package/dist/headless/tree.d.ts +54 -0
  56. package/dist/headless/tree.d.ts.map +1 -0
  57. package/dist/headless/tree.js +128 -0
  58. package/dist/headless/tree.js.map +1 -0
  59. package/dist/host/css.d.ts +118 -0
  60. package/dist/host/css.d.ts.map +1 -0
  61. package/dist/host/css.js +963 -0
  62. package/dist/host/css.js.map +1 -0
  63. package/dist/host/draw-budget.d.ts +10 -0
  64. package/dist/host/draw-budget.d.ts.map +1 -0
  65. package/dist/host/draw-budget.js +29 -0
  66. package/dist/host/draw-budget.js.map +1 -0
  67. package/dist/host/frame.d.ts +19 -1
  68. package/dist/host/frame.d.ts.map +1 -1
  69. package/dist/host/frame.js +21 -2
  70. package/dist/host/frame.js.map +1 -1
  71. package/dist/host/index.d.ts +4 -3
  72. package/dist/host/index.d.ts.map +1 -1
  73. package/dist/host/index.js +7 -1
  74. package/dist/host/index.js.map +1 -1
  75. package/dist/host/kit.d.ts +6 -2
  76. package/dist/host/kit.d.ts.map +1 -1
  77. package/dist/host/kit.js +12 -5
  78. package/dist/host/kit.js.map +1 -1
  79. package/dist/host/links.d.ts +24 -0
  80. package/dist/host/links.d.ts.map +1 -0
  81. package/dist/host/links.js +35 -0
  82. package/dist/host/links.js.map +1 -0
  83. package/dist/host/manifest.d.ts +50 -0
  84. package/dist/host/manifest.d.ts.map +1 -0
  85. package/dist/host/manifest.js +103 -0
  86. package/dist/host/manifest.js.map +1 -0
  87. package/dist/host/open-draw.d.ts +44 -0
  88. package/dist/host/open-draw.d.ts.map +1 -0
  89. package/dist/host/open-draw.js +108 -0
  90. package/dist/host/open-draw.js.map +1 -0
  91. package/dist/host/open-judge.d.ts +53 -0
  92. package/dist/host/open-judge.d.ts.map +1 -0
  93. package/dist/host/open-judge.js +95 -0
  94. package/dist/host/open-judge.js.map +1 -0
  95. package/dist/host/open-render.d.ts +32 -0
  96. package/dist/host/open-render.d.ts.map +1 -0
  97. package/dist/host/open-render.js +262 -0
  98. package/dist/host/open-render.js.map +1 -0
  99. package/dist/host/press.d.ts +15 -0
  100. package/dist/host/press.d.ts.map +1 -0
  101. package/dist/host/press.js +156 -0
  102. package/dist/host/press.js.map +1 -0
  103. package/dist/host/react.d.ts +15 -2
  104. package/dist/host/react.d.ts.map +1 -1
  105. package/dist/host/react.js +13 -5
  106. package/dist/host/react.js.map +1 -1
  107. package/dist/host/session.d.ts +94 -1
  108. package/dist/host/session.d.ts.map +1 -1
  109. package/dist/host/session.js +103 -14
  110. package/dist/host/session.js.map +1 -1
  111. package/dist/host/theme.d.ts +14 -0
  112. package/dist/host/theme.d.ts.map +1 -0
  113. package/dist/host/theme.js +51 -0
  114. package/dist/host/theme.js.map +1 -0
  115. package/dist/host/view-runtime.generated.d.ts +2 -0
  116. package/dist/host/view-runtime.generated.d.ts.map +1 -0
  117. package/dist/host/view-runtime.generated.js +7 -0
  118. package/dist/host/view-runtime.generated.js.map +1 -0
  119. package/dist/host/view-source.d.ts +10 -0
  120. package/dist/host/view-source.d.ts.map +1 -0
  121. package/dist/host/view-source.js +44 -0
  122. package/dist/host/view-source.js.map +1 -0
  123. package/dist/host/view.d.ts +138 -0
  124. package/dist/host/view.d.ts.map +1 -0
  125. package/dist/host/view.js +381 -0
  126. package/dist/host/view.js.map +1 -0
  127. package/dist/host/views.d.ts +10 -0
  128. package/dist/host/views.d.ts.map +1 -0
  129. package/dist/host/views.js +15 -0
  130. package/dist/host/views.js.map +1 -0
  131. package/dist/host/worker-react.d.ts +57 -0
  132. package/dist/host/worker-react.d.ts.map +1 -0
  133. package/dist/host/worker-react.js +129 -0
  134. package/dist/host/worker-react.js.map +1 -0
  135. package/dist/host/worker-start.d.ts +66 -0
  136. package/dist/host/worker-start.d.ts.map +1 -0
  137. package/dist/host/worker-start.js +188 -0
  138. package/dist/host/worker-start.js.map +1 -0
  139. package/dist/host/worker.d.ts +28 -19
  140. package/dist/host/worker.d.ts.map +1 -1
  141. package/dist/host/worker.js +60 -119
  142. package/dist/host/worker.js.map +1 -1
  143. package/dist/host/writes.d.ts +49 -0
  144. package/dist/host/writes.d.ts.map +1 -0
  145. package/dist/host/writes.js +76 -0
  146. package/dist/host/writes.js.map +1 -0
  147. package/dist/index.d.ts +1 -1
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/open-kit.d.ts +148 -0
  150. package/dist/open-kit.d.ts.map +1 -0
  151. package/dist/open-kit.js +321 -0
  152. package/dist/open-kit.js.map +1 -0
  153. package/dist/protocol.d.ts +109 -3
  154. package/dist/protocol.d.ts.map +1 -1
  155. package/dist/protocol.js +13 -0
  156. package/dist/protocol.js.map +1 -1
  157. package/dist/worker/harden.d.ts +13 -2
  158. package/dist/worker/harden.d.ts.map +1 -1
  159. package/dist/worker/harden.js +10 -4
  160. package/dist/worker/harden.js.map +1 -1
  161. package/dist/worker/natives.d.ts +1 -0
  162. package/dist/worker/natives.d.ts.map +1 -1
  163. package/dist/worker/natives.js +4 -0
  164. package/dist/worker/natives.js.map +1 -1
  165. package/dist/worker/view-global.d.ts +70 -0
  166. package/dist/worker/view-global.d.ts.map +1 -0
  167. package/dist/worker/view-global.js +264 -0
  168. package/dist/worker/view-global.js.map +1 -0
  169. package/dist/worker/view.d.ts +14 -0
  170. package/dist/worker/view.d.ts.map +1 -0
  171. package/dist/worker/view.js +64 -0
  172. package/dist/worker/view.js.map +1 -0
  173. package/package.json +30 -4
@@ -0,0 +1,143 @@
1
+ import { checkManifest, manifestActs, workerViewProps } from "../host/manifest.js";
2
+ import { checkViewSource } from "../host/view-source.js";
3
+ import { judgeTranscript } from "./judge.js";
4
+ import { HEADLESS_ENTRY, HEADLESS_VIEW } from "./protocol.js";
5
+ import { HEADLESS_RUNTIME } from "./runtime.generated.js";
6
+ export { describeDrawing } from "./describe.js";
7
+ export { drawTranscript } from "./draw.js";
8
+ export { judgeTranscript, sayRefusal } from "./judge.js";
9
+ export { HEADLESS_ENTRY, HEADLESS_VIEW } from "./protocol.js";
10
+ export { createTree, TreeDocument, TreeElement, TreeText } from "./tree.js";
11
+ /** The script a host's isolate runs for a view's source: the headless runtime, then the view, as a function handed to it at once. */
12
+ export function headlessScript(source) {
13
+ return `${HEADLESS_RUNTIME}\n;${HEADLESS_VIEW}(function (graview) {\n"use strict";\n${source}\n});\n`;
14
+ }
15
+ /** The framework's own look, light, for a run that is handed none. */
16
+ export const HEADLESS_THEME = {
17
+ scheme: "light",
18
+ accent: "#2f7d8c",
19
+ ground: "#f6f8f9",
20
+ panel: "#ffffff",
21
+ ink: "#14212b",
22
+ inkMuted: "#4f6470",
23
+ edge: "rgba(20, 33, 43, 0.14)",
24
+ fontBody: "system-ui, sans-serif",
25
+ fontMono: "ui-monospace, monospace",
26
+ };
27
+ const failed = (reason, detail, transcript) => ({ ok: false, reason, detail, ...(transcript ? { transcript } : {}) });
28
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
29
+ const REASONS = ["manifest", "source", "refused", "error", "silent", "nodes", "flood", "slow", "act", "isolate"];
30
+ /** A transcript as an isolate said it, held to its shape: anything else is no transcript. */
31
+ function transcriptOf(value) {
32
+ if (!isRecord(value))
33
+ return undefined;
34
+ const { renders, css, asked, went, ms, logs, messages } = value;
35
+ if (!Array.isArray(renders) || !renders.every(Array.isArray) || !Array.isArray(asked) || !Array.isArray(went) || !Array.isArray(logs))
36
+ return undefined;
37
+ if (typeof ms !== "number" || typeof messages !== "number" || (css !== undefined && typeof css !== "string"))
38
+ return undefined;
39
+ return {
40
+ renders: renders,
41
+ ...(typeof css === "string" ? { css } : {}),
42
+ asked: asked.filter(isRecord).map((one) => ({ name: String(one["name"]), args: one["args"] })),
43
+ went: went.filter(isRecord).flatMap((one) => (typeof one["record"] === "string" ? [{ record: one["record"] }] : typeof one["place"] === "string" ? [{ place: one["place"] }] : [])),
44
+ ms,
45
+ logs: logs.map(String).slice(0, 50),
46
+ messages,
47
+ };
48
+ }
49
+ /** The seat a description is for, said as `describePlace` says it. */
50
+ const seatOf = (principal) => (principal.kind === "system" ? "the system" : (principal.roles ?? []).length > 0 ? (principal.roles ?? []).join(", ") : principal.kind);
51
+ const slugOf = (title) => title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
52
+ /**
53
+ * RUN A WORKER VIEW ONCE, HEADLESS, IN THE HOST'S ISOLATE, AND SAY WHAT IT
54
+ * DREW FOR ONE SEAT. The manifest is checked against the app and the
55
+ * source against what a view may say before anything runs; the view is
56
+ * handed the seat's sight cut to its manifest; what comes back is judged
57
+ * here, in the host's context, from the transcript alone.
58
+ */
59
+ export async function runWorkerViewHeadless(options) {
60
+ if (typeof options.run !== "function") {
61
+ throw new TypeError("runWorkerViewHeadless runs a view only in an isolate the host supplies (`run`); it never runs one in the host's own context.");
62
+ }
63
+ const { manifest, source, store, principal } = options;
64
+ const limits = {
65
+ maxSourceBytes: options.limits?.maxSourceBytes ?? 256_000,
66
+ maxNodes: options.limits?.maxNodes ?? 5_000,
67
+ messages: options.limits?.messages ?? 120,
68
+ pushMs: options.limits?.pushMs ?? 1_000,
69
+ runMs: options.limits?.runMs ?? 5_000,
70
+ };
71
+ const findings = checkManifest(manifest, store);
72
+ if (findings.length > 0)
73
+ return failed("manifest", findings.join(" "));
74
+ if (typeof source !== "string")
75
+ return failed("source", "A view's source is text.");
76
+ if (new TextEncoder().encode(source).length > limits.maxSourceBytes)
77
+ return failed("source", `Its code is longer than the ${limits.maxSourceBytes.toLocaleString("en-US")} bytes a view may be.`);
78
+ const unloadable = checkViewSource(source);
79
+ if (unloadable.length > 0)
80
+ return failed("source", unloadable.join(" "));
81
+ const places = options.places ?? [];
82
+ const props = workerViewProps(store, principal, { manifest, ...(options.input ? { input: options.input } : {}), theme: options.theme ?? HEADLESS_THEME, places });
83
+ const input = {
84
+ props,
85
+ acts: manifestActs(manifest).map((one) => one.as ?? one.act),
86
+ places: places.map((place) => place.as),
87
+ limits: { maxNodes: limits.maxNodes, messages: limits.messages, pushMs: limits.pushMs },
88
+ };
89
+ /* The isolate, the host's: past `runMs` it is stopped, whatever its runner does. */
90
+ let text;
91
+ let stop;
92
+ try {
93
+ const ran = options.run({ script: headlessScript(source), entry: HEADLESS_ENTRY, input: JSON.stringify(input), timeoutMs: limits.runMs });
94
+ const late = new Promise((_, reject) => {
95
+ stop = setTimeout(() => reject(Object.assign(new Error("past runMs"), { reason: "slow" })), limits.runMs + 1_000);
96
+ });
97
+ text = await Promise.race([ran, late]);
98
+ }
99
+ catch (error) {
100
+ if (isRecord(error) && error["reason"] === "slow")
101
+ return failed("slow", `It ran longer than the ${limits.runMs.toLocaleString("en-US")} ms a headless run may take.`);
102
+ return failed("isolate", `The isolate could not run it: ${error instanceof Error ? error.message : String(error)}`);
103
+ }
104
+ finally {
105
+ if (stop !== undefined)
106
+ clearTimeout(stop);
107
+ }
108
+ let outcome;
109
+ try {
110
+ outcome = JSON.parse(String(text));
111
+ }
112
+ catch {
113
+ return failed("isolate", "The isolate answered with something that is not an outcome.");
114
+ }
115
+ if (!isRecord(outcome))
116
+ return failed("isolate", "The isolate answered with something that is not an outcome.");
117
+ const transcript = transcriptOf(outcome["transcript"]);
118
+ if (outcome["ok"] !== true) {
119
+ const reason = REASONS.includes(outcome["reason"]) ? outcome["reason"] : "isolate";
120
+ return failed(reason, typeof outcome["detail"] === "string" ? outcome["detail"].slice(0, 2_000) : "It failed, and the isolate did not say why.", transcript);
121
+ }
122
+ if (!transcript)
123
+ return failed("isolate", "The isolate answered without what the view sent.");
124
+ /* Judged again here, from the transcript alone. */
125
+ const judged = judgeTranscript(transcript, input);
126
+ if (!judged.ok)
127
+ return failed(judged.reason, judged.detail, transcript);
128
+ const title = manifest.title ?? manifest.name;
129
+ const slug = manifest.title ? slugOf(manifest.title) : manifest.name;
130
+ const kind = manifest.attach === "home" ? null : manifest.attach;
131
+ const description = {
132
+ place: { slug: manifest.attach === "home" ? "home" : slug, title, kind, address: manifest.attach === "home" ? "/" : `/places/${encodeURIComponent(slug)}` },
133
+ seat: seatOf(principal),
134
+ width: options.width ?? 1440,
135
+ variant: (options.width ?? 1440) < 640 ? "phone" : "wide",
136
+ drawnBy: `view:${manifest.name}`,
137
+ parts: judged.parts,
138
+ problems: judged.problems,
139
+ };
140
+ const { placeText } = await import("@graview/core/describe");
141
+ return { ok: true, description: { ...description, text: placeText(description) }, props, transcript };
142
+ }
143
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/headless/index.ts"],"names":[],"mappings":"AA4BA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,eAAe,EAA2B,MAAM,qBAAqB,CAAC;AAE5G,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,aAAa,EAA2E,MAAM,eAAe,CAAC;AACvI,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEzD,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9D,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AA2B5E,qIAAqI;AACrI,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,OAAO,GAAG,gBAAgB,MAAM,aAAa,yCAAyC,MAAM,SAAS,CAAC;AACxG,CAAC;AAsDD,sEAAsE;AACtE,MAAM,CAAC,MAAM,cAAc,GAAe;IACxC,MAAM,EAAE,OAAO;IACf,MAAM,EAAE,SAAS;IACjB,MAAM,EAAE,SAAS;IACjB,KAAK,EAAE,SAAS;IAChB,GAAG,EAAE,SAAS;IACd,QAAQ,EAAE,SAAS;IACnB,IAAI,EAAE,wBAAwB;IAC9B,QAAQ,EAAE,uBAAuB;IACjC,QAAQ,EAAE,yBAAyB;CACpC,CAAC;AAEF,MAAM,MAAM,GAAG,CAAC,MAA6B,EAAE,MAAc,EAAE,UAA+B,EAAmB,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;AAC3L,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAoC,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC5I,MAAM,OAAO,GAAqC,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;AAEnJ,6FAA6F;AAC7F,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACvC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IAChE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACxJ,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,GAAG,KAAK,SAAS,IAAI,OAAO,GAAG,KAAK,QAAQ,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/H,OAAO;QACL,OAAO,EAAE,OAAsB;QAC/B,GAAG,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3C,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC9F,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAwC,EAAE,CAAC,CAAC,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACzN,EAAE;QACF,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QACnC,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,sEAAsE;AACtE,MAAM,MAAM,GAAG,CAAC,SAAoB,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;AACjL,MAAM,MAAM,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAExG;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAsB,OAAwC;IACvG,IAAI,OAAO,OAAO,CAAC,GAAG,KAAK,UAAU,EAAE,CAAC;QACtC,MAAM,IAAI,SAAS,CAAC,8HAA8H,CAAC,CAAC;IACtJ,CAAC;IACD,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;IACvD,MAAM,MAAM,GAAG;QACb,cAAc,EAAE,OAAO,CAAC,MAAM,EAAE,cAAc,IAAI,OAAO;QACzD,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,IAAI,KAAK;QAC3C,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,IAAI,GAAG;QACzC,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,IAAI,KAAK;QACvC,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,IAAI,KAAK;KACtC,CAAC;IACF,MAAM,QAAQ,GAAG,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAChD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACvE,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,QAAQ,EAAE,0BAA0B,CAAC,CAAC;IACpF,IAAI,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,MAAM,CAAC,cAAc;QAAE,OAAO,MAAM,CAAC,QAAQ,EAAE,+BAA+B,MAAM,CAAC,cAAc,CAAC,cAAc,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAClM,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAEzE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC;IACpC,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,cAAc,EAAE,MAAM,EAAE,CAAC,CAAC;IAClK,MAAM,KAAK,GAAkB;QAC3B,KAAK;QACL,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,GAAG,CAAC;QAC5D,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;QACvC,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;KACxF,CAAC;IAEF,oFAAoF;IACpF,IAAI,IAAY,CAAC;IACjB,IAAI,IAA+C,CAAC;IACpD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;QAC1I,MAAM,IAAI,GAAG,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;YAC5C,IAAI,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC;QACpH,CAAC,CAAC,CAAC;QACH,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;IACzC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,MAAM;YAAE,OAAO,MAAM,CAAC,MAAM,EAAE,0BAA0B,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC;QACvK,OAAO,MAAM,CAAC,SAAS,EAAE,iCAAiC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACtH,CAAC;YAAS,CAAC;QACT,IAAI,IAAI,KAAK,SAAS;YAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAC7C,CAAC;IAED,IAAI,OAAgB,CAAC;IACrB,IAAI,CAAC;QACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,SAAS,EAAE,6DAA6D,CAAC,CAAC;IAC1F,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,MAAM,CAAC,SAAS,EAAE,6DAA6D,CAAC,CAAC;IAChH,MAAM,UAAU,GAAG,YAAY,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC;IACvD,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAA0B,CAAC,CAAC,CAAC,CAAE,OAAO,CAAC,QAAQ,CAA2B,CAAC,CAAC,CAAC,SAAS,CAAC;QACvI,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,OAAO,CAAC,QAAQ,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,6CAA6C,EAAE,UAAU,CAAC,CAAC;IAC/J,CAAC;IACD,IAAI,CAAC,UAAU;QAAE,OAAO,MAAM,CAAC,SAAS,EAAE,kDAAkD,CAAC,CAAC;IAE9F,mDAAmD;IACnD,MAAM,MAAM,GAAG,eAAe,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAClD,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACxE,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,IAAI,CAAC;IAC9C,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;IACrE,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;IACjE,MAAM,WAAW,GAAmC;QAClD,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,kBAAkB,CAAC,IAAI,CAAC,EAAE,EAAE;QAC3J,IAAI,EAAE,MAAM,CAAC,SAAS,CAAC;QACvB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI;QAC5B,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM;QACzD,OAAO,EAAE,QAAQ,QAAQ,CAAC,IAAI,EAAE;QAChC,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;KAC1B,CAAC;IACF,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;IAC7D,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,GAAG,WAAW,EAAE,IAAI,EAAE,SAAS,CAAC,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;AACxG,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { DescribedPart, DescribedProblem } from "@graview/core/describe";
2
+ import type { ViewRefusal } from "../host/open-draw.js";
3
+ import type { HeadlessFailureReason, HeadlessInput, HeadlessTranscript } from "./protocol.js";
4
+ export type Judged = {
5
+ readonly ok: true;
6
+ readonly parts: readonly DescribedPart[];
7
+ readonly problems: readonly DescribedProblem[];
8
+ } | {
9
+ readonly ok: false;
10
+ readonly reason: HeadlessFailureReason;
11
+ readonly detail: string;
12
+ };
13
+ /** One refusal of the open kit's, said to the author of the view. */
14
+ export declare function sayRefusal(refusal: ViewRefusal): DescribedProblem;
15
+ export declare function judgeTranscript(transcript: HeadlessTranscript, input: HeadlessInput): Judged;
16
+ //# sourceMappingURL=judge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"judge.d.ts","sourceRoot":"","sources":["../../src/headless/judge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGxD,OAAO,KAAK,EAAE,qBAAqB,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAW9F,MAAM,MAAM,MAAM,GACd;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,CAAA;CAAE,GAC/G;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5F,qEAAqE;AACrE,wBAAgB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,gBAAgB,CAqBjE;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,kBAAkB,EAAE,KAAK,EAAE,aAAa,GAAG,MAAM,CAmB5F"}
@@ -0,0 +1,52 @@
1
+ import { describeDrawing } from "./describe.js";
2
+ import { drawTranscript } from "./draw.js";
3
+ /** One refusal of the open kit's, said to the author of the view. */
4
+ export function sayRefusal(refusal) {
5
+ const element = "element" in refusal && refusal.element ? `<${refusal.element}>` : "";
6
+ const name = refusal.name ? ` ${refusal.name}` : "";
7
+ switch (refusal.reason) {
8
+ case "element":
9
+ return { at: `drawing ${element}`, says: `Not drawn: ${element} is not in the open kit.` };
10
+ case "attribute":
11
+ return { at: `drawing ${element}${name}`, says: `Not drawn: ${element || "an element"} does not take${name}.` };
12
+ case "value":
13
+ return { at: `drawing ${element}${name}`, says: `Not drawn: the value of${name} is not one the open kit takes.` };
14
+ case "url":
15
+ return { at: `drawing ${element}${name}`, says: `Not drawn: an address. A view loads nothing; an image is a data: image.` };
16
+ case "child":
17
+ return { at: `drawing ${element}`, says: `Not drawn: ${element} holds nothing.` };
18
+ case "record":
19
+ return { at: "drawing", says: "Not drawn: a record the drawing's format does not know." };
20
+ case "budget":
21
+ return { at: "drawing", says: "Not drawn: more nodes than a view may draw." };
22
+ case "css":
23
+ return { at: `stylesheet${name === " stylesheet" ? "" : name}`, says: `Not drawn: CSS the open kit leaves out${refusal.css ? ` (${refusal.css.reason}${refusal.css.name ? ` ${refusal.css.name}` : ""})` : ""}.` };
24
+ }
25
+ }
26
+ export function judgeTranscript(transcript, input) {
27
+ const { limits } = input;
28
+ if (transcript.messages > limits.messages)
29
+ return { ok: false, reason: "flood", detail: `It sent ${transcript.messages} messages, more than the ${limits.messages} a view may send in a second.` };
30
+ const acts = new Set(input.acts);
31
+ for (const asked of transcript.asked) {
32
+ if (!acts.has(asked.name))
33
+ return { ok: false, reason: "act", detail: `It asks for the act "${asked.name}", which its manifest does not name.` };
34
+ }
35
+ const drawn = drawTranscript(transcript.renders, transcript.css, { maxNodes: limits.maxNodes });
36
+ if (drawn.over)
37
+ return { ok: false, reason: "nodes", detail: `It drew more than the ${limits.maxNodes.toLocaleString("en-US")} things a view may draw.` };
38
+ const said = describeDrawing(drawn.root, { props: input.props, places: input.places });
39
+ for (const act of said.acts) {
40
+ if (!acts.has(act))
41
+ return { ok: false, reason: "act", detail: `It binds a press to the act "${act}" (data-act), which its manifest does not name.` };
42
+ }
43
+ const problems = [...drawn.refused.map(sayRefusal), ...said.problems];
44
+ for (const went of transcript.went) {
45
+ if ("record" in went && !(input.props.node?.id === went.record || (input.props.nodes ?? []).some((node) => node.id === went.record)))
46
+ problems.push({ at: "navigate", says: `It goes to "${went.record}", which this seat was not shown: the host will not follow it.` });
47
+ if ("place" in went && !input.places.includes(went.place))
48
+ problems.push({ at: "navigate", says: `It goes to the place "${went.place}", which this app does not have.` });
49
+ }
50
+ return { ok: true, parts: said.parts, problems };
51
+ }
52
+ //# sourceMappingURL=judge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"judge.js","sourceRoot":"","sources":["../../src/headless/judge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { HeadlessRun } from "./index.js";
2
+ export interface NodeIsolateOptions {
3
+ /** The thread's heap ceiling, in megabytes. 128 by default. */
4
+ readonly maxHeapMb?: number;
5
+ }
6
+ /** A `run` that gives each headless run a worker thread and a fresh, empty context of its own. */
7
+ export declare function nodeIsolate(options?: NodeIsolateOptions): HeadlessRun;
8
+ //# sourceMappingURL=node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/headless/node.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAmB,WAAW,EAAE,MAAM,YAAY,CAAC;AAE/D,MAAM,WAAW,kBAAkB;IACjC,+DAA+D;IAC/D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AA6BD,kGAAkG;AAClG,wBAAgB,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,WAAW,CA6BzE"}
@@ -0,0 +1,80 @@
1
+ /*
2
+ * AN ISOLATE FOR A HEADLESS RUN, IN NODE (FR-95): `@graview/guest/headless/node`.
3
+ *
4
+ * The reference `run` for `runWorkerViewHeadless`, for tests, for
5
+ * `graview view check`, and for a Node host that has nothing better. Each
6
+ * run gets a worker thread of its own, with a memory ceiling, and in it a
7
+ * fresh V8 context made from nothing: the language's own globals and no
8
+ * others — no `process`, no `require`, no `fetch`, no sockets, no timers —
9
+ * with code from strings and WebAssembly refused. The script is run there
10
+ * with a deadline that covers the microtasks it queues, and only text comes
11
+ * back. Past the deadline the context is abandoned and the thread ended.
12
+ *
13
+ * A context is not a security boundary in Node's own words, and this does
14
+ * not lean on it alone: nothing of the thread's is put into the context to
15
+ * be climbed back out of, the view is hardened inside it as a worker's
16
+ * would be, and the thread is ended when the run is done. A host that runs
17
+ * views it did not write, at scale, should run them in an isolate built
18
+ * for it — workerd with no outbound network is what Graview Cloud uses.
19
+ */
20
+ import { Worker } from "node:worker_threads";
21
+ /** What runs in the thread: the context, the script, the entry called once, and text back. */
22
+ const THREAD = `
23
+ const { parentPort, workerData } = require("node:worker_threads");
24
+ const vm = require("node:vm");
25
+ const { script, entry, input, timeoutMs } = workerData;
26
+ const deadline = Date.now() + timeoutMs;
27
+ const left = () => Math.max(1, deadline - Date.now());
28
+ const context = vm.createContext(Object.create(null), {
29
+ name: "graview headless view",
30
+ codeGeneration: { strings: false, wasm: false },
31
+ microtaskMode: "afterEvaluate",
32
+ });
33
+ try {
34
+ vm.runInContext(script, context, { timeout: left(), filename: "graview-headless-view.js" });
35
+ const read = vm.runInContext(
36
+ "(function () { var out; var run = globalThis[" + JSON.stringify(entry) + "]; if (typeof run === 'function') run(" + JSON.stringify(input) + ").then(function (text) { out = text; }); return function () { return out; }; })()",
37
+ context,
38
+ { timeout: left() },
39
+ );
40
+ const text = read();
41
+ parentPort.postMessage(typeof text === "string" ? { ok: true, text } : { ok: false, reason: "isolate", error: "The view's run did not finish: it waits on something a headless run never gives it." });
42
+ } catch (error) {
43
+ const late = error !== null && typeof error === "object" && error.code === "ERR_SCRIPT_EXECUTION_TIMEOUT";
44
+ parentPort.postMessage({ ok: false, reason: late ? "slow" : "isolate", error: late ? "It ran past its deadline." : String(error && typeof error === "object" && "message" in error ? error.message : error).slice(0, 500) });
45
+ }
46
+ `;
47
+ /** A `run` that gives each headless run a worker thread and a fresh, empty context of its own. */
48
+ export function nodeIsolate(options = {}) {
49
+ return (payload) => new Promise((resolve, reject) => {
50
+ const worker = new Worker(THREAD, {
51
+ eval: true,
52
+ workerData: { script: payload.script, entry: payload.entry, input: payload.input, timeoutMs: payload.timeoutMs },
53
+ resourceLimits: { maxOldGenerationSizeMb: options.maxHeapMb ?? 128 },
54
+ env: {},
55
+ stdout: true,
56
+ stderr: true,
57
+ });
58
+ let done = false;
59
+ const end = (settle) => {
60
+ if (done)
61
+ return;
62
+ done = true;
63
+ clearTimeout(timer);
64
+ void worker.terminate();
65
+ settle();
66
+ };
67
+ const refuse = (reason, message) => end(() => reject(Object.assign(new Error(message), { reason })));
68
+ /* The thread's own deadline covers the script and its microtasks; this one covers a thread that never answers. */
69
+ const timer = setTimeout(() => refuse("slow", "It ran past its deadline."), payload.timeoutMs + 500);
70
+ worker.once("message", (message) => {
71
+ if (message.ok && typeof message.text === "string")
72
+ end(() => resolve(message.text));
73
+ else
74
+ refuse(message.reason === "slow" ? "slow" : "isolate", message.error ?? "The isolate could not run it.");
75
+ });
76
+ worker.once("error", (error) => refuse("isolate", error.code === "ERR_WORKER_OUT_OF_MEMORY" ? "It used more memory than a headless run may." : error.message));
77
+ worker.once("exit", () => refuse("isolate", "The isolate stopped before it answered."));
78
+ });
79
+ }
80
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../../src/headless/node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAQ7C,8FAA8F;AAC9F,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;CAwBd,CAAC;AAEF,kGAAkG;AAClG,MAAM,UAAU,WAAW,CAAC,UAA8B,EAAE;IAC1D,OAAO,CAAC,OAAwB,EAAE,EAAE,CAClC,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE;YAChC,IAAI,EAAE,IAAI;YACV,UAAU,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE;YAChH,cAAc,EAAE,EAAE,sBAAsB,EAAE,OAAO,CAAC,SAAS,IAAI,GAAG,EAAE;YACpE,GAAG,EAAE,EAAE;YACP,MAAM,EAAE,IAAI;YACZ,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;QACH,IAAI,IAAI,GAAG,KAAK,CAAC;QACjB,MAAM,GAAG,GAAG,CAAC,MAAkB,EAAE,EAAE;YACjC,IAAI,IAAI;gBAAE,OAAO;YACjB,IAAI,GAAG,IAAI,CAAC;YACZ,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,KAAK,MAAM,CAAC,SAAS,EAAE,CAAC;YACxB,MAAM,EAAE,CAAC;QACX,CAAC,CAAC;QACF,MAAM,MAAM,GAAG,CAAC,MAA0B,EAAE,OAAe,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;QACjI,kHAAkH;QAClH,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,2BAA2B,CAAC,EAAE,OAAO,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC;QACrG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,OAAoF,EAAE,EAAE;YAC9G,IAAI,OAAO,CAAC,EAAE,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ;gBAAE,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAK,CAAC,CAAC,CAAC;;gBACjF,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,IAAI,+BAA+B,CAAC,CAAC;QAChH,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,EAAG,KAA2B,CAAC,IAAI,KAAK,0BAA0B,CAAC,CAAC,CAAC,8CAA8C,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QACtL,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,yCAAyC,CAAC,CAAC,CAAC;IAC1F,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,75 @@
1
+ import type { DescribedPart, DescribedProblem } from "@graview/core/describe";
2
+ import type { GuestProps } from "../protocol.js";
3
+ /** The global function a headless script leaves for its isolate to call, once. */
4
+ export declare const HEADLESS_ENTRY = "graviewHeadless";
5
+ /** The global a headless script hands the view to, as a function, on its own last line: run at once. */
6
+ export declare const HEADLESS_VIEW = "graviewHeadlessView";
7
+ /** What a run is told, as the JSON text of `HeadlessPayload.input`. */
8
+ export interface HeadlessInput {
9
+ /** What the view is shown: the seat's sight cut to its manifest (FR-91). */
10
+ readonly props: GuestProps;
11
+ /** What the view may ask for or bind a press to, by the name it uses: the manifest's acts. */
12
+ readonly acts: readonly string[];
13
+ /** The app's places, by slug. */
14
+ readonly places: readonly string[];
15
+ readonly limits: {
16
+ readonly maxNodes: number;
17
+ readonly messages: number;
18
+ readonly pushMs: number;
19
+ };
20
+ }
21
+ /** What the view said, in order: every message the host would have heard. */
22
+ export interface HeadlessTranscript {
23
+ /** Each batch of Remote DOM mutation records it sent. */
24
+ readonly renders: readonly unknown[][];
25
+ /** Its stylesheet, as it gave it. */
26
+ readonly css?: string;
27
+ /** The acts it asked for from its code. Nothing is applied in a headless run. */
28
+ readonly asked: readonly {
29
+ readonly name: string;
30
+ readonly args: unknown;
31
+ }[];
32
+ /** Where it asked to go. */
33
+ readonly went: readonly ({
34
+ readonly record: string;
35
+ } | {
36
+ readonly place: string;
37
+ })[];
38
+ /** The view's own time, at the top of its script and over the push, as the isolate's clock says it. */
39
+ readonly ms: number;
40
+ /** What it wrote to the console, the first 50 lines. */
41
+ readonly logs: readonly string[];
42
+ /** How many messages it sent. */
43
+ readonly messages: number;
44
+ }
45
+ /**
46
+ * Why a headless run says a view will not do: the live host's reasons
47
+ * (`WorkerViewFailure`) but `start`, which a run with no worker to start
48
+ * cannot meet; `refused`, the isolate's own refusal to run it; and two more.
49
+ */
50
+ export type HeadlessFailureReason = "manifest" | "source" | "refused" | "error" | "silent" | "nodes" | "flood" | "slow"
51
+ /** It names an act its manifest does not: asked from its code, or bound to a press (`data-act`). */
52
+ | "act"
53
+ /** The host's isolate could not run it, or answered with something that is not an outcome. */
54
+ | "isolate";
55
+ /** What an isolate answers, as the JSON text its entry resolves with. */
56
+ export type HeadlessOutcome = {
57
+ readonly ok: true;
58
+ readonly transcript: HeadlessTranscript;
59
+ /** What it drew, as the isolate read it. A host that has the transcript reads it again itself. */
60
+ readonly drawn: {
61
+ readonly parts: readonly DescribedPart[];
62
+ readonly problems: readonly DescribedProblem[];
63
+ };
64
+ /** How many names hardening took from the isolate's global, and whether it takes no new ones. */
65
+ readonly hardening: {
66
+ readonly removed: number;
67
+ readonly sealed: boolean;
68
+ };
69
+ } | {
70
+ readonly ok: false;
71
+ readonly reason: HeadlessFailureReason;
72
+ readonly detail: string;
73
+ readonly transcript?: HeadlessTranscript;
74
+ };
75
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/headless/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAYjD,kFAAkF;AAClF,eAAO,MAAM,cAAc,oBAAoB,CAAC;AAEhD,wGAAwG;AACxG,eAAO,MAAM,aAAa,wBAAwB,CAAC;AAEnD,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC5B,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,8FAA8F;IAC9F,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,iCAAiC;IACjC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CACpG;AAED,6EAA6E;AAC7E,MAAM,WAAW,kBAAkB;IACjC,yDAAyD;IACzD,QAAQ,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,EAAE,CAAC;IACvC,qCAAqC;IACrC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;IAC7E,4BAA4B;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,CAAC;IACrF,uGAAuG;IACvG,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,iCAAiC;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAC7B,UAAU,GACV,QAAQ,GACR,SAAS,GACT,OAAO,GACP,QAAQ,GACR,OAAO,GACP,OAAO,GACP,MAAM;AACR,oGAAoG;GAClG,KAAK;AACP,8FAA8F;GAC5F,SAAS,CAAC;AAEd,yEAAyE;AACzE,MAAM,MAAM,eAAe,GACvB;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAC;IACxC,kGAAkG;IAClG,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;QAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,CAAA;KAAE,CAAC;IAC7G,iGAAiG;IACjG,QAAQ,CAAC,SAAS,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC;CAC5E,GACD;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,kBAAkB,CAAA;CAAE,CAAC"}
@@ -0,0 +1,14 @@
1
+ /*
2
+ * WHAT A HOST AND A HEADLESS RUN SAY TO EACH OTHER (FR-95): text in, text
3
+ * out. The host hands its isolate one script and one JSON string; the
4
+ * isolate calls the script's entry with the string and hands back the
5
+ * string it resolves with. Nothing else crosses — no object, no function —
6
+ * so an isolate the host picked (workerd, a worker thread, a browser
7
+ * worker) is all a run needs, and the host's own context never runs a line
8
+ * of the view.
9
+ */
10
+ /** The global function a headless script leaves for its isolate to call, once. */
11
+ export const HEADLESS_ENTRY = "graviewHeadless";
12
+ /** The global a headless script hands the view to, as a function, on its own last line: run at once. */
13
+ export const HEADLESS_VIEW = "graviewHeadlessView";
14
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/headless/protocol.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,kFAAkF;AAClF,MAAM,CAAC,MAAM,cAAc,GAAG,iBAAiB,CAAC;AAEhD,wGAAwG;AACxG,MAAM,CAAC,MAAM,aAAa,GAAG,qBAAqB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import "@remote-dom/core/polyfill";
2
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/headless/runtime.ts"],"names":[],"mappings":"AA4BA,OAAO,2BAA2B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const HEADLESS_RUNTIME: string;
2
+ //# sourceMappingURL=runtime.generated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.generated.d.ts","sourceRoot":"","sources":["../../src/headless/runtime.generated.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,gBAAgB,EAAE,MAAihpG,CAAC"}