This website is meant to be read and understood quickly by humans, but is only fully parsable, on a technical level, with the aid of an AI system. Read why →
Loop MMT
Parse KEY=VALUE Config into a JSONL Record Streamsource← all gifts

kv-source

kv-source reads plain KEY=VALUE text (a FILE arg, or stdin) and emits one uniform JSON object per assignment — {"key":NAME,"value":VALUE}, both strings — the front of a pipe you feed into the fold/filter/transform gifts (kv-source .env | dedup-filter). Lines split on the FIRST '=', full-line # comments and blank lines skipped, outer quotes stripped, CRLF==LF. Zero dependencies, runs unchanged in Node or a browser, same input yields byte-identical output every run.

The honest edge
kv-source parses PLAIN KEY=VALUE text into uniform string records only — it does not coerce types ("8080" stays a string, never a number), does not interpolate ${VAR}, does not strip an `export ` prefix, does not honor inline comments, and does not process escapes inside quotes. Each of those is a context-dependent guess that would make the same file parse to different bytes across tools. A malformed line (no '='), an illegal/empty key, or --key-field==--value-field is refused (exit 2). An honest, pinnable parse — not a full dotenv runtime.
Run it
printf 'PORT=8080\n' | node kv-source.js # -> {"key":"PORT","value":"8080"} test_kv-source.js (50/50, independent regex-route oracle) + Plumb conformance GREEN (28/28, signed 2026-09-12, clock-independent, mutation-bite non-vacuous) Node / browser, no dependencies
The code — every file that ships
kv-source.js239 lineson GitHub →
#!/usr/bin/env node
/* kv-source.js — parse plain KEY=VALUE text into a uniform JSONL record stream.
   Dependency-free, deterministic, pure. Runs in Node or a browser. MIT.

   WHAT IT IS. A SOURCE: it turns non-JSONL text (a .env / .properties / a plain
   key=value config) into the front of a pipe — one JSON object per line (JSONL) that
   the fold/filter/transform gifts consume. Give it a file (or pipe text on stdin) and
   it emits one uniform record per assignment:

       PORT=8080          ->  {"key":"PORT","value":"8080"}
       # a comment        ->  (skipped)
       HOST = localhost   ->  {"key":"HOST","value":"localhost"}
       GREETING="hi there"->  {"key":"GREETING","value":"hi there"}

   Every record has the same two string fields, so it drops straight into a generic
   filter or fold:  kv-source .env | range-filter ...  /  kv-source .env | dedup-filter.

   STRINGS ONLY (the honesty axis). Values are emitted as LITERAL STRINGS — kv-source
   does not guess types. "8080" stays the string "8080"; "true" stays "true"; "" stays
   "". This is deliberate, not a limitation to apologize for: the moment a parser starts
   coercing (is 08 octal? is 1e3 a number? is TRUE a boolean?) the SAME file parses to
   different bytes under different tools, and a source you cannot pin is not a source.
   kv-source emits strings and lets a downstream typed gift decide. For the same reason
   it does NOT interpolate ${VAR}/$VAR, does NOT strip an `export ` prefix, does NOT
   honor inline (trailing) comments, and does NOT process backslash escapes inside a
   quoted value — each of those is a context-dependent guess that would break byte
   determinism. It parses the grammar it was given and REFUSES the rest, loudly.

   THE GRAMMAR (declared, so it is pinnable)
     * Lines split on newline; a trailing CR (CRLF files) is stripped, so a file parses
       identically whether it uses LF or CRLF.
     * A whitespace-only line emits nothing.
     * A line whose first non-whitespace character is `#` is a full-line COMMENT and
       emits nothing. (There are no inline/trailing comments — a `#` inside a value is
       part of the value.)
     * Any other line MUST be KEY=VALUE. It is split on the FIRST `=`; everything before
       is the key, everything after is the value.
         - KEY is trimmed and MUST match [A-Za-z_][A-Za-z0-9_.-]* — a non-empty name.
           An empty key, or a key with spaces/illegal characters (e.g. `export FOO`), is
           a hard error (exit 2), not a silent guess.
         - VALUE is trimmed. If, after trimming, it both begins and ends with a matching
           `"` or `'` (length >= 2), those OUTER quotes are stripped and the inner text
           is the literal value (no escape processing). Otherwise the trimmed remainder
           is the literal value.
     * A non-blank, non-comment line with no `=` is a MALFORMED line (exit 2).

   THE MODEL
     [FILE]              The key=value file to parse. If omitted, read stdin.
     --key-field NAME    The object key the parsed KEY is emitted under. Default "key".
     --value-field NAME  The object key the parsed VALUE is emitted under. Default
                         "value". Must differ from --key-field (else the record would
                         collapse) — a collision is exit 2.

   Duplicate keys across lines are NOT merged or de-duplicated — each line emits its own
   record, in file order. De-duplication (last-wins, first-wins) is dedup-filter's job
   downstream; kv-source's job is an honest, order-faithful parse.

   DETERMINISM. parse(text, opts) is a pure function — no clock, no randomness, no
   network — so the same text yields byte-identical output every run and every machine.

   USAGE
     node kv-source.js config.env
     cat .env | node kv-source.js
     node kv-source.js .env --key-field name --value-field val
     node kv-source.js --help

   Exit codes: 0 success (including an empty stream from empty input) · 2 input error
   (malformed line, illegal/empty key, key-field==value-field, empty field name, unknown
   option, unreadable file). Always a clean one-line message on stderr, never a stack
   trace.

   Released under MIT. Its edge is printed in the README: kv-source parses PLAIN
   KEY=VALUE text into uniform string records only — it does not coerce types, does not
   interpolate ${VAR}, does not strip an `export ` prefix, does not honor inline
   comments, and does not process escapes inside quotes. An honest, pinnable parse.
*/
"use strict";

