@yolk-sdk/emulators 0.1.0-canary.96

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2135 -0
  3. package/dist/anthropic.d.mts +269 -0
  4. package/dist/anthropic.d.mts.map +1 -0
  5. package/dist/anthropic.mjs +177 -0
  6. package/dist/anthropic.mjs.map +1 -0
  7. package/dist/chat-completions.d.mts +288 -0
  8. package/dist/chat-completions.d.mts.map +1 -0
  9. package/dist/chat-completions.mjs +451 -0
  10. package/dist/chat-completions.mjs.map +1 -0
  11. package/dist/codex.d.mts +265 -0
  12. package/dist/codex.d.mts.map +1 -0
  13. package/dist/codex.mjs +199 -0
  14. package/dist/codex.mjs.map +1 -0
  15. package/dist/dropbox/api.d.mts +63 -0
  16. package/dist/dropbox/api.d.mts.map +1 -0
  17. package/dist/dropbox/api.mjs +565 -0
  18. package/dist/dropbox/api.mjs.map +1 -0
  19. package/dist/dropbox/state.d.mts +136 -0
  20. package/dist/dropbox/state.d.mts.map +1 -0
  21. package/dist/dropbox/state.mjs +209 -0
  22. package/dist/dropbox/state.mjs.map +1 -0
  23. package/dist/dropbox.d.mts +79 -0
  24. package/dist/dropbox.d.mts.map +1 -0
  25. package/dist/dropbox.mjs +124 -0
  26. package/dist/dropbox.mjs.map +1 -0
  27. package/dist/email-fixtures.d.mts +36 -0
  28. package/dist/email-fixtures.d.mts.map +1 -0
  29. package/dist/email-fixtures.mjs +1080 -0
  30. package/dist/email-fixtures.mjs.map +1 -0
  31. package/dist/email.d.mts +160 -0
  32. package/dist/email.d.mts.map +1 -0
  33. package/dist/email.mjs +608 -0
  34. package/dist/email.mjs.map +1 -0
  35. package/dist/emulator-compose.d.mts +40 -0
  36. package/dist/emulator-compose.d.mts.map +1 -0
  37. package/dist/emulator-compose.mjs +91 -0
  38. package/dist/emulator-compose.mjs.map +1 -0
  39. package/dist/emulator-http.d.mts +53 -0
  40. package/dist/emulator-http.d.mts.map +1 -0
  41. package/dist/emulator-http.mjs +117 -0
  42. package/dist/emulator-http.mjs.map +1 -0
  43. package/dist/emulator-kernel.d.mts +176 -0
  44. package/dist/emulator-kernel.d.mts.map +1 -0
  45. package/dist/emulator-kernel.mjs +413 -0
  46. package/dist/emulator-kernel.mjs.map +1 -0
  47. package/dist/fixture-route.d.mts +129 -0
  48. package/dist/fixture-route.d.mts.map +1 -0
  49. package/dist/fixture-route.mjs +257 -0
  50. package/dist/fixture-route.mjs.map +1 -0
  51. package/dist/fortnox/api.d.mts +92 -0
  52. package/dist/fortnox/api.d.mts.map +1 -0
  53. package/dist/fortnox/api.mjs +750 -0
  54. package/dist/fortnox/api.mjs.map +1 -0
  55. package/dist/fortnox/state.d.mts +533 -0
  56. package/dist/fortnox/state.d.mts.map +1 -0
  57. package/dist/fortnox/state.mjs +612 -0
  58. package/dist/fortnox/state.mjs.map +1 -0
  59. package/dist/fortnox.d.mts +128 -0
  60. package/dist/fortnox.d.mts.map +1 -0
  61. package/dist/fortnox.mjs +403 -0
  62. package/dist/fortnox.mjs.map +1 -0
  63. package/dist/gateway-evaluate-recordings.d.mts +12 -0
  64. package/dist/gateway-evaluate-recordings.d.mts.map +1 -0
  65. package/dist/gateway-evaluate-recordings.mjs +128 -0
  66. package/dist/gateway-evaluate-recordings.mjs.map +1 -0
  67. package/dist/gateway.d.mts +272 -0
  68. package/dist/gateway.d.mts.map +1 -0
  69. package/dist/gateway.mjs +400 -0
  70. package/dist/gateway.mjs.map +1 -0
  71. package/dist/github/api.d.mts +63 -0
  72. package/dist/github/api.d.mts.map +1 -0
  73. package/dist/github/api.mjs +527 -0
  74. package/dist/github/api.mjs.map +1 -0
  75. package/dist/github/state.d.mts +272 -0
  76. package/dist/github/state.d.mts.map +1 -0
  77. package/dist/github/state.mjs +303 -0
  78. package/dist/github/state.mjs.map +1 -0
  79. package/dist/github.d.mts +77 -0
  80. package/dist/github.d.mts.map +1 -0
  81. package/dist/github.mjs +180 -0
  82. package/dist/github.mjs.map +1 -0
  83. package/dist/google/calendar.d.mts +10 -0
  84. package/dist/google/calendar.d.mts.map +1 -0
  85. package/dist/google/calendar.mjs +252 -0
  86. package/dist/google/calendar.mjs.map +1 -0
  87. package/dist/google/drive.d.mts +8 -0
  88. package/dist/google/drive.d.mts.map +1 -0
  89. package/dist/google/drive.mjs +252 -0
  90. package/dist/google/drive.mjs.map +1 -0
  91. package/dist/google/gmail.d.mts +10 -0
  92. package/dist/google/gmail.d.mts.map +1 -0
  93. package/dist/google/gmail.mjs +591 -0
  94. package/dist/google/gmail.mjs.map +1 -0
  95. package/dist/google/shared.d.mts +107 -0
  96. package/dist/google/shared.d.mts.map +1 -0
  97. package/dist/google/shared.mjs +146 -0
  98. package/dist/google/shared.mjs.map +1 -0
  99. package/dist/google/state.d.mts +385 -0
  100. package/dist/google/state.d.mts.map +1 -0
  101. package/dist/google/state.mjs +496 -0
  102. package/dist/google/state.mjs.map +1 -0
  103. package/dist/google.d.mts +75 -0
  104. package/dist/google.d.mts.map +1 -0
  105. package/dist/google.mjs +210 -0
  106. package/dist/google.mjs.map +1 -0
  107. package/dist/linkedin-search/api.d.mts +45 -0
  108. package/dist/linkedin-search/api.d.mts.map +1 -0
  109. package/dist/linkedin-search/api.mjs +181 -0
  110. package/dist/linkedin-search/api.mjs.map +1 -0
  111. package/dist/linkedin-search/state.d.mts +123 -0
  112. package/dist/linkedin-search/state.d.mts.map +1 -0
  113. package/dist/linkedin-search/state.mjs +261 -0
  114. package/dist/linkedin-search/state.mjs.map +1 -0
  115. package/dist/linkedin-search.d.mts +69 -0
  116. package/dist/linkedin-search.d.mts.map +1 -0
  117. package/dist/linkedin-search.mjs +158 -0
  118. package/dist/linkedin-search.mjs.map +1 -0
  119. package/dist/mcp/api.d.mts +44 -0
  120. package/dist/mcp/api.d.mts.map +1 -0
  121. package/dist/mcp/api.mjs +557 -0
  122. package/dist/mcp/api.mjs.map +1 -0
  123. package/dist/mcp/recordings.d.mts +40 -0
  124. package/dist/mcp/recordings.d.mts.map +1 -0
  125. package/dist/mcp/recordings.mjs +2390 -0
  126. package/dist/mcp/recordings.mjs.map +1 -0
  127. package/dist/mcp/state.d.mts +71 -0
  128. package/dist/mcp/state.d.mts.map +1 -0
  129. package/dist/mcp/state.mjs +83 -0
  130. package/dist/mcp/state.mjs.map +1 -0
  131. package/dist/mcp.d.mts +84 -0
  132. package/dist/mcp.d.mts.map +1 -0
  133. package/dist/mcp.mjs +241 -0
  134. package/dist/mcp.mjs.map +1 -0
  135. package/dist/messages.d.mts +183 -0
  136. package/dist/messages.d.mts.map +1 -0
  137. package/dist/messages.mjs +532 -0
  138. package/dist/messages.mjs.map +1 -0
  139. package/dist/microsoft/api.d.mts +47 -0
  140. package/dist/microsoft/api.d.mts.map +1 -0
  141. package/dist/microsoft/api.mjs +178 -0
  142. package/dist/microsoft/api.mjs.map +1 -0
  143. package/dist/microsoft/calendar.d.mts +30 -0
  144. package/dist/microsoft/calendar.d.mts.map +1 -0
  145. package/dist/microsoft/calendar.mjs +271 -0
  146. package/dist/microsoft/calendar.mjs.map +1 -0
  147. package/dist/microsoft/drive.d.mts +36 -0
  148. package/dist/microsoft/drive.d.mts.map +1 -0
  149. package/dist/microsoft/drive.mjs +298 -0
  150. package/dist/microsoft/drive.mjs.map +1 -0
  151. package/dist/microsoft/graph.d.mts +198 -0
  152. package/dist/microsoft/graph.d.mts.map +1 -0
  153. package/dist/microsoft/graph.mjs +264 -0
  154. package/dist/microsoft/graph.mjs.map +1 -0
  155. package/dist/microsoft/mail.d.mts +48 -0
  156. package/dist/microsoft/mail.d.mts.map +1 -0
  157. package/dist/microsoft/mail.mjs +488 -0
  158. package/dist/microsoft/mail.mjs.map +1 -0
  159. package/dist/microsoft/state.d.mts +480 -0
  160. package/dist/microsoft/state.d.mts.map +1 -0
  161. package/dist/microsoft/state.mjs +625 -0
  162. package/dist/microsoft/state.mjs.map +1 -0
  163. package/dist/microsoft.d.mts +168 -0
  164. package/dist/microsoft.d.mts.map +1 -0
  165. package/dist/microsoft.mjs +483 -0
  166. package/dist/microsoft.mjs.map +1 -0
  167. package/dist/node.d.mts +47 -0
  168. package/dist/node.d.mts.map +1 -0
  169. package/dist/node.mjs +178 -0
  170. package/dist/node.mjs.map +1 -0
  171. package/dist/notion/api.d.mts +51 -0
  172. package/dist/notion/api.d.mts.map +1 -0
  173. package/dist/notion/api.mjs +507 -0
  174. package/dist/notion/api.mjs.map +1 -0
  175. package/dist/notion/state.d.mts +329 -0
  176. package/dist/notion/state.d.mts.map +1 -0
  177. package/dist/notion/state.mjs +432 -0
  178. package/dist/notion/state.mjs.map +1 -0
  179. package/dist/notion.d.mts +72 -0
  180. package/dist/notion.d.mts.map +1 -0
  181. package/dist/notion.mjs +127 -0
  182. package/dist/notion.mjs.map +1 -0
  183. package/dist/openai.d.mts +165 -0
  184. package/dist/openai.d.mts.map +1 -0
  185. package/dist/openai.mjs +141 -0
  186. package/dist/openai.mjs.map +1 -0
  187. package/dist/opencode-recordings.d.mts +7 -0
  188. package/dist/opencode-recordings.d.mts.map +1 -0
  189. package/dist/opencode-recordings.mjs +222 -0
  190. package/dist/opencode-recordings.mjs.map +1 -0
  191. package/dist/opencode.d.mts +70 -0
  192. package/dist/opencode.d.mts.map +1 -0
  193. package/dist/opencode.mjs +217 -0
  194. package/dist/opencode.mjs.map +1 -0
  195. package/dist/r2-fixtures.d.mts +36 -0
  196. package/dist/r2-fixtures.d.mts.map +1 -0
  197. package/dist/r2-fixtures.mjs +245 -0
  198. package/dist/r2-fixtures.mjs.map +1 -0
  199. package/dist/r2-guard.d.mts +43 -0
  200. package/dist/r2-guard.d.mts.map +1 -0
  201. package/dist/r2-guard.mjs +215 -0
  202. package/dist/r2-guard.mjs.map +1 -0
  203. package/dist/r2.d.mts +166 -0
  204. package/dist/r2.d.mts.map +1 -0
  205. package/dist/r2.mjs +517 -0
  206. package/dist/r2.mjs.map +1 -0
  207. package/dist/responses.d.mts +231 -0
  208. package/dist/responses.d.mts.map +1 -0
  209. package/dist/responses.mjs +556 -0
  210. package/dist/responses.mjs.map +1 -0
  211. package/dist/route-evidence.d.mts +45 -0
  212. package/dist/route-evidence.d.mts.map +1 -0
  213. package/dist/route-evidence.mjs +56 -0
  214. package/dist/route-evidence.mjs.map +1 -0
  215. package/dist/router.d.mts +93 -0
  216. package/dist/router.d.mts.map +1 -0
  217. package/dist/router.mjs +259 -0
  218. package/dist/router.mjs.map +1 -0
  219. package/dist/stateful-core.d.mts +17 -0
  220. package/dist/stateful-core.d.mts.map +1 -0
  221. package/dist/stateful-core.mjs +41 -0
  222. package/dist/stateful-core.mjs.map +1 -0
  223. package/dist/stateful-emulator.d.mts +565 -0
  224. package/dist/stateful-emulator.d.mts.map +1 -0
  225. package/dist/stateful-emulator.mjs +1228 -0
  226. package/dist/stateful-emulator.mjs.map +1 -0
  227. package/dist/stateful-secrets.d.mts +84 -0
  228. package/dist/stateful-secrets.d.mts.map +1 -0
  229. package/dist/stateful-secrets.mjs +216 -0
  230. package/dist/stateful-secrets.mjs.map +1 -0
  231. package/dist/subscription-usage-recordings.d.mts +9 -0
  232. package/dist/subscription-usage-recordings.d.mts.map +1 -0
  233. package/dist/subscription-usage-recordings.mjs +53 -0
  234. package/dist/subscription-usage-recordings.mjs.map +1 -0
  235. package/dist/subscription-usage.d.mts +53 -0
  236. package/dist/subscription-usage.d.mts.map +1 -0
  237. package/dist/subscription-usage.mjs +21 -0
  238. package/dist/subscription-usage.mjs.map +1 -0
  239. package/dist/telegram/api.d.mts +34 -0
  240. package/dist/telegram/api.d.mts.map +1 -0
  241. package/dist/telegram/api.mjs +257 -0
  242. package/dist/telegram/api.mjs.map +1 -0
  243. package/dist/telegram/state.d.mts +128 -0
  244. package/dist/telegram/state.d.mts.map +1 -0
  245. package/dist/telegram/state.mjs +154 -0
  246. package/dist/telegram/state.mjs.map +1 -0
  247. package/dist/telegram.d.mts +62 -0
  248. package/dist/telegram.d.mts.map +1 -0
  249. package/dist/telegram.mjs +127 -0
  250. package/dist/telegram.mjs.map +1 -0
  251. package/dist/todoist/api.d.mts +55 -0
  252. package/dist/todoist/api.d.mts.map +1 -0
  253. package/dist/todoist/api.mjs +415 -0
  254. package/dist/todoist/api.mjs.map +1 -0
  255. package/dist/todoist/state.d.mts +275 -0
  256. package/dist/todoist/state.d.mts.map +1 -0
  257. package/dist/todoist/state.mjs +345 -0
  258. package/dist/todoist/state.mjs.map +1 -0
  259. package/dist/todoist.d.mts +68 -0
  260. package/dist/todoist.d.mts.map +1 -0
  261. package/dist/todoist.mjs +181 -0
  262. package/dist/todoist.mjs.map +1 -0
  263. package/dist/xai.d.mts +267 -0
  264. package/dist/xai.d.mts.map +1 -0
  265. package/dist/xai.mjs +252 -0
  266. package/dist/xai.mjs.map +1 -0
  267. package/package.json +157 -0
  268. package/src/anthropic.ts +289 -0
  269. package/src/chat-completions.ts +924 -0
  270. package/src/codex.ts +296 -0
  271. package/src/dropbox/api.ts +1084 -0
  272. package/src/dropbox/state.ts +364 -0
  273. package/src/dropbox.ts +203 -0
  274. package/src/email-fixtures.ts +1297 -0
  275. package/src/email.ts +1108 -0
  276. package/src/emulator-compose.ts +147 -0
  277. package/src/emulator-http.ts +184 -0
  278. package/src/emulator-kernel.ts +844 -0
  279. package/src/fixture-route.ts +561 -0
  280. package/src/fortnox/api.ts +1352 -0
  281. package/src/fortnox/state.ts +798 -0
  282. package/src/fortnox.ts +801 -0
  283. package/src/gateway-evaluate-recordings.ts +153 -0
  284. package/src/gateway.ts +530 -0
  285. package/src/github/api.ts +986 -0
  286. package/src/github/state.ts +439 -0
  287. package/src/github.ts +271 -0
  288. package/src/google/calendar.ts +486 -0
  289. package/src/google/drive.ts +471 -0
  290. package/src/google/gmail.ts +1011 -0
  291. package/src/google/shared.ts +321 -0
  292. package/src/google/state.ts +686 -0
  293. package/src/google.ts +298 -0
  294. package/src/linkedin-search/api.ts +363 -0
  295. package/src/linkedin-search/state.ts +373 -0
  296. package/src/linkedin-search.ts +241 -0
  297. package/src/mcp/api.ts +995 -0
  298. package/src/mcp/recordings.ts +2665 -0
  299. package/src/mcp/state.ts +125 -0
  300. package/src/mcp.ts +319 -0
  301. package/src/messages.ts +844 -0
  302. package/src/microsoft/api.ts +426 -0
  303. package/src/microsoft/calendar.ts +491 -0
  304. package/src/microsoft/drive.ts +541 -0
  305. package/src/microsoft/graph.ts +550 -0
  306. package/src/microsoft/mail.ts +833 -0
  307. package/src/microsoft/state.ts +822 -0
  308. package/src/microsoft.ts +982 -0
  309. package/src/node.ts +293 -0
  310. package/src/notion/api.ts +976 -0
  311. package/src/notion/state.ts +512 -0
  312. package/src/notion.ts +210 -0
  313. package/src/openai.ts +198 -0
  314. package/src/opencode-recordings.ts +257 -0
  315. package/src/opencode.ts +271 -0
  316. package/src/r2-fixtures.ts +295 -0
  317. package/src/r2-guard.ts +323 -0
  318. package/src/r2.ts +890 -0
  319. package/src/responses.ts +901 -0
  320. package/src/route-evidence.ts +90 -0
  321. package/src/router.ts +490 -0
  322. package/src/stateful-core.ts +70 -0
  323. package/src/stateful-emulator.ts +2571 -0
  324. package/src/stateful-secrets.ts +299 -0
  325. package/src/subscription-usage-recordings.ts +77 -0
  326. package/src/subscription-usage.ts +68 -0
  327. package/src/telegram/api.ts +437 -0
  328. package/src/telegram/state.ts +229 -0
  329. package/src/telegram.ts +227 -0
  330. package/src/todoist/api.ts +736 -0
  331. package/src/todoist/state.ts +456 -0
  332. package/src/todoist.ts +316 -0
  333. package/src/xai.ts +341 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.mjs","names":[],"sources":["../../src/fortnox/api.ts"],"sourcesContent":["/**\n * Fortnox emulator API: the route table (with its evidence) and the stateful route handlers\n * registered on the `@emulators/core` app (internal; re-exported by `src/fortnox.ts`).\n *\n * Wire shapes follow the synthetic Fortnox conformance fixtures, copied as data. Each route lists\n * the conformance cases whose claims it follows; the observed quirks are implemented here.\n *\n * @experimental\n */\nimport type { Hono } from '@emulators/core'\nimport { Predicate, Result } from 'effect'\nimport * as Schema from 'effect/Schema'\nimport { answeredOutsideCore, emulatorJobHeader, handlerFailedResponse } from '../emulator-http.ts'\nimport type { EmulatorRouteEvidence } from '../route-evidence.ts'\nimport {\n buildRow,\n compareNumbers,\n defaultEmailInformation,\n formatQuantity,\n fortnoxEmulatorCountries,\n fortnoxEmulatorVatCodes,\n invoiceFromSeed,\n invoiceTotals,\n newRowDefaults,\n type FortnoxEmulatorCustomer,\n type FortnoxEmulatorInvoice,\n type FortnoxEmulatorInvoiceRow,\n type FortnoxEmulatorInvoiceRowSeed,\n type FortnoxEmulatorInvoiceSeed,\n type FortnoxEmulatorState\n} from './state.ts'\n\n/**\n * Drill knobs: each flips one observed quirk to the documented-but-wrong behavior, ONLY to prove\n * that the conformance cases catch a disagreement. Defaults follow the observed behavior.\n */\nexport type FortnoxEmulatorQuirks = {\n /** `false`: a positionally matched row that omits `Discount`/`DiscountType` resets them. */\n readonly stickyRowDiscount?: boolean\n /** `true`: a customer update with `\"\"` clears the stored string instead of keeping it. */\n readonly emptyStringClears?: boolean\n /** `true`: `unpaid`/`unpaidoverdue`/`fullypaid` also match unbooked invoices. */\n readonly paymentFiltersIncludeUnbooked?: boolean\n}\n\nexport type FortnoxApiEnv = {\n /** Clock in epoch milliseconds (the `unpaidoverdue` \"today\", new invoice dates, the outbox). */\n readonly now: () => number\n /** Origin used in `@url` links, for example `https://api.fortnox.se`. */\n readonly linkOrigin: string\n readonly quirks: Required<FortnoxEmulatorQuirks>\n}\n\n/**\n * `ErrorInformation` codes the emulator answers with. The two customer-not-found codes are copied\n * from the rejection fixture; the `2999xxx` codes are synthetic emulator codes.\n */\nexport const fortnoxEmulatorErrorCodes = {\n customerNotFound: 2000204,\n invoiceCustomerNotFound: 2000433,\n unauthorized: 2999001,\n invalidBody: 2999002,\n invalidField: 2999003,\n readOnlyField: 2999004,\n notEditable: 2999005,\n invoiceNotFound: 2999006,\n unknownRoute: 2999007,\n unsupportedQuery: 2999008,\n rateLimited: 2999009,\n upstreamError: 2999010\n} as const\n\n/** Fortnox API base path. */\nexport const fortnoxEmulatorBasePath = '/3'\n\nconst jsonResponse = (status: number, body: unknown): Response =>\n new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json' }\n })\n\n/** A Fortnox `ErrorInformation` response with the lowercase fields of the rejection fixture. */\nexport const errorInformation = (status: number, code: number, message: string): Response =>\n jsonResponse(status, { ErrorInformation: { error: 1, message, code } })\n\nconst codes = fortnoxEmulatorErrorCodes\n\n// Messages copied from the rejection fixture.\nconst customerNotFound = (): Response =>\n errorInformation(404, codes.customerNotFound, 'Synthetic placeholder: customer not found.')\n\nconst invoiceCustomerNotFound = (): Response =>\n errorInformation(400, codes.invoiceCustomerNotFound, 'Synthetic placeholder: customer not found.')\n\nconst invoiceNotFound = (): Response =>\n errorInformation(404, codes.invoiceNotFound, 'Synthetic: invoice not found.')\n\nconst invalidBody = (message: string): Response =>\n errorInformation(400, codes.invalidBody, `Synthetic: ${message}`)\n\nconst invalidField = (message: string): Response =>\n errorInformation(400, codes.invalidField, `Synthetic: ${message}`)\n\nconst noReferencedEntity = (kind: string, value: string): Response =>\n invalidField(`${kind} ${value} does not exist in the emulated company.`)\n\nconst readOnlyField = (message: string): Response =>\n errorInformation(400, codes.readOnlyField, `Synthetic: ${message}`)\n\nconst notEditable = (message: string): Response =>\n errorInformation(400, codes.notEditable, `Synthetic: ${message}`)\n\nconst unsupportedQuery = (key: string): Response =>\n errorInformation(\n 400,\n codes.unsupportedQuery,\n key === 'lastmodified'\n ? 'Synthetic: query parameter lastmodified is not emulated (the emulator does not track modification times).'\n : `Synthetic: query parameter ${key} is not emulated.`\n )\n\n// The synthetic invoice preview PDF from the preview fixture (`bodyBase64`), copied as data.\nconst previewPdfBase64 =\n 'JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFtdIC9Db3VudCAwID4+CmVuZG9iagp0cmFpbGVyCjw8IC9Sb290IDEgMCBSID4+CiUlRU9GCg=='\n\nconst previewPdfBytes = (): Uint8Array<ArrayBuffer> =>\n Uint8Array.from(atob(previewPdfBase64), character => character.charCodeAt(0))\n\ntype RouteRequest = {\n readonly params: Readonly<Record<string, string>>\n readonly query: URLSearchParams\n /** Parsed JSON body; `undefined` when absent or not JSON. */\n readonly body: Schema.Json | undefined\n}\n\nconst isJsonObject = (value: Schema.Json | undefined): value is Schema.JsonObject =>\n value !== undefined && value !== null && Predicate.isObject(value) && !Array.isArray(value)\n\ntype RouteHandler = (\n state: FortnoxEmulatorState,\n request: RouteRequest,\n env: FortnoxApiEnv\n) => Response\n\ntype FortnoxApiRoute = EmulatorRouteEvidence & {\n /**\n * Query parameters the route emulates; any other key answers 400 before the handler runs, so\n * a rejected request never writes. Empty by default.\n */\n readonly queryKeys: ReadonlyArray<string>\n readonly handler: RouteHandler\n}\n\nconst isoDatePattern = /^\\d{4}-\\d{2}-\\d{2}$/\n\n/** A real `YYYY-MM-DD` calendar date (no JavaScript rollover such as `2026-02-30`). */\nconst isIsoDate = (value: string): boolean => {\n const parsed = Date.parse(`${value}T00:00:00.000Z`)\n\n return (\n isoDatePattern.test(value) &&\n !Number.isNaN(parsed) &&\n new Date(parsed).toISOString().slice(0, 10) === value\n )\n}\n\nconst today = (env: FortnoxApiEnv): string => new Date(env.now()).toISOString().slice(0, 10)\n\n/** `date` plus `days`, or `undefined` when the result is not a representable `YYYY-MM-DD` date. */\nconst addDays = (date: string, days: number): string | undefined => {\n const result = new Date(Date.parse(`${date}T00:00:00.000Z`) + days * 24 * 60 * 60 * 1000)\n\n if (Number.isNaN(result.getTime())) return undefined\n\n const iso = result.toISOString().slice(0, 10)\n\n return isIsoDate(iso) ? iso : undefined\n}\n\nconst maxTermsOfPaymentDays = 365\n\n/**\n * `TermsOfPayment` as a whole number of days (`0`–`365`, no sign or leading zeros), or\n * `undefined` when the value is not emulated. Fortnox also knows named terms (such as `K`); the\n * emulated company has day terms only.\n */\nconst termsOfPaymentDays = (value: string): number | undefined => {\n if (!/^(?:0|[1-9]\\d{0,2})$/.test(value)) return undefined\n\n const days = Number(value)\n\n return days <= maxTermsOfPaymentDays ? days : undefined\n}\n\nconst unsupportedTermsOfPayment = (value: string): Response =>\n invalidField(\n `TermsOfPayment ${value} is not emulated (whole days from 0 to ${maxTermsOfPaymentDays} only).`\n )\n\n/**\n * The customer categorical values the emulator deliberately models. This is the emulated subset,\n * not Fortnox's full enum: other values (for example export or EU reverse-charge VAT) would change\n * invoice VAT, which the emulator does not model, so they are rejected.\n */\nconst emulatedCustomerVatTypes: ReadonlyArray<string> = ['SEVAT']\n\nconst emulatedCustomerTypes: ReadonlyArray<string> = ['COMPANY', 'PRIVATE']\n\nconst unsupportedVatType = (value: string): Response =>\n invalidField(\n `VATType ${value} is not emulated (supported: ${emulatedCustomerVatTypes.join(', ')}).`\n )\n\nconst customerUrl = (env: FortnoxApiEnv, customerNumber: string): string =>\n `${env.linkOrigin}${fortnoxEmulatorBasePath}/customers/${encodeURIComponent(customerNumber)}`\n\nconst invoiceUrl = (env: FortnoxApiEnv, documentNumber: string): string =>\n `${env.linkOrigin}${fortnoxEmulatorBasePath}/invoices/${encodeURIComponent(documentNumber)}`\n\nconst renderCustomer = (env: FortnoxApiEnv, customer: FortnoxEmulatorCustomer) => ({\n '@url': customerUrl(env, customer.CustomerNumber),\n ...customer\n})\n\n// No fixture records the customer list: rows carry the list-only `Phone` and a subset of fields.\nconst renderCustomerRow = (env: FortnoxApiEnv, customer: FortnoxEmulatorCustomer) => ({\n '@url': customerUrl(env, customer.CustomerNumber),\n Address1: customer.Address1,\n Address2: customer.Address2,\n City: customer.City,\n CustomerNumber: customer.CustomerNumber,\n Email: customer.Email,\n Name: customer.Name,\n OrganisationNumber: customer.OrganisationNumber,\n Phone: customer.Phone1,\n ZipCode: customer.ZipCode\n})\n\ntype EmailInformationFields = {\n -readonly [\n K in keyof FortnoxEmulatorInvoice['EmailInformation']\n ]: FortnoxEmulatorInvoice['EmailInformation'][K]\n}\n\n/**\n * A single-invoice read: JSON-number amounts. `dropEmptyCopies` leaves empty CC/BCC addresses out\n * of `EmailInformation`, as the email-send fixture response does.\n */\nconst renderInvoice = (\n env: FortnoxApiEnv,\n invoice: FortnoxEmulatorInvoice,\n dropEmptyCopies = false\n) => {\n const source = invoice.EmailInformation\n\n const email: Partial<EmailInformationFields> = {\n EmailAddressFrom: source.EmailAddressFrom,\n EmailAddressTo: source.EmailAddressTo\n }\n\n if (!dropEmptyCopies || source.EmailAddressCC !== '') {\n email.EmailAddressCC = source.EmailAddressCC\n }\n\n if (!dropEmptyCopies || source.EmailAddressBCC !== '') {\n email.EmailAddressBCC = source.EmailAddressBCC\n }\n\n email.EmailSubject = source.EmailSubject\n email.EmailBody = source.EmailBody\n\n return {\n '@url': invoiceUrl(env, invoice.DocumentNumber),\n ...invoice,\n EmailInformation: email\n }\n}\n\n/** An invoice list row, in the list fixture's shape: `CurrencyRate` is a numeric string. */\nconst renderInvoiceRow = (env: FortnoxApiEnv, invoice: FortnoxEmulatorInvoice) => ({\n '@url': invoiceUrl(env, invoice.DocumentNumber),\n Balance: invoice.Balance,\n Booked: invoice.Booked,\n Cancelled: invoice.Cancelled,\n CostCenter: invoice.CostCenter,\n Currency: invoice.Currency,\n CurrencyRate: String(invoice.CurrencyRate),\n CurrencyUnit: invoice.CurrencyUnit,\n CustomerName: invoice.CustomerName,\n CustomerNumber: invoice.CustomerNumber,\n DocumentNumber: invoice.DocumentNumber,\n DueDate: invoice.DueDate,\n ExternalInvoiceReference1: invoice.ExternalInvoiceReference1,\n ExternalInvoiceReference2: invoice.ExternalInvoiceReference2,\n FinalPayDate: invoice.FinalPayDate,\n InvoiceDate: invoice.InvoiceDate,\n InvoiceType: invoice.InvoiceType,\n NoxFinans: invoice.NoxFinans,\n OCR: invoice.OCR,\n VoucherNumber: invoice.VoucherNumber,\n VoucherSeries: invoice.VoucherSeries,\n VoucherYear: invoice.VoucherYear,\n WayOfDelivery: invoice.WayOfDelivery,\n TermsOfPayment: invoice.TermsOfPayment,\n Project: invoice.Project,\n Sent: invoice.Sent,\n Total: invoice.Total\n})\n\n/** The 400 for the first query key the route does not emulate, or `undefined`. */\nconst unsupportedQueryKey = (\n query: URLSearchParams,\n allowed: ReadonlyArray<string>\n): Response | undefined => {\n const key = [...query.keys()].find(candidate => !allowed.includes(candidate))\n\n return key === undefined ? undefined : unsupportedQuery(key)\n}\n\nconst positiveInt = (\n query: URLSearchParams,\n key: string,\n fallback: number,\n maximum: number\n): number | Response => {\n const raw = query.get(key)\n\n if (raw === null) {\n return fallback\n }\n\n const value = Number(raw)\n\n return /^\\d+$/.test(raw) && value >= 1 && value <= maximum\n ? value\n : invalidField(`query parameter ${key} must be an integer from 1 to ${maximum}.`)\n}\n\n/** Fortnox default and maximum page sizes. */\nconst defaultLimit = 100\n\nconst maxLimit = 500\n\n/** One page plus `MetaInformation` (`@TotalPages` is at least 1, as the empty-list fixture shows). */\nconst paginate = <A>(\n items: ReadonlyArray<A>,\n query: URLSearchParams\n): { readonly items: ReadonlyArray<A>; readonly meta: Record<string, number> } | Response => {\n const limit = positiveInt(query, 'limit', defaultLimit, maxLimit)\n const page = positiveInt(query, 'page', 1, Number.MAX_SAFE_INTEGER)\n\n if (limit instanceof Response) return limit\n\n if (page instanceof Response) return page\n\n return {\n items: items.slice((page - 1) * limit, page * limit),\n meta: {\n '@CurrentPage': page,\n '@TotalPages': Math.max(1, Math.ceil(items.length / limit)),\n '@TotalResources': items.length\n }\n }\n}\n\nconst contains = (value: string, search: string): boolean =>\n value.toLowerCase().includes(search.toLowerCase())\n\ntype CustomerSearch = (customer: FortnoxEmulatorCustomer, value: string) => boolean\n\nconst customerSearch = {\n customernumber: (customer, value) => customer.CustomerNumber === value,\n name: (customer, value) => contains(customer.Name, value),\n organisationnumber: (customer, value) => contains(customer.OrganisationNumber, value),\n email: (customer, value) => contains(customer.Email, value),\n city: (customer, value) => contains(customer.City, value),\n phone: (customer, value) => contains(customer.Phone1, value) || contains(customer.Phone2, value)\n} satisfies Record<string, CustomerSearch>\n\nconst customerListQueryKeys: ReadonlyArray<string> = [\n 'page',\n 'limit',\n 'filter',\n ...Object.keys(customerSearch)\n]\n\nconst listCustomers: RouteHandler = (state, { query }, env) => {\n const filter = query.get('filter')\n\n if (filter !== null && filter !== 'active' && filter !== 'inactive') {\n return invalidField(`unknown customer filter ${filter}.`)\n }\n\n const matching = state.customers.filter(\n customer =>\n (filter === null || customer.Active === (filter === 'active')) &&\n Object.entries(customerSearch).every(([key, matches]) => {\n const value = query.get(key)\n\n return value === null || matches(customer, value)\n })\n )\n\n const page = paginate(matching, query)\n\n if (page instanceof Response) return page\n\n return jsonResponse(200, {\n Customers: page.items.map(customer => renderCustomerRow(env, customer)),\n MetaInformation: page.meta\n })\n}\n\nconst findCustomer = (state: FortnoxEmulatorState, customerNumber: string) =>\n state.customers.find(customer => customer.CustomerNumber === customerNumber)\n\nconst findInvoice = (state: FortnoxEmulatorState, documentNumber: string) =>\n state.invoices.find(invoice => invoice.DocumentNumber === documentNumber)\n\nconst getCustomer: RouteHandler = (state, { params }, env) => {\n const customer = findCustomer(state, params.CustomerNumber ?? '')\n\n return customer === undefined\n ? customerNotFound()\n : jsonResponse(200, { Customer: renderCustomer(env, customer) })\n}\n\n/** The single resource object of a `{ <envelope>: { ... } }` write body. */\nconst envelopeObject = (\n body: Schema.Json | undefined,\n envelope: string\n): Schema.JsonObject | Response => {\n const value = isJsonObject(body) ? body[envelope] : undefined\n\n return isJsonObject(body) &&\n Object.keys(body).every(key => key === envelope) &&\n isJsonObject(value)\n ? value\n : invalidBody(`the request body must be { \"${envelope}\": { ... } }.`)\n}\n\nconst writableCustomerStrings = [\n 'Name',\n 'OrganisationNumber',\n 'Email',\n 'Phone1',\n 'Phone2',\n 'Address1',\n 'Address2',\n 'City',\n 'ZipCode',\n 'CountryCode',\n 'Currency',\n 'VATNumber',\n 'VATType',\n 'TermsOfPayment',\n 'OurReference',\n 'YourReference',\n 'Comments',\n 'EmailInvoice',\n 'Type',\n 'CostCenter'\n] as const\n\ntype CustomerStringField = (typeof writableCustomerStrings)[number]\n\nconst isCustomerStringField = (key: string): key is CustomerStringField =>\n writableCustomerStrings.some(field => field === key)\n\ntype CustomerUpdates = {\n -readonly [K in keyof FortnoxEmulatorCustomer]?: FortnoxEmulatorCustomer[K]\n}\n\n/**\n * Why a customer field value is not emulated (fail closed), or `undefined`. The emulated company\n * has only SEK, no cost centers, terms of payment in whole days (0–365), `VATType` `SEVAT`, and\n * `Type` `COMPANY` or `PRIVATE`.\n */\nconst unsupportedCustomerValue = (\n key: CustomerStringField,\n value: string\n): Response | undefined => {\n switch (key) {\n case 'Currency':\n return value === 'SEK' ? undefined : noReferencedEntity('Currency', value)\n case 'CostCenter':\n return value === '' ? undefined : noReferencedEntity('CostCenter', value)\n case 'TermsOfPayment':\n return termsOfPaymentDays(value) === undefined ? unsupportedTermsOfPayment(value) : undefined\n case 'VATType':\n return emulatedCustomerVatTypes.includes(value) ? undefined : unsupportedVatType(value)\n case 'Type':\n return emulatedCustomerTypes.includes(value)\n ? undefined\n : invalidField(\n `Type ${value} is not emulated (supported: ${emulatedCustomerTypes.join(', ')}).`\n )\n default:\n return undefined\n }\n}\n\n/**\n * Quirk 2: an empty string keeps the stored value (unless the `emptyStringClears` drill knob is\n * set); omitted fields keep theirs. Quirk 5: `Country` is read-only. Unknown fields, wrong types,\n * and values the emulator does not support (non-SEK `Currency`, a `CostCenter`, `TermsOfPayment`\n * other than 0–365 days, `VATType` other than `SEVAT`, `Type` other than `COMPANY`/`PRIVATE`) are\n * rejected; the update is atomic.\n */\nconst customerUpdates = (\n fields: Schema.JsonObject,\n customer: FortnoxEmulatorCustomer,\n env: FortnoxApiEnv\n): CustomerUpdates | Response => {\n const updates: CustomerUpdates = {}\n\n for (const [key, value] of Object.entries(fields)) {\n if (key === 'Country') {\n return readOnlyField('Country is read-only; set CountryCode instead.')\n }\n\n if (key === 'CustomerNumber') {\n if (value !== customer.CustomerNumber) {\n return invalidField('CustomerNumber cannot be changed.')\n }\n\n continue\n }\n\n if (key === 'Active') {\n if (!Predicate.isBoolean(value)) return invalidField('Active must be a boolean.')\n\n updates.Active = value\n\n continue\n }\n\n if (!isCustomerStringField(key)) {\n return invalidField(`unknown customer field ${key}.`)\n }\n\n if (!Predicate.isString(value)) {\n return invalidField(`${key} must be a string.`)\n }\n\n if (value === '' && !env.quirks.emptyStringClears) {\n continue\n }\n\n const unsupported = unsupportedCustomerValue(key, value)\n\n if (unsupported !== undefined) return unsupported\n\n if (key === 'CountryCode' && value !== '') {\n const country = fortnoxEmulatorCountries.get(value)\n\n if (country === undefined) return invalidField(`unknown CountryCode ${value}.`)\n\n updates.Country = country\n }\n\n updates[key] = value\n }\n\n return updates\n}\n\nconst updateCustomer: RouteHandler = (state, { params, body }, env) => {\n const customer = findCustomer(state, params.CustomerNumber ?? '')\n\n if (customer === undefined) return customerNotFound()\n\n const fields = envelopeObject(body, 'Customer')\n\n if (fields instanceof Response) return fields\n\n const updates = customerUpdates(fields, customer, env)\n\n if (updates instanceof Response) return updates\n\n const updated: FortnoxEmulatorCustomer = { ...customer, ...updates }\n\n state.customers = state.customers.map(existing =>\n existing.CustomerNumber === updated.CustomerNumber ? updated : existing\n )\n\n return jsonResponse(200, { Customer: renderCustomer(env, updated) })\n}\n\ntype InvoiceFilter = 'cancelled' | 'fullypaid' | 'unpaid' | 'unpaidoverdue' | 'unbooked'\n\nconst invoiceFilters: ReadonlyArray<InvoiceFilter> = [\n 'cancelled',\n 'fullypaid',\n 'unpaid',\n 'unpaidoverdue',\n 'unbooked'\n]\n\nconst isInvoiceFilter = (value: string): value is InvoiceFilter =>\n invoiceFilters.some(filter => filter === value)\n\n/**\n * Quirk 3: the payment-status filters consider booked invoices only (unless the\n * `paymentFiltersIncludeUnbooked` drill knob is set); `unbooked` returns unbooked, uncancelled\n * invoices; `unpaidoverdue` needs a `DueDate` before today (UTC, from the emulator clock).\n */\nconst matchesInvoiceFilter = (\n invoice: FortnoxEmulatorInvoice,\n filter: InvoiceFilter,\n env: FortnoxApiEnv\n): boolean => {\n const considered =\n (invoice.Booked || env.quirks.paymentFiltersIncludeUnbooked) && !invoice.Cancelled\n\n switch (filter) {\n case 'cancelled':\n return invoice.Cancelled\n case 'unbooked':\n return !invoice.Booked && !invoice.Cancelled\n case 'unpaid':\n return considered && invoice.Balance > 0\n case 'unpaidoverdue':\n return (\n considered &&\n invoice.Balance > 0 &&\n invoice.DueDate !== null &&\n isIsoDate(invoice.DueDate) &&\n invoice.DueDate < today(env)\n )\n case 'fullypaid':\n return considered && invoice.Balance === 0\n }\n}\n\ntype InvoiceSearch = (invoice: FortnoxEmulatorInvoice, value: string) => boolean\n\nconst invoiceSearch = {\n customernumber: (invoice, value) => invoice.CustomerNumber === value,\n customername: (invoice, value) => contains(invoice.CustomerName, value),\n documentnumber: (invoice, value) => invoice.DocumentNumber === value,\n ocr: (invoice, value) => invoice.OCR === value\n} satisfies Record<string, InvoiceSearch>\n\nconst invoiceListQueryKeys: ReadonlyArray<string> = [\n 'page',\n 'limit',\n 'filter',\n 'fromdate',\n 'todate',\n ...Object.keys(invoiceSearch)\n]\n\nconst listInvoices: RouteHandler = (state, { query }, env) => {\n const filter = query.get('filter')\n\n if (filter !== null && !isInvoiceFilter(filter)) {\n return invalidField(`unknown invoice filter ${filter}.`)\n }\n\n const fromDate = query.get('fromdate')\n const toDate = query.get('todate')\n\n for (const [key, value] of [\n ['fromdate', fromDate],\n ['todate', toDate]\n ] as const) {\n if (value !== null && !isIsoDate(value)) {\n return invalidField(`query parameter ${key} must be a YYYY-MM-DD date.`)\n }\n }\n\n const matching = state.invoices.filter(\n invoice =>\n (filter === null || matchesInvoiceFilter(invoice, filter, env)) &&\n (fromDate === null || invoice.InvoiceDate >= fromDate) &&\n (toDate === null || invoice.InvoiceDate <= toDate) &&\n Object.entries(invoiceSearch).every(([key, matches]) => {\n const value = query.get(key)\n\n return value === null || matches(invoice, value)\n })\n )\n\n const page = paginate(matching, query)\n\n if (page instanceof Response) return page\n\n return jsonResponse(200, {\n Invoices: page.items.map(invoice => renderInvoiceRow(env, invoice)),\n MetaInformation: page.meta\n })\n}\n\nconst getInvoice: RouteHandler = (state, { params }, env) => {\n const invoice = findInvoice(state, params.DocumentNumber ?? '')\n\n return invoice === undefined\n ? invoiceNotFound()\n : jsonResponse(200, { Invoice: renderInvoice(env, invoice) })\n}\n\ntype RowPatch = {\n -readonly [K in keyof FortnoxEmulatorInvoiceRowSeed]?: FortnoxEmulatorInvoiceRowSeed[K]\n}\n\nconst vatRates: ReadonlyArray<number> = [...fortnoxEmulatorVatCodes.keys()]\n\n/**\n * One row of an `InvoiceRows` write. `null` means omitted. Referenced articles, cost centers, and\n * projects must exist (the emulated company has none); accounts must be in 1000-8999.\n */\nconst rowPatch = (value: Schema.Json, index: number): RowPatch | Response => {\n if (!isJsonObject(value)) {\n return invalidField(`InvoiceRows[${index}] must be an object.`)\n }\n\n const patch: RowPatch = {}\n const label = (key: string) => `InvoiceRows[${index}].${key}`\n\n for (const [key, field] of Object.entries(value)) {\n if (field === null) continue\n\n switch (key) {\n case 'RowId':\n if (!Predicate.isNumber(field) || !Number.isSafeInteger(field) || field < 1) {\n return invalidField(`${label(key)} must be a positive integer.`)\n }\n\n patch.RowId = field\n break\n case 'AccountNumber':\n if (\n !Predicate.isNumber(field) ||\n !Number.isInteger(field) ||\n field < 1000 ||\n field > 8999\n ) {\n return invalidField(`${label(key)} must be an account number from 1000 to 8999.`)\n }\n\n patch.AccountNumber = field\n break\n case 'ArticleNumber':\n case 'CostCenter':\n case 'Project':\n if (!Predicate.isString(field)) return invalidField(`${label(key)} must be a string.`)\n\n if (field !== '') return noReferencedEntity(key, field)\n\n patch[key] = field\n break\n case 'Description':\n case 'Unit':\n if (!Predicate.isString(field)) return invalidField(`${label(key)} must be a string.`)\n\n patch[key] = field\n break\n case 'DeliveredQuantity': {\n const quantity = Predicate.isString(field) ? Number(field.trim()) : field\n\n if (\n !Predicate.isNumber(quantity) ||\n !Number.isFinite(quantity) ||\n quantity < 0 ||\n (Predicate.isString(field) && field.trim() === '')\n ) {\n return invalidField(`${label(key)} must be a non-negative number.`)\n }\n\n patch.DeliveredQuantity = formatQuantity(quantity)\n break\n }\n\n case 'Price':\n if (!Predicate.isNumber(field) || !Number.isFinite(field)) {\n return invalidField(`${label(key)} must be a finite number.`)\n }\n\n patch.Price = field\n break\n case 'Discount':\n if (!Predicate.isNumber(field) || !Number.isFinite(field) || field < 0) {\n return invalidField(`${label(key)} must be a non-negative number.`)\n }\n\n patch.Discount = field\n break\n case 'DiscountType':\n if (field !== 'PERCENT' && field !== 'AMOUNT') {\n return invalidField(`${label(key)} must be PERCENT or AMOUNT.`)\n }\n\n patch.DiscountType = field\n break\n case 'VAT':\n if (!Predicate.isNumber(field) || !vatRates.includes(field)) {\n return invalidField(`${label(key)} must be one of ${vatRates.join(', ')}.`)\n }\n\n patch.VAT = field\n break\n default:\n return invalidField(`${label(key)} is not a writable invoice row field.`)\n }\n }\n\n return patch\n}\n\nconst rowPatches = (value: Schema.Json): ReadonlyArray<RowPatch> | Response => {\n if (!Array.isArray(value)) {\n return invalidField('InvoiceRows must be an array.')\n }\n\n const patches: Array<RowPatch> = []\n\n for (const [index, row] of value.entries()) {\n const patch = rowPatch(row, index)\n\n if (patch instanceof Response) return patch\n\n patches.push(patch)\n }\n\n return patches\n}\n\ntype RowIds = { next: number }\n\n/**\n * Quirk 1: `InvoiceRows` replaces the row list. Without any `RowId`, rows match existing rows by\n * position; with a `RowId`, that row is updated and rows without one are added. A matched row\n * keeps every omitted field, including `Discount`/`DiscountType` (unless the `stickyRowDiscount`\n * drill knob is off, which resets them); `Discount: 0` clears. RowIds are regenerated for every\n * row, and totals follow from the fields.\n */\nconst mergeRows = (\n existing: ReadonlyArray<FortnoxEmulatorInvoiceRow>,\n patches: ReadonlyArray<RowPatch>,\n ids: RowIds,\n env: FortnoxApiEnv\n): ReadonlyArray<FortnoxEmulatorInvoiceRow> | Response => {\n const byRowId = patches.some(patch => patch.RowId !== undefined)\n const rows: Array<FortnoxEmulatorInvoiceRow> = []\n\n for (const [index, patch] of patches.entries()) {\n const base = byRowId\n ? existing.find(row => patch.RowId !== undefined && row.RowId === patch.RowId)\n : existing[index]\n\n if (byRowId && patch.RowId !== undefined && base === undefined) {\n return invalidField(`InvoiceRows[${index}].RowId ${patch.RowId} does not exist.`)\n }\n\n const start = { ...newRowDefaults, ...base }\n\n // Drill knob only: forget the matched row's discount instead of keeping it.\n if (base !== undefined && !env.quirks.stickyRowDiscount) {\n start.Discount = 0\n start.DiscountType = 'PERCENT'\n }\n\n rows.push(buildRow({ ...start, ...patch, RowId: ids.next++ }))\n }\n\n return rows\n}\n\nconst invoiceTextFields = ['Comments', 'OurReference', 'YourReference', 'OCR'] as const\n\ntype InvoiceTextField = (typeof invoiceTextFields)[number]\n\nconst isInvoiceTextField = (key: string): key is InvoiceTextField =>\n invoiceTextFields.some(field => field === key)\n\nconst invoiceReadOnlyFields: ReadonlyArray<string> = [\n 'Balance',\n 'Booked',\n 'Cancelled',\n 'CustomerName',\n 'FinalPayDate',\n 'Gross',\n 'Net',\n 'Sent',\n 'Total',\n 'TotalToPay',\n 'TotalVAT',\n 'VoucherNumber',\n 'VoucherSeries',\n 'VoucherYear'\n]\n\ntype InvoiceScalars = {\n CustomerNumber?: string\n Currency?: string\n InvoiceDate?: string\n DueDate?: string\n CostCenter?: string\n Project?: string\n} & { -readonly [K in InvoiceTextField]?: string }\n\ntype InvoiceUpdates = {\n readonly scalars: InvoiceScalars\n readonly customer: FortnoxEmulatorCustomer | undefined\n readonly rows: ReadonlyArray<RowPatch> | undefined\n}\n\n/**\n * The writable invoice fields of a create or update body (the connector's invoice mutation\n * fields). Quirk 4: an unknown `CustomerNumber` is rejected with the rejection fixture's 400\n * `ErrorInformation`; other invalid or read-only fields are rejected with 400 too.\n */\nconst invoiceUpdates = (\n fields: Schema.JsonObject,\n state: FortnoxEmulatorState,\n existing: FortnoxEmulatorInvoice | undefined\n): InvoiceUpdates | Response => {\n const scalars: InvoiceScalars = {}\n let customer: FortnoxEmulatorCustomer | undefined\n let rows: ReadonlyArray<RowPatch> | undefined\n\n for (const [key, value] of Object.entries(fields)) {\n if (key === 'DocumentNumber') {\n if (existing === undefined || value !== existing.DocumentNumber) {\n return readOnlyField('DocumentNumber is assigned by Fortnox and cannot be changed.')\n }\n\n continue\n }\n\n if (key === 'InvoiceRows') {\n const patches = rowPatches(value)\n\n if (patches instanceof Response) return patches\n\n if (existing === undefined && patches.some(patch => patch.RowId !== undefined)) {\n return readOnlyField('RowId is assigned by Fortnox.')\n }\n\n rows = patches\n\n continue\n }\n\n if (invoiceReadOnlyFields.includes(key)) {\n return readOnlyField(`${key} is read-only.`)\n }\n\n if (!Predicate.isString(value)) {\n return invalidField(\n key === 'CustomerNumber' || key === 'Currency' || isInvoiceTextField(key)\n ? `${key} must be a string.`\n : `unknown or unsupported invoice field ${key}.`\n )\n }\n\n switch (key) {\n case 'CustomerNumber':\n customer = findCustomer(state, value)\n\n if (customer === undefined) return invoiceCustomerNotFound()\n\n scalars.CustomerNumber = value\n break\n case 'Currency':\n if (value !== 'SEK') return noReferencedEntity('Currency', value)\n\n scalars.Currency = value\n break\n case 'InvoiceDate':\n case 'DueDate':\n if (!isIsoDate(value)) return invalidField(`${key} must be a YYYY-MM-DD date.`)\n\n scalars[key] = value\n break\n case 'CostCenter':\n case 'Project':\n if (value !== '') return noReferencedEntity(key, value)\n\n scalars[key] = value\n break\n default:\n if (!isInvoiceTextField(key)) {\n return invalidField(`unknown or unsupported invoice field ${key}.`)\n }\n\n scalars[key] = value\n }\n }\n\n return { scalars, customer, rows }\n}\n\nconst replaceInvoice = (state: FortnoxEmulatorState, updated: FortnoxEmulatorInvoice): void => {\n state.invoices = state.invoices.map(invoice =>\n invoice.DocumentNumber === updated.DocumentNumber ? updated : invoice\n )\n}\n\nconst updateInvoice: RouteHandler = (state, { params, body }, env) => {\n const invoice = findInvoice(state, params.DocumentNumber ?? '')\n\n if (invoice === undefined) return invoiceNotFound()\n\n const fields = envelopeObject(body, 'Invoice')\n\n if (fields instanceof Response) return fields\n\n if (invoice.Booked) return notEditable('a booked invoice cannot be updated.')\n\n if (invoice.Cancelled) return notEditable('a cancelled invoice cannot be updated.')\n\n const updates = invoiceUpdates(fields, state, invoice)\n\n if (updates instanceof Response) return updates\n\n let updated: FortnoxEmulatorInvoice = {\n ...invoice,\n ...updates.scalars,\n CustomerName: updates.customer?.Name ?? invoice.CustomerName\n }\n\n if (updates.rows !== undefined) {\n const ids: RowIds = { next: state.counters.nextRowId }\n const rows = mergeRows(invoice.InvoiceRows, updates.rows, ids, env)\n\n if (rows instanceof Response) return rows\n\n const totals = invoiceTotals(rows)\n\n updated = { ...updated, InvoiceRows: rows, ...totals, Balance: totals.Total }\n state.counters = { ...state.counters, nextRowId: ids.next }\n }\n\n replaceInvoice(state, updated)\n\n return jsonResponse(200, { Invoice: renderInvoice(env, updated) })\n}\n\nconst createInvoice: RouteHandler = (state, { body }, env) => {\n const fields = envelopeObject(body, 'Invoice')\n\n if (fields instanceof Response) return fields\n\n const updates = invoiceUpdates(fields, state, undefined)\n\n if (updates instanceof Response) return updates\n\n const { customer } = updates\n\n if (customer === undefined) return invalidField('CustomerNumber is required.')\n\n // The invoice inherits the customer's currency when it names none; check the effective value\n // (a seeded customer can carry any currency) before anything is committed.\n const currency = updates.scalars.Currency ?? customer.Currency\n\n if (currency !== 'SEK') return noReferencedEntity('Currency', currency)\n\n // Likewise the inherited VAT type (invoice VAT comes from the rows only) and payment terms,\n // which the invoice records and derives its due date from.\n if (!emulatedCustomerVatTypes.includes(customer.VATType)) {\n return unsupportedVatType(customer.VATType)\n }\n\n const terms = termsOfPaymentDays(customer.TermsOfPayment)\n\n if (terms === undefined) return unsupportedTermsOfPayment(customer.TermsOfPayment)\n\n const invoiceDate = updates.scalars.InvoiceDate ?? today(env)\n const dueDate = updates.scalars.DueDate ?? addDays(invoiceDate, terms)\n\n if (dueDate === undefined) {\n return invalidField(\n `the due date ${invoiceDate} + ${terms} days is not a representable YYYY-MM-DD date.`\n )\n }\n\n const ids: RowIds = { next: state.counters.nextRowId }\n\n const seed: FortnoxEmulatorInvoiceSeed = {\n ...updates.scalars,\n DocumentNumber: String(state.counters.nextDocumentNumber),\n CustomerNumber: customer.CustomerNumber,\n InvoiceDate: invoiceDate,\n DueDate: dueDate,\n EmailInformation: defaultEmailInformation(customer, state.company),\n InvoiceRows: updates.rows ?? []\n }\n\n const invoice = invoiceFromSeed(seed, customer, state.company, ids)\n\n state.invoices = [...state.invoices, invoice].sort((left, right) =>\n compareNumbers(left.DocumentNumber, right.DocumentNumber)\n )\n state.counters = {\n nextRowId: ids.next,\n nextDocumentNumber: state.counters.nextDocumentNumber + 1\n }\n\n return jsonResponse(201, { Invoice: renderInvoice(env, invoice) })\n}\n\nconst previewInvoice: RouteHandler = (state, { params }) =>\n findInvoice(state, params.DocumentNumber ?? '') === undefined\n ? invoiceNotFound()\n : new Response(previewPdfBytes(), {\n status: 200,\n headers: { 'content-type': 'application/pdf' }\n })\n\n/** Marks the invoice `Sent` and records an outbox entry. Nothing is ever delivered. */\nconst emailInvoice: RouteHandler = (state, { params }, env) => {\n const invoice = findInvoice(state, params.DocumentNumber ?? '')\n\n if (invoice === undefined) return invoiceNotFound()\n\n if (invoice.Cancelled) return notEditable('a cancelled invoice cannot be sent.')\n\n const email = invoice.EmailInformation\n\n if (email.EmailAddressTo.trim() === '') {\n return invalidField('the invoice has no EmailInformation.EmailAddressTo.')\n }\n\n const updated: FortnoxEmulatorInvoice = { ...invoice, Sent: true }\n\n replaceInvoice(state, updated)\n state.outbox = [\n ...state.outbox,\n {\n DocumentNumber: invoice.DocumentNumber,\n EmailAddressTo: email.EmailAddressTo,\n EmailAddressCC: email.EmailAddressCC,\n EmailAddressBCC: email.EmailAddressBCC,\n EmailSubject: email.EmailSubject.replaceAll('{no}', invoice.DocumentNumber),\n sentAt: new Date(env.now()).toISOString()\n }\n ]\n\n return jsonResponse(200, { Invoice: renderInvoice(env, updated, true) })\n}\n\nconst companyInformation: RouteHandler = state =>\n jsonResponse(200, { CompanyInformation: state.company })\n\nconst connectorRoute = (\n method: string,\n path: string,\n write: boolean,\n caseIds: ReadonlyArray<string>,\n handler: RouteHandler,\n queryKeys: ReadonlyArray<string> = []\n): FortnoxApiRoute => ({\n method,\n path: `${fortnoxEmulatorBasePath}${path}`,\n kind: 'connector',\n write,\n caseIds,\n evidence: 'unverified',\n queryKeys,\n handler\n})\n\nconst listCase = 'fortnox.invoice.list-populated'\n\nconst previewCase = 'fortnox.invoice.preview-pdf'\n\nconst filtersCase = 'fortnox.invoice.payment-filters-exclude-unbooked'\n\nconst discountCase = 'fortnox.invoice.row-discount-sticky'\n\nconst emptyStringCase = 'fortnox.customer.empty-string-keeps-value'\n\nconst rejectionCase = 'fortnox.write.rejection-error-information'\n\nconst emailCase = 'fortnox.invoice.send-email'\n\n/** The route table: evidence plus handler. `fortnoxEmulatorRoutes` is its evidence part. */\nexport const fortnoxApiRoutes: ReadonlyArray<FortnoxApiRoute> = [\n connectorRoute('GET', '/companyinformation', false, [], companyInformation),\n connectorRoute('GET', '/customers', false, [], listCustomers, customerListQueryKeys),\n connectorRoute(\n 'GET',\n '/customers/{CustomerNumber}',\n false,\n [emptyStringCase, rejectionCase],\n getCustomer\n ),\n connectorRoute('PUT', '/customers/{CustomerNumber}', true, [emptyStringCase], updateCustomer),\n connectorRoute(\n 'GET',\n '/invoices',\n false,\n [listCase, filtersCase],\n listInvoices,\n invoiceListQueryKeys\n ),\n connectorRoute('POST', '/invoices', true, [rejectionCase], createInvoice),\n connectorRoute('GET', '/invoices/{DocumentNumber}', false, [discountCase, emailCase], getInvoice),\n connectorRoute('PUT', '/invoices/{DocumentNumber}', true, [discountCase], updateInvoice),\n connectorRoute('GET', '/invoices/{DocumentNumber}/preview', false, [previewCase], previewInvoice),\n connectorRoute('GET', '/invoices/{DocumentNumber}/email', true, [emailCase], emailInvoice)\n]\n\n/** One observed Fortnox quirk the emulator implements, and the case that claims it. */\nexport type FortnoxEmulatorQuirk = {\n readonly id: string\n /** `METHOD path` of the route that implements it. */\n readonly route: string\n /** The conformance case whose claim it follows; `undefined` when no case covers it yet. */\n readonly caseId: string | undefined\n /** The drill knob that flips it, if any. */\n readonly knob: keyof FortnoxEmulatorQuirks | undefined\n readonly summary: string\n}\n\n/** The observed quirks, each tied to its conformance case id. */\nexport const fortnoxEmulatorQuirks: ReadonlyArray<FortnoxEmulatorQuirk> = [\n {\n id: 'row-discount-sticky',\n route: 'PUT /3/invoices/{DocumentNumber}',\n caseId: discountCase,\n knob: 'stickyRowDiscount',\n summary:\n 'InvoiceRows replaces the rows; rows without RowId match by position; an omitted Discount/DiscountType keeps its value; Discount 0 clears; RowIds regenerate; totals are recomputed.'\n },\n {\n id: 'empty-string-keeps-value',\n route: 'PUT /3/customers/{CustomerNumber}',\n caseId: emptyStringCase,\n knob: 'emptyStringClears',\n summary: 'A customer update with \"\" for a string field keeps the stored value.'\n },\n {\n id: 'payment-filters-exclude-unbooked',\n route: 'GET /3/invoices',\n caseId: filtersCase,\n knob: 'paymentFiltersIncludeUnbooked',\n summary:\n 'unpaid, unpaidoverdue, and fullypaid consider booked invoices only; unbooked lists unbooked, uncancelled invoices; unpaidoverdue needs DueDate before today.'\n },\n {\n id: 'rejection-error-information',\n route: 'POST /3/invoices',\n caseId: rejectionCase,\n knob: undefined,\n summary:\n 'Writes for an unknown customer, or with invalid fields, answer 400 with a lowercase ErrorInformation { error, message, code }.'\n },\n {\n id: 'customer-country-read-only',\n route: 'PUT /3/customers/{CustomerNumber}',\n caseId: undefined,\n knob: undefined,\n summary:\n 'Customer Country is read-only (derived from CountryCode); sending it answers 400 ErrorInformation. No conformance case covers it yet.'\n }\n]\n\nconst templatePattern = (template: string): RegExp =>\n new RegExp(\n `^${template\n .split(/(\\{[A-Za-z]+\\})/)\n .map(part =>\n /^\\{[A-Za-z]+\\}$/.test(part) ? '([^/]+)' : part.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n )\n .join('')}$`\n )\n\nconst compiledRoutes = fortnoxApiRoutes.map(route => ({\n route,\n pattern: templatePattern(route.path)\n}))\n\n/** The route answering `method` + concrete `path`, or `undefined` (fail closed). */\nexport const matchFortnoxRoute = (method: string, path: string): FortnoxApiRoute | undefined =>\n compiledRoutes.find(\n candidate => candidate.route.method === method.toUpperCase() && candidate.pattern.test(path)\n )?.route\n\nconst decodeJsonText = Schema.decodeUnknownResult(Schema.fromJsonString(Schema.Json))\n\n/** Parsed JSON, or `undefined` for invalid JSON. */\nexport const parseJsonText = (text: string): Schema.Json | undefined => {\n const result = decodeJsonText(text)\n\n return Result.isSuccess(result) ? result.success : undefined\n}\n\n/** `{Name}` path templates become `:Name` core route parameters. */\nconst corePath = (template: string): string => template.replace(/\\{([A-Za-z]+)\\}/g, ':$1')\n\n/**\n * The wrapper's fault decision for the core request of job `jobId` (its `emulatorJobHeader`),\n * asked only once the route would answer successfully: `true` when a fault answers instead (it is\n * used up, and the wrapper sends its answer), `false` when none applies.\n */\nexport type FortnoxFaultDecision = (jobId: string | undefined) => boolean\n\n/**\n * Register every route of the table on the core app, over the generation's state. Each request is\n * planned, then faulted, then committed: the handler runs on a draft of the state (handlers replace\n * whole lists and counters, never edit them in place, so a shallow copy keeps every write off the\n * state); a refusal (any answer that is not 2xx) is sent as is and uses up no fault; a successful\n * answer asks `decideFault`, and only an unfaulted one commits the draft. The three steps run\n * synchronously together, so no other request interleaves. A handler that throws answers\n * `handlerFailedResponse()` (nothing written), which the wrapper turns into its `ErrorInformation`\n * 500 with `responseError` in the ledger.\n */\nexport const registerFortnoxApi = (\n app: Hono,\n state: FortnoxEmulatorState,\n env: FortnoxApiEnv,\n decideFault: FortnoxFaultDecision\n): void => {\n for (const route of fortnoxApiRoutes) {\n app.on(route.method, corePath(route.path), async context => {\n try {\n const query = new URL(context.req.url).searchParams\n\n // Fail closed on query parameters the route does not emulate, before the handler can write.\n const unsupported = unsupportedQueryKey(query, route.queryKeys)\n\n if (unsupported !== undefined) return unsupported\n\n const text = await context.req.text()\n const draft: FortnoxEmulatorState = { ...state }\n\n const response = route.handler(\n draft,\n {\n params: context.req.param(),\n query,\n body: text === '' ? undefined : parseJsonText(text)\n },\n env\n )\n\n if (!response.ok) return response\n\n if (decideFault(context.req.header(emulatorJobHeader))) return answeredOutsideCore()\n\n Object.assign(state, draft)\n\n return response\n } catch {\n return handlerFailedResponse()\n }\n })\n }\n}\n"],"mappings":"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"}