writeRow

tcmts.cli.data_pages.writeRow · #pfab4tt3 · term · runs on the server

uses node:crypto

Write value as row id of table (a new row, with a new id, if there's none), as the table's own writes are: checked against its row type first (the reason, if it doesn't fit), recorded with its schema, and refused when it would share a unique field with another row (that row named). Its id.

import type { Codebase } from "#codebase/tcmts/cli/codebase";
import { encode } from "#codebase/tcmts/cli/codec";
import { tableSchemaOf, toJson, withWritableStore } from "#codebase/tcmts/cli/data_pages";
import { checkValue } from "#codebase/tcmts/core/schema";
import { randomBytes } from "node:crypto";

export function writeRow(
  cb: Codebase,
  table: string,
  id: string | undefined,
  value: unknown,
): string {
  const t = tableSchemaOf(cb, table);
  if (!t)
    throw new Error(
      `there's no table "${table}" declared in the codebase: its row type is what writes are checked against`,
    );
  const bad = checkValue(t.s, value, "row", t.defs);
  if (bad) throw new TypeError(`table "${table}": ${bad}`);
  return withWritableStore(cb, (db) => {
    db.prepare(
      "INSERT OR IGNORE INTO schemas (tbl, hash, schema, first_seen) VALUES (?, ?, ?, ?)",
    ).run(table, t.hash, JSON.stringify({ s: t.s, defs: t.defs }), Date.now());
    let row = id;
    if (!row)
      do row = randomBytes(9).toString("base64url");
      while (
        row.startsWith("-") ||
        db.prepare("SELECT 1 FROM rows WHERE tbl = ? AND id = ?").get(table, row)
      );
    const field = (f: string) => `json_extract(data, '$."${f}"')`;
    const scalar = (v: unknown) =>
      typeof v === "boolean"
        ? Number(v)
        : v === null || ["string", "number"].includes(typeof v)
          ? v
          : encode(v);
    // (a unique field another row has: said, never a silent replacement)
    for (const f of t.unique ?? []) {
      const v = (value as Record<string, unknown>)[f];
      const other = db
        .prepare(`SELECT id FROM rows WHERE tbl = ? AND ${field(f)} = ? AND id != ?`)
        .get(table, scalar(v) as string | number | null, row) as { id: string } | undefined;
      if (other)
        throw new Error(
          `table "${table}": another row (${other.id}) already has ${f} = ${JSON.stringify(toJson(v))}`,
        );
    }
    db.prepare(
      "INSERT INTO rows (tbl, id, data, schema_hash, updated) VALUES (?, ?, ?, ?, ?) ON CONFLICT (tbl, id) DO UPDATE SET data = excluded.data, schema_hash = excluded.schema_hash, updated = excluded.updated",
    ).run(table, row, encode(value, `a ${table} row`), t.hash, Date.now());
    return row;
  });
}

uses

used by