@inploi/flows
v9.8.2
Published
`@inploi/flows/engine` is the shared application interpreter. Legacy callers keep their existing latest-answer `undo` and submission policy.
Readme
Flows
@inploi/flows/engine is the shared application interpreter. Legacy callers keep
their existing latest-answer undo and submission policy.
@inploi/flows/replay exports createEditableFlowInterpreter, an opt-in factory
with the same engine parameters plus onReplayState. It exposes reviewAnswers,
previewEdit({visitIndex, answer}) and editAnswer({visitIndex, answer}) at the
first submission confirmation. Visits identify completed question occurrences,
including explicit skips. Cancel and preview never mutate the application.
An edit restores its visit checkpoint and silently reconstructs the selected path. Missing, invalid or transitively dependent answers ask again; newly added, changed and removed branch outputs propagate through dependency invalidation. Each visit hashes the delivered question text, input type, options, labels, validation configuration and locale, plus SHA-256 over all bot-rendered history from the flow's start up to that question, in order (including text, links, images, system/info messages, earlier question prompts, every delivered control's text/configuration and delivered interpolations). Only candidate answer echoes, including skipped-answer annotations, are excluded. Changed earlier output invalidates every later answer; unchanged output permits reuse. Changed Continue/acknowledgement controls pause for an explicit response before later questions; an unchanged, previously acknowledged control can replay silently. Pre-apply Continue labels interpolate current answers. Reuse requires the saved fingerprint to match; independent answers can survive after that pause, including reload and Continue. Pending edits are saved before reconstruction starts. A separate persisted consent requirement survives journal loss: every submit after an edit, including automatic submit nodes and captcha retries, waits for explicit Send. Resumed editable sessions without a usable journal also show confirmation. A head run with isolated services compares payload, pre-apply input, path, messages, current input, question and context fingerprints and terminal state before review and Send. Timing-only confirmation fields do not affect equivalence; the real input retains its cooldown. A mismatch logs a content-free warning and asks from the edited visit.
The optional version-5 journal shares the host's existing storage lifetime. Version-1–4
journals fall back to confirmation without reuse. The host uses storableReplayJournal(data)
to replace values matching current non-file submissions with a key and SHA-256 fingerprint.
Loading verifies references before materialising them in interpreter memory; checkpoint values needed only by
an interrupted edit remain until they are reached or reconstruction reaches confirmation. Superseded checkpoint
comparisons retain only SHA-256 fingerprints; saved candidates omit old prompt/control text. Superseded values and saved replay candidates
are then pruned, retaining the minimal prefix for the second Send equivalence check. File references use SHA-256
over the complete encoded content, metadata and file order, computed when an
answer is interned or loaded. Same-name, same-size replacements remain distinct.
File bytes stay in the current submission and transient interpreter memory, never the journal.
Checkpoints refer to pool entries. Loading validates a checksum over answers,
visits, controls, history and transcript. Legacy Undo discards the journal. Stale records,
different-locale records and journals predating context fingerprints retain
latest correction until a fresh pass.
Legacy sessions without visits, cyclic replay suffixes, journals containing repeated question visits anywhere, co-occurring repeated writes and
unsupported write nodes are ineligible. Definitions are limited to 512 nodes and
journals to 4096 distinct values; these conservative limits retain correction.
Flow/build/job/context identity must match the checkpoint. The first Send attempt
discards the journal and permanently disables review edits for that session.
Duplicate rereads invalidate the cached request before calling the integration; a failure therefore
reconstructs using the engine's error default, with no stale-result fallback loop.
The cache uses SHA-256 request keys rather than raw email-bearing requests. Completion, Restart and expiry
discard the journal with the host snapshot; Candidate also deletes expired memory fallback records.
Impact preview follows the prospective path using existing answers, and
representative values for newly reached questions. Answers given during replay
can change later branches and therefore its question count. The result includes
questionCount and endsApplication, so a zero-question terminal branch can be
distinguished from returning to Send. It performs no real network or analytics calls.
Pre-apply deduplication is inside the engine: canonical SHA-256 fingerprints suppress identical snapshot requests, changed answers append a new snapshot, and final Send rebuilds its own pre-apply input. A failed snapshot request can be retried later; it is separate from the application write barrier.
Runtime dependency: @noble/hashes
@inploi/flows has exactly one runtime dependency besides its @inploi peers, @noble/hashes (pinned, audited, zero dependencies of its own).
contentFingerprint in src/pre-apply-request.ts uses its SHA-256 for pre-apply deduplication and every replay
journal fingerprint. The alternatives do not work here:
crypto.subtle.digestis only exposed on secure (https) pages, and customer embeds can run on http (PROD-7444). It is also asynchronous, while journal hashing is synchronous throughout the engine.- The existing
hashObjectinsession-key.tsis a 32-bit djb2 hash for session keys. It is not collision-resistant, so it cannot stand in for a content fingerprint that decides whether an answer is reused.
Cost: the two imported functions add about 2.4 KB gzipped (esbuild, minified, measured 7 Oct 2026). Chatbot and Candidate bundle flows, so they carry it too. Replace it only with another synchronous SHA-256 that works on http pages.
Tests
Run pnpm --filter @inploi/flows test, check and build. The generated oracle lives in
src/replay-tests; its README records the measured coverage and reproduction command. The frozen published corpus is
a repo-root test fixture in test-fixtures/flows/, outside packages/, so it never ships. All corpus answers are synthetic.
