@astrosheep/keiyaku 4.0.3 → 4.0.5

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 (238) hide show
  1. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
  2. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
  3. package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +4 -4
  5. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +1 -1
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +15 -1
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +3 -1
  8. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +34 -13
  9. package/build/src/akuma/akuma.d.ts +34 -12
  10. package/build/src/akuma/akuma.js +118 -63
  11. package/build/src/akuma/allowed.d.ts +7 -0
  12. package/build/src/akuma/allowed.js +33 -0
  13. package/build/src/akuma/archetype.d.ts +7 -4
  14. package/build/src/akuma/archetype.js +15 -16
  15. package/build/src/akuma/body.d.ts +6 -1
  16. package/build/src/akuma/body.js +36 -27
  17. package/build/src/akuma/heart/facts.d.ts +82 -14
  18. package/build/src/akuma/heart/index.d.ts +5 -18
  19. package/build/src/akuma/heart/index.js +14 -84
  20. package/build/src/akuma/heart/request-authority.d.ts +12 -0
  21. package/build/src/akuma/heart/request-authority.js +119 -0
  22. package/build/src/akuma/heart/request-rows.d.ts +16 -0
  23. package/build/src/akuma/heart/request-rows.js +88 -0
  24. package/build/src/akuma/heart/rows.d.ts +1 -26
  25. package/build/src/akuma/heart/rows.js +0 -50
  26. package/build/src/akuma/heart/schema.d.ts +1 -1
  27. package/build/src/akuma/heart/schema.js +22 -10
  28. package/build/src/akuma/heart/soul.js +2 -36
  29. package/build/src/akuma/heart/storage.d.ts +1 -0
  30. package/build/src/akuma/heart/storage.js +15 -0
  31. package/build/src/akuma/heart/timeline.d.ts +1 -5
  32. package/build/src/akuma/heart/timeline.js +8 -17
  33. package/build/src/akuma/index.d.ts +5 -2
  34. package/build/src/akuma/index.js +1 -0
  35. package/build/src/akuma/projection.d.ts +17 -3
  36. package/build/src/akuma/projection.js +40 -3
  37. package/build/src/akuma/provider-recipe.js +1 -11
  38. package/build/src/akuma/provider.d.ts +2 -7
  39. package/build/src/akuma/provider.js +2 -1
  40. package/build/src/akuma/providers/acp/core.d.ts +3 -1
  41. package/build/src/akuma/providers/acp/core.js +10 -6
  42. package/build/src/akuma/providers/acp/events.d.ts +11 -3
  43. package/build/src/akuma/providers/acp/events.js +78 -4
  44. package/build/src/akuma/providers/acp/index.js +0 -1
  45. package/build/src/akuma/providers/claude/events.js +62 -3
  46. package/build/src/akuma/providers/claude/index.d.ts +0 -4
  47. package/build/src/akuma/providers/claude/index.js +7 -3
  48. package/build/src/akuma/providers/codex-app-server/events.js +1 -13
  49. package/build/src/akuma/providers/codex-app-server/index.d.ts +2 -27
  50. package/build/src/akuma/providers/codex-app-server/index.js +0 -2
  51. package/build/src/akuma/providers/grok-build/index.d.ts +2 -1
  52. package/build/src/akuma/providers/grok-build/index.js +72 -3
  53. package/build/src/akuma/providers/opencode-sdk/events.js +71 -6
  54. package/build/src/akuma/providers/opencode-sdk/index.d.ts +2 -28
  55. package/build/src/akuma/providers/opencode-sdk/index.js +8 -4
  56. package/build/src/akuma/providers/opencode-sdk/session.d.ts +1 -1
  57. package/build/src/akuma/providers/opencode-sdk/session.js +1 -1
  58. package/build/src/akuma/providers/pi/events.js +17 -3
  59. package/build/src/akuma/providers/pi/index.js +11 -4
  60. package/build/src/akuma/providers/unified-patch.d.ts +4 -0
  61. package/build/src/akuma/providers/unified-patch.js +14 -0
  62. package/build/src/akuma/publication.d.ts +1 -1
  63. package/build/src/akuma/publication.js +1 -1
  64. package/build/src/akuma/request-serve.d.ts +87 -0
  65. package/build/src/akuma/request-serve.js +484 -0
  66. package/build/src/akuma/request-wire.d.ts +124 -0
  67. package/build/src/akuma/request-wire.js +380 -0
  68. package/build/src/akuma/requests.d.ts +41 -41
  69. package/build/src/akuma/requests.js +75 -374
  70. package/build/src/akuma-body.d.ts +8 -0
  71. package/build/src/akuma-body.js +84 -0
  72. package/build/src/body/arc.js +4 -29
  73. package/build/src/body/decode.js +5 -39
  74. package/build/src/body/envelope.d.ts +7 -0
  75. package/build/src/body/envelope.js +37 -0
  76. package/build/src/cli/accepted.d.ts +4 -1
  77. package/build/src/cli/accepted.js +39 -9
  78. package/build/src/cli/commands/akuma-invoke.d.ts +8 -7
  79. package/build/src/cli/commands/akuma-invoke.js +18 -10
  80. package/build/src/cli/commands/akuma.d.ts +11 -2
  81. package/build/src/cli/commands/akuma.js +67 -28
  82. package/build/src/cli/commands/amend.d.ts +1 -1
  83. package/build/src/cli/commands/amend.js +1 -1
  84. package/build/src/cli/commands/contract.d.ts +8 -8
  85. package/build/src/cli/commands/contract.js +3 -3
  86. package/build/src/cli/commands/install.js +2 -2
  87. package/build/src/cli/commands/task-invoke.js +98 -9
  88. package/build/src/cli/commands/task-query.d.ts +1 -1
  89. package/build/src/cli/commands/task-query.js +11 -5
  90. package/build/src/cli/coordinates.d.ts +2 -0
  91. package/build/src/cli/coordinates.js +12 -5
  92. package/build/src/cli/invoke.d.ts +1 -0
  93. package/build/src/cli/invoke.js +116 -75
  94. package/build/src/cli/main.d.ts +2 -0
  95. package/build/src/cli/main.js +29 -4
  96. package/build/src/cli/parse.d.ts +3 -2
  97. package/build/src/cli/parse.js +25 -8
  98. package/build/src/cli/render/akuma.d.ts +2 -2
  99. package/build/src/cli/render/akuma.js +138 -24
  100. package/build/src/cli/render/audit.js +18 -8
  101. package/build/src/cli/render/contract.d.ts +2 -0
  102. package/build/src/cli/render/contract.js +243 -36
  103. package/build/src/cli/render/kanshi.js +64 -29
  104. package/build/src/cli/render/receipt.d.ts +2 -1
  105. package/build/src/cli/render/receipt.js +37 -2
  106. package/build/src/cli/render/terminal.d.ts +0 -2
  107. package/build/src/cli/render/terminal.js +0 -29
  108. package/build/src/cli/render/text.js +3 -1
  109. package/build/src/cli/result.d.ts +52 -9
  110. package/build/src/cli/selectors.d.ts +1 -0
  111. package/build/src/cli/selectors.js +9 -6
  112. package/build/src/contract-worktree.d.ts +1 -2
  113. package/build/src/contract-worktree.js +7 -9
  114. package/build/src/core/facts/codec.js +9 -0
  115. package/build/src/core/facts/fold.js +13 -1
  116. package/build/src/core/facts/types.d.ts +8 -1
  117. package/build/src/core/subject.js +4 -3
  118. package/build/src/core/verbs/placement.d.ts +9 -1
  119. package/build/src/core/verbs/placement.js +24 -6
  120. package/build/src/core/verbs/reintegrate.d.ts +13 -0
  121. package/build/src/core/verbs/reintegrate.js +24 -0
  122. package/build/src/dispatch/index.d.ts +1 -1
  123. package/build/src/dispatch/index.js +50 -39
  124. package/build/src/git/admission.d.ts +2 -1
  125. package/build/src/git/hooks.d.ts +0 -4
  126. package/build/src/git/hooks.js +0 -36
  127. package/build/src/git/integration.d.ts +7 -8
  128. package/build/src/git/integration.js +62 -9
  129. package/build/src/git/observe.d.ts +2 -1
  130. package/build/src/git/observe.js +6 -5
  131. package/build/src/git/process.d.ts +26 -0
  132. package/build/src/git/process.js +94 -0
  133. package/build/src/git/read-observation.d.ts +2 -1
  134. package/build/src/git/read-observation.js +3 -2
  135. package/build/src/git/reconcile.d.ts +8 -2
  136. package/build/src/git/reconcile.js +63 -14
  137. package/build/src/git/repository.d.ts +2 -29
  138. package/build/src/git/repository.js +4 -118
  139. package/build/src/git/scratch.d.ts +1 -1
  140. package/build/src/git/scratch.js +1 -1
  141. package/build/src/git/target-placement.d.ts +1 -1
  142. package/build/src/git/target-placement.js +9 -7
  143. package/build/src/git/tender.d.ts +20 -5
  144. package/build/src/git/tender.js +45 -13
  145. package/build/src/git/terminal-seal.d.ts +16 -2
  146. package/build/src/git/terminal-seal.js +15 -5
  147. package/build/src/git/workspace.d.ts +2 -1
  148. package/build/src/git/workspace.js +13 -5
  149. package/build/src/index.d.ts +1 -2
  150. package/build/src/index.js +1 -1
  151. package/build/src/kanshi/index.d.ts +1 -1
  152. package/build/src/kanshi/index.js +1 -1
  153. package/build/src/kanshi/read.d.ts +9 -1
  154. package/build/src/kanshi/read.js +91 -37
  155. package/build/src/kanshi/report.d.ts +1 -0
  156. package/build/src/library/address.d.ts +5 -14
  157. package/build/src/library/address.js +27 -23
  158. package/build/src/library/akuma-creation.d.ts +4 -2
  159. package/build/src/library/akuma-creation.js +43 -26
  160. package/build/src/library/audit.d.ts +2 -1
  161. package/build/src/library/audit.js +1 -1
  162. package/build/src/library/bind.d.ts +2 -1
  163. package/build/src/library/bind.js +4 -4
  164. package/build/src/library/catalog.d.ts +1 -2
  165. package/build/src/library/catalog.js +11 -6
  166. package/build/src/library/configuration.d.ts +5 -1
  167. package/build/src/library/configuration.js +71 -18
  168. package/build/src/library/contract.d.ts +70 -3
  169. package/build/src/library/contract.js +213 -27
  170. package/build/src/library/delivery.d.ts +1 -1
  171. package/build/src/library/fleet.d.ts +70 -12
  172. package/build/src/library/fleet.js +236 -56
  173. package/build/src/library/input.d.ts +0 -1
  174. package/build/src/library/input.js +1 -5
  175. package/build/src/library/keiyaku.d.ts +4 -4
  176. package/build/src/library/keiyaku.js +1 -2
  177. package/build/src/library/mutation.d.ts +2 -1
  178. package/build/src/library/reconcile.d.ts +2 -1
  179. package/build/src/library/reconcile.js +3 -1
  180. package/build/src/library/region.d.ts +1 -1
  181. package/build/src/library/repo.d.ts +3 -1
  182. package/build/src/library/repo.js +8 -4
  183. package/build/src/protocol/abandon.d.ts +9 -0
  184. package/build/src/protocol/abandon.js +15 -0
  185. package/build/src/protocol/amend.d.ts +16 -0
  186. package/build/src/protocol/amend.js +77 -0
  187. package/build/src/protocol/arc.d.ts +6 -0
  188. package/build/src/protocol/arc.js +12 -0
  189. package/build/src/protocol/attempt.d.ts +2 -1
  190. package/build/src/protocol/audit.d.ts +53 -0
  191. package/build/src/protocol/audit.js +140 -0
  192. package/build/src/protocol/bind.d.ts +2 -3
  193. package/build/src/protocol/bind.js +2 -16
  194. package/build/src/protocol/completion.d.ts +32 -0
  195. package/build/src/protocol/completion.js +130 -0
  196. package/build/src/protocol/deliver.d.ts +37 -0
  197. package/build/src/protocol/deliver.js +160 -0
  198. package/build/src/protocol/intent.d.ts +1 -1
  199. package/build/src/protocol/intent.js +1 -1
  200. package/build/src/protocol/operations.d.ts +65 -198
  201. package/build/src/protocol/operations.js +21 -634
  202. package/build/src/protocol/placement.d.ts +4 -13
  203. package/build/src/protocol/placement.js +3 -15
  204. package/build/src/protocol/read/status.d.ts +4 -2
  205. package/build/src/protocol/read/status.js +27 -15
  206. package/build/src/protocol/reconcile.d.ts +35 -0
  207. package/build/src/protocol/reconcile.js +41 -0
  208. package/build/src/protocol/reintegrate.d.ts +31 -0
  209. package/build/src/protocol/reintegrate.js +74 -0
  210. package/build/src/protocol/review.d.ts +33 -0
  211. package/build/src/protocol/review.js +148 -0
  212. package/build/src/protocol/run.d.ts +1 -1
  213. package/build/src/runtime/proc/run.d.ts +1 -0
  214. package/build/src/runtime/proc/run.js +9 -2
  215. package/build/src/settlement/fence.d.ts +1 -1
  216. package/build/src/settlement/holder.d.ts +1 -1
  217. package/build/src/settlement/settle.d.ts +2 -1
  218. package/build/src/settlement/settle.js +4 -1
  219. package/build/src/task/board.d.ts +18 -4
  220. package/build/src/task/board.js +73 -29
  221. package/build/src/task/compose.d.ts +1 -1
  222. package/build/src/task/compose.js +11 -4
  223. package/build/src/task/index.d.ts +4 -4
  224. package/build/src/task/index.js +8 -131
  225. package/build/src/task/input.d.ts +18 -0
  226. package/build/src/task/input.js +189 -0
  227. package/build/src/task/mutation.d.ts +39 -0
  228. package/build/src/task/mutation.js +85 -0
  229. package/build/src/task/operations.d.ts +4 -19
  230. package/build/src/task/operations.js +64 -18
  231. package/build/src/task/query.d.ts +9 -2
  232. package/build/src/task/query.js +32 -64
  233. package/build/src/task/store.js +0 -2
  234. package/build/src/verification/execution.d.ts +1 -1
  235. package/build/src/verification/execution.js +2 -1
  236. package/build/src/workspace-place.d.ts +1 -4
  237. package/build/src/workspace-place.js +0 -15
  238. package/package.json +5 -3
