// DES1GN ADR D3 (§31): bulk-import dispatch-policy — ETT objekt, en sanning. // // Import-routen (/api/orders/import) är inline-exekvering med hårt tak. Rader // över maxRows är DES1GN D3:s signal för N0D-dispatch (job_type // 0pt.import_execute_job / capability 0pt.import_execute i n0d:s // JOB_TYPE_REGISTRY). TILLS descriptor-gaten släpper (N0D-worker // UNVERIFIED) svarar routen 413 med n0d_dispatch_required + hint — aldrig // tyst trunkering, aldrig tyst dispatch. export const IMPORT_DISPATCH_POLICY = { /** Inline-tak i import-routen (oförändrat beteendekontrakt, 0pt#53). */ maxInlineRows: 500, /** N0D-dispatch-signal: rader över detta är ett N0D-jobb, inget HTTP-anrop. */ maxRows: 5000, /** Byte-tak för hela CSV-kroppen (UTF-8). */ maxBytes: 1_000_000, /** Format-allowlist — endast RFC 4180 CSV i dag. */ allowedFormats: ["csv"] as readonly string[], } as const; export type ImportDispatchPolicy = typeof IMPORT_DISPATCH_POLICY; export type ImportDispatchVerdict = | { verdict: "inline" } | { verdict: "unsupported_format"; format: string } | { verdict: "byte_cap_exceeded"; bytes: number; maxBytes: number } | { verdict: "row_cap_exceeded"; rows: number; maxInlineRows: number } | { verdict: "dispatch_n0d"; rows: number; maxRows: number }; /** Ren funktion — inga I/O, fullt deterministisk, testad per kombination. */ export function evaluateImportDispatch( input: { format: string; bytes: number; rows: number }, policy: ImportDispatchPolicy = IMPORT_DISPATCH_POLICY, ): ImportDispatchVerdict { if (!policy.allowedFormats.includes(input.format)) { return { verdict: "unsupported_format", format: input.format }; } if (input.bytes > policy.maxBytes) { return { verdict: "byte_cap_exceeded", bytes: input.bytes, maxBytes: policy.maxBytes }; } if (input.rows > policy.maxRows) { return { verdict: "dispatch_n0d", rows: input.rows, maxRows: policy.maxRows }; } if (input.rows > policy.maxInlineRows) { return { verdict: "row_cap_exceeded", rows: input.rows, maxInlineRows: policy.maxInlineRows }; } return { verdict: "inline" }; }