@jigs.design/runtime 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,29 +1,26 @@
1
1
  # @jigs.design/runtime
2
2
 
3
- The layer the server and the browser share: the program format, the types on the wire, the sandboxed runtime, and the tree store. It imports neither the compiler nor the prompt.
3
+ The sandbox a program runs in: the frame document and its content security policy, the worker runtime, and the tree store the page draws from. The types it speaks are in `@jigs.design/protocol`, its only dependency. It imports neither the compiler nor the prompt.
4
4
 
5
- `@jigs.design/server`, `@jigs.design/client` and `@jigs.design/react` re-export the shared types, so most code imports them from one of those.
5
+ `@jigs.design/client` uses the tree store and the policy; `@jigs.design/server` serves the frame. An app rarely imports it directly.
6
6
 
7
7
  ## Entry points
8
8
 
9
- | Entry | What it holds |
10
- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
11
- | `@jigs.design/runtime` | `Program`, `SessionEvent`, `Message`, `Diagnostic` and the other shared types; the frame protocol (`HostMessage`, `FrameMessage`, `Op`); the JSON checks the browser runs; `createTreeStore` with its caps `MAX_NODES`, `MAX_OPS` and `MAX_TEXT`; the image and link allowlists |
12
- | `@jigs.design/runtime/frame` | The sandbox frame document, its content security policy, the worker runtime, and `WATCHDOG_MS` and `HEARTBEAT_MS`. The server serves it |
9
+ | Entry | What it holds |
10
+ | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
11
+ | `@jigs.design/runtime` | `createTreeStore` with its caps `MAX_NODES`, `MAX_OPS` and `MAX_TEXT`, the `TreeNode` and `TreeStore` types, and `frameSecurityPolicy` |
12
+ | `@jigs.design/runtime/frame` | The sandbox frame document, its content security policy, the worker runtime, and `WATCHDOG_MS` and `HEARTBEAT_MS`. The server serves it |
13
13
 
14
14
  ## How a program runs
15
15
 
16
- The server serves the frame document from its route. The frame starts a worker and runs the compiled program there, with a watchdog that kills a program that stops answering. The worker sends tree ops to the page, where the tree store applies them, within the same caps on nodes, ops and text the worker keeps, and the view draws the tree. Functions never leave the worker; they cross as ids. The browser checks every value from the server with `parse.ts`, since it imports no schema library.
16
+ The server serves the frame document from its route. The frame starts a worker and runs the compiled program there, with a watchdog that kills a program that stops answering. The worker sends tree ops to the page, where the tree store applies them, within the same caps on nodes, ops and text the worker keeps, and the view draws the tree. Functions never leave the worker; they cross as ids. The worker and the frame's runner ship as `Function.prototype.toString()` text, so `scripts/sandbox-check.ts` boots the built output after every build.
17
17
 
18
18
  ## Source
19
19
 
20
- - `src/types.ts`: the shared types and `PROGRAM_FORMAT`.
21
- - `src/protocol.ts`: messages between the page, the frame and the worker.
22
- - `src/parse.ts`: checks for JSON arriving in the browser.
23
20
  - `src/frame.ts`, `src/frame-policy.ts`: the frame document and its policy.
24
21
  - `src/runtime-core.ts`: the runtime inside the worker.
25
22
  - `src/tree-store.ts`: the tree the view draws.
26
- - `src/urls.ts`, `src/validate.ts`: allowed images and links, and prop checks.
23
+ - `src/validate.ts`: prop checks against the program's schemas.
27
24
 
28
25
  ## Docs
29
26
 
package/dist/index.d.ts CHANGED
@@ -1,7 +1,3 @@
1
1
  import { frameSecurityPolicy } from "./frame-policy.js";
2
- import { ChatBlock, ChatBlockStatus, ChildrenKind, ComponentSchema, Diagnostic, DiagnosticFix, Invalidation, JsonValue, Message, NumberPropSchema, PROGRAM_FORMAT, PickleErrorCode, PickleErrorData, ProcedureEntry, ProcedureKind, Program, PropSchema, SessionBody, SessionEvent, SessionOptions, Span, pickleErrorCodes } from "./types.js";
3
- import { FrameMessage, HostMessage, MutationCall, MutationResult, Op, QueryRead, QueryResult, ROOT_ID, Row, RuntimeError, WireArg, WireValue, WorkerMessage } from "./protocol.js";
4
- import { parseErrorData, parseInvalidations, parseProgram, parseSessionEvent } from "./parse.js";
5
2
  import { MAX_NODES, MAX_OPS, MAX_TEXT, TreeNode, TreeStore, createTreeStore } from "./tree-store.js";
