mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-10-25 14:00:40 +00:00
fa2755e183
* feat(materialize): headless op-log materializer bundle, goja runner and worker (TASK-2198 U2)
Builds the editor's own JS (Yjs -> y-tiptap -> Tiptap schema -> tiptap-markdown
-> the tab's flush pipeline) into one IIFE, embeds it, and runs it in goja. Not
wired into the server yet (U3 supervisor, U4 write path).
- web/src/lib/collab/materializer/: bundle entry (extension list mirrors
Editor.svelte's collab branch via the U1 shared modules), op-log replay
mirroring wsProvider's MESSAGE_SYNC arm, linkedom DOM shim and polyfills
from the spike, and a vendored patch of prosemirror-markdown
MarkdownSerializerState.atBlank (quadratic under goja; equivalent).
- web/scripts/build-materializer.mjs (esbuild) -> web/build-materializer/,
run by `npm run build`; embedded as pad.MaterializerJS.
- internal/materialize: Runner (Go-native atob/btoa/TextEncoder/TextDecoder/
getRandomValues, schema-version check, ctx interrupt with a clean VM after),
and RunWorker's length-prefixed JSON protocol behind the hidden
`pad __materialize-worker` command.
- Parity: vitest drives two real Editor.svelte mounts and compares
materialize() with the tab's flush on 50 seeded cases; the same cases are
committed as testdata/corpus.json and replayed in goja (TestCorpusParity).
- CI Go jobs, release tests, Docker and Nix produce the bundle; linkedom and
esbuild are new devDependencies.
Claude-Session: https://claude.ai/code/session_01H63zaWhkb5pAafx2WGjrVC
* test(materialize): pin the atBlank patch's equivalence under V8 too (TASK-2198 U2, codex r1)
Codex r1 claimed upstream's /(^|\n)$/ is true for a trailing CR in JS. It is
not (checked in V8: false for "\r" and "a\u2028", as the patch returns),
but the only equivalence test ran under goja and the patch header cited a
V8-side test that did not exist. This adds it, with the line-terminator edge
cases, and it fails when the patch checks CR instead of LF.
Claude-Session: https://claude.ai/code/session_01H63zaWhkb5pAafx2WGjrVC
* build(nix): vendorHash for goja (TASK-2198 U2)
Taken from the Nix job's recompute on c7fa1931 (specified sha256-iEqv…,
got sha256-P1bQ…), so main does not depend on the heal job after merge.
Claude-Session: https://claude.ai/code/session_01H63zaWhkb5pAafx2WGjrVC
65 lines
2.3 KiB
JavaScript
65 lines
2.3 KiB
JavaScript
// Builds the headless materializer bundle (TASK-2198):
|
|
// src/lib/collab/materializer/entry.ts -> build-materializer/materializer.js
|
|
//
|
|
// ONE self-contained IIFE that assigns globalThis.Materializer. The Go binary
|
|
// embeds it (embed.go, `//go:embed web/build-materializer/materializer.js`)
|
|
// and runs it in goja (internal/materialize), so a fresh checkout needs this
|
|
// file before `go build` works, exactly like web/build. `npm run build` runs
|
|
// it after `vite build`.
|
|
//
|
|
// Usage: node scripts/build-materializer.mjs [--outfile <path>] [--define KEY=VALUE]...
|
|
// --outfile write somewhere else (tests build deliberately broken variants
|
|
// this way, never over the real output)
|
|
// --define extra esbuild defines, same syntax as esbuild's
|
|
import { build } from 'esbuild';
|
|
import path from 'node:path';
|
|
import fs from 'node:fs';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const web = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
|
|
let outfile = path.join(web, 'build-materializer', 'materializer.js');
|
|
const extraDefines = {};
|
|
const argv = process.argv.slice(2);
|
|
for (let i = 0; i < argv.length; i++) {
|
|
const a = argv[i];
|
|
if (a === '--outfile') outfile = path.resolve(argv[++i]);
|
|
else if (a === '--define') {
|
|
const kv = argv[++i];
|
|
const eq = kv.indexOf('=');
|
|
extraDefines[kv.slice(0, eq)] = kv.slice(eq + 1);
|
|
} else {
|
|
console.error(`build-materializer: unknown argument ${a}`);
|
|
process.exit(2);
|
|
}
|
|
}
|
|
|
|
await build({
|
|
entryPoints: [path.join(web, 'src/lib/collab/materializer/entry.ts')],
|
|
bundle: true,
|
|
format: 'iife',
|
|
// Neutral, not browser: nothing here may assume a window. The DOM the
|
|
// editor needs comes from domShim.ts (linkedom).
|
|
platform: 'neutral',
|
|
mainFields: ['module', 'main'],
|
|
conditions: ['import', 'default'],
|
|
// goja implements ES2022 minus a few corners; esbuild lowers anything newer.
|
|
target: 'es2022',
|
|
minify: true,
|
|
legalComments: 'none',
|
|
outfile,
|
|
alias: { $lib: path.join(web, 'src/lib') },
|
|
loader: { '.json': 'json', '.svg': 'text' },
|
|
define: {
|
|
'process.env.NODE_ENV': '"production"',
|
|
'import.meta.env.DEV': 'false',
|
|
'import.meta.env.PROD': 'true',
|
|
'import.meta.env.SSR': 'false',
|
|
...extraDefines,
|
|
},
|
|
logLevel: 'warning',
|
|
});
|
|
|
|
const bytes = fs.statSync(outfile).size;
|
|
console.log(`build-materializer: wrote ${path.relative(process.cwd(), outfile)} (${bytes} bytes)`);
|