@nickmeriano/task 0.11.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.
- package/README.md +175 -1
- package/dist/board/github.d.ts +68 -0
- package/dist/board/github.d.ts.map +1 -0
- package/dist/board/github.js +112 -0
- package/dist/board/github.js.map +1 -0
- package/dist/board/handler.d.ts +50 -0
- package/dist/board/handler.d.ts.map +1 -0
- package/dist/board/handler.js +183 -0
- package/dist/board/handler.js.map +1 -0
- package/dist/board/handler.test.d.ts +11 -0
- package/dist/board/handler.test.d.ts.map +1 -0
- package/dist/board/handler.test.js +229 -0
- package/dist/board/handler.test.js.map +1 -0
- package/dist/board/pages-function.d.ts +23 -0
- package/dist/board/pages-function.d.ts.map +1 -0
- package/dist/board/pages-function.js +34 -0
- package/dist/board/pages-function.js.map +1 -0
- package/dist/board/source.d.ts +118 -0
- package/dist/board/source.d.ts.map +1 -0
- package/dist/board/source.js +333 -0
- package/dist/board/source.js.map +1 -0
- package/dist/board/source.test.d.ts +12 -0
- package/dist/board/source.test.d.ts.map +1 -0
- package/dist/board/source.test.js +165 -0
- package/dist/board/source.test.js.map +1 -0
- package/dist/board/tar.d.ts +28 -0
- package/dist/board/tar.d.ts.map +1 -0
- package/dist/board/tar.js +188 -0
- package/dist/board/tar.js.map +1 -0
- package/dist/board/tar.test.d.ts +9 -0
- package/dist/board/tar.test.d.ts.map +1 -0
- package/dist/board/tar.test.js +110 -0
- package/dist/board/tar.test.js.map +1 -0
- package/dist/cli.js +37 -2
- package/dist/cli.js.map +1 -1
- package/dist/export.d.ts +31 -0
- package/dist/export.d.ts.map +1 -1
- package/dist/export.js +61 -1
- package/dist/export.js.map +1 -1
- package/dist/export.test.js +80 -1
- package/dist/export.test.js.map +1 -1
- package/dist/functions/board.js +709 -0
- package/dist/git-serve.d.ts +14 -1
- package/dist/git-serve.d.ts.map +1 -1
- package/dist/git-serve.js +30 -2
- package/dist/git-serve.js.map +1 -1
- package/dist/git-serve.test.d.ts +1 -0
- package/dist/git-serve.test.d.ts.map +1 -1
- package/dist/git-serve.test.js +36 -1
- package/dist/git-serve.test.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/skill/SKILL.md +10 -3
- package/src/board/github.ts +151 -0
- package/src/board/handler.test.ts +276 -0
- package/src/board/handler.ts +228 -0
- package/src/board/pages-function.ts +42 -0
- package/src/board/source.test.ts +203 -0
- package/src/board/source.ts +422 -0
- package/src/board/tar.test.ts +128 -0
- package/src/board/tar.ts +199 -0
- package/src/cli.ts +36 -2
- package/src/export.test.ts +108 -1
- package/src/export.ts +79 -1
- package/src/git-serve.test.ts +41 -1
- package/src/git-serve.ts +30 -2
- package/src/index.ts +10 -0
- package/ui/dist/assets/index-B8M_DaOt.js +229 -0
- package/ui/dist/index.html +1 -1
- package/ui/dist/assets/index-mJmm4sWq.js +0 -229
|
@@ -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"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A repository, read as boards.
|
|
3
|
+
*
|
|
4
|
+
* This is `findBoards` + `FileStore`, re-expressed against a git tree instead
|
|
5
|
+
* of a filesystem, and producing byte-identical API payloads. That
|
|
6
|
+
* equivalence is the whole point: the hosted board and an exported board's
|
|
7
|
+
* live proxy run the *same* SPA as `task serve`, so anything that differs
|
|
8
|
+
* here shows up as a broken screen rather than a type error. The shapes below
|
|
9
|
+
* are pinned to `server.ts` — change one and change both — and the ticket
|
|
10
|
+
* format itself is not restated: parsing comes from `ticket-doc.ts`, the one
|
|
11
|
+
* definition every server shares, in its lenient mode (a hand-edited file
|
|
12
|
+
* that no longer parses becomes a degraded ticket or a skipped comment, never
|
|
13
|
+
* a broken board).
|
|
14
|
+
*
|
|
15
|
+
* Read-only by construction. There is no code here that could write, which is
|
|
16
|
+
* how "the board can't edit your repo" stays true no matter what the routing
|
|
17
|
+
* layer does.
|
|
18
|
+
*
|
|
19
|
+
* Cache-free on purpose. A snapshot is immutable per commit, so *where* to
|
|
20
|
+
* keep one is a deployment question — the hosted worker layers isolate memory
|
|
21
|
+
* and KV over `snapshotAt`, the live proxy keeps a bounded `SnapshotMemory` —
|
|
22
|
+
* and this module answers only "what is at this commit".
|
|
23
|
+
*/
|
|
24
|
+
import type { Ask, Comment, Goal, Task as StoredTask } from "../types.ts";
|
|
25
|
+
import { type CommitInfo } from "./github.ts";
|
|
26
|
+
export { STATUSES, type Status } from "../types.ts";
|
|
27
|
+
export type { Ask, Comment, Goal };
|
|
28
|
+
export interface BoardInfo {
|
|
29
|
+
id: string;
|
|
30
|
+
name: string;
|
|
31
|
+
prefix: string;
|
|
32
|
+
}
|
|
33
|
+
/** A stored task plus the count `GET /tasks` sends — exactly what `task serve` lists. */
|
|
34
|
+
export interface Task extends StoredTask {
|
|
35
|
+
commentCount: number;
|
|
36
|
+
}
|
|
37
|
+
export interface BoardData {
|
|
38
|
+
info: BoardInfo;
|
|
39
|
+
tasks: Task[];
|
|
40
|
+
/** Keyed by task key. */
|
|
41
|
+
comments: Map<string, Comment[]>;
|
|
42
|
+
/** Live goals first, archived flagged — the shape `GET /goals` serves. */
|
|
43
|
+
goals: Goal[];
|
|
44
|
+
}
|
|
45
|
+
export interface Snapshot {
|
|
46
|
+
commit: CommitInfo;
|
|
47
|
+
boards: BoardInfo[];
|
|
48
|
+
data: Map<string, BoardData>;
|
|
49
|
+
/**
|
|
50
|
+
* Historically "the tree listing hit GitHub's cap". The tarball read is
|
|
51
|
+
* always complete, so this is now always false — kept because the SPA's
|
|
52
|
+
* payloads carry it.
|
|
53
|
+
*/
|
|
54
|
+
truncated: boolean;
|
|
55
|
+
}
|
|
56
|
+
export declare class BoardSourceError extends Error {
|
|
57
|
+
readonly status: number;
|
|
58
|
+
/**
|
|
59
|
+
* Machine-readable cause, sent beside the message so the SPA can choose a
|
|
60
|
+
* screen rather than infer one from a status code. Optional: most of these
|
|
61
|
+
* are one of a kind and the message is the whole story.
|
|
62
|
+
*/
|
|
63
|
+
readonly reason: string | null;
|
|
64
|
+
constructor(status: number, message: string, reason?: string | null);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Board directories, from a flat list of repo paths.
|
|
68
|
+
*
|
|
69
|
+
* The CLI finds a board by the presence of `.task/config.json`; so does this.
|
|
70
|
+
* Depth is capped the same way, and the ordering — "." first, then
|
|
71
|
+
* lexicographic — is copied so the default board is the same one you'd get
|
|
72
|
+
* locally.
|
|
73
|
+
*/
|
|
74
|
+
export declare function findBoardDirs(paths: string[]): string[];
|
|
75
|
+
/**
|
|
76
|
+
* One board from its ticket files. `files` is keyed by path relative to the
|
|
77
|
+
* board's `.task/` — `tickets/12/ticket.md`, `tickets/12/comments/<stem>.md` —
|
|
78
|
+
* which is how `snapshotAt` fetches them and how the test feeds them from
|
|
79
|
+
* disk.
|
|
80
|
+
*/
|
|
81
|
+
export declare function readTicketsBoard(id: string, configBytes: Uint8Array, files: Map<string, Uint8Array>): BoardData;
|
|
82
|
+
/**
|
|
83
|
+
* Every board in a repository at a known commit, ready to serve.
|
|
84
|
+
*
|
|
85
|
+
* One request for the whole repository: every path, plus bytes for anything
|
|
86
|
+
* under a .task/ directory. (Earlier shapes — a tree listing plus per-file or
|
|
87
|
+
* batched blob reads — scaled subrequests with board size and tripped
|
|
88
|
+
* Workers' 50-subrequest ceiling; a tarball is one request regardless.)
|
|
89
|
+
*
|
|
90
|
+
* `ref` is only for the error message: told a branch, "this repository has
|
|
91
|
+
* no .task directory" is both wrong and confusing — the repository plainly
|
|
92
|
+
* has one, you were just looking at it — and `task init` is not the fix for
|
|
93
|
+
* previewing a branch that forked before the boards existed.
|
|
94
|
+
*/
|
|
95
|
+
export declare function snapshotAt(token: string, owner: string, repo: string, commit: CommitInfo, ref?: string): Promise<Snapshot>;
|
|
96
|
+
/**
|
|
97
|
+
* Every board at `ref`, cache-free: "what is HEAD" plus one tarball. Callers
|
|
98
|
+
* that serve more than one view put a cache between the two — see
|
|
99
|
+
* `SnapshotMemory` — because the second request is the expensive one and a
|
|
100
|
+
* commit never changes.
|
|
101
|
+
*/
|
|
102
|
+
export declare function loadSnapshot(token: string, owner: string, repo: string, ref: string): Promise<Snapshot>;
|
|
103
|
+
/**
|
|
104
|
+
* Parsed snapshots, keyed by `owner/repo@sha`, bounded.
|
|
105
|
+
*
|
|
106
|
+
* A commit is immutable, so this never serves anything stale: the only lookup
|
|
107
|
+
* that goes to GitHub on a warm instance is "what is HEAD right now", and a
|
|
108
|
+
* new commit is a new key. Bounded because an isolate's memory is not ours to
|
|
109
|
+
* fill; `Map` iterates in insertion order, so the first key is the oldest.
|
|
110
|
+
*/
|
|
111
|
+
export declare class SnapshotMemory {
|
|
112
|
+
private readonly snapshots;
|
|
113
|
+
private readonly max;
|
|
114
|
+
constructor(max?: number);
|
|
115
|
+
get(key: string): Snapshot | undefined;
|
|
116
|
+
remember(key: string, snapshot: Snapshot): Snapshot;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../src/board/source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAUH,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,IAAI,UAAU,EAAE,MAAM,aAAa,CAAA;AACzE,OAAO,EAAgC,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAE3E,OAAO,EAAE,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAA;AACnD,YAAY,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AAalC,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;CACf;AAED,yFAAyF;AACzF,MAAM,WAAW,IAAK,SAAQ,UAAU;IACtC,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,SAAS,CAAA;IACf,KAAK,EAAE,IAAI,EAAE,CAAA;IACb,yBAAyB;IACzB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;IAChC,0EAA0E;IAC1E,KAAK,EAAE,IAAI,EAAE,CAAA;CACd;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,UAAU,CAAA;IAClB,MAAM,EAAE,SAAS,EAAE,CAAA;IACnB,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;IAC5B;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAA;CACnB;AAED,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;gBAClB,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,GAAG,IAAW;CAM1E;AAkCD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAcvD;AAQD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,UAAU,EACvB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,GAC7B,SAAS,CAkIX;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,UAAU,EAClB,GAAG,SAAS,GACX,OAAO,CAAC,QAAQ,CAAC,CAsDnB;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,QAAQ,CAAC,CAGnB;AAED;;;;;;;GAOG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA8B;IACxD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;gBAEhB,GAAG,SAAK;IAIpB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAItC,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,QAAQ;CASpD"}
|