@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
package/dist/export.js ADDED
@@ -0,0 +1,259 @@
1
+ /**
2
+ * `task export` — the board as a directory of files.
3
+ *
4
+ * The third deployment of this app (after `task serve` and the hosted board)
5
+ * is a static host: the same prebuilt UI the package already ships for serve,
6
+ * plus a `snapshot.json` this module writes out of the local `.task/`. There
7
+ * is no server behind it, so the app reads its answers out of that file —
8
+ * `ui/src/snapshot.ts` is the reader, and this is the writer.
9
+ *
10
+ * The whole correctness question is one sentence: **the snapshot must answer
11
+ * what `routeApi` answers**. Every field below is sourced from the same store
12
+ * call the matching `/api/…` route makes, so a board rendered from a file and
13
+ * a board rendered from the server differ only in how stale they are.
14
+ *
15
+ * Split in two on purpose: `buildSnapshot` is the shape (no writes, so it is
16
+ * testable without a built UI, and the UI's own test can round-trip it through
17
+ * `parseSnapshot`), `writeExport` is the directory.
18
+ */
19
+ import { execFileSync } from "node:child_process";
20
+ import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
21
+ import { dirname, join, resolve } from "node:path";
22
+ import { fileURLToPath } from "node:url";
23
+ import { resolveAuthor } from "./author.js";
24
+ import { REPO_PATTERN } from "./board/handler.js";
25
+ import { openBoard } from "./file-store.js";
26
+ import { findBoards } from "./store.js";
27
+ import { STATUSES } from "./types.js";
28
+ /**
29
+ * The version the reader accepts, restated from `ui/src/snapshot.ts`. The UI
30
+ * is built separately from this NodeNext package, so the contract is mirrored
31
+ * rather than cross-imported — the same precedent `ui/src/api.ts` sets for
32
+ * `types.ts`. `ui/src/deployment.test.ts` imports `buildSnapshot` and feeds
33
+ * the result to the real `parseSnapshot`, which is what keeps the two honest.
34
+ */
35
+ export const SNAPSHOT_VERSION = 2;
36
+ /** The file's name in the output directory, matching `SNAPSHOT_PATH`. */
37
+ export const SNAPSHOT_FILE = "snapshot.json";
38
+ /** Where an export goes when `--out` doesn't say. */
39
+ export const DEFAULT_OUT_DIR = "task-export";
40
+ /**
41
+ * The live-proxy function, bundled by `vite.functions.config.ts` into one
42
+ * dependency-free file. Resolved from this module the way `UI_DIR` is: the
43
+ * same relative path works from `src/` (run from source) and from `dist/`.
44
+ */
45
+ export const FUNCTION_BUNDLE = fileURLToPath(new URL("../dist/functions/board.js", import.meta.url));
46
+ /** The sentence for a missing bundle, in the shape `NO_UI_BUILD` sets. */
47
+ const NO_FUNCTION_BUILD = "function bundle not found — run the package build (vite build -c vite.functions.config.ts).";
48
+ /** The sentence `serveStatic` prints for the same missing directory. */
49
+ const NO_UI_BUILD = "UI build not found — run the package build (vite build ui).";
50
+ /**
51
+ * The assets are built with relative refs (`base: "./"` in ui/vite.config.ts)
52
+ * because one build ships three ways and can't know where it will live. The
53
+ * `<base href>` each deployment stamps into index.html is what completes them:
54
+ * the browser resolves `./assets/…` — and the app resolves its routes, API and
55
+ * snapshot fetch — against it instead of the page's own URL, which is what
56
+ * keeps deep links working under any mount point.
57
+ */
58
+ /** `board` / `/board` / `board/` → `/board/`; `""` and `/` → `/`. */
59
+ export function normalizeBase(base) {
60
+ let path = base.trim();
61
+ if (path === "" || path === "/")
62
+ return "/";
63
+ if (!path.startsWith("/"))
64
+ path = `/${path}`;
65
+ if (!path.endsWith("/"))
66
+ path = `${path}/`;
67
+ if (path.includes("//") || /[\s<>"']/.test(path)) {
68
+ throw new Error(`--base must be a plain URL path like /board/ — got ${JSON.stringify(base)}`);
69
+ }
70
+ return path;
71
+ }
72
+ /**
73
+ * Stamp the deployment's mount point into the built page. The built
74
+ * index.html always has exactly one `<head>` for this to anchor on; refusing
75
+ * a page without one beats silently shipping a board that 404s its assets.
76
+ */
77
+ export function injectBase(html, base) {
78
+ if (!html.includes("<head>")) {
79
+ throw new Error("index.html has no <head> to carry the <base> tag — is this the built UI?");
80
+ }
81
+ return html.replace("<head>", `<head><base href="${normalizeBase(base)}" />`);
82
+ }
83
+ /**
84
+ * The commit this export is of.
85
+ *
86
+ * Every way this can fail — git not installed, not a repository, a repo with
87
+ * no commits yet — is the same non-answer, so they all collapse to null. The
88
+ * reader's type already says `string | null` and its banner degrades to
89
+ * "snapshot, built …", which is the honest thing to show.
90
+ */
91
+ export function exportedCommit(cwd) {
92
+ try {
93
+ const out = execFileSync("git", ["rev-parse", "HEAD"], {
94
+ cwd,
95
+ encoding: "utf8",
96
+ timeout: 2000,
97
+ stdio: ["ignore", "pipe", "ignore"],
98
+ });
99
+ return out.trim() || null;
100
+ }
101
+ catch {
102
+ return null;
103
+ }
104
+ }
105
+ /**
106
+ * Bake every board at or below `serveRoot` into one snapshot.
107
+ *
108
+ * Anchoring matches `cmdServe` exactly (`findRoot` ?? cwd, then `findBoards`),
109
+ * so "export what serve would serve" is true by construction — one board in a
110
+ * package, every nested board at a monorepo root, `.` first either way.
111
+ */
112
+ export function buildSnapshot(serveRoot, options = {}) {
113
+ const refs = findBoards(serveRoot);
114
+ if (refs.length === 0) {
115
+ throw new Error(`no .task directory found at or below ${serveRoot} — run \`task init\` first`);
116
+ }
117
+ // Resolved once, server-side, exactly as `createTaskServer` does it: all the
118
+ // boards live in one checkout, so one resolution covers them.
119
+ const author = resolveAuthor(undefined, serveRoot).name;
120
+ const boards = [];
121
+ const data = {};
122
+ for (const ref of refs) {
123
+ const store = openBoard(ref.root);
124
+ try {
125
+ boards.push({ id: ref.id, name: store.config.name, prefix: store.config.prefix });
126
+ const counts = store.commentCounts();
127
+ const tasks = store.list().map((task) => ({
128
+ ...task,
129
+ commentCount: counts.get(task.key) ?? 0,
130
+ }));
131
+ const comments = {};
132
+ for (const task of tasks) {
133
+ const list = store.comments(task.key);
134
+ // Omitted rather than empty: the reader defaults a missing key to [],
135
+ // and a snapshot is a file people open.
136
+ if (list.length > 0)
137
+ comments[task.key] = list;
138
+ }
139
+ data[ref.id] = {
140
+ project: { name: store.config.name, prefix: store.config.prefix, statuses: STATUSES, author },
141
+ tasks,
142
+ // Archived goals ride along flagged, as `/api/goals` sends them, so an
143
+ // old chip on a task can still resolve a title.
144
+ goals: [...store.goals(), ...store.goals(true)],
145
+ comments,
146
+ };
147
+ }
148
+ finally {
149
+ store.close();
150
+ }
151
+ }
152
+ return {
153
+ version: SNAPSHOT_VERSION,
154
+ meta: {
155
+ commit: exportedCommit(serveRoot),
156
+ builtAt: options.builtAt ?? new Date().toISOString(),
157
+ },
158
+ boards,
159
+ data,
160
+ };
161
+ }
162
+ /**
163
+ * Where a Cloudflare Pages Function has to sit to answer `<base>api/*`: the
164
+ * `functions/` tree mirrors URL paths, and `[[path]]` is Pages' catch-all.
165
+ */
166
+ export function functionPath(base) {
167
+ const mount = normalizeBase(base).slice(1); // "" or "board/"
168
+ return join("functions", ...mount.split("/").filter(Boolean), "api", "[[path]].js");
169
+ }
170
+ /**
171
+ * The bundled function, with the two things that are per-deploy filled in.
172
+ *
173
+ * Both replacements are validated first because they land inside string
174
+ * literals in a file that gets executed: `REPO_PATTERN` admits no quote, and
175
+ * `normalizeBase` refuses one.
176
+ */
177
+ export function renderFunction(bundle, repo, base) {
178
+ if (!REPO_PATTERN.test(repo)) {
179
+ throw new Error(`--repo must be owner/name — got ${JSON.stringify(repo)}`);
180
+ }
181
+ const basePath = `${normalizeBase(base)}api`;
182
+ for (const placeholder of ["__TASK_EXPORT_REPO__", "__TASK_EXPORT_BASE_PATH__"]) {
183
+ if (!bundle.includes(placeholder)) {
184
+ throw new Error(`function bundle has no ${placeholder} to fill — is this the built function?`);
185
+ }
186
+ }
187
+ return bundle
188
+ .replace("__TASK_EXPORT_REPO__", repo)
189
+ .replace("__TASK_EXPORT_BASE_PATH__", basePath);
190
+ }
191
+ /**
192
+ * Write the assets and the snapshot into `outDir`.
193
+ *
194
+ * Refresh is the interesting case. Re-exporting over a previous export
195
+ * replaces the UI's own entries and `snapshot.json` and leaves everything else
196
+ * alone — notably a `functions/` directory, so a deployed live-proxy function
197
+ * survives a re-export. Anything else in a non-empty directory is refused
198
+ * rather than overwritten: `--out .` against a repo is the one genuinely
199
+ * destructive mistake available here.
200
+ */
201
+ export function writeExport(options) {
202
+ const { serveRoot, outDir, uiDir, force = false } = options;
203
+ const base = normalizeBase(options.base ?? "/");
204
+ if (!existsSync(uiDir))
205
+ throw new Error(NO_UI_BUILD);
206
+ // Exporting onto the assets it copies from would clear them first and then
207
+ // copy an empty directory — the one way this can destroy the package itself.
208
+ if (resolve(outDir) === resolve(uiDir)) {
209
+ throw new Error(`${outDir} is the UI build itself — export somewhere else`);
210
+ }
211
+ const snapshot = buildSnapshot(serveRoot, { builtAt: options.builtAt });
212
+ // Rendered before anything is written, so a bad --repo or a missing bundle
213
+ // fails the export rather than leaving half of one behind.
214
+ const functions = options.functions
215
+ ? {
216
+ path: functionPath(base),
217
+ source: renderFunction(readFunctionBundle(options.functions.bundle ?? FUNCTION_BUNDLE), options.functions.repo, base),
218
+ }
219
+ : null;
220
+ const existing = existsSync(outDir) ? readdirSync(outDir) : null;
221
+ if (existing && existing.length > 0 && !existing.includes(SNAPSHOT_FILE) && !force) {
222
+ throw new Error(`${outDir} is not empty and isn't a previous export — pass --force to write into it anyway`);
223
+ }
224
+ mkdirSync(outDir, { recursive: true });
225
+ // Clear only what this export is about to write: a stale `assets/` full of
226
+ // last build's hashed bundles is dead weight, and everything else in there
227
+ // belongs to whoever put it there.
228
+ for (const entry of readdirSync(uiDir)) {
229
+ rmSync(join(outDir, entry), { recursive: true, force: true });
230
+ }
231
+ cpSync(uiDir, outDir, { recursive: true });
232
+ const indexPath = join(outDir, "index.html");
233
+ writeFileSync(indexPath, injectBase(readFileSync(indexPath, "utf8"), base));
234
+ // Pretty-printed: this lands in CI artifacts and gets diffed, and at this
235
+ // size the whitespace costs nothing.
236
+ writeFileSync(join(outDir, SNAPSHOT_FILE), `${JSON.stringify(snapshot, null, 2)}\n`);
237
+ // The function is the one thing under `functions/` this export owns; the
238
+ // rest of that directory is whoever's it was. Without `functions`, a
239
+ // function from an earlier export stays exactly as deployed.
240
+ if (functions) {
241
+ mkdirSync(dirname(join(outDir, functions.path)), { recursive: true });
242
+ writeFileSync(join(outDir, functions.path), functions.source);
243
+ }
244
+ return {
245
+ outDir,
246
+ base,
247
+ boards: snapshot.boards.length,
248
+ tasks: Object.values(snapshot.data).reduce((n, board) => n + board.tasks.length, 0),
249
+ commit: snapshot.meta.commit,
250
+ builtAt: snapshot.meta.builtAt,
251
+ functions: functions?.path ?? null,
252
+ };
253
+ }
254
+ function readFunctionBundle(path) {
255
+ if (!existsSync(path))
256
+ throw new Error(NO_FUNCTION_BUILD);
257
+ return readFileSync(path, "utf8");
258
+ }
259
+ //# sourceMappingURL=export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.js","sourceRoot":"","sources":["../src/export.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * `task export` writes a file another program reads, so what's checked here is
3
+ * the contract rather than the code: every field comes from the same store
4
+ * call the matching `/api/…` route makes, and the directory it lands in
5
+ * refreshes without eating what someone else put there.
6
+ *
7
+ * The round trip through the actual reader (`parseSnapshot` + `snapshotSource`)
8
+ * lives in `ui/src/deployment.test.ts` — it's the UI's tsconfig that can see
9
+ * both sides.
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=export.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.test.d.ts","sourceRoot":"","sources":["../src/export.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
@@ -0,0 +1,264 @@
1
+ /**
2
+ * `task export` writes a file another program reads, so what's checked here is
3
+ * the contract rather than the code: every field comes from the same store
4
+ * call the matching `/api/…` route makes, and the directory it lands in
5
+ * refreshes without eating what someone else put there.
6
+ *
7
+ * The round trip through the actual reader (`parseSnapshot` + `snapshotSource`)
8
+ * lives in `ui/src/deployment.test.ts` — it's the UI's tsconfig that can see
9
+ * both sides.
10
+ */
11
+ import assert from "node:assert/strict";
12
+ import { test } from "node:test";
13
+ import { execFileSync } from "node:child_process";
14
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
15
+ import { tmpdir } from "node:os";
16
+ import { join } from "node:path";
17
+ import { initProject, openBoard } from "./file-store.js";
18
+ import { buildSnapshot, functionPath, renderFunction, writeExport, SNAPSHOT_FILE, SNAPSHOT_VERSION, } from "./export.js";
19
+ import { STATUSES } from "./types.js";
20
+ function tempRoot() {
21
+ const dir = mkdtempSync(join(tmpdir(), "task-export-"));
22
+ process.on("exit", () => rmSync(dir, { recursive: true, force: true }));
23
+ return dir;
24
+ }
25
+ /** A fake `ui/dist` — the shape of one is all the copy step cares about. */
26
+ function fakeUi() {
27
+ const dir = tempRoot();
28
+ writeFileSync(join(dir, "index.html"), "<!doctype html><html><head><title>board</title></head></html>");
29
+ mkdirSync(join(dir, "assets"));
30
+ writeFileSync(join(dir, "assets", "index-abc123.js"), "// bundle");
31
+ return dir;
32
+ }
33
+ /** A single-board repo with enough in it to be worth exporting. */
34
+ function board() {
35
+ const root = tempRoot();
36
+ const store = initProject(root, { name: "acme", prefix: "ACM" });
37
+ const talked = store.create({ title: "Ship the export", status: "todo" });
38
+ store.addComment(talked.key, "on it", "claude");
39
+ store.addComment(talked.key, "still on it", "claude");
40
+ store.create({ title: "Quiet ticket", status: "backlog" });
41
+ const finished = store.create({ title: "Already shipped", status: "done" });
42
+ store.archive(finished.key);
43
+ store.createGoal({ title: "Launch", slug: "launch" });
44
+ store.createGoal({ title: "Old plan", slug: "old-plan" });
45
+ store.archiveGoal("old-plan");
46
+ store.close();
47
+ return root;
48
+ }
49
+ test("the snapshot answers what the serve API answers", () => {
50
+ const root = board();
51
+ const store = openBoard(root);
52
+ const snapshot = buildSnapshot(root);
53
+ const data = snapshot.data["."];
54
+ // /api/project — field for field, minus readOnly (the reader forces it).
55
+ assert.equal(data.project.name, store.config.name);
56
+ assert.equal(data.project.prefix, store.config.prefix);
57
+ assert.deepEqual(data.project.statuses, STATUSES);
58
+ assert.equal(typeof data.project.author, "string");
59
+ assert.ok(!("readOnly" in data.project));
60
+ // /api/tasks — the same list, in the same order, with the same counts.
61
+ const tasks = store.list();
62
+ const counts = store.commentCounts();
63
+ assert.deepEqual(data.tasks, tasks.map((t) => ({ ...t, commentCount: counts.get(t.key) ?? 0 })));
64
+ // /api/goals — live first, archived riding along flagged.
65
+ assert.deepEqual(data.goals, [...store.goals(), ...store.goals(true)]);
66
+ assert.ok(data.goals.some((g) => g.archived));
67
+ // /api/tasks/:n — comments per task, oldest first, empty ones omitted.
68
+ const talkative = tasks.find((t) => t.title === "Ship the export");
69
+ const quiet = tasks.find((t) => t.title === "Quiet ticket");
70
+ assert.deepEqual(data.comments[talkative.key], store.comments(talkative.key));
71
+ assert.equal(data.comments[quiet.key], undefined);
72
+ store.close();
73
+ });
74
+ test("an archived ticket is off the board, in the export too", () => {
75
+ const root = board();
76
+ const snapshot = buildSnapshot(root);
77
+ assert.ok(!snapshot.data["."].tasks.some((t) => t.title === "Already shipped"));
78
+ });
79
+ test("a monorepo root exports every board, keyed by what /boards says", () => {
80
+ const root = tempRoot();
81
+ initProject(root, { name: "acme", prefix: "ACM" }).close();
82
+ const nested = join(root, "projects", "widget");
83
+ mkdirSync(nested, { recursive: true });
84
+ initProject(nested, { name: "widget", prefix: "WID" }).close();
85
+ const snapshot = buildSnapshot(root);
86
+ assert.deepEqual(snapshot.boards.map((b) => b.id), [".", "projects/widget"]);
87
+ assert.deepEqual(snapshot.boards[1], { id: "projects/widget", name: "widget", prefix: "WID" });
88
+ // Every board in the list has an entry, and nothing else does.
89
+ assert.deepEqual(Object.keys(snapshot.data).sort(), [".", "projects/widget"]);
90
+ });
91
+ test("a board with nothing above or below it is not an export", () => {
92
+ assert.throws(() => buildSnapshot(tempRoot()), /run `task init`/);
93
+ });
94
+ test("the commit is the HEAD it was exported from, or null outside a repo", () => {
95
+ const loose = board();
96
+ assert.equal(buildSnapshot(loose).meta.commit, null);
97
+ const repo = board();
98
+ const git = (...args) => execFileSync("git", args, { cwd: repo, stdio: ["ignore", "pipe", "ignore"] });
99
+ git("init", "-q");
100
+ git("config", "user.email", "t@example.com");
101
+ git("config", "user.name", "Test");
102
+ git("add", "-A");
103
+ git("commit", "-qm", "board");
104
+ const head = git("rev-parse", "HEAD").toString().trim();
105
+ assert.equal(buildSnapshot(repo).meta.commit, head);
106
+ });
107
+ test("the export is the assets plus the snapshot", () => {
108
+ const root = board();
109
+ const out = join(tempRoot(), "board");
110
+ const summary = writeExport({ serveRoot: root, outDir: out, uiDir: fakeUi() });
111
+ assert.ok(existsSync(join(out, "index.html")));
112
+ assert.ok(existsSync(join(out, "assets", "index-abc123.js")));
113
+ const snapshot = JSON.parse(readFileSync(join(out, SNAPSHOT_FILE), "utf8"));
114
+ assert.equal(snapshot.version, SNAPSHOT_VERSION);
115
+ assert.equal(snapshot.boards.length, 1);
116
+ assert.deepEqual(summary, {
117
+ outDir: out,
118
+ base: "/",
119
+ boards: 1,
120
+ tasks: snapshot.data["."].tasks.length,
121
+ commit: null,
122
+ builtAt: snapshot.meta.builtAt,
123
+ functions: null,
124
+ });
125
+ });
126
+ test("exporting without a built UI says which build to run", () => {
127
+ assert.throws(() => writeExport({
128
+ serveRoot: board(),
129
+ outDir: join(tempRoot(), "board"),
130
+ uiDir: join(tempRoot(), "never-built"),
131
+ }), /run the package build/);
132
+ });
133
+ test("exporting onto the UI build is refused, not a way to delete it", () => {
134
+ const ui = fakeUi();
135
+ assert.throws(() => writeExport({ serveRoot: board(), outDir: ui, uiDir: ui, force: true }), /the UI build itself/);
136
+ assert.ok(existsSync(join(ui, "index.html")));
137
+ });
138
+ test("re-exporting refreshes in place and leaves a deployed function alone", () => {
139
+ const root = board();
140
+ const out = join(tempRoot(), "board");
141
+ const ui = fakeUi();
142
+ writeExport({ serveRoot: root, outDir: out, uiDir: ui });
143
+ // TAS-33's room: something the export didn't put there and must not remove.
144
+ mkdirSync(join(out, "functions"));
145
+ writeFileSync(join(out, "functions", "handler.js"), "// live proxy");
146
+ // A stale bundle from the previous build, which it *should* remove.
147
+ writeFileSync(join(out, "assets", "index-old.js"), "// last time");
148
+ const store = openBoard(root);
149
+ store.create({ title: "Added since the last export", status: "todo" });
150
+ store.close();
151
+ writeExport({ serveRoot: root, outDir: out, uiDir: ui });
152
+ const snapshot = JSON.parse(readFileSync(join(out, SNAPSHOT_FILE), "utf8"));
153
+ assert.ok(snapshot.data["."].tasks.some((t) => t.title === "Added since the last export"));
154
+ assert.equal(readFileSync(join(out, "functions", "handler.js"), "utf8"), "// live proxy");
155
+ assert.ok(!existsSync(join(out, "assets", "index-old.js")));
156
+ });
157
+ test("a directory that isn't a previous export is refused, not overwritten", () => {
158
+ const root = board();
159
+ const out = join(tempRoot(), "not-an-export");
160
+ mkdirSync(out);
161
+ writeFileSync(join(out, "thesis.md"), "years of work");
162
+ assert.throws(() => writeExport({ serveRoot: root, outDir: out, uiDir: fakeUi() }), /--force/);
163
+ assert.equal(readFileSync(join(out, "thesis.md"), "utf8"), "years of work");
164
+ // --force writes in; it still only ever overwrites what it writes.
165
+ writeExport({ serveRoot: root, outDir: out, uiDir: fakeUi(), force: true });
166
+ assert.ok(existsSync(join(out, SNAPSHOT_FILE)));
167
+ assert.equal(readFileSync(join(out, "thesis.md"), "utf8"), "years of work");
168
+ });
169
+ // ── The mount point, stamped into the page ──────────────────────────────────
170
+ test("the exported page carries its mount point as a <base> tag", () => {
171
+ const root = board();
172
+ const atRoot = join(tempRoot(), "root");
173
+ writeExport({ serveRoot: root, outDir: atRoot, uiDir: fakeUi() });
174
+ assert.match(readFileSync(join(atRoot, "index.html"), "utf8"), /<head><base href="\/" \/>/);
175
+ const atSubpath = join(tempRoot(), "sub");
176
+ const summary = writeExport({ serveRoot: root, outDir: atSubpath, uiDir: fakeUi(), base: "board" });
177
+ assert.equal(summary.base, "/board/");
178
+ assert.match(readFileSync(join(atSubpath, "index.html"), "utf8"), /<head><base href="\/board\/" \/>/);
179
+ });
180
+ test("a base that isn't a plain URL path is refused", () => {
181
+ const root = board();
182
+ for (const bad of ["//cdn.example.com", "/a b/", '/"/']) {
183
+ assert.throws(() => writeExport({ serveRoot: root, outDir: join(tempRoot(), "out"), uiDir: fakeUi(), base: bad }), /--base/, bad);
184
+ }
185
+ });
186
+ // ── The live proxy, written beside the snapshot ─────────────────────────────
187
+ /** A stand-in for `dist/functions/board.js`: the placeholders are the contract. */
188
+ function fakeBundle() {
189
+ const dir = tempRoot();
190
+ const path = join(dir, "board.js");
191
+ writeFileSync(path, 'const REPO = "__TASK_EXPORT_REPO__";\nconst BASE_PATH = "__TASK_EXPORT_BASE_PATH__";\nexport const onRequest = () => [REPO, BASE_PATH];\n');
192
+ return path;
193
+ }
194
+ test("--functions writes the Pages Function where the SPA will look for its API", () => {
195
+ const root = board();
196
+ const out = join(tempRoot(), "board");
197
+ const summary = writeExport({
198
+ serveRoot: root,
199
+ outDir: out,
200
+ uiDir: fakeUi(),
201
+ functions: { repo: "acme/widgets", bundle: fakeBundle() },
202
+ });
203
+ assert.equal(summary.functions, join("functions", "api", "[[path]].js"));
204
+ const written = readFileSync(join(out, summary.functions), "utf8");
205
+ assert.match(written, /const REPO = "acme\/widgets";/);
206
+ assert.match(written, /const BASE_PATH = "\/api";/);
207
+ assert.ok(!written.includes("__TASK_EXPORT_"));
208
+ // The snapshot is still there: the function is an upgrade, not a replacement.
209
+ assert.ok(existsSync(join(out, SNAPSHOT_FILE)));
210
+ });
211
+ test("under --base the function moves with the mount point", () => {
212
+ assert.equal(functionPath("/"), join("functions", "api", "[[path]].js"));
213
+ assert.equal(functionPath("board"), join("functions", "board", "api", "[[path]].js"));
214
+ assert.equal(functionPath("/a/b/"), join("functions", "a", "b", "api", "[[path]].js"));
215
+ const out = join(tempRoot(), "sub");
216
+ const summary = writeExport({
217
+ serveRoot: board(),
218
+ outDir: out,
219
+ uiDir: fakeUi(),
220
+ base: "/board/",
221
+ functions: { repo: "acme/widgets", bundle: fakeBundle() },
222
+ });
223
+ assert.equal(summary.functions, join("functions", "board", "api", "[[path]].js"));
224
+ assert.match(readFileSync(join(out, summary.functions), "utf8"), /const BASE_PATH = "\/board\/api";/);
225
+ });
226
+ test("a re-export without --functions leaves the function as deployed; with it, refreshes it", () => {
227
+ const root = board();
228
+ const out = join(tempRoot(), "board");
229
+ const ui = fakeUi();
230
+ const first = writeExport({
231
+ serveRoot: root,
232
+ outDir: out,
233
+ uiDir: ui,
234
+ functions: { repo: "acme/widgets", bundle: fakeBundle() },
235
+ });
236
+ const file = join(out, first.functions);
237
+ writeFileSync(file, "// hand-edited on the host");
238
+ const plain = writeExport({ serveRoot: root, outDir: out, uiDir: ui });
239
+ assert.equal(plain.functions, null);
240
+ assert.equal(readFileSync(file, "utf8"), "// hand-edited on the host");
241
+ writeExport({ serveRoot: root, outDir: out, uiDir: ui, functions: { repo: "acme/gadgets", bundle: fakeBundle() } });
242
+ assert.match(readFileSync(file, "utf8"), /acme\/gadgets/);
243
+ });
244
+ test("a repo that isn't owner/name, or a missing bundle, fails before anything is written", () => {
245
+ const out = join(tempRoot(), "board");
246
+ assert.throws(() => writeExport({
247
+ serveRoot: board(),
248
+ outDir: out,
249
+ uiDir: fakeUi(),
250
+ functions: { repo: 'acme/"; fetch("evil', bundle: fakeBundle() },
251
+ }), /owner\/name/);
252
+ assert.ok(!existsSync(out));
253
+ assert.throws(() => writeExport({
254
+ serveRoot: board(),
255
+ outDir: out,
256
+ uiDir: fakeUi(),
257
+ functions: { repo: "acme/widgets", bundle: join(tempRoot(), "missing.js") },
258
+ }), /function bundle not found/);
259
+ assert.ok(!existsSync(out));
260
+ // A bundle without the placeholders is not the function — refuse rather
261
+ // than ship a file that reads the wrong repository.
262
+ assert.throws(() => renderFunction("export const onRequest = 1", "acme/widgets", "/"), /placeholder|__TASK_EXPORT_/);
263
+ });
264
+ //# sourceMappingURL=export.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.test.js","sourceRoot":"","sources":["../src/export.test.ts"],"names":[],"mappings":"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"}