{"record":{"id":"e071b161180db23c","repo":"koala73/worldmonitor","slug":"worksheet-json-must-be-no-larger-than-64-kib-operational","errorCode":null,"errorMessage":"Worksheet JSON must be no larger than 64 KiB.","messagePattern":"Worksheet JSON must be no larger than 64 KiB\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/utils/operational-balance.ts","lineNumber":76,"sourceCode":"      const date = dateAt(input.startDate, index);\n      const arrivals = round(deliveries.filter(row => row.date === date).reduce((sum, row) => sum + row.quantity, 0));\n      const available = round(stock + arrivals);\n      const unmetDemand = round(Math.max(0, demand - available));\n      stock = round(Math.max(0, available - demand));\n      return { day: index + 1, date, arrivals, demand, closingStock: stock, unmetDemand };\n    });\n    return { days, firstGapDay: days.find(day => day.unmetDemand > 0)?.day ?? null, totalUnmetDemand: round(days.reduce((sum, day) => sum + day.unmetDemand, 0)) };\n  };\n  const baseline = balance(input.deliveries, input.dailyDemand);\n  const alternative = balance([...input.deliveries, ...input.alternativeDeliveries], input.alternativeDailyDemand ?? input.dailyDemand);\n  const costsKnown = [...input.deliveries, ...input.alternativeDeliveries].every(row => row.costUsd !== null);\n  return { schema: 'worldmonitor-operational-worksheet/v1', input, baseline, alternative,\n    avoidedUnmetDemand: round(baseline.totalUnmetDemand - alternative.totalUnmetDemand),\n    additionalDeliveryCostUsd: costsKnown ? round(input.alternativeDeliveries.reduce((sum, row) => sum + row.costUsd!, 0)) : null };\n}\n\nexport function importOperationalWorksheet(text: string): OperationalSnapshot {\n  if (new TextEncoder().encode(text).length > MAX_OPERATIONAL_JSON_BYTES) throw new Error('Worksheet JSON must be no larger than 64 KiB.');\n  const value = record(JSON.parse(text));\n  if (value.schema !== 'worldmonitor-operational-worksheet/v1') throw new Error('Unsupported worksheet format.');\n  return calculateOperationalBalance(value.input);\n}\n\nexport function operationalExample(): OperationalInput {\n  return { operation: 'Example operation', basis: 'example', unit: 'units', startDate: '2026-09-10', horizonDays: 10, startingStock: 100, dailyDemand: 20,\n    deliveries: [{ date: '2026-09-13', quantity: 40, unit: 'units', costUsd: null }], alternativeDeliveries: [], alternativeDailyDemand: null };\n}\n","sourceCodeStart":58,"sourceCodeEnd":86,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/src/utils/operational-balance.ts#L58-L86","documentation":"importOperationalWorksheet parses a pasted/uploaded worksheet JSON string and first enforces a size cap of MAX_OPERATIONAL_JSON_BYTES (64 KiB), measured as UTF-8 byte length via TextEncoder. Oversized text is rejected before JSON.parse to bound memory and abuse on the client.","triggerScenarios":"Calling importOperationalWorksheet(text) where text's UTF-8 encoding exceeds 65536 bytes — e.g. a worksheet with thousands of delivery rows, embedded notes/base64 blobs, or a user pasting an entire file's contents including unrelated data.","commonSituations":"Multi-byte characters (CJK, emoji) inflating byte count well past the character count; an export from another tool that doesn't share the 30-delivery cap; concatenating multiple worksheet exports.","solutions":["Measure new TextEncoder().encode(text).length before importing and trim deliveries/fields to fit under 65536 bytes.","Strip optional fields or extra whitespace/pretty-printing (minify the JSON) to shrink the payload.","Split the dataset across multiple worksheet imports.","Show a byte-counter in the import UI and warn before the user submits oversized text."],"exampleFix":"// before\nimportOperationalWorksheet(prettyPrintedJson);\n// after\nconst minified = JSON.stringify(JSON.parse(prettyPrintedJson));\nif (new TextEncoder().encode(minified).length > 65536) throw new Error('Worksheet exceeds 64 KiB after minification.');\nimportOperationalWorksheet(minified);","handlingStrategy":"validation","validationCode":"const bytes = new TextEncoder().encode(text).length;\nif (bytes > 65536) throw new Error(`Worksheet text is ${bytes} bytes; limit is 65536 (64 KiB).`);","typeGuard":null,"tryCatchPattern":"try {\n  const snapshot = importOperationalWorksheet(text);\n} catch (err) {\n  if (err instanceof Error && err.message === 'Worksheet JSON must be no larger than 64 KiB.') {\n    showImportError('Import too large; remove extra deliveries or minify the JSON.');\n  } else throw err;\n}","preventionTips":["Minify JSON (JSON.stringify(parsed)) before import to drop pretty-print whitespace.","Check UTF-8 byte length (not .length) — multi-byte characters count double or more.","Keep worksheets near the 30-delivery cap; they naturally stay far below 64 KiB."],"tags":["validation","payload-size","import"],"backgroundTag":"payload-too-large","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}