@nickmeriano/task 0.10.0 → 0.12.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 (101) hide show
  1. package/README.md +232 -1
  2. package/dist/asks.test.js +6 -0
  3. package/dist/asks.test.js.map +1 -1
  4. package/dist/board/github.d.ts +68 -0
  5. package/dist/board/github.d.ts.map +1 -0
  6. package/dist/board/github.js +112 -0
  7. package/dist/board/github.js.map +1 -0
  8. package/dist/board/handler.d.ts +50 -0
  9. package/dist/board/handler.d.ts.map +1 -0
  10. package/dist/board/handler.js +183 -0
  11. package/dist/board/handler.js.map +1 -0
  12. package/dist/board/handler.test.d.ts +11 -0
  13. package/dist/board/handler.test.d.ts.map +1 -0
  14. package/dist/board/handler.test.js +229 -0
  15. package/dist/board/handler.test.js.map +1 -0
  16. package/dist/board/pages-function.d.ts +23 -0
  17. package/dist/board/pages-function.d.ts.map +1 -0
  18. package/dist/board/pages-function.js +34 -0
  19. package/dist/board/pages-function.js.map +1 -0
  20. package/dist/board/source.d.ts +118 -0
  21. package/dist/board/source.d.ts.map +1 -0
  22. package/dist/board/source.js +333 -0
  23. package/dist/board/source.js.map +1 -0
  24. package/dist/board/source.test.d.ts +12 -0
  25. package/dist/board/source.test.d.ts.map +1 -0
  26. package/dist/board/source.test.js +165 -0
  27. package/dist/board/source.test.js.map +1 -0
  28. package/dist/board/tar.d.ts +28 -0
  29. package/dist/board/tar.d.ts.map +1 -0
  30. package/dist/board/tar.js +188 -0
  31. package/dist/board/tar.js.map +1 -0
  32. package/dist/board/tar.test.d.ts +9 -0
  33. package/dist/board/tar.test.d.ts.map +1 -0
  34. package/dist/board/tar.test.js +110 -0
  35. package/dist/board/tar.test.js.map +1 -0
  36. package/dist/claim-io.d.ts.map +1 -1
  37. package/dist/claim-io.js +4 -3
  38. package/dist/claim-io.js.map +1 -1
  39. package/dist/claim.d.ts +14 -2
  40. package/dist/claim.d.ts.map +1 -1
  41. package/dist/claim.js +9 -3
  42. package/dist/claim.js.map +1 -1
  43. package/dist/claim.test.js +41 -1
  44. package/dist/claim.test.js.map +1 -1
  45. package/dist/cli.js +127 -7
  46. package/dist/cli.js.map +1 -1
  47. package/dist/export.d.ts +163 -0
  48. package/dist/export.d.ts.map +1 -0
  49. package/dist/export.js +259 -0
  50. package/dist/export.js.map +1 -0
  51. package/dist/export.test.d.ts +12 -0
  52. package/dist/export.test.d.ts.map +1 -0
  53. package/dist/export.test.js +264 -0
  54. package/dist/export.test.js.map +1 -0
  55. package/dist/functions/board.js +709 -0
  56. package/dist/git-serve.d.ts +14 -1
  57. package/dist/git-serve.d.ts.map +1 -1
  58. package/dist/git-serve.js +30 -2
  59. package/dist/git-serve.js.map +1 -1
  60. package/dist/git-serve.test.d.ts +1 -0
  61. package/dist/git-serve.test.d.ts.map +1 -1
  62. package/dist/git-serve.test.js +36 -1
  63. package/dist/git-serve.test.js.map +1 -1
  64. package/dist/index.d.ts +2 -0
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +6 -0
  67. package/dist/index.js.map +1 -1
  68. package/dist/server.d.ts +6 -0
  69. package/dist/server.d.ts.map +1 -1
  70. package/dist/server.js +12 -3
  71. package/dist/server.js.map +1 -1
  72. package/dist/trailers.d.ts +51 -0
  73. package/dist/trailers.d.ts.map +1 -0
  74. package/dist/trailers.js +32 -0
  75. package/dist/trailers.js.map +1 -0
  76. package/package.json +3 -3
  77. package/skill/SKILL.md +16 -1
  78. package/src/asks.test.ts +15 -0
  79. package/src/board/github.ts +151 -0
  80. package/src/board/handler.test.ts +276 -0
  81. package/src/board/handler.ts +228 -0
  82. package/src/board/pages-function.ts +42 -0
  83. package/src/board/source.test.ts +203 -0
  84. package/src/board/source.ts +422 -0
  85. package/src/board/tar.test.ts +128 -0
  86. package/src/board/tar.ts +199 -0
  87. package/src/claim-io.ts +12 -3
  88. package/src/claim.test.ts +56 -1
  89. package/src/claim.ts +27 -4
  90. package/src/cli.ts +127 -7
  91. package/src/export.test.ts +361 -0
  92. package/src/export.ts +354 -0
  93. package/src/git-serve.test.ts +41 -1
  94. package/src/git-serve.ts +30 -2
  95. package/src/index.ts +10 -0
  96. package/src/server.ts +12 -3
  97. package/src/trailers.ts +65 -0
  98. package/ui/dist/assets/index-B8M_DaOt.js +229 -0
  99. package/ui/dist/assets/{index-CoKCUYic.css → index-eHsqltgs.css} +1 -1
  100. package/ui/dist/index.html +3 -3
  101. package/ui/dist/assets/index-BjsorZOU.js +0 -229