6
- import { isAllowedImage, isAllowedLink, isImageHostEntry, normalizeImageHost } from "./urls.js";
7
- export { type ChatBlock, type ChatBlockStatus, type ChildrenKind, type ComponentSchema, type Diagnostic, type DiagnosticFix, type FrameMessage, type HostMessage, type Invalidation, type JsonValue, MAX_NODES, MAX_OPS, MAX_TEXT, type Message, type MutationCall, type MutationResult, type NumberPropSchema, type Op, PROGRAM_FORMAT, type PickleErrorCode, type PickleErrorData, type ProcedureEntry, type ProcedureKind, type Program, type PropSchema, type QueryRead, type QueryResult, ROOT_ID, type Row, type RuntimeError, type SessionBody, type SessionEvent, type SessionOptions, type Span, type TreeNode, type TreeStore, type WireArg, type WireValue, type WorkerMessage, createTreeStore, frameSecurityPolicy, isAllowedImage, isAllowedLink, isImageHostEntry, normalizeImageHost, parseErrorData, parseInvalidations, parseProgram, parseSessionEvent, pickleErrorCodes };
3
+ export { MAX_NODES, MAX_OPS, MAX_TEXT, type TreeNode, type TreeStore, createTreeStore, frameSecurityPolicy };
package/dist/index.js CHANGED
@@ -1,7 +1,3 @@
1
- import { PROGRAM_FORMAT, pickleErrorCodes } from "./types.js";
2
- import { ROOT_ID } from "./protocol.js";
3
1
  import { frameSecurityPolicy } from "./frame-policy.js";
4
- import { parseErrorData, parseInvalidations, parseProgram, parseSessionEvent } from "./parse.js";
5
- import { isAllowedImage, isAllowedLink, isImageHostEntry, normalizeImageHost } from "./urls.js";
6
2
  import { MAX_NODES, MAX_OPS, MAX_TEXT, createTreeStore } from "./tree-store.js";
7
- export { MAX_NODES, MAX_OPS, MAX_TEXT, PROGRAM_FORMAT, ROOT_ID, createTreeStore, frameSecurityPolicy, isAllowedImage, isAllowedLink, isImageHostEntry, normalizeImageHost, parseErrorData, parseInvalidations, parseProgram, parseSessionEvent, pickleErrorCodes };
3
+ export { MAX_NODES, MAX_OPS, MAX_TEXT, createTreeStore, frameSecurityPolicy };
@@ -1,4 +1,4 @@
1
- import { HostMessage, WorkerMessage } from "./protocol.js";
1
+ import { HostMessage, WorkerMessage } from "@jigs.design/protocol";
2
2
  //#region src/runtime-core.d.ts
3
3
  export interface Runtime {
4
4
  handle: (message: HostMessage) => void;
@@ -11,7 +11,7 @@ export interface Runtime {
11
11
  * and fine). `FunctionCtor` is passed in because the worker removes dynamic
12
12
  * code evaluation from its globals before any program runs.
13
13
  *
14
- * A small React-style reconciler: programs build a tree with `__pickle.jsx`,
14
+ * A small React-style reconciler: programs build a tree with `__jigs.jsx`,
15
15
  * the runtime diffs it against the previous render by position and type, and
16
16
  * posts the differences as `Op`s. Each render also reports the queries it read
17
17
  * and the mutations its handlers called; the page runs those through the
@@ -7,7 +7,7 @@
7
7
  * and fine). `FunctionCtor` is passed in because the worker removes dynamic
8
8
  * code evaluation from its globals before any program runs.
9
9
  *
10
- * A small React-style reconciler: programs build a tree with `__pickle.jsx`,
10
+ * A small React-style reconciler: programs build a tree with `__jigs.jsx`,
11
11
  * the runtime diffs it against the previous render by position and type, and
12
12
  * posts the differences as `Op`s. Each render also reports the queries it read
13
13
  * and the mutations its handlers called; the page runs those through the
@@ -244,7 +244,7 @@ function createRuntime(post, FunctionCtor) {
244
244
  status: state.status
245
245
  });
246
246
  };
