@kisev/agentomatic 11.0.0-dev.46.gfbe1e4c6992e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -0
- package/README.ru.md +98 -0
- package/dist/agent-profiles.d.ts +125 -0
- package/dist/agent-profiles.js +820 -0
- package/dist/assets/agents/architect.md +67 -0
- package/dist/assets/agents/critic.md +55 -0
- package/dist/assets/agents/manager.md +40 -0
- package/dist/assets/agents/mapper.md +33 -0
- package/dist/assets/agents/review.md +37 -0
- package/dist/assets/agents/worker.md +78 -0
- package/dist/assets/commands/agents-md.md +10 -0
- package/dist/assets/commands/askme.md +10 -0
- package/dist/assets/commands/ast-grep.md +10 -0
- package/dist/assets/commands/briefing.md +10 -0
- package/dist/assets/commands/code-explain.md +10 -0
- package/dist/assets/commands/code-review.md +10 -0
- package/dist/assets/commands/commit-msg.md +10 -0
- package/dist/assets/commands/docs-prepare.md +10 -0
- package/dist/assets/commands/docs-review.md +10 -0
- package/dist/assets/commands/goal.md +10 -0
- package/dist/assets/commands/humanize.md +10 -0
- package/dist/assets/commands/mattermost-triage.md +10 -0
- package/dist/assets/commands/mattermost.md +10 -0
- package/dist/assets/commands/mr-prepare.md +10 -0
- package/dist/assets/commands/release-prepare.md +10 -0
- package/dist/assets/commands/release-review.md +10 -0
- package/dist/assets/commands/rtk-stats.md +13 -0
- package/dist/assets/commands/rtk.md +10 -0
- package/dist/assets/commands/skill-improve.md +10 -0
- package/dist/assets/commands/slides-prompts-prepare.md +10 -0
- package/dist/assets/commands/spec-manage.md +13 -0
- package/dist/assets/commands/stopit.md +10 -0
- package/dist/assets/commands/task-prepare.md +10 -0
- package/dist/assets/commands/task-review.md +10 -0
- package/dist/assets/commands/task-triage.md +10 -0
- package/dist/assets/commands/taskmatic.md +10 -0
- package/dist/assets/commands/team-retro.md +10 -0
- package/dist/assets/commands/team-roadmap.md +10 -0
- package/dist/assets/commands/team-sprint-close.md +10 -0
- package/dist/assets/commands/team-sprint-start.md +10 -0
- package/dist/assets/lsp-catalog.json +30 -0
- package/dist/assets/migration-inventory.json +301 -0
- package/dist/assets/plugins/memomatic.js +3 -0
- package/dist/assets/plugins/rtk.js +3 -0
- package/dist/assets/plugins/rules-injector.js +3 -0
- package/dist/assets/plugins/zed-bell.js +3 -0
- package/dist/catalog.d.ts +8 -0
- package/dist/catalog.js +39 -0
- package/dist/cli-output.d.ts +27 -0
- package/dist/cli-output.js +296 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +1135 -0
- package/dist/config-setup.d.ts +71 -0
- package/dist/config-setup.js +438 -0
- package/dist/contracts.d.ts +52 -0
- package/dist/contracts.js +102 -0
- package/dist/doctor.d.ts +44 -0
- package/dist/doctor.js +638 -0
- package/dist/generate-assets.d.ts +1 -0
- package/dist/generate-assets.js +54 -0
- package/dist/index.d.ts +125 -0
- package/dist/index.js +141 -0
- package/dist/installer.d.ts +56 -0
- package/dist/installer.js +710 -0
- package/dist/jsonc.d.ts +56 -0
- package/dist/jsonc.js +432 -0
- package/dist/lifecycle.d.ts +77 -0
- package/dist/lifecycle.js +736 -0
- package/dist/package-metadata.d.ts +4 -0
- package/dist/package-metadata.js +42 -0
- package/dist/plugins/memomatic.d.ts +76 -0
- package/dist/plugins/memomatic.js +101 -0
- package/dist/plugins/rtk.d.ts +41 -0
- package/dist/plugins/rtk.js +177 -0
- package/dist/plugins/rules-injector.d.ts +22 -0
- package/dist/plugins/rules-injector.js +131 -0
- package/dist/plugins/zed-bell.d.ts +13 -0
- package/dist/plugins/zed-bell.js +14 -0
- package/dist/reconcile.d.ts +47 -0
- package/dist/reconcile.js +490 -0
- package/dist/registry.d.ts +21 -0
- package/dist/registry.js +92 -0
- package/dist/routing.d.ts +133 -0
- package/dist/routing.js +578 -0
- package/dist/runtime/state.d.ts +9 -0
- package/dist/runtime/state.js +193 -0
- package/dist/runtime/worktree.d.ts +33 -0
- package/dist/runtime/worktree.js +220 -0
- package/dist/terminal-wizard.d.ts +3 -0
- package/dist/terminal-wizard.js +237 -0
- package/package.json +72 -0
package/dist/jsonc.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export declare class JsoncError extends Error {
|
|
2
|
+
readonly code: string;
|
|
3
|
+
constructor(code: string, message: string);
|
|
4
|
+
}
|
|
5
|
+
type BaseNode = {
|
|
6
|
+
start: number;
|
|
7
|
+
end: number;
|
|
8
|
+
};
|
|
9
|
+
type ObjectNode = BaseNode & {
|
|
10
|
+
kind: "object";
|
|
11
|
+
props: Map<string, Node>;
|
|
12
|
+
propOrder: string[];
|
|
13
|
+
closeStart: number;
|
|
14
|
+
trailingComma: boolean;
|
|
15
|
+
empty: boolean;
|
|
16
|
+
};
|
|
17
|
+
type ArrayNode = BaseNode & {
|
|
18
|
+
kind: "array";
|
|
19
|
+
items: Node[];
|
|
20
|
+
closeStart: number;
|
|
21
|
+
trailingComma: boolean;
|
|
22
|
+
empty: boolean;
|
|
23
|
+
};
|
|
24
|
+
type ScalarNode = BaseNode & {
|
|
25
|
+
kind: "scalar";
|
|
26
|
+
raw: string;
|
|
27
|
+
};
|
|
28
|
+
type Node = ObjectNode | ArrayNode | ScalarNode;
|
|
29
|
+
export type JsoncPath = Array<string | number>;
|
|
30
|
+
export type JsoncEdit = {
|
|
31
|
+
kind: "append-unique";
|
|
32
|
+
path: JsoncPath;
|
|
33
|
+
value: string;
|
|
34
|
+
} | {
|
|
35
|
+
kind: "replace-array-value";
|
|
36
|
+
path: JsoncPath;
|
|
37
|
+
from: string;
|
|
38
|
+
to: string;
|
|
39
|
+
} | {
|
|
40
|
+
kind: "set-if-absent";
|
|
41
|
+
path: JsoncPath;
|
|
42
|
+
value: unknown;
|
|
43
|
+
} | {
|
|
44
|
+
kind: "widen-scalar-map";
|
|
45
|
+
path: JsoncPath;
|
|
46
|
+
entries: Record<string, string>;
|
|
47
|
+
};
|
|
48
|
+
export type JsoncEditResult = "created" | "present" | "appended" | "replaced" | "widened";
|
|
49
|
+
export declare function nodeToValue(node: Node): unknown;
|
|
50
|
+
export declare function parseJsonc(text: string): unknown;
|
|
51
|
+
export declare function applyJsoncEdits(text: string, edits: readonly JsoncEdit[]): {
|
|
52
|
+
text: string;
|
|
53
|
+
results: JsoncEditResult[];
|
|
54
|
+
changed: boolean;
|
|
55
|
+
};
|
|
56
|
+
export {};
|
package/dist/jsonc.js
ADDED
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
// JSONC support: tolerant parsing and comment-preserving structural edits.
|
|
2
|
+
// The editor applies one splice at a time and revalidates the document after
|
|
3
|
+
// every edit, so an edit can never leave the file unparsable.
|
|
4
|
+
export class JsoncError extends Error {
|
|
5
|
+
code;
|
|
6
|
+
constructor(code, message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.code = code;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function tokenize(text) {
|
|
12
|
+
const tokens = [];
|
|
13
|
+
let index = 0;
|
|
14
|
+
const push = (type, start, end) => {
|
|
15
|
+
tokens.push({ type, start, end, raw: text.slice(start, end) });
|
|
16
|
+
};
|
|
17
|
+
while (index < text.length) {
|
|
18
|
+
const character = text[index];
|
|
19
|
+
if (character === " " || character === "\t" || character === "\r" || character === "\n") {
|
|
20
|
+
const start = index;
|
|
21
|
+
while (index < text.length && " \t\r\n".includes(text[index]))
|
|
22
|
+
index += 1;
|
|
23
|
+
push("ws", start, index);
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (character === "/" && text[index + 1] === "/") {
|
|
27
|
+
const start = index;
|
|
28
|
+
while (index < text.length && text[index] !== "\n")
|
|
29
|
+
index += 1;
|
|
30
|
+
push("comment", start, index);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (character === "/" && text[index + 1] === "*") {
|
|
34
|
+
const start = index;
|
|
35
|
+
index += 2;
|
|
36
|
+
while (index < text.length && !(text[index] === "*" && text[index + 1] === "/"))
|
|
37
|
+
index += 1;
|
|
38
|
+
if (index >= text.length)
|
|
39
|
+
throw new JsoncError("invalid_jsonc", `Unterminated block comment at offset ${start}`);
|
|
40
|
+
index += 2;
|
|
41
|
+
push("comment", start, index);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (character === '"') {
|
|
45
|
+
const start = index;
|
|
46
|
+
index += 1;
|
|
47
|
+
while (index < text.length && text[index] !== '"') {
|
|
48
|
+
if (text[index] === "\\")
|
|
49
|
+
index += 1;
|
|
50
|
+
index += 1;
|
|
51
|
+
}
|
|
52
|
+
if (index >= text.length)
|
|
53
|
+
throw new JsoncError("invalid_jsonc", `Unterminated string at offset ${start}`);
|
|
54
|
+
index += 1;
|
|
55
|
+
push("string", start, index);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (/[0-9-]/.test(character)) {
|
|
59
|
+
const start = index;
|
|
60
|
+
while (index < text.length && /[0-9+.eE-]/.test(text[index]))
|
|
61
|
+
index += 1;
|
|
62
|
+
push("number", start, index);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (/[a-z]/.test(character)) {
|
|
66
|
+
const start = index;
|
|
67
|
+
while (index < text.length && /[a-z]/.test(text[index]))
|
|
68
|
+
index += 1;
|
|
69
|
+
push("literal", start, index);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if ("{}[]:,".includes(character)) {
|
|
73
|
+
push("punct", index, index + 1);
|
|
74
|
+
index += 1;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
throw new JsoncError("invalid_jsonc", `Unexpected character at offset ${index}`);
|
|
78
|
+
}
|
|
79
|
+
return tokens;
|
|
80
|
+
}
|
|
81
|
+
function significant(parser) {
|
|
82
|
+
while (parser.cursor < parser.tokens.length) {
|
|
83
|
+
const token = parser.tokens[parser.cursor];
|
|
84
|
+
if (token.type === "ws" || token.type === "comment") {
|
|
85
|
+
parser.cursor += 1;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
return token;
|
|
89
|
+
}
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
function expectPunct(parser, value) {
|
|
93
|
+
const token = significant(parser);
|
|
94
|
+
if (!token || token.type !== "punct" || token.raw !== value)
|
|
95
|
+
throw new JsoncError("invalid_jsonc", `Expected '${value}' at offset ${token ? token.start : (parser.tokens.at(-1)?.end ?? 0)}`);
|
|
96
|
+
parser.cursor += 1;
|
|
97
|
+
return token;
|
|
98
|
+
}
|
|
99
|
+
function parseValue(parser) {
|
|
100
|
+
const token = significant(parser);
|
|
101
|
+
if (!token)
|
|
102
|
+
throw new JsoncError("invalid_jsonc", "Unexpected end of document");
|
|
103
|
+
if (token.type === "punct" && token.raw === "{")
|
|
104
|
+
return parseObject(parser);
|
|
105
|
+
if (token.type === "punct" && token.raw === "[")
|
|
106
|
+
return parseArray(parser);
|
|
107
|
+
if (token.type === "string" || token.type === "number" || token.type === "literal") {
|
|
108
|
+
parser.cursor += 1;
|
|
109
|
+
return { kind: "scalar", start: token.start, end: token.end, raw: token.raw };
|
|
110
|
+
}
|
|
111
|
+
throw new JsoncError("invalid_jsonc", `Unexpected token at offset ${token.start}`);
|
|
112
|
+
}
|
|
113
|
+
function parseObject(parser) {
|
|
114
|
+
const open = expectPunct(parser, "{");
|
|
115
|
+
const props = new Map();
|
|
116
|
+
const propOrder = [];
|
|
117
|
+
let trailingComma = false;
|
|
118
|
+
let closeStart = open.end;
|
|
119
|
+
let closed = false;
|
|
120
|
+
for (;;) {
|
|
121
|
+
const token = significant(parser);
|
|
122
|
+
if (!token)
|
|
123
|
+
throw new JsoncError("invalid_jsonc", `Unterminated object at offset ${open.start}`);
|
|
124
|
+
if (token.type === "punct" && token.raw === "}") {
|
|
125
|
+
closeStart = token.start;
|
|
126
|
+
parser.cursor += 1;
|
|
127
|
+
closed = true;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
if (token.type !== "string")
|
|
131
|
+
throw new JsoncError("invalid_jsonc", `Expected object key at offset ${token.start}`);
|
|
132
|
+
const key = JSON.parse(token.raw);
|
|
133
|
+
parser.cursor += 1;
|
|
134
|
+
expectPunct(parser, ":");
|
|
135
|
+
const value = parseValue(parser);
|
|
136
|
+
if (props.has(key))
|
|
137
|
+
throw new JsoncError("invalid_jsonc", `Duplicate object key at offset ${token.start}`);
|
|
138
|
+
props.set(key, value);
|
|
139
|
+
propOrder.push(key);
|
|
140
|
+
trailingComma = false;
|
|
141
|
+
const next = significant(parser);
|
|
142
|
+
if (next && next.type === "punct" && next.raw === ",") {
|
|
143
|
+
parser.cursor += 1;
|
|
144
|
+
trailingComma = true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (!closed)
|
|
148
|
+
throw new JsoncError("invalid_jsonc", `Unterminated object at offset ${open.start}`);
|
|
149
|
+
return {
|
|
150
|
+
kind: "object",
|
|
151
|
+
start: open.start,
|
|
152
|
+
end: closeStart + 1,
|
|
153
|
+
props,
|
|
154
|
+
propOrder,
|
|
155
|
+
closeStart,
|
|
156
|
+
trailingComma,
|
|
157
|
+
empty: props.size === 0,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function parseArray(parser) {
|
|
161
|
+
const open = expectPunct(parser, "[");
|
|
162
|
+
const items = [];
|
|
163
|
+
let trailingComma = false;
|
|
164
|
+
let closeStart = open.end;
|
|
165
|
+
let closed = false;
|
|
166
|
+
for (;;) {
|
|
167
|
+
const token = significant(parser);
|
|
168
|
+
if (!token)
|
|
169
|
+
throw new JsoncError("invalid_jsonc", `Unterminated array at offset ${open.start}`);
|
|
170
|
+
if (token.type === "punct" && token.raw === "]") {
|
|
171
|
+
closeStart = token.start;
|
|
172
|
+
parser.cursor += 1;
|
|
173
|
+
closed = true;
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
items.push(parseValue(parser));
|
|
177
|
+
trailingComma = false;
|
|
178
|
+
const next = significant(parser);
|
|
179
|
+
if (next && next.type === "punct" && next.raw === ",") {
|
|
180
|
+
parser.cursor += 1;
|
|
181
|
+
trailingComma = true;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (!closed)
|
|
185
|
+
throw new JsoncError("invalid_jsonc", `Unterminated array at offset ${open.start}`);
|
|
186
|
+
return {
|
|
187
|
+
kind: "array",
|
|
188
|
+
start: open.start,
|
|
189
|
+
end: closeStart + 1,
|
|
190
|
+
items,
|
|
191
|
+
closeStart,
|
|
192
|
+
trailingComma,
|
|
193
|
+
empty: items.length === 0,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
export function nodeToValue(node) {
|
|
197
|
+
if (node.kind === "scalar")
|
|
198
|
+
return JSON.parse(node.raw);
|
|
199
|
+
if (node.kind === "array")
|
|
200
|
+
return node.items.map(nodeToValue);
|
|
201
|
+
const value = {};
|
|
202
|
+
for (const key of node.propOrder)
|
|
203
|
+
value[key] = nodeToValue(node.props.get(key));
|
|
204
|
+
return value;
|
|
205
|
+
}
|
|
206
|
+
function parseDocument(text) {
|
|
207
|
+
const parser = { tokens: tokenize(text), cursor: 0 };
|
|
208
|
+
const node = parseValue(parser);
|
|
209
|
+
const trailing = significant(parser);
|
|
210
|
+
if (trailing)
|
|
211
|
+
throw new JsoncError("invalid_jsonc", `Unexpected trailing content at offset ${trailing.start}`);
|
|
212
|
+
return node;
|
|
213
|
+
}
|
|
214
|
+
export function parseJsonc(text) {
|
|
215
|
+
return nodeToValue(parseDocument(text));
|
|
216
|
+
}
|
|
217
|
+
function resolve(root, path) {
|
|
218
|
+
let current = root;
|
|
219
|
+
for (const segment of path) {
|
|
220
|
+
if (!current)
|
|
221
|
+
return undefined;
|
|
222
|
+
if (typeof segment === "string") {
|
|
223
|
+
if (current.kind !== "object")
|
|
224
|
+
return undefined;
|
|
225
|
+
current = current.props.get(segment);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
if (current.kind !== "array")
|
|
229
|
+
return undefined;
|
|
230
|
+
current = current.items[segment];
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return current;
|
|
234
|
+
}
|
|
235
|
+
function lineIndent(text, position) {
|
|
236
|
+
const lineStart = text.lastIndexOf("\n", Math.max(0, position - 1)) + 1;
|
|
237
|
+
return /^[ \t]*/.exec(text.slice(lineStart, position))[0];
|
|
238
|
+
}
|
|
239
|
+
function serialize(value, indent) {
|
|
240
|
+
const rendered = JSON.stringify(value, null, 2);
|
|
241
|
+
if (typeof value !== "object" || value === null)
|
|
242
|
+
return rendered;
|
|
243
|
+
return rendered
|
|
244
|
+
.split("\n")
|
|
245
|
+
.map((line, index) => (index === 0 ? line : `${indent}${line}`))
|
|
246
|
+
.join("\n");
|
|
247
|
+
}
|
|
248
|
+
function insertObjectEntry(text, node, key, value) {
|
|
249
|
+
const lastKey = node.propOrder.at(-1);
|
|
250
|
+
const lastValue = lastKey ? node.props.get(lastKey) : undefined;
|
|
251
|
+
const at = lastValue ? lastValue.end : node.start + 1;
|
|
252
|
+
const indent = lastValue
|
|
253
|
+
? lineIndent(text, lastValue.start)
|
|
254
|
+
: `${lineIndent(text, node.closeStart)} `;
|
|
255
|
+
const entry = `${JSON.stringify(key)}: ${serialize(value, indent)}`;
|
|
256
|
+
const insertion = lastValue
|
|
257
|
+
? `,\n${indent}${entry}`
|
|
258
|
+
: `\n${indent}${entry}\n${lineIndent(text, node.closeStart)}`;
|
|
259
|
+
return text.slice(0, at) + insertion + text.slice(at);
|
|
260
|
+
}
|
|
261
|
+
function requireObjectPath(text, root, path) {
|
|
262
|
+
let currentText = text;
|
|
263
|
+
for (let depth = 0; depth < path.length; depth += 1) {
|
|
264
|
+
const segment = path[depth];
|
|
265
|
+
if (typeof segment !== "string")
|
|
266
|
+
throw new JsoncError("unsupported_edit", "Object path segments must be strings");
|
|
267
|
+
const document = parseDocument(currentText);
|
|
268
|
+
if (resolve(document, path.slice(0, depth + 1)))
|
|
269
|
+
continue;
|
|
270
|
+
const parent = resolve(document, path.slice(0, depth));
|
|
271
|
+
if (!parent || parent.kind !== "object")
|
|
272
|
+
throw new JsoncError("conflict", `Path parent is not an object: ${segment}`);
|
|
273
|
+
currentText = insertObjectEntry(currentText, parent, segment, {});
|
|
274
|
+
}
|
|
275
|
+
const node = resolve(parseDocument(currentText), path);
|
|
276
|
+
if (!node || node.kind !== "object")
|
|
277
|
+
throw new JsoncError("conflict", "Path target is missing or not an object");
|
|
278
|
+
return { text: currentText, node };
|
|
279
|
+
}
|
|
280
|
+
function applyEdit(text, root, edit) {
|
|
281
|
+
if (edit.kind === "append-unique") {
|
|
282
|
+
const target = resolve(root, edit.path);
|
|
283
|
+
if (!target || target.kind !== "array")
|
|
284
|
+
throw new JsoncError("conflict", "append-unique target is missing or not an array");
|
|
285
|
+
if (target.items.map(nodeToValue).includes(edit.value))
|
|
286
|
+
return { text, result: "present" };
|
|
287
|
+
const insertion = target.empty ? JSON.stringify(edit.value) : `, ${JSON.stringify(edit.value)}`;
|
|
288
|
+
const at = target.empty ? target.start + 1 : target.items.at(-1).end;
|
|
289
|
+
return { text: text.slice(0, at) + insertion + text.slice(at), result: "appended" };
|
|
290
|
+
}
|
|
291
|
+
if (edit.kind === "replace-array-value") {
|
|
292
|
+
const target = resolve(root, edit.path);
|
|
293
|
+
if (!target || target.kind !== "array")
|
|
294
|
+
throw new JsoncError("conflict", "replace-array-value target is missing or not an array");
|
|
295
|
+
const index = target.items.map(nodeToValue).indexOf(edit.from);
|
|
296
|
+
if (index === -1)
|
|
297
|
+
return { text, result: "present" };
|
|
298
|
+
const node = target.items[index];
|
|
299
|
+
return {
|
|
300
|
+
text: text.slice(0, node.start) + JSON.stringify(edit.to) + text.slice(node.end),
|
|
301
|
+
result: "replaced",
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
if (edit.kind === "set-if-absent") {
|
|
305
|
+
const key = edit.path.at(-1);
|
|
306
|
+
if (typeof key !== "string")
|
|
307
|
+
throw new JsoncError("unsupported_edit", "set-if-absent requires a string object key");
|
|
308
|
+
const parentPath = edit.path.slice(0, -1);
|
|
309
|
+
const parent = resolve(root, parentPath);
|
|
310
|
+
if (parent && parent.kind === "object" && parent.props.has(key))
|
|
311
|
+
return { text, result: "present" };
|
|
312
|
+
const ensured = requireObjectPath(text, root, parentPath);
|
|
313
|
+
if (ensured.node.props.has(key))
|
|
314
|
+
return { text, result: "present" };
|
|
315
|
+
return {
|
|
316
|
+
text: insertObjectEntry(ensured.text, ensured.node, key, edit.value),
|
|
317
|
+
result: "created",
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
const target = resolve(root, edit.path);
|
|
321
|
+
if (!target) {
|
|
322
|
+
const ensured = requireObjectPath(text, root, edit.path);
|
|
323
|
+
let currentText = ensured.text;
|
|
324
|
+
let changed = false;
|
|
325
|
+
for (const [key, value] of Object.entries(edit.entries)) {
|
|
326
|
+
const node = resolve(parseDocument(currentText), edit.path);
|
|
327
|
+
if (!node || node.kind !== "object")
|
|
328
|
+
throw new JsoncError("conflict", "widen-scalar-map target stopped being an object");
|
|
329
|
+
if (node.props.has(key))
|
|
330
|
+
continue;
|
|
331
|
+
currentText = insertObjectEntry(currentText, node, key, value);
|
|
332
|
+
changed = true;
|
|
333
|
+
}
|
|
334
|
+
return { text: currentText, result: changed ? "created" : "present" };
|
|
335
|
+
}
|
|
336
|
+
if (target.kind === "object") {
|
|
337
|
+
let currentText = text;
|
|
338
|
+
let changed = false;
|
|
339
|
+
for (const [key, value] of Object.entries(edit.entries)) {
|
|
340
|
+
const node = resolve(parseDocument(currentText), edit.path);
|
|
341
|
+
if (!node || node.kind !== "object")
|
|
342
|
+
throw new JsoncError("conflict", "widen-scalar-map target stopped being an object");
|
|
343
|
+
if (node.props.has(key))
|
|
344
|
+
continue;
|
|
345
|
+
currentText = insertObjectEntry(currentText, node, key, value);
|
|
346
|
+
changed = true;
|
|
347
|
+
}
|
|
348
|
+
return { text: currentText, result: changed ? "created" : "present" };
|
|
349
|
+
}
|
|
350
|
+
if (target.kind !== "scalar" || !/^(?:"(?:[^"\\]|\\.)*"|true|false|null)$/.test(target.raw))
|
|
351
|
+
throw new JsoncError("conflict", "widen-scalar-map target is neither a map nor a scalar");
|
|
352
|
+
const parentIndent = lineIndent(text, target.start);
|
|
353
|
+
const childIndent = `${parentIndent} `;
|
|
354
|
+
const lines = [`"*": ${target.raw}`];
|
|
355
|
+
for (const [key, value] of Object.entries(edit.entries)) {
|
|
356
|
+
if (key === "*")
|
|
357
|
+
continue;
|
|
358
|
+
lines.push(`${JSON.stringify(key)}: ${JSON.stringify(value)}`);
|
|
359
|
+
}
|
|
360
|
+
const replacement = `{\n${childIndent}${lines.join(`,\n${childIndent}`)}\n${parentIndent}}`;
|
|
361
|
+
return {
|
|
362
|
+
text: text.slice(0, target.start) + replacement + text.slice(target.end),
|
|
363
|
+
result: "widened",
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
function at(value, path) {
|
|
367
|
+
return path.reduce((current, segment) => current && typeof current === "object"
|
|
368
|
+
? current[segment]
|
|
369
|
+
: undefined, value);
|
|
370
|
+
}
|
|
371
|
+
function verifyEdit(before, after, edit) {
|
|
372
|
+
if (edit.kind === "append-unique") {
|
|
373
|
+
const target = at(after, edit.path);
|
|
374
|
+
if (!Array.isArray(target) || !target.includes(edit.value))
|
|
375
|
+
throw new JsoncError("merge_validation_failed", "append-unique postcondition failed");
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
if (edit.kind === "replace-array-value") {
|
|
379
|
+
const beforeTarget = at(before, edit.path);
|
|
380
|
+
const afterTarget = at(after, edit.path);
|
|
381
|
+
if (Array.isArray(beforeTarget) && beforeTarget.includes(edit.from)) {
|
|
382
|
+
if (!Array.isArray(afterTarget) ||
|
|
383
|
+
!afterTarget.includes(edit.to) ||
|
|
384
|
+
afterTarget.includes(edit.from))
|
|
385
|
+
throw new JsoncError("merge_validation_failed", "replace-array-value postcondition failed");
|
|
386
|
+
}
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
if (edit.kind === "set-if-absent") {
|
|
390
|
+
const key = edit.path.at(-1);
|
|
391
|
+
const parentPath = edit.path.slice(0, -1);
|
|
392
|
+
const parentAfter = at(after, parentPath);
|
|
393
|
+
if (!parentAfter ||
|
|
394
|
+
typeof parentAfter !== "object" ||
|
|
395
|
+
Array.isArray(parentAfter) ||
|
|
396
|
+
!(key in parentAfter))
|
|
397
|
+
throw new JsoncError("merge_validation_failed", "set-if-absent postcondition failed");
|
|
398
|
+
const parentBefore = at(before, parentPath);
|
|
399
|
+
if (parentBefore &&
|
|
400
|
+
typeof parentBefore === "object" &&
|
|
401
|
+
!Array.isArray(parentBefore) &&
|
|
402
|
+
key in parentBefore &&
|
|
403
|
+
JSON.stringify(parentBefore[key]) !==
|
|
404
|
+
JSON.stringify(parentAfter[key]))
|
|
405
|
+
throw new JsoncError("merge_validation_failed", "set-if-absent modified an existing entry");
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
const target = at(after, edit.path);
|
|
409
|
+
if (!target || typeof target !== "object" || Array.isArray(target))
|
|
410
|
+
throw new JsoncError("merge_validation_failed", "widen-scalar-map postcondition failed");
|
|
411
|
+
for (const [key, value] of Object.entries(edit.entries)) {
|
|
412
|
+
if (target[key] !== value)
|
|
413
|
+
throw new JsoncError("merge_validation_failed", "widen-scalar-map entry missing");
|
|
414
|
+
}
|
|
415
|
+
const beforeTarget = at(before, edit.path);
|
|
416
|
+
if (beforeTarget !== undefined &&
|
|
417
|
+
(typeof beforeTarget !== "object" || beforeTarget === null || Array.isArray(beforeTarget)) &&
|
|
418
|
+
target["*"] !== beforeTarget)
|
|
419
|
+
throw new JsoncError("merge_validation_failed", "widened map lost the original scalar value");
|
|
420
|
+
}
|
|
421
|
+
export function applyJsoncEdits(text, edits) {
|
|
422
|
+
const before = parseJsonc(text);
|
|
423
|
+
let current = text;
|
|
424
|
+
const results = [];
|
|
425
|
+
for (const edit of edits) {
|
|
426
|
+
const applied = applyEdit(current, parseDocument(current), edit);
|
|
427
|
+
verifyEdit(before, parseJsonc(applied.text), edit);
|
|
428
|
+
results.push(applied.result);
|
|
429
|
+
current = applied.text;
|
|
430
|
+
}
|
|
431
|
+
return { text: current, results, changed: current !== text };
|
|
432
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export declare const RECEIPT_TTL_MS: number;
|
|
2
|
+
export { LifecycleError, assertSafePath, readRegular, writeAtomic } from "@kisev/safe-fs";
|
|
3
|
+
export type Scope = "global" | "project";
|
|
4
|
+
export type FileExpectation = {
|
|
5
|
+
sha256?: string;
|
|
6
|
+
absent?: true;
|
|
7
|
+
};
|
|
8
|
+
export type FileMutation = {
|
|
9
|
+
path: string;
|
|
10
|
+
root?: string;
|
|
11
|
+
operation: "write";
|
|
12
|
+
content: Buffer;
|
|
13
|
+
mode: number;
|
|
14
|
+
expected: FileExpectation;
|
|
15
|
+
} | {
|
|
16
|
+
path: string;
|
|
17
|
+
root?: string;
|
|
18
|
+
operation: "remove";
|
|
19
|
+
expected: FileExpectation;
|
|
20
|
+
} | {
|
|
21
|
+
path: string;
|
|
22
|
+
root?: string;
|
|
23
|
+
operation: "external-write";
|
|
24
|
+
content: Buffer;
|
|
25
|
+
mode: number;
|
|
26
|
+
expected: FileExpectation;
|
|
27
|
+
} | {
|
|
28
|
+
path: string;
|
|
29
|
+
root?: string;
|
|
30
|
+
operation: "external-remove";
|
|
31
|
+
expected: FileExpectation;
|
|
32
|
+
};
|
|
33
|
+
export type SupersededPlan = {
|
|
34
|
+
kind: string;
|
|
35
|
+
confirmation_digest: string;
|
|
36
|
+
created_at: string;
|
|
37
|
+
expires_at: string;
|
|
38
|
+
};
|
|
39
|
+
export declare function stable(value: unknown): string;
|
|
40
|
+
export declare function sha256(value: Buffer | string): string;
|
|
41
|
+
export declare function digest(value: unknown): string;
|
|
42
|
+
export declare function assertSafeRelative(value: string): void;
|
|
43
|
+
export declare function destination(root: string, relativePath: string): string;
|
|
44
|
+
export declare function deploymentRoot(scope: Scope, cwd?: string, home?: string): string;
|
|
45
|
+
export declare function lifecycleRoot(scope: Scope, cwd?: string, home?: string): string;
|
|
46
|
+
export declare function migrateLegacyDeploymentNamespace(root: string): Promise<boolean>;
|
|
47
|
+
export declare function migrateLegacyNamespaces(home?: string): Promise<string[]>;
|
|
48
|
+
export declare function archiveRoot(scope: Scope, cwd?: string, home?: string): string;
|
|
49
|
+
export declare function withLifecycleLock<T>(stateRoot: string, callback: () => Promise<T>): Promise<T>;
|
|
50
|
+
export declare function saveReceipt(stateRoot: string, kind: string, scope: Scope, root: string, payload: unknown, now?: number, planDigest?: string): Promise<{
|
|
51
|
+
digest: string;
|
|
52
|
+
plan_digest: string;
|
|
53
|
+
expires_at: string;
|
|
54
|
+
superseded_plan?: SupersededPlan;
|
|
55
|
+
}>;
|
|
56
|
+
export declare function consumeReceipt(stateRoot: string, expected: {
|
|
57
|
+
digest: string;
|
|
58
|
+
kind: string;
|
|
59
|
+
scope: Scope;
|
|
60
|
+
root: string;
|
|
61
|
+
}, now?: number): Promise<unknown>;
|
|
62
|
+
/** Invalidate an active receipt when a preview is blocked before a replacement exists. */
|
|
63
|
+
export declare function supersedeReceipt(stateRoot: string, now?: number): Promise<void>;
|
|
64
|
+
export declare function recoverTransaction(root: string, stateRoot: string, allowedRoots?: readonly string[]): Promise<boolean>;
|
|
65
|
+
export type TransactionOptions = {
|
|
66
|
+
beforePublish?: (index: number) => void;
|
|
67
|
+
afterPublish?: (published: number) => "continue" | "fail" | "interrupt";
|
|
68
|
+
applyExternal?: () => Promise<void>;
|
|
69
|
+
validateFinal?: () => Promise<void>;
|
|
70
|
+
};
|
|
71
|
+
export declare function applyTransaction(root: string, stateRoot: string, mutations: readonly FileMutation[], options?: TransactionOptions): Promise<void>;
|
|
72
|
+
export declare function appendPrivate(path: string, value: unknown): Promise<void>;
|
|
73
|
+
export declare function listDirectRegular(directory: string): Promise<Array<{
|
|
74
|
+
name: string;
|
|
75
|
+
content: Buffer;
|
|
76
|
+
mode: number;
|
|
77
|
+
}>>;
|