@contractspec/lib.surface-runtime 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/README.md +164 -0
- package/dist/adapters/ai-sdk-stub.d.ts +5 -0
- package/dist/adapters/ai-sdk-stub.js +13 -0
- package/dist/adapters/blocknote-stub.d.ts +6 -0
- package/dist/adapters/blocknote-stub.js +31 -0
- package/dist/adapters/dnd-kit-adapter.d.ts +13 -0
- package/dist/adapters/dnd-kit-adapter.js +44 -0
- package/dist/adapters/dnd-kit-stub.d.ts +6 -0
- package/dist/adapters/dnd-kit-stub.js +8 -0
- package/dist/adapters/floating-ui-stub.d.ts +6 -0
- package/dist/adapters/floating-ui-stub.js +19 -0
- package/dist/adapters/index.d.ts +11 -0
- package/dist/adapters/index.js +176 -0
- package/dist/adapters/interfaces.d.ts +75 -0
- package/dist/adapters/interfaces.js +1 -0
- package/dist/adapters/motion-stub.d.ts +7 -0
- package/dist/adapters/motion-stub.js +27 -0
- package/dist/adapters/motion-stub.test.d.ts +1 -0
- package/dist/adapters/resizable-panels-stub.d.ts +6 -0
- package/dist/adapters/resizable-panels-stub.js +46 -0
- package/dist/adapters/resizable-panels-stub.test.d.ts +1 -0
- package/dist/browser/adapters/ai-sdk-stub.js +12 -0
- package/dist/browser/adapters/blocknote-stub.js +30 -0
- package/dist/browser/adapters/dnd-kit-adapter.js +43 -0
- package/dist/browser/adapters/dnd-kit-stub.js +7 -0
- package/dist/browser/adapters/floating-ui-stub.js +18 -0
- package/dist/browser/adapters/index.js +175 -0
- package/dist/browser/adapters/interfaces.js +0 -0
- package/dist/browser/adapters/motion-stub.js +26 -0
- package/dist/browser/adapters/resizable-panels-stub.js +45 -0
- package/dist/browser/evals/golden-context.js +0 -0
- package/dist/browser/evals/golden-harness.js +848 -0
- package/dist/browser/examples/pm-workbench.bundle.js +476 -0
- package/dist/browser/i18n/catalogs/en.js +71 -0
- package/dist/browser/i18n/catalogs/es.js +32 -0
- package/dist/browser/i18n/catalogs/fr.js +32 -0
- package/dist/browser/i18n/catalogs/index.js +133 -0
- package/dist/browser/i18n/index.js +173 -0
- package/dist/browser/i18n/keys.js +19 -0
- package/dist/browser/i18n/messages.js +143 -0
- package/dist/browser/index.js +2466 -0
- package/dist/browser/react/BundleProvider.js +47 -0
- package/dist/browser/react/BundleRenderer.js +726 -0
- package/dist/browser/react/OverlayConflictResolver.js +255 -0
- package/dist/browser/react/PatchProposalCard.js +255 -0
- package/dist/browser/react/RegionRenderer.js +128 -0
- package/dist/browser/react/SlotRenderer.js +118 -0
- package/dist/browser/react/WidgetPalette.js +59 -0
- package/dist/browser/react/index.js +792 -0
- package/dist/browser/runtime/apply-surface-patch.js +322 -0
- package/dist/browser/runtime/audit-events.js +137 -0
- package/dist/browser/runtime/build-context.js +55 -0
- package/dist/browser/runtime/extension-registry.js +58 -0
- package/dist/browser/runtime/field-renderer-registry.js +145 -0
- package/dist/browser/runtime/index.js +1496 -0
- package/dist/browser/runtime/overlay-alignment.js +83 -0
- package/dist/browser/runtime/overlay-signer.js +15 -0
- package/dist/browser/runtime/override-store.js +52 -0
- package/dist/browser/runtime/planner-prompt.js +67 -0
- package/dist/browser/runtime/planner-tools.js +77 -0
- package/dist/browser/runtime/policy-eval.js +155 -0
- package/dist/browser/runtime/preference-adapter.js +67 -0
- package/dist/browser/runtime/resolve-bundle.js +767 -0
- package/dist/browser/runtime/resolve-preferences.js +59 -0
- package/dist/browser/runtime/rollback.js +347 -0
- package/dist/browser/runtime/widget-registry.js +36 -0
- package/dist/browser/spec/define-module-bundle.js +113 -0
- package/dist/browser/spec/index.js +319 -0
- package/dist/browser/spec/types.js +0 -0
- package/dist/browser/spec/validate-bundle.js +65 -0
- package/dist/browser/spec/validate-surface-patch.js +206 -0
- package/dist/browser/spec/verification-snapshot-types.js +0 -0
- package/dist/browser/telemetry/index.js +20 -0
- package/dist/browser/telemetry/surface-metrics.js +20 -0
- package/dist/evals/golden-context.d.ts +24 -0
- package/dist/evals/golden-context.js +1 -0
- package/dist/evals/golden-harness.d.ts +29 -0
- package/dist/evals/golden-harness.js +849 -0
- package/dist/evals/golden-harness.test.d.ts +1 -0
- package/dist/examples/pm-workbench.bundle.d.ts +177 -0
- package/dist/examples/pm-workbench.bundle.js +477 -0
- package/dist/i18n/catalogs/en.d.ts +1 -0
- package/dist/i18n/catalogs/en.js +72 -0
- package/dist/i18n/catalogs/es.d.ts +1 -0
- package/dist/i18n/catalogs/es.js +33 -0
- package/dist/i18n/catalogs/fr.d.ts +1 -0
- package/dist/i18n/catalogs/fr.js +33 -0
- package/dist/i18n/catalogs/index.d.ts +3 -0
- package/dist/i18n/catalogs/index.js +134 -0
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/index.js +174 -0
- package/dist/i18n/keys.d.ts +20 -0
- package/dist/i18n/keys.js +20 -0
- package/dist/i18n/messages.d.ts +5 -0
- package/dist/i18n/messages.js +144 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2467 -0
- package/dist/node/adapters/ai-sdk-stub.js +12 -0
- package/dist/node/adapters/blocknote-stub.js +30 -0
- package/dist/node/adapters/dnd-kit-adapter.js +43 -0
- package/dist/node/adapters/dnd-kit-stub.js +7 -0
- package/dist/node/adapters/floating-ui-stub.js +18 -0
- package/dist/node/adapters/index.js +175 -0
- package/dist/node/adapters/interfaces.js +0 -0
- package/dist/node/adapters/motion-stub.js +26 -0
- package/dist/node/adapters/resizable-panels-stub.js +45 -0
- package/dist/node/evals/golden-context.js +0 -0
- package/dist/node/evals/golden-harness.js +848 -0
- package/dist/node/examples/pm-workbench.bundle.js +476 -0
- package/dist/node/i18n/catalogs/en.js +71 -0
- package/dist/node/i18n/catalogs/es.js +32 -0
- package/dist/node/i18n/catalogs/fr.js +32 -0
- package/dist/node/i18n/catalogs/index.js +133 -0
- package/dist/node/i18n/index.js +173 -0
- package/dist/node/i18n/keys.js +19 -0
- package/dist/node/i18n/messages.js +143 -0
- package/dist/node/index.js +2466 -0
- package/dist/node/react/BundleProvider.js +47 -0
- package/dist/node/react/BundleRenderer.js +726 -0
- package/dist/node/react/OverlayConflictResolver.js +255 -0
- package/dist/node/react/PatchProposalCard.js +255 -0
- package/dist/node/react/RegionRenderer.js +128 -0
- package/dist/node/react/SlotRenderer.js +118 -0
- package/dist/node/react/WidgetPalette.js +59 -0
- package/dist/node/react/index.js +792 -0
- package/dist/node/runtime/apply-surface-patch.js +322 -0
- package/dist/node/runtime/audit-events.js +137 -0
- package/dist/node/runtime/build-context.js +55 -0
- package/dist/node/runtime/extension-registry.js +58 -0
- package/dist/node/runtime/field-renderer-registry.js +145 -0
- package/dist/node/runtime/index.js +1496 -0
- package/dist/node/runtime/overlay-alignment.js +83 -0
- package/dist/node/runtime/overlay-signer.js +15 -0
- package/dist/node/runtime/override-store.js +52 -0
- package/dist/node/runtime/planner-prompt.js +67 -0
- package/dist/node/runtime/planner-tools.js +77 -0
- package/dist/node/runtime/policy-eval.js +155 -0
- package/dist/node/runtime/preference-adapter.js +67 -0
- package/dist/node/runtime/resolve-bundle.js +767 -0
- package/dist/node/runtime/resolve-preferences.js +59 -0
- package/dist/node/runtime/rollback.js +347 -0
- package/dist/node/runtime/widget-registry.js +36 -0
- package/dist/node/spec/define-module-bundle.js +113 -0
- package/dist/node/spec/index.js +319 -0
- package/dist/node/spec/types.js +0 -0
- package/dist/node/spec/validate-bundle.js +65 -0
- package/dist/node/spec/validate-surface-patch.js +206 -0
- package/dist/node/spec/verification-snapshot-types.js +0 -0
- package/dist/node/telemetry/index.js +20 -0
- package/dist/node/telemetry/surface-metrics.js +20 -0
- package/dist/react/BundleProvider.d.ts +13 -0
- package/dist/react/BundleProvider.js +48 -0
- package/dist/react/BundleRenderer.d.ts +22 -0
- package/dist/react/BundleRenderer.js +727 -0
- package/dist/react/OverlayConflictResolver.d.ts +15 -0
- package/dist/react/OverlayConflictResolver.js +256 -0
- package/dist/react/PatchProposalCard.d.ts +13 -0
- package/dist/react/PatchProposalCard.js +256 -0
- package/dist/react/RegionRenderer.d.ts +13 -0
- package/dist/react/RegionRenderer.js +129 -0
- package/dist/react/SlotRenderer.d.ts +13 -0
- package/dist/react/SlotRenderer.js +119 -0
- package/dist/react/WidgetPalette.d.ts +12 -0
- package/dist/react/WidgetPalette.js +60 -0
- package/dist/react/index.d.ts +7 -0
- package/dist/react/index.js +793 -0
- package/dist/runtime/apply-surface-patch.d.ts +15 -0
- package/dist/runtime/apply-surface-patch.js +323 -0
- package/dist/runtime/apply-surface-patch.test.d.ts +1 -0
- package/dist/runtime/audit-events.d.ts +70 -0
- package/dist/runtime/audit-events.js +138 -0
- package/dist/runtime/audit-events.test.d.ts +1 -0
- package/dist/runtime/build-context.d.ts +9 -0
- package/dist/runtime/build-context.js +56 -0
- package/dist/runtime/extension-registry.d.ts +39 -0
- package/dist/runtime/extension-registry.js +59 -0
- package/dist/runtime/field-renderer-registry.d.ts +23 -0
- package/dist/runtime/field-renderer-registry.js +146 -0
- package/dist/runtime/field-renderer-registry.test.d.ts +1 -0
- package/dist/runtime/index.d.ts +16 -0
- package/dist/runtime/index.js +1497 -0
- package/dist/runtime/overlay-alignment.d.ts +49 -0
- package/dist/runtime/overlay-alignment.js +84 -0
- package/dist/runtime/overlay-alignment.test.d.ts +1 -0
- package/dist/runtime/overlay-signer.d.ts +15 -0
- package/dist/runtime/overlay-signer.js +16 -0
- package/dist/runtime/override-store.d.ts +44 -0
- package/dist/runtime/override-store.js +53 -0
- package/dist/runtime/override-store.test.d.ts +1 -0
- package/dist/runtime/planner-prompt.d.ts +39 -0
- package/dist/runtime/planner-prompt.js +68 -0
- package/dist/runtime/planner-prompt.test.d.ts +1 -0
- package/dist/runtime/planner-tools.d.ts +106 -0
- package/dist/runtime/planner-tools.js +78 -0
- package/dist/runtime/planner-tools.test.d.ts +1 -0
- package/dist/runtime/policy-eval.d.ts +23 -0
- package/dist/runtime/policy-eval.js +156 -0
- package/dist/runtime/preference-adapter.d.ts +6 -0
- package/dist/runtime/preference-adapter.js +68 -0
- package/dist/runtime/resolve-bundle.d.ts +68 -0
- package/dist/runtime/resolve-bundle.js +768 -0
- package/dist/runtime/resolve-bundle.test.d.ts +1 -0
- package/dist/runtime/resolve-preferences.d.ts +9 -0
- package/dist/runtime/resolve-preferences.js +60 -0
- package/dist/runtime/resolve-preferences.test.d.ts +1 -0
- package/dist/runtime/rollback.d.ts +21 -0
- package/dist/runtime/rollback.js +348 -0
- package/dist/runtime/rollback.test.d.ts +1 -0
- package/dist/runtime/widget-registry.d.ts +26 -0
- package/dist/runtime/widget-registry.js +37 -0
- package/dist/runtime/widget-registry.test.d.ts +1 -0
- package/dist/spec/define-module-bundle.d.ts +17 -0
- package/dist/spec/define-module-bundle.js +114 -0
- package/dist/spec/define-module-bundle.test.d.ts +1 -0
- package/dist/spec/index.d.ts +5 -0
- package/dist/spec/index.js +320 -0
- package/dist/spec/types.d.ts +494 -0
- package/dist/spec/types.js +1 -0
- package/dist/spec/validate-bundle.d.ts +23 -0
- package/dist/spec/validate-bundle.js +66 -0
- package/dist/spec/validate-bundle.test.d.ts +1 -0
- package/dist/spec/validate-surface-patch.d.ts +39 -0
- package/dist/spec/validate-surface-patch.js +207 -0
- package/dist/spec/validate-surface-patch.test.d.ts +1 -0
- package/dist/spec/verification-snapshot-types.d.ts +23 -0
- package/dist/spec/verification-snapshot-types.js +1 -0
- package/dist/spec/verification-snapshot.test.d.ts +5 -0
- package/dist/telemetry/index.d.ts +5 -0
- package/dist/telemetry/index.js +21 -0
- package/dist/telemetry/surface-metrics.d.ts +17 -0
- package/dist/telemetry/surface-metrics.js +21 -0
- package/package.json +920 -0
|
@@ -0,0 +1,2466 @@
|
|
|
1
|
+
// src/adapters/ai-sdk-stub.ts
|
|
2
|
+
var aiSdkAdapterStub = {
|
|
3
|
+
startThread(_args) {
|
|
4
|
+
return null;
|
|
5
|
+
},
|
|
6
|
+
async requestPatches(_args) {
|
|
7
|
+
return [];
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/adapters/blocknote-stub.tsx
|
|
12
|
+
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
13
|
+
var blocknoteAdapterStub = {
|
|
14
|
+
supportsNode(_kind) {
|
|
15
|
+
return false;
|
|
16
|
+
},
|
|
17
|
+
createSchema(_registry) {
|
|
18
|
+
return {};
|
|
19
|
+
},
|
|
20
|
+
renderNode(node, _ctx) {
|
|
21
|
+
return /* @__PURE__ */ jsxDEV("div", {
|
|
22
|
+
"data-blocknote-stub": true,
|
|
23
|
+
"data-node-id": node.nodeId,
|
|
24
|
+
children: node.title ?? node.kind
|
|
25
|
+
}, undefined, false, undefined, this);
|
|
26
|
+
},
|
|
27
|
+
async serialize(node) {
|
|
28
|
+
return { nodeId: node.nodeId, kind: node.kind };
|
|
29
|
+
},
|
|
30
|
+
async deserialize(input) {
|
|
31
|
+
const o = input;
|
|
32
|
+
return {
|
|
33
|
+
nodeId: o?.nodeId ?? "stub",
|
|
34
|
+
kind: o?.kind ?? "custom-widget"
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// src/adapters/dnd-kit-adapter.tsx
|
|
40
|
+
import {
|
|
41
|
+
DndContext,
|
|
42
|
+
PointerSensor,
|
|
43
|
+
useSensor,
|
|
44
|
+
useSensors
|
|
45
|
+
} from "@dnd-kit/core";
|
|
46
|
+
import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
|
|
47
|
+
"use client";
|
|
48
|
+
var noop = () => {
|
|
49
|
+
return;
|
|
50
|
+
};
|
|
51
|
+
var _onPatch = noop;
|
|
52
|
+
function handleDragEnd(event) {
|
|
53
|
+
const { active, over } = event;
|
|
54
|
+
if (!over)
|
|
55
|
+
return;
|
|
56
|
+
const fromId = active.id;
|
|
57
|
+
const toSlotId = over.id;
|
|
58
|
+
if (fromId && toSlotId) {
|
|
59
|
+
_onPatch([{ op: "move-node", nodeId: fromId, toSlotId }]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function DndWrapperComponent({ children, onPatch }) {
|
|
63
|
+
_onPatch = onPatch;
|
|
64
|
+
const sensors = useSensors(useSensor(PointerSensor, {
|
|
65
|
+
activationConstraint: { distance: 8 }
|
|
66
|
+
}));
|
|
67
|
+
return /* @__PURE__ */ jsxDEV2(DndContext, {
|
|
68
|
+
sensors,
|
|
69
|
+
onDragEnd: handleDragEnd,
|
|
70
|
+
children
|
|
71
|
+
}, undefined, false, undefined, this);
|
|
72
|
+
}
|
|
73
|
+
var dndKitAdapter = {
|
|
74
|
+
enableSurfaceEditing(args) {
|
|
75
|
+
_onPatch = args.onPatch;
|
|
76
|
+
},
|
|
77
|
+
DndWrapper: DndWrapperComponent
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// src/adapters/dnd-kit-stub.ts
|
|
81
|
+
var dndKitAdapterStub = {
|
|
82
|
+
enableSurfaceEditing(_args) {}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// src/adapters/floating-ui-stub.tsx
|
|
86
|
+
import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
|
|
87
|
+
var floatingUiAdapterStub = {
|
|
88
|
+
renderAnchoredMenu({ anchorId, items }) {
|
|
89
|
+
return /* @__PURE__ */ jsxDEV3("div", {
|
|
90
|
+
"data-floating-stub": true,
|
|
91
|
+
"data-anchor-id": anchorId,
|
|
92
|
+
role: "menu",
|
|
93
|
+
children: items.map((a) => /* @__PURE__ */ jsxDEV3("div", {
|
|
94
|
+
role: "menuitem",
|
|
95
|
+
children: a.title
|
|
96
|
+
}, a.actionId, false, undefined, this))
|
|
97
|
+
}, undefined, false, undefined, this);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// src/adapters/motion-stub.ts
|
|
102
|
+
var PACE_TOKENS = {
|
|
103
|
+
deliberate: {
|
|
104
|
+
durationMs: 300,
|
|
105
|
+
enableEntrance: true,
|
|
106
|
+
layout: true
|
|
107
|
+
},
|
|
108
|
+
balanced: {
|
|
109
|
+
durationMs: 150,
|
|
110
|
+
enableEntrance: true,
|
|
111
|
+
layout: true
|
|
112
|
+
},
|
|
113
|
+
rapid: {
|
|
114
|
+
durationMs: 50,
|
|
115
|
+
enableEntrance: false,
|
|
116
|
+
layout: false
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
var motionAdapterStub = {
|
|
120
|
+
getTokens(pace) {
|
|
121
|
+
return PACE_TOKENS[pace] ?? PACE_TOKENS.balanced;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// src/adapters/resizable-panels-stub.tsx
|
|
126
|
+
import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
|
|
127
|
+
var LAYOUT_STORAGE_KEY = "surface-runtime:panel-layout:";
|
|
128
|
+
var resizablePanelsAdapterStub = {
|
|
129
|
+
renderPanelGroup(region, ctx, renderChild) {
|
|
130
|
+
const direction = region.direction === "horizontal" ? "row" : "column";
|
|
131
|
+
return /* @__PURE__ */ jsxDEV4("div", {
|
|
132
|
+
"data-panel-group": true,
|
|
133
|
+
"data-persist-key": region.persistKey,
|
|
134
|
+
style: {
|
|
135
|
+
display: "flex",
|
|
136
|
+
flexDirection: direction,
|
|
137
|
+
flex: 1,
|
|
138
|
+
minHeight: 0
|
|
139
|
+
},
|
|
140
|
+
children: region.children.map((child, i) => /* @__PURE__ */ jsxDEV4("div", {
|
|
141
|
+
style: { flex: 1, minWidth: 0, minHeight: 0 },
|
|
142
|
+
children: renderChild(child, ctx)
|
|
143
|
+
}, i, false, undefined, this))
|
|
144
|
+
}, undefined, false, undefined, this);
|
|
145
|
+
},
|
|
146
|
+
async restoreLayout(persistKey) {
|
|
147
|
+
if (typeof localStorage === "undefined")
|
|
148
|
+
return null;
|
|
149
|
+
try {
|
|
150
|
+
const raw = localStorage.getItem(LAYOUT_STORAGE_KEY + persistKey);
|
|
151
|
+
if (!raw)
|
|
152
|
+
return null;
|
|
153
|
+
const parsed = JSON.parse(raw);
|
|
154
|
+
return Array.isArray(parsed) ? parsed : null;
|
|
155
|
+
} catch {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
async saveLayout(persistKey, sizes) {
|
|
160
|
+
if (typeof localStorage === "undefined")
|
|
161
|
+
return;
|
|
162
|
+
try {
|
|
163
|
+
localStorage.setItem(LAYOUT_STORAGE_KEY + persistKey, JSON.stringify(sizes));
|
|
164
|
+
} catch {}
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
// src/telemetry/surface-metrics.ts
|
|
168
|
+
import {
|
|
169
|
+
createCounter,
|
|
170
|
+
createHistogram
|
|
171
|
+
} from "@contractspec/lib.observability/metrics";
|
|
172
|
+
var METER_NAME = "@contractspec/lib.surface-runtime";
|
|
173
|
+
var resolutionDurationMs = createHistogram("bundle_surface_resolution_duration_ms", "Time to resolve bundle spec to surface plan in milliseconds", METER_NAME);
|
|
174
|
+
var patchAcceptanceCounter = createCounter("bundle_surface_patch_acceptance_total", "Total AI patch proposals accepted by user", METER_NAME);
|
|
175
|
+
var patchRejectionCounter = createCounter("bundle_surface_patch_rejection_total", "Total AI patch proposals rejected by user", METER_NAME);
|
|
176
|
+
var policyDenialCounter = createCounter("bundle_surface_policy_denial_total", "Total policy denials (actions denied by policy)", METER_NAME);
|
|
177
|
+
var surfaceFallbackCounter = createCounter("bundle_surface_fallback_total", "Total surface or layout fallbacks during resolution", METER_NAME);
|
|
178
|
+
var missingRendererCounter = createCounter("bundle_surface_renderer_missing_total", "Total slots with no renderer for requested node kind", METER_NAME);
|
|
179
|
+
// src/runtime/resolve-preferences.ts
|
|
180
|
+
var DIMENSION_KEYS = [
|
|
181
|
+
"guidance",
|
|
182
|
+
"density",
|
|
183
|
+
"dataDepth",
|
|
184
|
+
"control",
|
|
185
|
+
"media",
|
|
186
|
+
"pace",
|
|
187
|
+
"narrative"
|
|
188
|
+
];
|
|
189
|
+
var SCOPE_ORDER = [
|
|
190
|
+
"user",
|
|
191
|
+
"workspace-user",
|
|
192
|
+
"bundle",
|
|
193
|
+
"surface",
|
|
194
|
+
"entity",
|
|
195
|
+
"session"
|
|
196
|
+
];
|
|
197
|
+
function mergeByScope(layers, ctx) {
|
|
198
|
+
const merged = { ...ctx.preferences };
|
|
199
|
+
const sourceByDimension = {};
|
|
200
|
+
for (const scope of SCOPE_ORDER) {
|
|
201
|
+
const layer = layers[scope];
|
|
202
|
+
if (!layer)
|
|
203
|
+
continue;
|
|
204
|
+
for (const dim of DIMENSION_KEYS) {
|
|
205
|
+
const val = layer[dim];
|
|
206
|
+
if (val !== undefined) {
|
|
207
|
+
merged[dim] = val;
|
|
208
|
+
sourceByDimension[dim] = scope;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
for (const dim of DIMENSION_KEYS) {
|
|
213
|
+
if (sourceByDimension[dim] === undefined) {
|
|
214
|
+
sourceByDimension[dim] = "session";
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return { canonical: merged, sourceByDimension };
|
|
218
|
+
}
|
|
219
|
+
function resolveConstraints(_canonical, _ctx) {
|
|
220
|
+
return { constrained: {}, notes: [] };
|
|
221
|
+
}
|
|
222
|
+
function resolvePreferenceProfile(ctx) {
|
|
223
|
+
const layers = {
|
|
224
|
+
session: ctx.preferences
|
|
225
|
+
};
|
|
226
|
+
const { canonical, sourceByDimension } = mergeByScope(layers, ctx);
|
|
227
|
+
const { constrained, notes } = resolveConstraints(canonical, ctx);
|
|
228
|
+
return {
|
|
229
|
+
canonical,
|
|
230
|
+
sourceByDimension,
|
|
231
|
+
constrained,
|
|
232
|
+
notes
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// src/spec/validate-surface-patch.ts
|
|
237
|
+
var VALID_OPS = [
|
|
238
|
+
"insert-node",
|
|
239
|
+
"replace-node",
|
|
240
|
+
"remove-node",
|
|
241
|
+
"move-node",
|
|
242
|
+
"resize-panel",
|
|
243
|
+
"set-layout",
|
|
244
|
+
"reveal-field",
|
|
245
|
+
"hide-field",
|
|
246
|
+
"promote-action",
|
|
247
|
+
"set-focus"
|
|
248
|
+
];
|
|
249
|
+
var VALID_NODE_KINDS = [
|
|
250
|
+
"metric-strip",
|
|
251
|
+
"data-view",
|
|
252
|
+
"entity-card",
|
|
253
|
+
"entity-header",
|
|
254
|
+
"entity-summary",
|
|
255
|
+
"entity-section",
|
|
256
|
+
"entity-field",
|
|
257
|
+
"entity-activity",
|
|
258
|
+
"entity-relations",
|
|
259
|
+
"entity-timeline",
|
|
260
|
+
"entity-comments",
|
|
261
|
+
"entity-attachments",
|
|
262
|
+
"entity-view-switcher",
|
|
263
|
+
"entity-automation-panel",
|
|
264
|
+
"rich-doc",
|
|
265
|
+
"chat-thread",
|
|
266
|
+
"assistant-panel",
|
|
267
|
+
"action-bar",
|
|
268
|
+
"timeline",
|
|
269
|
+
"board",
|
|
270
|
+
"table",
|
|
271
|
+
"calendar",
|
|
272
|
+
"form",
|
|
273
|
+
"chart",
|
|
274
|
+
"relation-graph",
|
|
275
|
+
"custom-widget"
|
|
276
|
+
];
|
|
277
|
+
function validateSurfaceNode(node, path) {
|
|
278
|
+
if (!node.nodeId || typeof node.nodeId !== "string") {
|
|
279
|
+
throw new Error(`${path}: nodeId must be a non-empty string`);
|
|
280
|
+
}
|
|
281
|
+
if (!node.kind || !VALID_NODE_KINDS.includes(node.kind)) {
|
|
282
|
+
throw new Error(`${path}: kind must be one of ${VALID_NODE_KINDS.join(", ")}`);
|
|
283
|
+
}
|
|
284
|
+
if (node.children) {
|
|
285
|
+
for (let i = 0;i < node.children.length; i++) {
|
|
286
|
+
const child = node.children[i];
|
|
287
|
+
if (child)
|
|
288
|
+
validateSurfaceNode(child, `${path}.children[${i}]`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function validateSurfacePatchOp(op, index) {
|
|
293
|
+
const path = `ops[${index}]`;
|
|
294
|
+
if (!op || typeof op !== "object" || !("op" in op)) {
|
|
295
|
+
throw new Error(`${path}: must be an object with op field`);
|
|
296
|
+
}
|
|
297
|
+
const opType = op.op;
|
|
298
|
+
if (!VALID_OPS.includes(opType)) {
|
|
299
|
+
throw new Error(`${path}: op must be one of ${VALID_OPS.join(", ")}`);
|
|
300
|
+
}
|
|
301
|
+
switch (op.op) {
|
|
302
|
+
case "insert-node":
|
|
303
|
+
if (!op.slotId || typeof op.slotId !== "string") {
|
|
304
|
+
throw new Error(`${path}: insert-node requires slotId string`);
|
|
305
|
+
}
|
|
306
|
+
if (!op.node) {
|
|
307
|
+
throw new Error(`${path}: insert-node requires node`);
|
|
308
|
+
}
|
|
309
|
+
validateSurfaceNode(op.node, `${path}.node`);
|
|
310
|
+
if (op.index !== undefined && typeof op.index !== "number") {
|
|
311
|
+
throw new Error(`${path}: insert-node index must be number if present`);
|
|
312
|
+
}
|
|
313
|
+
break;
|
|
314
|
+
case "replace-node":
|
|
315
|
+
if (!op.nodeId || typeof op.nodeId !== "string") {
|
|
316
|
+
throw new Error(`${path}: replace-node requires nodeId string`);
|
|
317
|
+
}
|
|
318
|
+
if (!op.node) {
|
|
319
|
+
throw new Error(`${path}: replace-node requires node`);
|
|
320
|
+
}
|
|
321
|
+
validateSurfaceNode(op.node, `${path}.node`);
|
|
322
|
+
break;
|
|
323
|
+
case "remove-node":
|
|
324
|
+
if (!op.nodeId || typeof op.nodeId !== "string") {
|
|
325
|
+
throw new Error(`${path}: remove-node requires nodeId string`);
|
|
326
|
+
}
|
|
327
|
+
break;
|
|
328
|
+
case "move-node":
|
|
329
|
+
if (!op.nodeId || typeof op.nodeId !== "string") {
|
|
330
|
+
throw new Error(`${path}: move-node requires nodeId string`);
|
|
331
|
+
}
|
|
332
|
+
if (!op.toSlotId || typeof op.toSlotId !== "string") {
|
|
333
|
+
throw new Error(`${path}: move-node requires toSlotId string`);
|
|
334
|
+
}
|
|
335
|
+
if (op.index !== undefined && typeof op.index !== "number") {
|
|
336
|
+
throw new Error(`${path}: move-node index must be number if present`);
|
|
337
|
+
}
|
|
338
|
+
break;
|
|
339
|
+
case "resize-panel":
|
|
340
|
+
if (!op.persistKey || typeof op.persistKey !== "string") {
|
|
341
|
+
throw new Error(`${path}: resize-panel requires persistKey string`);
|
|
342
|
+
}
|
|
343
|
+
if (!Array.isArray(op.sizes) || op.sizes.some((s) => typeof s !== "number")) {
|
|
344
|
+
throw new Error(`${path}: resize-panel requires sizes number[]`);
|
|
345
|
+
}
|
|
346
|
+
break;
|
|
347
|
+
case "set-layout":
|
|
348
|
+
if (!op.layoutId || typeof op.layoutId !== "string") {
|
|
349
|
+
throw new Error(`${path}: set-layout requires layoutId string`);
|
|
350
|
+
}
|
|
351
|
+
break;
|
|
352
|
+
case "reveal-field":
|
|
353
|
+
case "hide-field":
|
|
354
|
+
if (!op.fieldId || typeof op.fieldId !== "string") {
|
|
355
|
+
throw new Error(`${path}: ${op.op} requires fieldId string`);
|
|
356
|
+
}
|
|
357
|
+
break;
|
|
358
|
+
case "promote-action": {
|
|
359
|
+
if (!op.actionId || typeof op.actionId !== "string") {
|
|
360
|
+
throw new Error(`${path}: promote-action requires actionId string`);
|
|
361
|
+
}
|
|
362
|
+
const validPlacements = ["header", "inline", "context", "assistant"];
|
|
363
|
+
if (!op.placement || !validPlacements.includes(op.placement)) {
|
|
364
|
+
throw new Error(`${path}: promote-action placement must be one of ${validPlacements.join(", ")}`);
|
|
365
|
+
}
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
case "set-focus":
|
|
369
|
+
if (!op.targetId || typeof op.targetId !== "string") {
|
|
370
|
+
throw new Error(`${path}: set-focus requires targetId string`);
|
|
371
|
+
}
|
|
372
|
+
break;
|
|
373
|
+
default:
|
|
374
|
+
throw new Error(`${path}: unknown op "${opType}"`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
function validateSurfacePatch(ops) {
|
|
378
|
+
if (!Array.isArray(ops)) {
|
|
379
|
+
throw new Error("Patch ops must be an array");
|
|
380
|
+
}
|
|
381
|
+
for (let i = 0;i < ops.length; i++) {
|
|
382
|
+
const op = ops[i];
|
|
383
|
+
if (op)
|
|
384
|
+
validateSurfacePatchOp(op, i);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
function validateSurfaceNodeAgainstKinds(node, allowedKinds, path) {
|
|
388
|
+
if (!allowedKinds.includes(node.kind)) {
|
|
389
|
+
throw new Error(`${path}: kind "${node.kind}" not in allowed list [${allowedKinds.join(", ")}]`);
|
|
390
|
+
}
|
|
391
|
+
if (node.children) {
|
|
392
|
+
for (let i = 0;i < node.children.length; i++) {
|
|
393
|
+
const child = node.children[i];
|
|
394
|
+
if (child)
|
|
395
|
+
validateSurfaceNodeAgainstKinds(child, allowedKinds, `${path}.children[${i}]`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
function validatePatchProposal(ops, constraints) {
|
|
400
|
+
if (!Array.isArray(ops)) {
|
|
401
|
+
throw new Error("Patch ops must be an array");
|
|
402
|
+
}
|
|
403
|
+
const { allowedOps, allowedSlots, allowedNodeKinds } = constraints;
|
|
404
|
+
for (let i = 0;i < ops.length; i++) {
|
|
405
|
+
const op = ops[i];
|
|
406
|
+
if (!op)
|
|
407
|
+
continue;
|
|
408
|
+
const path = `ops[${i}]`;
|
|
409
|
+
if (!allowedOps.includes(op.op)) {
|
|
410
|
+
throw new Error(`${path}: op "${op.op}" not in allowed list [${allowedOps.join(", ")}]`);
|
|
411
|
+
}
|
|
412
|
+
switch (op.op) {
|
|
413
|
+
case "insert-node":
|
|
414
|
+
if (!allowedSlots.includes(op.slotId)) {
|
|
415
|
+
throw new Error(`${path}: slotId "${op.slotId}" not in allowed slots [${allowedSlots.join(", ")}]`);
|
|
416
|
+
}
|
|
417
|
+
if (op.node) {
|
|
418
|
+
validateSurfaceNodeAgainstKinds(op.node, allowedNodeKinds, `${path}.node`);
|
|
419
|
+
}
|
|
420
|
+
break;
|
|
421
|
+
case "move-node":
|
|
422
|
+
if (!allowedSlots.includes(op.toSlotId)) {
|
|
423
|
+
throw new Error(`${path}: toSlotId "${op.toSlotId}" not in allowed slots [${allowedSlots.join(", ")}]`);
|
|
424
|
+
}
|
|
425
|
+
break;
|
|
426
|
+
case "replace-node":
|
|
427
|
+
if (op.node) {
|
|
428
|
+
validateSurfaceNodeAgainstKinds(op.node, allowedNodeKinds, `${path}.node`);
|
|
429
|
+
}
|
|
430
|
+
break;
|
|
431
|
+
default:
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
validateSurfacePatch(ops);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// src/runtime/apply-surface-patch.ts
|
|
439
|
+
import { Logger } from "@contractspec/lib.observability";
|
|
440
|
+
var logger = new Logger("@contractspec/lib.surface-runtime");
|
|
441
|
+
function findNode(nodes, nodeId) {
|
|
442
|
+
for (const n of nodes) {
|
|
443
|
+
if (n.nodeId === nodeId)
|
|
444
|
+
return n;
|
|
445
|
+
if (n.children) {
|
|
446
|
+
const found = findNode(n.children, nodeId);
|
|
447
|
+
if (found)
|
|
448
|
+
return found;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
function collectNodeIds(nodes) {
|
|
454
|
+
const ids = new Set;
|
|
455
|
+
for (const n of nodes) {
|
|
456
|
+
ids.add(n.nodeId);
|
|
457
|
+
if (n.children)
|
|
458
|
+
collectNodeIds(n.children).forEach((id) => ids.add(id));
|
|
459
|
+
}
|
|
460
|
+
return ids;
|
|
461
|
+
}
|
|
462
|
+
function validateOp(op, nodeIds) {
|
|
463
|
+
if (op.op === "remove-node" || op.op === "replace-node" || op.op === "move-node") {
|
|
464
|
+
if (!nodeIds.has(op.nodeId)) {
|
|
465
|
+
throw new Error(`Patch op references unknown nodeId: ${op.nodeId}`);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
if (op.op === "insert-node" && !op.node?.nodeId) {
|
|
469
|
+
throw new Error("insert-node requires node with nodeId");
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
function produceInverse(op, plan) {
|
|
473
|
+
switch (op.op) {
|
|
474
|
+
case "insert-node":
|
|
475
|
+
return op.node ? { op: "remove-node", nodeId: op.node.nodeId } : null;
|
|
476
|
+
case "remove-node": {
|
|
477
|
+
const node = findNode(plan.nodes, op.nodeId);
|
|
478
|
+
return node ? { op: "insert-node", slotId: "primary", node } : null;
|
|
479
|
+
}
|
|
480
|
+
case "replace-node": {
|
|
481
|
+
const prev = findNode(plan.nodes, op.nodeId);
|
|
482
|
+
return prev ? { op: "replace-node", nodeId: op.nodeId, node: prev } : null;
|
|
483
|
+
}
|
|
484
|
+
case "set-layout":
|
|
485
|
+
return { op: "set-layout", layoutId: plan.layoutId };
|
|
486
|
+
case "reveal-field":
|
|
487
|
+
return { op: "hide-field", fieldId: op.fieldId };
|
|
488
|
+
case "hide-field":
|
|
489
|
+
return { op: "reveal-field", fieldId: op.fieldId };
|
|
490
|
+
case "move-node":
|
|
491
|
+
case "resize-panel":
|
|
492
|
+
case "set-focus":
|
|
493
|
+
case "promote-action":
|
|
494
|
+
return null;
|
|
495
|
+
default:
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
function applySurfacePatch(plan, ops) {
|
|
500
|
+
if (ops.length === 0)
|
|
501
|
+
return { plan, inverseOps: [] };
|
|
502
|
+
validateSurfacePatch(ops);
|
|
503
|
+
const nodeIds = collectNodeIds(plan.nodes);
|
|
504
|
+
for (const op of ops) {
|
|
505
|
+
validateOp(op, nodeIds);
|
|
506
|
+
}
|
|
507
|
+
let nextNodes = [...plan.nodes];
|
|
508
|
+
let nextLayoutId = plan.layoutId;
|
|
509
|
+
const inverseOps = [];
|
|
510
|
+
for (const op of ops) {
|
|
511
|
+
const inv = produceInverse(op, {
|
|
512
|
+
...plan,
|
|
513
|
+
nodes: nextNodes,
|
|
514
|
+
layoutId: nextLayoutId
|
|
515
|
+
});
|
|
516
|
+
if (inv)
|
|
517
|
+
inverseOps.unshift(inv);
|
|
518
|
+
switch (op.op) {
|
|
519
|
+
case "insert-node":
|
|
520
|
+
if (op.node)
|
|
521
|
+
nextNodes = [...nextNodes, op.node];
|
|
522
|
+
break;
|
|
523
|
+
case "remove-node":
|
|
524
|
+
nextNodes = nextNodes.filter((n) => n.nodeId !== op.nodeId);
|
|
525
|
+
break;
|
|
526
|
+
case "replace-node": {
|
|
527
|
+
const replace = (nodes) => nodes.map((n) => n.nodeId === op.nodeId ? op.node : { ...n, children: n.children ? replace(n.children) : undefined });
|
|
528
|
+
nextNodes = replace(nextNodes);
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
case "set-layout":
|
|
532
|
+
nextLayoutId = op.layoutId;
|
|
533
|
+
break;
|
|
534
|
+
case "move-node":
|
|
535
|
+
case "resize-panel":
|
|
536
|
+
case "set-focus":
|
|
537
|
+
case "reveal-field":
|
|
538
|
+
case "hide-field":
|
|
539
|
+
case "promote-action":
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
const result = {
|
|
544
|
+
plan: { ...plan, nodes: nextNodes, layoutId: nextLayoutId },
|
|
545
|
+
inverseOps
|
|
546
|
+
};
|
|
547
|
+
logger.info("bundle.surface.patch.applied", {
|
|
548
|
+
bundleKey: plan.bundleKey,
|
|
549
|
+
surfaceId: plan.surfaceId,
|
|
550
|
+
opCount: ops.length,
|
|
551
|
+
opTypes: [...new Set(ops.map((o) => o.op))]
|
|
552
|
+
});
|
|
553
|
+
return result;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// src/runtime/override-store.ts
|
|
557
|
+
function buildOverrideTargetKey(bundleKey, surfaceId, routeId) {
|
|
558
|
+
return routeId ? `${bundleKey}:${routeId}:${surfaceId}` : `${bundleKey}:${surfaceId}`;
|
|
559
|
+
}
|
|
560
|
+
function createOverrideStoreWithApprovalGate(store, options) {
|
|
561
|
+
const { requireApprovalForWorkspacePatches = false, requestApproval } = options;
|
|
562
|
+
return {
|
|
563
|
+
list: store.list.bind(store),
|
|
564
|
+
async save(scope, targetKey, patch, saveOptions) {
|
|
565
|
+
if (requireApprovalForWorkspacePatches && scope === "workspace" && requestApproval) {
|
|
566
|
+
const approved = await requestApproval({ scope, targetKey, patch });
|
|
567
|
+
if (!approved) {
|
|
568
|
+
throw new Error("Workspace overlay save rejected: approval required and not granted");
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
return store.save(scope, targetKey, patch, saveOptions);
|
|
572
|
+
},
|
|
573
|
+
remove: store.remove.bind(store)
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
function createInMemoryOverrideStore() {
|
|
577
|
+
const overrides = new Map;
|
|
578
|
+
function nextId() {
|
|
579
|
+
return `ov_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
580
|
+
}
|
|
581
|
+
return {
|
|
582
|
+
async list(scope, targetKey) {
|
|
583
|
+
return Array.from(overrides.values()).filter((o) => o.scope === scope && o.targetKey === targetKey);
|
|
584
|
+
},
|
|
585
|
+
async save(scope, targetKey, patch, options) {
|
|
586
|
+
const overrideId = options?.overrideId ?? nextId();
|
|
587
|
+
const stored = {
|
|
588
|
+
overrideId,
|
|
589
|
+
scope,
|
|
590
|
+
targetKey,
|
|
591
|
+
patch,
|
|
592
|
+
createdAt: new Date().toISOString(),
|
|
593
|
+
createdBy: options?.createdBy
|
|
594
|
+
};
|
|
595
|
+
overrides.set(overrideId, stored);
|
|
596
|
+
return overrideId;
|
|
597
|
+
},
|
|
598
|
+
async remove(overrideId) {
|
|
599
|
+
overrides.delete(overrideId);
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// src/runtime/resolve-bundle.ts
|
|
605
|
+
import { traceAsync } from "@contractspec/lib.observability/tracing";
|
|
606
|
+
import { Logger as Logger2 } from "@contractspec/lib.observability";
|
|
607
|
+
var logger2 = new Logger2("@contractspec/lib.surface-runtime");
|
|
608
|
+
function getOpTarget(op) {
|
|
609
|
+
switch (op.op) {
|
|
610
|
+
case "hide-field":
|
|
611
|
+
case "reveal-field":
|
|
612
|
+
return `field:${op.fieldId}`;
|
|
613
|
+
case "remove-node":
|
|
614
|
+
case "replace-node":
|
|
615
|
+
case "move-node":
|
|
616
|
+
return `node:${op.nodeId}`;
|
|
617
|
+
case "insert-node":
|
|
618
|
+
return op.slotId ? `slot:${op.slotId}` : null;
|
|
619
|
+
default:
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
function matchRoute(pathPattern, route) {
|
|
624
|
+
if (pathPattern === route)
|
|
625
|
+
return true;
|
|
626
|
+
const patternParts = pathPattern.split("/");
|
|
627
|
+
const routeParts = route.split("/");
|
|
628
|
+
if (patternParts.length !== routeParts.length)
|
|
629
|
+
return false;
|
|
630
|
+
for (let i = 0;i < patternParts.length; i++) {
|
|
631
|
+
const p = patternParts[i];
|
|
632
|
+
const r = routeParts[i];
|
|
633
|
+
if (p?.startsWith(":"))
|
|
634
|
+
continue;
|
|
635
|
+
if (p !== r)
|
|
636
|
+
return false;
|
|
637
|
+
}
|
|
638
|
+
return true;
|
|
639
|
+
}
|
|
640
|
+
function selectSurface(spec, ctx) {
|
|
641
|
+
const route = spec.routes.find((r) => matchRoute(r.path, ctx.route)) ?? spec.routes[0];
|
|
642
|
+
if (!route) {
|
|
643
|
+
throw new Error("No routes declared in bundle spec.");
|
|
644
|
+
}
|
|
645
|
+
const fallbackRoute = !spec.routes.find((r) => matchRoute(r.path, ctx.route));
|
|
646
|
+
let surface = spec.surfaces[route.defaultSurface];
|
|
647
|
+
let fallback;
|
|
648
|
+
if (!surface && route.candidateSurfaces?.length) {
|
|
649
|
+
for (const sid of route.candidateSurfaces) {
|
|
650
|
+
surface = spec.surfaces[sid];
|
|
651
|
+
if (surface) {
|
|
652
|
+
fallback = `surface=${sid}`;
|
|
653
|
+
break;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
if (!surface) {
|
|
658
|
+
const overview = Object.values(spec.surfaces).find((s) => s.kind === "overview");
|
|
659
|
+
if (overview) {
|
|
660
|
+
surface = overview;
|
|
661
|
+
fallback = "surface=overview";
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
if (!surface) {
|
|
665
|
+
throw new Error(`Default surface "${route.defaultSurface}" was not found.`);
|
|
666
|
+
}
|
|
667
|
+
return {
|
|
668
|
+
surface,
|
|
669
|
+
routeId: route.routeId,
|
|
670
|
+
fallback: fallback ?? (fallbackRoute ? "route" : undefined)
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
function selectLayout(surface, ctx, spec) {
|
|
674
|
+
let preferredLayoutId;
|
|
675
|
+
if (ctx.activeViewId && ctx.entity?.type && spec?.entities?.viewKinds) {
|
|
676
|
+
const viewKind = spec.entities.viewKinds[ctx.activeViewId];
|
|
677
|
+
const entityType = spec.entities.entityTypes[ctx.entity.type];
|
|
678
|
+
if (viewKind?.defaultLayoutId && entityType?.supportedViews?.includes(ctx.activeViewId)) {
|
|
679
|
+
preferredLayoutId = viewKind.defaultLayoutId;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
const matching = surface.layouts.filter((candidate) => candidate.when?.(ctx) ?? true);
|
|
683
|
+
let layout;
|
|
684
|
+
if (preferredLayoutId) {
|
|
685
|
+
layout = matching.find((l) => l.layoutId === preferredLayoutId) ?? surface.layouts.find((l) => l.layoutId === preferredLayoutId) ?? matching[matching.length - 1] ?? surface.layouts[0];
|
|
686
|
+
} else {
|
|
687
|
+
layout = matching[matching.length - 1] ?? surface.layouts[0];
|
|
688
|
+
}
|
|
689
|
+
if (!layout) {
|
|
690
|
+
throw new Error(`Surface "${surface.surfaceId}" has no layouts.`);
|
|
691
|
+
}
|
|
692
|
+
const layoutFallback = matching.length === 0 && surface.layouts.length > 0;
|
|
693
|
+
return { layout, layoutFallback: layoutFallback || undefined };
|
|
694
|
+
}
|
|
695
|
+
function resolveDataRecipes(recipes, ctx) {
|
|
696
|
+
const bindings = {};
|
|
697
|
+
for (const r of recipes) {
|
|
698
|
+
if (r.when?.(ctx) ?? true) {
|
|
699
|
+
const key = r.hydrateInto ?? r.recipeId;
|
|
700
|
+
bindings[key] = { recipeId: r.recipeId, source: r.source, _stub: true };
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
return bindings;
|
|
704
|
+
}
|
|
705
|
+
function allowFromDecision(decision) {
|
|
706
|
+
if (!decision)
|
|
707
|
+
return true;
|
|
708
|
+
return decision.effect === "allow" || decision.effect === "allow-session-only";
|
|
709
|
+
}
|
|
710
|
+
function generateResolutionId() {
|
|
711
|
+
return `res_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
712
|
+
}
|
|
713
|
+
var OVERLAY_SCOPE_ORDER = [
|
|
714
|
+
"system",
|
|
715
|
+
"workspace",
|
|
716
|
+
"team",
|
|
717
|
+
"user",
|
|
718
|
+
"session"
|
|
719
|
+
];
|
|
720
|
+
function buildAdaptation(ctx) {
|
|
721
|
+
const profile = resolvePreferenceProfile(ctx);
|
|
722
|
+
const notes = [...profile.notes];
|
|
723
|
+
const constrainedEntries = Object.entries(profile.constrained);
|
|
724
|
+
if (constrainedEntries.length > 0) {
|
|
725
|
+
notes.push(`constrained: ${constrainedEntries.map(([k, v]) => `${k}=${v}`).join(", ")}`);
|
|
726
|
+
}
|
|
727
|
+
return {
|
|
728
|
+
appliedDimensions: profile.canonical,
|
|
729
|
+
notes
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
async function resolveBundle(spec, ctx, options) {
|
|
733
|
+
return traceAsync("surface.resolveBundle", async (span) => {
|
|
734
|
+
const start = performance.now();
|
|
735
|
+
try {
|
|
736
|
+
const plan = await resolveBundleInternal(spec, ctx, options);
|
|
737
|
+
const durationMs = performance.now() - start;
|
|
738
|
+
resolutionDurationMs.record(durationMs, {
|
|
739
|
+
bundleKey: spec.meta.key,
|
|
740
|
+
surfaceId: plan.surfaceId,
|
|
741
|
+
fallback: plan.surfaceId === "_error" ? "error" : plan.audit.reasons.some((r) => r.startsWith("fallback=")) ? "yes" : "no"
|
|
742
|
+
});
|
|
743
|
+
span.setAttribute("bundle.key", spec.meta.key);
|
|
744
|
+
span.setAttribute("surface.id", plan.surfaceId);
|
|
745
|
+
span.setAttribute("resolution.duration_ms", durationMs);
|
|
746
|
+
logger2.info("bundle.surface.resolved", {
|
|
747
|
+
bundleKey: spec.meta.key,
|
|
748
|
+
surfaceId: plan.surfaceId,
|
|
749
|
+
layoutId: plan.layoutId,
|
|
750
|
+
resolutionMs: Math.round(durationMs),
|
|
751
|
+
fallback: plan.audit.reasons.some((r) => r.startsWith("fallback=")),
|
|
752
|
+
nodeCount: plan.nodes.length
|
|
753
|
+
});
|
|
754
|
+
return plan;
|
|
755
|
+
} catch (err) {
|
|
756
|
+
const durationMs = performance.now() - start;
|
|
757
|
+
resolutionDurationMs.record(durationMs, {
|
|
758
|
+
bundleKey: spec.meta.key,
|
|
759
|
+
surfaceId: "_error",
|
|
760
|
+
fallback: "error"
|
|
761
|
+
});
|
|
762
|
+
return createErrorPlan(spec, ctx, err);
|
|
763
|
+
}
|
|
764
|
+
}, "@contractspec/lib.surface-runtime");
|
|
765
|
+
}
|
|
766
|
+
async function resolveBundleInternal(spec, ctx, options) {
|
|
767
|
+
const {
|
|
768
|
+
surface,
|
|
769
|
+
routeId,
|
|
770
|
+
fallback: surfaceFallback
|
|
771
|
+
} = selectSurface(spec, ctx);
|
|
772
|
+
const { layout, layoutFallback } = selectLayout(surface, ctx, spec);
|
|
773
|
+
let bindings = resolveDataRecipes(surface.data, ctx);
|
|
774
|
+
const redactBinding = options?.policy?.redactBinding;
|
|
775
|
+
if (redactBinding) {
|
|
776
|
+
const redacted = {};
|
|
777
|
+
for (const [k, v] of Object.entries(bindings)) {
|
|
778
|
+
redacted[k] = redactBinding(k, ctx) ?? v;
|
|
779
|
+
}
|
|
780
|
+
bindings = redacted;
|
|
781
|
+
}
|
|
782
|
+
let nodes = [];
|
|
783
|
+
const evaluateNode = options?.policy?.evaluateNode;
|
|
784
|
+
if (evaluateNode) {
|
|
785
|
+
nodes = nodes.filter((n) => allowFromDecision(evaluateNode(n, ctx)));
|
|
786
|
+
}
|
|
787
|
+
const reasons = [
|
|
788
|
+
`surface=${surface.surfaceId}`,
|
|
789
|
+
`layout=${layout.layoutId}`,
|
|
790
|
+
`density=${ctx.preferences.density}`,
|
|
791
|
+
`narrative=${ctx.preferences.narrative}`
|
|
792
|
+
];
|
|
793
|
+
if (surfaceFallback) {
|
|
794
|
+
reasons.push(`fallback=${surfaceFallback}`);
|
|
795
|
+
surfaceFallbackCounter.add(1, {
|
|
796
|
+
bundleKey: spec.meta.key,
|
|
797
|
+
type: "surface"
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
if (layoutFallback) {
|
|
801
|
+
reasons.push("fallback=layout");
|
|
802
|
+
surfaceFallbackCounter.add(1, { bundleKey: spec.meta.key, type: "layout" });
|
|
803
|
+
}
|
|
804
|
+
const resolutionId = generateResolutionId();
|
|
805
|
+
const plan = {
|
|
806
|
+
bundleKey: spec.meta.key,
|
|
807
|
+
surfaceId: surface.surfaceId,
|
|
808
|
+
layoutId: layout.layoutId,
|
|
809
|
+
layoutRoot: layout.root,
|
|
810
|
+
nodes,
|
|
811
|
+
actions: surface.actions ?? [],
|
|
812
|
+
commands: surface.commands ?? [],
|
|
813
|
+
bindings,
|
|
814
|
+
adaptation: buildAdaptation(ctx),
|
|
815
|
+
overlays: [],
|
|
816
|
+
ai: {
|
|
817
|
+
plannerId: spec.ai?.plannerId
|
|
818
|
+
},
|
|
819
|
+
audit: {
|
|
820
|
+
resolutionId,
|
|
821
|
+
createdAt: new Date().toISOString(),
|
|
822
|
+
reasons
|
|
823
|
+
},
|
|
824
|
+
locale: ctx.locale
|
|
825
|
+
};
|
|
826
|
+
options?.audit?.emit({
|
|
827
|
+
eventId: `evt_${resolutionId}`,
|
|
828
|
+
at: new Date().toISOString(),
|
|
829
|
+
actorId: ctx.actorId,
|
|
830
|
+
source: "system",
|
|
831
|
+
bundleKey: spec.meta.key,
|
|
832
|
+
surfaceId: surface.surfaceId,
|
|
833
|
+
eventType: "surface.resolved",
|
|
834
|
+
payload: {
|
|
835
|
+
resolutionId,
|
|
836
|
+
layoutId: layout.layoutId,
|
|
837
|
+
reasons,
|
|
838
|
+
nodeCount: nodes.length
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
let finalPlan = plan;
|
|
842
|
+
if (options?.overlayMerge) {
|
|
843
|
+
const { overrideStore, scopes = OVERLAY_SCOPE_ORDER } = options.overlayMerge;
|
|
844
|
+
const targetKey = buildOverrideTargetKey(spec.meta.key, surface.surfaceId, routeId);
|
|
845
|
+
const appliedOverlays = [];
|
|
846
|
+
const conflicts = [];
|
|
847
|
+
const seenTargets = new Map;
|
|
848
|
+
const conflictKeys = new Set;
|
|
849
|
+
for (const scope of scopes) {
|
|
850
|
+
if (scope === "session")
|
|
851
|
+
continue;
|
|
852
|
+
const stored = await overrideStore.list(scope, targetKey);
|
|
853
|
+
for (const ov of stored) {
|
|
854
|
+
if (ov.patch.length === 0)
|
|
855
|
+
continue;
|
|
856
|
+
for (const op of ov.patch) {
|
|
857
|
+
const opTarget = getOpTarget(op);
|
|
858
|
+
if (opTarget) {
|
|
859
|
+
const prev = seenTargets.get(opTarget);
|
|
860
|
+
if (prev && prev.scope !== scope) {
|
|
861
|
+
const ck = [opTarget, prev.scope, scope].sort().join(":");
|
|
862
|
+
if (!conflictKeys.has(ck)) {
|
|
863
|
+
conflictKeys.add(ck);
|
|
864
|
+
conflicts.push({
|
|
865
|
+
targetKey: opTarget,
|
|
866
|
+
scopeA: prev.scope,
|
|
867
|
+
scopeB: scope,
|
|
868
|
+
overlayIdA: prev.overlayId,
|
|
869
|
+
overlayIdB: ov.overrideId
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
seenTargets.set(opTarget, { scope, overlayId: ov.overrideId });
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
try {
|
|
877
|
+
const { plan: next } = applySurfacePatch(finalPlan, ov.patch);
|
|
878
|
+
finalPlan = next;
|
|
879
|
+
appliedOverlays.push({
|
|
880
|
+
overlayId: ov.overrideId,
|
|
881
|
+
scope,
|
|
882
|
+
appliedOps: ov.patch.length
|
|
883
|
+
});
|
|
884
|
+
} catch (err) {
|
|
885
|
+
logger2.warn("bundle.overlay.apply.failed", {
|
|
886
|
+
overlayId: ov.overrideId,
|
|
887
|
+
scope,
|
|
888
|
+
error: err instanceof Error ? err.message : String(err)
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
finalPlan = {
|
|
894
|
+
...finalPlan,
|
|
895
|
+
overlays: appliedOverlays,
|
|
896
|
+
overlayConflicts: conflicts.length > 0 ? conflicts : undefined
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
return finalPlan;
|
|
900
|
+
}
|
|
901
|
+
function createErrorPlan(spec, ctx, err) {
|
|
902
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
903
|
+
const errorLayoutRoot = {
|
|
904
|
+
type: "stack",
|
|
905
|
+
direction: "vertical",
|
|
906
|
+
children: [{ type: "slot", slotId: "primary" }]
|
|
907
|
+
};
|
|
908
|
+
return {
|
|
909
|
+
bundleKey: spec.meta.key,
|
|
910
|
+
surfaceId: "_error",
|
|
911
|
+
layoutId: "_error",
|
|
912
|
+
layoutRoot: errorLayoutRoot,
|
|
913
|
+
nodes: [],
|
|
914
|
+
actions: [],
|
|
915
|
+
commands: [],
|
|
916
|
+
bindings: {},
|
|
917
|
+
adaptation: {
|
|
918
|
+
appliedDimensions: ctx.preferences,
|
|
919
|
+
notes: [`resolution failed: ${message}`]
|
|
920
|
+
},
|
|
921
|
+
overlays: [],
|
|
922
|
+
ai: {},
|
|
923
|
+
audit: {
|
|
924
|
+
resolutionId: generateResolutionId(),
|
|
925
|
+
createdAt: new Date().toISOString(),
|
|
926
|
+
reasons: ["fallback=error", `error=${message}`]
|
|
927
|
+
},
|
|
928
|
+
locale: ctx.locale
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// src/runtime/build-context.ts
|
|
933
|
+
var DEFAULT_PREFERENCES = {
|
|
934
|
+
guidance: "hints",
|
|
935
|
+
density: "standard",
|
|
936
|
+
dataDepth: "detailed",
|
|
937
|
+
control: "standard",
|
|
938
|
+
media: "text",
|
|
939
|
+
pace: "balanced",
|
|
940
|
+
narrative: "top-down"
|
|
941
|
+
};
|
|
942
|
+
function buildContext(params) {
|
|
943
|
+
const {
|
|
944
|
+
route,
|
|
945
|
+
params: routeParams = {},
|
|
946
|
+
query = {},
|
|
947
|
+
tenantId = "default",
|
|
948
|
+
workspaceId,
|
|
949
|
+
actorId,
|
|
950
|
+
device = "desktop",
|
|
951
|
+
preferences: partialPrefs = {},
|
|
952
|
+
capabilities = [],
|
|
953
|
+
featureFlags = [],
|
|
954
|
+
mode,
|
|
955
|
+
locale,
|
|
956
|
+
timezone,
|
|
957
|
+
entity,
|
|
958
|
+
conversation,
|
|
959
|
+
activeViewId
|
|
960
|
+
} = params;
|
|
961
|
+
const preferences = {
|
|
962
|
+
...DEFAULT_PREFERENCES,
|
|
963
|
+
...partialPrefs
|
|
964
|
+
};
|
|
965
|
+
return {
|
|
966
|
+
tenantId,
|
|
967
|
+
workspaceId,
|
|
968
|
+
actorId,
|
|
969
|
+
route,
|
|
970
|
+
params: routeParams,
|
|
971
|
+
query,
|
|
972
|
+
device,
|
|
973
|
+
mode,
|
|
974
|
+
locale,
|
|
975
|
+
timezone,
|
|
976
|
+
entity,
|
|
977
|
+
conversation,
|
|
978
|
+
activeViewId,
|
|
979
|
+
preferences,
|
|
980
|
+
capabilities,
|
|
981
|
+
featureFlags
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
// src/spec/validate-bundle.ts
|
|
985
|
+
var KNOWN_NODE_KIND_RENDERERS = new Set([
|
|
986
|
+
"entity-section",
|
|
987
|
+
"entity-field",
|
|
988
|
+
"action-bar",
|
|
989
|
+
"table",
|
|
990
|
+
"timeline",
|
|
991
|
+
"rich-doc",
|
|
992
|
+
"chat-thread",
|
|
993
|
+
"assistant-panel",
|
|
994
|
+
"entity-card",
|
|
995
|
+
"entity-header",
|
|
996
|
+
"entity-summary",
|
|
997
|
+
"entity-activity",
|
|
998
|
+
"entity-relations",
|
|
999
|
+
"relation-graph",
|
|
1000
|
+
"custom-widget"
|
|
1001
|
+
]);
|
|
1002
|
+
function collectSlotIdsFromRegion(node) {
|
|
1003
|
+
const ids = [];
|
|
1004
|
+
if (node.type === "slot") {
|
|
1005
|
+
ids.push(node.slotId);
|
|
1006
|
+
}
|
|
1007
|
+
if (node.type === "panel-group" || node.type === "stack") {
|
|
1008
|
+
for (const child of node.children) {
|
|
1009
|
+
ids.push(...collectSlotIdsFromRegion(child));
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
if (node.type === "tabs") {
|
|
1013
|
+
for (const tab of node.tabs) {
|
|
1014
|
+
ids.push(...collectSlotIdsFromRegion(tab.child));
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
if (node.type === "floating") {
|
|
1018
|
+
ids.push(node.anchorSlotId);
|
|
1019
|
+
ids.push(...collectSlotIdsFromRegion(node.child));
|
|
1020
|
+
}
|
|
1021
|
+
return ids;
|
|
1022
|
+
}
|
|
1023
|
+
function validateLayoutSlots(surface) {
|
|
1024
|
+
const declaredSlotIds = new Set(surface.slots.map((s) => s.slotId));
|
|
1025
|
+
for (const layout of surface.layouts) {
|
|
1026
|
+
const layoutSlotIds = collectSlotIdsFromRegion(layout.root);
|
|
1027
|
+
for (const slotId of layoutSlotIds) {
|
|
1028
|
+
if (!declaredSlotIds.has(slotId)) {
|
|
1029
|
+
throw new Error(`Surface "${surface.surfaceId}" layout "${layout.layoutId}" references undeclared slot "${slotId}". Declared slots: ${[...declaredSlotIds].join(", ")}`);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
function validateBundleNodeKinds(surface) {
|
|
1035
|
+
const warnings = [];
|
|
1036
|
+
for (const slot of surface.slots) {
|
|
1037
|
+
for (const kind of slot.accepts) {
|
|
1038
|
+
if (!KNOWN_NODE_KIND_RENDERERS.has(kind)) {
|
|
1039
|
+
warnings.push(`Surface "${surface.surfaceId}" slot "${slot.slotId}" accepts "${kind}" which has no dedicated renderer (generic fallback used)`);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
return { warnings };
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
// src/spec/define-module-bundle.ts
|
|
1047
|
+
function defineModuleBundle(spec) {
|
|
1048
|
+
if (!spec.meta?.key || !spec.meta?.version || !spec.meta?.title) {
|
|
1049
|
+
throw new Error("ModuleBundleSpec must have meta.key, meta.version, and meta.title");
|
|
1050
|
+
}
|
|
1051
|
+
if (!spec.routes?.length) {
|
|
1052
|
+
throw new Error("ModuleBundleSpec must have at least one route");
|
|
1053
|
+
}
|
|
1054
|
+
if (!spec.surfaces || Object.keys(spec.surfaces).length === 0) {
|
|
1055
|
+
throw new Error("ModuleBundleSpec must have at least one surface");
|
|
1056
|
+
}
|
|
1057
|
+
if (spec.entities) {
|
|
1058
|
+
if (!spec.entities.entityTypes || typeof spec.entities.entityTypes !== "object") {
|
|
1059
|
+
throw new Error("ModuleBundleSpec.entities must have entityTypes object when present");
|
|
1060
|
+
}
|
|
1061
|
+
if (!spec.entities.fieldKinds || typeof spec.entities.fieldKinds !== "object") {
|
|
1062
|
+
throw new Error("ModuleBundleSpec.entities must have fieldKinds object when present");
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
const REQUIRED_DIMENSIONS = [
|
|
1066
|
+
"guidance",
|
|
1067
|
+
"density",
|
|
1068
|
+
"dataDepth",
|
|
1069
|
+
"control",
|
|
1070
|
+
"media",
|
|
1071
|
+
"pace",
|
|
1072
|
+
"narrative"
|
|
1073
|
+
];
|
|
1074
|
+
const MIN_DESCRIPTION_LENGTH = 10;
|
|
1075
|
+
for (const surface of Object.values(spec.surfaces)) {
|
|
1076
|
+
if (!surface.verification?.dimensions) {
|
|
1077
|
+
throw new Error(`Surface "${surface.surfaceId}" must have verification.dimensions for all 7 preference dimensions`);
|
|
1078
|
+
}
|
|
1079
|
+
const dims = surface.verification.dimensions;
|
|
1080
|
+
for (const d of REQUIRED_DIMENSIONS) {
|
|
1081
|
+
const val = dims[d];
|
|
1082
|
+
if (!val || typeof val !== "string") {
|
|
1083
|
+
throw new Error(`Surface "${surface.surfaceId}" verification.dimensions.${d} must be a non-empty string`);
|
|
1084
|
+
}
|
|
1085
|
+
const trimmed = val.trim();
|
|
1086
|
+
if (trimmed.length < MIN_DESCRIPTION_LENGTH) {
|
|
1087
|
+
throw new Error(`Surface "${surface.surfaceId}" verification.dimensions.${d} must be at least ${MIN_DESCRIPTION_LENGTH} characters (got ${trimmed.length})`);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
validateLayoutSlots(surface);
|
|
1091
|
+
}
|
|
1092
|
+
return spec;
|
|
1093
|
+
}
|
|
1094
|
+
// src/i18n/catalogs/en.ts
|
|
1095
|
+
import { defineTranslation } from "@contractspec/lib.contracts-spec/translations";
|
|
1096
|
+
var enMessages = defineTranslation({
|
|
1097
|
+
meta: {
|
|
1098
|
+
key: "surface-runtime.messages",
|
|
1099
|
+
version: "1.0.0",
|
|
1100
|
+
domain: "surface-runtime",
|
|
1101
|
+
description: "User-facing strings for surface-runtime UI components",
|
|
1102
|
+
owners: ["platform"],
|
|
1103
|
+
stability: "experimental"
|
|
1104
|
+
},
|
|
1105
|
+
locale: "en",
|
|
1106
|
+
fallback: "en",
|
|
1107
|
+
messages: {
|
|
1108
|
+
"overlay.conflicts.title": {
|
|
1109
|
+
value: "Overlay conflicts",
|
|
1110
|
+
description: "Title for overlay conflict resolution banner"
|
|
1111
|
+
},
|
|
1112
|
+
"overlay.conflicts.keepScope": {
|
|
1113
|
+
value: "Keep {scope}",
|
|
1114
|
+
description: "Button to keep overlay from scope A or B"
|
|
1115
|
+
},
|
|
1116
|
+
"patch.accept": {
|
|
1117
|
+
value: "Accept",
|
|
1118
|
+
description: "Accept patch proposal button"
|
|
1119
|
+
},
|
|
1120
|
+
"patch.reject": {
|
|
1121
|
+
value: "Reject",
|
|
1122
|
+
description: "Reject patch proposal button"
|
|
1123
|
+
},
|
|
1124
|
+
"patch.addWidget": {
|
|
1125
|
+
value: "Add {title} to {slot}",
|
|
1126
|
+
description: "Insert node proposal summary"
|
|
1127
|
+
},
|
|
1128
|
+
"patch.removeItem": {
|
|
1129
|
+
value: "Remove item",
|
|
1130
|
+
description: "Remove node proposal summary"
|
|
1131
|
+
},
|
|
1132
|
+
"patch.switchLayout": {
|
|
1133
|
+
value: "Switch to {layoutId} layout",
|
|
1134
|
+
description: "Set layout proposal summary"
|
|
1135
|
+
},
|
|
1136
|
+
"patch.showField": {
|
|
1137
|
+
value: "Show field {fieldId}",
|
|
1138
|
+
description: "Reveal field proposal summary"
|
|
1139
|
+
},
|
|
1140
|
+
"patch.hideField": {
|
|
1141
|
+
value: "Hide field {fieldId}",
|
|
1142
|
+
description: "Hide field proposal summary"
|
|
1143
|
+
},
|
|
1144
|
+
"patch.moveTo": {
|
|
1145
|
+
value: "Move to {slot}",
|
|
1146
|
+
description: "Move node proposal summary"
|
|
1147
|
+
},
|
|
1148
|
+
"patch.replaceItem": {
|
|
1149
|
+
value: "Replace item",
|
|
1150
|
+
description: "Replace node proposal summary"
|
|
1151
|
+
},
|
|
1152
|
+
"patch.promote": {
|
|
1153
|
+
value: "Promote {actionId}",
|
|
1154
|
+
description: "Promote action proposal summary"
|
|
1155
|
+
},
|
|
1156
|
+
"patch.changes": {
|
|
1157
|
+
value: "{count} changes",
|
|
1158
|
+
description: "Multiple patch ops summary"
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
});
|
|
1162
|
+
|
|
1163
|
+
// src/i18n/catalogs/es.ts
|
|
1164
|
+
import { defineTranslation as defineTranslation2 } from "@contractspec/lib.contracts-spec/translations";
|
|
1165
|
+
var esMessages = defineTranslation2({
|
|
1166
|
+
meta: {
|
|
1167
|
+
key: "surface-runtime.messages",
|
|
1168
|
+
version: "1.0.0",
|
|
1169
|
+
domain: "surface-runtime",
|
|
1170
|
+
description: "User-facing strings for surface-runtime UI components",
|
|
1171
|
+
owners: ["platform"],
|
|
1172
|
+
stability: "experimental"
|
|
1173
|
+
},
|
|
1174
|
+
locale: "es",
|
|
1175
|
+
fallback: "en",
|
|
1176
|
+
messages: {
|
|
1177
|
+
"overlay.conflicts.title": { value: "Conflictos de superposición" },
|
|
1178
|
+
"overlay.conflicts.keepScope": { value: "Mantener {scope}" },
|
|
1179
|
+
"patch.accept": { value: "Aceptar" },
|
|
1180
|
+
"patch.reject": { value: "Rechazar" },
|
|
1181
|
+
"patch.addWidget": { value: "Añadir {title} a {slot}" },
|
|
1182
|
+
"patch.removeItem": { value: "Eliminar elemento" },
|
|
1183
|
+
"patch.switchLayout": { value: "Cambiar a disposición {layoutId}" },
|
|
1184
|
+
"patch.showField": { value: "Mostrar campo {fieldId}" },
|
|
1185
|
+
"patch.hideField": { value: "Ocultar campo {fieldId}" },
|
|
1186
|
+
"patch.moveTo": { value: "Mover a {slot}" },
|
|
1187
|
+
"patch.replaceItem": { value: "Reemplazar elemento" },
|
|
1188
|
+
"patch.promote": { value: "Promover {actionId}" },
|
|
1189
|
+
"patch.changes": { value: "{count} cambios" }
|
|
1190
|
+
}
|
|
1191
|
+
});
|
|
1192
|
+
|
|
1193
|
+
// src/i18n/catalogs/fr.ts
|
|
1194
|
+
import { defineTranslation as defineTranslation3 } from "@contractspec/lib.contracts-spec/translations";
|
|
1195
|
+
var frMessages = defineTranslation3({
|
|
1196
|
+
meta: {
|
|
1197
|
+
key: "surface-runtime.messages",
|
|
1198
|
+
version: "1.0.0",
|
|
1199
|
+
domain: "surface-runtime",
|
|
1200
|
+
description: "User-facing strings for surface-runtime UI components",
|
|
1201
|
+
owners: ["platform"],
|
|
1202
|
+
stability: "experimental"
|
|
1203
|
+
},
|
|
1204
|
+
locale: "fr",
|
|
1205
|
+
fallback: "en",
|
|
1206
|
+
messages: {
|
|
1207
|
+
"overlay.conflicts.title": { value: "Conflits de superposition" },
|
|
1208
|
+
"overlay.conflicts.keepScope": { value: "Conserver {scope}" },
|
|
1209
|
+
"patch.accept": { value: "Accepter" },
|
|
1210
|
+
"patch.reject": { value: "Rejeter" },
|
|
1211
|
+
"patch.addWidget": { value: "Ajouter {title} à {slot}" },
|
|
1212
|
+
"patch.removeItem": { value: "Supprimer l'élément" },
|
|
1213
|
+
"patch.switchLayout": { value: "Passer à la disposition {layoutId}" },
|
|
1214
|
+
"patch.showField": { value: "Afficher le champ {fieldId}" },
|
|
1215
|
+
"patch.hideField": { value: "Masquer le champ {fieldId}" },
|
|
1216
|
+
"patch.moveTo": { value: "Déplacer vers {slot}" },
|
|
1217
|
+
"patch.replaceItem": { value: "Remplacer l'élément" },
|
|
1218
|
+
"patch.promote": { value: "Promouvoir {actionId}" },
|
|
1219
|
+
"patch.changes": { value: "{count} modifications" }
|
|
1220
|
+
}
|
|
1221
|
+
});
|
|
1222
|
+
|
|
1223
|
+
// src/i18n/messages.ts
|
|
1224
|
+
import {
|
|
1225
|
+
createI18nFactory
|
|
1226
|
+
} from "@contractspec/lib.contracts-spec/translations";
|
|
1227
|
+
var factory = createI18nFactory({
|
|
1228
|
+
specKey: "surface-runtime.messages",
|
|
1229
|
+
catalogs: [enMessages, frMessages, esMessages]
|
|
1230
|
+
});
|
|
1231
|
+
var createSurfaceI18n = factory.create;
|
|
1232
|
+
var getDefaultSurfaceI18n = factory.getDefault;
|
|
1233
|
+
|
|
1234
|
+
// src/i18n/keys.ts
|
|
1235
|
+
var SURFACE_KEYS = {
|
|
1236
|
+
"overlay.conflicts.title": "overlay.conflicts.title",
|
|
1237
|
+
"overlay.conflicts.keepScope": "overlay.conflicts.keepScope",
|
|
1238
|
+
"patch.accept": "patch.accept",
|
|
1239
|
+
"patch.reject": "patch.reject",
|
|
1240
|
+
"patch.addWidget": "patch.addWidget",
|
|
1241
|
+
"patch.removeItem": "patch.removeItem",
|
|
1242
|
+
"patch.switchLayout": "patch.switchLayout",
|
|
1243
|
+
"patch.showField": "patch.showField",
|
|
1244
|
+
"patch.hideField": "patch.hideField",
|
|
1245
|
+
"patch.moveTo": "patch.moveTo",
|
|
1246
|
+
"patch.replaceItem": "patch.replaceItem",
|
|
1247
|
+
"patch.promote": "patch.promote",
|
|
1248
|
+
"patch.changes": "patch.changes"
|
|
1249
|
+
};
|
|
1250
|
+
|
|
1251
|
+
// src/i18n/index.ts
|
|
1252
|
+
import {
|
|
1253
|
+
resolveLocale,
|
|
1254
|
+
isSupportedLocale,
|
|
1255
|
+
DEFAULT_LOCALE,
|
|
1256
|
+
SUPPORTED_LOCALES
|
|
1257
|
+
} from "@contractspec/lib.contracts-spec/translations";
|
|
1258
|
+
|
|
1259
|
+
// src/runtime/preference-adapter.ts
|
|
1260
|
+
var defaultPreferenceAdapter = {
|
|
1261
|
+
async resolve(ctx) {
|
|
1262
|
+
return resolvePreferenceProfile(ctx);
|
|
1263
|
+
},
|
|
1264
|
+
async savePreferencePatch(_args) {}
|
|
1265
|
+
};
|
|
1266
|
+
|
|
1267
|
+
// src/runtime/planner-prompt.ts
|
|
1268
|
+
var SAFETY_INSTRUCTIONS = `
|
|
1269
|
+
## Safety instructions (mandatory)
|
|
1270
|
+
- Do NOT emit JSX, HTML, or raw markup.
|
|
1271
|
+
- Do NOT invent node kinds; use only the allowed kinds listed above.
|
|
1272
|
+
- Do NOT call undeclared tools.
|
|
1273
|
+
- Prefer fewer high-confidence patches over many speculative ones.
|
|
1274
|
+
- Explain why a patch helps the user.
|
|
1275
|
+
`;
|
|
1276
|
+
function compilePlannerPrompt(input) {
|
|
1277
|
+
const {
|
|
1278
|
+
bundleMeta,
|
|
1279
|
+
surfaceId,
|
|
1280
|
+
allowedPatchOps,
|
|
1281
|
+
allowedSlots,
|
|
1282
|
+
allowedNodeKinds,
|
|
1283
|
+
actions,
|
|
1284
|
+
preferences,
|
|
1285
|
+
basePrompt,
|
|
1286
|
+
planSummary,
|
|
1287
|
+
entity
|
|
1288
|
+
} = input;
|
|
1289
|
+
const parts = [];
|
|
1290
|
+
parts.push(`# Bundle: ${bundleMeta.title} (${bundleMeta.key}@${bundleMeta.version})`);
|
|
1291
|
+
parts.push(`# Surface: ${surfaceId}`);
|
|
1292
|
+
parts.push("");
|
|
1293
|
+
if (basePrompt) {
|
|
1294
|
+
parts.push("## Base instructions");
|
|
1295
|
+
parts.push(basePrompt);
|
|
1296
|
+
parts.push("");
|
|
1297
|
+
}
|
|
1298
|
+
parts.push("## Allowed patch operations");
|
|
1299
|
+
parts.push(allowedPatchOps.join(", "));
|
|
1300
|
+
parts.push("");
|
|
1301
|
+
parts.push("## Allowed slots");
|
|
1302
|
+
parts.push(allowedSlots.join(", "));
|
|
1303
|
+
parts.push("");
|
|
1304
|
+
parts.push("## Allowed node kinds");
|
|
1305
|
+
parts.push(allowedNodeKinds.join(", "));
|
|
1306
|
+
parts.push("");
|
|
1307
|
+
if (actions.length > 0) {
|
|
1308
|
+
parts.push("## Visible actions");
|
|
1309
|
+
for (const a of actions) {
|
|
1310
|
+
parts.push(`- ${a.actionId}: ${a.title}`);
|
|
1311
|
+
}
|
|
1312
|
+
parts.push("");
|
|
1313
|
+
}
|
|
1314
|
+
parts.push("## Current preference profile");
|
|
1315
|
+
parts.push(`guidance=${preferences.guidance}, density=${preferences.density}, ` + `dataDepth=${preferences.dataDepth}, control=${preferences.control}, ` + `media=${preferences.media}, pace=${preferences.pace}, narrative=${preferences.narrative}`);
|
|
1316
|
+
parts.push("");
|
|
1317
|
+
if (planSummary) {
|
|
1318
|
+
parts.push("## Current plan summary");
|
|
1319
|
+
parts.push(planSummary);
|
|
1320
|
+
parts.push("");
|
|
1321
|
+
}
|
|
1322
|
+
if (entity) {
|
|
1323
|
+
parts.push("## Entity context");
|
|
1324
|
+
parts.push(`${entity.type}: ${entity.id}`);
|
|
1325
|
+
parts.push("");
|
|
1326
|
+
}
|
|
1327
|
+
parts.push(SAFETY_INSTRUCTIONS.trim());
|
|
1328
|
+
return parts.join(`
|
|
1329
|
+
`);
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
// src/runtime/planner-tools.ts
|
|
1333
|
+
var PROPOSE_PATCH_TOOL_SCHEMA = {
|
|
1334
|
+
type: "object",
|
|
1335
|
+
properties: {
|
|
1336
|
+
proposalId: { type: "string", description: "Unique proposal identifier" },
|
|
1337
|
+
ops: {
|
|
1338
|
+
type: "array",
|
|
1339
|
+
description: "Surface patch operations to propose",
|
|
1340
|
+
items: {
|
|
1341
|
+
type: "object",
|
|
1342
|
+
required: ["op"],
|
|
1343
|
+
properties: {
|
|
1344
|
+
op: {
|
|
1345
|
+
type: "string",
|
|
1346
|
+
enum: [
|
|
1347
|
+
"insert-node",
|
|
1348
|
+
"replace-node",
|
|
1349
|
+
"remove-node",
|
|
1350
|
+
"move-node",
|
|
1351
|
+
"resize-panel",
|
|
1352
|
+
"set-layout",
|
|
1353
|
+
"reveal-field",
|
|
1354
|
+
"hide-field",
|
|
1355
|
+
"promote-action",
|
|
1356
|
+
"set-focus"
|
|
1357
|
+
]
|
|
1358
|
+
},
|
|
1359
|
+
slotId: { type: "string" },
|
|
1360
|
+
nodeId: { type: "string" },
|
|
1361
|
+
toSlotId: { type: "string" },
|
|
1362
|
+
index: { type: "number" },
|
|
1363
|
+
node: {
|
|
1364
|
+
type: "object",
|
|
1365
|
+
properties: {
|
|
1366
|
+
nodeId: { type: "string" },
|
|
1367
|
+
kind: { type: "string" },
|
|
1368
|
+
title: { type: "string" },
|
|
1369
|
+
props: { type: "object" },
|
|
1370
|
+
children: { type: "array" }
|
|
1371
|
+
}
|
|
1372
|
+
},
|
|
1373
|
+
persistKey: { type: "string" },
|
|
1374
|
+
sizes: { type: "array", items: { type: "number" } },
|
|
1375
|
+
layoutId: { type: "string" },
|
|
1376
|
+
fieldId: { type: "string" },
|
|
1377
|
+
actionId: { type: "string" },
|
|
1378
|
+
placement: {
|
|
1379
|
+
type: "string",
|
|
1380
|
+
enum: ["header", "inline", "context", "assistant"]
|
|
1381
|
+
},
|
|
1382
|
+
targetId: { type: "string" }
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
},
|
|
1387
|
+
required: ["proposalId", "ops"]
|
|
1388
|
+
};
|
|
1389
|
+
var proposePatchToolConfig = {
|
|
1390
|
+
name: "propose-patch",
|
|
1391
|
+
description: "Propose surface patches (layout changes, node insertions, etc.) for user approval. " + "Only use allowed ops, slots, and node kinds from the planner context.",
|
|
1392
|
+
schema: PROPOSE_PATCH_TOOL_SCHEMA,
|
|
1393
|
+
automationSafe: false,
|
|
1394
|
+
requiresApproval: true
|
|
1395
|
+
};
|
|
1396
|
+
function buildSurfacePatchProposal(proposalId, ops) {
|
|
1397
|
+
return {
|
|
1398
|
+
proposalId,
|
|
1399
|
+
source: "assistant",
|
|
1400
|
+
ops,
|
|
1401
|
+
approvalState: "proposed"
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
// src/runtime/field-renderer-registry.ts
|
|
1406
|
+
var FALLBACK_FIELD_KIND = "text";
|
|
1407
|
+
var CORE_FIELD_KINDS = {
|
|
1408
|
+
text: {
|
|
1409
|
+
fieldKind: "text",
|
|
1410
|
+
viewer: "text-viewer",
|
|
1411
|
+
editor: "text-editor",
|
|
1412
|
+
summaryViewer: "text-summary",
|
|
1413
|
+
tableCell: "text-cell",
|
|
1414
|
+
filters: ["contains", "equals", "startsWith", "endsWith"],
|
|
1415
|
+
validators: ["required", "maxLength", "regex"]
|
|
1416
|
+
},
|
|
1417
|
+
number: {
|
|
1418
|
+
fieldKind: "number",
|
|
1419
|
+
viewer: "number-viewer",
|
|
1420
|
+
editor: "number-editor",
|
|
1421
|
+
summaryViewer: "number-summary",
|
|
1422
|
+
tableCell: "number-cell",
|
|
1423
|
+
filters: ["equals", "gt", "gte", "lt", "lte", "between"],
|
|
1424
|
+
validators: ["required", "min", "max", "integer"]
|
|
1425
|
+
},
|
|
1426
|
+
date: {
|
|
1427
|
+
fieldKind: "date",
|
|
1428
|
+
viewer: "date-viewer",
|
|
1429
|
+
editor: "date-editor",
|
|
1430
|
+
summaryViewer: "date-summary",
|
|
1431
|
+
tableCell: "date-cell",
|
|
1432
|
+
filters: ["equals", "before", "after", "between"],
|
|
1433
|
+
validators: ["required", "min", "max"]
|
|
1434
|
+
},
|
|
1435
|
+
select: {
|
|
1436
|
+
fieldKind: "select",
|
|
1437
|
+
viewer: "select-viewer",
|
|
1438
|
+
editor: "select-editor",
|
|
1439
|
+
summaryViewer: "select-summary",
|
|
1440
|
+
tableCell: "select-cell",
|
|
1441
|
+
filters: ["equals", "in"],
|
|
1442
|
+
validators: ["required", "oneOf"]
|
|
1443
|
+
},
|
|
1444
|
+
checkbox: {
|
|
1445
|
+
fieldKind: "checkbox",
|
|
1446
|
+
viewer: "checkbox-viewer",
|
|
1447
|
+
editor: "checkbox-editor",
|
|
1448
|
+
summaryViewer: "checkbox-summary",
|
|
1449
|
+
tableCell: "checkbox-cell",
|
|
1450
|
+
filters: ["equals"],
|
|
1451
|
+
validators: ["required"]
|
|
1452
|
+
}
|
|
1453
|
+
};
|
|
1454
|
+
var STUB_FIELD_KINDS = {
|
|
1455
|
+
relation: {
|
|
1456
|
+
fieldKind: "relation",
|
|
1457
|
+
viewer: "relation-viewer-stub",
|
|
1458
|
+
summaryViewer: "relation-chip-stub",
|
|
1459
|
+
tableCell: "relation-chip-stub"
|
|
1460
|
+
},
|
|
1461
|
+
rollup: {
|
|
1462
|
+
fieldKind: "rollup",
|
|
1463
|
+
viewer: "rollup-viewer-stub",
|
|
1464
|
+
summaryViewer: "rollup-chip-stub",
|
|
1465
|
+
tableCell: "rollup-cell-stub"
|
|
1466
|
+
},
|
|
1467
|
+
formula: {
|
|
1468
|
+
fieldKind: "formula",
|
|
1469
|
+
viewer: "formula-viewer-stub",
|
|
1470
|
+
summaryViewer: "formula-summary-stub",
|
|
1471
|
+
tableCell: "formula-cell-stub"
|
|
1472
|
+
},
|
|
1473
|
+
people: {
|
|
1474
|
+
fieldKind: "people",
|
|
1475
|
+
viewer: "people-viewer-stub",
|
|
1476
|
+
editor: "people-picker-stub",
|
|
1477
|
+
summaryViewer: "people-chip-stub",
|
|
1478
|
+
tableCell: "people-cell-stub"
|
|
1479
|
+
},
|
|
1480
|
+
options: {
|
|
1481
|
+
fieldKind: "options",
|
|
1482
|
+
viewer: "options-viewer-stub",
|
|
1483
|
+
editor: "options-editor-stub",
|
|
1484
|
+
summaryViewer: "options-chip-stub",
|
|
1485
|
+
tableCell: "options-cell-stub"
|
|
1486
|
+
},
|
|
1487
|
+
instance: {
|
|
1488
|
+
fieldKind: "instance",
|
|
1489
|
+
viewer: "instance-viewer-stub",
|
|
1490
|
+
summaryViewer: "instance-chip-stub",
|
|
1491
|
+
tableCell: "instance-cell-stub"
|
|
1492
|
+
},
|
|
1493
|
+
url: {
|
|
1494
|
+
fieldKind: "url",
|
|
1495
|
+
viewer: "url-viewer",
|
|
1496
|
+
editor: "url-editor",
|
|
1497
|
+
summaryViewer: "url-summary",
|
|
1498
|
+
tableCell: "url-cell",
|
|
1499
|
+
validators: ["required", "regex"]
|
|
1500
|
+
}
|
|
1501
|
+
};
|
|
1502
|
+
function createFieldRendererRegistry() {
|
|
1503
|
+
const all = { ...CORE_FIELD_KINDS, ...STUB_FIELD_KINDS };
|
|
1504
|
+
const fallback = CORE_FIELD_KINDS[FALLBACK_FIELD_KIND];
|
|
1505
|
+
if (!fallback) {
|
|
1506
|
+
throw new Error(`Fallback field kind "${FALLBACK_FIELD_KIND}" not found`);
|
|
1507
|
+
}
|
|
1508
|
+
return {
|
|
1509
|
+
get(kind) {
|
|
1510
|
+
return all[kind];
|
|
1511
|
+
},
|
|
1512
|
+
has(kind) {
|
|
1513
|
+
return kind in all;
|
|
1514
|
+
},
|
|
1515
|
+
getOrFallback(kind) {
|
|
1516
|
+
return all[kind] ?? fallback;
|
|
1517
|
+
}
|
|
1518
|
+
};
|
|
1519
|
+
}
|
|
1520
|
+
function createMutableFieldRendererRegistry() {
|
|
1521
|
+
const base = { ...CORE_FIELD_KINDS, ...STUB_FIELD_KINDS };
|
|
1522
|
+
const custom = new Map;
|
|
1523
|
+
const fallback = CORE_FIELD_KINDS[FALLBACK_FIELD_KIND];
|
|
1524
|
+
if (!fallback) {
|
|
1525
|
+
throw new Error(`Fallback field kind "${FALLBACK_FIELD_KIND}" not found`);
|
|
1526
|
+
}
|
|
1527
|
+
return {
|
|
1528
|
+
get(kind) {
|
|
1529
|
+
return custom.get(kind) ?? base[kind];
|
|
1530
|
+
},
|
|
1531
|
+
has(kind) {
|
|
1532
|
+
return custom.has(kind) || kind in base;
|
|
1533
|
+
},
|
|
1534
|
+
getOrFallback(kind) {
|
|
1535
|
+
return custom.get(kind) ?? base[kind] ?? fallback;
|
|
1536
|
+
},
|
|
1537
|
+
registerFieldRenderer(kind, entry) {
|
|
1538
|
+
if (entry.fieldKind !== kind) {
|
|
1539
|
+
throw new Error(`Field renderer entry fieldKind "${entry.fieldKind}" must match kind "${kind}"`);
|
|
1540
|
+
}
|
|
1541
|
+
custom.set(kind, entry);
|
|
1542
|
+
}
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
// src/runtime/widget-registry.ts
|
|
1547
|
+
function validateTrust(trust) {
|
|
1548
|
+
if (trust === "ephemeral-ai") {
|
|
1549
|
+
throw new Error("Widgets cannot be registered with ephemeral-ai trust. Registration is a code/package concern.");
|
|
1550
|
+
}
|
|
1551
|
+
if (trust !== "core" && trust !== "workspace" && trust !== "signed-plugin") {
|
|
1552
|
+
throw new Error(`Invalid widget trust: ${trust}`);
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
function createWidgetRegistry() {
|
|
1556
|
+
const entries = new Map;
|
|
1557
|
+
return {
|
|
1558
|
+
register(entry) {
|
|
1559
|
+
validateTrust(entry.trust);
|
|
1560
|
+
if (entry.nodeKind !== "custom-widget") {
|
|
1561
|
+
throw new Error(`Widget "${entry.widgetKey}" must have nodeKind "custom-widget"`);
|
|
1562
|
+
}
|
|
1563
|
+
entries.set(entry.widgetKey, entry);
|
|
1564
|
+
},
|
|
1565
|
+
get(widgetKey) {
|
|
1566
|
+
return entries.get(widgetKey);
|
|
1567
|
+
},
|
|
1568
|
+
has(widgetKey) {
|
|
1569
|
+
return entries.has(widgetKey);
|
|
1570
|
+
},
|
|
1571
|
+
list() {
|
|
1572
|
+
return Array.from(entries.values());
|
|
1573
|
+
},
|
|
1574
|
+
listByTrust(trust) {
|
|
1575
|
+
return Array.from(entries.values()).filter((e) => e.trust === trust);
|
|
1576
|
+
}
|
|
1577
|
+
};
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
// src/runtime/extension-registry.ts
|
|
1581
|
+
function createActionRegistry() {
|
|
1582
|
+
const entries = new Map;
|
|
1583
|
+
return {
|
|
1584
|
+
register(entry) {
|
|
1585
|
+
entries.set(entry.actionId, entry);
|
|
1586
|
+
},
|
|
1587
|
+
get(actionId) {
|
|
1588
|
+
return entries.get(actionId);
|
|
1589
|
+
},
|
|
1590
|
+
has(actionId) {
|
|
1591
|
+
return entries.has(actionId);
|
|
1592
|
+
},
|
|
1593
|
+
list() {
|
|
1594
|
+
return Array.from(entries.values());
|
|
1595
|
+
}
|
|
1596
|
+
};
|
|
1597
|
+
}
|
|
1598
|
+
function createCommandRegistry() {
|
|
1599
|
+
const entries = new Map;
|
|
1600
|
+
return {
|
|
1601
|
+
register(entry) {
|
|
1602
|
+
entries.set(entry.commandId, entry);
|
|
1603
|
+
},
|
|
1604
|
+
get(commandId) {
|
|
1605
|
+
return entries.get(commandId);
|
|
1606
|
+
},
|
|
1607
|
+
has(commandId) {
|
|
1608
|
+
return entries.has(commandId);
|
|
1609
|
+
},
|
|
1610
|
+
list() {
|
|
1611
|
+
return Array.from(entries.values());
|
|
1612
|
+
}
|
|
1613
|
+
};
|
|
1614
|
+
}
|
|
1615
|
+
function createBundleExtensionRegistry(options) {
|
|
1616
|
+
const actionRegistry = options.actionRegistry ?? createActionRegistry();
|
|
1617
|
+
const commandRegistry = options.commandRegistry ?? createCommandRegistry();
|
|
1618
|
+
return {
|
|
1619
|
+
registerWidget(entry) {
|
|
1620
|
+
options.widgetRegistry.register(entry);
|
|
1621
|
+
},
|
|
1622
|
+
registerFieldRenderer(kind, entry) {
|
|
1623
|
+
options.fieldRendererRegistry.registerFieldRenderer(kind, entry);
|
|
1624
|
+
},
|
|
1625
|
+
registerAction(entry) {
|
|
1626
|
+
actionRegistry.register(entry);
|
|
1627
|
+
},
|
|
1628
|
+
registerCommand(entry) {
|
|
1629
|
+
commandRegistry.register(entry);
|
|
1630
|
+
}
|
|
1631
|
+
};
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
// src/runtime/overlay-alignment.ts
|
|
1635
|
+
import { applyOverlayModifications } from "@contractspec/lib.overlay-engine/merger";
|
|
1636
|
+
function toOverlayScopeContext(ctx) {
|
|
1637
|
+
return {
|
|
1638
|
+
tenantId: ctx.tenantId,
|
|
1639
|
+
userId: ctx.actorId,
|
|
1640
|
+
device: ctx.device,
|
|
1641
|
+
tags: ctx.featureFlags
|
|
1642
|
+
};
|
|
1643
|
+
}
|
|
1644
|
+
function toOverlayTargetRef(_ctx, target) {
|
|
1645
|
+
return {
|
|
1646
|
+
presentation: target.surfaceId ?? target.routeId,
|
|
1647
|
+
bundleKey: target.bundleKey,
|
|
1648
|
+
surfaceId: target.surfaceId,
|
|
1649
|
+
routeId: target.routeId,
|
|
1650
|
+
entityType: target.entityType
|
|
1651
|
+
};
|
|
1652
|
+
}
|
|
1653
|
+
function toOverlayAppliesTo(ctx, target, scope) {
|
|
1654
|
+
const scopeCtx = toOverlayScopeContext(ctx);
|
|
1655
|
+
return {
|
|
1656
|
+
...scopeCtx,
|
|
1657
|
+
...target,
|
|
1658
|
+
...scope === "user" && ctx.actorId ? { userId: ctx.actorId } : {},
|
|
1659
|
+
...scope === "workspace" && ctx.workspaceId ? { tags: [...scopeCtx.tags ?? [], `workspace:${ctx.workspaceId}`] } : {}
|
|
1660
|
+
};
|
|
1661
|
+
}
|
|
1662
|
+
function toOverlayRenderableField(field) {
|
|
1663
|
+
return {
|
|
1664
|
+
key: field.fieldId,
|
|
1665
|
+
label: field.title,
|
|
1666
|
+
visible: field.visible,
|
|
1667
|
+
required: field.required,
|
|
1668
|
+
order: 0
|
|
1669
|
+
};
|
|
1670
|
+
}
|
|
1671
|
+
function fromOverlayRenderableField(overlay) {
|
|
1672
|
+
return {
|
|
1673
|
+
fieldId: overlay.key,
|
|
1674
|
+
title: overlay.label ?? overlay.key,
|
|
1675
|
+
visible: overlay.visible ?? true,
|
|
1676
|
+
required: overlay.required ?? false
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1679
|
+
function toOverlayRenderable(fields) {
|
|
1680
|
+
return {
|
|
1681
|
+
fields: fields.map(toOverlayRenderableField)
|
|
1682
|
+
};
|
|
1683
|
+
}
|
|
1684
|
+
function mergeOverlayResultIntoFields(fields, merged) {
|
|
1685
|
+
const fieldMap = new Map(fields.map((f) => [f.fieldId, { ...f }]));
|
|
1686
|
+
const result = [];
|
|
1687
|
+
for (const overlay of merged.fields) {
|
|
1688
|
+
const existing = fieldMap.get(overlay.key);
|
|
1689
|
+
if (existing) {
|
|
1690
|
+
result.push({
|
|
1691
|
+
...existing,
|
|
1692
|
+
title: overlay.label ?? existing.title,
|
|
1693
|
+
visible: overlay.visible ?? existing.visible,
|
|
1694
|
+
required: overlay.required ?? existing.required
|
|
1695
|
+
});
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
return result;
|
|
1699
|
+
}
|
|
1700
|
+
function applyEntityFieldOverlays(fields, overlays, options) {
|
|
1701
|
+
if (!overlays.length)
|
|
1702
|
+
return fields;
|
|
1703
|
+
const target = toOverlayRenderable(fields);
|
|
1704
|
+
const merged = applyOverlayModifications(target, overlays, options);
|
|
1705
|
+
return mergeOverlayResultIntoFields(fields, merged);
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
// src/runtime/overlay-signer.ts
|
|
1709
|
+
import {
|
|
1710
|
+
signOverlay,
|
|
1711
|
+
verifyOverlaySignature
|
|
1712
|
+
} from "@contractspec/lib.overlay-engine/signer";
|
|
1713
|
+
function signWorkspaceOverlay(spec, privateKey, options) {
|
|
1714
|
+
return signOverlay(spec, privateKey, options);
|
|
1715
|
+
}
|
|
1716
|
+
function verifyWorkspaceOverlay(overlay) {
|
|
1717
|
+
return verifyOverlaySignature(overlay);
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
// src/runtime/audit-events.ts
|
|
1721
|
+
function generateEventId() {
|
|
1722
|
+
return `evt_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
1723
|
+
}
|
|
1724
|
+
function emitPatchProposed(audit, args) {
|
|
1725
|
+
audit.emit({
|
|
1726
|
+
eventId: generateEventId(),
|
|
1727
|
+
at: new Date().toISOString(),
|
|
1728
|
+
actorId: args.actorId,
|
|
1729
|
+
source: args.proposal.source === "assistant" ? "assistant" : "user",
|
|
1730
|
+
bundleKey: args.bundleKey,
|
|
1731
|
+
surfaceId: args.surfaceId,
|
|
1732
|
+
eventType: "patch.proposed",
|
|
1733
|
+
payload: {
|
|
1734
|
+
proposalId: args.proposal.proposalId,
|
|
1735
|
+
source: args.proposal.source,
|
|
1736
|
+
opsCount: args.proposal.ops.length
|
|
1737
|
+
}
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
function emitPatchApproved(audit, args) {
|
|
1741
|
+
audit.emit({
|
|
1742
|
+
eventId: generateEventId(),
|
|
1743
|
+
at: new Date().toISOString(),
|
|
1744
|
+
actorId: args.actorId,
|
|
1745
|
+
source: "user",
|
|
1746
|
+
bundleKey: args.bundleKey,
|
|
1747
|
+
surfaceId: args.surfaceId,
|
|
1748
|
+
eventType: "patch.approved",
|
|
1749
|
+
payload: {
|
|
1750
|
+
proposalId: args.proposalId,
|
|
1751
|
+
source: args.source,
|
|
1752
|
+
opsCount: args.opsCount,
|
|
1753
|
+
reason: args.reason
|
|
1754
|
+
}
|
|
1755
|
+
});
|
|
1756
|
+
}
|
|
1757
|
+
function emitPatchRejected(audit, args) {
|
|
1758
|
+
audit.emit({
|
|
1759
|
+
eventId: generateEventId(),
|
|
1760
|
+
at: new Date().toISOString(),
|
|
1761
|
+
actorId: args.actorId,
|
|
1762
|
+
source: "user",
|
|
1763
|
+
bundleKey: args.bundleKey,
|
|
1764
|
+
surfaceId: args.surfaceId,
|
|
1765
|
+
eventType: "patch.rejected",
|
|
1766
|
+
payload: {
|
|
1767
|
+
proposalId: args.proposalId,
|
|
1768
|
+
source: args.source,
|
|
1769
|
+
opsCount: args.opsCount,
|
|
1770
|
+
reason: args.reason
|
|
1771
|
+
}
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
function emitOverlaySaved(audit, args) {
|
|
1775
|
+
audit.emit({
|
|
1776
|
+
eventId: generateEventId(),
|
|
1777
|
+
at: new Date().toISOString(),
|
|
1778
|
+
actorId: args.actorId,
|
|
1779
|
+
source: "user",
|
|
1780
|
+
bundleKey: args.bundleKey,
|
|
1781
|
+
eventType: "overlay.saved",
|
|
1782
|
+
payload: {
|
|
1783
|
+
overlayId: args.overlayId,
|
|
1784
|
+
scope: args.scope,
|
|
1785
|
+
opsCount: args.opsCount
|
|
1786
|
+
}
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1789
|
+
function emitOverlayApplied(audit, args) {
|
|
1790
|
+
audit.emit({
|
|
1791
|
+
eventId: generateEventId(),
|
|
1792
|
+
at: new Date().toISOString(),
|
|
1793
|
+
source: "system",
|
|
1794
|
+
bundleKey: args.bundleKey,
|
|
1795
|
+
surfaceId: args.surfaceId,
|
|
1796
|
+
eventType: "overlay.applied",
|
|
1797
|
+
payload: {
|
|
1798
|
+
overlayId: args.overlayId,
|
|
1799
|
+
opsCount: args.opsCount
|
|
1800
|
+
}
|
|
1801
|
+
});
|
|
1802
|
+
}
|
|
1803
|
+
function emitOverlayFailed(audit, args) {
|
|
1804
|
+
audit.emit({
|
|
1805
|
+
eventId: generateEventId(),
|
|
1806
|
+
at: new Date().toISOString(),
|
|
1807
|
+
source: "system",
|
|
1808
|
+
bundleKey: args.bundleKey,
|
|
1809
|
+
surfaceId: args.surfaceId,
|
|
1810
|
+
eventType: "overlay.failed",
|
|
1811
|
+
payload: {
|
|
1812
|
+
overlayId: args.overlayId,
|
|
1813
|
+
error: args.error
|
|
1814
|
+
}
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1817
|
+
function emitPolicyDenied(audit, args) {
|
|
1818
|
+
audit.emit({
|
|
1819
|
+
eventId: generateEventId(),
|
|
1820
|
+
at: new Date().toISOString(),
|
|
1821
|
+
actorId: args.actorId,
|
|
1822
|
+
source: "policy",
|
|
1823
|
+
bundleKey: args.bundleKey,
|
|
1824
|
+
surfaceId: args.surfaceId,
|
|
1825
|
+
eventType: "policy.denied",
|
|
1826
|
+
payload: {
|
|
1827
|
+
targetId: args.targetId,
|
|
1828
|
+
reason: args.reason
|
|
1829
|
+
}
|
|
1830
|
+
});
|
|
1831
|
+
}
|
|
1832
|
+
function emitPolicyRedacted(audit, args) {
|
|
1833
|
+
audit.emit({
|
|
1834
|
+
eventId: generateEventId(),
|
|
1835
|
+
at: new Date().toISOString(),
|
|
1836
|
+
actorId: args.actorId,
|
|
1837
|
+
source: "policy",
|
|
1838
|
+
bundleKey: args.bundleKey,
|
|
1839
|
+
surfaceId: args.surfaceId,
|
|
1840
|
+
eventType: "policy.redacted",
|
|
1841
|
+
payload: {
|
|
1842
|
+
targetId: args.targetId,
|
|
1843
|
+
redactions: args.redactions
|
|
1844
|
+
}
|
|
1845
|
+
});
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
// src/runtime/rollback.ts
|
|
1849
|
+
function rollbackSurfacePatches(plan, approvalStack, count = 1) {
|
|
1850
|
+
if (count <= 0 || approvalStack.length === 0) {
|
|
1851
|
+
return { plan, revertedCount: 0, remainingStack: approvalStack };
|
|
1852
|
+
}
|
|
1853
|
+
const toRevert = approvalStack.slice(-count);
|
|
1854
|
+
const remaining = approvalStack.slice(0, -count);
|
|
1855
|
+
const currentPlan = plan;
|
|
1856
|
+
const allInverseOps = [];
|
|
1857
|
+
for (const meta of toRevert) {
|
|
1858
|
+
if (meta.inverseOps.length > 0) {
|
|
1859
|
+
allInverseOps.push(...meta.inverseOps);
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
if (allInverseOps.length === 0) {
|
|
1863
|
+
return { plan, revertedCount: 0, remainingStack: approvalStack };
|
|
1864
|
+
}
|
|
1865
|
+
const result = applySurfacePatch(currentPlan, allInverseOps);
|
|
1866
|
+
return {
|
|
1867
|
+
plan: result.plan,
|
|
1868
|
+
revertedCount: toRevert.length,
|
|
1869
|
+
remainingStack: remaining
|
|
1870
|
+
};
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
// src/runtime/policy-eval.ts
|
|
1874
|
+
function evaluatePatchProposalPolicy(ops, ctx, policy) {
|
|
1875
|
+
const evaluate = policy?.evaluatePatchProposal;
|
|
1876
|
+
if (!evaluate)
|
|
1877
|
+
return "allow";
|
|
1878
|
+
return evaluate(ops, ctx);
|
|
1879
|
+
}
|
|
1880
|
+
function evaluateAndEmitPatchPolicy(ops, ctx, args) {
|
|
1881
|
+
const effect = evaluatePatchProposalPolicy(ops, ctx, args.policy);
|
|
1882
|
+
if (effect === "deny") {
|
|
1883
|
+
if (args.audit) {
|
|
1884
|
+
emitPolicyDenied(args.audit, {
|
|
1885
|
+
bundleKey: args.bundleKey,
|
|
1886
|
+
surfaceId: args.surfaceId,
|
|
1887
|
+
targetId: args.targetId ?? "patch-proposal",
|
|
1888
|
+
reason: "Policy denied patch proposal",
|
|
1889
|
+
actorId: ctx.actorId
|
|
1890
|
+
});
|
|
1891
|
+
}
|
|
1892
|
+
return false;
|
|
1893
|
+
}
|
|
1894
|
+
return true;
|
|
1895
|
+
}
|
|
1896
|
+
// src/react/BundleProvider.tsx
|
|
1897
|
+
import { createContext, useContext } from "react";
|
|
1898
|
+
import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
|
|
1899
|
+
"use client";
|
|
1900
|
+
var BundlePlanContext = createContext(null);
|
|
1901
|
+
var PreferencesContext = createContext(null);
|
|
1902
|
+
var EditingContext = createContext(false);
|
|
1903
|
+
function BundleProvider({
|
|
1904
|
+
plan,
|
|
1905
|
+
children,
|
|
1906
|
+
isEditing = false
|
|
1907
|
+
}) {
|
|
1908
|
+
const preferences = plan.adaptation.appliedDimensions;
|
|
1909
|
+
return /* @__PURE__ */ jsxDEV5(BundlePlanContext.Provider, {
|
|
1910
|
+
value: plan,
|
|
1911
|
+
children: /* @__PURE__ */ jsxDEV5(PreferencesContext.Provider, {
|
|
1912
|
+
value: preferences,
|
|
1913
|
+
children: /* @__PURE__ */ jsxDEV5(EditingContext.Provider, {
|
|
1914
|
+
value: isEditing,
|
|
1915
|
+
children
|
|
1916
|
+
}, undefined, false, undefined, this)
|
|
1917
|
+
}, undefined, false, undefined, this)
|
|
1918
|
+
}, undefined, false, undefined, this);
|
|
1919
|
+
}
|
|
1920
|
+
function useIsEditing() {
|
|
1921
|
+
return useContext(EditingContext);
|
|
1922
|
+
}
|
|
1923
|
+
function useBundlePlan() {
|
|
1924
|
+
const value = useContext(BundlePlanContext);
|
|
1925
|
+
if (!value) {
|
|
1926
|
+
throw new Error("useBundlePlan must be used inside BundleProvider.");
|
|
1927
|
+
}
|
|
1928
|
+
return value;
|
|
1929
|
+
}
|
|
1930
|
+
function useBundlePreferences() {
|
|
1931
|
+
const value = useContext(PreferencesContext);
|
|
1932
|
+
if (!value) {
|
|
1933
|
+
throw new Error("useBundlePreferences must be used inside BundleProvider.");
|
|
1934
|
+
}
|
|
1935
|
+
return value;
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
// src/react/RegionRenderer.tsx
|
|
1939
|
+
import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
|
|
1940
|
+
function RegionRenderer({
|
|
1941
|
+
region,
|
|
1942
|
+
ctx,
|
|
1943
|
+
renderSlot
|
|
1944
|
+
}) {
|
|
1945
|
+
const renderChild = (child, childCtx) => /* @__PURE__ */ jsxDEV6(RegionRenderer, {
|
|
1946
|
+
region: child,
|
|
1947
|
+
ctx: childCtx,
|
|
1948
|
+
renderSlot
|
|
1949
|
+
}, undefined, false, undefined, this);
|
|
1950
|
+
switch (region.type) {
|
|
1951
|
+
case "panel-group":
|
|
1952
|
+
return resizablePanelsAdapterStub.renderPanelGroup(region, ctx, renderChild);
|
|
1953
|
+
case "stack": {
|
|
1954
|
+
const direction = region.direction === "horizontal" ? "row" : "column";
|
|
1955
|
+
const gapMap = {
|
|
1956
|
+
none: 0,
|
|
1957
|
+
xs: 4,
|
|
1958
|
+
sm: 8,
|
|
1959
|
+
md: 16,
|
|
1960
|
+
lg: 24
|
|
1961
|
+
};
|
|
1962
|
+
const gap = gapMap[region.gap ?? "md"];
|
|
1963
|
+
return /* @__PURE__ */ jsxDEV6("div", {
|
|
1964
|
+
"data-region": "stack",
|
|
1965
|
+
style: {
|
|
1966
|
+
display: "flex",
|
|
1967
|
+
flexDirection: direction,
|
|
1968
|
+
gap,
|
|
1969
|
+
flex: 1,
|
|
1970
|
+
minHeight: 0
|
|
1971
|
+
},
|
|
1972
|
+
children: region.children.map((child, i) => /* @__PURE__ */ jsxDEV6("div", {
|
|
1973
|
+
style: { flex: 1, minWidth: 0, minHeight: 0 },
|
|
1974
|
+
children: /* @__PURE__ */ jsxDEV6(RegionRenderer, {
|
|
1975
|
+
region: child,
|
|
1976
|
+
ctx,
|
|
1977
|
+
renderSlot
|
|
1978
|
+
}, undefined, false, undefined, this)
|
|
1979
|
+
}, i, false, undefined, this))
|
|
1980
|
+
}, undefined, false, undefined, this);
|
|
1981
|
+
}
|
|
1982
|
+
case "tabs":
|
|
1983
|
+
return /* @__PURE__ */ jsxDEV6("div", {
|
|
1984
|
+
"data-region": "tabs",
|
|
1985
|
+
children: [
|
|
1986
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1987
|
+
role: "tablist",
|
|
1988
|
+
children: region.tabs.map((t) => /* @__PURE__ */ jsxDEV6("button", {
|
|
1989
|
+
type: "button",
|
|
1990
|
+
role: "tab",
|
|
1991
|
+
children: t.title
|
|
1992
|
+
}, t.key, false, undefined, this))
|
|
1993
|
+
}, undefined, false, undefined, this),
|
|
1994
|
+
region.tabs[0] && /* @__PURE__ */ jsxDEV6(RegionRenderer, {
|
|
1995
|
+
region: region.tabs[0].child,
|
|
1996
|
+
ctx,
|
|
1997
|
+
renderSlot
|
|
1998
|
+
}, undefined, false, undefined, this)
|
|
1999
|
+
]
|
|
2000
|
+
}, undefined, true, undefined, this);
|
|
2001
|
+
case "slot":
|
|
2002
|
+
return /* @__PURE__ */ jsxDEV6("div", {
|
|
2003
|
+
"data-slot": region.slotId,
|
|
2004
|
+
children: renderSlot(region.slotId, ctx)
|
|
2005
|
+
}, undefined, false, undefined, this);
|
|
2006
|
+
case "floating":
|
|
2007
|
+
return /* @__PURE__ */ jsxDEV6("div", {
|
|
2008
|
+
"data-floating": true,
|
|
2009
|
+
"data-anchor": region.anchorSlotId,
|
|
2010
|
+
children: /* @__PURE__ */ jsxDEV6(RegionRenderer, {
|
|
2011
|
+
region: region.child,
|
|
2012
|
+
ctx,
|
|
2013
|
+
renderSlot
|
|
2014
|
+
}, undefined, false, undefined, this)
|
|
2015
|
+
}, undefined, false, undefined, this);
|
|
2016
|
+
default:
|
|
2017
|
+
return null;
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
// src/react/SlotRenderer.tsx
|
|
2022
|
+
import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
|
|
2023
|
+
function SlotRenderer({ slotId, ctx, nodes }) {
|
|
2024
|
+
if (nodes.length === 0) {
|
|
2025
|
+
return /* @__PURE__ */ jsxDEV7("div", {
|
|
2026
|
+
"data-slot-placeholder": true,
|
|
2027
|
+
"data-slot-id": slotId
|
|
2028
|
+
}, undefined, false, undefined, this);
|
|
2029
|
+
}
|
|
2030
|
+
return /* @__PURE__ */ jsxDEV7("div", {
|
|
2031
|
+
"data-slot": slotId,
|
|
2032
|
+
children: nodes.map((node) => /* @__PURE__ */ jsxDEV7(SlotNodeRenderer, {
|
|
2033
|
+
node,
|
|
2034
|
+
ctx,
|
|
2035
|
+
slotId
|
|
2036
|
+
}, node.nodeId, false, undefined, this))
|
|
2037
|
+
}, undefined, false, undefined, this);
|
|
2038
|
+
}
|
|
2039
|
+
function SlotNodeRenderer({
|
|
2040
|
+
node,
|
|
2041
|
+
ctx,
|
|
2042
|
+
slotId
|
|
2043
|
+
}) {
|
|
2044
|
+
if (blocknoteAdapterStub.supportsNode(node.kind)) {
|
|
2045
|
+
return blocknoteAdapterStub.renderNode(node, ctx);
|
|
2046
|
+
}
|
|
2047
|
+
if (node.kind === "entity-section") {
|
|
2048
|
+
return /* @__PURE__ */ jsxDEV7("section", {
|
|
2049
|
+
"data-node-id": node.nodeId,
|
|
2050
|
+
"data-kind": "entity-section",
|
|
2051
|
+
children: [
|
|
2052
|
+
/* @__PURE__ */ jsxDEV7("header", {
|
|
2053
|
+
"data-entity-section-header": true,
|
|
2054
|
+
children: node.title ?? "Section"
|
|
2055
|
+
}, undefined, false, undefined, this),
|
|
2056
|
+
/* @__PURE__ */ jsxDEV7("div", {
|
|
2057
|
+
"data-entity-section-content": true,
|
|
2058
|
+
children: node.children?.map((child) => child ? /* @__PURE__ */ jsxDEV7(SlotNodeRenderer, {
|
|
2059
|
+
node: child,
|
|
2060
|
+
ctx,
|
|
2061
|
+
slotId
|
|
2062
|
+
}, child.nodeId, false, undefined, this) : null)
|
|
2063
|
+
}, undefined, false, undefined, this)
|
|
2064
|
+
]
|
|
2065
|
+
}, undefined, true, undefined, this);
|
|
2066
|
+
}
|
|
2067
|
+
if (node.kind === "entity-field") {
|
|
2068
|
+
const fieldId = node.sourceBinding?.fieldId;
|
|
2069
|
+
return /* @__PURE__ */ jsxDEV7("div", {
|
|
2070
|
+
"data-node-id": node.nodeId,
|
|
2071
|
+
"data-kind": "entity-field",
|
|
2072
|
+
"data-field-id": fieldId,
|
|
2073
|
+
children: [
|
|
2074
|
+
/* @__PURE__ */ jsxDEV7("span", {
|
|
2075
|
+
"data-entity-field-label": true,
|
|
2076
|
+
children: node.title ?? fieldId ?? "Field"
|
|
2077
|
+
}, undefined, false, undefined, this),
|
|
2078
|
+
/* @__PURE__ */ jsxDEV7("span", {
|
|
2079
|
+
"data-entity-field-value": true
|
|
2080
|
+
}, undefined, false, undefined, this)
|
|
2081
|
+
]
|
|
2082
|
+
}, undefined, true, undefined, this);
|
|
2083
|
+
}
|
|
2084
|
+
missingRendererCounter.add(1, {
|
|
2085
|
+
nodeKind: node.kind,
|
|
2086
|
+
slotId
|
|
2087
|
+
});
|
|
2088
|
+
return /* @__PURE__ */ jsxDEV7("div", {
|
|
2089
|
+
"data-node-id": node.nodeId,
|
|
2090
|
+
"data-kind": node.kind,
|
|
2091
|
+
"data-renderer-missing": true,
|
|
2092
|
+
children: node.title ?? node.kind
|
|
2093
|
+
}, undefined, false, undefined, this);
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2096
|
+
// src/react/PatchProposalCard.tsx
|
|
2097
|
+
import { useMemo } from "react";
|
|
2098
|
+
import { jsxDEV as jsxDEV8 } from "react/jsx-dev-runtime";
|
|
2099
|
+
"use client";
|
|
2100
|
+
function PatchProposalCard({
|
|
2101
|
+
proposal,
|
|
2102
|
+
onAccept,
|
|
2103
|
+
onReject,
|
|
2104
|
+
locale
|
|
2105
|
+
}) {
|
|
2106
|
+
const i18n = useMemo(() => createSurfaceI18n(locale), [locale]);
|
|
2107
|
+
const firstOp = proposal.ops[0];
|
|
2108
|
+
const summary = proposal.ops.length === 1 && firstOp ? describeOp(firstOp, i18n) : i18n.t("patch.changes", { count: proposal.ops.length });
|
|
2109
|
+
return /* @__PURE__ */ jsxDEV8("div", {
|
|
2110
|
+
"data-proposal-id": proposal.proposalId,
|
|
2111
|
+
style: {
|
|
2112
|
+
padding: "12px",
|
|
2113
|
+
border: "1px solid var(--border, #e5e7eb)",
|
|
2114
|
+
borderRadius: "8px",
|
|
2115
|
+
marginBottom: "8px",
|
|
2116
|
+
backgroundColor: "var(--muted, #f9fafb)"
|
|
2117
|
+
},
|
|
2118
|
+
children: [
|
|
2119
|
+
/* @__PURE__ */ jsxDEV8("div", {
|
|
2120
|
+
style: { marginBottom: "8px", fontSize: "14px" },
|
|
2121
|
+
children: summary
|
|
2122
|
+
}, undefined, false, undefined, this),
|
|
2123
|
+
/* @__PURE__ */ jsxDEV8("div", {
|
|
2124
|
+
style: { display: "flex", gap: "8px" },
|
|
2125
|
+
children: [
|
|
2126
|
+
/* @__PURE__ */ jsxDEV8("button", {
|
|
2127
|
+
type: "button",
|
|
2128
|
+
onClick: () => onAccept(proposal.proposalId),
|
|
2129
|
+
style: {
|
|
2130
|
+
padding: "4px 12px",
|
|
2131
|
+
fontSize: "13px",
|
|
2132
|
+
borderRadius: "4px",
|
|
2133
|
+
border: "none",
|
|
2134
|
+
backgroundColor: "var(--primary, #3b82f6)",
|
|
2135
|
+
color: "white",
|
|
2136
|
+
cursor: "pointer"
|
|
2137
|
+
},
|
|
2138
|
+
children: i18n.t("patch.accept")
|
|
2139
|
+
}, undefined, false, undefined, this),
|
|
2140
|
+
/* @__PURE__ */ jsxDEV8("button", {
|
|
2141
|
+
type: "button",
|
|
2142
|
+
onClick: () => onReject(proposal.proposalId),
|
|
2143
|
+
style: {
|
|
2144
|
+
padding: "4px 12px",
|
|
2145
|
+
fontSize: "13px",
|
|
2146
|
+
borderRadius: "4px",
|
|
2147
|
+
border: "1px solid var(--border, #e5e7eb)",
|
|
2148
|
+
backgroundColor: "transparent",
|
|
2149
|
+
cursor: "pointer"
|
|
2150
|
+
},
|
|
2151
|
+
children: i18n.t("patch.reject")
|
|
2152
|
+
}, undefined, false, undefined, this)
|
|
2153
|
+
]
|
|
2154
|
+
}, undefined, true, undefined, this)
|
|
2155
|
+
]
|
|
2156
|
+
}, undefined, true, undefined, this);
|
|
2157
|
+
}
|
|
2158
|
+
function describeOp(op, i18n) {
|
|
2159
|
+
switch (op.op) {
|
|
2160
|
+
case "insert-node":
|
|
2161
|
+
return i18n.t("patch.addWidget", {
|
|
2162
|
+
title: op.node?.title ?? op.node?.kind ?? "widget",
|
|
2163
|
+
slot: op.slotId ?? "slot"
|
|
2164
|
+
});
|
|
2165
|
+
case "remove-node":
|
|
2166
|
+
return i18n.t("patch.removeItem");
|
|
2167
|
+
case "set-layout":
|
|
2168
|
+
return i18n.t("patch.switchLayout", { layoutId: op.layoutId });
|
|
2169
|
+
case "reveal-field":
|
|
2170
|
+
return i18n.t("patch.showField", { fieldId: op.fieldId });
|
|
2171
|
+
case "hide-field":
|
|
2172
|
+
return i18n.t("patch.hideField", { fieldId: op.fieldId });
|
|
2173
|
+
case "move-node":
|
|
2174
|
+
return i18n.t("patch.moveTo", { slot: op.toSlotId ?? "slot" });
|
|
2175
|
+
case "replace-node":
|
|
2176
|
+
return i18n.t("patch.replaceItem");
|
|
2177
|
+
case "promote-action":
|
|
2178
|
+
return i18n.t("patch.promote", { actionId: op.actionId });
|
|
2179
|
+
default:
|
|
2180
|
+
return `${op.op}`;
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
// src/react/OverlayConflictResolver.tsx
|
|
2185
|
+
import { useMemo as useMemo2 } from "react";
|
|
2186
|
+
import { jsxDEV as jsxDEV9 } from "react/jsx-dev-runtime";
|
|
2187
|
+
"use client";
|
|
2188
|
+
function OverlayConflictResolver({
|
|
2189
|
+
conflicts,
|
|
2190
|
+
onResolve,
|
|
2191
|
+
locale
|
|
2192
|
+
}) {
|
|
2193
|
+
const i18n = useMemo2(() => createSurfaceI18n(locale), [locale]);
|
|
2194
|
+
return /* @__PURE__ */ jsxDEV9("div", {
|
|
2195
|
+
"data-overlay-conflicts": true,
|
|
2196
|
+
style: {
|
|
2197
|
+
padding: "12px",
|
|
2198
|
+
marginBottom: "12px",
|
|
2199
|
+
border: "1px solid var(--destructive, #ef4444)",
|
|
2200
|
+
borderRadius: "8px",
|
|
2201
|
+
backgroundColor: "var(--destructive/10, #fef2f2)"
|
|
2202
|
+
},
|
|
2203
|
+
children: [
|
|
2204
|
+
/* @__PURE__ */ jsxDEV9("div", {
|
|
2205
|
+
style: {
|
|
2206
|
+
fontSize: "14px",
|
|
2207
|
+
fontWeight: 600,
|
|
2208
|
+
marginBottom: "8px",
|
|
2209
|
+
color: "var(--destructive, #ef4444)"
|
|
2210
|
+
},
|
|
2211
|
+
children: i18n.t("overlay.conflicts.title")
|
|
2212
|
+
}, undefined, false, undefined, this),
|
|
2213
|
+
conflicts.map((c) => /* @__PURE__ */ jsxDEV9("div", {
|
|
2214
|
+
style: {
|
|
2215
|
+
padding: "8px",
|
|
2216
|
+
marginBottom: "8px",
|
|
2217
|
+
backgroundColor: "white",
|
|
2218
|
+
borderRadius: "4px",
|
|
2219
|
+
fontSize: "13px"
|
|
2220
|
+
},
|
|
2221
|
+
children: [
|
|
2222
|
+
/* @__PURE__ */ jsxDEV9("span", {
|
|
2223
|
+
children: c.targetKey
|
|
2224
|
+
}, undefined, false, undefined, this),
|
|
2225
|
+
/* @__PURE__ */ jsxDEV9("span", {
|
|
2226
|
+
style: { margin: "0 8px", color: "#9ca3af" },
|
|
2227
|
+
children: [
|
|
2228
|
+
"(",
|
|
2229
|
+
c.scopeA,
|
|
2230
|
+
" vs ",
|
|
2231
|
+
c.scopeB,
|
|
2232
|
+
")"
|
|
2233
|
+
]
|
|
2234
|
+
}, undefined, true, undefined, this),
|
|
2235
|
+
/* @__PURE__ */ jsxDEV9("div", {
|
|
2236
|
+
style: { marginTop: "8px", display: "flex", gap: "8px" },
|
|
2237
|
+
children: [
|
|
2238
|
+
/* @__PURE__ */ jsxDEV9("button", {
|
|
2239
|
+
type: "button",
|
|
2240
|
+
onClick: () => onResolve({ targetKey: c.targetKey, chosenScope: "A" }),
|
|
2241
|
+
style: {
|
|
2242
|
+
padding: "4px 12px",
|
|
2243
|
+
fontSize: "12px",
|
|
2244
|
+
borderRadius: "4px",
|
|
2245
|
+
border: "1px solid #e5e7eb",
|
|
2246
|
+
backgroundColor: "white",
|
|
2247
|
+
cursor: "pointer"
|
|
2248
|
+
},
|
|
2249
|
+
children: i18n.t("overlay.conflicts.keepScope", { scope: c.scopeA })
|
|
2250
|
+
}, undefined, false, undefined, this),
|
|
2251
|
+
/* @__PURE__ */ jsxDEV9("button", {
|
|
2252
|
+
type: "button",
|
|
2253
|
+
onClick: () => onResolve({ targetKey: c.targetKey, chosenScope: "B" }),
|
|
2254
|
+
style: {
|
|
2255
|
+
padding: "4px 12px",
|
|
2256
|
+
fontSize: "12px",
|
|
2257
|
+
borderRadius: "4px",
|
|
2258
|
+
border: "1px solid #e5e7eb",
|
|
2259
|
+
backgroundColor: "white",
|
|
2260
|
+
cursor: "pointer"
|
|
2261
|
+
},
|
|
2262
|
+
children: i18n.t("overlay.conflicts.keepScope", { scope: c.scopeB })
|
|
2263
|
+
}, undefined, false, undefined, this)
|
|
2264
|
+
]
|
|
2265
|
+
}, undefined, true, undefined, this)
|
|
2266
|
+
]
|
|
2267
|
+
}, `${c.targetKey}-${c.overlayIdA}-${c.overlayIdB}`, true, undefined, this))
|
|
2268
|
+
]
|
|
2269
|
+
}, undefined, true, undefined, this);
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
// src/react/BundleRenderer.tsx
|
|
2273
|
+
import { jsxDEV as jsxDEV10, Fragment } from "react/jsx-dev-runtime";
|
|
2274
|
+
"use client";
|
|
2275
|
+
function BundleRenderer({
|
|
2276
|
+
assistantSlotId,
|
|
2277
|
+
assistantSlotContent,
|
|
2278
|
+
onPatchAccept,
|
|
2279
|
+
onPatchReject,
|
|
2280
|
+
onOverlayConflictResolve
|
|
2281
|
+
} = {}) {
|
|
2282
|
+
const plan = useBundlePlan();
|
|
2283
|
+
const motionTokens = motionAdapterStub.getTokens(plan.adaptation.appliedDimensions.pace);
|
|
2284
|
+
const ctx = {
|
|
2285
|
+
plan,
|
|
2286
|
+
bindings: plan.bindings,
|
|
2287
|
+
preferences: plan.adaptation.appliedDimensions
|
|
2288
|
+
};
|
|
2289
|
+
const proposals = plan.ai?.proposals?.filter((p) => p.approvalState === "proposed");
|
|
2290
|
+
const locale = plan.locale;
|
|
2291
|
+
const renderSlot = (slotId, slotCtx) => {
|
|
2292
|
+
if (assistantSlotId && slotId === assistantSlotId) {
|
|
2293
|
+
return /* @__PURE__ */ jsxDEV10(Fragment, {
|
|
2294
|
+
children: [
|
|
2295
|
+
proposals && proposals.length > 0 && onPatchAccept && onPatchReject && /* @__PURE__ */ jsxDEV10("div", {
|
|
2296
|
+
style: { marginBottom: "12px" },
|
|
2297
|
+
children: proposals.map((p) => /* @__PURE__ */ jsxDEV10(PatchProposalCard, {
|
|
2298
|
+
proposal: p,
|
|
2299
|
+
onAccept: onPatchAccept,
|
|
2300
|
+
onReject: onPatchReject,
|
|
2301
|
+
locale
|
|
2302
|
+
}, p.proposalId, false, undefined, this))
|
|
2303
|
+
}, undefined, false, undefined, this),
|
|
2304
|
+
assistantSlotContent
|
|
2305
|
+
]
|
|
2306
|
+
}, undefined, true, undefined, this);
|
|
2307
|
+
}
|
|
2308
|
+
const nodes = getNodesForSlot(plan.nodes, slotId);
|
|
2309
|
+
return /* @__PURE__ */ jsxDEV10(SlotRenderer, {
|
|
2310
|
+
slotId,
|
|
2311
|
+
ctx: slotCtx,
|
|
2312
|
+
nodes
|
|
2313
|
+
}, undefined, false, undefined, this);
|
|
2314
|
+
};
|
|
2315
|
+
const conflicts = plan.overlayConflicts ?? [];
|
|
2316
|
+
return /* @__PURE__ */ jsxDEV10("div", {
|
|
2317
|
+
"data-bundle-key": plan.bundleKey,
|
|
2318
|
+
"data-surface-id": plan.surfaceId,
|
|
2319
|
+
"data-layout-id": plan.layoutId,
|
|
2320
|
+
style: {
|
|
2321
|
+
display: "flex",
|
|
2322
|
+
flexDirection: "column",
|
|
2323
|
+
height: "100%",
|
|
2324
|
+
minHeight: 0,
|
|
2325
|
+
transition: motionTokens.layout ? `all ${motionTokens.durationMs}ms ease` : undefined
|
|
2326
|
+
},
|
|
2327
|
+
children: [
|
|
2328
|
+
conflicts.length > 0 && onOverlayConflictResolve && /* @__PURE__ */ jsxDEV10(OverlayConflictResolver, {
|
|
2329
|
+
conflicts,
|
|
2330
|
+
onResolve: onOverlayConflictResolve,
|
|
2331
|
+
locale
|
|
2332
|
+
}, undefined, false, undefined, this),
|
|
2333
|
+
/* @__PURE__ */ jsxDEV10(RegionRenderer, {
|
|
2334
|
+
region: plan.layoutRoot,
|
|
2335
|
+
ctx,
|
|
2336
|
+
renderSlot
|
|
2337
|
+
}, undefined, false, undefined, this)
|
|
2338
|
+
]
|
|
2339
|
+
}, undefined, true, undefined, this);
|
|
2340
|
+
}
|
|
2341
|
+
function getNodesForSlot(nodes, slotId) {
|
|
2342
|
+
if (slotId === "primary")
|
|
2343
|
+
return nodes;
|
|
2344
|
+
return [];
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
// src/react/WidgetPalette.tsx
|
|
2348
|
+
import { jsxDEV as jsxDEV11 } from "react/jsx-dev-runtime";
|
|
2349
|
+
"use client";
|
|
2350
|
+
function WidgetPalette({
|
|
2351
|
+
registry,
|
|
2352
|
+
allowedNodeKinds,
|
|
2353
|
+
onInsert
|
|
2354
|
+
}) {
|
|
2355
|
+
const widgets = registry.listByTrust("core");
|
|
2356
|
+
const filtered = !allowedNodeKinds || allowedNodeKinds.includes("custom-widget") ? widgets : [];
|
|
2357
|
+
return /* @__PURE__ */ jsxDEV11("div", {
|
|
2358
|
+
"data-widget-palette": true,
|
|
2359
|
+
style: {
|
|
2360
|
+
padding: "12px",
|
|
2361
|
+
minWidth: "180px",
|
|
2362
|
+
borderRight: "1px solid var(--border, #e5e7eb)",
|
|
2363
|
+
backgroundColor: "var(--muted, #f9fafb)"
|
|
2364
|
+
},
|
|
2365
|
+
children: [
|
|
2366
|
+
/* @__PURE__ */ jsxDEV11("div", {
|
|
2367
|
+
style: {
|
|
2368
|
+
fontSize: "12px",
|
|
2369
|
+
fontWeight: 600,
|
|
2370
|
+
marginBottom: "8px",
|
|
2371
|
+
textTransform: "uppercase",
|
|
2372
|
+
color: "var(--muted-foreground, #6b7280)"
|
|
2373
|
+
},
|
|
2374
|
+
children: "Widgets"
|
|
2375
|
+
}, undefined, false, undefined, this),
|
|
2376
|
+
/* @__PURE__ */ jsxDEV11("ul", {
|
|
2377
|
+
style: { listStyle: "none", margin: 0, padding: 0 },
|
|
2378
|
+
children: filtered.map((w) => /* @__PURE__ */ jsxDEV11("li", {
|
|
2379
|
+
"data-widget-key": w.widgetKey,
|
|
2380
|
+
role: onInsert ? "button" : undefined,
|
|
2381
|
+
tabIndex: onInsert ? 0 : undefined,
|
|
2382
|
+
style: {
|
|
2383
|
+
padding: "8px 12px",
|
|
2384
|
+
marginBottom: "4px",
|
|
2385
|
+
borderRadius: "6px",
|
|
2386
|
+
cursor: onInsert ? "grab" : "default",
|
|
2387
|
+
backgroundColor: "white",
|
|
2388
|
+
border: "1px solid var(--border, #e5e7eb)"
|
|
2389
|
+
},
|
|
2390
|
+
onClick: onInsert ? () => onInsert(w.widgetKey, "primary") : undefined,
|
|
2391
|
+
onKeyDown: onInsert ? (e) => {
|
|
2392
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
2393
|
+
e.preventDefault();
|
|
2394
|
+
onInsert(w.widgetKey, "primary");
|
|
2395
|
+
}
|
|
2396
|
+
} : undefined,
|
|
2397
|
+
children: w.title
|
|
2398
|
+
}, w.widgetKey, false, undefined, this))
|
|
2399
|
+
}, undefined, false, undefined, this)
|
|
2400
|
+
]
|
|
2401
|
+
}, undefined, true, undefined, this);
|
|
2402
|
+
}
|
|
2403
|
+
export {
|
|
2404
|
+
verifyWorkspaceOverlay,
|
|
2405
|
+
validateSurfacePatchOp,
|
|
2406
|
+
validateSurfacePatch,
|
|
2407
|
+
validatePatchProposal,
|
|
2408
|
+
validateLayoutSlots,
|
|
2409
|
+
validateBundleNodeKinds,
|
|
2410
|
+
useIsEditing,
|
|
2411
|
+
useBundlePreferences,
|
|
2412
|
+
useBundlePlan,
|
|
2413
|
+
toOverlayTargetRef,
|
|
2414
|
+
toOverlayScopeContext,
|
|
2415
|
+
toOverlayRenderableField,
|
|
2416
|
+
toOverlayRenderable,
|
|
2417
|
+
signWorkspaceOverlay,
|
|
2418
|
+
rollbackSurfacePatches,
|
|
2419
|
+
resolvePreferenceProfile,
|
|
2420
|
+
resolveBundle,
|
|
2421
|
+
resizablePanelsAdapterStub,
|
|
2422
|
+
proposePatchToolConfig,
|
|
2423
|
+
motionAdapterStub,
|
|
2424
|
+
mergeOverlayResultIntoFields,
|
|
2425
|
+
fromOverlayRenderableField,
|
|
2426
|
+
floatingUiAdapterStub,
|
|
2427
|
+
evaluatePatchProposalPolicy,
|
|
2428
|
+
evaluateAndEmitPatchPolicy,
|
|
2429
|
+
emitPolicyRedacted,
|
|
2430
|
+
emitPolicyDenied,
|
|
2431
|
+
emitPatchRejected,
|
|
2432
|
+
emitPatchProposed,
|
|
2433
|
+
emitPatchApproved,
|
|
2434
|
+
emitOverlaySaved,
|
|
2435
|
+
emitOverlayFailed,
|
|
2436
|
+
emitOverlayApplied,
|
|
2437
|
+
dndKitAdapterStub,
|
|
2438
|
+
dndKitAdapter,
|
|
2439
|
+
defineModuleBundle,
|
|
2440
|
+
defaultPreferenceAdapter,
|
|
2441
|
+
createWidgetRegistry,
|
|
2442
|
+
createOverrideStoreWithApprovalGate,
|
|
2443
|
+
createMutableFieldRendererRegistry,
|
|
2444
|
+
createInMemoryOverrideStore,
|
|
2445
|
+
createFieldRendererRegistry,
|
|
2446
|
+
createCommandRegistry,
|
|
2447
|
+
createBundleExtensionRegistry,
|
|
2448
|
+
createActionRegistry,
|
|
2449
|
+
compilePlannerPrompt,
|
|
2450
|
+
buildSurfacePatchProposal,
|
|
2451
|
+
buildOverrideTargetKey,
|
|
2452
|
+
buildContext,
|
|
2453
|
+
blocknoteAdapterStub,
|
|
2454
|
+
applySurfacePatch,
|
|
2455
|
+
applyEntityFieldOverlays,
|
|
2456
|
+
aiSdkAdapterStub,
|
|
2457
|
+
WidgetPalette,
|
|
2458
|
+
SlotRenderer,
|
|
2459
|
+
RegionRenderer,
|
|
2460
|
+
PatchProposalCard,
|
|
2461
|
+
PROPOSE_PATCH_TOOL_SCHEMA,
|
|
2462
|
+
OverlayConflictResolver,
|
|
2463
|
+
FALLBACK_FIELD_KIND,
|
|
2464
|
+
BundleRenderer,
|
|
2465
|
+
BundleProvider
|
|
2466
|
+
};
|