@trojanbox-vcp-test/site-edit-engine 0.1.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.
Files changed (174) hide show
  1. package/README.md +85 -0
  2. package/dist/execute-integration/execute-fixture-harness.d.ts +25 -0
  3. package/dist/execute-integration/execute-fixture-harness.js +37 -0
  4. package/dist/index.d.ts +3 -0
  5. package/dist/index.js +2 -0
  6. package/dist/internal/ast/diagnostics/index.d.ts +5 -0
  7. package/dist/internal/ast/diagnostics/index.js +25 -0
  8. package/dist/internal/ast/history/index.d.ts +15 -0
  9. package/dist/internal/ast/history/index.js +62 -0
  10. package/dist/internal/ast/index.d.ts +8 -0
  11. package/dist/internal/ast/index.js +5 -0
  12. package/dist/internal/ast/locators/index.d.ts +1 -0
  13. package/dist/internal/ast/locators/index.js +1 -0
  14. package/dist/internal/ast/locators/resolve-locator.d.ts +16 -0
  15. package/dist/internal/ast/locators/resolve-locator.js +920 -0
  16. package/dist/internal/ast/parser/SourceParser.d.ts +30 -0
  17. package/dist/internal/ast/parser/SourceParser.js +49 -0
  18. package/dist/internal/ast/parser/index.d.ts +21 -0
  19. package/dist/internal/ast/parser/index.js +64 -0
  20. package/dist/internal/ast/primitives/conditional/conditional-primitives.d.ts +18 -0
  21. package/dist/internal/ast/primitives/conditional/conditional-primitives.js +237 -0
  22. package/dist/internal/ast/primitives/conditional/index.d.ts +1 -0
  23. package/dist/internal/ast/primitives/conditional/index.js +1 -0
  24. package/dist/internal/ast/primitives/imports/add-import.d.ts +18 -0
  25. package/dist/internal/ast/primitives/imports/add-import.js +111 -0
  26. package/dist/internal/ast/primitives/imports/index.d.ts +2 -0
  27. package/dist/internal/ast/primitives/imports/index.js +2 -0
  28. package/dist/internal/ast/primitives/imports/remove-import.d.ts +15 -0
  29. package/dist/internal/ast/primitives/imports/remove-import.js +72 -0
  30. package/dist/internal/ast/primitives/index.d.ts +10 -0
  31. package/dist/internal/ast/primitives/index.js +10 -0
  32. package/dist/internal/ast/primitives/jsx/index.d.ts +4 -0
  33. package/dist/internal/ast/primitives/jsx/index.js +4 -0
  34. package/dist/internal/ast/primitives/jsx/insert-child.d.ts +11 -0
  35. package/dist/internal/ast/primitives/jsx/insert-child.js +69 -0
  36. package/dist/internal/ast/primitives/jsx/move-node.d.ts +9 -0
  37. package/dist/internal/ast/primitives/jsx/move-node.js +76 -0
  38. package/dist/internal/ast/primitives/jsx/remove-node.d.ts +7 -0
  39. package/dist/internal/ast/primitives/jsx/remove-node.js +36 -0
  40. package/dist/internal/ast/primitives/jsx/update-text.d.ts +8 -0
  41. package/dist/internal/ast/primitives/jsx/update-text.js +81 -0
  42. package/dist/internal/ast/primitives/next/index.d.ts +1 -0
  43. package/dist/internal/ast/primitives/next/index.js +1 -0
  44. package/dist/internal/ast/primitives/next/next-primitives.d.ts +43 -0
  45. package/dist/internal/ast/primitives/next/next-primitives.js +211 -0
  46. package/dist/internal/ast/primitives/shared.d.ts +60 -0
  47. package/dist/internal/ast/primitives/shared.js +176 -0
  48. package/dist/internal/ast/primitives/style/class-expression.d.ts +23 -0
  49. package/dist/internal/ast/primitives/style/class-expression.js +174 -0
  50. package/dist/internal/ast/primitives/style/index.d.ts +1 -0
  51. package/dist/internal/ast/primitives/style/index.js +1 -0
  52. package/dist/internal/ast/primitives/style/style-primitives.d.ts +49 -0
  53. package/dist/internal/ast/primitives/style/style-primitives.js +555 -0
  54. package/dist/internal/ast/primitives/values/index.d.ts +1 -0
  55. package/dist/internal/ast/primitives/values/index.js +1 -0
  56. package/dist/internal/ast/primitives/values/value-primitives.d.ts +42 -0
  57. package/dist/internal/ast/primitives/values/value-primitives.js +158 -0
  58. package/dist/internal/ast/printer/SourcePrinter.d.ts +21 -0
  59. package/dist/internal/ast/printer/SourcePrinter.js +76 -0
  60. package/dist/internal/ast/printer/index.d.ts +6 -0
  61. package/dist/internal/ast/printer/index.js +126 -0
  62. package/dist/internal/ast/types.d.ts +190 -0
  63. package/dist/internal/ast/types.js +1 -0
  64. package/dist/internal/capability/capability-resolver.d.ts +16 -0
  65. package/dist/internal/capability/capability-resolver.js +127 -0
  66. package/dist/internal/classname-source.d.ts +24 -0
  67. package/dist/internal/classname-source.js +220 -0
  68. package/dist/internal/contracts/IEditEngineRuntime.d.ts +18 -0
  69. package/dist/internal/contracts/IEditEngineRuntime.js +1 -0
  70. package/dist/internal/domain/EditDiagnostic.d.ts +38 -0
  71. package/dist/internal/domain/EditDiagnostic.js +43 -0
  72. package/dist/internal/events/event-bus.d.ts +14 -0
  73. package/dist/internal/events/event-bus.js +21 -0
  74. package/dist/internal/graph/graph-builder.d.ts +12 -0
  75. package/dist/internal/graph/graph-builder.js +1371 -0
  76. package/dist/internal/graph/import-resolver.d.ts +31 -0
  77. package/dist/internal/graph/import-resolver.js +109 -0
  78. package/dist/internal/graph/project-graph-builder.d.ts +32 -0
  79. package/dist/internal/graph/project-graph-builder.js +133 -0
  80. package/dist/internal/graph/types.d.ts +114 -0
  81. package/dist/internal/graph/types.js +6 -0
  82. package/dist/internal/history/undo-redo.d.ts +28 -0
  83. package/dist/internal/history/undo-redo.js +42 -0
  84. package/dist/internal/index.d.ts +2 -0
  85. package/dist/internal/index.js +1 -0
  86. package/dist/internal/planner/planner.d.ts +104 -0
  87. package/dist/internal/planner/planner.js +2533 -0
  88. package/dist/internal/planner/types.d.ts +275 -0
  89. package/dist/internal/planner/types.js +6 -0
  90. package/dist/internal/protocol/boundary.d.ts +10 -0
  91. package/dist/internal/protocol/boundary.js +3 -0
  92. package/dist/internal/protocol/capability.d.ts +47 -0
  93. package/dist/internal/protocol/capability.js +8 -0
  94. package/dist/internal/protocol/error.d.ts +43 -0
  95. package/dist/internal/protocol/error.js +38 -0
  96. package/dist/internal/protocol/event.d.ts +39 -0
  97. package/dist/internal/protocol/event.js +3 -0
  98. package/dist/internal/protocol/identity.d.ts +26 -0
  99. package/dist/internal/protocol/identity.js +30 -0
  100. package/dist/internal/protocol/operation.d.ts +224 -0
  101. package/dist/internal/protocol/operation.js +8 -0
  102. package/dist/internal/protocol/render.d.ts +212 -0
  103. package/dist/internal/protocol/render.js +3 -0
  104. package/dist/internal/protocol.d.ts +9 -0
  105. package/dist/internal/protocol.js +2 -0
  106. package/dist/internal/provenance/binding-graph.d.ts +39 -0
  107. package/dist/internal/provenance/binding-graph.js +184 -0
  108. package/dist/internal/provenance/capability-policy.d.ts +15 -0
  109. package/dist/internal/provenance/capability-policy.js +96 -0
  110. package/dist/internal/provenance/data-source-classifier.d.ts +14 -0
  111. package/dist/internal/provenance/data-source-classifier.js +281 -0
  112. package/dist/internal/provenance/resolve-text-provenance.d.ts +45 -0
  113. package/dist/internal/provenance/resolve-text-provenance.js +3090 -0
  114. package/dist/internal/provenance/types.d.ts +89 -0
  115. package/dist/internal/provenance/types.js +1 -0
  116. package/dist/internal/render/component-semantic.d.ts +11 -0
  117. package/dist/internal/render/component-semantic.js +141 -0
  118. package/dist/internal/render/content-model.d.ts +3 -0
  119. package/dist/internal/render/content-model.js +89 -0
  120. package/dist/internal/render/media-model.d.ts +3 -0
  121. package/dist/internal/render/media-model.js +45 -0
  122. package/dist/internal/render/provenance-types.d.ts +33 -0
  123. package/dist/internal/render/provenance-types.js +1 -0
  124. package/dist/internal/render/render-projection.d.ts +24 -0
  125. package/dist/internal/render/render-projection.js +281 -0
  126. package/dist/internal/render/tailwind-style-model.d.ts +19 -0
  127. package/dist/internal/render/tailwind-style-model.js +1187 -0
  128. package/dist/internal/runtime/EditEngineRuntime.d.ts +25 -0
  129. package/dist/internal/runtime/EditEngineRuntime.js +89 -0
  130. package/dist/internal/runtime/EditEngineRuntimeSnapshot.d.ts +31 -0
  131. package/dist/internal/runtime/EditEngineRuntimeSnapshot.js +15 -0
  132. package/dist/internal/runtime/InternalEditEngine.d.ts +44 -0
  133. package/dist/internal/runtime/InternalEditEngine.js +1391 -0
  134. package/dist/internal/runtime.d.ts +3 -0
  135. package/dist/internal/runtime.js +1 -0
  136. package/dist/internal/topology/topology.d.ts +6 -0
  137. package/dist/internal/topology/topology.js +98 -0
  138. package/dist/internal/topology/types.d.ts +35 -0
  139. package/dist/internal/topology/types.js +5 -0
  140. package/dist/internal/types.d.ts +1 -0
  141. package/dist/internal/types.js +1 -0
  142. package/dist/internal/writeback/in-memory-fs.d.ts +7 -0
  143. package/dist/internal/writeback/in-memory-fs.js +44 -0
  144. package/dist/internal/writeback/types.d.ts +45 -0
  145. package/dist/internal/writeback/types.js +7 -0
  146. package/dist/internal/writeback/writeback-service.d.ts +7 -0
  147. package/dist/internal/writeback/writeback-service.js +568 -0
  148. package/dist/internal-adapter.d.ts +18 -0
  149. package/dist/internal-adapter.js +350 -0
  150. package/dist/next-app-router-fs.d.ts +2 -0
  151. package/dist/next-app-router-fs.js +64 -0
  152. package/dist/next-app-router.d.ts +11 -0
  153. package/dist/next-app-router.js +140 -0
  154. package/dist/preview-runtime.d.ts +394 -0
  155. package/dist/preview-runtime.js +102 -0
  156. package/dist/public-file-system.d.ts +7 -0
  157. package/dist/public-file-system.js +1 -0
  158. package/dist/runtime-sync.d.ts +95 -0
  159. package/dist/runtime-sync.js +321 -0
  160. package/dist/runtime.d.ts +340 -0
  161. package/dist/runtime.js +134 -0
  162. package/dist/site-edit-instrumentation.d.ts +19 -0
  163. package/dist/site-edit-instrumentation.js +322 -0
  164. package/dist/snapshot-file-system.d.ts +19 -0
  165. package/dist/snapshot-file-system.js +49 -0
  166. package/dist/source-watcher.d.ts +20 -0
  167. package/dist/source-watcher.js +150 -0
  168. package/dist/source-writeback-test-harness.d.ts +244 -0
  169. package/dist/source-writeback-test-harness.js +119 -0
  170. package/dist/types.d.ts +68 -0
  171. package/dist/types.js +1 -0
  172. package/dist/webpack-loader.cjs +592 -0
  173. package/dist/webpack-loader.d.ts +27 -0
  174. package/package.json +66 -0
