@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/dropbox/api.ts"],"sourcesContent":["/**\n * Dropbox emulator API: the route table (evidence, request-shape checks, and stateful handlers),\n * the fixture error envelopes, metadata rendering, and list and search cursors (internal;\n * re-exported by `src/dropbox.ts`).\n *\n * Only the RPC and upload routes the eight Dropbox conformance cases (and their cleanup) send are\n * emulated, with the wire shapes of their synthetic fixtures. Every route is a `POST` under `/2`: the RPC routes on `api.dropboxapi.com` with a JSON body, the\n * upload on `content.dropboxapi.com` with an `application/octet-stream` body and a\n * `Dropbox-API-Arg` header, each only on the origin its fixtures record. Route errors are the\n * fixtures' HTTP 409 `error_summary` envelopes, copied byte for byte. Every route plans first\n * (reading the state, writing nothing) and commits only an eligible request: anything no fixture\n * shows is not emulated (400, nothing written, no fault used up).\n *\n * @experimental\n */\nimport { Predicate } from 'effect'\nimport type * as Schema from 'effect/Schema'\nimport {\n exactObject,\n integerIn,\n isNotEmulated,\n jsonResponse,\n mediaType,\n notEmulated,\n parseJsonText,\n statefulRoute,\n type Commit,\n type EmulatedRequest,\n type NotEmulated,\n type StatefulRoute\n} from '../stateful-emulator.ts'\nimport {\n syntheticContentHash,\n type DropboxEmulatorDeleted,\n type DropboxEmulatorEntry,\n type DropboxEmulatorFile,\n type DropboxEmulatorState\n} from './state.ts'\n\n/** API version prefix of every Dropbox route. */\nexport const dropboxEmulatorBasePath = '/2'\n\n/**\n * The fixture error envelopes, byte for byte (`not-found-envelope.ts`, `create-folder-conflict.ts`,\n * and `upload-rev-precondition.ts`), each answered with HTTP 409.\n */\nexport const dropboxEmulatorErrorBodies = {\n notFound:\n '{\"error_summary\": \"path/not_found/.\", \"error\": {\".tag\": \"path\", \"path\": {\".tag\": \"not_found\"}}}',\n folderConflict:\n '{\"error_summary\": \"path/conflict/folder/..\", \"error\": {\".tag\": \"path\", \"path\": {\".tag\": \"conflict\", \"conflict\": {\".tag\": \"folder\"}}}}',\n uploadConflict:\n '{\"error_summary\": \"path/conflict/file/..\", \"error\": {\".tag\": \"path\", \"path\": {\"reason\": {\".tag\": \"conflict\", \"conflict\": {\".tag\": \"file\"}}}}}'\n} as const\n\n// Drill-only envelopes (tests only): each disagrees with one claim.\nconst drillErrorBodies = {\n notFoundAsPathLookup:\n '{\"error_summary\": \"path_lookup/not_found/.\", \"error\": {\".tag\": \"path_lookup\", \"path_lookup\": {\".tag\": \"not_found\"}}}',\n folderConflictAsFile:\n '{\"error_summary\": \"path/conflict/file/..\", \"error\": {\".tag\": \"path\", \"path\": {\".tag\": \"conflict\", \"conflict\": {\".tag\": \"file\"}}}}'\n} as const\n\n/** Drill knobs (tests only): each makes the emulator disagree with one conformance claim. */\nexport type DropboxEmulatorDrills = {\n /** `list_folder` ignores `limit`: one page with every entry and `has_more: false`. */\n readonly listFolderSinglePage?: boolean\n /** `get_metadata` matches path components case-sensitively. */\n readonly getMetadataCaseSensitive?: boolean\n /** `search/continue_v2` answers the earlier pages' matches again before the new ones. */\n readonly searchRepeatsMatches?: boolean\n /** A missing path answers a `path_lookup/not_found` envelope (still 409). */\n readonly notFoundAsPathLookup?: boolean\n /** A folder create conflict answers `path/conflict/file` instead of `path/conflict/folder`. */\n readonly folderConflictAsFile?: boolean\n /** `delete_v2` keeps no deleted-entry record, so `include_deleted` is not emulated. */\n readonly deleteLeavesNoTombstone?: boolean\n /** `move_v2` gives the moved file a new id. */\n readonly moveMintsNewId?: boolean\n /** An `update` upload naming a stale rev overwrites instead of conflicting. */\n readonly uploadIgnoresRev?: boolean\n}\n\nexport const dropboxEmulatorDrillKnobs: ReadonlyArray<keyof DropboxEmulatorDrills> = [\n 'listFolderSinglePage',\n 'getMetadataCaseSensitive',\n 'searchRepeatsMatches',\n 'notFoundAsPathLookup',\n 'folderConflictAsFile',\n 'deleteLeavesNoTombstone',\n 'moveMintsNewId',\n 'uploadIgnoresRev'\n]\n\n/** A `list_folder` cursor (runtime data, not state). */\nexport type DropboxListCursor = {\n readonly kind: 'list'\n readonly folderId: string\n /** The whole listing as rendered when the cursor was issued; any change is not emulated. */\n readonly fingerprint: string\n readonly offset: number\n readonly limit: number\n}\n\n/** A `search_v2` cursor (runtime data, not state). */\nexport type DropboxSearchCursor = {\n readonly kind: 'search'\n readonly query: string\n /** Every match as rendered when the cursor was issued; any change is not emulated. */\n readonly fingerprint: string\n readonly offset: number\n readonly maxResults: number\n}\n\nexport type DropboxCursor = DropboxListCursor | DropboxSearchCursor\n\nexport type DropboxApiEnv = {\n /** Clock in epoch milliseconds (upload timestamps only). */\n readonly now: () => number\n readonly drills: Readonly<Record<keyof DropboxEmulatorDrills, boolean>>\n /** Issued cursors by value (runtime-only; cleared by reset and seed). */\n readonly cursors: Map<string, DropboxCursor>\n /** Cursor numbers; never reset, so a cursor issued before a reset is never issued again. */\n readonly cursorCounters: { list: number; search: number }\n}\n\ntype Route = StatefulRoute<DropboxEmulatorState, DropboxApiEnv>\n\n/** Origin of the RPC routes. */\nexport const dropboxEmulatorApiOrigin = 'https://api.dropboxapi.com'\n\n/** Origin of the upload route. */\nexport const dropboxEmulatorContentOrigin = 'https://content.dropboxapi.com'\n\nconst pagingCase = 'dropbox.files.list-folder-cursor-paging'\n\nconst pathLowerCase = 'dropbox.files.path-lower-lookup'\n\nconst searchCase = 'dropbox.files.search-continue'\n\nconst notFoundCase = 'dropbox.errors.not-found-409-envelope'\n\nconst conflictCase = 'dropbox.files.create-folder-conflict'\n\nconst deleteCase = 'dropbox.files.delete-then-not-found'\n\nconst copyCase = 'dropbox.files.copy-move-metadata'\n\nconst uploadCase = 'dropbox.files.upload-rev-precondition'\n\nconst evidence = (\n path: string,\n write: boolean,\n caseIds: ReadonlyArray<string>,\n origin: string = dropboxEmulatorApiOrigin\n) => ({\n method: 'POST',\n path: `${dropboxEmulatorBasePath}${path}`,\n kind: 'connector' as const,\n write,\n caseIds,\n evidence: 'unverified' as const,\n origin\n})\n\n// Responses.\n\nconst json = (status: number, text: string): Response =>\n new Response(text, { status, headers: { 'content-type': 'application/json' } })\n\nconst ok = (body: Schema.Json): Response => jsonResponse(200, body)\n\nconst notFound = (env: DropboxApiEnv): Response =>\n json(\n 409,\n env.drills.notFoundAsPathLookup\n ? drillErrorBodies.notFoundAsPathLookup\n : dropboxEmulatorErrorBodies.notFound\n )\n\n// Paths.\n\n/** A parsed absolute path: its components as sent. */\ntype DropboxPath = { readonly components: ReadonlyArray<string> }\n\nconst pathPattern = /^(?:\\/[^/]+)+$/\n\nconst parsePath = (value: Schema.Json | undefined, label: string): DropboxPath | NotEmulated => {\n if (!Predicate.isString(value) || !pathPattern.test(value)) {\n return notEmulated(\n `${label} must be an absolute /path (the root, ids, and revs are not emulated)`\n )\n }\n\n const components = value.split('/').slice(1)\n\n return components.some(component => component === '.' || component === '..')\n ? notEmulated(`${label} with . or .. components is not emulated`)\n : { components }\n}\n\nconst idPattern = /^id:\\S+$/\n\nconst childrenOf = (state: DropboxEmulatorState, folderId: string | null) =>\n state.entries.filter(entry => entry.parentId === folderId)\n\nconst sameName = (left: string, right: string, caseSensitive: boolean): boolean =>\n caseSensitive ? left === right : left.toLowerCase() === right.toLowerCase()\n\n/** The entry at `components` (case-insensitive, like Dropbox, unless `caseSensitive`). */\nconst lookup = (\n state: DropboxEmulatorState,\n components: ReadonlyArray<string>,\n caseSensitive = false\n): DropboxEmulatorEntry | undefined => {\n let current: DropboxEmulatorEntry | undefined\n let parentId: string | null = null\n\n for (const component of components) {\n current = childrenOf(state, parentId).find(entry =>\n sameName(entry.name, component, caseSensitive)\n )\n\n if (current === undefined) return undefined\n\n parentId = current.id\n }\n\n return current\n}\n\n/**\n * The folder holding `components`: `null` for the root, the folder, or not emulated when it is\n * missing or a file (every fixture names a path whose parent folder exists).\n */\nconst parentFolder = (\n state: DropboxEmulatorState,\n components: ReadonlyArray<string>,\n label: string\n): DropboxEmulatorEntry | null | NotEmulated => {\n if (components.length === 1) return null\n\n const parent = lookup(state, components.slice(0, -1))\n\n return parent !== undefined && parent.file === null\n ? parent\n : notEmulated(`${label} under a missing parent folder (or a file) is not emulated`)\n}\n\n/** Stored names from the root down to the entry. */\nconst storedNames = (\n state: DropboxEmulatorState,\n entry: DropboxEmulatorEntry\n): ReadonlyArray<string> => {\n const names: Array<string> = []\n let current: DropboxEmulatorEntry | undefined = entry\n\n while (current !== undefined) {\n names.unshift(current.name)\n\n const parentId: string | null = current.parentId\n\n current =\n parentId === null ? undefined : state.entries.find(candidate => candidate.id === parentId)\n }\n\n return names\n}\n\nconst pathOf = (names: ReadonlyArray<string>): string => names.map(name => `/${name}`).join('')\n\n// Metadata, in the fixture key order.\n\nconst fileMetadata = (\n entry: DropboxEmulatorEntry,\n file: DropboxEmulatorFile,\n display: string\n): Schema.JsonObject => ({\n '.tag': 'file',\n name: entry.name,\n path_lower: display.toLowerCase(),\n path_display: display,\n id: entry.id,\n client_modified: file.clientModified,\n server_modified: file.serverModified,\n rev: file.rev,\n size: file.size,\n is_downloadable: true,\n content_hash: file.contentHash\n})\n\n/** Folder metadata; `create_folder_v2` answers it without `.tag`, as its fixtures record. */\nconst folderMetadata = (\n entry: DropboxEmulatorEntry,\n display: string,\n tagged: boolean\n): Schema.JsonObject => {\n const body = {\n name: entry.name,\n path_lower: display.toLowerCase(),\n path_display: display,\n id: entry.id\n }\n\n return tagged ? { '.tag': 'folder', ...body } : body\n}\n\nconst impliedRefusal = (): NotEmulated =>\n notEmulated(\n 'an answer showing a folder no fixture shows (an implied seed folder) is not emulated'\n )\n\n/** Metadata at the stored display path, or not emulated for an implied entry. */\nconst metadataOf = (\n state: DropboxEmulatorState,\n entry: DropboxEmulatorEntry\n): Schema.JsonObject | NotEmulated => {\n if (entry.implied) return impliedRefusal()\n\n const display = pathOf(storedNames(state, entry))\n\n return entry.file === null\n ? folderMetadata(entry, display, true)\n : fileMetadata(entry, entry.file, display)\n}\n\n/** Metadata of every entry, or not emulated when any is implied. */\nconst metadataOfAll = (\n state: DropboxEmulatorState,\n entries: ReadonlyArray<DropboxEmulatorEntry>\n): ReadonlyArray<Schema.JsonObject> | NotEmulated => {\n const rendered: Array<Schema.JsonObject> = []\n\n for (const entry of entries) {\n const metadata = metadataOf(state, entry)\n\n if (isNotEmulated(metadata)) return metadata\n\n rendered.push(metadata)\n }\n\n return rendered\n}\n\nconst deletedMetadata = (deleted: DropboxEmulatorDeleted): Schema.JsonObject => ({\n '.tag': 'deleted',\n name: deleted.name,\n path_lower: deleted.pathLower,\n path_display: deleted.pathDisplay\n})\n\n// Counters and timestamps (minted values).\n\nconst pad = (value: number, width: number): string => String(value).padStart(width, '0')\n\nconst nextId = (state: DropboxEmulatorState): string => {\n const number = state.counters.nextIdNumber\n\n state.counters = { ...state.counters, nextIdNumber: number + 1 }\n\n return `id:SyntheticEntry${pad(number, 8)}`\n}\n\nconst nextRev = (state: DropboxEmulatorState): string => {\n const number = state.counters.nextRevNumber\n\n state.counters = { ...state.counters, nextRevNumber: number + 1 }\n\n return `a1b2c3d4e5f6${pad(number, 4)}`\n}\n\n/** A Dropbox timestamp (`2026-09-29T14:00:00Z`) from the emulator clock. */\nconst nowTimestamp = (env: DropboxApiEnv): string =>\n new Date(Math.floor(env.now() / 1000) * 1000).toISOString().replace('.000Z', 'Z')\n\n/** Issue a cursor (in a commit): a value never issued before, registered until reset or seed. */\nconst issueCursor = (env: DropboxApiEnv, cursor: DropboxCursor): string => {\n const number = env.cursorCounters[cursor.kind]\n\n env.cursorCounters[cursor.kind] = number + 1\n\n const value = `AAHsynthetic${cursor.kind === 'list' ? 'List' : 'Search'}Cursor${pad(number, 4)}`\n\n env.cursors.set(value, cursor)\n\n return value\n}\n\n// Request-shape checks shared by the routes.\n\nconst withoutQuery = (request: EmulatedRequest): NotEmulated | undefined =>\n request.query.size > 0\n ? notEmulated('query parameters are not emulated on Dropbox routes')\n : undefined\n\nconst rpcBody = (\n request: EmulatedRequest,\n route: string,\n required: ReadonlyArray<string>,\n optional: ReadonlyArray<string> = []\n): Schema.JsonObject | NotEmulated =>\n withoutQuery(request) ?? exactObject(request.json, `the ${route} body`, required, optional)\n\nconst falseFlag = (value: Schema.Json | undefined, label: string): NotEmulated | undefined =>\n value === false ? undefined : notEmulated(`${label} other than false is not emulated`)\n\nconst cursorValue = (request: EmulatedRequest, route: string): string | NotEmulated => {\n const body = rpcBody(request, route, ['cursor'])\n\n if (isNotEmulated(body)) return body\n\n return Predicate.isString(body.cursor) && body.cursor.length > 0\n ? body.cursor\n : notEmulated('cursor must be a non-empty string')\n}\n\n/** A read-only commit. */\nconst answer =\n (response: () => Response): Commit =>\n () =>\n response()\n\n// Routes.\n\ntype GetMetadataInput = { readonly path: DropboxPath; readonly includeDeleted: boolean }\n\nconst getMetadata: Route = statefulRoute(\n evidence('/files/get_metadata', false, [\n pathLowerCase,\n notFoundCase,\n conflictCase,\n deleteCase,\n copyCase,\n uploadCase\n ]),\n 'json',\n (request): GetMetadataInput | NotEmulated => {\n const body = rpcBody(request, 'get_metadata', ['path'], ['include_deleted'])\n\n if (isNotEmulated(body)) return body\n\n if (body.include_deleted !== undefined && body.include_deleted !== true) {\n return notEmulated('include_deleted other than true is not emulated')\n }\n\n const path = parsePath(body.path, 'path')\n\n return isNotEmulated(path) ? path : { path, includeDeleted: body.include_deleted === true }\n },\n (state, input, { env }) => {\n const entry = lookup(state, input.path.components, env.drills.getMetadataCaseSensitive)\n\n if (entry === undefined) {\n const parent = parentFolder(state, input.path.components, 'get_metadata of a path')\n\n if (isNotEmulated(parent)) return parent\n\n if (!input.includeDeleted) return answer(() => notFound(env))\n\n const lower = pathOf(input.path.components).toLowerCase()\n const deleted = state.deleted.find(candidate => candidate.pathLower === lower)\n\n return deleted === undefined\n ? notEmulated('include_deleted for a path without a recorded delete is not emulated')\n : answer(() => ok(deletedMetadata(deleted)))\n }\n\n if (input.includeDeleted) {\n return notEmulated('include_deleted for an existing entry is not emulated')\n }\n\n if (entry.file === null) {\n return notEmulated('get_metadata of a folder is not emulated (no fixture records it)')\n }\n\n if (entry.implied) return impliedRefusal()\n\n // Only the last component keeps the stored casing; the rest echo the request.\n const display = pathOf([...input.path.components.slice(0, -1), entry.name])\n const file = entry.file\n\n return answer(() => ok(fileMetadata(entry, file, display)))\n }\n)\n\ntype ListInput = { readonly path: DropboxPath; readonly limit: number }\n\n/** The whole listing of a folder, rendered, or not emulated (an implied child). */\nconst listingOf = (\n state: DropboxEmulatorState,\n folderId: string\n): ReadonlyArray<Schema.JsonObject> | NotEmulated =>\n metadataOfAll(state, childrenOf(state, folderId))\n\n/** A commit answering one listing page and issuing its cursor. */\nconst listPage =\n (\n env: DropboxApiEnv,\n folderId: string,\n listing: ReadonlyArray<Schema.JsonObject>,\n offset: number,\n limit: number\n ): Commit =>\n () => {\n const page = listing.slice(offset, offset + limit)\n const end = offset + page.length\n\n const cursor = issueCursor(env, {\n kind: 'list',\n folderId,\n fingerprint: JSON.stringify(listing),\n offset: end,\n limit\n })\n\n return ok({ entries: [...page], cursor, has_more: end < listing.length })\n }\n\nconst listFolder: Route = statefulRoute(\n evidence('/files/list_folder', false, [pagingCase]),\n 'json',\n (request): ListInput | NotEmulated => {\n const body = rpcBody(request, 'list_folder', ['path', 'limit'])\n\n if (isNotEmulated(body)) return body\n\n const path = parsePath(body.path, 'path')\n\n if (isNotEmulated(path)) return path\n\n const limit = integerIn(body.limit, 'limit', 1, 2000)\n\n return isNotEmulated(limit) ? limit : { path, limit }\n },\n (state, input, { env }) => {\n const folder = lookup(state, input.path.components)\n\n if (folder === undefined || folder.file !== null) {\n return notEmulated('list_folder of a missing path or a file is not emulated')\n }\n\n const listing = listingOf(state, folder.id)\n\n if (isNotEmulated(listing)) return listing\n\n // Every recorded listing has entries; an empty one (for example the leftover lookup's empty\n // work folder) is no fixture's answer.\n if (listing.length === 0) {\n return notEmulated('a listing of an empty folder is not emulated (no fixture records one)')\n }\n\n const limit = env.drills.listFolderSinglePage ? Math.max(listing.length, 1) : input.limit\n\n return listPage(env, folder.id, listing, 0, limit)\n }\n)\n\nconst listFolderContinue: Route = statefulRoute(\n evidence('/files/list_folder/continue', false, [pagingCase]),\n 'json',\n request => cursorValue(request, 'list_folder/continue'),\n (state, value, { env }) => {\n const cursor = env.cursors.get(value)\n\n if (cursor === undefined || cursor.kind !== 'list') {\n return notEmulated(\n 'a list cursor this emulator did not issue since the last reset is not emulated'\n )\n }\n\n const folder = state.entries.find(entry => entry.id === cursor.folderId)\n const listing = folder === undefined ? undefined : listingOf(state, folder.id)\n\n if (\n folder === undefined ||\n listing === undefined ||\n isNotEmulated(listing) ||\n JSON.stringify(listing) !== cursor.fingerprint\n ) {\n return notEmulated(\n 'continuing a listing whose folder changed since its cursor is not emulated'\n )\n }\n\n if (cursor.offset >= listing.length) {\n return notEmulated('continuing a finished listing (changes since it) is not emulated')\n }\n\n return listPage(env, folder.id, listing, cursor.offset, cursor.limit)\n }\n)\n\ntype SearchInput = { readonly query: string; readonly maxResults: number }\n\n/** Every match of `query`, rendered as the fixture's match objects, or not emulated. */\nconst searchMatches = (\n state: DropboxEmulatorState,\n query: string\n): ReadonlyArray<Schema.JsonObject> | NotEmulated => {\n const lower = query.toLowerCase()\n const matches = state.entries.filter(entry => entry.name.toLowerCase().includes(lower))\n\n if (matches.length === 0) return notEmulated('a search without matches is not emulated')\n\n if (matches.some(entry => entry.file === null)) {\n return notEmulated('a search matching a folder is not emulated (the fixture matches files)')\n }\n\n const rendered = metadataOfAll(state, matches)\n\n return isNotEmulated(rendered)\n ? rendered\n : rendered.map(metadata => ({\n match_type: { '.tag': 'filename' },\n metadata: { '.tag': 'metadata', metadata }\n }))\n}\n\n/** A commit answering one search page (the fixtures' last page carries no cursor). */\nconst searchPage =\n (\n env: DropboxApiEnv,\n query: string,\n matches: ReadonlyArray<Schema.JsonObject>,\n start: number,\n offset: number,\n maxResults: number\n ): Commit =>\n () => {\n const end = Math.min(offset + maxResults, matches.length)\n const hasMore = end < matches.length\n const body = { matches: matches.slice(start, end), has_more: hasMore }\n\n if (!hasMore) return ok(body)\n\n const cursor = issueCursor(env, {\n kind: 'search',\n query,\n fingerprint: JSON.stringify(matches),\n offset: end,\n maxResults\n })\n\n return ok({ ...body, cursor })\n }\n\nconst search: Route = statefulRoute(\n evidence('/files/search_v2', false, [searchCase]),\n 'json',\n (request): SearchInput | NotEmulated => {\n const body = rpcBody(request, 'search_v2', ['query', 'options'])\n\n if (isNotEmulated(body)) return body\n\n if (!Predicate.isString(body.query) || body.query.trim().length === 0) {\n return notEmulated('query must be a non-empty string')\n }\n\n const options = exactObject(body.options, 'options', ['max_results', 'filename_only'])\n\n if (isNotEmulated(options)) return options\n\n if (options.filename_only !== true) {\n return notEmulated('options.filename_only other than true is not emulated')\n }\n\n const maxResults = integerIn(options.max_results, 'options.max_results', 1, 1000)\n\n return isNotEmulated(maxResults) ? maxResults : { query: body.query, maxResults }\n },\n (state, input, { env }) => {\n const matches = searchMatches(state, input.query)\n\n return isNotEmulated(matches)\n ? matches\n : searchPage(env, input.query, matches, 0, 0, input.maxResults)\n }\n)\n\nconst searchContinue: Route = statefulRoute(\n evidence('/files/search/continue_v2', false, [searchCase]),\n 'json',\n request => cursorValue(request, 'search/continue_v2'),\n (state, value, { env }) => {\n const cursor = env.cursors.get(value)\n\n if (cursor === undefined || cursor.kind !== 'search') {\n return notEmulated(\n 'a search cursor this emulator did not issue since the last reset is not emulated'\n )\n }\n\n const matches = searchMatches(state, cursor.query)\n\n if (isNotEmulated(matches) || JSON.stringify(matches) !== cursor.fingerprint) {\n return notEmulated(\n 'continuing a search whose matches changed since its cursor is not emulated'\n )\n }\n\n const start = env.drills.searchRepeatsMatches ? 0 : cursor.offset\n\n return searchPage(env, cursor.query, matches, start, cursor.offset, cursor.maxResults)\n }\n)\n\nconst createFolder: Route = statefulRoute(\n evidence('/files/create_folder_v2', true, [conflictCase, deleteCase, copyCase, uploadCase]),\n 'json',\n (request): DropboxPath | NotEmulated => {\n const body = rpcBody(request, 'create_folder_v2', ['path', 'autorename'])\n\n if (isNotEmulated(body)) return body\n\n return falseFlag(body.autorename, 'autorename') ?? parsePath(body.path, 'path')\n },\n (state, path, { env }) => {\n const parent = parentFolder(state, path.components, 'create_folder_v2')\n\n if (isNotEmulated(parent)) return parent\n\n const existing = lookup(state, path.components)\n\n if (existing !== undefined) {\n if (existing.file !== null) {\n return notEmulated('create_folder_v2 over a file is not emulated')\n }\n\n return answer(() =>\n json(\n 409,\n env.drills.folderConflictAsFile\n ? drillErrorBodies.folderConflictAsFile\n : dropboxEmulatorErrorBodies.folderConflict\n )\n )\n }\n\n return () => {\n const created: DropboxEmulatorEntry = {\n id: nextId(state),\n parentId: parent?.id ?? null,\n name: path.components.at(-1) ?? '',\n file: null,\n implied: false\n }\n\n state.entries = [...state.entries, created]\n\n return ok({ metadata: folderMetadata(created, pathOf(storedNames(state, created)), false) })\n }\n }\n)\n\n/** The entry and everything under it. */\nconst subtreeOf = (\n state: DropboxEmulatorState,\n root: DropboxEmulatorEntry\n): ReadonlyArray<DropboxEmulatorEntry> => [\n root,\n ...childrenOf(state, root.id).flatMap(child => subtreeOf(state, child))\n]\n\ntype Target = { readonly id: string } | { readonly path: DropboxPath }\n\nconst deleteEntry: Route = statefulRoute(\n evidence('/files/delete_v2', true, [conflictCase, deleteCase, copyCase, uploadCase]),\n 'json',\n (request): Target | NotEmulated => {\n const body = rpcBody(request, 'delete_v2', ['path'])\n\n if (isNotEmulated(body)) return body\n\n if (Predicate.isString(body.path) && idPattern.test(body.path)) return { id: body.path }\n\n const path = parsePath(body.path, 'path')\n\n return isNotEmulated(path) ? path : { path }\n },\n (state, target, { env }) => {\n const entry =\n 'id' in target\n ? state.entries.find(candidate => candidate.id === target.id)\n : lookup(state, target.path.components)\n\n if (entry === undefined) {\n return notEmulated('delete_v2 of a missing entry is not emulated (no fixture records it)')\n }\n\n if (entry.file !== null) {\n return notEmulated('delete_v2 of a file is not emulated (the fixtures delete folders)')\n }\n\n const removed = subtreeOf(state, entry)\n\n if (removed.some(candidate => candidate.implied)) return impliedRefusal()\n\n const metadata = metadataOf(state, entry)\n\n if (isNotEmulated(metadata)) return metadata\n\n return () => {\n const removedIds = new Set(removed.map(candidate => candidate.id))\n\n // Only an empty folder's delete has a recorded `include_deleted` answer.\n if (removed.length === 1 && !env.drills.deleteLeavesNoTombstone) {\n const display = pathOf(storedNames(state, entry))\n const lower = display.toLowerCase()\n\n state.deleted = [\n ...state.deleted.filter(record => record.pathLower !== lower),\n { name: entry.name, pathLower: lower, pathDisplay: display }\n ]\n }\n\n state.entries = state.entries.filter(candidate => !removedIds.has(candidate.id))\n\n return ok({ metadata })\n }\n }\n)\n\ntype RelocationInput = { readonly from: DropboxPath; readonly to: DropboxPath }\n\nconst relocationInput = (\n request: EmulatedRequest,\n route: string\n): RelocationInput | NotEmulated => {\n const body = rpcBody(request, route, ['from_path', 'to_path', 'autorename'])\n\n if (isNotEmulated(body)) return body\n\n const autorename = falseFlag(body.autorename, 'autorename')\n\n if (autorename !== undefined) return autorename\n\n const from = parsePath(body.from_path, 'from_path')\n\n if (isNotEmulated(from)) return from\n\n const to = parsePath(body.to_path, 'to_path')\n\n return isNotEmulated(to) ? to : { from, to }\n}\n\ntype Relocation = {\n readonly source: DropboxEmulatorEntry\n readonly file: DropboxEmulatorFile\n readonly parent: DropboxEmulatorEntry | null\n}\n\n/** The source file and destination folder of a copy or move, or not emulated. */\nconst relocation = (\n state: DropboxEmulatorState,\n input: RelocationInput,\n route: string\n): Relocation | NotEmulated => {\n const source = lookup(state, input.from.components)\n\n if (source === undefined || source.file === null || source.implied) {\n return notEmulated(`${route} of a missing entry or a folder is not emulated (files only)`)\n }\n\n const parent = parentFolder(state, input.to.components, route)\n\n if (isNotEmulated(parent)) return parent\n\n return lookup(state, input.to.components) === undefined\n ? { source, file: source.file, parent }\n : notEmulated(`${route} onto an existing entry is not emulated`)\n}\n\nconst copy: Route = statefulRoute(\n evidence('/files/copy_v2', true, [copyCase]),\n 'json',\n request => relocationInput(request, 'copy_v2'),\n (state, input) => {\n const found = relocation(state, input, 'copy_v2')\n\n if (isNotEmulated(found)) return found\n\n // The copy keeps the source's size, content hash, and timestamps, as the fixture records.\n return () => {\n const file = { ...found.file, rev: nextRev(state) }\n\n const created: DropboxEmulatorEntry = {\n id: nextId(state),\n parentId: found.parent?.id ?? null,\n name: input.to.components.at(-1) ?? '',\n file,\n implied: false\n }\n\n state.entries = [...state.entries, created]\n\n return ok({ metadata: fileMetadata(created, file, pathOf(storedNames(state, created))) })\n }\n }\n)\n\nconst move: Route = statefulRoute(\n evidence('/files/move_v2', true, [copyCase]),\n 'json',\n request => relocationInput(request, 'move_v2'),\n (state, input, { env }) => {\n const found = relocation(state, input, 'move_v2')\n\n if (isNotEmulated(found)) return found\n\n // The moved file keeps its id and timestamps and gets a new rev, as the fixture records.\n return () => {\n const file = { ...found.file, rev: nextRev(state) }\n\n const moved: DropboxEmulatorEntry = {\n id: env.drills.moveMintsNewId ? nextId(state) : found.source.id,\n parentId: found.parent?.id ?? null,\n name: input.to.components.at(-1) ?? '',\n file,\n implied: false\n }\n\n state.entries = state.entries.map(entry => (entry.id === found.source.id ? moved : entry))\n\n return ok({ metadata: fileMetadata(moved, file, pathOf(storedNames(state, moved))) })\n }\n }\n)\n\ntype UploadInput =\n | { readonly mode: 'add'; readonly path: DropboxPath; readonly bytes: Uint8Array }\n | {\n readonly mode: 'update'\n readonly id: string\n readonly rev: string\n readonly bytes: Uint8Array\n }\n\nconst revPattern = /^[0-9a-f]{9,}$/\n\n/** A file written by an upload (in a commit): a new rev, its size, hash, and clock timestamps. */\nconst uploadedFile = (\n state: DropboxEmulatorState,\n at: string,\n bytes: Uint8Array\n): DropboxEmulatorFile => {\n const rev = nextRev(state)\n\n return {\n rev,\n size: bytes.byteLength,\n contentHash: syntheticContentHash(rev.slice(-2)),\n clientModified: at,\n serverModified: at\n }\n}\n\nconst upload: Route = statefulRoute(\n evidence('/files/upload', true, [uploadCase], dropboxEmulatorContentOrigin),\n 'bytes',\n (request): UploadInput | NotEmulated => {\n const noQuery = withoutQuery(request)\n\n if (noQuery !== undefined) return noQuery\n\n if (mediaType(request.header('content-type')) !== 'application/octet-stream') {\n return notEmulated('the upload takes a content-type: application/octet-stream body')\n }\n\n const header = request.header('dropbox-api-arg')\n const arg = header === undefined ? undefined : parseJsonText(header)\n\n if (arg === undefined) return notEmulated('the upload needs a JSON Dropbox-API-Arg header')\n\n const fields = exactObject(arg, 'Dropbox-API-Arg', [\n 'path',\n 'mode',\n 'autorename',\n 'strict_conflict'\n ])\n\n if (isNotEmulated(fields)) return fields\n\n const autorename = falseFlag(fields.autorename, 'autorename')\n\n if (autorename !== undefined) return autorename\n\n if (fields.strict_conflict !== true) {\n return notEmulated('strict_conflict other than true is not emulated')\n }\n\n const bytes = request.bytes ?? new Uint8Array()\n\n if (fields.mode === 'add') {\n const path = parsePath(fields.path, 'path')\n\n return isNotEmulated(path) ? path : { mode: 'add', path, bytes }\n }\n\n const mode = exactObject(fields.mode, 'mode', ['.tag', 'update'])\n\n if (isNotEmulated(mode)) return mode\n\n if (\n mode['.tag'] !== 'update' ||\n !Predicate.isString(mode.update) ||\n !revPattern.test(mode.update)\n ) {\n return notEmulated(\n 'mode other than add or { \".tag\": \"update\", update: <rev> } is not emulated'\n )\n }\n\n return Predicate.isString(fields.path) && idPattern.test(fields.path)\n ? { mode: 'update', id: fields.path, rev: mode.update, bytes }\n : notEmulated('an update upload addresses the file by its id: form only')\n },\n (state, input, { env }) => {\n if (input.mode === 'add') {\n const parent = parentFolder(state, input.path.components, 'the upload')\n\n if (isNotEmulated(parent)) return parent\n\n const existing = lookup(state, input.path.components)\n\n if (existing !== undefined) {\n return existing.file === null\n ? notEmulated('an add upload onto a folder is not emulated')\n : answer(() => json(409, dropboxEmulatorErrorBodies.uploadConflict))\n }\n\n return () => {\n // Read the clock before any write: a failing clock writes nothing.\n const at = nowTimestamp(env)\n const file = uploadedFile(state, at, input.bytes)\n\n const created: DropboxEmulatorEntry = {\n id: nextId(state),\n parentId: parent?.id ?? null,\n name: input.path.components.at(-1) ?? '',\n file,\n implied: false\n }\n\n state.entries = [...state.entries, created]\n\n return ok(fileMetadata(created, file, pathOf(storedNames(state, created))))\n }\n }\n\n const existing = state.entries.find(entry => entry.id === input.id)\n\n if (existing === undefined || existing.file === null || existing.implied) {\n return notEmulated('an update upload of a missing file or a folder is not emulated')\n }\n\n if (existing.file.rev !== input.rev && !env.drills.uploadIgnoresRev) {\n return answer(() => json(409, dropboxEmulatorErrorBodies.uploadConflict))\n }\n\n return () => {\n const at = nowTimestamp(env)\n const file = uploadedFile(state, at, input.bytes)\n const updated: DropboxEmulatorEntry = { ...existing, file }\n\n state.entries = state.entries.map(entry => (entry.id === existing.id ? updated : entry))\n\n return ok(fileMetadata(updated, file, pathOf(storedNames(state, updated))))\n }\n }\n)\n\n/** The route table: evidence plus handlers. `dropboxEmulatorRoutes` is its evidence part. */\nexport const dropboxApiRoutes: ReadonlyArray<Route> = [\n listFolder,\n listFolderContinue,\n getMetadata,\n search,\n searchContinue,\n createFolder,\n deleteEntry,\n copy,\n move,\n upload\n]\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAwCA,MAAa,0BAA0B;;;;;AAMvC,MAAa,6BAA6B;CACxC,UACE;CACF,gBACE;CACF,gBACE;AACJ;AAGA,MAAM,mBAAmB;CACvB,sBACE;CACF,sBACE;AACJ;AAsBA,MAAa,4BAAwE;CACnF;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;AAqCA,MAAa,2BAA2B;;AAGxC,MAAa,+BAA+B;AAE5C,MAAM,aAAa;AAEnB,MAAM,gBAAgB;AAEtB,MAAM,aAAa;AAEnB,MAAM,eAAe;AAErB,MAAM,eAAe;AAErB,MAAM,aAAa;AAEnB,MAAM,WAAW;AAEjB,MAAM,aAAa;AAEnB,MAAM,YACJ,MACA,OACA,SACA,SAAiB,8BACb;CACJ,QAAQ;CACR,MAAM,KAA6B;CACnC,MAAM;CACN;CACA;CACA,UAAU;CACV;AACF;AAIA,MAAM,QAAQ,QAAgB,SAC5B,IAAI,SAAS,MAAM;CAAE;CAAQ,SAAS,EAAE,gBAAgB,mBAAmB;AAAE,CAAC;AAEhF,MAAM,MAAM,SAAgC,aAAa,KAAK,IAAI;AAElE,MAAM,YAAY,QAChB,KACE,KACA,IAAI,OAAO,uBACP,iBAAiB,uBACjB,2BAA2B,QACjC;AAOF,MAAM,cAAc;AAEpB,MAAM,aAAa,OAAgC,UAA6C;CAC9F,IAAI,CAAC,UAAU,SAAS,KAAK,KAAK,CAAC,YAAY,KAAK,KAAK,GACvD,OAAO,YACL,GAAG,MAAM,sEACX;CAGF,MAAM,aAAa,MAAM,MAAM,GAAG,EAAE,MAAM,CAAC;CAE3C,OAAO,WAAW,MAAK,cAAa,cAAc,OAAO,cAAc,IAAI,IACvE,YAAY,GAAG,MAAM,yCAAyC,IAC9D,EAAE,WAAW;AACnB;AAEA,MAAM,YAAY;AAElB,MAAM,cAAc,OAA6B,aAC/C,MAAM,QAAQ,QAAO,UAAS,MAAM,aAAa,QAAQ;AAE3D,MAAM,YAAY,MAAc,OAAe,kBAC7C,gBAAgB,SAAS,QAAQ,KAAK,YAAY,MAAM,MAAM,YAAY;;AAG5E,MAAM,UACJ,OACA,YACA,gBAAgB,UACqB;CACrC,IAAI;CACJ,IAAI,WAA0B;CAE9B,KAAK,MAAM,aAAa,YAAY;EAClC,UAAU,WAAW,OAAO,QAAQ,EAAE,MAAK,UACzC,SAAS,MAAM,MAAM,WAAW,aAAa,CAC/C;EAEA,IAAI,YAAY,KAAA,GAAW,OAAO,KAAA;EAElC,WAAW,QAAQ;CACrB;CAEA,OAAO;AACT;;;;;AAMA,MAAM,gBACJ,OACA,YACA,UAC8C;CAC9C,IAAI,WAAW,WAAW,GAAG,OAAO;CAEpC,MAAM,SAAS,OAAO,OAAO,WAAW,MAAM,GAAG,EAAE,CAAC;CAEpD,OAAO,WAAW,KAAA,KAAa,OAAO,SAAS,OAC3C,SACA,YAAY,GAAG,MAAM,2DAA2D;AACtF;;AAGA,MAAM,eACJ,OACA,UAC0B;CAC1B,MAAM,QAAuB,CAAC;CAC9B,IAAI,UAA4C;CAEhD,OAAO,YAAY,KAAA,GAAW;EAC5B,MAAM,QAAQ,QAAQ,IAAI;EAE1B,MAAM,WAA0B,QAAQ;EAExC,UACE,aAAa,OAAO,KAAA,IAAY,MAAM,QAAQ,MAAK,cAAa,UAAU,OAAO,QAAQ;CAC7F;CAEA,OAAO;AACT;AAEA,MAAM,UAAU,UAAyC,MAAM,KAAI,SAAQ,IAAI,MAAM,EAAE,KAAK,EAAE;AAI9F,MAAM,gBACJ,OACA,MACA,aACuB;CACvB,QAAQ;CACR,MAAM,MAAM;CACZ,YAAY,QAAQ,YAAY;CAChC,cAAc;CACd,IAAI,MAAM;CACV,iBAAiB,KAAK;CACtB,iBAAiB,KAAK;CACtB,KAAK,KAAK;CACV,MAAM,KAAK;CACX,iBAAiB;CACjB,cAAc,KAAK;AACrB;;AAGA,MAAM,kBACJ,OACA,SACA,WACsB;CACtB,MAAM,OAAO;EACX,MAAM,MAAM;EACZ,YAAY,QAAQ,YAAY;EAChC,cAAc;EACd,IAAI,MAAM;CACZ;CAEA,OAAO,SAAS;EAAE,QAAQ;EAAU,GAAG;CAAK,IAAI;AAClD;AAEA,MAAM,uBACJ,YACE,sFACF;;AAGF,MAAM,cACJ,OACA,UACoC;CACpC,IAAI,MAAM,SAAS,OAAO,eAAe;CAEzC,MAAM,UAAU,OAAO,YAAY,OAAO,KAAK,CAAC;CAEhD,OAAO,MAAM,SAAS,OAClB,eAAe,OAAO,SAAS,IAAI,IACnC,aAAa,OAAO,MAAM,MAAM,OAAO;AAC7C;;AAGA,MAAM,iBACJ,OACA,YACmD;CACnD,MAAM,WAAqC,CAAC;CAE5C,KAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,WAAW,WAAW,OAAO,KAAK;EAExC,IAAI,cAAc,QAAQ,GAAG,OAAO;EAEpC,SAAS,KAAK,QAAQ;CACxB;CAEA,OAAO;AACT;AAEA,MAAM,mBAAmB,aAAwD;CAC/E,QAAQ;CACR,MAAM,QAAQ;CACd,YAAY,QAAQ;CACpB,cAAc,QAAQ;AACxB;AAIA,MAAM,OAAO,OAAe,UAA0B,OAAO,KAAK,EAAE,SAAS,OAAO,GAAG;AAEvF,MAAM,UAAU,UAAwC;CACtD,MAAM,SAAS,MAAM,SAAS;CAE9B,MAAM,WAAW;EAAE,GAAG,MAAM;EAAU,cAAc,SAAS;CAAE;CAE/D,OAAO,oBAAoB,IAAI,QAAQ,CAAC;AAC1C;AAEA,MAAM,WAAW,UAAwC;CACvD,MAAM,SAAS,MAAM,SAAS;CAE9B,MAAM,WAAW;EAAE,GAAG,MAAM;EAAU,eAAe,SAAS;CAAE;CAEhE,OAAO,eAAe,IAAI,QAAQ,CAAC;AACrC;;AAGA,MAAM,gBAAgB,yBACpB,IAAI,KAAK,KAAK,MAAM,IAAI,IAAI,IAAI,GAAI,IAAI,GAAI,GAAE,YAAY,EAAE,QAAQ,SAAS,GAAG;;AAGlF,MAAM,eAAe,KAAoB,WAAkC;CACzE,MAAM,SAAS,IAAI,eAAe,OAAO;CAEzC,IAAI,eAAe,OAAO,QAAQ,SAAS;CAE3C,MAAM,QAAQ,eAAe,OAAO,SAAS,SAAS,SAAS,SAAS,QAAQ,IAAI,QAAQ,CAAC;CAE7F,IAAI,QAAQ,IAAI,OAAO,MAAM;CAE7B,OAAO;AACT;AAIA,MAAM,gBAAgB,YACpB,QAAQ,MAAM,OAAO,IACjB,YAAY,qDAAqD,IACjE,KAAA;AAEN,MAAM,WACJ,SACA,OACA,UACA,WAAkC,CAAC,MAEnC,aAAa,OAAO,KAAK,YAAY,QAAQ,MAAM,OAAO,MAAM,QAAQ,UAAU,QAAQ;AAE5F,MAAM,aAAa,OAAgC,UACjD,UAAU,QAAQ,KAAA,IAAY,YAAY,GAAG,MAAM,kCAAkC;AAEvF,MAAM,eAAe,SAA0B,UAAwC;CACrF,MAAM,OAAO,QAAQ,SAAS,OAAO,CAAC,QAAQ,CAAC;CAE/C,IAAI,cAAc,IAAI,GAAG,OAAO;CAEhC,OAAO,UAAU,SAAS,KAAK,MAAM,KAAK,KAAK,OAAO,SAAS,IAC3D,KAAK,SACL,YAAY,mCAAmC;AACrD;;AAGA,MAAM,UACH,mBAEC,SAAS;AAMb,MAAM,cAAqB,cACzB,SAAS,uBAAuB,OAAO;CACrC;CACA;CACA;CACA;CACA;CACA;AACF,CAAC,GACD,SACC,YAA4C;CAC3C,MAAM,OAAO,QAAQ,SAAS,gBAAgB,CAAC,MAAM,GAAG,CAAC,iBAAiB,CAAC;CAE3E,IAAI,cAAc,IAAI,GAAG,OAAO;CAEhC,IAAI,KAAK,oBAAoB,KAAA,KAAa,KAAK,oBAAoB,MACjE,OAAO,YAAY,iDAAiD;CAGtE,MAAM,OAAO,UAAU,KAAK,MAAM,MAAM;CAExC,OAAO,cAAc,IAAI,IAAI,OAAO;EAAE;EAAM,gBAAgB,KAAK,oBAAoB;CAAK;AAC5F,IACC,OAAO,OAAO,EAAE,UAAU;CACzB,MAAM,QAAQ,OAAO,OAAO,MAAM,KAAK,YAAY,IAAI,OAAO,wBAAwB;CAEtF,IAAI,UAAU,KAAA,GAAW;EACvB,MAAM,SAAS,aAAa,OAAO,MAAM,KAAK,YAAY,wBAAwB;EAElF,IAAI,cAAc,MAAM,GAAG,OAAO;EAElC,IAAI,CAAC,MAAM,gBAAgB,OAAO,aAAa,SAAS,GAAG,CAAC;EAE5D,MAAM,QAAQ,OAAO,MAAM,KAAK,UAAU,EAAE,YAAY;EACxD,MAAM,UAAU,MAAM,QAAQ,MAAK,cAAa,UAAU,cAAc,KAAK;EAE7E,OAAO,YAAY,KAAA,IACf,YAAY,sEAAsE,IAClF,aAAa,GAAG,gBAAgB,OAAO,CAAC,CAAC;CAC/C;CAEA,IAAI,MAAM,gBACR,OAAO,YAAY,uDAAuD;CAG5E,IAAI,MAAM,SAAS,MACjB,OAAO,YAAY,kEAAkE;CAGvF,IAAI,MAAM,SAAS,OAAO,eAAe;CAGzC,MAAM,UAAU,OAAO,CAAC,GAAG,MAAM,KAAK,WAAW,MAAM,GAAG,EAAE,GAAG,MAAM,IAAI,CAAC;CAC1E,MAAM,OAAO,MAAM;CAEnB,OAAO,aAAa,GAAG,aAAa,OAAO,MAAM,OAAO,CAAC,CAAC;AAC5D,CACF;;AAKA,MAAM,aACJ,OACA,aAEA,cAAc,OAAO,WAAW,OAAO,QAAQ,CAAC;;AAGlD,MAAM,YAEF,KACA,UACA,SACA,QACA,gBAEI;CACJ,MAAM,OAAO,QAAQ,MAAM,QAAQ,SAAS,KAAK;CACjD,MAAM,MAAM,SAAS,KAAK;CAE1B,MAAM,SAAS,YAAY,KAAK;EAC9B,MAAM;EACN;EACA,aAAa,KAAK,UAAU,OAAO;EACnC,QAAQ;EACR;CACF,CAAC;CAED,OAAO,GAAG;EAAE,SAAS,CAAC,GAAG,IAAI;EAAG;EAAQ,UAAU,MAAM,QAAQ;CAAO,CAAC;AAC1E;AAEF,MAAM,aAAoB,cACxB,SAAS,sBAAsB,OAAO,CAAC,UAAU,CAAC,GAClD,SACC,YAAqC;CACpC,MAAM,OAAO,QAAQ,SAAS,eAAe,CAAC,QAAQ,OAAO,CAAC;CAE9D,IAAI,cAAc,IAAI,GAAG,OAAO;CAEhC,MAAM,OAAO,UAAU,KAAK,MAAM,MAAM;CAExC,IAAI,cAAc,IAAI,GAAG,OAAO;CAEhC,MAAM,QAAQ,UAAU,KAAK,OAAO,SAAS,GAAG,GAAI;CAEpD,OAAO,cAAc,KAAK,IAAI,QAAQ;EAAE;EAAM;CAAM;AACtD,IACC,OAAO,OAAO,EAAE,UAAU;CACzB,MAAM,SAAS,OAAO,OAAO,MAAM,KAAK,UAAU;CAElD,IAAI,WAAW,KAAA,KAAa,OAAO,SAAS,MAC1C,OAAO,YAAY,yDAAyD;CAG9E,MAAM,UAAU,UAAU,OAAO,OAAO,EAAE;CAE1C,IAAI,cAAc,OAAO,GAAG,OAAO;CAInC,IAAI,QAAQ,WAAW,GACrB,OAAO,YAAY,uEAAuE;CAG5F,MAAM,QAAQ,IAAI,OAAO,uBAAuB,KAAK,IAAI,QAAQ,QAAQ,CAAC,IAAI,MAAM;CAEpF,OAAO,SAAS,KAAK,OAAO,IAAI,SAAS,GAAG,KAAK;AACnD,CACF;AAEA,MAAM,qBAA4B,cAChC,SAAS,+BAA+B,OAAO,CAAC,UAAU,CAAC,GAC3D,SACA,YAAW,YAAY,SAAS,sBAAsB,IACrD,OAAO,OAAO,EAAE,UAAU;CACzB,MAAM,SAAS,IAAI,QAAQ,IAAI,KAAK;CAEpC,IAAI,WAAW,KAAA,KAAa,OAAO,SAAS,QAC1C,OAAO,YACL,gFACF;CAGF,MAAM,SAAS,MAAM,QAAQ,MAAK,UAAS,MAAM,OAAO,OAAO,QAAQ;CACvE,MAAM,UAAU,WAAW,KAAA,IAAY,KAAA,IAAY,UAAU,OAAO,OAAO,EAAE;CAE7E,IACE,WAAW,KAAA,KACX,YAAY,KAAA,KACZ,cAAc,OAAO,KACrB,KAAK,UAAU,OAAO,MAAM,OAAO,aAEnC,OAAO,YACL,4EACF;CAGF,IAAI,OAAO,UAAU,QAAQ,QAC3B,OAAO,YAAY,kEAAkE;CAGvF,OAAO,SAAS,KAAK,OAAO,IAAI,SAAS,OAAO,QAAQ,OAAO,KAAK;AACtE,CACF;;AAKA,MAAM,iBACJ,OACA,UACmD;CACnD,MAAM,QAAQ,MAAM,YAAY;CAChC,MAAM,UAAU,MAAM,QAAQ,QAAO,UAAS,MAAM,KAAK,YAAY,EAAE,SAAS,KAAK,CAAC;CAEtF,IAAI,QAAQ,WAAW,GAAG,OAAO,YAAY,0CAA0C;CAEvF,IAAI,QAAQ,MAAK,UAAS,MAAM,SAAS,IAAI,GAC3C,OAAO,YAAY,wEAAwE;CAG7F,MAAM,WAAW,cAAc,OAAO,OAAO;CAE7C,OAAO,cAAc,QAAQ,IACzB,WACA,SAAS,KAAI,cAAa;EACxB,YAAY,EAAE,QAAQ,WAAW;EACjC,UAAU;GAAE,QAAQ;GAAY;EAAS;CAC3C,EAAE;AACR;;AAGA,MAAM,cAEF,KACA,OACA,SACA,OACA,QACA,qBAEI;CACJ,MAAM,MAAM,KAAK,IAAI,SAAS,YAAY,QAAQ,MAAM;CACxD,MAAM,UAAU,MAAM,QAAQ;CAC9B,MAAM,OAAO;EAAE,SAAS,QAAQ,MAAM,OAAO,GAAG;EAAG,UAAU;CAAQ;CAErE,IAAI,CAAC,SAAS,OAAO,GAAG,IAAI;CAE5B,MAAM,SAAS,YAAY,KAAK;EAC9B,MAAM;EACN;EACA,aAAa,KAAK,UAAU,OAAO;EACnC,QAAQ;EACR;CACF,CAAC;CAED,OAAO,GAAG;EAAE,GAAG;EAAM;CAAO,CAAC;AAC/B;AAEF,MAAM,SAAgB,cACpB,SAAS,oBAAoB,OAAO,CAAC,UAAU,CAAC,GAChD,SACC,YAAuC;CACtC,MAAM,OAAO,QAAQ,SAAS,aAAa,CAAC,SAAS,SAAS,CAAC;CAE/D,IAAI,cAAc,IAAI,GAAG,OAAO;CAEhC,IAAI,CAAC,UAAU,SAAS,KAAK,KAAK,KAAK,KAAK,MAAM,KAAK,EAAE,WAAW,GAClE,OAAO,YAAY,kCAAkC;CAGvD,MAAM,UAAU,YAAY,KAAK,SAAS,WAAW,CAAC,eAAe,eAAe,CAAC;CAErF,IAAI,cAAc,OAAO,GAAG,OAAO;CAEnC,IAAI,QAAQ,kBAAkB,MAC5B,OAAO,YAAY,uDAAuD;CAG5E,MAAM,aAAa,UAAU,QAAQ,aAAa,uBAAuB,GAAG,GAAI;CAEhF,OAAO,cAAc,UAAU,IAAI,aAAa;EAAE,OAAO,KAAK;EAAO;CAAW;AAClF,IACC,OAAO,OAAO,EAAE,UAAU;CACzB,MAAM,UAAU,cAAc,OAAO,MAAM,KAAK;CAEhD,OAAO,cAAc,OAAO,IACxB,UACA,WAAW,KAAK,MAAM,OAAO,SAAS,GAAG,GAAG,MAAM,UAAU;AAClE,CACF;AAEA,MAAM,iBAAwB,cAC5B,SAAS,6BAA6B,OAAO,CAAC,UAAU,CAAC,GACzD,SACA,YAAW,YAAY,SAAS,oBAAoB,IACnD,OAAO,OAAO,EAAE,UAAU;CACzB,MAAM,SAAS,IAAI,QAAQ,IAAI,KAAK;CAEpC,IAAI,WAAW,KAAA,KAAa,OAAO,SAAS,UAC1C,OAAO,YACL,kFACF;CAGF,MAAM,UAAU,cAAc,OAAO,OAAO,KAAK;CAEjD,IAAI,cAAc,OAAO,KAAK,KAAK,UAAU,OAAO,MAAM,OAAO,aAC/D,OAAO,YACL,4EACF;CAGF,MAAM,QAAQ,IAAI,OAAO,uBAAuB,IAAI,OAAO;CAE3D,OAAO,WAAW,KAAK,OAAO,OAAO,SAAS,OAAO,OAAO,QAAQ,OAAO,UAAU;AACvF,CACF;AAEA,MAAM,eAAsB,cAC1B,SAAS,2BAA2B,MAAM;CAAC;CAAc;CAAY;CAAU;AAAU,CAAC,GAC1F,SACC,YAAuC;CACtC,MAAM,OAAO,QAAQ,SAAS,oBAAoB,CAAC,QAAQ,YAAY,CAAC;CAExE,IAAI,cAAc,IAAI,GAAG,OAAO;CAEhC,OAAO,UAAU,KAAK,YAAY,YAAY,KAAK,UAAU,KAAK,MAAM,MAAM;AAChF,IACC,OAAO,MAAM,EAAE,UAAU;CACxB,MAAM,SAAS,aAAa,OAAO,KAAK,YAAY,kBAAkB;CAEtE,IAAI,cAAc,MAAM,GAAG,OAAO;CAElC,MAAM,WAAW,OAAO,OAAO,KAAK,UAAU;CAE9C,IAAI,aAAa,KAAA,GAAW;EAC1B,IAAI,SAAS,SAAS,MACpB,OAAO,YAAY,8CAA8C;EAGnE,OAAO,aACL,KACE,KACA,IAAI,OAAO,uBACP,iBAAiB,uBACjB,2BAA2B,cACjC,CACF;CACF;CAEA,aAAa;EACX,MAAM,UAAgC;GACpC,IAAI,OAAO,KAAK;GAChB,UAAU,QAAQ,MAAM;GACxB,MAAM,KAAK,WAAW,GAAG,EAAE,KAAK;GAChC,MAAM;GACN,SAAS;EACX;EAEA,MAAM,UAAU,CAAC,GAAG,MAAM,SAAS,OAAO;EAE1C,OAAO,GAAG,EAAE,UAAU,eAAe,SAAS,OAAO,YAAY,OAAO,OAAO,CAAC,GAAG,KAAK,EAAE,CAAC;CAC7F;AACF,CACF;;AAGA,MAAM,aACJ,OACA,SACwC,CACxC,MACA,GAAG,WAAW,OAAO,KAAK,EAAE,EAAE,SAAQ,UAAS,UAAU,OAAO,KAAK,CAAC,CACxE;AAIA,MAAM,cAAqB,cACzB,SAAS,oBAAoB,MAAM;CAAC;CAAc;CAAY;CAAU;AAAU,CAAC,GACnF,SACC,YAAkC;CACjC,MAAM,OAAO,QAAQ,SAAS,aAAa,CAAC,MAAM,CAAC;CAEnD,IAAI,cAAc,IAAI,GAAG,OAAO;CAEhC,IAAI,UAAU,SAAS,KAAK,IAAI,KAAK,UAAU,KAAK,KAAK,IAAI,GAAG,OAAO,EAAE,IAAI,KAAK,KAAK;CAEvF,MAAM,OAAO,UAAU,KAAK,MAAM,MAAM;CAExC,OAAO,cAAc,IAAI,IAAI,OAAO,EAAE,KAAK;AAC7C,IACC,OAAO,QAAQ,EAAE,UAAU;CAC1B,MAAM,QACJ,QAAQ,SACJ,MAAM,QAAQ,MAAK,cAAa,UAAU,OAAO,OAAO,EAAE,IAC1D,OAAO,OAAO,OAAO,KAAK,UAAU;CAE1C,IAAI,UAAU,KAAA,GACZ,OAAO,YAAY,sEAAsE;CAG3F,IAAI,MAAM,SAAS,MACjB,OAAO,YAAY,mEAAmE;CAGxF,MAAM,UAAU,UAAU,OAAO,KAAK;CAEtC,IAAI,QAAQ,MAAK,cAAa,UAAU,OAAO,GAAG,OAAO,eAAe;CAExE,MAAM,WAAW,WAAW,OAAO,KAAK;CAExC,IAAI,cAAc,QAAQ,GAAG,OAAO;CAEpC,aAAa;EACX,MAAM,aAAa,IAAI,IAAI,QAAQ,KAAI,cAAa,UAAU,EAAE,CAAC;EAGjE,IAAI,QAAQ,WAAW,KAAK,CAAC,IAAI,OAAO,yBAAyB;GAC/D,MAAM,UAAU,OAAO,YAAY,OAAO,KAAK,CAAC;GAChD,MAAM,QAAQ,QAAQ,YAAY;GAElC,MAAM,UAAU,CACd,GAAG,MAAM,QAAQ,QAAO,WAAU,OAAO,cAAc,KAAK,GAC5D;IAAE,MAAM,MAAM;IAAM,WAAW;IAAO,aAAa;GAAQ,CAC7D;EACF;EAEA,MAAM,UAAU,MAAM,QAAQ,QAAO,cAAa,CAAC,WAAW,IAAI,UAAU,EAAE,CAAC;EAE/E,OAAO,GAAG,EAAE,SAAS,CAAC;CACxB;AACF,CACF;AAIA,MAAM,mBACJ,SACA,UACkC;CAClC,MAAM,OAAO,QAAQ,SAAS,OAAO;EAAC;EAAa;EAAW;CAAY,CAAC;CAE3E,IAAI,cAAc,IAAI,GAAG,OAAO;CAEhC,MAAM,aAAa,UAAU,KAAK,YAAY,YAAY;CAE1D,IAAI,eAAe,KAAA,GAAW,OAAO;CAErC,MAAM,OAAO,UAAU,KAAK,WAAW,WAAW;CAElD,IAAI,cAAc,IAAI,GAAG,OAAO;CAEhC,MAAM,KAAK,UAAU,KAAK,SAAS,SAAS;CAE5C,OAAO,cAAc,EAAE,IAAI,KAAK;EAAE;EAAM;CAAG;AAC7C;;AASA,MAAM,cACJ,OACA,OACA,UAC6B;CAC7B,MAAM,SAAS,OAAO,OAAO,MAAM,KAAK,UAAU;CAElD,IAAI,WAAW,KAAA,KAAa,OAAO,SAAS,QAAQ,OAAO,SACzD,OAAO,YAAY,GAAG,MAAM,6DAA6D;CAG3F,MAAM,SAAS,aAAa,OAAO,MAAM,GAAG,YAAY,KAAK;CAE7D,IAAI,cAAc,MAAM,GAAG,OAAO;CAElC,OAAO,OAAO,OAAO,MAAM,GAAG,UAAU,MAAM,KAAA,IAC1C;EAAE;EAAQ,MAAM,OAAO;EAAM;CAAO,IACpC,YAAY,GAAG,MAAM,wCAAwC;AACnE;AAEA,MAAM,OAAc,cAClB,SAAS,kBAAkB,MAAM,CAAC,QAAQ,CAAC,GAC3C,SACA,YAAW,gBAAgB,SAAS,SAAS,IAC5C,OAAO,UAAU;CAChB,MAAM,QAAQ,WAAW,OAAO,OAAO,SAAS;CAEhD,IAAI,cAAc,KAAK,GAAG,OAAO;CAGjC,aAAa;EACX,MAAM,OAAO;GAAE,GAAG,MAAM;GAAM,KAAK,QAAQ,KAAK;EAAE;EAElD,MAAM,UAAgC;GACpC,IAAI,OAAO,KAAK;GAChB,UAAU,MAAM,QAAQ,MAAM;GAC9B,MAAM,MAAM,GAAG,WAAW,GAAG,EAAE,KAAK;GACpC;GACA,SAAS;EACX;EAEA,MAAM,UAAU,CAAC,GAAG,MAAM,SAAS,OAAO;EAE1C,OAAO,GAAG,EAAE,UAAU,aAAa,SAAS,MAAM,OAAO,YAAY,OAAO,OAAO,CAAC,CAAC,EAAE,CAAC;CAC1F;AACF,CACF;AAEA,MAAM,OAAc,cAClB,SAAS,kBAAkB,MAAM,CAAC,QAAQ,CAAC,GAC3C,SACA,YAAW,gBAAgB,SAAS,SAAS,IAC5C,OAAO,OAAO,EAAE,UAAU;CACzB,MAAM,QAAQ,WAAW,OAAO,OAAO,SAAS;CAEhD,IAAI,cAAc,KAAK,GAAG,OAAO;CAGjC,aAAa;EACX,MAAM,OAAO;GAAE,GAAG,MAAM;GAAM,KAAK,QAAQ,KAAK;EAAE;EAElD,MAAM,QAA8B;GAClC,IAAI,IAAI,OAAO,iBAAiB,OAAO,KAAK,IAAI,MAAM,OAAO;GAC7D,UAAU,MAAM,QAAQ,MAAM;GAC9B,MAAM,MAAM,GAAG,WAAW,GAAG,EAAE,KAAK;GACpC;GACA,SAAS;EACX;EAEA,MAAM,UAAU,MAAM,QAAQ,KAAI,UAAU,MAAM,OAAO,MAAM,OAAO,KAAK,QAAQ,KAAM;EAEzF,OAAO,GAAG,EAAE,UAAU,aAAa,OAAO,MAAM,OAAO,YAAY,OAAO,KAAK,CAAC,CAAC,EAAE,CAAC;CACtF;AACF,CACF;AAWA,MAAM,aAAa;;AAGnB,MAAM,gBACJ,OACA,IACA,UACwB;CACxB,MAAM,MAAM,QAAQ,KAAK;CAEzB,OAAO;EACL;EACA,MAAM,MAAM;EACZ,aAAa,qBAAqB,IAAI,MAAM,EAAE,CAAC;EAC/C,gBAAgB;EAChB,gBAAgB;CAClB;AACF;;AAsHA,MAAa,mBAAyC;CACpD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CA7HoB,cACpB,SAAS,iBAAiB,MAAM,CAAC,UAAU,GAAG,4BAA4B,GAC1E,UACC,YAAuC;EACtC,MAAM,UAAU,aAAa,OAAO;EAEpC,IAAI,YAAY,KAAA,GAAW,OAAO;EAElC,IAAI,UAAU,QAAQ,OAAO,cAAc,CAAC,MAAM,4BAChD,OAAO,YAAY,gEAAgE;EAGrF,MAAM,SAAS,QAAQ,OAAO,iBAAiB;EAC/C,MAAM,MAAM,WAAW,KAAA,IAAY,KAAA,IAAY,cAAc,MAAM;EAEnE,IAAI,QAAQ,KAAA,GAAW,OAAO,YAAY,gDAAgD;EAE1F,MAAM,SAAS,YAAY,KAAK,mBAAmB;GACjD;GACA;GACA;GACA;EACF,CAAC;EAED,IAAI,cAAc,MAAM,GAAG,OAAO;EAElC,MAAM,aAAa,UAAU,OAAO,YAAY,YAAY;EAE5D,IAAI,eAAe,KAAA,GAAW,OAAO;EAErC,IAAI,OAAO,oBAAoB,MAC7B,OAAO,YAAY,iDAAiD;EAGtE,MAAM,QAAQ,QAAQ,SAAS,IAAI,WAAW;EAE9C,IAAI,OAAO,SAAS,OAAO;GACzB,MAAM,OAAO,UAAU,OAAO,MAAM,MAAM;GAE1C,OAAO,cAAc,IAAI,IAAI,OAAO;IAAE,MAAM;IAAO;IAAM;GAAM;EACjE;EAEA,MAAM,OAAO,YAAY,OAAO,MAAM,QAAQ,CAAC,QAAQ,QAAQ,CAAC;EAEhE,IAAI,cAAc,IAAI,GAAG,OAAO;EAEhC,IACE,KAAK,YAAY,YACjB,CAAC,UAAU,SAAS,KAAK,MAAM,KAC/B,CAAC,WAAW,KAAK,KAAK,MAAM,GAE5B,OAAO,YACL,gFACF;EAGF,OAAO,UAAU,SAAS,OAAO,IAAI,KAAK,UAAU,KAAK,OAAO,IAAI,IAChE;GAAE,MAAM;GAAU,IAAI,OAAO;GAAM,KAAK,KAAK;GAAQ;EAAM,IAC3D,YAAY,0DAA0D;CAC5E,IACC,OAAO,OAAO,EAAE,UAAU;EACzB,IAAI,MAAM,SAAS,OAAO;GACxB,MAAM,SAAS,aAAa,OAAO,MAAM,KAAK,YAAY,YAAY;GAEtE,IAAI,cAAc,MAAM,GAAG,OAAO;GAElC,MAAM,WAAW,OAAO,OAAO,MAAM,KAAK,UAAU;GAEpD,IAAI,aAAa,KAAA,GACf,OAAO,SAAS,SAAS,OACrB,YAAY,6CAA6C,IACzD,aAAa,KAAK,KAAK,2BAA2B,cAAc,CAAC;GAGvE,aAAa;IAGX,MAAM,OAAO,aAAa,OADf,aAAa,GACU,GAAG,MAAM,KAAK;IAEhD,MAAM,UAAgC;KACpC,IAAI,OAAO,KAAK;KAChB,UAAU,QAAQ,MAAM;KACxB,MAAM,MAAM,KAAK,WAAW,GAAG,EAAE,KAAK;KACtC;KACA,SAAS;IACX;IAEA,MAAM,UAAU,CAAC,GAAG,MAAM,SAAS,OAAO;IAE1C,OAAO,GAAG,aAAa,SAAS,MAAM,OAAO,YAAY,OAAO,OAAO,CAAC,CAAC,CAAC;GAC5E;EACF;EAEA,MAAM,WAAW,MAAM,QAAQ,MAAK,UAAS,MAAM,OAAO,MAAM,EAAE;EAElE,IAAI,aAAa,KAAA,KAAa,SAAS,SAAS,QAAQ,SAAS,SAC/D,OAAO,YAAY,gEAAgE;EAGrF,IAAI,SAAS,KAAK,QAAQ,MAAM,OAAO,CAAC,IAAI,OAAO,kBACjD,OAAO,aAAa,KAAK,KAAK,2BAA2B,cAAc,CAAC;EAG1E,aAAa;GAEX,MAAM,OAAO,aAAa,OADf,aAAa,GACU,GAAG,MAAM,KAAK;GAChD,MAAM,UAAgC;IAAE,GAAG;IAAU;GAAK;GAE1D,MAAM,UAAU,MAAM,QAAQ,KAAI,UAAU,MAAM,OAAO,SAAS,KAAK,UAAU,KAAM;GAEvF,OAAO,GAAG,aAAa,SAAS,MAAM,OAAO,YAAY,OAAO,OAAO,CAAC,CAAC,CAAC;EAC5E;CACF,CAcA;AACF"}
@@ -0,0 +1,136 @@
1
+ import * as Schema from "effect/Schema";
2
+
3
+ //#region src/dropbox/state.d.ts
4
+ /** File fields (absent on folders). */
5
+ declare const DropboxEmulatorFile: Schema.Struct<{
6
+ readonly rev: Schema.String;
7
+ readonly size: Schema.Int;
8
+ readonly contentHash: Schema.String;
9
+ readonly clientModified: Schema.String;
10
+ readonly serverModified: Schema.String;
11
+ }>;
12
+ type DropboxEmulatorFile = typeof DropboxEmulatorFile.Type;
13
+ /** A stored file or folder. Its path is its parents' names and its own, in stored casing. */
14
+ declare const DropboxEmulatorEntry: Schema.Struct<{
15
+ readonly id: Schema.String; /** Parent folder id; `null` in the root folder. */
16
+ readonly parentId: Schema.NullOr<Schema.String>;
17
+ readonly name: Schema.String; /** `null` for a folder. */
18
+ readonly file: Schema.NullOr<Schema.Struct<{
19
+ readonly rev: Schema.String;
20
+ readonly size: Schema.Int;
21
+ readonly contentHash: Schema.String;
22
+ readonly clientModified: Schema.String;
23
+ readonly serverModified: Schema.String;
24
+ }>>;
25
+ /**
26
+ * `true` for an entry no fixture shows, held only because a seeded path needs it: lookups pass
27
+ * through it, but an answer that would render it is not emulated.
28
+ */
29
+ readonly implied: Schema.Boolean;
30
+ }>;
31
+ type DropboxEmulatorEntry = typeof DropboxEmulatorEntry.Type;
32
+ /**
33
+ * What Dropbox keeps of a deleted entry (`include_deleted` lookups answer it): its name and paths
34
+ * as they were. `delete_v2` records one only for an empty folder, the only delete whose
35
+ * `include_deleted` answer a fixture records.
36
+ */
37
+ declare const DropboxEmulatorDeleted: Schema.Struct<{
38
+ readonly name: Schema.String;
39
+ readonly pathLower: Schema.String;
40
+ readonly pathDisplay: Schema.String;
41
+ }>;
42
+ type DropboxEmulatorDeleted = typeof DropboxEmulatorDeleted.Type;
43
+ declare const Counters: Schema.Struct<{
44
+ /** Next number in created entry ids (`id:SyntheticEntry00000001`). */readonly nextIdNumber: Schema.Int; /** Next number in minted revs (`a1b2c3d4e5f60101`). */
45
+ readonly nextRevNumber: Schema.Int;
46
+ }>;
47
+ /** The whole emulator state (JSON-compatible; what `snapshot()` returns). */
48
+ declare const DropboxEmulatorStateSchema: Schema.Struct<{
49
+ readonly entries: Schema.$Array<Schema.Struct<{
50
+ readonly id: Schema.String; /** Parent folder id; `null` in the root folder. */
51
+ readonly parentId: Schema.NullOr<Schema.String>;
52
+ readonly name: Schema.String; /** `null` for a folder. */
53
+ readonly file: Schema.NullOr<Schema.Struct<{
54
+ readonly rev: Schema.String;
55
+ readonly size: Schema.Int;
56
+ readonly contentHash: Schema.String;
57
+ readonly clientModified: Schema.String;
58
+ readonly serverModified: Schema.String;
59
+ }>>;
60
+ /**
61
+ * `true` for an entry no fixture shows, held only because a seeded path needs it: lookups pass
62
+ * through it, but an answer that would render it is not emulated.
63
+ */
64
+ readonly implied: Schema.Boolean;
65
+ }>>;
66
+ readonly deleted: Schema.$Array<Schema.Struct<{
67
+ readonly name: Schema.String;
68
+ readonly pathLower: Schema.String;
69
+ readonly pathDisplay: Schema.String;
70
+ }>>;
71
+ readonly counters: Schema.Struct<{
72
+ /** Next number in created entry ids (`id:SyntheticEntry00000001`). */readonly nextIdNumber: Schema.Int; /** Next number in minted revs (`a1b2c3d4e5f60101`). */
73
+ readonly nextRevNumber: Schema.Int;
74
+ }>;
75
+ }>;
76
+ /**
77
+ * The emulator state. The container is mutable (routes replace whole lists); every entity is
78
+ * replaced, never edited in place.
79
+ */
80
+ type DropboxEmulatorState = {
81
+ entries: ReadonlyArray<DropboxEmulatorEntry>;
82
+ deleted: ReadonlyArray<DropboxEmulatorDeleted>;
83
+ counters: typeof Counters.Type;
84
+ };
85
+ /**
86
+ * A seeded entry, addressed by its display path (its parent folder must be seeded too). File
87
+ * fields default to synthetic values; a folder takes none.
88
+ */
89
+ declare const DropboxEmulatorEntrySeed: Schema.Struct<{
90
+ readonly path: Schema.String;
91
+ readonly id: Schema.String;
92
+ readonly kind: Schema.Literals<readonly ["file", "folder"]>;
93
+ readonly rev: Schema.optionalKey<Schema.String>;
94
+ readonly size: Schema.optionalKey<Schema.Int>;
95
+ readonly contentHash: Schema.optionalKey<Schema.String>;
96
+ readonly clientModified: Schema.optionalKey<Schema.String>;
97
+ readonly serverModified: Schema.optionalKey<Schema.String>; /** An entry no fixture shows (default `false`); see `DropboxEmulatorEntry.implied`. */
98
+ readonly implied: Schema.optionalKey<Schema.Boolean>;
99
+ }>;
100
+ type DropboxEmulatorEntrySeed = typeof DropboxEmulatorEntrySeed.Type;
101
+ /** Account-variance profiles for the default seed. */
102
+ declare const DropboxEmulatorProfile: Schema.Literals<readonly ["default", "empty"]>;
103
+ type DropboxEmulatorProfile = typeof DropboxEmulatorProfile.Type;
104
+ /**
105
+ * A typed seed. Start from `profile` (default `'default'`, the fixture entries); `entries` and
106
+ * `deleted`, when given, replace that part of the profile.
107
+ */
108
+ declare const DropboxEmulatorSeed: Schema.Struct<{
109
+ readonly profile: Schema.optionalKey<Schema.Literals<readonly ["default", "empty"]>>;
110
+ readonly entries: Schema.optionalKey<Schema.$Array<Schema.Struct<{
111
+ readonly path: Schema.String;
112
+ readonly id: Schema.String;
113
+ readonly kind: Schema.Literals<readonly ["file", "folder"]>;
114
+ readonly rev: Schema.optionalKey<Schema.String>;
115
+ readonly size: Schema.optionalKey<Schema.Int>;
116
+ readonly contentHash: Schema.optionalKey<Schema.String>;
117
+ readonly clientModified: Schema.optionalKey<Schema.String>;
118
+ readonly serverModified: Schema.optionalKey<Schema.String>; /** An entry no fixture shows (default `false`); see `DropboxEmulatorEntry.implied`. */
119
+ readonly implied: Schema.optionalKey<Schema.Boolean>;
120
+ }>>>;
121
+ readonly deleted: Schema.optionalKey<Schema.$Array<Schema.Struct<{
122
+ readonly name: Schema.String;
123
+ readonly pathLower: Schema.String;
124
+ readonly pathDisplay: Schema.String;
125
+ }>>>;
126
+ }>;
127
+ type DropboxEmulatorSeed = typeof DropboxEmulatorSeed.Type;
128
+ /** A synthetic 64-digit content hash ending in `suffix`, as the fixtures write them. */
129
+ declare const syntheticContentHash: (suffix: string) => string;
130
+ /** Decode and build a seed; a string is the reason it is invalid. */
131
+ declare const buildSeedState: (input: unknown) => DropboxEmulatorState | string;
132
+ /** Decode a full state (a restored snapshot); a string is the reason it is invalid. */
133
+ declare const decodeState: (input: unknown) => DropboxEmulatorState | string;
134
+ //#endregion
135
+ export { DropboxEmulatorDeleted, DropboxEmulatorEntry, DropboxEmulatorEntrySeed, DropboxEmulatorFile, DropboxEmulatorProfile, DropboxEmulatorSeed, DropboxEmulatorState, DropboxEmulatorStateSchema, buildSeedState, decodeState, syntheticContentHash };
136
+ //# sourceMappingURL=state.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.mts","names":[],"sources":["../../src/dropbox/state.ts"],"mappings":";;;;cAsCa,mBAAA,EAAmB,MAAA,CAAA,MAAA;EAAA;;;;;;KAQpB,mBAAA,UAA6B,mBAAA,CAAoB,IAAI;;cAGpD,oBAAA,EAAoB,MAAA,CAAA,MAAA;EAAA,4BAXD;EAAA;;;;;;;;;;;;;;;KAyBpB,oBAAA,UAA8B,oBAAA,CAAqB,IAAI;;;AAjBnE;;;cAwBa,sBAAA,EAAsB,MAAA,CAAA,MAAA;EAAA;;;;KAMvB,sBAAA,UAAgC,sBAAA,CAAuB,IAAI;AAAA,cAIjE,QAAA,EAAQ,MAAA,CAAA,MAAA;;;;;cAQD,0BAAA,EAA0B,MAAA,CAAA,MAAA;EAAA;;;kCAvCN;IAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;KAiDrB,oBAAA;EACV,OAAA,EAAS,aAAA,CAAc,oBAAA;EACvB,OAAA,EAAS,aAAA,CAAc,sBAAA;EACvB,QAAA,SAAiB,QAAA,CAAS,IAAA;AAAA;;;;;cAUf,wBAAA,EAAwB,MAAA,CAAA,MAAA;EAAA;;;;;;;;;;KAazB,wBAAA,UAAkC,wBAAA,CAAyB,IAAI;;cAG9D,sBAAA,EAAsB,MAAA,CAAA,QAAA;AAAA,KAEvB,sBAAA,UAAgC,sBAAA,CAAuB,IAAI;;;;;cAM1D,mBAAA,EAAmB,MAAA,CAAA,MAAA;EAAA;;;;;;;;;gEA3DuC;IAAA;;;;;;;;KAiE3D,mBAAA,UAA6B,mBAAA,CAAoB,IAAI;;cAYpD,oBAAA,GAAwB,MAAc;;cAqMtC,cAAA,GAAkB,KAAA,cAAiB,oBAAoB;;cASvD,WAAA,GAAe,KAAA,cAAiB,oBAAoB"}
@@ -0,0 +1,209 @@
1
+ import { Predicate, Result } from "effect";
2
+ import * as Schema from "effect/Schema";
3
+ //#region src/dropbox/state.ts
4
+ /**
5
+ * Dropbox emulator state: the typed entries, deleted-entry records, the seed input, the default
6
+ * seed, and the profiles (internal; re-exported by `src/dropbox.ts`).
7
+ *
8
+ * Entry shapes and the default entries follow the synthetic Dropbox conformance fixtures, copied
9
+ * as data (the same paths, ids, revs, sizes, content hashes, and timestamps as the fixtures and
10
+ * `dropboxConformanceFixtureSeeds`), never imported from SDK code. The parent folders the seeded
11
+ * paths need (`/Conformance`, `/Conformance/Paging`, `/Conformance/Search`, `/Conformance/Work`)
12
+ * are `implied`: no fixture shows their metadata, so they hold paths together but any answer that
13
+ * would render one is not emulated.
14
+ *
15
+ * Minted values never repeat seeded ones: the id and rev counters start above the highest seeded
16
+ * id (`id:SyntheticEntryNNNNNNNN`) and rev (`a1b2c3d4e5f6NNNN`) in the minted form.
17
+ *
18
+ * @experimental
19
+ */
20
+ /** A Dropbox revision: lower-case hex, at least nine digits (what the connector accepts). */
21
+ const Rev = Schema.String.check(Schema.isPattern(/^[0-9a-f]{9,}$/));
22
+ /** A Dropbox file or folder id (`id:` form). */
23
+ const EntryId = Schema.String.check(Schema.isPattern(/^id:\S+$/));
24
+ /** A Dropbox timestamp as the fixtures write it (`2026-09-20T10:00:00Z`). */
25
+ const Timestamp = Schema.String.check(Schema.isPattern(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/));
26
+ /** One path component: non-empty, no `/`, not `.` or `..`. */
27
+ const EntryName = Schema.String.check(Schema.makeFilter((name) => name.length > 0 && !name.includes("/") && name !== "." && name !== ".." ? true : "an entry name is one non-empty path component"));
28
+ /** File fields (absent on folders). */
29
+ const DropboxEmulatorFile = Schema.Struct({
30
+ rev: Rev,
31
+ size: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
32
+ contentHash: Schema.String,
33
+ clientModified: Timestamp,
34
+ serverModified: Timestamp
35
+ });
36
+ /** A stored file or folder. Its path is its parents' names and its own, in stored casing. */
37
+ const DropboxEmulatorEntry = Schema.Struct({
38
+ id: EntryId,
39
+ /** Parent folder id; `null` in the root folder. */
40
+ parentId: Schema.NullOr(EntryId),
41
+ name: EntryName,
42
+ /** `null` for a folder. */
43
+ file: Schema.NullOr(DropboxEmulatorFile),
44
+ /**
45
+ * `true` for an entry no fixture shows, held only because a seeded path needs it: lookups pass
46
+ * through it, but an answer that would render it is not emulated.
47
+ */
48
+ implied: Schema.Boolean
49
+ });
50
+ /**
51
+ * What Dropbox keeps of a deleted entry (`include_deleted` lookups answer it): its name and paths
52
+ * as they were. `delete_v2` records one only for an empty folder, the only delete whose
53
+ * `include_deleted` answer a fixture records.
54
+ */
55
+ const DropboxEmulatorDeleted = Schema.Struct({
56
+ name: EntryName,
57
+ pathLower: Schema.String,
58
+ pathDisplay: Schema.String
59
+ });
60
+ const Counter = Schema.Int.check(Schema.isGreaterThanOrEqualTo(1));
61
+ const Counters = Schema.Struct({
62
+ /** Next number in created entry ids (`id:SyntheticEntry00000001`). */
63
+ nextIdNumber: Counter,
64
+ /** Next number in minted revs (`a1b2c3d4e5f60101`). */
65
+ nextRevNumber: Counter
66
+ });
67
+ /** The whole emulator state (JSON-compatible; what `snapshot()` returns). */
68
+ const DropboxEmulatorStateSchema = Schema.Struct({
69
+ entries: Schema.Array(DropboxEmulatorEntry),
70
+ deleted: Schema.Array(DropboxEmulatorDeleted),
71
+ counters: Counters
72
+ });
73
+ /** An absolute path in display casing: `/`-separated non-empty components, no trailing `/`. */
74
+ const SeedPath = Schema.String.check(Schema.isPattern(/^(?:\/[^/]+)+$/));
75
+ /**
76
+ * A seeded entry, addressed by its display path (its parent folder must be seeded too). File
77
+ * fields default to synthetic values; a folder takes none.
78
+ */
79
+ const DropboxEmulatorEntrySeed = Schema.Struct({
80
+ path: SeedPath,
81
+ id: EntryId,
82
+ kind: Schema.Literals(["file", "folder"]),
83
+ rev: Schema.optionalKey(Rev),
84
+ size: Schema.optionalKey(Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))),
85
+ contentHash: Schema.optionalKey(Schema.String),
86
+ clientModified: Schema.optionalKey(Timestamp),
87
+ serverModified: Schema.optionalKey(Timestamp),
88
+ /** An entry no fixture shows (default `false`); see `DropboxEmulatorEntry.implied`. */
89
+ implied: Schema.optionalKey(Schema.Boolean)
90
+ });
91
+ /** Account-variance profiles for the default seed. */
92
+ const DropboxEmulatorProfile = Schema.Literals(["default", "empty"]);
93
+ /**
94
+ * A typed seed. Start from `profile` (default `'default'`, the fixture entries); `entries` and
95
+ * `deleted`, when given, replace that part of the profile.
96
+ */
97
+ const DropboxEmulatorSeed = Schema.Struct({
98
+ profile: Schema.optionalKey(DropboxEmulatorProfile),
99
+ entries: Schema.optionalKey(Schema.Array(DropboxEmulatorEntrySeed)),
100
+ deleted: Schema.optionalKey(Schema.Array(DropboxEmulatorDeleted))
101
+ });
102
+ const strict = { onExcessProperty: "error" };
103
+ const decodeSeedInput = Schema.decodeUnknownResult(DropboxEmulatorSeed, strict);
104
+ const decodeStateInput = Schema.decodeUnknownResult(DropboxEmulatorStateSchema, strict);
105
+ const issueMessage = (issue) => new Schema.SchemaError(issue).message;
106
+ /** A synthetic 64-digit content hash ending in `suffix`, as the fixtures write them. */
107
+ const syntheticContentHash = (suffix) => suffix.padStart(64, "0");
108
+ const file = (path, id, revSuffix, size, hashSuffix, clientModified, serverModified = clientModified) => ({
109
+ path,
110
+ id,
111
+ kind: "file",
112
+ rev: `a1b2c3d4e5f6${revSuffix}`,
113
+ size,
114
+ contentHash: syntheticContentHash(hashSuffix),
115
+ clientModified,
116
+ serverModified
117
+ });
118
+ const folder = (path, id) => ({
119
+ path,
120
+ id,
121
+ kind: "folder"
122
+ });
123
+ /** A parent folder no fixture shows. */
124
+ const impliedFolder = (path, id) => ({
125
+ ...folder(path, id),
126
+ implied: true
127
+ });
128
+ const defaultEntries = [
129
+ impliedFolder("/Conformance", "id:SyntheticConformanceFolder"),
130
+ impliedFolder("/Conformance/Paging", "id:SyntheticPagingFolder0000"),
131
+ file("/Conformance/Paging/paging-one.txt", "id:SyntheticPagingFile0001", "0001", 12, "1", "2026-09-20T10:00:00Z"),
132
+ folder("/Conformance/Paging/paging-two", "id:SyntheticPagingFolder0002"),
133
+ file("/Conformance/Paging/paging-three.txt", "id:SyntheticPagingFile0003", "0003", 14, "3", "2026-09-20T10:05:00Z"),
134
+ file("/Conformance/Mixed Case Notes.txt", "id:SyntheticMixedCaseFile01", "0010", 20, "10", "2026-09-20T11:00:00Z"),
135
+ impliedFolder("/Conformance/Search", "id:SyntheticSearchFolder0000"),
136
+ file("/Conformance/Search/yolk-search-probe-1.txt", "id:SyntheticSearchFile0001", "0021", 16, "21", "2026-09-20T12:00:00Z"),
137
+ file("/Conformance/Search/yolk-search-probe-2.txt", "id:SyntheticSearchFile0002", "0022", 16, "22", "2026-09-20T12:00:00Z"),
138
+ impliedFolder("/Conformance/Work", "id:SyntheticWorkFolder00000"),
139
+ file("/Conformance/copy-source.txt", "id:SyntheticCopySource001", "0030", 42, "42", "2026-09-20T13:00:00Z", "2026-09-29T13:00:00Z")
140
+ ];
141
+ const profileEntries = (profile) => profile === "default" ? defaultEntries : [];
142
+ const defaultTimestamp = "2026-09-20T10:00:00Z";
143
+ /** The form of minted ids (`id:SyntheticEntry00000001`) and revs (`a1b2c3d4e5f60101`). */
144
+ const mintedIdPattern = /^id:SyntheticEntry(\d+)$/;
145
+ const mintedRevPattern = /^a1b2c3d4e5f6(\d+)$/;
146
+ /** The highest number among `values` in a minted form (0 when none is). */
147
+ const highestMinted = (values, pattern) => Math.max(0, ...values.map((value) => Number(pattern.exec(value)?.[1] ?? 0)));
148
+ const duplicate = (values) => values.find((value, index) => values.indexOf(value) !== index);
149
+ const parentPath = (path) => path.slice(0, path.lastIndexOf("/"));
150
+ const lastName = (path) => path.slice(path.lastIndexOf("/") + 1);
151
+ /** Build the emulator state for a decoded seed; a string is an integrity problem. */
152
+ const stateFromSeed = (seed) => {
153
+ const seeded = seed.entries ?? profileEntries(seed.profile ?? "default");
154
+ const duplicates = [["entry id", duplicate(seeded.map((entry) => entry.id))], ["entry path", duplicate(seeded.map((entry) => entry.path.toLowerCase()))]];
155
+ for (const [label, value] of duplicates) if (value !== void 0) return `duplicate ${label} ${value}`;
156
+ const byPath = new Map(seeded.map((entry) => [entry.path.toLowerCase(), entry]));
157
+ const entries = [];
158
+ for (const entry of seeded) {
159
+ const name = lastName(entry.path);
160
+ if (name === "." || name === "..") return `entry ${entry.id} has an invalid path`;
161
+ const parent = parentPath(entry.path);
162
+ const parentEntry = parent === "" ? void 0 : byPath.get(parent.toLowerCase());
163
+ if (parent !== "" && (parentEntry === void 0 || parentEntry.kind !== "folder")) return `entry ${entry.path} needs a seeded parent folder ${parent}`;
164
+ const hasFileFields = [
165
+ entry.rev,
166
+ entry.size,
167
+ entry.contentHash,
168
+ entry.clientModified,
169
+ entry.serverModified
170
+ ].some(Predicate.isNotUndefined);
171
+ if (entry.kind === "folder" && hasFileFields) return `folder ${entry.path} cannot take file fields`;
172
+ const clientModified = entry.clientModified ?? defaultTimestamp;
173
+ entries.push({
174
+ id: entry.id,
175
+ parentId: parentEntry?.id ?? null,
176
+ name,
177
+ implied: entry.implied ?? false,
178
+ file: entry.kind === "folder" ? null : {
179
+ rev: entry.rev ?? "a1b2c3d4e5f60000",
180
+ size: entry.size ?? 0,
181
+ contentHash: entry.contentHash ?? syntheticContentHash("0"),
182
+ clientModified,
183
+ serverModified: entry.serverModified ?? clientModified
184
+ }
185
+ });
186
+ }
187
+ return {
188
+ entries,
189
+ deleted: seed.deleted ?? [],
190
+ counters: {
191
+ nextIdNumber: highestMinted(entries.map((entry) => entry.id), mintedIdPattern) + 1,
192
+ nextRevNumber: Math.max(101, highestMinted(entries.flatMap((entry) => entry.file?.rev ?? []), mintedRevPattern) + 1)
193
+ }
194
+ };
195
+ };
196
+ /** Decode and build a seed; a string is the reason it is invalid. */
197
+ const buildSeedState = (input) => {
198
+ const decoded = decodeSeedInput(input);
199
+ return Result.isFailure(decoded) ? issueMessage(decoded.failure.issue) : stateFromSeed(decoded.success);
200
+ };
201
+ /** Decode a full state (a restored snapshot); a string is the reason it is invalid. */
202
+ const decodeState = (input) => {
203
+ const decoded = decodeStateInput(input);
204
+ return Result.isFailure(decoded) ? issueMessage(decoded.failure.issue) : { ...decoded.success };
205
+ };
206
+ //#endregion
207
+ export { DropboxEmulatorDeleted, DropboxEmulatorEntry, DropboxEmulatorEntrySeed, DropboxEmulatorFile, DropboxEmulatorProfile, DropboxEmulatorSeed, DropboxEmulatorStateSchema, buildSeedState, decodeState, syntheticContentHash };
208
+
209
+ //# sourceMappingURL=state.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.mjs","names":[],"sources":["../../src/dropbox/state.ts"],"sourcesContent":["/**\n * Dropbox emulator state: the typed entries, deleted-entry records, the seed input, the default\n * seed, and the profiles (internal; re-exported by `src/dropbox.ts`).\n *\n * Entry shapes and the default entries follow the synthetic Dropbox conformance fixtures, copied\n * as data (the same paths, ids, revs, sizes, content hashes, and timestamps as the fixtures and\n * `dropboxConformanceFixtureSeeds`), never imported from SDK code. The parent folders the seeded\n * paths need (`/Conformance`, `/Conformance/Paging`, `/Conformance/Search`, `/Conformance/Work`)\n * are `implied`: no fixture shows their metadata, so they hold paths together but any answer that\n * would render one is not emulated.\n *\n * Minted values never repeat seeded ones: the id and rev counters start above the highest seeded\n * id (`id:SyntheticEntryNNNNNNNN`) and rev (`a1b2c3d4e5f6NNNN`) in the minted form.\n *\n * @experimental\n */\nimport { Predicate, Result } from 'effect'\nimport * as Schema from 'effect/Schema'\n\n/** A Dropbox revision: lower-case hex, at least nine digits (what the connector accepts). */\nconst Rev = Schema.String.check(Schema.isPattern(/^[0-9a-f]{9,}$/))\n\n/** A Dropbox file or folder id (`id:` form). */\nconst EntryId = Schema.String.check(Schema.isPattern(/^id:\\S+$/))\n\n/** A Dropbox timestamp as the fixtures write it (`2026-09-20T10:00:00Z`). */\nconst Timestamp = Schema.String.check(Schema.isPattern(/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$/))\n\n/** One path component: non-empty, no `/`, not `.` or `..`. */\nconst EntryName = Schema.String.check(\n Schema.makeFilter(name =>\n name.length > 0 && !name.includes('/') && name !== '.' && name !== '..'\n ? true\n : 'an entry name is one non-empty path component'\n )\n)\n\n/** File fields (absent on folders). */\nexport const DropboxEmulatorFile = Schema.Struct({\n rev: Rev,\n size: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),\n contentHash: Schema.String,\n clientModified: Timestamp,\n serverModified: Timestamp\n})\n\nexport type DropboxEmulatorFile = typeof DropboxEmulatorFile.Type\n\n/** A stored file or folder. Its path is its parents' names and its own, in stored casing. */\nexport const DropboxEmulatorEntry = Schema.Struct({\n id: EntryId,\n /** Parent folder id; `null` in the root folder. */\n parentId: Schema.NullOr(EntryId),\n name: EntryName,\n /** `null` for a folder. */\n file: Schema.NullOr(DropboxEmulatorFile),\n /**\n * `true` for an entry no fixture shows, held only because a seeded path needs it: lookups pass\n * through it, but an answer that would render it is not emulated.\n */\n implied: Schema.Boolean\n})\n\nexport type DropboxEmulatorEntry = typeof DropboxEmulatorEntry.Type\n\n/**\n * What Dropbox keeps of a deleted entry (`include_deleted` lookups answer it): its name and paths\n * as they were. `delete_v2` records one only for an empty folder, the only delete whose\n * `include_deleted` answer a fixture records.\n */\nexport const DropboxEmulatorDeleted = Schema.Struct({\n name: EntryName,\n pathLower: Schema.String,\n pathDisplay: Schema.String\n})\n\nexport type DropboxEmulatorDeleted = typeof DropboxEmulatorDeleted.Type\n\nconst Counter = Schema.Int.check(Schema.isGreaterThanOrEqualTo(1))\n\nconst Counters = Schema.Struct({\n /** Next number in created entry ids (`id:SyntheticEntry00000001`). */\n nextIdNumber: Counter,\n /** Next number in minted revs (`a1b2c3d4e5f60101`). */\n nextRevNumber: Counter\n})\n\n/** The whole emulator state (JSON-compatible; what `snapshot()` returns). */\nexport const DropboxEmulatorStateSchema = Schema.Struct({\n entries: Schema.Array(DropboxEmulatorEntry),\n deleted: Schema.Array(DropboxEmulatorDeleted),\n counters: Counters\n})\n\n/**\n * The emulator state. The container is mutable (routes replace whole lists); every entity is\n * replaced, never edited in place.\n */\nexport type DropboxEmulatorState = {\n entries: ReadonlyArray<DropboxEmulatorEntry>\n deleted: ReadonlyArray<DropboxEmulatorDeleted>\n counters: typeof Counters.Type\n}\n\n/** An absolute path in display casing: `/`-separated non-empty components, no trailing `/`. */\nconst SeedPath = Schema.String.check(Schema.isPattern(/^(?:\\/[^/]+)+$/))\n\n/**\n * A seeded entry, addressed by its display path (its parent folder must be seeded too). File\n * fields default to synthetic values; a folder takes none.\n */\nexport const DropboxEmulatorEntrySeed = Schema.Struct({\n path: SeedPath,\n id: EntryId,\n kind: Schema.Literals(['file', 'folder']),\n rev: Schema.optionalKey(Rev),\n size: Schema.optionalKey(Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))),\n contentHash: Schema.optionalKey(Schema.String),\n clientModified: Schema.optionalKey(Timestamp),\n serverModified: Schema.optionalKey(Timestamp),\n /** An entry no fixture shows (default `false`); see `DropboxEmulatorEntry.implied`. */\n implied: Schema.optionalKey(Schema.Boolean)\n})\n\nexport type DropboxEmulatorEntrySeed = typeof DropboxEmulatorEntrySeed.Type\n\n/** Account-variance profiles for the default seed. */\nexport const DropboxEmulatorProfile = Schema.Literals(['default', 'empty'])\n\nexport type DropboxEmulatorProfile = typeof DropboxEmulatorProfile.Type\n\n/**\n * A typed seed. Start from `profile` (default `'default'`, the fixture entries); `entries` and\n * `deleted`, when given, replace that part of the profile.\n */\nexport const DropboxEmulatorSeed = Schema.Struct({\n profile: Schema.optionalKey(DropboxEmulatorProfile),\n entries: Schema.optionalKey(Schema.Array(DropboxEmulatorEntrySeed)),\n deleted: Schema.optionalKey(Schema.Array(DropboxEmulatorDeleted))\n})\n\nexport type DropboxEmulatorSeed = typeof DropboxEmulatorSeed.Type\n\nconst strict = { onExcessProperty: 'error' } as const\n\nconst decodeSeedInput = Schema.decodeUnknownResult(DropboxEmulatorSeed, strict)\n\nconst decodeStateInput = Schema.decodeUnknownResult(DropboxEmulatorStateSchema, strict)\n\nconst issueMessage = (issue: Schema.SchemaError['issue']): string =>\n new Schema.SchemaError(issue).message\n\n/** A synthetic 64-digit content hash ending in `suffix`, as the fixtures write them. */\nexport const syntheticContentHash = (suffix: string): string => suffix.padStart(64, '0')\n\n// Default entries. Fixture-derived: every file, the `paging-two` folder, and every path, id, rev,\n// size, content hash, and timestamp of them. Implied (never rendered): the `/Conformance`,\n// `/Conformance/Paging`, `/Conformance/Search`, and `/Conformance/Work` folders.\n\nconst file = (\n path: string,\n id: string,\n revSuffix: string,\n size: number,\n hashSuffix: string,\n clientModified: string,\n serverModified: string = clientModified\n): DropboxEmulatorEntrySeed => ({\n path,\n id,\n kind: 'file',\n rev: `a1b2c3d4e5f6${revSuffix}`,\n size,\n contentHash: syntheticContentHash(hashSuffix),\n clientModified,\n serverModified\n})\n\nconst folder = (path: string, id: string): DropboxEmulatorEntrySeed => ({\n path,\n id,\n kind: 'folder'\n})\n\n/** A parent folder no fixture shows. */\nconst impliedFolder = (path: string, id: string): DropboxEmulatorEntrySeed => ({\n ...folder(path, id),\n implied: true\n})\n\nconst defaultEntries: ReadonlyArray<DropboxEmulatorEntrySeed> = [\n impliedFolder('/Conformance', 'id:SyntheticConformanceFolder'),\n impliedFolder('/Conformance/Paging', 'id:SyntheticPagingFolder0000'),\n file(\n '/Conformance/Paging/paging-one.txt',\n 'id:SyntheticPagingFile0001',\n '0001',\n 12,\n '1',\n '2026-09-20T10:00:00Z'\n ),\n folder('/Conformance/Paging/paging-two', 'id:SyntheticPagingFolder0002'),\n file(\n '/Conformance/Paging/paging-three.txt',\n 'id:SyntheticPagingFile0003',\n '0003',\n 14,\n '3',\n '2026-09-20T10:05:00Z'\n ),\n file(\n '/Conformance/Mixed Case Notes.txt',\n 'id:SyntheticMixedCaseFile01',\n '0010',\n 20,\n '10',\n '2026-09-20T11:00:00Z'\n ),\n impliedFolder('/Conformance/Search', 'id:SyntheticSearchFolder0000'),\n file(\n '/Conformance/Search/yolk-search-probe-1.txt',\n 'id:SyntheticSearchFile0001',\n '0021',\n 16,\n '21',\n '2026-09-20T12:00:00Z'\n ),\n file(\n '/Conformance/Search/yolk-search-probe-2.txt',\n 'id:SyntheticSearchFile0002',\n '0022',\n 16,\n '22',\n '2026-09-20T12:00:00Z'\n ),\n impliedFolder('/Conformance/Work', 'id:SyntheticWorkFolder00000'),\n file(\n '/Conformance/copy-source.txt',\n 'id:SyntheticCopySource001',\n '0030',\n 42,\n '42',\n '2026-09-20T13:00:00Z',\n '2026-09-29T13:00:00Z'\n )\n]\n\nconst profileEntries = (\n profile: DropboxEmulatorProfile\n): ReadonlyArray<DropboxEmulatorEntrySeed> => (profile === 'default' ? defaultEntries : [])\n\nconst defaultTimestamp = '2026-09-20T10:00:00Z'\n\n/** The form of minted ids (`id:SyntheticEntry00000001`) and revs (`a1b2c3d4e5f60101`). */\nconst mintedIdPattern = /^id:SyntheticEntry(\\d+)$/\n\nconst mintedRevPattern = /^a1b2c3d4e5f6(\\d+)$/\n\n/** The highest number among `values` in a minted form (0 when none is). */\nconst highestMinted = (values: ReadonlyArray<string>, pattern: RegExp): number =>\n Math.max(0, ...values.map(value => Number(pattern.exec(value)?.[1] ?? 0)))\n\nconst duplicate = (values: ReadonlyArray<string>): string | undefined =>\n values.find((value, index) => values.indexOf(value) !== index)\n\nconst parentPath = (path: string): string => path.slice(0, path.lastIndexOf('/'))\n\nconst lastName = (path: string): string => path.slice(path.lastIndexOf('/') + 1)\n\n/** Build the emulator state for a decoded seed; a string is an integrity problem. */\nconst stateFromSeed = (seed: DropboxEmulatorSeed): DropboxEmulatorState | string => {\n const seeded = seed.entries ?? profileEntries(seed.profile ?? 'default')\n\n const duplicates: ReadonlyArray<readonly [string, string | undefined]> = [\n ['entry id', duplicate(seeded.map(entry => entry.id))],\n ['entry path', duplicate(seeded.map(entry => entry.path.toLowerCase()))]\n ]\n\n for (const [label, value] of duplicates) {\n if (value !== undefined) return `duplicate ${label} ${value}`\n }\n\n const byPath = new Map(seeded.map(entry => [entry.path.toLowerCase(), entry]))\n const entries: Array<DropboxEmulatorEntry> = []\n\n for (const entry of seeded) {\n const name = lastName(entry.path)\n\n if (name === '.' || name === '..') return `entry ${entry.id} has an invalid path`\n\n const parent = parentPath(entry.path)\n const parentEntry = parent === '' ? undefined : byPath.get(parent.toLowerCase())\n\n if (parent !== '' && (parentEntry === undefined || parentEntry.kind !== 'folder')) {\n return `entry ${entry.path} needs a seeded parent folder ${parent}`\n }\n\n const hasFileFields = [\n entry.rev,\n entry.size,\n entry.contentHash,\n entry.clientModified,\n entry.serverModified\n ].some(Predicate.isNotUndefined)\n\n if (entry.kind === 'folder' && hasFileFields) {\n return `folder ${entry.path} cannot take file fields`\n }\n\n const clientModified = entry.clientModified ?? defaultTimestamp\n\n entries.push({\n id: entry.id,\n parentId: parentEntry?.id ?? null,\n name,\n implied: entry.implied ?? false,\n file:\n entry.kind === 'folder'\n ? null\n : {\n rev: entry.rev ?? 'a1b2c3d4e5f60000',\n size: entry.size ?? 0,\n contentHash: entry.contentHash ?? syntheticContentHash('0'),\n clientModified,\n serverModified: entry.serverModified ?? clientModified\n }\n })\n }\n\n return {\n entries,\n deleted: seed.deleted ?? [],\n counters: {\n nextIdNumber:\n highestMinted(\n entries.map(entry => entry.id),\n mintedIdPattern\n ) + 1,\n nextRevNumber: Math.max(\n 101,\n highestMinted(\n entries.flatMap(entry => entry.file?.rev ?? []),\n mintedRevPattern\n ) + 1\n )\n }\n }\n}\n\n/** Decode and build a seed; a string is the reason it is invalid. */\nexport const buildSeedState = (input: unknown): DropboxEmulatorState | string => {\n const decoded = decodeSeedInput(input)\n\n return Result.isFailure(decoded)\n ? issueMessage(decoded.failure.issue)\n : stateFromSeed(decoded.success)\n}\n\n/** Decode a full state (a restored snapshot); a string is the reason it is invalid. */\nexport const decodeState = (input: unknown): DropboxEmulatorState | string => {\n const decoded = decodeStateInput(input)\n\n return Result.isFailure(decoded) ? issueMessage(decoded.failure.issue) : { ...decoded.success }\n}\n"],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ import { EmulatorEvidence, EmulatorRouteEvidence, emulatorEvidenceHeader } from "./route-evidence.mjs";
2
+ import { StatefulCoverage, StatefulEmulatorApi, StatefulFault, StatefulFaultMatch, StatefulFaultState, StatefulInputKind, StatefulLedgerEntry } from "./stateful-emulator.mjs";
3
+ import { DropboxEmulatorDeleted, DropboxEmulatorEntry, DropboxEmulatorEntrySeed, DropboxEmulatorFile, DropboxEmulatorProfile, DropboxEmulatorSeed, DropboxEmulatorState, DropboxEmulatorStateSchema } from "./dropbox/state.mjs";
4
+ import { DropboxCursor, DropboxEmulatorDrills, dropboxEmulatorApiOrigin, dropboxEmulatorBasePath, dropboxEmulatorContentOrigin, dropboxEmulatorErrorBodies } from "./dropbox/api.mjs";
5
+
6
+ //#region src/dropbox.d.ts
7
+ /**
8
+ * Route evidence manifest: every emulated route, whether it writes, and the conformance cases
9
+ * whose (currently synthetic, unverified) wire claims it follows. Kept in sync with the handlers
10
+ * by construction (both come from one route table).
11
+ */
12
+ declare const dropboxEmulatorRoutes: ReadonlyArray<EmulatorRouteEvidence>;
13
+ /**
14
+ * Optional fault filter; an omitted field matches every request. `method` is the HTTP method;
15
+ * `path` is the raw request path (ending in `*`, a prefix); `route` is a route template of the
16
+ * manifest (another value is rejected when the fault is added).
17
+ */
18
+ declare const DropboxFaultMatch: import("effect/Schema").Struct<{
19
+ readonly method: import("effect/Schema").optionalKey<import("effect/Schema").String>;
20
+ readonly path: import("effect/Schema").optionalKey<import("effect/Schema").String>;
21
+ readonly route: import("effect/Schema").optionalKey<import("effect/Schema").String>;
22
+ }>;
23
+ type DropboxFaultMatch = typeof StatefulFaultMatch.Type;
24
+ /**
25
+ * A status fault (400-599): answer matching requests with this status, headers, and body instead
26
+ * of the route's write, so nothing is written. The body defaults to
27
+ * `{ error: { type: 'emulator_fault', message } }`; `count` limits how many requests it answers.
28
+ * Only a request the emulator would answer reaches a fault: a request that is not emulated, by
29
+ * its shape or by the state, never uses one up.
30
+ */
31
+ declare const DropboxFault: import("effect/Schema").Struct<{
32
+ readonly kind: import("effect/Schema").Literal<"status">;
33
+ readonly status: import("effect/Schema").Int;
34
+ readonly headers: import("effect/Schema").optionalKey<import("effect/Schema").$Record<import("effect/Schema").String, import("effect/Schema").String>>;
35
+ readonly body: import("effect/Schema").optionalKey<import("effect/Schema").Codec<import("effect/Schema").Json, import("effect/Schema").Json, never, never>>;
36
+ readonly match: import("effect/Schema").optionalKey<import("effect/Schema").Struct<{
37
+ readonly method: import("effect/Schema").optionalKey<import("effect/Schema").String>;
38
+ readonly path: import("effect/Schema").optionalKey<import("effect/Schema").String>;
39
+ readonly route: import("effect/Schema").optionalKey<import("effect/Schema").String>;
40
+ }>>;
41
+ readonly count: import("effect/Schema").optionalKey<import("effect/Schema").Int>;
42
+ }>;
43
+ type DropboxFault = StatefulFault;
44
+ type DropboxFaultState = StatefulFaultState;
45
+ type DropboxLedgerEntry = StatefulLedgerEntry;
46
+ type DropboxCoverage = StatefulCoverage;
47
+ declare const DropboxEmulatorInputInvalid_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => import("effect/Cause").YieldableError & {
48
+ readonly _tag: "DropboxEmulatorInputInvalid";
49
+ } & Readonly<A>;
50
+ /** Invalid emulator input from the JS API: a seed, a fault, or an option. A programmer error. */
51
+ declare class DropboxEmulatorInputInvalid extends DropboxEmulatorInputInvalid_base<{
52
+ readonly input: StatefulInputKind;
53
+ readonly reason: string;
54
+ }> {
55
+ get message(): string;
56
+ }
57
+ /** A cursor as reported by `cursors()` and `/_emulate/state`. */
58
+ type DropboxCursorState = {
59
+ readonly cursor: string;
60
+ readonly kind: DropboxCursor['kind']; /** Position of the next page in the listing or search. */
61
+ readonly offset: number;
62
+ };
63
+ type DropboxEmulatorOptions = {
64
+ /** Typed seed; defaults to the fixture entries (`profile: 'default'`). */readonly seed?: DropboxEmulatorSeed; /** Clock in epoch milliseconds (upload timestamps). Defaults to `Date.now`. */
65
+ readonly now?: () => number; /** Drill knobs (tests only): make the emulator disagree with one conformance claim. */
66
+ readonly drills?: DropboxEmulatorDrills;
67
+ };
68
+ type DropboxEmulator = StatefulEmulatorApi<DropboxEmulatorState, DropboxEmulatorSeed> & {
69
+ /** The list and search cursors issued since the last reset or seed (runtime data). */readonly cursors: () => ReadonlyArray<DropboxCursorState>;
70
+ };
71
+ /**
72
+ * Create a stateful Dropbox emulator on the `@emulators/core` custom runtime. Each call has its
73
+ * own state, ledger, faults, and cursors. Rejects with `DropboxEmulatorInputInvalid` for an
74
+ * invalid seed or option. See `src/stateful-emulator.ts` for the request precedence.
75
+ */
76
+ declare const makeDropboxEmulator: (options?: DropboxEmulatorOptions) => Promise<DropboxEmulator>;
77
+ //#endregion
78
+ export { DropboxCoverage, DropboxCursorState, DropboxEmulator, DropboxEmulatorDeleted, type DropboxEmulatorDrills, DropboxEmulatorEntry, DropboxEmulatorEntrySeed, DropboxEmulatorFile, DropboxEmulatorInputInvalid, DropboxEmulatorOptions, DropboxEmulatorProfile, DropboxEmulatorSeed, type DropboxEmulatorState, DropboxEmulatorStateSchema, DropboxFault, DropboxFaultMatch, DropboxFaultState, DropboxLedgerEntry, type EmulatorEvidence, type EmulatorRouteEvidence, dropboxEmulatorApiOrigin, dropboxEmulatorBasePath, dropboxEmulatorContentOrigin, dropboxEmulatorErrorBodies, dropboxEmulatorRoutes, emulatorEvidenceHeader, makeDropboxEmulator };
79
+ //# sourceMappingURL=dropbox.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dropbox.d.mts","names":[],"sources":["../src/dropbox.ts"],"mappings":";;;;;;;;AAoFuE;AAQvE;;cARa,qBAAA,EAAuB,aAAa,CAAC,qBAAA;;;;;;cAQrC,iBAAA,0BAAiB,MAAA;EAAA;;;;KAElB,iBAAA,UAA2B,kBAAA,CAAmB,IAAI;;;AAA9D;;;;AAA8D;cASjD,YAAA,0BAAY,MAAA;EAAA;;;;;;;;;;;KAEb,YAAA,GAAe,aAAa;AAAA,KAE5B,iBAAA,GAAoB,kBAAkB;AAAA,KAEtC,kBAAA,GAAqB,mBAAmB;AAAA,KAExC,eAAA,GAAkB,gBAAgB;AAAA,cAAA,gCAAA;;;;cAGjC,2BAAA,SAAoC,gCAAA;EAAA,SACtC,KAAA,EAAO,iBAAA;EAAA,SACP,MAAA;AAAA;EAAA,IAEI,OAAA,CAAA;AAAA;;KAMH,kBAAA;EAAA,SACD,MAAA;EAAA,SACA,IAAA,EAAM,aAAa;WAEnB,MAAA;AAAA;AAAA,KAGC,sBAAA;qFAED,IAAA,GAAO,mBAAA,EA5BN;EAAA,SA8BD,GAAA;WAEA,MAAA,GAAS,qBAAqB;AAAA;AAAA,KAG7B,eAAA,GAAkB,mBAAA,CAAoB,oBAAA,EAAsB,mBAAA;EAjC3C,+FAmClB,OAAA,QAAe,aAAA,CAAc,kBAAA;AAAA;AAnCU;AAElD;;;;AAFkD,cA8CrC,mBAAA,GACX,OAAA,GAAS,sBAAA,KACR,OAAA,CAAQ,eAAA"}