@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.
- package/README.md +85 -0
- package/dist/execute-integration/execute-fixture-harness.d.ts +25 -0
- package/dist/execute-integration/execute-fixture-harness.js +37 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/internal/ast/diagnostics/index.d.ts +5 -0
- package/dist/internal/ast/diagnostics/index.js +25 -0
- package/dist/internal/ast/history/index.d.ts +15 -0
- package/dist/internal/ast/history/index.js +62 -0
- package/dist/internal/ast/index.d.ts +8 -0
- package/dist/internal/ast/index.js +5 -0
- package/dist/internal/ast/locators/index.d.ts +1 -0
- package/dist/internal/ast/locators/index.js +1 -0
- package/dist/internal/ast/locators/resolve-locator.d.ts +16 -0
- package/dist/internal/ast/locators/resolve-locator.js +920 -0
- package/dist/internal/ast/parser/SourceParser.d.ts +30 -0
- package/dist/internal/ast/parser/SourceParser.js +49 -0
- package/dist/internal/ast/parser/index.d.ts +21 -0
- package/dist/internal/ast/parser/index.js +64 -0
- package/dist/internal/ast/primitives/conditional/conditional-primitives.d.ts +18 -0
- package/dist/internal/ast/primitives/conditional/conditional-primitives.js +237 -0
- package/dist/internal/ast/primitives/conditional/index.d.ts +1 -0
- package/dist/internal/ast/primitives/conditional/index.js +1 -0
- package/dist/internal/ast/primitives/imports/add-import.d.ts +18 -0
- package/dist/internal/ast/primitives/imports/add-import.js +111 -0
- package/dist/internal/ast/primitives/imports/index.d.ts +2 -0
- package/dist/internal/ast/primitives/imports/index.js +2 -0
- package/dist/internal/ast/primitives/imports/remove-import.d.ts +15 -0
- package/dist/internal/ast/primitives/imports/remove-import.js +72 -0
- package/dist/internal/ast/primitives/index.d.ts +10 -0
- package/dist/internal/ast/primitives/index.js +10 -0
- package/dist/internal/ast/primitives/jsx/index.d.ts +4 -0
- package/dist/internal/ast/primitives/jsx/index.js +4 -0
- package/dist/internal/ast/primitives/jsx/insert-child.d.ts +11 -0
- package/dist/internal/ast/primitives/jsx/insert-child.js +69 -0
- package/dist/internal/ast/primitives/jsx/move-node.d.ts +9 -0
- package/dist/internal/ast/primitives/jsx/move-node.js +76 -0
- package/dist/internal/ast/primitives/jsx/remove-node.d.ts +7 -0
- package/dist/internal/ast/primitives/jsx/remove-node.js +36 -0
- package/dist/internal/ast/primitives/jsx/update-text.d.ts +8 -0
- package/dist/internal/ast/primitives/jsx/update-text.js +81 -0
- package/dist/internal/ast/primitives/next/index.d.ts +1 -0
- package/dist/internal/ast/primitives/next/index.js +1 -0
- package/dist/internal/ast/primitives/next/next-primitives.d.ts +43 -0
- package/dist/internal/ast/primitives/next/next-primitives.js +211 -0
- package/dist/internal/ast/primitives/shared.d.ts +60 -0
- package/dist/internal/ast/primitives/shared.js +176 -0
- package/dist/internal/ast/primitives/style/class-expression.d.ts +23 -0
- package/dist/internal/ast/primitives/style/class-expression.js +174 -0
- package/dist/internal/ast/primitives/style/index.d.ts +1 -0
- package/dist/internal/ast/primitives/style/index.js +1 -0
- package/dist/internal/ast/primitives/style/style-primitives.d.ts +49 -0
- package/dist/internal/ast/primitives/style/style-primitives.js +555 -0
- package/dist/internal/ast/primitives/values/index.d.ts +1 -0
- package/dist/internal/ast/primitives/values/index.js +1 -0
- package/dist/internal/ast/primitives/values/value-primitives.d.ts +42 -0
- package/dist/internal/ast/primitives/values/value-primitives.js +158 -0
- package/dist/internal/ast/printer/SourcePrinter.d.ts +21 -0
- package/dist/internal/ast/printer/SourcePrinter.js +76 -0
- package/dist/internal/ast/printer/index.d.ts +6 -0
- package/dist/internal/ast/printer/index.js +126 -0
- package/dist/internal/ast/types.d.ts +190 -0
- package/dist/internal/ast/types.js +1 -0
- package/dist/internal/capability/capability-resolver.d.ts +16 -0
- package/dist/internal/capability/capability-resolver.js +127 -0
- package/dist/internal/classname-source.d.ts +24 -0
- package/dist/internal/classname-source.js +220 -0
- package/dist/internal/contracts/IEditEngineRuntime.d.ts +18 -0
- package/dist/internal/contracts/IEditEngineRuntime.js +1 -0
- package/dist/internal/domain/EditDiagnostic.d.ts +38 -0
- package/dist/internal/domain/EditDiagnostic.js +43 -0
- package/dist/internal/events/event-bus.d.ts +14 -0
- package/dist/internal/events/event-bus.js +21 -0
- package/dist/internal/graph/graph-builder.d.ts +12 -0
- package/dist/internal/graph/graph-builder.js +1371 -0
- package/dist/internal/graph/import-resolver.d.ts +31 -0
- package/dist/internal/graph/import-resolver.js +109 -0
- package/dist/internal/graph/project-graph-builder.d.ts +32 -0
- package/dist/internal/graph/project-graph-builder.js +133 -0
- package/dist/internal/graph/types.d.ts +114 -0
- package/dist/internal/graph/types.js +6 -0
- package/dist/internal/history/undo-redo.d.ts +28 -0
- package/dist/internal/history/undo-redo.js +42 -0
- package/dist/internal/index.d.ts +2 -0
- package/dist/internal/index.js +1 -0
- package/dist/internal/planner/planner.d.ts +104 -0
- package/dist/internal/planner/planner.js +2533 -0
- package/dist/internal/planner/types.d.ts +275 -0
- package/dist/internal/planner/types.js +6 -0
- package/dist/internal/protocol/boundary.d.ts +10 -0
- package/dist/internal/protocol/boundary.js +3 -0
- package/dist/internal/protocol/capability.d.ts +47 -0
- package/dist/internal/protocol/capability.js +8 -0
- package/dist/internal/protocol/error.d.ts +43 -0
- package/dist/internal/protocol/error.js +38 -0
- package/dist/internal/protocol/event.d.ts +39 -0
- package/dist/internal/protocol/event.js +3 -0
- package/dist/internal/protocol/identity.d.ts +26 -0
- package/dist/internal/protocol/identity.js +30 -0
- package/dist/internal/protocol/operation.d.ts +224 -0
- package/dist/internal/protocol/operation.js +8 -0
- package/dist/internal/protocol/render.d.ts +212 -0
- package/dist/internal/protocol/render.js +3 -0
- package/dist/internal/protocol.d.ts +9 -0
- package/dist/internal/protocol.js +2 -0
- package/dist/internal/provenance/binding-graph.d.ts +39 -0
- package/dist/internal/provenance/binding-graph.js +184 -0
- package/dist/internal/provenance/capability-policy.d.ts +15 -0
- package/dist/internal/provenance/capability-policy.js +96 -0
- package/dist/internal/provenance/data-source-classifier.d.ts +14 -0
- package/dist/internal/provenance/data-source-classifier.js +281 -0
- package/dist/internal/provenance/resolve-text-provenance.d.ts +45 -0
- package/dist/internal/provenance/resolve-text-provenance.js +3090 -0
- package/dist/internal/provenance/types.d.ts +89 -0
- package/dist/internal/provenance/types.js +1 -0
- package/dist/internal/render/component-semantic.d.ts +11 -0
- package/dist/internal/render/component-semantic.js +141 -0
- package/dist/internal/render/content-model.d.ts +3 -0
- package/dist/internal/render/content-model.js +89 -0
- package/dist/internal/render/media-model.d.ts +3 -0
- package/dist/internal/render/media-model.js +45 -0
- package/dist/internal/render/provenance-types.d.ts +33 -0
- package/dist/internal/render/provenance-types.js +1 -0
- package/dist/internal/render/render-projection.d.ts +24 -0
- package/dist/internal/render/render-projection.js +281 -0
- package/dist/internal/render/tailwind-style-model.d.ts +19 -0
- package/dist/internal/render/tailwind-style-model.js +1187 -0
- package/dist/internal/runtime/EditEngineRuntime.d.ts +25 -0
- package/dist/internal/runtime/EditEngineRuntime.js +89 -0
- package/dist/internal/runtime/EditEngineRuntimeSnapshot.d.ts +31 -0
- package/dist/internal/runtime/EditEngineRuntimeSnapshot.js +15 -0
- package/dist/internal/runtime/InternalEditEngine.d.ts +44 -0
- package/dist/internal/runtime/InternalEditEngine.js +1391 -0
- package/dist/internal/runtime.d.ts +3 -0
- package/dist/internal/runtime.js +1 -0
- package/dist/internal/topology/topology.d.ts +6 -0
- package/dist/internal/topology/topology.js +98 -0
- package/dist/internal/topology/types.d.ts +35 -0
- package/dist/internal/topology/types.js +5 -0
- package/dist/internal/types.d.ts +1 -0
- package/dist/internal/types.js +1 -0
- package/dist/internal/writeback/in-memory-fs.d.ts +7 -0
- package/dist/internal/writeback/in-memory-fs.js +44 -0
- package/dist/internal/writeback/types.d.ts +45 -0
- package/dist/internal/writeback/types.js +7 -0
- package/dist/internal/writeback/writeback-service.d.ts +7 -0
- package/dist/internal/writeback/writeback-service.js +568 -0
- package/dist/internal-adapter.d.ts +18 -0
- package/dist/internal-adapter.js +350 -0
- package/dist/next-app-router-fs.d.ts +2 -0
- package/dist/next-app-router-fs.js +64 -0
- package/dist/next-app-router.d.ts +11 -0
- package/dist/next-app-router.js +140 -0
- package/dist/preview-runtime.d.ts +394 -0
- package/dist/preview-runtime.js +102 -0
- package/dist/public-file-system.d.ts +7 -0
- package/dist/public-file-system.js +1 -0
- package/dist/runtime-sync.d.ts +95 -0
- package/dist/runtime-sync.js +321 -0
- package/dist/runtime.d.ts +340 -0
- package/dist/runtime.js +134 -0
- package/dist/site-edit-instrumentation.d.ts +19 -0
- package/dist/site-edit-instrumentation.js +322 -0
- package/dist/snapshot-file-system.d.ts +19 -0
- package/dist/snapshot-file-system.js +49 -0
- package/dist/source-watcher.d.ts +20 -0
- package/dist/source-watcher.js +150 -0
- package/dist/source-writeback-test-harness.d.ts +244 -0
- package/dist/source-writeback-test-harness.js +119 -0
- package/dist/types.d.ts +68 -0
- package/dist/types.js +1 -0
- package/dist/webpack-loader.cjs +592 -0
- package/dist/webpack-loader.d.ts +27 -0
- package/package.json +66 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { ValueLocator, ValuePathSegment, ValueRootSelector } from "../ast/types.js";
|
|
2
|
+
import type { NodePath } from "@babel/traverse";
|
|
3
|
+
import type * as t from "@babel/types";
|
|
4
|
+
import type { RenderSourceGraph } from "../graph/types.js";
|
|
5
|
+
export type ExternalSourceKind = "api-field" | "cms-field" | "i18n-message" | "config-entry";
|
|
6
|
+
export type ExternalAdapterCapability = "write-text" | "insert-child" | "remove-node" | "move-node";
|
|
7
|
+
export interface ExternalSourceDescriptor {
|
|
8
|
+
sourceType: ExternalSourceKind;
|
|
9
|
+
sourceId: string;
|
|
10
|
+
adapterId?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ExternalAdapterRegistration {
|
|
13
|
+
sourceType: ExternalSourceKind;
|
|
14
|
+
adapterId?: string;
|
|
15
|
+
capabilities?: ExternalAdapterCapability[];
|
|
16
|
+
}
|
|
17
|
+
export interface ExternalSourceMatch {
|
|
18
|
+
sourceType: ExternalSourceKind;
|
|
19
|
+
sourceId: string;
|
|
20
|
+
file: string;
|
|
21
|
+
displayPath: string;
|
|
22
|
+
adapterId?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ExternalSourceResolverContext {
|
|
25
|
+
currentFile: string;
|
|
26
|
+
expressionPath: NodePath<t.Expression>;
|
|
27
|
+
readFile: (file: string) => Promise<string>;
|
|
28
|
+
exists: (file: string) => Promise<boolean>;
|
|
29
|
+
graph?: RenderSourceGraph;
|
|
30
|
+
}
|
|
31
|
+
export interface ExternalSourceResolution {
|
|
32
|
+
match: ExternalSourceMatch;
|
|
33
|
+
editMode?: "readonly";
|
|
34
|
+
diagnostics?: ProvenanceDiagnostic[];
|
|
35
|
+
}
|
|
36
|
+
export type ExternalSourceResolverResult = ExternalSourceMatch | ExternalSourceResolution;
|
|
37
|
+
export interface ExternalSourceResolver {
|
|
38
|
+
helperNames?: string[];
|
|
39
|
+
resolve(params: ExternalSourceResolverContext): Promise<ExternalSourceResolverResult | null> | ExternalSourceResolverResult | null;
|
|
40
|
+
}
|
|
41
|
+
export interface FunctionCallContract {
|
|
42
|
+
helperName: string;
|
|
43
|
+
purity: "pure" | "side-effect";
|
|
44
|
+
sourceArgIndex?: number;
|
|
45
|
+
}
|
|
46
|
+
export type ResolvedTextWriteTarget = {
|
|
47
|
+
kind: "value-binding";
|
|
48
|
+
file: string;
|
|
49
|
+
locator: ValueLocator | null;
|
|
50
|
+
root?: ValueRootSelector;
|
|
51
|
+
path?: ValuePathSegment[];
|
|
52
|
+
displayExpression: string;
|
|
53
|
+
sourceKind: "literal" | "binding" | "imported-binding" | "repeat-template";
|
|
54
|
+
collectionIndexMap?: number[];
|
|
55
|
+
collectionPathMap?: ValuePathSegment[][];
|
|
56
|
+
} | {
|
|
57
|
+
kind: "external-entry";
|
|
58
|
+
file: string;
|
|
59
|
+
sourceType: ExternalSourceKind;
|
|
60
|
+
sourceId: string;
|
|
61
|
+
adapterId?: string;
|
|
62
|
+
displayExpression: string;
|
|
63
|
+
};
|
|
64
|
+
export interface SemanticSource {
|
|
65
|
+
kind: "jsx-literal" | "component-prop" | "binding" | "imported-binding" | "object-field" | "array-item" | "repeat-template" | "conditional-branch" | ExternalSourceKind;
|
|
66
|
+
file: string;
|
|
67
|
+
locator: ValueLocator | null;
|
|
68
|
+
root?: ValueRootSelector;
|
|
69
|
+
path?: ValuePathSegment[];
|
|
70
|
+
displayPath: string;
|
|
71
|
+
externalSource?: ExternalSourceDescriptor;
|
|
72
|
+
}
|
|
73
|
+
export interface ProvenanceHop {
|
|
74
|
+
kind: "jsx-literal" | "component-prop" | "binding" | "imported-binding" | "object-field" | "array-item" | "repeat-template" | "conditional-branch" | ExternalSourceKind;
|
|
75
|
+
file: string;
|
|
76
|
+
displayName: string;
|
|
77
|
+
canEditHere: boolean;
|
|
78
|
+
}
|
|
79
|
+
export interface ProvenanceDiagnostic {
|
|
80
|
+
code: "unsupported-expression" | "dynamic-key" | "conditional-branch-unresolved" | "component-prop-unresolved" | "ambiguous-prop-binding" | "external-source-readonly" | "missing-adapter";
|
|
81
|
+
message: string;
|
|
82
|
+
}
|
|
83
|
+
export interface ProvenanceResolution {
|
|
84
|
+
finalSource: SemanticSource | null;
|
|
85
|
+
chain: ProvenanceHop[];
|
|
86
|
+
confidence: "exact" | "safe-derived" | "partial" | "unknown";
|
|
87
|
+
editMode: "direct" | "proxy" | "upstream" | "readonly" | "unsupported";
|
|
88
|
+
diagnostics: ProvenanceDiagnostic[];
|
|
89
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ComponentSemanticDescriptor } from "../protocol.js";
|
|
2
|
+
import type { ElementNode, RenderSourceGraph } from "../graph/types.js";
|
|
3
|
+
import type { ProvenanceResolution } from "./provenance-types.js";
|
|
4
|
+
interface BuildComponentSemanticDescriptorParams {
|
|
5
|
+
key: string;
|
|
6
|
+
element: ElementNode;
|
|
7
|
+
graph: RenderSourceGraph;
|
|
8
|
+
provenances: Map<number, ProvenanceResolution | null>;
|
|
9
|
+
}
|
|
10
|
+
export declare function buildComponentSemanticDescriptor(params: BuildComponentSemanticDescriptorParams): ComponentSemanticDescriptor;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
export function buildComponentSemanticDescriptor(params) {
|
|
2
|
+
const { key, element, graph, provenances } = params;
|
|
3
|
+
const instanceElement = resolveInstanceElement(key, element, graph);
|
|
4
|
+
const componentName = instanceElement?.tag ??
|
|
5
|
+
(isComponentTag(element.tag) ? element.tag : element.componentName);
|
|
6
|
+
const instanceKey = instanceElement?.identity.key ??
|
|
7
|
+
resolveComponentCallerKey(element, graph) ??
|
|
8
|
+
key;
|
|
9
|
+
const variantKeys = resolveVariantKeys(instanceElement);
|
|
10
|
+
const slotKeys = resolveSlotKeys(key, element, graph, provenances, instanceElement);
|
|
11
|
+
const editableStrategy = resolveEditableStrategy(key, element, graph, provenances, instanceKey);
|
|
12
|
+
return {
|
|
13
|
+
componentName,
|
|
14
|
+
instanceKey,
|
|
15
|
+
templateOwner: resolveTemplateOwner(instanceElement ?? element, componentName),
|
|
16
|
+
...(variantKeys.length > 0 ? { variantKeys } : {}),
|
|
17
|
+
...(slotKeys.length > 0 ? { slotKeys } : {}),
|
|
18
|
+
editableStrategy,
|
|
19
|
+
impactScope: resolveImpactScope(editableStrategy),
|
|
20
|
+
alternativeStrategies: resolveAlternativeStrategies(editableStrategy),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function resolveEditableStrategy(key, element, graph, provenances, instanceKey) {
|
|
24
|
+
for (const provenance of provenances.values()) {
|
|
25
|
+
if (provenance?.editMode === "upstream") {
|
|
26
|
+
return "upstream";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (findNearestComponentAncestor(key, graph)) {
|
|
30
|
+
return "instance";
|
|
31
|
+
}
|
|
32
|
+
if (!isComponentTag(element.tag) &&
|
|
33
|
+
element.sourceFile !== graph.routeId &&
|
|
34
|
+
instanceKey !== key) {
|
|
35
|
+
return "template";
|
|
36
|
+
}
|
|
37
|
+
return "instance";
|
|
38
|
+
}
|
|
39
|
+
function resolveComponentCallerKey(element, graph) {
|
|
40
|
+
if (isComponentTag(element.tag)) {
|
|
41
|
+
return element.identity.key;
|
|
42
|
+
}
|
|
43
|
+
const callers = [...graph.elements.values()].filter((candidate) => candidate.importedFrom === element.sourceFile &&
|
|
44
|
+
matchesComponentExport(candidate, element.componentName));
|
|
45
|
+
if (callers.length !== 1) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return callers[0].identity.key;
|
|
49
|
+
}
|
|
50
|
+
function resolveInstanceElement(key, element, graph) {
|
|
51
|
+
if (isComponentTag(element.tag)) {
|
|
52
|
+
return element;
|
|
53
|
+
}
|
|
54
|
+
const parentComponent = findNearestComponentAncestor(key, graph);
|
|
55
|
+
if (parentComponent) {
|
|
56
|
+
return parentComponent;
|
|
57
|
+
}
|
|
58
|
+
const callerKey = resolveComponentCallerKey(element, graph);
|
|
59
|
+
return callerKey ? (graph.elements.get(callerKey) ?? null) : null;
|
|
60
|
+
}
|
|
61
|
+
function matchesComponentExport(candidate, componentName) {
|
|
62
|
+
return (candidate.exportName === componentName ||
|
|
63
|
+
candidate.tag === componentName ||
|
|
64
|
+
candidate.exportName === "default");
|
|
65
|
+
}
|
|
66
|
+
function resolveTemplateOwner(element, componentName) {
|
|
67
|
+
if (isComponentTag(element.tag) && element.importedFrom) {
|
|
68
|
+
return `${element.importedFrom}#${componentName}`;
|
|
69
|
+
}
|
|
70
|
+
return `${element.sourceFile}#${componentName}`;
|
|
71
|
+
}
|
|
72
|
+
function resolveVariantKeys(instanceElement) {
|
|
73
|
+
if (!instanceElement?.attributes) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
const variantNames = new Set(["variant", "size", "tone"]);
|
|
77
|
+
return instanceElement.attributes
|
|
78
|
+
.filter((attribute) => variantNames.has(attribute.name))
|
|
79
|
+
.map((attribute) => attribute.name);
|
|
80
|
+
}
|
|
81
|
+
function resolveSlotKeys(key, element, graph, provenances, instanceElement) {
|
|
82
|
+
const slotKeys = new Set();
|
|
83
|
+
const parentComponent = findNearestComponentAncestor(key, graph);
|
|
84
|
+
if (parentComponent &&
|
|
85
|
+
instanceElement?.identity.key === parentComponent.identity.key) {
|
|
86
|
+
slotKeys.add("children");
|
|
87
|
+
}
|
|
88
|
+
for (const provenance of provenances.values()) {
|
|
89
|
+
const propHop = provenance?.chain.find((hop) => hop.kind === "component-prop");
|
|
90
|
+
if (!propHop) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const propName = propHop.displayName.split(".").pop();
|
|
94
|
+
if (propName) {
|
|
95
|
+
slotKeys.add(propName);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (slotKeys.size === 0 &&
|
|
99
|
+
isComponentTag(element.tag) &&
|
|
100
|
+
element.childKeys.length > 0) {
|
|
101
|
+
slotKeys.add("children");
|
|
102
|
+
}
|
|
103
|
+
return [...slotKeys];
|
|
104
|
+
}
|
|
105
|
+
function findNearestComponentAncestor(key, graph) {
|
|
106
|
+
let currentKey = graph.elements.get(key)?.parentKey ?? null;
|
|
107
|
+
while (currentKey) {
|
|
108
|
+
const current = graph.elements.get(currentKey);
|
|
109
|
+
if (!current) {
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
if (isComponentTag(current.tag)) {
|
|
113
|
+
return current;
|
|
114
|
+
}
|
|
115
|
+
currentKey = current.parentKey;
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
function isComponentTag(tag) {
|
|
120
|
+
return /^[A-Z]/.test(tag);
|
|
121
|
+
}
|
|
122
|
+
function resolveImpactScope(editableStrategy) {
|
|
123
|
+
switch (editableStrategy) {
|
|
124
|
+
case "template":
|
|
125
|
+
return "component-template";
|
|
126
|
+
case "upstream":
|
|
127
|
+
return "upstream-source";
|
|
128
|
+
default:
|
|
129
|
+
return "current-instance";
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function resolveAlternativeStrategies(editableStrategy) {
|
|
133
|
+
switch (editableStrategy) {
|
|
134
|
+
case "template":
|
|
135
|
+
return ["instance"];
|
|
136
|
+
case "upstream":
|
|
137
|
+
return ["template"];
|
|
138
|
+
default:
|
|
139
|
+
return ["template"];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const RICH_TEXT_FIELD_NAMES = new Set([
|
|
2
|
+
"markdown",
|
|
3
|
+
"body",
|
|
4
|
+
"content",
|
|
5
|
+
"value",
|
|
6
|
+
"document",
|
|
7
|
+
]);
|
|
8
|
+
const CONTENT_OPERATIONS = [
|
|
9
|
+
"replace-rich-text-content",
|
|
10
|
+
"insert-rich-text-block",
|
|
11
|
+
"remove-rich-text-block",
|
|
12
|
+
];
|
|
13
|
+
export function buildContentModel(element) {
|
|
14
|
+
if (!isComponentTag(element.tag) || !element.attributes) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
for (const attribute of element.attributes) {
|
|
18
|
+
if (!RICH_TEXT_FIELD_NAMES.has(attribute.name)) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (isMarkdownField(attribute)) {
|
|
22
|
+
return {
|
|
23
|
+
kind: "markdown",
|
|
24
|
+
field: attribute.name,
|
|
25
|
+
schema: "markdown",
|
|
26
|
+
valuePath: attribute.source?.expression,
|
|
27
|
+
operationBoundary: CONTENT_OPERATIONS,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const richTextSchema = detectRichTextSchema(attribute.resolvedValue);
|
|
31
|
+
if (richTextSchema === "prosemirror-doc" ||
|
|
32
|
+
richTextSchema === "node-array") {
|
|
33
|
+
return {
|
|
34
|
+
kind: "rich-text-json",
|
|
35
|
+
field: attribute.name,
|
|
36
|
+
valuePath: attribute.source?.expression,
|
|
37
|
+
schema: richTextSchema,
|
|
38
|
+
operationBoundary: CONTENT_OPERATIONS,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (richTextSchema === "unsupported-json-tree") {
|
|
42
|
+
return {
|
|
43
|
+
kind: "rich-text-json",
|
|
44
|
+
field: attribute.name,
|
|
45
|
+
valuePath: attribute.source?.expression,
|
|
46
|
+
schema: "unsupported-json-tree",
|
|
47
|
+
operationBoundary: [],
|
|
48
|
+
diagnostics: [
|
|
49
|
+
{
|
|
50
|
+
code: "unsupported-schema",
|
|
51
|
+
field: attribute.name,
|
|
52
|
+
message: 'Only top-level node arrays and { type: "doc", content: [] } rich-text JSON are editable in stage D',
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
function detectRichTextSchema(value) {
|
|
61
|
+
if (!value) {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
if (Array.isArray(value) && value.every((item) => isObjectRecord(item))) {
|
|
65
|
+
return "node-array";
|
|
66
|
+
}
|
|
67
|
+
if (!isObjectRecord(value)) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
if (value.type === "doc" && Array.isArray(value.content)) {
|
|
71
|
+
return "prosemirror-doc";
|
|
72
|
+
}
|
|
73
|
+
if (Array.isArray(value.content) || Array.isArray(value.children)) {
|
|
74
|
+
return "unsupported-json-tree";
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
function isObjectRecord(value) {
|
|
79
|
+
return !Array.isArray(value) && typeof value === "object" && value !== null;
|
|
80
|
+
}
|
|
81
|
+
function isMarkdownField(attribute) {
|
|
82
|
+
if (attribute.name === "markdown") {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
function isComponentTag(tag) {
|
|
88
|
+
return /^[A-Z]/.test(tag);
|
|
89
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const MEDIA_FIELDS = [
|
|
2
|
+
"src",
|
|
3
|
+
"alt",
|
|
4
|
+
"poster",
|
|
5
|
+
"caption",
|
|
6
|
+
"width",
|
|
7
|
+
"height",
|
|
8
|
+
];
|
|
9
|
+
export function buildMediaModel(element) {
|
|
10
|
+
const attributes = element.attributes?.filter((attribute) => MEDIA_FIELDS.includes(attribute.name)) ?? [];
|
|
11
|
+
if (attributes.length === 0) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
const fields = attributes.map((attribute) => attribute.name);
|
|
15
|
+
const valuePaths = Object.fromEntries(attributes
|
|
16
|
+
.filter((attribute) => attribute.source?.expression)
|
|
17
|
+
.map((attribute) => [attribute.name, attribute.source.expression]));
|
|
18
|
+
const sourceType = resolveMediaSourceType(attributes.map((attribute) => attribute.value));
|
|
19
|
+
return {
|
|
20
|
+
kind: resolveMediaKind(element.tag, fields),
|
|
21
|
+
sourceType,
|
|
22
|
+
fields,
|
|
23
|
+
editableFields: [...fields],
|
|
24
|
+
operationBoundary: ["set-media-field"],
|
|
25
|
+
...(Object.keys(valuePaths).length > 0 ? { valuePaths } : {}),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function resolveMediaKind(tag, fields) {
|
|
29
|
+
if (tag === "img") {
|
|
30
|
+
return "image";
|
|
31
|
+
}
|
|
32
|
+
if (tag === "video" || fields.includes("poster")) {
|
|
33
|
+
return "video";
|
|
34
|
+
}
|
|
35
|
+
return "media";
|
|
36
|
+
}
|
|
37
|
+
function resolveMediaSourceType(values) {
|
|
38
|
+
if (values.some((value) => value.startsWith("cms:"))) {
|
|
39
|
+
return "cms-media-field";
|
|
40
|
+
}
|
|
41
|
+
if (values.some((value) => /^(\.?\/|\/)/.test(value))) {
|
|
42
|
+
return "local-file-reference";
|
|
43
|
+
}
|
|
44
|
+
return "asset-id";
|
|
45
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ValueLocator, ValuePathSegment, ValueRootSelector } from "../ast/index.js";
|
|
2
|
+
export type ExternalSourceKind = "api-field" | "cms-field" | "i18n-message" | "config-entry";
|
|
3
|
+
export interface ExternalSourceDescriptor {
|
|
4
|
+
sourceType: ExternalSourceKind;
|
|
5
|
+
sourceId: string;
|
|
6
|
+
adapterId?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface SemanticSource {
|
|
9
|
+
kind: "jsx-literal" | "component-prop" | "binding" | "imported-binding" | "object-field" | "array-item" | "repeat-template" | "conditional-branch" | ExternalSourceKind;
|
|
10
|
+
file: string;
|
|
11
|
+
locator: ValueLocator | null;
|
|
12
|
+
root?: ValueRootSelector;
|
|
13
|
+
path?: ValuePathSegment[];
|
|
14
|
+
displayPath: string;
|
|
15
|
+
externalSource?: ExternalSourceDescriptor;
|
|
16
|
+
}
|
|
17
|
+
export interface ProvenanceHop {
|
|
18
|
+
kind: "jsx-literal" | "component-prop" | "binding" | "imported-binding" | "object-field" | "array-item" | "repeat-template" | "conditional-branch" | ExternalSourceKind;
|
|
19
|
+
file: string;
|
|
20
|
+
displayName: string;
|
|
21
|
+
canEditHere: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface ProvenanceDiagnostic {
|
|
24
|
+
code: "unsupported-expression" | "dynamic-key" | "conditional-branch-unresolved" | "component-prop-unresolved" | "ambiguous-prop-binding" | "external-source-readonly" | "missing-adapter";
|
|
25
|
+
message: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ProvenanceResolution {
|
|
28
|
+
finalSource: SemanticSource | null;
|
|
29
|
+
chain: ProvenanceHop[];
|
|
30
|
+
confidence: "exact" | "safe-derived" | "partial" | "unknown";
|
|
31
|
+
editMode: "direct" | "proxy" | "upstream" | "readonly" | "unsupported";
|
|
32
|
+
diagnostics: ProvenanceDiagnostic[];
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ComponentSemanticDescriptor, ContentModelView, MediaModelView, StyleModelView, StyleSourceView, ConditionalDetailView, RenderDocument, RenderObjectDetail, TextSourceView, WriteTargetView, NodeCapabilitySnapshot } from "../protocol.js";
|
|
2
|
+
import type { RenderSourceGraph } from "../graph/types.js";
|
|
3
|
+
import type { EditableTopology } from "../topology/types.js";
|
|
4
|
+
import type { ProvenanceResolution } from "./provenance-types.js";
|
|
5
|
+
/**
|
|
6
|
+
* 投影 RenderDocument —— 从 graph + topology + capabilities 构建。
|
|
7
|
+
*/
|
|
8
|
+
export declare function projectRenderDocument(graph: RenderSourceGraph, _topology: EditableTopology, capabilities: Map<string, NodeCapabilitySnapshot>, options?: {
|
|
9
|
+
canUpdateTextByKey?: ReadonlyMap<string, boolean>;
|
|
10
|
+
}): RenderDocument;
|
|
11
|
+
/**
|
|
12
|
+
* 投影 RenderObjectDetail —— 按需加载单个节点的详细信息。
|
|
13
|
+
*/
|
|
14
|
+
export declare function projectRenderObjectDetail(key: string, graph: RenderSourceGraph, topology: EditableTopology, capabilities: Map<string, NodeCapabilitySnapshot>, textSources: Map<number, TextSourceView | null>, writeTarget: WriteTargetView, provenances?: Map<number, ProvenanceResolution | null>, conditional?: ConditionalDetailView, componentSemantic?: ComponentSemanticDescriptor, contentModel?: ContentModelView, mediaModel?: MediaModelView, styleSource?: StyleSourceView, styleModel?: StyleModelView): RenderObjectDetail;
|
|
15
|
+
export interface DetailTextSegmentProjection {
|
|
16
|
+
editable: boolean;
|
|
17
|
+
source: TextSourceView | null;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 选择 detail 的写回目标。
|
|
21
|
+
* 只有可编辑 segment 解析到 value target 时,才升级为 value-binding。
|
|
22
|
+
* 否则保持 jsx-node,并保留上游传入的 file / isProxy 元数据。
|
|
23
|
+
*/
|
|
24
|
+
export declare function projectDetailWriteTarget(topWriteTarget: WriteTargetView, segments: DetailTextSegmentProjection[]): WriteTargetView;
|