Files
0pt/lib/import-dispatch-policy.ts
admin 1c87b82e70
CI (SIAX Cloud) / integration (push) Skipped
CI (SIAX Cloud) / sonar (push) Skipped
masterplan-lock / check (push) Successful in 6s
masterplan-lock / check (pull_request) Successful in 7s
CI (SIAX Cloud) / security (push) Failing after 27s
Security Scan (SIAX Cloud) / security (push) Failing after 1m27s
CI (SIAX Cloud) / quality (push) Successful in 1m28s
CI (SIAX Cloud) / quality (pull_request) Successful in 2m29s
CI (SIAX Cloud) / integration (pull_request) Successful in 1m15s
CI (SIAX Cloud) / security (pull_request) Failing after 16s
CI (SIAX Cloud) / sonar (pull_request) Skipped
Security Scan (SIAX Cloud) / security (pull_request) Failing after 17s
feat(import): ImportDispatchPolicy (DES1GN ADR D3 §31) — ett objekt, en sanning
- lib/import-dispatch-policy.ts: maxInlineRows=500 (inline-tak, oförändrat
  kontrakt), maxRows=5000 (N0D-dispatch-signal), maxBytes=1_000_000 (bytes,
  UTF-8), format-allowlist ['csv']; ren evaluateImportDispatch() med
  deterministisk precedens format > bytes > rows
- route: taken härstammar från policyn; bytes-koll (ersätter chars); rows >
  maxRows -> 413 n0d_dispatch_required med hint {capability_id:
  0pt.import_execute, job_type: 0pt.import_execute_job, status: UNVERIFIED}
  (descriptor-gate stängd: aldrig tyst trunkering, aldrig tyst dispatch)
- tester: 9 nya (verdict per kombination inkl. gränsvärden 500/501/5000/5001,
  bytes-gräns, allowlist, precedens; route-koppling 200/413/413) — 21/21 gröna
  i sviten; full N0D-worker-integration markeras UNVERIFIED
2026-09-30 21:48:16 +02:00

48 lines
2.1 KiB
TypeScript

// 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" };
}