@@ -54,11 +54,19 @@ export type TaskDoctorIssue = Readonly<{
54
54
  relation: "needs" | "parent" | "supersedes";
55
55
  tasks: readonly TaskId[];
56
56
  }>;
57
+ export declare function taskRef(board: TaskBoard, id: TaskId): TaskRef;
58
+ export type TaskRelationProjection = Readonly<{
59
+ children(id: TaskId): readonly TaskRef[];
60
+ blocks(id: TaskId): readonly TaskRef[];
61
+ supersededBy(id: TaskId): readonly TaskRef[];
62
+ related(id: TaskId): readonly TaskRef[];
63
+ }>;
64
+ export declare function createTaskRelations(board: TaskBoard): TaskRelationProjection;
57
65
  export declare function taskDisposition(board: TaskBoard, task: TaskDocument): TaskDisposition;
58
- export declare function projectDetailFacts(board: TaskBoard, id: TaskId): TaskDetailFacts | null;
66
+ export declare function taskBlocked(board: TaskBoard, task: TaskDocument): boolean;
67
+ export declare function projectDetailFacts(board: TaskBoard, id: TaskId, relations: TaskRelationProjection): TaskDetailFacts | null;
59
68
  export declare function projectRows(board: TaskBoard, scope: readonly string[] | null, selection: "active" | "closed" | "all"): readonly TaskRow[];
