@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
package/src/git-serve.test.ts
CHANGED
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
* runs; the board's own origin and origin-less clients pass.
|
|
12
12
|
* 4. Degradation: a repo with no origin advertises no `git` capability and
|
|
13
13
|
* 404s the whole route group — the self-hiding rule the UI builds on.
|
|
14
|
+
* 5. A board reached through a symlinked path is still a board (TAS-qn8td).
|
|
14
15
|
*/
|
|
15
16
|
|
|
16
17
|
import assert from "node:assert/strict"
|
|
17
18
|
import { after, test } from "node:test"
|
|
18
|
-
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
|
|
19
|
+
import { mkdtempSync, mkdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs"
|
|
19
20
|
import { tmpdir } from "node:os"
|
|
20
21
|
import { join } from "node:path"
|
|
21
22
|
import { spawnSync } from "node:child_process"
|
|
@@ -232,6 +233,45 @@ test("a plain directory (no git at all) behaves the same", async () => {
|
|
|
232
233
|
assert.equal("git" in project.body, false)
|
|
233
234
|
})
|
|
234
235
|
|
|
236
|
+
test("a repo reached through a symlink still finds its boards", async () => {
|
|
237
|
+
// `git rev-parse --show-toplevel` resolves symlinks and a Store's taskDir
|
|
238
|
+
// does not, so comparing them unresolved drops every board — commit 400s and
|
|
239
|
+
// status calls board files `otherDirty`, quietly. The symlink is explicit
|
|
240
|
+
// here rather than inherited from tmpdir(): on macOS /var/folders is already
|
|
241
|
+
// one (which is how this was found), on Linux it is not, and the property
|
|
242
|
+
// has to hold on both.
|
|
243
|
+
const home = tempDir()
|
|
244
|
+
const real = join(home, "real")
|
|
245
|
+
mkdirSync(real)
|
|
246
|
+
const bare = join(real, "origin.git")
|
|
247
|
+
sh(real, "git", "init", "--quiet", "--bare", "-b", "main", bare)
|
|
248
|
+
sh(real, "git", "clone", "--quiet", bare, join(real, "clone"))
|
|
249
|
+
const link = join(home, "link")
|
|
250
|
+
symlinkSync(real, link)
|
|
251
|
+
|
|
252
|
+
const clone = join(link, "clone")
|
|
253
|
+
sh(clone, "git", "config", "user.name", "Test")
|
|
254
|
+
sh(clone, "git", "config", "user.email", "test@example.com")
|
|
255
|
+
const store = initProject(clone, { name: "linked", prefix: "LNK" })
|
|
256
|
+
const first = store.create({ title: "First", status: "todo" }).key
|
|
257
|
+
sh(clone, "git", "add", "-A")
|
|
258
|
+
sh(clone, "git", "commit", "--quiet", "-m", "board")
|
|
259
|
+
sh(clone, "git", "push", "--quiet", "-u", "origin", "main")
|
|
260
|
+
|
|
261
|
+
const base = await serve(clone)
|
|
262
|
+
await call(base, `/api/tasks/${first}`, Object.assign(post({ status: "in_progress" }), { method: "PATCH" }))
|
|
263
|
+
|
|
264
|
+
const status = await call(base, "/api/git/status")
|
|
265
|
+
assert.equal(status.status, 200)
|
|
266
|
+
const dirty = status.body.dirty as { path: string }[]
|
|
267
|
+
assert.ok(dirty.length > 0, `board edits must read as board dirt: ${JSON.stringify(status.body)}`)
|
|
268
|
+
assert.ok(dirty.every((f) => f.path.startsWith(".task/")))
|
|
269
|
+
assert.equal(status.body.otherDirty, 0, "and must not be counted as other people's mess")
|
|
270
|
+
|
|
271
|
+
const commit = await call(base, "/api/git/commit", post({ message: "chore(board): through a link" }))
|
|
272
|
+
assert.equal(commit.status, 200, JSON.stringify(commit.body))
|
|
273
|
+
})
|
|
274
|
+
|
|
235
275
|
test("the gh-less PR fallback is the exact GitHub compare URL, branches escaped", () => {
|
|
236
276
|
assert.equal(
|
|
237
277
|
compareUrl({ owner: "nicmeriano", repo: "nickmeriano.com" }, "main", "board/2026-09-01"),
|
package/src/git-serve.ts
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
import { spawnSync } from "node:child_process"
|
|
22
|
+
import { realpathSync } from "node:fs"
|
|
22
23
|
import { relative, sep } from "node:path"
|
|
23
24
|
import { claimBranch, claimNamespace, release } from "./claim.ts"
|
|
24
25
|
import { remoteClaimTips, syncClaimRefs } from "./claim-io.ts"
|
|
@@ -31,6 +32,19 @@ export interface ServedBoard {
|
|
|
31
32
|
store: Store
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
/**
|
|
36
|
+
* A path with its symlinks resolved, or the path itself when it cannot be —
|
|
37
|
+
* a board directory that does not exist on disk yet makes `realpathSync`
|
|
38
|
+
* throw ENOENT, and that is not a reason to lose the board.
|
|
39
|
+
*/
|
|
40
|
+
function realPath(path: string): string {
|
|
41
|
+
try {
|
|
42
|
+
return realpathSync(path)
|
|
43
|
+
} catch {
|
|
44
|
+
return path
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
34
48
|
/** A failure the route turns into a JSON error with this status. */
|
|
35
49
|
export class GitServeError extends Error {
|
|
36
50
|
readonly status: number
|
|
@@ -194,11 +208,25 @@ export class GitServe {
|
|
|
194
208
|
return records
|
|
195
209
|
}
|
|
196
210
|
|
|
197
|
-
/**
|
|
211
|
+
/**
|
|
212
|
+
* Each board's `.task` dir as a toplevel-relative posix pathspec.
|
|
213
|
+
*
|
|
214
|
+
* Both sides are resolved through {@link realPath} before they are
|
|
215
|
+
* compared. `git rev-parse --show-toplevel` already returns a symlink-free
|
|
216
|
+
* path, while a store's `taskDir` is built from whatever path the process
|
|
217
|
+
* was given — so on a checkout reached through a symlink the two disagree,
|
|
218
|
+
* `relative()` answers `../../…`, and the guard below silently drops every
|
|
219
|
+
* board. That is not a corner case on macOS, where `os.tmpdir()` is
|
|
220
|
+
* `/var/folders/…` → `/private/var/folders/…`; it is also any repo under a
|
|
221
|
+
* symlinked parent. Dropping every board turns `commit` into a 400 and,
|
|
222
|
+
* worse, makes `status` report board files as `otherDirty` with an empty
|
|
223
|
+
* `dirty` list — a board that looks clean while `.task/` is full of edits.
|
|
224
|
+
*/
|
|
198
225
|
private boardDirs(top: string): Map<string, string> {
|
|
226
|
+
const root = realPath(top)
|
|
199
227
|
const dirs = new Map<string, string>()
|
|
200
228
|
for (const { id, store } of this.boards()) {
|
|
201
|
-
const rel = relative(
|
|
229
|
+
const rel = relative(root, realPath(store.taskDir)).split(sep).join("/")
|
|
202
230
|
if (rel === "" || rel.startsWith("..")) continue
|
|
203
231
|
dirs.set(id, rel)
|
|
204
232
|
}
|
package/src/index.ts
CHANGED
|
@@ -42,5 +42,15 @@ export {
|
|
|
42
42
|
type ReleaseResult,
|
|
43
43
|
} from "./claim.ts"
|
|
44
44
|
export { createTaskServer } from "./server.ts"
|
|
45
|
+
// The live proxy for an exported board: the same read routes `task serve`
|
|
46
|
+
// answers, from a GitHub repository, as one platform-neutral fetch handler.
|
|
47
|
+
// `task export` ships it as a Cloudflare Pages Function beside the snapshot;
|
|
48
|
+
// any other host gets this and a three-line adapter — see the README.
|
|
49
|
+
export {
|
|
50
|
+
createBoardHandler,
|
|
51
|
+
type BoardHandler,
|
|
52
|
+
type BoardHandlerOptions,
|
|
53
|
+
} from "./board/handler.ts"
|
|
54
|
+
export { buildSnapshot, writeExport, type ExportSummary, type WriteExportOptions } from "./export.ts"
|
|
45
55
|
export { resolveAuthor, ANONYMOUS, type Author, type AuthorSource } from "./author.ts"
|
|
46
56
|
export * from "./types.ts"
|