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.
114 lines
4.2 KiB
JavaScript
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" },
|
|
});
|
|
}
|