@shivaedev/work-board 0.7.2 → 0.9.0

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 (277) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +110 -2
  3. package/assets/agent.js +37 -0
  4. package/assets/native.js +54 -25
  5. package/dist/board.d.ts +1 -0
  6. package/dist/board.d.ts.map +1 -1
  7. package/dist/board.js +15 -2
  8. package/dist/board.js.map +1 -1
  9. package/dist/browser/client.d.ts +19 -79
  10. package/dist/browser/client.d.ts.map +1 -1
  11. package/dist/browser/client.js +12 -1
  12. package/dist/browser/client.js.map +1 -1
  13. package/dist/browser/entry.d.ts +3 -0
  14. package/dist/browser/entry.d.ts.map +1 -0
  15. package/dist/browser/entry.js +7 -0
  16. package/dist/browser/entry.js.map +1 -0
  17. package/dist/browser/responses/Prompt.d.ts +10 -0
  18. package/dist/browser/responses/Prompt.d.ts.map +1 -0
  19. package/dist/browser/responses/Prompt.js +2 -0
  20. package/dist/browser/responses/Prompt.js.map +1 -0
  21. package/dist/browser/responses/answerBody.d.ts +4 -0
  22. package/dist/browser/responses/answerBody.d.ts.map +1 -0
  23. package/dist/browser/responses/answerBody.js +9 -0
  24. package/dist/browser/responses/answerBody.js.map +1 -0
  25. package/dist/browser/responses/draftSession.d.ts +8 -0
  26. package/dist/browser/responses/draftSession.d.ts.map +1 -0
  27. package/dist/browser/responses/draftSession.js +105 -0
  28. package/dist/browser/responses/draftSession.js.map +1 -0
  29. package/dist/browser/responses/drafts.d.ts +21 -0
  30. package/dist/browser/responses/drafts.d.ts.map +1 -0
  31. package/dist/browser/responses/drafts.js +68 -0
  32. package/dist/browser/responses/drafts.js.map +1 -0
  33. package/dist/browser/responses/preview.d.ts +4 -0
  34. package/dist/browser/responses/preview.d.ts.map +1 -0
  35. package/dist/browser/responses/preview.js +5 -0
  36. package/dist/browser/responses/preview.js.map +1 -0
  37. package/dist/browser/responses/questionnaireControls.d.ts +10 -0
  38. package/dist/browser/responses/questionnaireControls.d.ts.map +1 -0
  39. package/dist/browser/responses/questionnaireControls.js +68 -0
  40. package/dist/browser/responses/questionnaireControls.js.map +1 -0
  41. package/dist/browser/responses/schema.d.ts +168 -0
  42. package/dist/browser/responses/schema.d.ts.map +1 -0
  43. package/dist/browser/responses/schema.js +57 -0
  44. package/dist/browser/responses/schema.js.map +1 -0
  45. package/dist/browser/responses/setup.d.ts +2 -0
  46. package/dist/browser/responses/setup.d.ts.map +1 -0
  47. package/dist/browser/responses/setup.js +129 -0
  48. package/dist/browser/responses/setup.js.map +1 -0
  49. package/dist/browser/responses/submissionFields.d.ts +15 -0
  50. package/dist/browser/responses/submissionFields.d.ts.map +1 -0
  51. package/dist/browser/responses/submissionFields.js +14 -0
  52. package/dist/browser/responses/submissionFields.js.map +1 -0
  53. package/dist/browser/responses/submit.d.ts +4 -0
  54. package/dist/browser/responses/submit.d.ts.map +1 -0
  55. package/dist/browser/responses/submit.js +30 -0
  56. package/dist/browser/responses/submit.js.map +1 -0
  57. package/dist/browser/session.d.ts +1 -79
  58. package/dist/browser/session.d.ts.map +1 -1
  59. package/dist/cli.d.ts.map +1 -1
  60. package/dist/cli.js +24 -15
  61. package/dist/cli.js.map +1 -1
  62. package/dist/files/changes.d.ts +1 -0
  63. package/dist/files/changes.d.ts.map +1 -1
  64. package/dist/files/changes.js +1 -1
  65. package/dist/files/changes.js.map +1 -1
  66. package/dist/http/respond.d.ts.map +1 -1
  67. package/dist/http/respond.js +2 -0
  68. package/dist/http/respond.js.map +1 -1
  69. package/dist/metadata/backlinks.d.ts.map +1 -1
  70. package/dist/metadata/backlinks.js +5 -0
  71. package/dist/metadata/backlinks.js.map +1 -1
  72. package/dist/metadata/fields.d.ts +23 -1
  73. package/dist/metadata/fields.d.ts.map +1 -1
  74. package/dist/metadata/problems.d.ts.map +1 -1
  75. package/dist/metadata/problems.js +3 -0
  76. package/dist/metadata/problems.js.map +1 -1
  77. package/dist/metadata/schema.d.ts +23 -1
  78. package/dist/metadata/schema.d.ts.map +1 -1
  79. package/dist/metadata/schema.js +6 -2
  80. package/dist/metadata/schema.js.map +1 -1
  81. package/dist/page/attentionDetails.d.ts.map +1 -1
  82. package/dist/page/attentionDetails.js +2 -1
  83. package/dist/page/attentionDetails.js.map +1 -1
  84. package/dist/page/metadataDetails.d.ts.map +1 -1
  85. package/dist/page/metadataDetails.js +6 -0
  86. package/dist/page/metadataDetails.js.map +1 -1
  87. package/dist/page/navigationScript.js +1 -1
  88. package/dist/page/shell.d.ts +1 -1
  89. package/dist/page/shell.d.ts.map +1 -1
  90. package/dist/page/shell.js.map +1 -1
  91. package/dist/page/style.d.ts.map +1 -1
  92. package/dist/page/style.js +3 -1
  93. package/dist/page/style.js.map +1 -1
  94. package/dist/page/swap.d.ts +1 -1
  95. package/dist/page/swap.d.ts.map +1 -1
  96. package/dist/page/swap.js +3 -0
  97. package/dist/page/swap.js.map +1 -1
  98. package/dist/path/Identity.d.ts +3 -0
  99. package/dist/path/Identity.d.ts.map +1 -0
  100. package/dist/path/Identity.js +3 -0
  101. package/dist/path/Identity.js.map +1 -0
  102. package/dist/render/markdown.d.ts +1 -0
  103. package/dist/render/markdown.d.ts.map +1 -1
  104. package/dist/render/markdown.js +4 -2
  105. package/dist/render/markdown.js.map +1 -1
  106. package/dist/render/questionnaireDocuments.d.ts +3 -0
  107. package/dist/render/questionnaireDocuments.d.ts.map +1 -0
  108. package/dist/render/questionnaireDocuments.js +11 -0
  109. package/dist/render/questionnaireDocuments.js.map +1 -0
  110. package/dist/render/visualDocuments.d.ts.map +1 -1
  111. package/dist/render/visualDocuments.js +3 -0
  112. package/dist/render/visualDocuments.js.map +1 -1
  113. package/dist/responses/agent-cli.d.ts +2 -0
  114. package/dist/responses/agent-cli.d.ts.map +1 -0
  115. package/dist/responses/agent-cli.js +31 -0
  116. package/dist/responses/agent-cli.js.map +1 -0
  117. package/dist/responses/agent.d.ts +80 -0
  118. package/dist/responses/agent.d.ts.map +1 -0
  119. package/dist/responses/agent.js +49 -0
  120. package/dist/responses/agent.js.map +1 -0
  121. package/dist/responses/await.d.ts +39 -0
  122. package/dist/responses/await.d.ts.map +1 -0
  123. package/dist/responses/await.js +21 -0
  124. package/dist/responses/await.js.map +1 -0
  125. package/dist/responses/composerHtml.d.ts +27 -0
  126. package/dist/responses/composerHtml.d.ts.map +1 -0
  127. package/dist/responses/composerHtml.js +14 -0
  128. package/dist/responses/composerHtml.js.map +1 -0
  129. package/dist/responses/flags.d.ts +6 -0
  130. package/dist/responses/flags.d.ts.map +1 -0
  131. package/dist/responses/flags.js +23 -0
  132. package/dist/responses/flags.js.map +1 -0
  133. package/dist/responses/history.d.ts +20 -0
  134. package/dist/responses/history.d.ts.map +1 -0
  135. package/dist/responses/history.js +16 -0
  136. package/dist/responses/history.js.map +1 -0
  137. package/dist/responses/locate.d.ts +13 -0
  138. package/dist/responses/locate.d.ts.map +1 -0
  139. package/dist/responses/locate.js +55 -0
  140. package/dist/responses/locate.js.map +1 -0
  141. package/dist/responses/make.d.ts +3 -0
  142. package/dist/responses/make.d.ts.map +1 -0
  143. package/dist/responses/make.js +16 -0
  144. package/dist/responses/make.js.map +1 -0
  145. package/dist/responses/outcome.d.ts +71 -0
  146. package/dist/responses/outcome.d.ts.map +1 -0
  147. package/dist/responses/outcome.js +14 -0
  148. package/dist/responses/outcome.js.map +1 -0
  149. package/dist/responses/page.d.ts +6 -0
  150. package/dist/responses/page.d.ts.map +1 -0
  151. package/dist/responses/page.js +22 -0
  152. package/dist/responses/page.js.map +1 -0
  153. package/dist/responses/publish.d.ts +4 -0
  154. package/dist/responses/publish.d.ts.map +1 -0
  155. package/dist/responses/publish.js +56 -0
  156. package/dist/responses/publish.js.map +1 -0
  157. package/dist/responses/publishIn.d.ts +3 -0
  158. package/dist/responses/publishIn.d.ts.map +1 -0
  159. package/dist/responses/publishIn.js +69 -0
  160. package/dist/responses/publishIn.js.map +1 -0
  161. package/dist/responses/read.d.ts +36 -0
  162. package/dist/responses/read.d.ts.map +1 -0
  163. package/dist/responses/read.js +43 -0
  164. package/dist/responses/read.js.map +1 -0
  165. package/dist/responses/records.d.ts +12 -0
  166. package/dist/responses/records.d.ts.map +1 -0
  167. package/dist/responses/records.js +49 -0
  168. package/dist/responses/records.js.map +1 -0
  169. package/dist/responses/retry.d.ts +34 -0
  170. package/dist/responses/retry.d.ts.map +1 -0
  171. package/dist/responses/retry.js +14 -0
  172. package/dist/responses/retry.js.map +1 -0
  173. package/dist/responses/service.d.ts +142 -0
  174. package/dist/responses/service.d.ts.map +1 -0
  175. package/dist/responses/service.js +95 -0
  176. package/dist/responses/service.js.map +1 -0
  177. package/dist/responses/template.d.ts +7 -0
  178. package/dist/responses/template.d.ts.map +1 -0
  179. package/dist/responses/template.js +58 -0
  180. package/dist/responses/template.js.map +1 -0
  181. package/dist/responses/templateHtml.d.ts +7 -0
  182. package/dist/responses/templateHtml.d.ts.map +1 -0
  183. package/dist/responses/templateHtml.js +18 -0
  184. package/dist/responses/templateHtml.js.map +1 -0
  185. package/dist/responses/templateLinks.d.ts +3 -0
  186. package/dist/responses/templateLinks.d.ts.map +1 -0
  187. package/dist/responses/templateLinks.js +45 -0
  188. package/dist/responses/templateLinks.js.map +1 -0
  189. package/dist/responses/templateNodes.d.ts +17 -0
  190. package/dist/responses/templateNodes.d.ts.map +1 -0
  191. package/dist/responses/templateNodes.js +47 -0
  192. package/dist/responses/templateNodes.js.map +1 -0
  193. package/dist/responses/validateSupersedes.d.ts +5 -0
  194. package/dist/responses/validateSupersedes.d.ts.map +1 -0
  195. package/dist/responses/validateSupersedes.js +21 -0
  196. package/dist/responses/validateSupersedes.js.map +1 -0
  197. package/dist/responses/validatedInput.d.ts +3 -0
  198. package/dist/responses/validatedInput.d.ts.map +1 -0
  199. package/dist/responses/validatedInput.js +43 -0
  200. package/dist/responses/validatedInput.js.map +1 -0
  201. package/dist/responses/waitFor.d.ts +37 -0
  202. package/dist/responses/waitFor.d.ts.map +1 -0
  203. package/dist/responses/waitFor.js +47 -0
  204. package/dist/responses/waitFor.js.map +1 -0
  205. package/dist/rpc/contract.d.ts +125 -2
  206. package/dist/rpc/contract.d.ts.map +1 -1
  207. package/dist/rpc/contract.js +3 -2
  208. package/dist/rpc/contract.js.map +1 -1
  209. package/dist/rpc/responseContract.d.ts +260 -0
  210. package/dist/rpc/responseContract.d.ts.map +1 -0
  211. package/dist/rpc/responseContract.js +37 -0
  212. package/dist/rpc/responseContract.js.map +1 -0
  213. package/dist/rpc/server.d.ts +3 -1
  214. package/dist/rpc/server.d.ts.map +1 -1
  215. package/dist/rpc/server.js +6 -1
  216. package/dist/rpc/server.js.map +1 -1
  217. package/dist/serve-cli.d.ts +3 -0
  218. package/dist/serve-cli.d.ts.map +1 -0
  219. package/dist/serve-cli.js +20 -0
  220. package/dist/serve-cli.js.map +1 -0
  221. package/package.json +3 -3
  222. package/src/board.ts +16 -2
  223. package/src/browser/client.ts +30 -4
  224. package/src/browser/entry.ts +9 -0
  225. package/src/browser/responses/Prompt.ts +6 -0
  226. package/src/browser/responses/answerBody.ts +11 -0
  227. package/src/browser/responses/draftSession.ts +101 -0
  228. package/src/browser/responses/drafts.ts +82 -0
  229. package/src/browser/responses/preview.ts +7 -0
  230. package/src/browser/responses/questionnaireControls.ts +74 -0
  231. package/src/browser/responses/schema.ts +63 -0
  232. package/src/browser/responses/setup.ts +132 -0
  233. package/src/browser/responses/submissionFields.ts +15 -0
  234. package/src/browser/responses/submit.ts +33 -0
  235. package/src/cli.ts +14 -22
  236. package/src/files/changes.ts +2 -1
  237. package/src/http/respond.ts +2 -0
  238. package/src/metadata/backlinks.ts +5 -0
  239. package/src/metadata/problems.ts +3 -0
  240. package/src/metadata/schema.ts +6 -2
  241. package/src/page/attentionDetails.ts +2 -1
  242. package/src/page/metadataDetails.ts +6 -0
  243. package/src/page/navigationScript.ts +1 -1
  244. package/src/page/shell.ts +1 -1
  245. package/src/page/style.ts +3 -1
  246. package/src/page/swap.ts +3 -0
  247. package/src/path/Identity.ts +3 -0
  248. package/src/render/markdown.ts +5 -2
  249. package/src/render/questionnaireDocuments.ts +12 -0
  250. package/src/render/visualDocuments.ts +3 -0
  251. package/src/responses/agent-cli.ts +35 -0
  252. package/src/responses/agent.ts +59 -0
  253. package/src/responses/await.ts +21 -0
  254. package/src/responses/composerHtml.ts +28 -0
  255. package/src/responses/flags.ts +22 -0
  256. package/src/responses/history.ts +18 -0
  257. package/src/responses/locate.ts +60 -0
  258. package/src/responses/make.ts +17 -0
  259. package/src/responses/outcome.ts +15 -0
  260. package/src/responses/page.ts +30 -0
  261. package/src/responses/publish.ts +53 -0
  262. package/src/responses/publishIn.ts +64 -0
  263. package/src/responses/read.ts +45 -0
  264. package/src/responses/records.ts +55 -0
  265. package/src/responses/retry.ts +23 -0
  266. package/src/responses/service.ts +117 -0
  267. package/src/responses/template.ts +72 -0
  268. package/src/responses/templateHtml.ts +26 -0
  269. package/src/responses/templateLinks.ts +43 -0
  270. package/src/responses/templateNodes.ts +55 -0
  271. package/src/responses/validateSupersedes.ts +24 -0
  272. package/src/responses/validatedInput.ts +45 -0
  273. package/src/responses/waitFor.ts +58 -0
  274. package/src/rpc/contract.ts +3 -2
  275. package/src/rpc/responseContract.ts +46 -0
  276. package/src/rpc/server.ts +8 -1
  277. package/src/serve-cli.ts +37 -0
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/rpc/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/rpc/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export declare function runServe(): void;
3
+ //# sourceMappingURL=serve-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve-cli.d.ts","sourceRoot":"","sources":["../src/serve-cli.ts"],"names":[],"mappings":";AAkCA,wBAAgB,QAAQ,SAEvB"}
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ import { NodeRuntime, NodeServices } from "@effect/platform-node";
3
+ import { Effect, Layer, Option, Path } from "effect";
4
+ import { Argument, Command, Flag } from "effect/unstable/cli";
5
+ import { HOST, serveBoard } from "./serve.js";
6
+ const VERSION = "0.9.0";
7
+ const serve = Effect.fn("WorkBoard.serve")(function* (input) {
8
+ const path = yield* Path.Path;
9
+ return yield* Layer.launch(serveBoard({ home: Option.getOrUndefined(input.home), port: input.port, responses: input.responses, root: path.resolve(input.dir) }));
10
+ });
11
+ const workBoard = Command.make("work-board", {
12
+ dir: Argument.directory("dir", { mustExist: true }).pipe(Argument.withDescription("The folder of markdown files to serve")),
13
+ home: Flag.string("home").pipe(Flag.optional, Flag.withDescription("The file shown as a board at /, relative to the folder")),
14
+ port: Flag.integer("port").pipe(Flag.withDefault(4747), Flag.withDescription(`The port to listen on at ${HOST}`)),
15
+ responses: Flag.boolean("responses").pipe(Flag.withDefault(false), Flag.withDescription("Explicitly enable local durable question and response writes (Linux)")),
16
+ }, serve).pipe(Command.withDescription("Serve a folder of markdown files as a live page that updates in place when a file changes."));
17
+ export function runServe() {
18
+ Command.run(workBoard, { version: VERSION }).pipe(Effect.provide(NodeServices.layer), NodeRuntime.runMain);
19
+ }
20
+ //# sourceMappingURL=serve-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve-cli.js","sourceRoot":"","sources":["../src/serve-cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,OAAO,GAAG,OAAO,CAAC;AAExB,MAAM,KAAK,GAAG,MAAM,CAAC,EAAE,CAAC,iBAAiB,CAAC,CAAC,QAAQ,CAAC,EAAE,KAKrD;IACA,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;IAC9B,OAAO,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,CACzB,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CACpI,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAC7B,YAAY,EACZ;IACC,GAAG,EAAE,QAAQ,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,uCAAuC,CAAC,CAAC;IAC3H,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,eAAe,CAAC,wDAAwD,CAAC,CAAC;IAC7H,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,eAAe,CAAC,4BAA4B,IAAI,EAAE,CAAC,CAAC;IACjH,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,IAAI,CACxC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EACvB,IAAI,CAAC,eAAe,CAAC,sEAAsE,CAAC,CAC5F;CACD,EACD,KAAK,CACL,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,4FAA4F,CAAC,CAAC,CAAC;AAE9H,MAAM,UAAU,QAAQ;IACvB,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;AAC5G,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shivaedev/work-board",
3
- "version": "0.7.2",
3
+ "version": "0.9.0",
4
4
  "description": "Serve a folder of markdown files as a live local page that updates in place when a file changes",