/* ---- the pure core ------------------------------------------------ */

var KEY_RE = /^[A-Za-z_][A-Za-z0-9_.\-]*$/;

// Strip one pair of matching outer quotes from an already-trimmed value, if present.
function stripQuotes(v) {
  if (v.length >= 2) {
    var a = v.charAt(0), b = v.charAt(v.length - 1);
    if ((a === '"' && b === '"') || (a === "'" && b === "'")) {
      return v.slice(1, v.length - 1);
    }
  }
  return v;
}

// Parse KEY=VALUE text into an array of uniform two-field records. Throws a clean Error
// on any malformed input — the CLI turns that into exit 2. Pure; no side effects.
function parse(text, opts) {
  opts = opts || {};
  var keyField = opts.keyField === undefined ? "key" : opts.keyField;
  var valueField = opts.valueField === undefined ? "value" : opts.valueField;

  if (typeof keyField !== "string" || keyField.length === 0) throw new Error("--key-field must be a non-empty name");
  if (typeof valueField !== "string" || valueField.length === 0) throw new Error("--value-field must be a non-empty name");
  if (keyField === valueField) throw new Error("--key-field and --value-field must differ (both \"" + keyField + "\")");
  if (typeof text !== "string") throw new Error("input must be text");

  var out = [];
  var lines = text.split("\n");
  for (var i = 0; i < lines.length; i++) {
    var line = lines[i];
    // strip a trailing CR so CRLF parses identically to LF
    if (line.charAt(line.length - 1) === "\r") line = line.slice(0, line.length - 1);

    var trimmed = line.replace(/^\s+/, "").replace(/\s+$/, "");
    if (trimmed.length === 0) continue;            // blank line
    if (trimmed.charAt(0) === "#") continue;       // full-line comment

    var eq = line.indexOf("=");
    if (eq === -1) throw new Error("malformed line " + (i + 1) + " (no '='): " + JSON.stringify(line));

    var key = line.slice(0, eq).replace(/^\s+/, "").replace(/\s+$/, "");
    var value = line.slice(eq + 1).replace(/^\s+/, "").replace(/\s+$/, "");

    if (!KEY_RE.test(key)) throw new Error("illegal key on line " + (i + 1) + ": " + JSON.stringify(key) + " (keys must match [A-Za-z_][A-Za-z0-9_.-]*)");

    value = stripQuotes(value);

    var rec = {};
    rec[keyField] = key;
    rec[valueField] = value;
    out.push(rec);
  }
  return out;
}

// Render records as JSONL text (one JSON object per line, trailing newline if any).
function toJSONL(records) {
  var s = "";
  for (var i = 0; i < records.length; i++) s += JSON.stringify(records[i]) + "\n";
  return s;
}

