Expand description
The Ignition Flint script codec (07-04, INTR-01) — decode/encode
of the scripts EMBEDDED inside JSON resource members (Perspective
view.json component scripts, tag event scripts, …) into editable
.py sidecars, with a byte-exact unedited round-trip.
PURE — the client/resources.rs discipline: zero
crate::client::GatewayApi surface, unit-testable without a
gateway. ignition/script-python members are ALREADY plain .py
text in the export (live-proven, 07-RESEARCH) and never decode —
this module targets the escaped string values under
SCRIPT_KEYS only (scope honesty, README “Script decode/
encode”).
§The codec (ignition-nvim’s exact contract, dual-ported there)
flint_encode— the ordered multi-pass replacement table, BACKSLASH FIRST.flint_decode— a SINGLE-PASS state machine (multi-pass cannot distinguish\\tfrom\t); unknown\uXXXXescapes keep the backslash.- The invariant
flint_encode(flint_decode(x)) == xis SACRED (over strings in the table’s image — what Ignition writes). dedent/reindentstrip/restore the common leading-TAB prefix (only non-empty lines reindent; whitespace-only lines normalize to empty — scripts with such lines re-encode with that one normalization, an accepted ignition-nvim semantic).
§Addressing = counter-named sidecars + JSON-pointer manifest
--decode-scripts writes the export’s members PLUS
<member>.<n>.py sidecar siblings PLUS a scripts-manifest.json
at the tree root mapping each member’s JSON-pointer addresses →
{sidecar, indent_prefix}. The exported JSON stays MARKER-FREE
(gateway-clean — the manifest-aside beats markers, 07-RESEARCH
anti-patterns).
§Round-trip = raw byte-span splicing (NO preserve_order)
decode_member/encode_member walk the member’s RAW bytes
with ONE shared position-tracking scanner: decode resolves each
script string to its byte span, encode RE-RESOLVES each manifest
pointer in the CURRENT bytes (hand-edits stay valid) and splices
the re-encoded replacement at that span. serde_json’s
preserve_order feature is deliberately NOT enabled — feature
unification is workspace-wide and would flip every
serde_json::Value map to insertion order, churning key order in
the existing Value-re-serializing goldens (tags export -o -,
doctor/webdev passthroughs). serde_json is used READ-ONLY here
(manifest parse/serialize; parse-to-Value walks are
order-agnostic); NO code path re-serializes a member Value.
Acceptance = byte-equality of UNEDITED re-encoded members (the
sacred invariant at file level).
Structs§
- Decoded
Entry - One entry with its sidecar content.
- Decoded
Member - One decoded member: the manifest entries plus each sidecar’s text (decoded + dedented) ready to write.
- Manifest
- The decoded-export manifest carried at the tree root — consumed +
stripped by
encode_export_tree(it never rides an upload). - Manifest
Entry - One manifest entry: the JSON-pointer address of a script string
value, its counter-named sidecar sibling (
<member>.<n>.py), and the indent prefixdedentstripped.
Constants§
- MANIFEST_
NAME - The manifest file a decoded export tree carries at its root — consumed + stripped on re-encode (it never enters an uploaded zip).
- SCRIPT_
KEYS - The JSON keys whose string values carry embedded scripts — the ignition-nvim list verbatim (nine keys; the plan sketch said ten, the dual-ported source is the authority); kept in sync by comment reference (lua/ignition/json_parser.lua SCRIPT_KEYS / ignition_lsp/json_scanner.py SCRIPT_KEYS).
Functions§
- count_
file_ members - Count a zip’s FILE members (directory entries excluded) — the export-decode result’s member count.
- decode_
export_ tree - Decode an export zip into a DIRECTORY: every member written at
its path,
<member>.<n>.pysidecars beside the.jsonmembers that carry embedded scripts, andMANIFEST_NAMEat the tree root. Returns the sidecar count. The exported JSON stays MARKER-FREE (gateway-clean). - decode_
member - Decode one member’s embedded scripts: walk the RAW bytes for
SCRIPT_KEYS string values that [
looks_like_script] accepts, producing sidecar texts (decoded + dedented) and manifest entries with counter-named sidecars (<member-basename>.<n>.py).Nonewhen the member holds no decodable scripts (or does not scan — the caller leaves such members untouched, byte-verbatim). - dedent
- Strip the common leading-TAB prefix Ignition stores scripts with —
the ignition-nvim semantics verbatim: the minimum leading-tab
count over non-empty lines decides the prefix; stray spaces in
the leading whitespace strip alongside the tabs. Returns
(dedented_text, indent_prefix)soreindentcan restore it. - encode_
export_ tree - Re-zip a decoded export DIRECTORY back into importable zip bytes:
the manifest is consumed + stripped, every sidecar referenced by
it is stripped, members with manifest entries ride
encode_member(span-level splice), everything else copies verbatim. Missing sidecars keep the member’s current value (the decode rule). A directory withoutMANIFEST_NAMEis not a decoded export tree (usage-class refusal). - encode_
member - Encode one member’s scripts back: re-resolve each manifest
pointer to its raw byte span in the CURRENT member bytes (the
same shared scanner — re-scanning at encode time keeps splices
valid even when the user hand-edited the member JSON), then
splice the re-encoded replacement (reindent +
flint_encode) at that span. Rules: - flint_
decode - Decode the Ignition Flint JSON-string form back to plain text —
SINGLE-PASS (multi-pass cannot distinguish
\\t— literal backslash + t — from\t— a tab). Unknown\uXXXXand unknown single escapes KEEP the backslash (the ignition-nvim semantics: the escape sequence rides through verbatim for the re-encode). - flint_
encode - Encode plain text into the Ignition Flint JSON-string form — the EXACT ordered multi-pass table (backslash FIRST so later passes cannot double-escape), cross-validated Lua + Python in ignition-nvim.
- reindent
- Restore the prefix
dedentstripped — ONLY non-empty lines reindent (the ignition-nvim semantics verbatim).