@oscharko-dev/keiko-contracts 0.2.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/.tsbuildinfo +1 -0
- package/dist/bff-wire.d.ts +661 -0
- package/dist/bff-wire.d.ts.map +1 -0
- package/dist/bff-wire.js +102 -0
- package/dist/bug-investigation-events.d.ts +92 -0
- package/dist/bug-investigation-events.d.ts.map +1 -0
- package/dist/bug-investigation-events.js +18 -0
- package/dist/coding-context.d.ts +76 -0
- package/dist/coding-context.d.ts.map +1 -0
- package/dist/coding-context.js +158 -0
- package/dist/connected-context.d.ts +174 -0
- package/dist/connected-context.d.ts.map +1 -0
- package/dist/connected-context.js +636 -0
- package/dist/conversation-budget.d.ts +37 -0
- package/dist/conversation-budget.d.ts.map +1 -0
- package/dist/conversation-budget.js +97 -0
- package/dist/editor-agent.d.ts +131 -0
- package/dist/editor-agent.d.ts.map +1 -0
- package/dist/editor-agent.js +197 -0
- package/dist/editor-completion.d.ts +62 -0
- package/dist/editor-completion.d.ts.map +1 -0
- package/dist/editor-completion.js +147 -0
- package/dist/editor-dirty-close.d.ts +17 -0
- package/dist/editor-dirty-close.d.ts.map +1 -0
- package/dist/editor-dirty-close.js +8 -0
- package/dist/editor-hot-exit.d.ts +18 -0
- package/dist/editor-hot-exit.d.ts.map +1 -0
- package/dist/editor-hot-exit.js +42 -0
- package/dist/editor-inline-completion.d.ts +70 -0
- package/dist/editor-inline-completion.d.ts.map +1 -0
- package/dist/editor-inline-completion.js +215 -0
- package/dist/editor-layout.d.ts +105 -0
- package/dist/editor-layout.d.ts.map +1 -0
- package/dist/editor-layout.js +479 -0
- package/dist/editor-patch-apply.d.ts +77 -0
- package/dist/editor-patch-apply.d.ts.map +1 -0
- package/dist/editor-patch-apply.js +122 -0
- package/dist/editor-session.d.ts +31 -0
- package/dist/editor-session.d.ts.map +1 -0
- package/dist/editor-session.js +75 -0
- package/dist/editor-test-generation.d.ts +104 -0
- package/dist/editor-test-generation.d.ts.map +1 -0
- package/dist/editor-test-generation.js +211 -0
- package/dist/evaluations.d.ts +75 -0
- package/dist/evaluations.d.ts.map +1 -0
- package/dist/evaluations.js +16 -0
- package/dist/evidence.d.ts +297 -0
- package/dist/evidence.d.ts.map +1 -0
- package/dist/evidence.js +9 -0
- package/dist/gateway.d.ts +129 -0
- package/dist/gateway.d.ts.map +1 -0
- package/dist/gateway.js +66 -0
- package/dist/harness.d.ts +274 -0
- package/dist/harness.d.ts.map +1 -0
- package/dist/harness.js +38 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +83 -0
- package/dist/language-service.d.ts +145 -0
- package/dist/language-service.d.ts.map +1 -0
- package/dist/language-service.js +161 -0
- package/dist/local-knowledge-large-document-validation.d.ts +7 -0
- package/dist/local-knowledge-large-document-validation.d.ts.map +1 -0
- package/dist/local-knowledge-large-document-validation.js +161 -0
- package/dist/local-knowledge-large-document.d.ts +113 -0
- package/dist/local-knowledge-large-document.d.ts.map +1 -0
- package/dist/local-knowledge-large-document.js +142 -0
- package/dist/local-knowledge-paths.d.ts +3 -0
- package/dist/local-knowledge-paths.d.ts.map +1 -0
- package/dist/local-knowledge-paths.js +65 -0
- package/dist/local-knowledge-records.d.ts +190 -0
- package/dist/local-knowledge-records.d.ts.map +1 -0
- package/dist/local-knowledge-records.js +36 -0
- package/dist/local-knowledge-schema-validation.d.ts +19 -0
- package/dist/local-knowledge-schema-validation.d.ts.map +1 -0
- package/dist/local-knowledge-schema-validation.js +115 -0
- package/dist/local-knowledge-schema.d.ts +14 -0
- package/dist/local-knowledge-schema.d.ts.map +1 -0
- package/dist/local-knowledge-schema.js +715 -0
- package/dist/local-knowledge-validation.d.ts +20 -0
- package/dist/local-knowledge-validation.d.ts.map +1 -0
- package/dist/local-knowledge-validation.js +487 -0
- package/dist/local-knowledge.d.ts +158 -0
- package/dist/local-knowledge.d.ts.map +1 -0
- package/dist/local-knowledge.js +63 -0
- package/dist/memory-audit-events.d.ts +73 -0
- package/dist/memory-audit-events.d.ts.map +1 -0
- package/dist/memory-audit-events.js +44 -0
- package/dist/memory-audit-validation.d.ts +4 -0
- package/dist/memory-audit-validation.d.ts.map +1 -0
- package/dist/memory-audit-validation.js +151 -0
- package/dist/memory-barrel.d.ts +15 -0
- package/dist/memory-barrel.d.ts.map +1 -0
- package/dist/memory-barrel.js +20 -0
- package/dist/memory-internal.d.ts +26 -0
- package/dist/memory-internal.d.ts.map +1 -0
- package/dist/memory-internal.js +104 -0
- package/dist/memory-operations-validation.d.ts +12 -0
- package/dist/memory-operations-validation.d.ts.map +1 -0
- package/dist/memory-operations-validation.js +267 -0
- package/dist/memory-operations.d.ts +156 -0
- package/dist/memory-operations.d.ts.map +1 -0
- package/dist/memory-operations.js +29 -0
- package/dist/memory-record-validation.d.ts +10 -0
- package/dist/memory-record-validation.d.ts.map +1 -0
- package/dist/memory-record-validation.js +101 -0
- package/dist/memory-records.d.ts +66 -0
- package/dist/memory-records.d.ts.map +1 -0
- package/dist/memory-records.js +22 -0
- package/dist/memory-retrieval-validation.d.ts +6 -0
- package/dist/memory-retrieval-validation.d.ts.map +1 -0
- package/dist/memory-retrieval-validation.js +108 -0
- package/dist/memory-validation.d.ts +31 -0
- package/dist/memory-validation.d.ts.map +1 -0
- package/dist/memory-validation.js +318 -0
- package/dist/memory-workflow-port.d.ts +26 -0
- package/dist/memory-workflow-port.d.ts.map +1 -0
- package/dist/memory-workflow-port.js +13 -0
- package/dist/memory.d.ts +81 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +104 -0
- package/dist/prompt-enhancer-analyzer.d.ts +7 -0
- package/dist/prompt-enhancer-analyzer.d.ts.map +1 -0
- package/dist/prompt-enhancer-analyzer.js +745 -0
- package/dist/prompt-enhancer-bff.d.ts +67 -0
- package/dist/prompt-enhancer-bff.d.ts.map +1 -0
- package/dist/prompt-enhancer-bff.js +156 -0
- package/dist/prompt-enhancer-critic.d.ts +46 -0
- package/dist/prompt-enhancer-critic.d.ts.map +1 -0
- package/dist/prompt-enhancer-critic.js +35 -0
- package/dist/prompt-enhancer-grounding.d.ts +19 -0
- package/dist/prompt-enhancer-grounding.d.ts.map +1 -0
- package/dist/prompt-enhancer-grounding.js +235 -0
- package/dist/prompt-enhancer-safety.d.ts +66 -0
- package/dist/prompt-enhancer-safety.d.ts.map +1 -0
- package/dist/prompt-enhancer-safety.js +446 -0
- package/dist/prompt-enhancer-validation.d.ts +28 -0
- package/dist/prompt-enhancer-validation.d.ts.map +1 -0
- package/dist/prompt-enhancer-validation.js +931 -0
- package/dist/prompt-enhancer.d.ts +184 -0
- package/dist/prompt-enhancer.d.ts.map +1 -0
- package/dist/prompt-enhancer.js +350 -0
- package/dist/qualityIntelligence/assertNever.d.ts +2 -0
- package/dist/qualityIntelligence/assertNever.d.ts.map +1 -0
- package/dist/qualityIntelligence/assertNever.js +7 -0
- package/dist/qualityIntelligence/auditSummary.d.ts +25 -0
- package/dist/qualityIntelligence/auditSummary.d.ts.map +1 -0
- package/dist/qualityIntelligence/auditSummary.js +7 -0
- package/dist/qualityIntelligence/bffWire.d.ts +356 -0
- package/dist/qualityIntelligence/bffWire.d.ts.map +1 -0
- package/dist/qualityIntelligence/bffWire.js +22 -0
- package/dist/qualityIntelligence/coverageMap.d.ts +21 -0
- package/dist/qualityIntelligence/coverageMap.d.ts.map +1 -0
- package/dist/qualityIntelligence/coverageMap.js +29 -0
- package/dist/qualityIntelligence/editableRevision.d.ts +21 -0
- package/dist/qualityIntelligence/editableRevision.d.ts.map +1 -0
- package/dist/qualityIntelligence/editableRevision.js +8 -0
- package/dist/qualityIntelligence/evidenceAtom.d.ts +35 -0
- package/dist/qualityIntelligence/evidenceAtom.d.ts.map +1 -0
- package/dist/qualityIntelligence/evidenceAtom.js +29 -0
- package/dist/qualityIntelligence/exportBundle.d.ts +28 -0
- package/dist/qualityIntelligence/exportBundle.d.ts.map +1 -0
- package/dist/qualityIntelligence/exportBundle.js +46 -0
- package/dist/qualityIntelligence/handoffEnvelope.d.ts +23 -0
- package/dist/qualityIntelligence/handoffEnvelope.d.ts.map +1 -0
- package/dist/qualityIntelligence/handoffEnvelope.js +8 -0
- package/dist/qualityIntelligence/ids.d.ts +58 -0
- package/dist/qualityIntelligence/ids.d.ts.map +1 -0
- package/dist/qualityIntelligence/ids.js +93 -0
- package/dist/qualityIntelligence/index.d.ts +29 -0
- package/dist/qualityIntelligence/index.d.ts.map +1 -0
- package/dist/qualityIntelligence/index.js +20 -0
- package/dist/qualityIntelligence/reviewRecord.d.ts +19 -0
- package/dist/qualityIntelligence/reviewRecord.d.ts.map +1 -0
- package/dist/qualityIntelligence/reviewRecord.js +20 -0
- package/dist/qualityIntelligence/runPlanAndEvents.d.ts +84 -0
- package/dist/qualityIntelligence/runPlanAndEvents.d.ts.map +1 -0
- package/dist/qualityIntelligence/runPlanAndEvents.js +51 -0
- package/dist/qualityIntelligence/sourceEnvelope.d.ts +77 -0
- package/dist/qualityIntelligence/sourceEnvelope.d.ts.map +1 -0
- package/dist/qualityIntelligence/sourceEnvelope.js +118 -0
- package/dist/qualityIntelligence/testCaseCandidate.d.ts +21 -0
- package/dist/qualityIntelligence/testCaseCandidate.d.ts.map +1 -0
- package/dist/qualityIntelligence/testCaseCandidate.js +21 -0
- package/dist/qualityIntelligence/testQualityRubric.d.ts +17 -0
- package/dist/qualityIntelligence/testQualityRubric.d.ts.map +1 -0
- package/dist/qualityIntelligence/testQualityRubric.js +32 -0
- package/dist/qualityIntelligence/validationFinding.d.ts +48 -0
- package/dist/qualityIntelligence/validationFinding.d.ts.map +1 -0
- package/dist/qualityIntelligence/validationFinding.js +36 -0
- package/dist/relationships-validation.d.ts +13 -0
- package/dist/relationships-validation.d.ts.map +1 -0
- package/dist/relationships-validation.js +422 -0
- package/dist/relationships.d.ts +79 -0
- package/dist/relationships.d.ts.map +1 -0
- package/dist/relationships.js +307 -0
- package/dist/text-safety.d.ts +7 -0
- package/dist/text-safety.d.ts.map +1 -0
- package/dist/text-safety.js +58 -0
- package/dist/tools.d.ts +153 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +118 -0
- package/dist/unit-test-events.d.ts +87 -0
- package/dist/unit-test-events.d.ts.map +1 -0
- package/dist/unit-test-events.js +14 -0
- package/dist/verification-summary.d.ts +38 -0
- package/dist/verification-summary.d.ts.map +1 -0
- package/dist/verification-summary.js +5 -0
- package/dist/verification.d.ts +64 -0
- package/dist/verification.d.ts.map +1 -0
- package/dist/verification.js +13 -0
- package/dist/workflow-descriptor.d.ts +21 -0
- package/dist/workflow-descriptor.d.ts.map +1 -0
- package/dist/workflow-descriptor.js +8 -0
- package/dist/workflow-handoff.d.ts +69 -0
- package/dist/workflow-handoff.d.ts.map +1 -0
- package/dist/workflow-handoff.js +381 -0
- package/dist/workspace-descriptors.d.ts +21 -0
- package/dist/workspace-descriptors.d.ts.map +1 -0
- package/dist/workspace-descriptors.js +180 -0
- package/dist/workspace-ui.d.ts +119 -0
- package/dist/workspace-ui.d.ts.map +1 -0
- package/dist/workspace-ui.js +105 -0
- package/dist/workspace.d.ts +104 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +27 -0
- package/package.json +71 -0
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
export const EDITOR_LAYOUT_SCHEMA_VERSION = 2;
|
|
2
|
+
const MIN_SPLIT_RATIO = 15;
|
|
3
|
+
const MAX_SPLIT_RATIO = 85;
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
function clampNumber(value, min, max) {
|
|
8
|
+
return Math.min(max, Math.max(min, value));
|
|
9
|
+
}
|
|
10
|
+
function clampRatio(value) {
|
|
11
|
+
return clampNumber(Math.round(value), MIN_SPLIT_RATIO, MAX_SPLIT_RATIO);
|
|
12
|
+
}
|
|
13
|
+
function stringArray(value) {
|
|
14
|
+
if (!Array.isArray(value))
|
|
15
|
+
return [];
|
|
16
|
+
const out = [];
|
|
17
|
+
for (const entry of value) {
|
|
18
|
+
if (typeof entry === "string" && entry.length > 0 && !out.includes(entry))
|
|
19
|
+
out.push(entry);
|
|
20
|
+
}
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
function orderedFiles(activeFile, openFiles, tabOrder) {
|
|
24
|
+
const out = [];
|
|
25
|
+
const add = (file) => {
|
|
26
|
+
if (file.length > 0 && !out.includes(file))
|
|
27
|
+
out.push(file);
|
|
28
|
+
};
|
|
29
|
+
for (const file of tabOrder ?? [])
|
|
30
|
+
add(file);
|
|
31
|
+
for (const file of openFiles)
|
|
32
|
+
add(file);
|
|
33
|
+
add(activeFile);
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
function createPane(id, activeFile, openFiles) {
|
|
37
|
+
const tabOrder = orderedFiles(activeFile, openFiles);
|
|
38
|
+
return {
|
|
39
|
+
id,
|
|
40
|
+
openFiles: tabOrder,
|
|
41
|
+
activeFile: activeFile.length > 0 ? activeFile : (tabOrder[0] ?? ""),
|
|
42
|
+
tabOrder,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function normalizePane(value, fallbackId) {
|
|
46
|
+
if (!isRecord(value))
|
|
47
|
+
return null;
|
|
48
|
+
const id = typeof value.id === "string" && value.id.length > 0 ? value.id : fallbackId;
|
|
49
|
+
const activeFile = typeof value.activeFile === "string"
|
|
50
|
+
? value.activeFile
|
|
51
|
+
: typeof value.file === "string"
|
|
52
|
+
? value.file
|
|
53
|
+
: "";
|
|
54
|
+
const openFiles = stringArray(value.openFiles);
|
|
55
|
+
const tabOrder = stringArray(value.tabOrder);
|
|
56
|
+
const pane = createPane(id, activeFile, orderedFiles(activeFile, openFiles, tabOrder));
|
|
57
|
+
return pane.openFiles.length > 0 || pane.activeFile.length > 0 ? pane : null;
|
|
58
|
+
}
|
|
59
|
+
function parseDirection(value) {
|
|
60
|
+
return value === "column" ? "column" : "row";
|
|
61
|
+
}
|
|
62
|
+
function paneIdsFromTree(node, out = []) {
|
|
63
|
+
if (node.type === "pane") {
|
|
64
|
+
out.push(node.paneId);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
paneIdsFromTree(node.first, out);
|
|
68
|
+
paneIdsFromTree(node.second, out);
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
function normalizePaneNode(value, panes) {
|
|
73
|
+
if (value.type === "pane" && typeof value.paneId === "string" && panes[value.paneId] !== undefined) {
|
|
74
|
+
return { type: "pane", paneId: value.paneId };
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
function normalizeSplitNode(value, panes) {
|
|
79
|
+
if (value.type !== "split")
|
|
80
|
+
return null;
|
|
81
|
+
const first = normalizeTree(value.first, panes);
|
|
82
|
+
const second = normalizeTree(value.second, panes);
|
|
83
|
+
if (first === null || second === null)
|
|
84
|
+
return null;
|
|
85
|
+
const id = typeof value.id === "string" && value.id.length > 0 ? value.id : "split-1";
|
|
86
|
+
const ratio = typeof value.ratio === "number" && Number.isFinite(value.ratio) ? value.ratio : 50;
|
|
87
|
+
return {
|
|
88
|
+
type: "split",
|
|
89
|
+
id,
|
|
90
|
+
direction: parseDirection(value.direction),
|
|
91
|
+
ratio: clampRatio(ratio),
|
|
92
|
+
first,
|
|
93
|
+
second,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function normalizeTree(value, panes) {
|
|
97
|
+
if (!isRecord(value))
|
|
98
|
+
return null;
|
|
99
|
+
return normalizePaneNode(value, panes) ?? normalizeSplitNode(value, panes);
|
|
100
|
+
}
|
|
101
|
+
function normalizePaneRecord(value) {
|
|
102
|
+
if (!isRecord(value))
|
|
103
|
+
return {};
|
|
104
|
+
const panes = {};
|
|
105
|
+
for (const [id, rawPane] of Object.entries(value)) {
|
|
106
|
+
const pane = normalizePane(rawPane, id);
|
|
107
|
+
if (pane !== null)
|
|
108
|
+
panes[pane.id] = pane;
|
|
109
|
+
}
|
|
110
|
+
return panes;
|
|
111
|
+
}
|
|
112
|
+
function normalizePaneList(value) {
|
|
113
|
+
if (!Array.isArray(value))
|
|
114
|
+
return [];
|
|
115
|
+
return value
|
|
116
|
+
.map((pane, index) => normalizePane(pane, `pane-${String(index + 1)}`))
|
|
117
|
+
.filter((pane) => pane !== null);
|
|
118
|
+
}
|
|
119
|
+
function persistedSidebarWidth(record, input) {
|
|
120
|
+
return typeof record.sidebarWidth === "number" && Number.isFinite(record.sidebarWidth)
|
|
121
|
+
? clampNumber(record.sidebarWidth, input.minSidebarWidth, input.maxSidebarWidth)
|
|
122
|
+
: input.defaultSidebarWidth;
|
|
123
|
+
}
|
|
124
|
+
function visiblePanesForTree(tree, panes) {
|
|
125
|
+
const visiblePanes = {};
|
|
126
|
+
for (const paneId of new Set(paneIdsFromTree(tree))) {
|
|
127
|
+
const pane = panes[paneId];
|
|
128
|
+
if (pane !== undefined)
|
|
129
|
+
visiblePanes[paneId] = pane;
|
|
130
|
+
}
|
|
131
|
+
return visiblePanes;
|
|
132
|
+
}
|
|
133
|
+
function activePaneIdFromRecord(record, panes, fallbackPaneId) {
|
|
134
|
+
return typeof record.activePaneId === "string" && panes[record.activePaneId] !== undefined
|
|
135
|
+
? record.activePaneId
|
|
136
|
+
: fallbackPaneId;
|
|
137
|
+
}
|
|
138
|
+
function parseV2(root, record, input) {
|
|
139
|
+
const panes = normalizePaneRecord(record.panes);
|
|
140
|
+
const firstPaneId = Object.keys(panes)[0];
|
|
141
|
+
if (firstPaneId === undefined)
|
|
142
|
+
return null;
|
|
143
|
+
const tree = normalizeTree(record.tree, panes) ?? { type: "pane", paneId: firstPaneId };
|
|
144
|
+
const visiblePanes = visiblePanesForTree(tree, panes);
|
|
145
|
+
const fallbackPaneId = paneIdsFromTree(tree)[0] ?? firstPaneId;
|
|
146
|
+
return {
|
|
147
|
+
schemaVersion: EDITOR_LAYOUT_SCHEMA_VERSION,
|
|
148
|
+
root,
|
|
149
|
+
activePaneId: activePaneIdFromRecord(record, visiblePanes, fallbackPaneId),
|
|
150
|
+
tree,
|
|
151
|
+
panes: visiblePanes,
|
|
152
|
+
sidebarWidth: persistedSidebarWidth(record, input),
|
|
153
|
+
sidebarCollapsed: record.sidebarCollapsed === true,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function v1Tree(first, second, record) {
|
|
157
|
+
if (second === undefined)
|
|
158
|
+
return { type: "pane", paneId: first.id };
|
|
159
|
+
const ratio = typeof record.splitRatio === "number" && Number.isFinite(record.splitRatio)
|
|
160
|
+
? clampRatio(record.splitRatio)
|
|
161
|
+
: 50;
|
|
162
|
+
return {
|
|
163
|
+
type: "split",
|
|
164
|
+
id: "split-1",
|
|
165
|
+
direction: parseDirection(record.direction),
|
|
166
|
+
ratio,
|
|
167
|
+
first: { type: "pane", paneId: first.id },
|
|
168
|
+
second: { type: "pane", paneId: second.id },
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function parseV1(root, record, input) {
|
|
172
|
+
const panesList = normalizePaneList(record.panes);
|
|
173
|
+
if (panesList.length === 0)
|
|
174
|
+
return null;
|
|
175
|
+
const panes = {};
|
|
176
|
+
for (const pane of panesList)
|
|
177
|
+
panes[pane.id] = pane;
|
|
178
|
+
const first = panesList[0];
|
|
179
|
+
if (first === undefined)
|
|
180
|
+
return null;
|
|
181
|
+
const second = panesList[1];
|
|
182
|
+
const tree = v1Tree(first, second, record);
|
|
183
|
+
return {
|
|
184
|
+
schemaVersion: EDITOR_LAYOUT_SCHEMA_VERSION,
|
|
185
|
+
root,
|
|
186
|
+
activePaneId: activePaneIdFromRecord(record, panes, first.id),
|
|
187
|
+
tree,
|
|
188
|
+
panes,
|
|
189
|
+
sidebarWidth: persistedSidebarWidth(record, input),
|
|
190
|
+
sidebarCollapsed: record.sidebarCollapsed === true,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function parsePersistedLayout(input) {
|
|
194
|
+
if (input.layoutJson !== undefined && input.layoutJson.trim().length > 0) {
|
|
195
|
+
try {
|
|
196
|
+
const parsed = JSON.parse(input.layoutJson);
|
|
197
|
+
if (isRecord(parsed)) {
|
|
198
|
+
const version = parsed.schemaVersion ?? parsed.version;
|
|
199
|
+
const migrated = version === EDITOR_LAYOUT_SCHEMA_VERSION || version === "2"
|
|
200
|
+
? parseV2(input.root, parsed, input)
|
|
201
|
+
: parseV1(input.root, parsed, input);
|
|
202
|
+
if (migrated !== null)
|
|
203
|
+
return migrated;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
// Fall through to a fresh layout. Invalid persisted UI state must not block the editor.
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
export function createEditorLayoutStateV2(input) {
|
|
213
|
+
const persisted = parsePersistedLayout(input);
|
|
214
|
+
if (persisted !== null)
|
|
215
|
+
return persisted;
|
|
216
|
+
const activeFile = input.file.length > 0 ? input.file : (input.openFiles[0] ?? "");
|
|
217
|
+
const pane = createPane("pane-1", activeFile, input.openFiles);
|
|
218
|
+
return {
|
|
219
|
+
schemaVersion: EDITOR_LAYOUT_SCHEMA_VERSION,
|
|
220
|
+
root: input.root,
|
|
221
|
+
activePaneId: pane.id,
|
|
222
|
+
tree: { type: "pane", paneId: pane.id },
|
|
223
|
+
panes: { [pane.id]: pane },
|
|
224
|
+
sidebarWidth: input.defaultSidebarWidth,
|
|
225
|
+
sidebarCollapsed: false,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
export function serializeEditorLayoutStateV2(layout) {
|
|
229
|
+
return JSON.stringify({
|
|
230
|
+
schemaVersion: EDITOR_LAYOUT_SCHEMA_VERSION,
|
|
231
|
+
root: layout.root,
|
|
232
|
+
activePaneId: layout.activePaneId,
|
|
233
|
+
tree: layout.tree,
|
|
234
|
+
panes: layout.panes,
|
|
235
|
+
sidebarWidth: Math.round(layout.sidebarWidth),
|
|
236
|
+
sidebarCollapsed: layout.sidebarCollapsed,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
export function editorLayoutPaneIds(layout) {
|
|
240
|
+
return paneIdsFromTree(layout.tree);
|
|
241
|
+
}
|
|
242
|
+
export function editorLayoutPanes(layout) {
|
|
243
|
+
return editorLayoutPaneIds(layout)
|
|
244
|
+
.map((paneId) => layout.panes[paneId])
|
|
245
|
+
.filter((pane) => pane !== undefined);
|
|
246
|
+
}
|
|
247
|
+
export function activeEditorPane(layout) {
|
|
248
|
+
return layout.panes[layout.activePaneId] ?? editorLayoutPanes(layout)[0] ?? createPane("pane-1", "", []);
|
|
249
|
+
}
|
|
250
|
+
function updatePane(layout, paneId, update) {
|
|
251
|
+
const pane = layout.panes[paneId];
|
|
252
|
+
if (pane === undefined)
|
|
253
|
+
return layout;
|
|
254
|
+
return { ...layout, panes: { ...layout.panes, [paneId]: update(pane) } };
|
|
255
|
+
}
|
|
256
|
+
function withoutFile(files, file) {
|
|
257
|
+
return files.filter((entry) => entry !== file);
|
|
258
|
+
}
|
|
259
|
+
function insertFile(files, file, index) {
|
|
260
|
+
const without = withoutFile(files, file);
|
|
261
|
+
const clamped = index === undefined ? without.length : clampNumber(index, 0, without.length);
|
|
262
|
+
return [...without.slice(0, clamped), file, ...without.slice(clamped)];
|
|
263
|
+
}
|
|
264
|
+
function withPaneFiles(pane, files, activeFile) {
|
|
265
|
+
const tabOrder = orderedFiles(activeFile, files);
|
|
266
|
+
return { ...pane, openFiles: tabOrder, tabOrder, activeFile: activeFile.length > 0 ? activeFile : (tabOrder[0] ?? "") };
|
|
267
|
+
}
|
|
268
|
+
function nextPaneId(layout) {
|
|
269
|
+
let index = Object.keys(layout.panes).length + 1;
|
|
270
|
+
while (layout.panes[`pane-${String(index)}`] !== undefined)
|
|
271
|
+
index += 1;
|
|
272
|
+
return `pane-${String(index)}`;
|
|
273
|
+
}
|
|
274
|
+
function nextSplitId(node) {
|
|
275
|
+
const used = new Set();
|
|
276
|
+
const visit = (current) => {
|
|
277
|
+
if (current.type === "split") {
|
|
278
|
+
used.add(current.id);
|
|
279
|
+
visit(current.first);
|
|
280
|
+
visit(current.second);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
visit(node);
|
|
284
|
+
let index = used.size + 1;
|
|
285
|
+
while (used.has(`split-${String(index)}`))
|
|
286
|
+
index += 1;
|
|
287
|
+
return `split-${String(index)}`;
|
|
288
|
+
}
|
|
289
|
+
function replacePaneNode(node, paneId, replacement) {
|
|
290
|
+
if (node.type === "pane")
|
|
291
|
+
return node.paneId === paneId ? replacement : node;
|
|
292
|
+
return {
|
|
293
|
+
...node,
|
|
294
|
+
first: replacePaneNode(node.first, paneId, replacement),
|
|
295
|
+
second: replacePaneNode(node.second, paneId, replacement),
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
function removePaneNode(node, paneId) {
|
|
299
|
+
if (node.type === "pane")
|
|
300
|
+
return node.paneId === paneId ? null : node;
|
|
301
|
+
const first = removePaneNode(node.first, paneId);
|
|
302
|
+
const second = removePaneNode(node.second, paneId);
|
|
303
|
+
if (first === null)
|
|
304
|
+
return second;
|
|
305
|
+
if (second === null)
|
|
306
|
+
return first;
|
|
307
|
+
return { ...node, first, second };
|
|
308
|
+
}
|
|
309
|
+
function resizeSplitNode(node, splitId, ratio) {
|
|
310
|
+
if (node.type === "pane")
|
|
311
|
+
return node;
|
|
312
|
+
return {
|
|
313
|
+
...node,
|
|
314
|
+
ratio: node.id === splitId ? clampRatio(ratio) : node.ratio,
|
|
315
|
+
first: resizeSplitNode(node.first, splitId, ratio),
|
|
316
|
+
second: resizeSplitNode(node.second, splitId, ratio),
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
function splitPane(layout, paneId, direction, file, before) {
|
|
320
|
+
const source = layout.panes[paneId];
|
|
321
|
+
if (source === undefined)
|
|
322
|
+
return layout;
|
|
323
|
+
const activeFile = file ?? source.activeFile;
|
|
324
|
+
if (activeFile.length === 0)
|
|
325
|
+
return layout;
|
|
326
|
+
const newPaneId = nextPaneId(layout);
|
|
327
|
+
const newPane = createPane(newPaneId, activeFile, [activeFile]);
|
|
328
|
+
const first = { type: "pane", paneId: before ? newPaneId : paneId };
|
|
329
|
+
const second = { type: "pane", paneId: before ? paneId : newPaneId };
|
|
330
|
+
const replacement = {
|
|
331
|
+
type: "split",
|
|
332
|
+
id: nextSplitId(layout.tree),
|
|
333
|
+
direction,
|
|
334
|
+
ratio: 50,
|
|
335
|
+
first,
|
|
336
|
+
second,
|
|
337
|
+
};
|
|
338
|
+
return {
|
|
339
|
+
...layout,
|
|
340
|
+
activePaneId: newPaneId,
|
|
341
|
+
tree: replacePaneNode(layout.tree, paneId, replacement),
|
|
342
|
+
panes: { ...layout.panes, [newPaneId]: newPane },
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
function removePane(layout, paneId) {
|
|
346
|
+
if (layout.panes[paneId] === undefined || editorLayoutPaneIds(layout).length <= 1)
|
|
347
|
+
return layout;
|
|
348
|
+
const nextTree = removePaneNode(layout.tree, paneId);
|
|
349
|
+
if (nextTree === null)
|
|
350
|
+
return layout;
|
|
351
|
+
const panes = {};
|
|
352
|
+
for (const [id, pane] of Object.entries(layout.panes)) {
|
|
353
|
+
if (id !== paneId)
|
|
354
|
+
panes[id] = pane;
|
|
355
|
+
}
|
|
356
|
+
const nextActivePaneId = layout.activePaneId === paneId ? (paneIdsFromTree(nextTree)[0] ?? layout.activePaneId) : layout.activePaneId;
|
|
357
|
+
return { ...layout, tree: nextTree, panes, activePaneId: nextActivePaneId };
|
|
358
|
+
}
|
|
359
|
+
function closeTab(layout, paneId, file) {
|
|
360
|
+
const pane = layout.panes[paneId];
|
|
361
|
+
if (!pane?.openFiles.includes(file))
|
|
362
|
+
return layout;
|
|
363
|
+
const closingIndex = pane.tabOrder.indexOf(file);
|
|
364
|
+
const nextFiles = withoutFile(pane.openFiles, file);
|
|
365
|
+
if (nextFiles.length === 0) {
|
|
366
|
+
if (editorLayoutPaneIds(layout).length > 1)
|
|
367
|
+
return removePane(layout, paneId);
|
|
368
|
+
return updatePane(layout, paneId, (current) => withPaneFiles(current, [], ""));
|
|
369
|
+
}
|
|
370
|
+
const nextActive = pane.activeFile === file ? (nextFiles[closingIndex] ?? nextFiles[closingIndex - 1] ?? nextFiles[0] ?? "") : pane.activeFile;
|
|
371
|
+
return updatePane(layout, paneId, (current) => withPaneFiles(current, nextFiles, nextActive));
|
|
372
|
+
}
|
|
373
|
+
function moveTab(layout, fromPaneId, toPaneId, file, targetIndex) {
|
|
374
|
+
if (fromPaneId === toPaneId) {
|
|
375
|
+
return updatePane(layout, toPaneId, (pane) => withPaneFiles(pane, insertFile(pane.tabOrder, file, targetIndex), file));
|
|
376
|
+
}
|
|
377
|
+
const fromPane = layout.panes[fromPaneId];
|
|
378
|
+
const toPane = layout.panes[toPaneId];
|
|
379
|
+
if (fromPane === undefined || toPane === undefined || !fromPane.openFiles.includes(file))
|
|
380
|
+
return layout;
|
|
381
|
+
let next = closeTab(layout, fromPaneId, file);
|
|
382
|
+
if (next.panes[toPaneId] === undefined)
|
|
383
|
+
return next;
|
|
384
|
+
next = updatePane(next, toPaneId, (pane) => withPaneFiles(pane, insertFile(pane.tabOrder, file, targetIndex), file));
|
|
385
|
+
return { ...next, activePaneId: toPaneId };
|
|
386
|
+
}
|
|
387
|
+
const PANE_FILE_ACTION_TYPES = new Set([
|
|
388
|
+
"open-file",
|
|
389
|
+
"select-file",
|
|
390
|
+
"close-tab",
|
|
391
|
+
"reorder-tab",
|
|
392
|
+
]);
|
|
393
|
+
const STRUCTURE_ACTION_TYPES = new Set([
|
|
394
|
+
"close-pane",
|
|
395
|
+
"move-tab",
|
|
396
|
+
"drop-tab",
|
|
397
|
+
"split-pane",
|
|
398
|
+
]);
|
|
399
|
+
function isPaneFileAction(action) {
|
|
400
|
+
return PANE_FILE_ACTION_TYPES.has(action.type);
|
|
401
|
+
}
|
|
402
|
+
function isStructureAction(action) {
|
|
403
|
+
return STRUCTURE_ACTION_TYPES.has(action.type);
|
|
404
|
+
}
|
|
405
|
+
function reducePaneFileAction(layout, action) {
|
|
406
|
+
switch (action.type) {
|
|
407
|
+
case "open-file":
|
|
408
|
+
return updatePane(layout, action.paneId, (pane) => withPaneFiles(pane, orderedFiles(action.file, pane.openFiles), action.file));
|
|
409
|
+
case "select-file":
|
|
410
|
+
return updatePane({ ...layout, activePaneId: action.paneId }, action.paneId, (pane) => pane.openFiles.includes(action.file) ? withPaneFiles(pane, pane.openFiles, action.file) : pane);
|
|
411
|
+
case "close-tab":
|
|
412
|
+
return closeTab(layout, action.paneId, action.file);
|
|
413
|
+
case "reorder-tab":
|
|
414
|
+
return updatePane(layout, action.paneId, (pane) => pane.openFiles.includes(action.file)
|
|
415
|
+
? withPaneFiles(pane, insertFile(pane.tabOrder, action.file, action.targetIndex), action.file)
|
|
416
|
+
: pane);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
function reduceDropTab(layout, intent) {
|
|
420
|
+
if (intent.zone === "center") {
|
|
421
|
+
return moveTab(layout, intent.fromPaneId, intent.toPaneId, intent.file, intent.targetIndex);
|
|
422
|
+
}
|
|
423
|
+
return splitPane(moveTab(layout, intent.fromPaneId, intent.fromPaneId, intent.file), intent.toPaneId, intent.zone === "top" || intent.zone === "bottom" ? "column" : "row", intent.file, intent.zone === "top" || intent.zone === "left");
|
|
424
|
+
}
|
|
425
|
+
function reduceStructureAction(layout, action) {
|
|
426
|
+
switch (action.type) {
|
|
427
|
+
case "close-pane":
|
|
428
|
+
return removePane(layout, action.paneId);
|
|
429
|
+
case "move-tab":
|
|
430
|
+
return moveTab(layout, action.fromPaneId, action.toPaneId, action.file, action.targetIndex);
|
|
431
|
+
case "drop-tab":
|
|
432
|
+
return reduceDropTab(layout, action.intent);
|
|
433
|
+
case "split-pane":
|
|
434
|
+
return splitPane(layout, action.paneId, action.direction, action.file, false);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
function replaceRoot(layout, action) {
|
|
438
|
+
const pane = createPane("pane-1", "", []);
|
|
439
|
+
return {
|
|
440
|
+
schemaVersion: EDITOR_LAYOUT_SCHEMA_VERSION,
|
|
441
|
+
root: action.root,
|
|
442
|
+
activePaneId: pane.id,
|
|
443
|
+
tree: { type: "pane", paneId: pane.id },
|
|
444
|
+
panes: { [pane.id]: pane },
|
|
445
|
+
sidebarWidth: action.sidebarWidth ?? layout.sidebarWidth,
|
|
446
|
+
sidebarCollapsed: false,
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
function reduceLayoutStateAction(layout, action) {
|
|
450
|
+
switch (action.type) {
|
|
451
|
+
case "set-active-pane":
|
|
452
|
+
return layout.panes[action.paneId] === undefined ? layout : { ...layout, activePaneId: action.paneId };
|
|
453
|
+
case "resize-split":
|
|
454
|
+
return { ...layout, tree: resizeSplitNode(layout.tree, action.splitId, action.ratio) };
|
|
455
|
+
case "set-sidebar":
|
|
456
|
+
return {
|
|
457
|
+
...layout,
|
|
458
|
+
sidebarWidth: action.width ?? layout.sidebarWidth,
|
|
459
|
+
sidebarCollapsed: action.collapsed ?? layout.sidebarCollapsed,
|
|
460
|
+
};
|
|
461
|
+
case "replace-root":
|
|
462
|
+
return replaceRoot(layout, action);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
export function editorLayoutReducer(layout, action) {
|
|
466
|
+
if (isPaneFileAction(action))
|
|
467
|
+
return reducePaneFileAction(layout, action);
|
|
468
|
+
return isStructureAction(action) ? reduceStructureAction(layout, action) : reduceLayoutStateAction(layout, action);
|
|
469
|
+
}
|
|
470
|
+
export function editorLayoutOpenFiles(layout) {
|
|
471
|
+
const out = [];
|
|
472
|
+
for (const pane of editorLayoutPanes(layout)) {
|
|
473
|
+
for (const file of pane.openFiles) {
|
|
474
|
+
if (file.length > 0 && !out.includes(file))
|
|
475
|
+
out.push(file);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return out;
|
|
479
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export declare const EDITOR_PATCH_APPLY_SCHEMA_VERSION: "1";
|
|
2
|
+
export type EditorPatchApplyDecision = "apply" | "reject";
|
|
3
|
+
export declare const EDITOR_PATCH_APPLY_DECISIONS: readonly EditorPatchApplyDecision[];
|
|
4
|
+
export interface EditorPatchApplyWireRequest {
|
|
5
|
+
readonly schemaVersion: typeof EDITOR_PATCH_APPLY_SCHEMA_VERSION;
|
|
6
|
+
readonly root: string;
|
|
7
|
+
readonly patchId: string;
|
|
8
|
+
readonly decision: EditorPatchApplyDecision;
|
|
9
|
+
readonly diff: string;
|
|
10
|
+
readonly allowOverwrite?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export type EditorPatchApplyStatus = "disabled" | "applied" | "rejected" | "conflict" | "failed";
|
|
13
|
+
export declare const EDITOR_PATCH_APPLY_STATUSES: readonly EditorPatchApplyStatus[];
|
|
14
|
+
export type EditorPatchRejectionReason = "out-of-scope" | "would-overwrite" | "write-conflict" | "invalid-patch" | "empty-patch";
|
|
15
|
+
export declare const EDITOR_PATCH_REJECTION_REASONS: readonly EditorPatchRejectionReason[];
|
|
16
|
+
export interface EditorPatchApplyRejection {
|
|
17
|
+
readonly reason: EditorPatchRejectionReason;
|
|
18
|
+
readonly path?: string;
|
|
19
|
+
readonly detail: string;
|
|
20
|
+
}
|
|
21
|
+
export interface EditorPatchApplyChangeCounts {
|
|
22
|
+
readonly changedFiles: number;
|
|
23
|
+
readonly created: number;
|
|
24
|
+
readonly deleted: number;
|
|
25
|
+
}
|
|
26
|
+
export type EditorPatchVerificationOutcome = "passed" | "failed" | "skipped" | "denied" | "not-run";
|
|
27
|
+
export declare const EDITOR_PATCH_VERIFICATION_OUTCOMES: readonly EditorPatchVerificationOutcome[];
|
|
28
|
+
export interface EditorPatchVerificationBounds {
|
|
29
|
+
readonly wallTimeMs: number;
|
|
30
|
+
readonly maxOutputBytes: number;
|
|
31
|
+
readonly envAllowlistCount: number;
|
|
32
|
+
}
|
|
33
|
+
export interface EditorPatchVerificationSummary {
|
|
34
|
+
readonly outcome: EditorPatchVerificationOutcome;
|
|
35
|
+
readonly networkEnforced: boolean;
|
|
36
|
+
readonly sandboxBackend: string;
|
|
37
|
+
readonly stepCount: number;
|
|
38
|
+
readonly passed: number;
|
|
39
|
+
readonly failed: number;
|
|
40
|
+
readonly durationMs: number;
|
|
41
|
+
readonly bounds: EditorPatchVerificationBounds;
|
|
42
|
+
readonly preApply: boolean;
|
|
43
|
+
readonly secretsRedacted: boolean;
|
|
44
|
+
readonly detail?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface EditorPatchRevertProposal {
|
|
47
|
+
readonly patchId: string;
|
|
48
|
+
readonly diff: string;
|
|
49
|
+
readonly changedFiles: readonly string[];
|
|
50
|
+
readonly reason: string;
|
|
51
|
+
}
|
|
52
|
+
export interface EditorPatchApplyEvidenceRefs {
|
|
53
|
+
readonly applyRunId: string;
|
|
54
|
+
readonly verificationRunId?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface EditorPatchApplyWireResponse {
|
|
57
|
+
readonly schemaVersion: typeof EDITOR_PATCH_APPLY_SCHEMA_VERSION;
|
|
58
|
+
readonly status: EditorPatchApplyStatus;
|
|
59
|
+
readonly patchId: string;
|
|
60
|
+
readonly reason?: string;
|
|
61
|
+
readonly applied?: EditorPatchApplyChangeCounts;
|
|
62
|
+
readonly verification?: EditorPatchVerificationSummary;
|
|
63
|
+
readonly rejections?: readonly EditorPatchApplyRejection[];
|
|
64
|
+
readonly revertProposal?: EditorPatchRevertProposal;
|
|
65
|
+
readonly evidence?: EditorPatchApplyEvidenceRefs;
|
|
66
|
+
}
|
|
67
|
+
export interface EditorPatchApplyParseOk {
|
|
68
|
+
readonly ok: true;
|
|
69
|
+
readonly value: EditorPatchApplyWireRequest;
|
|
70
|
+
}
|
|
71
|
+
export interface EditorPatchApplyParseFail {
|
|
72
|
+
readonly ok: false;
|
|
73
|
+
readonly errors: readonly string[];
|
|
74
|
+
}
|
|
75
|
+
export type EditorPatchApplyParse = EditorPatchApplyParseOk | EditorPatchApplyParseFail;
|
|
76
|
+
export declare function parseEditorPatchApplyRequest(value: unknown): EditorPatchApplyParse;
|
|
77
|
+
//# sourceMappingURL=editor-patch-apply.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editor-patch-apply.d.ts","sourceRoot":"","sources":["../src/editor-patch-apply.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// Editor-driven patch-APPLY + post-apply VERIFICATION WIRE contract (Issue #1204, Epic #1189,
|
|
2
|
+
// ADR-0042 D7, ADR-0043). These are the request/response shapes the `POST /api/editor/patch-apply`
|
|
3
|
+
// BFF route consumes and emits. The route is the governed seam between the browser-tier
|
|
4
|
+
// `@oscharko-dev/keiko-editor` patch-review action (apply / reject) and the server-owned, gated patch
|
|
5
|
+
// application (keiko-tools `validatePatch`/`applyPatch`) and post-apply verification orchestration
|
|
6
|
+
// (keiko-verification, isolated by an enforced deny-by-default egress boundary — keiko-sandbox).
|
|
7
|
+
//
|
|
8
|
+
// Wave-2 surface, shipped switched off (ADR-0042 D7): editor-driven patch apply and the verification
|
|
9
|
+
// that follows it EXECUTE untrusted, model-generated test code, so they are gated behind a default-off
|
|
10
|
+
// feature flag that may be enabled only once the enforced egress boundary (delivered by #1202,
|
|
11
|
+
// ADR-0043) is available and proven by an automated test. With the flag off the route reports
|
|
12
|
+
// `disabled` and performs no validation, write, or execution.
|
|
13
|
+
//
|
|
14
|
+
// Trust and content boundary:
|
|
15
|
+
// - The REQUEST carries the reviewable unified `diff` the user explicitly chose to apply (round-tripped
|
|
16
|
+
// from the generation response). It is reviewable code — the same content class as the candidate
|
|
17
|
+
// `newText` the editor already rendered — never a raw prompt, model reasoning, retrieved excerpt, or
|
|
18
|
+
// secret. The server re-validates it from scratch (containment, limits, conflicts, overwrite) and
|
|
19
|
+
// never trusts the client's framing of it.
|
|
20
|
+
// - The RESPONSE is content-free apart from the reviewable `diff` of a guarded revert proposal (again
|
|
21
|
+
// reviewable code the user explicitly reviews before re-applying). Status, change counts, rejection
|
|
22
|
+
// reasons, the verification summary, and evidence references are enum literals, counts, hashes, and
|
|
23
|
+
// content-free pointers only (EU AI Act Reg. (EU) 2024/1689 Art. 12 record-keeping).
|
|
24
|
+
//
|
|
25
|
+
// Leaf-package rules (ADR-0019): pure types and frozen const tables plus throw-free validators only.
|
|
26
|
+
// No filesystem, no clock, no crypto, no randomness, and no imports of other `@oscharko-dev/keiko-*`
|
|
27
|
+
// packages.
|
|
28
|
+
// Schema version for the patch-apply envelope, evolved independently of the test-generation envelope
|
|
29
|
+
// (ADR-0010 D2: a new shape is a new literal member, never a mutation). Consumers pin against the literal.
|
|
30
|
+
export const EDITOR_PATCH_APPLY_SCHEMA_VERSION = "1";
|
|
31
|
+
export const EDITOR_PATCH_APPLY_DECISIONS = [
|
|
32
|
+
"apply",
|
|
33
|
+
"reject",
|
|
34
|
+
];
|
|
35
|
+
export const EDITOR_PATCH_APPLY_STATUSES = [
|
|
36
|
+
"disabled",
|
|
37
|
+
"applied",
|
|
38
|
+
"rejected",
|
|
39
|
+
"conflict",
|
|
40
|
+
"failed",
|
|
41
|
+
];
|
|
42
|
+
export const EDITOR_PATCH_REJECTION_REASONS = [
|
|
43
|
+
"out-of-scope",
|
|
44
|
+
"would-overwrite",
|
|
45
|
+
"write-conflict",
|
|
46
|
+
"invalid-patch",
|
|
47
|
+
"empty-patch",
|
|
48
|
+
];
|
|
49
|
+
export const EDITOR_PATCH_VERIFICATION_OUTCOMES = [
|
|
50
|
+
"passed",
|
|
51
|
+
"failed",
|
|
52
|
+
"skipped",
|
|
53
|
+
"denied",
|
|
54
|
+
"not-run",
|
|
55
|
+
];
|
|
56
|
+
function isRecord(value) {
|
|
57
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
58
|
+
}
|
|
59
|
+
function isNonEmptyString(value) {
|
|
60
|
+
return typeof value === "string" && value.length > 0;
|
|
61
|
+
}
|
|
62
|
+
function isOpaquePatchId(value) {
|
|
63
|
+
return typeof value === "string" && /^[a-f0-9]{32}$/u.test(value);
|
|
64
|
+
}
|
|
65
|
+
function isDecision(value) {
|
|
66
|
+
return value === "apply" || value === "reject";
|
|
67
|
+
}
|
|
68
|
+
function collectDiffErrors(value, errors) {
|
|
69
|
+
if (typeof value.diff !== "string") {
|
|
70
|
+
errors.push("diff must be a string");
|
|
71
|
+
}
|
|
72
|
+
else if (value.decision === "apply" && value.diff.length === 0) {
|
|
73
|
+
errors.push("diff must be a non-empty string for an apply decision");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function collectOptionalFlagErrors(value, errors) {
|
|
77
|
+
if (value.allowOverwrite !== undefined && typeof value.allowOverwrite !== "boolean") {
|
|
78
|
+
errors.push("allowOverwrite must be a boolean when provided");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function collectRequestErrors(value) {
|
|
82
|
+
const errors = [];
|
|
83
|
+
if (value.schemaVersion !== EDITOR_PATCH_APPLY_SCHEMA_VERSION) {
|
|
84
|
+
errors.push(`schemaVersion must be ${EDITOR_PATCH_APPLY_SCHEMA_VERSION}`);
|
|
85
|
+
}
|
|
86
|
+
if (!isNonEmptyString(value.root)) {
|
|
87
|
+
errors.push("root must be a non-empty string");
|
|
88
|
+
}
|
|
89
|
+
if (!isOpaquePatchId(value.patchId)) {
|
|
90
|
+
errors.push("patchId must be a 32-character lowercase hex string");
|
|
91
|
+
}
|
|
92
|
+
if (!isDecision(value.decision)) {
|
|
93
|
+
errors.push("decision must be one of: apply, reject");
|
|
94
|
+
}
|
|
95
|
+
collectDiffErrors(value, errors);
|
|
96
|
+
collectOptionalFlagErrors(value, errors);
|
|
97
|
+
return errors;
|
|
98
|
+
}
|
|
99
|
+
// Validates an `unknown` payload into an EditorPatchApplyWireRequest, reporting one error per failed
|
|
100
|
+
// invariant. The BFF uses this to reject a malformed request with 400 INVALID_REQUEST.
|
|
101
|
+
export function parseEditorPatchApplyRequest(value) {
|
|
102
|
+
if (!isRecord(value)) {
|
|
103
|
+
return { ok: false, errors: ["request must be an object"] };
|
|
104
|
+
}
|
|
105
|
+
const errors = collectRequestErrors(value);
|
|
106
|
+
if (errors.length > 0) {
|
|
107
|
+
return { ok: false, errors };
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
ok: true,
|
|
111
|
+
value: {
|
|
112
|
+
schemaVersion: EDITOR_PATCH_APPLY_SCHEMA_VERSION,
|
|
113
|
+
root: value.root,
|
|
114
|
+
patchId: value.patchId,
|
|
115
|
+
decision: value.decision,
|
|
116
|
+
diff: value.diff,
|
|
117
|
+
...(typeof value.allowOverwrite === "boolean"
|
|
118
|
+
? { allowOverwrite: value.allowOverwrite }
|
|
119
|
+
: {}),
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|