@@ -0,0 +1,183 @@
1
+ /**
2
+ * The live proxy: an exported board's API, as one fetch handler.
3
+ *
4
+ * `task export` ships the board as static files plus a baked `snapshot.json`.
5
+ * Drop this next to them — as a Cloudflare Pages Function, or behind any host
6
+ * that can run a `(Request) => Response` — hand it a token, and the same
7
+ * files become a live board: the SPA probes for an API before it mounts,
8
+ * finds this one answering, and never reads the snapshot at all. No token,
9
+ * no function, and the probe fails and the snapshot is what you get. The
10
+ * upgrade is the deploy, not a rebuild.
11
+ *
12
+ * The routes are `task serve`'s read routes — the same ones the snapshot
13
+ * answers (`ui/src/snapshot.ts`), plus the two things a live proxy is *for*:
14
+ * `?ref=` to read any branch, and `/branches` so the switcher has something to
15
+ * list. Payloads come from `source.ts`, which is what the hosted board serves,
16
+ * so a board rendered from this differs from the hosted one in one thing:
17
+ * who may look. This tier has no viewer auth — the perimeter is wherever you
18
+ * deploy it — and it says so in `/project` the only way the SPA reads:
19
+ * `readOnly: true`, no `git` capability.
20
+ *
21
+ * Read-only by construction: anything but GET is a 405 before a token is
22
+ * even resolved.
23
+ */
24
+ import { GitHubError, headCommit, isMissingRef, listBranches, repoInfo } from "./github.js";
25
+ import { BoardSourceError, SnapshotMemory, STATUSES, snapshotAt, } from "./source.js";
26
+ const REF_PATTERN = /^[\w.\-/]{1,255}$/;
27
+ /** `owner/name`, both halves as GitHub spells them. */
28
+ export const REPO_PATTERN = /^[\w.-]+\/[\w.-]+$/;
29
+ function json(body, status = 200) {
30
+ return new Response(JSON.stringify(body), {
31
+ status,
32
+ headers: {
33
+ "Content-Type": "application/json; charset=utf-8",
34
+ // Every answer is "the repository right now"; nothing between this
35
+ // function and the browser should hold on to one.
36
+ "Cache-Control": "no-store",
37
+ },
38
+ });
39
+ }
40
+ /** The board the request asked for, or the repo's default — `pickBoard`'s rule. */
41
+ function pickBoard(snapshot, requested) {
42
+ const id = requested && snapshot.data.has(requested) ? requested : snapshot.boards[0]?.id;
43
+ return id === undefined ? null : (snapshot.data.get(id) ?? null);
44
+ }
45
+ export function createBoardHandler(options) {
46
+ const { repo: slug, ref: defaultRef = "HEAD" } = options;
47
+ if (!REPO_PATTERN.test(slug))
48
+ throw new Error(`repo must be owner/name — got ${JSON.stringify(slug)}`);
49
+ const [owner, repo] = slug.split("/");
50
+ const basePath = (options.basePath ?? "/api").replace(/\/+$/, "");
51
+ const memory = new SnapshotMemory(options.maxCachedSnapshots);
52
+ // A cold start sees the SPA's four boot requests at once, all for the same
53
+ // commit. One tarball, not four: the first caller's load is shared with
54
+ // everyone who asks for that sha while it is still in flight.
55
+ const inFlight = new Map();
56
+ async function resolveToken() {
57
+ const token = typeof options.token === "function" ? await options.token() : options.token;
58
+ if (!token) {
59
+ // Unconfigured is not an error the board can act on; it is "no API",
60
+ // and a 5xx is exactly what makes the SPA's probe fall back to the
61
+ // snapshot sitting next to this function.
62
+ throw new BoardSourceError(503, "no GitHub token configured for the live board", "no-token");
63
+ }
64
+ return token;
65
+ }
66
+ async function load(token, ref, explicit) {
67
+ let commit;
68
+ try {
69
+ commit = await headCommit(token, owner, repo, ref);
70
+ }
71
+ catch (error) {
72
+ // With an explicit ref, a miss almost always means the branch — a
73
+ // deleted preview, a typo — not the repository.
74
+ if (explicit && isMissingRef(error)) {
75
+ throw new BoardSourceError(404, `no such branch or ref: ${ref}`, "no-such-ref");
76
+ }
77
+ throw error;
78
+ }
79
+ const key = `${owner}/${repo}@${commit.sha}`;
80
+ const cached = memory.get(key);
81
+ if (cached)
82
+ return cached;
83
+ let pending = inFlight.get(key);
84
+ if (!pending) {
85
+ pending = snapshotAt(token, owner, repo, commit, ref)
86
+ .then((snapshot) => memory.remember(key, snapshot))
87
+ .finally(() => inFlight.delete(key));
88
+ inFlight.set(key, pending);
89
+ }
90
+ return pending;
91
+ }
92
+ async function handle(request) {
93
+ if (request.method !== "GET" && request.method !== "HEAD") {
94
+ // Every editing affordance is already hidden by `readOnly`; this is the
95
+ // backstop, in the shape `ApiError` reads.
96
+ return json({ error: "this board is read-only — commit and push to change it", reason: "read-only" }, 405);
97
+ }
98
+ const url = new URL(request.url);
99
+ if (url.pathname !== basePath && !url.pathname.startsWith(`${basePath}/`)) {
100
+ return json({ error: `not an API path: ${url.pathname}` }, 404);
101
+ }
102
+ const route = url.pathname.slice(basePath.length) || "/";
103
+ const token = await resolveToken();
104
+ if (route === "/branches") {
105
+ // Default branch first (it's the way back), then alphabetical — the
106
+ // hosted board's order, which is the order the switcher expects.
107
+ const [names, info] = await Promise.all([
108
+ listBranches(token, owner, repo),
109
+ repoInfo(token, owner, repo),
110
+ ]);
111
+ const branches = names
112
+ .map((name) => ({ name, isDefault: name === info.defaultBranch }))
113
+ .sort((a, b) => Number(b.isDefault) - Number(a.isDefault) || a.name.localeCompare(b.name));
114
+ return json({ branches });
115
+ }
116
+ // A ref is a viewer-supplied string that lands in a URL path; anything
117
+ // outside git's own naming rules is ignored rather than escaped.
118
+ const requestedRef = url.searchParams.get("ref");
119
+ const ref = requestedRef && REF_PATTERN.test(requestedRef) && !requestedRef.includes("..")
120
+ ? requestedRef
121
+ : null;
122
+ const snapshot = await load(token, ref ?? defaultRef, ref !== null);
123
+ if (route === "/boards")
124
+ return json({ boards: snapshot.boards });
125
+ const board = pickBoard(snapshot, url.searchParams.get("board"));
126
+ if (!board)
127
+ return json({ error: "no such board" }, 404);
128
+ if (route === "/project") {
129
+ return json({
130
+ name: board.info.name,
131
+ prefix: board.info.prefix,
132
+ statuses: STATUSES,
133
+ // The payload the SPA reads to decide whether to offer editing at all.
134
+ // The data is a commit; the only way to change one is to push.
135
+ readOnly: true,
136
+ // Locally this is who would author a comment. Nothing here can
137
+ // comment, and nothing here knows who is looking.
138
+ author: "",
139
+ });
140
+ }
141
+ if (route === "/tasks")
142
+ return json({ tasks: board.tasks });
143
+ if (route === "/goals")
144
+ return json({ goals: board.goals });
145
+ const detail = /^\/tasks\/([^/]+)$/.exec(route);
146
+ if (detail) {
147
+ // The CLI accepts "PREFIX-x7k4m" or "x7k4m"; so does this.
148
+ const match = /^(?:[A-Za-z0-9]+-)?([A-Za-z0-9]+)$/.exec(decodeURIComponent(detail[1]));
149
+ if (!match)
150
+ return json({ error: "invalid task id" }, 400);
151
+ const key = match[1].toLowerCase();
152
+ const task = board.tasks.find((candidate) => candidate.key === key);
153
+ if (!task)
154
+ return json({ error: `no such task: ${key}` }, 404);
155
+ return json({ task, comments: board.comments.get(key) ?? [] });
156
+ }
157
+ // `/inbox` and the git surface are deliberately absent: the SPA degrades
158
+ // to no inbox and no git chrome when they 404, same as the hosted board.
159
+ return json({ error: `not here: ${route}` }, 404);
160
+ }
161
+ return async (request) => {
162
+ try {
163
+ return await handle(request);
164
+ }
165
+ catch (error) {
166
+ if (error instanceof BoardSourceError) {
167
+ return json({ error: error.message, reason: error.reason }, error.status);
168
+ }
169
+ if (error instanceof GitHubError) {
170
+ // A bad or expired token, a repo the token can't see, GitHub itself
171
+ // down: all of them are "the live board can't answer right now". A
172
+ // 5xx is deliberate — on the boot probe it is what sends the SPA to
173
+ // the snapshot instead of an error screen.
174
+ return json({
175
+ error: `GitHub answered ${error.status} — check the token and the repository name`,
176
+ reason: "github",
177
+ }, 502);
178
+ }
179
+ return json({ error: error instanceof Error ? error.message : "internal error" }, 500);
180
+ }
181
+ };
182
+ }
183
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/board/handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The live proxy, against a fake GitHub.
3
+ *
4
+ * `globalThis.fetch` is replaced with a tiny router that answers the four
5
+ * calls the handler makes — head commit, tarball, branches, repo info — from
6
+ * an in-memory repository, so what's under test is the handler's routing,
7
+ * caching and error shapes, not the network. The payloads themselves are
8
+ * pinned elsewhere (`source.test.ts` diffs them against the CLI's store).
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=handler.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.test.d.ts","sourceRoot":"","sources":["../../src/board/handler.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
@@ -0,0 +1,229 @@
1
+ /**
2
+ * The live proxy, against a fake GitHub.
3
+ *
4
+ * `globalThis.fetch` is replaced with a tiny router that answers the four
5
+ * calls the handler makes — head commit, tarball, branches, repo info — from
6
+ * an in-memory repository, so what's under test is the handler's routing,
7
+ * caching and error shapes, not the network. The payloads themselves are
8
+ * pinned elsewhere (`source.test.ts` diffs them against the CLI's store).
9
+ */
10
+ import assert from "node:assert/strict";
11
+ import { afterEach, beforeEach, mock, test } from "node:test";
12
+ import { gzipSync } from "node:zlib";
13
+ import { createBoardHandler } from "./handler.js";
14
+ const encoder = new TextEncoder();
15
+ /** Just enough tar to be a GitHub tarball: ustar headers, one root dir. */
16
+ function tarball(root, files) {
17
+ const blocks = [];
18
+ for (const [path, text] of Object.entries(files)) {
19
+ const data = encoder.encode(text);
20
+ const header = new Uint8Array(512);
21
+ header.set(encoder.encode(`${root}/${path}`).subarray(0, 100), 0);
22
+ header.set(encoder.encode("0000644\0"), 100);
23
+ header.set(encoder.encode(data.length.toString(8).padStart(11, "0") + "\0"), 124);
24
+ header.set(encoder.encode("00000000000\0"), 136);
25
+ header[156] = 0x30;
26
+ header.set(encoder.encode("ustar\x0000"), 257);
27
+ header.set(encoder.encode(" "), 148);
28
+ const sum = header.reduce((a, b) => a + b, 0);
29
+ header.set(encoder.encode(sum.toString(8).padStart(6, "0") + "\0 "), 148);
30
+ const padded = new Uint8Array(Math.ceil(data.length / 512) * 512);
31
+ padded.set(data);
32
+ blocks.push(header, padded);
33
+ }
34
+ blocks.push(new Uint8Array(1024));
35
+ const out = new Uint8Array(blocks.reduce((n, b) => n + b.length, 0));
36
+ let at = 0;
37
+ for (const block of blocks) {
38
+ out.set(block, at);
39
+ at += block.length;
40
+ }
41
+ return out;
42
+ }
43
+ const CONFIG = JSON.stringify({ name: "acme", prefix: "ACM", version: 2 });
44
+ const ticket = (title, status, position = 0) => `---\nstatus: ${status}\nposition: ${position}\ncreated: 2026-01-01T00:00:00.000Z\nupdated: 2026-01-01T00:00:00.000Z\n---\n\n# ${title}\n`;
45
+ /** Two commits: `main` at `aaa`, `feature` at `bbb`, each its own board state. */
46
+ const COMMITS = { HEAD: "aaa", main: "aaa", feature: "bbb" };
47
+ const TREES = {
48
+ aaa: {
49
+ ".task/config.json": CONFIG,
50
+ ".task/tickets/x7k4m/ticket.md": ticket("Ship it", "todo"),
51
+ ".task/tickets/x7k4m/comments/2026-01-02T000000-nick.md": '---\nauthor: "nick"\ncreated: 2026-01-02T00:00:00.000Z\n---\n\nsoon',
52
+ ".task/goals/launch.md": "---\ncreated: 2026-01-01T00:00:00.000Z\nupdated: 2026-01-01T00:00:00.000Z\n---\n\n# Launch\n",
53
+ "pkg/.task/config.json": JSON.stringify({ name: "pkg", prefix: "PKG", version: 2 }),
54
+ "pkg/.task/tickets/q3v8d/ticket.md": ticket("Nested", "backlog"),
55
+ },
56
+ bbb: {
57
+ ".task/config.json": CONFIG,
58
+ ".task/tickets/x7k4m/ticket.md": ticket("Ship it", "done"),
59
+ ".task/tickets/zz9zz/ticket.md": ticket("Only on the branch", "todo", 1),
60
+ },
61
+ };
62
+ let calls = [];
63
+ let githubStatus = null;
64
+ function fakeGitHub(input, init) {
65
+ const url = new URL(typeof input === "string" ? input : input instanceof URL ? input.href : input.url);
66
+ const auth = new Headers(init?.headers).get("Authorization");
67
+ calls.push({ path: url.pathname, auth });
68
+ if (githubStatus !== null)
69
+ return Promise.resolve(new Response("nope", { status: githubStatus }));
70
+ const commit = /^\/repos\/acme\/widgets\/commits\/(.+)$/.exec(url.pathname);
71
+ if (commit) {
72
+ const sha = COMMITS[decodeURIComponent(commit[1])];
73
+ if (!sha)
74
+ return Promise.resolve(new Response("{}", { status: 422 }));
75
+ return Promise.resolve(Response.json({
76
+ sha,
77
+ commit: { message: `commit ${sha}\n\nbody`, committer: { date: "2026-01-03T00:00:00Z" } },
78
+ }));
79
+ }
80
+ const tar = /^\/repos\/acme\/widgets\/tarball\/(.+)$/.exec(url.pathname);
81
+ if (tar) {
82
+ const tree = TREES[tar[1]];
83
+ if (!tree)
84
+ return Promise.resolve(new Response(null, { status: 404 }));
85
+ return Promise.resolve(new Response(gzipSync(tarball(`acme-widgets-${tar[1]}`, tree))));
86
+ }
87
+ if (url.pathname === "/repos/acme/widgets/branches") {
88
+ return Promise.resolve(Response.json([{ name: "feature" }, { name: "main" }, { name: "docs" }]));
89
+ }
90
+ if (url.pathname === "/repos/acme/widgets") {
91
+ return Promise.resolve(Response.json({ default_branch: "main", private: true }));
92
+ }
93
+ return Promise.resolve(new Response(null, { status: 404 }));
94
+ }
95
+ beforeEach(() => {
96
+ calls = [];
97
+ githubStatus = null;
98
+ mock.method(globalThis, "fetch", fakeGitHub);
99
+ });
100
+ afterEach(() => mock.restoreAll());
101
+ function handler(extra = {}) {
102
+ return createBoardHandler({ token: "ghp_test", repo: "acme/widgets", ...extra });
103
+ }
104
+ async function get(h, path, base = "/api") {
105
+ const res = await h(new Request(`https://board.example${base}${path}`));
106
+ return { status: res.status, body: (await res.json()) };
107
+ }
108
+ test("answers the read routes in the serve API's shapes", async () => {
109
+ const h = handler();
110
+ const project = await get(h, "/project");
111
+ assert.equal(project.status, 200);
112
+ assert.deepEqual(project.body, {
113
+ name: "acme",
114
+ prefix: "ACM",
115
+ statuses: ["backlog", "todo", "in_progress", "done", "canceled"],
116
+ readOnly: true,
117
+ author: "",
118
+ });
119
+ const boards = await get(h, "/boards");
120
+ assert.deepEqual(boards.body, {
121
+ boards: [
122
+ { id: ".", name: "acme", prefix: "ACM" },
123
+ { id: "pkg", name: "pkg", prefix: "PKG" },
124
+ ],
125
+ });
126
+ const tasks = await get(h, "/tasks");
127
+ const list = tasks.body.tasks;
128
+ assert.deepEqual(list.map((t) => [t.id, t.status, t.commentCount]), [["ACM-x7k4m", "todo", 1]]);
129
+ const goals = await get(h, "/goals");
130
+ assert.deepEqual(goals.body.goals.map((g) => g.slug), ["launch"]);
131
+ // Detail takes the display id or the bare key, like the CLI.
132
+ for (const id of ["ACM-x7k4m", "x7k4m"]) {
133
+ const detail = await get(h, `/tasks/${id}`);
134
+ assert.equal(detail.status, 200, id);
135
+ assert.equal(detail.body.task.title, "Ship it");
136
+ assert.deepEqual(detail.body.comments.map((c) => [c.author, c.body]), [["nick", "soon"]]);
137
+ }
138
+ assert.equal((await get(h, "/tasks/nope1")).status, 404);
139
+ assert.equal((await get(h, "/tasks/not%20an%20id")).status, 400);
140
+ // `?board=` picks the nested board; an unknown one falls back to the default.
141
+ const nested = await get(h, "/tasks?board=pkg");
142
+ assert.deepEqual(nested.body.tasks.map((t) => t.id), ["PKG-q3v8d"]);
143
+ assert.equal((await get(h, "/project?board=nope")).body.prefix, "ACM");
144
+ // Not in this API: the SPA degrades on a 404 exactly as it does hosted.
145
+ assert.equal((await get(h, "/inbox")).status, 404);
146
+ assert.equal((await get(h, "/git/status")).status, 404);
147
+ });
148
+ test("anything but a read is refused with the read-only reason", async () => {
149
+ const h = handler();
150
+ const res = await h(new Request("https://board.example/api/tasks", { method: "POST", body: "{}" }));
151
+ assert.equal(res.status, 405);
152
+ assert.equal((await res.json()).reason, "read-only");
153
+ // Refused before GitHub is asked anything.
154
+ assert.equal(calls.length, 0);
155
+ });
156
+ test("?ref= reads another branch; a missing ref is a 404 the SPA can name", async () => {
157
+ const h = handler();
158
+ const branch = await get(h, "/tasks?ref=feature");
159
+ assert.deepEqual(branch.body.tasks.map((t) => [t.id, t.status]), [
160
+ ["ACM-x7k4m", "done"],
161
+ ["ACM-zz9zz", "todo"],
162
+ ]);
163
+ const gone = await get(h, "/tasks?ref=gone");
164
+ assert.equal(gone.status, 404);
165
+ assert.equal(gone.body.reason, "no-such-ref");
166
+ // A ref that isn't a git name is ignored rather than sent to GitHub.
167
+ const bad = await get(h, "/tasks?ref=..%2F..%2Fetc");
168
+ assert.equal(bad.status, 200);
169
+ assert.ok(calls.every((c) => !c.path.includes("etc")));
170
+ });
171
+ test("/branches lists the repo's branches, default first", async () => {
172
+ const h = handler();
173
+ const res = await get(h, "/branches");
174
+ assert.deepEqual(res.body, {
175
+ branches: [
176
+ { name: "main", isDefault: true },
177
+ { name: "docs", isDefault: false },
178
+ { name: "feature", isDefault: false },
179
+ ],
180
+ });
181
+ });
182
+ test("one tarball per commit, however many requests ask", async () => {
183
+ const h = handler();
184
+ // The SPA's boot: four requests at once, before anything is cached.
185
+ await Promise.all([get(h, "/boards"), get(h, "/project"), get(h, "/tasks"), get(h, "/goals")]);
186
+ await get(h, "/tasks/x7k4m");
187
+ const tarballs = calls.filter((c) => c.path.includes("/tarball/"));
188
+ assert.equal(tarballs.length, 1, "the tarball was fetched more than once for one sha");
189
+ // HEAD is asked every time — that is the freshness check.
190
+ assert.equal(calls.filter((c) => c.path.includes("/commits/")).length, 5);
191
+ // Another ref is another commit, hence another tarball; then cached too.
192
+ await get(h, "/tasks?ref=feature");
193
+ await get(h, "/tasks?ref=feature");
194
+ assert.equal(calls.filter((c) => c.path.includes("/tarball/")).length, 2);
195
+ });
196
+ test("the token can be a getter, and no token is a 503 the probe reads as no API", async () => {
197
+ let minted = 0;
198
+ const h = handler({
199
+ token: async () => {
200
+ minted++;
201
+ return "ghp_minted";
202
+ },
203
+ });
204
+ assert.equal((await get(h, "/project")).status, 200);
205
+ assert.equal(minted, 1);
206
+ assert.equal(calls[0].auth, "Bearer ghp_minted");
207
+ const unset = handler({ token: () => "" });
208
+ const res = await get(unset, "/project");
209
+ assert.equal(res.status, 503);
210
+ assert.equal(res.body.reason, "no-token");
211
+ });
212
+ test("mounted under a base path, only that path is the API", async () => {
213
+ const h = handler({ basePath: "/board/api" });
214
+ assert.equal((await get(h, "/project", "/board/api")).status, 200);
215
+ assert.equal((await get(h, "/project", "/api")).status, 404);
216
+ assert.equal((await get(h, "", "/board/api")).status, 404);
217
+ });
218
+ test("GitHub refusing the token is a 502, not a board", async () => {
219
+ githubStatus = 401;
220
+ const res = await get(handler(), "/project");
221
+ assert.equal(res.status, 502);
222
+ assert.equal(res.body.reason, "github");
223
+ assert.match(String(res.body.error), /401/);
224
+ });
225
+ test("a repo that isn't owner/name is refused at construction", () => {
226
+ assert.throws(() => createBoardHandler({ token: "t", repo: "not a repo" }), /owner\/name/);
227
+ assert.throws(() => createBoardHandler({ token: "t", repo: "a/b/c" }), /owner\/name/);
228
+ });
229
+ //# sourceMappingURL=handler.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.test.js","sourceRoot":"","sources":["../../src/board/handler.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The Cloudflare Pages Function `task export --functions` writes.
3
+ *
4
+ * Bundled into one dependency-free module (`dist/functions/board.js`, built
5
+ * by `vite.functions.config.ts`) because the deployed directory has no
6
+ * `node_modules` — the file has to carry the handler with it. The export
7
+ * copies it to `functions[/<base>]/api/[[path]].js` and fills in the two
8
+ * placeholders below; nothing else about it is per-repo.
9
+ *
10
+ * The token is read per request from the Pages project's secrets. Unset, the
11
+ * function answers 503 without touching GitHub: the SPA's boot probe reads
12
+ * that as "no API" and falls back to the snapshot beside this file, which is
13
+ * the documented behaviour of a live board that isn't configured yet.
14
+ */
15
+ /** The secret's name in the Pages project; the README says how to set it. */
16
+ export declare const TOKEN_SECRET = "TASK_GITHUB_TOKEN";
17
+ interface PagesContext {
18
+ request: Request;
19
+ env: Record<string, string | undefined>;
20
+ }
21
+ export declare function onRequest(context: PagesContext): Promise<Response> | Response;
22
+ export {};
23
+ //# sourceMappingURL=pages-function.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pages-function.d.ts","sourceRoot":"","sources":["../../src/board/pages-function.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH,6EAA6E;AAC7E,eAAO,MAAM,YAAY,sBAAsB,CAAA;AAE/C,UAAU,YAAY;IACpB,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;CACxC;AAKD,wBAAgB,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAS7E"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The Cloudflare Pages Function `task export --functions` writes.
3
+ *
4
+ * Bundled into one dependency-free module (`dist/functions/board.js`, built
5
+ * by `vite.functions.config.ts`) because the deployed directory has no
6
+ * `node_modules` — the file has to carry the handler with it. The export
7
+ * copies it to `functions[/<base>]/api/[[path]].js` and fills in the two
8
+ * placeholders below; nothing else about it is per-repo.
9
+ *
10
+ * The token is read per request from the Pages project's secrets. Unset, the
11
+ * function answers 503 without touching GitHub: the SPA's boot probe reads
12
+ * that as "no API" and falls back to the snapshot beside this file, which is
13
+ * the documented behaviour of a live board that isn't configured yet.
14
+ */
15
+ import { createBoardHandler } from "./handler.js";
16
+ /** Replaced by `task export` when the file is written. */
17
+ const REPO = "__TASK_EXPORT_REPO__";
18
+ const BASE_PATH = "__TASK_EXPORT_BASE_PATH__";
19
+ /** The secret's name in the Pages project; the README says how to set it. */
20
+ export const TOKEN_SECRET = "TASK_GITHUB_TOKEN";
21
+ let currentToken = "";
22
+ let handler = null;
23
+ export function onRequest(context) {
24
+ const token = context.env[TOKEN_SECRET];
25
+ if (!token)
26
+ return new Response(null, { status: 503 });
27
+ currentToken = token;
28
+ // One handler per isolate, so the snapshot cache survives between requests;
29
+ // the token is read through a getter so a rotated secret takes effect
30
+ // without waiting for the isolate to be recycled.
31
+ handler ??= createBoardHandler({ token: () => currentToken, repo: REPO, basePath: BASE_PATH });
32
+ return handler(context.request);
33
+ }
34
+ //# sourceMappingURL=pages-function.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pages-function.js","sourceRoot":"","sources":["../../src/board/pages-function.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,kBAAkB,EAAqB,MAAM,cAAc,CAAA;AAEpE,0DAA0D;AAC1D,MAAM,IAAI,GAAG,sBAAsB,CAAA;AACnC,MAAM,SAAS,GAAG,2BAA2B,CAAA;AAE7C,6EAA6E;AAC7E,MAAM,CAAC,MAAM,YAAY,GAAG,mBAAmB,CAAA;AAO/C,IAAI,YAAY,GAAG,EAAE,CAAA;AACrB,IAAI,OAAO,GAAwB,IAAI,CAAA;AAEvC,MAAM,UAAU,SAAS,CAAC,OAAqB;IAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;IACvC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IACtD,YAAY,GAAG,KAAK,CAAA;IACpB,4EAA4E;IAC5E,sEAAsE;IACtE,kDAAkD;IAClD,OAAO,KAAK,kBAAkB,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAA;IAC9F,OAAO,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;AACjC,CAAC"}