/* ---- exports (browser + Node) ------------------------------------ */
if (typeof window !== "undefined") {
  window.ForestGifts = window.ForestGifts || {};
  window.ForestGifts.kvSource = { parse: parse, toJSONL: toJSONL };
}
if (typeof module !== "undefined" && module.exports) {
  module.exports = { parse: parse, toJSONL: toJSONL };
}

/* ---- CLI (runs only when invoked directly, never on require) ------ */

function parseArgs(args) {
  var opts = {};
  var file;
  var i = 0;
  while (i < args.length) {
    var a = args[i];
    if (a === "--key-field") {
      if (args[i + 1] === undefined) throw new Error("--key-field requires a name");
      opts.keyField = args[i + 1];
      i += 2;
    } else if (a === "--value-field") {
      if (args[i + 1] === undefined) throw new Error("--value-field requires a name");
      opts.valueField = args[i + 1];
      i += 2;
    } else if (a.charAt(0) === "-" && a !== "-") {
      throw new Error("unknown option " + a);
    } else {
      if (file !== undefined) throw new Error("only one input file may be given (got a second: " + JSON.stringify(a) + ")");
      file = a;
      i += 1;
    }
  }
  return { file: file, opts: opts };
}

function readAll(stream) {
  return new Promise(function (resolve, reject) {
    var chunks = [];
    stream.on("data", function (c) { chunks.push(c); });
    stream.on("end", function () { resolve(Buffer.concat(chunks).toString("utf8")); });
    stream.on("error", reject);
  });
}

function helpText() {
  return (
    "kv-source.js — parse plain KEY=VALUE text into a uniform JSONL record stream.\n\n" +
    "  node kv-source.js config.env\n" +
    "  cat .env | node kv-source.js\n" +
    "  node kv-source.js .env --key-field name --value-field val\n" +
    "  node kv-source.js --help\n\n" +
    "  [FILE]              key=value file to parse (default: read stdin)\n" +
    "  --key-field NAME    field the parsed KEY is emitted under (default \"key\")\n" +
    "  --value-field NAME  field the parsed VALUE is emitted under (default \"value\")\n\n" +
    "Emits one JSON object per assignment: { key: NAME, value: VALUE }, one per line.\n" +
    "Full-line # comments and blank lines are skipped; lines split on the first '='.\n\n" +
    "Edge: PLAIN KEY=VALUE only, values are literal STRINGS. It does not coerce types,\n" +
    "does not interpolate ${VAR}, does not strip an `export ` prefix, does not honor\n" +
    "inline comments, and does not process escapes inside quotes. An honest, pinnable parse.\n"
  );
}

function main(argv) {
  var args = argv.slice(2);
  if (args.indexOf("--help") !== -1 || args.indexOf("-h") !== -1) {
    process.stdout.write(helpText());
    return Promise.resolve(0);
  }
  var parsed;
  try { parsed = parseArgs(args); }
  catch (e) { process.stderr.write("kv-source: " + e.message + "\n"); return Promise.resolve(2); }

  var getText;
  if (parsed.file !== undefined) {
    getText = new Promise(function (resolve, reject) {
      require("fs").readFile(parsed.file, "utf8", function (err, data) {
        if (err) reject(new Error("cannot read " + JSON.stringify(parsed.file) + ": " + err.code));
        else resolve(data);
      });
    });
  } else {
    getText = readAll(process.stdin);
  }

  return getText.then(function (text) {
    var records = parse(text, parsed.opts);   // throws -> caught below
    process.stdout.write(toJSONL(records));
    return 0;
  }).catch(function (e) {
    process.stderr.write("kv-source: " + e.message + "\n");
    return 2;
  });
}

if (typeof require !== "undefined" && require.main === module) {
  main(process.argv).then(function (code) { process.exitCode = code; });
}
test_kv-source.js147 lineson GitHub →
#!/usr/bin/env node
/* test_kv-source.js — battery for the kv-source gift.
   node test_kv-source.js  ->  exit 0 PASS / non-zero FAIL. Zero dependencies.

   Cross-checks the gift against an INDEPENDENT regex-route oracle (a different parse
   route than the gift's indexOf-split), frozen hand goldens, the uniform-record and
   strings-only contract, determinism, and every fail-closed edge. */
"use strict";
var kv = require("./kv-source.js");

