@botiverse/porthole 0.1.6 → 0.1.8
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/dist/config/schema.d.ts +26 -3
- package/dist/config/validate.js +29 -4
- package/dist/conformance/index.d.ts +2 -0
- package/dist/conformance/kit.d.ts +2 -1
- package/dist/conformance/limits.d.ts +10 -0
- package/dist/conformance/limits.js +523 -0
- package/dist/conformance/probes.js +2 -0
- package/dist/conformance/served.js +13 -0
- package/dist/guide/guide.js +34 -37
- package/dist/guide/limits.d.ts +24 -0
- package/dist/guide/limits.js +160 -0
- package/dist/host/create-host.js +9 -3
- package/dist/presets/presets.d.ts +3 -1
- package/dist/presets/presets.js +4 -0
- package/dist/react/frame.js +6 -2
- package/dist/shell/plan.d.ts +1 -1
- package/dist/shell/runtime.js +1 -1
- package/package.json +1 -1
- package/src/config/schema.ts +26 -3
- package/src/config/validate.ts +44 -4
- package/src/conformance/index.ts +2 -0
- package/src/conformance/kit.ts +2 -1
- package/src/conformance/limits.ts +580 -0
- package/src/conformance/probes.ts +2 -0
- package/src/conformance/served.ts +18 -0
- package/src/guide/guide.ts +40 -45
- package/src/guide/limits.ts +232 -0
- package/src/host/create-host.ts +9 -3
- package/src/presets/presets.ts +7 -1
- package/src/react/frame.tsx +6 -2
- package/src/shell/plan.ts +1 -1
- package/src/shell/runtime.ts +1 -1
package/dist/config/schema.d.ts
CHANGED
|
@@ -80,9 +80,11 @@ export interface ThemeConfig {
|
|
|
80
80
|
* Whether a whole document (an artifact page) takes the person's light or dark scheme:
|
|
81
81
|
* `declared` (default): only a page that declares it supports both
|
|
82
82
|
* (`<meta name="color-scheme" content="light dark">`); any other page shows as its author
|
|
83
|
-
* styled it, in light. `always`: every page.
|
|
83
|
+
* styled it, in light. `always`: every page. `never` (pages only, P065): the host has no
|
|
84
|
+
* theme for it: it always gets the light values, its frame says light, and no theme change
|
|
85
|
+
* reaches it. The variables are there in every case.
|
|
84
86
|
*/
|
|
85
|
-
readonly documents?: "declared" | "always";
|
|
87
|
+
readonly documents?: "declared" | "always" | "never";
|
|
86
88
|
}
|
|
87
89
|
export interface Limits {
|
|
88
90
|
/** The tallest the frame grows before its content scrolls inside it. */
|
|
@@ -126,6 +128,13 @@ export interface LayoutConfig {
|
|
|
126
128
|
readonly narrowWidthPx: number;
|
|
127
129
|
/** The widest the frame is (a desktop column). */
|
|
128
130
|
readonly wideWidthPx: number;
|
|
131
|
+
/**
|
|
132
|
+
* How the frame sits in the host (P065). `flow` (default): a card in a column, as tall as its
|
|
133
|
+
* content (up to `maxHeightPx`). `fill`: it fills the viewer (the page itself): as tall as its
|
|
134
|
+
* container, the content scrolling itself, `position: fixed` and `sticky` working. Every mount
|
|
135
|
+
* then fills (`MountOptions.fill`), and the manual says so.
|
|
136
|
+
*/
|
|
137
|
+
readonly kind?: "flow" | "fill";
|
|
129
138
|
}
|
|
130
139
|
export interface GuideConfig {
|
|
131
140
|
/** The host's tool that shows this content, as the manual names it (`show_widget`). */
|
|
@@ -173,8 +182,22 @@ export interface PortholeConfig {
|
|
|
173
182
|
* "conversation" }`), so the manual says storage is shared across it and keys need a prefix.
|
|
174
183
|
*/
|
|
175
184
|
readonly storage?: StorageConfig;
|
|
185
|
+
/**
|
|
186
|
+
* Where the content's own errors go, for the manual (P065): `agent`, back to the agent that
|
|
187
|
+
* wrote it; `host`, to the app only (the agent never sees them); `none`, nowhere (the config
|
|
188
|
+
* declares no `reportError`). Absent: the manual says they are reported to the app.
|
|
189
|
+
*/
|
|
190
|
+
readonly errors?: "agent" | "host" | "none";
|
|
176
191
|
}
|
|
177
192
|
export interface StorageConfig {
|
|
178
|
-
/**
|
|
193
|
+
/**
|
|
194
|
+
* What the displays sharing one origin at once have in common, as a noun ("conversation"):
|
|
195
|
+
* the manual tells authors to prefix their keys.
|
|
196
|
+
*/
|
|
179
197
|
readonly shared?: string;
|
|
198
|
+
/**
|
|
199
|
+
* What one display's storage is kept for, when only one display uses its origin at a time, in
|
|
200
|
+
* words ("this artifact, across its versions", P065): the manual says so, with no prefix advice.
|
|
201
|
+
*/
|
|
202
|
+
readonly scope?: string;
|
|
180
203
|
}
|
package/dist/config/validate.js
CHANGED
|
@@ -259,6 +259,7 @@ export function validateConfig(input) {
|
|
|
259
259
|
"sandbox",
|
|
260
260
|
"permissions",
|
|
261
261
|
"storage",
|
|
262
|
+
"errors",
|
|
262
263
|
], "config");
|
|
263
264
|
if (!MODES.includes(config.mode)) {
|
|
264
265
|
fail("config.mode", `must be one of ${MODES.join(", ")}`);
|
|
@@ -315,8 +316,12 @@ export function validateConfig(input) {
|
|
|
315
316
|
only(theme, ["variables", "event", "documents"], "config.theme");
|
|
316
317
|
if (theme.documents !== undefined &&
|
|
317
318
|
theme.documents !== "declared" &&
|
|
318
|
-
theme.documents !== "always"
|
|
319
|
-
|
|
319
|
+
theme.documents !== "always" &&
|
|
320
|
+
theme.documents !== "never") {
|
|
321
|
+
fail("config.theme.documents", 'must be "declared", "always" or "never"');
|
|
322
|
+
}
|
|
323
|
+
if (theme.documents === "never" && config.mode !== "artifact") {
|
|
324
|
+
fail("config.theme.documents", '"never" is for whole pages: a fragment takes the theme it is drawn in');
|
|
320
325
|
}
|
|
321
326
|
const variables = object(theme.variables, "config.theme.variables");
|
|
322
327
|
for (const [name, value] of Object.entries(variables)) {
|
|
@@ -361,7 +366,10 @@ export function validateConfig(input) {
|
|
|
361
366
|
fail("config.limits.minHeightPx", "must not exceed maxHeightPx");
|
|
362
367
|
}
|
|
363
368
|
const layout = object(config.layout, "config.layout");
|
|
364
|
-
only(layout, ["minWidthPx", "narrowWidthPx", "wideWidthPx"], "config.layout");
|
|
369
|
+
only(layout, ["minWidthPx", "narrowWidthPx", "wideWidthPx", "kind"], "config.layout");
|
|
370
|
+
if (layout.kind !== undefined && layout.kind !== "flow" && layout.kind !== "fill") {
|
|
371
|
+
fail("config.layout.kind", 'must be "flow" or "fill"');
|
|
372
|
+
}
|
|
365
373
|
for (const name of ["minWidthPx", "narrowWidthPx", "wideWidthPx"]) {
|
|
366
374
|
positive(layout[name], `config.layout.${name}`, 10_000);
|
|
367
375
|
}
|
|
@@ -421,7 +429,14 @@ export function validateConfig(input) {
|
|
|
421
429
|
}
|
|
422
430
|
if (config.storage !== undefined) {
|
|
423
431
|
const storage = object(config.storage, "config.storage");
|
|
424
|
-
only(storage, ["shared"], "config.storage");
|
|
432
|
+
only(storage, ["shared", "scope"], "config.storage");
|
|
433
|
+
if (storage.shared !== undefined && storage.scope !== undefined) {
|
|
434
|
+
fail("config.storage", "shared (several displays at once) or scope (one at a time), not both");
|
|
435
|
+
}
|
|
436
|
+
if (storage.scope !== undefined &&
|
|
437
|
+
(typeof storage.scope !== "string" || !/^[a-z][a-z ,'-]{0,59}$/u.test(storage.scope))) {
|
|
438
|
+
fail("config.storage.scope", 'must be short lowercase words ("this artifact, across its versions")');
|
|
439
|
+
}
|
|
425
440
|
if (config.isolation !== "domain") {
|
|
426
441
|
fail("config.storage", "only domain isolation keeps storage across displays");
|
|
427
442
|
}
|
|
@@ -430,6 +445,16 @@ export function validateConfig(input) {
|
|
|
430
445
|
fail("config.storage.shared", 'must be a short lowercase noun ("conversation")');
|
|
431
446
|
}
|
|
432
447
|
}
|
|
448
|
+
if (config.errors !== undefined) {
|
|
449
|
+
if (config.errors !== "agent" && config.errors !== "host" && config.errors !== "none") {
|
|
450
|
+
fail("config.errors", 'must be "agent", "host" or "none"');
|
|
451
|
+
}
|
|
452
|
+
if ((config.errors === "none") !== (capabilities.reportError === undefined)) {
|
|
453
|
+
fail("config.errors", config.errors === "none"
|
|
454
|
+
? '"none" means no reportError: drop the capability'
|
|
455
|
+
: `${JSON.stringify(config.errors)} needs the reportError capability, which carries them`);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
433
458
|
if (config.permissions !== undefined) {
|
|
434
459
|
if (config.mode !== "url") {
|
|
435
460
|
fail("config.permissions", "only url mode may be delegated features");
|
|
@@ -39,6 +39,8 @@ export interface LaunchSpec {
|
|
|
39
39
|
readonly scheme?: ColorScheme;
|
|
40
40
|
/** The frame's width, px. */
|
|
41
41
|
readonly widthPx?: number;
|
|
42
|
+
/** The height of the box the frame sits in, px: for a layout that fills it (`layout.kind`). */
|
|
43
|
+
readonly heightPx?: number;
|
|
42
44
|
/** The host's live values for theme variables, per scheme (as `HostOptions.theme` answers). */
|
|
43
45
|
readonly hostTheme?: {
|
|
44
46
|
readonly light?: Readonly<Record<string, string>>;
|
|
@@ -31,7 +31,8 @@ export interface Probe {
|
|
|
31
31
|
readonly id: string;
|
|
32
32
|
/**
|
|
33
33
|
* The DESIGN.md egress-table item it proves; 0 for the shell's own wiring and interop, 11 for
|
|
34
|
-
* behavior (streaming, sizing, theming), 12 for host-function gates, 13 for the clipboard
|
|
34
|
+
* behavior (streaming, sizing, theming), 12 for host-function gates, 13 for the clipboard, 14
|
|
35
|
+
* for the manual's claims about the sandbox (`LIMIT_CHECKS`).
|
|
35
36
|
*/
|
|
36
37
|
readonly item: number;
|
|
37
38
|
readonly title: string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { LimitId } from "../guide/limits.ts";
|
|
2
|
+
import { type Probe } from "./kit.ts";
|
|
3
|
+
export declare const LIMIT_PROBES: readonly Probe[];
|
|
4
|
+
/**
|
|
5
|
+
* The checks that prove each limit the manual can state: leak-suite probes, and served cases for
|
|
6
|
+
* what holds in domain mode. A unit test fails if a limit names none, or one that doesn't exist.
|
|
7
|
+
*/
|
|
8
|
+
export declare const LIMIT_CHECKS: {
|
|
9
|
+
readonly [Id in LimitId]: readonly string[];
|
|
10
|
+
};
|