@@ -28,7 +28,7 @@ Use Node.js 24 or newer. This example uses the workspace version of Jco, which i
2828` pnpm run all ` follows the other examples: generate types, check TypeScript, build the components,
2929transpile the engine and dataset initializer, run the Node.js demo, and run Node.js and Puppeteer tests.
3030The QuickJS engine uses a synchronous ABI and stubbed WASI because these processors only operate on
31- in-memory JSON . Neither host needs JSPI flags, filesystem access from the guest, or WASI shims.
31+ in-memory records . Neither host needs JSPI flags, filesystem access from the guest, or WASI shims.
3232
3333## Try the browser demo
3434
@@ -42,6 +42,10 @@ Open `http://localhost:5173/demo/` after `pnpm run demo`, or use `pnpm run demo:
42425 . Set the risk step's options to ` {"minScore":70} ` and rerun. Three logs remain, with scores 70, 70, and 90.
43436 . Move the page counter to the beginning. Its report now counts all 12 input logs, before filtering.
4444
45+ Choose ** Use one sample log** to call ` process-single ` with one ` access-log ` record. You can also paste
46+ a JSON object into the input; arrays call ` process-batch ` with ` list<access-log> ` . A single-record filter
47+ can return no record, which stops subsequent steps. ** Restore sample** switches back to the full batch.
48+
4549The JSON input is editable. Every run starts from that input, so a previous run's filters, scores, and
4650reports never leak into the next run. ** Restore sample** resets the input; ** Clear pipeline** removes
4751the steps. Invalid uploads or options show an error on the page and can be corrected without a reload.
@@ -71,8 +75,10 @@ It reads that Wasm back through the same loader as an upload and validates `desc
7175the step. Code runs inside the shared QuickJS engine. The JavaScript engine is reused as you build
7276and load new components.
7377
74- The editor and compiler load from versioned CDN URLs, so browser authoring needs internet access.
75- Your source stays in the browser; no compilation requests are sent to a server. If the editor CDN is
78+ The editor and compiler load from versioned [ jsDelivr] ( https://www.jsdelivr.com/ ) CDN URLs, so browser
79+ authoring needs internet access. An import map pins CodeMirror's shared dependencies to one version
80+ each so its extension identity checks work. Your source stays in the browser; no compilation requests
81+ are sent to a server. If the editor CDN is
7682unavailable, the page keeps a plain source text area. Build errors appear beside the editor and leave
7783the pipeline intact. Browser builds validate syntax and exports; full TypeScript checking for the
7884checked-in components and editor template is provided by ` pnpm run check ` .
@@ -118,18 +124,29 @@ export is checked against its generated interface, for example:
118124
119125``` ts
120126import type { processor as ProcessorInterface } from ' example:log-pipeline/pipeline' ;
127+ import { getDataset } from ' ../shared/state.ts' ;
121128
122129export const processor: typeof ProcessorInterface = {
123130 describe() {
124- /* name, description, defaultOptions */
131+ return { name: ' My processor' , description: ' Process access logs.' , defaultOptions: ' {}' };
132+ },
133+ processBatch(logs , options ) {
134+ const data = getDataset ();
135+ data .logs = logs ;
136+ /* parse options and update shared data */
137+ return { logs: data .logs , summary: ` Processed ${logs .length } logs ` };
125138 },
126- process(options ) {
127- /* update shared data and return a summary */
139+ processSingle(log , options ) {
140+ const { logs, summary } = processor .processBatch ([log ], options );
141+ return { log: logs [0 ], summary };
128142 },
129143};
130144```
131145
132- The generated ` AccessLog ` type also describes the shared records. These declarations are checked in
146+ The shared WIT ` types ` interface defines ` access-log ` . The dataset and processor interfaces both use
147+ it: ` reset ` accepts a list, ` snapshot ` returns typed logs, ` process-batch ` accepts and returns a list,
148+ and ` process-single ` accepts one record and returns an optional record. The generated ` AccessLog `
149+ TypeScript type also describes the shared records. These declarations are checked in
133150for editor support and regenerated by ` build ` ; Jco bundles TypeScript directly, so there is no separate
134151JavaScript compilation step.
135152
@@ -172,12 +189,20 @@ The shared [`createPipeline`](./demo/shared/pipeline.js) runner instantiates the
172189platform adapter is a function that reads bytes from a URL: Node.js supplies ` readFile ` , and the browser
173190supplies ` fetch ` . Both entry points use the same script reader and execution code.
174191
175- For each run, the runner loads the dataset script and calls ` dataset.reset(input) ` . For each step, it
176- loads that step's source with ` scriptLoader.load ` , calls ` processor.process(options) ` , then reloads the
177- dataset script to read a snapshot. Loading scripts replaces the active exports, but retains the
178- engine's JavaScript realm. The working dataset lives on that realm's ` globalThis ` , shared through
179- [ ` components/shared/state.ts ` ] ( ./components/shared/state.ts ) ; it is not passed back and forth between
180- processors as JSON. Only input, options, summaries, and output snapshots cross the host boundary.
192+ For each run, the host parses the JSON input into records. The runner loads the dataset script and
193+ calls ` dataset.reset(logs) ` . ` runBatch(logs, stages) ` calls each processor's ` processBatch(logs, options) `
194+ and passes its returned records to the next step. ` runSingle(log, stages) ` instead calls
195+ ` processSingle(log, options) ` and stops if a filter returns no record. The checked-in single-record
196+ implementations reuse the batch algorithm with a one-element list. Batch statistics, such as repeated
197+ IP counts, therefore apply only to the records in that call.
198+
199+ Logs cross the WIT boundary as records, including optional country and risk-score annotations.
200+ Options and extensible reports remain JSON strings; log records are never encoded as JSON inside the
201+ engine. After each step, the runner reloads the dataset script to read a typed snapshot. Loading
202+ scripts replaces the active exports, but retains the engine's JavaScript realm. The dataset and
203+ reports live on that realm's ` globalThis ` , shared through
204+ [ ` components/shared/state.ts ` ] ( ./components/shared/state.ts ) . Reports survive switching processors
205+ and reset at the beginning of each run. Both hosts format the final records as JSON for display.
181206
182207> [ !NOTE]
183208> Scripts in one engine share a realm and can access each other's data. Use trusted scripts together;
@@ -202,15 +227,19 @@ Choose the Node.js stages and their execution order with positional arguments or
202227pnpm run run top-pages geoip
203228PIPELINE_STAGES=geoip,top-pages pnpm run run
204229PIPELINE_STAGES= pnpm run run
230+ pnpm run run --single risk-score top-pages
205231```
206232
207233Arguments take precedence over the environment variable. With neither specified, the demo runs
208234` geoip ` , ` risk-score ` , then ` top-pages ` . An empty environment value runs an empty pipeline. Stage names
209235are the filenames in ` dist/components/ ` without ` .wasm ` ; unknown names report the available stages and
210236exit with a nonzero status. Each selected stage uses its own default options.
237+ ` --single ` processes the first sample record through ` process-single ` ; the default processes the
238+ whole sample array through ` process-batch ` .
211239
212- The entry point exports an async ` main() ` and calls it only when executed directly, so importing
213- ` demo/node.mjs ` does not run the demo.
240+ The entry point exports an async ` main({ stageNames, mode }) ` and calls it only when executed directly,
241+ so importing ` demo/node.mjs ` does not run the demo. CLI and environment parsing happen in the
242+ direct-execution block at the bottom; ` main ` receives named options.
214243
215244### Add a processor
216245
@@ -227,8 +256,9 @@ pnpm run test:node
227256pnpm run test:browser
228257```
229258
230- The tests exercise the built Wasm components, shared data across script loads, ordering and options,
231- repeat runs, empty input, invalid uploads, and recovery from errors. Node.js tests also cover CLI and
259+ The tests exercise typed batches and single records through the built Wasm components, optional
260+ filter results, shared data across script loads, ordering and options, repeat runs, empty input,
261+ invalid uploads, and recovery from errors. Node.js tests also cover CLI and
232262environment stage selection, precedence, unknown names, and the direct-execution guard. The Puppeteer
233263test uploads actual ` .wasm ` files, edits the highlighted TypeScript editor, builds custom components,
234264loads the downloadable bytes in Node.js, uploads them again, and checks recovery from syntax errors.
0 commit comments