Files
jcoffey-dev 136fc2540b
ci / fork-checks (pull_request) Skipped
ci / build (pull_request) Skipped
github/ci (branch) GitHub Actions
ci / github (pull_request) Successful in 7m45s
Sieve playground: the server's interpreter in the browser
tools/sieve-playground builds the vendored sieve-rs 0.7.3 (vnd.inbuxa.*
names included) to WebAssembly, with a static page around it, for inbuxa
Admin's Debug button on a Sieve script: until now that button sent the
script to an outside site whose interpreter knows only upstream's
extension names, so any script that requires vnd.inbuxa.* failed to
compile there.

Ported from the playground in the upstream sieve-rs repository's web/
directory (commit 549a2d57f7), which targets sieve-rs 1.x:

- run.rs and handler.rs drive 0.7's event loop the way the server's
  delivery does, carrying on after a runtime error; llm_prompt compiles
  and answers false, as on a server without a model.
- The expression functions are the server's own modules, included by
  path; tests fail if the list, ApplyString or the few functions copied
  from misc.rs/unicode.rs stop matching crates/common.
- Built for wasm32-wasip1 rather than with wasm-bindgen: sieve-rs and
  mail-builder read the clock, which panics on wasm32-unknown-unknown.
  The page's worker answers the six WASI calls, giving the module the
  playground's current-time setting. shim/gethostname stands in for a
  crate with no WASI build. Nothing in vendor/ or crates/ changes.
- The page carries no upstream names or links; storage keys and the
  samples are renamed to match.

build.sh --admin <dir> stages the result into an inbuxa-admin checkout.
2026-10-09 01:13:53 -07:00

114 lines
4.2 KiB
JavaScript

/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 Coffey Labs LLC
*
* SPDX-License-Identifier: AGPL-3.0-only
*
* Modified by Coffey Labs in 2026 for INBUXA.
*/
export const EML_ID = "eml";
class EmlState {
constructor(inHeaders, boundaries) {
this.inHeaders = inHeaders;
this.boundaries = boundaries;
}
clone() {
return new EmlState(this.inHeaders, this.boundaries.slice());
}
equals(other) {
return (
other instanceof EmlState &&
other.inHeaders === this.inHeaders &&
other.boundaries.length === this.boundaries.length &&
other.boundaries.every((b, i) => b === this.boundaries[i])
);
}
}
const HEADER = /^([A-Za-z0-9!#$%&'*+.^_`|~-]+)(\s*:)/;
const BOUNDARY_PARAM = /boundary\s*=\s*"?([^";\s]+)"?/i;
export function registerEml(monaco) {
monaco.languages.register({ id: EML_ID, extensions: [".eml"], aliases: ["Email"] });
monaco.languages.setTokensProvider(EML_ID, {
getInitialState: () => new EmlState(true, []),
tokenize(line, state) {
const next = state.clone();
const tokens = [];
if (next.boundaries.length) {
const trimmed = line.trimEnd();
const hit = next.boundaries.find((b) => trimmed === `--${b}` || trimmed === `--${b}--`);
if (hit) {
tokens.push({ startIndex: 0, scopes: "keyword.boundary" });
if (trimmed.endsWith("--") && trimmed === `--${hit}--`) {
next.boundaries = next.boundaries.filter((b) => b !== hit);
next.inHeaders = false;
} else {
next.inHeaders = true;
}
return { tokens, endState: next };
}
}
if (next.inHeaders) {
if (line.trim() === "") {
next.inHeaders = false;
return { tokens: [{ startIndex: 0, scopes: "" }], endState: next };
}
const boundary = BOUNDARY_PARAM.exec(line);
if (boundary && !next.boundaries.includes(boundary[1])) next.boundaries.push(boundary[1]);
const header = HEADER.exec(line);
if (header) {
tokens.push({ startIndex: 0, scopes: "attribute.name.header" });
tokens.push({ startIndex: header[1].length, scopes: "delimiter" });
tokens.push({ startIndex: header[0].length, scopes: "string.header" });
} else if (/^\s/.test(line)) {
tokens.push({ startIndex: 0, scopes: "string.header" });
} else {
tokens.push({ startIndex: 0, scopes: "invalid" });
}
return { tokens, endState: next };
}
tokens.push({ startIndex: 0, scopes: /^>/.test(line) ? "comment.quote" : "" });
return { tokens, endState: next };
},
});
}
export function defineThemes(monaco) {
const rules = (dark) => [
{ token: "keyword", foreground: dark ? "c792ea" : "7a3eb1", fontStyle: "bold" },
{ token: "type", foreground: dark ? "82aaff" : "1f5fbf" },
{ token: "attribute.name", foreground: dark ? "f78c6c" : "b35300" },
{ token: "variable", foreground: dark ? "ffcb6b" : "a0680a", fontStyle: "bold" },
{ token: "string", foreground: dark ? "c3e88d" : "2f7d32" },
{ token: "string.heredoc", foreground: dark ? "89ddff" : "0a7f94", fontStyle: "bold" },
{ token: "string.escape", foreground: dark ? "89ddff" : "0a7f94" },
{ token: "number", foreground: dark ? "f78c6c" : "b35300" },
{ token: "comment", foreground: dark ? "6a7390" : "7d8595", fontStyle: "italic" },
{ token: "attribute.name.header", foreground: dark ? "82aaff" : "1f5fbf", fontStyle: "bold" },
{ token: "string.header", foreground: dark ? "d7dae0" : "3a3f4b" },
{ token: "keyword.boundary", foreground: dark ? "c792ea" : "7a3eb1" },
{ token: "comment.quote", foreground: dark ? "6a7390" : "7d8595" },
{ token: "invalid", foreground: dark ? "ff7b82" : "c6313a" },
];
monaco.editor.defineTheme("playground-light", {
base: "vs",
inherit: true,
rules: rules(false),
colors: { "editor.background": "#ffffff", "editorGutter.background": "#ffffff" },
});
monaco.editor.defineTheme("playground-dark", {
base: "vs-dark",
inherit: true,
rules: rules(true),
colors: { "editor.background": "#1b1d24", "editorGutter.background": "#1b1d24" },
});
}