@adecore/plan 0.21.1 → 0.22.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/dist/apply.js +14 -10
- package/dist/permissions.js +5 -5
- package/dist/tree.d.ts +1 -0
- package/dist/tree.js +3 -0
- package/package.json +1 -1
- package/src/apply.ts +15 -11
- package/src/permissions.ts +5 -5
- package/src/tree.ts +4 -0
package/dist/apply.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PlanChecksSchema, PlanItemIdSchema, PlanKindSchema, PlanMetaSchema, PlanOpSchema, PlanSchema, PlanSectionSchema, PlanStepSchema, PlanStepStateSchema, PlanTextSchema } from './protocol.js';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { canApply } from './permissions.js';
|
|
4
|
-
import { allItems, allSteps, effectiveChecks, locateItem, refuse, structureProblem } from './tree.js';
|
|
4
|
+
import { allItems, allSteps, effectiveChecks, locateItem, missingItem, refuse, structureProblem } from './tree.js';
|
|
5
5
|
const ID_ALPHABET = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
|
6
6
|
export function randomItemId() {
|
|
7
7
|
const bytes = crypto.getRandomValues(new Uint8Array(6));
|
|
@@ -59,7 +59,7 @@ function placeFor(plan, type, under, after) {
|
|
|
59
59
|
if (under !== undefined) {
|
|
60
60
|
const location = locateItem(plan, under);
|
|
61
61
|
if (!location) {
|
|
62
|
-
return
|
|
62
|
+
return missingItem(under);
|
|
63
63
|
}
|
|
64
64
|
const target = location.item;
|
|
65
65
|
if (target.type === 'text') {
|
|
@@ -77,7 +77,7 @@ function placeFor(plan, type, under, after) {
|
|
|
77
77
|
else if (after !== undefined) {
|
|
78
78
|
const location = locateItem(plan, after);
|
|
79
79
|
if (!location) {
|
|
80
|
-
return
|
|
80
|
+
return missingItem(after);
|
|
81
81
|
}
|
|
82
82
|
parent = location.parent;
|
|
83
83
|
siblings = location.siblings;
|
|
@@ -116,6 +116,16 @@ function pruneEmptySteps(items) {
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
|
+
function newItem(op, id) {
|
|
120
|
+
const base = { id, title: op.title, ...(op.description ? { description: op.description } : {}) };
|
|
121
|
+
if (op.type === 'section') {
|
|
122
|
+
return { type: 'section', ...base, items: [] };
|
|
123
|
+
}
|
|
124
|
+
if (op.type === 'text') {
|
|
125
|
+
return { type: 'text', ...base };
|
|
126
|
+
}
|
|
127
|
+
return { type: 'step', ...base, ...(op.checks ? { checks: op.checks } : {}) };
|
|
128
|
+
}
|
|
119
129
|
function applyOne(plan, op, options, mint, dropped) {
|
|
120
130
|
const verdict = canApply(op, options.actor, plan);
|
|
121
131
|
if (!verdict.ok) {
|
|
@@ -160,13 +170,7 @@ function applyOne(plan, op, options, mint, dropped) {
|
|
|
160
170
|
if ('ok' in place) {
|
|
161
171
|
return place;
|
|
162
172
|
}
|
|
163
|
-
|
|
164
|
-
const item = op.type === 'section'
|
|
165
|
-
? { type: 'section', ...base, items: [] }
|
|
166
|
-
: op.type === 'text'
|
|
167
|
-
? { type: 'text', ...base }
|
|
168
|
-
: { type: 'step', ...base, ...(op.checks ? { checks: op.checks } : {}) };
|
|
169
|
-
place.siblings.splice(place.index, 0, item);
|
|
173
|
+
place.siblings.splice(place.index, 0, newItem(op, id));
|
|
170
174
|
becomeParent(place.parent, dropped);
|
|
171
175
|
return null;
|
|
172
176
|
}
|
package/dist/permissions.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { allSteps, effectiveChecks, holdsPersonState, isParentStep, locateItem, refuse } from './tree.js';
|
|
1
|
+
import { allSteps, effectiveChecks, holdsPersonState, isParentStep, locateItem, missingItem, refuse } from './tree.js';
|
|
2
2
|
const ALLOWED = { ok: true };
|
|
3
3
|
const PERSON_OPS = new Set(['set', 'note', 'unlock']);
|
|
4
4
|
const AGENT_OPS = new Set(['set', 'note', 'add', 'edit', 'move', 'remove', 'meta']);
|
|
5
5
|
function stepFor(plan, id, leaf) {
|
|
6
6
|
const location = locateItem(plan, id);
|
|
7
7
|
if (!location) {
|
|
8
|
-
return
|
|
8
|
+
return missingItem(id);
|
|
9
9
|
}
|
|
10
10
|
if (location.item.type !== 'step') {
|
|
11
11
|
return refuse('plan-not-a-step', `"${id}" is a ${location.item.type}, not a step`);
|
|
@@ -90,7 +90,7 @@ export function canApply(op, actor, plan) {
|
|
|
90
90
|
case 'edit': {
|
|
91
91
|
const location = locateItem(plan, op.id);
|
|
92
92
|
if (!location) {
|
|
93
|
-
return
|
|
93
|
+
return missingItem(op.id);
|
|
94
94
|
}
|
|
95
95
|
const item = location.item;
|
|
96
96
|
if (item.type !== 'step') {
|
|
@@ -118,14 +118,14 @@ export function canApply(op, actor, plan) {
|
|
|
118
118
|
}
|
|
119
119
|
case 'move': {
|
|
120
120
|
if (!locateItem(plan, op.id)) {
|
|
121
|
-
return
|
|
121
|
+
return missingItem(op.id);
|
|
122
122
|
}
|
|
123
123
|
return mayHoldChildren(plan, op.under);
|
|
124
124
|
}
|
|
125
125
|
case 'remove': {
|
|
126
126
|
const location = locateItem(plan, op.id);
|
|
127
127
|
if (!location) {
|
|
128
|
-
return
|
|
128
|
+
return missingItem(op.id);
|
|
129
129
|
}
|
|
130
130
|
if (holdsPersonState(location.item)) {
|
|
131
131
|
return refuse('set-by-person', `A person checked "${op.id}" or a step under it, so it stays`);
|
package/dist/tree.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export interface PlanRefusal {
|
|
|
6
6
|
message: string;
|
|
7
7
|
}
|
|
8
8
|
export declare function refuse(code: PlanRefusalCode, message: string): PlanRefusal;
|
|
9
|
+
export declare function missingItem(id: string): PlanRefusal;
|
|
9
10
|
export interface PlanLocation {
|
|
10
11
|
item: PlanItem;
|
|
11
12
|
siblings: PlanItem[];
|
package/dist/tree.js
CHANGED
|
@@ -2,6 +2,9 @@ import { PLAN_LIMITS } from './protocol.js';
|
|
|
2
2
|
export function refuse(code, message) {
|
|
3
3
|
return { ok: false, code, message };
|
|
4
4
|
}
|
|
5
|
+
export function missingItem(id) {
|
|
6
|
+
return refuse('plan-missing-item', `The plan has no item "${id}"`);
|
|
7
|
+
}
|
|
5
8
|
function childrenOf(item) {
|
|
6
9
|
if (item.type === 'section') {
|
|
7
10
|
return item.items;
|
package/package.json
CHANGED
package/src/apply.ts
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
} from './protocol.ts';
|
|
22
22
|
import { z } from 'zod';
|
|
23
23
|
import { canApply } from './permissions.ts';
|
|
24
|
-
import { allItems, allSteps, effectiveChecks, locateItem, refuse, structureProblem, type PlanRefusal } from './tree.ts';
|
|
24
|
+
import { allItems, allSteps, effectiveChecks, locateItem, missingItem, refuse, structureProblem, type PlanRefusal } from './tree.ts';
|
|
25
25
|
|
|
26
26
|
export interface PlanApplyOptions {
|
|
27
27
|
actor: PlanActor;
|
|
@@ -107,7 +107,7 @@ function placeFor(
|
|
|
107
107
|
if (under !== undefined) {
|
|
108
108
|
const location = locateItem(plan, under);
|
|
109
109
|
if (!location) {
|
|
110
|
-
return
|
|
110
|
+
return missingItem(under);
|
|
111
111
|
}
|
|
112
112
|
const target = location.item;
|
|
113
113
|
if (target.type === 'text') {
|
|
@@ -123,7 +123,7 @@ function placeFor(
|
|
|
123
123
|
} else if (after !== undefined) {
|
|
124
124
|
const location = locateItem(plan, after);
|
|
125
125
|
if (!location) {
|
|
126
|
-
return
|
|
126
|
+
return missingItem(after);
|
|
127
127
|
}
|
|
128
128
|
parent = location.parent;
|
|
129
129
|
siblings = location.siblings;
|
|
@@ -165,6 +165,17 @@ function pruneEmptySteps(items: readonly PlanItem[]): void {
|
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
function newItem(op: Extract<PlanOp, { op: 'add' }>, id: string): PlanItem {
|
|
169
|
+
const base = { id, title: op.title, ...(op.description ? { description: op.description } : {}) };
|
|
170
|
+
if (op.type === 'section') {
|
|
171
|
+
return { type: 'section', ...base, items: [] };
|
|
172
|
+
}
|
|
173
|
+
if (op.type === 'text') {
|
|
174
|
+
return { type: 'text', ...base };
|
|
175
|
+
}
|
|
176
|
+
return { type: 'step', ...base, ...(op.checks ? { checks: op.checks } : {}) };
|
|
177
|
+
}
|
|
178
|
+
|
|
168
179
|
function applyOne(plan: Plan, op: PlanOp, options: PlanApplyOptions, mint: () => string, dropped: string[]): PlanRefusal | null {
|
|
169
180
|
const verdict = canApply(op, options.actor, plan);
|
|
170
181
|
if (!verdict.ok) {
|
|
@@ -209,14 +220,7 @@ function applyOne(plan: Plan, op: PlanOp, options: PlanApplyOptions, mint: () =>
|
|
|
209
220
|
if ('ok' in place) {
|
|
210
221
|
return place;
|
|
211
222
|
}
|
|
212
|
-
|
|
213
|
-
const item: PlanItem =
|
|
214
|
-
op.type === 'section'
|
|
215
|
-
? { type: 'section', ...base, items: [] }
|
|
216
|
-
: op.type === 'text'
|
|
217
|
-
? { type: 'text', ...base }
|
|
218
|
-
: { type: 'step', ...base, ...(op.checks ? { checks: op.checks } : {}) };
|
|
219
|
-
place.siblings.splice(place.index, 0, item);
|
|
223
|
+
place.siblings.splice(place.index, 0, newItem(op, id));
|
|
220
224
|
becomeParent(place.parent, dropped);
|
|
221
225
|
return null;
|
|
222
226
|
}
|
package/src/permissions.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Plan, PlanActor, PlanOp, PlanStep, PlanStepState } from './protocol.ts';
|
|
2
|
-
import { allSteps, effectiveChecks, holdsPersonState, isParentStep, locateItem, refuse, type PlanRefusal } from './tree.ts';
|
|
2
|
+
import { allSteps, effectiveChecks, holdsPersonState, isParentStep, locateItem, missingItem, refuse, type PlanRefusal } from './tree.ts';
|
|
3
3
|
|
|
4
4
|
export type PlanVerdict = { ok: true } | PlanRefusal;
|
|
5
5
|
|
|
@@ -11,7 +11,7 @@ const AGENT_OPS: ReadonlySet<PlanOp['op']> = new Set(['set', 'note', 'add', 'edi
|
|
|
11
11
|
function stepFor(plan: Plan, id: string, leaf: boolean): PlanStep | PlanRefusal {
|
|
12
12
|
const location = locateItem(plan, id);
|
|
13
13
|
if (!location) {
|
|
14
|
-
return
|
|
14
|
+
return missingItem(id);
|
|
15
15
|
}
|
|
16
16
|
if (location.item.type !== 'step') {
|
|
17
17
|
return refuse('plan-not-a-step', `"${id}" is a ${location.item.type}, not a step`);
|
|
@@ -99,7 +99,7 @@ export function canApply(op: PlanOp, actor: PlanActor, plan: Plan): PlanVerdict
|
|
|
99
99
|
case 'edit': {
|
|
100
100
|
const location = locateItem(plan, op.id);
|
|
101
101
|
if (!location) {
|
|
102
|
-
return
|
|
102
|
+
return missingItem(op.id);
|
|
103
103
|
}
|
|
104
104
|
const item = location.item;
|
|
105
105
|
if (item.type !== 'step') {
|
|
@@ -127,14 +127,14 @@ export function canApply(op: PlanOp, actor: PlanActor, plan: Plan): PlanVerdict
|
|
|
127
127
|
}
|
|
128
128
|
case 'move': {
|
|
129
129
|
if (!locateItem(plan, op.id)) {
|
|
130
|
-
return
|
|
130
|
+
return missingItem(op.id);
|
|
131
131
|
}
|
|
132
132
|
return mayHoldChildren(plan, op.under);
|
|
133
133
|
}
|
|
134
134
|
case 'remove': {
|
|
135
135
|
const location = locateItem(plan, op.id);
|
|
136
136
|
if (!location) {
|
|
137
|
-
return
|
|
137
|
+
return missingItem(op.id);
|
|
138
138
|
}
|
|
139
139
|
if (holdsPersonState(location.item)) {
|
|
140
140
|
return refuse('set-by-person', `A person checked "${op.id}" or a step under it, so it stays`);
|
package/src/tree.ts
CHANGED
|
@@ -27,6 +27,10 @@ export function refuse(code: PlanRefusalCode, message: string): PlanRefusal {
|
|
|
27
27
|
return { ok: false, code, message };
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
export function missingItem(id: string): PlanRefusal {
|
|
31
|
+
return refuse('plan-missing-item', `The plan has no item "${id}"`);
|
|
32
|
+
}
|
|
33
|
+
|
|
30
34
|
/* Where an item stands: the array that holds it, so an operation can splice it, and the step or section around it. */
|
|
31
35
|
export interface PlanLocation {
|
|
32
36
|
item: PlanItem;
|