247
- const Pickle = Object.freeze({
247
+ const Jigs = Object.freeze({
248
248
  render: (element) => {
249
249
  rootElement = element;
250
250
  },
@@ -325,7 +325,7 @@ function createRuntime(post, FunctionCtor) {
325
325
  componentDepth--;
326
326
  }
327
327
  };
328
- const pickle = Object.freeze({
328
+ const jigs = Object.freeze({
329
329
  call: callComponent,
330
330
  each,
331
331
  Fragment: FRAGMENT,
@@ -335,7 +335,7 @@ function createRuntime(post, FunctionCtor) {
335
335
  type
336
336
  })
337
337
  });
338
- const pickleSafe = (evaluate, fallback, line) => {
338
+ const jigsSafe = (evaluate, fallback, line) => {
339
339
  try {
340
340
  return evaluate();
341
341
  } catch (error) {
@@ -591,7 +591,7 @@ function createRuntime(post, FunctionCtor) {
591
591
  reads = /* @__PURE__ */ new Map();
592
592
  const next = [];
593
593
  try {
594
- run(Pickle, pickle, pickleSafe);
594
+ run(Jigs, jigs, jigsSafe);
595
595
  expand(rootElement, next);
596
596
  } catch (error) {
597
597
  reads = previousReads;
@@ -682,7 +682,7 @@ function createRuntime(post, FunctionCtor) {
682
682
  unstableSites.clear();
683
683
  querySites = /* @__PURE__ */ new Map();
684
684
  try {
685
- candidate = new FunctionCtor("Pickle", "__pickle", "__pickleSafe", message.code);
685
+ candidate = new FunctionCtor("Jigs", "__jigs", "__jigsSafe", message.code);
686
686
  } catch (error) {
687
687
  prose = previousProse;
688
688
  procedures = previousProcedures;
@@ -1,5 +1,4 @@
1
- import { ComponentSchema } from "./types.js";
2
- import { Op, WireValue } from "./protocol.js";
1
+ import { ComponentSchema, Op, WireValue } from "@jigs.design/protocol";
3
2
  //#region src/tree-store.d.ts
4
3
  export interface TreeNode {
5
4
  children: ReadonlyArray<number>;
@@ -1,5 +1,5 @@
1
- import "./protocol.js";
2
1
  import { acceptsProp } from "./validate.js";
2
+ import { ROOT_ID } from "@jigs.design/protocol";
3
3
  //#region src/tree-store.ts
4
4
  const ROOT_TYPE = "#root";
5
5
  const TEXT_TYPE = "#text";
@@ -46,7 +46,7 @@ function isWellFormed(op) {
46
46
  }
47
47
  const rootNode = () => ({
48
48
  children: [],
49
- id: 0,
49
+ id: ROOT_ID,
50
50
  props: {},
51
51
  text: "",
52
52
  type: ROOT_TYPE
@@ -107,7 +107,7 @@ const MAX_WALK_STEPS = 64 * MAX_OPS;
107
107
  * `useSyncExternalStore` snapshot.
108
108
  */
109
109
  function createTreeStore() {
110
- const nodes = /* @__PURE__ */ new Map([[0, rootNode()]]);
110
+ const nodes = /* @__PURE__ */ new Map([[ROOT_ID, rootNode()]]);
111
111
  const parents = /* @__PURE__ */ new Map();
112
112
  /** Children being rebuilt by the current `apply`, by parent. */
113
113
  const drafts = /* @__PURE__ */ new Map();
@@ -216,7 +216,7 @@ function createTreeStore() {
216
216
  reportTooMany();
217
217
  for (let index = created.length - 1; index >= 0; index--) {
218
218
  if (nodes.size <= 10001) break;
219
- const id = created[index] ?? 0;
219
+ const id = created[index] ?? ROOT_ID;
220
220
  if (nodes.has(id)) {
221
221
  parents.delete(id);
222
222
  removeSubtree(id);
@@ -230,7 +230,7 @@ function createTreeStore() {
230
230
  return;
231
231
  }
232
232
  if (op.op === "create" || op.op === "createText") {
233
- if (op.id === 0 || nodes.has(op.id)) {
233
+ if (op.id === ROOT_ID || nodes.has(op.id)) {
234
234
  report("The sandbox reused a node id; the operation was dropped");
235
235
  return;
236
236
  }
@@ -276,13 +276,13 @@ function createTreeStore() {
276
276
  } : node);
277
277
  return;
278
278
  case "place":
279
- if (!nodes.has(op.id) || !nodes.has(op.parent) || op.id === 0 || isWithin(op.parent, op.id)) return;
279
+ if (!nodes.has(op.id) || !nodes.has(op.parent) || op.id === ROOT_ID || isWithin(op.parent, op.id)) return;
280
280
  detach(op.id);
281
281
  if (!insert(draftOf(op.parent), op.id, op.index)) slow++;
282
282
  parents.set(op.id, op.parent);
283
283
  return;
284
284
  case "remove":
285
- if (op.id === 0) return;
285
+ if (op.id === ROOT_ID) return;
286
286
  detach(op.id);
287
287
  removeSubtree(op.id);
288
288
  return;
@@ -320,7 +320,7 @@ function createTreeStore() {
320
320
  nodes.clear();
321
321
  parents.clear();
322
322
  drafts.clear();
323
- nodes.set(0, rootNode());
323
+ nodes.set(ROOT_ID, rootNode());
324
324
  issues = [];
325
325
  notify();
326
326
  },
package/dist/validate.js CHANGED
@@ -1,4 +1,4 @@
1
- import { isAllowedImage, isAllowedLink } from "./urls.js";
1
+ import { isAllowedImage, isAllowedLink } from "@jigs.design/protocol";
2
2
  //#region src/validate.ts
3
3
  const MAX_ROWS = 500;
4
4
  const MAX_ROW_KEYS = 20;
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@jigs.design/runtime",
3
- "version": "0.1.1",
4
- "description": "The program format, the types the server and the browser share, the sandboxed runtime, and the tree store.",
3
+ "version": "0.2.0",
4
+ "description": "The sandbox: the frame document, the worker runtime that runs a compiled program, and the tree store the page draws from.",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "git+https://github.com/yoamomonstruos/pickle.git",
8
+ "url": "git+https://github.com/yoamomonstruos/jigs.git",
9
9
  "directory": "packages/runtime"
10
10
  },
11
11
  "type": "module",
@@ -27,5 +27,8 @@
27
27
  },
28
28
  "publishConfig": {
29
29
  "access": "public"
30
+ },
31
+ "dependencies": {
32
+ "@jigs.design/protocol": "0.2.0"
30
33
  }
31
34
  }
package/dist/parse.d.ts DELETED
@@ -1,9 +0,0 @@
1
- import { Invalidation, PickleErrorData, Program, SessionEvent } from "./types.js";
2
- //#region src/parse.d.ts
3
- export declare function parseErrorData(value: unknown): PickleErrorData | null;
4
- /** The `invalidate` list of a mutation's reply. */
5
- export declare function parseInvalidations(value: unknown): ReadonlyArray<Invalidation> | null;
6
- export declare function parseProgram(value: unknown): Program | null;
7
- /** One session event from its JSON text, or null when it is not one. */
8
- export declare function parseSessionEvent(text: string): SessionEvent | null;
9
- //#endregion
package/dist/parse.js DELETED
@@ -1,150 +0,0 @@
1
- import { pickleErrorCodes } from "./types.js";
2
- //#region src/parse.ts
3
- const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
4
- const isString = (value) => typeof value === "string";
5
- const isStringList = (value) => Array.isArray(value) && value.every(isString);
6
- function recordOf(value, item) {
7
- if (!isRecord(value)) return null;
8
- const out = {};
9
- for (const [key, entry] of Object.entries(value)) {
10
- const parsed = item(entry);
11
- if (parsed === null) return null;
12
- out[key] = parsed;
13
- }
14
- return out;
15
- }
16
- const isErrorCode = (value) => pickleErrorCodes.some((code) => code === value);
17
- function parseErrorData(value) {
18
- if (!isRecord(value) || !isErrorCode(value.code) || !isString(value.message)) return null;
19
- return {
20
- code: value.code,
21
- message: value.message
22
- };
23
- }
24
- function parseInvalidation(value) {
25
- if (!isRecord(value) || !isString(value.name)) return null;
26
- if (!("input" in value)) return { name: value.name };
27
- return {
28
- input: value.input,
29
- name: value.name
30
- };
31
- }
32
- /** The `invalidate` list of a mutation's reply. */
33
- function parseInvalidations(value) {
34
- if (!Array.isArray(value)) return null;
35
- const out = [];
36
- for (const entry of value) {
37
- const parsed = parseInvalidation(entry);
38
- if (parsed === null) return null;
39
- out.push(parsed);
40
- }
41
- return out;
42
- }
43
- function parsePropSchema(value) {
44
- if (!isRecord(value)) return null;
45
- switch (value.kind) {
46
- case "boolean":
47
- case "handler":
48
- case "image":
49
- case "link":
50
- case "list":
51
- case "string": return { kind: value.kind };
52
- case "enum": return isStringList(value.values) ? {
53
- kind: "enum",
54
- values: value.values
55
- } : null;
56
- case "number": {
57
- const schema = { kind: "number" };
58
- if (typeof value.min === "number") schema.min = value.min;
59
- if (typeof value.max === "number") schema.max = value.max;
60
- return schema;
61
- }
62
- default: return null;
63
- }
64
- }
65
- const isChildrenKind = (value) => value === "block" || value === "inline" || value === "none";
66
- function parseComponentSchema(value) {
67
- if (!isRecord(value) || !isChildrenKind(value.children)) return null;
68
- const props = recordOf(value.props, parsePropSchema);
69
- return props === null ? null : {
70
- children: value.children,
71
- props
72
- };
73
- }
74
- function parseProcedure(value) {
75
- if (!isRecord(value) || value.kind !== "query" && value.kind !== "mutation" && value.kind !== "action" || !isStringList(value.invalidates)) return null;
76
- return {
77
- invalidates: value.invalidates,
78
- kind: value.kind
79
- };
80
- }
81
- function parseProgram(value) {
82
- if (!isRecord(value) || value.format !== 1 || !isString(value.code) || !isStringList(value.imageHosts)) return null;
83
- const components = recordOf(value.components, parseComponentSchema);
84
- const procedures = recordOf(value.procedures, parseProcedure);
85
- const prose = recordOf(value.prose, (entry) => isString(entry) ? entry : null);
86
- if (components === null || procedures === null || prose === null) return null;
87
- return {
88
- code: value.code,
89
- components,
90
- format: 1,
91
- imageHosts: value.imageHosts,
92
- procedures,
93
- prose
94
- };
95
- }
96
- function parseDiagnostic(value) {
97
- if (!isRecord(value) || !isString(value.code) || !isString(value.message) || value.severity !== "error" && value.severity !== "warning" && value.severity !== "info" && value.severity !== "hint" || !isRecord(value.span) || typeof value.span.line !== "number") return null;
98
- return {
99
- code: value.code,
100
- message: value.message,
101
- severity: value.severity,
102
- span: { line: value.span.line }
103
- };
104
- }
105
- /** The diagnostics that parse; one the browser cannot read is skipped, not the event. */
106
- function parseDiagnostics(value) {
107
- if (!Array.isArray(value)) return null;
108
- return value.flatMap((entry) => {
109
- const parsed = parseDiagnostic(entry);
110
- return parsed === null ? [] : [parsed];
111
- });
112
- }
113
- /** One session event from its JSON text, or null when it is not one. */
114
- function parseSessionEvent(text) {
115
- let value;
116
- try {
117
- value = JSON.parse(text);
118
- } catch {
119
- return null;
120
- }
121
- if (!isRecord(value)) return null;
122
- if (value.type === "error") {
123
- const error = parseErrorData(value.error);
124
- return error === null ? null : {
125
- error,
126
- type: "error"
127
- };
128
- }
129
- const diagnostics = parseDiagnostics(value.diagnostics);
130
- if (diagnostics === null || typeof value.revision !== "number") return null;
131
- if (value.type === "program") {
132
- const program = parseProgram(value.program);
133
- return program === null ? null : {
134
- diagnostics,
135
- fresh: value.fresh === true,
136
- program,
137
- revision: value.revision,
138
- type: "program"
139
- };
140
- }
141
- if (value.type === "done" && isString(value.source)) return {
142
- diagnostics,
143
- revision: value.revision,
144
- source: value.source,
145
- type: "done"
146
- };
147
- return null;
148
- }
149
- //#endregion
150
- export { parseErrorData, parseInvalidations, parseProgram, parseSessionEvent };
@@ -1,132 +0,0 @@
1
- import { PickleErrorData, ProcedureEntry } from "./types.js";
2
- //#region src/protocol.d.ts
3
- /**
4
- * Prop value on the wire. Functions never leave the worker; they travel as
5
- * `{ fn }` ids. Lists of plain objects travel as `{ rows }`.
6
- */
7
- export type WireValue = boolean | number | string | {
8
- fn: string;
9
- } | {
10
- rows: ReadonlyArray<Row>;
11
- };
12
- /** One row of a list prop: flat fields only. */
13
- export type Row = Readonly<Record<string, boolean | number | string | null>>;
14
- export type WireArg = boolean | number | string;
15
- /** The props of one node on the wire. */
16
- export type WireProps = Record<string, WireValue>;
17
- /** UI operations produced by the worker's reconciler. The root container has id 0. */
18
- export type Op = {
19
- id: number;
20
- op: "create";
21
- props: WireProps;
22
- type: string;
23
- } | {
24
- id: number;
25
- op: "createText";
26
- text: string;
27
- } | {
28
- id: number;
29
- index: number;
30
- op: "place";
31
- parent: number;
32
- } | {
33
- id: number;
34
- op: "remove";
35
- } | {
36
- id: number;
37
- op: "set";
38
- props: Record<string, WireValue | null>;
39
- } | {
40
- id: number;
41
- op: "text";
42
- text: string;
43
- };
44
- export interface RuntimeError {
45
- line: number | null;
46
- message: string;
47
- }
48
- /** A query as the program sees it from `useQuery`. */
49
- export type QueryResult = {
50
- data: null;
51
- error: null;
52
- status: "pending";
53
- } | {
54
- data: unknown;
55
- error: null;
56
- status: "success";
57
- } | {
58
- data: null;
59
- error: PickleErrorData;
60
- status: "error";
61
- };
62
- /** How a mutation settled. */
63
- export type MutationResult = {
64
- data: unknown;
65
- status: "success";
66
- } | {
67
- error: PickleErrorData;
68
- status: "error";
69
- };
70
- /** A query the program read during a render. `key` is stable for a name and input. */
71
- export interface QueryRead {
72
- input: unknown;
73
- key: string;
74
- name: string;
75
- }
76
- /** A `mutate()` call made inside a handler. */
77
- export interface MutationCall {
78
- id: number;
79
- input: unknown;
80
- name: string;
81
- }
82
- /** Page to worker. */
83
- export type HostMessage = {
84
- args: ReadonlyArray<WireArg>;
85
- fn: string;
86
- seq: number;
87
- type: "call";
88
- } | {
89
- id: number;
90
- result: MutationResult;
91
- seq: number;
92
- type: "mutation";
93
- } | {
94
- key: string;
95
- result: QueryResult;
96
- seq: number;
97
- type: "query";
98
- } | {
99
- code: string;
100
- /** Drop the state and mutation statuses of the program before. */
101
- fresh: boolean;
102
- procedures: Readonly<Record<string, ProcedureEntry>>;
103
- prose: Readonly<Record<string, string>>;
104
- seq: number;
105
- type: "update";
106
- };
107
- /** Worker to page. */
108
- export type WorkerMessage = {
109
- errors: Array<RuntimeError>;
110
- mutations: Array<MutationCall>;
111
- ops: Array<Op>;
112
- queries: Array<QueryRead>;
113
- seq: number;
114
- type: "result";
115
- } | {
116
- message: string;
117
- seq: number;
118
- type: "failed";
119
- };
120
- /** What the frame sends to the page. */
121
- export type FrameMessage = WorkerMessage | {
122
- seq: number;
123
- type: "quarantined";
124
- } | {
125
- type: "ready";
126
- } |
127
- /** The frame's own security policy is not in force, so it will not run. */
128
- {
129
- type: "unprotected";
130
- };
131
- export declare const ROOT_ID = 0;
132
- //#endregion
package/dist/protocol.js DELETED
@@ -1,4 +0,0 @@
1
- //#region src/protocol.ts
2
- const ROOT_ID = 0;
3
- //#endregion
4
- export { ROOT_ID };
package/dist/types.d.ts DELETED
@@ -1,168 +0,0 @@
1
- //#region src/types.d.ts
2
- /**
3
- * Types the server and the browser share. The server builds these values;
4
- * the browser receives them as JSON and checks them with `parse.ts`.
5
- */
6
- /** The version of the program format. The runtime refuses any other. */
7
- export declare const PROGRAM_FORMAT = 1;
8
- export declare const pickleErrorCodes: readonly ["BAD_INPUT", "UNAUTHORIZED", "FORBIDDEN", "NOT_FOUND", "CONFLICT", "RATE_LIMITED", "INTERNAL", "UNKNOWN_PROCEDURE", "UNAVAILABLE", "NETWORK", "DECLINED"];
9
- export type PickleErrorCode = (typeof pickleErrorCodes)[number];
10
- /** A failure as the spec and the browser see it. */
11
- export interface PickleErrorData {
12
- code: PickleErrorCode;
13
- message: string;
14
- }
15
- /**
16
- * Where a diagnostic points. Every span has a line; one about something the
17
- * compiler can place in the source also has its range, as offsets into the
18
- * source the line counts (JavaScript string indices), and the column of its
19
- * start.
20
- */
21
- export interface Span {
22
- /** 1-based, on `line`. */
23
- column?: number;
24
- /** Just past the range. */
25
- end?: number;
26
- line: number;
27
- /** The range's first character. */
28
- start?: number;
29
- }
30
- /**
31
- * A rewrite that makes a diagnostic's problem go away: replace each edit's
32
- * span with its text. The server attaches one only when there is a single
33
- * way to write it, and applies it itself, so its diagnostic is `info`.
34
- */
35
- export interface DiagnosticFix {
36
- edits: Array<{
37
- span: Span;
38
- text: string;
39
- }>;
40
- title: string;
41
- }
42
- export interface Diagnostic {
43
- code: string;
44
- fix?: DiagnosticFix;
45
- message: string;
46
- /** `error` marks something the compiler had to change; the program still runs. */
47
- severity: "error" | "hint" | "info" | "warning";
48
- span: Span;
49
- }
50
- export interface NumberPropSchema {
51
- kind: "number";
52
- max?: number;
53
- min?: number;
54
- }
55
- /** How the view checks one prop before a registry component sees it. */
56
- export type PropSchema = {
57
- kind: "boolean";
58
- } | {
59
- kind: "enum";
60
- values: ReadonlyArray<string>;
61
- } | {
62
- kind: "handler";
63
- } | {
64
- kind: "image";
65
- } | {
66
- kind: "link";
67
- } | {
68
- kind: "list";
69
- } | NumberPropSchema | {
70
- kind: "string";
71
- };
72
- export type ChildrenKind = "block" | "inline" | "none";
73
- export interface ComponentSchema {
74
- children: ChildrenKind;
75
- props: Readonly<Record<string, PropSchema>>;
76
- }
77
- /** `action` is a mutation the embedding page handles instead of the server. */
78
- export type ProcedureKind = "action" | "mutation" | "query";
79
- export interface ProcedureEntry {
80
- invalidates: ReadonlyArray<string>;
81
- kind: ProcedureKind;
82
- }
83
- /**
84
- * One query a mutation refreshed, as the server sends it with the mutation's
85
- * data. Without `input`, every input of the query refetches. With it, only
86
- * the queries whose input contains those values.
87
- */
88
- export interface Invalidation {
89
- input?: JsonValue;
90
- name: string;
91
- }
92
- /**
93
- * A compiled spec. Opaque to apps: pass it from the server to the view and
94
- * nowhere else. The view trusts its schemas and image hosts.
95
- */
96
- export interface Program {
97
- code: string;
98
- /** Prop schemas of the components the program draws. */
99
- components: Readonly<Record<string, ComponentSchema>>;
100
- format: typeof PROGRAM_FORMAT;
101
- imageHosts: ReadonlyArray<string>;
102
- /** Procedures the program calls, with their kind and `invalidates` list. */
103
- procedures: Readonly<Record<string, ProcedureEntry>>;
104
- /** Text nodes pulled out of the code, so a prose edit swaps a table. */
105
- prose: Readonly<Record<string, string>>;
106
- }
107
- export interface Message {
108
- content: string;
109
- role: "assistant" | "user";
110
- }
111
- export type SessionEvent = {
112
- diagnostics: ReadonlyArray<Diagnostic>;
113
- /**
114
- * True on the first program of a new UI: the model wrote a whole
115
- * document rather than editing the last one, or a stored one was
116
- * restored. The view starts it from fresh state.
117
- */
118
- fresh: boolean;
119
- program: Program;
120
- revision: number;
121
- type: "program";
122
- } | {
123
- diagnostics: ReadonlyArray<Diagnostic>;
124
- revision: number;
125
- source: string;
126
- type: "done";
127
- } | {
128
- error: PickleErrorData;
129
- type: "error";
130
- };
131
- /**
132
- * Where a chat block is: still arriving, being repaired after it closed with
133
- * errors, or final.
134
- */
135
- export type ChatBlockStatus = "done" | "repairing" | "streaming";
136
- /**
137
- * A pickle in a chat reply: one fenced `pickle` block, compiled on its own.
138
- * The server sends it again whenever its program changes, under the same
139
- * `key`.
140
- */
141
- export interface ChatBlock {
142
- /** Lines refer to `source`, which starts with its pragma line. */
143
- diagnostics: ReadonlyArray<Diagnostic>;
144
- /** Set when the block could not be built, e.g. an edit to a missing UI. */
145
- error?: PickleErrorData;
146
- /** The id the model gave the block, e.g. "monthly-spending". */
147
- id: string;
148
- /** `${id}@${version}`: unique in a conversation. */
149
- key: string;
150
- program: Program;
151
- /** The checked source, with its pragma. Store this, not the program. */
152
- source: string;
153
- status: ChatBlockStatus;
154
- /** 1 for the first block with this id in the conversation, then 2, 3… */
155
- version: number;
156
- }
157
- /** A value that travels as JSON. */
158
- export type JsonValue = boolean | null | number | string | ReadonlyArray<JsonValue> | {
159
- readonly [key: string]: JsonValue;
160
- };
161
- /** App options sent with a turn; the server parses them with its `options` schema. */
162
- export type SessionOptions = Readonly<Record<string, JsonValue>>;
163
- /** What a session request carries. */
164
- export interface SessionBody {
165
- messages: ReadonlyArray<Message>;
166
- options?: SessionOptions;
167
- }
168
- //#endregion
package/dist/types.js DELETED
@@ -1,22 +0,0 @@
1
- //#region src/types.ts
2
- /**
3
- * Types the server and the browser share. The server builds these values;
4
- * the browser receives them as JSON and checks them with `parse.ts`.
5
- */
6
- /** The version of the program format. The runtime refuses any other. */
7
- const PROGRAM_FORMAT = 1;
8
- const pickleErrorCodes = [
9
- "BAD_INPUT",
10
- "UNAUTHORIZED",
11
- "FORBIDDEN",
12
- "NOT_FOUND",
13
- "CONFLICT",
14
- "RATE_LIMITED",
15
- "INTERNAL",
16
- "UNKNOWN_PROCEDURE",
17
- "UNAVAILABLE",
18
- "NETWORK",
19
- "DECLINED"
20
- ];
21
- //#endregion
22
- export { PROGRAM_FORMAT, pickleErrorCodes };
package/dist/urls.d.ts DELETED
@@ -1,14 +0,0 @@
1
- //#region src/urls.d.ts
2
- /**
3
- * A hostname, or `*.example.com` for its subdomains (not the apex),
4
- * optionally followed by a path prefix that ends in `/`, such as
5
- * `images.example.com/products/`.
6
- */
7
- export declare function isImageHostEntry(entry: string): boolean;
8
- /** The entry with its host lowercased; the path keeps its case. */
9
- export declare function normalizeImageHost(entry: string): string;
10
- /** An `https` URL with no credentials or port, on one of `hosts`. */
11
- export declare function isAllowedImage(src: string, hosts: ReadonlyArray<string>): boolean;
12
- /** An absolute `http` or `https` URL. Anything else, such as `javascript:`, is refused. */
13
- export declare function isAllowedLink(href: string): boolean;
14
- //#endregion
package/dist/urls.js DELETED
@@ -1,59 +0,0 @@
1
- //#region src/urls.ts
2
- const HOST = /^(\*\.)?[a-z0-9-]+(\.[a-z0-9-]+)*$/;
3
- const PATH_PREFIX = /^\/(?:[A-Za-z0-9._~!$&'()*+,;=:@%/-]*\/)?$/;
4
- /** An image host entry split into its host and optional path prefix. */
5
- function splitEntry(entry) {
6
- const slash = entry.indexOf("/");
7
- return slash === -1 ? {
8
- host: entry.toLowerCase(),
9
- path: "/"
10
- } : {
11
- host: entry.slice(0, slash).toLowerCase(),
12
- path: entry.slice(slash)
13
- };
14
- }
15
- /**
16
- * A hostname, or `*.example.com` for its subdomains (not the apex),
17
- * optionally followed by a path prefix that ends in `/`, such as
18
- * `images.example.com/products/`.
19
- */
20
- function isImageHostEntry(entry) {
21
- const parts = splitEntry(entry);
22
- return HOST.test(parts.host) && PATH_PREFIX.test(parts.path);
23
- }
24
- /** The entry with its host lowercased; the path keeps its case. */
25
- function normalizeImageHost(entry) {
26
- const parts = splitEntry(entry);
27
- return entry.includes("/") ? `${parts.host}${parts.path}` : parts.host;
28
- }
29
- /**
30
- * An encoded `/`, `\`, `.` or `%`: a server that decodes it after matching can
31
- * be walked out of the prefix with `..%2F`, or with `..%252F` if it decodes
32
- * twice, and the URL parser leaves it alone. So a prefix can't serve object
33
- * keys that hold an encoded `/`, such as Firebase Storage's `o/a%2Fb.jpg`.
34
- */
35
- const ENCODED_SEPARATOR = /%(?:2f|5c|2e|25)/i;
36
- function matchesHost(url, entry) {
37
- if (!isImageHostEntry(entry)) return false;
38
- const parts = splitEntry(entry);
39
- return (parts.host.startsWith("*.") ? url.hostname.endsWith(parts.host.slice(1)) : url.hostname === parts.host) && url.pathname.startsWith(parts.path) && (parts.path === "/" || !ENCODED_SEPARATOR.test(url.pathname));
40
- }
41
- function parseUrl(text) {
42
- try {
43
- return new URL(text);
44
- } catch {
45
- return null;
46
- }
47
- }
48
- /** An `https` URL with no credentials or port, on one of `hosts`. */
49
- function isAllowedImage(src, hosts) {
50
- const url = parseUrl(src);
51
- return url !== null && url.protocol === "https:" && url.username === "" && url.password === "" && url.port === "" && hosts.some((entry) => matchesHost(url, entry));
52
- }
53
- /** An absolute `http` or `https` URL. Anything else, such as `javascript:`, is refused. */
54
- function isAllowedLink(href) {
55
- const url = parseUrl(href);
56
- return url !== null && (url.protocol === "https:" || url.protocol === "http:") && url.username === "" && url.password === "";
57
- }
58
- //#endregion
59
- export { isAllowedImage, isAllowedLink, isImageHostEntry, normalizeImageHost };