@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,350 @@
1
+ const STRUCTURAL_KEY_MAP = new Map([
2
+ ["routeId", "routeId"],
3
+ ["documentVersion", "documentVersion"],
4
+ ["segmentIndex", "segmentIndex"],
5
+ ["preserveBindings", "preserveBindings"],
6
+ ["exportName", "exportName"],
7
+ ["anchorKey", "anchorKey"],
8
+ ["parentKey", "parentKey"],
9
+ ["slotName", "slotName"],
10
+ ["toIndex", "toIndex"],
11
+ ["fromIndex", "fromIndex"],
12
+ ["nextExpression", "nextExpression"],
13
+ ["nextToken", "nextToken"],
14
+ ]);
15
+ const OPAQUE_VALUE_KEYS = new Set(["value", "block", "properties", "props"]);
16
+ export function toInternalOperationRequest(request) {
17
+ return {
18
+ id: request.id,
19
+ routeId: request.routeId,
20
+ kind: request.kind,
21
+ target: toInternalTarget(request.target),
22
+ documentVersion: request.documentVersion,
23
+ params: renameStructuralKeys(request.params),
24
+ };
25
+ }
26
+ export function toSiteEditOperationResult(result) {
27
+ return {
28
+ requestId: result.requestId,
29
+ kind: result.kind,
30
+ target: toSiteEditTarget(result.target),
31
+ ok: result.ok,
32
+ resultVersion: result.resultVersion,
33
+ error: result.error,
34
+ };
35
+ }
36
+ export function toSiteEditRenderDocument(document, context) {
37
+ return {
38
+ siteId: context.siteId,
39
+ snapshotId: context.snapshotId,
40
+ routeId: document.routeId,
41
+ version: document.version,
42
+ rootKeys: document.rootKeys,
43
+ entries: document.entries.map((entry) => ({
44
+ key: entry.key,
45
+ tag: entry.tag,
46
+ label: entry.label,
47
+ parentKey: entry.parentKey,
48
+ childKeys: entry.childKeys,
49
+ operationSummary: {
50
+ canUpdateText: entry.operationSummary.canUpdateText,
51
+ canInsertChild: entry.operationSummary.canInsertChild,
52
+ canMove: entry.operationSummary.canMove,
53
+ canRemove: entry.operationSummary.canRemove,
54
+ },
55
+ })),
56
+ };
57
+ }
58
+ export function toSiteEditObjectSummary(detail) {
59
+ return {
60
+ key: detail.key,
61
+ tag: detail.tag,
62
+ label: detail.label,
63
+ capabilities: {
64
+ constraints: detail.capabilities.constraints,
65
+ },
66
+ isOpaque: detail.isOpaque,
67
+ provenanceChain: detail.provenanceChain
68
+ ? {
69
+ editMode: detail.provenanceChain.editMode,
70
+ diagnostics: detail.provenanceChain.diagnostics,
71
+ }
72
+ : undefined,
73
+ ancestors: detail.ancestors,
74
+ };
75
+ }
76
+ export function toSiteEditObjectEditContext(detail) {
77
+ return {
78
+ key: detail.key,
79
+ componentName: detail.componentName,
80
+ sourceFile: detail.sourceFile,
81
+ capabilities: toSiteEditCapability(detail.capabilities),
82
+ writeTarget: {
83
+ kind: detail.writeTarget.kind,
84
+ file: detail.writeTarget.file,
85
+ componentName: detail.writeTarget.componentName,
86
+ isProxy: detail.writeTarget.isProxy,
87
+ },
88
+ effectiveWriteTarget: detail.effectiveWriteTarget
89
+ ? {
90
+ kind: detail.effectiveWriteTarget.kind,
91
+ file: detail.effectiveWriteTarget.file,
92
+ isProxy: detail.effectiveWriteTarget.isProxy,
93
+ displayPath: detail.effectiveWriteTarget.displayPath,
94
+ sourceType: detail.effectiveWriteTarget.sourceType,
95
+ sourceId: detail.effectiveWriteTarget.sourceId,
96
+ }
97
+ : undefined,
98
+ provenanceChain: toSiteEditProvenanceChain(detail),
99
+ componentSemantic: asRecord(detail.componentSemantic),
100
+ textSegments: detail.textSegments.map((segment) => ({
101
+ index: segment.index,
102
+ value: segment.value,
103
+ editable: segment.editable,
104
+ source: segment.source
105
+ ? {
106
+ kind: segment.source.kind,
107
+ file: segment.source.file,
108
+ expression: segment.source.expression,
109
+ bindingName: segment.source.bindingName,
110
+ path: segment.source.path,
111
+ }
112
+ : null,
113
+ })),
114
+ boundaryKind: detail.boundaryKind,
115
+ children: detail.children,
116
+ siblings: detail.siblings,
117
+ };
118
+ }
119
+ export function toSiteEditObjectStyleDetail(detail) {
120
+ return {
121
+ key: detail.key,
122
+ styleSource: detail.styleSource,
123
+ styleModel: detail.styleModel
124
+ ? {
125
+ kind: detail.styleModel.kind,
126
+ properties: detail.styleModel.properties.map((property) => ({
127
+ property: property.property,
128
+ value: property.value,
129
+ resolvedValue: property.resolvedValue,
130
+ ...(property.breakpoint ? { breakpoint: property.breakpoint } : {}),
131
+ ...(property.state ? { state: property.state } : {}),
132
+ ...(property.variants?.length
133
+ ? { variants: property.variants }
134
+ : {}),
135
+ ...(property.options?.length ? { options: property.options } : {}),
136
+ editable: property.editable,
137
+ ...(property.diagnostics?.length
138
+ ? { diagnostics: property.diagnostics }
139
+ : {}),
140
+ operationBoundary: property.operationBoundary,
141
+ })),
142
+ }
143
+ : undefined,
144
+ };
145
+ }
146
+ export function toSiteEditObjectContentDetail(detail) {
147
+ return {
148
+ key: detail.key,
149
+ contentModel: asRecord(detail.contentModel),
150
+ };
151
+ }
152
+ export function toSiteEditObjectMediaDetail(detail) {
153
+ return {
154
+ key: detail.key,
155
+ mediaModel: asRecord(detail.mediaModel),
156
+ };
157
+ }
158
+ export function toSiteEditObjectDetail(detail) {
159
+ const styleDetail = toSiteEditObjectStyleDetail(detail);
160
+ return {
161
+ key: detail.key,
162
+ tag: detail.tag,
163
+ componentName: detail.componentName,
164
+ sourceFile: detail.sourceFile,
165
+ label: detail.label,
166
+ capabilities: toSiteEditCapability(detail.capabilities),
167
+ writeTarget: {
168
+ kind: detail.writeTarget.kind,
169
+ file: detail.writeTarget.file,
170
+ componentName: detail.writeTarget.componentName,
171
+ isProxy: detail.writeTarget.isProxy,
172
+ },
173
+ effectiveWriteTarget: detail.effectiveWriteTarget
174
+ ? {
175
+ kind: detail.effectiveWriteTarget.kind,
176
+ file: detail.effectiveWriteTarget.file,
177
+ isProxy: detail.effectiveWriteTarget.isProxy,
178
+ displayPath: detail.effectiveWriteTarget.displayPath,
179
+ sourceType: detail.effectiveWriteTarget.sourceType,
180
+ sourceId: detail.effectiveWriteTarget.sourceId,
181
+ }
182
+ : undefined,
183
+ provenanceChain: toSiteEditProvenanceChain(detail),
184
+ componentSemantic: asRecord(detail.componentSemantic),
185
+ contentModel: asRecord(detail.contentModel),
186
+ mediaModel: asRecord(detail.mediaModel),
187
+ styleSource: styleDetail.styleSource,
188
+ styleModel: styleDetail.styleModel,
189
+ conditional: asRecord(detail.conditional),
190
+ textSegments: detail.textSegments.map((segment) => ({
191
+ index: segment.index,
192
+ value: segment.value,
193
+ editable: segment.editable,
194
+ source: segment.source
195
+ ? {
196
+ kind: segment.source.kind,
197
+ file: segment.source.file,
198
+ expression: segment.source.expression,
199
+ bindingName: segment.source.bindingName,
200
+ path: segment.source.path,
201
+ }
202
+ : null,
203
+ })),
204
+ boundaryKind: detail.boundaryKind,
205
+ isOpaque: detail.isOpaque,
206
+ ancestors: detail.ancestors,
207
+ children: detail.children,
208
+ siblings: detail.siblings,
209
+ };
210
+ }
211
+ export function toSiteEditEvent(event, context) {
212
+ const at = new Date().toISOString();
213
+ if (event.type === "document-changed") {
214
+ return {
215
+ type: "site-edit.document.changed",
216
+ siteId: context.siteId,
217
+ snapshotId: context.snapshotId,
218
+ routeId: event.routeId,
219
+ version: event.version,
220
+ at,
221
+ };
222
+ }
223
+ if (event.type === "operation-completed") {
224
+ return {
225
+ type: "site-edit.operation.completed",
226
+ siteId: context.siteId,
227
+ result: toSiteEditOperationResult(event.result),
228
+ at,
229
+ };
230
+ }
231
+ if (event.type === "operation-failed") {
232
+ return {
233
+ type: "site-edit.operation.failed",
234
+ siteId: context.siteId,
235
+ requestId: event.requestId,
236
+ error: event.error,
237
+ at,
238
+ };
239
+ }
240
+ if (event.type === "capability-changed") {
241
+ return {
242
+ type: "site-edit.capability.changed",
243
+ siteId: context.siteId,
244
+ routeId: "",
245
+ keys: event.keys,
246
+ at,
247
+ };
248
+ }
249
+ return null;
250
+ }
251
+ function toInternalTarget(target) {
252
+ if (target.kind === "route") {
253
+ return { kind: "route", routeId: target.routeId };
254
+ }
255
+ return target;
256
+ }
257
+ function toSiteEditTarget(target) {
258
+ if (target.kind === "route") {
259
+ return { kind: "route", routeId: target.routeId };
260
+ }
261
+ return target;
262
+ }
263
+ function toSiteEditCapability(capability) {
264
+ return {
265
+ key: capability.key,
266
+ canUpdateText: capability.canUpdateText,
267
+ canInsertChild: capability.canInsertChild,
268
+ canMove: capability.canMove,
269
+ canRemove: capability.canRemove,
270
+ editModes: capability.editModes
271
+ ? {
272
+ updateText: capability.editModes.updateText
273
+ ? {
274
+ mode: capability.editModes.updateText.mode,
275
+ reasonCode: capability.editModes.updateText.reasonCode,
276
+ }
277
+ : undefined,
278
+ insertChild: capability.editModes.insertChild
279
+ ? {
280
+ mode: capability.editModes.insertChild.mode,
281
+ reasonCode: capability.editModes.insertChild.reasonCode,
282
+ }
283
+ : undefined,
284
+ move: capability.editModes.move
285
+ ? {
286
+ mode: capability.editModes.move.mode,
287
+ reasonCode: capability.editModes.move.reasonCode,
288
+ }
289
+ : undefined,
290
+ remove: capability.editModes.remove
291
+ ? {
292
+ mode: capability.editModes.remove.mode,
293
+ reasonCode: capability.editModes.remove.reasonCode,
294
+ }
295
+ : undefined,
296
+ }
297
+ : undefined,
298
+ constraints: capability.constraints,
299
+ };
300
+ }
301
+ function toSiteEditProvenanceChain(detail) {
302
+ return detail.provenanceChain
303
+ ? {
304
+ finalSource: detail.provenanceChain.finalSource
305
+ ? {
306
+ kind: detail.provenanceChain.finalSource.kind,
307
+ file: detail.provenanceChain.finalSource.file,
308
+ displayPath: detail.provenanceChain.finalSource.displayPath,
309
+ externalSource: detail.provenanceChain.finalSource.externalSource
310
+ ? {
311
+ sourceType: detail.provenanceChain.finalSource.externalSource
312
+ .sourceType,
313
+ sourceId: detail.provenanceChain.finalSource.externalSource
314
+ .sourceId,
315
+ adapterId: detail.provenanceChain.finalSource.externalSource
316
+ .adapterId,
317
+ }
318
+ : undefined,
319
+ }
320
+ : null,
321
+ chain: detail.provenanceChain.chain.map((hop) => ({
322
+ kind: hop.kind,
323
+ file: hop.file,
324
+ displayName: hop.displayName,
325
+ canEditHere: hop.canEditHere,
326
+ })),
327
+ confidence: detail.provenanceChain.confidence,
328
+ editMode: detail.provenanceChain.editMode,
329
+ diagnostics: detail.provenanceChain.diagnostics,
330
+ }
331
+ : undefined;
332
+ }
333
+ function renameStructuralKeys(value) {
334
+ if (Array.isArray(value)) {
335
+ return value.map((item) => renameStructuralKeys(item));
336
+ }
337
+ if (!value || typeof value !== "object") {
338
+ return value;
339
+ }
340
+ return Object.fromEntries(Object.entries(value).map(([key, nestedValue]) => {
341
+ const nextKey = STRUCTURAL_KEY_MAP.get(key) ?? key;
342
+ const nextValue = OPAQUE_VALUE_KEYS.has(key)
343
+ ? nestedValue
344
+ : renameStructuralKeys(nestedValue);
345
+ return [nextKey, nextValue];
346
+ }));
347
+ }
348
+ function asRecord(value) {
349
+ return value ? value : undefined;
350
+ }
@@ -0,0 +1,2 @@
1
+ import type { SiteEditNextRouteConfig } from "./next-app-router.js";
2
+ export declare function scanNextAppRouterRoutes(projectRoot: string): SiteEditNextRouteConfig[];
@@ -0,0 +1,64 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ const pageFilePattern = /^page\.(?:tsx|ts|jsx|js|mjs|cjs)$/;
4
+ export function scanNextAppRouterRoutes(projectRoot) {
5
+ const appRoot = resolveAppRoot(projectRoot);
6
+ if (!appRoot) {
7
+ return [];
8
+ }
9
+ return collectPageFiles(appRoot)
10
+ .map((file) => ({
11
+ routeId: toRouteId(appRoot, file),
12
+ entryFile: toPosixPath(path.relative(projectRoot, file)),
13
+ }))
14
+ .sort((left, right) => left.routeId.localeCompare(right.routeId));
15
+ }
16
+ function resolveAppRoot(projectRoot) {
17
+ for (const candidate of ["app", "src/app"]) {
18
+ const absolutePath = path.join(projectRoot, candidate);
19
+ if (fs.existsSync(absolutePath) &&
20
+ fs.statSync(absolutePath).isDirectory()) {
21
+ return absolutePath;
22
+ }
23
+ }
24
+ return null;
25
+ }
26
+ function collectPageFiles(directory) {
27
+ const files = [];
28
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
29
+ const absolutePath = path.join(directory, entry.name);
30
+ if (entry.isDirectory()) {
31
+ if (entry.name === "node_modules" ||
32
+ entry.name.startsWith(".") ||
33
+ entry.name.startsWith("_") ||
34
+ entry.name.startsWith("@")) {
35
+ continue;
36
+ }
37
+ files.push(...collectPageFiles(absolutePath));
38
+ continue;
39
+ }
40
+ if (entry.isFile() && pageFilePattern.test(entry.name)) {
41
+ files.push(absolutePath);
42
+ }
43
+ }
44
+ return files;
45
+ }
46
+ function toRouteId(appRoot, pageFile) {
47
+ const relativeDirectory = toPosixPath(path.relative(appRoot, path.dirname(pageFile)));
48
+ const segments = relativeDirectory
49
+ ? relativeDirectory.split("/").filter((segment) => isRouteSegment(segment))
50
+ : [];
51
+ return segments.length === 0 ? "/" : `/${segments.join("/")}`;
52
+ }
53
+ function isRouteSegment(segment) {
54
+ return (Boolean(segment) &&
55
+ !segment.startsWith("@") &&
56
+ !segment.startsWith("_") &&
57
+ !isRouteGroup(segment));
58
+ }
59
+ function isRouteGroup(segment) {
60
+ return segment.startsWith("(") && segment.endsWith(")");
61
+ }
62
+ function toPosixPath(value) {
63
+ return value.replace(/\\/g, "/");
64
+ }
@@ -0,0 +1,11 @@
1
+ export type SiteEditNextRouteConfig = {
2
+ routeId: string;
3
+ entryFile: string;
4
+ };
5
+ export { scanNextAppRouterRoutes } from "./next-app-router-fs.js";
6
+ export type SiteEditRouteSourceFile = {
7
+ path: string;
8
+ };
9
+ export declare function scanNextAppRouterRoutesFromFiles(files: SiteEditRouteSourceFile[]): SiteEditNextRouteConfig[];
10
+ export declare function resolveNextAppRouteId(pathname: string | null | undefined, routes: SiteEditNextRouteConfig[], basePath?: string): string;
11
+ export declare function normalizeNextRoutePath(pathname: string | null | undefined, basePath?: string): string;
@@ -0,0 +1,140 @@
1
+ export { scanNextAppRouterRoutes } from "./next-app-router-fs.js";
2
+ const pageFilePattern = /^page\.(?:tsx|ts|jsx|js|mjs|cjs)$/;
3
+ const dynamicSegmentPattern = /^\[[^\]]+\]$/;
4
+ const catchAllSegmentPattern = /^\[\.\.\.[^\]]+\]$/;
5
+ const optionalCatchAllSegmentPattern = /^\[\[\.\.\.[^\]]+\]\]$/;
6
+ export function scanNextAppRouterRoutesFromFiles(files) {
7
+ const normalizedPaths = files.map((file) => toPosixPath(file.path));
8
+ const appRoot = resolveAppRoot(normalizedPaths);
9
+ if (!appRoot) {
10
+ return [];
11
+ }
12
+ return normalizedPaths
13
+ .filter((file) => isAppPageFile(appRoot, file))
14
+ .map((file) => ({
15
+ routeId: toRouteId(appRoot, file),
16
+ entryFile: file,
17
+ }))
18
+ .sort((left, right) => left.routeId.localeCompare(right.routeId));
19
+ }
20
+ export function resolveNextAppRouteId(pathname, routes, basePath = "") {
21
+ const normalized = normalizeNextRoutePath(pathname, basePath);
22
+ if (routes.some((route) => route.routeId === normalized)) {
23
+ return normalized;
24
+ }
25
+ const matchingRoute = [...routes]
26
+ .sort((left, right) => getRouteScore(right.routeId) - getRouteScore(left.routeId))
27
+ .find((route) => createRouteMatcher(route.routeId).test(normalized));
28
+ return matchingRoute?.routeId ?? "/";
29
+ }
30
+ export function normalizeNextRoutePath(pathname, basePath = "") {
31
+ const value = (pathname ?? "/").trim() || "/";
32
+ const withoutQuery = value.split(/[?#]/, 1)[0] || "/";
33
+ const normalizedPathname = withoutQuery.startsWith("/")
34
+ ? withoutQuery
35
+ : `/${withoutQuery}`;
36
+ const normalizedBasePath = normalizeBasePath(basePath);
37
+ const withoutBasePath = normalizedBasePath &&
38
+ (normalizedPathname === normalizedBasePath ||
39
+ normalizedPathname.startsWith(`${normalizedBasePath}/`))
40
+ ? normalizedPathname.slice(normalizedBasePath.length) || "/"
41
+ : normalizedPathname;
42
+ const withLeadingSlash = withoutBasePath.startsWith("/")
43
+ ? withoutBasePath
44
+ : `/${withoutBasePath}`;
45
+ return withLeadingSlash === "/" ? "/" : withLeadingSlash.replace(/\/+$/, "");
46
+ }
47
+ function resolveAppRoot(files) {
48
+ const hasRootApp = files.some((file) => file.startsWith("app/"));
49
+ if (hasRootApp) {
50
+ return "app";
51
+ }
52
+ const hasSrcApp = files.some((file) => file.startsWith("src/app/"));
53
+ return hasSrcApp ? "src/app" : null;
54
+ }
55
+ function isAppPageFile(appRoot, file) {
56
+ if (!file.startsWith(`${appRoot}/`)) {
57
+ return false;
58
+ }
59
+ const basename = file.slice(file.lastIndexOf("/") + 1);
60
+ if (!pageFilePattern.test(basename)) {
61
+ return false;
62
+ }
63
+ return file
64
+ .slice(appRoot.length + 1, -basename.length - 1)
65
+ .split("/")
66
+ .filter(Boolean)
67
+ .every((segment) => !segment.startsWith("@") && !segment.startsWith("_"));
68
+ }
69
+ function toRouteId(appRoot, pageFile) {
70
+ const relativeDirectory = pageFile
71
+ .slice(appRoot.length + 1)
72
+ .split("/")
73
+ .slice(0, -1)
74
+ .filter((segment) => isRouteSegment(segment));
75
+ return relativeDirectory.length === 0
76
+ ? "/"
77
+ : `/${relativeDirectory.join("/")}`;
78
+ }
79
+ function isRouteSegment(segment) {
80
+ return (Boolean(segment) &&
81
+ !segment.startsWith("@") &&
82
+ !segment.startsWith("_") &&
83
+ !isRouteGroup(segment));
84
+ }
85
+ function isRouteGroup(segment) {
86
+ return segment.startsWith("(") && segment.endsWith(")");
87
+ }
88
+ function createRouteMatcher(routeId) {
89
+ if (routeId === "/") {
90
+ return /^\/$/;
91
+ }
92
+ const pattern = routeId
93
+ .split("/")
94
+ .filter(Boolean)
95
+ .map((segment) => {
96
+ if (optionalCatchAllSegmentPattern.test(segment)) {
97
+ return "(?:/.*)?";
98
+ }
99
+ if (catchAllSegmentPattern.test(segment)) {
100
+ return "/.+";
101
+ }
102
+ if (dynamicSegmentPattern.test(segment)) {
103
+ return "/[^/]+";
104
+ }
105
+ return `/${escapeRegExp(segment)}`;
106
+ })
107
+ .join("");
108
+ return new RegExp(`^${pattern}$`);
109
+ }
110
+ function getRouteScore(routeId) {
111
+ return routeId
112
+ .split("/")
113
+ .filter(Boolean)
114
+ .reduce((score, segment) => {
115
+ if (optionalCatchAllSegmentPattern.test(segment)) {
116
+ return score + 1;
117
+ }
118
+ if (catchAllSegmentPattern.test(segment)) {
119
+ return score + 2;
120
+ }
121
+ if (dynamicSegmentPattern.test(segment)) {
122
+ return score + 3;
123
+ }
124
+ return score + 4;
125
+ }, 0);
126
+ }
127
+ function normalizeBasePath(basePath) {
128
+ const trimmed = basePath.trim();
129
+ if (!trimmed || trimmed === "/") {
130
+ return "";
131
+ }
132
+ const withLeadingSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
133
+ return withLeadingSlash.replace(/\/+$/, "");
134
+ }
135
+ function escapeRegExp(value) {
136
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
137
+ }
138
+ function toPosixPath(value) {
139
+ return value.replace(/\\/g, "/").replace(/^\.\//, "");
140
+ }