60
- export declare function projectReady(board: TaskBoard, scope: readonly string[] | null): readonly TaskRow[];
61
- export declare function projectBlocked(board: TaskBoard, scope: readonly string[] | null): readonly BlockedTaskRow[];
69
+ export declare function projectBlocked(board: TaskBoard, scope: readonly string[] | null, relations: TaskRelationProjection): readonly BlockedTaskRow[];
62
70
  export declare function projectStatusRows(board: TaskBoard, scope: readonly string[] | null): (Readonly<{
63
71
  id: TaskId;
64
72
  title: string;
@@ -77,7 +85,13 @@ export declare function projectStatusRows(board: TaskBoard, scope: readonly stri
77
85
  priority: TaskPriority;
78
86
  disposition: TaskDisposition;
79
87
  })[];
88
+ export type TaskBoardObservation = Readonly<{
89
+ statusRows: ReturnType<typeof projectStatusRows>;
90
+ selectNamespace(namespace: readonly string[]): readonly TaskRow[];
91
+ selectCreatedBy(createdBy: string): readonly TaskRow[];
92
+ }>;
93
+ export declare function projectTaskBoardObservation(board: TaskBoard): TaskBoardObservation;
80
94
  export declare function relationProblem(board: TaskBoard, before: TaskDocument | null, after: TaskDocument): string | null;
81
95
  export declare function diagnoseBoard(board: TaskBoard): readonly TaskDoctorIssue[];
82
- export declare function buildTree(board: TaskBoard, root: TaskId): TaskTreeNode | null;
96
+ export declare function buildTree(board: TaskBoard, root: TaskId, relations: TaskRelationProjection): TaskTreeNode | null;
83
97
  export {};
@@ -1,42 +1,81 @@
1
1
  import { parseTaskId, sameNamespace } from "./identity.js";
2
2
  const terminal = (state) => state === "done" || state === "drop";
3
+ const none = Object.freeze([]);
3
4
  const sorted = (values) => [...values].sort((a, b) => Buffer.compare(Buffer.from(a.id), Buffer.from(b.id)));
4
- function taskRef(board, id) {
5
+ export function taskRef(board, id) {
5
6
  const task = board.tasks.get(id);
6
7
  return task === undefined ? { id, title: null, state: "missing" } : { id, title: task.title, state: task.state };
7
8
  }
8
- function outgoing(document, relation) { return document[relation]; }
9
- function reverse(board, id, relation) {
10
- return sorted([...board.tasks.values()].filter((task) => outgoing(task, relation).includes(id)).map((task) => taskRef(board, task.id)));
9
+ function append(index, key, value) {
10
+ const values = index.get(key);
11
+ if (values === undefined)
12
+ index.set(key, [value]);
13
+ else
14
+ values.push(value);
11
15
  }
12
- function children(board, id) {
13
- return sorted([...board.tasks.values()].filter((task) => task.parent === id).map((task) => taskRef(board, task.id)));
16
+ function freezeRefs(board, index) {
17
+ return new Map([...index].map(([id, targets]) => [id, sorted(targets.map((target) => taskRef(board, target)))]));
14
18
  }
15
- function related(board, task) {
16
- const ids = new Set(task.relates);
17
- for (const candidate of board.tasks.values())
18
- if (candidate.relates.includes(task.id))
19
- ids.add(candidate.id);
20
- return sorted([...ids].map((id) => taskRef(board, id)));
19
+ export function createTaskRelations(board) {
20
+ const children = new Map();
21
+ const blocks = new Map();
22
+ const supersededBy = new Map();
23
+ const incoming = new Map();
24
+ for (const task of board.tasks.values()) {
25
+ if (task.parent !== null)
26
+ append(children, task.parent, task.id);
27
+ for (const need of task.needs)
28
+ append(blocks, need, task.id);
29
+ for (const target of task.supersedes)
30
+ append(supersededBy, target, task.id);
31
+ for (const target of task.relates)
32
+ append(incoming, target, task.id);
33
+ }
34
+ const childRefs = freezeRefs(board, children);
35
+ const blockRefs = freezeRefs(board, blocks);
36
+ const successorRefs = freezeRefs(board, supersededBy);
37
+ const incomingRefs = freezeRefs(board, incoming);
38
+ return {
39
+ children: (id) => childRefs.get(id) ?? none,
40
+ blocks: (id) => blockRefs.get(id) ?? none,
41
+ supersededBy: (id) => successorRefs.get(id) ?? none,
42
+ related: (id) => {
43
+ const task = board.tasks.get(id);
44
+ const ids = new Set(task?.relates ?? []);
45
+ for (const ref of incomingRefs.get(id) ?? none)
46
+ ids.add(ref.id);
47
+ return ids.size === 0 ? none : sorted([...ids].map((related) => taskRef(board, related)));
48
+ },
49
+ };
50
+ }
51
+ function needReleased(board, id) {
52
+ const target = board.tasks.get(id);
53
+ return target !== undefined && terminal(target.state);
54
+ }
55
+ function hasUnresolvedNeeds(board, task) {
56
+ return task.needs.some((id) => !needReleased(board, id));
21
57
  }
22
58
  export function taskDisposition(board, task) {
23
59
  if (task.state !== "open")
24
60
  return task.state;
25
- return task.needs.every((id) => { const target = board.tasks.get(id); return target !== undefined && terminal(target.state); }) ? "ready" : "blocked";
61
+ return hasUnresolvedNeeds(board, task) ? "blocked" : "ready";
26
62
  }
27
- export function projectDetailFacts(board, id) {
63
+ export function taskBlocked(board, task) {
64
+ return (task.state === "open" || task.state === "in_progress") && hasUnresolvedNeeds(board, task);
65
+ }
66
+ export function projectDetailFacts(board, id, relations) {
28
67
  const task = board.tasks.get(id);
29
68
  if (task === undefined)
30
69
  return null;
31
- const needs = task.needs.map((need) => ({ ...taskRef(board, need), released: board.tasks.has(need) && terminal(board.tasks.get(need).state) }));
70
+ const needs = task.needs.map((need) => ({ ...taskRef(board, need), released: needReleased(board, need) }));
32
71
  return {
33
72
  task,
34
73
  needs,
35
74
  blockers: needs.filter((need) => !need.released).map(({ released: _released, ...ref }) => ref),
36
- blocks: reverse(board, id, "needs"),
37
- parent: task.parent === null ? null : taskRef(board, task.parent), children: children(board, id),
38
- supersedes: task.supersedes.map((target) => taskRef(board, target)), supersededBy: reverse(board, id, "supersedes"),
39
- related: related(board, task),
75
+ blocks: relations.blocks(id),
76
+ parent: task.parent === null ? null : taskRef(board, task.parent), children: relations.children(id),
77
+ supersedes: task.supersedes.map((target) => taskRef(board, target)), supersededBy: relations.supersededBy(id),
78
+ related: relations.related(id),
40
79
  };
41
80
  }
42
81
  function inScope(task, scope) { return scope === null || sameNamespace(parseTaskId(task.id).namespace, scope); }
@@ -47,24 +86,29 @@ export function projectRows(board, scope, selection) {
47
86
  disposition: taskDisposition(board, task),
48
87
  })).sort((a, b) => a.priority - b.priority || Buffer.compare(Buffer.from(a.id), Buffer.from(b.id)));
49
88
  }
50
- export function projectReady(board, scope) {
51
- return projectRows(board, scope, "active").filter((row) => row.disposition === "ready");
52
- }
53
- export function projectBlocked(board, scope) {
89
+ export function projectBlocked(board, scope, relations) {
54
90
  return projectRows(board, scope, "active").flatMap((row) => {
55
- if (row.state !== "open" && row.state !== "in_progress")
91
+ if (!taskBlocked(board, board.tasks.get(row.id)))
56
92
  return [];
57
- const blockers = projectDetailFacts(board, row.id).blockers;
58
- return blockers.length === 0 ? [] : [{ ...row, blockers }];
93
+ return [{ ...row, blockers: projectDetailFacts(board, row.id, relations).blockers }];
59
94
  });
60
95
  }
61
96
  export function projectStatusRows(board, scope) {
62
- const blockers = new Map(projectBlocked(board, scope).map((row) => [row.id, row.blockers]));
97
+ const relations = createTaskRelations(board);
98
+ const blockers = new Map(projectBlocked(board, scope, relations).map((row) => [row.id, row.blockers]));
63
99
  return projectRows(board, scope, "all").map((row) => {
64
100
  const unresolved = blockers.get(row.id);
65
101
  return unresolved === undefined ? row : { ...row, blockers: unresolved };
66
102
  });
67
103
  }
104
+ export function projectTaskBoardObservation(board) {
105
+ const rows = projectRows(board, null, "all");
106
+ return {
107
+ statusRows: projectStatusRows(board, null),
108
+ selectNamespace: (namespace) => projectRows(board, namespace, "all"),
109
+ selectCreatedBy: (createdBy) => rows.filter((row) => board.tasks.get(row.id)?.createdBy === createdBy),
110
+ };
111
+ }
68
112
  function relationIds(task, relation) {
69
113
  return relation === "parent" ? task.parent === null ? [] : [task.parent] : task[relation];
70
114
  }
@@ -147,7 +191,7 @@ export function diagnoseBoard(board) {
147
191
  }
148
192
  return issues;
149
193
  }
150
- export function buildTree(board, root) {
194
+ export function buildTree(board, root, relations) {
151
195
  if (!board.tasks.has(root))
152
196
  return null;
153
197
  const walk = (id, path) => {
@@ -156,7 +200,7 @@ export function buildTree(board, root) {
156
200
  const node = { task: { ...ref, priority: task?.priority ?? null } };
157
201
  if (path.includes(id))
158
202
  return { ...node, cycle: true, children: [] };
159
- return { ...node, children: children(board, id).map((child) => walk(child.id, [...path, id])) };
203
+ return { ...node, children: relations.children(id).map((child) => walk(child.id, [...path, id])) };
160
204
  };
161
205
  return walk(root, []);
162
206
  }
@@ -1,6 +1,6 @@
1
1
  import { type TaskId } from "./identity.js";
2
2
  import type { WorldRoot } from "../world.js";
3
- import type { TaskRefusal, TaskRetry } from "./operations.js";
3
+ import { type TaskRefusal, type TaskRetry } from "./operations.js";
4
4
  type TaskDocumentChange = Readonly<{
5
5
  taskId: TaskId;
6
6
  kind: "created" | "updated";
@@ -5,6 +5,7 @@ import { relationProblem } from "./board.js";
5
5
  import { serializeTaskDocument } from "./document.js";
6
6
  import { allocateLocalId, deriveLocalStem, formatTaskId, parseTaskId, sameNamespace } from "./identity.js";
7
7
  import { authorityPath, readBoard, replaceAuthority, withTaskLocks } from "./store.js";
8
+ import { advanceTaskTimestamp } from "./operations.js";
8
9
  function failure(diagnostic) { return { kind: "invalid-composition", diagnostic }; }
9
10
  function parseNamespace(value) {
10
11
  if (value === "")
@@ -118,8 +119,9 @@ function applyAssignments(document, assignments, body) {
118
119
  }
119
120
  return body === undefined ? next : { ...next, body };
120
121
  }
121
- function currentTimestamp() { return new Date().toISOString(); }
122
- function advancedTimestamp(previous, current) { return current > previous ? current : new Date(Date.parse(previous) + 1).toISOString(); }
122
+ function currentTimestamp() {
123
+ return new Date().toISOString();
124
+ }
123
125
  function createdTask(id, title, at, actor) {
124
126
  return {
125
127
  id, title, state: "open", priority: 2, needs: [], parent: null, supersedes: [], relates: [], note: "",
@@ -164,8 +166,13 @@ function plan(sketch, board, defaultNamespace, at, actor) {
164
166
  current = { ...current, parent };
165
167
  }
166
168
  current = applyAssignments(current, node.assignments, node.body);
167
- if (before !== null && !Buffer.from(serializeTaskDocument(current)).equals(Buffer.from(serializeTaskDocument(before))))
168
- current = { ...current, updatedAt: advancedTimestamp(before.updatedAt, at) };
169
+ if (before !== null
170
+ && !Buffer.from(serializeTaskDocument(current)).equals(Buffer.from(serializeTaskDocument(before)))) {
171
+ current = {
172
+ ...current,
173
+ updatedAt: advanceTaskTimestamp(before.updatedAt, at),
174
+ };
175
+ }
169
176
  byDepth[node.depth] = current.id;
170
177
  byDepth.length = node.depth + 1;
171
178
  all.set(current.id, current);
@@ -3,9 +3,9 @@ export type { WorldRoot } from "../world.js";
3
3
  import { type BlockedTaskRow, type TaskDetailFacts, type TaskDoctorIssue, type TaskRef, type TaskRow, type TaskTreeNode } from "./board.js";
4
4
  import { type TaskCompositionResult } from "./compose.js";
5
5
  import { TaskAuthorityCorruptionError, type TaskPriority, type TaskState } from "./document.js";
6
- import { type TaskId } from "./identity.js";
6
+ import type { TaskId } from "./identity.js";
7
7
  import { type AddTaskDocumentInput, type AddTaskInput, type TaskBatchResult, type TaskMutationResult, type TaskOutcome, type TaskRefusal, type TaskRetry, type TaskUpdateResult, type TaskView, type UpdateTaskInput } from "./operations.js";
8
- import { type TaskPage, type TaskQueryExpression, type TaskQueryPredicate, type TaskQueryRow, type TaskQuerySort } from "./query.js";
8
+ import { TASK_RELATION_PREDICATE_FIELDS, type TaskPage, type TaskQueryExpression, type TaskQueryPredicate, type TaskQueryRow, type TaskQuerySort, type TaskRelationPredicateField } from "./query.js";
9
9
  export type TaskDetail = Omit<TaskDetailFacts, "task"> & Readonly<{
10
10
  task: TaskView;
11
11
  }>;
@@ -17,8 +17,8 @@ export type TaskDoctorReport = Readonly<{
17
17
  issues: readonly TaskDoctorIssue[];
18
18
  }>;
19
19
  export type TaskDecompositionTree = TaskOutcome<TaskTreeNode>;
20
- export type { AddTaskDocumentInput, AddTaskInput, BlockedTaskRow, TaskBatchResult, TaskCompositionResult, TaskDoctorIssue, TaskId, TaskMutationResult, TaskOutcome, TaskPriority, TaskRef, TaskRefusal, TaskRetry, TaskRow, TaskState, TaskTreeNode, TaskUpdateResult, TaskView, UpdateTaskInput, TaskPage, TaskQueryExpression, TaskQueryPredicate, TaskQueryRow, TaskQuerySort };
21
- export { TaskAuthorityCorruptionError };
20
+ export type { AddTaskDocumentInput, AddTaskInput, BlockedTaskRow, TaskBatchResult, TaskCompositionResult, TaskDoctorIssue, TaskId, TaskMutationResult, TaskOutcome, TaskPriority, TaskRef, TaskRefusal, TaskRetry, TaskRow, TaskState, TaskTreeNode, TaskUpdateResult, TaskView, UpdateTaskInput, TaskPage, TaskQueryExpression, TaskQueryPredicate, TaskQueryRow, TaskQuerySort, TaskRelationPredicateField, };
21
+ export { TaskAuthorityCorruptionError, TASK_RELATION_PREDICATE_FIELDS };
22
22
  declare class TaskHandle {
23
23
  readonly id: TaskId;
24
24
  private readonly world;
@@ -1,136 +1,11 @@
1
- import { buildTree, diagnoseBoard, projectDetailFacts } from "./board.js";
1
+ import { buildTree, createTaskRelations, diagnoseBoard, projectDetailFacts, } from "./board.js";
2
2
  import { composeTasks } from "./compose.js";
3
3
  import { TaskAuthorityCorruptionError } from "./document.js";
4
- import { isTaskSegment, parseTaskId } from "./identity.js";
5
4
  import { addTask, addTaskDocument, batchTasks, blockedTasks, currentNamespace, lifecycleTask, listTasks, queryTasks, readyTasks, setCurrentNamespace, taskView, updateTask, } from "./operations.js";
6
5
  import { readBoard } from "./store.js";
7
- import { isValidTaskLimit, normalizeTaskQuery, MAX_TASK_LIMIT, DEFAULT_TASK_LIMIT } from "./query.js";
8
- export { TaskAuthorityCorruptionError };
9
- function record(value, label) {
10
- if (typeof value !== "object" || value === null || Array.isArray(value))
11
- throw new TypeError(`${label} must be an object`);
12
- return value;
13
- }
14
- function closed(value, allowed, label) {
15
- for (const key of Object.keys(value))
16
- if (!allowed.includes(key))
17
- throw new TypeError(`${label} has unknown field: ${key}`);
18
- }
19
- function id(value) { if (typeof value !== "string")
20
- throw new TypeError("task ID must be a string"); parseTaskId(value); return value; }
21
- function signal(value) {
22
- if (value === undefined)
23
- return undefined;
24
- if (!(value instanceof AbortSignal))
25
- throw new TypeError("signal must be an AbortSignal");
26
- return value;
27
- }
28
- function strings(value, label) {
29
- if (value === undefined)
30
- return undefined;
31
- if (!Array.isArray(value) || !value.every((item) => typeof item === "string"))
32
- throw new TypeError(`${label} must be an array of strings`);
33
- return value;
34
- }
35
- function taskIds(value, label) {
36
- const values = strings(value, label)?.map(id);
37
- if (values !== undefined && new Set(values).size !== values.length)
38
- throw new TypeError(`${label} must not contain duplicates`);
39
- return values;
40
- }
41
- function namespace(value) {
42
- const values = strings(value, "namespace");
43
- if (values === undefined)
44
- return undefined;
45
- if (!values.every(isTaskSegment))
46
- throw new TypeError("namespace must contain canonical segments");
47
- return values;
48
- }
49
- function text(value, label) { if (value === undefined)
50
- return undefined; if (typeof value !== "string")
51
- throw new TypeError(`${label} must be a string`); return value; }
52
- function actor(value) {
53
- if (value === undefined)
54
- return undefined;
55
- if (typeof value !== "string" || value.trim().length === 0)
56
- throw new TypeError("actor must be a nonblank string");
57
- return value;
58
- }
59
- function priority(value) { if (value === undefined)
60
- return undefined; if (!Number.isInteger(value) || typeof value !== "number" || value < 0 || value > 3)
61
- throw new TypeError("priority must be 0..3"); return value; }
62
- function limit(value) { if (value === undefined)
63
- return undefined; if (typeof value !== "number" || !isValidTaskLimit(value))
64
- throw new TypeError(`limit must be an integer from 1 to ${MAX_TASK_LIMIT}`); return value; }
65
- function sort(value) {
66
- if (value === undefined)
67
- return undefined;
68
- if (value !== "priority" && value !== "created" && value !== "updated" && value !== "id")
69
- throw new TypeError("sort is invalid");
70
- return value;
71
- }
72
- function state(value) {
73
- if (value === undefined)
74
- return undefined;
75
- if (value !== "open" && value !== "in_progress" && value !== "on_hold" && value !== "done" && value !== "drop")
76
- throw new TypeError("state is invalid");
77
- return value;
78
- }
79
- function nullableId(value) { return value === undefined || value === null ? value : id(value); }
80
- function addInput(input) {
81
- const v = record(input, "add input");
82
- closed(v, ["title", "namespace", "body", "note", "state", "priority", "needs", "parent", "supersedes", "relates", "actor", "signal"], "add input");
83
- const title = text(v.title, "title");
84
- if (title === undefined || title.trim() === "")
85
- throw new TypeError("title is required");
86
- const ns = namespace(v.namespace), body = text(v.body, "body"), note = text(v.note, "note"), initialState = state(v.state), pri = priority(v.priority);
87
- const needs = taskIds(v.needs, "needs"), parent = nullableId(v.parent), supersedes = taskIds(v.supersedes, "supersedes"), relates = taskIds(v.relates, "relates");
88
- const createdBy = actor(v.actor), abort = signal(v.signal);
89
- return { title, ...(ns === undefined ? {} : { namespace: ns }), ...(body === undefined ? {} : { body }), ...(note === undefined ? {} : { note }),
90
- ...(initialState === undefined ? {} : { state: initialState }), ...(pri === undefined ? {} : { priority: pri }),
91
- ...(needs === undefined ? {} : { needs }), ...(parent === undefined ? {} : { parent }),
92
- ...(supersedes === undefined ? {} : { supersedes }), ...(relates === undefined ? {} : { relates }),
93
- ...(createdBy === undefined ? {} : { actor: createdBy }),
94
- ...(abort === undefined ? {} : { signal: abort }) };
95
- }
96
- function updateInput(input) {
97
- const v = record(input, "update input");
98
- closed(v, ["title", "body", "appendBody", "note", "priority", "needs", "addNeeds", "dropNeeds", "parent", "supersedes", "addSupersedes", "dropSupersedes", "relates", "addRelates", "dropRelates", "signal"], "update input");
99
- if (v.body !== undefined && v.appendBody !== undefined)
100
- throw new TypeError("body and appendBody are mutually exclusive");
101
- const result = {};
102
- const title = text(v.title, "title");
103
- if (title !== undefined) {
104
- if (title.trim().length === 0)
105
- throw new TypeError("title must be nonblank");
106
- result.title = title;
107
- }
108
- const body = text(v.body, "body");
109
- if (body !== undefined)
110
- result.body = body;
111
- const appendBody = text(v.appendBody, "appendBody");
112
- if (appendBody !== undefined)
113
- result.appendBody = appendBody;
114
- const note = text(v.note, "note");
115
- if (note !== undefined)
116
- result.note = note;
117
- const pri = priority(v.priority);
118
- if (pri !== undefined)
119
- result.priority = pri;
120
- for (const [key, value] of [["needs", taskIds(v.needs, "needs")], ["addNeeds", taskIds(v.addNeeds, "addNeeds")], ["dropNeeds", taskIds(v.dropNeeds, "dropNeeds")], ["supersedes", taskIds(v.supersedes, "supersedes")], ["addSupersedes", taskIds(v.addSupersedes, "addSupersedes")], ["dropSupersedes", taskIds(v.dropSupersedes, "dropSupersedes")], ["relates", taskIds(v.relates, "relates")], ["addRelates", taskIds(v.addRelates, "addRelates")], ["dropRelates", taskIds(v.dropRelates, "dropRelates")]])
121
- if (value !== undefined)
122
- result[key] = value;
123
- const parent = nullableId(v.parent);
124
- if (parent !== undefined)
125
- result.parent = parent;
126
- const abort = signal(v.signal);
127
- if (abort !== undefined)
128
- result.signal = abort;
129
- const update = result;
130
- if (Object.keys(result).filter((key) => key !== "signal").length === 0)
131
- throw new TypeError("update requires at least one field change");
132
- return update;
133
- }
6
+ import { actor, addInput, closed, limit, namespace, record, signal, sort, taskId as id, taskIds, text, updateInput, } from "./input.js";
7
+ import { normalizeTaskQuery, TASK_RELATION_PREDICATE_FIELDS, DEFAULT_TASK_LIMIT, } from "./query.js";
8
+ export { TaskAuthorityCorruptionError, TASK_RELATION_PREDICATE_FIELDS };
134
9
  class TaskHandle {
135
10
  id;
136
11
  world;
@@ -139,13 +14,15 @@ class TaskHandle {
139
14
  this.world = world;
140
15
  }
141
16
  async read() {
142
- const facts = projectDetailFacts((await readBoard(this.world)).board, this.id);
17
+ const board = (await readBoard(this.world)).board;
18
+ const facts = projectDetailFacts(board, this.id, createTaskRelations(board));
143
19
  return facts === null ? null : { ...facts, task: taskView(facts.task) };
144
20
  }
145
21
  async tree() {
146
22
  if (arguments.length > 0)
147
23
  throw new TypeError("tree accepts no input");
148
- const node = buildTree((await readBoard(this.world)).board, this.id);
24
+ const board = (await readBoard(this.world)).board;
25
+ const node = buildTree(board, this.id, createTaskRelations(board));
149
26
  return node === null ? { kind: "refused", refusal: { kind: "task-missing", taskId: this.id } } : { kind: "accepted", value: node };
150
27
  }
151
28
  update(input) { return updateTask(this.world, this.id, updateInput(input)); }
@@ -0,0 +1,18 @@
1
+ import { type TaskPriority, type TaskState } from "./document.js";
2
+ import { type TaskId } from "./identity.js";
3
+ import type { AddTaskInput, UpdateTaskInput } from "./operations.js";
4
+ import { type TaskQuerySort } from "./query.js";
5
+ export declare function record(value: unknown, label: string): Record<string, unknown>;
6
+ export declare function closed(value: Record<string, unknown>, allowed: readonly string[], label: string): void;
7
+ export declare function taskId(value: unknown): TaskId;
8
+ export declare function signal(value: unknown): AbortSignal | undefined;
9
+ export declare function taskIds(value: unknown, label: string): readonly TaskId[] | undefined;
10
+ export declare function namespace(value: unknown): readonly string[] | undefined;
11
+ export declare function text(value: unknown, label: string): string | undefined;
12
+ export declare function actor(value: unknown): string | undefined;
13
+ export declare function priority(value: unknown): TaskPriority | undefined;
14
+ export declare function limit(value: unknown): number | undefined;
15
+ export declare function sort(value: unknown): TaskQuerySort | undefined;
16
+ export declare function state(value: unknown): TaskState | undefined;
17
+ export declare function addInput(input: unknown): AddTaskInput;
18
+ export declare function updateInput(input: unknown): UpdateTaskInput;
@@ -0,0 +1,189 @@
1
+ import { isTaskSegment, parseTaskId } from "./identity.js";
2
+ import { isValidTaskLimit, MAX_TASK_LIMIT } from "./query.js";
3
+ export function record(value, label) {
4
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
5
+ throw new TypeError(`${label} must be an object`);
6
+ }
7
+ return value;
8
+ }
9
+ export function closed(value, allowed, label) {
10
+ for (const key of Object.keys(value)) {
11
+ if (!allowed.includes(key))
12
+ throw new TypeError(`${label} has unknown field: ${key}`);
13
+ }
14
+ }
15
+ export function taskId(value) {
16
+ if (typeof value !== "string")
17
+ throw new TypeError("task ID must be a string");
18
+ parseTaskId(value);
19
+ return value;
20
+ }
21
+ export function signal(value) {
22
+ if (value === undefined)
23
+ return undefined;
24
+ if (!(value instanceof AbortSignal))
25
+ throw new TypeError("signal must be an AbortSignal");
26
+ return value;
27
+ }
28
+ function strings(value, label) {
29
+ if (value === undefined)
30
+ return undefined;
31
+ if (!Array.isArray(value) || !value.every((item) => typeof item === "string")) {
32
+ throw new TypeError(`${label} must be an array of strings`);
33
+ }
34
+ return value;
35
+ }
36
+ export function taskIds(value, label) {
37
+ const values = strings(value, label)?.map(taskId);
38
+ if (values !== undefined && new Set(values).size !== values.length) {
39
+ throw new TypeError(`${label} must not contain duplicates`);
40
+ }
41
+ return values;
42
+ }
43
+ export function namespace(value) {
44
+ const values = strings(value, "namespace");
45
+ if (values === undefined)
46
+ return undefined;
47
+ if (!values.every(isTaskSegment))
48
+ throw new TypeError("namespace must contain canonical segments");
49
+ return values;
50
+ }
51
+ export function text(value, label) {
52
+ if (value === undefined)
53
+ return undefined;
54
+ if (typeof value !== "string")
55
+ throw new TypeError(`${label} must be a string`);
56
+ return value;
57
+ }
58
+ export function actor(value) {
59
+ if (value === undefined)
60
+ return undefined;
61
+ if (typeof value !== "string" || value.trim().length === 0) {
62
+ throw new TypeError("actor must be a nonblank string");
63
+ }
64
+ return value;
65
+ }
66
+ export function priority(value) {
67
+ if (value === undefined)
68
+ return undefined;
69
+ if (!Number.isInteger(value) || typeof value !== "number" || value < 0 || value > 3) {
70
+ throw new TypeError("priority must be 0..3");
71
+ }
72
+ return value;
73
+ }
74
+ export function limit(value) {
75
+ if (value === undefined)
76
+ return undefined;
77
+ if (typeof value !== "number" || !isValidTaskLimit(value)) {
78
+ throw new TypeError(`limit must be an integer from 1 to ${MAX_TASK_LIMIT}`);
79
+ }
80
+ return value;
81
+ }
82
+ export function sort(value) {
83
+ if (value === undefined)
84
+ return undefined;
85
+ if (value !== "priority" && value !== "created" && value !== "updated" && value !== "id") {
86
+ throw new TypeError("sort is invalid");
87
+ }
88
+ return value;
89
+ }
90
+ export function state(value) {
91
+ if (value === undefined)
92
+ return undefined;
93
+ if (value !== "open" && value !== "in_progress" && value !== "on_hold"
94
+ && value !== "done" && value !== "drop") {
95
+ throw new TypeError("state is invalid");
96
+ }
97
+ return value;
98
+ }
99
+ function nullableId(value) {
100
+ return value === undefined || value === null ? value : taskId(value);
101
+ }
102
+ export function addInput(input) {
103
+ const value = record(input, "add input");
104
+ closed(value, [
105
+ "title", "namespace", "body", "note", "state", "priority", "needs", "parent",
106
+ "supersedes", "relates", "actor", "signal",
107
+ ], "add input");
108
+ const title = text(value.title, "title");
109
+ if (title === undefined || title.trim() === "")
110
+ throw new TypeError("title is required");
111
+ const selectedNamespace = namespace(value.namespace);
112
+ const body = text(value.body, "body");
113
+ const note = text(value.note, "note");
114
+ const initialState = state(value.state);
115
+ const selectedPriority = priority(value.priority);
116
+ const needs = taskIds(value.needs, "needs");
117
+ const parent = nullableId(value.parent);
118
+ const supersedes = taskIds(value.supersedes, "supersedes");
119
+ const relates = taskIds(value.relates, "relates");
120
+ const createdBy = actor(value.actor);
121
+ const abort = signal(value.signal);
122
+ return {
123
+ title,
124
+ ...(selectedNamespace === undefined ? {} : { namespace: selectedNamespace }),
125
+ ...(body === undefined ? {} : { body }),
126
+ ...(note === undefined ? {} : { note }),
127
+ ...(initialState === undefined ? {} : { state: initialState }),
128
+ ...(selectedPriority === undefined ? {} : { priority: selectedPriority }),
129
+ ...(needs === undefined ? {} : { needs }),
130
+ ...(parent === undefined ? {} : { parent }),
131
+ ...(supersedes === undefined ? {} : { supersedes }),
132
+ ...(relates === undefined ? {} : { relates }),
133
+ ...(createdBy === undefined ? {} : { actor: createdBy }),
134
+ ...(abort === undefined ? {} : { signal: abort }),
135
+ };
136
+ }
137
+ export function updateInput(input) {
138
+ const value = record(input, "update input");
139
+ closed(value, [
140
+ "title", "body", "appendBody", "note", "priority", "needs", "addNeeds", "dropNeeds",
141
+ "parent", "supersedes", "addSupersedes", "dropSupersedes", "relates", "addRelates",
142
+ "dropRelates", "signal",
143
+ ], "update input");
144
+ if (value.body !== undefined && value.appendBody !== undefined) {
145
+ throw new TypeError("body and appendBody are mutually exclusive");
146
+ }
147
+ const result = {};
148
+ const title = text(value.title, "title");
149
+ if (title !== undefined) {
150
+ if (title.trim().length === 0)
151
+ throw new TypeError("title must be nonblank");
152
+ result.title = title;
153
+ }
154
+ const body = text(value.body, "body");
155
+ if (body !== undefined)
156
+ result.body = body;
157
+ const appendBody = text(value.appendBody, "appendBody");
158
+ if (appendBody !== undefined)
159
+ result.appendBody = appendBody;
160
+ const note = text(value.note, "note");
161
+ if (note !== undefined)
162
+ result.note = note;
163
+ const selectedPriority = priority(value.priority);
164
+ if (selectedPriority !== undefined)
165
+ result.priority = selectedPriority;
166
+ for (const [key, ids] of [
167
+ ["needs", taskIds(value.needs, "needs")],
168
+ ["addNeeds", taskIds(value.addNeeds, "addNeeds")],
169
+ ["dropNeeds", taskIds(value.dropNeeds, "dropNeeds")],
170
+ ["supersedes", taskIds(value.supersedes, "supersedes")],
171
+ ["addSupersedes", taskIds(value.addSupersedes, "addSupersedes")],
172
+ ["dropSupersedes", taskIds(value.dropSupersedes, "dropSupersedes")],
173
+ ["relates", taskIds(value.relates, "relates")],
174
+ ["addRelates", taskIds(value.addRelates, "addRelates")],
175
+ ["dropRelates", taskIds(value.dropRelates, "dropRelates")],
176
+ ])
177
+ if (ids !== undefined)
178
+ result[key] = ids;
179
+ const parent = nullableId(value.parent);
180
+ if (parent !== undefined)
181
+ result.parent = parent;
182
+ const abort = signal(value.signal);
183
+ if (abort !== undefined)
184
+ result.signal = abort;
185
+ if (Object.keys(result).filter((key) => key !== "signal").length === 0) {
186
+ throw new TypeError("update requires at least one field change");
187
+ }
188
+ return result;
189
+ }