@@ -0,0 +1,568 @@
1
+ // ── WritebackService 骨架 ──────────────────────────────────────
2
+ // 根文档 03 §5 定义:
3
+ // 1. 先做 documentVersion stale check
4
+ // 2. 按 steps[].order 顺序执行
5
+ // 3. 任一步失败,按 preSnapshots 回滚
6
+ // 4. undo/redo 基于 operation-level snapshot
7
+ import { addImport, addDirective, addTailwindClass, removeImport, removeDirective, removeJsxAttribute, removeTailwindClass, setJsxAttribute, updateClassName, updateCssModuleClassReference, updateDirective, updateGenerateMetadata, updateMetadataField, updateNamedExport, updateStyleProperties, updateStyleProperty, updateValue, insertArrayElement, removeArrayElement, moveArrayElement, insertObjectProperty, removeObjectProperty, replaceConditionalExpression, setConditionalBranchContent, updateText as astUpdateText, removeNode as astRemoveNode, insertChild as astInsertChild, moveNode as astMoveNode, } from "../ast/primitives/index.js";
8
+ import { createJsxAttribute, parseMutationSource, printMutationWithOperation, resolveRequiredJsxElement, } from "../ast/primitives/shared.js";
9
+ import { primitiveFailure } from "../ast/diagnostics/index.js";
10
+ /* ------------------------------------------------------------------ */
11
+ /* createWritebackService */
12
+ /* ------------------------------------------------------------------ */
13
+ export function createWritebackService(fs, options = {}) {
14
+ const undoStack = [];
15
+ const redoStack = [];
16
+ const externalAdapters = options.externalAdapters ?? [];
17
+ // Allow file systems that don't implement versioning (e.g., minimal test stubs).
18
+ const _localVersions = new Map();
19
+ function _getVersion(file) {
20
+ return typeof fs.getVersion === "function"
21
+ ? fs.getVersion(file)
22
+ : (_localVersions.get(file) ?? 0);
23
+ }
24
+ function _bumpVersion(file) {
25
+ if (typeof fs.bumpVersion === "function") {
26
+ return fs.bumpVersion(file);
27
+ }
28
+ const v = (_localVersions.get(file) ?? 0) + 1;
29
+ _localVersions.set(file, v);
30
+ return v;
31
+ }
32
+ return {
33
+ execute,
34
+ undo,
35
+ redo,
36
+ };
37
+ /* ---------------------------------------------------------------- */
38
+ /* execute */
39
+ /* ---------------------------------------------------------------- */
40
+ async function execute(plan) {
41
+ // ── 1. 拍下 pre-snapshots ──
42
+ const preSnapshots = [];
43
+ const involvedFiles = new Set(plan.steps
44
+ .filter((step) => step.type !== "invoke-external-adapter")
45
+ .map((s) => s.file));
46
+ for (const file of involvedFiles) {
47
+ try {
48
+ const content = await fs.readFile(file);
49
+ const version = _getVersion(file);
50
+ preSnapshots.push({ file, content, version });
51
+ }
52
+ catch {
53
+ // 文件不存在时用空内容
54
+ preSnapshots.push({ file, content: "", version: 0 });
55
+ }
56
+ }
57
+ // ── 2. 按 order 排序步骤 ──
58
+ const sortedSteps = [...plan.steps].sort((a, b) => a.order - b.order);
59
+ // ── 3. 顺序执行 ──
60
+ // 保存当前文件内容的 working copies
61
+ const workingCopies = new Map();
62
+ for (const snap of preSnapshots) {
63
+ workingCopies.set(snap.file, snap.content);
64
+ }
65
+ for (let index = 0; index < sortedSteps.length;) {
66
+ const step = sortedSteps[index];
67
+ if (step.type === "invoke-external-adapter") {
68
+ const requiredCapability = step.capability ?? "write-text";
69
+ const adapter = externalAdapters.find((candidate) => candidate.sourceType === step.sourceType &&
70
+ (step.adapterId === undefined ||
71
+ candidate.adapterId === step.adapterId) &&
72
+ (candidate.capabilities === undefined ||
73
+ candidate.capabilities.includes(requiredCapability)));
74
+ if (!adapter) {
75
+ await rollback(preSnapshots);
76
+ return {
77
+ ok: false,
78
+ operationId: plan.operationId,
79
+ errorCode: "unsupported-operation",
80
+ reason: `No external adapter registered for ${step.sourceType}`,
81
+ rolledBack: true,
82
+ };
83
+ }
84
+ const result = await adapter.execute(step);
85
+ if (!result.ok) {
86
+ await rollback(preSnapshots);
87
+ return {
88
+ ok: false,
89
+ operationId: plan.operationId,
90
+ errorCode: "writeback-failed",
91
+ reason: result.reason ?? `External adapter failed for ${step.sourceType}`,
92
+ rolledBack: true,
93
+ };
94
+ }
95
+ index += 1;
96
+ continue;
97
+ }
98
+ const currentContent = workingCopies.get(step.file) ?? "";
99
+ const batchedSetJsxPropSteps = collectAdjacentSetJsxPropSteps(sortedSteps, index);
100
+ const batchedRemoveJsxPropSteps = collectAdjacentRemoveJsxPropSteps(sortedSteps, index);
101
+ const result = batchedSetJsxPropSteps.length > 1
102
+ ? executeSetJsxPropBatch(batchedSetJsxPropSteps, currentContent)
103
+ : batchedRemoveJsxPropSteps.length > 1
104
+ ? executeRemoveJsxPropBatch(batchedRemoveJsxPropSteps, currentContent)
105
+ : executeStep(step, currentContent);
106
+ if (!result.ok) {
107
+ // ── 4. 回滚 ──
108
+ await rollback(preSnapshots);
109
+ return {
110
+ ok: false,
111
+ operationId: plan.operationId,
112
+ errorCode: result.errorCode,
113
+ reason: result.reason,
114
+ rolledBack: true,
115
+ };
116
+ }
117
+ workingCopies.set(step.file, result.source);
118
+ index += Math.max(1, batchedSetJsxPropSteps.length, batchedRemoveJsxPropSteps.length);
119
+ }
120
+ // ── 5. 写回文件 ──
121
+ const postSnapshots = [];
122
+ for (const [file, content] of workingCopies) {
123
+ await fs.writeFile(file, content);
124
+ const version = _bumpVersion(file);
125
+ postSnapshots.push({ file, content, version });
126
+ }
127
+ // ── 6. 压入 undo 栈 ──
128
+ undoStack.push({
129
+ operationId: plan.operationId,
130
+ preSnapshots,
131
+ postSnapshots,
132
+ plan,
133
+ });
134
+ // 新操作清空 redo 栈
135
+ redoStack.length = 0;
136
+ return {
137
+ ok: true,
138
+ operationId: plan.operationId,
139
+ resultVersion: Math.max(1, ...postSnapshots.map((s) => s.version)),
140
+ affectedFiles: [...involvedFiles],
141
+ };
142
+ }
143
+ /* ---------------------------------------------------------------- */
144
+ /* undo */
145
+ /* ---------------------------------------------------------------- */
146
+ async function undo() {
147
+ const entry = undoStack.pop();
148
+ if (!entry) {
149
+ return {
150
+ ok: false,
151
+ operationId: "",
152
+ errorCode: "unknown",
153
+ reason: "Nothing to undo",
154
+ };
155
+ }
156
+ // 恢复 preSnapshots
157
+ for (const snap of entry.preSnapshots) {
158
+ await fs.writeFile(snap.file, snap.content);
159
+ _bumpVersion(snap.file);
160
+ }
161
+ redoStack.push(entry);
162
+ return {
163
+ ok: true,
164
+ operationId: entry.operationId,
165
+ resultVersion: Math.max(...entry.preSnapshots.map((s) => s.version + 1)),
166
+ affectedFiles: entry.preSnapshots.map((s) => s.file),
167
+ };
168
+ }
169
+ /* ---------------------------------------------------------------- */
170
+ /* redo */
171
+ /* ---------------------------------------------------------------- */
172
+ async function redo() {
173
+ const entry = redoStack.pop();
174
+ if (!entry) {
175
+ return {
176
+ ok: false,
177
+ operationId: "",
178
+ errorCode: "unknown",
179
+ reason: "Nothing to redo",
180
+ };
181
+ }
182
+ // 恢复 postSnapshots
183
+ for (const snap of entry.postSnapshots) {
184
+ await fs.writeFile(snap.file, snap.content);
185
+ _bumpVersion(snap.file);
186
+ }
187
+ undoStack.push(entry);
188
+ return {
189
+ ok: true,
190
+ operationId: entry.operationId,
191
+ resultVersion: Math.max(...entry.postSnapshots.map((s) => s.version + 1)),
192
+ affectedFiles: entry.postSnapshots.map((s) => s.file),
193
+ };
194
+ }
195
+ /* ---------------------------------------------------------------- */
196
+ /* rollback */
197
+ /* ---------------------------------------------------------------- */
198
+ async function rollback(snapshots) {
199
+ for (const snap of snapshots) {
200
+ try {
201
+ await fs.writeFile(snap.file, snap.content);
202
+ }
203
+ catch {
204
+ // rollback 失败时继续尝试恢复其他文件
205
+ }
206
+ }
207
+ }
208
+ }
209
+ function executeStep(step, source) {
210
+ switch (step.type) {
211
+ case "invoke-external-adapter":
212
+ return {
213
+ ok: false,
214
+ reason: "External adapter steps must be executed asynchronously",
215
+ errorCode: "unsupported-operation",
216
+ };
217
+ case "add-import":
218
+ return toStepResult(addImport({
219
+ source,
220
+ moduleSpecifier: step.moduleSpecifier,
221
+ imports: step.imports,
222
+ }));
223
+ case "remove-import":
224
+ return toStepResult(removeImport({
225
+ source,
226
+ moduleSpecifier: step.moduleSpecifier,
227
+ imports: step.imports,
228
+ }));
229
+ case "remove-node":
230
+ return toStepResult(astRemoveNode({
231
+ source,
232
+ locator: step.locator,
233
+ removeWhitespace: step.removeWhitespace,
234
+ }));
235
+ case "insert-child":
236
+ return toStepResult(astInsertChild({
237
+ source,
238
+ locator: step.parentLocator,
239
+ node: step.node,
240
+ placement: step.placement,
241
+ anchorLocator: step.anchorLocator,
242
+ }));
243
+ case "update-text":
244
+ return toStepResult(astUpdateText({
245
+ source,
246
+ locator: step.locator,
247
+ segmentIndex: step.segmentIndex,
248
+ newText: step.newText,
249
+ }));
250
+ case "update-value":
251
+ return toStepResult(updateValue({
252
+ source,
253
+ locator: step.locator,
254
+ next: step.value,
255
+ }));
256
+ case "set-object-field":
257
+ return toStepResult(updateValue({
258
+ source,
259
+ locator: step.locator,
260
+ next: step.value,
261
+ }));
262
+ case "insert-object-field":
263
+ return toStepResult(insertObjectProperty({
264
+ source,
265
+ locator: step.locator,
266
+ property: {
267
+ key: step.field,
268
+ value: step.value,
269
+ },
270
+ }));
271
+ case "remove-object-field":
272
+ return toStepResult(removeObjectProperty({
273
+ source,
274
+ locator: step.locator,
275
+ key: step.field,
276
+ }));
277
+ case "update-array-item":
278
+ return toStepResult(updateValue({
279
+ source,
280
+ locator: step.locator,
281
+ next: step.value,
282
+ }));
283
+ case "insert-array-item":
284
+ return toStepResult(insertArrayElement({
285
+ source,
286
+ locator: step.locator,
287
+ index: step.index,
288
+ value: step.value,
289
+ }));
290
+ case "remove-array-item":
291
+ return toStepResult(removeArrayElement({
292
+ source,
293
+ locator: step.locator,
294
+ index: step.index,
295
+ }));
296
+ case "move-array-item":
297
+ return toStepResult(moveArrayElement({
298
+ source,
299
+ locator: step.locator,
300
+ fromIndex: step.fromIndex,
301
+ toIndex: step.toIndex,
302
+ }));
303
+ case "replace-conditional-expression":
304
+ return toStepResult(replaceConditionalExpression({
305
+ source,
306
+ locator: step.locator,
307
+ nextExpression: step.nextExpression,
308
+ }));
309
+ case "set-conditional-branch-content":
310
+ return toStepResult(setConditionalBranchContent({
311
+ source,
312
+ locator: step.locator,
313
+ branch: step.branch,
314
+ content: step.content,
315
+ }));
316
+ case "move-node":
317
+ return toStepResult(astMoveNode({
318
+ source,
319
+ locator: step.locator,
320
+ targetParentLocator: step.targetParentLocator,
321
+ placement: step.placement,
322
+ anchorLocator: step.anchorLocator,
323
+ }));
324
+ case "set-jsx-prop":
325
+ return toStepResult(setJsxAttribute({
326
+ source,
327
+ locator: step.locator,
328
+ value: step.value,
329
+ }));
330
+ case "remove-jsx-prop":
331
+ return toStepResult(removeJsxAttribute({
332
+ source,
333
+ locator: step.locator,
334
+ }));
335
+ case "set-class-name":
336
+ return toStepResult(updateClassName({
337
+ source,
338
+ locator: step.locator,
339
+ value: step.value,
340
+ }));
341
+ case "add-class-token":
342
+ return toStepResult(addTailwindClass({
343
+ source,
344
+ locator: step.locator,
345
+ token: step.token,
346
+ }));
347
+ case "remove-class-token":
348
+ return toStepResult(removeTailwindClass({
349
+ source,
350
+ locator: step.locator,
351
+ token: step.token,
352
+ }));
353
+ case "set-style-property":
354
+ return toStepResult(updateStyleProperty({
355
+ source,
356
+ locator: step.locator,
357
+ property: step.property,
358
+ value: step.value,
359
+ }));
360
+ case "set-style-properties":
361
+ return toStepResult(updateStyleProperties({
362
+ source,
363
+ locator: step.locator,
364
+ properties: step.properties,
365
+ }));
366
+ case "set-css-module-class":
367
+ return toStepResult(updateCssModuleClassReference({
368
+ source,
369
+ locator: step.locator,
370
+ nextClass: step.to,
371
+ }));
372
+ case "set-directive":
373
+ return toStepResult(step.mode === "add"
374
+ ? addDirective({
375
+ source,
376
+ value: step.value,
377
+ })
378
+ : updateDirective({
379
+ source,
380
+ locator: {
381
+ kind: "directive",
382
+ value: step.currentValue ?? step.value,
383
+ },
384
+ nextValue: step.value,
385
+ }));
386
+ case "remove-directive":
387
+ return toStepResult(removeDirective({
388
+ source,
389
+ locator: { kind: "directive", value: step.value },
390
+ }));
391
+ case "set-route-export":
392
+ return toStepResult(updateNamedExport({
393
+ source,
394
+ locator: { kind: "export", exportKind: "named", name: step.name },
395
+ next: step.value,
396
+ }));
397
+ case "set-metadata-field":
398
+ return toStepResult(updateMetadataField({
399
+ source,
400
+ locator: { kind: "export", exportKind: "metadata" },
401
+ path: [{ kind: "property", name: step.field }],
402
+ next: step.value,
403
+ }));
404
+ case "set-generate-metadata":
405
+ return toStepResult(updateGenerateMetadata({
406
+ source,
407
+ locator: { kind: "export", exportKind: "generate-metadata" },
408
+ path: [{ kind: "property", name: step.field }],
409
+ next: step.value,
410
+ }));
411
+ default:
412
+ return {
413
+ ok: false,
414
+ reason: `Unknown step type: ${step.type}`,
415
+ errorCode: "unknown",
416
+ };
417
+ }
418
+ }
419
+ function collectAdjacentSetJsxPropSteps(steps, startIndex) {
420
+ const first = steps[startIndex];
421
+ if (first?.type !== "set-jsx-prop") {
422
+ return [];
423
+ }
424
+ const result = [];
425
+ for (let index = startIndex; index < steps.length; index += 1) {
426
+ const step = steps[index];
427
+ if (step.type !== "set-jsx-prop" || step.file !== first.file) {
428
+ break;
429
+ }
430
+ result.push(step);
431
+ }
432
+ return result;
433
+ }
434
+ function collectAdjacentRemoveJsxPropSteps(steps, startIndex) {
435
+ const first = steps[startIndex];
436
+ if (first?.type !== "remove-jsx-prop") {
437
+ return [];
438
+ }
439
+ const result = [];
440
+ for (let index = startIndex; index < steps.length; index += 1) {
441
+ const step = steps[index];
442
+ if (step.type !== "remove-jsx-prop" || step.file !== first.file) {
443
+ break;
444
+ }
445
+ result.push(step);
446
+ }
447
+ return result;
448
+ }
449
+ function executeSetJsxPropBatch(steps, source) {
450
+ const parsed = parseMutationSource(source, "jsx-attribute");
451
+ if (!parsed.ok) {
452
+ return toStepResult(parsed.result);
453
+ }
454
+ for (const step of steps) {
455
+ const result = applySetJsxAttributeMutation(parsed.ast, source, step);
456
+ if (!result.ok) {
457
+ return toStepResult(result);
458
+ }
459
+ }
460
+ return toStepResult(printMutationWithOperation(parsed.ast, source, {
461
+ kind: "set-jsx-attribute",
462
+ target: "jsx-attribute",
463
+ inverse: "remove-jsx-attribute",
464
+ }));
465
+ }
466
+ function executeRemoveJsxPropBatch(steps, source) {
467
+ const parsed = parseMutationSource(source, "jsx-attribute");
468
+ if (!parsed.ok) {
469
+ return toStepResult(parsed.result);
470
+ }
471
+ for (const step of steps) {
472
+ const result = applyRemoveJsxAttributeMutation(parsed.ast, source, step);
473
+ if (!result.ok) {
474
+ return toStepResult(result);
475
+ }
476
+ }
477
+ return toStepResult(printMutationWithOperation(parsed.ast, source, {
478
+ kind: "remove-jsx-attribute",
479
+ target: "jsx-attribute",
480
+ inverse: "set-jsx-attribute",
481
+ }));
482
+ }
483
+ function applySetJsxAttributeMutation(ast, source, step) {
484
+ const elementMatch = resolveRequiredJsxElement(ast, step.locator.element, source);
485
+ if (!elementMatch.ok) {
486
+ return elementMatch.result;
487
+ }
488
+ const attributes = elementMatch.match.path.node.openingElement.attributes;
489
+ const targetIndex = attributes.findIndex((attribute) => attribute.type === "JSXAttribute" &&
490
+ attribute.name.type === "JSXIdentifier" &&
491
+ attribute.name.name === step.locator.name);
492
+ if (targetIndex >= 0 &&
493
+ attributes.some((attribute, index) => index > targetIndex && attribute.type === "JSXSpreadAttribute")) {
494
+ return primitiveFailure(source, "unsupported-syntax", "cannot edit explicit attribute when spread props follow it", "jsx-attribute");
495
+ }
496
+ const existing = attributes.find((attribute) => attribute.type === "JSXAttribute" &&
497
+ attribute.name.type === "JSXIdentifier" &&
498
+ attribute.name.name === step.locator.name);
499
+ if (existing?.type === "JSXAttribute") {
500
+ existing.value = createJsxAttribute(step.locator.name, step.value).value;
501
+ }
502
+ else {
503
+ attributes.push(createJsxAttribute(step.locator.name, step.value));
504
+ }
505
+ return {
506
+ ok: true,
507
+ source,
508
+ };
509
+ }
510
+ function applyRemoveJsxAttributeMutation(ast, source, step) {
511
+ const elementMatch = resolveRequiredJsxElement(ast, step.locator.element, source);
512
+ if (!elementMatch.ok) {
513
+ return elementMatch.result;
514
+ }
515
+ elementMatch.match.path.node.openingElement.attributes =
516
+ elementMatch.match.path.node.openingElement.attributes.filter((attribute) => attribute.type !== "JSXAttribute" ||
517
+ attribute.name.type !== "JSXIdentifier" ||
518
+ attribute.name.name !== step.locator.name);
519
+ return {
520
+ ok: true,
521
+ source,
522
+ };
523
+ }
524
+ function toStepResult(result) {
525
+ if (result.ok) {
526
+ return { ok: true, source: result.source };
527
+ }
528
+ switch (result.diagnostic.code) {
529
+ case "locator-miss":
530
+ return {
531
+ ok: false,
532
+ reason: result.diagnostic.message,
533
+ errorCode: "locator-miss",
534
+ };
535
+ case "unsupported-syntax":
536
+ case "binding-not-found":
537
+ case "export-not-found":
538
+ case "property-not-found":
539
+ case "array-index-out-of-range":
540
+ case "directive-conflict":
541
+ case "metadata-shape-unsupported":
542
+ return {
543
+ ok: false,
544
+ reason: result.diagnostic.message,
545
+ errorCode: "unsupported-operation",
546
+ };
547
+ case "invalid-parent-child-relation":
548
+ case "self-closing-element":
549
+ return {
550
+ ok: false,
551
+ reason: result.diagnostic.message,
552
+ errorCode: "invalid-target",
553
+ };
554
+ case "parse-failed":
555
+ case "printer-failed":
556
+ return {
557
+ ok: false,
558
+ reason: result.diagnostic.message,
559
+ errorCode: "ast-error",
560
+ };
561
+ default:
562
+ return {
563
+ ok: false,
564
+ reason: result.diagnostic.message,
565
+ errorCode: "writeback-failed",
566
+ };
567
+ }
568
+ }
@@ -0,0 +1,18 @@
1
+ import type { SiteEditEvent, SiteEditObjectContentDetail, SiteEditObjectDetail, SiteEditObjectEditContext, SiteEditObjectMediaDetail, SiteEditObjectStyleDetail, SiteEditObjectSummary, SiteEditOperationRequest, SiteEditOperationResult, SiteEditRenderDocument } from "@trojanbox-vcp-test/contracts";
2
+ import type { DesignerEvent, OperationRequest, OperationResult, RenderDocument, RenderObjectDetail } from "./internal/protocol.js";
3
+ export declare function toInternalOperationRequest(request: SiteEditOperationRequest): OperationRequest;
4
+ export declare function toSiteEditOperationResult(result: OperationResult): SiteEditOperationResult;
5
+ export declare function toSiteEditRenderDocument(document: RenderDocument, context: {
6
+ siteId: string;
7
+ snapshotId: string;
8
+ }): SiteEditRenderDocument;
9
+ export declare function toSiteEditObjectSummary(detail: RenderObjectDetail): SiteEditObjectSummary;
10
+ export declare function toSiteEditObjectEditContext(detail: RenderObjectDetail): SiteEditObjectEditContext;
11
+ export declare function toSiteEditObjectStyleDetail(detail: RenderObjectDetail): SiteEditObjectStyleDetail;
12
+ export declare function toSiteEditObjectContentDetail(detail: RenderObjectDetail): SiteEditObjectContentDetail;
13
+ export declare function toSiteEditObjectMediaDetail(detail: RenderObjectDetail): SiteEditObjectMediaDetail;
14
+ export declare function toSiteEditObjectDetail(detail: RenderObjectDetail): SiteEditObjectDetail;
15
+ export declare function toSiteEditEvent(event: DesignerEvent, context: {
16
+ siteId: string;
17
+ snapshotId: string;
18
+ }): SiteEditEvent | null;