5
5
  "homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/work-board#readme",
6
6
  "bugs": {
@@ -63,7 +63,7 @@
63
63
  "devDependencies": {
64
64
  "@effect/platform-node": "4.0.0-rc.112",
65
65
  "@effect/platform-node-shared": "4.0.0-rc.112",
66
- "@shivaedev/quality": "0.16.0",
66
+ "@shivaedev/quality": "0.17.0",
67
67
  "@types/hast": "3.0.5",
68
68
  "@types/mdast": "4.0.4",
69
69
  "@types/node": "24.10.1",
@@ -90,7 +90,7 @@
90
90
  },
91
91
  "scripts": {
92
92
  "build": "node --eval \"import('node:fs').then(({ rmSync }) => rmSync('dist', { force: true, recursive: true }))\" && tsc6 --project tsconfig.build.json && pnpm build:browser",
93
- "build:browser": "esbuild src/browser/session.ts --bundle --platform=browser --format=esm --conditions=source --minify --outfile=assets/native.js",
93
+ "build:browser": "esbuild src/browser/entry.ts --bundle --platform=browser --format=esm --conditions=source --minify --outfile=assets/native.js && esbuild src/responses/agent-cli.ts --bundle --platform=node --format=esm --conditions=source --minify --outfile=assets/agent.js",
94
94
  "ready": "pnpm typecheck && pnpm test && pnpm build",
95
95
  "test": "pnpm test:prepare && vitest run",
96
96
  "test:prepare": "pnpm build:browser",
package/src/board.ts CHANGED
@@ -17,11 +17,14 @@ import { start } from "#http/start.ts";
17
17
  import { work } from "#http/work.ts";
18
18
  import type { RenderFailed } from "#render/failed.ts";
19
19
  import { Highlighter } from "#render/highlighter.ts";
20
+ import { responsePage } from "#responses/page.ts";
21
+ import { responseService } from "#responses/service.ts";
20
22
  import { server } from "#rpc/server.ts";
21
23
  import { searchSnapshot } from "#search/snapshot.ts";
22
24
 
23
25
  export interface BoardOptions {
24
26
  readonly home?: string | undefined;
27
+ readonly responses?: boolean;
25
28
  readonly root: string;
26
29
  }
27
30
 
@@ -59,6 +62,14 @@ const routes = (options: BoardOptions) =>
59
62
  }
60
63
  yield* serve(MERMAID_ROUTE, mermaidFile(mermaid));
61
64
  yield* serve(NATIVE_ROUTE, nativeAsset);
65
+ const responses = yield* responseService({
66
+ changes,
67
+ enabled: options.responses === true,
68
+ index: Effect.provideContext(index, context),
69
+ root: options.root,
70
+ });
71
+ const replyPage = responsePage(responses, changes, home, options.responses === true);
72
+ yield* serve("/_board/respond", replyPage);
62
73
  const pages = page({ home, root: options.root }, changes, index);
63
74
  const items = identity(index, pages, changes, home);
64
75
  yield* serve("/_board/item/*", items);
@@ -67,6 +78,9 @@ const routes = (options: BoardOptions) =>
67
78
  const pathname = new URL(url, "http://127.0.0.1").pathname;
68
79
  let selected: ReturnType<typeof pages>;
69
80
  switch (pathname) {
81
+ case "/_board/respond":
82
+ selected = replyPage(request);
83
+ break;
70
84
  case "/_board/start":
71
85
  selected = startPage();
72
86
  break;
@@ -84,11 +98,11 @@ const routes = (options: BoardOptions) =>
84
98
  }
85
99
  return Effect.provideContext(selected, context);
86
100
  }