var pass = 0, fail = 0;
function ok(name, cond) { if (cond) { pass++; } else { fail++; console.log("  FAIL  " + name); } }
function eq(name, a, b) {
  var same = JSON.stringify(a) === JSON.stringify(b);
  if (!same) console.log("  FAIL  " + name + "\n    exp " + JSON.stringify(b) + "\n    got " + JSON.stringify(a));
  else pass++;
  if (!same) fail++;
}
function throws(name, fn) {
  var threw = false;
  try { fn(); } catch (e) { threw = true; }
  if (threw) pass++; else { fail++; console.log("  FAIL  " + name + " (expected a throw)"); }
}

/* ---- independent oracle: regex-route (different route than the gift) ---- */
function oracle(text, opts) {
  opts = opts || {};
  var kf = opts.keyField === undefined ? "key" : opts.keyField;
  var vf = opts.valueField === undefined ? "value" : opts.valueField;
  if (!kf || !vf || kf === vf) throw new Error("bad fields");
  var out = [];
  var lines = text.split(/\r?\n/);
  for (var i = 0; i < lines.length; i++) {
    var ln = lines[i];
    if (/^\s*$/.test(ln)) continue;
    if (/^\s*#/.test(ln)) continue;
    var m = ln.match(/^\s*([A-Za-z_][A-Za-z0-9_.\-]*)\s*=([\s\S]*)$/);
    if (!m) throw new Error("bad line " + (i + 1));
    var val = m[2].replace(/^\s+/, "").replace(/\s+$/, "");
    if (val.length >= 2) {
      var a = val.charAt(0), b = val.charAt(val.length - 1);
      if ((a === '"' && b === '"') || (a === "'" && b === "'")) val = val.slice(1, val.length - 1);
    }
    var r = {}; r[kf] = m[1]; r[vf] = val; out.push(r);
  }
  return out;
}

/* ---- 1. differential grid: gift == oracle on every good vector ---- */
var goodVectors = [
  "PORT=8080",
  "A=1\nB=2\nC=3",
  "# a comment\nHOST=localhost\n\n# another\nPORT=80",
  "  SPACED  =  value here  ",              // trim both sides
  'GREETING="hi there"',                     // double-quoted, embedded space
  "MSG='single quoted'",
  "URL=http://example.com/path?a=b&c=d",     // first-= split; value keeps later '='
  "EMPTY=",                                  // empty value -> ""
  "HASHVAL=a#b#c",                           // '#' inside a value is literal (no inline comments)
  "K.dotted=1\nk-dashed=2\n_under=3",        // legal key charset
  "Q=\"with = and # inside\"",               // quotes protect nothing special but are stripped
  "CRLF=one\r\nSECOND=two\r\n",              // CRLF parses same as LF
  "",                                        // empty input -> empty stream
  "\n\n   \n# only comments and blanks\n",   // -> empty stream
  "EQ====",                                  // key EQ, value "===" (first '=' splits)
];
for (var gi = 0; gi < goodVectors.length; gi++) {
  var t = goodVectors[gi];
  eq("differential[" + gi + "] gift==oracle", kv.parse(t), oracle(t));
}

/* ---- 2. hand goldens (frozen) ---- */
eq("golden: basic", kv.parse("PORT=8080"), [{ key: "PORT", value: "8080" }]);
eq("golden: three", kv.parse("A=1\nB=2\nC=3"),
   [{ key: "A", value: "1" }, { key: "B", value: "2" }, { key: "C", value: "3" }]);
eq("golden: comment+blank skipped", kv.parse("# c\n\nHOST=localhost"), [{ key: "HOST", value: "localhost" }]);
eq("golden: trim both sides", kv.parse("  K  =  v  "), [{ key: "K", value: "v" }]);
eq("golden: double-quote stripped", kv.parse('G="hi there"'), [{ key: "G", value: "hi there" }]);
eq("golden: single-quote stripped", kv.parse("G='hi there'"), [{ key: "G", value: "hi there" }]);
eq("golden: first-= split keeps rest", kv.parse("U=a=b=c"), [{ key: "U", value: "a=b=c" }]);
eq("golden: empty value", kv.parse("E="), [{ key: "E", value: "" }]);
eq("golden: hash in value literal", kv.parse("H=a#b"), [{ key: "H", value: "a#b" }]);
eq("golden: CRLF == LF", kv.parse("A=1\r\nB=2\r\n"), kv.parse("A=1\nB=2\n"));
eq("golden: empty input empty stream", kv.parse(""), []);
eq("golden: only comments/blanks empty", kv.parse("\n#x\n   \n"), []);

/* ---- 3. uniform 2-field, strings-only ---- */
var recs = kv.parse("N=8080\nB=true\nF=1.5\nZ=");
ok("uniform: every record has exactly 2 keys", recs.every(function (r) { return Object.keys(r).length === 2; }));
ok("uniform: fields are key+value", recs.every(function (r) { return "key" in r && "value" in r; }));
ok("strings-only: numbers stay strings", recs[0].value === "8080" && typeof recs[0].value === "string");
ok("strings-only: bool stays string", recs[1].value === "true" && typeof recs[1].value === "string");
ok("strings-only: float stays string (no coercion/drift)", recs[2].value === "1.5" && typeof recs[2].value === "string");

/* ---- 4. custom field names ---- */
eq("fields: custom names", kv.parse("A=1", { keyField: "name", valueField: "val" }), [{ name: "A", val: "1" }]);
eq("fields: order key then value", Object.keys(kv.parse("A=1", { keyField: "k2", valueField: "v2" })[0]), ["k2", "v2"]);

/* ---- 5. no interpolation / no export / no inline comment ---- */
eq("no interpolation: ${VAR} literal", kv.parse("A=${HOME}/bin"), [{ key: "A", value: "${HOME}/bin" }]);
eq("no inline comment: trailing # kept", kv.parse("A=v # not a comment"), [{ key: "A", value: "v # not a comment" }]);
throws("no export: `export FOO` is an illegal key", function () { kv.parse("export FOO=bar"); });

/* ---- 6. fail-closed (exit-2 class) ---- */
throws("bad: line with no '='", function () { kv.parse("JUST_A_LINE"); });
throws("bad: empty key", function () { kv.parse("=value"); });
throws("bad: key with space", function () { kv.parse("bad key=1"); });
throws("bad: key starts with digit", function () { kv.parse("9key=1"); });
throws("bad: key with illegal char", function () { kv.parse("k!=1"); });
throws("bad: key-field == value-field", function () { kv.parse("A=1", { keyField: "x", valueField: "x" }); });
throws("bad: empty key-field", function () { kv.parse("A=1", { keyField: "" }); });
throws("bad: empty value-field", function () { kv.parse("A=1", { valueField: "" }); });
throws("bad: non-string input", function () { kv.parse(123); });

/* ---- 7. determinism: parse twice -> byte-identical JSONL ---- */
var big = "";
for (var k = 0; k < 50; k++) big += "K" + k + "=v" + k + "\n";
ok("determinism: two parses byte-identical", kv.toJSONL(kv.parse(big)) === kv.toJSONL(kv.parse(big)));

/* ---- 8. toJSONL shape ---- */
ok("toJSONL: one object per line + trailing nl", kv.toJSONL([{ key: "A", value: "1" }]) === '{"key":"A","value":"1"}\n');
ok("toJSONL: empty records -> empty string", kv.toJSONL([]) === "");

/* ---- 9. mutation tripwire: a LAST-'=' split must diverge on a value containing '=' ---- */
(function () {
  function mutantLastEq(text) {
    var out = [], lines = text.split("\n");
    for (var i = 0; i < lines.length; i++) {
      var line = lines[i];
      if (line.charAt(line.length - 1) === "\r") line = line.slice(0, -1);
      var tr = line.replace(/^\s+|\s+$/g, "");
      if (!tr || tr.charAt(0) === "#") continue;
      var eqp = line.lastIndexOf("=");                 // BUG: last instead of first
      if (eqp === -1) throw new Error("no =");
      var key = line.slice(0, eqp).replace(/^\s+|\s+$/g, "");
      var val = line.slice(eqp + 1).replace(/^\s+|\s+$/g, "");
      var r = {}; r.key = key; r.value = val; out.push(r);
    }
    return out;
  }
  var v = "U=a=b";
  ok("mutation: last-= split is CAUGHT (diverges from gift)",
     JSON.stringify(mutantLastEq(v)) !== JSON.stringify(kv.parse(v)));
})();

console.log((fail === 0 ? "PASS" : "FAIL") + "  " + pass + "/" + (pass + fail));
process.exit(fail === 0 ? 0 : 1);
Take the whole folder → MIT Node / browser, no dependencies