SLICE-6: export & the round-trip lock — canonical serializer + streamed export (SD-0002 §7.2) #29
@@ -0,0 +1,12 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { EXPORT_STATUSES, exportUrl } from "./productsApi";
|
||||
|
||||
describe("export url", () => {
|
||||
it("lists the four status filters with 'all' first", () => {
|
||||
expect(EXPORT_STATUSES.map((s) => s.value)).toEqual(["all", "active", "draft", "archived"]);
|
||||
});
|
||||
it("builds the endpoint url with the status query", () => {
|
||||
expect(exportUrl("all")).toBe("/api/products/export?status=all");
|
||||
expect(exportUrl("archived")).toBe("/api/products/export?status=archived");
|
||||
});
|
||||
});
|
||||
@@ -47,6 +47,22 @@ export function dialectLabel(d: string): string {
|
||||
return d === "canonical" ? "Canonical format" : d;
|
||||
}
|
||||
|
||||
export type ExportStatus = "all" | "active" | "draft" | "archived";
|
||||
|
||||
// PUC-9 status filter, 'all' first (the default). Labels drive the export menu.
|
||||
export const EXPORT_STATUSES: { value: ExportStatus; label: string }[] = [
|
||||
{ value: "all", label: "All products" },
|
||||
{ value: "active", label: "Active" },
|
||||
{ value: "draft", label: "Draft" },
|
||||
{ value: "archived", label: "Archived" },
|
||||
];
|
||||
|
||||
// The export is a browser download (native save dialog + streaming), not a fetch
|
||||
// wrapper — so the API module contributes a URL builder, not a request().
|
||||
export function exportUrl(status: ExportStatus): string {
|
||||
return `/api/products/export?status=${status}`;
|
||||
}
|
||||
|
||||
async function request<T>(path: string, init?: RequestInit): Promise<Result<T>> {
|
||||
const resp = await fetch(path, { credentials: "include", ...init });
|
||||
if (!resp.ok) return { ok: false, error: await errorOf(resp), status: resp.status };
|
||||
|
||||
Reference in New Issue
Block a user