87
- const rpc = yield* server({ changes, home, index: Effect.provideContext(index, context), page: readPage });
101
+ const rpc = yield* server({ changes, home, index: Effect.provideContext(index, context), page: readPage, responses });
88
102
  yield* serve(
89
103
  "/_board/rpc",
90
104
  (request) =>
91
- sameOrigin(request)
105
+ sameOrigin(request) && (options.responses !== true || request.headers["content-type"]?.split(";")[0] === "application/ndjson")
92
106
  ? rpc.pipe(Effect.provideService(HttpServerRequest.HttpServerRequest, request))
93
107
  : Effect.succeed(respond("Local same-origin requests only", "text/plain", 403)),
94
108
  "POST",
@@ -4,18 +4,34 @@ import type * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
4
4
  import type * as Atom from "effect/unstable/reactivity/Atom";
5
5
  import * as AtomRegistry from "effect/unstable/reactivity/AtomRegistry";
6
6
  import * as AtomRpc from "effect/unstable/reactivity/AtomRpc";
7
+ import type * as Reactivity from "effect/unstable/reactivity/Reactivity";
7
8
  import { RpcClient, RpcSerialization } from "effect/unstable/rpc";
8
- import { bind } from "@shivaedev/effect-contract/bind.ts";
9
+ import type { RpcClientError } from "effect/unstable/rpc/RpcClientError";
10
+ import { type Bound, bind } from "@shivaedev/effect-contract/bind.ts";
11
+ import type { Contract } from "@shivaedev/effect-contract/contract.ts";
9
12
  import { live } from "@shivaedev/effect-contract/live.ts";
10
13
  import { type ResumeOptions, resumeSignal } from "@shivaedev/effect-contract/resume.ts";
11
- import { workContract, workRpcs } from "#rpc/contract.ts";
14
+ import { type ReadFailed, workContract, workRpcs } from "#rpc/contract.ts";
12
15
  import { workspace } from "#rpc/keys.ts";
16
+ import { responseContract } from "#rpc/responseContract.ts";
13
17
 
14
18
  export interface Reader {
15
19
  readonly reader: unique symbol;
16
20
  }
17
21
 
18
- export function browserClient(url: string, options: ResumeOptions = {}) {
22
+ type BoundContract<TContract> =
23
+ TContract extends Contract<infer TName, infer TQueries, infer TCommands, infer TRpcs> ? Bound<TName, TQueries, TCommands, TRpcs, Reader> : never;
24
+ export interface BrowserClient {
25
+ readonly api: BoundContract<typeof workContract>;
26
+ readonly mutate: <A, E>(effect: Effect.Effect<A, E, Reader | Reactivity.Reactivity>, signal?: AbortSignal) => Promise<A>;
27
+ readonly read: <A, E>(atom: Atom.Atom<AsyncResult.AsyncResult<A, E>>, signal?: AbortSignal) => Promise<A>;
28
+ readonly registry: AtomRegistry.AtomRegistry;
29
+ readonly responses: BoundContract<typeof responseContract>;
30
+ readonly run: <A, E>(effect: Effect.Effect<A, E, Reader>, signal?: AbortSignal) => Promise<A>;
31
+ readonly updates: ReturnType<typeof live<Reader, ReadFailed | RpcClientError, never>>;
32
+ }
33
+
34
+ export function browserClient(url: string, options: ResumeOptions = {}): BrowserClient {
19
35
  class Client extends AtomRpc.Service<Reader>()("WorkBoard/Reader", {
20
36
  group: workRpcs,
21
37
  protocol: RpcClient.layerProtocolHttp({ url: `${url}/_board/rpc` }).pipe(Layer.provide([FetchHttpClient.layer, RpcSerialization.layerNdjson])),
@@ -55,5 +71,15 @@ export function browserClient(url: string, options: ResumeOptions = {}) {
55
71
  throw error;
56
72
  }
57
73
  }
58
- return { api, read, registry, run, updates };
74
+ async function mutate<A, E>(effect: Effect.Effect<A, E, Reader | Reactivity.Reactivity>, signal?: AbortSignal) {
75
+ const result = await Effect.runPromise(Effect.result(AtomRegistry.getResult(registry, Client.runtime.atom(effect), { suspendOnWaiting: true })), {
76
+ signal,
77
+ });
78
+ if (result._tag === "Failure") {
79
+ throw result.failure;
80
+ }
81
+ return result.success;
82
+ }
83
+ const responses = bind(responseContract, Client);
84
+ return { api, mutate, read, registry, responses, run, updates };
59
85
  }
@@ -0,0 +1,9 @@
1
+ import { setupResponses } from "#browser/responses/setup.ts";
2
+
3
+ if (typeof window !== "undefined" && typeof document !== "undefined") {
4
+ setupResponses();
5
+ }
6
+
7
+ import { session as ownedSession } from "./session.ts";
8
+
9
+ export const session = ownedSession;
@@ -0,0 +1,6 @@
1
+ export interface Prompt {
2
+ readonly id: string;
3
+ readonly mode: "one" | "many" | "text";
4
+ readonly options: readonly { readonly id: string; readonly source: string }[];
5
+ readonly source: string;
6
+ }
@@ -0,0 +1,11 @@
1
+ import type { Prompt } from "./Prompt.ts";
2
+ import type { PromptAnswer } from "./schema.ts";
3
+
4
+ export function answerBody(prompts: readonly Prompt[], answers: readonly PromptAnswer[], note: string) {
5
+ const sections = prompts.map((prompt, index) => {
6
+ const answer = answers[index];
7
+ const chosen = prompt.options.filter((option) => answer?.selected.includes(option.id));
8
+ return `## Question: ${prompt.id}\n\n${prompt.source}\n\nSelected: ${chosen.map((option) => option.id).join(", ") || "No supplied option"}\n\n${chosen.map((option) => option.source).join("\n\n")}\n\n${answer?.text.trim() ? `Additional text:\n\n${answer.text}` : ""}`;
9
+ });
10
+ return `# Recorded response\n\n${sections.join("\n\n")}\n\n${note.trim() ? `## Overall response\n\n${note}` : ""}`.trim();
11
+ }
@@ -0,0 +1,101 @@
1
+ import { Clock, Effect } from "effect";
2
+ import { DRAFT_AGE, type Draft, encodeDrafts, readDrafts } from "./drafts.ts";
3
+
4
+ const states = new Map<string, Draft>();
5
+ export function draftSession(key: string, revision: string, storage: Storage | undefined, storageKey: string, report: (message: string) => void) {
6
+ const stateKey = `${JSON.stringify(storageKey)}:${key}`;
7
+ function local() {
8
+ if (!storage) {
9
+ throw new Error("Draft storage unavailable");
10
+ }
11
+ return storage;
12
+ }
13
+ let blocked = false;
14
+ let drafts: Record<string, Draft> = {};
15
+ try {
16
+ const stored = readDrafts(local().getItem(storageKey), Effect.runSync(Clock.currentTimeMillis));
17
+ drafts = stored.drafts;
18
+ if (stored.expired) {
19
+ report("Expired drafts were cleared after 30 days. Saved responses are unaffected.");
20
+ }
21
+ local().setItem(storageKey, encodeDrafts(drafts));
22
+ } catch {
23
+ blocked = true;
24
+ report("Draft history is unavailable or malformed. Keep a copy; edits remain in this window.");
25
+ }
26
+ const memory = states.get(stateKey);
27
+ if (memory && Effect.runSync(Clock.currentTimeMillis) - memory.updatedAt >= DRAFT_AGE) {
28
+ states.delete(stateKey);
29
+ report("A window draft expired after 30 days. Saved responses are unaffected.");
30
+ }
31
+ let current = states.get(stateKey)
32
+ ?? drafts[key] ?? {
33
+ author: "",
34
+ body: "",
35
+ id: `response.${crypto.randomUUID()}`,
36
+ revision,
37
+ type: "answer",
38
+ updatedAt: Effect.runSync(Clock.currentTimeMillis),
39
+ };
40
+ if (current.revision !== revision) {
41
+ report("Source changed since this draft. Your text is retained; review the new source and preview to bind it to this revision.");
42
+ }
43
+ function update(patch: Partial<Draft>) {
44
+ current = { ...(states.get(stateKey) ?? current), ...patch, updatedAt: Effect.runSync(Clock.currentTimeMillis) };
45
+ states.set(stateKey, current);
46
+ try {
47
+ if (blocked) {
48
+ throw new Error("Unknown draft history");
49
+ }
50
+ const latest = readDrafts(local().getItem(storageKey), Effect.runSync(Clock.currentTimeMillis)).drafts;
51
+ local().setItem(storageKey, encodeDrafts({ ...latest, [key]: current }));
52
+ } catch {
53
+ report("Draft could not be persisted (storage full or unavailable). Keep a copy; your text remains in this window.");
54
+ }
55
+ return current;
56
+ }
57
+ function clear() {
58
+ try {
59
+ local().removeItem(storageKey);
60
+ blocked = false;
61
+ for (const storedKey of states.keys()) {
62
+ if (storedKey.startsWith(`${JSON.stringify(storageKey)}:`)) {
63
+ states.delete(storedKey);
64
+ }
65
+ }
66
+ report("Workspace drafts cleared. Saved responses are unchanged; this visible text remains until you navigate away.");
67
+ } catch {
68
+ report("Draft storage could not be cleared.");
69
+ }
70
+ }
71
+ function saved(pending: Draft) {
72
+ if (states.get(stateKey) !== pending) {
73
+ const live = states.get(stateKey);
74
+ if (live?.id === pending.id) {
75
+ update({ id: `response.${crypto.randomUUID()}` });
76
+ }
77
+ return false;
78
+ }
79
+ states.delete(stateKey);
80
+ try {
81
+ const latest = readDrafts(local().getItem(storageKey), Effect.runSync(Clock.currentTimeMillis)).drafts;
82
+ if (latest[key]?.id === pending.id) {
83
+ delete latest[key];
84
+ }
85
+ local().setItem(storageKey, encodeDrafts(latest));
86
+ } catch {
87
+ report("Saved response confirmed, but its browser draft could not be cleared.");
88
+ }
89
+ current = {
90
+ ...current,
91
+ answers: undefined,
92
+ body: "",
93
+ id: `response.${crypto.randomUUID()}`,
94
+ recovery: undefined,
95
+ supersedes: undefined,
96
+ updatedAt: Effect.runSync(Clock.currentTimeMillis),
97
+ };
98
+ return true;
99
+ }
100
+ return { clear, current: () => current, saved, update };
101
+ }
@@ -0,0 +1,82 @@
1
+ import { Schema } from "effect";
2
+ import { Identity } from "#path/Identity.ts";
3
+ import { Answers, type PromptAnswer } from "./schema.ts";
4
+
5
+ export const DRAFT_BYTES = 2 * 1024 * 1024;
6
+ export const DRAFT_AGE = 30 * 24 * 60 * 60 * 1000;
7
+ export interface Draft {
8
+ readonly answers?: readonly PromptAnswer[] | undefined;
9
+ readonly author: string;
10
+ readonly body: string;
11
+ readonly id: string;
12
+ readonly question?: string | undefined;
13
+ readonly recovery?: string | undefined;
14
+ readonly revision: string;
15
+ readonly supersedes?: string | undefined;
16
+ readonly type: string;
17
+ readonly updatedAt: number;
18
+ }
19
+ function extraFields(entry: object) {
20
+ return {
21
+ ...("question" in entry ? { question: Schema.decodeUnknownSync(Identity)(entry.question) } : {}),
22
+ ...("recovery" in entry ? { recovery: Schema.decodeUnknownSync(Schema.String)(entry.recovery) } : {}),
23
+ ...("answers" in entry ? { answers: Schema.decodeUnknownSync(Answers, { onExcessProperty: "error" })(entry.answers) } : {}),
24
+ ...("supersedes" in entry ? { supersedes: Schema.decodeUnknownSync(Identity)(entry.supersedes) } : {}),
25
+ };
26
+ }
27
+ export function readDrafts(raw: string | null, now: number): { drafts: Record<string, Draft>; expired: boolean } {
28
+ if (raw === null) {
29
+ return { drafts: {}, expired: false };
30
+ }
31
+ if (new TextEncoder().encode(raw).length > DRAFT_BYTES) {
32
+ throw new Error("Draft storage exceeds the workspace limit.");
33
+ }
34
+ const value: unknown = JSON.parse(raw);
35
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
36
+ throw new Error("Draft storage is malformed.");
37
+ }
38
+ const drafts: Record<string, Draft> = {};
39
+ let expired = false;
40
+ for (const [key, entry] of Object.entries(value)) {
41
+ if (
42
+ typeof entry !== "object"
43
+ || entry === null
44
+ || !("author" in entry)
45
+ || !("body" in entry)
46
+ || !("id" in entry)
47
+ || !("revision" in entry)
48
+ || !("type" in entry)
49
+ || !("updatedAt" in entry)
50
+ || typeof entry.author !== "string"
51
+ || typeof entry.body !== "string"
52
+ || typeof entry.id !== "string"
53
+ || typeof entry.revision !== "string"
54
+ || (entry.type !== "answer" && entry.type !== "clarify" && entry.type !== "not_now")
55
+ || typeof entry.updatedAt !== "number"
56
+ || !Number.isFinite(entry.updatedAt)
57
+ ) {
58
+ throw new Error("Draft storage is malformed.");
59
+ }
60
+ if (entry.updatedAt > now || now - entry.updatedAt >= DRAFT_AGE) {
61
+ expired = true;
62
+ continue;
63
+ }
64
+ drafts[key] = {
65
+ ...extraFields(entry),
66
+ author: entry.author,
67
+ body: entry.body,
68
+ id: entry.id,
69
+ revision: entry.revision,
70
+ type: entry.type,
71
+ updatedAt: entry.updatedAt,
72
+ };
73
+ }
74
+ return { drafts, expired };
75
+ }
76
+ export function encodeDrafts(drafts: Readonly<Record<string, Draft>>): string {
77
+ const value = JSON.stringify(drafts);
78
+ if (new TextEncoder().encode(value).length > DRAFT_BYTES) {
79
+ throw new Error("Draft storage is full. Copy this draft before clearing workspace drafts.");
80
+ }
81
+ return value;
82
+ }
@@ -0,0 +1,7 @@
1
+ import { answerBody } from "./answerBody.ts";
2
+ import type { Draft } from "./drafts.ts";
3
+ import type { Prompt } from "./Prompt.ts";
4
+
5
+ export function responsePreview(draft: Draft, question: string, prompts: readonly Prompt[] = []): string {
6
+ return `New file: responses/${draft.id}.md\nRegistration (if needed): responses/${question}.md\nRecorded time is assigned by the server when saving.\n\n---\n${JSON.stringify({ id: draft.id, kind: "response", response: { answers: draft.answers, author: draft.author, question, reviewedRevision: draft.revision, supersedes: draft.supersedes, type: draft.type } }, null, 2)}\n---\n${draft.answers ? answerBody(prompts, draft.answers, draft.body) : draft.body}`;
7
+ }
@@ -0,0 +1,74 @@
1
+ import type { Draft } from "./drafts.ts";
2
+ import type { Prompt } from "./Prompt.ts";
3
+ import type { PromptAnswer } from "./schema.ts";
4
+
5
+ function restoreAnswers(fields: readonly HTMLFieldSetElement[], draft: Draft, same: boolean) {
6
+ for (const field of fields) {
7
+ const answer = draft.answers?.find((entry) => entry.prompt === field.dataset.prompt);
8
+ for (const input of field.querySelectorAll<HTMLInputElement>("input")) {
9
+ input.checked = same && (answer?.selected.includes(input.value) ?? false);
10
+ }
11
+ const text = field.querySelector<HTMLTextAreaElement>("textarea");
12
+ if (text) {
13
+ text.value = answer?.text ?? "";
14
+ }
15
+ }
16
+ }
17
+ export function questionnaireControls(form: HTMLFormElement) {
18
+ function modeOf(field: HTMLFieldSetElement) {
19
+ if (field.dataset.mode === "text") {
20
+ return "text";
21
+ }
22
+ return field.dataset.mode === "many" ? "many" : "one";
23
+ }
24
+ const fields = [...form.querySelectorAll<HTMLFieldSetElement>("fieldset[data-prompt]")];
25
+ const models: Prompt[] = fields.map((field) => ({
26
+ id: field.dataset.prompt ?? "",
27
+ mode: modeOf(field),
28
+ options: [...field.querySelectorAll<HTMLInputElement>("input[data-option-source]")].map((input) => ({
29
+ id: input.value,
30
+ source: input.dataset.optionSource ?? "",
31
+ })),
32
+ source: field.dataset.source ?? "",
33
+ }));
34
+ function read(): readonly PromptAnswer[] | undefined {
35
+ return fields.length === 0
36
+ ? undefined
37
+ : fields.map((field) => ({
38
+ prompt: field.dataset.prompt ?? "",
39
+ selected: [...field.querySelectorAll<HTMLInputElement>("input:checked")].map((input) => input.value),
40
+ text: field.querySelector<HTMLTextAreaElement>("textarea")?.value ?? "",
41
+ }));
42
+ }
43
+ function restore(draft: Draft, revision: string) {
44
+ const same = draft.revision === revision && (draft.question === undefined || draft.question === form.dataset.question);
45
+ restoreAnswers(fields, draft, same);
46
+ const recovery = form.querySelector<HTMLPreElement>("#response-draft-recovery");
47
+ const retained =
48
+ !same && draft.answers
49
+ ? `Earlier template answers (selections cleared; review before choosing again):\n${JSON.stringify(draft.answers, null, 2)}\n${draft.recovery ?? ""}`
50
+ : draft.recovery;
51
+ if (recovery) {
52
+ recovery.textContent = retained ?? "";
53
+ recovery.hidden = !retained;
54
+ }
55
+ return retained;
56
+ }
57
+ function validate(type: string) {
58
+ for (const field of fields) {
59
+ const text = field.querySelector<HTMLTextAreaElement>("textarea");
60
+ text?.setCustomValidity(
61
+ type === "answer" && !text.value.trim() && !field.querySelector("input:checked") ? "Choose an option or answer in your own words." : "",
62
+ );
63
+ }
64
+ }
65
+ for (const field of fields) {
66
+ field.querySelector("[data-clear-choices]")?.addEventListener("click", () => {
67
+ for (const input of field.querySelectorAll<HTMLInputElement>("input")) {
68
+ input.checked = false;
69
+ }
70
+ form.dispatchEvent(new Event("input", { bubbles: true }));
71
+ });
72
+ }
73
+ return { models, read, restore, validate };
74
+ }
@@ -0,0 +1,63 @@
1
+ import { Schema } from "effect";
2
+ import { Identity } from "#path/Identity.ts";
3
+
4
+ export const Revision = Schema.String.check(Schema.isPattern(/^[a-f\d]{64}$/u));
5
+ export const ResponseKind = Schema.Literals(["answer", "clarify", "not_now"]);
6
+ export const PromptAnswer = Schema.Struct({
7
+ prompt: Identity.check(Schema.isMaxLength(128)),
8
+ selected: Schema.Array(Identity.check(Schema.isMaxLength(128))).check(Schema.isMaxLength(32)),
9
+ text: Schema.String.check(Schema.isMaxLength(8192)),
10
+ });
11
+ export const Answers = Schema.Array(PromptAnswer).check(Schema.isMaxLength(32));
12
+ export type PromptAnswer = typeof PromptAnswer.Type;
13
+ export const QuestionRecord = Schema.Struct({
14
+ deadline: Schema.Number.check(Schema.isFinite(), Schema.isGreaterThanOrEqualTo(0), Schema.isLessThanOrEqualTo(8_640_000_000_000_000)),
15
+ item: Identity,
16
+ reason: Schema.String,
17
+ registeredAt: Schema.Number.check(Schema.isFinite(), Schema.isGreaterThanOrEqualTo(0), Schema.isLessThanOrEqualTo(8_640_000_000_000_000)),
18
+ request: Identity,
19
+ reviewedRevision: Revision,
20
+ source: Schema.String,
21
+ });
22
+ export const ResponseRecord = Schema.Struct({
23
+ answers: Schema.optional(Answers),
24
+ author: Schema.String.check(Schema.isPattern(/\S/u), Schema.isMaxLength(200)),
25
+ question: Identity,
26
+ recordedAt: Schema.Number.check(Schema.isFinite(), Schema.isGreaterThanOrEqualTo(0), Schema.isLessThanOrEqualTo(8_640_000_000_000_000)),
27
+ reviewedRevision: Revision,
28
+ supersedes: Schema.optional(Identity),
29
+ type: ResponseKind,
30
+ });
31
+ export const QuestionPreview = Schema.Struct({
32
+ context: Schema.String,
33
+ id: Identity,
34
+ item: Identity,
35
+ reason: Schema.String,
36
+ request: Identity,
37
+ reviewedRevision: Revision,
38
+ source: Schema.String,
39
+ });
40
+ export type QuestionPreview = typeof QuestionPreview.Type;
41
+ export const Question = Schema.Struct({ context: Schema.String, id: Identity, question: QuestionRecord });
42
+ export const RecordedResponse = Schema.Struct({ body: Schema.String, id: Identity, response: ResponseRecord });
43
+ export const Reading = Schema.Struct({ question: Question, responses: Schema.Array(RecordedResponse) });
44
+ export const WaitReading = Schema.Struct({ expired: Schema.Boolean, question: Question, response: Schema.optional(RecordedResponse) });
45
+ export type WaitReading = typeof WaitReading.Type;
46
+ export class ResponseFailed extends Schema.TaggedError<ResponseFailed>()("ResponseFailed", {
47
+ cause: Schema.optional(Schema.Defect()),
48
+ code: Schema.Literals(["Disabled", "Missing", "Stale", "Conflict", "Unavailable", "Unsupported", "Uncertain", "Unanswered"]),
49
+ message: Schema.String,
50
+ }) {}
51
+ export const DraftInput = Schema.Struct({
52
+ answers: Schema.optional(Answers),
53
+ author: ResponseRecord.fields.author,
54
+ body: Schema.String.check(Schema.isMaxLength(32_768)),
55
+ id: Identity,
56
+ question: Identity,
57
+ supersedes: Schema.optional(Identity),
58
+ type: ResponseKind,
59
+ });
60
+ export type Question = typeof Question.Type;
61
+ export type RecordedResponse = typeof RecordedResponse.Type;
62
+ export type Reading = typeof Reading.Type;
63
+ export type DraftInput = typeof DraftInput.Type;