docs: codify the FK-safe stream ordering as a wire-format invariant
CI / lint (pull_request) Successful in 31s
CI / format (pull_request) Successful in 31s
CI / install-and-build (pull_request) Successful in 55s
CI / typecheck-tests (pull_request) Successful in 31s
CI / typecheck-source (pull_request) Successful in 31s
CI / test (pull_request) Successful in 42s
CI / lint (pull_request) Successful in 31s
CI / format (pull_request) Successful in 31s
CI / install-and-build (pull_request) Successful in 55s
CI / typecheck-tests (pull_request) Successful in 31s
CI / typecheck-source (pull_request) Successful in 31s
CI / test (pull_request) Successful in 42s
The postgres importer inserts records sequentially with FK enforcement live, so it depends on records arriving in dependency order. That requirement was implicit in each backend's export table list; make it explicit and enforced. - Add EXPORT_KIND_ORDER (user, abode, resident, apikey, note) as a documented single source of truth, with the FK dependency chain spelled out on its doc comment, and note the ordering guarantee on the ExportEnvelope wire-format doc. Derive the isExportKind set from it. - Both backends' export() now iterate EXPORT_KIND_ORDER via a loader map (postgres omits note by leaving it out of the map), so emission order is tied to the constant and can't drift. - Tests: a change-detector on EXPORT_KIND_ORDER, plus assertions that both the sqlite and postgres (fake) exports emit record kinds grouped in FK-safe order (kind rank non-decreasing down the stream, after the leading meta). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit was merged in pull request #13.
This commit is contained in:
@@ -45,13 +45,14 @@ import type {
|
||||
import type { WrappedPgClient } from "./pool.js";
|
||||
import { Readable } from "node:stream";
|
||||
import readline from "node:readline";
|
||||
import type {
|
||||
Exportable,
|
||||
ExportKind,
|
||||
ExportOptions,
|
||||
Importable,
|
||||
ImportOptions,
|
||||
ImportResult,
|
||||
import {
|
||||
EXPORT_KIND_ORDER,
|
||||
type Exportable,
|
||||
type ExportKind,
|
||||
type ExportOptions,
|
||||
type Importable,
|
||||
type ImportOptions,
|
||||
type ImportResult,
|
||||
} from "../types/ExportImport.js";
|
||||
import { isExportKind, kindAllowed, recordAllowed } from "../export/filter.js";
|
||||
|
||||
@@ -527,18 +528,19 @@ export class PostgresInterface
|
||||
const db = this.#db;
|
||||
const source = this.name;
|
||||
|
||||
// `note` is omitted: the postgres backend has no note CRUD yet, so a pg
|
||||
// database can hold none. Each `load()` is a single query, run lazily and
|
||||
// skipped once the destination aborts.
|
||||
const tables: [
|
||||
ExportKind,
|
||||
() => Promise<{ uid?: string; aid?: string }[]>,
|
||||
][] = [
|
||||
["user", () => selectClientUsers(db)],
|
||||
["abode", () => selectAbodes(db)],
|
||||
["resident", () => selectResidents(db)],
|
||||
["apikey", () => selectClientApikeys(db)],
|
||||
];
|
||||
// Emission follows the FK-safe EXPORT_KIND_ORDER (part of the wire
|
||||
// contract; see ExportImport.ts). `note` has no loader: the postgres
|
||||
// backend has no note CRUD yet, so a pg database can hold none. Each
|
||||
// `load()` is a single query, run lazily and skipped once the destination
|
||||
// aborts.
|
||||
const loaders: Partial<
|
||||
Record<ExportKind, () => Promise<{ uid?: string; aid?: string }[]>>
|
||||
> = {
|
||||
user: () => selectClientUsers(db),
|
||||
abode: () => selectAbodes(db),
|
||||
resident: () => selectResidents(db),
|
||||
apikey: () => selectClientApikeys(db),
|
||||
};
|
||||
|
||||
async function* generate(): AsyncGenerator<string> {
|
||||
if (signal?.aborted) return;
|
||||
@@ -552,8 +554,10 @@ export class PostgresInterface
|
||||
},
|
||||
}) + "\n";
|
||||
try {
|
||||
for (const [kind, load] of tables) {
|
||||
for (const kind of EXPORT_KIND_ORDER) {
|
||||
if (signal?.aborted) return;
|
||||
const load = loaders[kind];
|
||||
if (!load) continue;
|
||||
if (!kindAllowed(filter, kind)) continue;
|
||||
for (const row of await load()) {
|
||||
if (signal?.aborted) return;
|
||||
|
||||
Reference in New Issue
Block a user