@astrosheep/keiyaku 4.0.4 → 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.
- package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -3
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +14 -0
- package/build/src/akuma/akuma.d.ts +0 -1
- package/build/src/akuma/akuma.js +1 -6
- package/build/src/akuma/allowed.d.ts +1 -1
- package/build/src/akuma/allowed.js +3 -10
- package/build/src/akuma/body.d.ts +5 -2
- package/build/src/akuma/body.js +29 -23
- package/build/src/akuma/heart/facts.d.ts +35 -9
- package/build/src/akuma/heart/index.d.ts +4 -13
- package/build/src/akuma/heart/index.js +2 -133
- package/build/src/akuma/heart/request-authority.d.ts +12 -0
- package/build/src/akuma/heart/request-authority.js +119 -0
- package/build/src/akuma/heart/request-rows.d.ts +16 -0
- package/build/src/akuma/heart/request-rows.js +88 -0
- package/build/src/akuma/heart/rows.d.ts +1 -28
- package/build/src/akuma/heart/rows.js +0 -137
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +9 -2
- package/build/src/akuma/heart/soul.js +0 -36
- package/build/src/akuma/index.d.ts +1 -1
- package/build/src/akuma/provider-recipe.js +1 -11
- package/build/src/akuma/provider.d.ts +1 -6
- package/build/src/akuma/providers/acp/core.js +6 -2
- package/build/src/akuma/providers/acp/index.js +0 -1
- package/build/src/akuma/providers/claude/index.d.ts +0 -4
- package/build/src/akuma/providers/claude/index.js +7 -3
- package/build/src/akuma/providers/codex-app-server/index.d.ts +2 -27
- package/build/src/akuma/providers/codex-app-server/index.js +0 -2
- package/build/src/akuma/providers/grok-build/index.js +0 -1
- package/build/src/akuma/providers/opencode-sdk/index.d.ts +2 -28
- package/build/src/akuma/providers/opencode-sdk/index.js +8 -4
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +1 -1
- package/build/src/akuma/providers/opencode-sdk/session.js +1 -1
- package/build/src/akuma/providers/pi/index.js +11 -4
- package/build/src/akuma/request-serve.d.ts +87 -0
- package/build/src/akuma/request-serve.js +484 -0
- package/build/src/akuma/request-wire.d.ts +124 -0
- package/build/src/akuma/request-wire.js +380 -0
- package/build/src/akuma/requests.d.ts +28 -94
- package/build/src/akuma/requests.js +52 -562
- package/build/src/akuma-body.d.ts +7 -0
- package/build/src/akuma-body.js +54 -1
- package/build/src/body/arc.js +4 -29
- package/build/src/body/decode.js +5 -39
- package/build/src/body/envelope.d.ts +7 -0
- package/build/src/body/envelope.js +37 -0
- package/build/src/cli/accepted.js +17 -9
- package/build/src/cli/commands/akuma.js +2 -2
- package/build/src/cli/commands/contract.d.ts +6 -6
- package/build/src/cli/commands/contract.js +3 -3
- package/build/src/cli/commands/task-invoke.js +98 -9
- package/build/src/cli/coordinates.d.ts +1 -0
- package/build/src/cli/coordinates.js +7 -4
- package/build/src/cli/invoke.js +28 -4
- package/build/src/cli/parse.d.ts +2 -2
- package/build/src/cli/parse.js +21 -8
- package/build/src/cli/render/akuma.js +56 -13
- package/build/src/cli/render/audit.js +2 -6
- package/build/src/cli/render/contract.js +40 -0
- package/build/src/cli/render/receipt.js +28 -1
- package/build/src/cli/render/terminal.d.ts +0 -2
- package/build/src/cli/render/terminal.js +0 -29
- package/build/src/cli/result.d.ts +15 -7
- package/build/src/contract-worktree.d.ts +1 -2
- package/build/src/contract-worktree.js +4 -9
- package/build/src/core/facts/codec.js +9 -0
- package/build/src/core/facts/fold.js +13 -1
- package/build/src/core/facts/types.d.ts +8 -1
- package/build/src/core/subject.js +4 -3
- package/build/src/core/verbs/placement.d.ts +9 -1
- package/build/src/core/verbs/placement.js +24 -6
- package/build/src/core/verbs/reintegrate.d.ts +13 -0
- package/build/src/core/verbs/reintegrate.js +24 -0
- package/build/src/dispatch/index.d.ts +1 -1
- package/build/src/git/admission.d.ts +2 -1
- package/build/src/git/integration.d.ts +5 -2
- package/build/src/git/integration.js +54 -1
- package/build/src/git/observe.d.ts +2 -1
- package/build/src/git/observe.js +6 -5
- package/build/src/git/process.d.ts +26 -0
- package/build/src/git/process.js +94 -0
- package/build/src/git/read-observation.d.ts +2 -1
- package/build/src/git/read-observation.js +3 -2
- package/build/src/git/reconcile.d.ts +2 -1
- package/build/src/git/reconcile.js +9 -6
- package/build/src/git/repository.d.ts +2 -29
- package/build/src/git/repository.js +4 -118
- package/build/src/git/scratch.d.ts +1 -1
- package/build/src/git/scratch.js +1 -1
- package/build/src/git/target-placement.d.ts +1 -1
- package/build/src/git/target-placement.js +9 -7
- package/build/src/git/tender.d.ts +1 -1
- package/build/src/git/tender.js +2 -1
- package/build/src/git/terminal-seal.d.ts +2 -2
- package/build/src/git/terminal-seal.js +3 -6
- package/build/src/git/workspace.d.ts +1 -1
- package/build/src/git/workspace.js +1 -1
- package/build/src/index.d.ts +1 -2
- package/build/src/index.js +1 -1
- package/build/src/library/address.js +7 -1
- package/build/src/library/audit.d.ts +2 -1
- package/build/src/library/audit.js +1 -1
- package/build/src/library/bind.d.ts +2 -1
- package/build/src/library/bind.js +1 -1
- package/build/src/library/configuration.d.ts +1 -1
- package/build/src/library/configuration.js +52 -18
- package/build/src/library/contract.d.ts +55 -2
- package/build/src/library/contract.js +162 -23
- package/build/src/library/delivery.d.ts +1 -1
- package/build/src/library/fleet.d.ts +27 -7
- package/build/src/library/fleet.js +79 -39
- package/build/src/library/input.d.ts +0 -1
- package/build/src/library/input.js +0 -5
- package/build/src/library/keiyaku.d.ts +1 -1
- package/build/src/library/mutation.d.ts +2 -1
- package/build/src/library/reconcile.d.ts +2 -1
- package/build/src/library/reconcile.js +3 -1
- package/build/src/library/region.d.ts +1 -1
- package/build/src/library/repo.d.ts +3 -1
- package/build/src/library/repo.js +8 -4
- package/build/src/protocol/abandon.d.ts +9 -0
- package/build/src/protocol/abandon.js +15 -0
- package/build/src/protocol/amend.d.ts +16 -0
- package/build/src/protocol/amend.js +77 -0
- package/build/src/protocol/arc.d.ts +6 -0
- package/build/src/protocol/arc.js +12 -0
- package/build/src/protocol/attempt.d.ts +2 -1
- package/build/src/protocol/audit.d.ts +53 -0
- package/build/src/protocol/audit.js +140 -0
- package/build/src/protocol/bind.d.ts +1 -1
- package/build/src/protocol/completion.d.ts +32 -0
- package/build/src/protocol/completion.js +130 -0
- package/build/src/protocol/deliver.d.ts +37 -0
- package/build/src/protocol/deliver.js +160 -0
- package/build/src/protocol/intent.d.ts +1 -1
- package/build/src/protocol/intent.js +1 -1
- package/build/src/protocol/operations.d.ts +64 -200
- package/build/src/protocol/operations.js +21 -640
- package/build/src/protocol/placement.d.ts +4 -13
- package/build/src/protocol/placement.js +3 -15
- package/build/src/protocol/read/status.d.ts +1 -1
- package/build/src/protocol/read/status.js +3 -1
- package/build/src/protocol/reconcile.d.ts +35 -0
- package/build/src/protocol/reconcile.js +41 -0
- package/build/src/protocol/reintegrate.d.ts +31 -0
- package/build/src/protocol/reintegrate.js +74 -0
- package/build/src/protocol/review.d.ts +33 -0
- package/build/src/protocol/review.js +148 -0
- package/build/src/protocol/run.d.ts +1 -1
- package/build/src/settlement/fence.d.ts +1 -1
- package/build/src/settlement/holder.d.ts +1 -1
- package/build/src/settlement/settle.d.ts +1 -1
- package/build/src/task/board.d.ts +0 -1
- package/build/src/task/board.js +0 -3
- package/build/src/task/compose.d.ts +1 -1
- package/build/src/task/compose.js +11 -4
- package/build/src/task/index.d.ts +1 -1
- package/build/src/task/index.js +2 -127
- package/build/src/task/input.d.ts +18 -0
- package/build/src/task/input.js +189 -0
- package/build/src/task/mutation.d.ts +39 -0
- package/build/src/task/mutation.js +85 -0
- package/build/src/task/operations.d.ts +2 -0
- package/build/src/task/operations.js +35 -11
- package/build/src/task/store.js +0 -2
- package/build/src/verification/execution.d.ts +1 -1
- package/build/src/workspace-place.d.ts +1 -4
- package/build/src/workspace-place.js +0 -15
- package/package.json +3 -3
package/build/src/task/index.js
CHANGED
|
@@ -1,136 +1,11 @@
|
|
|
1
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 {
|
|
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
8
|
export { TaskAuthorityCorruptionError, TASK_RELATION_PREDICATE_FIELDS };
|
|
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
|
-
}
|
|
134
9
|
class TaskHandle {
|
|
135
10
|
id;
|
|
136
11
|
world;
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { WorldRoot } from "../world.js";
|
|
2
|
+
import type { TaskCompositionResult } from "./compose.js";
|
|
3
|
+
import type { AddTaskDocumentInput, AddTaskInput, TaskBatchResult, TaskMutationResult, TaskUpdateResult, UpdateTaskInput } from "./operations.js";
|
|
4
|
+
import type { TaskId } from "./identity.js";
|
|
5
|
+
export declare const TASK_MUTATION_ACTIONS: readonly ["task.add", "task.addDocument", "task.compose", "task.done", "task.drop", "task.hold", "task.resume", "task.start", "task.stop", "task.update"];
|
|
6
|
+
export type TaskMutationAction = (typeof TASK_MUTATION_ACTIONS)[number];
|
|
7
|
+
export declare function isTaskMutationAction(value: unknown): value is TaskMutationAction;
|
|
8
|
+
export type TaskMutationRequest = Readonly<{
|
|
9
|
+
action: "task.add";
|
|
10
|
+
input: Omit<AddTaskInput, "actor" | "signal">;
|
|
11
|
+
}> | Readonly<{
|
|
12
|
+
action: "task.addDocument";
|
|
13
|
+
input: Omit<AddTaskDocumentInput, "actor" | "signal">;
|
|
14
|
+
}> | Readonly<{
|
|
15
|
+
action: "task.compose";
|
|
16
|
+
markdown: string;
|
|
17
|
+
}> | Readonly<{
|
|
18
|
+
action: "task.update";
|
|
19
|
+
id: TaskId;
|
|
20
|
+
input: Omit<UpdateTaskInput, "signal">;
|
|
21
|
+
}> | Readonly<{
|
|
22
|
+
action: "task.start" | "task.stop" | "task.resume";
|
|
23
|
+
id: TaskId;
|
|
24
|
+
}> | Readonly<{
|
|
25
|
+
action: "task.hold";
|
|
26
|
+
ids: readonly TaskId[];
|
|
27
|
+
}> | Readonly<{
|
|
28
|
+
action: "task.done" | "task.drop";
|
|
29
|
+
ids: readonly TaskId[];
|
|
30
|
+
note?: string;
|
|
31
|
+
}>;
|
|
32
|
+
export type TaskMutationExecutionResult = TaskMutationResult | TaskUpdateResult | TaskBatchResult | TaskCompositionResult;
|
|
33
|
+
export declare function decodeTaskMutationRequest(action: TaskMutationAction, value: unknown): TaskMutationRequest;
|
|
34
|
+
export declare function executeTaskMutation(input: Readonly<{
|
|
35
|
+
world: WorldRoot;
|
|
36
|
+
request: TaskMutationRequest;
|
|
37
|
+
requester: string;
|
|
38
|
+
signal?: AbortSignal;
|
|
39
|
+
}>): Promise<TaskMutationExecutionResult>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { addInput, closed, namespace, record, taskId, taskIds, text, updateInput } from "./input.js";
|
|
2
|
+
import { Tasks } from "./index.js";
|
|
3
|
+
export const TASK_MUTATION_ACTIONS = Object.freeze([
|
|
4
|
+
"task.add", "task.addDocument", "task.compose", "task.done", "task.drop",
|
|
5
|
+
"task.hold", "task.resume", "task.start", "task.stop", "task.update",
|
|
6
|
+
]);
|
|
7
|
+
const TASK_MUTATION_ACTION_SET = new Set(TASK_MUTATION_ACTIONS);
|
|
8
|
+
export function isTaskMutationAction(value) {
|
|
9
|
+
return typeof value === "string" && TASK_MUTATION_ACTION_SET.has(value);
|
|
10
|
+
}
|
|
11
|
+
function mutationObject(value, keys, label) {
|
|
12
|
+
const decoded = record(value, label);
|
|
13
|
+
closed(decoded, keys, label);
|
|
14
|
+
return decoded;
|
|
15
|
+
}
|
|
16
|
+
export function decodeTaskMutationRequest(action, value) {
|
|
17
|
+
switch (action) {
|
|
18
|
+
case "task.add": {
|
|
19
|
+
const input = mutationObject(value, ["input"], "task.add request");
|
|
20
|
+
const raw = mutationObject(input.input, [
|
|
21
|
+
"title", "namespace", "body", "note", "state", "priority", "needs", "parent", "supersedes", "relates",
|
|
22
|
+
], "task.add input");
|
|
23
|
+
return { action, input: addInput(raw) };
|
|
24
|
+
}
|
|
25
|
+
case "task.addDocument": {
|
|
26
|
+
const input = mutationObject(value, ["input"], "task.addDocument request");
|
|
27
|
+
const raw = mutationObject(input.input, ["markdown", "namespace"], "task.addDocument input");
|
|
28
|
+
const markdown = text(raw.markdown, "markdown");
|
|
29
|
+
if (markdown === undefined)
|
|
30
|
+
throw new TypeError("markdown is required");
|
|
31
|
+
const selected = namespace(raw.namespace);
|
|
32
|
+
return { action, input: { markdown, ...(selected === undefined ? {} : { namespace: selected }) } };
|
|
33
|
+
}
|
|
34
|
+
case "task.compose": {
|
|
35
|
+
const input = mutationObject(value, ["markdown"], "task.compose request");
|
|
36
|
+
const markdown = text(input.markdown, "markdown");
|
|
37
|
+
if (markdown === undefined)
|
|
38
|
+
throw new TypeError("markdown is required");
|
|
39
|
+
return { action, markdown };
|
|
40
|
+
}
|
|
41
|
+
case "task.update": {
|
|
42
|
+
const request = mutationObject(value, ["id", "input"], "task.update request");
|
|
43
|
+
const raw = mutationObject(request.input, [
|
|
44
|
+
"title", "body", "appendBody", "note", "priority", "needs", "addNeeds", "dropNeeds", "parent",
|
|
45
|
+
"supersedes", "addSupersedes", "dropSupersedes", "relates", "addRelates", "dropRelates",
|
|
46
|
+
], "task.update input");
|
|
47
|
+
return { action, id: taskId(request.id), input: updateInput(raw) };
|
|
48
|
+
}
|
|
49
|
+
case "task.start":
|
|
50
|
+
case "task.stop":
|
|
51
|
+
case "task.resume": {
|
|
52
|
+
const request = mutationObject(value, ["id"], `${action} request`);
|
|
53
|
+
return { action, id: taskId(request.id) };
|
|
54
|
+
}
|
|
55
|
+
case "task.hold": {
|
|
56
|
+
const request = mutationObject(value, ["ids"], "task.hold request");
|
|
57
|
+
return { action, ids: taskIds(request.ids, "ids") ?? [] };
|
|
58
|
+
}
|
|
59
|
+
case "task.done":
|
|
60
|
+
case "task.drop": {
|
|
61
|
+
const request = mutationObject(value, ["ids", "note"], `${action} request`);
|
|
62
|
+
const note = text(request.note, "note");
|
|
63
|
+
return { action, ids: taskIds(request.ids, "ids") ?? [], ...(note === undefined ? {} : { note }) };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export async function executeTaskMutation(input) {
|
|
68
|
+
const tasks = Tasks.of(input.world);
|
|
69
|
+
const { request, signal } = input;
|
|
70
|
+
const withSignal = signal === undefined ? {} : { signal };
|
|
71
|
+
switch (request.action) {
|
|
72
|
+
case "task.add": return await tasks.add({ ...request.input, actor: input.requester, ...withSignal });
|
|
73
|
+
case "task.addDocument": return await tasks.addDocument({ ...request.input, actor: input.requester, ...withSignal });
|
|
74
|
+
case "task.compose": return await tasks.compose({ markdown: request.markdown, actor: input.requester, ...withSignal });
|
|
75
|
+
case "task.update": return await tasks.task({ id: request.id }).update({ ...request.input, ...withSignal });
|
|
76
|
+
case "task.start": return await tasks.task({ id: request.id }).start(withSignal);
|
|
77
|
+
case "task.stop": return await tasks.task({ id: request.id }).stop(withSignal);
|
|
78
|
+
case "task.resume": return await tasks.task({ id: request.id }).resume(withSignal);
|
|
79
|
+
case "task.hold": return await tasks.batch({ verb: "hold", ids: request.ids, ...withSignal });
|
|
80
|
+
case "task.done": return await tasks.batch({ verb: "done", ids: request.ids, ...withSignal,
|
|
81
|
+
...(request.note === undefined ? {} : { note: request.note }) });
|
|
82
|
+
case "task.drop": return await tasks.batch({ verb: "drop", ids: request.ids, ...withSignal,
|
|
83
|
+
...(request.note === undefined ? {} : { note: request.note }) });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -105,6 +105,8 @@ export type UpdateTaskInput = Readonly<{
|
|
|
105
105
|
signal?: AbortSignal;
|
|
106
106
|
}>;
|
|
107
107
|
export declare function taskView(document: TaskDocument): TaskView;
|
|
108
|
+
/** Internal Task timestamp rule shared by ordinary mutations and compose. */
|
|
109
|
+
export declare function advanceTaskTimestamp(previous: string, candidate: string): string;
|
|
108
110
|
export declare function addTask(world: WorldRoot, input: AddTaskInput): Promise<TaskMutationResult>;
|
|
109
111
|
export declare function addTaskDocument(world: WorldRoot, input: AddTaskDocumentInput): Promise<TaskMutationResult>;
|
|
110
112
|
export declare function updateTask(world: WorldRoot, id: TaskId, input: UpdateTaskInput): Promise<TaskUpdateResult>;
|
|
@@ -24,10 +24,14 @@ function occupied(board, namespace) {
|
|
|
24
24
|
return sameNamespace(coordinate.namespace, namespace) ? [coordinate.localId] : [];
|
|
25
25
|
}));
|
|
26
26
|
}
|
|
27
|
-
function currentTimestamp() {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
function currentTimestamp() {
|
|
28
|
+
return new Date().toISOString();
|
|
29
|
+
}
|
|
30
|
+
/** Internal Task timestamp rule shared by ordinary mutations and compose. */
|
|
31
|
+
export function advanceTaskTimestamp(previous, candidate) {
|
|
32
|
+
return candidate > previous
|
|
33
|
+
? candidate
|
|
34
|
+
: new Date(Date.parse(previous) + 1).toISOString();
|
|
31
35
|
}
|
|
32
36
|
function addDocument(base, namespace, board, at, actor) {
|
|
33
37
|
const localId = allocateLocalId(deriveLocalStem(base.title), occupied(board, namespace));
|
|
@@ -108,12 +112,20 @@ export async function updateTask(world, id, input) {
|
|
|
108
112
|
const problem = relationProblem(boardWith(snapshot.board, candidate), current, candidate);
|
|
109
113
|
if (problem !== null)
|
|
110
114
|
return { kind: "refused", refusal: { kind: "invalid-graph", diagnostic: problem } };
|
|
111
|
-
const predecessor = snapshot.bytes.get(id)
|
|
115
|
+
const predecessor = snapshot.bytes.get(id);
|
|
116
|
+
const before = Buffer.from(predecessor).toString("utf8");
|
|
112
117
|
const changed = !Buffer.from(serializeTaskDocument(candidate)).equals(Buffer.from(predecessor));
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
118
|
+
let next = candidate;
|
|
119
|
+
if (changed) {
|
|
120
|
+
const at = currentTimestamp();
|
|
121
|
+
next = { ...candidate, updatedAt: advanceTaskTimestamp(current.updatedAt, at) };
|
|
122
|
+
}
|
|
123
|
+
const afterBytes = serializeTaskDocument(next);
|
|
124
|
+
const after = Buffer.from(afterBytes).toString("utf8");
|
|
125
|
+
if (before !== after
|
|
126
|
+
&& await replaceAuthority({ path: authorityPath(world, id), expected: predecessor, next: afterBytes }) !== "replaced") {
|
|
116
127
|
return { kind: "retry", reason: "concurrent-modification" };
|
|
128
|
+
}
|
|
117
129
|
const label = `${id}.md`;
|
|
118
130
|
return { kind: "accepted", value: { task: taskView(next), documentDiff: documentDiff(label, label, before, after) } };
|
|
119
131
|
});
|
|
@@ -130,9 +142,16 @@ export async function lifecycleTask(world, id, verb, signal, note) {
|
|
|
130
142
|
if (current === undefined)
|
|
131
143
|
return refused({ kind: "task-missing", taskId: id });
|
|
132
144
|
const state = TRANSITIONS[verb][current.state];
|
|
133
|
-
if (state === undefined)
|
|
145
|
+
if (state === undefined) {
|
|
134
146
|
return refused({ kind: "invalid-lifecycle-transition", taskId: id, state: current.state, verb });
|
|
135
|
-
|
|
147
|
+
}
|
|
148
|
+
const at = currentTimestamp();
|
|
149
|
+
const next = {
|
|
150
|
+
...current,
|
|
151
|
+
state,
|
|
152
|
+
...(note === undefined ? {} : { note }),
|
|
153
|
+
updatedAt: advanceTaskTimestamp(current.updatedAt, at),
|
|
154
|
+
};
|
|
136
155
|
const bytes = serializeTaskDocument(next);
|
|
137
156
|
return await replaceAuthority({ path: authorityPath(world, id), expected: snapshot.bytes.get(id), next: bytes }) === "replaced"
|
|
138
157
|
? { kind: "accepted", value: taskView(next) } : retry("concurrent-modification");
|
|
@@ -157,7 +176,12 @@ export async function settleTask(world, id) {
|
|
|
157
176
|
if (current.state === "drop") {
|
|
158
177
|
return { kind: "refused", refusal: { kind: "invalid-lifecycle-transition", taskId: id, state: current.state, verb: "done" } };
|
|
159
178
|
}
|
|
160
|
-
const
|
|
179
|
+
const at = currentTimestamp();
|
|
180
|
+
const next = {
|
|
181
|
+
...current,
|
|
182
|
+
state: "done",
|
|
183
|
+
updatedAt: advanceTaskTimestamp(current.updatedAt, at),
|
|
184
|
+
};
|
|
161
185
|
const replaced = await replaceAuthority({ path: authorityPath(world, id), expected: snapshot.bytes.get(id), next: serializeTaskDocument(next) });
|
|
162
186
|
return replaced === "replaced"
|
|
163
187
|
? { kind: "changed", task: taskView(next), action: "done" }
|
package/build/src/task/store.js
CHANGED
|
@@ -34,8 +34,6 @@ async function authorityFiles(directory) {
|
|
|
34
34
|
files.push(...await authorityFiles(path));
|
|
35
35
|
else if (entry.isFile() && entry.name.endsWith(".md"))
|
|
36
36
|
files.push(path);
|
|
37
|
-
else
|
|
38
|
-
throw new Error(`unexpected Task authority entry: ${path}`);
|
|
39
37
|
}
|
|
40
38
|
return files.sort((a, b) => Buffer.compare(Buffer.from(a), Buffer.from(b)));
|
|
41
39
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { SnapshotId } from "../core/facts/types.js";
|
|
2
2
|
import { type HookFailure } from "../git/hooks.js";
|
|
3
3
|
import type { MaterializedScratchCandidate, WorktreeLeak } from "../git/scratch.js";
|
|
4
|
-
import type { GitRepository } from "../git/
|
|
4
|
+
import type { GitRepository } from "../git/process.js";
|
|
5
5
|
import type { Settings } from "../settings.js";
|
|
6
6
|
import type { VerificationDeclaration } from "./declaration.js";
|
|
7
7
|
type VerificationVerdict = "satisfied" | "unsatisfied";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ContractId } from "./core/facts/types.js";
|
|
2
|
-
import type { GitRepository } from "./git/
|
|
2
|
+
import type { GitRepository } from "./git/process.js";
|
|
3
3
|
export declare const CONTRACT_PLACES: readonly string[];
|
|
4
4
|
export type Place = string & {
|
|
5
5
|
readonly [placeBrand]: "Place";
|
|
@@ -27,15 +27,12 @@ export type ManagedWorktreeAppointment = Readonly<{
|
|
|
27
27
|
export declare function placeRegisterPath(repository: GitRepository): string;
|
|
28
28
|
export declare function place(value: string): Place;
|
|
29
29
|
export declare function nextPlace(current?: Place): Place;
|
|
30
|
-
export declare function firstFreePlace(register: PlaceRegister): Place;
|
|
31
30
|
export declare function canonicalPlaceRegister(register: PlaceRegister): string;
|
|
32
31
|
export declare function decodePlaceRegister(path: string, bytes: string): PlaceRegister;
|
|
33
32
|
export declare function emptyPlaceRegister(): PlaceRegister;
|
|
34
33
|
export declare function readPlaceRegister(repository: GitRepository): Promise<PlaceRegister>;
|
|
35
34
|
export declare function appointmentFor(register: PlaceRegister, contract: ContractId): PlaceAppointment | undefined;
|
|
36
35
|
export declare function appointManagedWorktrees(repository: GitRepository, contracts: readonly ContractId[]): Promise<PlaceRegister>;
|
|
37
|
-
export declare function appointManagedWorktree(repository: GitRepository, contract: ContractId): Promise<PlaceAppointment>;
|
|
38
36
|
export declare function releaseManagedWorktrees(repository: GitRepository, contracts: readonly ContractId[]): Promise<void>;
|
|
39
|
-
export declare function releaseManagedWorktree(repository: GitRepository, contract: ContractId): Promise<void>;
|
|
40
37
|
export declare function readManagedWorktreeAppointment(repository: GitRepository, contract: ContractId, register?: PlaceRegister): Promise<ManagedWorktreeAppointment>;
|
|
41
38
|
export {};
|
|
@@ -230,12 +230,6 @@ export function nextPlace(current) {
|
|
|
230
230
|
? composePlace(CONTRACT_PLACES[index + 1], parsed.generation)
|
|
231
231
|
: composePlace(CONTRACT_PLACES[0], parsed.generation + 1n);
|
|
232
232
|
}
|
|
233
|
-
export function firstFreePlace(register) {
|
|
234
|
-
for (let candidate = nextPlace();; candidate = nextPlace(candidate)) {
|
|
235
|
-
if (!register.byPlace.has(candidate))
|
|
236
|
-
return candidate;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
233
|
export function canonicalPlaceRegister(register) {
|
|
240
234
|
const appointments = Object.fromEntries(register.appointments
|
|
241
235
|
.slice()
|
|
@@ -354,12 +348,6 @@ function withAppointments(register, contracts) {
|
|
|
354
348
|
export async function appointManagedWorktrees(repository, contracts) {
|
|
355
349
|
return await mutatePlaceRegister(repository, (current) => withAppointments(current, contracts));
|
|
356
350
|
}
|
|
357
|
-
export async function appointManagedWorktree(repository, contract) {
|
|
358
|
-
const appointed = appointmentFor(await appointManagedWorktrees(repository, [contract]), contract);
|
|
359
|
-
if (appointed === undefined)
|
|
360
|
-
throw new Error(`Place appointment was not recorded: ${contract}`);
|
|
361
|
-
return appointed;
|
|
362
|
-
}
|
|
363
351
|
export async function releaseManagedWorktrees(repository, contracts) {
|
|
364
352
|
if (contracts.length === 0)
|
|
365
353
|
return;
|
|
@@ -369,9 +357,6 @@ export async function releaseManagedWorktrees(repository, contracts) {
|
|
|
369
357
|
return kept.length === current.appointments.length ? current : indexedRegister(kept);
|
|
370
358
|
});
|
|
371
359
|
}
|
|
372
|
-
export async function releaseManagedWorktree(repository, contract) {
|
|
373
|
-
await releaseManagedWorktrees(repository, [contract]);
|
|
374
|
-
}
|
|
375
360
|
export async function readManagedWorktreeAppointment(repository, contract, register) {
|
|
376
361
|
try {
|
|
377
362
|
const snapshot = register ?? await readPlaceRegister(repository);
|